]> git.saurik.com Git - apple/configd.git/blob - dnsinfo/dnsinfo_copy.c
26b1e9de3f17a3eed069fc0532f12c796c4d18e9
[apple/configd.git] / dnsinfo / dnsinfo_copy.c
1 /*
2 * Copyright (c) 2004, 2006, 2008-2011 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
37 #include "dnsinfo.h"
38 #include "dnsinfo_private.h"
39 #include "shared_dns_info.h"
40 #include "network_information_priv.h"
41
42
43 static pthread_once_t _dns_initialized = PTHREAD_ONCE_INIT;
44 static pthread_mutex_t _dns_lock = PTHREAD_MUTEX_INITIALIZER;
45 static mach_port_t _dns_server = MACH_PORT_NULL;
46
47 enum {
48 get_dns_info = 1,
49 get_nwi_state = 2,
50 };
51
52 typedef uint32_t getflags;
53
54 static void
55 __dns_fork_handler()
56 {
57 // the process has forked (and we are the child process)
58 _dns_server = MACH_PORT_NULL;
59 return;
60 }
61
62
63 static void
64 __dns_initialize(void)
65 {
66 // add handler to cleanup after fork()
67 (void) pthread_atfork(NULL, NULL, __dns_fork_handler);
68
69 return;
70 }
71
72
73 static boolean_t
74 add_list(void **padding, uint32_t *n_padding, int32_t count, int32_t size, void **list)
75 {
76 int32_t need;
77
78 need = count * size;
79 if (need > *n_padding) {
80 return FALSE;
81 }
82
83 *list = (need == 0) ? NULL : *padding;
84 *padding += need;
85 *n_padding -= need;
86 return TRUE;
87 }
88
89
90 #define DNS_CONFIG_BUF_MAX 1024*1024
91
92
93 static kern_return_t
94 _dns_server_copy(void* dataRef, mach_msg_type_number_t* dataLen, getflags flags){
95 mach_port_t server;
96 kern_return_t status = KERN_FAILURE;
97
98 // initialize runtime
99 pthread_once(&_dns_initialized, __dns_initialize);
100
101 // open a new session with the DNS configuration server
102 server = _dns_server;
103 while (TRUE) {
104 if (server != MACH_PORT_NULL) {
105 if (flags == get_dns_info) {
106 status = shared_dns_infoGet(server, dataRef, dataLen);
107 } else {
108 status = shared_nwi_stateGet(server, dataRef, dataLen);
109 }
110 if (status == KERN_SUCCESS) {
111 break;
112 }
113
114 // our [cached] server port is not valid
115 if ((status != MACH_SEND_INVALID_DEST) && (status != MIG_SERVER_DIED)) {
116 // if we got an unexpected error, don't retry
117 fprintf(stderr,
118 "dns_configuration_copy shared_dns_infoGet(): %s\n",
119 mach_error_string(status));
120 break;
121 }
122 }
123
124 pthread_mutex_lock(&_dns_lock);
125 if (_dns_server != MACH_PORT_NULL) {
126 if (server == _dns_server) {
127 // if the server we tried returned the error
128 (void)mach_port_deallocate(mach_task_self(), server);
129 _dns_server = _dns_configuration_server_port();
130 } else {
131 // another thread has refreshed the DNS server port
132 }
133 } else {
134 _dns_server = _dns_configuration_server_port();
135 }
136 server = _dns_server;
137 pthread_mutex_unlock(&_dns_lock);
138
139 if (server == MACH_PORT_NULL) {
140 // if server not available
141 break;
142 }
143 }
144
145 return status;
146 }
147
148
149 __private_extern__
150 nwi_state*
151 _nwi_state_copy() {
152 dnsDataOut_t dataRef = NULL;
153 mach_msg_type_number_t dataLen = 0;
154 kern_return_t status;
155 nwi_state* state = NULL;
156
157 status = _dns_server_copy(&dataRef, &dataLen, get_nwi_state);
158 if (status != KERN_SUCCESS) {
159 return NULL;
160 }
161
162 if (dataRef != NULL) {
163 state = malloc(dataLen);
164 if (state == NULL) {
165 vm_deallocate(mach_task_self(), (vm_address_t)dataRef,
166 dataLen);
167 return NULL;
168 }
169 memcpy((void*) state, (void*) dataRef, dataLen);
170 state->ref = 0;
171 status = vm_deallocate(mach_task_self(), (vm_address_t)dataRef, dataLen);
172 if (status != KERN_SUCCESS) {
173 mach_error("vm_deallocate():", status);
174 free(state);
175 return NULL;
176 }
177 }
178
179 return state;
180 }
181
182
183 static _dns_config_buf_t *
184 copy_dns_info()
185 {
186 uint8_t *buf = NULL;
187 dnsDataOut_t dataRef = NULL;
188 mach_msg_type_number_t dataLen = 0;
189 kern_return_t status;
190
191 status = _dns_server_copy(&dataRef, &dataLen, get_dns_info);
192 if (status != KERN_SUCCESS) {
193 return NULL;
194 }
195
196 if (dataRef != NULL) {
197 if ((dataLen >= sizeof(_dns_config_buf_t)) && (dataLen <= DNS_CONFIG_BUF_MAX)) {
198 /* ALIGN: cast okay since _dns_config_buf_t is int aligned */
199 _dns_config_buf_t *config = (_dns_config_buf_t *)(void *)dataRef;
200 uint32_t n_padding = ntohl(config->n_padding);
201
202 if (n_padding <= (DNS_CONFIG_BUF_MAX - dataLen)) {
203 uint32_t len;
204
205 len = dataLen + n_padding;
206 buf = malloc(len);
207 bcopy((void *)dataRef, buf, dataLen);
208 bzero(&buf[dataLen], n_padding);
209 }
210 }
211
212 status = vm_deallocate(mach_task_self(), (vm_address_t)dataRef, dataLen);
213 if (status != KERN_SUCCESS) {
214 mach_error("vm_deallocate():", status);
215 free(buf);
216 return NULL;
217 }
218 }
219
220 /* ALIGN: buf malloc'ed, should be aligned >8 bytes */
221 return (_dns_config_buf_t *)(void *)buf;
222 }
223
224
225 static dns_resolver_t *
226 expand_resolver(_dns_resolver_buf_t *buf, uint32_t n_buf, void **padding, uint32_t *n_padding)
227 {
228 dns_attribute_t *attribute;
229 uint32_t n_attribute;
230 int32_t n_nameserver = 0;
231 int32_t n_search = 0;
232 int32_t n_sortaddr = 0;
233 dns_resolver_t *resolver = (dns_resolver_t *)&buf->resolver;
234
235 if (n_buf < sizeof(_dns_resolver_buf_t)) {
236 goto error;
237 }
238
239 // initialize domain
240
241 resolver->domain = NULL;
242
243 // initialize nameserver list
244
245 resolver->n_nameserver = ntohl(resolver->n_nameserver);
246 if (!add_list(padding,
247 n_padding,
248 resolver->n_nameserver,
249 sizeof(DNS_PTR(struct sockaddr *, x)),
250 (void **)&resolver->nameserver)) {
251 goto error;
252 }
253
254 // initialize port
255
256 resolver->port = ntohs(resolver->port);
257
258 // initialize search list
259
260 resolver->n_search = ntohl(resolver->n_search);
261 if (!add_list(padding,
262 n_padding,
263 resolver->n_search,
264 sizeof(DNS_PTR(char *, x)),
265 (void **)&resolver->search)) {
266 goto error;
267 }
268
269 // initialize sortaddr list
270
271 resolver->n_sortaddr = ntohl(resolver->n_sortaddr);
272 if (!add_list(padding,
273 n_padding,
274 resolver->n_sortaddr,
275 sizeof(DNS_PTR(dns_sortaddr_t *, x)),
276 (void **)&resolver->sortaddr)) {
277 goto error;
278 }
279
280 // initialize options
281
282 resolver->options = NULL;
283
284 // initialize timeout
285
286 resolver->timeout = ntohl(resolver->timeout);
287
288 // initialize search_order
289
290 resolver->search_order = ntohl(resolver->search_order);
291
292 // initialize if_index
293
294 resolver->if_index = ntohl(resolver->if_index);
295
296 // initialize flags
297
298 resolver->flags = ntohl(resolver->flags);
299
300 // initialize SCNetworkReachability flags
301
302 resolver->reach_flags = ntohl(resolver->reach_flags);
303
304 // process resolver buffer "attribute" data
305
306 n_attribute = n_buf - sizeof(_dns_resolver_buf_t);
307 /* ALIGN: alignment not assumed, using accessors */
308 attribute = (dns_attribute_t *)(void *)&buf->attribute[0];
309 if (n_attribute != ntohl(buf->n_attribute)) {
310 goto error;
311 }
312
313 while (n_attribute >= sizeof(dns_attribute_t)) {
314 int32_t attribute_length = ntohl(attribute->length);
315
316 switch (ntohl(attribute->type)) {
317 case RESOLVER_ATTRIBUTE_DOMAIN :
318 resolver->domain = (char *)&attribute->attribute[0];
319 break;
320
321 case RESOLVER_ATTRIBUTE_ADDRESS :
322 resolver->nameserver[n_nameserver++] = (struct sockaddr *)&attribute->attribute[0];
323 break;
324
325 case RESOLVER_ATTRIBUTE_SEARCH :
326 resolver->search[n_search++] = (char *)&attribute->attribute[0];
327 break;
328
329 case RESOLVER_ATTRIBUTE_SORTADDR :
330 resolver->sortaddr[n_sortaddr++] = (dns_sortaddr_t *)(void *)&attribute->attribute[0];
331 break;
332
333 case RESOLVER_ATTRIBUTE_OPTIONS :
334 resolver->options = (char *)&attribute->attribute[0];
335 break;
336
337 default :
338 break;
339 }
340
341 attribute = (dns_attribute_t *)((void *)attribute + attribute_length);
342 n_attribute -= attribute_length;
343 }
344
345 if ((n_nameserver != resolver->n_nameserver) ||
346 (n_search != resolver->n_search ) ||
347 (n_sortaddr != resolver->n_sortaddr )) {
348 goto error;
349 }
350
351 return resolver;
352
353 error :
354
355 return NULL;
356 }
357
358
359 static dns_config_t *
360 expand_config(_dns_config_buf_t *buf)
361 {
362 dns_attribute_t *attribute;
363 dns_config_t *config = (dns_config_t *)buf;
364 uint32_t n_attribute;
365 uint32_t n_padding;
366 int32_t n_resolver = 0;
367 int32_t n_scoped_resolver = 0;
368 void *padding;
369
370 // establish padding
371
372 padding = &buf->attribute[ntohl(buf->n_attribute)];
373 n_padding = ntohl(buf->n_padding);
374
375 // initialize resolver lists
376
377 config->n_resolver = ntohl(config->n_resolver);
378 if (!add_list(&padding,
379 &n_padding,
380 config->n_resolver,
381 sizeof(DNS_PTR(dns_resolver_t *, x)),
382 (void **)&config->resolver)) {
383 goto error;
384 }
385
386 config->n_scoped_resolver = ntohl(config->n_scoped_resolver);
387 if (!add_list(&padding,
388 &n_padding,
389 config->n_scoped_resolver,
390 sizeof(DNS_PTR(dns_resolver_t *, x)),
391 (void **)&config->scoped_resolver)) {
392 goto error;
393 }
394
395 // process configuration buffer "attribute" data
396
397 n_attribute = ntohl(buf->n_attribute);
398 attribute = (dns_attribute_t *)(void *)&buf->attribute[0];
399
400 while (n_attribute >= sizeof(dns_attribute_t)) {
401 uint32_t attribute_length = ntohl(attribute->length);
402 uint32_t attribute_type = ntohl(attribute->type);
403
404 switch (attribute_type) {
405 case CONFIG_ATTRIBUTE_RESOLVER :
406 case CONFIG_ATTRIBUTE_SCOPED_RESOLVER : {
407 dns_resolver_t *resolver;
408
409 // expand resolver buffer
410
411 resolver = expand_resolver((_dns_resolver_buf_t *)(void *)&attribute->attribute[0],
412 attribute_length - sizeof(dns_attribute_t),
413 &padding,
414 &n_padding);
415 if (resolver == NULL) {
416 goto error;
417 }
418
419 // add resolver to config list
420
421 if (attribute_type == CONFIG_ATTRIBUTE_RESOLVER) {
422 config->resolver[n_resolver++] = resolver;
423 } else {
424 config->scoped_resolver[n_scoped_resolver++] = resolver;
425 }
426
427 break;
428 }
429
430 default :
431 break;
432 }
433
434 attribute = (dns_attribute_t *)((void *)attribute + attribute_length);
435 n_attribute -= attribute_length;
436 }
437
438 if (n_resolver != config->n_resolver) {
439 goto error;
440 }
441
442 if (n_scoped_resolver != config->n_scoped_resolver) {
443 goto error;
444 }
445
446 return config;
447
448 error :
449
450 return NULL;
451 }
452
453
454 const char *
455 dns_configuration_notify_key()
456 {
457 const char *key;
458
459 // initialize runtime
460 pthread_once(&_dns_initialized, __dns_initialize);
461
462 key = _dns_configuration_notify_key();
463 return key;
464 }
465
466
467 dns_config_t *
468 dns_configuration_copy()
469 {
470 _dns_config_buf_t *buf;
471 dns_config_t *config;
472
473 buf = copy_dns_info();
474 if (buf == NULL) {
475 return NULL;
476 }
477
478 config = expand_config(buf);
479 if (config == NULL) {
480 free(buf);
481 return NULL;
482 }
483
484 return config;
485 }
486
487
488 void
489 dns_configuration_free(dns_config_t *config)
490 {
491 if (config == NULL) {
492 return;
493 }
494
495 free((void *)config);
496 return;
497 }
498
499
500 void
501 _dns_configuration_ack(dns_config_t *config, const char *bundle_id)
502 {
503 return;
504 }
505
506 #ifdef MAIN
507
508 int
509 main(int argc, char **argv)
510 {
511 dns_config_t *config;
512
513 config = dns_configuration_copy();
514 if (config != NULL) {
515 dns_configuration_free(&config);
516 }
517
518 exit(0);
519 }
520
521 #endif