1 /* $OpenBSD: parse.y,v 1.24 2020/11/09 04:22:05 tb 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 RB_FOREACH_SAFE(n, force_tree, &$5, nxt) { 325 n->acceptbogus = $2; 326 n->type = $3; 327 RB_REMOVE(force_tree, &$5, n); 328 if (RB_INSERT(force_tree, &conf->force, 329 n)) { 330 yyerror("%s already in an force " 331 "list", n->domain); 332 error = 1; 333 } 334 } 335 if (error) 336 YYERROR; 337 } 338 ; 339 340 acceptbogus: ACCEPT BOGUS { $$ = 1; } 341 | /* empty */ { $$ = 0; } 342 ; 343 344 force_list: force_list optnl STRING { 345 struct force_tree_entry *e; 346 size_t len; 347 348 len = strlen($3); 349 e = malloc(sizeof(*e)); 350 if (e == NULL) 351 err(1, NULL); 352 if (strlcpy(e->domain, $3, sizeof(e->domain)) >= 353 sizeof(e->domain)) { 354 yyerror("force %s too long", $3); 355 free($3); 356 YYERROR; 357 } 358 free($3); 359 if (len == 0 || e->domain[len-1] != '.') { 360 if (strlcat(e->domain, ".", 361 sizeof((e->domain))) >= 362 sizeof((e->domain))) { 363 yyerror("force %s too long", $3); 364 YYERROR; 365 } 366 } 367 if (RB_INSERT(force_tree, &$$, e) != NULL) { 368 log_warnx("duplicate force %s", e->domain); 369 free(e); 370 } 371 } 372 | /* empty */ { 373 RB_INIT(&$$); 374 } 375 ; 376 377 %% 378 379 struct keywords { 380 const char *k_name; 381 int k_val; 382 }; 383 384 int 385 yyerror(const char *fmt, ...) 386 { 387 va_list ap; 388 char *msg; 389 390 file->errors++; 391 va_start(ap, fmt); 392 if (vasprintf(&msg, fmt, ap) == -1) 393 fatalx("yyerror vasprintf"); 394 va_end(ap); 395 logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg); 396 free(msg); 397 return (0); 398 } 399 400 int 401 kw_cmp(const void *k, const void *e) 402 { 403 return (strcmp(k, ((const struct keywords *)e)->k_name)); 404 } 405 406 int 407 lookup(char *s) 408 { 409 /* This has to be sorted always. */ 410 static const struct keywords keywords[] = { 411 {"DoT", DOT}, 412 {"accept", ACCEPT}, 413 {"authentication", AUTHENTICATION}, 414 {"block", BLOCK}, 415 {"bogus", BOGUS}, 416 {"dhcp", DHCP}, 417 {"dot", DOT}, 418 {"force", FORCE}, 419 {"forwarder", FORWARDER}, 420 {"include", INCLUDE}, 421 {"list", LIST}, 422 {"log", LOG}, 423 {"name", NAME}, 424 {"oDoT-dhcp", ODOT_DHCP}, 425 {"oDoT-forwarder", ODOT_FORWARDER}, 426 {"port", PORT}, 427 {"preference", PREFERENCE}, 428 {"recursor", RECURSOR}, 429 {"stub", STUB}, 430 {"tls", DOT}, 431 }; 432 const struct keywords *p; 433 434 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 435 sizeof(keywords[0]), kw_cmp); 436 437 if (p) 438 return (p->k_val); 439 else 440 return (STRING); 441 } 442 443 #define START_EXPAND 1 444 #define DONE_EXPAND 2 445 446 static int expanding; 447 448 int 449 igetc(void) 450 { 451 int c; 452 453 while (1) { 454 if (file->ungetpos > 0) 455 c = file->ungetbuf[--file->ungetpos]; 456 else 457 c = getc(file->stream); 458 459 if (c == START_EXPAND) 460 expanding = 1; 461 else if (c == DONE_EXPAND) 462 expanding = 0; 463 else 464 break; 465 } 466 return (c); 467 } 468 469 int 470 lgetc(int quotec) 471 { 472 int c, next; 473 474 if (quotec) { 475 if ((c = igetc()) == EOF) { 476 yyerror("reached end of file while parsing " 477 "quoted string"); 478 if (file == topfile || popfile() == EOF) 479 return (EOF); 480 return (quotec); 481 } 482 return (c); 483 } 484 485 while ((c = igetc()) == '\\') { 486 next = igetc(); 487 if (next != '\n') { 488 c = next; 489 break; 490 } 491 yylval.lineno = file->lineno; 492 file->lineno++; 493 } 494 495 if (c == EOF) { 496 /* 497 * Fake EOL when hit EOF for the first time. This gets line 498 * count right if last line in included file is syntactically 499 * invalid and has no newline. 500 */ 501 if (file->eof_reached == 0) { 502 file->eof_reached = 1; 503 return ('\n'); 504 } 505 while (c == EOF) { 506 if (file == topfile || popfile() == EOF) 507 return (EOF); 508 c = igetc(); 509 } 510 } 511 return (c); 512 } 513 514 void 515 lungetc(int c) 516 { 517 if (c == EOF) 518 return; 519 520 if (file->ungetpos >= file->ungetsize) { 521 void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); 522 if (p == NULL) 523 err(1, "lungetc"); 524 file->ungetbuf = p; 525 file->ungetsize *= 2; 526 } 527 file->ungetbuf[file->ungetpos++] = c; 528 } 529 530 int 531 findeol(void) 532 { 533 int c; 534 535 /* Skip to either EOF or the first real EOL. */ 536 while (1) { 537 c = lgetc(0); 538 if (c == '\n') { 539 file->lineno++; 540 break; 541 } 542 if (c == EOF) 543 break; 544 } 545 return (ERROR); 546 } 547 548 int 549 yylex(void) 550 { 551 unsigned char buf[8096]; 552 unsigned char *p, *val; 553 int quotec, next, c; 554 int token; 555 556 top: 557 p = buf; 558 while ((c = lgetc(0)) == ' ' || c == '\t') 559 ; /* nothing */ 560 561 yylval.lineno = file->lineno; 562 if (c == '#') 563 while ((c = lgetc(0)) != '\n' && c != EOF) 564 ; /* nothing */ 565 if (c == '$' && !expanding) { 566 while (1) { 567 if ((c = lgetc(0)) == EOF) 568 return (0); 569 570 if (p + 1 >= buf + sizeof(buf) - 1) { 571 yyerror("string too long"); 572 return (findeol()); 573 } 574 if (isalnum(c) || c == '_') { 575 *p++ = c; 576 continue; 577 } 578 *p = '\0'; 579 lungetc(c); 580 break; 581 } 582 val = symget(buf); 583 if (val == NULL) { 584 yyerror("macro '%s' not defined", buf); 585 return (findeol()); 586 } 587 p = val + strlen(val) - 1; 588 lungetc(DONE_EXPAND); 589 while (p >= val) { 590 lungetc(*p); 591 p--; 592 } 593 lungetc(START_EXPAND); 594 goto top; 595 } 596 597 switch (c) { 598 case '\'': 599 case '"': 600 quotec = c; 601 while (1) { 602 if ((c = lgetc(quotec)) == EOF) 603 return (0); 604 if (c == '\n') { 605 file->lineno++; 606 continue; 607 } else if (c == '\\') { 608 if ((next = lgetc(quotec)) == EOF) 609 return (0); 610 if (next == quotec || next == ' ' || 611 next == '\t') 612 c = next; 613 else if (next == '\n') { 614 file->lineno++; 615 continue; 616 } else 617 lungetc(next); 618 } else if (c == quotec) { 619 *p = '\0'; 620 break; 621 } else if (c == '\0') { 622 yyerror("syntax error"); 623 return (findeol()); 624 } 625 if (p + 1 >= buf + sizeof(buf) - 1) { 626 yyerror("string too long"); 627 return (findeol()); 628 } 629 *p++ = c; 630 } 631 yylval.v.string = strdup(buf); 632 if (yylval.v.string == NULL) 633 err(1, "yylex: strdup"); 634 return (STRING); 635 } 636 637 #define allowed_to_end_number(x) \ 638 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 639 640 if (c == '-' || isdigit(c)) { 641 do { 642 *p++ = c; 643 if ((size_t)(p-buf) >= sizeof(buf)) { 644 yyerror("string too long"); 645 return (findeol()); 646 } 647 } while ((c = lgetc(0)) != EOF && isdigit(c)); 648 lungetc(c); 649 if (p == buf + 1 && buf[0] == '-') 650 goto nodigits; 651 if (c == EOF || allowed_to_end_number(c)) { 652 const char *errstr = NULL; 653 654 *p = '\0'; 655 yylval.v.number = strtonum(buf, LLONG_MIN, 656 LLONG_MAX, &errstr); 657 if (errstr) { 658 yyerror("\"%s\" invalid number: %s", 659 buf, errstr); 660 return (findeol()); 661 } 662 return (NUMBER); 663 } else { 664 nodigits: 665 while (p > buf + 1) 666 lungetc(*--p); 667 c = *--p; 668 if (c == '-') 669 return (c); 670 } 671 } 672 673 #define allowed_in_string(x) \ 674 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 675 x != '{' && x != '}' && \ 676 x != '!' && x != '=' && x != '#' && \ 677 x != ',')) 678 679 if (isalnum(c) || c == ':' || c == '_') { 680 do { 681 *p++ = c; 682 if ((size_t)(p-buf) >= sizeof(buf)) { 683 yyerror("string too long"); 684 return (findeol()); 685 } 686 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 687 lungetc(c); 688 *p = '\0'; 689 if ((token = lookup(buf)) == STRING) 690 if ((yylval.v.string = strdup(buf)) == NULL) 691 err(1, "yylex: strdup"); 692 return (token); 693 } 694 if (c == '\n') { 695 yylval.lineno = file->lineno; 696 file->lineno++; 697 } 698 if (c == EOF) 699 return (0); 700 return (c); 701 } 702 703 int 704 check_file_secrecy(int fd, const char *fname) 705 { 706 struct stat st; 707 708 if (fstat(fd, &st)) { 709 log_warn("cannot stat %s", fname); 710 return (-1); 711 } 712 if (st.st_uid != 0 && st.st_uid != getuid()) { 713 log_warnx("%s: owner not root or current user", fname); 714 return (-1); 715 } 716 if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { 717 log_warnx("%s: group writable or world read/writable", fname); 718 return (-1); 719 } 720 return (0); 721 } 722 723 struct file * 724 pushfile(const char *name, int secret) 725 { 726 struct file *nfile; 727 728 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 729 log_warn("calloc"); 730 return (NULL); 731 } 732 if ((nfile->name = strdup(name)) == NULL) { 733 log_warn("strdup"); 734 free(nfile); 735 return (NULL); 736 } 737 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 738 free(nfile->name); 739 free(nfile); 740 return (NULL); 741 } else if (secret && 742 check_file_secrecy(fileno(nfile->stream), nfile->name)) { 743 fclose(nfile->stream); 744 free(nfile->name); 745 free(nfile); 746 return (NULL); 747 } 748 nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; 749 nfile->ungetsize = 16; 750 nfile->ungetbuf = malloc(nfile->ungetsize); 751 if (nfile->ungetbuf == NULL) { 752 log_warn("malloc"); 753 fclose(nfile->stream); 754 free(nfile->name); 755 free(nfile); 756 return (NULL); 757 } 758 TAILQ_INSERT_TAIL(&files, nfile, entry); 759 return (nfile); 760 } 761 762 int 763 popfile(void) 764 { 765 struct file *prev; 766 767 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 768 prev->errors += file->errors; 769 770 TAILQ_REMOVE(&files, file, entry); 771 fclose(file->stream); 772 free(file->name); 773 free(file->ungetbuf); 774 free(file); 775 file = prev; 776 return (file ? 0 : EOF); 777 } 778 779 struct uw_conf * 780 parse_config(char *filename) 781 { 782 struct sym *sym, *next; 783 784 conf = config_new_empty(); 785 786 file = pushfile(filename != NULL ? filename : CONF_FILE, 0); 787 if (file == NULL) { 788 /* no default config file is fine */ 789 if (errno == ENOENT && filename == NULL) 790 return (conf); 791 log_warn("%s", filename); 792 free(conf); 793 return (NULL); 794 } 795 topfile = file; 796 797 yyparse(); 798 errors = file->errors; 799 popfile(); 800 801 /* Free macros and check which have not been used. */ 802 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { 803 if ((cmd_opts & OPT_VERBOSE2) && !sym->used) 804 fprintf(stderr, "warning: macro '%s' not used\n", 805 sym->nam); 806 if (!sym->persist) { 807 free(sym->nam); 808 free(sym->val); 809 TAILQ_REMOVE(&symhead, sym, entry); 810 free(sym); 811 } 812 } 813 814 if (errors) { 815 clear_config(conf); 816 return (NULL); 817 } 818 819 return (conf); 820 } 821 822 int 823 symset(const char *nam, const char *val, int persist) 824 { 825 struct sym *sym; 826 827 TAILQ_FOREACH(sym, &symhead, entry) { 828 if (strcmp(nam, sym->nam) == 0) 829 break; 830 } 831 832 if (sym != NULL) { 833 if (sym->persist == 1) 834 return (0); 835 else { 836 free(sym->nam); 837 free(sym->val); 838 TAILQ_REMOVE(&symhead, sym, entry); 839 free(sym); 840 } 841 } 842 if ((sym = calloc(1, sizeof(*sym))) == NULL) 843 return (-1); 844 845 sym->nam = strdup(nam); 846 if (sym->nam == NULL) { 847 free(sym); 848 return (-1); 849 } 850 sym->val = strdup(val); 851 if (sym->val == NULL) { 852 free(sym->nam); 853 free(sym); 854 return (-1); 855 } 856 sym->used = 0; 857 sym->persist = persist; 858 TAILQ_INSERT_TAIL(&symhead, sym, entry); 859 return (0); 860 } 861 862 int 863 cmdline_symset(char *s) 864 { 865 char *sym, *val; 866 int ret; 867 868 if ((val = strrchr(s, '=')) == NULL) 869 return (-1); 870 sym = strndup(s, val - s); 871 if (sym == NULL) 872 errx(1, "%s: strndup", __func__); 873 ret = symset(sym, val + 1, 1); 874 free(sym); 875 876 return (ret); 877 } 878 879 char * 880 symget(const char *nam) 881 { 882 struct sym *sym; 883 884 TAILQ_FOREACH(sym, &symhead, entry) { 885 if (strcmp(nam, sym->nam) == 0) { 886 sym->used = 1; 887 return (sym->val); 888 } 889 } 890 return (NULL); 891 } 892 893 void 894 clear_config(struct uw_conf *xconf) 895 { 896 struct uw_forwarder *uw_forwarder; 897 898 while ((uw_forwarder = TAILQ_FIRST(&xconf->uw_forwarder_list)) != 899 NULL) { 900 TAILQ_REMOVE(&xconf->uw_forwarder_list, uw_forwarder, entry); 901 free(uw_forwarder); 902 } 903 while ((uw_forwarder = TAILQ_FIRST(&xconf->uw_dot_forwarder_list)) != 904 NULL) { 905 TAILQ_REMOVE(&xconf->uw_dot_forwarder_list, uw_forwarder, 906 entry); 907 free(uw_forwarder); 908 } 909 910 free(xconf); 911 } 912 913 struct sockaddr_storage * 914 host_ip(const char *s) 915 { 916 struct addrinfo hints, *res; 917 struct sockaddr_storage *ss = NULL; 918 919 memset(&hints, 0, sizeof(hints)); 920 hints.ai_family = AF_UNSPEC; 921 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 922 hints.ai_flags = AI_NUMERICHOST; 923 if (getaddrinfo(s, "0", &hints, &res) == 0) { 924 if (res->ai_family == AF_INET || 925 res->ai_family == AF_INET6) { 926 if ((ss = calloc(1, sizeof(*ss))) == NULL) 927 fatal(NULL); 928 memcpy(ss, res->ai_addr, res->ai_addrlen); 929 } 930 freeaddrinfo(res); 931 } 932 933 return (ss); 934 } 935 936 int 937 check_pref_uniq(enum uw_resolver_type type) 938 { 939 int i; 940 941 for (i = 0; i < conf->res_pref.len; i++) 942 if (conf->res_pref.types[i] == type) { 943 yyerror("%s is already in the preference list", 944 uw_resolver_type_str[type]); 945 return (0); 946 } 947 948 return (1); 949 } 950