From: Wouter Wijngaards Date: Fri, 18 Jan 2008 13:52:22 +0000 (+0000) Subject: unbound lib work. X-Git-Tag: release-0.9~60 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c7ad292438a6933746717c302a1204f82d24e488;p=thirdparty%2Funbound.git unbound lib work. git-svn-id: file:///svn/unbound/trunk@880 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/doc/Changelog b/doc/Changelog index 02911a0f5..2935af0a1 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -6,6 +6,8 @@ - goodbye ip4to6 mapping. - update ldns-testpkts with latest version from ldns-trunk. - updated makedist for relative ldns pathnames. + - library API with more information inside the result structure. + - work on background resolves. 17 January 2008: Wouter - fixup configure in case -lldns is installed. diff --git a/doc/libunbound.3 b/doc/libunbound.3 index a49e8b16d..d2a2bfd95 100644 --- a/doc/libunbound.3 +++ b/doc/libunbound.3 @@ -245,11 +245,16 @@ The result of the DNS resolution and validation is returned as char** data; /* array of rdata items, NULL terminated*/ int* len; /* array with lengths of rdata items */ char* canonname; /* canonical name of result */ - int rcode; /* additional error code in case of error */ - int nxdomain; /* true if nodata because no domain */ - int bogus; /* true if there was a security failure */ + int rcode; /* additional error code in case of no data */ + int havedata; /* true if there is data */ + int nxdomain; /* true if nodata because name does not exist */ + int secure; /* true if result is secure */ + int bogus; /* true if a security failure happened */ }; .fi +.P +If both secure and bogus are false, security was not enabled for the +domain of the query. .SH "RETURN VALUES" Many routines return an error code. The value 0 (zero) denotes no error happened. Other values can be passed to diff --git a/libunbound/context.h b/libunbound/context.h index 8a472d63d..1362ad01e 100644 --- a/libunbound/context.h +++ b/libunbound/context.h @@ -154,10 +154,11 @@ struct ctx_query { enum ub_ctx_err { /** no error */ UB_NOERROR = 0, + /** socket operation. Set to -1, so that if an error from _fd() is + * passed (-1) it gives a socket error. */ + UB_SOCKET = -1, /** alloc failure */ - UB_NOMEM = -1, - /** socket operation */ - UB_SOCKET = -2, + UB_NOMEM = -2, /** syntax error */ UB_SYNTAX = -3, /** DNS service failed */ diff --git a/libunbound/unbound.c b/libunbound/unbound.c index fc39982b3..6785c9e97 100644 --- a/libunbound/unbound.c +++ b/libunbound/unbound.c @@ -304,7 +304,7 @@ ub_val_ctx_process(struct ub_val_ctx* ctx) int ub_val_resolve(struct ub_val_ctx* ctx, char* name, int rrtype, - int rrclass, int* secure, int* data, struct ub_val_result** result) + int rrclass, struct ub_val_result** result) { struct ctx_query* q; int r; @@ -323,8 +323,6 @@ ub_val_resolve(struct ub_val_ctx* ctx, char* name, int rrtype, if(!q) return UB_NOMEM; /* become a resolver thread for a bit */ - *secure = 0; - *data = 0; *result = NULL; r = libworker_fg(ctx, q); @@ -333,11 +331,6 @@ ub_val_resolve(struct ub_val_ctx* ctx, char* name, int rrtype, free(q); return r; } - - if(q->msg_security == sec_status_secure) - *secure = 1; - if(q->res->data && q->res->data[0]) - *data = 1; *result = q->res; (void)rbtree_delete(&ctx->queries, q->node.key); @@ -418,8 +411,8 @@ ub_val_strerror(int err) { switch(err) { case UB_NOERROR: return "no error"; - case UB_NOMEM: return "out of memory"; case UB_SOCKET: return "socket io error"; + case UB_NOMEM: return "out of memory"; case UB_SYNTAX: return "syntax error"; case UB_SERVFAIL: return "server failure"; case UB_FORKFAIL: return "could not fork"; diff --git a/libunbound/unbound.h b/libunbound/unbound.h index 711caefa3..91d39fd3d 100644 --- a/libunbound/unbound.h +++ b/libunbound/unbound.h @@ -73,11 +73,12 @@ * shared cache data from the context. * * Application threaded. Non-blocking ('asynchronous'). + * ... setup threaded-asynchronous config option * err = ub_val_ctx_async(ctx, 1); * ... same as async for non-threaded * ... the callbacks are called in the thread that calls process(ctx) * - * If not threading is compiled in, the above async example uses fork(2) to + * If no threading is compiled in, the above async example uses fork(2) to * create a process to perform the work. The forked process exits when the * calling process exits, or ctx_delete() is called. * Otherwise, for asynchronous with threading, a worker thread is created. @@ -85,9 +86,9 @@ * The blocking calls use shared ctx-cache when threaded. Thus * ub_val_resolve() and ub_val_resolve_async() && ub_val_ctx_wait() are * not the same. The first makes the current thread do the work, setting - * up buffers, etc, to perform its thing (but using shared cache data). + * up buffers, etc, to perform the work (but using shared cache data). * The second calls another worker thread (or process) to perform the work. - * And no buffers need to be setup, but a context-switch happens. + * And no buffers need to be set up, but a context-switch happens. */ #ifndef _UB_UNBOUND_H #define _UB_UNBOUND_H @@ -138,6 +139,12 @@ struct ub_val_result { */ int rcode; + /** + * If there is any data, this is true. + * If false, there was no data (nxdomain may be true, rcode can be set). + */ + int havedata; + /** * If there was no data, and the domain did not exist, this is true. * If it is false, and there was no data, then the domain name @@ -145,6 +152,16 @@ struct ub_val_result { */ int nxdomain; + /** + * True, if the result is validated securely. + * False, if validation failed or domain queried has no security info. + * + * It is possible to get a result with no data (havedata is false), + * and secure is true. This means that the non-existance of the data + * was cryptographically proven (with signatures). + */ + int secure; + /** * If the result was not secure (secure==0), and this result is due * to a security failure, bogus is true. @@ -165,16 +182,15 @@ struct ub_val_result { * void my_callback(void* my_arg, int err, int secure, int havedata, * struct ub_val_result* result); * It is called with - * my_arg: your pointer to a (struct of) data of your choice, or NULL. - * err: if 0 all is OK, otherwise an error occured and no results + * void* my_arg: your pointer to a (struct of) data of your choice, + * or NULL. + * int err: if 0 all is OK, otherwise an error occured and no results * are forthcoming. - * secure: if true, the result is validated securely. - * havedata: if true, there was data, false if no data. - * result: pointer to more detailed result structure. + * struct result: pointer to more detailed result structure. * This structure is allocated on the heap and needs to be * freed with ub_val_result_free(result); */ -typedef void (*ub_val_callback_t)(void*, int, int, int, struct ub_val_result*); +typedef void (*ub_val_callback_t)(void*, int, struct ub_val_result*); /** * Create a resolving and validation context. @@ -306,19 +322,13 @@ int ub_val_ctx_process(struct ub_val_ctx* ctx); * @param name: domain name in text format (a zero terminated text string). * @param rrtype: type of RR in host order, 1 is A (address). * @param rrclass: class of RR in host order, 1 is IN (for internet). - * @param secure: returns true if the answer validated securely. - * false if not. - * It is possible to get a result with no data (data is false), - * and secure is true. This means that the non-existance of the data - * was cryptographically proven (with signatures). - * @param data: returns false if there was no data, or the domain did not exist, - * else true. * @param result: the result data is returned in a newly allocated result - * structure. + * structure. May be NULL on return, return value is set to an error + * in that case (out of memory). * @return 0 if OK, else error. */ int ub_val_resolve(struct ub_val_ctx* ctx, char* name, int rrtype, - int rrclass, int* secure, int* data, struct ub_val_result** result); + int rrclass, struct ub_val_result** result); /** * Perform resolution and validation of the target name. @@ -337,14 +347,13 @@ int ub_val_resolve(struct ub_val_ctx* ctx, char* name, int rrtype, * It is called as: * void callback(void* mydata, int err, int secure, int havedata, * struct ub_val_result* result) - * with mydata, the same as passed here, - * with err is 0 when a result has been found. - * with secure true if the answer validated securely. - * with havedata true if any data was found. - * with result newly allocated result structure. + * with mydata: the same as passed here, you may pass NULL, + * with err: is 0 when a result has been found. + * with result: a newly allocated result structure. + * The result may be NULL, in that case err is set. * * If an error happens during processing, your callback will be called - * with error set to a nonzero value (and secure=0, data=0, result=0). + * with error set to a nonzero value (and result==NULL). * @param async_id: if you pass a non-NULL value, an identifier number is * returned for the query as it is in progress. It can be used to * cancel the query. diff --git a/libunbound/worker.c b/libunbound/worker.c index c890bb2f1..f2a48da93 100644 --- a/libunbound/worker.c +++ b/libunbound/worker.c @@ -78,6 +78,8 @@ libworker_delete(struct libworker* w) free(w->env); } outside_network_delete(w->back); + comm_point_delete(w->cmd_com); + comm_point_delete(w->res_com); comm_base_delete(w->base); free(w); } @@ -153,14 +155,86 @@ libworker_setup(struct ub_val_ctx* ctx) return w; } +static int +libworker_handle_control_cmd(struct comm_point* c, void* arg, + int err, struct comm_reply* ATTR_UNUSED(rep)) +{ + /*struct ub_val_ctx* ctx = (struct ub_val_ctx*)arg;*/ + if(err != NETEVENT_NOERROR) { + if(err == NETEVENT_CLOSED) { + /* parent closed pipe, must have exited somehow */ + /* it is of no use to go on, exit */ + exit(0); + } + log_err("internal error: control cmd err %d", err); + exit(0); + } + return 0; +} + +static int +libworker_handle_result_write(struct comm_point* c, void* arg, + int err, struct comm_reply* ATTR_UNUSED(rep)) +{ + /*struct ub_val_ctx* ctx = (struct ub_val_ctx*)arg;*/ + if(err != NETEVENT_NOERROR) { + if(err == NETEVENT_CLOSED) { + /* parent closed pipe, must have exited somehow */ + /* it is of no use to go on, exit */ + exit(0); + } + log_err("internal error: pipe comm err %d", err); + exit(0); + } + return 0; +} + +/** get bufsize for cfg */ +static size_t +getbufsz(struct ub_val_ctx* ctx) +{ + size_t s = 65535; + lock_basic_lock(&ctx->cfglock); + s = ctx->env->cfg->msg_buffer_size; + lock_basic_unlock(&ctx->cfglock); + return s; +} + /** the background thread func */ static void* libworker_dobg(void* arg) { + /* setup */ struct ub_val_ctx* ctx = (struct ub_val_ctx*)arg; struct libworker* w = libworker_setup(ctx); + size_t bufsz = getbufsz(ctx); log_thread_set(&w->thread_num); - /* TODO do bg thread */ + if(!w) { + log_err("libunbound bg worker init failed, nomem"); + return NULL; + } + lock_basic_lock(&ctx->qqpipe_lock); + if(!(w->cmd_com=comm_point_create_local(w->base, ctx->qqpipe[0], + bufsz, libworker_handle_control_cmd, w))) { + lock_basic_unlock(&ctx->qqpipe_lock); + log_err("libunbound bg worker init failed, no cmdcom"); + return NULL; + } + lock_basic_unlock(&ctx->qqpipe_lock); + lock_basic_lock(&ctx->rrpipe_lock); + /* TODO create writing local commpoint */ + if(!(w->res_com=comm_point_create_local(w->base, ctx->rrpipe[1], + bufsz, libworker_handle_result_write, w))) { + lock_basic_unlock(&ctx->qqpipe_lock); + log_err("libunbound bg worker init failed, no cmdcom"); + return NULL; + } + lock_basic_unlock(&ctx->rrpipe_lock); + + /* do the work */ + comm_base_dispatch(w->base); + + /* cleanup */ libworker_delete(w); return NULL; } @@ -287,10 +361,14 @@ libworker_fg_done_cb(void* arg, int rcode, ldns_buffer* buf, enum sec_status s) if(!fill_res(d->q->res, reply_find_answer_rrset(&rq, rep), reply_find_final_cname_target(&rq, rep), &rq)) return; /* out of memory */ - /* rcode, nxdomain, bogus */ + /* rcode, havedata, nxdomain, secure, bogus */ d->q->res->rcode = (int)LDNS_RCODE_WIRE(d->q->msg); + if(d->q->res->data && d->q->res->data[0]) + d->q->res->havedata = 1; if(d->q->res->rcode == LDNS_RCODE_NXDOMAIN) d->q->res->nxdomain = 1; + if(s == sec_status_secure) + d->q->res->secure = 1; if(s == sec_status_bogus) d->q->res->bogus = 1; @@ -340,20 +418,25 @@ int libworker_fg(struct ub_val_ctx* ctx, struct ctx_query* q) qflags = BIT_RD; d.q = q; d.w = w; + /* see if there is a fixed answer */ if(local_zones_answer(ctx->local_zones, &qinfo, &edns, w->back->udp_buff, w->env->scratch)) { libworker_fg_done_cb(&d, LDNS_RCODE_NOERROR, w->back->udp_buff, sec_status_insecure); + libworker_delete(w); + return UB_NOERROR; } - else { - if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns, - w->back->udp_buff, qid, libworker_fg_done_cb, &d)) { - free(qinfo.qname); - return UB_NOMEM; - } + /* process new query */ + if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns, + w->back->udp_buff, qid, libworker_fg_done_cb, &d)) { free(qinfo.qname); - comm_base_dispatch(w->base); + return UB_NOMEM; } + free(qinfo.qname); + + /* wait for reply */ + comm_base_dispatch(w->base); + libworker_delete(w); return UB_NOERROR; } diff --git a/libunbound/worker.h b/libunbound/worker.h index dae67da43..3382b6dd9 100644 --- a/libunbound/worker.h +++ b/libunbound/worker.h @@ -76,6 +76,10 @@ struct libworker { /** random() table for this worker. */ struct ub_randstate* rndstate; + /** commpoint to listen to commands */ + struct comm_point* cmd_com; + /** commpoint to write results back (nonblocking) */ + struct comm_point* res_com; }; /** diff --git a/smallapp/unbound-host.c b/smallapp/unbound-host.c index 4b5651943..f8f8782dc 100644 --- a/smallapp/unbound-host.c +++ b/smallapp/unbound-host.c @@ -176,10 +176,10 @@ massage_class(const char* c) /** nice security status string */ static const char* -statstr(int sec, struct ub_val_result* result) +secure_str(struct ub_val_result* result) { - if(sec) return "(secure)"; - if(result->bogus) return "(BOGUS (security failure))]"; + if(result->secure) return "(secure)"; + if(result->bogus) return "(BOGUS (security failure))"; return "(insecure)"; } @@ -274,11 +274,10 @@ pretty_rdata(char* q, char* cstr, char* tstr, int t, const char* sec, /** pretty line of output for results */ static void -pretty_output(char* q, int t, int c, int sec, int haved, - struct ub_val_result* result, int docname) +pretty_output(char* q, int t, int c, struct ub_val_result* result, int docname) { int i; - const char *secstatus = statstr(sec, result); + const char *secstatus = secure_str(result); char tstr[16]; char cstr[16]; char rcodestr[16]; @@ -286,7 +285,7 @@ pretty_output(char* q, int t, int c, int sec, int haved, pretty_class(cstr, 16, c); pretty_rcode(rcodestr, 16, result->rcode); - if(!haved && result->rcode) { + if(!result->havedata && result->rcode) { printf("Host %s not found: %d(%s).", q, result->rcode, rcodestr); if(verb > 0) @@ -305,7 +304,7 @@ pretty_output(char* q, int t, int c, int sec, int haved, /* remove trailing . from long canonnames for nicer output */ if(result->canonname && strlen(result->canonname) > 1) result->canonname[strlen(result->canonname)-1] = 0; - if(!haved) { + if(!result->havedata) { if(verb > 0) { printf("%s", result->canonname?result->canonname:q); if(strcmp(cstr, "IN") != 0) @@ -339,15 +338,15 @@ pretty_output(char* q, int t, int c, int sec, int haved, static int dnslook(struct ub_val_ctx* ctx, char* q, int t, int c, int docname) { - int ret, sec, haved; + int ret; struct ub_val_result* result; - ret = ub_val_resolve(ctx, q, t, c, &sec, &haved, &result); + ret = ub_val_resolve(ctx, q, t, c, &result); if(ret != 0) { fprintf(stderr, "resolve error: %s\n", ub_val_strerror(ret)); exit(1); } - pretty_output(q, t, c, sec, haved, result, docname); + pretty_output(q, t, c, result, docname); ret = result->nxdomain; ub_val_result_free(result); return ret;