Simplify DNS error messages.
[polipo.git] / dns.c
blob3b6e03b954dd8ecdad877e3df5cfcd3722d2f4e8
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 void
753 removeQuery(DnsQueryPtr query)
755 DnsQueryPtr previous;
756 if(query == inFlightDnsQueries) {
757 inFlightDnsQueries = query->next;
758 if(inFlightDnsQueries == NULL)
759 inFlightDnsQueriesLast = NULL;
760 } else {
761 previous = inFlightDnsQueries;
762 while(previous->next) {
763 if(previous->next == query)
764 break;
765 previous = previous->next;
767 assert(previous->next != NULL);
768 previous->next = query->next;
769 if(previous->next == NULL)
770 inFlightDnsQueriesLast = previous;
774 static void
775 insertQuery(DnsQueryPtr query)
777 if(inFlightDnsQueriesLast)
778 inFlightDnsQueriesLast->next = query;
779 else
780 inFlightDnsQueries = query;
781 inFlightDnsQueriesLast = query;
784 static DnsQueryPtr
785 findQuery(int id, AtomPtr name)
787 DnsQueryPtr query;
788 query = inFlightDnsQueries;
789 while(query) {
790 if(query->id == id && (name == NULL || query->name == name))
791 return query;
792 query = query->next;
794 return NULL;
797 static int
798 dnsTimeoutHandler(TimeEventHandlerPtr event)
800 DnsQueryPtr query = *(DnsQueryPtr*)event->data;
801 ObjectPtr object = query->object;
802 int rc;
804 query->timeout = MAX(10, query->timeout * 2);
806 if(query->timeout > dnsMaxTimeout) {
807 removeQuery(query);
808 abortObject(object, 501, internAtom("Timeout"));
809 goto fail;
810 } else {
811 query->timeout_handler =
812 scheduleTimeEvent(query->timeout, dnsTimeoutHandler,
813 sizeof(query), &query);
814 if(query->timeout_handler == NULL) {
815 do_log(L_ERROR, "Couldn't schedule DNS timeout handler.\n");
816 abortObject(object, 501,
817 internAtom("Couldn't schedule DNS timeout handler"));
818 goto fail;
820 rc = sendQuery(query);
821 if(rc < 0) {
822 do_log(L_ERROR, "Couldn't send DNS query.\n");
823 if(rc != -EWOULDBLOCK && rc != -EAGAIN && rc != -ENOBUFS) {
824 abortObject(object, 501,
825 internAtom("Couldn't send DNS query"));
826 goto fail;
828 /* else let it timeout */
830 return 1;
833 fail:
834 object->flags &= ~OBJECT_INPROGRESS;
835 if(query->inet4) releaseAtom(query->inet4);
836 if(query->inet6) releaseAtom(query->inet6);
837 free(query);
838 releaseNotifyObject(object);
839 return 1;
842 static int
843 establishDnsSocket()
845 int rc;
846 #ifdef HAVE_IPv6
847 int inet6 = (nameserverAddress.sa_family == AF_INET6);
848 int pf = inet6 ? PF_INET6 : PF_INET;
849 int sa_size =
850 inet6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
851 #else
852 int pf = PF_INET;
853 int sa_size = sizeof(struct sockaddr_in);
854 #endif
856 if(dnsSocket < 0) {
857 assert(!dnsSocketHandler);
858 dnsSocket = socket(pf, SOCK_DGRAM, 0);
859 if(dnsSocket < 0) {
860 do_log_error(L_ERROR, errno, "Couldn't create DNS socket");
861 return -errno;
864 rc = connect(dnsSocket, &nameserverAddress, sa_size);
865 if(rc < 0) {
866 close(dnsSocket);
867 dnsSocket = -1;
868 do_log_error(L_ERROR, errno, "Couldn't create DNS \"connection\"");
869 return -errno;
873 if(!dnsSocketHandler) {
874 dnsSocketHandler =
875 registerFdEvent(dnsSocket, POLLIN, dnsReplyHandler, 0, NULL);
876 if(dnsSocketHandler == NULL) {
877 do_log(L_ERROR, "Couldn't register DNS socket handler.\n");
878 close(dnsSocket);
879 dnsSocket = -1;
880 return -ENOMEM;
884 return 1;
887 static int
888 sendQuery(DnsQueryPtr query)
890 char buf[512];
891 int buflen;
892 int rc;
893 int af[2];
894 int i;
896 if(dnsSocket < 0)
897 return -1;
899 if(dnsQueryIPv6 <= 0) {
900 af[0] = 4; af[1] = 0;
901 } else if(dnsQueryIPv6 <= 2) {
902 af[0] = 4; af[1] = 6;
903 } else {
904 af[0] = 6; af[1] = 0;
907 for(i = 0; i < 2; i++) {
908 if(af[i] == 0)
909 continue;
910 if(af[i] == 4 && query->inet4)
911 continue;
912 else if(af[i] == 6 && query->inet6)
913 continue;
915 buflen = dnsBuildQuery(query->id, buf, 0, 512, query->name, af[i]);
916 if(buflen <= 0) {
917 do_log(L_ERROR, "Couldn't build DNS query.\n");
918 return buflen;
921 rc = send(dnsSocket, buf, buflen, 0);
922 if(rc < buflen) {
923 if(rc >= 0) {
924 do_log(L_ERROR, "Couldn't send DNS query: partial send");
925 return -EAGAIN;
926 } else {
927 do_log_error(L_ERROR, errno, "Couldn't send DNS query");
928 return -errno;
932 return 1;
935 static int
936 really_do_dns(AtomPtr name, ObjectPtr object)
938 int rc;
939 DnsQueryPtr query;
940 AtomPtr message = NULL;
941 int id;
942 AtomPtr a = NULL;
944 if(a == NULL) {
945 if(name == atomLocalhost || name == atomLocalhostDot) {
946 char s[1 + sizeof(HostAddressRec)];
947 memset(s, 0, sizeof(s));
948 s[0] = DNS_A;
949 s[1] = 4;
950 s[2] = 127;
951 s[3] = 0;
952 s[4] = 0;
953 s[5] = 1;
954 a = internAtomN(s, 1 + sizeof(HostAddressRec));
955 if(a == NULL) {
956 abortObject(object, 501,
957 internAtom("Couldn't allocate address"));
958 notifyObject(object);
959 errno = ENOMEM;
960 return -1;
965 if(a == NULL) {
966 struct in_addr ina;
967 rc = inet_aton(name->string, &ina);
968 if(rc == 1) {
969 char s[1 + sizeof(HostAddressRec)];
970 memset(s, 0, sizeof(s));
971 s[0] = DNS_A;
972 s[1] = 4;
973 memcpy(s + 2, &ina, 4);
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;
984 #ifdef HAVE_IPv6
985 if(a == NULL)
986 a = rfc2732(name);
987 #endif
989 if(a) {
990 object->headers = a;
991 object->age = current_time.tv_sec;
992 object->expires = current_time.tv_sec + 240;
993 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
994 notifyObject(object);
995 return 0;
998 rc = establishDnsSocket();
999 if(rc < 0) {
1000 do_log_error(L_ERROR, -rc, "Couldn't establish DNS socket.\n");
1001 message = internAtomError(-rc, "Couldn't establish DNS socket");
1002 goto fallback;
1005 /* The id is used to speed up detecting replies to queries that
1006 are no longer current -- see dnsReplyHandler. */
1007 id = (idSeed++) & 0xFFFF;
1009 query = malloc(sizeof(DnsQueryRec));
1010 if(query == NULL) {
1011 do_log(L_ERROR, "Couldn't allocate DNS query.\n");
1012 message = internAtom("Couldn't allocate DNS query");
1013 goto fallback;
1015 query->id = id;
1016 query->inet4 = NULL;
1017 query->inet6 = NULL;
1018 query->name = name;
1019 query->time = current_time.tv_sec;
1020 query->object = retainObject(object);
1021 query->timeout = 4;
1022 query->timeout_handler = NULL;
1023 query->next = NULL;
1025 query->timeout_handler =
1026 scheduleTimeEvent(query->timeout, dnsTimeoutHandler,
1027 sizeof(query), &query);
1028 if(query->timeout_handler == NULL) {
1029 do_log(L_ERROR, "Couldn't schedule DNS timeout handler.\n");
1030 message = internAtom("Couldn't schedule DNS timeout handler");
1031 goto free_fallback;
1033 insertQuery(query);
1035 object->flags |= OBJECT_INPROGRESS;
1036 rc = sendQuery(query);
1037 if(rc < 0) {
1038 if(rc != -EWOULDBLOCK && rc != -EAGAIN && rc != -ENOBUFS) {
1039 object->flags &= ~OBJECT_INPROGRESS;
1040 message = internAtomError(-rc, "Couldn't send DNS query");
1041 goto remove_fallback;
1043 /* else let it timeout */
1045 releaseAtom(message);
1046 return 1;
1048 remove_fallback:
1049 removeQuery(query);
1050 free_fallback:
1051 releaseObject(query->object);
1052 cancelTimeEvent(query->timeout_handler);
1053 free(query);
1054 fallback:
1055 if(dnsUseGethostbyname >= 1) {
1056 releaseAtom(message);
1057 do_log(L_WARN, "Falling back on gethostbyname.\n");
1058 return really_do_gethostbyname(name, object);
1059 } else {
1060 abortObject(object, 501, message);
1061 notifyObject(object);
1062 return 1;
1066 static int
1067 dnsReplyHandler(int abort, FdEventHandlerPtr event)
1069 int fd = event->fd;
1070 char buf[2048];
1071 int len, rc;
1072 ObjectPtr object;
1073 unsigned ttl = 0;
1074 AtomPtr name, value, message = NULL;
1075 int id;
1076 int af;
1077 DnsQueryPtr query;
1078 AtomPtr cname = NULL;
1080 if(abort) {
1081 dnsSocketHandler = NULL;
1082 rc = establishDnsSocket();
1083 if(rc < 0) {
1084 do_log(L_ERROR, "Couldn't reestablish DNS socket.\n");
1085 /* At this point, we should abort all in-flight
1086 DNS requests. Oh, well, they'll timeout anyway. */
1088 return 1;
1091 len = recv(fd, buf, 2048, 0);
1092 if(len <= 0) {
1093 if(errno == EINTR || errno == EAGAIN) return 0;
1094 /* This is where we get ECONNREFUSED for an ICMP port unreachable */
1095 do_log_error(L_ERROR, errno, "DNS: recv failed");
1096 dnsGethostbynameFallback(-1, message);
1097 return 0;
1100 /* This could be a late reply to a query that timed out and was
1101 resent, a reply to a query that timed out, or a reply to an
1102 AAAA query when we already got a CNAME reply to the associated
1103 A. We filter such replies straight away, without trying to
1104 parse them. */
1105 rc = dnsReplyId(buf, 0, len, &id);
1106 if(rc < 0) {
1107 do_log(L_WARN, "Short DNS reply.\n");
1108 return 0;
1110 if(!findQuery(id, NULL)) {
1111 return 0;
1114 rc = dnsDecodeReply(buf, 0, len, &id, &name, &value, &af, &ttl);
1115 if(rc < 0) {
1116 assert(value == NULL);
1117 /* We only want to fallback on gethostbyname if we received a
1118 reply that we could not understand. What about truncated
1119 replies? */
1120 if(rc < 0) {
1121 do_log_error(L_WARN, -rc, "DNS");
1122 if(dnsUseGethostbyname >= 2 ||
1123 (dnsUseGethostbyname &&
1124 (rc != -EDNS_HOST_NOT_FOUND && rc != -EDNS_NO_RECOVERY &&
1125 rc != -EDNS_FORMAT))) {
1126 dnsGethostbynameFallback(id, message);
1127 return 0;
1128 } else {
1129 message = internAtomError(-rc, NULL);
1131 } else {
1132 assert(name != NULL && id >= 0 && af >= 0);
1136 query = findQuery(id, name);
1137 if(query == NULL) {
1138 /* Duplicate id ? */
1139 releaseAtom(value);
1140 releaseAtom(name);
1141 return 0;
1144 /* We're going to use the information in this reply. If it was an
1145 error, construct an empty atom to distinguish it from information
1146 we're still waiting for. */
1147 if(value == NULL)
1148 value = internAtom("");
1150 again:
1151 if(af == 4) {
1152 if(query->inet4 == NULL) {
1153 query->inet4 = value;
1154 query->ttl4 = current_time.tv_sec + ttl;
1155 } else
1156 releaseAtom(value);
1157 } else if(af == 6) {
1158 if(query->inet6 == NULL) {
1159 query->inet6 = value;
1160 query->ttl6 = current_time.tv_sec + ttl;
1161 } else
1162 releaseAtom(value);
1163 } else if(af == 0) {
1164 if(query->inet4 || query->inet6) {
1165 do_log(L_WARN, "Host %s has both %s and CNAME -- "
1166 "ignoring CNAME.\n", query->name->string,
1167 query->inet4 ? "A" : "AAAA");
1168 releaseAtom(value);
1169 value = internAtom("");
1170 af = query->inet4 ? 4 : 6;
1171 goto again;
1172 } else {
1173 cname = value;
1177 if(rc >= 0 && !cname &&
1178 ((dnsQueryIPv6 < 3 && query->inet4 == NULL) ||
1179 (dnsQueryIPv6 > 0 && query->inet6 == NULL)))
1180 return 0;
1182 /* This query is complete */
1184 cancelTimeEvent(query->timeout_handler);
1185 object = query->object;
1187 if(object->flags & OBJECT_INITIAL) {
1188 assert(!object->headers);
1189 if(cname) {
1190 assert(query->inet4 == NULL && query->inet6 == NULL);
1191 object->headers = cname;
1192 object->expires = current_time.tv_sec + ttl;
1193 } else if((!query->inet4 || query->inet4->length == 0) &&
1194 (!query->inet6 || query->inet6->length == 0)) {
1195 releaseAtom(query->inet4);
1196 releaseAtom(query->inet6);
1197 object->expires = current_time.tv_sec + dnsNegativeTtl;
1198 abortObject(object, 500, retainAtom(message));
1199 } else if(!query->inet4 || query->inet4->length == 0) {
1200 object->headers = query->inet6;
1201 object->expires = query->ttl6;
1202 releaseAtom(query->inet4);
1203 } else if(!query->inet6 || query->inet6->length == 0) {
1204 object->headers = query->inet4;
1205 object->expires = query->ttl4;
1206 releaseAtom(query->inet6);
1207 } else {
1208 /* need to merge results */
1209 char buf[1024];
1210 if(query->inet4->length + query->inet6->length > 1024) {
1211 releaseAtom(query->inet4);
1212 releaseAtom(query->inet6);
1213 abortObject(object, 500, internAtom("DNS reply too long"));
1214 } else {
1215 if(dnsQueryIPv6 <= 1) {
1216 memcpy(buf, query->inet4->string, query->inet4->length);
1217 memcpy(buf + query->inet4->length,
1218 query->inet6->string + 1, query->inet6->length - 1);
1219 } else {
1220 memcpy(buf, query->inet6->string, query->inet6->length);
1221 memcpy(buf + query->inet6->length,
1222 query->inet4->string + 1, query->inet4->length - 1);
1224 object->headers =
1225 internAtomN(buf,
1226 query->inet4->length +
1227 query->inet6->length - 1);
1228 if(object->headers == NULL)
1229 abortObject(object, 500,
1230 internAtom("Couldn't allocate DNS atom"));
1232 object->expires = MIN(query->ttl4, query->ttl6);
1234 object->age = current_time.tv_sec;
1235 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
1236 } else {
1237 do_log(L_WARN, "DNS object ex nihilo for %s.\n", query->name->string);
1240 removeQuery(query);
1241 free(query);
1243 releaseAtom(name);
1244 releaseAtom(message);
1245 releaseNotifyObject(object);
1246 return 0;
1249 static int
1250 dnsGethostbynameFallback(int id, AtomPtr message)
1252 DnsQueryPtr query, previous;
1253 ObjectPtr object;
1255 if(inFlightDnsQueries == NULL) {
1256 releaseAtom(message);
1257 return 1;
1260 query = NULL;
1261 if(id < 0 || inFlightDnsQueries->id == id) {
1262 previous = NULL;
1263 query = inFlightDnsQueries;
1264 } else {
1265 previous = inFlightDnsQueries;
1266 while(previous->next) {
1267 if(previous->next->id == id) {
1268 query = previous->next;
1269 break;
1272 if(!query) {
1273 previous = NULL;
1274 query = inFlightDnsQueries;
1278 if(previous == NULL) {
1279 inFlightDnsQueries = query->next;
1280 if(inFlightDnsQueries == NULL)
1281 inFlightDnsQueriesLast = NULL;
1282 } else {
1283 previous->next = query->next;
1284 if(query->next == NULL)
1285 inFlightDnsQueriesLast = NULL;
1288 object = makeObject(OBJECT_DNS, query->name->string, query->name->length,
1289 1, 0, NULL, NULL);
1290 if(!object) {
1291 do_log(L_ERROR, "Couldn't make DNS object.\n");
1292 releaseAtom(query->name);
1293 releaseAtom(message);
1294 releaseObject(query->object);
1295 cancelTimeEvent(query->timeout_handler);
1296 free(query);
1297 return -1;
1299 if(dnsUseGethostbyname >= 1) {
1300 releaseAtom(message);
1301 do_log(L_WARN, "Falling back to using system resolver.\n");
1302 really_do_gethostbyname(retainAtom(query->name), object);
1303 } else {
1304 releaseAtom(object->message);
1305 object->message = message;
1306 object->flags &= ~OBJECT_INPROGRESS;
1307 releaseNotifyObject(object);
1309 cancelTimeEvent(query->timeout_handler);
1310 releaseAtom(query->name);
1311 if(query->inet4) releaseAtom(query->inet4);
1312 if(query->inet6) releaseAtom(query->inet6);
1313 releaseObject(query->object);
1314 free(query);
1315 return 1;
1318 static int
1319 stringToLabels(char *buf, int offset, int n, char *string)
1321 int i = offset;
1322 int j = 0, k = 0;
1323 while(1) {
1324 while(string[k] != '.' && string[k] != '\0')
1325 k++;
1326 if(k >= j + 256) return -1;
1327 buf[i] = (unsigned char)(k - j); i++; if(i >= n) return -1;
1328 while(j < k) {
1329 buf[i] = string[j]; i++; j++; if(i >= n) return -1;
1331 if(string[j] == '\0') {
1332 buf[i] = '\0';
1333 i++; if(i >= n) return -1;
1334 break;
1336 j++; k++;
1339 return i;
1342 #ifdef UNALIGNED_ACCESS
1343 #define DO_NTOHS(_d, _s) _d = ntohs(*(short*)(_s));
1344 #define DO_NTOHL(_d, _s) _d = ntohl(*(unsigned*)(_s))
1345 #define DO_HTONS(_d, _s) *(short*)(_d) = htons(_s);
1346 #define DO_HTONL(_d, _s) *(unsigned*)(_d) = htonl(_s)
1347 #else
1348 #define DO_NTOHS(_d, _s) \
1349 do { short _dd; \
1350 memcpy(&(_dd), (_s), sizeof(short)); \
1351 _d = ntohs(_dd); } while(0)
1352 #define DO_NTOHL(_d, _s) \
1353 do { unsigned _dd; \
1354 memcpy(&(_dd), (_s), sizeof(unsigned)); \
1355 _d = ntohl(_dd); } while(0)
1356 #define DO_HTONS(_d, _s) \
1357 do { unsigned short _dd; \
1358 _dd = htons(_s); \
1359 memcpy((_d), &(_dd), sizeof(unsigned short)); } while(0);
1360 #define DO_HTONL(_d, _s) \
1361 do { unsigned _dd; \
1362 _dd = htonl(_s); \
1363 memcpy((_d), &(_dd), sizeof(unsigned)); } while(0);
1364 #endif
1366 static int
1367 labelsToString(char *buf, int offset, int n, char *d, int m, int *j_return)
1369 int i = offset, j, k;
1370 int ll;
1372 j = 0;
1373 while(1) {
1374 if(i >= n) return -1;
1375 ll = *(unsigned char*)&buf[i]; i++;
1376 if(ll == 0) {
1377 break;
1379 if((ll & (3 << 6)) == (3 << 6)) {
1380 /* RFC 1035, 4.1.4 */
1381 int o;
1382 if(i >= n) return -1;
1383 o = (ll & ~(3 << 6)) << 8 | *(unsigned char*)&buf[i];
1384 i++;
1385 labelsToString(buf, o, n, &d[j], m - j, &k);
1386 j += k;
1387 break;
1388 } else if((ll & (3 << 6)) == 0) {
1389 for(k = 0; k < ll; k++) {
1390 if(i >= n || j >= m) return -1;
1391 d[j++] = buf[i++];
1393 if(i >= n) return -1;
1394 if(buf[i] != '\0') {
1395 if(j >= m) return -1;
1396 d[j++] = '.';
1398 } else {
1399 return -1;
1402 *j_return = j;
1403 return i;
1406 static int
1407 dnsBuildQuery(int id, char *buf, int offset, int n, AtomPtr name, int af)
1409 int i = offset;
1410 int type;
1411 switch(af) {
1412 case 4: type = 1; break;
1413 case 6: type = 28; break;
1414 default: return EINVAL;
1417 if(i + 12 >= n) return -1;
1418 DO_HTONS(&buf[i], id); i += 2;
1419 DO_HTONS(&buf[i], 1<<8); i += 2;
1420 DO_HTONS(&buf[i], 1); i += 2;
1421 DO_HTONS(&buf[i], 0); i += 2;
1422 DO_HTONS(&buf[i], 0); i += 2;
1423 DO_HTONS(&buf[i], 0); i += 2;
1425 i = stringToLabels(buf, i, n, name->string);
1426 if(i < 0) return -ENAMETOOLONG;
1428 if(i + 4 >= n) return -ENAMETOOLONG;
1429 DO_HTONS(&buf[i], type); i += 2;
1430 DO_HTONS(&buf[i], 1); i += 2;
1431 return i;
1434 static int
1435 dnsReplyId(char *buf, int offset, int n, int *id_return)
1437 if(n - offset < 12)
1438 return -1;
1439 *id_return = ntohs(*(short*)&buf[offset]);
1440 return 1;
1443 static int
1444 dnsDecodeReply(char *buf, int offset, int n, int *id_return,
1445 AtomPtr *name_return, AtomPtr *value_return,
1446 int *af_return, unsigned *ttl_return)
1448 int i = offset, j, m;
1449 int id = -1, b23, qdcount, ancount, nscount, arcount, rdlength;
1450 int class, type;
1451 unsigned int ttl;
1452 char b[2048];
1453 int af = -1;
1454 AtomPtr name = NULL, value;
1455 char addresses[1024];
1456 int addr_index = 0;
1457 int error = EDNS_NO_ADDRESS;
1458 unsigned final_ttl = 7 * 24 * 3600;
1459 int dnserror;
1461 if(n - i < 12) {
1462 error = EDNS_INVALID;
1463 goto fail;
1466 DO_NTOHS(id, &buf[i]); i += 2;
1467 DO_NTOHS(b23, &buf[i]); i += 2;
1468 DO_NTOHS(qdcount, &buf[i]); i += 2;
1469 DO_NTOHS(ancount, &buf[i]); i += 2;
1470 DO_NTOHS(nscount, &buf[i]); i += 2;
1471 DO_NTOHS(arcount, &buf[i]); i += 2;
1473 do_log(D_DNS,
1474 "DNS id %d, b23 0x%x, qdcount %d, ancount %d, "
1475 "nscount %d, arcount %d\n",
1476 id, b23, qdcount, ancount, nscount, arcount);
1478 if((b23 & (0xF870)) != 0x8000) {
1479 do_log(L_ERROR, "Incorrect DNS reply (b23 = 0x%x).\n", b23);
1480 error = EDNS_INVALID;
1481 goto fail;
1484 dnserror = b23 & 0xF;
1486 if(b23 & 0x200) {
1487 do_log(L_WARN, "Truncated DNS reply (b23 = 0x%x).\n", b23);
1490 if(dnserror || qdcount != 1) {
1491 if(!dnserror)
1492 do_log(L_ERROR,
1493 "Unexpected number %d of DNS questions.\n", qdcount);
1494 if(dnserror == 1)
1495 error = EDNS_FORMAT;
1496 else if(dnserror == 2)
1497 error = EDNS_NO_RECOVERY;
1498 else if(dnserror == 3)
1499 error = EDNS_HOST_NOT_FOUND;
1500 else if(dnserror == 4 || dnserror == 5)
1501 error = EDNS_REFUSED;
1502 else if(dnserror == 0)
1503 error = EDNS_INVALID;
1504 else
1505 error = EUNKNOWN;
1506 goto fail;
1509 /* We do this early, so that we can return the address family to
1510 the caller in case of error. */
1511 i = labelsToString(buf, i, n, b, 2048, &m);
1512 if(i < 0) {
1513 error = EDNS_FORMAT;
1514 goto fail;
1516 DO_NTOHS(type, &buf[i]); i += 2;
1517 DO_NTOHS(class, &buf[i]); i += 2;
1519 if(type == 1)
1520 af = 4;
1521 else if(type == 28)
1522 af = 6;
1523 else {
1524 error = EDNS_FORMAT;
1525 goto fail;
1528 do_log(D_DNS, "DNS q: ");
1529 do_log_n(D_DNS, b, m);
1530 do_log(D_DNS, " (%d, %d)\n", type, class);
1531 name = internAtomLowerN(b, m);
1532 if(name == NULL) {
1533 error = ENOMEM;
1534 goto fail;
1537 if(class != 1) {
1538 error = EDNS_FORMAT;
1539 goto fail;
1542 #define PARSE_ANSWER(kind, label) \
1543 do { \
1544 i = labelsToString(buf, i, 1024, b, 2048, &m); \
1545 if(i < 0) goto label; \
1546 DO_NTOHS(type, &buf[i]); i += 2; if(i > 1024) goto label; \
1547 DO_NTOHS(class, &buf[i]); i += 2; if(i > 1024) goto label; \
1548 DO_NTOHL(ttl, &buf[i]); i += 4; if(i > 1024) goto label; \
1549 DO_NTOHS(rdlength, &buf[i]); i += 2; if(i > 1024) goto label; \
1550 do_log(D_DNS, "DNS " kind ": "); \
1551 do_log_n(D_DNS, b, m); \
1552 do_log(D_DNS, " (%d, %d): %d bytes, ttl %u\n", \
1553 type, class, rdlength, ttl); \
1554 } while(0)
1557 for(j = 0; j < ancount; j++) {
1558 PARSE_ANSWER("an", fail);
1559 if(strcasecmp_n(name->string, b, m) == 0) {
1560 if(class != 1) {
1561 do_log(D_DNS, "DNS: %s: unknown class %d.\n",
1562 name->string, class);
1563 error = EDNS_UNSUPPORTED;
1564 goto cont;
1566 if(type == 1 || type == 28) {
1567 if((type == 1 && rdlength != 4) ||
1568 (type == 28 && rdlength != 16)) {
1569 do_log(L_ERROR,
1570 "DNS: %s: unexpected length %d of %s record.\n",
1571 name->string, rdlength, type == 1 ? "A" : "AAAA");
1572 error = EDNS_INVALID;
1573 if(rdlength <= 0 || rdlength >= 32)
1574 goto fail;
1575 goto cont;
1577 if(af == 0) {
1578 do_log(L_WARN, "DNS: %s: host has both A and CNAME -- "
1579 "ignoring CNAME.\n", name->string);
1580 addr_index = 0;
1581 af = -1;
1583 if(type == 1) {
1584 if(af < 0)
1585 af = 4;
1586 else if(af == 6) {
1587 do_log(L_WARN, "Unexpected AAAA reply.\n");
1588 goto cont;
1590 } else {
1591 if(af < 0)
1592 af = 6;
1593 else if(af == 4) {
1594 do_log(L_WARN, "Unexpected A reply.\n");
1595 goto cont;
1599 if(addr_index == 0) {
1600 addresses[0] = DNS_A;
1601 addr_index++;
1602 } else {
1603 if(addr_index > 1000) {
1604 error = EDNS_INVALID;
1605 goto fail;
1608 assert(addresses[0] == DNS_A);
1609 if(final_ttl > ttl)
1610 final_ttl = ttl;
1611 memset(&addresses[addr_index], 0, sizeof(HostAddressRec));
1612 if(type == 1) {
1613 addresses[addr_index] = 4;
1614 memcpy(addresses + addr_index + 1, buf + i, 4);
1615 } else {
1616 addresses[addr_index] = 6;
1617 memcpy(addresses + addr_index + 1, buf + i, 16);
1619 addr_index += sizeof(HostAddressRec);
1620 } else if(type == 5) {
1621 int j, k;
1622 if(af != 0 && addr_index > 0) {
1623 do_log(L_WARN, "DNS: host has both CNAME and A -- "
1624 "ignoring CNAME.\n");
1625 goto cont;
1627 af = 0;
1629 if(addr_index != 0) {
1630 /* Only warn if the CNAMEs are not identical */
1631 char tmp[512]; int jj, kk;
1632 assert(addresses[0] == DNS_CNAME);
1633 jj = labelsToString(buf, i, n,
1634 tmp, 512, &kk);
1635 if(jj < 0 ||
1636 kk != strlen(addresses + 1) ||
1637 memcmp(addresses + 1, tmp, kk) != 0) {
1638 do_log(L_WARN, "DNS: "
1639 "%s: host has multiple CNAMEs -- "
1640 "ignoring subsequent.\n", name->string);
1643 goto cont;
1645 addresses[0] = DNS_CNAME;
1646 addr_index++;
1647 j = labelsToString(buf, i, n,
1648 addresses + 1, 1020, &k);
1649 if(j < 0) {
1650 addr_index = 0;
1651 error = ENAMETOOLONG;
1652 continue;
1654 addr_index = k + 1;
1655 } else {
1656 error = EDNS_NO_ADDRESS;
1657 i += rdlength;
1658 continue;
1662 cont:
1663 i += rdlength;
1666 #if (LOGGING & D_DNS)
1667 for(j = 0; j < nscount; j++) {
1668 PARSE_ANSWER("ns", nofail);
1669 i += rdlength;
1672 for(j = 0; j < arcount; j++) {
1673 PARSE_ANSWER("ar", nofail);
1674 i += rdlength;
1677 nofail:
1678 #endif
1680 #undef PARSE_ANSWER
1682 do_log(D_DNS, "DNS: %d bytes\n", addr_index);
1683 if(af < 0)
1684 goto fail;
1686 value = internAtomN(addresses, addr_index);
1687 if(value == NULL) {
1688 error = ENOMEM;
1689 goto fail;
1692 assert(af >= 0);
1693 *id_return = id;
1694 *name_return = name;
1695 *value_return = value;
1696 *af_return = af;
1697 *ttl_return = final_ttl;
1698 return 1;
1700 fail:
1701 *id_return = id;
1702 *name_return = name;
1703 *value_return = NULL;
1704 *af_return = af;
1705 return -error;
1708 #else
1710 static int
1711 really_do_dns(AtomPtr name, ObjectPtr object)
1713 abort();
1716 #endif