]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - libunbound/libunbound.c
Vendor import of Unbound 1.6.1.
[FreeBSD/FreeBSD.git] / libunbound / libunbound.c
1 /*
2  * unbound.c - unbound validating resolver public API implementation
3  *
4  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5  *
6  * This software is open source.
7  * 
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 
12  * Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  * 
15  * Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  * 
19  * Neither the name of the NLNET LABS nor the names of its contributors may
20  * be used to endorse or promote products derived from this software without
21  * specific prior written permission.
22  * 
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35
36 /**
37  * \file
38  *
39  * This file contains functions to resolve DNS queries and 
40  * validate the answers. Synchonously and asynchronously.
41  *
42  */
43
44 /* include the public api first, it should be able to stand alone */
45 #include "libunbound/unbound.h"
46 #include "libunbound/unbound-event.h"
47 #include "config.h"
48 #include <ctype.h>
49 #include "libunbound/context.h"
50 #include "libunbound/libworker.h"
51 #include "util/locks.h"
52 #include "util/config_file.h"
53 #include "util/alloc.h"
54 #include "util/module.h"
55 #include "util/regional.h"
56 #include "util/log.h"
57 #include "util/random.h"
58 #include "util/net_help.h"
59 #include "util/tube.h"
60 #include "util/ub_event.h"
61 #include "services/modstack.h"
62 #include "services/localzone.h"
63 #include "services/cache/infra.h"
64 #include "services/cache/rrset.h"
65 #include "sldns/sbuffer.h"
66 #ifdef HAVE_PTHREAD
67 #include <signal.h>
68 #endif
69 #ifdef HAVE_SYS_WAIT_H
70 #include <sys/wait.h>
71 #endif
72 #ifdef HAVE_TIME_H
73 #include <time.h>
74 #endif
75
76 #if defined(UB_ON_WINDOWS) && defined (HAVE_WINDOWS_H)
77 #include <windows.h>
78 #include <iphlpapi.h>
79 #endif /* UB_ON_WINDOWS */
80
81 /** create context functionality, but no pipes */
82 static struct ub_ctx* ub_ctx_create_nopipe(void)
83 {
84         struct ub_ctx* ctx;
85         unsigned int seed;
86 #ifdef USE_WINSOCK
87         int r;
88         WSADATA wsa_data;
89 #endif
90         
91         log_init(NULL, 0, NULL); /* logs to stderr */
92         log_ident_set("libunbound");
93 #ifdef USE_WINSOCK
94         if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) {
95                 log_err("could not init winsock. WSAStartup: %s",
96                         wsa_strerror(r));
97                 return NULL;
98         }
99 #endif
100         verbosity = 0; /* errors only */
101         checklock_start();
102         ctx = (struct ub_ctx*)calloc(1, sizeof(*ctx));
103         if(!ctx) {
104                 errno = ENOMEM;
105                 return NULL;
106         }
107         alloc_init(&ctx->superalloc, NULL, 0);
108         seed = (unsigned int)time(NULL) ^ (unsigned int)getpid();
109         if(!(ctx->seed_rnd = ub_initstate(seed, NULL))) {
110                 seed = 0;
111                 ub_randfree(ctx->seed_rnd);
112                 free(ctx);
113                 errno = ENOMEM;
114                 return NULL;
115         }
116         seed = 0;
117         lock_basic_init(&ctx->qqpipe_lock);
118         lock_basic_init(&ctx->rrpipe_lock);
119         lock_basic_init(&ctx->cfglock);
120         ctx->env = (struct module_env*)calloc(1, sizeof(*ctx->env));
121         if(!ctx->env) {
122                 ub_randfree(ctx->seed_rnd);
123                 free(ctx);
124                 errno = ENOMEM;
125                 return NULL;
126         }
127         ctx->env->cfg = config_create_forlib();
128         if(!ctx->env->cfg) {
129                 free(ctx->env);
130                 ub_randfree(ctx->seed_rnd);
131                 free(ctx);
132                 errno = ENOMEM;
133                 return NULL;
134         }
135         /* init edns_known_options */
136         if(!edns_known_options_init(ctx->env)) {
137                 config_delete(ctx->env->cfg);
138                 free(ctx->env);
139                 ub_randfree(ctx->seed_rnd);
140                 free(ctx);
141                 errno = ENOMEM;
142                 return NULL;
143         }
144         ctx->env->alloc = &ctx->superalloc;
145         ctx->env->worker = NULL;
146         ctx->env->need_to_validate = 0;
147         modstack_init(&ctx->mods);
148         rbtree_init(&ctx->queries, &context_query_cmp);
149         return ctx;
150 }
151
152 struct ub_ctx* 
153 ub_ctx_create(void)
154 {
155         struct ub_ctx* ctx = ub_ctx_create_nopipe();
156         if(!ctx)
157                 return NULL;
158         if((ctx->qq_pipe = tube_create()) == NULL) {
159                 int e = errno;
160                 ub_randfree(ctx->seed_rnd);
161                 config_delete(ctx->env->cfg);
162                 modstack_desetup(&ctx->mods, ctx->env);
163                 edns_known_options_delete(ctx->env);
164                 free(ctx->env);
165                 free(ctx);
166                 errno = e;
167                 return NULL;
168         }
169         if((ctx->rr_pipe = tube_create()) == NULL) {
170                 int e = errno;
171                 tube_delete(ctx->qq_pipe);
172                 ub_randfree(ctx->seed_rnd);
173                 config_delete(ctx->env->cfg);
174                 modstack_desetup(&ctx->mods, ctx->env);
175                 edns_known_options_delete(ctx->env);
176                 free(ctx->env);
177                 free(ctx);
178                 errno = e;
179                 return NULL;
180         }
181         return ctx;
182 }
183
184 struct ub_ctx* 
185 ub_ctx_create_ub_event(struct ub_event_base* ueb)
186 {
187         struct ub_ctx* ctx = ub_ctx_create_nopipe();
188         if(!ctx)
189                 return NULL;
190         /* no pipes, but we have the locks to make sure everything works */
191         ctx->created_bg = 0;
192         ctx->dothread = 1; /* the processing is in the same process,
193                 makes ub_cancel and ub_ctx_delete do the right thing */
194         ctx->event_base = ueb;
195         return ctx;
196 }
197
198 struct ub_ctx* 
199 ub_ctx_create_event(struct event_base* eb)
200 {
201         struct ub_ctx* ctx = ub_ctx_create_nopipe();
202         if(!ctx)
203                 return NULL;
204         /* no pipes, but we have the locks to make sure everything works */
205         ctx->created_bg = 0;
206         ctx->dothread = 1; /* the processing is in the same process,
207                 makes ub_cancel and ub_ctx_delete do the right thing */
208         ctx->event_base = ub_libevent_event_base(eb);
209         if (!ctx->event_base) {
210                 ub_ctx_delete(ctx);
211                 return NULL;
212         }
213         return ctx;
214 }
215         
216 /** delete q */
217 static void
218 delq(rbnode_type* n, void* ATTR_UNUSED(arg))
219 {
220         struct ctx_query* q = (struct ctx_query*)n;
221         context_query_delete(q);
222 }
223
224 /** stop the bg thread */
225 static void ub_stop_bg(struct ub_ctx* ctx)
226 {
227         /* stop the bg thread */
228         lock_basic_lock(&ctx->cfglock);
229         if(ctx->created_bg) {
230                 uint8_t* msg;
231                 uint32_t len;
232                 uint32_t cmd = UB_LIBCMD_QUIT;
233                 lock_basic_unlock(&ctx->cfglock);
234                 lock_basic_lock(&ctx->qqpipe_lock);
235                 (void)tube_write_msg(ctx->qq_pipe, (uint8_t*)&cmd, 
236                         (uint32_t)sizeof(cmd), 0);
237                 lock_basic_unlock(&ctx->qqpipe_lock);
238                 lock_basic_lock(&ctx->rrpipe_lock);
239                 while(tube_read_msg(ctx->rr_pipe, &msg, &len, 0)) {
240                         /* discard all results except a quit confirm */
241                         if(context_serial_getcmd(msg, len) == UB_LIBCMD_QUIT) {
242                                 free(msg);
243                                 break;
244                         }
245                         free(msg);
246                 }
247                 lock_basic_unlock(&ctx->rrpipe_lock);
248
249                 /* if bg worker is a thread, wait for it to exit, so that all
250                  * resources are really gone. */
251                 lock_basic_lock(&ctx->cfglock);
252                 if(ctx->dothread) {
253                         lock_basic_unlock(&ctx->cfglock);
254                         ub_thread_join(ctx->bg_tid);
255                 } else {
256                         lock_basic_unlock(&ctx->cfglock);
257 #ifndef UB_ON_WINDOWS
258                         if(waitpid(ctx->bg_pid, NULL, 0) == -1) {
259                                 if(verbosity > 2)
260                                         log_err("waitpid: %s", strerror(errno));
261                         }
262 #endif
263                 }
264         }
265         else {
266                 lock_basic_unlock(&ctx->cfglock);
267         }
268 }
269
270 void 
271 ub_ctx_delete(struct ub_ctx* ctx)
272 {
273         struct alloc_cache* a, *na;
274         int do_stop = 1;
275         if(!ctx) return;
276
277         /* see if bg thread is created and if threads have been killed */
278         /* no locks, because those may be held by terminated threads */
279         /* for processes the read pipe is closed and we see that on read */
280 #ifdef HAVE_PTHREAD
281         if(ctx->created_bg && ctx->dothread) {
282                 if(pthread_kill(ctx->bg_tid, 0) == ESRCH) {
283                         /* thread has been killed */
284                         do_stop = 0;
285                 }
286         }
287 #endif /* HAVE_PTHREAD */
288         if(do_stop)
289                 ub_stop_bg(ctx);
290         libworker_delete_event(ctx->event_worker);
291
292         modstack_desetup(&ctx->mods, ctx->env);
293         a = ctx->alloc_list;
294         while(a) {
295                 na = a->super;
296                 a->super = &ctx->superalloc;
297                 alloc_clear(a);
298                 free(a);
299                 a = na;
300         }
301         local_zones_delete(ctx->local_zones);
302         lock_basic_destroy(&ctx->qqpipe_lock);
303         lock_basic_destroy(&ctx->rrpipe_lock);
304         lock_basic_destroy(&ctx->cfglock);
305         tube_delete(ctx->qq_pipe);
306         tube_delete(ctx->rr_pipe);
307         if(ctx->env) {
308                 slabhash_delete(ctx->env->msg_cache);
309                 rrset_cache_delete(ctx->env->rrset_cache);
310                 infra_delete(ctx->env->infra_cache);
311                 config_delete(ctx->env->cfg);
312                 edns_known_options_delete(ctx->env);
313                 inplace_cb_lists_delete(ctx->env);
314                 free(ctx->env);
315         }
316         ub_randfree(ctx->seed_rnd);
317         alloc_clear(&ctx->superalloc);
318         traverse_postorder(&ctx->queries, delq, NULL);
319         free(ctx);
320 #ifdef USE_WINSOCK
321         WSACleanup();
322 #endif
323 }
324
325 int 
326 ub_ctx_set_option(struct ub_ctx* ctx, const char* opt, const char* val)
327 {
328         lock_basic_lock(&ctx->cfglock);
329         if(ctx->finalized) {
330                 lock_basic_unlock(&ctx->cfglock);
331                 return UB_AFTERFINAL;
332         }
333         if(!config_set_option(ctx->env->cfg, opt, val)) {
334                 lock_basic_unlock(&ctx->cfglock);
335                 return UB_SYNTAX;
336         }
337         lock_basic_unlock(&ctx->cfglock);
338         return UB_NOERROR;
339 }
340
341 int
342 ub_ctx_get_option(struct ub_ctx* ctx, const char* opt, char** str)
343 {
344         int r;
345         lock_basic_lock(&ctx->cfglock);
346         r = config_get_option_collate(ctx->env->cfg, opt, str);
347         lock_basic_unlock(&ctx->cfglock);
348         if(r == 0) r = UB_NOERROR;
349         else if(r == 1) r = UB_SYNTAX;
350         else if(r == 2) r = UB_NOMEM;
351         return r;
352 }
353
354 int 
355 ub_ctx_config(struct ub_ctx* ctx, const char* fname)
356 {
357         lock_basic_lock(&ctx->cfglock);
358         if(ctx->finalized) {
359                 lock_basic_unlock(&ctx->cfglock);
360                 return UB_AFTERFINAL;
361         }
362         if(!config_read(ctx->env->cfg, fname, NULL)) {
363                 lock_basic_unlock(&ctx->cfglock);
364                 return UB_SYNTAX;
365         }
366         lock_basic_unlock(&ctx->cfglock);
367         return UB_NOERROR;
368 }
369
370 int 
371 ub_ctx_add_ta(struct ub_ctx* ctx, const char* ta)
372 {
373         char* dup = strdup(ta);
374         if(!dup) return UB_NOMEM;
375         lock_basic_lock(&ctx->cfglock);
376         if(ctx->finalized) {
377                 lock_basic_unlock(&ctx->cfglock);
378                 free(dup);
379                 return UB_AFTERFINAL;
380         }
381         if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_list, dup)) {
382                 lock_basic_unlock(&ctx->cfglock);
383                 free(dup);
384                 return UB_NOMEM;
385         }
386         lock_basic_unlock(&ctx->cfglock);
387         return UB_NOERROR;
388 }
389
390 int 
391 ub_ctx_add_ta_file(struct ub_ctx* ctx, const char* fname)
392 {
393         char* dup = strdup(fname);
394         if(!dup) return UB_NOMEM;
395         lock_basic_lock(&ctx->cfglock);
396         if(ctx->finalized) {
397                 lock_basic_unlock(&ctx->cfglock);
398                 free(dup);
399                 return UB_AFTERFINAL;
400         }
401         if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_file_list, dup)) {
402                 lock_basic_unlock(&ctx->cfglock);
403                 free(dup);
404                 return UB_NOMEM;
405         }
406         lock_basic_unlock(&ctx->cfglock);
407         return UB_NOERROR;
408 }
409
410 int ub_ctx_add_ta_autr(struct ub_ctx* ctx, const char* fname)
411 {
412         char* dup = strdup(fname);
413         if(!dup) return UB_NOMEM;
414         lock_basic_lock(&ctx->cfglock);
415         if(ctx->finalized) {
416                 lock_basic_unlock(&ctx->cfglock);
417                 free(dup);
418                 return UB_AFTERFINAL;
419         }
420         if(!cfg_strlist_insert(&ctx->env->cfg->auto_trust_anchor_file_list,
421                 dup)) {
422                 lock_basic_unlock(&ctx->cfglock);
423                 free(dup);
424                 return UB_NOMEM;
425         }
426         lock_basic_unlock(&ctx->cfglock);
427         return UB_NOERROR;
428 }
429
430 int 
431 ub_ctx_trustedkeys(struct ub_ctx* ctx, const char* fname)
432 {
433         char* dup = strdup(fname);
434         if(!dup) return UB_NOMEM;
435         lock_basic_lock(&ctx->cfglock);
436         if(ctx->finalized) {
437                 lock_basic_unlock(&ctx->cfglock);
438                 free(dup);
439                 return UB_AFTERFINAL;
440         }
441         if(!cfg_strlist_insert(&ctx->env->cfg->trusted_keys_file_list, dup)) {
442                 lock_basic_unlock(&ctx->cfglock);
443                 free(dup);
444                 return UB_NOMEM;
445         }
446         lock_basic_unlock(&ctx->cfglock);
447         return UB_NOERROR;
448 }
449
450 int
451 ub_ctx_debuglevel(struct ub_ctx* ctx, int d)
452 {
453         lock_basic_lock(&ctx->cfglock);
454         verbosity = d;
455         ctx->env->cfg->verbosity = d;
456         lock_basic_unlock(&ctx->cfglock);
457         return UB_NOERROR;
458 }
459
460 int ub_ctx_debugout(struct ub_ctx* ctx, void* out)
461 {
462         lock_basic_lock(&ctx->cfglock);
463         log_file((FILE*)out);
464         ctx->logfile_override = 1;
465         ctx->log_out = out;
466         lock_basic_unlock(&ctx->cfglock);
467         return UB_NOERROR;
468 }
469
470 int 
471 ub_ctx_async(struct ub_ctx* ctx, int dothread)
472 {
473 #ifdef THREADS_DISABLED
474         if(dothread) /* cannot do threading */
475                 return UB_NOERROR;
476 #endif
477         lock_basic_lock(&ctx->cfglock);
478         if(ctx->finalized) {
479                 lock_basic_unlock(&ctx->cfglock);
480                 return UB_AFTERFINAL;
481         }
482         ctx->dothread = dothread;
483         lock_basic_unlock(&ctx->cfglock);
484         return UB_NOERROR;
485 }
486
487 int 
488 ub_poll(struct ub_ctx* ctx)
489 {
490         /* no need to hold lock while testing for readability. */
491         return tube_poll(ctx->rr_pipe);
492 }
493
494 int 
495 ub_fd(struct ub_ctx* ctx)
496 {
497         return tube_read_fd(ctx->rr_pipe);
498 }
499
500 /** process answer from bg worker */
501 static int
502 process_answer_detail(struct ub_ctx* ctx, uint8_t* msg, uint32_t len,
503         ub_callback_type* cb, void** cbarg, int* err,
504         struct ub_result** res)
505 {
506         struct ctx_query* q;
507         if(context_serial_getcmd(msg, len) != UB_LIBCMD_ANSWER) {
508                 log_err("error: bad data from bg worker %d",
509                         (int)context_serial_getcmd(msg, len));
510                 return 0;
511         }
512
513         lock_basic_lock(&ctx->cfglock);
514         q = context_deserialize_answer(ctx, msg, len, err);
515         if(!q) {
516                 lock_basic_unlock(&ctx->cfglock);
517                 /* probably simply the lookup that failed, i.e.
518                  * response returned before cancel was sent out, so noerror */
519                 return 1;
520         }
521         log_assert(q->async);
522
523         /* grab cb while locked */
524         if(q->cancelled) {
525                 *cb = NULL;
526                 *cbarg = NULL;
527         } else {
528                 *cb = q->cb;
529                 *cbarg = q->cb_arg;
530         }
531         if(*err) {
532                 *res = NULL;
533                 ub_resolve_free(q->res);
534         } else {
535                 /* parse the message, extract rcode, fill result */
536                 sldns_buffer* buf = sldns_buffer_new(q->msg_len);
537                 struct regional* region = regional_create();
538                 *res = q->res;
539                 (*res)->rcode = LDNS_RCODE_SERVFAIL;
540                 if(region && buf) {
541                         sldns_buffer_clear(buf);
542                         sldns_buffer_write(buf, q->msg, q->msg_len);
543                         sldns_buffer_flip(buf);
544                         libworker_enter_result(*res, buf, region,
545                                 q->msg_security);
546                 }
547                 (*res)->answer_packet = q->msg;
548                 (*res)->answer_len = (int)q->msg_len;
549                 q->msg = NULL;
550                 sldns_buffer_free(buf);
551                 regional_destroy(region);
552         }
553         q->res = NULL;
554         /* delete the q from list */
555         (void)rbtree_delete(&ctx->queries, q->node.key);
556         ctx->num_async--;
557         context_query_delete(q);
558         lock_basic_unlock(&ctx->cfglock);
559
560         if(*cb) return 2;
561         ub_resolve_free(*res);
562         return 1;
563 }
564
565 /** process answer from bg worker */
566 static int
567 process_answer(struct ub_ctx* ctx, uint8_t* msg, uint32_t len)
568 {
569         int err;
570         ub_callback_type cb;
571         void* cbarg;
572         struct ub_result* res;
573         int r;
574
575         r = process_answer_detail(ctx, msg, len, &cb, &cbarg, &err, &res);
576
577         /* no locks held while calling callback, so that library is
578          * re-entrant. */
579         if(r == 2)
580                 (*cb)(cbarg, err, res);
581
582         return r;
583 }
584
585 int 
586 ub_process(struct ub_ctx* ctx)
587 {
588         int r;
589         uint8_t* msg;
590         uint32_t len;
591         while(1) {
592                 msg = NULL;
593                 lock_basic_lock(&ctx->rrpipe_lock);
594                 r = tube_read_msg(ctx->rr_pipe, &msg, &len, 1);
595                 lock_basic_unlock(&ctx->rrpipe_lock);
596                 if(r == 0)
597                         return UB_PIPE;
598                 else if(r == -1)
599                         break;
600                 if(!process_answer(ctx, msg, len)) {
601                         free(msg);
602                         return UB_PIPE;
603                 }
604                 free(msg);
605         }
606         return UB_NOERROR;
607 }
608
609 int 
610 ub_wait(struct ub_ctx* ctx)
611 {
612         int err;
613         ub_callback_type cb;
614         void* cbarg;
615         struct ub_result* res;
616         int r;
617         uint8_t* msg;
618         uint32_t len;
619         /* this is basically the same loop as _process(), but with changes.
620          * holds the rrpipe lock and waits with tube_wait */
621         while(1) {
622                 lock_basic_lock(&ctx->rrpipe_lock);
623                 lock_basic_lock(&ctx->cfglock);
624                 if(ctx->num_async == 0) {
625                         lock_basic_unlock(&ctx->cfglock);
626                         lock_basic_unlock(&ctx->rrpipe_lock);
627                         break;
628                 }
629                 lock_basic_unlock(&ctx->cfglock);
630
631                 /* keep rrpipe locked, while
632                  *      o waiting for pipe readable
633                  *      o parsing message
634                  *      o possibly decrementing num_async
635                  * do callback without lock
636                  */
637                 r = tube_wait(ctx->rr_pipe);
638                 if(r) {
639                         r = tube_read_msg(ctx->rr_pipe, &msg, &len, 1);
640                         if(r == 0) {
641                                 lock_basic_unlock(&ctx->rrpipe_lock);
642                                 return UB_PIPE;
643                         }
644                         if(r == -1) {
645                                 lock_basic_unlock(&ctx->rrpipe_lock);
646                                 continue;
647                         }
648                         r = process_answer_detail(ctx, msg, len, 
649                                 &cb, &cbarg, &err, &res);
650                         lock_basic_unlock(&ctx->rrpipe_lock);
651                         free(msg);
652                         if(r == 0)
653                                 return UB_PIPE;
654                         if(r == 2)
655                                 (*cb)(cbarg, err, res);
656                 } else {
657                         lock_basic_unlock(&ctx->rrpipe_lock);
658                 }
659         }
660         return UB_NOERROR;
661 }
662
663 int 
664 ub_resolve(struct ub_ctx* ctx, const char* name, int rrtype, 
665         int rrclass, struct ub_result** result)
666 {
667         struct ctx_query* q;
668         int r;
669         *result = NULL;
670
671         lock_basic_lock(&ctx->cfglock);
672         if(!ctx->finalized) {
673                 r = context_finalize(ctx);
674                 if(r) {
675                         lock_basic_unlock(&ctx->cfglock);
676                         return r;
677                 }
678         }
679         /* create new ctx_query and attempt to add to the list */
680         lock_basic_unlock(&ctx->cfglock);
681         q = context_new(ctx, name, rrtype, rrclass, NULL, NULL);
682         if(!q)
683                 return UB_NOMEM;
684         /* become a resolver thread for a bit */
685
686         r = libworker_fg(ctx, q);
687         if(r) {
688                 lock_basic_lock(&ctx->cfglock);
689                 (void)rbtree_delete(&ctx->queries, q->node.key);
690                 context_query_delete(q);
691                 lock_basic_unlock(&ctx->cfglock);
692                 return r;
693         }
694         q->res->answer_packet = q->msg;
695         q->res->answer_len = (int)q->msg_len;
696         q->msg = NULL;
697         *result = q->res;
698         q->res = NULL;
699
700         lock_basic_lock(&ctx->cfglock);
701         (void)rbtree_delete(&ctx->queries, q->node.key);
702         context_query_delete(q);
703         lock_basic_unlock(&ctx->cfglock);
704         return UB_NOERROR;
705 }
706
707 int 
708 ub_resolve_event(struct ub_ctx* ctx, const char* name, int rrtype, 
709         int rrclass, void* mydata, ub_event_callback_type callback,
710         int* async_id)
711 {
712         struct ctx_query* q;
713         int r;
714
715         if(async_id)
716                 *async_id = 0;
717         lock_basic_lock(&ctx->cfglock);
718         if(!ctx->finalized) {
719                 int r = context_finalize(ctx);
720                 if(r) {
721                         lock_basic_unlock(&ctx->cfglock);
722                         return r;
723                 }
724         }
725         lock_basic_unlock(&ctx->cfglock);
726         if(!ctx->event_worker) {
727                 ctx->event_worker = libworker_create_event(ctx,
728                         ctx->event_base);
729                 if(!ctx->event_worker) {
730                         return UB_INITFAIL;
731                 }
732         }
733
734         /* set time in case answer comes from cache */
735         ub_comm_base_now(ctx->event_worker->base);
736
737         /* create new ctx_query and attempt to add to the list */
738         q = context_new(ctx, name, rrtype, rrclass, (ub_callback_type)callback,
739                 mydata);
740         if(!q)
741                 return UB_NOMEM;
742
743         /* attach to mesh */
744         if((r=libworker_attach_mesh(ctx, q, async_id)) != 0)
745                 return r;
746         return UB_NOERROR;
747 }
748
749
750 int 
751 ub_resolve_async(struct ub_ctx* ctx, const char* name, int rrtype, 
752         int rrclass, void* mydata, ub_callback_type callback, int* async_id)
753 {
754         struct ctx_query* q;
755         uint8_t* msg = NULL;
756         uint32_t len = 0;
757
758         if(async_id)
759                 *async_id = 0;
760         lock_basic_lock(&ctx->cfglock);
761         if(!ctx->finalized) {
762                 int r = context_finalize(ctx);
763                 if(r) {
764                         lock_basic_unlock(&ctx->cfglock);
765                         return r;
766                 }
767         }
768         if(!ctx->created_bg) {
769                 int r;
770                 ctx->created_bg = 1;
771                 lock_basic_unlock(&ctx->cfglock);
772                 r = libworker_bg(ctx);
773                 if(r) {
774                         lock_basic_lock(&ctx->cfglock);
775                         ctx->created_bg = 0;
776                         lock_basic_unlock(&ctx->cfglock);
777                         return r;
778                 }
779         } else {
780                 lock_basic_unlock(&ctx->cfglock);
781         }
782
783         /* create new ctx_query and attempt to add to the list */
784         q = context_new(ctx, name, rrtype, rrclass, callback, mydata);
785         if(!q)
786                 return UB_NOMEM;
787
788         /* write over pipe to background worker */
789         lock_basic_lock(&ctx->cfglock);
790         msg = context_serialize_new_query(q, &len);
791         if(!msg) {
792                 (void)rbtree_delete(&ctx->queries, q->node.key);
793                 ctx->num_async--;
794                 context_query_delete(q);
795                 lock_basic_unlock(&ctx->cfglock);
796                 return UB_NOMEM;
797         }
798         if(async_id)
799                 *async_id = q->querynum;
800         lock_basic_unlock(&ctx->cfglock);
801         
802         lock_basic_lock(&ctx->qqpipe_lock);
803         if(!tube_write_msg(ctx->qq_pipe, msg, len, 0)) {
804                 lock_basic_unlock(&ctx->qqpipe_lock);
805                 free(msg);
806                 return UB_PIPE;
807         }
808         lock_basic_unlock(&ctx->qqpipe_lock);
809         free(msg);
810         return UB_NOERROR;
811 }
812
813 int 
814 ub_cancel(struct ub_ctx* ctx, int async_id)
815 {
816         struct ctx_query* q;
817         uint8_t* msg = NULL;
818         uint32_t len = 0;
819         lock_basic_lock(&ctx->cfglock);
820         q = (struct ctx_query*)rbtree_search(&ctx->queries, &async_id);
821         if(!q || !q->async) {
822                 /* it is not there, so nothing to do */
823                 lock_basic_unlock(&ctx->cfglock);
824                 return UB_NOID;
825         }
826         log_assert(q->async);
827         q->cancelled = 1;
828         
829         /* delete it */
830         if(!ctx->dothread) { /* if forked */
831                 (void)rbtree_delete(&ctx->queries, q->node.key);
832                 ctx->num_async--;
833                 msg = context_serialize_cancel(q, &len);
834                 context_query_delete(q);
835                 lock_basic_unlock(&ctx->cfglock);
836                 if(!msg) {
837                         return UB_NOMEM;
838                 }
839                 /* send cancel to background worker */
840                 lock_basic_lock(&ctx->qqpipe_lock);
841                 if(!tube_write_msg(ctx->qq_pipe, msg, len, 0)) {
842                         lock_basic_unlock(&ctx->qqpipe_lock);
843                         free(msg);
844                         return UB_PIPE;
845                 }
846                 lock_basic_unlock(&ctx->qqpipe_lock);
847                 free(msg);
848         } else {
849                 lock_basic_unlock(&ctx->cfglock);
850         }
851         return UB_NOERROR;
852 }
853
854 void 
855 ub_resolve_free(struct ub_result* result)
856 {
857         char** p;
858         if(!result) return;
859         free(result->qname);
860         if(result->canonname != result->qname)
861                 free(result->canonname);
862         if(result->data)
863                 for(p = result->data; *p; p++)
864                         free(*p);
865         free(result->data);
866         free(result->len);
867         free(result->answer_packet);
868         free(result->why_bogus);
869         free(result);
870 }
871
872 const char* 
873 ub_strerror(int err)
874 {
875         switch(err) {
876                 case UB_NOERROR: return "no error";
877                 case UB_SOCKET: return "socket io error";
878                 case UB_NOMEM: return "out of memory";
879                 case UB_SYNTAX: return "syntax error";
880                 case UB_SERVFAIL: return "server failure";
881                 case UB_FORKFAIL: return "could not fork";
882                 case UB_INITFAIL: return "initialization failure";
883                 case UB_AFTERFINAL: return "setting change after finalize";
884                 case UB_PIPE: return "error in pipe communication with async";
885                 case UB_READFILE: return "error reading file";
886                 case UB_NOID: return "error async_id does not exist";
887                 default: return "unknown error";
888         }
889 }
890
891 int 
892 ub_ctx_set_fwd(struct ub_ctx* ctx, const char* addr)
893 {
894         struct sockaddr_storage storage;
895         socklen_t stlen;
896         struct config_stub* s;
897         char* dupl;
898         lock_basic_lock(&ctx->cfglock);
899         if(ctx->finalized) {
900                 lock_basic_unlock(&ctx->cfglock);
901                 errno=EINVAL;
902                 return UB_AFTERFINAL;
903         }
904         if(!addr) {
905                 /* disable fwd mode - the root stub should be first. */
906                 if(ctx->env->cfg->forwards &&
907                         strcmp(ctx->env->cfg->forwards->name, ".") == 0) {
908                         s = ctx->env->cfg->forwards;
909                         ctx->env->cfg->forwards = s->next;
910                         s->next = NULL;
911                         config_delstubs(s);
912                 }
913                 lock_basic_unlock(&ctx->cfglock);
914                 return UB_NOERROR;
915         }
916         lock_basic_unlock(&ctx->cfglock);
917
918         /* check syntax for addr */
919         if(!extstrtoaddr(addr, &storage, &stlen)) {
920                 errno=EINVAL;
921                 return UB_SYNTAX;
922         }
923         
924         /* it parses, add root stub in front of list */
925         lock_basic_lock(&ctx->cfglock);
926         if(!ctx->env->cfg->forwards ||
927                 strcmp(ctx->env->cfg->forwards->name, ".") != 0) {
928                 s = calloc(1, sizeof(*s));
929                 if(!s) {
930                         lock_basic_unlock(&ctx->cfglock);
931                         errno=ENOMEM;
932                         return UB_NOMEM;
933                 }
934                 s->name = strdup(".");
935                 if(!s->name) {
936                         free(s);
937                         lock_basic_unlock(&ctx->cfglock);
938                         errno=ENOMEM;
939                         return UB_NOMEM;
940                 }
941                 s->next = ctx->env->cfg->forwards;
942                 ctx->env->cfg->forwards = s;
943         } else {
944                 log_assert(ctx->env->cfg->forwards);
945                 s = ctx->env->cfg->forwards;
946         }
947         dupl = strdup(addr);
948         if(!dupl) {
949                 lock_basic_unlock(&ctx->cfglock);
950                 errno=ENOMEM;
951                 return UB_NOMEM;
952         }
953         if(!cfg_strlist_insert(&s->addrs, dupl)) {
954                 free(dupl);
955                 lock_basic_unlock(&ctx->cfglock);
956                 errno=ENOMEM;
957                 return UB_NOMEM;
958         }
959         lock_basic_unlock(&ctx->cfglock);
960         return UB_NOERROR;
961 }
962
963 int ub_ctx_set_stub(struct ub_ctx* ctx, const char* zone, const char* addr,
964         int isprime)
965 {
966         char* a;
967         struct config_stub **prev, *elem;
968
969         /* check syntax for zone name */
970         if(zone) {
971                 uint8_t* nm;
972                 int nmlabs;
973                 size_t nmlen;
974                 if(!parse_dname(zone, &nm, &nmlen, &nmlabs)) {
975                         errno=EINVAL;
976                         return UB_SYNTAX;
977                 }
978                 free(nm);
979         } else {
980                 zone = ".";
981         }
982
983         /* check syntax for addr (if not NULL) */
984         if(addr) {
985                 struct sockaddr_storage storage;
986                 socklen_t stlen;
987                 if(!extstrtoaddr(addr, &storage, &stlen)) {
988                         errno=EINVAL;
989                         return UB_SYNTAX;
990                 }
991         }
992
993         lock_basic_lock(&ctx->cfglock);
994         if(ctx->finalized) {
995                 lock_basic_unlock(&ctx->cfglock);
996                 errno=EINVAL;
997                 return UB_AFTERFINAL;
998         }
999
1000         /* arguments all right, now find or add the stub */
1001         prev = &ctx->env->cfg->stubs;
1002         elem = cfg_stub_find(&prev, zone);
1003         if(!elem && !addr) {
1004                 /* not found and we want to delete, nothing to do */
1005                 lock_basic_unlock(&ctx->cfglock);
1006                 return UB_NOERROR;
1007         } else if(elem && !addr) {
1008                 /* found, and we want to delete */
1009                 *prev = elem->next;
1010                 config_delstub(elem);
1011                 lock_basic_unlock(&ctx->cfglock);
1012                 return UB_NOERROR;
1013         } else if(!elem) {
1014                 /* not found, create the stub entry */
1015                 elem=(struct config_stub*)calloc(1, sizeof(struct config_stub));
1016                 if(elem) elem->name = strdup(zone);
1017                 if(!elem || !elem->name) {
1018                         free(elem);
1019                         lock_basic_unlock(&ctx->cfglock);
1020                         errno = ENOMEM;
1021                         return UB_NOMEM;
1022                 }
1023                 elem->next = ctx->env->cfg->stubs;
1024                 ctx->env->cfg->stubs = elem;
1025         }
1026
1027         /* add the address to the list and set settings */
1028         elem->isprime = isprime;
1029         a = strdup(addr);
1030         if(!a) {
1031                 lock_basic_unlock(&ctx->cfglock);
1032                 errno = ENOMEM;
1033                 return UB_NOMEM;
1034         }
1035         if(!cfg_strlist_insert(&elem->addrs, a)) {
1036                 lock_basic_unlock(&ctx->cfglock);
1037                 free(a);
1038                 errno = ENOMEM;
1039                 return UB_NOMEM;
1040         }
1041         lock_basic_unlock(&ctx->cfglock);
1042         return UB_NOERROR;
1043 }
1044
1045 int 
1046 ub_ctx_resolvconf(struct ub_ctx* ctx, const char* fname)
1047 {
1048         FILE* in;
1049         int numserv = 0;
1050         char buf[1024];
1051         char* parse, *addr;
1052         int r;
1053
1054         if(fname == NULL) {
1055 #if !defined(UB_ON_WINDOWS) || !defined(HAVE_WINDOWS_H)
1056                 fname = "/etc/resolv.conf";
1057 #else
1058                 FIXED_INFO *info;
1059                 ULONG buflen = sizeof(*info);
1060                 IP_ADDR_STRING *ptr;
1061
1062                 info = (FIXED_INFO *) malloc(sizeof (FIXED_INFO));
1063                 if (info == NULL) 
1064                         return UB_READFILE;
1065
1066                 if (GetNetworkParams(info, &buflen) == ERROR_BUFFER_OVERFLOW) {
1067                         free(info);
1068                         info = (FIXED_INFO *) malloc(buflen);
1069                         if (info == NULL)
1070                                 return UB_READFILE;
1071                 }
1072
1073                 if (GetNetworkParams(info, &buflen) == NO_ERROR) {
1074                         int retval=0;
1075                         ptr = &(info->DnsServerList);
1076                         while (ptr) {
1077                                 numserv++;
1078                                 if((retval=ub_ctx_set_fwd(ctx, 
1079                                         ptr->IpAddress.String))!=0) {
1080                                         free(info);
1081                                         return retval;
1082                                 }
1083                                 ptr = ptr->Next;
1084                         }
1085                         free(info);
1086                         if (numserv==0)
1087                                 return UB_READFILE;
1088                         return UB_NOERROR;
1089                 }
1090                 free(info);
1091                 return UB_READFILE;
1092 #endif /* WINDOWS */
1093         }
1094         in = fopen(fname, "r");
1095         if(!in) {
1096                 /* error in errno! perror(fname) */
1097                 return UB_READFILE;
1098         }
1099         while(fgets(buf, (int)sizeof(buf), in)) {
1100                 buf[sizeof(buf)-1] = 0;
1101                 parse=buf;
1102                 while(*parse == ' ' || *parse == '\t')
1103                         parse++;
1104                 if(strncmp(parse, "nameserver", 10) == 0) {
1105                         numserv++;
1106                         parse += 10; /* skip 'nameserver' */
1107                         /* skip whitespace */
1108                         while(*parse == ' ' || *parse == '\t')
1109                                 parse++;
1110                         addr = parse;
1111                         /* skip [0-9a-fA-F.:]*, i.e. IP4 and IP6 address */
1112                         while(isxdigit((unsigned char)*parse) || *parse=='.' || *parse==':')
1113                                 parse++;
1114                         /* terminate after the address, remove newline */
1115                         *parse = 0;
1116                         
1117                         if((r = ub_ctx_set_fwd(ctx, addr)) != UB_NOERROR) {
1118                                 fclose(in);
1119                                 return r;
1120                         }
1121                 }
1122         }
1123         fclose(in);
1124         if(numserv == 0) {
1125                 /* from resolv.conf(5) if none given, use localhost */
1126                 return ub_ctx_set_fwd(ctx, "127.0.0.1");
1127         }
1128         return UB_NOERROR;
1129 }
1130
1131 int
1132 ub_ctx_hosts(struct ub_ctx* ctx, const char* fname)
1133 {
1134         FILE* in;
1135         char buf[1024], ldata[1024];
1136         char* parse, *addr, *name, *ins;
1137         lock_basic_lock(&ctx->cfglock);
1138         if(ctx->finalized) {
1139                 lock_basic_unlock(&ctx->cfglock);
1140                 errno=EINVAL;
1141                 return UB_AFTERFINAL;
1142         }
1143         lock_basic_unlock(&ctx->cfglock);
1144         if(fname == NULL) {
1145 #if defined(UB_ON_WINDOWS) && defined(HAVE_WINDOWS_H)
1146                 /*
1147                  * If this is Windows NT/XP/2K it's in
1148                  * %WINDIR%\system32\drivers\etc\hosts.
1149                  * If this is Windows 95/98/Me it's in %WINDIR%\hosts.
1150                  */
1151                 name = getenv("WINDIR");
1152                 if (name != NULL) {
1153                         int retval=0;
1154                         snprintf(buf, sizeof(buf), "%s%s", name, 
1155                                 "\\system32\\drivers\\etc\\hosts");
1156                         if((retval=ub_ctx_hosts(ctx, buf)) !=0 ) {
1157                                 snprintf(buf, sizeof(buf), "%s%s", name, 
1158                                         "\\hosts");
1159                                 retval=ub_ctx_hosts(ctx, buf);
1160                         }
1161                         return retval;
1162                 }
1163                 return UB_READFILE;
1164 #else
1165                 fname = "/etc/hosts";
1166 #endif /* WIN32 */
1167         }
1168         in = fopen(fname, "r");
1169         if(!in) {
1170                 /* error in errno! perror(fname) */
1171                 return UB_READFILE;
1172         }
1173         while(fgets(buf, (int)sizeof(buf), in)) {
1174                 buf[sizeof(buf)-1] = 0;
1175                 parse=buf;
1176                 while(*parse == ' ' || *parse == '\t')
1177                         parse++;
1178                 if(*parse == '#')
1179                         continue; /* skip comment */
1180                 /* format: <addr> spaces <name> spaces <name> ... */
1181                 addr = parse;
1182                 /* skip addr */
1183                 while(isxdigit((unsigned char)*parse) || *parse == '.' || *parse == ':')
1184                         parse++;
1185                 if(*parse == '\r')
1186                         parse++;
1187                 if(*parse == '\n' || *parse == 0)
1188                         continue;
1189                 if(*parse == '%') 
1190                         continue; /* ignore macOSX fe80::1%lo0 localhost */
1191                 if(*parse != ' ' && *parse != '\t') {
1192                         /* must have whitespace after address */
1193                         fclose(in);
1194                         errno=EINVAL;
1195                         return UB_SYNTAX;
1196                 }
1197                 *parse++ = 0; /* end delimiter for addr ... */
1198                 /* go to names and add them */
1199                 while(*parse) {
1200                         while(*parse == ' ' || *parse == '\t' || *parse=='\n'
1201                                 || *parse=='\r')
1202                                 parse++;
1203                         if(*parse == 0 || *parse == '#')
1204                                 break;
1205                         /* skip name, allows (too) many printable characters */
1206                         name = parse;
1207                         while('!' <= *parse && *parse <= '~')
1208                                 parse++;
1209                         if(*parse)
1210                                 *parse++ = 0; /* end delimiter for name */
1211                         snprintf(ldata, sizeof(ldata), "%s %s %s",
1212                                 name, str_is_ip6(addr)?"AAAA":"A", addr);
1213                         ins = strdup(ldata);
1214                         if(!ins) {
1215                                 /* out of memory */
1216                                 fclose(in);
1217                                 errno=ENOMEM;
1218                                 return UB_NOMEM;
1219                         }
1220                         lock_basic_lock(&ctx->cfglock);
1221                         if(!cfg_strlist_insert(&ctx->env->cfg->local_data, 
1222                                 ins)) {
1223                                 lock_basic_unlock(&ctx->cfglock);
1224                                 fclose(in);
1225                                 free(ins);
1226                                 errno=ENOMEM;
1227                                 return UB_NOMEM;
1228                         }
1229                         lock_basic_unlock(&ctx->cfglock);
1230                 }
1231         }
1232         fclose(in);
1233         return UB_NOERROR;
1234 }
1235
1236 /** finalize the context, if not already finalized */
1237 static int ub_ctx_finalize(struct ub_ctx* ctx)
1238 {
1239         int res = 0;
1240         lock_basic_lock(&ctx->cfglock);
1241         if (!ctx->finalized) {
1242                 res = context_finalize(ctx);
1243         }
1244         lock_basic_unlock(&ctx->cfglock);
1245         return res;
1246 }
1247
1248 /* Print local zones and RR data */
1249 int ub_ctx_print_local_zones(struct ub_ctx* ctx)
1250 {   
1251         int res = ub_ctx_finalize(ctx);
1252         if (res) return res;
1253
1254         local_zones_print(ctx->local_zones);
1255
1256         return UB_NOERROR;
1257 }
1258
1259 /* Add a new zone */
1260 int ub_ctx_zone_add(struct ub_ctx* ctx, const char *zone_name, 
1261         const char *zone_type)
1262 {
1263         enum localzone_type t;
1264         struct local_zone* z;
1265         uint8_t* nm;
1266         int nmlabs;
1267         size_t nmlen;
1268
1269         int res = ub_ctx_finalize(ctx);
1270         if (res) return res;
1271
1272         if(!local_zone_str2type(zone_type, &t)) {
1273                 return UB_SYNTAX;
1274         }
1275
1276         if(!parse_dname(zone_name, &nm, &nmlen, &nmlabs)) {
1277                 return UB_SYNTAX;
1278         }
1279
1280         lock_rw_wrlock(&ctx->local_zones->lock);
1281         if((z=local_zones_find(ctx->local_zones, nm, nmlen, nmlabs, 
1282                 LDNS_RR_CLASS_IN))) {
1283                 /* already present in tree */
1284                 lock_rw_wrlock(&z->lock);
1285                 z->type = t; /* update type anyway */
1286                 lock_rw_unlock(&z->lock);
1287                 lock_rw_unlock(&ctx->local_zones->lock);
1288                 free(nm);
1289                 return UB_NOERROR;
1290         }
1291         if(!local_zones_add_zone(ctx->local_zones, nm, nmlen, nmlabs, 
1292                 LDNS_RR_CLASS_IN, t)) {
1293                 lock_rw_unlock(&ctx->local_zones->lock);
1294                 return UB_NOMEM;
1295         }
1296         lock_rw_unlock(&ctx->local_zones->lock);
1297         return UB_NOERROR;
1298 }
1299
1300 /* Remove zone */
1301 int ub_ctx_zone_remove(struct ub_ctx* ctx, const char *zone_name)
1302 {   
1303         struct local_zone* z;
1304         uint8_t* nm;
1305         int nmlabs;
1306         size_t nmlen;
1307
1308         int res = ub_ctx_finalize(ctx);
1309         if (res) return res;
1310
1311         if(!parse_dname(zone_name, &nm, &nmlen, &nmlabs)) {
1312                 return UB_SYNTAX;
1313         }
1314
1315         lock_rw_wrlock(&ctx->local_zones->lock);
1316         if((z=local_zones_find(ctx->local_zones, nm, nmlen, nmlabs, 
1317                 LDNS_RR_CLASS_IN))) {
1318                 /* present in tree */
1319                 local_zones_del_zone(ctx->local_zones, z);
1320         }
1321         lock_rw_unlock(&ctx->local_zones->lock);
1322         free(nm);
1323         return UB_NOERROR;
1324 }
1325
1326 /* Add new RR data */
1327 int ub_ctx_data_add(struct ub_ctx* ctx, const char *data)
1328 {
1329         int res = ub_ctx_finalize(ctx);
1330         if (res) return res;
1331
1332         res = local_zones_add_RR(ctx->local_zones, data);
1333         return (!res) ? UB_NOMEM : UB_NOERROR;
1334 }
1335
1336 /* Remove RR data */
1337 int ub_ctx_data_remove(struct ub_ctx* ctx, const char *data)
1338 {
1339         uint8_t* nm;
1340         int nmlabs;
1341         size_t nmlen;
1342         int res = ub_ctx_finalize(ctx);
1343         if (res) return res;
1344
1345         if(!parse_dname(data, &nm, &nmlen, &nmlabs)) 
1346                 return UB_SYNTAX;
1347
1348         local_zones_del_data(ctx->local_zones, nm, nmlen, nmlabs, 
1349                 LDNS_RR_CLASS_IN);
1350
1351         free(nm);
1352         return UB_NOERROR;
1353 }
1354
1355 const char* ub_version(void)
1356 {
1357         return PACKAGE_VERSION;
1358 }
1359
1360 int 
1361 ub_ctx_set_event(struct ub_ctx* ctx, struct event_base* base) {
1362         struct ub_event_base* new_base;
1363
1364         if (!ctx || !ctx->event_base || !base) {
1365                 return UB_INITFAIL;
1366         }
1367         if (ub_libevent_get_event_base(ctx->event_base) == base) {
1368                 /* already set */
1369                 return UB_NOERROR;
1370         }
1371         
1372         lock_basic_lock(&ctx->cfglock);
1373         /* destroy the current worker - safe to pass in NULL */
1374         libworker_delete_event(ctx->event_worker);
1375         ctx->event_worker = NULL;
1376         new_base = ub_libevent_event_base(base);
1377         if (new_base)
1378                 ctx->event_base = new_base;     
1379         ctx->created_bg = 0;
1380         ctx->dothread = 1;
1381         lock_basic_unlock(&ctx->cfglock);
1382         return new_base ? UB_NOERROR : UB_INITFAIL;
1383 }