#include <stdio.h>
#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
#include "adns.h"
-int main(void) {
+static void failure(const char *what, adns_status st) {
+ fprintf(stderr,"adns failure: %s: %s\n",what,adns_strerror(st));
+ exit(2);
+}
+
+static const char *defaultargv[]= { "ns.chiark.greenend.org.uk", 0 };
+
+int main(int argc, const char *const *argv) {
adns_state ads;
- adns_query qu;
+ adns_query *qus, qu;
adns_answer *ans;
- int r;
+ const char *rrtn, *fmtn;
+ char *show;
+ int len, i, qc, qi;
+ adns_status r, ri;
+
+ if (argv[0] && argv[1]) argv++;
+ else argv= defaultargv;
+
+ for (qc=0; qc[argv]; qc++);
+ qus= malloc(sizeof(qus)*qc);
+ if (!qus) { perror("malloc qus"); exit(3); }
r= adns_init(&ads,adns_if_debug|adns_if_noautosys,0);
- if (r) { perror("init"); exit(2); }
+ if (r) failure("init",r);
+
+ for (qi=0; qi<qc; qi++) {
+ r= adns_submit(ads,argv[qi],adns_r_a,0,0,&qus[qi]);
+ if (r) failure("submit",r);
+ }
- r= adns_submit(ads,"anarres.relativity.greenend.org.uk",adns_r_a,0,0,&qu);
- if (r) { perror("submit"); exit(2); }
+ for (qi=0; qi<qc; qi++) {
+ qu= qus[qi];
+ r= adns_wait(ads,&qu,&ans,0);
+ if (r) failure("wait",r);
- r= adns_wait(ads,&qu,&ans,0);
- if (r) { perror("wait"); exit(2); }
+ ri= adns_rr_info(ans->type, &rrtn,&fmtn,&len, 0,0);
+ fprintf(stdout, "%s: %s; nrrs=%d; cname=%s; ",
+ argv[qi], adns_strerror(ans->status),
+ ans->nrrs, ans->cname ? ans->cname : "$");
+ fprintf(stdout, "type %s(%s) %s\n",
+ ri ? "?" : rrtn, ri ? "?" : fmtn ? fmtn : "-",
+ adns_strerror(ri));
+ if (ans->nrrs) {
+ assert(!ri);
+ for (i=0; i<ans->nrrs; i++) {
+ r= adns_rr_info(ans->type, 0,0,0, ans->rrs.bytes+i*len,&show);
+ if (r) failure("info",r);
+ printf(" %s\n",show);
+ free(show);
+ }
+ }
+ free(ans);
+ }
- fprintf(stderr,"answer status %d type %d rrs %d cname %s\n",
- ans->status,ans->type,ans->nrrs,
- ans->cname ? ans->cname : "-");
-
+ free(qus);
exit(0);
}
typedef struct {
adns_status status;
char *cname; /* always NULL if query was for CNAME records */
- adns_rrtype type;
+ adns_rrtype type; /* guaranteed to be same as in query */
int nrrs, rrsz;
union {
void *untyped;
* }
*/
+adns_status adns_rr_info(adns_rrtype type,
+ const char **rrtname_r, const char **fmtname_r,
+ int *len_r,
+ const void *datap, char **data_r);
+/* Gets information in human-readable (but non-i18n) form
+ * for eg debugging purposes. type must be specified,
+ * and the official name of the corresponding RR type will
+ * be returned in *rrtname_r, and information about the processing
+ * style in *fmtname_r. The length of the table entry in an answer
+ * for that type will be returned in in *len_r.
+ * Any or all of rrtname_r, fmtname_r and len_r may be 0.
+ * If fmtname_r is non-null then *fmtname_r may be
+ * null on return, indicating that no special processing is
+ * involved.
+ *
+ * data_r be must be non-null iff datap is. In this case
+ * *data_r will be set to point to a human-readable text
+ * string representing the RR data. The text will have
+ * been obtained from malloc() and must be freed by the caller.
+ *
+ * Usually this routine will succeed. Possible errors include:
+ * adns_s_nomemory
+ * adns_s_notimplemented (RR type not known)
+ * adns_s_invaliddata (*datap contained garbage)
+ * If an error occurs then no memory has been allocated,
+ * and *rrtname_r, *fmtname_r, *len_r and *data_r are undefined.
+ */
+
const char *adns_strerror(adns_status st);
#endif
bef,
adns__diag_domain(qu->ads,-1,0, &vb,qu->flags,
qu->query_dgram,qu->query_dglen,DNS_HDRSIZE),
- qu->typei ? qu->typei->name : "<unknown>");
+ qu->typei ? qu->typei->rrtname : "<unknown>");
+ if (qu->typei && qu->typei->fmtname)
+ fprintf(stderr,"(%s)",qu->typei->fmtname);
bef=", "; aft=")\n";
}
}
return vb->buf;
}
-
+
+adns_status adns_rr_info(adns_rrtype type,
+ const char **rrtname_r, const char **fmtname_r,
+ int *len_r,
+ const void *datap, char **data_r) {
+ const typeinfo *typei;
+ vbuf vb;
+ adns_status st;
+
+ typei= adns__findtype(type);
+ if (!typei) return adns_s_notimplemented;
+
+ if (rrtname_r) *rrtname_r= typei->rrtname;
+ if (fmtname_r) *fmtname_r= typei->fmtname;
+ if (len_r) *len_r= typei->rrsz;
+
+ if (!datap) return adns_s_ok;
+
+ adns__vbuf_init(&vb);
+ st= typei->convstring(&vb,datap);
+ if (st) goto x_freevb;
+ if (!adns__vbuf_append(&vb,"",1)) { st= adns_s_nolocalmem; goto x_freevb; }
+ assert(strlen(vb.buf) == vb.used-1);
+ *data_r= realloc(vb.buf,vb.used);
+ if (!*data_r) *data_r= vb.buf;
+ return adns_s_ok;
+
+ x_freevb:
+ adns__vbuf_free(&vb);
+ return st;
+}
+
+#define SINFO(n,s) { adns_s_##n, s }
+
+static const struct sinfo {
+ adns_status st;
+ const char *string;
+} sinfos[]= {
+ SINFO( ok, "OK" ),
+ SINFO( timeout, "Timed out" ),
+ SINFO( nolocalmem, "Out of memory" ),
+ SINFO( allservfail, "No working nameservers" ),
+ SINFO( servfail, "Nameserver failure" ),
+ SINFO( notimplemented, "Query not implemented" ),
+ SINFO( refused, "Refused by nameserver" ),
+ SINFO( reasonunknown, "Reason unknown" ),
+ SINFO( norecurse, "Recursion denied by nameserver" ),
+ SINFO( serverfaulty, "Nameserver sent bad data" ),
+ SINFO( unknownreply, "Reply from nameserver not understood" ),
+ SINFO( invaliddata, "Invalid data" ),
+ SINFO( inconsistent, "Inconsistent data" ),
+ SINFO( cname, "RR refers to an alias" ),
+ SINFO( nxdomain, "No such domain" ),
+ SINFO( nodata, "No such data" ),
+ SINFO( invaliddomain, "Domain syntactically invalid" ),
+ SINFO( domaintoolong, "Domain name too long" )
+};
+
+static int si_compar(const void *key, const void *elem) {
+ const adns_status *st= key;
+ const struct sinfo *si= elem;
+
+ return *st < si->st ? -1 : *st > si->st ? 1 : 0;
+}
+
const char *adns_strerror(adns_status st) {
static char buf[100];
+
+ const struct sinfo *si;
+
+ si= bsearch(&st,sinfos,sizeof(sinfos)/sizeof(*si),sizeof(*si),si_compar);
+ if (si) return si->string;
+
snprintf(buf,sizeof(buf),"code %d",st);
return buf;
}
typedef struct {
adns_rrtype type;
- const char *name;
+ const char *rrtname;
+ const char *fmtname;
int rrsz;
+ void (*makefinal)(adns_query qu, void *data);
+ /* Change memory management of *data.
+ * Previously, used alloc_interim, now use alloc_final.
+ */
+
+ adns_status (*convstring)(vbuf *vb, const void *data);
+ /* Converts the RR data to a string representation in vbuf.
+ * vbuf will be appended to (it must have been initialised),
+ * and will not be null-terminated by convstring.
+ */
+
adns_status (*parse)(adns_query qu, int serv,
const byte *dgram, int dglen, int cbyte, int max,
void *store_r);
* If there is an overrun which might indicate truncation, it should set
* *rdstart to -1; otherwise it may set it to anything else positive.
*/
-
- void (*makefinal)(adns_query qu, void *data);
- /* Change memory management of *data.
- * Previously, used alloc_interim, now use alloc_final.
- */
} typeinfo;
typedef struct allocnode {
#define LIST_LINK_TAIL_PART(list,node,part) \
do { \
- (node)->part back= 0; \
- (node)->part next= (list).tail; \
- if ((list).tail) (list).tail->part back= (node); else (list).part head= (node); \
+ (node)->part next= 0; \
+ (node)->part back= (list).tail; \
+ if ((list).tail) (list).tail->part next= (node); else (list).part head= (node); \
(list).tail= (node); \
} while(0)
#define LIST_UNLINK(list,node) LIST_UNLINK_PART(list,node,)
#define LIST_LINK_TAIL(list,node) LIST_LINK_TAIL_PART(list,node,)
-#define GETIL_B(cb) (dgram[(cb)++])
+#define GETIL_B(cb) (((dgram)[(cb)++]) & 0x0ff)
#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))
adns_status adns__findlabel_next(findlabel_state *fls,
int *lablen_r, int *labstart_r) {
- int lablen, jumped;
+ int lablen, jumped, jumpto;
const char *dgram;
jumped= 0;
dgram= fls->dgram;
for (;;) {
- if (fls->cbyte+2 > fls->dglen) goto x_truncated;
- if (fls->cbyte+2 > fls->max) goto x_serverfaulty;
- GET_W(fls->cbyte,lablen);
- if (!(lablen & 0x0c000)) break;
- if ((lablen & 0x0c000) != 0x0c000) return adns_s_unknownreply;
+ if (fls->cbyte >= fls->dglen) goto x_truncated;
+ if (fls->cbyte >= fls->max) goto x_serverfaulty;
+ GET_B(fls->cbyte,lablen);
+ if (!(lablen & 0x0c0)) break;
+ if ((lablen & 0x0c0) != 0x0c0) return adns_s_unknownreply;
if (jumped++) {
adns__diag(fls->ads,fls->serv,fls->qu,"compressed datagram contains loop");
return adns_s_serverfaulty;
}
+ if (fls->cbyte >= fls->dglen) goto x_truncated;
+ if (fls->cbyte >= fls->max) goto x_serverfaulty;
+ GET_B(fls->cbyte,jumpto);
+ jumpto |= (lablen&0x3f)<<8;
if (fls->dmend_r) *(fls->dmend_r)= fls->cbyte;
- fls->cbyte= DNS_HDRSIZE+(lablen&0x3fff);
+ fls->cbyte= jumpto;
fls->dmend_r= 0; fls->max= fls->dglen+1;
}
+ if (labstart_r) *labstart_r= fls->cbyte;
if (lablen) {
if (fls->namelen) fls->namelen++;
fls->namelen+= lablen;
} else {
if (fls->dmend_r) *(fls->dmend_r)= fls->cbyte;
}
- if (labstart_r) *labstart_r= fls->cbyte;
*lablen_r= lablen;
+/*if (labstart_r) fprintf(stderr,"label %d >%.*s<\n",lablen,lablen,fls->dgram+*labstart_r);*/
return adns_s_ok;
x_truncated:
st= adns__findlabel_next(&eo_fls,&eo_lablen,&eo_labstart);
assert(!st); assert(eo_lablen>=0);
if (lablen != eo_lablen) mismatch= 1;
- while (!mismatch && lablen-- > 0) {
+ while (!mismatch && eo_lablen-- > 0) {
ch= dgram[labstart++]; if (ctype_alpha(ch)) ch &= ~32;
eo_ch= eo_dgram[eo_labstart++]; if (ctype_alpha(eo_ch)) eo_ch &= ~32;
if (ch != eo_ch) mismatch= 1;
}
}
+ if (!lablen) break;
}
if (eo_matched_r) *eo_matched_r= !mismatch;
GET_W(cbyte,tmp); *type_r= tmp;
GET_W(cbyte,tmp); *class_r= tmp;
cbyte+= 4; /* we skip the TTL */
- GET_W(cbyte,rdlen); if (rdlen_r) *rdlen_r= tmp;
+ GET_W(cbyte,rdlen); if (rdlen_r) *rdlen_r= rdlen;
if (rdstart_r) *rdstart_r= cbyte;
cbyte+= rdlen;
if (cbyte>dglen) goto x_truncated;
x_truncated:
*type_r= -1;
- return 0;;
+ return 0;
}
adns_status adns__findrr(adns_query qu, int serv,
#include "internal.h"
static void cname_recurse(adns_query qu, adns_queryflags xflags) {
- abort(); /* FIXME */
+ assert(!"cname not implemented"); /* FIXME */
}
void adns__procdgram(adns_state ads, const byte *dgram, int dglen,
int serv, struct timeval now) {
- int cbyte, rrstart, wantedrrs, rri, foundsoa, foundns;
+ int cbyte, rrstart, wantedrrs, rri, foundsoa, foundns, cname_here;
int id, f1, f2, qdcount, ancount, nscount, arcount;
int flg_ra, flg_rd, flg_tc, flg_qr, opcode;
int rrtype, rrclass, rdlength, rdstart, ownermatched, l;
adns_query qu, nqu;
dns_rcode rcode;
adns_status st;
+ vbuf tempvb;
if (dglen<DNS_HDRSIZE) {
adns__diag(ads,serv,0,"received datagram too short for message header (%d)",dglen);
flg_tc= f1&0x20;
flg_rd= f1&0x01;
flg_ra= f2&0x80;
- rcode= (f1&0x0f);
- /* fixme: change this to f2 (which is where rcode really is), BUT
- * not until we've figured out why the error code is wrong (bad format code 203)
- */
+ rcode= (f2&0x0f);
+ cname_here= 0;
+
if (!flg_qr) {
adns__diag(ads,serv,0,"server sent us a query, not a response");
return;
continue;
break;
}
- anstart= qu->query_dglen;
if (!qu) {
- adns__debug(ads,serv,0,"reply not found (id=%02x)",id);
+ if (ads->iflags & adns_if_debug) {
+ adns__vbuf_init(&tempvb);
+ adns__debug(ads,serv,0,"reply not found, id %02x, query owner %s",
+ id, adns__diag_domain(ads,serv,0,&tempvb,adns_qf_anyquote,
+ dgram,dglen,DNS_HDRSIZE));
+ adns__vbuf_free(&tempvb);
+ }
return;
}
+ anstart= qu->query_dglen;
LIST_UNLINK(ads->timew,qu);
/* We're definitely going to do something with this query now */
* If it has any CNAMEs we stuff them in the answer.
*/
wantedrrs= 0;
+ cbyte= anstart;
for (rri= 0; rri<ancount; rri++) {
rrstart= cbyte;
st= adns__findrr(qu,serv, dgram,dglen,&cbyte,
&rrtype,&rrclass,&rdlength,&rdstart,
&ownermatched);
- if (st) adns__query_fail(qu,st);
+ if (st) { adns__query_fail(qu,st); return; }
if (rrtype == -1) goto x_truncated;
if (rrclass != DNS_CLASS_IN) {
qu->answer->cname= adns__alloc_interim(qu,l);
if (!qu->answer->cname) return;
memcpy(qu->answer->cname,qu->vb.buf,l);
+ cname_here= 1;
/* 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
* a CNAME in this datagram then we should probably do our own CNAME
* lookup now in the hope that we won't get a referral again.
*/
- if (qu->cname_dgram == dgram) { cname_recurse(qu,0); return; }
+ if (cname_here) { cname_recurse(qu,0); return; }
/* Bloody hell, I thought we asked for recursion ? */
if (flg_rd) {
qu->answer->rrs.bytes+qu->answer->nrrs*qu->typei->rrsz);
if (st) { adns__query_fail(qu,st); return; }
if (rdstart==-1) goto x_truncated;
+ qu->answer->nrrs++;
}
/* This may have generated some child queries ... */
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <arpa/inet.h>
+
#include "internal.h"
-static adns_status rp_inaddr(adns_query qu, int serv,
+static adns_status pa_inaddr(adns_query qu, int serv,
const byte *dgram, int dglen, int cbyte, int max,
void *store_r) {
struct in_addr *dr= store_r;
return adns_s_ok;
}
-static void rmf_null(adns_query qu, void *data) { }
+static adns_status cs_inaddr(vbuf *vb, const void *data) {
+ const struct in_addr *dp= data;
+ const char *ia;
+
+ ia= inet_ntoa(*dp); assert(ia);
+ return adns__vbuf_appendstr(vb,ia) ? adns_s_ok : adns_s_nolocalmem;
+}
-#define TYPE_SF(size,func,free) size, rp_##func, rmf_##free
-#define TYPE_SN(size,func) size, rp_##func, rmf_null
+static void fr_null(adns_query qu, void *data) { }
+
+#define TYPE_SF(size,func,cp,free) size, pa_##func, fr_##free, cs_##cp
+#define TYPE_SN(size,func,cp) size, pa_##func, fr_null, cs_##cp
#define TYPESZ_M(member) (sizeof(((adns_answer*)0)->rrs.member))
-#define TYPE_MF(member,parse) TYPE_SF(TYPESZ_M(member),parse,member)
-#define TYPE_MN(member,parse) TYPE_SN(TYPESZ_M(member),parse)
+#define TYPE_MF(memb,parse) TYPE_SF(TYPESZ_M(memb),parse,memb,memb)
+#define TYPE_MN(memb,parse) TYPE_SN(TYPESZ_M(memb),parse,memb)
+
+#define DEEP_MEMB(memb) TYPESZ_M(memb), fr_##memb, cs_##memb
+#define FLAT_MEMB(memb) TYPESZ_M(memb), fr_null, cs_##memb
+#define NULL_MEMB 0, fr_null, cs_null
/* TYPE_<ms><nf>
* ms is M specify member name
static const typeinfo typeinfos[] = {
/* Must be in ascending order of rrtype ! */
- /* rr type code name style member size parser */
+ /* rr type code rrt fmt mem.mgmt member parser */
- { adns_r_a, "A", TYPE_MN( inaddr, inaddr ) },
-#if 0 /*fixme*/
- { adns_r_ns_raw, "NS(raw)", TYPE_MF( str, domain_raw ) },
- { adns_r_cname, "CNAME", TYPE_MF( str, domain_raw ) },
- { adns_r_soa_raw, "SOA(raw)", TYPE_MF( soa, soa ) },
- { adns_r_null, "NULL", TYPE_SN( 0, null ) },
- { adns_r_ptr_raw, "PTR(raw)", TYPE_MF( str, domain_raw ) },
- { adns_r_hinfo, "HINFO", TYPE_MF( strpair, hinfo ) },
- { adns_r_mx_raw, "MX(raw)", TYPE_MF( intstr, mx_raw ) },
- { adns_r_txt, "TXT", TYPE_MF( str, txt ) },
- { adns_r_rp_raw, "RP(raw)", TYPE_MF( strpair, rp ) },
-
- { adns_r_ns, "NS(+addr)", TYPE_MF( dmaddr, dmaddr ) },
- { adns_r_ptr, "PTR(checked)", TYPE_MF( str, ptr ) },
- { adns_r_mx, "MX(+addr)", TYPE_MF( intdmaddr, mx ) },
-
- { adns_r_soa, "SOA(822)", TYPE_MF( soa, soa ) },
- { adns_r_rp, "RP(822)", TYPE_MF( strpair, rp ) },
+ { adns_r_a, "A", 0, FLAT_MEMB(inaddr), pa_inaddr },
+#if 0 /*fixme*/
+ { adns_r_ns_raw, "NS", "raw", DEEP_MEMB(str), pa_domain_raw },
+ { adns_r_cname, "CNAME", 0, DEEP_MEMB(str), pa_domain_raw },
+ { adns_r_soa_raw, "SOA", "raw", DEEP_MEMB(soa), pa_soa },
+ { adns_r_null, "NULL", 0, NULL_MEMB, pa_null },
+ { adns_r_ptr_raw, "PTR", "raw", DEEP_MEMB(str), pa_domain_raw },
+ { adns_r_hinfo, "HINFO", 0, DEEP_MEMB(strpair), pa_hinfo },
+ { adns_r_mx_raw, "MX", "raw", DEEP_MEMB(intstr), pa_mx_raw },
+ { adns_r_txt, "TXT", 0, DEEP_MEMB(str), pa_txt },
+ { adns_r_rp_raw, "RP", "raw", DEEP_MEMB(strpair), pa_rp },
+
+ { adns_r_ns, "NS", "+addr", DEEP_MEMB(dmaddr), pa_dmaddr },
+ { adns_r_ptr, "PTR", "checked", DEEP_MEMB(str), pa_ptr },
+ { adns_r_mx, "MX", "+addr", DEEP_MEMB(intdmaddr), pa_mx },
+
+ { adns_r_soa, "SOA", "822", DEEP_MEMB(soa), pa_soa },
+ { adns_r_rp, "RP", "822", DEEP_MEMB(strpair), pa_rp },
#endif
};