1 /* $OpenBSD: asr.c,v 1.51 2016/02/24 20:52:53 eric Exp $ */ 2 /* 3 * Copyright (c) 2010-2012 Eric Faurot <eric@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 #include <sys/socket.h> 20 #include <sys/stat.h> 21 #include <netinet/in.h> 22 #include <arpa/inet.h> 23 #include <arpa/nameser.h> 24 #include <netdb.h> 25 26 #include <asr.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <resolv.h> 30 #include <poll.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 #include <limits.h> 36 37 #include "asr_private.h" 38 39 #include "thread_private.h" 40 41 #define DEFAULT_CONF "lookup file\n" 42 #define DEFAULT_LOOKUP "lookup bind file" 43 44 #define RELOAD_DELAY 15 /* seconds */ 45 46 static void asr_check_reload(struct asr *); 47 static struct asr_ctx *asr_ctx_create(void); 48 static void asr_ctx_ref(struct asr_ctx *); 49 static void asr_ctx_free(struct asr_ctx *); 50 static int asr_ctx_add_searchdomain(struct asr_ctx *, const char *); 51 static int asr_ctx_from_file(struct asr_ctx *, const char *); 52 static int asr_ctx_from_string(struct asr_ctx *, const char *); 53 static int asr_ctx_parse(struct asr_ctx *, const char *); 54 static int asr_parse_nameserver(struct sockaddr *, const char *); 55 static int asr_ndots(const char *); 56 static void pass0(char **, int, struct asr_ctx *); 57 static int strsplit(char *, char **, int); 58 static void asr_ctx_envopts(struct asr_ctx *); 59 static void *__THREAD_NAME(_asr); 60 61 static struct asr *_asr = NULL; 62 63 /* Allocate and configure an async "resolver". */ 64 static void * 65 _asr_resolver(void) 66 { 67 static int init = 0; 68 struct asr *asr; 69 70 if (init == 0) { 71 #ifdef DEBUG 72 if (getenv("ASR_DEBUG")) 73 _asr_debug = stderr; 74 #endif 75 init = 1; 76 } 77 78 if ((asr = calloc(1, sizeof(*asr))) == NULL) 79 goto fail; 80 81 asr_check_reload(asr); 82 if (asr->a_ctx == NULL) { 83 if ((asr->a_ctx = asr_ctx_create()) == NULL) 84 goto fail; 85 if (asr_ctx_from_string(asr->a_ctx, DEFAULT_CONF) == -1) 86 goto fail; 87 asr_ctx_envopts(asr->a_ctx); 88 } 89 90 #ifdef DEBUG 91 _asr_dump_config(_asr_debug, asr); 92 #endif 93 return (asr); 94 95 fail: 96 if (asr) { 97 if (asr->a_ctx) 98 asr_ctx_free(asr->a_ctx); 99 free(asr); 100 } 101 102 return (NULL); 103 } 104 105 /* 106 * Free the "asr" async resolver (or the thread-local resolver if NULL). 107 * Drop the reference to the current context. 108 */ 109 void 110 _asr_resolver_done(void *arg) 111 { 112 struct asr *asr = arg; 113 struct asr **priv; 114 115 if (asr == NULL) { 116 priv = _THREAD_PRIVATE(_asr, _asr, &_asr); 117 if (*priv == NULL) 118 return; 119 asr = *priv; 120 *priv = NULL; 121 } 122 123 _asr_ctx_unref(asr->a_ctx); 124 free(asr); 125 } 126 127 /* 128 * Cancel an async query. 129 */ 130 void 131 asr_abort(struct asr_query *as) 132 { 133 _asr_async_free(as); 134 } 135 136 /* 137 * Resume the "as" async query resolution. Return one of ASYNC_COND, 138 * or ASYNC_DONE and put query-specific return values in the user-allocated 139 * memory at "ar". 140 */ 141 int 142 asr_run(struct asr_query *as, struct asr_result *ar) 143 { 144 int r, saved_errno = errno; 145 146 DPRINT("asr: asr_run(%p, %p) %s ctx=[%p]\n", as, ar, 147 _asr_querystr(as->as_type), as->as_ctx); 148 r = as->as_run(as, ar); 149 150 DPRINT("asr: asr_run(%p, %p) -> %s", as, ar, _asr_transitionstr(r)); 151 #ifdef DEBUG 152 if (r == ASYNC_COND) 153 #endif 154 DPRINT(" fd=%i timeout=%i", ar->ar_fd, ar->ar_timeout); 155 DPRINT("\n"); 156 if (r == ASYNC_DONE) 157 _asr_async_free(as); 158 159 errno = saved_errno; 160 161 return (r); 162 } 163 DEF_WEAK(asr_run); 164 165 /* 166 * Same as above, but run in a loop that handles the fd conditions result. 167 */ 168 int 169 asr_run_sync(struct asr_query *as, struct asr_result *ar) 170 { 171 struct pollfd fds[1]; 172 int r, saved_errno = errno; 173 174 while ((r = asr_run(as, ar)) == ASYNC_COND) { 175 fds[0].fd = ar->ar_fd; 176 fds[0].events = (ar->ar_cond == ASR_WANT_READ) ? POLLIN:POLLOUT; 177 again: 178 r = poll(fds, 1, ar->ar_timeout); 179 if (r == -1 && errno == EINTR) 180 goto again; 181 /* 182 * Otherwise, just ignore the error and let asr_run() 183 * catch the failure. 184 */ 185 } 186 187 errno = saved_errno; 188 189 return (r); 190 } 191 DEF_WEAK(asr_run_sync); 192 193 /* 194 * Create a new async request of the given "type" on the async context "ac". 195 * Take a reference on it so it does not gets deleted while the async query 196 * is running. 197 */ 198 struct asr_query * 199 _asr_async_new(struct asr_ctx *ac, int type) 200 { 201 struct asr_query *as; 202 203 DPRINT("asr: asr_async_new(ctx=%p) type=%i refcount=%i\n", ac, type, 204 ac ? ac->ac_refcount : 0); 205 if (ac == NULL || (as = calloc(1, sizeof(*as))) == NULL) 206 return (NULL); 207 208 ac->ac_refcount += 1; 209 as->as_ctx = ac; 210 as->as_fd = -1; 211 as->as_type = type; 212 as->as_state = ASR_STATE_INIT; 213 214 return (as); 215 } 216 217 /* 218 * Free an async query and unref the associated context. 219 */ 220 void 221 _asr_async_free(struct asr_query *as) 222 { 223 DPRINT("asr: asr_async_free(%p)\n", as); 224 switch (as->as_type) { 225 case ASR_SEND: 226 if (as->as_fd != -1) 227 close(as->as_fd); 228 if (as->as.dns.obuf && !(as->as.dns.flags & ASYNC_EXTOBUF)) 229 free(as->as.dns.obuf); 230 if (as->as.dns.ibuf) 231 free(as->as.dns.ibuf); 232 if (as->as.dns.dname) 233 free(as->as.dns.dname); 234 break; 235 236 case ASR_SEARCH: 237 if (as->as.search.subq) 238 _asr_async_free(as->as.search.subq); 239 if (as->as.search.name) 240 free(as->as.search.name); 241 break; 242 243 case ASR_GETRRSETBYNAME: 244 if (as->as.rrset.subq) 245 _asr_async_free(as->as.rrset.subq); 246 if (as->as.rrset.name) 247 free(as->as.rrset.name); 248 break; 249 250 case ASR_GETHOSTBYNAME: 251 case ASR_GETHOSTBYADDR: 252 if (as->as.hostnamadr.subq) 253 _asr_async_free(as->as.hostnamadr.subq); 254 if (as->as.hostnamadr.name) 255 free(as->as.hostnamadr.name); 256 break; 257 258 case ASR_GETNETBYNAME: 259 case ASR_GETNETBYADDR: 260 if (as->as.netnamadr.subq) 261 _asr_async_free(as->as.netnamadr.subq); 262 if (as->as.netnamadr.name) 263 free(as->as.netnamadr.name); 264 break; 265 266 case ASR_GETADDRINFO: 267 if (as->as.ai.subq) 268 _asr_async_free(as->as.ai.subq); 269 if (as->as.ai.aifirst) 270 freeaddrinfo(as->as.ai.aifirst); 271 if (as->as.ai.hostname) 272 free(as->as.ai.hostname); 273 if (as->as.ai.servname) 274 free(as->as.ai.servname); 275 if (as->as.ai.fqdn) 276 free(as->as.ai.fqdn); 277 break; 278 279 case ASR_GETNAMEINFO: 280 if (as->as.ni.subq) 281 _asr_async_free(as->as.ni.subq); 282 break; 283 } 284 285 _asr_ctx_unref(as->as_ctx); 286 free(as); 287 } 288 289 /* 290 * Get a context from the given resolver. This takes a new reference to 291 * the returned context, which *must* be explicitely dropped when done 292 * using this context. 293 */ 294 struct asr_ctx * 295 _asr_use_resolver(void *arg) 296 { 297 struct asr *asr = arg; 298 struct asr **priv; 299 300 if (asr == NULL) { 301 DPRINT("using thread-local resolver\n"); 302 priv = _THREAD_PRIVATE(_asr, _asr, &_asr); 303 if (*priv == NULL) { 304 DPRINT("setting up thread-local resolver\n"); 305 *priv = _asr_resolver(); 306 } 307 asr = *priv; 308 } 309 if (asr != NULL) { 310 asr_check_reload(asr); 311 asr_ctx_ref(asr->a_ctx); 312 return (asr->a_ctx); 313 } 314 return (NULL); 315 } 316 317 static void 318 asr_ctx_ref(struct asr_ctx *ac) 319 { 320 DPRINT("asr: asr_ctx_ref(ctx=%p) refcount=%i\n", ac, ac->ac_refcount); 321 ac->ac_refcount += 1; 322 } 323 324 /* 325 * Drop a reference to an async context, freeing it if the reference 326 * count drops to 0. 327 */ 328 void 329 _asr_ctx_unref(struct asr_ctx *ac) 330 { 331 DPRINT("asr: asr_ctx_unref(ctx=%p) refcount=%i\n", ac, 332 ac ? ac->ac_refcount : 0); 333 if (ac == NULL) 334 return; 335 if (--ac->ac_refcount) 336 return; 337 338 asr_ctx_free(ac); 339 } 340 341 static void 342 asr_ctx_free(struct asr_ctx *ac) 343 { 344 int i; 345 346 if (ac->ac_domain) 347 free(ac->ac_domain); 348 for (i = 0; i < ASR_MAXNS; i++) 349 free(ac->ac_ns[i]); 350 for (i = 0; i < ASR_MAXDOM; i++) 351 free(ac->ac_dom[i]); 352 353 free(ac); 354 } 355 356 /* 357 * Reload the configuration file if it has changed on disk. 358 */ 359 static void 360 asr_check_reload(struct asr *asr) 361 { 362 struct asr_ctx *ac; 363 struct stat st; 364 struct timespec ts; 365 pid_t pid; 366 367 pid = getpid(); 368 if (pid != asr->a_pid) { 369 asr->a_pid = pid; 370 asr->a_rtime = 0; 371 } 372 373 if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) 374 return; 375 376 if ((ts.tv_sec - asr->a_rtime) < RELOAD_DELAY && asr->a_rtime != 0) 377 return; 378 asr->a_rtime = ts.tv_sec; 379 380 DPRINT("asr: checking for update of \"%s\"\n", _PATH_RESCONF); 381 if (stat(_PATH_RESCONF, &st) == -1 || 382 asr->a_mtime == st.st_mtime || 383 (ac = asr_ctx_create()) == NULL) 384 return; 385 asr->a_mtime = st.st_mtime; 386 387 DPRINT("asr: reloading config file\n"); 388 if (asr_ctx_from_file(ac, _PATH_RESCONF) == -1) { 389 asr_ctx_free(ac); 390 return; 391 } 392 393 asr_ctx_envopts(ac); 394 if (asr->a_ctx) 395 _asr_ctx_unref(asr->a_ctx); 396 asr->a_ctx = ac; 397 } 398 399 /* 400 * Construct a fully-qualified domain name for the given name and domain. 401 * If "name" ends with a '.' it is considered as a FQDN by itself. 402 * Otherwise, the domain, which must be a FQDN, is appended to "name" (it 403 * may have a leading dot which would be ignored). If the domain is null, 404 * then "." is used. Return the length of the constructed FQDN or (0) on 405 * error. 406 */ 407 size_t 408 _asr_make_fqdn(const char *name, const char *domain, char *buf, size_t buflen) 409 { 410 size_t len; 411 412 if (domain == NULL) 413 domain = "."; 414 else if ((len = strlen(domain)) == 0) 415 return (0); 416 else if (domain[len -1] != '.') 417 return (0); 418 419 len = strlen(name); 420 if (len == 0) { 421 if (strlcpy(buf, domain, buflen) >= buflen) 422 return (0); 423 } else if (name[len - 1] != '.') { 424 if (domain[0] == '.') 425 domain += 1; 426 if (strlcpy(buf, name, buflen) >= buflen || 427 strlcat(buf, ".", buflen) >= buflen || 428 strlcat(buf, domain, buflen) >= buflen) 429 return (0); 430 } else { 431 if (strlcpy(buf, name, buflen) >= buflen) 432 return (0); 433 } 434 435 return (strlen(buf)); 436 } 437 438 /* 439 * Count the dots in a string. 440 */ 441 static int 442 asr_ndots(const char *s) 443 { 444 int n; 445 446 for (n = 0; *s; s++) 447 if (*s == '.') 448 n += 1; 449 450 return (n); 451 } 452 453 /* 454 * Allocate a new empty context. 455 */ 456 static struct asr_ctx * 457 asr_ctx_create(void) 458 { 459 struct asr_ctx *ac; 460 461 if ((ac = calloc(1, sizeof(*ac))) == NULL) 462 return (NULL); 463 464 ac->ac_options = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH; 465 ac->ac_refcount = 1; 466 ac->ac_ndots = 1; 467 ac->ac_family[0] = AF_INET; 468 ac->ac_family[1] = AF_INET6; 469 ac->ac_family[2] = -1; 470 471 ac->ac_nscount = 0; 472 ac->ac_nstimeout = 5; 473 ac->ac_nsretries = 4; 474 475 return (ac); 476 } 477 478 struct asr_ctx * 479 _asr_no_resolver(void) 480 { 481 return asr_ctx_create(); 482 } 483 484 /* 485 * Add a search domain to the async context. 486 */ 487 static int 488 asr_ctx_add_searchdomain(struct asr_ctx *ac, const char *domain) 489 { 490 char buf[MAXDNAME]; 491 492 if (ac->ac_domcount == ASR_MAXDOM) 493 return (-1); 494 495 if (_asr_make_fqdn(domain, NULL, buf, sizeof(buf)) == 0) 496 return (-1); 497 498 if ((ac->ac_dom[ac->ac_domcount] = strdup(buf)) == NULL) 499 return (0); 500 501 ac->ac_domcount += 1; 502 503 return (1); 504 } 505 506 static int 507 strsplit(char *line, char **tokens, int ntokens) 508 { 509 int ntok; 510 char *cp, **tp; 511 512 for (cp = line, tp = tokens, ntok = 0; 513 ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; ) 514 if (**tp != '\0') { 515 tp++; 516 ntok++; 517 } 518 519 return (ntok); 520 } 521 522 /* 523 * Pass on a split config line. 524 */ 525 static void 526 pass0(char **tok, int n, struct asr_ctx *ac) 527 { 528 int i, j, d; 529 const char *e; 530 struct sockaddr_storage ss; 531 532 if (!strcmp(tok[0], "nameserver")) { 533 if (ac->ac_nscount == ASR_MAXNS) 534 return; 535 if (n != 2) 536 return; 537 if (asr_parse_nameserver((struct sockaddr *)&ss, tok[1])) 538 return; 539 if ((ac->ac_ns[ac->ac_nscount] = calloc(1, ss.ss_len)) == NULL) 540 return; 541 memmove(ac->ac_ns[ac->ac_nscount], &ss, ss.ss_len); 542 ac->ac_nscount += 1; 543 544 } else if (!strcmp(tok[0], "domain")) { 545 if (n != 2) 546 return; 547 if (ac->ac_domain) 548 return; 549 ac->ac_domain = strdup(tok[1]); 550 551 } else if (!strcmp(tok[0], "lookup")) { 552 /* ensure that each lookup is only given once */ 553 for (i = 1; i < n; i++) 554 for (j = i + 1; j < n; j++) 555 if (!strcmp(tok[i], tok[j])) 556 return; 557 ac->ac_dbcount = 0; 558 for (i = 1; i < n && ac->ac_dbcount < ASR_MAXDB; i++) { 559 if (!strcmp(tok[i], "yp")) { 560 /* silently deprecated */ 561 } else if (!strcmp(tok[i], "bind")) 562 ac->ac_db[ac->ac_dbcount++] = ASR_DB_DNS; 563 else if (!strcmp(tok[i], "file")) 564 ac->ac_db[ac->ac_dbcount++] = ASR_DB_FILE; 565 } 566 } else if (!strcmp(tok[0], "search")) { 567 /* resolv.conf says the last line wins */ 568 for (i = 0; i < ASR_MAXDOM; i++) { 569 free(ac->ac_dom[i]); 570 ac->ac_dom[i] = NULL; 571 } 572 ac->ac_domcount = 0; 573 for (i = 1; i < n; i++) 574 asr_ctx_add_searchdomain(ac, tok[i]); 575 576 } else if (!strcmp(tok[0], "family")) { 577 if (n == 1 || n > 3) 578 return; 579 for (i = 1; i < n; i++) 580 if (strcmp(tok[i], "inet4") && strcmp(tok[i], "inet6")) 581 return; 582 for (i = 1; i < n; i++) 583 ac->ac_family[i - 1] = strcmp(tok[i], "inet4") ? \ 584 AF_INET6 : AF_INET; 585 ac->ac_family[i - 1] = -1; 586 587 } else if (!strcmp(tok[0], "options")) { 588 for (i = 1; i < n; i++) { 589 if (!strcmp(tok[i], "tcp")) 590 ac->ac_options |= RES_USEVC; 591 else if ((!strncmp(tok[i], "ndots:", 6))) { 592 e = NULL; 593 d = strtonum(tok[i] + 6, 1, 16, &e); 594 if (e == NULL) 595 ac->ac_ndots = d; 596 } 597 } 598 } 599 } 600 601 /* 602 * Setup an async context with the config specified in the string "str". 603 */ 604 static int 605 asr_ctx_from_string(struct asr_ctx *ac, const char *str) 606 { 607 char buf[512], *ch; 608 609 asr_ctx_parse(ac, str); 610 611 if (ac->ac_dbcount == 0) { 612 /* No lookup directive */ 613 asr_ctx_parse(ac, DEFAULT_LOOKUP); 614 } 615 616 if (ac->ac_nscount == 0) 617 asr_ctx_parse(ac, "nameserver 127.0.0.1"); 618 619 if (ac->ac_domain == NULL) 620 if (gethostname(buf, sizeof buf) == 0) { 621 ch = strchr(buf, '.'); 622 if (ch) 623 ac->ac_domain = strdup(ch + 1); 624 else /* Assume root. see resolv.conf(5) */ 625 ac->ac_domain = strdup(""); 626 } 627 628 /* If no search domain was specified, use the local subdomains */ 629 if (ac->ac_domcount == 0) 630 for (ch = ac->ac_domain; ch; ) { 631 asr_ctx_add_searchdomain(ac, ch); 632 ch = strchr(ch, '.'); 633 if (ch && asr_ndots(++ch) == 0) 634 break; 635 } 636 637 return (0); 638 } 639 640 /* 641 * Setup the "ac" async context from the file at location "path". 642 */ 643 static int 644 asr_ctx_from_file(struct asr_ctx *ac, const char *path) 645 { 646 FILE *cf; 647 char buf[4096]; 648 ssize_t r; 649 650 cf = fopen(path, "re"); 651 if (cf == NULL) 652 return (-1); 653 654 r = fread(buf, 1, sizeof buf - 1, cf); 655 if (feof(cf) == 0) { 656 DPRINT("asr: config file too long: \"%s\"\n", path); 657 r = -1; 658 } 659 fclose(cf); 660 if (r == -1) 661 return (-1); 662 buf[r] = '\0'; 663 664 return asr_ctx_from_string(ac, buf); 665 } 666 667 /* 668 * Parse lines in the configuration string. For each one, split it into 669 * tokens and pass them to "pass0" for processing. 670 */ 671 static int 672 asr_ctx_parse(struct asr_ctx *ac, const char *str) 673 { 674 size_t len; 675 const char *line; 676 char buf[1024]; 677 char *tok[10]; 678 int ntok; 679 680 line = str; 681 while (*line) { 682 len = strcspn(line, "\n\0"); 683 if (len < sizeof buf) { 684 memmove(buf, line, len); 685 buf[len] = '\0'; 686 } else 687 buf[0] = '\0'; 688 line += len; 689 if (*line == '\n') 690 line++; 691 buf[strcspn(buf, ";#")] = '\0'; 692 if ((ntok = strsplit(buf, tok, 10)) == 0) 693 continue; 694 695 pass0(tok, ntok, ac); 696 } 697 698 return (0); 699 } 700 701 /* 702 * Check for environment variables altering the configuration as described 703 * in resolv.conf(5). Altough not documented there, this feature is disabled 704 * for setuid/setgid programs. 705 */ 706 static void 707 asr_ctx_envopts(struct asr_ctx *ac) 708 { 709 char buf[4096], *e; 710 size_t s; 711 712 if (issetugid()) { 713 ac->ac_options |= RES_NOALIASES; 714 return; 715 } 716 717 if ((e = getenv("RES_OPTIONS")) != NULL) { 718 strlcpy(buf, "options ", sizeof buf); 719 strlcat(buf, e, sizeof buf); 720 s = strlcat(buf, "\n", sizeof buf); 721 s = strlcat(buf, "\n", sizeof buf); 722 if (s < sizeof buf) 723 asr_ctx_parse(ac, buf); 724 } 725 726 if ((e = getenv("LOCALDOMAIN")) != NULL) { 727 strlcpy(buf, "search ", sizeof buf); 728 strlcat(buf, e, sizeof buf); 729 s = strlcat(buf, "\n", sizeof buf); 730 if (s < sizeof buf) 731 asr_ctx_parse(ac, buf); 732 } 733 } 734 735 /* 736 * Parse a resolv.conf(5) nameserver string into a sockaddr. 737 */ 738 static int 739 asr_parse_nameserver(struct sockaddr *sa, const char *s) 740 { 741 in_port_t portno = 53; 742 743 if (_asr_sockaddr_from_str(sa, PF_UNSPEC, s) == -1) 744 return (-1); 745 746 if (sa->sa_family == PF_INET) 747 ((struct sockaddr_in *)sa)->sin_port = htons(portno); 748 else if (sa->sa_family == PF_INET6) 749 ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno); 750 751 return (0); 752 } 753 754 /* 755 * Turn a (uncompressed) DNS domain name into a regular nul-terminated string 756 * where labels are separated by dots. The result is put into the "buf" buffer, 757 * truncated if it exceeds "max" chars. The function returns "buf". 758 */ 759 char * 760 _asr_strdname(const char *_dname, char *buf, size_t max) 761 { 762 const unsigned char *dname = _dname; 763 char *res; 764 size_t left, n, count; 765 766 if (_dname[0] == 0) { 767 strlcpy(buf, ".", max); 768 return buf; 769 } 770 771 res = buf; 772 left = max - 1; 773 for (n = 0; dname[0] && left; n += dname[0]) { 774 count = (dname[0] < (left - 1)) ? dname[0] : (left - 1); 775 memmove(buf, dname + 1, count); 776 dname += dname[0] + 1; 777 left -= count; 778 buf += count; 779 if (left) { 780 left -= 1; 781 *buf++ = '.'; 782 } 783 } 784 buf[0] = 0; 785 786 return (res); 787 } 788 789 /* 790 * Read and split the next line from the given namedb file. 791 * Return -1 on error, or put the result in the "tokens" array of 792 * size "ntoken" and returns the number of token on the line. 793 */ 794 int 795 _asr_parse_namedb_line(FILE *file, char **tokens, int ntoken, char *lbuf, size_t sz) 796 { 797 size_t len; 798 char *buf; 799 int ntok; 800 801 again: 802 if ((buf = fgetln(file, &len)) == NULL) 803 return (-1); 804 805 if (len >= sz) 806 goto again; 807 808 if (buf[len - 1] == '\n') 809 len--; 810 else { 811 memcpy(lbuf, buf, len); 812 buf = lbuf; 813 } 814 815 buf[len] = '\0'; 816 buf[strcspn(buf, "#")] = '\0'; 817 if ((ntok = strsplit(buf, tokens, ntoken)) == 0) 818 goto again; 819 820 return (ntok); 821 } 822 823 /* 824 * Update the async context so that it uses the next configured DB. 825 * Return 0 on success, or -1 if no more DBs is available. 826 */ 827 int 828 _asr_iter_db(struct asr_query *as) 829 { 830 if (as->as_db_idx >= as->as_ctx->ac_dbcount) { 831 DPRINT("asr_iter_db: done\n"); 832 return (-1); 833 } 834 835 as->as_db_idx += 1; 836 DPRINT("asr_iter_db: %i\n", as->as_db_idx); 837 838 return (0); 839 } 840