1 /* $OpenBSD: parse.y,v 1.22 2019/12/08 09:47:50 florian Exp $ */ 2 3 /* 4 * Copyright (c) 2018 Florian Obser <florian@openbsd.org> 5 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 6 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> 7 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 8 * Copyright (c) 2001 Markus Friedl. All rights reserved. 9 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 10 * Copyright (c) 2001 Theo de Raadt. All rights reserved. 11 * 12 * Permission to use, copy, modify, and distribute this software for any 13 * purpose with or without fee is hereby granted, provided that the above 14 * copyright notice and this permission notice appear in all copies. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 17 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 19 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 21 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 22 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 23 */ 24 25 %{ 26 #include <sys/queue.h> 27 #include <sys/socket.h> 28 #include <sys/stat.h> 29 #include <sys/types.h> 30 31 #include <ctype.h> 32 #include <err.h> 33 #include <errno.h> 34 #include <limits.h> 35 #include <netdb.h> 36 #include <stdarg.h> 37 #include <stdint.h> 38 #include <stdio.h> 39 #include <syslog.h> 40 #include <unistd.h> 41 42 #include "log.h" 43 #include "unwind.h" 44 45 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 46 static struct file { 47 TAILQ_ENTRY(file) entry; 48 FILE *stream; 49 char *name; 50 size_t ungetpos; 51 size_t ungetsize; 52 u_char *ungetbuf; 53 int eof_reached; 54 int lineno; 55 int errors; 56 } *file, *topfile; 57 struct file *pushfile(const char *, int); 58 int popfile(void); 59 int check_file_secrecy(int, const char *); 60 int yyparse(void); 61 int yylex(void); 62 int yyerror(const char *, ...) 63 __attribute__((__format__ (printf, 1, 2))) 64 __attribute__((__nonnull__ (1))); 65 int kw_cmp(const void *, const void *); 66 int lookup(char *); 67 int igetc(void); 68 int lgetc(int); 69 void lungetc(int); 70 int findeol(void); 71 72 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 73 struct sym { 74 TAILQ_ENTRY(sym) entry; 75 int used; 76 int persist; 77 char *nam; 78 char *val; 79 }; 80 81 int symset(const char *, const char *, int); 82 char *symget(const char *); 83 int check_pref_uniq(enum uw_resolver_type); 84 85 static struct uw_conf *conf; 86 static int errors; 87 88 void clear_config(struct uw_conf *xconf); 89 struct sockaddr_storage *host_ip(const char *); 90 91 typedef struct { 92 union { 93 int64_t number; 94 char *string; 95 struct force_tree force; 96 } v; 97 int lineno; 98 } YYSTYPE; 99 100 %} 101 102 %token INCLUDE ERROR 103 %token FORWARDER DOT PORT ODOT_FORWARDER ODOT_DHCP 104 %token AUTHENTICATION NAME PREFERENCE RECURSOR DHCP STUB 105 %token BLOCK LIST LOG FORCE ACCEPT BOGUS 106 107 %token <v.string> STRING 108 %token <v.number> NUMBER 109 %type <v.number> port dot prefopt log acceptbogus 110 %type <v.string> string authname 111 %type <v.force> force_list 112 113 %% 114 115 grammar : /* empty */ 116 | grammar include '\n' 117 | grammar '\n' 118 | grammar varset '\n' 119 | grammar uw_pref '\n' 120 | grammar uw_forwarder '\n' 121 | grammar block_list '\n' 122 | grammar force '\n' 123 | grammar error '\n' { file->errors++; } 124 ; 125 126 include : INCLUDE STRING { 127 struct file *nfile; 128 129 if ((nfile = pushfile($2, 0)) == NULL) { 130 yyerror("failed to include file %s", $2); 131 free($2); 132 YYERROR; 133 } 134 free($2); 135 136 file = nfile; 137 lungetc('\n'); 138 } 139 ; 140 141 string : string STRING { 142 if (asprintf(&$$, "%s %s", $1, $2) == -1) { 143 free($1); 144 free($2); 145 yyerror("string: asprintf"); 146 YYERROR; 147 } 148 free($1); 149 free($2); 150 } 151 | STRING 152 ; 153 154 varset : STRING '=' string { 155 char *s = $1; 156 if (cmd_opts & OPT_VERBOSE) 157 printf("%s = \"%s\"\n", $1, $3); 158 while (*s++) { 159 if (isspace((unsigned char)*s)) { 160 yyerror("macro name cannot contain " 161 "whitespace"); 162 free($1); 163 free($3); 164 YYERROR; 165 } 166 } 167 if (symset($1, $3, 0) == -1) 168 fatal("cannot store variable"); 169 free($1); 170 free($3); 171 } 172 ; 173 174 175 optnl : '\n' optnl /* zero or more newlines */ 176 | /*empty*/ 177 ; 178 179 block_list : BLOCK LIST STRING log { 180 if (conf->blocklist_file != NULL) { 181 yyerror("block list already " 182 "configured"); 183 free($3); 184 YYERROR; 185 } else { 186 conf->blocklist_file = strdup($3); 187 if (conf->blocklist_file == NULL) 188 err(1, "strdup"); 189 free($3); 190 conf->blocklist_log = $4; 191 } 192 } 193 ; 194 195 uw_pref : PREFERENCE { conf->res_pref.len = 0; } pref_block 196 ; 197 198 pref_block : '{' optnl prefopts_l '}' 199 | prefoptsl 200 ; 201 202 prefopts_l : prefopts_l prefoptsl optnl 203 | prefoptsl optnl 204 ; 205 206 prefoptsl : prefopt { 207 if (!check_pref_uniq($1)) 208 YYERROR; 209 if (conf->res_pref.len >= UW_RES_NONE) { 210 yyerror("preference list too long"); 211 YYERROR; 212 } 213 conf->res_pref.types[conf->res_pref.len++] = $1; 214 } 215 ; 216 217 prefopt : DOT { $$ = UW_RES_DOT; } 218 | FORWARDER { $$ = UW_RES_FORWARDER; } 219 | ODOT_FORWARDER { $$ = UW_RES_ODOT_FORWARDER; } 220 | RECURSOR { $$ = UW_RES_RECURSOR; } 221 | DHCP { $$ = UW_RES_DHCP; } 222 | ODOT_DHCP { $$ = UW_RES_ODOT_DHCP; } 223 | STUB { $$ = UW_RES_ASR; } 224 ; 225 226 uw_forwarder : FORWARDER forwarder_block 227 ; 228 229 forwarder_block : '{' optnl forwarderopts_l '}' 230 | forwarderoptsl 231 ; 232 233 forwarderopts_l : forwarderopts_l forwarderoptsl optnl 234 | forwarderoptsl optnl 235 ; 236 237 forwarderoptsl : STRING port authname dot { 238 struct uw_forwarder *uw_fwd; 239 struct sockaddr_storage *ss; 240 241 if ((ss = host_ip($1)) == NULL) { 242 yyerror("%s is not an ip-address", $1); 243 free($1); 244 YYERROR; 245 } 246 free(ss); 247 248 if ((uw_fwd = calloc(1, sizeof(*uw_fwd))) == 249 NULL) 250 err(1, NULL); 251 252 if ($2 < 0 || $2 > (int)USHRT_MAX) { 253 yyerror("invalid port: %lld", $2); 254 free($1); 255 free(uw_fwd); 256 YYERROR; 257 } 258 if ($2 == 0) 259 uw_fwd->port = $4 == DOT ? 853 : 53; 260 else 261 uw_fwd->port = $2; 262 263 if ($3 != NULL && $4 == 0) { 264 yyerror("authentication name can only " 265 "be used with DoT"); 266 free($1); 267 free(uw_fwd); 268 YYERROR; 269 } 270 271 if (strlcpy(uw_fwd->ip, $1, sizeof(uw_fwd->ip)) 272 >= sizeof(uw_fwd->ip)) { 273 free(uw_fwd); 274 yyerror("forwarder %s too long", $1); 275 free($1); 276 YYERROR; 277 } 278 279 if ($4 == DOT && $3 != NULL) { 280 if (strlcpy(uw_fwd->auth_name, $3, 281 sizeof(uw_fwd->auth_name)) 282 >= sizeof(uw_fwd->auth_name)) { 283 free(uw_fwd); 284 yyerror("authentication name " 285 "%s too long", $3); 286 free($1); 287 YYERROR; 288 } 289 } 290 291 if ($4 == DOT) 292 TAILQ_INSERT_TAIL( 293 &conf->uw_dot_forwarder_list, 294 uw_fwd, entry); 295 else { 296 TAILQ_INSERT_TAIL( 297 &conf->uw_forwarder_list, 298 uw_fwd, entry); 299 } 300 free($1); 301 } 302 ; 303 304 port : PORT NUMBER { $$ = $2; } 305 | /* empty */ { $$ = 0; } 306 ; 307 308 authname: AUTHENTICATION NAME STRING { $$ = $3; } 309 | /* empty */ { $$ = NULL; } 310 ; 311 312 dot : DOT { $$ = DOT; } 313 | /* empty */ { $$ = 0; } 314 ; 315 316 log : LOG { $$ = 1; } 317 | /* empty */ { $$ = 0; } 318 ; 319 320 force : FORCE acceptbogus prefopt '{' force_list optnl '}' { 321 struct force_tree_entry *n, *nxt; 322 int error = 0; 323 324 for (n = RB_MIN(force_tree, &$5); n != NULL; 325 n = nxt) { 326 nxt = RB_NEXT(force_tree, &conf->force, n); 327 n->acceptbogus = $2; 328 n->type = $3; 329 RB_REMOVE(force_tree, &$5, n); 330 if (RB_INSERT(force_tree, &conf->force, 331 n)) { 332 yyerror("%s already in an force " 333 "list", n->domain); 334 error = 1; 335 } 336 } 337 if (error) 338 YYERROR; 339 } 340 ; 341 342 acceptbogus: ACCEPT BOGUS { $$ = 1; } 343 | /* empty */ { $$ = 0; } 344 ; 345 346 force_list: force_list optnl STRING { 347 struct force_tree_entry *e; 348 size_t len; 349 350 len = strlen($3); 351 e = malloc(sizeof(*e)); 352 if (e == NULL) 353 err(1, NULL); 354 if (strlcpy(e->domain, $3, sizeof(e->domain)) >= 355 sizeof(e->domain)) { 356 yyerror("force %s too long", $3); 357 free($3); 358 YYERROR; 359 } 360 free($3); 361 if (len == 0 || e->domain[len-1] != '.') { 362 if (strlcat(e->domain, ".", 363 sizeof((e->domain))) >= 364 sizeof((e->domain))) { 365 yyerror("force %s too long", $3); 366 YYERROR; 367 } 368 } 369 RB_INSERT(force_tree, &$$, e); 370 } 371 | /* empty */ { 372 RB_INIT(&$$); 373 } 374 ; 375 376 %% 377 378 struct keywords { 379 const char *k_name; 380 int k_val; 381 }; 382 383 int 384 yyerror(const char *fmt, ...) 385 { 386 va_list ap; 387 char *msg; 388 389 file->errors++; 390 va_start(ap, fmt); 391 if (vasprintf(&msg, fmt, ap) == -1) 392 fatalx("yyerror vasprintf"); 393 va_end(ap); 394 logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg); 395 free(msg); 396 return (0); 397 } 398 399 int 400 kw_cmp(const void *k, const void *e) 401 { 402 return (strcmp(k, ((const struct keywords *)e)->k_name)); 403 } 404 405 int 406 lookup(char *s) 407 { 408 /* This has to be sorted always. */ 409 static const struct keywords keywords[] = { 410 {"DoT", DOT}, 411 {"accept", ACCEPT}, 412 {"authentication", AUTHENTICATION}, 413 {"block", BLOCK}, 414 {"bogus", BOGUS}, 415 {"dhcp", DHCP}, 416 {"dot", DOT}, 417 {"force", FORCE}, 418 {"forwarder", FORWARDER}, 419 {"include", INCLUDE}, 420 {"list", LIST}, 421 {"log", LOG}, 422 {"name", NAME}, 423 {"oDoT-dhcp", ODOT_DHCP}, 424 {"oDoT-forwarder", ODOT_FORWARDER}, 425 {"port", PORT}, 426 {"preference", PREFERENCE}, 427 {"recursor", RECURSOR}, 428 {"stub", STUB}, 429 {"tls", DOT}, 430 }; 431 const struct keywords *p; 432 433 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 434 sizeof(keywords[0]), kw_cmp); 435 436 if (p) 437 return (p->k_val); 438 else 439 return (STRING); 440 } 441 442 #define START_EXPAND 1 443 #define DONE_EXPAND 2 444 445 static int expanding; 446 447 int 448 igetc(void) 449 { 450 int c; 451 452 while (1) { 453 if (file->ungetpos > 0) 454 c = file->ungetbuf[--file->ungetpos]; 455 else 456 c = getc(file->stream); 457 458 if (c == START_EXPAND) 459 expanding = 1; 460 else if (c == DONE_EXPAND) 461 expanding = 0; 462 else 463 break; 464 } 465 return (c); 466 } 467 468 int 469 lgetc(int quotec) 470 { 471 int c, next; 472 473 if (quotec) { 474 if ((c = igetc()) == EOF) { 475 yyerror("reached end of file while parsing " 476 "quoted string"); 477 if (file == topfile || popfile() == EOF) 478 return (EOF); 479 return (quotec); 480 } 481 return (c); 482 } 483 484 while ((c = igetc()) == '\\') { 485 next = igetc(); 486 if (next != '\n') { 487 c = next; 488 break; 489 } 490 yylval.lineno = file->lineno; 491 file->lineno++; 492 } 493 494 if (c == EOF) { 495 /* 496 * Fake EOL when hit EOF for the first time. This gets line 497 * count right if last line in included file is syntactically 498 * invalid and has no newline. 499 */ 500 if (file->eof_reached == 0) { 501 file->eof_reached = 1; 502 return ('\n'); 503 } 504 while (c == EOF) { 505 if (file == topfile || popfile() == EOF) 506 return (EOF); 507 c = igetc(); 508 } 509 } 510 return (c); 511 } 512 513 void 514 lungetc(int c) 515 { 516 if (c == EOF) 517 return; 518 519 if (file->ungetpos >= file->ungetsize) { 520 void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); 521 if (p == NULL) 522 err(1, "lungetc"); 523 file->ungetbuf = p; 524 file->ungetsize *= 2; 525 } 526 file->ungetbuf[file->ungetpos++] = c; 527 } 528 529 int 530 findeol(void) 531 { 532 int c; 533 534 /* Skip to either EOF or the first real EOL. */ 535 while (1) { 536 c = lgetc(0); 537 if (c == '\n') { 538 file->lineno++; 539 break; 540 } 541 if (c == EOF) 542 break; 543 } 544 return (ERROR); 545 } 546 547 int 548 yylex(void) 549 { 550 unsigned char buf[8096]; 551 unsigned char *p, *val; 552 int quotec, next, c; 553 int token; 554 555 top: 556 p = buf; 557 while ((c = lgetc(0)) == ' ' || c == '\t') 558 ; /* nothing */ 559 560 yylval.lineno = file->lineno; 561 if (c == '#') 562 while ((c = lgetc(0)) != '\n' && c != EOF) 563 ; /* nothing */ 564 if (c == '$' && !expanding) { 565 while (1) { 566 if ((c = lgetc(0)) == EOF) 567 return (0); 568 569 if (p + 1 >= buf + sizeof(buf) - 1) { 570 yyerror("string too long"); 571 return (findeol()); 572 } 573 if (isalnum(c) || c == '_') { 574 *p++ = c; 575 continue; 576 } 577 *p = '\0'; 578 lungetc(c); 579 break; 580 } 581 val = symget(buf); 582 if (val == NULL) { 583 yyerror("macro '%s' not defined", buf); 584 return (findeol()); 585 } 586 p = val + strlen(val) - 1; 587 lungetc(DONE_EXPAND); 588 while (p >= val) { 589 lungetc(*p); 590 p--; 591 } 592 lungetc(START_EXPAND); 593 goto top; 594 } 595 596 switch (c) { 597 case '\'': 598 case '"': 599 quotec = c; 600 while (1) { 601 if ((c = lgetc(quotec)) == EOF) 602 return (0); 603 if (c == '\n') { 604 file->lineno++; 605 continue; 606 } else if (c == '\\') { 607 if ((next = lgetc(quotec)) == EOF) 608 return (0); 609 if (next == quotec || next == ' ' || 610 next == '\t') 611 c = next; 612 else if (next == '\n') { 613 file->lineno++; 614 continue; 615 } else 616 lungetc(next); 617 } else if (c == quotec) { 618 *p = '\0'; 619 break; 620 } else if (c == '\0') { 621 yyerror("syntax error"); 622 return (findeol()); 623 } 624 if (p + 1 >= buf + sizeof(buf) - 1) { 625 yyerror("string too long"); 626 return (findeol()); 627 } 628 *p++ = c; 629 } 630 yylval.v.string = strdup(buf); 631 if (yylval.v.string == NULL) 632 err(1, "yylex: strdup"); 633 return (STRING); 634 } 635 636 #define allowed_to_end_number(x) \ 637 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 638 639 if (c == '-' || isdigit(c)) { 640 do { 641 *p++ = c; 642 if ((size_t)(p-buf) >= sizeof(buf)) { 643 yyerror("string too long"); 644 return (findeol()); 645 } 646 } while ((c = lgetc(0)) != EOF && isdigit(c)); 647 lungetc(c); 648 if (p == buf + 1 && buf[0] == '-') 649 goto nodigits; 650 if (c == EOF || allowed_to_end_number(c)) { 651 const char *errstr = NULL; 652 653 *p = '\0'; 654 yylval.v.number = strtonum(buf, LLONG_MIN, 655 LLONG_MAX, &errstr); 656 if (errstr) { 657 yyerror("\"%s\" invalid number: %s", 658 buf, errstr); 659 return (findeol()); 660 } 661 return (NUMBER); 662 } else { 663 nodigits: 664 while (p > buf + 1) 665 lungetc(*--p); 666 c = *--p; 667 if (c == '-') 668 return (c); 669 } 670 } 671 672 #define allowed_in_string(x) \ 673 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 674 x != '{' && x != '}' && \ 675 x != '!' && x != '=' && x != '#' && \ 676 x != ',')) 677 678 if (isalnum(c) || c == ':' || c == '_') { 679 do { 680 *p++ = c; 681 if ((size_t)(p-buf) >= sizeof(buf)) { 682 yyerror("string too long"); 683 return (findeol()); 684 } 685 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 686 lungetc(c); 687 *p = '\0'; 688 if ((token = lookup(buf)) == STRING) 689 if ((yylval.v.string = strdup(buf)) == NULL) 690 err(1, "yylex: strdup"); 691 return (token); 692 } 693 if (c == '\n') { 694 yylval.lineno = file->lineno; 695 file->lineno++; 696 } 697 if (c == EOF) 698 return (0); 699 return (c); 700 } 701 702 int 703 check_file_secrecy(int fd, const char *fname) 704 { 705 struct stat st; 706 707 if (fstat(fd, &st)) { 708 log_warn("cannot stat %s", fname); 709 return (-1); 710 } 711 if (st.st_uid != 0 && st.st_uid != getuid()) { 712 log_warnx("%s: owner not root or current user", fname); 713 return (-1); 714 } 715 if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { 716 log_warnx("%s: group writable or world read/writable", fname); 717 return (-1); 718 } 719 return (0); 720 } 721 722 struct file * 723 pushfile(const char *name, int secret) 724 { 725 struct file *nfile; 726 727 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 728 log_warn("calloc"); 729 return (NULL); 730 } 731 if ((nfile->name = strdup(name)) == NULL) { 732 log_warn("strdup"); 733 free(nfile); 734 return (NULL); 735 } 736 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 737 free(nfile->name); 738 free(nfile); 739 return (NULL); 740 } else if (secret && 741 check_file_secrecy(fileno(nfile->stream), nfile->name)) { 742 fclose(nfile->stream); 743 free(nfile->name); 744 free(nfile); 745 return (NULL); 746 } 747 nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; 748 nfile->ungetsize = 16; 749 nfile->ungetbuf = malloc(nfile->ungetsize); 750 if (nfile->ungetbuf == NULL) { 751 log_warn("malloc"); 752 fclose(nfile->stream); 753 free(nfile->name); 754 free(nfile); 755 return (NULL); 756 } 757 TAILQ_INSERT_TAIL(&files, nfile, entry); 758 return (nfile); 759 } 760 761 int 762 popfile(void) 763 { 764 struct file *prev; 765 766 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 767 prev->errors += file->errors; 768 769 TAILQ_REMOVE(&files, file, entry); 770 fclose(file->stream); 771 free(file->name); 772 free(file->ungetbuf); 773 free(file); 774 file = prev; 775 return (file ? 0 : EOF); 776 } 777 778 struct uw_conf * 779 parse_config(char *filename) 780 { 781 struct sym *sym, *next; 782 783 conf = config_new_empty(); 784 785 file = pushfile(filename != NULL ? filename : CONF_FILE, 0); 786 if (file == NULL) { 787 /* no default config file is fine */ 788 if (errno == ENOENT && filename == NULL) 789 return (conf); 790 log_warn("%s", filename); 791 free(conf); 792 return (NULL); 793 } 794 topfile = file; 795 796 yyparse(); 797 errors = file->errors; 798 popfile(); 799 800 /* Free macros and check which have not been used. */ 801 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { 802 if ((cmd_opts & OPT_VERBOSE2) && !sym->used) 803 fprintf(stderr, "warning: macro '%s' not used\n", 804 sym->nam); 805 if (!sym->persist) { 806 free(sym->nam); 807 free(sym->val); 808 TAILQ_REMOVE(&symhead, sym, entry); 809 free(sym); 810 } 811 } 812 813 if (errors) { 814 clear_config(conf); 815 return (NULL); 816 } 817 818 return (conf); 819 } 820 821 int 822 symset(const char *nam, const char *val, int persist) 823 { 824 struct sym *sym; 825 826 TAILQ_FOREACH(sym, &symhead, entry) { 827 if (strcmp(nam, sym->nam) == 0) 828 break; 829 } 830 831 if (sym != NULL) { 832 if (sym->persist == 1) 833 return (0); 834 else { 835 free(sym->nam); 836 free(sym->val); 837 TAILQ_REMOVE(&symhead, sym, entry); 838 free(sym); 839 } 840 } 841 if ((sym = calloc(1, sizeof(*sym))) == NULL) 842 return (-1); 843 844 sym->nam = strdup(nam); 845 if (sym->nam == NULL) { 846 free(sym); 847 return (-1); 848 } 849 sym->val = strdup(val); 850 if (sym->val == NULL) { 851 free(sym->nam); 852 free(sym); 853 return (-1); 854 } 855 sym->used = 0; 856 sym->persist = persist; 857 TAILQ_INSERT_TAIL(&symhead, sym, entry); 858 return (0); 859 } 860 861 int 862 cmdline_symset(char *s) 863 { 864 char *sym, *val; 865 int ret; 866 867 if ((val = strrchr(s, '=')) == NULL) 868 return (-1); 869 sym = strndup(s, val - s); 870 if (sym == NULL) 871 errx(1, "%s: strndup", __func__); 872 ret = symset(sym, val + 1, 1); 873 free(sym); 874 875 return (ret); 876 } 877 878 char * 879 symget(const char *nam) 880 { 881 struct sym *sym; 882 883 TAILQ_FOREACH(sym, &symhead, entry) { 884 if (strcmp(nam, sym->nam) == 0) { 885 sym->used = 1; 886 return (sym->val); 887 } 888 } 889 return (NULL); 890 } 891 892 void 893 clear_config(struct uw_conf *xconf) 894 { 895 struct uw_forwarder *uw_forwarder; 896 897 while ((uw_forwarder = TAILQ_FIRST(&xconf->uw_forwarder_list)) != 898 NULL) { 899 TAILQ_REMOVE(&xconf->uw_forwarder_list, uw_forwarder, entry); 900 free(uw_forwarder); 901 } 902 while ((uw_forwarder = TAILQ_FIRST(&xconf->uw_dot_forwarder_list)) != 903 NULL) { 904 TAILQ_REMOVE(&xconf->uw_dot_forwarder_list, uw_forwarder, 905 entry); 906 free(uw_forwarder); 907 } 908 909 free(xconf); 910 } 911 912 struct sockaddr_storage * 913 host_ip(const char *s) 914 { 915 struct addrinfo hints, *res; 916 struct sockaddr_storage *ss = NULL; 917 918 memset(&hints, 0, sizeof(hints)); 919 hints.ai_family = AF_UNSPEC; 920 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 921 hints.ai_flags = AI_NUMERICHOST; 922 if (getaddrinfo(s, "0", &hints, &res) == 0) { 923 if (res->ai_family == AF_INET || 924 res->ai_family == AF_INET6) { 925 if ((ss = calloc(1, sizeof(*ss))) == NULL) 926 fatal(NULL); 927 memcpy(ss, res->ai_addr, res->ai_addrlen); 928 } 929 freeaddrinfo(res); 930 } 931 932 return (ss); 933 } 934 935 int 936 check_pref_uniq(enum uw_resolver_type type) 937 { 938 int i; 939 940 for (i = 0; i < conf->res_pref.len; i++) 941 if (conf->res_pref.types[i] == type) { 942 yyerror("%s is already in the preference list", 943 uw_resolver_type_str[type]); 944 return (0); 945 } 946 947 return (1); 948 } 949