X-Git-Url: https://git.distorted.org.uk/~mdw/adns/blobdiff_plain/b9de380c1e587b6c5828cb9de796746024946880..d3eea6424e9ba051c0e72b3c39cd826fda168762:/src/reply.c diff --git a/src/reply.c b/src/reply.c index b6218c1..39337e8 100644 --- a/src/reply.c +++ b/src/reply.c @@ -2,141 +2,26 @@ #include "internal.h" -typedef enum { - rcode_noerror, - rcode_formaterror, - rcode_servfail, - rcode_nxdomain, - rcode_notimp, - rcode_refused -} dns_rcode; - -#define GETIL_B(cb) (dgram[*(cb)++]) -#define GET_B(cb,tv) ((tv)= GETIL_B((cb))) -#define GET_W(cb,tv) ((tv)=0, (tv)|=(GETIL_B((cb))<<8), (tv)|=GETIL_B(cb), (tv)) - -static void vbuf__append_quoted1035(vbuf *vb, const byte *buf, int len) { - char qbuf[10]; - int i; - - while (len) { - qbuf[0]= 0; - for (i=0; i= 127) { - sprintf(qbuf,"\\%03o",ch); - break; - } - } - if (!adns__vbuf_append(vb,buf,i) || !adns__vbuf_append(vb,qbuf,strlen(qbuf))) - return adns_s_nolocalmem; - buf+= i; len-= i; - } -} - -static adns_status get_domain_perm(adns_state ads, adns_query qu, int serv, - const byte *dgram, int dglen, - int *cbyte_io, int max, char **domain_r) { - /* Returns 0 for OK (*domain_r set) or truncated (*domain_r null) - * or any other adns_s_* value. - */ - int cbyte, sused, lablen; - - /* If we follow a pointer we set cbyte_io to 0 to indicate that - * we've lost our original starting and ending points; we don't - * put the end of the pointed-to thing into the original *cbyte_io. - */ - cbyte= *cbyte_io; - sused= qu->ans.used; - *domain_r= 0; - for (;;) { - if (cbyte>=max) goto x_truncated; - lablen= GET_B(cbyte); - if (!lablen) break; - if (lablen&0x0c000) { - if ((lablen&0x0c000) != 0x0c0000) return adns_s_unknownreply; - if (cbyte_io) { *cbyte_io= cbyte; cbyte_io= 0; } - cbyte= (lablen&0x3fff) + DNS_HDR_SIZE; - max= dglen; - continue; - } - if (cbyte+lablen>=max) bgoto x_truncated; - if (qu->ans.used != sused) - if (!adns__vbuf_append(&qu->ans,".",1)) return adns_s_nolocalmem; - if (qu->flags & adns_qf_anyquote) { - if (!vbuf__append_quoted1035(&qu->ans,dgram+cbyte,lablen)) - return adns_s_nolocalmem; - } else { - if (!ctype_isalpha(dgram[cbyte])) return adns_s_invaliddomain; - for (i= cbyte+1; ians,dgram+cbyte,lablen)) - return adns_s_nolocalmem; - } - } - if (cbyte_io) *cbyte_io= cbyte; - if (!adns__vbuf_append(&qu->ans,"",1)) return adns_s_nolocalmem; - *domain_r= qu->ans.buf+sused; - return adns_s_ok; - - x_truncated: - return cbyte_io ? -1 : adns_s_serverfaulty; +static void cname_recurse(adns_state ads, adns_query qu, adns_queryflags xflags) { + abort(); /* FIXME */ } -static adns_status get_domain_temp(adns_state ads, adns_query qu, int serv, - const byte *dgram, int dglen, - int *cbyte_io, int max, char **domain_r) { - int sused; +void adns__procdgram(adns_state ads, const byte *dgram, int dglen, + int serv, struct timeval now) { + int cbyte, rrstart, wantedrrs, rri, foundsoa, foundns; + int id, f1, f2, qdcount, ancount, nscount, arcount, flg_ra, flg_tc; + int rrtype, rrclass, rdlength, rdstart, ownermatched, ownerstart; + int anstart, nsstart, arstart; + int currentrrs; + adns_query qu, nqu; + dns_rcode rcode; adns_status st; - - sused= qu->ans.used; - st= get_domain_perm(ads,qu,serv,dgram,dglen,cbyte_io,max,domain_r); - qu->ans.used= sused; - return st; -} - -/* fixme: sensible comparison of owners */ - -static adns_status get_rr_temp(adns_state ads, adns_query qu, int serv, - const byte *dgram, int dglen, - int *cbyte_io, - int *type_r, int *class_r, int *rdlen_r, int *rdstart_r, - char **owner_r) { - int cbyte, tmp, rdlen; - - cbyte= *cbyte_io; - st= get_domain_temp(ads,qu,serv,dgram,dglen,&cbyte,dglen,owner_r); - if (st) return st; - - if (cbyte+10>len) goto x_truncated; - GET_W(cbyte,tmp); if (type_r) *type_r= tmp; - GET_W(cbyte,tmp); if (class_r) *class_r= tmp; - cbyte+= 4; /* we skip the TTL */ - GET_W(cbyte,rdlen); if (rdlen_r) *rdlen_r= tmp; - if (rdstart_r) *rdstart_r= cbyte; - cbyte+= rdlen; - if (cbyte>dglen) goto x_truncated; - *cbyte_io= cbyte; - return adns_s_ok; - - x_truncated: - *owner_r= 0; return 0;; -} - -void adns__procdgram(adns_state ads, const byte *dgram, int dglen, int serv) { - int cbyte, anstart, rrstart, lablen, wantedrrs, get_t; + adns_answer *ans; cbyte= 0; - if (dglentimew; qu= nqu; qu++) { + for (qu= ads->timew.head; qu; qu= nqu) { nqu= qu->next; if (qu->id != id) continue; - if (len < qu->querylen) continue; - if (memcmp(qu->querymsg+DNSHDRSIZE,dgram+DNSHDRSIZE,qu->querylen-DNSHDRSIZE)) + if (dglen < qu->querylen) continue; + if (memcmp(qu->querymsg+DNS_HDRSIZE,dgram+DNS_HDRSIZE,qu->querylen-DNS_HDRSIZE)) continue; break; } + assert(qu->cnameoff == -1); anstart= qu->querylen; if (!qu) { adns__debug(ads,serv,"reply not found (id=%02x)",id); return; } + + LIST_UNLINK(ads->timew,qu); + /* We're definitely going to do something with this query now */ + if (!(f1&0x01)) { adns__diag(ads,serv,"server thinks we didn't ask for recursive lookup"); adns__query_fail(ads,qu,adns_s_serverfaulty); @@ -193,8 +86,8 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen, int serv) { adns__warn(ads,serv,"server cannot understand our query (Format Error)"); adns__query_fail(ads,qu,adns_s_serverfaulty); return; - case rcode_servfail; - adns__query_fail(ads,qu,adns_s_serverfailure); + case rcode_servfail: + adns__query_fail(ads,qu,adns_s_servfail); return; case rcode_notimp: adns__warn(ads,serv,"server claims not to implement our query"); @@ -216,28 +109,50 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen, int serv) { wantedrrs= 0; for (rri= 0; rricnameoff >= 0) { + st= adns__get_rr_temp(ads,qu,serv, dgram,dglen,&cbyte, + &rrtype,&rrclass,&rdlength,&rdstart, + dgram,dglen,qu->cnameoff, &ownermatched); + } else { + st= adns__get_rr_temp(ads,qu,serv, dgram,dglen,&cbyte, + &rrtype,&rrclass,&rdlength,&rdstart, + qu->querymsg,qu->querylen,DNS_HDRSIZE, &ownermatched); + } if (st) adns__query_fail(ads,qu,st); + if (rrtype == -1) goto x_truncated; + if (rrclass != DNS_CLASS_IN) { - adns__diag(ads,serv,"ignoring RR with wrong class %d (expected IN=%d)", + adns__diag(ads,serv,"ignoring answer RR with wrong class %d (expected IN=%d)", rrclass,DNS_CLASS_IN); continue; } - if (strcmp_quoted1035(cowner, qu->cname ? qu->cname : qu->owner)) { - adns__debug(ads,serv,"ignoring answer RR with irrelevant owner \"%s\"",cowner); + if (!ownermatched) { + if (ads->iflags & adns_if_debug) { + st= adns__get_domain_temp(ads,qu,serv, dgram,dglen,&rrstart,dglen, &ownerstart); + if (st) + adns__debug(ads,serv, "ignoring RR with an irrelevant owner" + " whose format is bad, code %d",st); + else if (ownerstart>=0) + adns__debug(ads,serv, "ignoring RR with an irrelevant owner" + " \"%s\"", qu->ans.buf+ownerstart); + else + adns__debug(ads,serv,"ignoring RR with an irrelevant truncated owner"); + } continue; } - if (!qu->cname && - (qu->type & adns__rrt_typemask) != adns_cname && - rrtype == adns_cname) { /* Ignore second and subsequent CNAMEs */ - qu->cname= get_domain_perm(ads,qu,dgram,len,rdstart,rdstart+rdlength); - /* If we find the answer section truncated after this point we restart - * the query at the CNAME; otherwise we can use it as-is. - */ - } else if (rrtype == (qu->type & adns__rrt_typemask)) { + if (qu->cnameoff<0 && + (qu->typei->type & adns__rrt_typemask) != adns_r_cname && + rrtype == adns_r_cname) { /* Ignore second and subsequent CNAMEs */ + st= adns__get_domain_perm(ads,qu,serv, dgram,dglen, + &rdstart,rdstart+rdlength,&qu->cnameoff); + if (st) { adns__query_fail(ads,qu,st); return; } + if (qu->cnameoff==-1) goto x_truncated; + /* If we find the answer section truncated after this point we restart + * the query at the CNAME; if beforehand then we obviously have to use + * TCP. If there is no truncation we can use the whole answer if + * it contains the relevant info. + */ + } else if (rrtype == (qu->typei->type & adns__rrt_typemask)) { wantedrrs++; } else { adns__debug(ads,serv,"ignoring answer RR with irrelevant type %d",rrtype); @@ -251,48 +166,94 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen, int serv) { /* Oops, NODATA or NXDOMAIN or perhaps a referral (which would be a problem) */ if (rcode == rcode_nxdomain) { - adnns__query_finish(ads,qu,adns_s_nxdomain); + adns__query_finish(ads,qu,adns_s_nxdomain); return; } /* RFC2308: NODATA has _either_ a SOA _or_ _no_ NS records in authority section */ + foundsoa= 0; foundns= 0; for (rri= 0; rricnameoff != -1) { cname_recurse(ads,qu,0); return; } + + /* Bloody hell, I thought we asked for recursion ? */ + if (!flg_ra) { + adns__diag(ads,serv,"server is not willing to do recursive lookups for us"); + adns__query_fail(ads,qu,adns_s_norecurse); + return; + } + adns__diag(ads,serv,"server claims to do recursion, but gave us a referral"); + adns__query_fail(ads,qu,adns_s_serverfaulty); return; } - - ) { - if (type - if (cbyte+lab - if (anstart > dgend) { truncated(ads,qu,f1); return; } - } - for + /* Now, we have some RRs which we wanted. */ - /* Look for CNAMEs in the answer section */ + qu->rrsoff= adns__vbuf_malloc(&qu->ans,qu->typei->rrsz*wantedrrs); + if (qu->rrsoff == -1) adns__query_fail(ads,qu,adns_s_nolocalmem); + cbyte= anstart; + currentrrs= 0; + arstart= -1; + for (rri=0; rritypei->type & adns__rrt_typemask)) + continue; + assert(currentrrstypei->get_fn(ads,qu,serv, dgram,dglen, &rdstart,rdstart+rdlength, + nsstart,arcount,&arstart, qu->rrsoff,¤trrs); + if (st) { adns__query_fail(ads,qu,st); return; } + if (currentrrs==-1) goto x_truncated; } - - - adns__diag(ads,serv,"server refused our query"); - - case rcode_ - - case 0: /* NOERROR - break; - case 1: /* Format error */ - case 3: /* Name Error */ - - qr= f1&0x80; - - - adns__diag(ads,serv,"received datagram size %d",len); + /* This may have generated some child queries ... */ + if (qu->children.head) { + qu->state= query_child; + LIST_LINK_TAIL(ads->childw,qu); + return; + } + + adns__query_finish(ads,qu,adns_s_ok); + return; + +x_truncated: + if (!flg_tc) { + adns__diag(ads,serv,"server sent datagram which points outside itself"); + adns__query_fail(ads,qu,adns_s_serverfaulty); + return; + } + if (qu->cnameoff != -1) { cname_recurse(ads,qu,adns_qf_usevc); return; } + qu->cnameoff= -1; + qu->rrsoff= -1; + ans= (adns_answer*)qu->ans.buf; + ans->nrrs= 0; + qu->ans.used= sizeof(adns_answer); + qu->flags |= adns_qf_usevc; + adns__query_udp(ads,qu,now); }