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