X-Git-Url: https://git.distorted.org.uk/~mdw/adns/blobdiff_plain/ec477b9e0921b754a2edcee988d16a6339be1708..b9de380c1e587b6c5828cb9de796746024946880:/src/reply.c diff --git a/src/reply.c b/src/reply.c index 6aacd2b..b6218c1 100644 --- a/src/reply.c +++ b/src/reply.c @@ -5,26 +5,148 @@ typedef enum { rcode_noerror, rcode_formaterror, - rcode_serverfail, + rcode_servfail, rcode_nxdomain, rcode_notimp, rcode_refused } dns_rcode; -void adns__procdgram(adns_state ads, const byte *dgram, int len, int serv) { - unsigned char *rpp; +#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 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; + 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; + + cbyte= 0; - if (len<12) { + if (dglennext; if (qu->id != id) continue; if (len < qu->querylen) continue; - if (memcmp(qu->querymsg+12,rpp,qu->querylen-12)) continue; + if (memcmp(qu->querymsg+DNSHDRSIZE,dgram+DNSHDRSIZE,qu->querylen-DNSHDRSIZE)) + continue; break; } + anstart= qu->querylen; if (!qu) { adns__debug(ads,serv,"reply not found (id=%02x)",id); return; } - if (!(f2&0x80)) { - adns__diag(ads,serv,"server is not willing to do recursive lookups for us"); - adns__query_fail(ads,qu,adns_s_norecurse); - return; - } if (!(f1&0x01)) { adns__diag(ads,serv,"server thinks we didn't ask for recursive lookup"); adns__query_fail(ads,qu,adns_s_serverfaulty); return; } - switch (f1&0x0f) { - case 0: /* NOERROR */ + + rcode= (f1&0x0f); + switch (rcode) { + case rcode_noerror: + case rcode_nxdomain: break; - case 1: /* Format error */ - adns__diag(ads,serv,"server cannot understand our query (Format Error)"); + case rcode_formaterror: + adns__warn(ads,serv,"server cannot understand our query (Format Error)"); adns__query_fail(ads,qu,adns_s_serverfaulty); return; - case 2: /* Server failure */ + case rcode_servfail; adns__query_fail(ads,qu,adns_s_serverfailure); return; + case rcode_notimp: + adns__warn(ads,serv,"server claims not to implement our query"); + adns__query_fail(ads,qu,adns_s_notimplemented); + return; + case rcode_refused: + adns__warn(ads,serv,"server refused our query"); + adns__query_fail(ads,qu,adns_s_refused); + return; + default: + adns__warn(ads,serv,"server gave unknown response code %d",rcode); + adns__query_fail(ads,qu,adns_s_reasonunknown); + return; + } + + /* Now, take a look at the answer section, and see if it is complete. + * If it has any CNAMEs we stuff them in the answer. + */ + wantedrrs= 0; + for (rri= 0; rricname ? qu->cname : qu->owner)) { + adns__debug(ads,serv,"ignoring answer RR with irrelevant owner \"%s\"",cowner); + 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)) { + wantedrrs++; + } else { + adns__debug(ads,serv,"ignoring answer RR with irrelevant type %d",rrtype); + } + } + + /* If we got here then the answer section is intact. */ + nsstart= cbyte; + + if (!wantedrrs) { + /* 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); + return; + } + + /* RFC2308: NODATA has _either_ a SOA _or_ _no_ NS records in authority section */ + for (rri= 0; rri dgend) { truncated(ads,qu,f1); return; } + } + for + + /* Look for CNAMEs in the answer section */ + + } + + + 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;