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