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