/*---------- important types and forward declarations ----------*/
+typedef struct Query Query;
typedef struct Resolver Resolver;
typedef struct OptionInfo OptionInfo;
static void asynch_check(Resolver *res);
static void asynch_sethandlers(Resolver *res, int shutdown);
+static void asynch_query_dispose(Tcl_Interp *interp, Query *query);
/*---------- common resolver/query option processing ----------*/
/*---------- resolver management ----------*/
-IdDataTable adnstcl_resolvers= { "adns-res" };
-
struct Resolver {
int ix; /* first! */
Tcl_Interp *interp;
abort(); /* fixme implement adns_logcallbackfn */
}
-int do_adns_destroy_resolver(ClientData cd, Tcl_Interp *ip, void *res_v) {
- Resolver *res= res_v;
- tabledataid_disposing(res,&adnstcl_resolvers);
- if (res->ads) adns_finish(res->ads);
+static void destroy_resolver(Tcl_Interp *ip, Resolver *res) {
+ void *query_v;
+ adns_query aqu;
+
+ if (res->ads) {
+ /* although adns would throw these away for us, we need to
+ * destroy our own data too and only adns has a list of them */
+ for (;;) {
+ adns_forallqueries_begin(res->ads);
+ aqu= adns_forallqueries_next(res->ads, &query_v);
+ if (!aqu) break;
+ asynch_query_dispose(ip, query_v);
+ }
+ adns_finish(res->ads);
+ }
asynch_sethandlers(res,1);
TFREE(res);
- /* fixme what about outstanding queries */
/* fixme what about the default resolver */
- /* fixme what if the tcl interpreter gets deleted */
+}
+
+static void destroy_resolver_idtabcb(Tcl_Interp *ip, void *resolver_v) {
+ destroy_resolver(ip,resolver_v);
+}
+
+int do_adns_destroy_resolver(ClientData cd, Tcl_Interp *ip, void *res_v) {
+ destroy_resolver(ip,res_v);
+ tabledataid_disposing(ip,res_v,&adnstcl_resolvers);
return TCL_OK;
}
return rc;
}
+const IdDataSpec adnstcl_resolvers= {
+ "adns-res", "adns-resolvers-table", destroy_resolver_idtabcb
+};
+
/*---------- query, query option and answers - common stuff ----------*/
#define RRTYPE_EXACTLY(t) { #t, adns_r_##t }
static int query_submit(Tcl_Interp *ip,
const AdnsTclRRTypeInfo *type, const char *domain,
int queryopts_objc, Tcl_Obj *const *queryopts_objv,
- adns_query *aqu_r, OptionParse *op) {
+ adns_query *aqu_r, void *context, OptionParse *op) {
struct sockaddr sa;
static const int aftry[]= { AF_INET, AF_INET6 };
int rc, r, ec;
op->aflags= adns_qf_owner;
op->sflags= 0;
- op->resolver= 0;
+ op->resolver= 0; /* fixme default */
+ op->reverseany= 0;
rc= parse_options(ip, queryopts_objc,queryopts_objv, query_optioninfos,op);
if (rc) return rc;
if (op->reverseany) {
ec= adns_submit_reverse_any(ads, &sa, op->reverseany,
- type->number, op->aflags, 0, aqu_r);
+ type->number, op->aflags, context, aqu_r);
} else if (op->sflags & oisf_reverse) {
ec= adns_submit_reverse(ads, &sa,
- type->number, op->aflags, 0, aqu_r);
+ type->number, op->aflags, context, aqu_r);
} else {
ec= adns_submit(ads, domain,
- type->number, op->aflags, 0, aqu_r);
+ type->number, op->aflags, context, aqu_r);
}
if (ec)
return posixerr(ip,ec,"submit adns query");
}
static Tcl_Obj *make_resultrdata(Tcl_Interp *ip, adns_answer *answer) {
- Tcl_Obj **rdata;
+ Tcl_Obj **rdata, *rl;
int i, rrsz;
adns_status st;
char *datap, *rdatastring;
rdata[i]= ret_string(ip, rdatastring);
free(rdatastring);
}
+ rl= Tcl_NewListObj(answer->nrrs, rdata);
TFREE(rdata);
- return Tcl_NewListObj(answer->nrrs, rdata);
+ return rl;
}
static void make_resultlist(Tcl_Interp *ip, adns_answer *answer,
Resolver *res;
int rc, ec;
- rc= query_submit(ip,rrtype,domain,objc,objv,&aqu,&op);
+ rc= query_submit(ip,rrtype,domain,objc,objv,&aqu,0,&op);
if (rc) return rc;
res= op.resolver;
/*---------- asynchronous query handling ----------*/
-typedef struct {
+struct Query {
int ix; /* first! */
Resolver *res;
adns_query aqu;
ScriptToInvoke on_yes, on_no, on_fail;
Tcl_Obj *xargs;
-} Query;
-
-IdDataTable adnstcl_queries= { "adns" };
+};
static void asynch_timerhandler(void *res_v) {
Resolver *res= res_v;
}
}
-static void asynch_query_dispose(Query *query) {
- tabledataid_disposing(query, &adnstcl_queries);
- 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;
query= query_v;
query->aqu= 0;
- tabledataid_disposing(query, &adnstcl_queries);
+ tabledataid_disposing(interp, query, &adnstcl_queries);
si= (!answer->status ? si= &query->on_yes
: answer->status > adns_s_max_tempfail ? &query->on_no
make_resultlist(interp, answer, results);
free(answer);
scriptinv_invoke(si, RESULTLIST_LLEN, results);
- asynch_query_dispose(query);
+ asynch_query_dispose(interp, query);
}
asynch_sethandlers(res,0);
scriptinv_init(&query->on_fail);
query->xargs= 0;
- rc= query_submit(ip,rrtype,domain,objc,objv,&query->aqu,&op);
+ rc= query_submit(ip,rrtype,domain,objc,objv,&query->aqu,query,&op);
if (rc) goto x_rc;
res= op.resolver;
return TCL_OK;
x_rc:
- if (query) asynch_query_dispose(query);
+ if (query) asynch_query_dispose(ip, query);
if (res) asynch_sethandlers(res,0);
return rc;
}
int do_adns_asynch_cancel(ClientData cd, Tcl_Interp *ip, void *query_v) {
Query *query= query_v;
Resolver *res= query->res;
-
- asynch_query_dispose(query);
+ asynch_query_dispose(ip, query);
asynch_sethandlers(res,0);
return TCL_OK;
}
+
+static void asynch_query_dispose(Tcl_Interp *interp, Query *query) {
+ tabledataid_disposing(interp, query, &adnstcl_queries);
+ 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 destroy_query_idtabcb(Tcl_Interp *interp, void *query_v) {
+ asynch_query_dispose(interp, query_v);
+}
+
+const IdDataSpec adnstcl_queries= {
+ "adns", "adns-query-table", destroy_query_idtabcb
+};
/* from idtable.c */
typedef struct {
- const char *const prefix;
- int n;
- void **a;
-} IdDataTable;
+ const char *valprefix, *assockey;
+ void (*destroyitem)(Tcl_Interp *ip, void *val);
+} IdDataSpec;
/* The stored struct must start with a single int, conventionally
* named `ix'. When the struct is returned for the first time ix must
* the struct is registered by the iddatatable machinery. */
extern Tcl_ObjType tabledataid_nearlytype;
-int tabledataid_parse(Tcl_Interp *ip, Tcl_Obj *o, IdDataTable *tab);
-void tabledataid_disposing(void *val, IdDataTable *tab);
+int tabledataid_parse(Tcl_Interp *ip, Tcl_Obj *o, const IdDataSpec *idds);
+void tabledataid_disposing(Tcl_Interp *ip, void *val, const IdDataSpec *idds);
/* call this when you destroy the struct, to remove its name;
- * _disposing is idempotent*/
+ * _disposing is idempotent */
/* from adns.c */
adns_rrtype number;
} AdnsTclRRTypeInfo;
-extern IdDataTable adnstcl_queries, adnstcl_resolvers;
+extern const IdDataSpec adnstcl_queries, adnstcl_resolvers;
/* from dgram.c */
-extern IdDataTable dgram_socks;
+extern const IdDataSpec dgram_socks;
int newfdposixerr(Tcl_Interp *ip, int fd, const char *m);
/* from tuntap.c */
-extern IdDataTable tuntap_socks;
+extern const IdDataSpec tuntap_socks;
/* from hook.c */
/* Arg parsing */
-static void setobjdataid(Tcl_Obj *o, int ix, IdDataTable *tab) {
- unsigned long *ulp;
+typedef struct {
+ const IdDataSpec *idds;
+ int n;
+ void **a;
+} IdDataAssocData;
+
+typedef struct {
+ Tcl_Interp *interp;
+ IdDataAssocData *assoc;
+ int ix;
+} IdDataValue;
+
+static void assoc_del(ClientData assoc_cd, Tcl_Interp *ip) {
+ IdDataAssocData *assoc;
+ int ix;
+ void **p, *v;
+
+ assoc= assoc_cd;
+ for (ix=0, p=assoc->a; ix<assoc->n; ix++, p++) {
+ v= *p;
+ if (!v) continue;
+ assert(*(int*)v == ix);
+ *(int*)v= -1;
+ assoc->idds->destroyitem(ip,v);
+ *p= 0;
+ }
+ TFREE(assoc->a);
+ TFREE(assoc);
+}
+
+static void setobjdataid(Tcl_Interp *interp, Tcl_Obj *o,
+ int ix, const IdDataSpec *idds) {
+ IdDataValue *dv;
+ IdDataAssocData *assoc;
+
+ assoc= Tcl_GetAssocData(interp, (char*)idds->assockey, 0);
+ if (!assoc) {
+ assoc= TALLOC(sizeof(*assoc));
+ assoc->idds= idds;
+ assoc->n= 0;
+ assoc->a= 0;
+ Tcl_SetAssocData(interp, (char*)idds->assockey, assoc_del, assoc);
+ }
+
+ dv= TALLOC(sizeof(*dv));
+ dv->interp= interp;
+ dv->assoc= assoc;
+ dv->ix= ix;
- ulp= TALLOC(sizeof(unsigned long));
- *ulp= ix;
- o->internalRep.twoPtrValue.ptr1= tab;
- o->internalRep.twoPtrValue.ptr2= ulp;
o->typePtr= &tabledataid_nearlytype;
+ o->internalRep.otherValuePtr= dv;
}
-int tabledataid_parse(Tcl_Interp *ip, Tcl_Obj *o, IdDataTable *tab) {
+int tabledataid_parse(Tcl_Interp *ip, Tcl_Obj *o, const IdDataSpec *idds) {
int l;
unsigned long ul;
+ IdDataValue *dv;
+ IdDataAssocData *assoc;
char *ep, *str;
- if (o->typePtr == &tabledataid_nearlytype &&
- o->internalRep.twoPtrValue.ptr1 == tab) return TCL_OK;
+ if (o->typePtr != &tabledataid_nearlytype) goto convert;
+
+ dv= o->internalRep.otherValuePtr;
+ if (dv->interp != ip) goto convert;
+ assoc= dv->assoc;
+ if (dv->assoc->idds != idds) goto convert;
- l= strlen(tab->prefix);
+ return TCL_OK;
+
+convert:
+ l= strlen(idds->valprefix);
str= Tcl_GetStringFromObj(o,0);
- if (memcmp(str,tab->prefix,l))
+ if (memcmp(str,idds->valprefix,l))
return staticerr(ip,"bad id (wrong prefix)",0);
+
errno=0; ul=strtoul(str+l,&ep,10);
if (errno || *ep) return staticerr(ip,"bad id number",0);
if (ul > INT_MAX) return staticerr(ip,"out of range id number",0);
objfreeir(o);
- setobjdataid(o,ul,tab);
+ setobjdataid(ip,o,ul,idds);
return TCL_OK;
}
-void tabledataid_disposing(void *val, IdDataTable *tab) {
- int ix;
-
- ix= *(int*)val;
- if (ix==-1) return;
- assert(tab->a[ix] == val);
- tab->a[ix]= 0;
- *(int*)val= -1;
-}
-
-int pat_iddata(Tcl_Interp *ip, Tcl_Obj *o, void **rv, IdDataTable *tab) {
+int pat_iddata(Tcl_Interp *ip, Tcl_Obj *o, void **rv, const IdDataSpec *idds) {
int rc, ix;
+ IdDataValue *dv;
+ IdDataAssocData *assoc;
void *r;
- rc= tabledataid_parse(ip,o,tab);
+ rc= tabledataid_parse(ip,o,idds);
if (rc) return rc;
- ix= *(unsigned long*)o->internalRep.twoPtrValue.ptr2;
- if (ix >= tab->n || !(r= tab->a[ix]))
+ dv= o->internalRep.otherValuePtr;
+ ix= dv->ix;
+ assoc= dv->assoc;
+
+ if (ix >= assoc->n || !(r= assoc->a[ix]))
return staticerr(ip,"id not in use",0);
assert(*(int*)r == ix);
return TCL_OK;
}
-Tcl_Obj *ret_iddata(Tcl_Interp *ip, void *val, IdDataTable *tab) {
+Tcl_Obj *ret_iddata(Tcl_Interp *ip, void *val, const IdDataSpec *idds) {
/* Command procedure implementation may set val->ix,
* ie *(int*)val, to -1, to mean it's a new struct. Otherwise
* it had better be an old one !
*/
Tcl_Obj *o;
+ IdDataValue *dv;
+ IdDataAssocData *assoc;
int ix;
+ o= Tcl_NewObj();
+ setobjdataid(ip,o,0,idds);
+ dv= o->internalRep.otherValuePtr;
+ assoc= dv->assoc;
+
ix= *(int*)val;
if (ix==-1) {
- for (ix=0; ix<tab->n && tab->a[ix]; ix++);
- if (ix>=tab->n) {
- tab->n += 2;
- tab->n *= 2;
- tab->a= (void*)Tcl_Realloc((void*)tab->a, tab->n*sizeof(*tab->a));
- while (ix<tab->n) tab->a[ix++]=0;
+ for (ix=0; ix<assoc->n && assoc->a[ix]; ix++);
+ if (ix>=assoc->n) {
+ assoc->n += 2;
+ assoc->n *= 2;
+ assoc->a= TREALLOC(assoc->a, assoc->n*sizeof(*assoc->a));
+ while (ix<assoc->n) assoc->a[ix++]=0;
ix--;
}
- tab->a[ix]= val;
+ assoc->a[ix]= val;
*(int*)val= ix;
} else {
- assert(val == tab->a[ix]);
+ assert(val == assoc->a[ix]);
}
-
- o= Tcl_NewObj();
- setobjdataid(o,ix,tab);
+ dv->ix= ix;
Tcl_InvalidateStringRep(o);
return o;
}
+void tabledataid_disposing(Tcl_Interp *ip, void *val, const IdDataSpec *idds) {
+ IdDataAssocData *assoc;
+ int ix;
+
+ ix= *(int*)val;
+ if (ix==-1) return;
+
+ assoc= Tcl_GetAssocData(ip, (char*)idds->assockey, 0);
+ assert(assoc->a[ix] == val);
+ assoc->a[ix]= 0;
+ *(int*)val= -1;
+}
+
static int tabledataid_nt_sfa(Tcl_Interp *ip, Tcl_Obj *o) {
abort();
}
static void tabledataid_nt_free(Tcl_Obj *o) {
- TFREE(o->internalRep.twoPtrValue.ptr2);
- o->internalRep.twoPtrValue.ptr2= 0;
+ TFREE(o->internalRep.otherValuePtr);
+ o->internalRep.otherValuePtr= 0;
}
static void tabledataid_nt_dup(Tcl_Obj *src, Tcl_Obj *dup) {
- setobjdataid(dup,*(unsigned long*)src->internalRep.twoPtrValue.ptr2,
- src->internalRep.twoPtrValue.ptr1);
+ IdDataValue *sv, *dv;
+
+ sv= src->internalRep.otherValuePtr;
+ dv= TALLOC(sizeof(*dv));
+ *dv= *sv;
+ dup->typePtr= &tabledataid_nearlytype;
+ dup->internalRep.otherValuePtr= dv;
}
static void tabledataid_nt_ustr(Tcl_Obj *o) {
+ const IdDataValue *dv;
+ const IdDataAssocData *assoc;
+ const IdDataSpec *idds;
char buf[75];
- const char *prefix;
- prefix= ((IdDataTable*)o->internalRep.twoPtrValue.ptr1)->prefix;
- snprintf(buf,sizeof(buf), "%lu",
- *(unsigned long*)o->internalRep.twoPtrValue.ptr2);
+ dv= o->internalRep.otherValuePtr;
+ assoc= dv->assoc;
+ idds= assoc->idds;
+
+ snprintf(buf,sizeof(buf), "%d", dv->ix);
obj_updatestr_vstringls(o,
- prefix, strlen(prefix),
+ idds->valprefix, strlen(idds->valprefix),
buf, strlen(buf),
(char*)0);
}
Type sockaddr: SockAddr_Value @
Init sockaddr sockaddr_clear(&@);
-Type iddata(IdDataTable *tab): void *@
+Type iddata(const IdDataSpec *idds): void *@
Type ulong: uint32_t @
Type long: long @
Type string: const char *@
int addr_buflen, msg_buflen;
} DgramSocket;
-IdDataTable dgram_socks= { "dgramsock" };
-
int do_dgram_socket_create(ClientData cd, Tcl_Interp *ip,
SockAddr_Value local, void **sock_r) {
int fd, al, r;
return TCL_OK;
}
-int do_dgram_socket_close(ClientData cd, Tcl_Interp *ip, void *sock_v) {
- DgramSocket *sock= sock_v;
-
+static void destroy(DgramSocket *sock) {
cancel(sock);
close(sock->fd); /* nothing useful to be done with errors */
- tabledataid_disposing(sock,&dgram_socks);
TFREE(sock->addr_buf);
TFREE(sock->msg_buf);
TFREE(sock);
+}
+
+static void destroy_idtabcb(Tcl_Interp *ip, void *sock_v) {
+ destroy(sock_v);
+}
+
+int do_dgram_socket_close(ClientData cd, Tcl_Interp *ip, void *sock_v) {
+ destroy(sock_v);
+ tabledataid_disposing(ip,sock_v,&dgram_socks);
return TCL_OK;
}
+
+const IdDataSpec dgram_socks= {
+ "dgramsock", "dgramsock-table", destroy_idtabcb
+};
/* from idtable.c */
typedef struct {
- const char *const prefix;
- int n;
- void **a;
-} IdDataTable;
+ const char *valprefix, *assockey;
+ void (*destroyitem)(Tcl_Interp *ip, void *val);
+} IdDataSpec;
/* The stored struct must start with a single int, conventionally
* named `ix'. When the struct is returned for the first time ix must
* the struct is registered by the iddatatable machinery. */
extern Tcl_ObjType tabledataid_nearlytype;
-int tabledataid_parse(Tcl_Interp *ip, Tcl_Obj *o, IdDataTable *tab);
-void tabledataid_disposing(void *val, IdDataTable *tab);
+int tabledataid_parse(Tcl_Interp *ip, Tcl_Obj *o, const IdDataSpec *idds);
+void tabledataid_disposing(Tcl_Interp *ip, void *val, const IdDataSpec *idds);
/* call this when you destroy the struct, to remove its name;
- * _disposing is idempotent*/
+ * _disposing is idempotent */
/* from adns.c */
adns_rrtype number;
} AdnsTclRRTypeInfo;
-extern IdDataTable adnstcl_queries, adnstcl_resolvers;
+extern const IdDataSpec adnstcl_queries, adnstcl_resolvers;
/* from dgram.c */
-extern IdDataTable dgram_socks;
+extern const IdDataSpec dgram_socks;
int newfdposixerr(Tcl_Interp *ip, int fd, const char *m);
/* from tuntap.c */
-extern IdDataTable tuntap_socks;
+extern const IdDataSpec tuntap_socks;
/* from hook.c */
char *ifname;
} TuntapSocket;
-IdDataTable tuntap_socks= { "tuntap" };
-
int do_tuntap_socket_raw_create(ClientData cd, Tcl_Interp *ip,
const char *ifname, void **sock_r) {
int fd, r;
return TCL_OK;
}
-int do_tuntap_socket_raw_close(ClientData cd, Tcl_Interp *ip, void *sock_v) {
+static void destroy(void *sock_v) {
TuntapSocket *sock= sock_v;
-
- int sockix;
cancel(sock);
close(sock->fd); /* nothing useful to be done with errors */
- sockix= sock->ix;
TFREE(sock->msg_buf);
TFREE(sock);
- tuntap_socks.a[sockix]= 0;
+}
+
+static void destroy_idtabcb(Tcl_Interp *ip, void *sock_v) {
+ destroy(sock_v);
+}
+
+int do_tuntap_socket_raw_close(ClientData cd, Tcl_Interp *ip, void *sock) {
+ destroy(sock);
+ tabledataid_disposing(ip,sock,&tuntap_socks);
return TCL_OK;
}
+
+const IdDataSpec tuntap_socks= {
+ "tuntap", "tuntap-table", destroy_idtabcb
+};