From: ian Date: Tue, 30 Nov 2004 23:10:04 +0000 (+0000) Subject: adns wip X-Git-Tag: debian/1.1.1~118 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=chiark-tcl.git;a=commitdiff_plain;h=c7fc51a4ad6bb6f01266ed5912b062ae98a653b8 adns wip --- diff --git a/adns/adns.c b/adns/adns.c new file mode 100644 index 0000000..c18ccad --- /dev/null +++ b/adns/adns.c @@ -0,0 +1,505 @@ +/* + */ +/* + * adns lookup TYPE DOMAIN [QUERY-OPTIONS] => [list RDATA] + * if no or dontknow, throws an exception, with errorCode one of + * ADNS NO 300 nxdomain {No such domain} + * ADNS NO 301 nodata {No such data} + * ADNS FAILED ERROR-CODE ERROR-NAME ERROR-STRING + * where + * ERROR-CODE is the numerical adns status value + * ERROR-NAME is the symbolic adns status value (in lowercase) + * ERROR-STRING is the result of adns_strstatus + * + * adns synch TYPE DOMAIN [QUERY-OPTIONS] => RESULTS + * RESULTS is [list ok|permfail|tempfail + * ERROR-CODE ERROR-NAME ERROR-STRING \ + * OWNER CNAME \ + * [list RDATA ...]] + * OWNER is the RR owner + * CNAME is the empty string or the canonical name if we went + * via a CNAME + * + * adns asynch ON-YES ON-NO ON-DONTKNOW XARGS \ + * TYPE DOMAIN \ + * [QUERY-OPTIONS] => QUERY-ID + * calls, later, + * [concat ON-YES|ON-NO|ON-DONTKNOW XARGS RESULTS] + * adns asynch-cancel QUERY-ID + * + * QUERY-OPTIONS are zero or more of + * -resolver RESOLVER (see adns new-resolver) + * default is to use a default resolver + * -search + * -usevc + * -quoteok-query + * -quoteok-anshost + * -quotefail-cname + * -cname-loose + * -cname-forbid + * + * adns new-resolver [RES-OPTIONS...] => RESOLVER + * options: + * -errfile stdout|stderr (stderr is the default) + * -noerrprint + * -errcallback CALLBACK results in eval CALLBACK [list MESSAGE] + * -noenv|-debug|-logpid + * -checkc-entex + * -checkc-freq + * -reverse + * -reverse-any ZONE-A-LIKE + * -config CONFIG-STRING + * + * adns destroy-resolver RESOLVER + */ + +#include "tables.h" +#include "hbytes.h" + +IdDataTable adnstcl_resolvers= { "adns-res" }; +IdDataTable adnstcl_queries= { "adns-q" }; + +typedef struct { + int ix; /* first! */ + Tcl_Interp *interp; fixme what about interpreter deletion + adns_state *ads; + int maxfd; + fd_set handling[3]; +} Resolver; + +typedef struct { + int ix; /* first! */ + adns_query *aqu; + Tcl_Obj *on_yes, *on_no, *on_fail, *xargs; +} Query; + +static adns_state *adnstcl_default; +static int default_auto; + +#define RRTYPE_EXACTLY(t) { #t, adns_r_##t } +#define RRTYPE_RAW(t) { #t, adns_r_##t##_raw } +#define RRTYPE_PLUS(t) { #t "+", adns_r_##t } +#define RRTYPE_MINUS(t) { #t "-", adns_r_##t##_raw } + +const AdnsTclRRTypeInfo adnstclrrtypeinfos[]= { + RRTYPE_EXACTLY(a), + RRTYPE_EXACTLY(cname), + RRTYPE_EXACTLY(hinfo), + RRTYPE_EXACTLY(addr), + + RRTYPE_RAW(ns), + RRTYPE_RAW(mx), + + RRTYPE_EXACTLY(soa), + RRTYPE_EXACTLY(ptr), + RRTYPE_EXACTLY(rp), + + RRTYPE_MINUS(soa), + RRTYPE_MINUS(ptr), + RRTYPE_MINUS(rp), + { 0 } +}; + +typedef struct { + /* this struct type is used to hold both resolver and query options */ + /* common to resolver and query: */ + unsigned long *aflags; + unsigned long *sflags; + /* resolver: */ + FILE *errfile; + Tcl_Obj *errcallback; + const char *config_string; + /* query: */ + adns_state *resolver; + const char *reverseany; +} OptionParse; + +typedef struct OptionInfo OptionInfo; +struct OptionInfo { + const char *name; + int (*fn)(Tcl_Interp *ip, const OptionInfo *oi, Tcl_Obj *arg, + OptionParse *op); + int takesarg; + unsigned long flags_add, flags_remove; +}; + +enum { + oisf_makedefault= 0x0001, + oisf_reverse= 0x0002 +}; + +static int oiufn_f(const OptionInfo *oi, unsigned long *flags) { + flags &= ~oi->flags_remove; + flags |= oi->flsgs_add; +} +static int oifn_fa(Tcl_Interp *ip, const OptionInfo *oi, Tcl_Obj *arg, + OptionParse *op) { oiufn_f(oi,op->aflags); } +static int oifn_fs(Tcl_Interp *ip, const OptionInfo *oi, Tcl_Obj *arg, + OptionParse *op) { oiufn_f(oi,op->sflags); } + +static int oifn_errfile(Tcl_Interp *ip, const OptionInfo *oi, + Tcl_Obj *arg, OptionParse *op) { + int rc; + const char *str; + + rc= pat_string(ip,arg,&str); if (rc) return rc; + if (!strcmp(str,"stderr")) op.errfile= stderr; + else if (!strcmp(str,"stdout")) op.errfile= stdout; + else return staticerr(ip,"-errfile argument must be stderr or stdout",0); + + op.aflags &= ~adns_if_noerrprint; + op.errcallback= 0; + return TCL_OK; +} + +static int oifn_errcallback(Tcl_Interp *ip, const OptionInfo *oi, + Tcl_Obj *arg, OptionParse *op) { + op.errcallback= arg; + op.aflags &= ~adns_if_noeerpring; + op.errfile= 0; + return TCL_OK; +} + +static int oifn_config(Tcl_Interp *ip, const OptionInfo *oi, + Tcl_Obj *arg, OptionParse *op) { + return pat_string(ip,arg,&op.config_string); +} + +static int oifn_resolver(Tcl_Interp *ip, const OptionInfo *oi, + Tcl_Obj *arg, OptionParse *op) { + void *val_v; + rc= pat_iddata(ip,arg,&val_v,adnstcl_resolvers); + if (rc) return rc; + op.resolver= val_v; +} + +static int oifn_reverse_any(Tcl_Interp *ip, const OptionInfo *oi, + Tcl_Obj *arg, OptionParse *op) { + return pat_string(ip,arg,&op.reverseany); +} + +#define OIFA1(t,f,r) { "-" #f, oifn_fa, 0, adns_##t##_##f, r } +#define OIFA2(t,f,g) { "-" #f "-" #g, oifn_fa, 0, adns_##t##_##f##_##g, 0 } +#define OIFS(f) { "-" #f, oifn_fs, 0, oisf_##f, 0 } +#define OICA(o) { "-" #o, oifn_##o, 1 } + +static const OptionInfo resolver_optioninfos[]= { + OIFA1(if,noenv, 0), + OIFA1(if,debug, adns_if_noerrprint), + OIFA1(if,logpid, adns_if_noerrprint), + OIFA1(if,noerrprint, adns_if_debug), + OIFA2(if,checkc,entex), + OIFA2(if,checkc,freq), + OIFS(makedefault), + OICA(errprint), + OICA(errcallback), + OICA(config), + { 0 } +}; + +static const OptionInfo query_optioninfos[]= { + OIFA1(if,search,0), + OIFA1(if,usevc,0), + OIFA2(if,quoteok,query), + OIFA2(if,quoteok,anshost), + OIFA2(if,quotefail,cname), + OIFA2(if,cname,loose), + OIFA2(if,cname,forbid), + OICA(resolver), + OIFS(reverse), + { "-reverse-any", oifn_reverse_any, 1 }, + { 0 } +}; + +static int parse_options(Tcl_Interp *ip, int objc, Tcl_Obj *const *objv, + const OptionInfo opttable[], OptionParse *op) { + const OptionInfo *oi; + Tcl_Obj *arg; + + for (;;) { + if (!objc--) break; + + rc= pat_enum(ip,*objv++, &oi,opttable, sizeof(OptionInfo)); + if (rc) return rc; + + if (oi->takesarg) { + if (!objc--) { + setstringresult(ip,"missing value for option"); + return TCL_ERROR; + } + arg= *objv++; + } else { + arg= 0; + } + rc= oi->fn(ip,oi,arg,op); + if (rc) return rc; + } + return TCL_OK; +} + +int do_adns_destroy_resolver(ClientData cd, Tcl_Interp *ip, void *res_v) { + adns_state *res= res_v; + + if (res == adnstcl_default) { + adnstcl_default= 0; + default_auto= 0; + } + adns_finish(res); fixme what about outstanding qs + return TCL_OK; +} + +static void asynch_check(Resolver *res); + +static void asynch_timerhandler(void *res_v) { + Resolver *res= res_v; + res->timertoken= 0; + adns_processtimeouts(res->ads,0); + checkqueries_updatehandlers(res); +} + +static void asynch_filehandler(void *res_v, int mask) { + Resolver *res= res_v; + ec= adns_processany(res->ads); + if (ec) adns_globalsystemfailure(res->ads); + checkqueries_updatehandlers(res); +} + +static void asynch_sethandlers(Resolver *res, int shutdown) { + fd_set want[3]; + int maxfd; + struct timeval tvbuf, *timeout; + + for (i=0; i<3; i++) FD_ZERO(&want[i]); + maxfd= 0; + timeout= 0; + + if (!shutdown) + adns_beforeselect(res->ads,&maxfd,&want[0],&want[1],&want[2], + &timeout,&tv_buf,0); + + for (fd= 0; fd < maxfd || fd < res->maxfd; fd++) + for (i=0; i<3; i++) + if (!!FD_ISSET(fd, &res->handling[i]) + != !!FD_ISSET(fd, &want[i])) { + int mask=0; + if (FD->ISSET(fd, &want[0])) mask |= TCL_READABLE; + if (FD->ISSET(fd, &want[1])) mask |= TCL_WRITABLE; + if (FD->ISSET(fd, &want[2])) mask |= TCL_EXCEPTION; + if (mask) Tcl_CreateFileHandler(fd,mask,filehandler,res); + else Tcl_DeleteFileHandler(fd); + } + + Tcl_DeleteTimerHandler(res->timertoken); + + if (timeout) { + int milliseconds; + + if (timeout->tv_sec >= INT_MAX/1000 - 1) + milliseconds= INT_MAX; + else + milliseconds= timeout->tv_sec * 1000 + + (timeout->tv_usec + 999) / 1000; + + res->timertoken= Tcl_CreateTimerHandler(milliseconds,timerhandler,res); + } +} + +int do_adns_new_resolver(ClientData cd, Tcl_Interp *ip, + int objc, Tcl_Obj *const *objv, + void **result) { + OptionParse op; + adns_state *ads=0; + Resolver *res=0; + + op.aflags= adns_if_noautosys; + op.sflags= 0; + op.errfile= 0; + op.errcallback= 0; + op.config_string= 0; + rc= parse_options(ip,objc,objv,resolver_optioninfos,&op); + if (rc) goto x_rc; + + res= TALLOC(sizeof(*res)); assert(res); + res->maxfd= 0; + for (i=0; i<3; i++) FD_ZERO(&res->handling); + res->interp= ip; + + if (op.aflags & adns_if_noerrprint) { + op.errfile= 0; + op.errcallback= 0; + } + + ec= adns_init_logfn(&ads, op.aflags, op.configstring, + op.errcallback ? adnslogfn_callback : 0, + op.errcallback ? op.errcallback : op.errfile); + if (ec) { rc= posixerr(ip,ec,"create adns resolver"); goto x_rc; } + res->ads= ads; + + if (op.errcallback) + Tcl_IncrRefCount(op.errcallback); + + rc= update_handlers(&res); if (rc) goto x_rc; + + *result= res; + return TCL_OK; +} + +static int query_submit(Tcl_Interp *ip, int objc, Tcl_Obj *const *objv, + const char *domain) { + struct sockaddr sa; + static const int aftry[]= { AF_INET, AF_INET6 }; + + op.aflags= adns_qf_owner; + op.sflags= 0; + op.resolver= 0; + rc= parse_options(ip,objc,objv,query_optioninfos,&op); + if (rc) return rc; + + if (op.reverseany || (op.sflags & oisf_reverse)) { + const int *af; + for (af=aftry; af < af + sizeof(af)/sizeof(*af); af++) { + memset(&sa,0,sizeof(sa)); + sa.sa_family= *af; + r= inet_pton(*af,domain,&sa); + if (!r) goto af_found; + } + return staticerr(ip,ec,"invalid address for adns reverse submit",""); + } + + if (!op.resolver) { + if (!adnstcl_default) { + ec= adns_init(&adnstcl_default,adns_if_noautosys,0); + if (ec) return posixerr(ip,ec,"create default adns resolver"); + } + default_auto= 1; + op.resolver= adnstcl_default; + } + + if (op.reverseany) { + ec= adns_submit_reverse_any(op.resolver, &sa, op.reverseany, rrtype, + op.aflags, 0, &query); + } else if (op.sflags & oisf_reverse) { + ec= adns_submit_reverse(op.resolver, &sa, rrtype, op.aflags, 0, &query); + } else { + ec= adns_submit(op.resolver, domain, rrtype, op.aflags, 0, &query); + } + if (ec) + return posixerr(ip,ec,"submit adns query"); + + return TCL_OK; +} + +int do_adns_lookup(ClientData cd, Tcl_Interp *ip, + const AdnsTclRRTypeInfo *rrtype, + const char *domain, + int objc, Tcl_Obj *const *objv, + Tcl_Obj **result) { + OptionParse op; + + rc= query_submit(ip,domain,objc,objv,&op, ); if (rc) return rc; + + ec= adns_wait(op.resolver,&query,&answer,0); + if (ec) return posixerr(ip,ec,"wait for adns lookup"); + + ...; +} + +#define RESULTS_LLEN 7 +static void make_results(Tcl_Interp *ip, adns_answer *answer, + Tcl_Obj results[RESULTS_LLEN]) { + Tcl_Obj *rdata; + + results[0]= ret_string(ip, adns_errtypeabbrev(answer->status)); + results[1]= ret_int(ip, answer->status); + results[2]= ret_string(ip, adns_errabbrev(answer->status)); + results[3]= ret_string(ip, adns_strstatus(answer->status)); + results[4]= ret_string(ip, answer->owner); + results[5]= ret_string(ip, answer->cname ? answer->cname : ""); + + rdata= TALLOC(sizeof(*rdata) * answer->nrrs); + for (i=0, datap=answer->rrs.untyped; + inrrs; i++, datap += rrsz) { + st= adns_rr_info(answer->type, 0,0, &rrsz, datap, &rdatastring); + assert(!st); + rdata[i]= ret_string(ip, rdatastring); + free(rdatastring); + } + results[6]= Tcl_NewListObj(RESULTS_LLEN,rdata); + TFREE(rdata); +} + +static void asynch_query_dispose(Query *query) { + scriptinv_cancel(&query->on_yes); + scriptinv_cancel(&query->on_no); + scriptinv_cancel(&query->on_fail); + if (query->xargs) Tcl_DecrRefCount(query->xargs); + if (query->aqu) adns_cancel(query->aqu); + TFREE(query); +} + +static void asynch_check(Resolver *res) { + Tcl_Interp *interp= res->interp; + adns_query *aqu; + adns_answer *answer; + Query *qu; + ScriptToInvoke *si; + Tcl_Obj results[RESULTS_LLEN]; + + for (;;) { + ec= adns_check(res->ads, &aqu, &answer, &qu); + if (ec==ESRCH || ec==EAGAIN) break; + assert(!ec); + + qu->aqu= 0; + + si= (!answer->status ? si= &query->on_yes + : answer->status > adns_s_max_tempfail ? &query->on_no + : &query->on_fail); + + adnstcl_queries[qu->ix].a= 0; + make_results(ip, answer, results); + scriptinv_invoke(si, RESULTS_LLEN, results); + asynch_query_dispose(query); + } + + asynch_sethandlers(res,0); +} + +int do_adns_asynch(ClientData cd, Tcl_Interp *ip, + const AdnsTclRRTypeInfo *rrtype, const char *domain, + Tcl_Obj *on_yes, Tcl_Obj *on_no, + Tcl_Obj *on_fail, Tcl_Obj *xargs, + int objc, Tcl_Obj *const *objv, void **result) { + Query *query; + adns_query *aqu; + + query= TALLOC(sizeof(*query)); + query->aqu= 0; + scriptinv_init(&query->on_yes); + scriptinv_init(&query->on_no); + scriptinv_init(&query->on_fail); + query->xargs= 0; + + rc= query_submit(ip,rrtype,domain, ...); if (rc) goto x_rc; + query->aqu= aqu; + + rc= scriptinv_set(&query->on_yes, ip,on_yes); if (rc) goto x_rc; + rc= scriptinv_set(&query->on_no, ip,on_no); if (rc) goto x_rc; + rc= scriptinv_set(&query->on_fail,ip,on_fail); if (rc) goto x_rc; + query->xargs= xargs; + Tcl_IncrRefCount(xargs); + *result= query; + + return TCL_OK; + + x_rc: + asynch_cancel(query); + return rc; +} + +int do_adns_asynch_cancel(ClientData cd, Tcl_Interp *ip, void *query_v) { + adns_query *qu; + +} + +int do_adns_synch(ClientData cd, Tcl_Interp *ip, const AdnsTclRRTypeInfo *rrtype, const char *domain, int objc, Tcl_Obj *const *objv, adns_answer **result); diff --git a/base/troglodyte-Makefile b/base/troglodyte-Makefile index b6009f8..9eff482 100644 --- a/base/troglodyte-Makefile +++ b/base/troglodyte-Makefile @@ -1,6 +1,7 @@ OBJS= tables.o \ tcmdiflib.o \ hbytes.o \ + adns.o \ enum.o \ idtable.o \ scriptinv.o \