root / trunk / src / dot / proxy / dns.c

Revision 7627, 48.7 kB (checked in by BradNeuberg, 22 months ago)

Doing QA of our automatic network detection and request replay code; this has several bug fixes based on things that were found. The biggest change is we now shorten our timeouts for cancelling network communication, so after 5 seconds we cancel a server proxy communication and attempt to move offline and replay the request. A small fix for assert() failures that also popped up under some conditions, and one where we still try to replay a request even if we think we are offline.

Line 
1/*
2Copyright (c) 2003-2006 by Juliusz Chroboczek
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"), to deal
6in the Software without restriction, including without limitation the rights
7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8copies of the Software, and to permit persons to whom the Software is
9furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20THE SOFTWARE.
21*/
22
23#include "polipo.h"
24
25#ifndef NO_STANDARD_RESOLVER
26#ifndef NO_FANCY_RESOLVER
27int dnsUseGethostbyname = 1;
28#else 
29const int dnsUseGethostbyname = 3;
30#endif
31#else
32#ifndef NO_FANCY_RESOLVER
33const int dnsUseGethostbyname = 0;
34#else
35#error use no resolver at all?
36#endif
37#endif
38
39#ifndef NO_FANCY_RESOLVER
40AtomPtr dnsNameServer = NULL;
41int dnsMaxTimeout = 60;
42#endif
43
44#ifndef NO_STANDARD_RESOLVER
45int dnsGethostbynameTtl = 1200;
46#endif
47
48int dnsNegativeTtl = 120;
49
50#ifdef HAVE_IPv6
51int dnsQueryIPv6 = 2;
52#else
53const int dnsQueryIPv6 = 0;
54#endif
55
56typedef 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;
67
68union {
69    struct sockaddr sa;
70    struct sockaddr_in sin;
71#ifdef HAVE_IPv6
72    struct sockaddr_in6 sin6;
73#endif
74} nameserverAddress_storage;
75
76#ifndef NO_FANCY_RESOLVER
77static AtomPtr atomLocalhost, atomLocalhostDot;
78
79#define nameserverAddress nameserverAddress_storage.sa
80
81static DnsQueryPtr inFlightDnsQueries;
82static DnsQueryPtr inFlightDnsQueriesLast;
83#endif
84
85static int really_do_gethostbyname(AtomPtr name, ObjectPtr object);
86static int really_do_dns(AtomPtr name, ObjectPtr object);
87
88#ifndef NO_FANCY_RESOLVER
89static int stringToLabels(char *buf, int offset, int n, char *string);
90static int labelsToString(char *buf, int offset, int n, char *d,
91                          int m, int *j_return);
92static int dnsBuildQuery(int id, char *buf, int offset, int n,
93                         AtomPtr name, int af);
94static int dnsReplyHandler(int abort, FdEventHandlerPtr event);
95static int dnsReplyId(char *buf, int offset, int n, int *id_return);
96static int dnsDecodeReply(char *buf, int offset, int n,
97                          int *id_return,
98                          AtomPtr *name_return, AtomPtr *value_return,
99                          int *af_return, unsigned *ttl_return);
100static int dnsHandler(int status, ConditionHandlerPtr chandler);
101static int dnsGethostbynameFallback(int id, AtomPtr message);
102static int sendQuery(DnsQueryPtr query);
103
104static int idSeed;
105#endif
106
107#ifndef NO_FANCY_RESOLVER
108static int
109parseResolvConf(char *filename)
110{
111    FILE *f;
112    char buf[512];
113    char *p, *q;
114    int n;
115    AtomPtr nameserver = NULL;
116
117    f = fopen(filename, "r");
118    if(f == NULL) {
119        do_log_error(L_ERROR, errno, "DNS: couldn't open %s", filename);
120        return 0;
121    }
122
123    while(1) {
124        p = fgets(buf, 512, f);
125        if(p == NULL)
126            break;
127
128        n = strlen(buf);
129        if(buf[n - 1] != '\n') {
130            int c;
131            do_log(L_WARN, "DNS: overly long line in %s -- skipping.\n",
132                   filename);
133            do {
134                c = fgetc(f);
135                if(c == EOF)
136                    break;
137            } while(c != '\n');
138            if(c == EOF)
139                break;
140        }
141       
142        while(*p == ' ' || *p == '\t')
143            p++;
144        if(strcasecmp_n("nameserver", p, 10) != 0)
145            continue;
146        p += 10;
147        while(*p == ' ' || *p == '\t')
148            p++;
149        q = p;
150        while(*q == '.' || *q == ':' || digit(*q) || letter(*q))
151            q++;
152        if(*q != ' ' && *q != '\t' && *q != '\r' && *q != '\n') {
153            do_log(L_WARN, "DNS: couldn't parse line in %s -- skipping.\n",
154                   filename);
155            continue;
156        }
157        nameserver = internAtomLowerN(p, q - p);
158        break;
159    }
160
161    fclose(f);
162    if(nameserver) {
163        dnsNameServer = nameserver;
164        return 1;
165    } else {
166        return 0;
167    }
168}
169#endif
170
171void
172preinitDns()
173{
174#ifdef HAVE_IPv6
175    int fd;
176#endif
177
178    assert(sizeof(struct in_addr) == 4);
179#ifdef HAVE_IPv6
180    assert(sizeof(struct in6_addr) == 16);
181#endif
182
183#ifndef NO_STANDARD_RESOLVER
184    CONFIG_VARIABLE(dnsGethostbynameTtl, CONFIG_TIME,
185                    "TTL for gethostbyname addresses.");
186#endif
187
188#ifdef HAVE_IPv6
189    fd = socket(PF_INET6, SOCK_STREAM, 0);
190    if(fd < 0) {
191        if(errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT) {
192            dnsQueryIPv6 = 0;
193        } else {
194            do_log_error(L_WARN, errno, "DNS: couldn't create socket");
195        }
196    } else {
197        close(fd);
198    }
199#endif
200
201#ifndef NO_FANCY_RESOLVER
202    parseResolvConf("/etc/resolv.conf");
203    if(dnsNameServer == NULL || dnsNameServer->string[0] == '\0')
204        dnsNameServer = internAtom("127.0.0.1");
205    CONFIG_VARIABLE(dnsMaxTimeout, CONFIG_TIME,
206                    "Max timeout for DNS queries.");
207    CONFIG_VARIABLE(dnsNegativeTtl, CONFIG_TIME,
208                    "TTL for negative DNS replies with no TTL.");
209    CONFIG_VARIABLE(dnsNameServer, CONFIG_ATOM_LOWER,
210                    "The name server to use.");
211#ifndef NO_STANDARD_RESOLVER
212    CONFIG_VARIABLE(dnsUseGethostbyname, CONFIG_TETRASTATE,
213                    "Use the system resolver.");
214#endif
215#endif
216
217#ifdef HAVE_IPv6
218    CONFIG_VARIABLE(dnsQueryIPv6, CONFIG_TETRASTATE,
219                    "Query for IPv6 addresses.");
220#endif
221}
222
223void
224initDns()
225{
226#ifndef NO_FANCY_RESOLVER
227    int rc;
228    struct timeval t;
229    struct sockaddr_in *sin = (struct sockaddr_in*)&nameserverAddress;
230#ifdef HAVE_IPv6
231    struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&nameserverAddress;
232#endif
233
234    atomLocalhost = internAtom("localhost");
235    atomLocalhostDot = internAtom("localhost.");
236    inFlightDnsQueries = NULL;
237    inFlightDnsQueriesLast = NULL;
238
239    gettimeofday(&t, NULL);
240    idSeed = t.tv_usec & 0xFFFF;
241    sin->sin_family = AF_INET;
242    sin->sin_port = htons(53);
243    rc = inet_aton(dnsNameServer->string, &sin->sin_addr);
244#ifdef HAVE_IPv6
245    if(rc != 1) {
246        sin6->sin6_family = AF_INET6;
247        sin6->sin6_port = htons(53);
248        rc = inet_pton(AF_INET6, dnsNameServer->string, &sin6->sin6_addr);
249    }
250#endif
251    if(rc != 1) {
252        do_log(L_ERROR, "DNS: couldn't parse name server %s.\n",
253               dnsNameServer->string);
254        exit(1);
255    }
256#endif
257}
258
259int
260do_gethostbyname(char *origname,
261                 int count,
262                 int (*handler)(int, GethostbynameRequestPtr),
263                 void *data)
264{
265    ObjectPtr object;
266    int n = strlen(origname);
267    AtomPtr name;
268    GethostbynameRequestRec request;
269    int done, rc;
270
271    memset(&request, 0, sizeof(request));
272    request.name = NULL;
273    request.addr = NULL;
274    request.error_message = NULL;
275    request.count = count;
276    request.handler = handler;
277    request.data = data;
278
279    if(n <= 0 || n > 131) {
280        if(n <= 0) {
281            request.error_message = internAtom("empty name");
282            do_log(L_ERROR, "Empty DNS name.\n");
283            done = handler(-EINVAL, &request);
284        } else {
285            request.error_message = internAtom("name too long");
286            do_log(L_ERROR, "DNS name too long.\n");
287            done = handler(-ENAMETOOLONG, &request);
288        }
289        assert(done);
290        releaseAtom(request.error_message);
291        return 1;
292    }
293
294    if(origname[n - 1] == '.')
295        n--;
296
297    name = internAtomLowerN(origname, n);
298
299    if(name == NULL) {
300        request.error_message = internAtom("couldn't allocate name");
301        do_log(L_ERROR, "Couldn't allocate DNS name.\n");
302        done = handler(-ENOMEM, &request);
303        assert(done);
304        releaseAtom(request.error_message);
305        return 1;
306    }
307
308    request.name = name;
309    request.addr = NULL;
310    request.error_message = NULL;
311    request.count = count;
312    request.object = NULL;
313    request.handler = handler;
314    request.data = data;
315
316    object = findObject(OBJECT_DNS, name->string, name->length);
317    if(object == NULL || objectMustRevalidate(object, NULL)) {
318        if(object) {
319            privatiseObject(object, 0);
320            releaseObject(object);
321        }
322        object = makeObject(OBJECT_DNS, name->string, name->length, 1, 0,
323                            NULL, NULL);
324        if(object == NULL) {
325            request.error_message = internAtom("Couldn't allocate object");
326            do_log(L_ERROR, "Couldn't allocate DNS object.\n");
327            done = handler(-ENOMEM, &request);
328            assert(done);
329            releaseAtom(name);
330            releaseAtom(request.error_message);
331            return 1;
332        }
333    }
334
335    if((object->flags & (OBJECT_INITIAL | OBJECT_INPROGRESS)) ==
336       OBJECT_INITIAL) {
337        if(dnsUseGethostbyname >= 3)
338            rc = really_do_gethostbyname(name, object);
339        else
340            rc = really_do_dns(name, object);
341        if(rc < 0) {
342            assert(!(object->flags & (OBJECT_INITIAL | OBJECT_INPROGRESS)));
343            goto fail;
344        }
345    }
346
347    if(dnsUseGethostbyname >= 3)
348        assert(!(object->flags & OBJECT_INITIAL));
349
350#ifndef NO_FANCY_RESOLVER   
351    if(object->flags & OBJECT_INITIAL) {
352        ConditionHandlerPtr chandler;
353        assert(object->flags & OBJECT_INPROGRESS);
354        request.object = object;
355        chandler = conditionWait(&object->condition, dnsHandler,
356                                 sizeof(request), &request);
357        if(chandler == NULL) {
358            rc = ENOMEM;
359            goto fail;
360        }
361        return 1;
362    }
363#endif
364
365    if(object->headers && object->headers->length > 0) {
366        if(object->headers->string[0] == DNS_A)
367            assert(((object->headers->length - 1) %
368                    sizeof(HostAddressRec)) == 0);
369        else
370            assert(object->headers->string[0] == DNS_CNAME);
371        request.addr = retainAtom(object->headers);
372    } else if(object->message) {
373        request.error_message = retainAtom(object->message);
374    }
375
376    releaseObject(object);
377
378    if(request.addr && request.addr->length > 0)
379        done = handler(1, &request);
380    else
381        done = handler(-EDNS_HOST_NOT_FOUND, &request);
382    assert(done);
383
384    releaseAtom(request.addr); request.addr = NULL;
385    releaseAtom(request.name); request.name = NULL;
386    releaseAtom(request.error_message); request.error_message = NULL;
387    return 1;
388
389 fail:
390    releaseNotifyObject(object);
391    done = handler(-errno, &request);
392    assert(done);
393    releaseAtom(name);
394    return 1;
395}
396
397static int
398dnsDelayedErrorNotifyHandler(TimeEventHandlerPtr event)
399{
400    int done;
401    GethostbynameRequestRec request =
402        *(GethostbynameRequestPtr)event->data;
403    done = request.handler(-EDNS_HOST_NOT_FOUND, &request);
404    assert(done);
405    releaseAtom(request.name); request.name = NULL;
406    releaseAtom(request.addr); request.addr = NULL;
407    releaseAtom(request.error_message); request.error_message = NULL;
408    return 1;
409}
410   
411static int
412dnsDelayedDoneNotifyHandler(TimeEventHandlerPtr event)
413{
414    int done;
415    GethostbynameRequestRec request = *(GethostbynameRequestPtr)event->data;
416    done = request.handler(1, &request);
417    assert(done);
418    releaseAtom(request.name); request.name = NULL;
419    releaseAtom(request.addr); request.addr = NULL;
420    releaseAtom(request.error_message); request.error_message = NULL;
421    return 1;
422}
423
424static int
425dnsDelayedNotify(int error, GethostbynameRequestPtr request)
426{
427    TimeEventHandlerPtr handler;
428
429    if(error)
430        handler = scheduleTimeEvent(0,
431                                    dnsDelayedErrorNotifyHandler,
432                                    sizeof(*request), request);
433    else
434        handler = scheduleTimeEvent(0,
435                                    dnsDelayedDoneNotifyHandler,
436                                    sizeof(*request), request);
437    if(handler == NULL) {
438        do_log(L_ERROR, "Couldn't schedule DNS notification.\n");
439        return -1;
440    }
441    return 1;
442}
443
444#ifdef HAVE_IPv6
445AtomPtr
446rfc2732(AtomPtr name)
447{
448    char buf[38];
449    int rc;
450    AtomPtr a = NULL;
451
452    if(name->length < 38 &&
453       name->string[0] == '[' && name->string[name->length - 1] == ']') {
454        struct in6_addr in6a;
455        memcpy(buf, name->string + 1, name->length - 2);
456        buf[name->length - 2] = '\0';
457        rc = inet_pton(AF_INET6, buf, &in6a);
458        if(rc == 1) {
459            char s[1 + sizeof(HostAddressRec)];
460            memset(s, 0, sizeof(s));
461            s[0] = DNS_A;
462            s[1] = 6;
463            memcpy(s + 2, &in6a, 16);
464            a = internAtomN(s, 1 + sizeof(HostAddressRec));
465            if(a == NULL)
466                return NULL;
467        }
468    }
469    return a;
470}
471
472/* Used for sorting host addresses depending on the value of dnsQueryIPv6 */
473int
474compare_hostaddr(const void *av, const void *bv)
475{
476    const HostAddressRec *a = av, *b = bv;
477    int r;
478    if(a->af == 4) {
479        if(b->af == 4)
480            r = 0;
481        else
482            r = -1;
483    } else {
484        if(b->af == 6)
485            r = 0;
486        else
487            r = 1;
488    }
489    if(dnsQueryIPv6 >= 2)
490        return -r;
491    else
492        return r;
493}
494
495#ifndef NO_STANDARD_RESOLVER
496static int
497really_do_gethostbyname(AtomPtr name, ObjectPtr object)
498{
499    struct addrinfo *ai, *entry, hints;
500    int rc;
501    int error, i;
502    char buf[1024];
503    AtomPtr a;
504
505    a = rfc2732(name);
506    if(a) {
507        object->headers = a;
508        object->age = current_time.tv_sec;
509        object->expires = current_time.tv_sec + 240;
510        object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
511        notifyObject(object);
512        return 0;
513    }
514
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;
521
522    rc = getaddrinfo(name->string, NULL, &hints, &ai);
523
524    switch(rc) {
525    case 0: error = 0; break;
526    case EAI_FAMILY:
527#ifdef EAI_ADDRFAMILY
528    case EAI_ADDRFAMILY:
529#endif
530    case EAI_SOCKTYPE:
531        error = EAFNOSUPPORT; break;
532    case EAI_BADFLAGS: error = EINVAL; break;
533    case EAI_SERVICE: error = EDNS_NO_RECOVERY; break;
534#ifdef EAI_NONAME
535    case EAI_NONAME:
536#endif
537#ifdef EAI_NODATA
538    case EAI_NODATA:
539#endif
540        error = EDNS_NO_ADDRESS; break;
541    case EAI_FAIL: error = EDNS_NO_RECOVERY; break;
542    case EAI_AGAIN: error = EDNS_TRY_AGAIN; break;
543#ifdef EAI_MEMORY
544    case EAI_MEMORY: error = ENOMEM; break;
545#endif
546    case EAI_SYSTEM: error = errno; break;
547    default: error = EUNKNOWN;
548    }
549
550    if(error == EDNS_NO_ADDRESS) {
551        object->headers = NULL;
552        object->age = current_time.tv_sec;
553        object->expires = current_time.tv_sec + dnsNegativeTtl;
554        object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
555        notifyObject(object);
556        return 0;
557    } else if(error) {
558        do_log_error(L_ERROR, error, "Getaddrinfo failed");
559        object->flags &= ~OBJECT_INPROGRESS;
560        abortObject(object, 404,
561                    internAtomError(error, "Getaddrinfo failed"));
562        notifyObject(object);
563        return 0;
564    }
565
566    entry = ai;
567    buf[0] = DNS_A;
568    i = 0;
569    while(entry) {
570        HostAddressRec host;
571        int host_valid = 0;
572        if(entry->ai_family == AF_INET && entry->ai_protocol == IPPROTO_TCP) {
573            if(dnsQueryIPv6 < 3) {
574                host.af = 4;
575                memset(host.data, 0, sizeof(host.data));
576                memcpy(&host.data,
577                       &((struct sockaddr_in*)entry->ai_addr)->sin_addr,
578                       4);
579                host_valid = 1;
580            }
581        } else if(entry->ai_family == AF_INET6 &&
582                  entry->ai_protocol == IPPROTO_TCP) {
583            if(dnsQueryIPv6 > 0) {
584                host.af = 6;
585                memset(&host.data, 0, sizeof(host.data));
586                memcpy(&host.data,
587                       &((struct sockaddr_in6*)entry->ai_addr)->sin6_addr,
588                       16);
589                host_valid = 1;
590            }
591        }
592        if(host_valid) {
593            if(i >= 1024 / sizeof(HostAddressRec) - 2) {
594                do_log(L_ERROR, "Too many addresses for host %s\n",
595                       name->string);
596                break;
597            }
598            memcpy(buf + 1 + i * sizeof(HostAddressRec),
599                   &host, sizeof(HostAddressRec));
600            i++;
601        }
602        entry = entry->ai_next;
603    }
604    freeaddrinfo(ai);
605    if(i == 0) {
606        do_log(L_ERROR, "Getaddrinfo returned no useful addresses\n");
607        object->flags &= ~OBJECT_INPROGRESS;
608        abortObject(object, 404,
609                    internAtom("Getaddrinfo returned no useful addresses"));
610        notifyObject(object);
611        return 0;
612    }
613
614    if(1 <= dnsQueryIPv6 && dnsQueryIPv6 <= 2)
615        qsort(buf + 1, i, sizeof(HostAddressRec), compare_hostaddr);
616
617    a = internAtomN(buf, 1 + i * sizeof(HostAddressRec));
618    if(a == NULL) {
619        object->flags &= ~OBJECT_INPROGRESS;
620        abortObject(object, 501, internAtom("Couldn't allocate address"));
621        notifyObject(object);
622        return 0;
623    }
624    object->headers = a;
625    object->age = current_time.tv_sec;
626    object->expires = current_time.tv_sec + dnsGethostbynameTtl;
627    object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
628    notifyObject(object);
629    return 0;
630}
631#endif
632   
633#else
634
635#ifndef NO_STANDARD_RESOLVER
636static int
637really_do_gethostbyname(AtomPtr name, ObjectPtr object)
638{
639    struct hostent *host;
640    char *s;
641    AtomPtr a;
642    int i, j;
643    int error;
644
645    host = gethostbyname(name->string);
646    if(host == NULL) {
647        switch(h_errno) {
648        case HOST_NOT_FOUND: error = EDNS_HOST_NOT_FOUND; break;
649        case NO_ADDRESS: error = EDNS_NO_ADDRESS; break;
650        case NO_RECOVERY: error = EDNS_NO_RECOVERY; break;
651        case TRY_AGAIN: error = EDNS_TRY_AGAIN; break;
652        default: error = EUNKNOWN; break;
653        }
654        if(error == EDNS_HOST_NOT_FOUND) {
655            object->headers = NULL;
656            object->age = current_time.tv_sec;
657            object->expires = current_time.tv_sec + dnsNegativeTtl;
658            object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
659            object->flags &= ~OBJECT_INPROGRESS;
660            notifyObject(object);
661            return 0;
662        } else {
663            do_log_error(L_ERROR, error, "Gethostbyname failed");
664            abortObject(object, 404,
665                        internAtomError(error, "Gethostbyname failed"));
666            object->flags &= ~OBJECT_INPROGRESS;
667            notifyObject(object);
668            return 0;
669        }
670    }
671    if(host->h_addrtype != AF_INET) {
672        do_log(L_ERROR, "Address is not AF_INET.\n");
673        object->flags &= ~OBJECT_INPROGRESS;
674        abortObject(object, 404, internAtom("Address is not AF_INET"));
675        notifyObject(object);
676        return -1;
677    }
678    if(host->h_length != sizeof(struct in_addr)) {
679        do_log(L_ERROR, "Address size inconsistent.\n");
680        object->flags &= ~OBJECT_INPROGRESS;
681        abortObject(object, 404, internAtom("Address size inconsistent"));
682        notifyObject(object);
683        return 0;
684    }
685    i = 0;
686    while(host->h_addr_list[i] != NULL) i++;
687    s = malloc(1 + i * sizeof(HostAddressRec));
688    if(s == NULL) {
689        a = NULL;
690    } else {
691        memset(s, 0, 1 + i * sizeof(HostAddressRec));
692        s[0] = DNS_A;
693        for(j = 0; j < i; j++) {
694            s[j * sizeof(HostAddressRec) + 1] = 4;
695            memcpy(&s[j * sizeof(HostAddressRec) + 2], host->h_addr_list[j],
696                   sizeof(struct in_addr));
697        }
698        a = internAtomN(s, i * sizeof(HostAddressRec) + 1);
699        free(s);
700    }
701    if(!a) {
702        object->flags &= ~OBJECT_INPROGRESS;
703        abortObject(object, 501, internAtom("Couldn't allocate address"));
704        notifyObject(object);
705        return 0;
706    }
707    object->headers = a;
708    object->age = current_time.tv_sec;
709    object->expires = current_time.tv_sec + dnsGethostbynameTtl;
710    object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
711    notifyObject(object);
712    return 0;
713
714}
715#endif
716
717#endif
718
719#ifdef NO_STANDARD_RESOLVER
720static int
721really_do_gethostbyname(AtomPtr name, ObjectPtr object)
722{
723    abort();
724}
725#endif
726
727#ifndef NO_FANCY_RESOLVER
728
729static int dnsSocket = -1;
730static FdEventHandlerPtr dnsSocketHandler = NULL;
731
732static int
733dnsHandler(int status, ConditionHandlerPtr chandler)
734{
735    GethostbynameRequestRec request = *(GethostbynameRequestPtr)chandler->data;
736    ObjectPtr object = request.object;
737
738    assert(!(object->flags & OBJECT_INPROGRESS));
739
740    if(object->headers) {
741        request.addr = retainAtom(object->headers);
742        dnsDelayedNotify(0, &request);
743    } else {
744        if(object->message)
745            request.error_message = retainAtom(object->message);
746        dnsDelayedNotify(1, &request);
747    }
748    releaseObject(object);
749    return 1;
750}
751
752static void
753removeQuery(DnsQueryPtr query)
754{
755    DnsQueryPtr previous;
756    if(query == inFlightDnsQueries) {
757        inFlightDnsQueries = query->next;
758        if(inFlightDnsQueries == NULL)
759            inFlightDnsQueriesLast = NULL;
760    } else {
761        previous = inFlightDnsQueries;
762        while(previous->next) {
763            if(previous->next == query)
764                break;
765            previous = previous->next;
766        }
767        assert(previous->next != NULL);
768        previous->next = query->next;
769        if(previous->next == NULL)
770            inFlightDnsQueriesLast = previous;
771    }
772}
773
774static void
775insertQuery(DnsQueryPtr query)
776{
777    if(inFlightDnsQueriesLast)
778        inFlightDnsQueriesLast->next = query;
779    else
780        inFlightDnsQueries = query;
781    inFlightDnsQueriesLast = query;
782}
783
784static DnsQueryPtr
785findQuery(int id, AtomPtr name)
786{
787    DnsQueryPtr query;
788    query = inFlightDnsQueries;
789    while(query) {
790        if(query->id == id && (name == NULL || query->name == name))
791            return query;
792        query = query->next;
793    }
794    return NULL;
795}
796
797static int
798dnsTimeoutHandler(TimeEventHandlerPtr event)
799{
800    DnsQueryPtr query = *(DnsQueryPtr*)event->data;
801    ObjectPtr object = query->object;
802    int rc;
803
804    query->timeout = MAX(10, query->timeout * 2);
805
806    if(query->timeout > dnsMaxTimeout) {
807        abortObject(object, 501, internAtom("Timeout"));
808        goto fail;
809    } else {
810        rc = sendQuery(query);
811        if(rc < 0) {
812            if(rc != -EWOULDBLOCK && rc != -EAGAIN && rc != -ENOBUFS) {
813                abortObject(object, 501,
814                            internAtomError(-rc,
815                                            "Couldn't send DNS query"));
816                goto fail;
817            }
818            /* else let it timeout */
819        }
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"));
827            goto fail;
828        }
829        return 1;
830    }
831
832 fail:
833    removeQuery(query);
834    object->flags &= ~OBJECT_INPROGRESS;
835    if(query->inet4) releaseAtom(query->inet4);
836    if(query->inet6) releaseAtom(query->inet6);
837    free(query);
838    releaseNotifyObject(object);
839    return 1;
840}
841
842static int
843establishDnsSocket()
844{
845    int rc;
846#ifdef HAVE_IPv6
847    int inet6 = (nameserverAddress.sa_family == AF_INET6);
848    int pf = inet6 ? PF_INET6 : PF_INET;
849    int sa_size =
850        inet6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
851#else
852    int pf = PF_INET;
853    int sa_size = sizeof(struct sockaddr_in);
854#endif
855
856    if(dnsSocket < 0) {
857        assert(!dnsSocketHandler);
858        dnsSocket = socket(pf, SOCK_DGRAM, 0);
859        if(dnsSocket < 0) {
860            do_log_error(L_ERROR, errno, "Couldn't create DNS socket");
861            return -errno;
862        }
863
864        rc = connect(dnsSocket, &nameserverAddress, sa_size);
865        if(rc < 0) {
866            close(dnsSocket);
867            dnsSocket = -1;
868            do_log_error(L_ERROR, errno, "Couldn't create DNS \"connection\"");
869            return -errno;
870        }
871    }
872
873    if(!dnsSocketHandler) {
874        dnsSocketHandler =
875            registerFdEvent(dnsSocket, POLLIN, dnsReplyHandler, 0, NULL);
876        if(dnsSocketHandler == NULL) {
877            do_log(L_ERROR, "Couldn't register DNS socket handler.\n");
878            close(dnsSocket);
879            dnsSocket = -1;
880            return -ENOMEM;
881        }
882    }
883
884    return 1;
885}
886
887static int
888sendQuery(DnsQueryPtr query)
889{
890    char buf[512];
891    int buflen;
892    int rc;
893    int af[2];
894    int i;
895
896    if(dnsSocket < 0)
897        return -1;
898
899    if(dnsQueryIPv6 <= 0) {
900        af[0] = 4; af[1] = 0;
901    } else if(dnsQueryIPv6 <= 2) {
902        af[0] = 4; af[1] = 6;
903    } else {
904        af[0] = 6; af[1] = 0;
905    }
906
907    for(i = 0; i < 2; i++) {
908        if(af[i] == 0)
909            continue;
910        if(af[i] == 4 && query->inet4)
911            continue;
912        else if(af[i] == 6 && query->inet6)
913            continue;
914
915        buflen = dnsBuildQuery(query->id, buf, 0, 512, query->name, af[i]);
916        if(buflen <= 0) {
917            do_log(L_ERROR, "Couldn't build DNS query.\n");
918            return buflen;
919        }
920
921        rc = send(dnsSocket, buf, buflen, 0);
922        if(rc < buflen) {
923            if(rc >= 0) {
924                do_log(L_ERROR, "Couldn't send DNS query: partial send.\n");
925                return -EAGAIN;
926            } else {
927                do_log_error(L_ERROR, errno, "Couldn't send DNS query");
928                return -errno;
929            }
930        }
931    }
932    return 1;
933}
934
935static int
936really_do_dns(AtomPtr name, ObjectPtr object)
937{
938    int rc;
939    DnsQueryPtr query;
940    AtomPtr message = NULL;
941    int id;
942    AtomPtr a = NULL;
943
944    if(a == NULL) {
945        if(name == atomLocalhost || name == atomLocalhostDot) {
946            char s[1 + sizeof(HostAddressRec)];
947            memset(s, 0, sizeof(s));
948            s[0] = DNS_A;
949            s[1] = 4;
950            s[2] = 127;
951            s[3] = 0;
952            s[4] = 0;
953            s[5] = 1;
954            a = internAtomN(s, 1 + sizeof(HostAddressRec));
955            if(a == NULL) {
956                abortObject(object, 501,
957                            internAtom("Couldn't allocate address"));
958                notifyObject(object);
959                errno = ENOMEM;
960                return -1;
961            }
962        }
963    }
964
965    if(a == NULL) {
966        struct in_addr ina;
967        rc = inet_aton(name->string, &ina);
968        if(rc == 1) {
969            char s[1 + sizeof(HostAddressRec)];
970            memset(s, 0, sizeof(s));
971            s[0] = DNS_A;
972            s[1] = 4;
973            memcpy(s + 2, &ina, 4);
974            a = internAtomN(s, 1 + sizeof(HostAddressRec));
975            if(a == NULL) {
976                abortObject(object, 501,
977                            internAtom("Couldn't allocate address"));
978                notifyObject(object);
979                errno = ENOMEM;
980                return -1;
981            }
982        }
983    }
984#ifdef HAVE_IPv6
985    if(a == NULL)
986        a = rfc2732(name);
987#endif
988
989    if(a) {
990        object->headers = a;
991        object->age = current_time.tv_sec;
992        object->expires = current_time.tv_sec + 240;
993        object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
994        notifyObject(object);
995        return 0;
996    }
997
998    rc = establishDnsSocket();
999    if(rc < 0) {
1000        do_log_error(L_ERROR, -rc, "Couldn't establish DNS socket.\n");
1001        message = internAtomError(-rc, "Couldn't establish DNS socket");
1002        goto fallback;
1003    }
1004
1005    /* The id is used to speed up detecting replies to queries that
1006       are no longer current -- see dnsReplyHandler. */
1007    id = (idSeed++) & 0xFFFF;
1008
1009    query = malloc(sizeof(DnsQueryRec));
1010    if(query == NULL) {
1011        do_log(L_ERROR, "Couldn't allocate DNS query.\n");
1012        message = internAtom("Couldn't allocate DNS query");
1013        goto fallback;
1014    }
1015    query->id = id;
1016    query->inet4 = NULL;
1017    query->inet6 = NULL;
1018    query->name = name;
1019    query->time = current_time.tv_sec;
1020    query->object = retainObject(object);
1021    query->timeout = 4;
1022    query->timeout_handler = NULL;
1023    query->next = NULL;
1024
1025    query->timeout_handler =
1026        scheduleTimeEvent(query->timeout, dnsTimeoutHandler,
1027