From 0e5b2871ca6456b01d4bf037a6e68badf1ff1a41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henryk=20Pl=C3=B6tz?= Date: Fri, 3 Oct 2014 19:58:52 +0200 Subject: Initial commit of djbdns-1.05.tar.gz Source was http://cr.yp.to/djbdns/djbdns-1.05.tar.gz, SHA1 2efdb3a039d0c548f40936aa9cb30829e0ce8c3d --- query.c | 851 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 851 insertions(+) create mode 100644 query.c (limited to 'query.c') diff --git a/query.c b/query.c new file mode 100644 index 0000000..46cdc00 --- /dev/null +++ b/query.c @@ -0,0 +1,851 @@ +#include "error.h" +#include "roots.h" +#include "log.h" +#include "case.h" +#include "cache.h" +#include "byte.h" +#include "dns.h" +#include "uint64.h" +#include "uint32.h" +#include "uint16.h" +#include "dd.h" +#include "alloc.h" +#include "response.h" +#include "query.h" + +static int flagforwardonly = 0; + +void query_forwardonly(void) +{ + flagforwardonly = 1; +} + +static void cachegeneric(const char type[2],const char *d,const char *data,unsigned int datalen,uint32 ttl) +{ + unsigned int len; + char key[257]; + + len = dns_domain_length(d); + if (len > 255) return; + + byte_copy(key,2,type); + byte_copy(key + 2,len,d); + case_lowerb(key + 2,len); + + cache_set(key,len + 2,data,datalen,ttl); +} + +static char save_buf[8192]; +static unsigned int save_len; +static unsigned int save_ok; + +static void save_start(void) +{ + save_len = 0; + save_ok = 1; +} + +static void save_data(const char *buf,unsigned int len) +{ + if (!save_ok) return; + if (len > (sizeof save_buf) - save_len) { save_ok = 0; return; } + byte_copy(save_buf + save_len,len,buf); + save_len += len; +} + +static void save_finish(const char type[2],const char *d,uint32 ttl) +{ + if (!save_ok) return; + cachegeneric(type,d,save_buf,save_len,ttl); +} + + +static int typematch(const char rtype[2],const char qtype[2]) +{ + return byte_equal(qtype,2,rtype) || byte_equal(qtype,2,DNS_T_ANY); +} + +static uint32 ttlget(char buf[4]) +{ + uint32 ttl; + + uint32_unpack_big(buf,&ttl); + if (ttl > 1000000000) return 0; + if (ttl > 604800) return 604800; + return ttl; +} + + +static void cleanup(struct query *z) +{ + int j; + int k; + + dns_transmit_free(&z->dt); + for (j = 0;j < QUERY_MAXALIAS;++j) + dns_domain_free(&z->alias[j]); + for (j = 0;j < QUERY_MAXLEVEL;++j) { + dns_domain_free(&z->name[j]); + for (k = 0;k < QUERY_MAXNS;++k) + dns_domain_free(&z->ns[j][k]); + } +} + +static int rqa(struct query *z) +{ + int i; + + for (i = QUERY_MAXALIAS - 1;i >= 0;--i) + if (z->alias[i]) { + if (!response_query(z->alias[i],z->type,z->class)) return 0; + while (i > 0) { + if (!response_cname(z->alias[i],z->alias[i - 1],z->aliasttl[i])) return 0; + --i; + } + if (!response_cname(z->alias[0],z->name[0],z->aliasttl[0])) return 0; + return 1; + } + + if (!response_query(z->name[0],z->type,z->class)) return 0; + return 1; +} + +static int globalip(char *d,char ip[4]) +{ + if (dns_domain_equal(d,"\011localhost\0")) { + byte_copy(ip,4,"\177\0\0\1"); + return 1; + } + if (dd(d,"",ip) == 4) return 1; + return 0; +} + +static char *t1 = 0; +static char *t2 = 0; +static char *t3 = 0; +static char *cname = 0; +static char *referral = 0; +static unsigned int *records = 0; + +static int smaller(char *buf,unsigned int len,unsigned int pos1,unsigned int pos2) +{ + char header1[12]; + char header2[12]; + int r; + unsigned int len1; + unsigned int len2; + + pos1 = dns_packet_getname(buf,len,pos1,&t1); + dns_packet_copy(buf,len,pos1,header1,10); + pos2 = dns_packet_getname(buf,len,pos2,&t2); + dns_packet_copy(buf,len,pos2,header2,10); + + r = byte_diff(header1,4,header2); + if (r < 0) return 1; + if (r > 0) return 0; + + len1 = dns_domain_length(t1); + len2 = dns_domain_length(t2); + if (len1 < len2) return 1; + if (len1 > len2) return 0; + + r = case_diffb(t1,len1,t2); + if (r < 0) return 1; + if (r > 0) return 0; + + if (pos1 < pos2) return 1; + return 0; +} + +static int doit(struct query *z,int state) +{ + char key[257]; + char *cached; + unsigned int cachedlen; + char *buf; + unsigned int len; + const char *whichserver; + char header[12]; + char misc[20]; + unsigned int rcode; + unsigned int posanswers; + uint16 numanswers; + unsigned int posauthority; + uint16 numauthority; + unsigned int posglue; + uint16 numglue; + unsigned int pos; + unsigned int pos2; + uint16 datalen; + char *control; + char *d; + const char *dtype; + unsigned int dlen; + int flagout; + int flagcname; + int flagreferral; + int flagsoa; + uint32 ttl; + uint32 soattl; + uint32 cnamettl; + int i; + int j; + int k; + int p; + int q; + + errno = error_io; + if (state == 1) goto HAVEPACKET; + if (state == -1) { + log_servfail(z->name[z->level]); + goto SERVFAIL; + } + + + NEWNAME: + if (++z->loop == 100) goto DIE; + d = z->name[z->level]; + dtype = z->level ? DNS_T_A : z->type; + dlen = dns_domain_length(d); + + if (globalip(d,misc)) { + if (z->level) { + for (k = 0;k < 64;k += 4) + if (byte_equal(z->servers[z->level - 1] + k,4,"\0\0\0\0")) { + byte_copy(z->servers[z->level - 1] + k,4,misc); + break; + } + goto LOWERLEVEL; + } + if (!rqa(z)) goto DIE; + if (typematch(DNS_T_A,dtype)) { + if (!response_rstart(d,DNS_T_A,655360)) goto DIE; + if (!response_addbytes(misc,4)) goto DIE; + response_rfinish(RESPONSE_ANSWER); + } + cleanup(z); + return 1; + } + + if (dns_domain_equal(d,"\0011\0010\0010\003127\7in-addr\4arpa\0")) { + if (z->level) goto LOWERLEVEL; + if (!rqa(z)) goto DIE; + if (typematch(DNS_T_PTR,dtype)) { + if (!response_rstart(d,DNS_T_PTR,655360)) goto DIE; + if (!response_addname("\011localhost\0")) goto DIE; + response_rfinish(RESPONSE_ANSWER); + } + cleanup(z); + log_stats(); + return 1; + } + + if (dlen <= 255) { + byte_copy(key,2,DNS_T_ANY); + byte_copy(key + 2,dlen,d); + case_lowerb(key + 2,dlen); + cached = cache_get(key,dlen + 2,&cachedlen,&ttl); + if (cached) { + log_cachednxdomain(d); + goto NXDOMAIN; + } + + byte_copy(key,2,DNS_T_CNAME); + cached = cache_get(key,dlen + 2,&cachedlen,&ttl); + if (cached) { + if (typematch(DNS_T_CNAME,dtype)) { + log_cachedanswer(d,DNS_T_CNAME); + if (!rqa(z)) goto DIE; + if (!response_cname(z->name[0],cached,ttl)) goto DIE; + cleanup(z); + return 1; + } + log_cachedcname(d,cached); + if (!dns_domain_copy(&cname,cached)) goto DIE; + goto CNAME; + } + + if (typematch(DNS_T_NS,dtype)) { + byte_copy(key,2,DNS_T_NS); + cached = cache_get(key,dlen + 2,&cachedlen,&ttl); + if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) { + log_cachedanswer(d,DNS_T_NS); + if (!rqa(z)) goto DIE; + pos = 0; + while (pos = dns_packet_getname(cached,cachedlen,pos,&t2)) { + if (!response_rstart(d,DNS_T_NS,ttl)) goto DIE; + if (!response_addname(t2)) goto DIE; + response_rfinish(RESPONSE_ANSWER); + } + cleanup(z); + return 1; + } + } + + if (typematch(DNS_T_PTR,dtype)) { + byte_copy(key,2,DNS_T_PTR); + cached = cache_get(key,dlen + 2,&cachedlen,&ttl); + if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) { + log_cachedanswer(d,DNS_T_PTR); + if (!rqa(z)) goto DIE; + pos = 0; + while (pos = dns_packet_getname(cached,cachedlen,pos,&t2)) { + if (!response_rstart(d,DNS_T_PTR,ttl)) goto DIE; + if (!response_addname(t2)) goto DIE; + response_rfinish(RESPONSE_ANSWER); + } + cleanup(z); + return 1; + } + } + + if (typematch(DNS_T_MX,dtype)) { + byte_copy(key,2,DNS_T_MX); + cached = cache_get(key,dlen + 2,&cachedlen,&ttl); + if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) { + log_cachedanswer(d,DNS_T_MX); + if (!rqa(z)) goto DIE; + pos = 0; + while (pos = dns_packet_copy(cached,cachedlen,pos,misc,2)) { + pos = dns_packet_getname(cached,cachedlen,pos,&t2); + if (!pos) break; + if (!response_rstart(d,DNS_T_MX,ttl)) goto DIE; + if (!response_addbytes(misc,2)) goto DIE; + if (!response_addname(t2)) goto DIE; + response_rfinish(RESPONSE_ANSWER); + } + cleanup(z); + return 1; + } + } + + if (typematch(DNS_T_A,dtype)) { + byte_copy(key,2,DNS_T_A); + cached = cache_get(key,dlen + 2,&cachedlen,&ttl); + if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) { + if (z->level) { + log_cachedanswer(d,DNS_T_A); + while (cachedlen >= 4) { + for (k = 0;k < 64;k += 4) + if (byte_equal(z->servers[z->level - 1] + k,4,"\0\0\0\0")) { + byte_copy(z->servers[z->level - 1] + k,4,cached); + break; + } + cached += 4; + cachedlen -= 4; + } + goto LOWERLEVEL; + } + + log_cachedanswer(d,DNS_T_A); + if (!rqa(z)) goto DIE; + while (cachedlen >= 4) { + if (!response_rstart(d,DNS_T_A,ttl)) goto DIE; + if (!response_addbytes(cached,4)) goto DIE; + response_rfinish(RESPONSE_ANSWER); + cached += 4; + cachedlen -= 4; + } + cleanup(z); + return 1; + } + } + + if (!typematch(DNS_T_ANY,dtype) && !typematch(DNS_T_AXFR,dtype) && !typematch(DNS_T_CNAME,dtype) && !typematch(DNS_T_NS,dtype) && !typematch(DNS_T_PTR,dtype) && !typematch(DNS_T_A,dtype) && !typematch(DNS_T_MX,dtype)) { + byte_copy(key,2,dtype); + cached = cache_get(key,dlen + 2,&cachedlen,&ttl); + if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) { + log_cachedanswer(d,dtype); + if (!rqa(z)) goto DIE; + while (cachedlen >= 2) { + uint16_unpack_big(cached,&datalen); + cached += 2; + cachedlen -= 2; + if (datalen > cachedlen) goto DIE; + if (!response_rstart(d,dtype,ttl)) goto DIE; + if (!response_addbytes(cached,datalen)) goto DIE; + response_rfinish(RESPONSE_ANSWER); + cached += datalen; + cachedlen -= datalen; + } + cleanup(z); + return 1; + } + } + } + + for (;;) { + if (roots(z->servers[z->level],d)) { + for (j = 0;j < QUERY_MAXNS;++j) + dns_domain_free(&z->ns[z->level][j]); + z->control[z->level] = d; + break; + } + + if (!flagforwardonly && (z->level < 2)) + if (dlen < 255) { + byte_copy(key,2,DNS_T_NS); + byte_copy(key + 2,dlen,d); + case_lowerb(key + 2,dlen); + cached = cache_get(key,dlen + 2,&cachedlen,&ttl); + if (cached && cachedlen) { + z->control[z->level] = d; + byte_zero(z->servers[z->level],64); + for (j = 0;j < QUERY_MAXNS;++j) + dns_domain_free(&z->ns[z->level][j]); + pos = 0; + j = 0; + while (pos = dns_packet_getname(cached,cachedlen,pos,&t1)) { + log_cachedns(d,t1); + if (j < QUERY_MAXNS) + if (!dns_domain_copy(&z->ns[z->level][j++],t1)) goto DIE; + } + break; + } + } + + if (!*d) goto DIE; + j = 1 + (unsigned int) (unsigned char) *d; + dlen -= j; + d += j; + } + + + HAVENS: + for (j = 0;j < QUERY_MAXNS;++j) + if (z->ns[z->level][j]) { + if (z->level + 1 < QUERY_MAXLEVEL) { + if (!dns_domain_copy(&z->name[z->level + 1],z->ns[z->level][j])) goto DIE; + dns_domain_free(&z->ns[z->level][j]); + ++z->level; + goto NEWNAME; + } + dns_domain_free(&z->ns[z->level][j]); + } + + for (j = 0;j < 64;j += 4) + if (byte_diff(z->servers[z->level] + j,4,"\0\0\0\0")) + break; + if (j == 64) goto SERVFAIL; + + dns_sortip(z->servers[z->level],64); + if (z->level) { + log_tx(z->name[z->level],DNS_T_A,z->control[z->level],z->servers[z->level],z->level); + if (dns_transmit_start(&z->dt,z->servers[z->level],flagforwardonly,z->name[z->level],DNS_T_A,z->localip) == -1) goto DIE; + } + else { + log_tx(z->name[0],z->type,z->control[0],z->servers[0],0); + if (dns_transmit_start(&z->dt,z->servers[0],flagforwardonly,z->name[0],z->type,z->localip) == -1) goto DIE; + } + return 0; + + + LOWERLEVEL: + dns_domain_free(&z->name[z->level]); + for (j = 0;j < QUERY_MAXNS;++j) + dns_domain_free(&z->ns[z->level][j]); + --z->level; + goto HAVENS; + + + HAVEPACKET: + if (++z->loop == 100) goto DIE; + buf = z->dt.packet; + len = z->dt.packetlen; + + whichserver = z->dt.servers + 4 * z->dt.curserver; + control = z->control[z->level]; + d = z->name[z->level]; + dtype = z->level ? DNS_T_A : z->type; + + pos = dns_packet_copy(buf,len,0,header,12); if (!pos) goto DIE; + pos = dns_packet_skipname(buf,len,pos); if (!pos) goto DIE; + pos += 4; + posanswers = pos; + + uint16_unpack_big(header + 6,&numanswers); + uint16_unpack_big(header + 8,&numauthority); + uint16_unpack_big(header + 10,&numglue); + + rcode = header[3] & 15; + if (rcode && (rcode != 3)) goto DIE; /* impossible; see irrelevant() */ + + flagout = 0; + flagcname = 0; + flagreferral = 0; + flagsoa = 0; + soattl = 0; + cnamettl = 0; + for (j = 0;j < numanswers;++j) { + pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; + + if (dns_domain_equal(t1,d)) + if (byte_equal(header + 2,2,DNS_C_IN)) { /* should always be true */ + if (typematch(header,dtype)) + flagout = 1; + else if (typematch(header,DNS_T_CNAME)) { + if (!dns_packet_getname(buf,len,pos,&cname)) goto DIE; + flagcname = 1; + cnamettl = ttlget(header + 4); + } + } + + uint16_unpack_big(header + 8,&datalen); + pos += datalen; + } + posauthority = pos; + + for (j = 0;j < numauthority;++j) { + pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; + + if (typematch(header,DNS_T_SOA)) { + flagsoa = 1; + soattl = ttlget(header + 4); + if (soattl > 3600) soattl = 3600; + } + else if (typematch(header,DNS_T_NS)) { + flagreferral = 1; + if (!dns_domain_copy(&referral,t1)) goto DIE; + } + + uint16_unpack_big(header + 8,&datalen); + pos += datalen; + } + posglue = pos; + + + if (!flagcname && !rcode && !flagout && flagreferral && !flagsoa) + if (dns_domain_equal(referral,control) || !dns_domain_suffix(referral,control)) { + log_lame(whichserver,control,referral); + byte_zero(whichserver,4); + goto HAVENS; + } + + + if (records) { alloc_free(records); records = 0; } + + k = numanswers + numauthority + numglue; + records = (unsigned int *) alloc(k * sizeof(unsigned int)); + if (!records) goto DIE; + + pos = posanswers; + for (j = 0;j < k;++j) { + records[j] = pos; + pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; + uint16_unpack_big(header + 8,&datalen); + pos += datalen; + } + + i = j = k; + while (j > 1) { + if (i > 1) { --i; pos = records[i - 1]; } + else { pos = records[j - 1]; records[j - 1] = records[i - 1]; --j; } + + q = i; + while ((p = q * 2) < j) { + if (!smaller(buf,len,records[p],records[p - 1])) ++p; + records[q - 1] = records[p - 1]; q = p; + } + if (p == j) { + records[q - 1] = records[p - 1]; q = p; + } + while ((q > i) && smaller(buf,len,records[(p = q/2) - 1],pos)) { + records[q - 1] = records[p - 1]; q = p; + } + records[q - 1] = pos; + } + + i = 0; + while (i < k) { + char type[2]; + + pos = dns_packet_getname(buf,len,records[i],&t1); if (!pos) goto DIE; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; + ttl = ttlget(header + 4); + + byte_copy(type,2,header); + if (byte_diff(header + 2,2,DNS_C_IN)) { ++i; continue; } + + for (j = i + 1;j < k;++j) { + pos = dns_packet_getname(buf,len,records[j],&t2); if (!pos) goto DIE; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; + if (!dns_domain_equal(t1,t2)) break; + if (byte_diff(header,2,type)) break; + if (byte_diff(header + 2,2,DNS_C_IN)) break; + } + + if (!dns_domain_suffix(t1,control)) { i = j; continue; } + if (!roots_same(t1,control)) { i = j; continue; } + + if (byte_equal(type,2,DNS_T_ANY)) + ; + else if (byte_equal(type,2,DNS_T_AXFR)) + ; + else if (byte_equal(type,2,DNS_T_SOA)) { + while (i < j) { + pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE; + pos = dns_packet_getname(buf,len,pos + 10,&t2); if (!pos) goto DIE; + pos = dns_packet_getname(buf,len,pos,&t3); if (!pos) goto DIE; + pos = dns_packet_copy(buf,len,pos,misc,20); if (!pos) goto DIE; + if (records[i] < posauthority) + log_rrsoa(whichserver,t1,t2,t3,misc,ttl); + ++i; + } + } + else if (byte_equal(type,2,DNS_T_CNAME)) { + pos = dns_packet_skipname(buf,len,records[j - 1]); if (!pos) goto DIE; + pos = dns_packet_getname(buf,len,pos + 10,&t2); if (!pos) goto DIE; + log_rrcname(whichserver,t1,t2,ttl); + cachegeneric(DNS_T_CNAME,t1,t2,dns_domain_length(t2),ttl); + } + else if (byte_equal(type,2,DNS_T_PTR)) { + save_start(); + while (i < j) { + pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE; + pos = dns_packet_getname(buf,len,pos + 10,&t2); if (!pos) goto DIE; + log_rrptr(whichserver,t1,t2,ttl); + save_data(t2,dns_domain_length(t2)); + ++i; + } + save_finish(DNS_T_PTR,t1,ttl); + } + else if (byte_equal(type,2,DNS_T_NS)) { + save_start(); + while (i < j) { + pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE; + pos = dns_packet_getname(buf,len,pos + 10,&t2); if (!pos) goto DIE; + log_rrns(whichserver,t1,t2,ttl); + save_data(t2,dns_domain_length(t2)); + ++i; + } + save_finish(DNS_T_NS,t1,ttl); + } + else if (byte_equal(type,2,DNS_T_MX)) { + save_start(); + while (i < j) { + pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE; + pos = dns_packet_copy(buf,len,pos + 10,misc,2); if (!pos) goto DIE; + pos = dns_packet_getname(buf,len,pos,&t2); if (!pos) goto DIE; + log_rrmx(whichserver,t1,t2,misc,ttl); + save_data(misc,2); + save_data(t2,dns_domain_length(t2)); + ++i; + } + save_finish(DNS_T_MX,t1,ttl); + } + else if (byte_equal(type,2,DNS_T_A)) { + save_start(); + while (i < j) { + pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; + if (byte_equal(header + 8,2,"\0\4")) { + pos = dns_packet_copy(buf,len,pos,header,4); if (!pos) goto DIE; + save_data(header,4); + log_rr(whichserver,t1,DNS_T_A,header,4,ttl); + } + ++i; + } + save_finish(DNS_T_A,t1,ttl); + } + else { + save_start(); + while (i < j) { + pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; + uint16_unpack_big(header + 8,&datalen); + if (datalen > len - pos) goto DIE; + save_data(header + 8,2); + save_data(buf + pos,datalen); + log_rr(whichserver,t1,type,buf + pos,datalen,ttl); + ++i; + } + save_finish(type,t1,ttl); + } + + i = j; + } + + alloc_free(records); records = 0; + + + if (flagcname) { + ttl = cnamettl; + CNAME: + if (!z->level) { + if (z->alias[QUERY_MAXALIAS - 1]) goto DIE; + for (j = QUERY_MAXALIAS - 1;j > 0;--j) + z->alias[j] = z->alias[j - 1]; + for (j = QUERY_MAXALIAS - 1;j > 0;--j) + z->aliasttl[j] = z->aliasttl[j - 1]; + z->alias[0] = z->name[0]; + z->aliasttl[0] = ttl; + z->name[0] = 0; + } + if (!dns_domain_copy(&z->name[z->level],cname)) goto DIE; + goto NEWNAME; + } + + if (rcode == 3) { + log_nxdomain(whichserver,d,soattl); + cachegeneric(DNS_T_ANY,d,"",0,soattl); + + NXDOMAIN: + if (z->level) goto LOWERLEVEL; + if (!rqa(z)) goto DIE; + response_nxdomain(); + cleanup(z); + return 1; + } + + if (!flagout && flagsoa) + if (byte_diff(DNS_T_ANY,2,dtype)) + if (byte_diff(DNS_T_AXFR,2,dtype)) + if (byte_diff(DNS_T_CNAME,2,dtype)) { + save_start(); + save_finish(dtype,d,soattl); + log_nodata(whichserver,d,dtype,soattl); + } + + log_stats(); + + + if (flagout || flagsoa || !flagreferral) { + if (z->level) { + pos = posanswers; + for (j = 0;j < numanswers;++j) { + pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; + uint16_unpack_big(header + 8,&datalen); + if (dns_domain_equal(t1,d)) + if (typematch(header,DNS_T_A)) + if (byte_equal(header + 2,2,DNS_C_IN)) /* should always be true */ + if (datalen == 4) + for (k = 0;k < 64;k += 4) + if (byte_equal(z->servers[z->level - 1] + k,4,"\0\0\0\0")) { + if (!dns_packet_copy(buf,len,pos,z->servers[z->level - 1] + k,4)) goto DIE; + break; + } + pos += datalen; + } + goto LOWERLEVEL; + } + + if (!rqa(z)) goto DIE; + + pos = posanswers; + for (j = 0;j < numanswers;++j) { + pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; + ttl = ttlget(header + 4); + uint16_unpack_big(header + 8,&datalen); + if (dns_domain_equal(t1,d)) + if (byte_equal(header + 2,2,DNS_C_IN)) /* should always be true */ + if (typematch(header,dtype)) { + if (!response_rstart(t1,header,ttl)) goto DIE; + + if (typematch(header,DNS_T_NS) || typematch(header,DNS_T_CNAME) || typematch(header,DNS_T_PTR)) { + if (!dns_packet_getname(buf,len,pos,&t2)) goto DIE; + if (!response_addname(t2)) goto DIE; + } + else if (typematch(header,DNS_T_MX)) { + pos2 = dns_packet_copy(buf,len,pos,misc,2); if (!pos2) goto DIE; + if (!response_addbytes(misc,2)) goto DIE; + if (!dns_packet_getname(buf,len,pos2,&t2)) goto DIE; + if (!response_addname(t2)) goto DIE; + } + else if (typematch(header,DNS_T_SOA)) { + pos2 = dns_packet_getname(buf,len,pos,&t2); if (!pos2) goto DIE; + if (!response_addname(t2)) goto DIE; + pos2 = dns_packet_getname(buf,len,pos2,&t3); if (!pos2) goto DIE; + if (!response_addname(t3)) goto DIE; + pos2 = dns_packet_copy(buf,len,pos2,misc,20); if (!pos2) goto DIE; + if (!response_addbytes(misc,20)) goto DIE; + } + else { + if (pos + datalen > len) goto DIE; + if (!response_addbytes(buf + pos,datalen)) goto DIE; + } + + response_rfinish(RESPONSE_ANSWER); + } + + pos += datalen; + } + + cleanup(z); + return 1; + } + + + if (!dns_domain_suffix(d,referral)) goto DIE; + control = d + dns_domain_suffixpos(d,referral); + z->control[z->level] = control; + byte_zero(z->servers[z->level],64); + for (j = 0;j < QUERY_MAXNS;++j) + dns_domain_free(&z->ns[z->level][j]); + k = 0; + + pos = posauthority; + for (j = 0;j < numauthority;++j) { + pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; + uint16_unpack_big(header + 8,&datalen); + if (dns_domain_equal(referral,t1)) /* should always be true */ + if (typematch(header,DNS_T_NS)) /* should always be true */ + if (byte_equal(header + 2,2,DNS_C_IN)) /* should always be true */ + if (k < QUERY_MAXNS) + if (!dns_packet_getname(buf,len,pos,&z->ns[z->level][k++])) goto DIE; + pos += datalen; + } + + goto HAVENS; + + + SERVFAIL: + if (z->level) goto LOWERLEVEL; + if (!rqa(z)) goto DIE; + response_servfail(); + cleanup(z); + return 1; + + + DIE: + cleanup(z); + if (records) { alloc_free(records); records = 0; } + return -1; +} + +int query_start(struct query *z,char *dn,char type[2],char class[2],char localip[4]) +{ + if (byte_equal(type,2,DNS_T_AXFR)) { errno = error_perm; return -1; } + + cleanup(z); + z->level = 0; + z->loop = 0; + + if (!dns_domain_copy(&z->name[0],dn)) return -1; + byte_copy(z->type,2,type); + byte_copy(z->class,2,class); + byte_copy(z->localip,4,localip); + + return doit(z,0); +} + +int query_get(struct query *z,iopause_fd *x,struct taia *stamp) +{ + switch(dns_transmit_get(&z->dt,x,stamp)) { + case 1: + return doit(z,1); + case -1: + return doit(z,-1); + } + return 0; +} + +void query_io(struct query *z,iopause_fd *x,struct taia *deadline) +{ + dns_transmit_io(&z->dt,x,deadline); +} -- cgit v1.2.3