unbound lib work.

git-svn-id: file:///svn/unbound/trunk@880 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2008-01-18 13:52:22 +00:00
parent b63db13e72
commit c7ad292438
8 changed files with 155 additions and 59 deletions

View File

@ -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.

View File

@ -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

View File

@ -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 */

View File

@ -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";

View File

@ -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.

View File

@ -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;
}

View File

@ -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;
};
/**

View File

@ -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;