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
25 #ifndef NO_STANDARD_RESOLVER
26 #ifndef NO_FANCY_RESOLVER
27 int dnsUseGethostbyname
= 1;
29 const int dnsUseGethostbyname
= 3;
32 #ifndef NO_FANCY_RESOLVER
33 const int dnsUseGethostbyname
= 0;
35 #error use no resolver at all?
39 #ifndef NO_FANCY_RESOLVER
40 AtomPtr dnsNameServer
= NULL
;
41 int dnsMaxTimeout
= 60;
44 #ifndef NO_STANDARD_RESOLVER
45 int dnsGethostbynameTtl
= 1200;
48 int dnsNegativeTtl
= 120;
53 const int dnsQueryIPv6
= 0;
56 typedef struct _DnsQuery
{
64 TimeEventHandlerPtr timeout_handler
;
65 struct _DnsQuery
*next
;
66 } DnsQueryRec
, *DnsQueryPtr
;
70 struct sockaddr_in sin
;
72 struct sockaddr_in6 sin6
;
74 } nameserverAddress_storage
;
76 #ifndef NO_FANCY_RESOLVER
77 #define nameserverAddress nameserverAddress_storage.sa
79 static DnsQueryPtr inFlightDnsQueries
;
80 static DnsQueryPtr inFlightDnsQueriesLast
;
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
,
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
);
105 #ifndef NO_FANCY_RESOLVER
107 parseResolvConf(char *filename
)
113 AtomPtr nameserver
= NULL
;
115 f
= fopen(filename
, "r");
117 do_log_error(L_ERROR
, errno
, "DNS: couldn't open %s", filename
);
122 p
= fgets(buf
, 512, f
);
127 if(buf
[n
- 1] != '\n') {
129 do_log(L_WARN
, "DNS: overly long line in %s -- skipping.\n",
140 while(*p
== ' ' || *p
== '\t')
142 if(strcasecmp_n("nameserver", p
, 10) != 0)
145 while(*p
== ' ' || *p
== '\t')
148 while(*q
== '.' || *q
== ':' || digit(*q
) || letter(*q
))
150 if(*q
!= ' ' && *q
!= '\t' && *q
!= '\r' && *q
!= '\n') {
151 do_log(L_WARN
, "DNS: couldn't parse line in %s -- skipping.\n",
155 nameserver
= internAtomLowerN(p
, q
- p
);
161 dnsNameServer
= nameserver
;
176 assert(sizeof(struct in_addr
) == 4);
178 assert(sizeof(struct in6_addr
) == 16);
181 #ifndef NO_STANDARD_RESOLVER
182 CONFIG_VARIABLE(dnsGethostbynameTtl
, CONFIG_TIME
,
183 "TTL for gethostbyname addresses.");
187 fd
= socket(PF_INET6
, SOCK_STREAM
, 0);
189 if(errno
== EPROTONOSUPPORT
|| errno
== EAFNOSUPPORT
) {
192 do_log_error(L_WARN
, errno
, "DNS: couldn't create socket");
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.");
216 CONFIG_VARIABLE(dnsQueryIPv6
, CONFIG_TETRASTATE
,
217 "Query for IPv6 addresses.");
224 #ifndef NO_FANCY_RESOLVER
227 struct sockaddr_in
*sin
= (struct sockaddr_in
*)&nameserverAddress
;
229 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&nameserverAddress
;
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
);
242 sin6
->sin6_family
= AF_INET6
;
243 sin6
->sin6_port
= htons(53);
244 rc
= inet_pton(AF_INET6
, dnsNameServer
->string
, &sin6
->sin6_addr
);
248 do_log(L_ERROR
, "DNS: couldn't parse name server %s.\n",
249 dnsNameServer
->string
);
256 do_gethostbyname(char *origname
,
258 int (*handler
)(int, GethostbynameRequestPtr
),
262 int n
= strlen(origname
);
264 GethostbynameRequestRec request
;
267 memset(&request
, 0, sizeof(request
));
270 request
.error_message
= NULL
;
271 request
.count
= count
;
272 request
.handler
= handler
;
275 if(n
<= 0 || n
> 131) {
277 request
.error_message
= internAtom("empty name");
278 do_log(L_ERROR
, "Empty DNS name.\n");
279 done
= handler(-EINVAL
, &request
);
281 request
.error_message
= internAtom("name too long");
282 do_log(L_ERROR
, "DNS name too long.\n");
283 done
= handler(-ENAMETOOLONG
, &request
);
286 releaseAtom(request
.error_message
);
290 if(origname
[n
- 1] == '.')
293 name
= internAtomLowerN(origname
, n
);
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
);
300 releaseAtom(request
.error_message
);
306 request
.error_message
= NULL
;
307 request
.count
= count
;
308 request
.object
= NULL
;
309 request
.handler
= handler
;
312 object
= findObject(OBJECT_DNS
, name
->string
, name
->length
);
313 if(object
== NULL
|| objectMustRevalidate(object
, NULL
)) {
315 privatiseObject(object
, 0);
316 releaseObject(object
);
318 object
= makeObject(OBJECT_DNS
, name
->string
, name
->length
, 1, 0,
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
);
326 releaseAtom(request
.error_message
);
331 if((object
->flags
& (OBJECT_INITIAL
| OBJECT_INPROGRESS
)) ==
333 if(dnsUseGethostbyname
>= 3)
334 rc
= really_do_gethostbyname(name
, object
);
336 rc
= really_do_dns(name
, object
);
338 assert(!(object
->flags
& (OBJECT_INITIAL
| OBJECT_INPROGRESS
)));
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
) {
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);
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
);
377 done
= handler(-EDNS_HOST_NOT_FOUND
, &request
);
380 releaseAtom(request
.addr
); request
.addr
= NULL
;
381 releaseAtom(request
.name
); request
.name
= NULL
;
382 releaseAtom(request
.error_message
); request
.error_message
= NULL
;
386 releaseNotifyObject(object
);
387 done
= handler(-errno
, &request
);
394 dnsDelayedErrorNotifyHandler(TimeEventHandlerPtr event
)
397 GethostbynameRequestRec request
=
398 *(GethostbynameRequestPtr
)event
->data
;
399 done
= request
.handler(-EDNS_HOST_NOT_FOUND
, &request
);
401 releaseAtom(request
.name
); request
.name
= NULL
;
402 releaseAtom(request
.addr
); request
.addr
= NULL
;
403 releaseAtom(request
.error_message
); request
.error_message
= NULL
;
408 dnsDelayedDoneNotifyHandler(TimeEventHandlerPtr event
)
411 GethostbynameRequestRec request
= *(GethostbynameRequestPtr
)event
->data
;
412 done
= request
.handler(1, &request
);
414 releaseAtom(request
.name
); request
.name
= NULL
;
415 releaseAtom(request
.addr
); request
.addr
= NULL
;
416 releaseAtom(request
.error_message
); request
.error_message
= NULL
;
421 dnsDelayedNotify(int error
, GethostbynameRequestPtr request
)
423 TimeEventHandlerPtr handler
;
426 handler
= scheduleTimeEvent(0,
427 dnsDelayedErrorNotifyHandler
,
428 sizeof(*request
), request
);
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");
442 rfc2732(AtomPtr name
)
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
);
455 char s
[1 + sizeof(HostAddressRec
)];
456 memset(s
, 0, sizeof(s
));
459 memcpy(s
+ 2, &in6a
, 16);
460 a
= internAtomN(s
, 1 + sizeof(HostAddressRec
));
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
;
485 if(dnsQueryIPv6
>= 2)
491 #ifndef NO_STANDARD_RESOLVER
493 really_do_gethostbyname(AtomPtr name
, ObjectPtr object
)
495 struct addrinfo
*ai
, *entry
, hints
;
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
);
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
);
521 case 0: error
= 0; break;
523 #ifdef EAI_ADDRFAMILY
527 error
= EAFNOSUPPORT
; break;
528 case EAI_BADFLAGS
: error
= EINVAL
; break;
529 case EAI_SERVICE
: error
= EDNS_NO_RECOVERY
; break;
536 error
= EDNS_NO_ADDRESS
; break;
537 case EAI_FAIL
: error
= EDNS_NO_RECOVERY
; break;
538 case EAI_AGAIN
: error
= EDNS_TRY_AGAIN
; break;
540 case EAI_MEMORY
: error
= ENOMEM
; break;
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
);
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
);
568 if(entry
->ai_family
== AF_INET
&& entry
->ai_protocol
== IPPROTO_TCP
) {
569 if(dnsQueryIPv6
< 3) {
571 memset(host
.data
, 0, sizeof(host
.data
));
573 &((struct sockaddr_in
*)entry
->ai_addr
)->sin_addr
,
577 } else if(entry
->ai_family
== AF_INET6
&&
578 entry
->ai_protocol
== IPPROTO_TCP
) {
579 if(dnsQueryIPv6
> 0) {
581 memset(&host
.data
, 0, sizeof(host
.data
));
583 &((struct sockaddr_in6
*)entry
->ai_addr
)->sin6_addr
,
589 if(i
>= 1024 / sizeof(HostAddressRec
) - 2) {
590 do_log(L_ERROR
, "Too many addresses for host %s\n",
594 memcpy(buf
+ 1 + i
* sizeof(HostAddressRec
),
595 &host
, sizeof(HostAddressRec
));
598 entry
= entry
->ai_next
;
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
);
610 if(1 <= dnsQueryIPv6
&& dnsQueryIPv6
<= 2)
611 qsort(buf
+ 1, i
, sizeof(HostAddressRec
), compare_hostaddr
);
613 a
= internAtomN(buf
, 1 + i
* sizeof(HostAddressRec
));
615 object
->flags
&= ~OBJECT_INPROGRESS
;
616 abortObject(object
, 501, internAtom("Couldn't allocate address"));
617 notifyObject(object
);
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
);
631 #ifndef NO_STANDARD_RESOLVER
633 really_do_gethostbyname(AtomPtr name
, ObjectPtr object
)
635 struct hostent
*host
;
641 host
= gethostbyname(name
->string
);
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
);
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
);
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
);
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
);
682 while(host
->h_addr_list
[i
] != NULL
) i
++;
683 s
= malloc(1 + i
* sizeof(HostAddressRec
));
687 memset(s
, 0, 1 + i
* sizeof(HostAddressRec
));
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);
698 object
->flags
&= ~OBJECT_INPROGRESS
;
699 abortObject(object
, 501, internAtom("Couldn't allocate address"));
700 notifyObject(object
);
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
);
715 #ifdef NO_STANDARD_RESOLVER
717 really_do_gethostbyname(AtomPtr name
, ObjectPtr object
)
723 #ifndef NO_FANCY_RESOLVER
725 static int dnsSocket
= -1;
726 static FdEventHandlerPtr dnsSocketHandler
= NULL
;
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
);
741 request
.error_message
= retainAtom(object
->message
);
742 dnsDelayedNotify(1, &request
);
744 releaseObject(object
);
749 removeQuery(DnsQueryPtr query
)
751 DnsQueryPtr previous
;
752 if(query
== inFlightDnsQueries
) {
753 inFlightDnsQueries
= query
->next
;
754 if(inFlightDnsQueries
== NULL
)
755 inFlightDnsQueriesLast
= NULL
;
757 previous
= inFlightDnsQueries
;
758 while(previous
->next
) {
759 if(previous
->next
== query
)
761 previous
= previous
->next
;
763 assert(previous
->next
!= NULL
);
764 previous
->next
= query
->next
;
765 if(previous
->next
== NULL
)
766 inFlightDnsQueriesLast
= previous
;
771 insertQuery(DnsQueryPtr query
)
773 if(inFlightDnsQueriesLast
)
774 inFlightDnsQueriesLast
->next
= query
;
776 inFlightDnsQueries
= query
;
777 inFlightDnsQueriesLast
= query
;
781 findQuery(int id
, AtomPtr name
)
784 query
= inFlightDnsQueries
;
786 if(query
->id
== id
&& (name
== NULL
|| query
->name
== name
))
794 dnsTimeoutHandler(TimeEventHandlerPtr event
)
796 DnsQueryPtr query
= *(DnsQueryPtr
*)event
->data
;
797 ObjectPtr object
= query
->object
;
800 query
->timeout
= MAX(10, query
->timeout
* 2);
802 if(query
->timeout
> dnsMaxTimeout
) {
804 abortObject(object
, 501, internAtom("Timeout"));
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"));
816 rc
= sendQuery(query
);
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"));
824 /* else let it timeout */
830 object
->flags
&= ~OBJECT_INPROGRESS
;
831 if(query
->inet4
) releaseAtom(query
->inet4
);
832 if(query
->inet6
) releaseAtom(query
->inet6
);
834 releaseNotifyObject(object
);
843 int inet6
= (nameserverAddress
.sa_family
== AF_INET6
);
844 int pf
= inet6
? PF_INET6
: PF_INET
;
846 inet6
? sizeof(struct sockaddr_in6
) : sizeof(struct sockaddr_in
);
849 int sa_size
= sizeof(struct sockaddr_in
);
853 assert(!dnsSocketHandler
);
854 dnsSocket
= socket(pf
, SOCK_DGRAM
, 0);
856 do_log_error(L_ERROR
, errno
, "Couldn't create DNS socket");
860 rc
= connect(dnsSocket
, &nameserverAddress
, sa_size
);
864 do_log_error(L_ERROR
, errno
, "Couldn't create DNS \"connection\"");
869 if(!dnsSocketHandler
) {
871 registerFdEvent(dnsSocket
, POLLIN
, dnsReplyHandler
, 0, NULL
);
872 if(dnsSocketHandler
== NULL
) {
873 do_log(L_ERROR
, "Couldn't register DNS socket handler.\n");
884 sendQuery(DnsQueryPtr query
)
895 if(dnsQueryIPv6
<= 0) {
896 af
[0] = 4; af
[1] = 0;
897 } else if(dnsQueryIPv6
<= 2) {
898 af
[0] = 4; af
[1] = 6;
900 af
[0] = 6; af
[1] = 0;
903 for(i
= 0; i
< 2; i
++) {
906 if(af
[i
] == 4 && query
->inet4
)
908 else if(af
[i
] == 6 && query
->inet6
)
911 buflen
= dnsBuildQuery(query
->id
, buf
, 0, 512, query
->name
, af
[i
]);
913 do_log(L_ERROR
, "Couldn't build DNS query.\n");
917 rc
= send(dnsSocket
, buf
, buflen
, 0);
920 do_log(L_ERROR
, "Couldn't send DNS query: partial send");
923 do_log_error(L_ERROR
, errno
, "Couldn't send DNS query");
932 really_do_dns(AtomPtr name
, ObjectPtr object
)
936 AtomPtr message
= NULL
;
942 rc
= inet_aton(name
->string
, &ina
);
944 char s
[1 + sizeof(HostAddressRec
)];
945 memset(s
, 0, sizeof(s
));
948 memcpy(s
+ 2, &ina
, 4);
949 a
= internAtomN(s
, 1 + sizeof(HostAddressRec
));
951 abortObject(object
, 501,
952 internAtom("Couldn't allocate address"));
953 notifyObject(object
);
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
);
973 rc
= establishDnsSocket();
975 do_log_error(L_ERROR
, -rc
, "Couldn't establish DNS socket.\n");
976 message
= internAtomError(-rc
, "Couldn't establish DNS socket");
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
));
986 do_log(L_ERROR
, "Couldn't allocate DNS query.\n");
987 message
= internAtom("Couldn't allocate DNS query");
994 query
->time
= current_time
.tv_sec
;
995 query
->object
= retainObject(object
);
997 query
->timeout_handler
= 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");
1010 object
->flags
|= OBJECT_INPROGRESS
;
1011 rc
= sendQuery(query
);
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
);
1026 releaseObject(query
->object
);
1027 cancelTimeEvent(query
->timeout_handler
);
1030 if(dnsUseGethostbyname
>= 1) {
1031 releaseAtom(message
);
1032 do_log(L_WARN
, "Falling back on gethostbyname.\n");
1033 return really_do_gethostbyname(name
, object
);
1035 abortObject(object
, 501, message
);
1036 notifyObject(object
);
1042 dnsReplyHandler(int abort
, FdEventHandlerPtr event
)
1049 AtomPtr name
, value
, message
= NULL
;
1053 AtomPtr cname
= NULL
;
1056 dnsSocketHandler
= NULL
;
1057 rc
= establishDnsSocket();
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. */
1066 len
= recv(fd
, buf
, 2048, 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
);
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
1080 rc
= dnsReplyId(buf
, 0, len
, &id
);
1082 do_log(L_WARN
, "Short DNS reply.\n");
1085 if(!findQuery(id
, NULL
)) {
1089 rc
= dnsDecodeReply(buf
, 0, len
, &id
, &name
, &value
, &af
, &ttl
);
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
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
);
1104 message
= internAtomError(-rc
, "DNS reply failure");
1107 assert(name
!= NULL
&& id
>= 0 && af
>= 0);
1111 query
= findQuery(id
, name
);
1113 /* Duplicate id ? */
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. */
1123 value
= internAtom("");
1126 if(query
->inet4
== NULL
) {
1127 query
->inet4
= value
;
1128 query
->ttl4
= current_time
.tv_sec
+ ttl
;
1131 } else if(af
== 6) {
1132 if(query
->inet6
== NULL
) {
1133 query
->inet6
= value
;
1134 query
->ttl6
= current_time
.tv_sec
+ ttl
;
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");
1148 if(rc
>= 0 && !cname
&&
1149 ((dnsQueryIPv6
< 3 && query
->inet4
== NULL
) ||
1150 (dnsQueryIPv6
> 0 && query
->inet6
== NULL
)))
1153 /* This query is complete */
1155 cancelTimeEvent(query
->timeout_handler
);
1156 object
= query
->object
;
1158 if(object
->flags
& OBJECT_INITIAL
) {
1159 assert(!object
->headers
);
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
);
1179 /* need to merge results */
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"));
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);
1191 memcpy(buf
, query
->inet6
->string
, query
->inet6
->length
);
1192 memcpy(buf
+ query
->inet6
->length
,
1193 query
->inet4
->string
+ 1, query
->inet4
->length
- 1);
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
);
1208 do_log(L_WARN
, "DNS object ex nihilo for %s.\n", query
->name
->string
);
1215 releaseAtom(message
);
1216 releaseNotifyObject(object
);
1221 dnsGethostbynameFallback(int id
, AtomPtr message
)
1223 DnsQueryPtr query
, previous
;
1226 if(inFlightDnsQueries
== NULL
) {
1227 releaseAtom(message
);
1232 if(id
< 0 || inFlightDnsQueries
->id
== id
) {
1234 query
= inFlightDnsQueries
;
1236 previous
= inFlightDnsQueries
;
1237 while(previous
->next
) {
1238 if(previous
->next
->id
== id
) {
1239 query
= previous
->next
;
1245 query
= inFlightDnsQueries
;
1249 if(previous
== NULL
) {
1250 inFlightDnsQueries
= query
->next
;
1251 if(inFlightDnsQueries
== NULL
)
1252 inFlightDnsQueriesLast
= NULL
;
1254 previous
->next
= query
->next
;
1255 if(query
->next
== NULL
)
1256 inFlightDnsQueriesLast
= NULL
;
1259 object
= makeObject(OBJECT_DNS
, query
->name
->string
, query
->name
->length
,
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
);
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
);
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
);
1290 stringToLabels(char *buf
, int offset
, int n
, char *string
)
1295 while(string
[k
] != '.' && string
[k
] != '\0')
1297 if(k
>= j
+ 256) return -1;
1298 buf
[i
] = (unsigned char)(k
- j
); i
++; if(i
>= n
) return -1;
1300 buf
[i
] = string
[j
]; i
++; j
++; if(i
>= n
) return -1;
1302 if(string
[j
] == '\0') {
1304 i
++; if(i
>= n
) return -1;
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)
1319 #define DO_NTOHS(_d, _s) \
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; \
1330 memcpy((_d), &(_dd), sizeof(unsigned short)); } while(0);
1331 #define DO_HTONL(_d, _s) \
1332 do { unsigned long _dd; \
1334 memcpy((_d), &(_dd), sizeof(unsigned long)); } while(0);
1338 labelsToString(char *buf
, int offset
, int n
, char *d
, int m
, int *j_return
)
1340 int i
= offset
, j
, k
;
1345 if(i
>= n
) return -1;
1346 ll
= *(unsigned char*)&buf
[i
]; i
++;
1350 if((ll
& (3 << 6)) == (3 << 6)) {
1351 /* RFC 1035, 4.1.4 */
1353 if(i
>= n
) return -1;
1354 o
= (ll
& ~(3 << 6)) << 8 | *(unsigned char*)&buf
[i
];
1356 labelsToString(buf
, o
, n
, &d
[j
], m
- j
, &k
);
1359 } else if((ll
& (3 << 6)) == 0) {
1360 for(k
= 0; k
< ll
; k
++) {
1361 if(i
>= n
|| j
>= m
) return -1;
1364 if(i
>= n
) return -1;
1365 if(buf
[i
] != '\0') {
1366 if(j
>= m
) return -1;
1378 dnsBuildQuery(int id
, char *buf
, int offset
, int n
, AtomPtr name
, int 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;
1406 dnsReplyId(char *buf
, int offset
, int n
, int *id_return
)
1410 *id_return
= ntohs(*(short*)&buf
[offset
]);
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
;
1425 AtomPtr name
= NULL
, value
;
1426 char addresses
[1024];
1428 int error
= EDNS_NO_ADDRESS
;
1429 unsigned final_ttl
= 7 * 24 * 3600;
1433 error
= EDNS_INVALID
;
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;
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
;
1455 dnserror
= b23
& 0xF;
1458 do_log(L_WARN
, "Truncated DNS reply (b23 = 0x%x).\n", b23
);
1461 if(dnserror
|| qdcount
!= 1) {
1464 "Unexpected number %d of DNS questions.\n", qdcount
);
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
;
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
);
1484 error
= EDNS_FORMAT
;
1487 DO_NTOHS(type
, &buf
[i
]); i
+= 2;
1488 DO_NTOHS(class, &buf
[i
]); i
+= 2;
1495 error
= EDNS_FORMAT
;
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
);
1509 error
= EDNS_FORMAT
;
1513 #define PARSE_ANSWER(kind, label) \
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); \
1528 for(j
= 0; j
< ancount
; j
++) {
1529 PARSE_ANSWER("an", fail
);
1530 if(strcasecmp_n(name
->string
, b
, m
) == 0) {
1532 do_log(D_DNS
, "DNS: %s: unknown class %d.\n",
1533 name
->string
, class);
1534 error
= EDNS_UNSUPPORTED
;
1537 if(type
== 1 || type
== 28) {
1538 if((type
== 1 && rdlength
!= 4) ||
1539 (type
== 28 && rdlength
!= 16)) {
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)
1549 do_log(L_WARN
, "DNS: %s: host has both A and CNAME -- "
1550 "ignoring CNAME.\n", name
->string
);
1558 do_log(L_WARN
, "Unexpected AAAA reply.\n");
1565 do_log(L_WARN
, "Unexpected A reply.\n");
1570 if(addr_index
== 0) {
1571 addresses
[0] = DNS_A
;
1574 if(addr_index
> 1000) {
1575 error
= EDNS_INVALID
;
1579 assert(addresses
[0] == DNS_A
);
1582 memset(&addresses
[addr_index
], 0, sizeof(HostAddressRec
));
1584 addresses
[addr_index
] = 4;
1585 memcpy(addresses
+ addr_index
+ 1, buf
+ i
, 4);
1587 addresses
[addr_index
] = 6;
1588 memcpy(addresses
+ addr_index
+ 1, buf
+ i
, 16);
1590 addr_index
+= sizeof(HostAddressRec
);
1591 } else if(type
== 5) {
1593 if(af
!= 0 && addr_index
> 0) {
1594 do_log(L_WARN
, "DNS: host has both CNAME and A -- "
1595 "ignoring CNAME.\n");
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
,
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
);
1616 addresses
[0] = DNS_CNAME
;
1618 j
= labelsToString(buf
, i
, n
,
1619 addresses
+ 1, 1020, &k
);
1622 error
= ENAMETOOLONG
;
1627 error
= EDNS_NO_ADDRESS
;
1637 #if (LOGGING & D_DNS)
1638 for(j
= 0; j
< nscount
; j
++) {
1639 PARSE_ANSWER("ns", nofail
);
1643 for(j
= 0; j
< arcount
; j
++) {
1644 PARSE_ANSWER("ar", nofail
);
1653 do_log(D_DNS
, "DNS: %d bytes\n", addr_index
);
1657 value
= internAtomN(addresses
, addr_index
);
1665 *name_return
= name
;
1666 *value_return
= value
;
1668 *ttl_return
= final_ttl
;
1673 *name_return
= name
;
1674 *value_return
= NULL
;
1682 really_do_dns(AtomPtr name
, ObjectPtr object
)