From 2e28288b1d2741cffe0d635a58f7e2186a69bda3 Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Thu, 24 Jan 2008 14:58:51 +0000 Subject: [PATCH] forwarder mode options for library. git-svn-id: file:///svn/unbound/trunk@895 be551aaa-1e26-0410-a405-d3ace91eadb9 --- doc/Changelog | 2 + doc/libunbound.3 | 61 ++++++++++++++------ libunbound/context.h | 4 +- libunbound/ubsyms.def | 10 ++-- libunbound/unbound.c | 131 ++++++++++++++++++++++++++++++++++++++++-- libunbound/unbound.h | 57 ++++++++++++++---- testcode/asynclook.c | 31 +++++++++- util/config_file.c | 3 +- util/config_file.h | 6 ++ 9 files changed, 263 insertions(+), 42 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index aa60812ff..fd0c7b4f0 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -5,6 +5,8 @@ - make pipe nonblocking at start. - update plane for retry mode with caution to limit bandwidth. - fix Makefile for concurrent make of unbound-host. + - renamed ub_val_ctx_wait/poll/process/fd to ub_val*. + - new calls to set forwarding added to header and docs. 23 January 2008: Wouter - removed debug prints from if-auto, verb-algo enables some. diff --git a/doc/libunbound.3 b/doc/libunbound.3 index d2a2bfd95..27d2bee42 100644 --- a/doc/libunbound.3 +++ b/doc/libunbound.3 @@ -17,15 +17,17 @@ .B ub_val_ctx_create, .B ub_val_ctx_delete, .B ub_val_ctx_config, +.B ub_val_ctx_set_fwd, +.B ub_val_ctx_resolvconf, .B ub_val_ctx_add_ta, .B ub_val_ctx_add_ta_file, .B ub_val_ctx_trustedkeys, .B ub_val_ctx_debuglevel, .B ub_val_ctx_async, -.B ub_val_ctx_poll, -.B ub_val_ctx_wait, -.B ub_val_ctx_fd, -.B ub_val_ctx_process, +.B ub_val_poll, +.B ub_val_wait, +.B ub_val_fd, +.B ub_val_process, .B ub_val_resolve, .B ub_val_resolve_async, .B ub_val_cancel, @@ -46,6 +48,12 @@ \fBub_val_ctx_config\fR(\fIstruct ub_val_ctx*\fR ctx, \fIchar*\fR fname); .LP \fIint\fR +\fBub_val_ctx_set_fwd\fR(\fIstruct ub_val_ctx*\fR ctx, \fIchar*\fR addr); +.LP +\fIint\fR +\fBub_val_ctx_resolvconf\fR(\fIstruct ub_val_ctx*\fR ctx, \fIchar*\fR fname); +.LP +\fIint\fR \fBub_val_ctx_add_ta\fR(\fIstruct ub_val_ctx*\fR ctx, \fIchar*\fR ta); .LP \fIint\fR @@ -65,16 +73,16 @@ \fBub_val_ctx_async\fR(\fIstruct ub_val_ctx*\fR ctx, \fIint\fR dothread); .LP \fIint\fR -\fBub_val_ctx_poll\fR(\fIstruct ub_val_ctx*\fR ctx); +\fBub_val_poll\fR(\fIstruct ub_val_ctx*\fR ctx); .LP \fIint\fR -\fBub_val_ctx_wait\fR(\fIstruct ub_val_ctx*\fR ctx); +\fBub_val_wait\fR(\fIstruct ub_val_ctx*\fR ctx); .LP \fIint\fR -\fBub_val_ctx_fd\fR(\fIstruct ub_val_ctx*\fR ctx); +\fBub_val_fd\fR(\fIstruct ub_val_ctx*\fR ctx); .LP \fIint\fR -\fBub_val_ctx_process\fR(\fIstruct ub_val_ctx*\fR ctx); +\fBub_val_process\fR(\fIstruct ub_val_ctx*\fR ctx); .LP \fIint\fR \fBub_val_resolve\fR(\fIstruct ub_val_ctx*\fR ctx, \fIchar*\fR name, @@ -138,6 +146,25 @@ A power\-user interface that lets you specify an unbound config file, see relevant. For some specific options, such as adding trust anchors, special routines exist. .TP +.B ub_val_ctx_set_fwd +Set machine to forward DNS queries to, the caching resolver to use. +IP4 or IP6 address. Forwards all DNS requests to that machine, which +is expected to run a recursive resolver. If the proxy is not +DNSSEC capable, validation may fail. Can be called several times, in +that case the addresses are used as backup servers. +At this time it is only possible to set configuration before the +first resolve is done. +.TP +.B ub_val_ctx_resolvconf +Read list of nameservers to use from the filename given. +Usually "/etc/resolv.conf". Uses those nameservers as caching proxies. +If they do not support DNSSEC, validation may fail. +Only nameservers are picked up, the searchdomain, ndots and other +settings from \fIresolv.conf\fR(5) are ignored. +If fname NULL is passed, "/etc/resolv.conf" is used. +At this time it is only possible to set configuration before the +first resolve is done. +.TP .B ub_val_ctx_add_ta Add a trust anchor to the given context. @@ -173,27 +200,27 @@ Changes to this setting after calls have been made have no effect (delete and re\-create the context to change). .TP -.B ub_val_ctx_poll +.B ub_val_poll Poll a context to see if it has any new results. Do not poll in a loop, instead extract the fd below to poll for readiness, and then check, or wait using the wait routine. Returns 0 if nothing to read, or nonzero if a result is available. If nonzero, call -.B ub_val_ctx_process +.B ub_val_process to do callbacks. .TP -.B ub_val_ctx_wait +.B ub_val_wait Wait for a context to finish with results. Calls -.B ub_val_ctx_process +.B ub_val_process after the wait for you. After the wait, there are no more outstanding asynchronous queries. .TP -.B ub_val_ctx_fd +.B ub_val_fd Get file descriptor. Wait for it to become readable, at this point answers are returned from the asynchronous validating resolver. -Then call the \fBub_val_ctx_process\fR to continue processing. +Then call the \fBub_val_process\fR to continue processing. .TP -.B ub_val_ctx_process +.B ub_val_process Call this routine to continue processing results from the validating resolver (when the fd becomes readable). Will perform necessary callbacks. @@ -264,9 +291,9 @@ to obtain a readable error string. returns a zero terminated string. .B ub_val_ctx_create returns NULL on an error (a malloc failure). -.B ub_val_ctx_poll +.B ub_val_poll returns true if some information may be available, false otherwise. -.B ub_val_ctx_fd +.B ub_val_fd returns a file descriptor or -1 on error. .SH "SEE ALSO" \fIunbound.conf\fR(5), diff --git a/libunbound/context.h b/libunbound/context.h index e1e8819ec..5be7dc8ac 100644 --- a/libunbound/context.h +++ b/libunbound/context.h @@ -175,7 +175,9 @@ enum ub_ctx_err { /** initialization failed (bad settings) */ UB_INITFAIL = -7, /** error in pipe communication with async bg worker */ - UB_PIPE = -8 + UB_PIPE = -8, + /** error reading from file (resolv.conf) */ + UB_READFILE = -9 }; /** diff --git a/libunbound/ubsyms.def b/libunbound/ubsyms.def index 8e4ae9d48..ed1ebbf6d 100644 --- a/libunbound/ubsyms.def +++ b/libunbound/ubsyms.def @@ -1,15 +1,17 @@ ub_val_ctx_create ub_val_ctx_delete ub_val_ctx_config +ub_val_ctx_set_fwd +ub_val_ctx_resolvconf ub_val_ctx_add_ta ub_val_ctx_add_ta_file ub_val_ctx_trustedkeys ub_val_ctx_debuglevel ub_val_ctx_async -ub_val_ctx_poll -ub_val_ctx_wait -ub_val_ctx_fd -ub_val_ctx_process +ub_val_poll +ub_val_wait +ub_val_fd +ub_val_process ub_val_resolve ub_val_resolve_async ub_val_cancel diff --git a/libunbound/unbound.c b/libunbound/unbound.c index 148c22737..a3fcf95c3 100644 --- a/libunbound/unbound.c +++ b/libunbound/unbound.c @@ -319,7 +319,7 @@ pollit(struct ub_val_ctx* ctx, struct timeval* t) } int -ub_val_ctx_poll(struct ub_val_ctx* ctx) +ub_val_poll(struct ub_val_ctx* ctx) { struct timeval t; int r; @@ -331,7 +331,7 @@ ub_val_ctx_poll(struct ub_val_ctx* ctx) } int -ub_val_ctx_wait(struct ub_val_ctx* ctx) +ub_val_wait(struct ub_val_ctx* ctx) { int r; lock_basic_lock(&ctx->cfglock); @@ -341,7 +341,7 @@ ub_val_ctx_wait(struct ub_val_ctx* ctx) r = pollit(ctx, NULL); lock_basic_unlock(&ctx->rrpipe_lock); if(r) - ub_val_ctx_process(ctx); + ub_val_process(ctx); lock_basic_lock(&ctx->cfglock); } lock_basic_unlock(&ctx->cfglock); @@ -349,7 +349,7 @@ ub_val_ctx_wait(struct ub_val_ctx* ctx) } int -ub_val_ctx_fd(struct ub_val_ctx* ctx) +ub_val_fd(struct ub_val_ctx* ctx) { int fd; lock_basic_lock(&ctx->rrpipe_lock); @@ -418,7 +418,7 @@ process_answer(struct ub_val_ctx* ctx, uint8_t* msg, uint32_t len) } int -ub_val_ctx_process(struct ub_val_ctx* ctx) +ub_val_process(struct ub_val_ctx* ctx) { int r; uint8_t* msg = NULL; @@ -601,6 +601,127 @@ ub_val_strerror(int err) case UB_INITFAIL: return "initialization failure"; case UB_AFTERFINAL: return "setting change after finalize"; case UB_PIPE: return "error in pipe communication with async"; + case UB_READFILE: return "error reading file"; default: return "unknown error"; } } + +int +ub_val_ctx_set_fwd(struct ub_val_ctx* ctx, char* addr) +{ + struct sockaddr_storage storage; + socklen_t stlen; + struct config_stub* s; + char* dupl; + lock_basic_lock(&ctx->cfglock); + if(ctx->finalized) { + lock_basic_unlock(&ctx->cfglock); + errno=EINVAL; + return UB_AFTERFINAL; + } + if(!addr) { + /* disable fwd mode - the root stub should be first. */ + if(ctx->env->cfg->forwards && + strcmp(ctx->env->cfg->forwards->name, ".") == 0) { + s = ctx->env->cfg->forwards; + ctx->env->cfg->forwards = s->next; + s->next = NULL; + config_delstubs(s); + } + lock_basic_unlock(&ctx->cfglock); + return UB_NOERROR; + } + lock_basic_unlock(&ctx->cfglock); + + /* check syntax for addr */ + if(!extstrtoaddr(addr, &storage, &stlen)) { + errno=EINVAL; + return UB_SYNTAX; + } + + /* it parses, add root stub in front of list */ + lock_basic_lock(&ctx->cfglock); + if(!ctx->env->cfg->forwards || + strcmp(ctx->env->cfg->forwards->name, ".") != 0) { + s = calloc(1, sizeof(*s)); + if(!s) { + lock_basic_unlock(&ctx->cfglock); + errno=ENOMEM; + return UB_NOMEM; + } + s->name = strdup("."); + if(!s->name) { + free(s); + lock_basic_unlock(&ctx->cfglock); + errno=ENOMEM; + return UB_NOMEM; + } + s->next = ctx->env->cfg->forwards; + ctx->env->cfg->forwards = s; + } else { + log_assert(ctx->env->cfg->forwards); + s = ctx->env->cfg->forwards; + } + dupl = strdup(addr); + if(!dupl) { + lock_basic_unlock(&ctx->cfglock); + errno=ENOMEM; + return UB_NOMEM; + } + if(!cfg_strlist_insert(&s->addrs, dupl)) { + free(dupl); + lock_basic_unlock(&ctx->cfglock); + errno=ENOMEM; + return UB_NOMEM; + } + lock_basic_unlock(&ctx->cfglock); + return UB_NOERROR; +} + +int +ub_val_ctx_resolvconf(struct ub_val_ctx* ctx, char* fname) +{ + FILE* in; + int numserv = 0; + char buf[1024]; + char* parse, *addr; + int r; + if(fname == NULL) + fname = "/etc/resolv.conf"; + in = fopen(fname, "r"); + if(!in) { + /* error in errno! perror(fname) */ + return UB_READFILE; + } + while(fgets(buf, (int)sizeof(buf), in)) { + buf[sizeof(buf)-1] = 0; + parse=buf; + while(*parse == ' ' || *parse == '\t') + parse++; + if(strncmp(parse, "nameserver", 10) == 0) { + numserv++; + parse += 10; /* skip 'nameserver' */ + /* skip whitespace */ + while(*parse == ' ' || *parse == '\t') + parse++; + addr = parse; + /* skip [0-9a-fA-F.:]*, i.e. IP4 and IP6 address */ + while(isxdigit(*parse) || *parse=='.' || *parse==':') + parse++; + /* terminate after the address, remove newline */ + *parse = 0; + + if((r = ub_val_ctx_set_fwd(ctx, addr)) != UB_NOERROR) { + fclose(in); + return r; + } + } + } + fclose(in); + if(numserv == 0) { + /* from resolv.conf(5) if none given, use localhost */ + log_info("resconf: no nameservers, using localhost"); + return ub_val_ctx_set_fwd(ctx, "127.0.0.1"); + } + return UB_NOERROR; +} diff --git a/libunbound/unbound.h b/libunbound/unbound.h index bbfb64690..aa6b81a14 100644 --- a/libunbound/unbound.h +++ b/libunbound/unbound.h @@ -57,15 +57,15 @@ * Application not threaded. Non-blocking ('asynchronous'). * err = ub_val_resolve_async(ctx, "www.example.com", ... my_callback); * ... application resumes processing ... - * ... and when either ub_val_ctx_poll(ctx) is true - * ... or when the file descriptor ub_val_ctx_fd(ctx) is readable, + * ... and when either ub_val_poll(ctx) is true + * ... or when the file descriptor ub_val_fd(ctx) is readable, * ... or whenever, the app calls ... - * ub_val_ctx_process(ctx); + * ub_val_process(ctx); * ... if no result is ready, the app resumes processing above, * ... or process() calls my_callback() with results. * * ... if the application has nothing more to do, wait for answer - * ub_val_ctx_wait(ctx); + * ub_val_wait(ctx); * * Application threaded. Blocking. * Blocking, same as above. The current thread does the work. @@ -84,7 +84,7 @@ * Otherwise, for asynchronous with threading, a worker thread is created. * * The blocking calls use shared ctx-cache when threaded. Thus - * ub_val_resolve() and ub_val_resolve_async() && ub_val_ctx_wait() are + * ub_val_resolve() and ub_val_resolve_async() && ub_val_wait() are * not the same. The first makes the current thread do the work, setting * up buffers, etc, to perform the work (but using shared cache data). * The second calls another worker thread (or process) to perform the work. @@ -217,6 +217,41 @@ void ub_val_ctx_delete(struct ub_val_ctx* ctx); */ int ub_val_ctx_config(struct ub_val_ctx* ctx, char* fname); +/** + * Set machine to forward DNS queries to, the caching resolver to use. + * IP4 or IP6 address. Forwards all DNS requests to that machine, which + * is expected to run a recursive resolver. If the proxy is not + * DNSSEC-capable, validation may fail. Can be called several times, in + * that case the addresses are used as backup servers. + * + * To read the list of nameservers from /etc/resolv.conf (from DHCP or so), + * use the call ub_val_ctx_resolvconf. + * + * @param ctx: context. + * At this time it is only possible to set configuration before the + * first resolve is done. + * @param addr: address, IP4 or IP6 in string format. + * If the addr is NULL, forwarding is disabled. + * @return 0 if OK, else error. + */ +int ub_val_ctx_set_fwd(struct ub_val_ctx* ctx, char* addr); + +/** + * Read list of nameservers to use from the filename given. + * Usually "/etc/resolv.conf". Uses those nameservers as caching proxies. + * If they do not support DNSSEC, validation may fail. + * + * Only nameservers are picked up, the searchdomain, ndots and other + * settings from resolv.conf(5) are ignored. + * + * @param ctx: context. + * At this time it is only possible to set configuration before the + * first resolve is done. + * @param fname: file name string. If NULL "/etc/resolv.conf" is used. + * @return 0 if OK, else error. + */ +int ub_val_ctx_resolvconf(struct ub_val_ctx* ctx, char* fname); + /** * Add a trust anchor to the given context. * The trust anchor is a string, on one line, that holds a valid DNSKEY or @@ -283,27 +318,27 @@ int ub_val_ctx_async(struct ub_val_ctx* ctx, int dothread); * @return: 0 if nothing to read, or nonzero if a result is available. * If nonzero, call ctx_process() to do callbacks. */ -int ub_val_ctx_poll(struct ub_val_ctx* ctx); +int ub_val_poll(struct ub_val_ctx* ctx); /** - * Wait for a context to finish with results. Calls ctx_process() after + * Wait for a context to finish with results. Calls ub_val_process() after * the wait for you. After the wait, there are no more outstanding * asynchronous queries. * @param ctx: context. * @return: 0 if OK, else error. */ -int ub_val_ctx_wait(struct ub_val_ctx* ctx); +int ub_val_wait(struct ub_val_ctx* ctx); /** * Get file descriptor. Wait for it to become readable, at this point * answers are returned from the asynchronous validating resolver. - * Then call the ub_val_ctx_process to continue processing. + * Then call the ub_val_process to continue processing. * This routine works immediately after context creation, the fd * does not change. * @param ctx: context. * @return: -1 on error, or file descriptor to use select(2) with. */ -int ub_val_ctx_fd(struct ub_val_ctx* ctx); +int ub_val_fd(struct ub_val_ctx* ctx); /** * Call this routine to continue processing results from the validating @@ -312,7 +347,7 @@ int ub_val_ctx_fd(struct ub_val_ctx* ctx); * @param ctx: context * @return: 0 if OK, else error. */ -int ub_val_ctx_process(struct ub_val_ctx* ctx); +int ub_val_process(struct ub_val_ctx* ctx); /** * Perform resolution and validation of the target name. diff --git a/testcode/asynclook.c b/testcode/asynclook.c index 53714275a..91939eb98 100644 --- a/testcode/asynclook.c +++ b/testcode/asynclook.c @@ -69,6 +69,8 @@ void usage(char* argv[]) printf("-d : enable debug output\n"); printf("-t : use a resolver thread instead of forking a process\n"); printf("-c : cancel the requests\n"); + printf("-r fname : read resolv.conf from fname\n"); + printf("-f addr : use addr, forward to that server\n"); exit(1); } @@ -119,6 +121,26 @@ int main(int argc, char** argv) argc--; argv++; } + if(argc > 1 && strcmp(argv[0], "-r") == 0) { + r = ub_val_ctx_resolvconf(ctx, argv[1]); + if(r != 0) { + printf("ub_val_ctx_resolvconf error: %s : %s\n", + ub_val_strerror(r), strerror(errno)); + return 1; + } + argc-=2; + argv+=2; + } + if(argc > 1 && strcmp(argv[0], "-f") == 0) { + r = ub_val_ctx_set_fwd(ctx, argv[1]); + if(r != 0) { + printf("ub_val_ctx_set_fwd error: %s\n", + ub_val_strerror(r)); + return 1; + } + argc-=2; + argv+=2; + } /* allocate array for results. */ lookups = (struct lookinfo*)calloc((size_t)argc, @@ -136,6 +158,11 @@ int main(int argc, char** argv) r = ub_val_resolve_async(ctx, argv[i], LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN, &lookups[i], &lookup_is_done, &lookups[i].async_id); + if(r != 0) { + printf("ub_val_resolve_async error: %s\n", + ub_val_strerror(r)); + return 1; + } } if(cancel) { for(i=0; i