From b9de380c1e587b6c5828cb9de796746024946880 Mon Sep 17 00:00:00 2001 From: ian Date: Sun, 4 Oct 1998 21:12:35 +0000 Subject: [PATCH] Much decoding of incoming packets. --- src/adns.h | 10 +- src/internal.h | 13 ++- src/query.c | 5 +- src/reply.c | 258 ++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 254 insertions(+), 32 deletions(-) diff --git a/src/adns.h b/src/adns.h index 901ea03..d362a31 100644 --- a/src/adns.h +++ b/src/adns.h @@ -94,13 +94,17 @@ typedef enum { adns_s_nolocalmem, adns_s_allservfail, adns_s_serverfailure, - adns_s_max_tempfail= 99, + adns_s_notimplemented, + adns_s_refused, + adns_s_reasonunknown, adns_s_norecurse, adns_s_serverfaulty, - adns_s_max_localmisconfig= 199, + adns_s_unknownreply, + adns_s_max_tempfail= 99, adns_s_inconsistent, /* PTR gives domain whose A does not match */ adns_s_cname, /* CNAME found where data eg A expected (not if _qf_loosecname) */ - adns_s_max_remotemisconfig= 299, + /* fixme: implement _s_cname */ + adns_s_max_remotemisconfig= 199, adns_s_nxdomain, adns_s_norecord, adns_s_invaliddomain diff --git a/src/internal.h b/src/internal.h index 5b5097b..bf6d3ac 100644 --- a/src/internal.h +++ b/src/internal.h @@ -17,13 +17,16 @@ typedef unsigned char byte; /* Configuration and constants */ #define MAXSERVERS 5 -#define MAXUDPRETRIES /*15*/5 +#define UDPMAXRETRIES /*15*/5 #define UDPRETRYMS 2000 #define TCPMS 30000 #define LOCALRESOURCEMS 20 -#define MAXUDPDGRAM 512 -#define NSPORT 53 -#define MAXDNAME 255 + +#define DNS_UDPPORT 53 +#define DNS_MAXUDP 512 +#define DNS_MAXDOMAIN 255 +#define DNS_HDRSIZE 12 +#define DNS_CLASS_IN 1 /* Shared data structures */ @@ -53,7 +56,7 @@ struct adns__query { struct { adns_query head, tail; } children; struct { adns_query back, next; } siblings; adns_rrtype type; - vbuf answer; + vbuf ans; int id, flags, udpretries; int udpnextserver; unsigned long udpsent, tcpfailed; /* bitmap indexed by server */ diff --git a/src/query.c b/src/query.c index 35269e9..2b21da4 100644 --- a/src/query.c +++ b/src/query.c @@ -17,7 +17,8 @@ adns_status adns__mkquery(adns_state ads, const char *owner, int ol, int id, #define MKQUERY_ADDB(b) *rqp++= (b) #define MKQUERY_ADDW(w) (MKQUERY_ADDB(((w)>>8)&0x0ff), MKQUERY_ADDB((w)&0x0ff)) - if (!adns__vbuf_ensure(&ads->rqbuf,12+strlen(owner)+1+5)) return adns_s_nolocalmem; + if (!adns__vbuf_ensure(&ads->rqbuf,DNSHDRSIZE+strlen(owner)+1+5)) + return adns_s_nolocalmem; rqp= ads->rqbuf.buf; MKQUERY_ADDW(id); @@ -64,7 +65,7 @@ adns_status adns__mkquery(adns_state ads, const char *owner, int ol, int id, MKQUERY_ADDB(0); MKQUERY_ADDW(type & adns__rrt_typemask); /* QTYPE */ - MKQUERY_ADDW(1); /* QCLASS=IN */ + MKQUERY_ADDW(DNS_CLASS_IN); /* QCLASS=IN */ ads->rqbuf.used= rqp - ads->rqbuf.buf; assert(ads->rqbuf.used <= ads->rqbuf.avail); 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; -- 2.30.2