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