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