parse.y revision 1.18
1/* $OpenBSD: parse.y,v 1.18 2007/01/25 19:40:08 niallo Exp $ */ 2 3/* 4 * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.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/types.h> 27#include <sys/socket.h> 28#include <sys/queue.h> 29#include <netinet/in.h> 30#include <net/if.h> 31#include <arpa/inet.h> 32#include <arpa/nameser.h> 33 34#include <ctype.h> 35#include <err.h> 36#include <errno.h> 37#include <event.h> 38#include <limits.h> 39#include <stdarg.h> 40#include <stdio.h> 41#include <netdb.h> 42#include <string.h> 43 44#include "hoststated.h" 45 46struct hoststated *conf = NULL; 47static FILE *fin = NULL; 48static int lineno = 1; 49static int errors = 0; 50const char *infile; 51char *start_state; 52objid_t last_service_id = 0; 53objid_t last_table_id = 0; 54objid_t last_host_id = 0; 55 56static struct service *service = NULL; 57static struct table *table = NULL; 58 59int yyerror(const char *, ...); 60int yyparse(void); 61int kw_cmp(const void *, const void *); 62int lookup(char *); 63int lgetc(FILE *, int *); 64int lungetc(int); 65int findeol(void); 66int yylex(void); 67 68TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 69struct sym { 70 TAILQ_ENTRY(sym) entries; 71 int used; 72 int persist; 73 char *nam; 74 char *val; 75}; 76 77int symset(const char *, const char *, int); 78char *symget(const char *); 79int cmdline_symset(char *); 80 81struct address *host_v4(const char *); 82struct address *host_v6(const char *); 83int host_dns(const char *, struct addresslist *, 84 int, in_port_t, const char *); 85int host(const char *, struct addresslist *, 86 int, in_port_t, const char *); 87 88typedef struct { 89 union { 90 u_int32_t number; 91 char *string; 92 struct host *host; 93 struct timeval tv; 94 } v; 95 int lineno; 96} YYSTYPE; 97 98%} 99 100%token SERVICE TABLE BACKUP HOST REAL 101%token CHECK HTTP TCP ICMP EXTERNAL 102%token TIMEOUT CODE DIGEST PORT TAG INTERFACE 103%token VIRTUAL IP INTERVAL DISABLE STICKYADDR 104%token SEND EXPECT NOTHING 105%token ERROR 106%token <v.string> STRING 107%type <v.string> interface 108%type <v.number> number port 109%type <v.host> host 110%type <v.tv> timeout 111 112%% 113 114grammar : /* empty */ 115 | grammar '\n' 116 | grammar varset '\n' 117 | grammar main '\n' 118 | grammar service '\n' 119 | grammar table '\n' 120 | grammar error '\n' { errors++; } 121 ; 122 123number : STRING { 124 const char *estr; 125 126 $$ = strtonum($1, 0, UINT_MAX, &estr); 127 if (estr) { 128 yyerror("cannot parse number %s : %s", 129 $1, estr); 130 free($1); 131 YYERROR; 132 } 133 free($1); 134 } 135 ; 136 137port : PORT STRING { 138 const char *estr; 139 struct servent *servent; 140 141 $$ = strtonum($2, 1, USHRT_MAX, &estr); 142 if (estr) { 143 if (errno == ERANGE) { 144 yyerror("port %s is out of range", $2); 145 free($2); 146 YYERROR; 147 } 148 servent = getservbyname($2, "tcp"); 149 if (servent == NULL) { 150 yyerror("port %s is invalid", $2); 151 free($2); 152 YYERROR; 153 } 154 $$ = servent->s_port; 155 } else 156 $$ = htons($$); 157 free($2); 158 } 159 | PORT HTTP { 160 struct servent *servent; 161 162 servent = getservbyname("http", "tcp"); 163 if (servent == NULL) 164 $$ = htons(80); 165 else 166 $$ = servent->s_port; 167 } 168 ; 169 170varset : STRING '=' STRING { 171 if (symset($1, $3, 0) == -1) 172 fatal("cannot store variable"); 173 free($1); 174 free($3); 175 } 176 ; 177 178sendbuf : NOTHING { 179 table->sendbuf = NULL; 180 } 181 | STRING { 182 table->sendbuf = strdup($1); 183 if (table->sendbuf == NULL) 184 fatal("out of memory"); 185 free($1); 186 } 187 ; 188 189main : INTERVAL number { conf->interval.tv_sec = $2; } 190 | TIMEOUT timeout { 191 bcopy(&$2, &conf->timeout, sizeof(struct timeval)); 192 } 193 ; 194 195service : SERVICE STRING { 196 struct service *srv; 197 198 TAILQ_FOREACH(srv, &conf->services, entry) 199 if (!strcmp(srv->name, $2)) 200 break; 201 if (srv != NULL) { 202 yyerror("service %s defined twice", $2); 203 free($2); 204 YYERROR; 205 } 206 if ((srv = calloc(1, sizeof (*srv))) == NULL) 207 fatal("out of memory"); 208 209 if (strlcpy(srv->name, $2, sizeof(srv->name)) >= 210 sizeof(srv->name)) { 211 yyerror("service name truncated"); 212 YYERROR; 213 } 214 free($2); 215 srv->id = last_service_id++; 216 if (last_service_id == UINT_MAX) { 217 yyerror("too many services defined"); 218 YYERROR; 219 } 220 service = srv; 221 } '{' optnl serviceopts_l '}' { 222 if (service->table == NULL) { 223 yyerror("service %s has no table", 224 service->name); 225 YYERROR; 226 } 227 if (TAILQ_EMPTY(&service->virts)) { 228 yyerror("service %s has no virtual ip", 229 service->name); 230 YYERROR; 231 } 232 conf->servicecount++; 233 if (service->backup == NULL) 234 service->backup = &conf->empty_table; 235 else if (service->backup->port != 236 service->table->port) { 237 yyerror("service %s uses two different ports " 238 "for its table and backup table", 239 service->name); 240 YYERROR; 241 } 242 243 if (!(service->flags & F_DISABLE)) 244 service->flags |= F_ADD; 245 TAILQ_INSERT_HEAD(&conf->services, service, entry); 246 } 247 ; 248 249serviceopts_l : serviceopts_l serviceoptsl nl 250 | serviceoptsl optnl 251 ; 252 253serviceoptsl : TABLE STRING { 254 struct table *tb; 255 256 TAILQ_FOREACH(tb, &conf->tables, entry) 257 if (!strcmp(tb->name, $2)) 258 break; 259 if (tb == NULL) { 260 yyerror("no such table: %s", $2); 261 free($2); 262 YYERROR; 263 } else { 264 service->table = tb; 265 service->table->serviceid = service->id; 266 service->table->flags |= F_USED; 267 free($2); 268 } 269 } 270 | BACKUP TABLE STRING { 271 struct table *tb; 272 273 if (service->backup) { 274 yyerror("backup already specified"); 275 free($3); 276 YYERROR; 277 } 278 279 TAILQ_FOREACH(tb, &conf->tables, entry) 280 if (!strcmp(tb->name, $3)) 281 break; 282 283 if (tb == NULL) { 284 yyerror("no such table: %s", $3); 285 free($3); 286 YYERROR; 287 } else { 288 service->backup = tb; 289 service->backup->serviceid = service->id; 290 service->backup->flags |= (F_USED|F_BACKUP); 291 free($3); 292 } 293 } 294 | VIRTUAL IP STRING port interface { 295 if (host($3, &service->virts, 296 SRV_MAX_VIRTS, $4, $5) <= 0) { 297 yyerror("invalid virtual ip: %s", $3); 298 free($3); 299 free($5); 300 YYERROR; 301 } 302 free($3); 303 free($5); 304 } 305 | DISABLE { service->flags |= F_DISABLE; } 306 | STICKYADDR { service->flags |= F_STICKY; } 307 | TAG STRING { 308 if (strlcpy(service->tag, $2, sizeof(service->tag)) >= 309 sizeof(service->tag)) { 310 yyerror("service tag name truncated"); 311 free($2); 312 YYERROR; 313 } 314 free($2); 315 } 316 ; 317 318table : TABLE STRING { 319 struct table *tb; 320 321 TAILQ_FOREACH(tb, &conf->tables, entry) 322 if (!strcmp(tb->name, $2)) 323 break; 324 if (tb != NULL) { 325 yyerror("table %s defined twice"); 326 free($2); 327 YYERROR; 328 } 329 330 if ((tb = calloc(1, sizeof (*tb))) == NULL) 331 fatal("out of memory"); 332 333 if (strlcpy(tb->name, $2, sizeof(tb->name)) >= 334 sizeof(tb->name)) { 335 yyerror("table name truncated"); 336 YYERROR; 337 } 338 tb->id = last_table_id++; 339 bcopy(&conf->timeout, &tb->timeout, 340 sizeof(struct timeval)); 341 if (last_table_id == UINT_MAX) { 342 yyerror("too many tables defined"); 343 YYERROR; 344 } 345 free($2); 346 table = tb; 347 } '{' optnl tableopts_l '}' { 348 if (table->port == 0) { 349 yyerror("table %s has no port", table->name); 350 YYERROR; 351 } 352 if (TAILQ_EMPTY(&table->hosts)) { 353 yyerror("table %s has no hosts", table->name); 354 YYERROR; 355 } 356 if (table->check == CHECK_NOCHECK) { 357 yyerror("table %s has no check", table->name); 358 YYERROR; 359 } 360 conf->tablecount++; 361 TAILQ_INSERT_HEAD(&conf->tables, table, entry); 362 } 363 ; 364 365tableopts_l : tableopts_l tableoptsl nl 366 | tableoptsl optnl 367 ; 368 369tableoptsl : host { 370 $1->tableid = table->id; 371 $1->tablename = table->name; 372 TAILQ_INSERT_HEAD(&table->hosts, $1, entry); 373 } 374 | TIMEOUT timeout { 375 bcopy(&$2, &table->timeout, sizeof(struct timeval)); 376 } 377 | CHECK ICMP { 378 table->check = CHECK_ICMP; 379 } 380 | CHECK TCP { 381 table->check = CHECK_TCP; 382 } 383 | CHECK HTTP STRING CODE number { 384 table->check = CHECK_HTTP_CODE; 385 table->retcode = $5; 386 asprintf(&table->sendbuf, "HEAD %s HTTP/1.0\r\n\r\n", 387 $3); 388 free($3); 389 if (table->sendbuf == NULL) 390 fatal("out of memory"); 391 } 392 | CHECK HTTP STRING DIGEST STRING { 393 table->check = CHECK_HTTP_DIGEST; 394 asprintf(&table->sendbuf, "GET %s HTTP/1.0\r\n\r\n", 395 $3); 396 free($3); 397 if (table->sendbuf == NULL) 398 fatal("out of memory"); 399 if (strlcpy(table->digest, $5, 400 sizeof(table->digest)) >= sizeof(table->digest)) { 401 yyerror("http digest truncated"); 402 free($5); 403 YYERROR; 404 } 405 free($5); 406 } 407 | CHECK SEND sendbuf EXPECT STRING { 408 table->check = CHECK_SEND_EXPECT; 409 if (strlcpy(table->exbuf, $5, sizeof(table->exbuf)) 410 >= sizeof(table->exbuf)) { 411 yyerror("yyparse: expect buffer truncated"); 412 free($5); 413 YYERROR; 414 } 415 free($5); 416 } 417 | REAL port { 418 table->port = $2; 419 } 420 | DISABLE { table->flags |= F_DISABLE; } 421 ; 422 423interface : /*empty*/ { $$ = NULL; } 424 | INTERFACE STRING { $$ = $2; } 425 ; 426 427host : HOST STRING { 428 struct host *r; 429 struct address *a; 430 struct addresslist al; 431 432 if ((r = calloc(1, sizeof(*r))) == NULL) 433 fatal("out of memory"); 434 435 TAILQ_INIT(&al); 436 if (host($2, &al, 1, 0, NULL) <= 0) { 437 yyerror("invalid host %s", $2); 438 free($2); 439 YYERROR; 440 } 441 a = TAILQ_FIRST(&al); 442 memcpy(&r->ss, &a->ss, sizeof(r->ss)); 443 free(a); 444 445 if (strlcpy(r->name, $2, sizeof(r->name)) >= 446 sizeof(r->name)) { 447 yyerror("host name truncated"); 448 free($2); 449 YYERROR; 450 } else { 451 r->id = last_host_id++; 452 if (last_host_id == UINT_MAX) { 453 yyerror("too many hosts defined"); 454 YYERROR; 455 } 456 free($2); 457 $$ = r; 458 } 459 } 460 ; 461 462timeout : number 463 { 464 $$.tv_sec = $1 / 1000; 465 $$.tv_usec = ($1 % 1000) * 1000; 466 } 467 ; 468 469optnl : '\n' optnl 470 | 471 ; 472 473nl : '\n' optnl 474 ; 475 476%% 477 478struct keywords { 479 const char *k_name; 480 int k_val; 481}; 482 483int 484yyerror(const char *fmt, ...) 485{ 486 va_list ap; 487 488 errors = 1; 489 va_start(ap, fmt); 490 fprintf(stderr, "%s:%d: ", infile, yylval.lineno); 491 vfprintf(stderr, fmt, ap); 492 fprintf(stderr, "\n"); 493 va_end(ap); 494 return (0); 495} 496 497int 498kw_cmp(const void *k, const void *e) 499{ 500 501 return (strcmp(k, ((const struct keywords *)e)->k_name)); 502} 503 504int 505lookup(char *s) 506{ 507 /* this has to be sorted always */ 508 static const struct keywords keywords[] = { 509 { "backup", BACKUP }, 510 { "check", CHECK }, 511 { "code", CODE }, 512 { "digest", DIGEST }, 513 { "disable", DISABLE }, 514 { "expect", EXPECT }, 515 { "external", EXTERNAL }, 516 { "host", HOST }, 517 { "http", HTTP }, 518 { "icmp", ICMP }, 519 { "interface", INTERFACE }, 520 { "interval", INTERVAL }, 521 { "ip", IP }, 522 { "nothing", NOTHING }, 523 { "port", PORT }, 524 { "real", REAL }, 525 { "send", SEND }, 526 { "service", SERVICE }, 527 { "sticky-address", STICKYADDR }, 528 { "table", TABLE }, 529 { "tag", TAG }, 530 { "tcp", TCP }, 531 { "timeout", TIMEOUT }, 532 { "virtual", VIRTUAL } 533 }; 534 const struct keywords *p; 535 536 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 537 sizeof(keywords[0]), kw_cmp); 538 539 if (p) 540 return (p->k_val); 541 else 542 return (STRING); 543} 544 545#define MAXPUSHBACK 128 546 547char *parsebuf; 548int parseindex; 549char pushback_buffer[MAXPUSHBACK]; 550int pushback_index = 0; 551 552int 553lgetc(FILE *f, int *keep) 554{ 555 int c, next; 556 557 *keep = 0; 558 if (parsebuf) { 559 /* Read character from the parsebuffer instead of input. */ 560 if (parseindex >= 0) { 561 c = parsebuf[parseindex++]; 562 if (c != '\0') 563 return (c); 564 parsebuf = NULL; 565 } else 566 parseindex++; 567 } 568 569 if (pushback_index) 570 return (pushback_buffer[--pushback_index]); 571 572 while ((c = getc(f)) == '\\') { 573 next = getc(f); 574 if (next == 'n') { 575 *keep = 1; 576 c = '\n'; 577 break; 578 } else if (next == 'r') { 579 *keep = 1; 580 c = '\r'; 581 break; 582 } else if (next != '\n') { 583 c = next; 584 break; 585 } 586 yylval.lineno = lineno; 587 lineno++; 588 } 589 if (c == '\t' || c == ' ') { 590 /* Compress blanks to a single space. */ 591 do { 592 c = getc(f); 593 } while (c == '\t' || c == ' '); 594 ungetc(c, f); 595 c = ' '; 596 } 597 598 return (c); 599} 600 601int 602lungetc(int c) 603{ 604 if (c == EOF) 605 return (EOF); 606 if (parsebuf) { 607 parseindex--; 608 if (parseindex >= 0) 609 return (c); 610 } 611 if (pushback_index < MAXPUSHBACK-1) 612 return (pushback_buffer[pushback_index++] = c); 613 else 614 return (EOF); 615} 616 617int 618findeol(void) 619{ 620 int c; 621 int k; 622 623 parsebuf = NULL; 624 pushback_index = 0; 625 626 /* skip to either EOF or the first real EOL */ 627 while (1) { 628 c = lgetc(fin, &k); 629 if (c == '\n' && k == 0) { 630 lineno++; 631 break; 632 } 633 if (c == EOF) 634 break; 635 } 636 return (ERROR); 637} 638 639int 640yylex(void) 641{ 642 char buf[8096]; 643 char *p, *val; 644 int endc, c; 645 int token; 646 int keep; 647 648top: 649 p = buf; 650 while ((c = lgetc(fin, &keep)) == ' ') 651 ; /* nothing */ 652 653 yylval.lineno = lineno; 654 if (c == '#') 655 do { 656 while ((c = lgetc(fin, &keep)) != '\n' && c != EOF) 657 ; /* nothing */ 658 } while (keep == 1); 659 if (c == '$' && parsebuf == NULL) { 660 while (1) { 661 if ((c = lgetc(fin, &keep)) == EOF) 662 return (0); 663 664 if (p + 1 >= buf + sizeof(buf) - 1) { 665 yyerror("string too long"); 666 return (findeol()); 667 } 668 if (isalnum(c) || c == '_') { 669 *p++ = (char)c; 670 continue; 671 } 672 *p = '\0'; 673 lungetc(c); 674 break; 675 } 676 val = symget(buf); 677 if (val == NULL) { 678 yyerror("macro '%s' not defined", buf); 679 return (findeol()); 680 } 681 parsebuf = val; 682 parseindex = 0; 683 goto top; 684 } 685 686 switch (c) { 687 case '\'': 688 case '"': 689 endc = c; 690 while (1) { 691 if ((c = lgetc(fin, &keep)) == EOF) 692 return (0); 693 if (c == endc) { 694 *p = '\0'; 695 break; 696 } 697 if (c == '\n' && keep == 0) { 698 lineno++; 699 continue; 700 } 701 if (p + 1 >= buf + sizeof(buf) - 1) { 702 yyerror("string too long"); 703 return (findeol()); 704 } 705 *p++ = (char)c; 706 } 707 yylval.v.string = strdup(buf); 708 if (yylval.v.string == NULL) 709 errx(1, "yylex: strdup"); 710 return (STRING); 711 } 712 713#define allowed_in_string(x) \ 714 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 715 x != '{' && x != '}' && \ 716 x != '!' && x != '=' && x != '#' && \ 717 x != ',')) 718 719 if (isalnum(c) || c == ':' || c == '_') { 720 do { 721 *p++ = c; 722 if ((unsigned)(p-buf) >= sizeof(buf)) { 723 yyerror("string too long"); 724 return (findeol()); 725 } 726 } while ((c = lgetc(fin, &keep)) != EOF && 727 (allowed_in_string(c))); 728 lungetc(c); 729 *p = '\0'; 730 if ((token = lookup(buf)) == STRING) 731 if ((yylval.v.string = strdup(buf)) == NULL) 732 err(1, "yylex: strdup"); 733 return (token); 734 } 735 if (c == '\n') { 736 yylval.lineno = lineno; 737 lineno++; 738 } 739 if (c == EOF) 740 return (0); 741 return (c); 742} 743 744int 745parse_config(struct hoststated *x_conf, const char *filename, int opts) 746{ 747 struct sym *sym, *next; 748 749 conf = x_conf; 750 751 TAILQ_INIT(&conf->services); 752 TAILQ_INIT(&conf->tables); 753 memset(&conf->empty_table, 0, sizeof(conf->empty_table)); 754 conf->empty_table.id = EMPTY_TABLE; 755 conf->empty_table.flags |= F_DISABLE; 756 (void)strlcpy(conf->empty_table.name, "empty", 757 sizeof(conf->empty_table.name)); 758 759 conf->timeout.tv_sec = CHECK_TIMEOUT / 1000; 760 conf->timeout.tv_usec = (CHECK_TIMEOUT % 1000) * 1000; 761 conf->interval.tv_sec = CHECK_INTERVAL; 762 conf->interval.tv_usec = 0; 763 conf->opts = opts; 764 765 if ((fin = fopen(filename, "r")) == NULL) { 766 warn("%s", filename); 767 return (0); 768 } 769 infile = filename; 770 setservent(1); 771 yyparse(); 772 endservent(); 773 fclose(fin); 774 775 /* Free macros and check which have not been used. */ 776 for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { 777 next = TAILQ_NEXT(sym, entries); 778 if ((conf->opts & HOSTSTATED_OPT_VERBOSE) && !sym->used) 779 fprintf(stderr, "warning: macro '%s' not " 780 "used\n", sym->nam); 781 if (!sym->persist) { 782 free(sym->nam); 783 free(sym->val); 784 TAILQ_REMOVE(&symhead, sym, entries); 785 free(sym); 786 } 787 } 788 789 if (TAILQ_EMPTY(&conf->services)) { 790 log_warnx("no services, nothing to do"); 791 errors++; 792 } 793 794 if (timercmp(&conf->timeout, &conf->interval, >=)) { 795 log_warnx("global timeout exceeds interval"); 796 errors++; 797 } 798 799 /* Verify that every table is used */ 800 TAILQ_FOREACH(table, &conf->tables, entry) { 801 if (!(table->flags & F_USED)) { 802 log_warnx("unused table: %s", table->name); 803 errors++; 804 } 805 if (timercmp(&table->timeout, &conf->interval, >=)) { 806 log_warnx("table timeout exceeds interval: %s", 807 table->name); 808 errors++; 809 } 810 } 811 812 if (errors) { 813 bzero(&conf, sizeof (*conf)); 814 return (-1); 815 } 816 817 return (0); 818} 819 820int 821symset(const char *nam, const char *val, int persist) 822{ 823 struct sym *sym; 824 825 for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); 826 sym = TAILQ_NEXT(sym, entries)) 827 ; /* nothing */ 828 829 if (sym != NULL) { 830 if (sym->persist == 1) 831 return (0); 832 else { 833 free(sym->nam); 834 free(sym->val); 835 TAILQ_REMOVE(&symhead, sym, entries); 836 free(sym); 837 } 838 } 839 if ((sym = calloc(1, sizeof(*sym))) == NULL) 840 return (-1); 841 842 sym->nam = strdup(nam); 843 if (sym->nam == NULL) { 844 free(sym); 845 return (-1); 846 } 847 sym->val = strdup(val); 848 if (sym->val == NULL) { 849 free(sym->nam); 850 free(sym); 851 return (-1); 852 } 853 sym->used = 0; 854 sym->persist = persist; 855 TAILQ_INSERT_TAIL(&symhead, sym, entries); 856 return (0); 857} 858 859int 860cmdline_symset(char *s) 861{ 862 char *sym, *val; 863 int ret; 864 size_t len; 865 866 if ((val = strrchr(s, '=')) == NULL) 867 return (-1); 868 869 len = strlen(s) - strlen(val) + 1; 870 if ((sym = malloc(len)) == NULL) 871 errx(1, "cmdline_symset: malloc"); 872 873 strlcpy(sym, s, len); 874 875 ret = symset(sym, val + 1, 1); 876 free(sym); 877 878 return (ret); 879} 880 881char * 882symget(const char *nam) 883{ 884 struct sym *sym; 885 886 TAILQ_FOREACH(sym, &symhead, entries) 887 if (strcmp(nam, sym->nam) == 0) { 888 sym->used = 1; 889 return (sym->val); 890 } 891 return (NULL); 892} 893 894struct address * 895host_v4(const char *s) 896{ 897 struct in_addr ina; 898 struct sockaddr_in *sain; 899 struct address *h; 900 901 bzero(&ina, sizeof(ina)); 902 if (inet_pton(AF_INET, s, &ina) != 1) 903 return (NULL); 904 905 if ((h = calloc(1, sizeof(*h))) == NULL) 906 fatal(NULL); 907 sain = (struct sockaddr_in *)&h->ss; 908 sain->sin_len = sizeof(struct sockaddr_in); 909 sain->sin_family = AF_INET; 910 sain->sin_addr.s_addr = ina.s_addr; 911 912 return (h); 913} 914 915struct address * 916host_v6(const char *s) 917{ 918 struct in6_addr ina6; 919 struct sockaddr_in6 *sin6; 920 struct address *h; 921 922 bzero(&ina6, sizeof(ina6)); 923 if (inet_pton(AF_INET6, s, &ina6) != 1) 924 return (NULL); 925 926 if ((h = calloc(1, sizeof(*h))) == NULL) 927 fatal(NULL); 928 sin6 = (struct sockaddr_in6 *)&h->ss; 929 sin6->sin6_len = sizeof(struct sockaddr_in6); 930 sin6->sin6_family = AF_INET6; 931 memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6)); 932 933 return (h); 934} 935 936int 937host_dns(const char *s, struct addresslist *al, int max, 938 in_port_t port, const char *ifname) 939{ 940 struct addrinfo hints, *res0, *res; 941 int error, cnt = 0; 942 struct sockaddr_in *sain; 943 struct sockaddr_in6 *sin6; 944 struct address *h; 945 946 bzero(&hints, sizeof(hints)); 947 hints.ai_family = PF_UNSPEC; 948 hints.ai_socktype = SOCK_DGRAM; /* DUMMY */ 949 error = getaddrinfo(s, NULL, &hints, &res0); 950 if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME) 951 return (0); 952 if (error) { 953 log_warnx("host_dns: could not parse \"%s\": %s", s, 954 gai_strerror(error)); 955 return (-1); 956 } 957 958 for (res = res0; res && cnt < max; res = res->ai_next) { 959 if (res->ai_family != AF_INET && 960 res->ai_family != AF_INET6) 961 continue; 962 if ((h = calloc(1, sizeof(*h))) == NULL) 963 fatal(NULL); 964 965 h->port = port; 966 if (ifname != NULL) { 967 if (strlcpy(h->ifname, ifname, sizeof(h->ifname)) >= 968 sizeof(h->ifname)) 969 log_warnx("host_dns: interface name truncated"); 970 return (-1); 971 } 972 h->ss.ss_family = res->ai_family; 973 if (res->ai_family == AF_INET) { 974 sain = (struct sockaddr_in *)&h->ss; 975 sain->sin_len = sizeof(struct sockaddr_in); 976 sain->sin_addr.s_addr = ((struct sockaddr_in *) 977 res->ai_addr)->sin_addr.s_addr; 978 } else { 979 sin6 = (struct sockaddr_in6 *)&h->ss; 980 sin6->sin6_len = sizeof(struct sockaddr_in6); 981 memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *) 982 res->ai_addr)->sin6_addr, sizeof(struct in6_addr)); 983 } 984 985 TAILQ_INSERT_HEAD(al, h, entry); 986 cnt++; 987 } 988 if (cnt == max && res) { 989 log_warnx("host_dns: %s resolves to more than %d hosts", 990 s, max); 991 } 992 freeaddrinfo(res0); 993 return (cnt); 994} 995 996int 997host(const char *s, struct addresslist *al, int max, 998 in_port_t port, const char *ifname) 999{ 1000 struct address *h; 1001 1002 h = host_v4(s); 1003 1004 /* IPv6 address? */ 1005 if (h == NULL) 1006 h = host_v6(s); 1007 1008 if (h != NULL) { 1009 h->port = port; 1010 if (ifname != NULL) { 1011 if (strlcpy(h->ifname, ifname, sizeof(h->ifname)) >= 1012 sizeof(h->ifname)) { 1013 log_warnx("host: interface name truncated"); 1014 return (-1); 1015 } 1016 } 1017 1018 TAILQ_INSERT_HEAD(al, h, entry); 1019 return (1); 1020 } 1021 1022 return (host_dns(s, al, max, port, ifname)); 1023} 1024