1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756 |
- /*
- Copyright (c) 2003-2006 by Juliusz Chroboczek
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
- #include "polipo.h"
- #ifndef NO_STANDARD_RESOLVER
- #ifndef NO_FANCY_RESOLVER
- int dnsUseGethostbyname = 1;
- #else
- const int dnsUseGethostbyname = 3;
- #endif
- #else
- #ifndef NO_FANCY_RESOLVER
- const int dnsUseGethostbyname = 0;
- #else
- #error use no resolver at all?
- #endif
- #endif
- #ifndef NO_FANCY_RESOLVER
- AtomPtr dnsNameServer = NULL;
- int dnsMaxTimeout = 60;
- int dnsNameServerPort = 53;
- #endif
- #ifndef NO_STANDARD_RESOLVER
- int dnsGethostbynameTtl = 1200;
- #endif
- int dnsNegativeTtl = 120;
- #ifdef HAVE_IPv6
- int dnsQueryIPv6 = 2;
- #else
- const int dnsQueryIPv6 = 0;
- #endif
- typedef struct _DnsQuery {
- unsigned id;
- AtomPtr name;
- ObjectPtr object;
- AtomPtr inet4, inet6;
- time_t ttl4, ttl6;
- time_t time;
- int timeout;
- TimeEventHandlerPtr timeout_handler;
- struct _DnsQuery *next;
- } DnsQueryRec, *DnsQueryPtr;
- union {
- struct sockaddr sa;
- struct sockaddr_in sin;
- #ifdef HAVE_IPv6
- struct sockaddr_in6 sin6;
- #endif
- } nameserverAddress_storage;
- #ifndef NO_FANCY_RESOLVER
- static AtomPtr atomLocalhost, atomLocalhostDot;
- #define nameserverAddress nameserverAddress_storage.sa
- static DnsQueryPtr inFlightDnsQueries;
- static DnsQueryPtr inFlightDnsQueriesLast;
- #endif
- static int really_do_gethostbyname(AtomPtr name, ObjectPtr object);
- static int really_do_dns(AtomPtr name, ObjectPtr object);
- #ifndef NO_FANCY_RESOLVER
- static int stringToLabels(char *buf, int offset, int n, char *string);
- static int labelsToString(char *buf, int offset, int n, char *d,
- int m, int *j_return);
- static int dnsBuildQuery(int id, char *buf, int offset, int n,
- AtomPtr name, int af);
- static int dnsReplyHandler(int abort, FdEventHandlerPtr event);
- static int dnsReplyId(char *buf, int offset, int n, int *id_return);
- static int dnsDecodeReply(char *buf, int offset, int n,
- int *id_return,
- AtomPtr *name_return, AtomPtr *value_return,
- int *af_return, unsigned *ttl_return);
- static int dnsHandler(int status, ConditionHandlerPtr chandler);
- static int dnsGethostbynameFallback(int id, AtomPtr message);
- static int sendQuery(DnsQueryPtr query);
- static int idSeed;
- #endif
- #if !defined(NO_FANCY_RESOLVER) && !defined(WIN32)
- static int
- parseResolvConf(char *filename)
- {
- FILE *f;
- char buf[512];
- char *p, *q;
- int n;
- AtomPtr nameserver = NULL;
- f = fopen(filename, "r");
- if(f == NULL) {
- do_log_error(L_ERROR, errno, "DNS: couldn't open %s", filename);
- return 0;
- }
- while(1) {
- p = fgets(buf, 512, f);
- if(p == NULL)
- break;
- n = strlen(buf);
- if(buf[n - 1] != '\n') {
- int c;
- do_log(L_WARN, "DNS: overly long line in %s -- skipping.\n",
- filename);
- do {
- c = fgetc(f);
- if(c == EOF)
- break;
- } while(c != '\n');
- if(c == EOF)
- break;
- }
-
- while(*p == ' ' || *p == '\t')
- p++;
- if(strcasecmp_n("nameserver", p, 10) != 0)
- continue;
- p += 10;
- while(*p == ' ' || *p == '\t')
- p++;
- q = p;
- while(*q == '.' || *q == ':' || digit(*q) || letter(*q))
- q++;
- if(*q != ' ' && *q != '\t' && *q != '\r' && *q != '\n') {
- do_log(L_WARN, "DNS: couldn't parse line in %s -- skipping.\n",
- filename);
- continue;
- }
- nameserver = internAtomLowerN(p, q - p);
- break;
- }
- fclose(f);
- if(nameserver) {
- dnsNameServer = nameserver;
- return 1;
- } else {
- return 0;
- }
- }
- #endif
- void
- preinitDns()
- {
- #ifdef HAVE_IPv6
- int fd;
- #endif
- assert(sizeof(struct in_addr) == 4);
- #ifdef HAVE_IPv6
- assert(sizeof(struct in6_addr) == 16);
- #endif
- #ifndef NO_STANDARD_RESOLVER
- CONFIG_VARIABLE(dnsGethostbynameTtl, CONFIG_TIME,
- "TTL for gethostbyname addresses.");
- #endif
- #ifdef HAVE_IPv6
- fd = socket(PF_INET6, SOCK_STREAM, 0);
- if(fd < 0) {
- if(errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT) {
- dnsQueryIPv6 = 0;
- } else {
- do_log_error(L_WARN, errno, "DNS: couldn't create socket");
- }
- } else {
- close(fd);
- }
- #endif
- #ifndef NO_FANCY_RESOLVER
- #ifndef WIN32
- parseResolvConf("/etc/resolv.conf");
- #endif
- if(dnsNameServer == NULL || dnsNameServer->string[0] == '\0')
- dnsNameServer = internAtom("127.0.0.1");
- CONFIG_VARIABLE(dnsMaxTimeout, CONFIG_TIME,
- "Max timeout for DNS queries.");
- CONFIG_VARIABLE(dnsNegativeTtl, CONFIG_TIME,
- "TTL for negative DNS replies with no TTL.");
- CONFIG_VARIABLE(dnsNameServer, CONFIG_ATOM_LOWER,
- "The name server to use.");
- CONFIG_VARIABLE(dnsNameServerPort, CONFIG_INT,
- "The name server port to use.");
- #ifndef NO_STANDARD_RESOLVER
- CONFIG_VARIABLE(dnsUseGethostbyname, CONFIG_TETRASTATE,
- "Use the system resolver.");
- #endif
- #endif
- #ifdef HAVE_IPv6
- CONFIG_VARIABLE(dnsQueryIPv6, CONFIG_TETRASTATE,
- "Query for IPv6 addresses.");
- #endif
- }
- void
- initDns()
- {
- #ifndef NO_FANCY_RESOLVER
- int rc;
- struct timeval t;
- struct sockaddr_in *sin = (struct sockaddr_in*)&nameserverAddress;
- #ifdef HAVE_IPv6
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&nameserverAddress;
- #endif
- atomLocalhost = internAtom("localhost");
- atomLocalhostDot = internAtom("localhost.");
- inFlightDnsQueries = NULL;
- inFlightDnsQueriesLast = NULL;
- gettimeofday(&t, NULL);
- idSeed = t.tv_usec & 0xFFFF;
- sin->sin_family = AF_INET;
- sin->sin_port = htons(dnsNameServerPort);
- rc = inet_aton(dnsNameServer->string, &sin->sin_addr);
- #ifdef HAVE_IPv6
- if(rc != 1) {
- sin6->sin6_family = AF_INET6;
- sin6->sin6_port = htons(dnsNameServerPort);
- rc = inet_pton(AF_INET6, dnsNameServer->string, &sin6->sin6_addr);
- }
- #endif
- if(rc != 1) {
- do_log(L_ERROR, "DNS: couldn't parse name server %s.\n",
- dnsNameServer->string);
- exit(1);
- }
- #endif
- }
- int
- do_gethostbyname(char *origname,
- int count,
- int (*handler)(int, GethostbynameRequestPtr),
- void *data)
- {
- ObjectPtr object;
- int n = strlen(origname);
- AtomPtr name;
- GethostbynameRequestRec request;
- int done, rc;
- memset(&request, 0, sizeof(request));
- request.name = NULL;
- request.addr = NULL;
- request.error_message = NULL;
- request.count = count;
- request.handler = handler;
- request.data = data;
- if(n <= 0 || n > 131) {
- if(n <= 0) {
- request.error_message = internAtom("empty name");
- do_log(L_ERROR, "Empty DNS name.\n");
- done = handler(-EINVAL, &request);
- } else {
- request.error_message = internAtom("name too long");
- do_log(L_ERROR, "DNS name too long.\n");
- done = handler(-ENAMETOOLONG, &request);
- }
- assert(done);
- releaseAtom(request.error_message);
- return 1;
- }
- if(origname[n - 1] == '.')
- n--;
- name = internAtomLowerN(origname, n);
- if(name == NULL) {
- request.error_message = internAtom("couldn't allocate name");
- do_log(L_ERROR, "Couldn't allocate DNS name.\n");
- done = handler(-ENOMEM, &request);
- assert(done);
- releaseAtom(request.error_message);
- return 1;
- }
- request.name = name;
- request.addr = NULL;
- request.error_message = NULL;
- request.count = count;
- request.object = NULL;
- request.handler = handler;
- request.data = data;
- object = findObject(OBJECT_DNS, name->string, name->length);
- if(object == NULL || objectMustRevalidate(object, NULL)) {
- if(object) {
- privatiseObject(object, 0);
- releaseObject(object);
- }
- object = makeObject(OBJECT_DNS, name->string, name->length, 1, 0,
- NULL, NULL);
- if(object == NULL) {
- request.error_message = internAtom("Couldn't allocate object");
- do_log(L_ERROR, "Couldn't allocate DNS object.\n");
- done = handler(-ENOMEM, &request);
- assert(done);
- releaseAtom(name);
- releaseAtom(request.error_message);
- return 1;
- }
- }
- if((object->flags & (OBJECT_INITIAL | OBJECT_INPROGRESS)) ==
- OBJECT_INITIAL) {
- if(dnsUseGethostbyname >= 3)
- rc = really_do_gethostbyname(name, object);
- else
- rc = really_do_dns(name, object);
- if(rc < 0) {
- assert(!(object->flags & (OBJECT_INITIAL | OBJECT_INPROGRESS)));
- goto fail;
- }
- }
- if(dnsUseGethostbyname >= 3)
- assert(!(object->flags & OBJECT_INITIAL));
- #ifndef NO_FANCY_RESOLVER
- if(object->flags & OBJECT_INITIAL) {
- ConditionHandlerPtr chandler;
- assert(object->flags & OBJECT_INPROGRESS);
- request.object = object;
- chandler = conditionWait(&object->condition, dnsHandler,
- sizeof(request), &request);
- if(chandler == NULL)
- goto fail;
- return 1;
- }
- #endif
- if(object->headers && object->headers->length > 0) {
- if(object->headers->string[0] == DNS_A)
- assert(((object->headers->length - 1) %
- sizeof(HostAddressRec)) == 0);
- else
- assert(object->headers->string[0] == DNS_CNAME);
- request.addr = retainAtom(object->headers);
- } else if(object->message) {
- request.error_message = retainAtom(object->message);
- }
- releaseObject(object);
- if(request.addr && request.addr->length > 0)
- done = handler(1, &request);
- else
- done = handler(-EDNS_HOST_NOT_FOUND, &request);
- assert(done);
- releaseAtom(request.addr); request.addr = NULL;
- releaseAtom(request.name); request.name = NULL;
- releaseAtom(request.error_message); request.error_message = NULL;
- return 1;
- fail:
- releaseNotifyObject(object);
- done = handler(-errno, &request);
- assert(done);
- releaseAtom(name);
- return 1;
- }
- static int
- dnsDelayedErrorNotifyHandler(TimeEventHandlerPtr event)
- {
- int done;
- GethostbynameRequestRec request =
- *(GethostbynameRequestPtr)event->data;
- done = request.handler(-EDNS_HOST_NOT_FOUND, &request);
- assert(done);
- releaseAtom(request.name); request.name = NULL;
- releaseAtom(request.addr); request.addr = NULL;
- releaseAtom(request.error_message); request.error_message = NULL;
- return 1;
- }
-
- static int
- dnsDelayedDoneNotifyHandler(TimeEventHandlerPtr event)
- {
- int done;
- GethostbynameRequestRec request = *(GethostbynameRequestPtr)event->data;
- done = request.handler(1, &request);
- assert(done);
- releaseAtom(request.name); request.name = NULL;
- releaseAtom(request.addr); request.addr = NULL;
- releaseAtom(request.error_message); request.error_message = NULL;
- return 1;
- }
- static int
- dnsDelayedNotify(int error, GethostbynameRequestPtr request)
- {
- TimeEventHandlerPtr handler;
- if(error)
- handler = scheduleTimeEvent(0,
- dnsDelayedErrorNotifyHandler,
- sizeof(*request), request);
- else
- handler = scheduleTimeEvent(0,
- dnsDelayedDoneNotifyHandler,
- sizeof(*request), request);
- if(handler == NULL) {
- do_log(L_ERROR, "Couldn't schedule DNS notification.\n");
- return -1;
- }
- return 1;
- }
- #ifdef HAVE_IPv6
- AtomPtr
- rfc2732(AtomPtr name)
- {
- char buf[40]; /* 8*4 (hexdigits) + 7 (colons) + 1 ('\0') */
- int rc;
- AtomPtr a = NULL;
- if(name->length < 40+2 &&
- name->string[0] == '[' && name->string[name->length - 1] == ']') {
- struct in6_addr in6a;
- memcpy(buf, name->string + 1, name->length - 2);
- buf[name->length - 2] = '\0';
- rc = inet_pton(AF_INET6, buf, &in6a);
- if(rc == 1) {
- char s[1 + sizeof(HostAddressRec)];
- memset(s, 0, sizeof(s));
- s[0] = DNS_A;
- s[1] = 6;
- memcpy(s + 2, &in6a, 16);
- a = internAtomN(s, 1 + sizeof(HostAddressRec));
- if(a == NULL)
- return NULL;
- }
- }
- return a;
- }
- /* Used for sorting host addresses depending on the value of dnsQueryIPv6 */
- int
- compare_hostaddr(const void *av, const void *bv)
- {
- const HostAddressRec *a = av, *b = bv;
- int r;
- if(a->af == 4) {
- if(b->af == 4)
- r = 0;
- else
- r = -1;
- } else {
- if(b->af == 6)
- r = 0;
- else
- r = 1;
- }
- if(dnsQueryIPv6 >= 2)
- return -r;
- else
- return r;
- }
- #ifndef NO_STANDARD_RESOLVER
- static int
- really_do_gethostbyname(AtomPtr name, ObjectPtr object)
- {
- struct addrinfo *ai, *entry, hints;
- int rc;
- int error, i;
- char buf[1024];
- AtomPtr a;
- a = rfc2732(name);
- if(a) {
- object->headers = a;
- object->age = current_time.tv_sec;
- object->expires = current_time.tv_sec + 240;
- object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
- notifyObject(object);
- return 0;
- }
- memset(&hints, 0, sizeof(hints));
- hints.ai_protocol = IPPROTO_TCP;
- if(dnsQueryIPv6 <= 0)
- hints.ai_family = AF_INET;
- else if(dnsQueryIPv6 >= 3)
- hints.ai_family = AF_INET6;
- rc = getaddrinfo(name->string, NULL, &hints, &ai);
- switch(rc) {
- case 0: error = 0; break;
- case EAI_FAMILY:
- #ifdef EAI_ADDRFAMILY
- case EAI_ADDRFAMILY:
- #endif
- case EAI_SOCKTYPE:
- error = EAFNOSUPPORT; break;
- case EAI_BADFLAGS: error = EINVAL; break;
- case EAI_SERVICE: error = EDNS_NO_RECOVERY; break;
- #ifdef EAI_NONAME
- case EAI_NONAME:
- #endif
- #ifdef EAI_NODATA
- case EAI_NODATA:
- #endif
- error = EDNS_NO_ADDRESS; break;
- case EAI_FAIL: error = EDNS_NO_RECOVERY; break;
- case EAI_AGAIN: error = EDNS_TRY_AGAIN; break;
- #ifdef EAI_MEMORY
- case EAI_MEMORY: error = ENOMEM; break;
- #endif
- case EAI_SYSTEM: error = errno; break;
- default: error = EUNKNOWN;
- }
- if(error == EDNS_NO_ADDRESS) {
- object->headers = NULL;
- object->age = current_time.tv_sec;
- object->expires = current_time.tv_sec + dnsNegativeTtl;
- object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
- notifyObject(object);
- return 0;
- } else if(error) {
- do_log_error(L_ERROR, error, "Getaddrinfo failed");
- object->flags &= ~OBJECT_INPROGRESS;
- abortObject(object, 404,
- internAtomError(error, "Getaddrinfo failed"));
- notifyObject(object);
- return 0;
- }
- entry = ai;
- buf[0] = DNS_A;
- i = 0;
- while(entry) {
- HostAddressRec host;
- int host_valid = 0;
- if(entry->ai_family == AF_INET && entry->ai_protocol == IPPROTO_TCP) {
- if(dnsQueryIPv6 < 3) {
- host.af = 4;
- memset(host.data, 0, sizeof(host.data));
- memcpy(&host.data,
- &((struct sockaddr_in*)entry->ai_addr)->sin_addr,
- 4);
- host_valid = 1;
- }
- } else if(entry->ai_family == AF_INET6 &&
- entry->ai_protocol == IPPROTO_TCP) {
- if(dnsQueryIPv6 > 0) {
- host.af = 6;
- memset(&host.data, 0, sizeof(host.data));
- memcpy(&host.data,
- &((struct sockaddr_in6*)entry->ai_addr)->sin6_addr,
- 16);
- host_valid = 1;
- }
- }
- if(host_valid) {
- if(i >= 1024 / sizeof(HostAddressRec) - 2) {
- do_log(L_ERROR, "Too many addresses for host %s\n",
- name->string);
- break;
- }
- memcpy(buf + 1 + i * sizeof(HostAddressRec),
- &host, sizeof(HostAddressRec));
- i++;
- }
- entry = entry->ai_next;
- }
- freeaddrinfo(ai);
- if(i == 0) {
- do_log(L_ERROR, "Getaddrinfo returned no useful addresses\n");
- object->flags &= ~OBJECT_INPROGRESS;
- abortObject(object, 404,
- internAtom("Getaddrinfo returned no useful addresses"));
- notifyObject(object);
- return 0;
- }
- if(1 <= dnsQueryIPv6 && dnsQueryIPv6 <= 2)
- qsort(buf + 1, i, sizeof(HostAddressRec), compare_hostaddr);
- a = internAtomN(buf, 1 + i * sizeof(HostAddressRec));
- if(a == NULL) {
- object->flags &= ~OBJECT_INPROGRESS;
- abortObject(object, 501, internAtom("Couldn't allocate address"));
- notifyObject(object);
- return 0;
- }
- object->headers = a;
- object->age = current_time.tv_sec;
- object->expires = current_time.tv_sec + dnsGethostbynameTtl;
- object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
- notifyObject(object);
- return 0;
- }
- #endif
-
- #else
- #ifndef NO_STANDARD_RESOLVER
- static int
- really_do_gethostbyname(AtomPtr name, ObjectPtr object)
- {
- struct hostent *host;
- char *s;
- AtomPtr a;
- int i, j;
- int error;
- host = gethostbyname(name->string);
- if(host == NULL) {
- switch(h_errno) {
- case HOST_NOT_FOUND: error = EDNS_HOST_NOT_FOUND; break;
- #ifdef NO_ADDRESS
- case NO_ADDRESS: error = EDNS_NO_ADDRESS; break;
- #endif
- case NO_RECOVERY: error = EDNS_NO_RECOVERY; break;
- case TRY_AGAIN: error = EDNS_TRY_AGAIN; break;
- default: error = EUNKNOWN; break;
- }
- if(error == EDNS_HOST_NOT_FOUND) {
- object->headers = NULL;
- object->age = current_time.tv_sec;
- object->expires = current_time.tv_sec + dnsNegativeTtl;
- object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
- object->flags &= ~OBJECT_INPROGRESS;
- notifyObject(object);
- return 0;
- } else {
- do_log_error(L_ERROR, error, "Gethostbyname failed");
- abortObject(object, 404,
- internAtomError(error, "Gethostbyname failed"));
- object->flags &= ~OBJECT_INPROGRESS;
- notifyObject(object);
- return 0;
- }
- }
- if(host->h_addrtype != AF_INET) {
- do_log(L_ERROR, "Address is not AF_INET.\n");
- object->flags &= ~OBJECT_INPROGRESS;
- abortObject(object, 404, internAtom("Address is not AF_INET"));
- notifyObject(object);
- return -1;
- }
- if(host->h_length != sizeof(struct in_addr)) {
- do_log(L_ERROR, "Address size inconsistent.\n");
- object->flags &= ~OBJECT_INPROGRESS;
- abortObject(object, 404, internAtom("Address size inconsistent"));
- notifyObject(object);
- return 0;
- }
- i = 0;
- while(host->h_addr_list[i] != NULL) i++;
- s = malloc(1 + i * sizeof(HostAddressRec));
- if(s == NULL) {
- a = NULL;
- } else {
- memset(s, 0, 1 + i * sizeof(HostAddressRec));
- s[0] = DNS_A;
- for(j = 0; j < i; j++) {
- s[j * sizeof(HostAddressRec) + 1] = 4;
- memcpy(&s[j * sizeof(HostAddressRec) + 2], host->h_addr_list[j],
- sizeof(struct in_addr));
- }
- a = internAtomN(s, i * sizeof(HostAddressRec) + 1);
- free(s);
- }
- if(!a) {
- object->flags &= ~OBJECT_INPROGRESS;
- abortObject(object, 501, internAtom("Couldn't allocate address"));
- notifyObject(object);
- return 0;
- }
- object->headers = a;
- object->age = current_time.tv_sec;
- object->expires = current_time.tv_sec + dnsGethostbynameTtl;
- object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
- notifyObject(object);
- return 0;
- }
- #endif
- #endif
- #ifdef NO_STANDARD_RESOLVER
- static int
- really_do_gethostbyname(AtomPtr name, ObjectPtr object)
- {
- abort();
- }
- #endif
- #ifndef NO_FANCY_RESOLVER
- static int dnsSocket = -1;
- static FdEventHandlerPtr dnsSocketHandler = NULL;
- static int
- dnsHandler(int status, ConditionHandlerPtr chandler)
- {
- GethostbynameRequestRec request = *(GethostbynameRequestPtr)chandler->data;
- ObjectPtr object = request.object;
- assert(!(object->flags & OBJECT_INPROGRESS));
- if(object->headers) {
- request.addr = retainAtom(object->headers);
- dnsDelayedNotify(0, &request);
- } else {
- if(object->message)
- request.error_message = retainAtom(object->message);
- dnsDelayedNotify(1, &request);
- }
- releaseObject(object);
- return 1;
- }
- static int
- queryInFlight(DnsQueryPtr query)
- {
- DnsQueryPtr other;
- other = inFlightDnsQueries;
- while(other) {
- if(other == query)
- return 1;
- other = other->next;
- }
- return 0;
- }
- static void
- removeQuery(DnsQueryPtr query)
- {
- DnsQueryPtr previous;
- if(query == inFlightDnsQueries) {
- inFlightDnsQueries = query->next;
- if(inFlightDnsQueries == NULL)
- inFlightDnsQueriesLast = NULL;
- } else {
- previous = inFlightDnsQueries;
- while(previous->next) {
- if(previous->next == query)
- break;
- previous = previous->next;
- }
- assert(previous->next != NULL);
- previous->next = query->next;
- if(previous->next == NULL)
- inFlightDnsQueriesLast = previous;
- }
- }
- static void
- insertQuery(DnsQueryPtr query)
- {
- if(inFlightDnsQueriesLast)
- inFlightDnsQueriesLast->next = query;
- else
- inFlightDnsQueries = query;
- inFlightDnsQueriesLast = query;
- }
- static DnsQueryPtr
- findQuery(int id, AtomPtr name)
- {
- DnsQueryPtr query;
- query = inFlightDnsQueries;
- while(query) {
- if(query->id == id && (name == NULL || query->name == name))
- return query;
- query = query->next;
- }
- return NULL;
- }
- static int
- dnsTimeoutHandler(TimeEventHandlerPtr event)
- {
- DnsQueryPtr query = *(DnsQueryPtr*)event->data;
- ObjectPtr object = query->object;
- int rc;
- /* People are reporting that this does happen. And I have no idea why. */
- if(!queryInFlight(query)) {
- do_log(L_ERROR, "BUG: timing out martian query (%s, flags: 0x%x).\n",
- scrub(query->name->string), (unsigned)object->flags);
- return 1;
- }
- query->timeout = MAX(10, query->timeout * 2);
- if(query->timeout > dnsMaxTimeout) {
- abortObject(object, 501, internAtom("Timeout"));
- goto fail;
- } else {
- rc = sendQuery(query);
- if(rc < 0) {
- if(rc != -EWOULDBLOCK && rc != -EAGAIN && rc != -ENOBUFS) {
- abortObject(object, 501,
- internAtomError(-rc,
- "Couldn't send DNS query"));
- goto fail;
- }
- /* else let it timeout */
- }
- query->timeout_handler =
- scheduleTimeEvent(query->timeout, dnsTimeoutHandler,
- sizeof(query), &query);
- if(query->timeout_handler == NULL) {
- do_log(L_ERROR, "Couldn't schedule DNS timeout handler.\n");
- abortObject(object, 501,
- internAtom("Couldn't schedule DNS timeout handler"));
- goto fail;
- }
- return 1;
- }
- fail:
- removeQuery(query);
- object->flags &= ~OBJECT_INPROGRESS;
- if(query->inet4) releaseAtom(query->inet4);
- if(query->inet6) releaseAtom(query->inet6);
- free(query);
- releaseNotifyObject(object);
- return 1;
- }
- static int
- establishDnsSocket()
- {
- int rc;
- #ifdef HAVE_IPv6
- int inet6 = (nameserverAddress.sa_family == AF_INET6);
- int pf = inet6 ? PF_INET6 : PF_INET;
- int sa_size =
- inet6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
- #else
- int pf = PF_INET;
- int sa_size = sizeof(struct sockaddr_in);
- #endif
- if(dnsSocket < 0) {
- assert(!dnsSocketHandler);
- dnsSocket = socket(pf, SOCK_DGRAM, 0);
- if(dnsSocket < 0) {
- do_log_error(L_ERROR, errno, "Couldn't create DNS socket");
- return -errno;
- }
- rc = connect(dnsSocket, &nameserverAddress, sa_size);
- if(rc < 0) {
- CLOSE(dnsSocket);
- dnsSocket = -1;
- do_log_error(L_ERROR, errno, "Couldn't create DNS \"connection\"");
- return -errno;
- }
- }
- if(!dnsSocketHandler) {
- dnsSocketHandler =
- registerFdEvent(dnsSocket, POLLIN, dnsReplyHandler, 0, NULL);
- if(dnsSocketHandler == NULL) {
- do_log(L_ERROR, "Couldn't register DNS socket handler.\n");
- CLOSE(dnsSocket);
- dnsSocket = -1;
- return -ENOMEM;
- }
- }
- return 1;
- }
- static int
- sendQuery(DnsQueryPtr query)
- {
- char buf[512];
- int buflen;
- int rc;
- int af[2];
- int i;
- if(dnsSocket < 0)
- return -1;
- if(dnsQueryIPv6 <= 0) {
- af[0] = 4; af[1] = 0;
- } else if(dnsQueryIPv6 <= 2) {
- af[0] = 4; af[1] = 6;
- } else {
- af[0] = 6; af[1] = 0;
- }
- for(i = 0; i < 2; i++) {
- if(af[i] == 0)
- continue;
- if(af[i] == 4 && query->inet4)
- continue;
- else if(af[i] == 6 && query->inet6)
- continue;
- buflen = dnsBuildQuery(query->id, buf, 0, 512, query->name, af[i]);
- if(buflen <= 0) {
- do_log(L_ERROR, "Couldn't build DNS query.\n");
- return buflen;
- }
- rc = send(dnsSocket, buf, buflen, 0);
- if(rc < buflen) {
- if(rc >= 0) {
- do_log(L_ERROR, "Couldn't send DNS query: partial send.\n");
- return -EAGAIN;
- } else {
- do_log_error(L_ERROR, errno, "Couldn't send DNS query");
- return -errno;
- }
- }
- }
- return 1;
- }
- static int
- really_do_dns(AtomPtr name, ObjectPtr object)
- {
- int rc;
- DnsQueryPtr query;
- AtomPtr message = NULL;
- int id;
- AtomPtr a = NULL;
- if(a == NULL) {
- if(name == atomLocalhost || name == atomLocalhostDot) {
- char s[1 + sizeof(HostAddressRec)];
- memset(s, 0, sizeof(s));
- s[0] = DNS_A;
- s[1] = 4;
- s[2] = 127;
- s[3] = 0;
- s[4] = 0;
- s[5] = 1;
- a = internAtomN(s, 1 + sizeof(HostAddressRec));
- if(a == NULL) {
- abortObject(object, 501,
- internAtom("Couldn't allocate address"));
- notifyObject(object);
- errno = ENOMEM;
- return -1;
- }
- }
- }
- if(a == NULL) {
- struct in_addr ina;
- rc = inet_aton(name->string, &ina);
- if(rc == 1) {
- char s[1 + sizeof(HostAddressRec)];
- memset(s, 0, sizeof(s));
- s[0] = DNS_A;
- s[1] = 4;
- memcpy(s + 2, &ina, 4);
- a = internAtomN(s, 1 + sizeof(HostAddressRec));
- if(a == NULL) {
- abortObject(object, 501,
- internAtom("Couldn't allocate address"));
- notifyObject(object);
- errno = ENOMEM;
- return -1;
- }
- }
- }
- #ifdef HAVE_IPv6
- if(a == NULL)
- a = rfc2732(name);
- #endif
- if(a) {
- object->headers = a;
- object->age = current_time.tv_sec;
- object->expires = current_time.tv_sec + 240;
- object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
- notifyObject(object);
- return 0;
- }
- rc = establishDnsSocket();
- if(rc < 0) {
- do_log_error(L_ERROR, -rc, "Couldn't establish DNS socket.\n");
- message = internAtomError(-rc, "Couldn't establish DNS socket");
- goto fallback;
- }
- /* The id is used to speed up detecting replies to queries that
- are no longer current -- see dnsReplyHandler. */
- id = (idSeed++) & 0xFFFF;
- query = malloc(sizeof(DnsQueryRec));
- if(query == NULL) {
- do_log(L_ERROR, "Couldn't allocate DNS query.\n");
- message = internAtom("Couldn't allocate DNS query");
- goto fallback;
- }
- query->id = id;
- query->inet4 = NULL;
- query->inet6 = NULL;
- query->name = name;
- query->time = current_time.tv_sec;
- query->object = retainObject(object);
- query->timeout = 4;
- query->timeout_handler = NULL;
- query->next = NULL;
- query->timeout_handler =
- scheduleTimeEvent(query->timeout, dnsTimeoutHandler,
- sizeof(query), &query);
- if(query->timeout_handler == NULL) {
- do_log(L_ERROR, "Couldn't schedule DNS timeout handler.\n");
- message = internAtom("Couldn't schedule DNS timeout handler");
- goto free_fallback;
- }
- insertQuery(query);
- object->flags |= OBJECT_INPROGRESS;
- rc = sendQuery(query);
- if(rc < 0) {
- if(rc != -EWOULDBLOCK && rc != -EAGAIN && rc != -ENOBUFS) {
- object->flags &= ~OBJECT_INPROGRESS;
- message = internAtomError(-rc, "Couldn't send DNS query");
- goto remove_fallback;
- }
- /* else let it timeout */
- }
- releaseAtom(message);
- return 1;
- remove_fallback:
- removeQuery(query);
- free_fallback:
- releaseObject(query->object);
- cancelTimeEvent(query->timeout_handler);
- free(query);
- fallback:
- if(dnsUseGethostbyname >= 1) {
- releaseAtom(message);
- do_log(L_WARN, "Falling back on gethostbyname.\n");
- return really_do_gethostbyname(name, object);
- } else {
- abortObject(object, 501, message);
- notifyObject(object);
- return 1;
- }
- }
- static int
- dnsReplyHandler(int abort, FdEventHandlerPtr event)
- {
- int fd = event->fd;
- char buf[2048];
- int len, rc;
- ObjectPtr object;
- unsigned ttl = 0;
- AtomPtr name, value, message = NULL;
- int id;
- int af;
- DnsQueryPtr query;
- AtomPtr cname = NULL;
- if(abort) {
- dnsSocketHandler = NULL;
- rc = establishDnsSocket();
- if(rc < 0) {
- do_log(L_ERROR, "Couldn't reestablish DNS socket.\n");
- /* At this point, we should abort all in-flight
- DNS requests. Oh, well, they'll timeout anyway. */
- }
- return 1;
- }
- len = recv(fd, buf, 2048, 0);
- if(len <= 0) {
- if(errno == EINTR || errno == EAGAIN) return 0;
- /* This is where we get ECONNREFUSED for an ICMP port unreachable */
- do_log_error(L_ERROR, errno, "DNS: recv failed");
- dnsGethostbynameFallback(-1, message);
- return 0;
- }
- /* This could be a late reply to a query that timed out and was
- resent, a reply to a query that timed out, or a reply to an
- AAAA query when we already got a CNAME reply to the associated
- A. We filter such replies straight away, without trying to
- parse them. */
- rc = dnsReplyId(buf, 0, len, &id);
- if(rc < 0) {
- do_log(L_WARN, "Short DNS reply.\n");
- return 0;
- }
- if(!findQuery(id, NULL)) {
- return 0;
- }
- rc = dnsDecodeReply(buf, 0, len, &id, &name, &value, &af, &ttl);
- if(rc < 0) {
- assert(value == NULL);
- /* We only want to fallback on gethostbyname if we received a
- reply that we could not understand. What about truncated
- replies? */
- if(rc < 0) {
- do_log_error(L_WARN, -rc, "DNS");
- if(dnsUseGethostbyname >= 2 ||
- (dnsUseGethostbyname &&
- (rc != -EDNS_HOST_NOT_FOUND && rc != -EDNS_NO_RECOVERY &&
- rc != -EDNS_FORMAT))) {
- dnsGethostbynameFallback(id, message);
- return 0;
- } else {
- message = internAtom(pstrerror(-rc));
- }
- } else {
- assert(name != NULL && id >= 0 && af >= 0);
- }
- }
- query = findQuery(id, name);
- if(query == NULL) {
- /* Duplicate id ? */
- releaseAtom(value);
- releaseAtom(name);
- return 0;
- }
- /* We're going to use the information in this reply. If it was an
- error, construct an empty atom to distinguish it from information
- we're still waiting for. */
- if(value == NULL)
- value = internAtom("");
- again:
- if(af == 4) {
- if(query->inet4 == NULL) {
- query->inet4 = value;
- query->ttl4 = current_time.tv_sec + ttl;
- } else
- releaseAtom(value);
- } else if(af == 6) {
- if(query->inet6 == NULL) {
- query->inet6 = value;
- query->ttl6 = current_time.tv_sec + ttl;
- } else
- releaseAtom(value);
- } else if(af == 0) {
- /* Ignore errors in this case. */
- if(query->inet4 && query->inet4->length == 0) {
- releaseAtom(query->inet4);
- query->inet4 = NULL;
- }
- if(query->inet6 && query->inet6->length == 0) {
- releaseAtom(query->inet6);
- query->inet6 = NULL;
- }
- if(query->inet4 || query->inet6) {
- do_log(L_WARN, "Host %s has both %s and CNAME -- "
- "ignoring CNAME.\n", scrub(query->name->string),
- query->inet4 ? "A" : "AAAA");
- releaseAtom(value);
- value = internAtom("");
- af = query->inet4 ? 4 : 6;
- goto again;
- } else {
- cname = value;
- }
- }
- if(rc >= 0 && !cname &&
- ((dnsQueryIPv6 < 3 && query->inet4 == NULL) ||
- (dnsQueryIPv6 > 0 && query->inet6 == NULL)))
- return 0;
- /* This query is complete */
- cancelTimeEvent(query->timeout_handler);
- object = query->object;
- if(object->flags & OBJECT_INITIAL) {
- assert(!object->headers);
- if(cname) {
- assert(query->inet4 == NULL && query->inet6 == NULL);
- object->headers = cname;
- object->expires = current_time.tv_sec + ttl;
- } else if((!query->inet4 || query->inet4->length == 0) &&
- (!query->inet6 || query->inet6->length == 0)) {
- releaseAtom(query->inet4);
- releaseAtom(query->inet6);
- object->expires = current_time.tv_sec + dnsNegativeTtl;
- abortObject(object, 500, retainAtom(message));
- } else if(!query->inet4 || query->inet4->length == 0) {
- object->headers = query->inet6;
- object->expires = query->ttl6;
- releaseAtom(query->inet4);
- } else if(!query->inet6 || query->inet6->length == 0) {
- object->headers = query->inet4;
- object->expires = query->ttl4;
- releaseAtom(query->inet6);
- } else {
- /* need to merge results */
- char buf[1024];
- if(query->inet4->length + query->inet6->length > 1024) {
- releaseAtom(query->inet4);
- releaseAtom(query->inet6);
- abortObject(object, 500, internAtom("DNS reply too long"));
- } else {
- if(dnsQueryIPv6 <= 1) {
- memcpy(buf, query->inet4->string, query->inet4->length);
- memcpy(buf + query->inet4->length,
- query->inet6->string + 1, query->inet6->length - 1);
- } else {
- memcpy(buf, query->inet6->string, query->inet6->length);
- memcpy(buf + query->inet6->length,
- query->inet4->string + 1, query->inet4->length - 1);
- }
- object->headers =
- internAtomN(buf,
- query->inet4->length +
- query->inet6->length - 1);
- if(object->headers == NULL)
- abortObject(object, 500,
- internAtom("Couldn't allocate DNS atom"));
- }
- object->expires = MIN(query->ttl4, query->ttl6);
- }
- object->age = current_time.tv_sec;
- object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
- } else {
- do_log(L_WARN, "DNS object ex nihilo for %s.\n",
- scrub(query->name->string));
- }
-
- removeQuery(query);
- free(query);
- releaseAtom(name);
- releaseAtom(message);
- releaseNotifyObject(object);
- return 0;
- }
- static int
- dnsGethostbynameFallback(int id, AtomPtr message)
- {
- DnsQueryPtr query, previous;
- ObjectPtr object;
- if(inFlightDnsQueries == NULL) {
- releaseAtom(message);
- return 1;
- }
- query = NULL;
- if(id < 0 || inFlightDnsQueries->id == id) {
- previous = NULL;
- query = inFlightDnsQueries;
- } else {
- previous = inFlightDnsQueries;
- while(previous->next) {
- if(previous->next->id == id) {
- query = previous->next;
- break;
- }
- previous = previous->next;
- }
- if(!query) {
- previous = NULL;
- query = inFlightDnsQueries;
- }
- }
- if(previous == NULL) {
- inFlightDnsQueries = query->next;
- if(inFlightDnsQueries == NULL)
- inFlightDnsQueriesLast = NULL;
- } else {
- previous->next = query->next;
- if(query->next == NULL)
- inFlightDnsQueriesLast = NULL;
- }
- object = makeObject(OBJECT_DNS, query->name->string, query->name->length,
- 1, 0, NULL, NULL);
- if(!object) {
- do_log(L_ERROR, "Couldn't make DNS object.\n");
- releaseAtom(query->name);
- releaseAtom(message);
- releaseObject(query->object);
- cancelTimeEvent(query->timeout_handler);
- free(query);
- return -1;
- }
- if(dnsUseGethostbyname >= 1) {
- releaseAtom(message);
- do_log(L_WARN, "Falling back to using system resolver.\n");
- really_do_gethostbyname(retainAtom(query->name), object);
- } else {
- releaseAtom(object->message);
- object->message = message;
- object->flags &= ~OBJECT_INPROGRESS;
- releaseNotifyObject(object);
- }
- cancelTimeEvent(query->timeout_handler);
- releaseAtom(query->name);
- if(query->inet4) releaseAtom(query->inet4);
- if(query->inet6) releaseAtom(query->inet6);
- releaseObject(query->object);
- free(query);
- return 1;
- }
- static int
- stringToLabels(char *buf, int offset, int n, char *string)
- {
- int i = offset;
- int j = 0, k = 0;
- while(1) {
- while(string[k] != '.' && string[k] != '\0')
- k++;
- if(k >= j + 256) return -1;
- buf[i] = (unsigned char)(k - j); i++; if(i >= n) return -1;
- while(j < k) {
- buf[i] = string[j]; i++; j++; if(i >= n) return -1;
- }
- if(string[j] == '\0') {
- buf[i] = '\0';
- i++; if(i >= n) return -1;
- break;
- }
- j++; k++;
- }
- return i;
- }
- #ifdef UNALIGNED_ACCESS
- #define DO_NTOHS(_d, _s) _d = ntohs(*(unsigned short*)(_s));
- #define DO_NTOHL(_d, _s) _d = ntohl(*(unsigned*)(_s))
- #define DO_HTONS(_d, _s) *(unsigned short*)(_d) = htons(_s);
- #define DO_HTONL(_d, _s) *(unsigned*)(_d) = htonl(_s)
- #else
- #define DO_NTOHS(_d, _s) \
- do { unsigned short _dd; \
- memcpy(&(_dd), (_s), sizeof(unsigned short)); \
- _d = ntohs(_dd); } while(0)
- #define DO_NTOHL(_d, _s) \
- do { unsigned _dd; \
- memcpy(&(_dd), (_s), sizeof(unsigned)); \
- _d = ntohl(_dd); } while(0)
- #define DO_HTONS(_d, _s) \
- do { unsigned short _dd; \
- _dd = htons(_s); \
- memcpy((_d), &(_dd), sizeof(unsigned short)); } while(0);
- #define DO_HTONL(_d, _s) \
- do { unsigned _dd; \
- _dd = htonl(_s); \
- memcpy((_d), &(_dd), sizeof(unsigned)); } while(0);
- #endif
- static int
- labelsToString(char *buf, int offset, int n, char *d, int m, int *j_return)
- {
- int i = offset, j, k;
- int ll, rc;
- j = 0;
- while(1) {
- if(i >= n) return -1;
- ll = *(unsigned char*)&buf[i]; i++;
- if(ll == 0) {
- break;
- }
- if((ll & (3 << 6)) == (3 << 6)) {
- /* RFC 1035, 4.1.4 */
- int o;
- if(i >= n) return -1;
- o = (ll & ~(3 << 6)) << 8 | *(unsigned char*)&buf[i];
- i++;
- rc = labelsToString(buf, o, n, &d[j], m - j, &k);
- if(rc < 0)
- return -1;
- j += k;
- break;
- } else if((ll & (3 << 6)) == 0) {
- for(k = 0; k < ll; k++) {
- if(i >= n || j >= m) return -1;
- d[j++] = buf[i++];
- }
- if(i >= n) return -1;
- if(buf[i] != '\0') {
- if(j >= m) return -1;
- d[j++] = '.';
- }
- } else {
- return -1;
- }
- }
- *j_return = j;
- return i;
- }
- static int
- dnsBuildQuery(int id, char *buf, int offset, int n, AtomPtr name, int af)
- {
- int i = offset;
- int type;
- switch(af) {
- case 4: type = 1; break;
- case 6: type = 28; break;
- default: return -EINVAL;
- }
- if(i + 12 >= n) return -1;
- DO_HTONS(&buf[i], id); i += 2;
- DO_HTONS(&buf[i], 1<<8); i += 2;
- DO_HTONS(&buf[i], 1); i += 2;
- DO_HTONS(&buf[i], 0); i += 2;
- DO_HTONS(&buf[i], 0); i += 2;
- DO_HTONS(&buf[i], 0); i += 2;
- i = stringToLabels(buf, i, n, name->string);
- if(i < 0) return -ENAMETOOLONG;
-
- if(i + 4 >= n) return -ENAMETOOLONG;
- DO_HTONS(&buf[i], type); i += 2;
- DO_HTONS(&buf[i], 1); i += 2;
- return i;
- }
- static int
- dnsReplyId(char *buf, int offset, int n, int *id_return)
- {
- if(n - offset < 12)
- return -1;
- DO_NTOHS(*id_return, &buf[offset]);
- return 1;
- }
- static int
- dnsDecodeReply(char *buf, int offset, int n, int *id_return,
- AtomPtr *name_return, AtomPtr *value_return,
- int *af_return, unsigned *ttl_return)
- {
- int i = offset, j, m;
- int id = -1, b23, qdcount, ancount, nscount, arcount, rdlength;
- int class, type;
- unsigned int ttl;
- char b[2048];
- int af = -1;
- AtomPtr name = NULL, value;
- char addresses[1024];
- int addr_index = 0;
- int error = EDNS_NO_ADDRESS;
- unsigned final_ttl = 7 * 24 * 3600;
- int dnserror;
- if(n - i < 12) {
- error = EDNS_INVALID;
- goto fail;
- }
- DO_NTOHS(id, &buf[i]); i += 2;
- DO_NTOHS(b23, &buf[i]); i += 2;
- DO_NTOHS(qdcount, &buf[i]); i += 2;
- DO_NTOHS(ancount, &buf[i]); i += 2;
- DO_NTOHS(nscount, &buf[i]); i += 2;
- DO_NTOHS(arcount, &buf[i]); i += 2;
- do_log(D_DNS,
- "DNS id %d, b23 0x%x, qdcount %d, ancount %d, "
- "nscount %d, arcount %d\n",
- id, b23, qdcount, ancount, nscount, arcount);
- if((b23 & (0xF870)) != 0x8000) {
- do_log(L_ERROR, "Incorrect DNS reply (b23 = 0x%x).\n", b23);
- error = EDNS_INVALID;
- goto fail;
- }
- dnserror = b23 & 0xF;
- if(b23 & 0x200) {
- do_log(L_WARN, "Truncated DNS reply (b23 = 0x%x).\n", b23);
- }
- if(dnserror || qdcount != 1) {
- if(!dnserror)
- do_log(L_ERROR,
- "Unexpected number %d of DNS questions.\n", qdcount);
- if(dnserror == 1)
- error = EDNS_FORMAT;
- else if(dnserror == 2)
- error = EDNS_NO_RECOVERY;
- else if(dnserror == 3)
- error = EDNS_HOST_NOT_FOUND;
- else if(dnserror == 4 || dnserror == 5)
- error = EDNS_REFUSED;
- else if(dnserror == 0)
- error = EDNS_INVALID;
- else
- error = EUNKNOWN;
- goto fail;
- }
- /* We do this early, so that we can return the address family to
- the caller in case of error. */
- i = labelsToString(buf, i, n, b, 2048, &m);
- if(i < 0) {
- error = EDNS_FORMAT;
- goto fail;
- }
- DO_NTOHS(type, &buf[i]); i += 2;
- DO_NTOHS(class, &buf[i]); i += 2;
- if(type == 1)
- af = 4;
- else if(type == 28)
- af = 6;
- else {
- error = EDNS_FORMAT;
- goto fail;
- }
- do_log(D_DNS, "DNS q: ");
- do_log_n(D_DNS, b, m);
- do_log(D_DNS, " (%d, %d)\n", type, class);
- name = internAtomLowerN(b, m);
- if(name == NULL) {
- error = ENOMEM;
- goto fail;
- }
- if(class != 1) {
- error = EDNS_FORMAT;
- goto fail;
- }
- #define PARSE_ANSWER(kind, label) \
- do { \
- i = labelsToString(buf, i, 1024, b, 2048, &m); \
- if(i < 0) goto label; \
- DO_NTOHS(type, &buf[i]); i += 2; if(i > 1024) goto label; \
- DO_NTOHS(class, &buf[i]); i += 2; if(i > 1024) goto label; \
- DO_NTOHL(ttl, &buf[i]); i += 4; if(i > 1024) goto label; \
- DO_NTOHS(rdlength, &buf[i]); i += 2; if(i > 1024) goto label; \
- do_log(D_DNS, "DNS " kind ": "); \
- do_log_n(D_DNS, b, m); \
- do_log(D_DNS, " (%d, %d): %d bytes, ttl %u\n", \
- type, class, rdlength, ttl); \
- } while(0)
- for(j = 0; j < ancount; j++) {
- PARSE_ANSWER("an", fail);
- if(strcasecmp_n(name->string, b, m) == 0) {
- if(class != 1) {
- do_log(D_DNS, "DNS: %s: unknown class %d.\n",
- name->string, class);
- error = EDNS_UNSUPPORTED;
- goto cont;
- }
- if(type == 1 || type == 28) {
- if((type == 1 && rdlength != 4) ||
- (type == 28 && rdlength != 16)) {
- do_log(L_ERROR,
- "DNS: %s: unexpected length %d of %s record.\n",
- scrub(name->string),
- rdlength, type == 1 ? "A" : "AAAA");
- error = EDNS_INVALID;
- if(rdlength <= 0 || rdlength >= 32)
- goto fail;
- goto cont;
- }
- if(af == 0) {
- do_log(L_WARN, "DNS: %s: host has both A and CNAME -- "
- "ignoring CNAME.\n", scrub(name->string));
- addr_index = 0;
- af = -1;
- }
- if(type == 1) {
- if(af < 0)
- af = 4;
- else if(af == 6) {
- do_log(L_WARN, "Unexpected AAAA reply.\n");
- goto cont;
- }
- } else {
- if(af < 0)
- af = 6;
- else if(af == 4) {
- do_log(L_WARN, "Unexpected A reply.\n");
- goto cont;
- }
- }
- if(addr_index == 0) {
- addresses[0] = DNS_A;
- addr_index++;
- } else {
- if(addr_index > 1000) {
- error = EDNS_INVALID;
- goto fail;
- }
- }
- assert(addresses[0] == DNS_A);
- if(final_ttl > ttl)
- final_ttl = ttl;
- memset(&addresses[addr_index], 0, sizeof(HostAddressRec));
- if(type == 1) {
- addresses[addr_index] = 4;
- memcpy(addresses + addr_index + 1, buf + i, 4);
- } else {
- addresses[addr_index] = 6;
- memcpy(addresses + addr_index + 1, buf + i, 16);
- }
- addr_index += sizeof(HostAddressRec);
- } else if(type == 5) {
- int j, k;
- if(af != 0 && addr_index > 0) {
- do_log(L_WARN, "DNS: host has both CNAME and A -- "
- "ignoring CNAME.\n");
- goto cont;
- }
- af = 0;
- if(addr_index != 0) {
- /* Only warn if the CNAMEs are not identical */
- char tmp[512]; int jj, kk;
- assert(addresses[0] == DNS_CNAME);
- jj = labelsToString(buf, i, n,
- tmp, 512, &kk);
- if(jj < 0 ||
- kk != strlen(addresses + 1) ||
- memcmp(addresses + 1, tmp, kk) != 0) {
- do_log(L_WARN, "DNS: "
- "%s: host has multiple CNAMEs -- "
- "ignoring subsequent.\n",
- scrub(name->string));
- }
- goto cont;
- }
- addresses[0] = DNS_CNAME;
- addr_index++;
- j = labelsToString(buf, i, n,
- addresses + 1, 1020, &k);
- if(j < 0) {
- addr_index = 0;
- error = ENAMETOOLONG;
- continue;
- }
- addr_index = k + 1;
- } else {
- error = EDNS_NO_ADDRESS;
- i += rdlength;
- continue;
- }
- }
- cont:
- i += rdlength;
- }
- #if (LOGGING_MAX & D_DNS)
- for(j = 0; j < nscount; j++) {
- PARSE_ANSWER("ns", nofail);
- i += rdlength;
- }
- for(j = 0; j < arcount; j++) {
- PARSE_ANSWER("ar", nofail);
- i += rdlength;
- }
- nofail:
- #endif
- #undef PARSE_ANSWER
- do_log(D_DNS, "DNS: %d bytes\n", addr_index);
- if(af < 0)
- goto fail;
- value = internAtomN(addresses, addr_index);
- if(value == NULL) {
- error = ENOMEM;
- goto fail;
- }
- assert(af >= 0);
- *id_return = id;
- *name_return = name;
- *value_return = value;
- *af_return = af;
- *ttl_return = final_ttl;
- return 1;
- fail:
- *id_return = id;
- *name_return = name;
- *value_return = NULL;
- *af_return = af;
- return -error;
- }
- #else
- static int
- really_do_dns(AtomPtr name, ObjectPtr object)
- {
- abort();
- }
- #endif
|