Make headings in config page explicit.
[polipo.git] / dns.c
blob9525e93b47c120c0588817e7c3fe49f2c2d726a8
1 /*
2 Copyright (c) 2003, 2004 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 #define nameserverAddress nameserverAddress_storage.sa
79 static DnsQueryPtr inFlightDnsQueries;
80 static DnsQueryPtr inFlightDnsQueriesLast;
81 #endif
83 static int really_do_gethostbyname(AtomPtr name, ObjectPtr object);
84 static int really_do_dns(AtomPtr name, ObjectPtr object);
86 #ifndef NO_FANCY_RESOLVER
87 static int stringToLabels(char *buf, int offset, int n, char *string);
88 static int labelsToString(char *buf, int offset, int n, char *d,
89 int m, int *j_return);
90 static int dnsBuildQuery(int id, char *buf, int offset, int n,
91 AtomPtr name, int af);
92 static int dnsReplyHandler(int abort, FdEventHandlerPtr event);
93 static int dnsReplyId(char *buf, int offset, int n, int *id_return);
94 static int dnsDecodeReply(char *buf, int offset, int n,
95 int *id_return,
96 AtomPtr *name_return, AtomPtr *value_return,
97 int *af_return, unsigned *ttl_return);
98 static int dnsHandler(int status, ConditionHandlerPtr chandler);
99 static int dnsGethostbynameFallback(int id, AtomPtr message);
100 static int sendQuery(DnsQueryPtr query);
102 static int idSeed;
103 #endif
105 #ifndef NO_FANCY_RESOLVER
106 static int
107 parseResolvConf(char *filename)
109 FILE *f;
110 char buf[512];
111 char *p, *q;
112 int n;
113 AtomPtr nameserver = NULL;
115 f = fopen(filename, "r");
116 if(f == NULL) {
117 do_log_error(L_ERROR, errno, "DNS: couldn't open %s", filename);
118 return 0;
121 while(1) {
122 p = fgets(buf, 512, f);
123 if(p == NULL)
124 break;
126 n = strlen(buf);
127 if(buf[n - 1] != '\n') {
128 int c;
129 do_log(L_WARN, "DNS: overly long line in %s -- skipping.\n",
130 filename);
131 do {
132 c = fgetc(f);
133 if(c == EOF)
134 break;
135 } while(c != '\n');
136 if(c == EOF)
137 break;
140 while(*p == ' ' || *p == '\t')
141 p++;
142 if(strcasecmp_n("nameserver", p, 10) != 0)
143 continue;
144 p += 10;
145 while(*p == ' ' || *p == '\t')
146 p++;
147 q = p;
148 while(*q == '.' || *q == ':' || digit(*q) || letter(*q))
149 q++;
150 if(*q != ' ' && *q != '\t' && *q != '\r' && *q != '\n') {
151 do_log(L_WARN, "DNS: couldn't parse line in %s -- skipping.\n",
152 filename);
153 continue;
155 nameserver = internAtomLowerN(p, q - p);
156 break;
159 fclose(f);
160 if(nameserver) {
161 dnsNameServer = nameserver;
162 return 1;
163 } else {
164 return 0;
167 #endif
169 void
170 preinitDns()
172 #ifdef HAVE_IPv6
173 int fd;
174 #endif
176 assert(sizeof(struct in_addr) == 4);
177 #ifdef HAVE_IPv6
178 assert(sizeof(struct in6_addr) == 16);
179 #endif
181 #ifndef NO_STANDARD_RESOLVER
182 CONFIG_VARIABLE(dnsGethostbynameTtl, CONFIG_TIME,
183 "TTL for gethostbyname addresses.");
184 #endif
186 #ifdef HAVE_IPv6
187 fd = socket(PF_INET6, SOCK_STREAM, 0);
188 if(fd < 0) {
189 if(errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT) {
190 dnsQueryIPv6 = 0;
191 } else {
192 do_log_error(L_WARN, errno, "DNS: couldn't create socket");
194 } else {
195 close(fd);
197 #endif
199 #ifndef NO_FANCY_RESOLVER
200 parseResolvConf("/etc/resolv.conf");
201 if(dnsNameServer == NULL || dnsNameServer->string[0] == '\0')
202 dnsNameServer = internAtom("127.0.0.1");
203 CONFIG_VARIABLE(dnsMaxTimeout, CONFIG_TIME,
204 "Max timeout for DNS queries.");
205 CONFIG_VARIABLE(dnsNegativeTtl, CONFIG_TIME,
206 "TTL for negative DNS replies with no TTL.");
207 CONFIG_VARIABLE(dnsNameServer, CONFIG_ATOM_LOWER,
208 "The name server to use.");
209 #ifndef NO_STANDARD_RESOLVER
210 CONFIG_VARIABLE(dnsUseGethostbyname, CONFIG_TETRASTATE,
211 "Use the system resolver.");
212 #endif
213 #endif
215 #ifdef HAVE_IPv6
216 CONFIG_VARIABLE(dnsQueryIPv6, CONFIG_TETRASTATE,
217 "Query for IPv6 addresses.");
218 #endif
221 void
222 initDns()
224 #ifndef NO_FANCY_RESOLVER
225 int rc;
226 struct timeval t;
227 struct sockaddr_in *sin = (struct sockaddr_in*)&nameserverAddress;
228 #ifdef HAVE_IPv6
229 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&nameserverAddress;
230 #endif
232 inFlightDnsQueries = NULL;
233 inFlightDnsQueriesLast = NULL;
235 gettimeofday(&t, NULL);
236 idSeed = t.tv_usec & 0xFFFF;
237 sin->sin_family = AF_INET;
238 sin->sin_port = htons(53);
239 rc = inet_aton(dnsNameServer->string, &sin->sin_addr);
240 #ifdef HAVE_IPv6
241 if(rc != 1) {
242 sin6->sin6_family = AF_INET6;
243 sin6->sin6_port = htons(53);
244 rc = inet_pton(AF_INET6, dnsNameServer->string, &sin6->sin6_addr);
246 #endif
247 if(rc != 1) {
248 do_log(L_ERROR, "DNS: couldn't parse name server %s.\n",
249 dnsNameServer->string);
250 exit(1);
252 #endif
256 do_gethostbyname(char *origname,
257 int count,
258 int (*handler)(int, GethostbynameRequestPtr),
259 void *data)
261 ObjectPtr object;
262 int n = strlen(origname);
263 AtomPtr name;
264 GethostbynameRequestRec request;
265 int done, rc;
267 memset(&request, 0, sizeof(request));
268 request.name = NULL;
269 request.addr = NULL;
270 request.error_message = NULL;
271 request.count = count;
272 request.handler = handler;
273 request.data = data;
275 if(n <= 0 || n > 131) {
276 if(n <= 0) {
277 request.error_message = internAtom("empty name");
278 do_log(L_ERROR, "Empty DNS name.\n");
279 done = handler(-EINVAL, &request);
280 } else {
281 request.error_message = internAtom("name too long");
282 do_log(L_ERROR, "DNS name too long.\n");
283 done = handler(-ENAMETOOLONG, &request);
285 assert(done);
286 releaseAtom(request.error_message);
287 return 1;
290 if(origname[n - 1] == '.')
291 n--;
293 name = internAtomLowerN(origname, n);
295 if(name == NULL) {
296 request.error_message = internAtom("couldn't allocate name");
297 do_log(L_ERROR, "Couldn't allocate DNS name.\n");
298 done = handler(-ENOMEM, &request);
299 assert(done);
300 releaseAtom(request.error_message);
301 return 1;
304 request.name = name;
305 request.addr = NULL;
306 request.error_message = NULL;
307 request.count = count;
308 request.object = NULL;
309 request.handler = handler;
310 request.data = data;
312 object = findObject(OBJECT_DNS, name->string, name->length);
313 if(object == NULL || objectMustRevalidate(object, NULL)) {
314 if(object) {
315 privatiseObject(object, 0);
316 releaseObject(object);
318 object = makeObject(OBJECT_DNS, name->string, name->length, 1, 0,
319 NULL, NULL);
320 if(object == NULL) {
321 request.error_message = internAtom("Couldn't allocate object");
322 do_log(L_ERROR, "Couldn't allocate DNS object.\n");
323 done = handler(-ENOMEM, &request);
324 assert(done);
325 releaseAtom(name);
326 releaseAtom(request.error_message);
327 return 1;
331 if((object->flags & (OBJECT_INITIAL | OBJECT_INPROGRESS)) ==
332 OBJECT_INITIAL) {
333 if(dnsUseGethostbyname >= 3)
334 rc = really_do_gethostbyname(name, object);
335 else
336 rc = really_do_dns(name, object);
337 if(rc < 0) {
338 assert(!(object->flags & (OBJECT_INITIAL | OBJECT_INPROGRESS)));
339 goto fail;
343 if(dnsUseGethostbyname >= 3)
344 assert(!(object->flags & OBJECT_INITIAL));
346 #ifndef NO_FANCY_RESOLVER
347 if(object->flags & OBJECT_INITIAL) {
348 ConditionHandlerPtr chandler;
349 assert(object->flags & OBJECT_INPROGRESS);
350 request.object = object;
351 chandler = conditionWait(&object->condition, dnsHandler,
352 sizeof(request), &request);
353 if(chandler == NULL) {
354 rc = ENOMEM;
355 goto fail;
357 return 1;
359 #endif
361 if(object->headers && object->headers->length > 0) {
362 if(object->headers->string[0] == DNS_A)
363 assert(((object->headers->length - 1) %
364 sizeof(HostAddressRec)) == 0);
365 else
366 assert(object->headers->string[0] == DNS_CNAME);
367 request.addr = retainAtom(object->headers);
368 } else if(object->message) {
369 request.error_message = retainAtom(object->message);
372 releaseObject(object);
374 if(request.addr && request.addr->length > 0)
375 done = handler(1, &request);
376 else
377 done = handler(-EDNS_HOST_NOT_FOUND, &request);
378 assert(done);
380 releaseAtom(request.addr); request.addr = NULL;
381 releaseAtom(request.name); request.name = NULL;
382 releaseAtom(request.error_message); request.error_message = NULL;
383 return 1;
385 fail:
386 releaseNotifyObject(object);
387 done = handler(-errno, &request);
388 assert(done);
389 releaseAtom(name);
390 return 1;
393 static int
394 dnsDelayedErrorNotifyHandler(TimeEventHandlerPtr event)
396 int done;
397 GethostbynameRequestRec request =
398 *(GethostbynameRequestPtr)event->data;
399 done = request.handler(-EDNS_HOST_NOT_FOUND, &request);
400 assert(done);
401 releaseAtom(request.name); request.name = NULL;
402 releaseAtom(request.addr); request.addr = NULL;
403 releaseAtom(request.error_message); request.error_message = NULL;
404 return 1;
407 static int
408 dnsDelayedDoneNotifyHandler(TimeEventHandlerPtr event)
410 int done;
411 GethostbynameRequestRec request = *(GethostbynameRequestPtr)event->data;
412 done = request.handler(1, &request);
413 assert(done);
414 releaseAtom(request.name); request.name = NULL;
415 releaseAtom(request.addr); request.addr = NULL;
416 releaseAtom(request.error_message); request.error_message = NULL;
417 return 1;
420 static int
421 dnsDelayedNotify(int error, GethostbynameRequestPtr request)
423 TimeEventHandlerPtr handler;
425 if(error)
426 handler = scheduleTimeEvent(0,
427 dnsDelayedErrorNotifyHandler,
428 sizeof(*request), request);
429 else
430 handler = scheduleTimeEvent(0,
431 dnsDelayedDoneNotifyHandler,
432 sizeof(*request), request);
433 if(handler == NULL) {
434 do_log(L_ERROR, "Couldn't schedule DNS notification.\n");
435 return -1;
437 return 1;
440 #ifdef HAVE_IPv6
441 AtomPtr
442 rfc2732(AtomPtr name)
444 char buf[38];
445 int rc;
446 AtomPtr a = NULL;
448 if(name->length < 38 &&
449 name->string[0] == '[' && name->string[name->length - 1] == ']') {
450 struct in6_addr in6a;
451 memcpy(buf, name->string + 1, name->length - 2);
452 buf[name->length - 2] = '\0';
453 rc = inet_pton(AF_INET6, buf, &in6a);
454 if(rc == 1) {
455 char s[1 + sizeof(HostAddressRec)];
456 memset(s, 0, sizeof(s));
457 s[0] = DNS_A;
458 s[1] = 6;
459 memcpy(s + 2, &in6a, 16);
460 a = internAtomN(s, 1 + sizeof(HostAddressRec));
461 if(a == NULL)
462 return NULL;
465 return a;
468 /* Used for sorting host addresses depending on the value of dnsQueryIPv6 */
470 compare_hostaddr(const void *av, const void *bv)
472 const HostAddressRec *a = av, *b = bv;
473 int r;
474 if(a->af == 4) {
475 if(b->af == 4)
476 r = 0;
477 else
478 r = -1;
479 } else {
480 if(b->af == 6)
481 r = 0;
482 else
483 r = 1;
485 if(dnsQueryIPv6 >= 2)
486 return -r;
487 else
488 return r;
491 #ifndef NO_STANDARD_RESOLVER
492 static int
493 really_do_gethostbyname(AtomPtr name, ObjectPtr object)
495 struct addrinfo *ai, *entry, hints;
496 int rc;
497 int error, i;
498 char buf[1024];
499 AtomPtr a;
501 a = rfc2732(name);
502 if(a) {
503 object->headers = a;
504 object->age = current_time.tv_sec;
505 object->expires = current_time.tv_sec + 240;
506 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
507 notifyObject(object);
508 return 0;
511 memset(&hints, 0, sizeof(hints));
512 hints.ai_protocol = IPPROTO_TCP;
513 if(dnsQueryIPv6 <= 0)
514 hints.ai_family = AF_INET;
515 else if(dnsQueryIPv6 >= 3)
516 hints.ai_family = AF_INET6;
518 rc = getaddrinfo(name->string, NULL, NULL, &ai);
520 switch(rc) {
521 case 0: error = 0; break;
522 case EAI_FAMILY:
523 #ifdef EAI_ADDRFAMILY
524 case EAI_ADDRFAMILY:
525 #endif
526 case EAI_SOCKTYPE:
527 error = EAFNOSUPPORT; break;
528 case EAI_BADFLAGS: error = EINVAL; break;
529 case EAI_SERVICE: error = EDNS_NO_RECOVERY; break;
530 #ifdef EAI_NONAME
531 case EAI_NONAME:
532 #endif
533 #ifdef EAI_NODATA
534 case EAI_NODATA:
535 #endif
536 error = EDNS_NO_ADDRESS; break;
537 case EAI_FAIL: error = EDNS_NO_RECOVERY; break;
538 case EAI_AGAIN: error = EDNS_TRY_AGAIN; break;
539 #ifdef EAI_MEMORY
540 case EAI_MEMORY: error = ENOMEM; break;
541 #endif
542 case EAI_SYSTEM: error = errno; break;
543 default: error = EUNKNOWN;
546 if(error == EDNS_NO_ADDRESS) {
547 object->headers = NULL;
548 object->age = current_time.tv_sec;
549 object->expires = current_time.tv_sec + dnsNegativeTtl;
550 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
551 notifyObject(object);
552 return 0;
553 } else if(error) {
554 do_log_error(L_ERROR, error, "Getaddrinfo failed");
555 object->flags &= ~OBJECT_INPROGRESS;
556 abortObject(object, 404,
557 internAtomError(error, "Getaddrinfo failed"));
558 notifyObject(object);
559 return 0;
562 entry = ai;
563 buf[0] = DNS_A;
564 i = 0;
565 while(entry) {
566 HostAddressRec host;
567 int host_valid = 0;
568 if(entry->ai_family == AF_INET && entry->ai_protocol == IPPROTO_TCP) {
569 if(dnsQueryIPv6 < 3) {
570 host.af = 4;
571 memset(host.data, 0, sizeof(host.data));
572 memcpy(&host.data,
573 &((struct sockaddr_in*)entry->ai_addr)->sin_addr,
575 host_valid = 1;
577 } else if(entry->ai_family == AF_INET6 &&
578 entry->ai_protocol == IPPROTO_TCP) {
579 if(dnsQueryIPv6 > 0) {
580 host.af = 6;
581 memset(&host.data, 0, sizeof(host.data));
582 memcpy(&host.data,
583 &((struct sockaddr_in6*)entry->ai_addr)->sin6_addr,
584 16);
585 host_valid = 1;
588 if(host_valid) {
589 if(i >= 1024 / sizeof(HostAddressRec) - 2) {
590 do_log(L_ERROR, "Too many addresses for host %s\n",
591 name->string);
592 break;
594 memcpy(buf + 1 + i * sizeof(HostAddressRec),
595 &host, sizeof(HostAddressRec));
596 i++;
598 entry = entry->ai_next;
600 freeaddrinfo(ai);
601 if(i == 0) {
602 do_log(L_ERROR, "Getaddrinfo returned no useful addresses\n");
603 object->flags &= ~OBJECT_INPROGRESS;
604 abortObject(object, 404,
605 internAtom("Getaddrinfo returned no useful addresses"));
606 notifyObject(object);
607 return 0;
610 if(1 <= dnsQueryIPv6 && dnsQueryIPv6 <= 2)
611 qsort(buf + 1, i, sizeof(HostAddressRec), compare_hostaddr);
613 a = internAtomN(buf, 1 + i * sizeof(HostAddressRec));
614 if(a == NULL) {
615 object->flags &= ~OBJECT_INPROGRESS;
616 abortObject(object, 501, internAtom("Couldn't allocate address"));
617 notifyObject(object);
618 return 0;
620 object->headers = a;
621 object->age = current_time.tv_sec;
622 object->expires = current_time.tv_sec + dnsGethostbynameTtl;
623 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
624 notifyObject(object);
625 return 0;
627 #endif
629 #else
631 #ifndef NO_STANDARD_RESOLVER
632 static int
633 really_do_gethostbyname(AtomPtr name, ObjectPtr object)
635 struct hostent *host;
636 char *s;
637 AtomPtr a;
638 int i, j;
639 int error;
641 host = gethostbyname(name->string);
642 if(host == NULL) {
643 switch(h_errno) {
644 case HOST_NOT_FOUND: error = EDNS_HOST_NOT_FOUND; break;
645 case NO_ADDRESS: error = EDNS_NO_ADDRESS; break;
646 case NO_RECOVERY: error = EDNS_NO_RECOVERY; break;
647 case TRY_AGAIN: error = EDNS_TRY_AGAIN; break;
648 default: error = EUNKNOWN; break;
650 if(error == EDNS_HOST_NOT_FOUND) {
651 object->headers = NULL;
652 object->age = current_time.tv_sec;
653 object->expires = current_time.tv_sec + dnsNegativeTtl;
654 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
655 object->flags &= ~OBJECT_INPROGRESS;
656 notifyObject(object);
657 return 0;
658 } else {
659 do_log_error(L_ERROR, error, "Gethostbyname failed");
660 abortObject(object, 404,
661 internAtomError(error, "Gethostbyname failed"));
662 object->flags &= ~OBJECT_INPROGRESS;
663 notifyObject(object);
664 return 0;
667 if(host->h_addrtype != AF_INET) {
668 do_log(L_ERROR, "Address is not AF_INET.\n");
669 object->flags &= ~OBJECT_INPROGRESS;
670 abortObject(object, 404, internAtom("Address is not AF_INET"));
671 notifyObject(object);
672 return -1;
674 if(host->h_length != sizeof(struct in_addr)) {
675 do_log(L_ERROR, "Address size inconsistent.\n");
676 object->flags &= ~OBJECT_INPROGRESS;
677 abortObject(object, 404, internAtom("Address size inconsistent"));
678 notifyObject(object);
679 return 0;
681 i = 0;
682 while(host->h_addr_list[i] != NULL) i++;
683 s = malloc(1 + i * sizeof(HostAddressRec));
684 if(s == NULL) {
685 a = NULL;
686 } else {
687 memset(s, 0, 1 + i * sizeof(HostAddressRec));
688 s[0] = DNS_A;
689 for(j = 0; j < i; j++) {
690 s[j * sizeof(HostAddressRec) + 1] = 4;
691 memcpy(&s[j * sizeof(HostAddressRec) + 2], host->h_addr_list[j],
692 sizeof(struct in_addr));
694 a = internAtomN(s, i * sizeof(HostAddressRec) + 1);
695 free(s);
697 if(!a) {
698 object->flags &= ~OBJECT_INPROGRESS;
699 abortObject(object, 501, internAtom("Couldn't allocate address"));
700 notifyObject(object);
701 return 0;
703 object->headers = a;
704 object->age = current_time.tv_sec;
705 object->expires = current_time.tv_sec + dnsGethostbynameTtl;
706 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
707 notifyObject(object);
708 return 0;
711 #endif
713 #endif
715 #ifdef NO_STANDARD_RESOLVER
716 static int
717 really_do_gethostbyname(AtomPtr name, ObjectPtr object)
719 abort();
721 #endif
723 #ifndef NO_FANCY_RESOLVER
725 static int dnsSocket = -1;
726 static FdEventHandlerPtr dnsSocketHandler = NULL;
728 static int
729 dnsHandler(int status, ConditionHandlerPtr chandler)
731 GethostbynameRequestRec request = *(GethostbynameRequestPtr)chandler->data;
732 ObjectPtr object = request.object;
734 assert(!(object->flags & OBJECT_INPROGRESS));
736 if(object->headers) {
737 request.addr = retainAtom(object->headers);
738 dnsDelayedNotify(0, &request);
739 } else {
740 if(object->message)
741 request.error_message = retainAtom(object->message);
742 dnsDelayedNotify(1, &request);
744 releaseObject(object);
745 return 1;
748 static void
749 removeQuery(DnsQueryPtr query)
751 DnsQueryPtr previous;
752 if(query == inFlightDnsQueries) {
753 inFlightDnsQueries = query->next;
754 if(inFlightDnsQueries == NULL)
755 inFlightDnsQueriesLast = NULL;
756 } else {
757 previous = inFlightDnsQueries;
758 while(previous->next) {
759 if(previous->next == query)
760 break;
761 previous = previous->next;
763 assert(previous->next != NULL);
764 previous->next = query->next;
765 if(previous->next == NULL)
766 inFlightDnsQueriesLast = previous;
770 static void
771 insertQuery(DnsQueryPtr query)
773 if(inFlightDnsQueriesLast)
774 inFlightDnsQueriesLast->next = query;
775 else
776 inFlightDnsQueries = query;
777 inFlightDnsQueriesLast = query;
780 static DnsQueryPtr
781 findQuery(int id, AtomPtr name)
783 DnsQueryPtr query;
784 query = inFlightDnsQueries;
785 while(query) {
786 if(query->id == id && (name == NULL || query->name == name))
787 return query;
788 query = query->next;
790 return NULL;
793 static int
794 dnsTimeoutHandler(TimeEventHandlerPtr event)
796 DnsQueryPtr query = *(DnsQueryPtr*)event->data;
797 ObjectPtr object = query->object;
798 int rc;
800 query->timeout = MAX(10, query->timeout * 2);
802 if(query->timeout > dnsMaxTimeout) {
803 removeQuery(query);
804 abortObject(object, 501, internAtom("Timeout"));
805 goto fail;
806 } else {
807 query->timeout_handler =
808 scheduleTimeEvent(query->timeout, dnsTimeoutHandler,
809 sizeof(query), &query);
810 if(query->timeout_handler == NULL) {
811 do_log(L_ERROR, "Couldn't schedule DNS timeout handler.\n");
812 abortObject(object, 501,
813 internAtom("Couldn't schedule DNS timeout handler"));
814 goto fail;
816 rc = sendQuery(query);
817 if(rc < 0) {
818 do_log(L_ERROR, "Couldn't send DNS query.\n");
819 if(rc != -EWOULDBLOCK && rc != -EAGAIN && rc != -ENOBUFS) {
820 abortObject(object, 501,
821 internAtom("Couldn't send DNS query"));
822 goto fail;
824 /* else let it timeout */
826 return 1;
829 fail:
830 object->flags &= ~OBJECT_INPROGRESS;
831 if(query->inet4) releaseAtom(query->inet4);
832 if(query->inet6) releaseAtom(query->inet6);
833 free(query);
834 releaseNotifyObject(object);
835 return 1;
838 static int
839 establishDnsSocket()
841 int rc;
842 #ifdef HAVE_IPv6
843 int inet6 = (nameserverAddress.sa_family == AF_INET6);
844 int pf = inet6 ? PF_INET6 : PF_INET;
845 int sa_size =
846 inet6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
847 #else
848 int pf = PF_INET;
849 int sa_size = sizeof(struct sockaddr_in);
850 #endif
852 if(dnsSocket < 0) {
853 assert(!dnsSocketHandler);
854 dnsSocket = socket(pf, SOCK_DGRAM, 0);
855 if(dnsSocket < 0) {
856 do_log_error(L_ERROR, errno, "Couldn't create DNS socket");
857 return -errno;
860 rc = connect(dnsSocket, &nameserverAddress, sa_size);
861 if(rc < 0) {
862 close(dnsSocket);
863 dnsSocket = -1;
864 do_log_error(L_ERROR, errno, "Couldn't create DNS \"connection\"");
865 return -errno;
869 if(!dnsSocketHandler) {
870 dnsSocketHandler =
871 registerFdEvent(dnsSocket, POLLIN, dnsReplyHandler, 0, NULL);
872 if(dnsSocketHandler == NULL) {
873 do_log(L_ERROR, "Couldn't register DNS socket handler.\n");
874 close(dnsSocket);
875 dnsSocket = -1;
876 return -ENOMEM;
880 return 1;
883 static int
884 sendQuery(DnsQueryPtr query)
886 char buf[512];
887 int buflen;
888 int rc;
889 int af[2];
890 int i;
892 if(dnsSocket < 0)
893 return -1;
895 if(dnsQueryIPv6 <= 0) {
896 af[0] = 4; af[1] = 0;
897 } else if(dnsQueryIPv6 <= 2) {
898 af[0] = 4; af[1] = 6;
899 } else {
900 af[0] = 6; af[1] = 0;
903 for(i = 0; i < 2; i++) {
904 if(af[i] == 0)
905 continue;
906 if(af[i] == 4 && query->inet4)
907 continue;
908 else if(af[i] == 6 && query->inet6)
909 continue;
911 buflen = dnsBuildQuery(query->id, buf, 0, 512, query->name, af[i]);
912 if(buflen <= 0) {
913 do_log(L_ERROR, "Couldn't build DNS query.\n");
914 return buflen;
917 rc = send(dnsSocket, buf, buflen, 0);
918 if(rc < buflen) {
919 if(rc >= 0) {
920 do_log(L_ERROR, "Couldn't send DNS query: partial send");
921 return -EAGAIN;
922 } else {
923 do_log_error(L_ERROR, errno, "Couldn't send DNS query");
924 return -errno;
928 return 1;
931 static int
932 really_do_dns(AtomPtr name, ObjectPtr object)
934 int rc;
935 DnsQueryPtr query;
936 AtomPtr message = NULL;
937 int id;
938 AtomPtr a = NULL;
940 if(a == NULL) {
941 struct in_addr ina;
942 rc = inet_aton(name->string, &ina);
943 if(rc == 1) {
944 char s[1 + sizeof(HostAddressRec)];
945 memset(s, 0, sizeof(s));
946 s[0] = DNS_A;
947 s[1] = 4;
948 memcpy(s + 2, &ina, 4);
949 a = internAtomN(s, 1 + sizeof(HostAddressRec));
950 if(a == NULL) {
951 abortObject(object, 501,
952 internAtom("Couldn't allocate address"));
953 notifyObject(object);
954 errno = ENOMEM;
955 return -1;
959 #ifdef HAVE_IPv6
960 if(a == NULL)
961 a = rfc2732(name);
962 #endif
964 if(a) {
965 object->headers = a;
966 object->age = current_time.tv_sec;
967 object->expires = current_time.tv_sec + 240;
968 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
969 notifyObject(object);
970 return 0;
973 rc = establishDnsSocket();
974 if(rc < 0) {
975 do_log_error(L_ERROR, -rc, "Couldn't establish DNS socket.\n");
976 message = internAtomError(-rc, "Couldn't establish DNS socket");
977 goto fallback;
980 /* The id is used to speed up detecting replies to queries that
981 are no longer current -- see dnsReplyHandler. */
982 id = (idSeed++) & 0xFFFF;
984 query = malloc(sizeof(DnsQueryRec));
985 if(query == NULL) {
986 do_log(L_ERROR, "Couldn't allocate DNS query.\n");
987 message = internAtom("Couldn't allocate DNS query");
988 goto fallback;
990 query->id = id;
991 query->inet4 = NULL;
992 query->inet6 = NULL;
993 query->name = name;
994 query->time = current_time.tv_sec;
995 query->object = retainObject(object);
996 query->timeout = 4;
997 query->timeout_handler = NULL;
998 query->next = NULL;
1000 query->timeout_handler =
1001 scheduleTimeEvent(query->timeout, dnsTimeoutHandler,
1002 sizeof(query), &query);
1003 if(query->timeout_handler == NULL) {
1004 do_log(L_ERROR, "Couldn't schedule DNS timeout handler.\n");
1005 message = internAtom("Couldn't schedule DNS timeout handler");
1006 goto free_fallback;
1008 insertQuery(query);
1010 object->flags |= OBJECT_INPROGRESS;
1011 rc = sendQuery(query);
1012 if(rc < 0) {
1013 if(rc != -EWOULDBLOCK && rc != -EAGAIN && rc != -ENOBUFS) {
1014 object->flags &= ~OBJECT_INPROGRESS;
1015 message = internAtomError(-rc, "Couldn't send DNS query");
1016 goto remove_fallback;
1018 /* else let it timeout */
1020 releaseAtom(message);
1021 return 1;
1023 remove_fallback:
1024 removeQuery(query);
1025 free_fallback:
1026 releaseObject(query->object);
1027 cancelTimeEvent(query->timeout_handler);
1028 free(query);
1029 fallback:
1030 if(dnsUseGethostbyname >= 1) {
1031 releaseAtom(message);
1032 do_log(L_WARN, "Falling back on gethostbyname.\n");
1033 return really_do_gethostbyname(name, object);
1034 } else {
1035 abortObject(object, 501, message);
1036 notifyObject(object);
1037 return 1;
1041 static int
1042 dnsReplyHandler(int abort, FdEventHandlerPtr event)
1044 int fd = event->fd;
1045 char buf[2048];
1046 int len, rc;
1047 ObjectPtr object;
1048 unsigned ttl;
1049 AtomPtr name, value, message = NULL;
1050 int id;
1051 int af;
1052 DnsQueryPtr query;
1053 AtomPtr cname = NULL;
1055 if(abort) {
1056 dnsSocketHandler = NULL;
1057 rc = establishDnsSocket();
1058 if(rc < 0) {
1059 do_log(L_ERROR, "Couldn't reestablish DNS socket.\n");
1060 /* At this point, we should abort all in-flight
1061 DNS requests. Oh, well, they'll timeout anyway. */
1063 return 1;
1066 len = recv(fd, buf, 2048, 0);
1067 if(len <= 0) {
1068 if(errno == EINTR || errno == EAGAIN) return 0;
1069 /* This is where we get ECONNREFUSED for an ICMP port unreachable */
1070 do_log_error(L_ERROR, errno, "DNS: recv failed");
1071 dnsGethostbynameFallback(-1, message);
1072 return 0;
1075 /* This could be a late reply to a query that timed out and was
1076 resent, a reply to a query that timed out, or a reply to an
1077 AAAA query when we already got a CNAME reply to the associated
1078 A. We filter such replies straight away, without trying to
1079 parse them. */
1080 rc = dnsReplyId(buf, 0, len, &id);
1081 if(rc < 0) {
1082 do_log(L_WARN, "Short DNS reply.\n");
1083 return 0;
1085 if(!findQuery(id, NULL)) {
1086 return 0;
1089 rc = dnsDecodeReply(buf, 0, len, &id, &name, &value, &af, &ttl);
1090 if(rc < 0) {
1091 assert(value == NULL);
1092 /* We only want to fallback on gethostbyname if we received a
1093 reply that we could not understand. What about truncated
1094 replies? */
1095 if(rc < 0) {
1096 do_log_error(L_WARN, -rc, "DNS: reply failure");
1097 if(dnsUseGethostbyname >= 2 ||
1098 (dnsUseGethostbyname &&
1099 (rc != -EDNS_HOST_NOT_FOUND && rc != -EDNS_NO_RECOVERY &&
1100 rc != -EDNS_FORMAT))) {
1101 dnsGethostbynameFallback(id, message);
1102 return 0;
1103 } else {
1104 message = internAtomError(-rc, "DNS reply failure");
1106 } else {
1107 assert(name != NULL && id >= 0 && af >= 0);
1111 query = findQuery(id, name);
1112 if(query == NULL) {
1113 /* Duplicate id ? */
1114 releaseAtom(value);
1115 releaseAtom(name);
1116 return 0;
1119 /* We're going to use the information in this reply. If it was an
1120 error, construct an empty atom to distinguish it from information
1121 we're still waiting for. */
1122 if(value == NULL)
1123 value = internAtom("");
1125 if(af == 4) {
1126 if(query->inet4 == NULL) {
1127 query->inet4 = value;
1128 query->ttl4 = current_time.tv_sec + ttl;
1129 } else
1130 releaseAtom(value);
1131 } else if(af == 6) {
1132 if(query->inet6 == NULL) {
1133 query->inet6 = value;
1134 query->ttl6 = current_time.tv_sec + ttl;
1135 } else
1136 releaseAtom(value);
1137 } else if(af == 0) {
1138 if(query->inet4 || query->inet6) {
1139 do_log(L_WARN, "Host %s has both %s and CNAME -- "
1140 "ignoring CNAME.\n", query->name->string,
1141 query->inet4 ? "A" : "AAAA");
1142 releaseAtom(value);
1143 } else {
1144 cname = value;
1148 if(rc >= 0 && !cname &&
1149 ((dnsQueryIPv6 < 3 && query->inet4 == NULL) ||
1150 (dnsQueryIPv6 > 0 && query->inet6 == NULL)))
1151 return 0;
1153 /* This query is complete */
1155 cancelTimeEvent(query->timeout_handler);
1156 object = query->object;
1158 if(object->flags & OBJECT_INITIAL) {
1159 assert(!object->headers);
1160 if(cname) {
1161 assert(query->inet4 == NULL && query->inet6 == NULL);
1162 object->headers = cname;
1163 object->expires = current_time.tv_sec + ttl;
1164 } else if((!query->inet4 || query->inet4->length == 0) &&
1165 (!query->inet6 || query->inet6->length == 0)) {
1166 releaseAtom(query->inet4);
1167 releaseAtom(query->inet6);
1168 object->expires = current_time.tv_sec + dnsNegativeTtl;
1169 abortObject(object, 500, retainAtom(message));
1170 } else if(!query->inet4 || query->inet4->length == 0) {
1171 object->headers = query->inet6;
1172 object->expires = query->ttl6;
1173 releaseAtom(query->inet4);
1174 } else if(!query->inet6 || query->inet6->length == 0) {
1175 object->headers = query->inet4;
1176 object->expires = query->ttl4;
1177 releaseAtom(query->inet6);
1178 } else {
1179 /* need to merge results */
1180 char buf[1024];
1181 if(query->inet4->length + query->inet6->length > 1024) {
1182 releaseAtom(query->inet4);
1183 releaseAtom(query->inet6);
1184 abortObject(object, 500, internAtom("DNS reply too long"));
1185 } else {
1186 if(dnsQueryIPv6 <= 1) {
1187 memcpy(buf, query->inet4->string, query->inet4->length);
1188 memcpy(buf + query->inet4->length,
1189 query->inet6->string + 1, query->inet6->length - 1);
1190 } else {
1191 memcpy(buf, query->inet6->string, query->inet6->length);
1192 memcpy(buf + query->inet6->length,
1193 query->inet4->string + 1, query->inet4->length - 1);
1195 object->headers =
1196 internAtomN(buf,
1197 query->inet4->length +
1198 query->inet6->length - 1);
1199 if(object->headers == NULL)
1200 abortObject(object, 500,
1201 internAtom("Couldn't allocate DNS atom"));
1203 object->expires = MIN(query->ttl4, query->ttl6);
1205 object->age = current_time.tv_sec;
1206 object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
1207 } else {
1208 do_log(L_WARN, "DNS object ex nihilo for %s.\n", query->name->string);
1211 removeQuery(query);
1212 free(query);
1214 releaseAtom(name);
1215 releaseAtom(message);
1216 releaseNotifyObject(object);
1217 return 0;
1220 static int
1221 dnsGethostbynameFallback(int id, AtomPtr message)
1223 DnsQueryPtr query, previous;
1224 ObjectPtr object;
1226 if(inFlightDnsQueries == NULL) {
1227 releaseAtom(message);
1228 return 1;
1231 query = NULL;
1232 if(id < 0 || inFlightDnsQueries->id == id) {
1233 previous = NULL;
1234 query = inFlightDnsQueries;
1235 } else {
1236 previous = inFlightDnsQueries;
1237 while(previous->next) {
1238 if(previous->next->id == id) {
1239 query = previous->next;
1240 break;
1243 if(!query) {
1244 previous = NULL;
1245 query = inFlightDnsQueries;
1249 if(previous == NULL) {
1250 inFlightDnsQueries = query->next;
1251 if(inFlightDnsQueries == NULL)
1252 inFlightDnsQueriesLast = NULL;
1253 } else {
1254 previous->next = query->next;
1255 if(query->next == NULL)
1256 inFlightDnsQueriesLast = NULL;
1259 object = makeObject(OBJECT_DNS, query->name->string, query->name->length,
1260 1, 0, NULL, NULL);
1261 if(!object) {
1262 do_log(L_ERROR, "Couldn't make DNS object.\n");
1263 releaseAtom(query->name);
1264 releaseAtom(message);
1265 releaseObject(query->object);
1266 cancelTimeEvent(query->timeout_handler);
1267 free(query);
1268 return -1;
1270 if(dnsUseGethostbyname >= 1) {
1271 releaseAtom(message);
1272 do_log(L_WARN, "Falling back to using system resolver.\n");
1273 really_do_gethostbyname(retainAtom(query->name), object);
1274 } else {
1275 releaseAtom(object->message);
1276 object->message = message;
1277 object->flags &= ~OBJECT_INPROGRESS;
1278 releaseNotifyObject(object);
1280 cancelTimeEvent(query->timeout_handler);
1281 releaseAtom(query->name);
1282 if(query->inet4) releaseAtom(query->inet4);
1283 if(query->inet6) releaseAtom(query->inet6);
1284 releaseObject(query->object);
1285 free(query);
1286 return 1;
1289 static int
1290 stringToLabels(char *buf, int offset, int n, char *string)
1292 int i = offset;
1293 int j = 0, k = 0;
1294 while(1) {
1295 while(string[k] != '.' && string[k] != '\0')
1296 k++;
1297 if(k >= j + 256) return -1;
1298 buf[i] = (unsigned char)(k - j); i++; if(i >= n) return -1;
1299 while(j < k) {
1300 buf[i] = string[j]; i++; j++; if(i >= n) return -1;
1302 if(string[j] == '\0') {
1303 buf[i] = '\0';
1304 i++; if(i >= n) return -1;
1305 break;
1307 j++; k++;
1310 return i;
1313 #ifdef UNALIGNED_ACCESS
1314 #define DO_NTOHS(_d, _s) _d = ntohs(*(short*)(_s));
1315 #define DO_NTOHL(_d, _s) _d = ntohl(*(unsigned*)(_s))
1316 #define DO_HTONS(_d, _s) *(short*)(_d) = htons(_s);
1317 #define DO_HTONL(_d, _s) *(short*)(_d) = htonl(_s)
1318 #else
1319 #define DO_NTOHS(_d, _s) \
1320 do { short _dd; \
1321 memcpy(&(_dd), (_s), sizeof(short)); \
1322 _d = ntohs(_dd); } while(0)
1323 #define DO_NTOHL(_d, _s) \
1324 do { unsigned _dd; \
1325 memcpy(&(_dd), (_s), sizeof(unsigned)); \
1326 _d = ntohl(_dd); } while(0)
1327 #define DO_HTONS(_d, _s) \
1328 do { unsigned short _dd; \
1329 _dd = htons(_s); \
1330 memcpy((_d), &(_dd), sizeof(unsigned short)); } while(0);
1331 #define DO_HTONL(_d, _s) \
1332 do { unsigned long _dd; \
1333 _dd = htonl(_s); \
1334 memcpy((_d), &(_dd), sizeof(unsigned long)); } while(0);
1335 #endif
1337 static int
1338 labelsToString(char *buf, int offset, int n, char *d, int m, int *j_return)
1340 int i = offset, j, k;
1341 int ll;
1343 j = 0;
1344 while(1) {
1345 if(i >= n) return -1;
1346 ll = *(unsigned char*)&buf[i]; i++;
1347 if(ll == 0) {
1348 break;
1350 if((ll & (3 << 6)) == (3 << 6)) {
1351 /* RFC 1035, 4.1.4 */
1352 int o;
1353 if(i >= n) return -1;
1354 o = (ll & ~(3 << 6)) << 8 | *(unsigned char*)&buf[i];
1355 i++;
1356 labelsToString(buf, o, n, &d[j], m - j, &k);
1357 j += k;
1358 break;
1359 } else if((ll & (3 << 6)) == 0) {
1360 for(k = 0; k < ll; k++) {
1361 if(i >= n || j >= m) return -1;
1362 d[j++] = buf[i++];
1364 if(i >= n) return -1;
1365 if(buf[i] != '\0') {
1366 if(j >= m) return -1;
1367 d[j++] = '.';
1369 } else {
1370 return -1;
1373 *j_return = j;
1374 return i;
1377 static int
1378 dnsBuildQuery(int id, char *buf, int offset, int n, AtomPtr name, int af)
1380 int i = offset;
1381 int type;
1382 switch(af) {
1383 case 4: type = 1; break;
1384 case 6: type = 28; break;
1385 default: return EINVAL;
1388 if(i + 12 >= n) return -1;
1389 DO_HTONS(&buf[i], id); i += 2;
1390 DO_HTONS(&buf[i], 1<<8); i += 2;
1391 DO_HTONS(&buf[i], 1); i += 2;
1392 DO_HTONS(&buf[i], 0); i += 2;
1393 DO_HTONS(&buf[i], 0); i += 2;
1394 DO_HTONS(&buf[i], 0); i += 2;
1396 i = stringToLabels(buf, i, n, name->string);
1397 if(i < 0) return -ENAMETOOLONG;
1399 if(i + 4 >= n) return -ENAMETOOLONG;
1400 DO_HTONS(&buf[i], type); i += 2;
1401 DO_HTONS(&buf[i], 1); i += 2;
1402 return i;
1405 static int
1406 dnsReplyId(char *buf, int offset, int n, int *id_return)
1408 if(n - offset < 12)
1409 return -1;
1410 *id_return = ntohs(*(short*)&buf[offset]);
1411 return 1;
1414 static int
1415 dnsDecodeReply(char *buf, int offset, int n, int *id_return,
1416 AtomPtr *name_return, AtomPtr *value_return,
1417 int *af_return, unsigned *ttl_return)
1419 int i = offset, j, m;
1420 int id = -1, b23, qdcount, ancount, nscount, arcount, rdlength;
1421 int class, type;
1422 unsigned int ttl;
1423 char b[2048];
1424 int af = -1;
1425 AtomPtr name = NULL, value;
1426 char addresses[1024];
1427 int addr_index = 0;
1428 int error = EDNS_NO_ADDRESS;
1429 unsigned final_ttl = 7 * 24 * 3600;
1430 int dnserror;
1432 if(n - i < 12) {
1433 error = EDNS_INVALID;
1434 goto fail;
1437 DO_NTOHS(id, &buf[i]); i += 2;
1438 DO_NTOHS(b23, &buf[i]); i += 2;
1439 DO_NTOHS(qdcount, &buf[i]); i += 2;
1440 DO_NTOHS(ancount, &buf[i]); i += 2;
1441 DO_NTOHS(nscount, &buf[i]); i += 2;
1442 DO_NTOHS(arcount, &buf[i]); i += 2;
1444 do_log(D_DNS,
1445 "DNS id %d, b23 0x%x, qdcount %d, ancount %d, "
1446 "nscount %d, arcount %d\n",
1447 id, b23, qdcount, ancount, nscount, arcount);
1449 if((b23 & (0xF870)) != 0x8000) {
1450 do_log(L_ERROR, "Incorrect DNS reply (b23 = 0x%x).\n", b23);
1451 error = EDNS_INVALID;
1452 goto fail;
1455 dnserror = b23 & 0xF;
1457 if(b23 & 0x200) {
1458 do_log(L_WARN, "Truncated DNS reply (b23 = 0x%x).\n", b23);
1461 if(dnserror || qdcount != 1) {
1462 if(!dnserror)
1463 do_log(L_ERROR,
1464 "Unexpected number %d of DNS questions.\n", qdcount);
1465 if(dnserror == 1)
1466 error = EDNS_FORMAT;
1467 else if(dnserror == 2)
1468 error = EDNS_NO_RECOVERY;
1469 else if(dnserror == 3)
1470 error = EDNS_HOST_NOT_FOUND;
1471 else if(dnserror == 4 || dnserror == 5)
1472 error = EDNS_REFUSED;
1473 else if(dnserror == 0)
1474 error = EDNS_INVALID;
1475 else
1476 error = EUNKNOWN;
1477 goto fail;
1480 /* We do this early, so that we can return the address family to
1481 the caller in case of error. */
1482 i = labelsToString(buf, i, n, b, 2048, &m);
1483 if(i < 0) {
1484 error = EDNS_FORMAT;
1485 goto fail;
1487 DO_NTOHS(type, &buf[i]); i += 2;
1488 DO_NTOHS(class, &buf[i]); i += 2;
1490 if(type == 1)
1491 af = 4;
1492 else if(type == 28)
1493 af = 6;
1494 else {
1495 error = EDNS_FORMAT;
1496 goto fail;
1499 do_log(D_DNS, "DNS q: ");
1500 do_log_n(D_DNS, b, m);
1501 do_log(D_DNS, " (%d, %d)\n", type, class);
1502 name = internAtomLowerN(b, m);
1503 if(name == NULL) {
1504 error = ENOMEM;
1505 goto fail;
1508 if(class != 1) {
1509 error = EDNS_FORMAT;
1510 goto fail;
1513 #define PARSE_ANSWER(kind, label) \
1514 do { \
1515 i = labelsToString(buf, i, 1024, b, 2048, &m); \
1516 if(i < 0) goto label; \
1517 DO_NTOHS(type, &buf[i]); i += 2; if(i > 1024) goto label; \
1518 DO_NTOHS(class, &buf[i]); i += 2; if(i > 1024) goto label; \
1519 DO_NTOHL(ttl, &buf[i]); i += 4; if(i > 1024) goto label; \
1520 DO_NTOHS(rdlength, &buf[i]); i += 2; if(i > 1024) goto label; \
1521 do_log(D_DNS, "DNS " kind ": "); \
1522 do_log_n(D_DNS, b, m); \
1523 do_log(D_DNS, " (%d, %d): %d bytes, ttl %u\n", \
1524 type, class, rdlength, ttl); \
1525 } while(0)
1528 for(j = 0; j < ancount; j++) {
1529 PARSE_ANSWER("an", fail);
1530 if(strcasecmp_n(name->string, b, m) == 0) {
1531 if(class != 1) {
1532 do_log(D_DNS, "DNS: %s: unknown class %d.\n",
1533 name->string, class);
1534 error = EDNS_UNSUPPORTED;
1535 goto cont;
1537 if(type == 1 || type == 28) {
1538 if((type == 1 && rdlength != 4) ||
1539 (type == 28 && rdlength != 16)) {
1540 do_log(L_ERROR,
1541 "DNS: %s: unexpected length %d of %s record.\n",
1542 name->string, rdlength, type == 1 ? "A" : "AAAA");
1543 error = EDNS_INVALID;
1544 if(rdlength <= 0 || rdlength >= 32)
1545 goto fail;
1546 goto cont;
1548 if(af == 0) {
1549 do_log(L_WARN, "DNS: %s: host has both A and CNAME -- "
1550 "ignoring CNAME.\n", name->string);
1551 addr_index = 0;
1552 af = -1;
1554 if(type == 1) {
1555 if(af < 0)
1556 af = 4;
1557 else if(af == 6) {
1558 do_log(L_WARN, "Unexpected AAAA reply.\n");
1559 goto cont;
1561 } else {
1562 if(af < 0)
1563 af = 6;
1564 else if(af == 4) {
1565 do_log(L_WARN, "Unexpected A reply.\n");
1566 goto cont;
1570 if(addr_index == 0) {
1571 addresses[0] = DNS_A;
1572 addr_index++;
1573 } else {
1574 if(addr_index > 1000) {
1575 error = EDNS_INVALID;
1576 goto fail;
1579 assert(addresses[0] == DNS_A);
1580 if(final_ttl > ttl)
1581 final_ttl = ttl;
1582 memset(&addresses[addr_index], 0, sizeof(HostAddressRec));
1583 if(type == 1) {
1584 addresses[addr_index] = 4;
1585 memcpy(addresses + addr_index + 1, buf + i, 4);
1586 } else {
1587 addresses[addr_index] = 6;
1588 memcpy(addresses + addr_index + 1, buf + i, 16);
1590 addr_index += sizeof(HostAddressRec);
1591 } else if(type == 5) {
1592 int j, k;
1593 if(af != 0 && addr_index > 0) {
1594 do_log(L_WARN, "DNS: host has both CNAME and A -- "
1595 "ignoring CNAME.\n");
1596 goto cont;
1598 af = 0;
1600 if(addr_index != 0) {
1601 /* Only warn if the CNAMEs are not identical */
1602 char tmp[512]; int jj, kk;
1603 assert(addresses[0] == DNS_CNAME);
1604 jj = labelsToString(buf, i, n,
1605 tmp, 512, &kk);
1606 if(jj < 0 ||
1607 kk != strlen(addresses + 1) ||
1608 memcmp(addresses + 1, tmp, kk) != 0) {
1609 do_log(L_WARN, "DNS: "
1610 "%s: host has multiple CNAMEs -- "
1611 "ignoring subsequent.\n", name->string);
1614 goto cont;
1616 addresses[0] = DNS_CNAME;
1617 addr_index++;
1618 j = labelsToString(buf, i, n,
1619 addresses + 1, 1020, &k);
1620 if(j < 0) {
1621 addr_index = 0;
1622 error = ENAMETOOLONG;
1623 continue;
1625 addr_index = k + 1;
1626 } else {
1627 error = EDNS_NO_ADDRESS;
1628 i += rdlength;
1629 continue;
1633 cont:
1634 i += rdlength;
1637 #if (LOGGING & D_DNS)
1638 for(j = 0; j < nscount; j++) {
1639 PARSE_ANSWER("ns", nofail);
1640 i += rdlength;
1643 for(j = 0; j < arcount; j++) {
1644 PARSE_ANSWER("ar", nofail);
1645 i += rdlength;
1648 nofail:
1649 #endif
1651 #undef PARSE_ANSWER
1653 do_log(D_DNS, "DNS: %d bytes\n", addr_index);
1654 if(af < 0)
1655 goto fail;
1657 value = internAtomN(addresses, addr_index);
1658 if(value == NULL) {
1659 error = ENOMEM;
1660 goto fail;
1663 assert(af >= 0);
1664 *id_return = id;
1665 *name_return = name;
1666 *value_return = value;
1667 *af_return = af;
1668 *ttl_return = final_ttl;
1669 return 1;
1671 fail:
1672 *id_return = id;
1673 *name_return = name;
1674 *value_return = NULL;
1675 *af_return = af;
1676 return -error;
1679 #else
1681 static int
1682 really_do_dns(AtomPtr name, ObjectPtr object)
1684 abort();
1687 #endif