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
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 static AtomPtr atomLocalhost
, atomLocalhostDot
;
79 #define nameserverAddress nameserverAddress_storage.sa
81 static DnsQueryPtr inFlightDnsQueries
;
82 static DnsQueryPtr inFlightDnsQueriesLast
;
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
,
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
);
107 #ifndef NO_FANCY_RESOLVER
109 parseResolvConf(char *filename
)
115 AtomPtr nameserver
= NULL
;
117 f
= fopen(filename
, "r");
119 do_log_error(L_ERROR
, errno
, "DNS: couldn't open %s", filename
);
124 p
= fgets(buf
, 512, f
);
129 if(buf
[n
- 1] != '\n') {
131 do_log(L_WARN
, "DNS: overly long line in %s -- skipping.\n",
142 while(*p
== ' ' || *p
== '\t')
144 if(strcasecmp_n("nameserver", p
, 10) != 0)
147 while(*p
== ' ' || *p
== '\t')
150 while(*q
== '.' || *q
== ':' || digit(*q
) || letter(*q
))
152 if(*q
!= ' ' && *q
!= '\t' && *q
!= '\r' && *q
!= '\n') {
153 do_log(L_WARN
, "DNS: couldn't parse line in %s -- skipping.\n",
157 nameserver
= internAtomLowerN(p
, q
- p
);
163 dnsNameServer
= nameserver
;
178 assert(sizeof(struct in_addr
) == 4);
180 assert(sizeof(struct in6_addr
) == 16);
183 #ifndef NO_STANDARD_RESOLVER
184 CONFIG_VARIABLE(dnsGethostbynameTtl
, CONFIG_TIME
,
185 "TTL for gethostbyname addresses.");
189 fd
= socket(PF_INET6
, SOCK_STREAM
, 0);
191 if(errno
== EPROTONOSUPPORT
|| errno
== EAFNOSUPPORT
) {
194 do_log_error(L_WARN
, errno
, "DNS: couldn't create socket");
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.");
218 CONFIG_VARIABLE(dnsQueryIPv6
, CONFIG_TETRASTATE
,
219 "Query for IPv6 addresses.");
226 #ifndef NO_FANCY_RESOLVER
229 struct sockaddr_in
*sin
= (struct sockaddr_in
*)&nameserverAddress
;
231 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&nameserverAddress
;
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
);
246 sin6
->sin6_family
= AF_INET6
;
247 sin6
->sin6_port
= htons(53);
248 rc
= inet_pton(AF_INET6
, dnsNameServer
->string
, &sin6
->sin6_addr
);
252 do_log(L_ERROR
, "DNS: couldn't parse name server %s.\n",
253 dnsNameServer
->string
);
260 do_gethostbyname(char *origname
,
262 int (*handler
)(int, GethostbynameRequestPtr
),
266 int n
= strlen(origname
);
268 GethostbynameRequestRec request
;
271 memset(&request
, 0, sizeof(request
));
274 request
.error_message
= NULL
;
275 request
.count
= count
;
276 request
.handler
= handler
;
279 if(n
<= 0 || n
> 131) {
281 request
.error_message
= internAtom("empty name");
282 do_log(L_ERROR
, "Empty DNS name.\n");
283 done
= handler(-EINVAL
, &request
);
285 request
.error_message
= internAtom("name too long");
286 do_log(L_ERROR
, "DNS name too long.\n");
287 done
= handler(-ENAMETOOLONG
, &request
);
290 releaseAtom(request
.error_message
);
294 if(origname
[n
- 1] == '.')
297 name
= internAtomLowerN(origname
, n
);
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
);
304 releaseAtom(request
.error_message
);
310 request
.error_message
= NULL
;
311 request
.count
= count
;
312 request
.object
= NULL
;
313 request
.handler
= handler
;
316 object
= findObject(OBJECT_DNS
, name
->string
, name
->length
);
317 if(object
== NULL
|| objectMustRevalidate(object
, NULL
)) {
319 privatiseObject(object
, 0);
320 releaseObject(object
);
322 object
= makeObject(OBJECT_DNS
, name
->string
, name
->length
, 1, 0,
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
);
330 releaseAtom(request
.error_message
);
335 if((object
->flags
& (OBJECT_INITIAL
| OBJECT_INPROGRESS
)) ==
337 if(dnsUseGethostbyname
>= 3)
338 rc
= really_do_gethostbyname(name
, object
);
340 rc
= really_do_dns(name
, object
);
342 assert(!(object
->flags
& (OBJECT_INITIAL
| OBJECT_INPROGRESS
)));
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
) {
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);
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
);
381 done
= handler(-EDNS_HOST_NOT_FOUND
, &request
);
384 releaseAtom(request
.addr
); request
.addr
= NULL
;
385 releaseAtom(request
.name
); request
.name
= NULL
;
386 releaseAtom(request
.error_message
); request
.error_message
= NULL
;
390 releaseNotifyObject(object
);
391 done
= handler(-errno
, &request
);
398 dnsDelayedErrorNotifyHandler(TimeEventHandlerPtr event
)
401 GethostbynameRequestRec request
=
402 *(GethostbynameRequestPtr
)event
->data
;
403 done
= request
.handler(-EDNS_HOST_NOT_FOUND
, &request
);
405 releaseAtom(request
.name
); request
.name
= NULL
;
406 releaseAtom(request
.addr
); request
.addr
= NULL
;
407 releaseAtom(request
.error_message
); request
.error_message
= NULL
;
412 dnsDelayedDoneNotifyHandler(TimeEventHandlerPtr event
)
415 GethostbynameRequestRec request
= *(GethostbynameRequestPtr
)event
->data
;
416 done
= request
.handler(1, &request
);
418 releaseAtom(request
.name
); request
.name
= NULL
;
419 releaseAtom(request
.addr
); request
.addr
= NULL
;
420 releaseAtom(request
.error_message
); request
.error_message
= NULL
;
425 dnsDelayedNotify(int error
, GethostbynameRequestPtr request
)
427 TimeEventHandlerPtr handler
;
430 handler
= scheduleTimeEvent(0,
431 dnsDelayedErrorNotifyHandler
,
432 sizeof(*request
), request
);
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");
446 rfc2732(AtomPtr name
)
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
);
459 char s
[1 + sizeof(HostAddressRec
)];
460 memset(s
, 0, sizeof(s
));
463 memcpy(s
+ 2, &in6a
, 16);
464 a
= internAtomN(s
, 1 + sizeof(HostAddressRec
));
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
;
489 if(dnsQueryIPv6
>= 2)
495 #ifndef NO_STANDARD_RESOLVER
497 really_do_gethostbyname(AtomPtr name
, ObjectPtr object
)
499 struct addrinfo
*ai
, *entry
, hints
;
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
);
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
);
525 case 0: error
= 0; break;
527 #ifdef EAI_ADDRFAMILY
531 error
= EAFNOSUPPORT
; break;
532 case EAI_BADFLAGS
: error
= EINVAL
; break;
533 case EAI_SERVICE
: error
= EDNS_NO_RECOVERY
; break;
540 error
= EDNS_NO_ADDRESS
; break;
541 case EAI_FAIL
: error
= EDNS_NO_RECOVERY
; break;
542 case EAI_AGAIN
: error
= EDNS_TRY_AGAIN
; break;
544 case EAI_MEMORY
: error
= ENOMEM
; break;
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
);
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
);
572 if(entry
->ai_family
== AF_INET
&& entry
->ai_protocol
== IPPROTO_TCP
) {
573 if(dnsQueryIPv6
< 3) {
575 memset(host
.data
, 0, sizeof(host
.data
));
577 &((struct sockaddr_in
*)entry
->ai_addr
)->sin_addr
,
581 } else if(entry
->ai_family
== AF_INET6
&&
582 entry
->ai_protocol
== IPPROTO_TCP
) {
583 if(dnsQueryIPv6
> 0) {
585 memset(&host
.data
, 0, sizeof(host
.data
));
587 &((struct sockaddr_in6
*)entry
->ai_addr
)->sin6_addr
,
593 if(i
>= 1024 / sizeof(HostAddressRec
) - 2) {
594 do_log(L_ERROR
, "Too many addresses for host %s\n",
598 memcpy(buf
+ 1 + i
* sizeof(HostAddressRec
),
599 &host
, sizeof(HostAddressRec
));
602 entry
= entry
->ai_next
;
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
);
614 if(1 <= dnsQueryIPv6
&& dnsQueryIPv6
<= 2)
615 qsort(buf
+ 1, i
, sizeof(HostAddressRec
), compare_hostaddr
);
617 a
= internAtomN(buf
, 1 + i
* sizeof(HostAddressRec
));
619 object
->flags
&= ~OBJECT_INPROGRESS
;
620 abortObject(object
, 501, internAtom("Couldn't allocate address"));
621 notifyObject(object
);
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
);
635 #ifndef NO_STANDARD_RESOLVER
637 really_do_gethostbyname(AtomPtr name
, ObjectPtr object
)
639 struct hostent
*host
;
645 host
= gethostbyname(name
->string
);
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
);
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
);
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
);
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
);
686 while(host
->h_addr_list
[i
] != NULL
) i
++;
687 s
= malloc(1 + i
* sizeof(HostAddressRec
));
691 memset(s
, 0, 1 + i
* sizeof(HostAddressRec
));
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);
702 object
->flags
&= ~OBJECT_INPROGRESS
;
703 abortObject(object
, 501, internAtom("Couldn't allocate address"));
704 notifyObject(object
);
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
);
719 #ifdef NO_STANDARD_RESOLVER
721 really_do_gethostbyname(AtomPtr name
, ObjectPtr object
)
727 #ifndef NO_FANCY_RESOLVER
729 static int dnsSocket
= -1;
730 static FdEventHandlerPtr dnsSocketHandler
= NULL
;
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
);
745 request
.error_message
= retainAtom(object
->message
);
746 dnsDelayedNotify(1, &request
);
748 releaseObject(object
);
753 removeQuery(DnsQueryPtr query
)
755 DnsQueryPtr previous
;
756 if(query
== inFlightDnsQueries
) {
757 inFlightDnsQueries
= query
->next
;
758 if(inFlightDnsQueries
== NULL
)
759 inFlightDnsQueriesLast
= NULL
;
761 previous
= inFlightDnsQueries
;
762 while(previous
->next
) {
763 if(previous
->next
== query
)
765 previous
= previous
->next
;
767 assert(previous
->next
!= NULL
);
768 previous
->next
= query
->next
;
769 if(previous
->next
== NULL
)
770 inFlightDnsQueriesLast
= previous
;
775 insertQuery(DnsQueryPtr query
)
777 if(inFlightDnsQueriesLast
)
778 inFlightDnsQueriesLast
->next
= query
;
780 inFlightDnsQueries
= query
;
781 inFlightDnsQueriesLast
= query
;
785 findQuery(int id
, AtomPtr name
)
788 query
= inFlightDnsQueries
;
790 if(query
->id
== id
&& (name
== NULL
|| query
->name
== name
))
798 dnsTimeoutHandler(TimeEventHandlerPtr event
)
800 DnsQueryPtr query
= *(DnsQueryPtr
*)event
->data
;
801 ObjectPtr object
= query
->object
;
804 query
->timeout
= MAX(10, query
->timeout
* 2);
806 if(query
->timeout
> dnsMaxTimeout
) {
807 abortObject(object
, 501, internAtom("Timeout"));
810 rc
= sendQuery(query
);
812 if(rc
!= -EWOULDBLOCK
&& rc
!= -EAGAIN
&& rc
!= -ENOBUFS
) {
813 abortObject(object
, 501,
815 "Couldn't send DNS query"));
818 /* else let it timeout */
820 query
->timeout_handler
=
821 scheduleTimeEvent(query
->timeout
, dnsTimeoutHandler
,
822 sizeof(query
), &query
);
823 if(query
->timeout_handler
== NULL
) {
824 do_log(L_ERROR
, "Couldn't schedule DNS timeout handler.\n");
825 abortObject(object
, 501,
826 internAtom("Couldn't schedule DNS timeout handler"));
834 object
->flags
&= ~OBJECT_INPROGRESS
;
835 if(query
->inet4
) releaseAtom(query
->inet4
);
836 if(query
->inet6
) releaseAtom(query
->inet6
);
838 releaseNotifyObject(object
);
847 int inet6
= (nameserverAddress
.sa_family
== AF_INET6
);
848 int pf
= inet6
? PF_INET6
: PF_INET
;
850 inet6
? sizeof(struct sockaddr_in6
) : sizeof(struct sockaddr_in
);
853 int sa_size
= sizeof(struct sockaddr_in
);
857 assert(!dnsSocketHandler
);
858 dnsSocket
= socket(pf
, SOCK_DGRAM
, 0);
860 do_log_error(L_ERROR
, errno
, "Couldn't create DNS socket");
864 rc
= connect(dnsSocket
, &nameserverAddress
, sa_size
);
868 do_log_error(L_ERROR
, errno
, "Couldn't create DNS \"connection\"");
873 if(!dnsSocketHandler
) {
875 registerFdEvent(dnsSocket
, POLLIN
, dnsReplyHandler
, 0, NULL
);
876 if(dnsSocketHandler
== NULL
) {
877 do_log(L_ERROR
, "Couldn't register DNS socket handler.\n");
888 sendQuery(DnsQueryPtr query
)
899 if(dnsQueryIPv6
<= 0) {
900 af
[0] = 4; af
[1] = 0;
901 } else if(dnsQueryIPv6
<= 2) {
902 af
[0] = 4; af
[1] = 6;
904 af
[0] = 6; af
[1] = 0;
907 for(i
= 0; i
< 2; i
++) {
910 if(af
[i
] == 4 && query
->inet4
)
912 else if(af
[i
] == 6 && query
->inet6
)
915 buflen
= dnsBuildQuery(query
->id
, buf
, 0, 512, query
->name
, af
[i
]);
917 do_log(L_ERROR
, "Couldn't build DNS query.\n");
921 rc
= send(dnsSocket
, buf
, buflen
, 0);
924 do_log(L_ERROR
, "Couldn't send DNS query: partial send.\n");
927 do_log_error(L_ERROR
, errno
, "Couldn't send DNS query");
936 really_do_dns(AtomPtr name
, ObjectPtr object
)
940 AtomPtr message
= NULL
;
945 if(name
== atomLocalhost
|| name
== atomLocalhostDot
) {
946 char s
[1 + sizeof(HostAddressRec
)];
947 memset(s
, 0, sizeof(s
));
954 a
= internAtomN(s
, 1 + sizeof(HostAddressRec
));
956 abortObject(object
, 501,
957 internAtom("Couldn't allocate address"));
958 notifyObject(object
);
967 rc
= inet_aton(name
->string
, &ina
);
969 char s
[1 + sizeof(HostAddressRec
)];
970 memset(s
, 0, sizeof(s
));
973 memcpy(s
+ 2, &ina
, 4);
974 a
= internAtomN(s
, 1 + sizeof(HostAddressRec
));
976 abortObject(object
, 501,
977 internAtom("Couldn't allocate address"));
978 notifyObject(object
);
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
);
998 rc
= establishDnsSocket();
1000 do_log_error(L_ERROR
, -rc
, "Couldn't establish DNS socket.\n");
1001 message
= internAtomError(-rc
, "Couldn't establish DNS socket");
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
));
1011 do_log(L_ERROR
, "Couldn't allocate DNS query.\n");
1012 message
= internAtom("Couldn't allocate DNS query");
1016 query
->inet4
= NULL
;
1017 query
->inet6
= NULL
;
1019 query
->time
= current_time
.tv_sec
;
1020 query
->object
= retainObject(object
);
1022 query
->timeout_handler
= 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");
1035 object
->flags
|= OBJECT_INPROGRESS
;
1036 rc
= sendQuery(query
);
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
);
1051 releaseObject(query
->object
);
1052 cancelTimeEvent(query
->timeout_handler
);
1055 if(dnsUseGethostbyname
>= 1) {
1056 releaseAtom(message
);
1057 do_log(L_WARN
, "Falling back on gethostbyname.\n");
1058 return really_do_gethostbyname(name
, object
);
1060 abortObject(object
, 501, message
);
1061 notifyObject(object
);
1067 dnsReplyHandler(int abort
, FdEventHandlerPtr event
)
1074 AtomPtr name
, value
, message
= NULL
;
1078 AtomPtr cname
= NULL
;
1081 dnsSocketHandler
= NULL
;
1082 rc
= establishDnsSocket();
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. */
1091 len
= recv(fd
, buf
, 2048, 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
);
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
1105 rc
= dnsReplyId(buf
, 0, len
, &id
);
1107 do_log(L_WARN
, "Short DNS reply.\n");
1110 if(!findQuery(id
, NULL
)) {
1114 rc
= dnsDecodeReply(buf
, 0, len
, &id
, &name
, &value
, &af
, &ttl
);
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
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
);
1129 message
= internAtomError(-rc
, NULL
);
1132 assert(name
!= NULL
&& id
>= 0 && af
>= 0);
1136 query
= findQuery(id
, name
);
1138 /* Duplicate id ? */
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. */
1148 value
= internAtom("");
1152 if(query
->inet4
== NULL
) {
1153 query
->inet4
= value
;
1154 query
->ttl4
= current_time
.tv_sec
+ ttl
;
1157 } else if(af
== 6) {
1158 if(query
->inet6
== NULL
) {
1159 query
->inet6
= value
;
1160 query
->ttl6
= current_time
.tv_sec
+ ttl
;
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");
1169 value
= internAtom("");
1170 af
= query
->inet4
? 4 : 6;
1177 if(rc
>= 0 && !cname
&&
1178 ((dnsQueryIPv6
< 3 && query
->inet4
== NULL
) ||
1179 (dnsQueryIPv6
> 0 && query
->inet6
== NULL
)))
1182 /* This query is complete */
1184 cancelTimeEvent(query
->timeout_handler
);
1185 object
= query
->object
;
1187 if(object
->flags
& OBJECT_INITIAL
) {
1188 assert(!object
->headers
);
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
);
1208 /* need to merge results */
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"));
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);
1220 memcpy(buf
, query
->inet6
->string
, query
->inet6
->length
);
1221 memcpy(buf
+ query
->inet6
->length
,
1222 query
->inet4
->string
+ 1, query
->inet4
->length
- 1);
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
);
1237 do_log(L_WARN
, "DNS object ex nihilo for %s.\n", query
->name
->string
);
1244 releaseAtom(message
);
1245 releaseNotifyObject(object
);
1250 dnsGethostbynameFallback(int id
, AtomPtr message
)
1252 DnsQueryPtr query
, previous
;
1255 if(inFlightDnsQueries
== NULL
) {
1256 releaseAtom(message
);
1261 if(id
< 0 || inFlightDnsQueries
->id
== id
) {
1263 query
= inFlightDnsQueries
;
1265 previous
= inFlightDnsQueries
;
1266 while(previous
->next
) {
1267 if(previous
->next
->id
== id
) {
1268 query
= previous
->next
;
1271 previous
= previous
->next
;
1275 query
= inFlightDnsQueries
;
1279 if(previous
== NULL
) {
1280 inFlightDnsQueries
= query
->next
;
1281 if(inFlightDnsQueries
== NULL
)
1282 inFlightDnsQueriesLast
= NULL
;
1284 previous
->next
= query
->next
;
1285 if(query
->next
== NULL
)
1286 inFlightDnsQueriesLast
= NULL
;
1289 object
= makeObject(OBJECT_DNS
, query
->name
->string
, query
->name
->length
,
1292 do_log(L_ERROR
, "Couldn't make DNS object.\n");
1293 releaseAtom(query
->name
);
1294 releaseAtom(message
);
1295 releaseObject(query
->object
);
1296 cancelTimeEvent(query
->timeout_handler
);
1300 if(dnsUseGethostbyname
>= 1) {
1301 releaseAtom(message
);
1302 do_log(L_WARN
, "Falling back to using system resolver.\n");
1303 really_do_gethostbyname(retainAtom(query
->name
), object
);
1305 releaseAtom(object
->message
);
1306 object
->message
= message
;
1307 object
->flags
&= ~OBJECT_INPROGRESS
;
1308 releaseNotifyObject(object
);
1310 cancelTimeEvent(query
->timeout_handler
);
1311 releaseAtom(query
->name
);
1312 if(query
->inet4
) releaseAtom(query
->inet4
);
1313 if(query
->inet6
) releaseAtom(query
->inet6
);
1314 releaseObject(query
->object
);
1320 stringToLabels(char *buf
, int offset
, int n
, char *string
)
1325 while(string
[k
] != '.' && string
[k
] != '\0')
1327 if(k
>= j
+ 256) return -1;
1328 buf
[i
] = (unsigned char)(k
- j
); i
++; if(i
>= n
) return -1;
1330 buf
[i
] = string
[j
]; i
++; j
++; if(i
>= n
) return -1;
1332 if(string
[j
] == '\0') {
1334 i
++; if(i
>= n
) return -1;
1343 #ifdef UNALIGNED_ACCESS
1344 #define DO_NTOHS(_d, _s) _d = ntohs(*(short*)(_s));
1345 #define DO_NTOHL(_d, _s) _d = ntohl(*(unsigned*)(_s))
1346 #define DO_HTONS(_d, _s) *(short*)(_d) = htons(_s);
1347 #define DO_HTONL(_d, _s) *(unsigned*)(_d) = htonl(_s)
1349 #define DO_NTOHS(_d, _s) \
1351 memcpy(&(_dd), (_s), sizeof(short)); \
1352 _d = ntohs(_dd); } while(0)
1353 #define DO_NTOHL(_d, _s) \
1354 do { unsigned _dd; \
1355 memcpy(&(_dd), (_s), sizeof(unsigned)); \
1356 _d = ntohl(_dd); } while(0)
1357 #define DO_HTONS(_d, _s) \
1358 do { unsigned short _dd; \
1360 memcpy((_d), &(_dd), sizeof(unsigned short)); } while(0);
1361 #define DO_HTONL(_d, _s) \
1362 do { unsigned _dd; \
1364 memcpy((_d), &(_dd), sizeof(unsigned)); } while(0);
1368 labelsToString(char *buf
, int offset
, int n
, char *d
, int m
, int *j_return
)
1370 int i
= offset
, j
, k
;
1375 if(i
>= n
) return -1;
1376 ll
= *(unsigned char*)&buf
[i
]; i
++;
1380 if((ll
& (3 << 6)) == (3 << 6)) {
1381 /* RFC 1035, 4.1.4 */
1383 if(i
>= n
) return -1;
1384 o
= (ll
& ~(3 << 6)) << 8 | *(unsigned char*)&buf
[i
];
1386 labelsToString(buf
, o
, n
, &d
[j
], m
- j
, &k
);
1389 } else if((ll
& (3 << 6)) == 0) {
1390 for(k
= 0; k
< ll
; k
++) {
1391 if(i
>= n
|| j
>= m
) return -1;
1394 if(i
>= n
) return -1;
1395 if(buf
[i
] != '\0') {
1396 if(j
>= m
) return -1;
1408 dnsBuildQuery(int id
, char *buf
, int offset
, int n
, AtomPtr name
, int af
)
1413 case 4: type
= 1; break;
1414 case 6: type
= 28; break;
1415 default: return EINVAL
;
1418 if(i
+ 12 >= n
) return -1;
1419 DO_HTONS(&buf
[i
], id
); i
+= 2;
1420 DO_HTONS(&buf
[i
], 1<<8); i
+= 2;
1421 DO_HTONS(&buf
[i
], 1); i
+= 2;
1422 DO_HTONS(&buf
[i
], 0); i
+= 2;
1423 DO_HTONS(&buf
[i
], 0); i
+= 2;
1424 DO_HTONS(&buf
[i
], 0); i
+= 2;
1426 i
= stringToLabels(buf
, i
, n
, name
->string
);
1427 if(i
< 0) return -ENAMETOOLONG
;
1429 if(i
+ 4 >= n
) return -ENAMETOOLONG
;
1430 DO_HTONS(&buf
[i
], type
); i
+= 2;
1431 DO_HTONS(&buf
[i
], 1); i
+= 2;
1436 dnsReplyId(char *buf
, int offset
, int n
, int *id_return
)
1440 *id_return
= ntohs(*(short*)&buf
[offset
]);
1445 dnsDecodeReply(char *buf
, int offset
, int n
, int *id_return
,
1446 AtomPtr
*name_return
, AtomPtr
*value_return
,
1447 int *af_return
, unsigned *ttl_return
)
1449 int i
= offset
, j
, m
;
1450 int id
= -1, b23
, qdcount
, ancount
, nscount
, arcount
, rdlength
;
1455 AtomPtr name
= NULL
, value
;
1456 char addresses
[1024];
1458 int error
= EDNS_NO_ADDRESS
;
1459 unsigned final_ttl
= 7 * 24 * 3600;
1463 error
= EDNS_INVALID
;
1467 DO_NTOHS(id
, &buf
[i
]); i
+= 2;
1468 DO_NTOHS(b23
, &buf
[i
]); i
+= 2;
1469 DO_NTOHS(qdcount
, &buf
[i
]); i
+= 2;
1470 DO_NTOHS(ancount
, &buf
[i
]); i
+= 2;
1471 DO_NTOHS(nscount
, &buf
[i
]); i
+= 2;
1472 DO_NTOHS(arcount
, &buf
[i
]); i
+= 2;
1475 "DNS id %d, b23 0x%x, qdcount %d, ancount %d, "
1476 "nscount %d, arcount %d\n",
1477 id
, b23
, qdcount
, ancount
, nscount
, arcount
);
1479 if((b23
& (0xF870)) != 0x8000) {
1480 do_log(L_ERROR
, "Incorrect DNS reply (b23 = 0x%x).\n", b23
);
1481 error
= EDNS_INVALID
;
1485 dnserror
= b23
& 0xF;
1488 do_log(L_WARN
, "Truncated DNS reply (b23 = 0x%x).\n", b23
);
1491 if(dnserror
|| qdcount
!= 1) {
1494 "Unexpected number %d of DNS questions.\n", qdcount
);
1496 error
= EDNS_FORMAT
;
1497 else if(dnserror
== 2)
1498 error
= EDNS_NO_RECOVERY
;
1499 else if(dnserror
== 3)
1500 error
= EDNS_HOST_NOT_FOUND
;
1501 else if(dnserror
== 4 || dnserror
== 5)
1502 error
= EDNS_REFUSED
;
1503 else if(dnserror
== 0)
1504 error
= EDNS_INVALID
;
1510 /* We do this early, so that we can return the address family to
1511 the caller in case of error. */
1512 i
= labelsToString(buf
, i
, n
, b
, 2048, &m
);
1514 error
= EDNS_FORMAT
;
1517 DO_NTOHS(type
, &buf
[i
]); i
+= 2;
1518 DO_NTOHS(class, &buf
[i
]); i
+= 2;
1525 error
= EDNS_FORMAT
;
1529 do_log(D_DNS
, "DNS q: ");
1530 do_log_n(D_DNS
, b
, m
);
1531 do_log(D_DNS
, " (%d, %d)\n", type
, class);
1532 name
= internAtomLowerN(b
, m
);
1539 error
= EDNS_FORMAT
;
1543 #define PARSE_ANSWER(kind, label) \
1545 i = labelsToString(buf, i, 1024, b, 2048, &m); \
1546 if(i < 0) goto label; \
1547 DO_NTOHS(type, &buf[i]); i += 2; if(i > 1024) goto label; \
1548 DO_NTOHS(class, &buf[i]); i += 2; if(i > 1024) goto label; \
1549 DO_NTOHL(ttl, &buf[i]); i += 4; if(i > 1024) goto label; \
1550 DO_NTOHS(rdlength, &buf[i]); i += 2; if(i > 1024) goto label; \
1551 do_log(D_DNS, "DNS " kind ": "); \
1552 do_log_n(D_DNS, b, m); \
1553 do_log(D_DNS, " (%d, %d): %d bytes, ttl %u\n", \
1554 type, class, rdlength, ttl); \
1558 for(j
= 0; j
< ancount
; j
++) {
1559 PARSE_ANSWER("an", fail
);
1560 if(strcasecmp_n(name
->string
, b
, m
) == 0) {
1562 do_log(D_DNS
, "DNS: %s: unknown class %d.\n",
1563 name
->string
, class);
1564 error
= EDNS_UNSUPPORTED
;
1567 if(type
== 1 || type
== 28) {
1568 if((type
== 1 && rdlength
!= 4) ||
1569 (type
== 28 && rdlength
!= 16)) {
1571 "DNS: %s: unexpected length %d of %s record.\n",
1572 name
->string
, rdlength
, type
== 1 ? "A" : "AAAA");
1573 error
= EDNS_INVALID
;
1574 if(rdlength
<= 0 || rdlength
>= 32)
1579 do_log(L_WARN
, "DNS: %s: host has both A and CNAME -- "
1580 "ignoring CNAME.\n", name
->string
);
1588 do_log(L_WARN
, "Unexpected AAAA reply.\n");
1595 do_log(L_WARN
, "Unexpected A reply.\n");
1600 if(addr_index
== 0) {
1601 addresses
[0] = DNS_A
;
1604 if(addr_index
> 1000) {
1605 error
= EDNS_INVALID
;
1609 assert(addresses
[0] == DNS_A
);
1612 memset(&addresses
[addr_index
], 0, sizeof(HostAddressRec
));
1614 addresses
[addr_index
] = 4;
1615 memcpy(addresses
+ addr_index
+ 1, buf
+ i
, 4);
1617 addresses
[addr_index
] = 6;
1618 memcpy(addresses
+ addr_index
+ 1, buf
+ i
, 16);
1620 addr_index
+= sizeof(HostAddressRec
);
1621 } else if(type
== 5) {
1623 if(af
!= 0 && addr_index
> 0) {
1624 do_log(L_WARN
, "DNS: host has both CNAME and A -- "
1625 "ignoring CNAME.\n");
1630 if(addr_index
!= 0) {
1631 /* Only warn if the CNAMEs are not identical */
1632 char tmp
[512]; int jj
, kk
;
1633 assert(addresses
[0] == DNS_CNAME
);
1634 jj
= labelsToString(buf
, i
, n
,
1637 kk
!= strlen(addresses
+ 1) ||
1638 memcmp(addresses
+ 1, tmp
, kk
) != 0) {
1639 do_log(L_WARN
, "DNS: "
1640 "%s: host has multiple CNAMEs -- "
1641 "ignoring subsequent.\n", name
->string
);
1646 addresses
[0] = DNS_CNAME
;
1648 j
= labelsToString(buf
, i
, n
,
1649 addresses
+ 1, 1020, &k
);
1652 error
= ENAMETOOLONG
;
1657 error
= EDNS_NO_ADDRESS
;
1667 #if (LOGGING & D_DNS)
1668 for(j
= 0; j
< nscount
; j
++) {
1669 PARSE_ANSWER("ns", nofail
);
1673 for(j
= 0; j
< arcount
; j
++) {
1674 PARSE_ANSWER("ar", nofail
);
1683 do_log(D_DNS
, "DNS: %d bytes\n", addr_index
);
1687 value
= internAtomN(addresses
, addr_index
);
1695 *name_return
= name
;
1696 *value_return
= value
;
1698 *ttl_return
= final_ttl
;
1703 *name_return
= name
;
1704 *value_return
= NULL
;
1712 really_do_dns(AtomPtr name
, ObjectPtr object
)