]>
Commit | Line | Data |
---|---|---|
8a97ab44 | 1 | /* |
e01cf2fc | 2 | * Copyright (c) 1999-2007 Apple Inc. All rights reserved. |
8a97ab44 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
e01cf2fc A |
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. | |
8a97ab44 A |
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, | |
e01cf2fc A |
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. | |
8a97ab44 A |
20 | * |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | #include "dns.h" | |
25 | #include <stdio.h> | |
26 | #include <stdlib.h> | |
27 | #include <string.h> | |
28 | #include <unistd.h> | |
29 | #include <time.h> | |
30 | #include <netdb.h> | |
31 | #include <stdarg.h> | |
32 | #include <sys/stat.h> | |
33 | #include <sys/dir.h> | |
34 | #include <errno.h> | |
35 | #include <ifaddrs.h> | |
36 | #include <net/if.h> | |
37 | #include <pthread.h> | |
38 | #include <netinet/in.h> | |
39 | #include <arpa/nameser.h> | |
40 | #include <resolv.h> | |
41 | #include <fcntl.h> | |
42 | #include <notify.h> | |
43 | #include <dnsinfo.h> | |
44 | #include "dns_private.h" | |
45 | #include "res_private.h" | |
46 | ||
47 | #define INET_NTOP_AF_INET_OFFSET 4 | |
48 | #define INET_NTOP_AF_INET6_OFFSET 8 | |
49 | ||
50 | #define SEARCH_COUNT_INIT -1 | |
51 | ||
52 | #define DNS_RESOLVER_DIR "/etc/resolver" | |
53 | ||
9571391b A |
54 | #define NOTIFY_DNS_CONTROL_NAME "com.apple.system.dns" |
55 | #define DNS_CONTROL_FLAG_DEBUG 0x0000000000000001LL | |
56 | #define DNS_CONTROL_FLAG_NO_MDNS 0x0000000000000002LL | |
57 | ||
8a97ab44 A |
58 | #define NOTIFY_DIR_NAME "com.apple.system.dns.resolver.dir" |
59 | #define DNS_DELAY_NAME "com.apple.system.dns.delay" | |
60 | ||
61 | #define DNS_DELAY_INTERVAL 4 | |
62 | ||
63 | #define DNS_PRIVATE_HANDLE_TYPE_SUPER 0 | |
64 | #define DNS_PRIVATE_HANDLE_TYPE_PLAIN 1 | |
65 | ||
66 | #define MDNS_MIN_TTL 2 | |
67 | ||
9571391b A |
68 | static int dns_control_token = -1; |
69 | static int dns_control_mdns = 1; | |
70 | static int dns_control_debug = 0; | |
71 | static pthread_mutex_t dns_control_lock = PTHREAD_MUTEX_INITIALIZER; | |
72 | ||
8a97ab44 A |
73 | extern uint32_t notify_monitor_file(int token, const char *name, int flags); |
74 | ||
75 | extern void res_client_close(res_state res); | |
76 | extern res_state res_state_new(); | |
77 | extern int res_nquery_soa_min(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen, int *min); | |
78 | extern int res_nsearch_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, uint32_t *fromlen); | |
79 | extern int __res_nsearch_list_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, uint32_t *fromlen, int nsearch, char **search); | |
80 | ||
81 | extern char *res_next_word(char **p); | |
82 | extern res_state res_build_start(res_state res); | |
83 | extern int res_build(res_state res, uint16_t port, uint32_t *nsrch, char *key, char *val); | |
84 | extern int res_build_sortlist(res_state res, struct in_addr addr, struct in_addr mask); | |
85 | ||
86 | static void | |
87 | _pdns_set_name(pdns_handle_t *pdns, const char *name) | |
88 | { | |
89 | int n; | |
90 | ||
91 | if (pdns == NULL) return; | |
92 | if (name == NULL) return; | |
93 | ||
94 | /* only set the name once */ | |
95 | if (pdns->name != NULL) return; | |
96 | ||
97 | /* strip trailing dots */ | |
98 | n = strlen(name) - 1; | |
99 | while ((n >= 0) && (name[n] == '.')) n--; | |
100 | ||
101 | if (n < 0) return; | |
102 | ||
103 | n++; | |
104 | pdns->name = calloc(n + 1, sizeof(char)); | |
105 | if (pdns->name == NULL) return; | |
106 | memcpy(pdns->name, name, n); | |
107 | } | |
108 | ||
109 | static pdns_handle_t * | |
110 | _pdns_build_start(char *name) | |
111 | { | |
112 | pdns_handle_t *pdns; | |
113 | ||
114 | pdns = (pdns_handle_t *)calloc(1, sizeof(pdns_handle_t)); | |
115 | if (pdns == NULL) return NULL; | |
116 | ||
117 | pdns->res = res_build_start(NULL); | |
118 | if (pdns->res == NULL) | |
119 | { | |
120 | free(pdns); | |
121 | return NULL; | |
122 | } | |
123 | ||
124 | _pdns_set_name(pdns, name); | |
125 | pdns->port = NS_DEFAULTPORT; | |
126 | ||
127 | return pdns; | |
128 | } | |
129 | ||
130 | static int | |
131 | _pdns_build_finish(pdns_handle_t *pdns) | |
132 | { | |
133 | uint32_t n; | |
134 | ||
135 | if (pdns == NULL) return -1; | |
136 | ||
137 | n = pdns->res->nscount; | |
138 | if (n == 0) n = 1; | |
139 | ||
140 | if (pdns->total_timeout == 0) | |
141 | { | |
142 | if (pdns->send_timeout == 0) pdns->total_timeout = RES_MAXRETRANS; | |
143 | else pdns->total_timeout = pdns->send_timeout * pdns->res->retry * n; | |
144 | } | |
145 | ||
146 | if (pdns->total_timeout == 0) pdns->res->retrans = RES_MAXRETRANS; | |
147 | else pdns->res->retrans = pdns->total_timeout; | |
148 | ||
149 | pdns->res->options |= RES_INIT; | |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
154 | static int | |
155 | _pdns_build_sortlist(pdns_handle_t *pdns, struct in_addr addr, struct in_addr mask) | |
156 | { | |
157 | if (pdns == NULL) return -1; | |
158 | return res_build_sortlist(pdns->res, addr, mask); | |
159 | } | |
160 | ||
161 | static void | |
162 | _pdns_free(pdns_handle_t *pdns) | |
163 | { | |
164 | int i; | |
165 | ||
166 | if (pdns == NULL) return; | |
167 | ||
168 | if ((pdns->search_count != SEARCH_COUNT_INIT) && (pdns->search_count > 0) && (pdns->search_list != NULL)) | |
169 | { | |
170 | for (i = 0; i < pdns->search_count; i++) free(pdns->search_list[i]); | |
171 | free(pdns->search_list); | |
172 | } | |
173 | ||
174 | if (pdns->name != NULL) free(pdns->name); | |
175 | if (pdns->res != NULL) res_client_close(pdns->res); | |
176 | ||
177 | free(pdns); | |
178 | } | |
179 | ||
180 | static int | |
181 | _pdns_build(pdns_handle_t *pdns, char *key, char *val) | |
182 | { | |
183 | struct in6_addr addr6; | |
184 | int32_t status; | |
185 | char *dupval; | |
186 | ||
187 | if (pdns == NULL) return -1; | |
188 | ||
189 | /* | |
190 | * Detect IPv6 server addresses. | |
191 | */ | |
192 | if (((pdns->flags & DNS_FLAG_HAVE_IPV6_SERVER) == 0) && (!strcmp(key, "nameserver"))) | |
193 | { | |
194 | memset(&addr6, 0, sizeof(struct in6_addr)); | |
195 | status = inet_pton(AF_INET6, val, &addr6); | |
196 | if (status == 1) pdns->flags |= DNS_FLAG_HAVE_IPV6_SERVER; | |
197 | } | |
198 | ||
199 | /* | |
200 | * We handle some keys here. | |
201 | * Other keys get passed on to res_build. | |
202 | */ | |
203 | if (!strcmp(key, "default")) | |
204 | { | |
205 | pdns->flags |= DNS_FLAG_DEFAULT_RESOLVER; | |
206 | return 0; | |
207 | } | |
208 | ||
209 | if (!strcmp(key, "port")) | |
210 | { | |
211 | pdns->port = atoi(val); | |
212 | return 0; | |
213 | } | |
214 | ||
215 | if (!strcmp(key, "search")) | |
216 | { | |
217 | dupval = strdup(val); | |
218 | if (dupval == NULL) return -1; | |
219 | ||
220 | if (pdns->search_count == SEARCH_COUNT_INIT) pdns->search_count = 0; | |
221 | if (pdns->search_count == 0) | |
222 | { | |
223 | pdns->search_list = (char **)calloc(1, sizeof(char *)); | |
224 | } | |
225 | else | |
226 | { | |
227 | pdns->search_list = (char **)reallocf(pdns->search_list, (pdns->search_count + 1) * sizeof(char *)); | |
228 | } | |
229 | ||
230 | if (pdns->search_list == NULL) | |
231 | { | |
232 | free(dupval); | |
233 | _pdns_free(pdns); | |
234 | return -1; | |
235 | } | |
236 | ||
237 | pdns->search_list[pdns->search_count] = dupval; | |
238 | pdns->search_count++; | |
239 | return 0; | |
240 | } | |
241 | ||
242 | if (!strcmp(key, "total_timeout")) | |
243 | { | |
244 | pdns->total_timeout = atoi(val); | |
245 | return 0; | |
246 | } | |
247 | ||
248 | if (!strcmp(key, "timeout")) | |
249 | { | |
250 | pdns->send_timeout = atoi(val); | |
251 | return 0; | |
252 | } | |
253 | ||
254 | if (!strcmp(key, "search_order")) | |
255 | { | |
256 | pdns->search_order = atoi(val); | |
257 | return 0; | |
258 | } | |
259 | ||
260 | if (!strcmp(key, "pdns")) | |
261 | { | |
262 | pdns->flags |= DNS_FLAG_FORWARD_TO_MDNSRESPONDER; | |
263 | return 0; | |
264 | } | |
265 | ||
266 | if (!strcmp(key, "mdns")) | |
267 | { | |
268 | pdns->flags |= DNS_FLAG_FORWARD_TO_MDNSRESPONDER; | |
269 | return 0; | |
270 | } | |
271 | ||
272 | /* pass on to res_build */ | |
273 | return res_build(pdns->res, pdns->port, &(pdns->search_count), key, val); | |
274 | } | |
275 | ||
276 | static pdns_handle_t * | |
277 | _pdns_convert_sc(dns_resolver_t *r) | |
278 | { | |
279 | pdns_handle_t *pdns; | |
280 | char *val, *p, *x; | |
281 | int i; | |
282 | ||
283 | pdns = _pdns_build_start(r->domain); | |
284 | if (r->domain == NULL) _pdns_build(pdns, "default", NULL); | |
285 | ||
286 | p = getenv("RES_RETRY_TIMEOUT"); | |
287 | if (p != NULL) pdns->send_timeout = atoi(p); | |
288 | ||
289 | p = getenv("RES_RETRY"); | |
290 | if (p != NULL) pdns->res->retry= atoi(p); | |
291 | ||
292 | if (r->port != 0) | |
293 | { | |
294 | val = NULL; | |
295 | asprintf(&val, "%hu", r->port); | |
296 | if (val == NULL) | |
297 | { | |
298 | _pdns_free(pdns); | |
299 | return NULL; | |
300 | } | |
301 | ||
302 | _pdns_build(pdns, "port", val); | |
303 | free(val); | |
304 | } | |
305 | ||
306 | if (r->n_nameserver > MAXNS) r->n_nameserver = MAXNS; | |
307 | for (i = 0; i < r->n_nameserver; i++) | |
308 | { | |
309 | if (r->nameserver[i]->sa_family == AF_INET) | |
310 | { | |
311 | val = calloc(1, 256); | |
312 | if (val == NULL) | |
313 | { | |
314 | _pdns_free(pdns); | |
315 | return NULL; | |
316 | } | |
317 | ||
318 | inet_ntop(AF_INET, (char *)(r->nameserver[i]) + INET_NTOP_AF_INET_OFFSET, val, 256); | |
319 | _pdns_build(pdns, "nameserver", val); | |
320 | free(val); | |
321 | } | |
322 | else if (r->nameserver[i]->sa_family == AF_INET6) | |
323 | { | |
324 | pdns->flags |= DNS_FLAG_HAVE_IPV6_SERVER; | |
325 | val = calloc(1, 256); | |
326 | if (val == NULL) | |
327 | { | |
328 | _pdns_free(pdns); | |
329 | return NULL; | |
330 | } | |
331 | ||
332 | inet_ntop(AF_INET6, (char *)(r->nameserver[i]) + INET_NTOP_AF_INET6_OFFSET, val, 256); | |
333 | _pdns_build(pdns, "nameserver", val); | |
334 | free(val); | |
335 | } | |
336 | } | |
337 | ||
338 | if (r->n_search > MAXDNSRCH) r->n_search = MAXDNSRCH; | |
339 | for (i = 0; i < r->n_search; i++) | |
340 | { | |
341 | val = NULL; | |
342 | asprintf(&val, "%s", r->search[i]); | |
343 | if (val == NULL) | |
344 | { | |
345 | _pdns_free(pdns); | |
346 | return NULL; | |
347 | } | |
348 | ||
349 | _pdns_build(pdns, "search", val); | |
350 | free(val); | |
351 | } | |
352 | ||
353 | if (r->timeout > 0) | |
354 | { | |
355 | val = NULL; | |
356 | asprintf(&val, "%d", r->timeout); | |
357 | if (val == NULL) | |
358 | { | |
359 | _pdns_free(pdns); | |
360 | return NULL; | |
361 | } | |
362 | ||
363 | _pdns_build(pdns, "total_timeout", val); | |
364 | free(val); | |
365 | } | |
366 | ||
367 | val = NULL; | |
368 | asprintf(&val, "%d", r->search_order); | |
369 | if (val == NULL) | |
370 | { | |
371 | _pdns_free(pdns); | |
372 | return NULL; | |
373 | } | |
374 | ||
375 | _pdns_build(pdns, "search_order", val); | |
376 | free(val); | |
377 | ||
378 | if (r->n_sortaddr > MAXRESOLVSORT) r->n_sortaddr = MAXRESOLVSORT; | |
379 | for (i = 0; i < r->n_sortaddr; i++) | |
380 | { | |
381 | _pdns_build_sortlist(pdns, r->sortaddr[i]->address, r->sortaddr[i]->mask); | |
382 | } | |
383 | ||
384 | p = r->options; | |
385 | while (NULL != (x = res_next_word(&p))) | |
386 | { | |
387 | /* search for and process individual options */ | |
388 | if (!strncmp(x, "ndots:", 6)) | |
389 | { | |
390 | _pdns_build(pdns, "ndots", x+6); | |
391 | } | |
392 | ||
393 | else if (!strncmp(x, "nibble:", 7)) | |
394 | { | |
395 | _pdns_build(pdns, "nibble", x+7); | |
396 | } | |
397 | ||
398 | else if (!strncmp(x, "nibble2:", 8)) | |
399 | { | |
400 | _pdns_build(pdns, "nibble2", x+8); | |
401 | } | |
402 | ||
403 | else if (!strncmp(x, "timeout:", 8)) | |
404 | { | |
405 | _pdns_build(pdns, "timeout", x+8); | |
406 | } | |
407 | ||
408 | else if (!strncmp(x, "attempts:", 9)) | |
409 | { | |
410 | _pdns_build(pdns, "attempts", x+9); | |
411 | } | |
412 | ||
413 | else if (!strncmp(x, "bitstring:", 10)) | |
414 | { | |
415 | _pdns_build(pdns, "bitstring", x+10); | |
416 | } | |
417 | ||
418 | else if (!strncmp(x, "v6revmode:", 10)) | |
419 | { | |
420 | _pdns_build(pdns, "v6revmode", x+10); | |
421 | } | |
422 | ||
423 | else if (!strcmp(x, "debug")) | |
424 | { | |
425 | _pdns_build(pdns, "debug", NULL); | |
426 | } | |
427 | ||
428 | else if (!strcmp(x, "no_tld_query")) | |
429 | { | |
430 | _pdns_build(pdns, "no_tld_query", NULL); | |
431 | } | |
432 | ||
433 | else if (!strcmp(x, "inet6")) | |
434 | { | |
435 | _pdns_build(pdns, "inet6", NULL); | |
436 | } | |
437 | ||
438 | else if (!strcmp(x, "rotate")) | |
439 | { | |
440 | _pdns_build(pdns, "rotate", NULL); | |
441 | } | |
442 | ||
443 | else if (!strcmp(x, "no-check-names")) | |
444 | { | |
445 | _pdns_build(pdns, "no-check-names", NULL); | |
446 | } | |
447 | ||
448 | #ifdef RES_USE_EDNS0 | |
449 | else if (!strcmp(x, "edns0")) | |
450 | { | |
451 | _pdns_build(pdns, "edns0", NULL); | |
452 | } | |
453 | #endif | |
454 | else if (!strcmp(x, "a6")) | |
455 | { | |
456 | _pdns_build(pdns, "a6", NULL); | |
457 | } | |
458 | ||
459 | else if (!strcmp(x, "dname")) | |
460 | { | |
461 | _pdns_build(pdns, "dname", NULL); | |
462 | } | |
463 | ||
464 | else if (!strcmp(x, "default")) | |
465 | { | |
466 | _pdns_build(pdns, "default", NULL); | |
467 | } | |
468 | ||
469 | else if (!strcmp(x, "pdns")) | |
470 | { | |
471 | _pdns_build(pdns, "pdns", NULL); | |
472 | } | |
473 | ||
474 | else if (!strcmp(x, "mdns")) | |
475 | { | |
476 | _pdns_build(pdns, "mdns", NULL); | |
477 | } | |
478 | } | |
479 | ||
480 | _pdns_build_finish(pdns); | |
481 | return pdns; | |
482 | } | |
483 | ||
9571391b A |
484 | static pdns_handle_t * |
485 | _mdns_primary(dns_resolver_t *r) | |
486 | { | |
487 | pdns_handle_t *pdns; | |
488 | char *val, *p; | |
489 | int i; | |
490 | ||
491 | pdns = _pdns_build_start(r->domain); | |
492 | if (r->domain == NULL) _pdns_build(pdns, "default", NULL); | |
493 | ||
494 | p = getenv("RES_RETRY_TIMEOUT"); | |
495 | if (p != NULL) pdns->send_timeout = atoi(p); | |
496 | ||
497 | p = getenv("RES_RETRY"); | |
498 | if (p != NULL) pdns->res->retry= atoi(p); | |
499 | ||
500 | if (r->n_search > MAXDNSRCH) r->n_search = MAXDNSRCH; | |
501 | for (i = 0; i < r->n_search; i++) | |
502 | { | |
503 | val = NULL; | |
504 | asprintf(&val, "%s", r->search[i]); | |
505 | if (val == NULL) | |
506 | { | |
507 | _pdns_free(pdns); | |
508 | return NULL; | |
509 | } | |
510 | ||
511 | _pdns_build(pdns, "search", val); | |
512 | free(val); | |
513 | } | |
514 | ||
515 | _pdns_build(pdns, "mdns", NULL); | |
516 | ||
517 | _pdns_build_finish(pdns); | |
518 | return pdns; | |
519 | } | |
520 | ||
8a97ab44 A |
521 | /* |
522 | * Open a named resolver client from the system config data. | |
523 | */ | |
524 | static pdns_handle_t * | |
525 | _pdns_sc_open(const char *name) | |
526 | { | |
527 | pdns_handle_t *pdns; | |
528 | int i; | |
529 | dns_config_t *sc_dns; | |
530 | dns_resolver_t *sc_res; | |
531 | ||
532 | sc_dns = dns_configuration_copy(); | |
533 | if (sc_dns == NULL) return NULL; | |
534 | ||
535 | sc_res = NULL; | |
536 | ||
537 | if (name == NULL) | |
538 | { | |
539 | if (sc_dns->n_resolver != 0) sc_res = sc_dns->resolver[0]; | |
540 | } | |
541 | else | |
542 | { | |
543 | for (i = 0; (sc_res == NULL) && (i < sc_dns->n_resolver); i++) | |
544 | { | |
545 | if (sc_dns->resolver[i] == NULL) continue; | |
546 | if (sc_dns->resolver[i]->domain == NULL) continue; | |
547 | if (!strcasecmp(name, sc_dns->resolver[i]->domain)) sc_res = sc_dns->resolver[i]; | |
548 | } | |
549 | } | |
550 | ||
551 | if (sc_res == NULL) | |
552 | { | |
553 | dns_configuration_free(sc_dns); | |
554 | return NULL; | |
555 | } | |
556 | ||
557 | pdns = (pdns_handle_t *)calloc(1, sizeof(pdns_handle_t)); | |
558 | if (pdns == NULL) | |
559 | { | |
560 | dns_configuration_free(sc_dns); | |
561 | return NULL; | |
562 | } | |
563 | ||
564 | pdns = _pdns_convert_sc(sc_res); | |
565 | ||
566 | dns_configuration_free(sc_dns); | |
567 | ||
568 | if (pdns == NULL) return NULL; | |
569 | ||
570 | if (pdns->res == NULL) | |
571 | { | |
572 | free(pdns); | |
573 | return NULL; | |
574 | } | |
575 | ||
576 | pdns->name = NULL; | |
577 | if (pdns->res->defdname[0] != '\0') _pdns_set_name(pdns, pdns->res->defdname); | |
578 | else if (name != NULL) _pdns_set_name(pdns, name); | |
579 | ||
580 | if (name != NULL) pdns->search_count = SEARCH_COUNT_INIT; | |
581 | ||
582 | return pdns; | |
583 | } | |
584 | ||
585 | /* | |
586 | * Open a named resolver client from file. | |
587 | */ | |
588 | static pdns_handle_t * | |
589 | _pdns_file_open(const char *name) | |
590 | { | |
591 | pdns_handle_t *pdns; | |
592 | char *path, buf[1024]; | |
593 | char *p, *x, *y; | |
594 | FILE *fp; | |
595 | ||
596 | path = NULL; | |
597 | if (name == NULL) | |
598 | { | |
599 | asprintf(&path, "%s", _PATH_RESCONF); | |
600 | } | |
601 | else if ((name[0] == '.') || (name[0] == '/')) | |
602 | { | |
603 | asprintf(&path, "%s", name); | |
604 | } | |
605 | else | |
606 | { | |
607 | asprintf(&path, "%s/%s", DNS_RESOLVER_DIR, name); | |
608 | } | |
609 | ||
610 | if (path == NULL) return NULL; | |
611 | ||
612 | fp = fopen(path, "r"); | |
613 | free(path); | |
614 | if (fp == NULL) return NULL; | |
615 | ||
616 | pdns = _pdns_build_start(NULL); | |
617 | if (pdns == NULL) | |
618 | { | |
619 | fclose(fp); | |
620 | return NULL; | |
621 | } | |
622 | ||
623 | p = getenv("RES_RETRY_TIMEOUT"); | |
624 | if (p != NULL) pdns->send_timeout = atoi(p); | |
625 | ||
626 | p = getenv("RES_RETRY"); | |
627 | if (p != NULL) pdns->res->retry= atoi(p); | |
628 | ||
629 | while (fgets(buf, sizeof(buf), fp) != NULL) | |
630 | { | |
631 | /* skip comments */ | |
632 | if ((buf[0] == ';') || (buf[0] == '#')) continue; | |
633 | p = buf; | |
634 | x = res_next_word(&p); | |
635 | if (x == NULL) continue; | |
636 | if (!strcmp(x, "sortlist")) | |
637 | { | |
638 | while (NULL != (x = res_next_word(&p))) | |
639 | { | |
640 | _pdns_build(pdns, "sortlist", x); | |
641 | } | |
642 | } | |
643 | else if (!strcmp(x, "timeout")) | |
644 | { | |
645 | x = res_next_word(&p); | |
646 | if (x != NULL) _pdns_build(pdns, "total_timeout", x); | |
647 | } | |
648 | else if (!strcmp(x, "options")) | |
649 | { | |
650 | while (NULL != (x = res_next_word(&p))) | |
651 | { | |
652 | y = strchr(x, ':'); | |
653 | if (y != NULL) | |
654 | { | |
655 | *y = '\0'; | |
656 | y++; | |
657 | } | |
658 | _pdns_build(pdns, x, y); | |
659 | } | |
660 | } | |
661 | else | |
662 | { | |
663 | y = res_next_word(&p); | |
664 | _pdns_build(pdns, x, y); | |
665 | ||
666 | if ((!strcmp(x, "domain")) && (pdns->name == NULL)) _pdns_set_name(pdns, y); | |
667 | } | |
668 | } | |
669 | ||
670 | fclose(fp); | |
671 | ||
672 | if (pdns->name == NULL) _pdns_set_name(pdns, name); | |
673 | ||
674 | _pdns_build_finish(pdns); | |
675 | ||
676 | return pdns; | |
677 | } | |
678 | ||
679 | /* | |
680 | * If there was no search list, use domain name and parent domain components. | |
681 | * | |
682 | * N.B. This code deals with multiple trailing dots, but does not deal with | |
683 | * multiple internal dots, e.g. "foo.....com". | |
684 | */ | |
685 | static void | |
686 | _pdns_check_search_list(pdns_handle_t *pdns) | |
687 | { | |
688 | int n; | |
689 | char *p; | |
690 | ||
691 | if (pdns == NULL) return; | |
692 | if (pdns->name == NULL) return; | |
693 | if (pdns->search_count > 0) return; | |
694 | ||
695 | /* Count dots */ | |
696 | n = 0; | |
697 | for (p = pdns->name; *p != '\0'; p++) | |
698 | { | |
699 | if (*p == '.') n++; | |
700 | } | |
701 | ||
702 | /* Back up over any trailing dots and cut them out of the name */ | |
703 | for (p--; (p >= pdns->name) && (*p == '.'); p--) | |
704 | { | |
705 | *p = '\0'; | |
706 | n--; | |
707 | } | |
708 | ||
709 | /* This will be true if name was all dots */ | |
710 | if (p < pdns->name) return; | |
711 | ||
712 | /* dots are separators, so number of components is one larger */ | |
713 | n++; | |
714 | ||
715 | _pdns_build(pdns, "search", pdns->name); | |
716 | ||
717 | /* Include parent domains with at least LOCALDOMAINPARTS components */ | |
718 | p = pdns->name; | |
719 | while (n > LOCALDOMAINPARTS) | |
720 | { | |
721 | /* Find next component */ | |
722 | while ((*p != '.') && (*p != '\0')) p++; | |
723 | if (*p == '\0') break; | |
724 | p++; | |
725 | ||
726 | n--; | |
727 | _pdns_build(pdns, "search", p); | |
728 | } | |
729 | } | |
730 | ||
731 | __private_extern__ void | |
732 | _check_cache(sdns_handle_t *sdns) | |
733 | { | |
734 | int i, n, status, refresh, sc_dns_count; | |
735 | DIR *dp; | |
736 | struct direct *d; | |
737 | pdns_handle_t *c; | |
738 | dns_config_t *sc_dns; | |
739 | ||
740 | if (sdns == NULL) return; | |
741 | ||
742 | refresh = 0; | |
743 | ||
744 | if (sdns->stattime == 0) refresh = 1; | |
745 | ||
746 | if (refresh == 0) | |
747 | { | |
748 | if (sdns->notify_sys_config_token == -1) refresh = 1; | |
749 | else | |
750 | { | |
751 | n = 1; | |
752 | status = notify_check(sdns->notify_sys_config_token, &n); | |
753 | if ((status != NOTIFY_STATUS_OK) || (n == 1)) refresh = 1; | |
754 | } | |
755 | } | |
756 | ||
757 | if (refresh == 0) | |
758 | { | |
759 | if (sdns->notify_dir_token == -1) refresh = 1; | |
760 | else | |
761 | { | |
762 | n = 1; | |
763 | status = notify_check(sdns->notify_dir_token, &n); | |
764 | if ((status != NOTIFY_STATUS_OK) || (n == 1)) refresh = 1; | |
765 | } | |
766 | } | |
767 | ||
768 | if (refresh == 0) return; | |
769 | ||
770 | /* Free old clients */ | |
771 | sdns->pdns_primary = NULL; | |
772 | ||
773 | for (i = 0; i < sdns->client_count; i++) | |
774 | { | |
775 | _pdns_free(sdns->client[i]); | |
776 | } | |
777 | ||
778 | sdns->client_count = 0; | |
779 | if (sdns->client != NULL) free(sdns->client); | |
780 | sdns->client = NULL; | |
781 | ||
782 | /* Fetch clients from System Configuration */ | |
783 | sc_dns = dns_configuration_copy(); | |
784 | ||
785 | /* Set up Primary resolver. It's the one we consult for a search list */ | |
786 | sc_dns_count = 0; | |
787 | if ((sc_dns != NULL) && (sc_dns->n_resolver > 0)) | |
788 | { | |
9571391b A |
789 | if (sdns->flags & DNS_FLAG_FORWARD_TO_MDNSRESPONDER) |
790 | { | |
791 | sc_dns_count = 1; | |
792 | sdns->pdns_primary = _mdns_primary(sc_dns->resolver[0]); | |
793 | } | |
794 | else | |
795 | { | |
796 | sc_dns_count = sc_dns->n_resolver; | |
797 | sdns->pdns_primary = _pdns_convert_sc(sc_dns->resolver[0]); | |
798 | } | |
799 | ||
8a97ab44 A |
800 | _pdns_check_search_list(sdns->pdns_primary); |
801 | } | |
802 | else | |
803 | { | |
804 | sdns->pdns_primary = _pdns_file_open(_PATH_RESCONF); | |
805 | } | |
806 | ||
807 | if (sdns->pdns_primary != NULL) | |
808 | { | |
809 | if ((sdns->flags & DNS_FLAG_DEBUG) && (sdns->pdns_primary->res != NULL)) sdns->pdns_primary->res->options |= RES_DEBUG; | |
810 | if (sdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA) sdns->pdns_primary->flags |= DNS_FLAG_OK_TO_SKIP_AAAA; | |
811 | sdns->pdns_primary->flags |= DNS_FLAG_DEFAULT_RESOLVER; | |
812 | ||
813 | sdns->client = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *)); | |
814 | if (sdns->client == NULL) | |
815 | { | |
816 | if (sc_dns != NULL) dns_configuration_free(sc_dns); | |
817 | return; | |
818 | } | |
819 | ||
820 | sdns->client[sdns->client_count] = sdns->pdns_primary; | |
821 | sdns->client_count++; | |
822 | } | |
823 | ||
824 | /* Convert System Configuration resolvers */ | |
825 | for (i = 1; i < sc_dns_count; i++) | |
826 | { | |
827 | c = _pdns_convert_sc(sc_dns->resolver[i]); | |
828 | if (c == NULL) continue; | |
829 | ||
830 | if (sdns->flags & DNS_FLAG_DEBUG) c->res->options |= RES_DEBUG; | |
831 | if (sdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA) c->flags |= DNS_FLAG_OK_TO_SKIP_AAAA; | |
832 | ||
833 | if (sdns->client_count == 0) | |
834 | { | |
835 | sdns->client = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *)); | |
836 | } | |
837 | else | |
838 | { | |
839 | sdns->client = (pdns_handle_t **)reallocf(sdns->client, (sdns->client_count + 1) * sizeof(pdns_handle_t *)); | |
840 | } | |
841 | ||
842 | if (sdns->client == NULL) | |
843 | { | |
844 | sdns->client_count = 0; | |
845 | dns_configuration_free(sc_dns); | |
846 | return; | |
847 | } | |
848 | ||
849 | sdns->client[sdns->client_count] = c; | |
850 | sdns->client_count++; | |
851 | } | |
852 | ||
853 | if (sc_dns != NULL) dns_configuration_free(sc_dns); | |
854 | ||
855 | if (sdns->flags & DNS_FLAG_CHECK_RESOLVER_DIR) | |
856 | { | |
857 | /* Read /etc/resolvers clients */ | |
858 | dp = opendir(DNS_RESOLVER_DIR); | |
859 | if (dp == NULL) | |
860 | { | |
861 | sdns->flags &= ~DNS_FLAG_CHECK_RESOLVER_DIR; | |
862 | } | |
863 | else | |
864 | { | |
865 | while (NULL != (d = readdir(dp))) | |
866 | { | |
867 | if (d->d_name[0] == '.') continue; | |
868 | ||
869 | c = _pdns_file_open(d->d_name); | |
870 | if (c == NULL) continue; | |
871 | if (sdns->flags & DNS_FLAG_DEBUG) c->res->options |= RES_DEBUG; | |
872 | if (sdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA) c->flags |= DNS_FLAG_OK_TO_SKIP_AAAA; | |
873 | ||
874 | if (sdns->client_count == 0) | |
875 | { | |
876 | sdns->client = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *)); | |
877 | } | |
878 | else | |
879 | { | |
880 | sdns->client = (pdns_handle_t **)reallocf(sdns->client, (sdns->client_count + 1) * sizeof(pdns_handle_t *)); | |
881 | } | |
882 | ||
883 | if (sdns->client == NULL) | |
884 | { | |
885 | sdns->client_count = 0; | |
886 | return; | |
887 | } | |
888 | ||
889 | sdns->client[sdns->client_count] = c; | |
890 | sdns->client_count++; | |
891 | } | |
892 | closedir(dp); | |
893 | } | |
894 | } | |
895 | ||
896 | sdns->stattime = 1; | |
897 | } | |
898 | ||
899 | static uint32_t | |
900 | _pdns_get_default_handles(sdns_handle_t *sdns, pdns_handle_t ***pdns) | |
901 | { | |
902 | int i, j, k, count; | |
903 | ||
904 | if (sdns == NULL) return 0; | |
905 | if (pdns == NULL) return 0; | |
906 | ||
907 | count = 0; | |
908 | ||
909 | for (i = 0; i < sdns->client_count; i++) | |
910 | { | |
911 | if (sdns->client[i]->flags & DNS_FLAG_DEFAULT_RESOLVER) | |
912 | { | |
913 | if (count == 0) | |
914 | { | |
915 | *pdns = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *)); | |
916 | } | |
917 | else | |
918 | { | |
919 | *pdns = (pdns_handle_t **)reallocf((*pdns), (count + 1) * sizeof(pdns_handle_t *)); | |
920 | } | |
921 | ||
922 | if (*pdns == NULL) return 0; | |
923 | ||
924 | /* Insert sorted by search_order */ | |
925 | for (j = 0; j < count; j++) | |
926 | { | |
927 | if (sdns->client[i]->search_order < (*pdns)[j]->search_order) break; | |
928 | } | |
929 | ||
930 | for (k = count; k > j; k--) (*pdns)[k] = (*pdns)[k-1]; | |
931 | (*pdns)[j] = sdns->client[i]; | |
932 | count++; | |
933 | } | |
934 | } | |
935 | ||
936 | return count; | |
937 | } | |
938 | ||
939 | static uint32_t | |
9571391b | 940 | _pdns_get_handles_for_name(sdns_handle_t *sdns, const char *name, pdns_handle_t ***pdns) |
8a97ab44 A |
941 | { |
942 | char *p, *vname; | |
943 | int i, j, k, count; | |
944 | ||
945 | if (sdns == NULL) return 0; | |
946 | if (pdns == NULL) return 0; | |
947 | ||
948 | if (name == NULL) return _pdns_get_default_handles(sdns, pdns); | |
949 | else if (name[0] == '\0') return _pdns_get_default_handles(sdns, pdns); | |
950 | ||
951 | count = 0; | |
952 | ||
953 | vname = strdup(name); | |
954 | i = strlen(vname) - 1; | |
955 | if ((i >= 0) && (vname[i] == '.')) vname[i] = '\0'; | |
956 | ||
957 | p = vname; | |
958 | while (p != NULL) | |
959 | { | |
960 | for (i = 0; i < sdns->client_count; i++) | |
961 | { | |
962 | if (sdns->client[i]->name == NULL) continue; | |
963 | ||
964 | if (!strcasecmp(sdns->client[i]->name, p)) | |
965 | { | |
966 | if (count == 0) | |
967 | { | |
968 | *pdns = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *)); | |
969 | } | |
970 | else | |
971 | { | |
972 | *pdns = (pdns_handle_t **)reallocf((*pdns), (count + 1) * sizeof(pdns_handle_t *)); | |
973 | } | |
974 | ||
975 | if (*pdns == NULL) return 0; | |
976 | ||
977 | /* Insert sorted by search_order */ | |
978 | for (j = 0; j < count; j++) | |
979 | { | |
980 | if (sdns->client[i]->search_order < (*pdns)[j]->search_order) break; | |
981 | } | |
982 | ||
983 | for (k = count; k > j; k--) (*pdns)[k] = (*pdns)[k-1]; | |
984 | (*pdns)[j] = sdns->client[i]; | |
985 | count++; | |
986 | } | |
987 | } | |
988 | ||
989 | p = strchr(p, '.'); | |
990 | if (p != NULL) p++; | |
991 | } | |
992 | ||
993 | free(vname); | |
994 | ||
995 | if (count != 0) return count; | |
996 | ||
9571391b | 997 | return _pdns_get_default_handles(sdns, pdns); |
8a97ab44 A |
998 | } |
999 | ||
1000 | static void | |
1001 | _pdns_process_res_search_list(pdns_handle_t *pdns) | |
1002 | { | |
1003 | if (pdns->search_count != SEARCH_COUNT_INIT) return; | |
1004 | for (pdns->search_count = 0; (pdns->res->dnsrch[pdns->search_count] != NULL) && (pdns->res->dnsrch[pdns->search_count][0] != '\0'); pdns->search_count++); | |
1005 | } | |
1006 | ||
1007 | static char * | |
1008 | _pdns_search_list_domain(pdns_handle_t *pdns, uint32_t i) | |
1009 | { | |
1010 | char *s; | |
1011 | ||
1012 | if (pdns == NULL) return NULL; | |
1013 | if (pdns->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(pdns); | |
1014 | if (i >= pdns->search_count) return NULL; | |
1015 | ||
1016 | s = pdns->search_list[i]; | |
1017 | if (s == NULL) return NULL; | |
1018 | return strdup(s); | |
1019 | } | |
1020 | ||
1021 | static void | |
1022 | _dns_open_notify(sdns_handle_t *sdns) | |
1023 | { | |
1024 | int status, n; | |
1025 | ||
1026 | if (sdns == NULL) return; | |
1027 | ||
1028 | if (sdns->notify_delay_token == -1) | |
1029 | { | |
1030 | status = notify_register_check(DNS_DELAY_NAME, &(sdns->notify_delay_token)); | |
1031 | if (status != NOTIFY_STATUS_OK) sdns->notify_delay_token = -1; | |
1032 | else status = notify_check(sdns->notify_delay_token, &n); | |
1033 | } | |
1034 | ||
1035 | if (sdns->notify_sys_config_token == -1) | |
1036 | { | |
1037 | status = notify_register_check(dns_configuration_notify_key(), &(sdns->notify_sys_config_token)); | |
1038 | if (status != NOTIFY_STATUS_OK) sdns->notify_sys_config_token = -1; | |
1039 | } | |
1040 | ||
1041 | if (sdns->notify_dir_token == -1) | |
1042 | { | |
1043 | status = notify_register_check(NOTIFY_DIR_NAME, &(sdns->notify_dir_token)); | |
1044 | if (status == NOTIFY_STATUS_OK) | |
1045 | { | |
1046 | status = notify_monitor_file(sdns->notify_dir_token, "/private/etc/resolver", 0); | |
1047 | if (status != NOTIFY_STATUS_OK) | |
1048 | { | |
1049 | notify_cancel(sdns->notify_dir_token); | |
1050 | sdns->notify_dir_token = -1; | |
1051 | } | |
1052 | } | |
1053 | else | |
1054 | { | |
1055 | sdns->notify_dir_token = -1; | |
1056 | } | |
1057 | } | |
1058 | } | |
1059 | ||
1060 | static void | |
1061 | _dns_close_notify(sdns_handle_t *sdns) | |
1062 | { | |
1063 | if (sdns == NULL) return; | |
1064 | ||
1065 | if (sdns->notify_delay_token != -1) notify_cancel(sdns->notify_delay_token); | |
1066 | sdns->notify_delay_token = -1; | |
1067 | ||
1068 | if (sdns->notify_sys_config_token != -1) notify_cancel(sdns->notify_sys_config_token); | |
1069 | sdns->notify_sys_config_token = -1; | |
1070 | ||
1071 | if (sdns->notify_dir_token != -1) notify_cancel(sdns->notify_dir_token); | |
1072 | sdns->notify_dir_token = -1; | |
1073 | } | |
1074 | ||
1075 | dns_handle_t | |
1076 | dns_open(const char *name) | |
1077 | { | |
1078 | dns_private_handle_t *dns; | |
9571391b A |
1079 | struct stat sb; |
1080 | int check, status, local_control; | |
1081 | uint64_t control; | |
8a97ab44 A |
1082 | |
1083 | dns = (dns_private_handle_t *)calloc(1, sizeof(dns_private_handle_t)); | |
1084 | if (dns == NULL) return NULL; | |
1085 | ||
9571391b A |
1086 | /* set up control notification if necessary */ |
1087 | if (dns_control_token == -1) | |
1088 | { | |
1089 | pthread_mutex_lock(&dns_control_lock); | |
1090 | if (dns_control_token == -1) status = notify_register_check(NOTIFY_DNS_CONTROL_NAME, &dns_control_token); | |
1091 | pthread_mutex_unlock(&dns_control_lock); | |
1092 | } | |
1093 | ||
1094 | /* check for dns flags */ | |
1095 | if (dns_control_token != -1) | |
1096 | { | |
1097 | pthread_mutex_lock(&dns_control_lock); | |
1098 | status = notify_check(dns_control_token, &check); | |
1099 | if ((status == 0) && (check == 1)) | |
1100 | { | |
1101 | /* notification was triggered */ | |
1102 | status = notify_get_state(dns_control_token, &control); | |
1103 | if (status == 0) | |
1104 | { | |
1105 | if (control & DNS_CONTROL_FLAG_NO_MDNS) dns_control_mdns = 0; | |
1106 | if (control & DNS_CONTROL_FLAG_DEBUG) dns_control_debug = 1; | |
1107 | } | |
1108 | } | |
1109 | ||
1110 | pthread_mutex_unlock(&dns_control_lock); | |
1111 | } | |
1112 | ||
1113 | if (name == NULL) local_control = dns_control_mdns; | |
1114 | else if (!strcmp(name, MDNS_HANDLE_NAME)) local_control = 2; | |
1115 | else local_control = 0; | |
1116 | ||
1117 | if ((name == NULL) && (local_control == 0)) | |
8a97ab44 A |
1118 | { |
1119 | dns->handle_type = DNS_PRIVATE_HANDLE_TYPE_SUPER; | |
1120 | dns->sdns = (sdns_handle_t *)calloc(1, sizeof(sdns_handle_t)); | |
1121 | if (dns->sdns == NULL) | |
1122 | { | |
1123 | free(dns); | |
1124 | return NULL; | |
1125 | } | |
1126 | ||
1127 | dns->sdns->flags |= DNS_FLAG_CHECK_RESOLVER_DIR; | |
1128 | dns->sdns->notify_sys_config_token = -1; | |
1129 | dns->sdns->notify_dir_token = -1; | |
1130 | dns->sdns->notify_delay_token = -1; | |
1131 | _dns_open_notify(dns->sdns); | |
1132 | ||
9571391b A |
1133 | memset(&sb, 0, sizeof(struct stat)); |
1134 | dns_set_debug((dns_handle_t)dns, dns_control_debug); | |
1135 | ||
1136 | return (dns_handle_t)dns; | |
1137 | } | |
1138 | ||
1139 | if (local_control != 0) | |
1140 | { | |
1141 | dns->handle_type = DNS_PRIVATE_HANDLE_TYPE_SUPER; | |
1142 | dns->sdns = (sdns_handle_t *)calloc(1, sizeof(sdns_handle_t)); | |
1143 | if (dns->sdns == NULL) | |
1144 | { | |
1145 | free(dns); | |
1146 | return NULL; | |
1147 | } | |
1148 | ||
1149 | dns->sdns->flags = DNS_FLAG_FORWARD_TO_MDNSRESPONDER; | |
1150 | dns->sdns->notify_sys_config_token = -1; | |
1151 | dns->sdns->notify_dir_token = -1; | |
1152 | dns->sdns->notify_delay_token = -1; | |
1153 | if (local_control == 1) _dns_open_notify(dns->sdns); | |
1154 | ||
1155 | memset(&sb, 0, sizeof(struct stat)); | |
1156 | dns_set_debug((dns_handle_t)dns, dns_control_debug); | |
1157 | ||
8a97ab44 A |
1158 | return (dns_handle_t)dns; |
1159 | } | |
1160 | ||
1161 | dns->handle_type = DNS_PRIVATE_HANDLE_TYPE_PLAIN; | |
1162 | ||
1163 | /* Look for name in System Configuration first */ | |
1164 | dns->pdns = _pdns_sc_open(name); | |
1165 | if (dns->pdns == NULL) dns->pdns = _pdns_file_open(name); | |
1166 | ||
1167 | if (dns->pdns == NULL) | |
1168 | { | |
1169 | free(dns); | |
1170 | return NULL; | |
1171 | } | |
1172 | ||
9571391b | 1173 | dns_set_debug((dns_handle_t)dns, dns_control_debug); |
8a97ab44 A |
1174 | return (dns_handle_t)dns; |
1175 | } | |
1176 | ||
1177 | /* | |
1178 | * Release a DNS client handle | |
1179 | */ | |
1180 | void | |
1181 | dns_free(dns_handle_t d) | |
1182 | { | |
1183 | dns_private_handle_t *dns; | |
1184 | int i; | |
1185 | ||
1186 | if (d == NULL) return; | |
1187 | ||
1188 | dns = (dns_private_handle_t *)d; | |
1189 | ||
1190 | if (dns->recvbuf != NULL) free(dns->recvbuf); | |
1191 | ||
1192 | if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER) | |
1193 | { | |
1194 | if (dns->sdns == NULL) return; | |
1195 | ||
1196 | _dns_close_notify(dns->sdns); | |
1197 | ||
1198 | for (i = 0; i < dns->sdns->client_count; i++) | |
1199 | { | |
1200 | _pdns_free(dns->sdns->client[i]); | |
1201 | } | |
1202 | ||
1203 | dns->sdns->client_count = 0; | |
1204 | if (dns->sdns->client != NULL) free(dns->sdns->client); | |
1205 | ||
1206 | free(dns->sdns); | |
1207 | } | |
1208 | else | |
1209 | { | |
1210 | _pdns_free(dns->pdns); | |
1211 | } | |
1212 | ||
1213 | free(dns); | |
1214 | } | |
1215 | ||
1216 | static void | |
1217 | _pdns_debug(pdns_handle_t *pdns, uint32_t flag) | |
1218 | { | |
1219 | if (pdns == NULL) return; | |
1220 | ||
1221 | if (flag == 0) | |
1222 | { | |
1223 | pdns->res->options &= ~RES_DEBUG; | |
1224 | } | |
1225 | else | |
1226 | { | |
1227 | pdns->res->options |= RES_DEBUG; | |
1228 | } | |
1229 | } | |
1230 | ||
1231 | static void | |
1232 | _sdns_debug(sdns_handle_t *sdns, uint32_t flag) | |
1233 | { | |
1234 | int i; | |
1235 | ||
1236 | if (sdns == NULL) return; | |
1237 | ||
1238 | if (flag == 0) | |
1239 | { | |
1240 | sdns->flags &= ~ DNS_FLAG_DEBUG; | |
1241 | ||
1242 | for (i = 0; i < sdns->client_count; i++) | |
1243 | { | |
1244 | sdns->client[i]->res->options &= ~RES_DEBUG; | |
1245 | } | |
1246 | } | |
1247 | else | |
1248 | { | |
1249 | sdns->flags |= DNS_FLAG_DEBUG; | |
1250 | ||
1251 | for (i = 0; i < sdns->client_count; i++) | |
1252 | { | |
1253 | sdns->client[i]->res->options |= RES_DEBUG; | |
1254 | } | |
1255 | } | |
1256 | } | |
1257 | ||
1258 | /* | |
1259 | * Enable / Disable debugging | |
1260 | */ | |
1261 | void | |
1262 | dns_set_debug(dns_handle_t d, uint32_t flag) | |
1263 | { | |
1264 | dns_private_handle_t *dns; | |
1265 | ||
1266 | if (d == NULL) return; | |
1267 | ||
1268 | dns = (dns_private_handle_t *)d; | |
1269 | ||
1270 | if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER) | |
1271 | { | |
1272 | _sdns_debug(dns->sdns, flag); | |
1273 | } | |
1274 | else | |
1275 | { | |
1276 | _pdns_debug(dns->pdns, flag); | |
1277 | } | |
1278 | } | |
1279 | ||
1280 | /* | |
1281 | * Returns the number of names in the search list | |
1282 | */ | |
1283 | uint32_t | |
1284 | dns_search_list_count(dns_handle_t d) | |
1285 | { | |
1286 | dns_private_handle_t *dns; | |
1287 | pdns_handle_t *pdns; | |
1288 | ||
1289 | if (d == NULL) return 0; | |
1290 | ||
1291 | dns = (dns_private_handle_t *)d; | |
1292 | ||
1293 | if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER) | |
1294 | { | |
1295 | _check_cache(dns->sdns); | |
1296 | pdns = dns->sdns->pdns_primary; | |
1297 | } | |
1298 | else | |
1299 | { | |
1300 | pdns = dns->pdns; | |
1301 | } | |
1302 | ||
1303 | if (pdns->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(pdns); | |
1304 | return pdns->search_count; | |
1305 | } | |
1306 | ||
1307 | /* | |
1308 | * Returns the domain name at index i in the search list. | |
1309 | * Returns NULL if there are no names in the search list. | |
1310 | * Caller must free the returned name. | |
1311 | */ | |
1312 | char * | |
1313 | dns_search_list_domain(dns_handle_t d, uint32_t i) | |
1314 | { | |
1315 | dns_private_handle_t *dns; | |
1316 | pdns_handle_t *pdns; | |
1317 | ||
1318 | if (d == NULL) return NULL; | |
1319 | ||
1320 | dns = (dns_private_handle_t *)d; | |
1321 | ||
1322 | if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER) | |
1323 | { | |
1324 | _check_cache(dns->sdns); | |
1325 | pdns = dns->sdns->pdns_primary; | |
1326 | } | |
1327 | else | |
1328 | { | |
1329 | pdns = dns->pdns; | |
1330 | } | |
1331 | ||
1332 | return _pdns_search_list_domain(pdns, i); | |
1333 | } | |
1334 | ||
1335 | static int | |
1336 | _pdns_delay(sdns_handle_t *sdns) | |
1337 | { | |
1338 | int status, n, snooze; | |
1339 | time_t tick; | |
1340 | ||
1341 | if (sdns == NULL) return 0; | |
1342 | ||
1343 | snooze = 0; | |
1344 | n = 0; | |
1345 | ||
1346 | /* No delay if we are not receiving notifications */ | |
1347 | if (sdns->notify_delay_token == -1) return 0; | |
1348 | ||
1349 | if (sdns->dns_delay == 0) | |
1350 | { | |
1351 | status = notify_check(sdns->notify_delay_token, &n); | |
1352 | if ((status == NOTIFY_STATUS_OK) && (n == 1)) | |
1353 | { | |
1354 | /* | |
1355 | * First thread to hit this condition sleeps for DNS_DELAY_INTERVAL seconds | |
1356 | */ | |
1357 | sdns->dns_delay = time(NULL) + DNS_DELAY_INTERVAL; | |
1358 | snooze = DNS_DELAY_INTERVAL; | |
1359 | } | |
1360 | } | |
1361 | else | |
1362 | { | |
1363 | tick = time(NULL); | |
1364 | /* | |
1365 | * Subsequent threads sleep for the remaining duration. | |
51a689d1 | 1366 | * We add one to round up the interval since our granularity is coarse. |
8a97ab44 A |
1367 | */ |
1368 | snooze = 1 + (sdns->dns_delay - tick); | |
1369 | if (snooze < 0) snooze = 0; | |
1370 | } | |
1371 | ||
1372 | if (snooze == 0) return 0; | |
1373 | ||
1374 | sleep(snooze); | |
1375 | ||
1376 | /* When exiting, first thread in resets the delay condition */ | |
1377 | if (n == 1) sdns->dns_delay = 0; | |
1378 | ||
1379 | return 0; | |
1380 | } | |
1381 | ||
1382 | static int | |
1383 | _pdns_query(sdns_handle_t *sdns, pdns_handle_t *pdns, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen, int *min) | |
1384 | { | |
1385 | int n; | |
1386 | ||
1387 | if (name == NULL) return -1; | |
1388 | if (pdns == NULL) return -1; | |
1389 | ||
1390 | if (pdns->flags & DNS_FLAG_FORWARD_TO_MDNSRESPONDER) | |
1391 | { | |
1392 | n = res_query_mDNSResponder(pdns->res, name, class, type, (u_char *)buf, len, from, fromlen); | |
1393 | if ((n < 0) && (min != NULL)) *min = MDNS_MIN_TTL; | |
1394 | return n; | |
1395 | } | |
1396 | ||
1397 | if (pdns->res == NULL) return -1; | |
1398 | if (pdns->res->nscount == 0) return -1; | |
1399 | ||
1400 | if ((type == ns_t_aaaa) && ((pdns->flags & DNS_FLAG_HAVE_IPV6_SERVER) == 0) && (pdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA)) return -1; | |
1401 | ||
1402 | _pdns_delay(sdns); | |
1403 | ||
1404 | /* BIND_9 API */ | |
1405 | return res_nquery_soa_min(pdns->res, name, class, type, (u_char *)buf, len, from, (int32_t *)fromlen, min); | |
1406 | } | |
1407 | ||
1408 | __private_extern__ int | |
1409 | _pdns_search(sdns_handle_t *sdns, pdns_handle_t *pdns, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen) | |
1410 | { | |
1411 | char *dot, *qname; | |
1412 | int append, status; | |
1413 | ||
1414 | if (name == NULL) return -1; | |
1415 | if (pdns == NULL) return -1; | |
1416 | if (pdns->res == NULL) return -1; | |
1417 | if (pdns->res->nscount == 0) return -1; | |
1418 | ||
1419 | if ((type == ns_t_aaaa) && ((pdns->flags & DNS_FLAG_HAVE_IPV6_SERVER) == 0) && (pdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA)) return -1; | |
1420 | ||
1421 | qname = NULL; | |
1422 | append = 1; | |
1423 | ||
1424 | /* | |
1425 | * don't append my name if: | |
1426 | * - my name is NULL | |
1427 | * - input name is qualified (i.e. not single component) | |
1428 | * - there is a search list | |
1429 | * - there is a domain name | |
1430 | */ | |
1431 | ||
1432 | if (pdns->name == NULL) append = 0; | |
1433 | ||
1434 | if (append == 1) | |
1435 | { | |
1436 | dot = strrchr(name, '.'); | |
1437 | if (dot != NULL) append = 0; | |
1438 | } | |
1439 | ||
1440 | if (append == 1) | |
1441 | { | |
1442 | if (pdns->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(pdns); | |
1443 | if (pdns->search_count > 0) append = 0; | |
1444 | } | |
1445 | ||
1446 | if ((append == 1) && (pdns->res->defdname != NULL) && (pdns->res->defdname[0] != '\0')) append = 0; | |
1447 | ||
1448 | status = -1; | |
1449 | if (append == 0) | |
1450 | { | |
1451 | /* BIND_9 API */ | |
1452 | _pdns_delay(sdns); | |
1453 | ||
1454 | status = __res_nsearch_list_2(pdns->res, name, class, type, (u_char *)buf, len, from, fromlen, pdns->search_count, pdns->search_list); | |
1455 | } | |
1456 | else | |
1457 | { | |
1458 | _pdns_delay(sdns); | |
1459 | ||
1460 | qname = NULL; | |
1461 | asprintf(&qname, "%s.%s.", name, pdns->name); | |
1462 | if (qname == NULL) return -1; | |
1463 | ||
1464 | /* BIND_9 API */ | |
1465 | status = res_nsearch_2(pdns->res, qname, class, type, (u_char *)buf, len, from, fromlen); | |
1466 | free(qname); | |
1467 | } | |
1468 | ||
1469 | return status; | |
1470 | } | |
1471 | ||
1472 | static int | |
9571391b | 1473 | _sdns_send(sdns_handle_t *sdns, const char *name, uint32_t class, uint32_t type, uint32_t fqdn, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen, int *min) |
8a97ab44 A |
1474 | { |
1475 | char *qname; | |
1476 | pdns_handle_t **pdns; | |
1477 | uint32_t pdns_count; | |
e01cf2fc A |
1478 | int i, n; |
1479 | int m, tmin, minstate; | |
8a97ab44 A |
1480 | |
1481 | pdns = NULL; | |
1482 | pdns_count = 0; | |
1483 | n = -1; | |
e01cf2fc A |
1484 | minstate = 0; |
1485 | *min = -1; | |
1486 | m = -1; | |
8a97ab44 | 1487 | |
9571391b | 1488 | pdns_count = _pdns_get_handles_for_name(sdns, name, &pdns); |
8a97ab44 A |
1489 | |
1490 | if (pdns_count == 0) return -1; | |
1491 | ||
1492 | qname = NULL; | |
1493 | asprintf(&qname, "%s%s", name, (fqdn == 0) ? "." : ""); | |
1494 | if (qname == NULL) return -1; | |
1495 | ||
1496 | for (i = 0; i < pdns_count; i++) | |
1497 | { | |
e01cf2fc A |
1498 | tmin = -1; |
1499 | n = _pdns_query(sdns, pdns[i], qname, class, type, buf, len, from, fromlen, &tmin); | |
1500 | if (n <= 0) | |
8a97ab44 | 1501 | { |
e01cf2fc A |
1502 | if (tmin < 0) |
1503 | { | |
1504 | minstate = -1; | |
1505 | } | |
1506 | else if (minstate == 0) | |
1507 | { | |
1508 | if (m == -1) m = tmin; | |
1509 | else if (tmin < m) m = tmin; | |
1510 | } | |
8a97ab44 A |
1511 | } |
1512 | ||
1513 | if (n > 0) break; | |
1514 | } | |
1515 | ||
e01cf2fc A |
1516 | if (minstate == 0) *min = m; |
1517 | ||
8a97ab44 A |
1518 | free(pdns); |
1519 | free(qname); | |
1520 | return n; | |
1521 | } | |
1522 | ||
1523 | __private_extern__ int | |
1524 | _sdns_search(sdns_handle_t *sdns, const char *name, uint32_t class, uint32_t type, uint32_t fqdn, uint32_t recurse, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen, int *min) | |
1525 | { | |
1526 | pdns_handle_t *primary, **pdns; | |
9571391b | 1527 | int i, n, ndots, status; |
e01cf2fc | 1528 | int m, tmin, minstate; |
8a97ab44 A |
1529 | char *dot, *qname; |
1530 | uint32_t pdns_count; | |
1531 | ||
1532 | if (sdns == NULL) return -1; | |
1533 | if (name == NULL) return -1; | |
1534 | ||
e01cf2fc A |
1535 | /* |
1536 | * A minimum TTL derived from the minimim of all SOA records | |
1537 | * that are received with NXDOMAIN or no data is returned to | |
1538 | * the caller if every call returns an NXDOMAIN or no data | |
1539 | * and a SOA min ttl. If any call times out or returns some | |
1540 | * other error, we return "-1" in the "min" out parameter. | |
1541 | * The minstate variable is set to -1 if we must return -1. | |
1542 | */ | |
1543 | minstate = 0; | |
1544 | *min = -1; | |
1545 | ||
1546 | /* m is the lowest of all minima. -1 is unset */ | |
1547 | m = -1; | |
1548 | ||
8a97ab44 A |
1549 | /* ndots is the threshold for trying a qualified name "as is" */ |
1550 | ndots = 1; | |
1551 | primary = sdns->pdns_primary; | |
1552 | if ((primary != NULL) && (primary->res != NULL)) ndots = primary->res->ndots; | |
1553 | ||
1554 | /* count dots in input name, and keep track of the location of the last dot */ | |
1555 | n = 0; | |
1556 | dot = NULL; | |
1557 | ||
1558 | for (i = 0; name[i] != '\0'; i++) | |
1559 | { | |
1560 | if (name[i] == '.') | |
1561 | { | |
1562 | n++; | |
1563 | dot = (char *)(name + i); | |
1564 | } | |
1565 | } | |
1566 | ||
1567 | /* the last dot is the last character, name is fully qualified */ | |
1568 | if ((fqdn == 0) && (dot != NULL) && (*(dot + 1) == '\0')) fqdn = 1; | |
1569 | ||
1570 | /* | |
1571 | * If n >= ndots, or it's a FQDN, or if it's a PTR query, | |
1572 | * we try a query with the name "as is". | |
1573 | */ | |
1574 | if ((n >= ndots) || (fqdn == 1) || (type == ns_t_ptr)) | |
1575 | { | |
e01cf2fc | 1576 | tmin = -1; |
9571391b | 1577 | status = _sdns_send(sdns, name, class, type, fqdn, buf, len, from, fromlen, &tmin); |
8a97ab44 | 1578 | if (status > 0) return status; |
e01cf2fc A |
1579 | |
1580 | if (tmin < 0) minstate = -1; | |
1581 | else m = tmin; | |
8a97ab44 A |
1582 | } |
1583 | ||
1584 | /* end of the line for FQDNs or PTR queries */ | |
e01cf2fc A |
1585 | if ((fqdn == 1) || (type == ns_t_ptr) || (recurse == 0) || (primary == NULL)) |
1586 | { | |
1587 | if (minstate == 0) *min = m; | |
1588 | return -1; | |
1589 | } | |
8a97ab44 A |
1590 | |
1591 | /* Try appending names from the search list */ | |
1592 | if (primary->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(primary); | |
1593 | n = primary->search_count; | |
1594 | if (n > 0) | |
1595 | { | |
1596 | /* Try qualifying with each name in the search list */ | |
1597 | for (i = 0; i < n ; i++) | |
1598 | { | |
1599 | qname = NULL; | |
1600 | asprintf(&qname, "%s.%s", name, primary->search_list[i]); | |
1601 | if (qname == NULL) return -1; | |
1602 | ||
e01cf2fc A |
1603 | tmin = -1; |
1604 | status = _sdns_search(sdns, qname, class, type, fqdn, 0, buf, len, from, fromlen, &tmin); | |
1605 | if (status <= 0) | |
8a97ab44 | 1606 | { |
e01cf2fc A |
1607 | if (tmin < 0) |
1608 | { | |
1609 | minstate = -1; | |
1610 | } | |
1611 | else if (minstate == 0) | |
1612 | { | |
1613 | if (m == -1) m = tmin; | |
1614 | else if (tmin < m) m = tmin; | |
1615 | } | |
8a97ab44 | 1616 | } |
e01cf2fc | 1617 | |
8a97ab44 A |
1618 | free(qname); |
1619 | if (status > 0) return status; | |
1620 | } | |
1621 | ||
e01cf2fc | 1622 | if (minstate == 0) *min = m; |
8a97ab44 A |
1623 | return -1; |
1624 | } | |
1625 | ||
1626 | /* | |
1627 | * We get here if the name is not fully qualified (no trailing dot), and there is no search list. | |
1628 | * Try each default client, qualifying with that client's name. | |
1629 | */ | |
1630 | pdns = NULL; | |
1631 | pdns_count = _pdns_get_default_handles(sdns, &pdns); | |
1632 | status = -1; | |
1633 | ||
e01cf2fc A |
1634 | if (pdns_count == 0) |
1635 | { | |
1636 | if (minstate == 0) *min = m; | |
1637 | return -1; | |
1638 | } | |
8a97ab44 A |
1639 | |
1640 | for (i = 0; i < pdns_count; i++) | |
1641 | { | |
1642 | qname = NULL; | |
1643 | if (pdns[i]->name == NULL) asprintf(&qname, "%s", name); | |
1644 | else asprintf(&qname, "%s.%s", name, pdns[i]->name); | |
e01cf2fc A |
1645 | |
1646 | /* leave *min at -1 in case of a malloc failure */ | |
8a97ab44 A |
1647 | if (qname == NULL) return -1; |
1648 | ||
e01cf2fc A |
1649 | tmin = -1; |
1650 | status = _pdns_query(sdns, pdns[i], qname, class, type, buf, len, from, fromlen, &tmin); | |
1651 | if (status <= 0) | |
8a97ab44 | 1652 | { |
e01cf2fc A |
1653 | if (tmin < 0) |
1654 | { | |
1655 | minstate = -1; | |
1656 | } | |
1657 | else if (minstate == 0) | |
1658 | { | |
1659 | if (m == -1) m = tmin; | |
1660 | else if (tmin < m) m = tmin; | |
1661 | } | |
8a97ab44 | 1662 | } |
e01cf2fc | 1663 | |
8a97ab44 A |
1664 | free(qname); |
1665 | if (status > 0) break; | |
1666 | } | |
1667 | ||
1668 | free(pdns); | |
e01cf2fc A |
1669 | |
1670 | if (minstate == 0) *min = m; | |
8a97ab44 A |
1671 | return status; |
1672 | } | |
1673 | ||
1674 | int | |
1675 | dns_query(dns_handle_t d, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen) | |
1676 | { | |
1677 | dns_private_handle_t *dns; | |
1678 | int status, unused; | |
1679 | ||
1680 | if (d == NULL) return -1; | |
1681 | if (name == NULL) return -1; | |
1682 | dns = (dns_private_handle_t *)d; | |
1683 | ||
1684 | status = -1; | |
1685 | unused = 0; | |
1686 | ||
1687 | if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER) | |
1688 | { | |
1689 | _check_cache(dns->sdns); | |
1690 | status = _sdns_search(dns->sdns, name, class, type, 1, 1, buf, len, from, fromlen, &unused); | |
1691 | } | |
1692 | else | |
1693 | { | |
1694 | status = _pdns_query(dns->sdns, dns->pdns, name, class, type, buf, len, from, fromlen, &unused); | |
1695 | } | |
1696 | ||
1697 | return status; | |
1698 | } | |
1699 | ||
1700 | ||
1701 | int | |
1702 | dns_search(dns_handle_t d, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen) | |
1703 | { | |
1704 | dns_private_handle_t *dns; | |
1705 | int status, unused; | |
1706 | ||
1707 | if (d == NULL) return -1; | |
1708 | if (name == NULL) return -1; | |
1709 | dns = (dns_private_handle_t *)d; | |
1710 | ||
1711 | status = -1; | |
1712 | unused = 0; | |
1713 | ||
1714 | if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER) | |
1715 | { | |
1716 | _check_cache(dns->sdns); | |
1717 | status = _sdns_search(dns->sdns, name, class, type, 0, 1, buf, len, from, fromlen, &unused); | |
1718 | } | |
1719 | else | |
1720 | { | |
1721 | status = _pdns_search(dns->sdns, dns->pdns, name, class, type, buf, len, from, fromlen); | |
1722 | } | |
1723 | ||
1724 | return status; | |
1725 | } | |
1726 | ||
1727 | /* | |
1728 | * PRIVATE | |
1729 | */ | |
1730 | ||
1731 | uint32_t | |
1732 | dns_server_list_count(dns_handle_t d) | |
1733 | { | |
1734 | dns_private_handle_t *dns; | |
1735 | res_state r; | |
1736 | ||
1737 | if (d == NULL) return 0; | |
1738 | dns = (dns_private_handle_t *)d; | |
1739 | ||
1740 | if (dns->handle_type != DNS_PRIVATE_HANDLE_TYPE_PLAIN) return 0; | |
1741 | ||
1742 | if (dns->pdns == NULL) return 0; | |
1743 | ||
1744 | r = dns->pdns->res; | |
1745 | if (r == NULL) return 0; | |
1746 | ||
1747 | return r->nscount; | |
1748 | } | |
1749 | ||
1750 | struct sockaddr * | |
1751 | dns_server_list_address(dns_handle_t d, uint32_t i) | |
1752 | { | |
1753 | dns_private_handle_t *dns; | |
1754 | res_state r; | |
1755 | struct sockaddr_storage *s; | |
1756 | struct sockaddr *sa; | |
1757 | ||
1758 | if (d == NULL) return NULL; | |
1759 | dns = (dns_private_handle_t *)d; | |
1760 | ||
1761 | if (dns->handle_type != DNS_PRIVATE_HANDLE_TYPE_PLAIN) return NULL; | |
1762 | ||
1763 | if (dns->pdns == NULL) return NULL; | |
1764 | ||
1765 | r = dns->pdns->res; | |
1766 | if (r == NULL) return NULL; | |
1767 | ||
1768 | if (i >= r->nscount) return NULL; | |
1769 | sa = get_nsaddr(r, i); | |
1770 | if (sa == NULL) return NULL; | |
1771 | ||
1772 | s = (struct sockaddr_storage *)calloc(1, sizeof(struct sockaddr_storage)); | |
1773 | if (s == NULL) return NULL; | |
1774 | ||
1775 | memcpy(s, sa, sizeof(struct sockaddr_storage)); | |
1776 | return (struct sockaddr *)s; | |
1777 | } |