Define HAVE_FFSL on FreeBSD.
[polipo.git] / dns.c
blob8d4ac756b9b457d8d8a2979a2e1e7bda7825fa10
1 /*
2 Copyright (c) 2003-2006 by Juliusz Chroboczek
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
23 #include "polipo.h"
25 #ifndef NO_STANDARD_RESOLVER
26 #ifndef NO_FANCY_RESOLVER
27 int dnsUseGethostbyname = 1;
28 #else
29 const int dnsUseGethostbyname = 3;
30 #endif
31 #else
32 #ifndef NO_FANCY_RESOLVER
33 const int dnsUseGethostbyname = 0;
34 #else
35 #error use no resolver at all?
36 #endif
37 #endif
39 #ifndef NO_FANCY_RESOLVER
40 AtomPtr dnsNameServer = NULL;
41 int dnsMaxTimeout = 60;
42 #endif
44 #ifndef NO_STANDARD_RESOLVER
45 int dnsGethostbynameTtl = 1200;
46 #endif
48 int dnsNegativeTtl = 120;
50 #ifdef HAVE_IPv6
51 int dnsQueryIPv6 = 2;
52 #else
53 const int dnsQueryIPv6 = 0;
54 #endif
56 typedef struct _DnsQuery {
57 unsigned id;
58 AtomPtr name;
59 ObjectPtr object;
60 AtomPtr inet4, inet6;
61 time_t ttl4, ttl6;
62 time_t time;
63 int timeout;
64 TimeEventHandlerPtr timeout_handler;
65 struct _DnsQuery *next;
66 } DnsQueryRec, *DnsQueryPtr;
68 union {
69 struct sockaddr sa;
70 struct sockaddr_in sin;
71 #ifdef HAVE_IPv6
72 struct sockaddr_in6 sin6;
73 #endif
74 } nameserverAddress_storage;
76 #ifndef NO_FANCY_RESOLVER
77 static AtomPtr atomLocalhost, atomLocalhostDot;
79 #define nameserverAddress nameserverAddress_storage.sa
81 static DnsQueryPtr inFlightDnsQueries;
82 static DnsQueryPtr inFlightDnsQueriesLast;
83 #endif
85 static int really_do_gethostbyname(AtomPtr name, ObjectPtr object);
86 static int really_do_dns(AtomPtr name, ObjectPtr object);
88 #ifndef NO_FANCY_RESOLVER
89 static int stringToLabels(char *buf, int offset, int n, char *string);
90 static int labelsToString(char *buf, int offset, int n, char *d,
91 int m, int *j_return);
92 static int dnsBuildQuery(int id, char *buf, int offset, int n,
93 AtomPtr name, int af);
94 static int dnsReplyHandler(int abort, FdEventHandlerPtr event);
95 static int dnsReplyId(char *buf, int offset, int n, int *id_return);
96 static int dnsDecodeReply(char *buf, int offset, int n,
97 int *id_return,
98 AtomPtr *name_return, AtomPtr *value_return,
99 int *af_return, unsigned *ttl_return);
100 static int dnsHandler(int status, ConditionHandlerPtr chandler);
101 static int dnsGethostbynameFallback(int id, AtomPtr message);
102 static int sendQuery(DnsQueryPtr query);
104 static int idSeed;
105 #endif
107 #ifndef NO_FANCY_RESOLVER
108 static int
109 parseResolvConf(char *filename)
111 FILE *f;
112 char buf[512];
113 char *p, *q;
114 int n;
115 AtomPtr nameserver = NULL;
117 f = fopen(filename, "r");
118 if(f == NULL) {
119 do_log_error(L_ERROR, errno, "DNS: couldn't open %s", filename);
120 return 0;
123 while(1) {
124 p = fgets(buf, 512, f);
125 if(p == NULL)
126 break;
128 n = strlen(buf);
129 if(buf[n - 1] != '\n') {
130 int c;
131 do_log(L_WARN, "DNS: overly long line in %s -- skipping.\n",
132 filename);
133 do {
134 c = fgetc(f);
135 if(c == EOF)
136 break;
137 } while(c != '\n');
138 if(c == EOF)
139 break;
142 while(*p == ' ' || *p == '\t')
143 p++;
144 if(strcasecmp_n("nameserver", p, 10) != 0)
145 continue;
146 p += 10;
147 while(*p == ' ' || *p == '\t')
148 p++;
149 q = p;
150 while(*q == '.' || *q == ':' || digit(*q) || letter(*q))
151 q++;
152 if(*q != ' ' && *q != '\t' && *q != '\r' && *q != '\n') {
153 do_log(L_WARN, "DNS: couldn't parse line in %s -- skipping.\n",
154 filename);
155 continue;
157 nameserver = internAtomLowerN(p, q - p);
158 break;
161 fclose(f);
162 if(nameserver) {
163 dnsNameServer = nameserver;
164 return 1;
165 } else {
166 return 0;
169 #endif
171 void
172 preinitDns()
174 #ifdef HAVE_IPv6
175 int fd;
176 #endif
178 assert(sizeof(struct in_addr) == 4);
179 #ifdef HAVE_IPv6
180 assert(sizeof(struct in6_addr) == 16);
181 #endif
183 #ifndef NO_STANDARD_RESOLVER
184 CONFIG_VARIABLE(dnsGethostbynameTtl, CONFIG_TIME,
185 "TTL for gethostbyname addresses.");
186 #endif
188 #ifdef HAVE_IPv6
189 fd = socket(PF_INET6, SOCK_STREAM, 0);
190 if(fd < 0) {
191 if(errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT) {
192 dnsQueryIPv6 = 0;
193 } else {
194 do_log_error(L_WARN, errno, "DNS: couldn't create socket");
196 } else {
197 close(fd);
199 #endif
201 #ifndef NO_FANCY_RESOLVER
202 parseResolvConf("/etc/resolv.conf");
203 if(dnsNameServer == NULL || dnsNameServer->string[0] == '\0')
204 dnsNameServer = internAtom("127.0.0.1");
205 CONFIG_VARIABLE(dnsMaxTimeout, CONFIG_TIME,
206 "Max timeout for DNS queries.");
207 CONFIG_VARIABLE(dnsNegativeTtl, CONFIG_TIME,
208 "TTL for negative DNS replies with no TTL.");
209 CONFIG_VARIABLE(dnsNameServer, CONFIG_ATOM_LOWER,
210 "The name server to use.");
211 #ifndef NO_STANDARD_RESOLVER
212 CONFIG_VARIABLE(dnsUseGethostbyname, CONFIG_TETRASTATE,
213 "Use the system resolver.");
214 #endif
215 #endif
217 #ifdef HAVE_IPv6
218 CONFIG_VARIABLE(dnsQueryIPv6, CONFIG_TETRASTATE,
219 "Query for IPv6 addresses.");
220 #endif
223 void
224 initDns()
226 #ifndef NO_FANCY_RESOLVER
227 int rc;
228 struct timeval t;
229 struct sockaddr_in *sin = (struct sockaddr_in*)&nameserverAddress;
230 #ifdef HAVE_IPv6
231 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&nameserverAddress;
232 #endif
234 atomLocalhost = internAtom("localhost");
235 atomLocalhostDot = internAtom("localhost.");
236 inFlightDnsQueries = NULL;
237 inFlightDnsQueriesLast = NULL;
239 gettimeofday(&t, NULL);
240 idSeed = t.tv_usec & 0xFFFF;
241 sin->sin_family = AF_INET;
242 sin->sin_port = htons(53);
243 rc = inet_aton(dnsNameServer->string, &sin->sin_addr);
244 #ifdef HAVE_IPv6
245 if(rc != 1) {
246 sin6->sin6_family = AF_INET6;
247 sin6->sin6_port = htons(53);
248 rc = inet_pton(AF_INET6, dnsNameServer->string, &sin6->sin6_addr);
250 #endif
251 if(rc != 1) {
252 do_log(L_ERROR, "DNS: couldn't parse name server %s.\n",
253 dnsNameServer->string);
254 exit(1);
256 #endif
260 do_gethostbyname(char *origname,
261 int count,
262 int (*handler)(int, GethostbynameRequestPtr),
263 void *data)
265 ObjectPtr object;
266 int n = strlen(origname);
267 AtomPtr name;
268 GethostbynameRequestRec request;
269 int done, rc;
271 memset(&request, 0, sizeof(request));
272 request.name = NULL;
273 request.addr = NULL;
274 request.error_message = NULL;
275 request.count = count;
276 request.handler = handler;
277 request.data = data;
279 if(n <= 0 || n > 131) {
280 if(n <= 0) {
281 request.error_message = internAtom("empty name");
282 do_log(L_ERROR, "Empty DNS name.\n");
283 done = handler(-EINVAL, &request);
284 } else {
285 request.error_message = internAtom("name too long");
286 do_log(L_ERROR, "DNS name too long.\n");
287 done = handler(-ENAMETOOLONG, &request);
289 assert(done);
290 releaseAtom(request.error_message);
291 return 1;
294 if(origname[n - 1] == '.')
295 n--;
297 name = internAtomLowerN(origname, n);
299 if(name == NULL) {
300 request.error_message = internAtom("couldn't allocate name");
301 do_log(L_ERROR, "Couldn't allocate DNS name.\n");
302 done = handler(-ENOMEM, &request);
303 assert(done);
304 releaseAtom(request.error_message);
305 return 1;
308 request.name = name;
309 request.addr = NULL;
310 request.error_message = NULL;
311 request.count = count;
312 request.object = NULL;
313 request.handler = handler;
314 request.data = data;
316 object = findObject(OBJECT_DNS, name->string, name->length);
317 if(object == NULL || objectMustRevalidate(object, NULL)) {
318 if(object) {
319 privatiseObject(object, 0);
320 releaseObject(object);
322 object = makeObject(OBJECT_DNS, name->string, name->length, 1, 0,
323 NULL, NULL);
324 if(object == NULL) {
325 request.error_message = internAtom("Couldn't allocate object");
326 do_log(L_ERROR, "Couldn't allocate DNS object.\n");
327 done = handler(-ENOMEM, &request);
328 assert(done);
329 releaseAtom(name);
330 releaseAtom(request.error_message);
331 return 1;
335 if((object->flags & (OBJECT_INITIAL | OBJECT_INPROGRESS)) ==
336 OBJECT_INITIAL) {
337 if(dnsUseGethostbyname >= 3)
338 rc = really_do_gethostbyname(name, object);
339 else
340 rc = really_do_dns(name, object);
341 if(rc < 0) {
342 assert(!(object->flags & (OBJECT_INITIAL | OBJECT_INPROGRESS)));
343 goto fail;
347 if(dnsUseGethostbyname >= 3)
348 assert(!(object->flags & OBJECT_INITIAL));
350 #ifndef NO_FANCY_RESOLVER
351 if(object->flags & OBJECT_INITIAL) {
352 ConditionHandlerPtr chandler;
353 assert(object->flags & OBJECT_INPROGRESS);
354 request.object = object;
355 chandler = conditionWait(&object->condition, dnsHandler,
356 sizeof(request), &request);
357 if(chandler == NULL) {
358 rc = ENOMEM;
359 goto fail;
361 return 1;
363 #endif
365 if(object->headers && object->headers->length > 0) {
366 if(object->headers->string[0] == DNS_A)
367 assert(((object->headers->length - 1) %
368 sizeof(HostAddressRec)) == 0);
369 else
370 assert(object->headers->string[0] == DNS_CNAME);
371 request.addr = retainAtom(object->headers);
372 } else if(object->message) {
373 request.error_message = retainAtom(object->message);
376 releaseObject(object);
378 if(request.addr && request.addr->length > 0)
379 done = handler(1, &request);
380 else
381 done = handler(-EDNS_HOST_NOT_FOUND, &request);
382 assert(done);
384 releaseAtom(request.addr); request.addr = NULL;
385 releaseAtom(request.name); request.name = NULL;
386 releaseAtom(request.error_message); request.error_message = NULL;
387 return 1;
389 fail:
390 releaseNotifyObject(object);
391 done = handler(-errno, &request);
392 assert(done);
393 releaseAtom(name);
394 return 1;
397 static int
398 dnsDelayedErrorNotifyHandler(TimeEventHandlerPtr event)
400 int done;
401 GethostbynameRequestRec request =
402 *(GethostbynameRequestPtr)event->data;
403 done = request.handler(-EDNS_HOST_NOT_FOUND, &request);
404 assert(done);
405 releaseAtom(request.name); request.name = NULL;
406 releaseAtom(request.addr); request.addr = NULL;
407 releaseAtom(request.error_message); request.error_message = NULL;
408 return 1;
411 static int
412 dnsDelayedDoneNotifyHandler(TimeEventHandlerPtr event)
414 int done;
415 GethostbynameRequestRec request = *(GethostbynameRequestPtr)event->data;
416 done = request.handler(1, &request);
417 assert(done);
418 releaseAtom(request.name); request.name = NULL;
419 releaseAtom(request.addr); request.addr = NULL;
420 releaseAtom(request.error_message); request.error_message = NULL;
421 return 1;
424 static int
425 dnsDelayedNotify(int error, GethostbynameRequestPtr request)
427 TimeEventHandlerPtr handler;
429 if(error)
430 handler = scheduleTimeEvent(0,
431 dnsDelayedErrorNotifyHandler,
432 sizeof(*request), request);
433 else
434 handler = scheduleTimeEvent(0,
435 dnsDelayedDoneNotifyHandler,
436 sizeof(*request), request);
437 if(handler == NULL) {
438 do_log(L_ERROR, "Couldn't schedule DNS notification.\n");
439 return -1;
441 return 1;
444 #ifdef HAVE_IPv6
445 AtomPtr
446 rfc2732(AtomPtr name)
448 char buf[38];
449 int rc;
450 AtomPtr a = NULL;
452 if(name->length < 38 &&
453 name->string[0] == '[' && name->string[name->length - 1] == ']') {
454 struct in6_addr in6a;
455 memcpy(buf, name->string + 1, name->length - 2);
456 buf[name->length - 2] = '\0';
457 rc = inet_pton(AF_INET6, buf, &in6a);
458 if(rc == 1) {
459 char s[1 + sizeof(HostAddressRec)];
460 memset(s, 0, sizeof(s));
461 s[0] = DNS_A;
462 s[1] = 6;
463 memcpy(s + 2, &in6a, 16);
464 a = internAtomN(s, 1 + sizeof(HostAddressRec));
465 if(a == NULL)
466 return NULL;
469 return a;
472 /* Used for sorting host addresses depending on the value of dnsQueryIPv6 */
474 compare_hostaddr(const void *av, const void *bv)
476 const HostAddressRec *a = av, *b = bv;
477 int r;
478 if(a->af == 4) {
479 if(b->af == 4)
480 r = 0;
481 else
482 r = -1;
483 } else {
484 if(b->af == 6)
485 r = 0;
486 else
487 r = 1;
489 if(dnsQueryIPv6 >= 2)
490 return -r;
491 else
492 return r;
495 #ifndef NO_STANDARD_RESOLVER
496 static int
497 really_do_gethostbyname(AtomPtr name, ObjectPtr object)
499 struct addrinfo *ai, *entry, hints;
500 int rc;
501 int error, i;
502 char buf[1024];
503 AtomPtr a;
505 a = rfc2732(name);
506 if(a) {
507 object->headers = a;
508 object->age = current_time.tv_sec;
509 object->expires = current_time.tv_sec + 240;
510 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
511 notifyObject(object);
512 return 0;
515 memset(&hints, 0, sizeof(hints));
516 hints.ai_protocol = IPPROTO_TCP;
517 if(dnsQueryIPv6 <= 0)
518 hints.ai_family = AF_INET;
519 else if(dnsQueryIPv6 >= 3)
520 hints.ai_family = AF_INET6;
522 rc = getaddrinfo(name->string, NULL, &hints, &ai);
524 switch(rc) {
525 case 0: error = 0; break;
526 case EAI_FAMILY:
527 #ifdef EAI_ADDRFAMILY
528 case EAI_ADDRFAMILY:
529 #endif
530 case EAI_SOCKTYPE:
531 error = EAFNOSUPPORT; break;
532 case EAI_BADFLAGS: error = EINVAL; break;
533 case EAI_SERVICE: error = EDNS_NO_RECOVERY; break;
534 #ifdef EAI_NONAME
535 case EAI_NONAME:
536 #endif
537 #ifdef EAI_NODATA
538 case EAI_NODATA:
539 #endif
540 error = EDNS_NO_ADDRESS; break;
541 case EAI_FAIL: error = EDNS_NO_RECOVERY; break;
542 case EAI_AGAIN: error = EDNS_TRY_AGAIN; break;
543 #ifdef EAI_MEMORY
544 case EAI_MEMORY: error = ENOMEM; break;
545 #endif
546 case EAI_SYSTEM: error = errno; break;
547 default: error = EUNKNOWN;
550 if(error == EDNS_NO_ADDRESS) {
551 object->headers = NULL;
552 object->age = current_time.tv_sec;
553 object->expires = current_time.tv_sec + dnsNegativeTtl;
554 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
555 notifyObject(object);
556 return 0;
557 } else if(error) {
558 do_log_error(L_ERROR, error, "Getaddrinfo failed");
559 object->flags &= ~OBJECT_INPROGRESS;
560 abortObject(object, 404,
561 internAtomError(error, "Getaddrinfo failed"));
562 notifyObject(object);
563 return 0;
566 entry = ai;
567 buf[0] = DNS_A;
568 i = 0;
569 while(entry) {
570 HostAddressRec host;
571 int host_valid = 0;
572 if(entry->ai_family == AF_INET && entry->ai_protocol == IPPROTO_TCP) {
573 if(dnsQueryIPv6 < 3) {
574 host.af = 4;
575 memset(host.data, 0, sizeof(host.data));
576 memcpy(&host.data,
577 &((struct sockaddr_in*)entry->ai_addr)->sin_addr,
579 host_valid = 1;
581 } else if(entry->ai_family == AF_INET6 &&
582 entry->ai_protocol == IPPROTO_TCP) {
583 if(dnsQueryIPv6 > 0) {
584 host.af = 6;
585 memset(&host.data, 0, sizeof(host.data));
586 memcpy(&host.data,
587 &((struct sockaddr_in6*)entry->ai_addr)->sin6_addr,
588 16);
589 host_valid = 1;
592 if(host_valid) {
593 if(i >= 1024 / sizeof(HostAddressRec) - 2) {
594 do_log(L_ERROR, "Too many addresses for host %s\n",
595 name->string);
596 break;
598 memcpy(buf + 1 + i * sizeof(HostAddressRec),
599 &host, sizeof(HostAddressRec));
600 i++;
602 entry = entry->ai_next;
604 freeaddrinfo(ai);
605 if(i == 0) {
606 do_log(L_ERROR, "Getaddrinfo returned no useful addresses\n");
607 object->flags &= ~OBJECT_INPROGRESS;
608 abortObject(object, 404,
609 internAtom("Getaddrinfo returned no useful addresses"));
610 notifyObject(object);
611 return 0;
614 if(1 <= dnsQueryIPv6 && dnsQueryIPv6 <= 2)
615 qsort(buf + 1, i, sizeof(HostAddressRec), compare_hostaddr);
617 a = internAtomN(buf, 1 + i * sizeof(HostAddressRec));
618 if(a == NULL) {
619 object->flags &= ~OBJECT_INPROGRESS;
620 abortObject(object, 501, internAtom("Couldn't allocate address"));
621 notifyObject(object);
622 return 0;
624 object->headers = a;
625 object->age = current_time.tv_sec;
626 object->expires = current_time.tv_sec + dnsGethostbynameTtl;
627 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
628 notifyObject(object);
629 return 0;
631 #endif
633 #else
635 #ifndef NO_STANDARD_RESOLVER
636 static int
637 really_do_gethostbyname(AtomPtr name, ObjectPtr object)
639 struct hostent *host;
640 char *s;
641 AtomPtr a;
642 int i, j;
643 int error;
645 host = gethostbyname(name->string);
646 if(host == NULL) {
647 switch(h_errno) {
648 case HOST_NOT_FOUND: error = EDNS_HOST_NOT_FOUND; break;
649 case NO_ADDRESS: error = EDNS_NO_ADDRESS; break;
650 case NO_RECOVERY: error = EDNS_NO_RECOVERY; break;
651 case TRY_AGAIN: error = EDNS_TRY_AGAIN; break;
652 default: error = EUNKNOWN; break;
654 if(error == EDNS_HOST_NOT_FOUND) {
655 object->headers = NULL;
656 object->age = current_time.tv_sec;
657 object->expires = current_time.tv_sec + dnsNegativeTtl;
658 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
659 object->flags &= ~OBJECT_INPROGRESS;
660 notifyObject(object);
661 return 0;
662 } else {
663 do_log_error(L_ERROR, error, "Gethostbyname failed");
664 abortObject(object, 404,
665 internAtomError(error, "Gethostbyname failed"));
666 object->flags &= ~OBJECT_INPROGRESS;
667 notifyObject(object);
668 return 0;
671 if(host->h_addrtype != AF_INET) {
672 do_log(L_ERROR, "Address is not AF_INET.\n");
673 object->flags &= ~OBJECT_INPROGRESS;
674 abortObject(object, 404, internAtom("Address is not AF_INET"));
675 notifyObject(object);
676 return -1;
678 if(host->h_length != sizeof(struct in_addr)) {
679 do_log(L_ERROR, "Address size inconsistent.\n");
680 object->flags &= ~OBJECT_INPROGRESS;
681 abortObject(object, 404, internAtom("Address size inconsistent"));
682 notifyObject(object);
683 return 0;
685 i = 0;
686 while(host->h_addr_list[i] != NULL) i++;
687 s = malloc(1 + i * sizeof(HostAddressRec));
688 if(s == NULL) {
689 a = NULL;
690 } else {
691 memset(s, 0, 1 + i * sizeof(HostAddressRec));
692 s[0] = DNS_A;
693 for(j = 0; j < i; j++) {
694 s[j * sizeof(HostAddressRec) + 1] = 4;
695 memcpy(&s[j * sizeof(HostAddressRec) + 2], host->h_addr_list[j],
696 sizeof(struct in_addr));
698 a = internAtomN(s, i * sizeof(HostAddressRec) + 1);
699 free(s);
701 if(!a) {
702 object->flags &= ~OBJECT_INPROGRESS;
703 abortObject(object, 501, internAtom("Couldn't allocate address"));
704 notifyObject(object);
705 return 0;
707 object->headers = a;
708 object->age = current_time.tv_sec;
709 object->expires = current_time.tv_sec + dnsGethostbynameTtl;
710 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
711 notifyObject(object);
712 return 0;
715 #endif
717 #endif
719 #ifdef NO_STANDARD_RESOLVER
720 static int
721 really_do_gethostbyname(AtomPtr name, ObjectPtr object)
723 abort();
725 #endif
727 #ifndef NO_FANCY_RESOLVER
729 static int dnsSocket = -1;
730 static FdEventHandlerPtr dnsSocketHandler = NULL;
732 static int
733 dnsHandler(int status, ConditionHandlerPtr chandler)
735 GethostbynameRequestRec request = *(GethostbynameRequestPtr)chandler->data;
736 ObjectPtr object = request.object;
738 assert(!(object->flags & OBJECT_INPROGRESS));
740 if(object->headers) {
741 request.addr = retainAtom(object->headers);
742 dnsDelayedNotify(0, &request);
743 } else {
744 if(object->message)
745 request.error_message = retainAtom(object->message);
746 dnsDelayedNotify(1, &request);
748 releaseObject(object);
749 return 1;
752 static int
753 queryInFlight(DnsQueryPtr query)
755 DnsQueryPtr other;
756 other = inFlightDnsQueries;
757 while(other) {
758 if(other == query)
759 return 1;
760 other = other->next;
762 return 0;
765 static void
766 removeQuery(DnsQueryPtr query)
768 DnsQueryPtr previous;
769 if(query == inFlightDnsQueries) {
770 inFlightDnsQueries = query->next;
771 if(inFlightDnsQueries == NULL)
772 inFlightDnsQueriesLast = NULL;
773 } else {
774 previous = inFlightDnsQueries;
775 while(previous->next) {
776 if(previous->next == query)
777 break;
778 previous = previous->next;
780 assert(previous->next != NULL);
781 previous->next = query->next;
782 if(previous->next == NULL)
783 inFlightDnsQueriesLast = previous;
787 static void
788 insertQuery(DnsQueryPtr query)
790 if(inFlightDnsQueriesLast)
791 inFlightDnsQueriesLast->next = query;
792 else
793 inFlightDnsQueries = query;
794 inFlightDnsQueriesLast = query;
797 static DnsQueryPtr
798 findQuery(int id, AtomPtr name)
800 DnsQueryPtr query;
801 query = inFlightDnsQueries;
802 while(query) {
803 if(query->id == id && (name == NULL || query->name == name))
804 return query;
805 query = query->next;
807 return NULL;
810 static int
811 dnsTimeoutHandler(TimeEventHandlerPtr event)
813 DnsQueryPtr query = *(DnsQueryPtr*)event->data;
814 ObjectPtr object = query->object;
815 int rc;
817 /* People are reporting that this does happen. And I have no idea why. */
818 if(!queryInFlight(query)) {
819 do_log(L_ERROR, "BUG: timing out martian query (%s, flags: 0x%x).\n",
820 scrub(query->name->string), (unsigned)object->flags);
821 return 1;
824 query->timeout = MAX(10, query->timeout * 2);
826 if(query->timeout > dnsMaxTimeout) {
827 abortObject(object, 501, internAtom("Timeout"));
828 goto fail;
829 } else {
830 rc = sendQuery(query);
831 if(rc < 0) {
832 if(rc != -EWOULDBLOCK && rc != -EAGAIN && rc != -ENOBUFS) {
833 abortObject(object, 501,
834 internAtomError(-rc,
835 "Couldn't send DNS query"));
836 goto fail;
838 /* else let it timeout */
840 query->timeout_handler =
841 scheduleTimeEvent(query->timeout, dnsTimeoutHandler,
842 sizeof(query), &query);
843 if(query->timeout_handler == NULL) {
844 do_log(L_ERROR, "Couldn't schedule DNS timeout handler.\n");
845 abortObject(object, 501,
846 internAtom("Couldn't schedule DNS timeout handler"));
847 goto fail;
849 return 1;
852 fail:
853 removeQuery(query);
854 object->flags &= ~OBJECT_INPROGRESS;
855 if(query->inet4) releaseAtom(query->inet4);
856 if(query->inet6) releaseAtom(query->inet6);
857 free(query);
858 releaseNotifyObject(object);
859 return 1;
862 static int
863 establishDnsSocket()
865 int rc;
866 #ifdef HAVE_IPv6
867 int inet6 = (nameserverAddress.sa_family == AF_INET6);
868 int pf = inet6 ? PF_INET6 : PF_INET;
869 int sa_size =
870 inet6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
871 #else
872 int pf = PF_INET;
873 int sa_size = sizeof(struct sockaddr_in);
874 #endif
876 if(dnsSocket < 0) {
877 assert(!dnsSocketHandler);
878 dnsSocket = socket(pf, SOCK_DGRAM, 0);
879 if(dnsSocket < 0) {
880 do_log_error(L_ERROR, errno, "Couldn't create DNS socket");
881 return -errno;
884 rc = connect(dnsSocket, &nameserverAddress, sa_size);
885 if(rc < 0) {
886 CLOSE(dnsSocket);
887 dnsSocket = -1;
888 do_log_error(L_ERROR, errno, "Couldn't create DNS \"connection\"");
889 return -errno;
893 if(!dnsSocketHandler) {
894 dnsSocketHandler =
895 registerFdEvent(dnsSocket, POLLIN, dnsReplyHandler, 0, NULL);
896 if(dnsSocketHandler == NULL) {
897 do_log(L_ERROR, "Couldn't register DNS socket handler.\n");
898 CLOSE(dnsSocket);
899 dnsSocket = -1;
900 return -ENOMEM;
904 return 1;
907 static int
908 sendQuery(DnsQueryPtr query)
910 char buf[512];
911 int buflen;
912 int rc;
913 int af[2];
914 int i;
916 if(dnsSocket < 0)
917 return -1;
919 if(dnsQueryIPv6 <= 0) {
920 af[0] = 4; af[1] = 0;
921 } else if(dnsQueryIPv6 <= 2) {
922 af[0] = 4; af[1] = 6;
923 } else {
924 af[0] = 6; af[1] = 0;
927 for(i = 0; i < 2; i++) {
928 if(af[i] == 0)
929 continue;
930 if(af[i] == 4 && query->inet4)
931 continue;
932 else if(af[i] == 6 && query->inet6)
933 continue;
935 buflen = dnsBuildQuery(query->id, buf, 0, 512, query->name, af[i]);
936 if(buflen <= 0) {
937 do_log(L_ERROR, "Couldn't build DNS query.\n");
938 return buflen;
941 rc = send(dnsSocket, buf, buflen, 0);
942 if(rc < buflen) {
943 if(rc >= 0) {
944 do_log(L_ERROR, "Couldn't send DNS query: partial send.\n");
945 return -EAGAIN;
946 } else {
947 do_log_error(L_ERROR, errno, "Couldn't send DNS query");
948 return -errno;
952 return 1;
955 static int
956 really_do_dns(AtomPtr name, ObjectPtr object)
958 int rc;
959 DnsQueryPtr query;
960 AtomPtr message = NULL;
961 int id;
962 AtomPtr a = NULL;
964 if(a == NULL) {
965 if(name == atomLocalhost || name == atomLocalhostDot) {
966 char s[1 + sizeof(HostAddressRec)];
967 memset(s, 0, sizeof(s));
968 s[0] = DNS_A;
969 s[1] = 4;
970 s[2] = 127;
971 s[3] = 0;
972 s[4] = 0;
973 s[5] = 1;
974 a = internAtomN(s, 1 + sizeof(HostAddressRec));
975 if(a == NULL) {
976 abortObject(object, 501,
977 internAtom("Couldn't allocate address"));
978 notifyObject(object);
979 errno = ENOMEM;
980 return -1;
985 if(a == NULL) {
986 struct in_addr ina;
987 rc = inet_aton(name->string, &ina);
988 if(rc == 1) {
989 char s[1 + sizeof(HostAddressRec)];
990 memset(s, 0, sizeof(s));
991 s[0] = DNS_A;
992 s[1] = 4;
993 memcpy(s + 2, &ina, 4);
994 a = internAtomN(s, 1 + sizeof(HostAddressRec));
995 if(a == NULL) {
996 abortObject(object, 501,
997 internAtom("Couldn't allocate address"));
998 notifyObject(object);
999 errno = ENOMEM;
1000 return -1;
1004 #ifdef HAVE_IPv6
1005 if(a == NULL)
1006 a = rfc2732(name);
1007 #endif
1009 if(a) {
1010 object->headers = a;
1011 object->age = current_time.tv_sec;
1012 object->expires = current_time.tv_sec + 240;
1013 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
1014 notifyObject(object);
1015 return 0;
1018 rc = establishDnsSocket();
1019 if(rc < 0) {
1020 do_log_error(L_ERROR, -rc, "Couldn't establish DNS socket.\n");
1021 message = internAtomError(-rc, "Couldn't establish DNS socket");
1022 goto fallback;
1025 /* The id is used to speed up detecting replies to queries that
1026 are no longer current -- see dnsReplyHandler. */
1027 id = (idSeed++) & 0xFFFF;
1029 query = malloc(sizeof(DnsQueryRec));
1030 if(query == NULL) {
1031 do_log(L_ERROR, "Couldn't allocate DNS query.\n");
1032 message = internAtom("Couldn't allocate DNS query");
1033 goto fallback;
1035 query->id = id;
1036 query->inet4 = NULL;
1037 query->inet6 = NULL;
1038 query->name = name;
1039 query->time = current_time.tv_sec;
1040 query->object = retainObject(object);
1041 query->timeout = 4;
1042 query->timeout_handler = NULL;
1043 query->next = NULL;
1045 query->timeout_handler =
1046 scheduleTimeEvent(query->timeout, dnsTimeoutHandler,
1047 sizeof(query), &query);
1048 if(query->timeout_handler == NULL) {
1049 do_log(L_ERROR, "Couldn't schedule DNS timeout handler.\n");
1050 message = internAtom("Couldn't schedule DNS timeout handler");
1051 goto free_fallback;
1053 insertQuery(query);
1055 object->flags |= OBJECT_INPROGRESS;
1056 rc = sendQuery(query);
1057 if(rc < 0) {
1058 if(rc != -EWOULDBLOCK && rc != -EAGAIN && rc != -ENOBUFS) {
1059 object->flags &= ~OBJECT_INPROGRESS;
1060 message = internAtomError(-rc, "Couldn't send DNS query");
1061 goto remove_fallback;
1063 /* else let it timeout */
1065 releaseAtom(message);
1066 return 1;
1068 remove_fallback:
1069 removeQuery(query);
1070 free_fallback:
1071 releaseObject(query->object);
1072 cancelTimeEvent(query->timeout_handler);
1073 free(query);
1074 fallback:
1075 if(dnsUseGethostbyname >= 1) {
1076 releaseAtom(message);
1077 do_log(L_WARN, "Falling back on gethostbyname.\n");
1078 return really_do_gethostbyname(name, object);
1079 } else {
1080 abortObject(object, 501, message);
1081 notifyObject(object);
1082 return 1;
1086 static int
1087 dnsReplyHandler(int abort, FdEventHandlerPtr event)
1089 int fd = event->fd;
1090 char buf[2048];
1091 int len, rc;
1092 ObjectPtr object;
1093 unsigned ttl = 0;
1094 AtomPtr name, value, message = NULL;
1095 int id;
1096 int af;
1097 DnsQueryPtr query;
1098 AtomPtr cname = NULL;
1100 if(abort) {
1101 dnsSocketHandler = NULL;
1102 rc = establishDnsSocket();
1103 if(rc < 0) {
1104 do_log(L_ERROR, "Couldn't reestablish DNS socket.\n");
1105 /* At this point, we should abort all in-flight
1106 DNS requests. Oh, well, they'll timeout anyway. */
1108 return 1;
1111 len = recv(fd, buf, 2048, 0);
1112 if(len <= 0) {
1113 if(errno == EINTR || errno == EAGAIN) return 0;
1114 /* This is where we get ECONNREFUSED for an ICMP port unreachable */
1115 do_log_error(L_ERROR, errno, "DNS: recv failed");
1116 dnsGethostbynameFallback(-1, message);
1117 return 0;
1120 /* This could be a late reply to a query that timed out and was
1121 resent, a reply to a query that timed out, or a reply to an
1122 AAAA query when we already got a CNAME reply to the associated
1123 A. We filter such replies straight away, without trying to
1124 parse them. */
1125 rc = dnsReplyId(buf, 0, len, &id);
1126 if(rc < 0) {
1127 do_log(L_WARN, "Short DNS reply.\n");
1128 return 0;
1130 if(!findQuery(id, NULL)) {
1131 return 0;
1134 rc = dnsDecodeReply(buf, 0, len, &id, &name, &value, &af, &ttl);
1135 if(rc < 0) {
1136 assert(value == NULL);
1137 /* We only want to fallback on gethostbyname if we received a
1138 reply that we could not understand. What about truncated
1139 replies? */
1140 if(rc < 0) {
1141 do_log_error(L_WARN, -rc, "DNS");
1142 if(dnsUseGethostbyname >= 2 ||
1143 (dnsUseGethostbyname &&
1144 (rc != -EDNS_HOST_NOT_FOUND && rc != -EDNS_NO_RECOVERY &&
1145 rc != -EDNS_FORMAT))) {
1146 dnsGethostbynameFallback(id, message);
1147 return 0;
1148 } else {
1149 message = internAtomError(-rc, NULL);
1151 } else {
1152 assert(name != NULL && id >= 0 && af >= 0);
1156 query = findQuery(id, name);
1157 if(query == NULL) {
1158 /* Duplicate id ? */
1159 releaseAtom(value);
1160 releaseAtom(name);
1161 return 0;
1164 /* We're going to use the information in this reply. If it was an
1165 error, construct an empty atom to distinguish it from information
1166 we're still waiting for. */
1167 if(value == NULL)
1168 value = internAtom("");
1170 again:
1171 if(af == 4) {
1172 if(query->inet4 == NULL) {
1173 query->inet4 = value;
1174 query->ttl4 = current_time.tv_sec + ttl;
1175 } else
1176 releaseAtom(value);
1177 } else if(af == 6) {
1178 if(query->inet6 == NULL) {
1179 query->inet6 = value;
1180 query->ttl6 = current_time.tv_sec + ttl;
1181 } else
1182 releaseAtom(value);
1183 } else if(af == 0) {
1184 if(query->inet4 || query->inet6) {
1185 do_log(L_WARN, "Host %s has both %s and CNAME -- "
1186 "ignoring CNAME.\n", scrub(query->name->string),
1187 query->inet4 ? "A" : "AAAA");
1188 releaseAtom(value);
1189 value = internAtom("");
1190 af = query->inet4 ? 4 : 6;
1191 goto again;
1192 } else {
1193 cname = value;
1197 if(rc >= 0 && !cname &&
1198 ((dnsQueryIPv6 < 3 && query->inet4 == NULL) ||
1199 (dnsQueryIPv6 > 0 && query->inet6 == NULL)))
1200 return 0;
1202 /* This query is complete */
1204 cancelTimeEvent(query->timeout_handler);
1205 object = query->object;
1207 if(object->flags & OBJECT_INITIAL) {
1208 assert(!object->headers);
1209 if(cname) {
1210 assert(query->inet4 == NULL && query->inet6 == NULL);
1211 object->headers = cname;
1212 object->expires = current_time.tv_sec + ttl;
1213 } else if((!query->inet4 || query->inet4->length == 0) &&
1214 (!query->inet6 || query->inet6->length == 0)) {
1215 releaseAtom(query->inet4);
1216 releaseAtom(query->inet6);
1217 object->expires = current_time.tv_sec + dnsNegativeTtl;
1218 abortObject(object, 500, retainAtom(message));
1219 } else if(!query->inet4 || query->inet4->length == 0) {
1220 object->headers = query->inet6;
1221 object->expires = query->ttl6;
1222 releaseAtom(query->inet4);
1223 } else if(!query->inet6 || query->inet6->length == 0) {
1224 object->headers = query->inet4;
1225 object->expires = query->ttl4;
1226 releaseAtom(query->inet6);
1227 } else {
1228 /* need to merge results */
1229 char buf[1024];
1230 if(query->inet4->length + query->inet6->length > 1024) {
1231 releaseAtom(query->inet4);
1232 releaseAtom(query->inet6);
1233 abortObject(object, 500, internAtom("DNS reply too long"));
1234 } else {
1235 if(dnsQueryIPv6 <= 1) {
1236 memcpy(buf, query->inet4->string, query->inet4->length);
1237 memcpy(buf + query->inet4->length,
1238 query->inet6->string + 1, query->inet6->length - 1);
1239 } else {
1240 memcpy(buf, query->inet6->string, query->inet6->length);
1241 memcpy(buf + query->inet6->length,
1242 query->inet4->string + 1, query->inet4->length - 1);
1244 object->headers =
1245 internAtomN(buf,
1246 query->inet4->length +
1247 query->inet6->length - 1);
1248 if(object->headers == NULL)
1249 abortObject(object, 500,
1250 internAtom("Couldn't allocate DNS atom"));
1252 object->expires = MIN(query->ttl4, query->ttl6);
1254 object->age = current_time.tv_sec;
1255 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
1256 } else {
1257 do_log(L_WARN, "DNS object ex nihilo for %s.\n",
1258 scrub(query->name->string));
1261 removeQuery(query);
1262 free(query);
1264 releaseAtom(name);
1265 releaseAtom(message);
1266 releaseNotifyObject(object);
1267 return 0;
1270 static int
1271 dnsGethostbynameFallback(int id, AtomPtr message)
1273 DnsQueryPtr query, previous;
1274 ObjectPtr object;
1276 if(inFlightDnsQueries == NULL) {
1277 releaseAtom(message);
1278 return 1;
1281 query = NULL;
1282 if(id < 0 || inFlightDnsQueries->id == id) {
1283 previous = NULL;
1284 query = inFlightDnsQueries;
1285 } else {
1286 previous = inFlightDnsQueries;
1287 while(previous->next) {
1288 if(previous->next->id == id) {
1289 query = previous->next;
1290 break;
1292 previous = previous->next;
1294 if(!query) {
1295 previous = NULL;
1296 query = inFlightDnsQueries;
1300 if(previous == NULL) {
1301 inFlightDnsQueries = query->next;
1302 if(inFlightDnsQueries == NULL)
1303 inFlightDnsQueriesLast = NULL;
1304 } else {
1305 previous->next = query->next;
1306 if(query->next == NULL)
1307 inFlightDnsQueriesLast = NULL;
1310 object = makeObject(OBJECT_DNS, query->name->string, query->name->length,
1311 1, 0, NULL, NULL);
1312 if(!object) {
1313 do_log(L_ERROR, "Couldn't make DNS object.\n");
1314 releaseAtom(query->name);
1315 releaseAtom(message);
1316 releaseObject(query->object);
1317 cancelTimeEvent(query->timeout_handler);
1318 free(query);
1319 return -1;
1321 if(dnsUseGethostbyname >= 1) {
1322 releaseAtom(message);
1323 do_log(L_WARN, "Falling back to using system resolver.\n");
1324 really_do_gethostbyname(retainAtom(query->name), object);
1325 } else {
1326 releaseAtom(object->message);
1327 object->message = message;
1328 object->flags &= ~OBJECT_INPROGRESS;
1329 releaseNotifyObject(object);
1331 cancelTimeEvent(query->timeout_handler);
1332 releaseAtom(query->name);
1333 if(query->inet4) releaseAtom(query->inet4);
1334 if(query->inet6) releaseAtom(query->inet6);
1335 releaseObject(query->object);
1336 free(query);
1337 return 1;
1340 static int
1341 stringToLabels(char *buf, int offset, int n, char *string)
1343 int i = offset;
1344 int j = 0, k = 0;
1345 while(1) {
1346 while(string[k] != '.' && string[k] != '\0')
1347 k++;
1348 if(k >= j + 256) return -1;
1349 buf[i] = (unsigned char)(k - j); i++; if(i >= n) return -1;
1350 while(j < k) {
1351 buf[i] = string[j]; i++; j++; if(i >= n) return -1;
1353 if(string[j] == '\0') {
1354 buf[i] = '\0';
1355 i++; if(i >= n) return -1;
1356 break;
1358 j++; k++;
1361 return i;
1364 #ifdef UNALIGNED_ACCESS
1365 #define DO_NTOHS(_d, _s) _d = ntohs(*(short*)(_s));
1366 #define DO_NTOHL(_d, _s) _d = ntohl(*(unsigned*)(_s))
1367 #define DO_HTONS(_d, _s) *(short*)(_d) = htons(_s);
1368 #define DO_HTONL(_d, _s) *(unsigned*)(_d) = htonl(_s)
1369 #else
1370 #define DO_NTOHS(_d, _s) \
1371 do { short _dd; \
1372 memcpy(&(_dd), (_s), sizeof(short)); \
1373 _d = ntohs(_dd); } while(0)
1374 #define DO_NTOHL(_d, _s) \
1375 do { unsigned _dd; \
1376 memcpy(&(_dd), (_s), sizeof(unsigned)); \
1377 _d = ntohl(_dd); } while(0)
1378 #define DO_HTONS(_d, _s) \
1379 do { unsigned short _dd; \
1380 _dd = htons(_s); \
1381 memcpy((_d), &(_dd), sizeof(unsigned short)); } while(0);
1382 #define DO_HTONL(_d, _s) \
1383 do { unsigned _dd; \
1384 _dd = htonl(_s); \
1385 memcpy((_d), &(_dd), sizeof(unsigned)); } while(0);
1386 #endif
1388 static int
1389 labelsToString(char *buf, int offset, int n, char *d, int m, int *j_return)
1391 int i = offset, j, k;
1392 int ll;
1394 j = 0;
1395 while(1) {
1396 if(i >= n) return -1;
1397 ll = *(unsigned char*)&buf[i]; i++;
1398 if(ll == 0) {
1399 break;
1401 if((ll & (3 << 6)) == (3 << 6)) {
1402 /* RFC 1035, 4.1.4 */
1403 int o;
1404 if(i >= n) return -1;
1405 o = (ll & ~(3 << 6)) << 8 | *(unsigned char*)&buf[i];
1406 i++;
1407 labelsToString(buf, o, n, &d[j], m - j, &k);
1408 j += k;
1409 break;
1410 } else if((ll & (3 << 6)) == 0) {
1411 for(k = 0; k < ll; k++) {
1412 if(i >= n || j >= m) return -1;
1413 d[j++] = buf[i++];
1415 if(i >= n) return -1;
1416 if(buf[i] != '\0') {
1417 if(j >= m) return -1;
1418 d[j++] = '.';
1420 } else {
1421 return -1;
1424 *j_return = j;
1425 return i;
1428 static int
1429 dnsBuildQuery(int id, char *buf, int offset, int n, AtomPtr name, int af)
1431 int i = offset;
1432 int type;
1433 switch(af) {
1434 case 4: type = 1; break;
1435 case 6: type = 28; break;
1436 default: return -EINVAL;
1439 if(i + 12 >= n) return -1;
1440 DO_HTONS(&buf[i], id); i += 2;
1441 DO_HTONS(&buf[i], 1<<8); i += 2;
1442 DO_HTONS(&buf[i], 1); i += 2;
1443 DO_HTONS(&buf[i], 0); i += 2;
1444 DO_HTONS(&buf[i], 0); i += 2;
1445 DO_HTONS(&buf[i], 0); i += 2;
1447 i = stringToLabels(buf, i, n, name->string);
1448 if(i < 0) return -ENAMETOOLONG;
1450 if(i + 4 >= n) return -ENAMETOOLONG;
1451 DO_HTONS(&buf[i], type); i += 2;
1452 DO_HTONS(&buf[i], 1); i += 2;
1453 return i;
1456 static int
1457 dnsReplyId(char *buf, int offset, int n, int *id_return)
1459 if(n - offset < 12)
1460 return -1;
1461 DO_NTOHS(*id_return, &buf[offset]);
1462 return 1;
1465 static int
1466 dnsDecodeReply(char *buf, int offset, int n, int *id_return,
1467 AtomPtr *name_return, AtomPtr *value_return,
1468 int *af_return, unsigned *ttl_return)
1470 int i = offset, j, m;
1471 int id = -1, b23, qdcount, ancount, nscount, arcount, rdlength;
1472 int class, type;
1473 unsigned int ttl;
1474 char b[2048];
1475 int af = -1;
1476 AtomPtr name = NULL, value;
1477 char addresses[1024];
1478 int addr_index = 0;
1479 int error = EDNS_NO_ADDRESS;
1480 unsigned final_ttl = 7 * 24 * 3600;
1481 int dnserror;
1483 if(n - i < 12) {
1484 error = EDNS_INVALID;
1485 goto fail;
1488 DO_NTOHS(id, &buf[i]); i += 2;
1489 DO_NTOHS(b23, &buf[i]); i += 2;
1490 DO_NTOHS(qdcount, &buf[i]); i += 2;
1491 DO_NTOHS(ancount, &buf[i]); i += 2;
1492 DO_NTOHS(nscount, &buf[i]); i += 2;
1493 DO_NTOHS(arcount, &buf[i]); i += 2;
1495 do_log(D_DNS,
1496 "DNS id %d, b23 0x%x, qdcount %d, ancount %d, "
1497 "nscount %d, arcount %d\n",
1498 id, b23, qdcount, ancount, nscount, arcount);
1500 if((b23 & (0xF870)) != 0x8000) {
1501 do_log(L_ERROR, "Incorrect DNS reply (b23 = 0x%x).\n", b23);
1502 error = EDNS_INVALID;
1503 goto fail;
1506 dnserror = b23 & 0xF;
1508 if(b23 & 0x200) {
1509 do_log(L_WARN, "Truncated DNS reply (b23 = 0x%x).\n", b23);
1512 if(dnserror || qdcount != 1) {
1513 if(!dnserror)
1514 do_log(L_ERROR,
1515 "Unexpected number %d of DNS questions.\n", qdcount);
1516 if(dnserror == 1)
1517 error = EDNS_FORMAT;
1518 else if(dnserror == 2)
1519 error = EDNS_NO_RECOVERY;
1520 else if(dnserror == 3)
1521 error = EDNS_HOST_NOT_FOUND;
1522 else if(dnserror == 4 || dnserror == 5)
1523 error = EDNS_REFUSED;
1524 else if(dnserror == 0)
1525 error = EDNS_INVALID;
1526 else
1527 error = EUNKNOWN;
1528 goto fail;
1531 /* We do this early, so that we can return the address family to
1532 the caller in case of error. */
1533 i = labelsToString(buf, i, n, b, 2048, &m);
1534 if(i < 0) {
1535 error = EDNS_FORMAT;
1536 goto fail;
1538 DO_NTOHS(type, &buf[i]); i += 2;
1539 DO_NTOHS(class, &buf[i]); i += 2;
1541 if(type == 1)
1542 af = 4;
1543 else if(type == 28)
1544 af = 6;
1545 else {
1546 error = EDNS_FORMAT;
1547 goto fail;
1550 do_log(D_DNS, "DNS q: ");
1551 do_log_n(D_DNS, b, m);
1552 do_log(D_DNS, " (%d, %d)\n", type, class);
1553 name = internAtomLowerN(b, m);
1554 if(name == NULL) {
1555 error = ENOMEM;
1556 goto fail;
1559 if(class != 1) {
1560 error = EDNS_FORMAT;
1561 goto fail;
1564 #define PARSE_ANSWER(kind, label) \
1565 do { \
1566 i = labelsToString(buf, i, 1024, b, 2048, &m); \
1567 if(i < 0) goto label; \
1568 DO_NTOHS(type, &buf[i]); i += 2; if(i > 1024) goto label; \
1569 DO_NTOHS(class, &buf[i]); i += 2; if(i > 1024) goto label; \
1570 DO_NTOHL(ttl, &buf[i]); i += 4; if(i > 1024) goto label; \
1571 DO_NTOHS(rdlength, &buf[i]); i += 2; if(i > 1024) goto label; \
1572 do_log(D_DNS, "DNS " kind ": "); \
1573 do_log_n(D_DNS, b, m); \
1574 do_log(D_DNS, " (%d, %d): %d bytes, ttl %u\n", \
1575 type, class, rdlength, ttl); \
1576 } while(0)
1579 for(j = 0; j < ancount; j++) {
1580 PARSE_ANSWER("an", fail);
1581 if(strcasecmp_n(name->string, b, m) == 0) {
1582 if(class != 1) {
1583 do_log(D_DNS, "DNS: %s: unknown class %d.\n",
1584 name->string, class);
1585 error = EDNS_UNSUPPORTED;
1586 goto cont;
1588 if(type == 1 || type == 28) {
1589 if((type == 1 && rdlength != 4) ||
1590 (type == 28 && rdlength != 16)) {
1591 do_log(L_ERROR,
1592 "DNS: %s: unexpected length %d of %s record.\n",
1593 scrub(name->string),
1594 rdlength, type == 1 ? "A" : "AAAA");
1595 error = EDNS_INVALID;
1596 if(rdlength <= 0 || rdlength >= 32)
1597 goto fail;
1598 goto cont;
1600 if(af == 0) {
1601 do_log(L_WARN, "DNS: %s: host has both A and CNAME -- "
1602 "ignoring CNAME.\n", scrub(name->string));
1603 addr_index = 0;
1604 af = -1;
1606 if(type == 1) {
1607 if(af < 0)
1608 af = 4;
1609 else if(af == 6) {
1610 do_log(L_WARN, "Unexpected AAAA reply.\n");
1611 goto cont;
1613 } else {
1614 if(af < 0)
1615 af = 6;
1616 else if(af == 4) {
1617 do_log(L_WARN, "Unexpected A reply.\n");
1618 goto cont;
1622 if(addr_index == 0) {
1623 addresses[0] = DNS_A;
1624 addr_index++;
1625 } else {
1626 if(addr_index > 1000) {
1627 error = EDNS_INVALID;
1628 goto fail;
1631 assert(addresses[0] == DNS_A);
1632 if(final_ttl > ttl)
1633 final_ttl = ttl;
1634 memset(&addresses[addr_index], 0, sizeof(HostAddressRec));
1635 if(type == 1) {
1636 addresses[addr_index] = 4;
1637 memcpy(addresses + addr_index + 1, buf + i, 4);
1638 } else {
1639 addresses[addr_index] = 6;
1640 memcpy(addresses + addr_index + 1, buf + i, 16);
1642 addr_index += sizeof(HostAddressRec);
1643 } else if(type == 5) {
1644 int j, k;
1645 if(af != 0 && addr_index > 0) {
1646 do_log(L_WARN, "DNS: host has both CNAME and A -- "
1647 "ignoring CNAME.\n");
1648 goto cont;
1650 af = 0;
1652 if(addr_index != 0) {
1653 /* Only warn if the CNAMEs are not identical */
1654 char tmp[512]; int jj, kk;
1655 assert(addresses[0] == DNS_CNAME);
1656 jj = labelsToString(buf, i, n,
1657 tmp, 512, &kk);
1658 if(jj < 0 ||
1659 kk != strlen(addresses + 1) ||
1660 memcmp(addresses + 1, tmp, kk) != 0) {
1661 do_log(L_WARN, "DNS: "
1662 "%s: host has multiple CNAMEs -- "
1663 "ignoring subsequent.\n",
1664 scrub(name->string));
1667 goto cont;
1669 addresses[0] = DNS_CNAME;
1670 addr_index++;
1671 j = labelsToString(buf, i, n,
1672 addresses + 1, 1020, &k);
1673 if(j < 0) {
1674 addr_index = 0;
1675 error = ENAMETOOLONG;
1676 continue;
1678 addr_index = k + 1;
1679 } else {
1680 error = EDNS_NO_ADDRESS;
1681 i += rdlength;
1682 continue;
1686 cont:
1687 i += rdlength;
1690 #if (LOGGING_MAX & D_DNS)
1691 for(j = 0; j < nscount; j++) {
1692 PARSE_ANSWER("ns", nofail);
1693 i += rdlength;
1696 for(j = 0; j < arcount; j++) {
1697 PARSE_ANSWER("ar", nofail);
1698 i += rdlength;
1701 nofail:
1702 #endif
1704 #undef PARSE_ANSWER
1706 do_log(D_DNS, "DNS: %d bytes\n", addr_index);
1707 if(af < 0)
1708 goto fail;
1710 value = internAtomN(addresses, addr_index);
1711 if(value == NULL) {
1712 error = ENOMEM;
1713 goto fail;
1716 assert(af >= 0);
1717 *id_return = id;
1718 *name_return = name;
1719 *value_return = value;
1720 *af_return = af;
1721 *ttl_return = final_ttl;
1722 return 1;
1724 fail:
1725 *id_return = id;
1726 *name_return = name;
1727 *value_return = NULL;
1728 *af_return = af;
1729 return -error;
1732 #else
1734 static int
1735 really_do_dns(AtomPtr name, ObjectPtr object)
1737 abort();
1740 #endif