1/*
2 * Copyright (c) 2004, 2006, 2008-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * Modification History
26 *
27 * March 9, 2004		Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <pthread.h>
34#include <mach/mach.h>
35#include <mach/mach_error.h>
36#include <dispatch/dispatch.h>
37#include <xpc/xpc.h>
38
39#include "libSystemConfiguration_client.h"
40#include "dnsinfo.h"
41#include "dnsinfo_private.h"
42
43typedef uint32_t getflags;
44
45static boolean_t
46add_list(void **padding, uint32_t *n_padding, int32_t count, int32_t size, void **list)
47{
48	int32_t	need;
49
50	need = count * size;
51	if (need > *n_padding) {
52		return FALSE;
53	}
54
55	*list = (need == 0) ? NULL : *padding;
56	*padding   += need;
57	*n_padding -= need;
58	return TRUE;
59}
60
61
62#define	DNS_CONFIG_BUF_MAX	1024*1024
63
64
65static dns_resolver_t *
66expand_resolver(_dns_resolver_buf_t *buf, uint32_t n_buf, void **padding, uint32_t *n_padding)
67{
68	dns_attribute_t		*attribute;
69	uint32_t		n_attribute;
70	int32_t			n_nameserver    = 0;
71	int32_t			n_search	= 0;
72	int32_t			n_sortaddr      = 0;
73	dns_resolver_t		*resolver	= (dns_resolver_t *)&buf->resolver;
74
75	if (n_buf < sizeof(_dns_resolver_buf_t)) {
76		goto error;
77	}
78
79	// initialize domain
80
81	resolver->domain = NULL;
82
83	// initialize nameserver list
84
85	resolver->n_nameserver = ntohl(resolver->n_nameserver);
86	if (!add_list(padding,
87		      n_padding,
88		      resolver->n_nameserver,
89		      sizeof(DNS_PTR(struct sockaddr *, x)),
90		      (void **)&resolver->nameserver)) {
91		goto error;
92	}
93
94	// initialize port
95
96	resolver->port = ntohs(resolver->port);
97
98	// initialize search list
99
100	resolver->n_search = ntohl(resolver->n_search);
101	if (!add_list(padding,
102		      n_padding,
103		      resolver->n_search,
104		      sizeof(DNS_PTR(char *, x)),
105		      (void **)&resolver->search)) {
106		goto error;
107	}
108
109	// initialize sortaddr list
110
111	resolver->n_sortaddr = ntohl(resolver->n_sortaddr);
112	if (!add_list(padding,
113		      n_padding,
114		      resolver->n_sortaddr,
115		      sizeof(DNS_PTR(dns_sortaddr_t *, x)),
116		      (void **)&resolver->sortaddr)) {
117		goto error;
118	}
119
120	// initialize options
121
122	resolver->options = NULL;
123
124	// initialize timeout
125
126	resolver->timeout = ntohl(resolver->timeout);
127
128	// initialize search_order
129
130	resolver->search_order = ntohl(resolver->search_order);
131
132	// initialize if_index
133
134	resolver->if_index = ntohl(resolver->if_index);
135
136	// initialize service_identifier
137
138	resolver->service_identifier = ntohl(resolver->service_identifier);
139
140	// initialize flags
141
142	resolver->flags = ntohl(resolver->flags);
143
144	// initialize SCNetworkReachability flags
145
146	resolver->reach_flags = ntohl(resolver->reach_flags);
147
148	// process resolver buffer "attribute" data
149
150	n_attribute = n_buf - sizeof(_dns_resolver_buf_t);
151	/* ALIGN: alignment not assumed, using accessors */
152	attribute = (dns_attribute_t *)(void *)&buf->attribute[0];
153	if (n_attribute != ntohl(buf->n_attribute)) {
154		goto error;
155	}
156
157	while (n_attribute >= sizeof(dns_attribute_t)) {
158		uint32_t	attribute_length	= ntohl(attribute->length);
159
160		switch (ntohl(attribute->type)) {
161			case RESOLVER_ATTRIBUTE_DOMAIN :
162				resolver->domain = (char *)&attribute->attribute[0];
163				break;
164
165			case RESOLVER_ATTRIBUTE_ADDRESS :
166				resolver->nameserver[n_nameserver++] = (struct sockaddr *)&attribute->attribute[0];
167				break;
168
169			case RESOLVER_ATTRIBUTE_SEARCH :
170				resolver->search[n_search++] = (char *)&attribute->attribute[0];
171				break;
172
173			case RESOLVER_ATTRIBUTE_SORTADDR :
174				resolver->sortaddr[n_sortaddr++] = (dns_sortaddr_t *)(void *)&attribute->attribute[0];
175				break;
176
177			case RESOLVER_ATTRIBUTE_OPTIONS :
178				resolver->options = (char *)&attribute->attribute[0];
179				break;
180
181			default :
182				break;
183		}
184
185		attribute   = (dns_attribute_t *)((void *)attribute + attribute_length);
186		n_attribute -= attribute_length;
187	}
188
189	if ((n_nameserver != resolver->n_nameserver) ||
190	    (n_search     != resolver->n_search    ) ||
191	    (n_sortaddr   != resolver->n_sortaddr  )) {
192		goto error;
193	}
194
195	return resolver;
196
197    error :
198
199	return NULL;
200}
201
202
203static dns_config_t *
204expand_config(_dns_config_buf_t *buf)
205{
206	dns_attribute_t		*attribute;
207	dns_config_t		*config			= (dns_config_t *)buf;
208	uint32_t		n_attribute;
209	uint32_t		n_padding;
210	int32_t			n_resolver		= 0;
211	int32_t			n_scoped_resolver	= 0;
212	int32_t			n_service_specific_resolver	= 0;
213	void			*padding;
214
215	// establish padding
216
217	padding   = &buf->attribute[ntohl(buf->n_attribute)];
218	n_padding = ntohl(buf->n_padding);
219
220	// initialize resolver lists
221
222	config->n_resolver = ntohl(config->n_resolver);
223	if (!add_list(&padding,
224		      &n_padding,
225		      config->n_resolver,
226		      sizeof(DNS_PTR(dns_resolver_t *, x)),
227		      (void **)&config->resolver)) {
228		goto error;
229	}
230
231	config->n_scoped_resolver = ntohl(config->n_scoped_resolver);
232	if (!add_list(&padding,
233		      &n_padding,
234		      config->n_scoped_resolver,
235		      sizeof(DNS_PTR(dns_resolver_t *, x)),
236		      (void **)&config->scoped_resolver)) {
237		goto error;
238	}
239
240	config->n_service_specific_resolver = ntohl(config->n_service_specific_resolver);
241	if (!add_list(&padding,
242		      &n_padding,
243		      config->n_service_specific_resolver,
244		      sizeof(DNS_PTR(dns_resolver_t *, x)),
245		      (void **)&config->service_specific_resolver)) {
246		goto error;
247	}
248
249	// process configuration buffer "attribute" data
250
251	n_attribute = ntohl(buf->n_attribute);
252	attribute   = (dns_attribute_t *)(void *)&buf->attribute[0];
253
254	while (n_attribute >= sizeof(dns_attribute_t)) {
255		uint32_t	attribute_length	= ntohl(attribute->length);
256		uint32_t	attribute_type		= ntohl(attribute->type);
257
258		switch (attribute_type) {
259			case CONFIG_ATTRIBUTE_RESOLVER :
260			case CONFIG_ATTRIBUTE_SCOPED_RESOLVER   :
261			case CONFIG_ATTRIBUTE_SERVICE_SPECIFIC_RESOLVER : {
262				dns_resolver_t	*resolver;
263
264				// expand resolver buffer
265
266				resolver = expand_resolver((_dns_resolver_buf_t *)(void *)&attribute->attribute[0],
267							   attribute_length - sizeof(dns_attribute_t),
268							   &padding,
269							   &n_padding);
270				if (resolver == NULL) {
271					goto error;
272				}
273
274				// add resolver to config list
275
276				if (attribute_type == CONFIG_ATTRIBUTE_RESOLVER) {
277					config->resolver[n_resolver++] = resolver;
278				} else if (attribute_type == CONFIG_ATTRIBUTE_SCOPED_RESOLVER) {
279					config->scoped_resolver[n_scoped_resolver++] = resolver;
280				} else if (attribute_type == CONFIG_ATTRIBUTE_SERVICE_SPECIFIC_RESOLVER) {
281					config->service_specific_resolver[n_service_specific_resolver++] = resolver;
282				}
283
284				break;
285			}
286
287			default :
288				break;
289		}
290
291		attribute   = (dns_attribute_t *)((void *)attribute + attribute_length);
292		n_attribute -= attribute_length;
293	}
294
295	if (n_resolver != config->n_resolver) {
296		goto error;
297	}
298
299	if (n_scoped_resolver != config->n_scoped_resolver) {
300		goto error;
301	}
302
303	if (n_service_specific_resolver != config->n_service_specific_resolver) {
304		goto error;
305	}
306
307	return config;
308
309    error :
310
311	return NULL;
312}
313
314
315const char *
316dns_configuration_notify_key()
317{
318	const char	*key;
319
320#if	!TARGET_IPHONE_SIMULATOR
321	key = "com.apple.system.SystemConfiguration.dns_configuration";
322#else	// !TARGET_IPHONE_SIMULATOR
323	key = "com.apple.iOS_Simulator.SystemConfiguration.dns_configuration";
324#endif	// !TARGET_IPHONE_SIMULATOR
325	return key;
326}
327
328
329#pragma mark -
330#pragma mark DNS configuration [dnsinfo] client support
331
332
333// Note: protected by __dns_configuration_queue()
334static int			dnsinfo_active	= 0;
335static libSC_info_client_t	*dnsinfo_client	= NULL;
336
337
338static dispatch_queue_t
339__dns_configuration_queue()
340{
341	static dispatch_once_t  once;
342	static dispatch_queue_t q;
343
344	dispatch_once(&once, ^{
345		q = dispatch_queue_create(DNSINFO_SERVICE_NAME, NULL);
346	});
347
348	return q;
349}
350
351
352dns_config_t *
353dns_configuration_copy()
354{
355	uint8_t			*buf		= NULL;
356	dns_config_t		*config		= NULL;
357	static const char	*proc_name	= NULL;
358	xpc_object_t		reqdict;
359	xpc_object_t		reply;
360
361	dispatch_sync(__dns_configuration_queue(), ^{
362		if ((dnsinfo_active++ == 0) || (dnsinfo_client == NULL)) {
363			static dispatch_once_t	once;
364			static const char	*service_name	= DNSINFO_SERVICE_NAME;
365
366			dispatch_once(&once, ^{
367				const char	*name;
368
369				// get [XPC] service name
370				name = getenv(service_name);
371				if ((name != NULL) && (issetugid() == 0)) {
372					service_name = strdup(name);
373				}
374
375				// get process name
376				proc_name = getprogname();
377			});
378
379			dnsinfo_client =
380				libSC_info_client_create(__dns_configuration_queue(),	// dispatch queue
381							 service_name,			// XPC service name
382							 "DNS configuration");		// service description
383			if (dnsinfo_client == NULL) {
384				--dnsinfo_active;
385			}
386		}
387	});
388
389	if ((dnsinfo_client == NULL) || !dnsinfo_client->active) {
390		// if DNS configuration server not available
391		return NULL;
392	}
393
394	// create message
395	reqdict = xpc_dictionary_create(NULL, NULL, 0);
396
397	// set process name
398	if (proc_name != NULL) {
399		xpc_dictionary_set_string(reqdict, DNSINFO_PROC_NAME, proc_name);
400	}
401
402	// set request
403	xpc_dictionary_set_int64(reqdict, DNSINFO_REQUEST, DNSINFO_REQUEST_COPY);
404
405	// send request to the DNS configuration server
406	reply = libSC_send_message_with_reply_sync(dnsinfo_client, reqdict);
407	xpc_release(reqdict);
408
409	if (reply != NULL) {
410		const void	*dataRef;
411		size_t		dataLen	= 0;
412
413		dataRef = xpc_dictionary_get_data(reply, DNSINFO_CONFIGURATION, &dataLen);
414		if ((dataRef != NULL) &&
415		    ((dataLen >= sizeof(_dns_config_buf_t)) && (dataLen <= DNS_CONFIG_BUF_MAX))) {
416			_dns_config_buf_t       *config         = (_dns_config_buf_t *)(void *)dataRef;
417			uint32_t                n_padding       = ntohl(config->n_padding);
418
419			if (n_padding <= (DNS_CONFIG_BUF_MAX - dataLen)) {
420				uint32_t        len;
421
422				len = dataLen + n_padding;
423				buf = malloc(len);
424				bcopy((void *)dataRef, buf, dataLen);
425				bzero(&buf[dataLen], n_padding);
426			}
427		}
428
429		xpc_release(reply);
430	}
431
432	if (buf != NULL) {
433		/* ALIGN: cast okay since _dns_config_buf_t is int aligned */
434		config = expand_config((_dns_config_buf_t *)(void *)buf);
435		if (config == NULL) {
436			free(buf);
437		}
438	}
439
440	return config;
441}
442
443
444void
445dns_configuration_free(dns_config_t *config)
446{
447	if (config == NULL) {
448		return;	// ASSERT
449	}
450
451	dispatch_sync(__dns_configuration_queue(), ^{
452		if (--dnsinfo_active == 0) {
453			// if last reference, drop connection
454			libSC_info_client_release(dnsinfo_client);
455			dnsinfo_client = NULL;
456		}
457	});
458
459	free((void *)config);
460	return;
461}
462
463
464void
465_dns_configuration_ack(dns_config_t *config, const char *bundle_id)
466{
467	xpc_object_t	reqdict;
468
469	if (config == NULL) {
470		return;	// ASSERT
471	}
472
473	if ((dnsinfo_client == NULL) || !dnsinfo_client->active) {
474		// if DNS configuration server not available
475		return;
476	}
477
478	dispatch_sync(__dns_configuration_queue(), ^{
479		dnsinfo_active++;	// keep connection active (for the life of the process)
480	});
481
482	// create message
483	reqdict = xpc_dictionary_create(NULL, NULL, 0);
484
485	// set request
486	xpc_dictionary_set_int64(reqdict, DNSINFO_REQUEST, DNSINFO_REQUEST_ACKNOWLEDGE);
487
488	// set generation
489	xpc_dictionary_set_uint64(reqdict, DNSINFO_GENERATION, config->generation);
490
491	// send acknowledgement to the DNS configuration server
492	xpc_connection_send_message(dnsinfo_client->connection, reqdict);
493
494	xpc_release(reqdict);
495	return;
496}
497
498#ifdef MAIN
499
500int
501main(int argc, char **argv)
502{
503	dns_config_t	*config;
504
505	config = dns_configuration_copy();
506	if (config != NULL) {
507		dns_configuration_free(config);
508	}
509
510	exit(0);
511}
512
513#endif
514