1 /* $OpenBSD: parse.y,v 1.7 2019/06/28 13:32:48 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> 5 * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 6 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 7 * Copyright (c) 2001 Markus Friedl. All rights reserved. 8 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 9 * Copyright (c) 2001 Theo de Raadt. All rights reserved. 10 * 11 * Permission to use, copy, modify, and distribute this software for any 12 * purpose with or without fee is hereby granted, provided that the above 13 * copyright notice and this permission notice appear in all copies. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 */ 23 24 %{ 25 #include <sys/types.h> 26 #include <sys/socket.h> 27 #include <sys/stat.h> 28 #include <sys/ioctl.h> 29 #include <sys/un.h> 30 31 #include <net/if.h> 32 #include <netinet/in.h> 33 #include <arpa/inet.h> 34 35 #include <ctype.h> 36 #include <err.h> 37 #include <errno.h> 38 #include <ifaddrs.h> 39 #include <inttypes.h> 40 #include <limits.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 45 #include "lpd.h" 46 47 #include "log.h" 48 49 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 50 static struct file { 51 TAILQ_ENTRY(file) entry; 52 FILE *stream; 53 char *name; 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 kw_cmp(const void *, const void *); 63 int lookup(char *); 64 int lgetc(int); 65 int lungetc(int); 66 int findeol(void); 67 int yyerror(const char *, ...) 68 __attribute__((__format__ (printf, 1, 2))) 69 __attribute__((__nonnull__ (1))); 70 71 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 72 struct sym { 73 TAILQ_ENTRY(sym) entry; 74 int used; 75 int persist; 76 char *nam; 77 char *val; 78 }; 79 int symset(const char *, const char *, int); 80 char *symget(const char *); 81 82 static int errors = 0; 83 84 struct lpd_conf *conf = NULL; 85 86 enum listen_options { 87 LO_FAMILY = 0x000001, 88 LO_PORT = 0x000002, 89 }; 90 91 static struct listen_opts { 92 char *ifx; 93 int family; 94 int proto; 95 in_port_t port; 96 uint32_t options; 97 } listen_opts; 98 99 static void config_free(struct lpd_conf *); 100 static void create_listeners(struct listen_opts *); 101 static void config_listener(struct listener *, struct listen_opts *); 102 static int local(struct listen_opts *); 103 static int host_v4(struct listen_opts *); 104 static int host_v6(struct listen_opts *); 105 static int host_dns(struct listen_opts *); 106 static int interface(struct listen_opts *); 107 static int is_if_in_group(const char *, const char *); 108 109 typedef struct { 110 union { 111 int64_t number; 112 char *string; 113 struct host *host; 114 } v; 115 int lineno; 116 } YYSTYPE; 117 118 %} 119 120 %token ERROR ARROW INCLUDE 121 %token LISTEN ON PORT INET4 INET6 LOCAL SOCKET 122 %token <v.string> STRING 123 %token <v.number> NUMBER 124 %type <v.number> family_inet portno 125 126 %% 127 128 grammar : /* empty */ 129 | grammar '\n' 130 | grammar include '\n' 131 | grammar varset '\n' 132 | grammar main '\n' 133 | grammar error '\n' { file->errors++; } 134 ; 135 136 include : INCLUDE STRING { 137 struct file *nfile; 138 139 if ((nfile = pushfile($2, 0)) == NULL) { 140 yyerror("failed to include file %s", $2); 141 free($2); 142 YYERROR; 143 } 144 free($2); 145 146 file = nfile; 147 lungetc('\n'); 148 } 149 ; 150 151 varset : STRING '=' STRING { 152 char *s = $1; 153 while (*s++) { 154 if (isspace((unsigned char)*s)) { 155 yyerror("macro name cannot contain " 156 "whitespace"); 157 free($1); 158 free($3); 159 YYERROR; 160 } 161 } 162 if (symset($1, $3, 0) == -1) 163 fatal("cannot store variable"); 164 free($1); 165 free($3); 166 } 167 ; 168 169 portno : STRING { 170 struct servent *servent; 171 servent = getservbyname($1, "tcp"); 172 if (servent == NULL) { 173 yyerror("invalid port: %s", $1); 174 free($1); 175 YYERROR; 176 } 177 free($1); 178 $$ = ntohs(servent->s_port); 179 } 180 | NUMBER { 181 if ($1 <= 0 || $1 > (int)USHRT_MAX) { 182 yyerror("invalid port: %" PRId64, $1); 183 YYERROR; 184 } 185 $$ = $1; 186 } 187 ; 188 189 family_inet : INET4 { $$ = AF_INET; } 190 | INET6 { $$ = AF_INET6; } 191 ; 192 193 opt_listen : family_inet { 194 if (listen_opts.options & LO_FAMILY) { 195 yyerror("address family already specified"); 196 YYERROR; 197 } 198 listen_opts.options |= LO_FAMILY; 199 listen_opts.family = $1; 200 } 201 | PORT portno { 202 if (listen_opts.options & LO_PORT) { 203 yyerror("port already specified"); 204 YYERROR; 205 } 206 listen_opts.options |= LO_PORT; 207 listen_opts.port = htons($2); 208 } 209 ; 210 211 listener : opt_listen listener 212 | /* empty */ { 213 create_listeners(&listen_opts); 214 } 215 ; 216 217 main : LISTEN ON STRING { 218 memset(&listen_opts, 0, sizeof listen_opts); 219 listen_opts.ifx = $3; 220 listen_opts.family = AF_UNSPEC; 221 listen_opts.proto = PROTO_LPR; 222 listen_opts.port = htons(PORT_LPR); 223 } listener 224 ; 225 %% 226 227 struct keywords { 228 const char *k_name; 229 int k_val; 230 }; 231 232 int 233 yyerror(const char *fmt, ...) 234 { 235 va_list ap; 236 char *msg; 237 238 file->errors++; 239 va_start(ap, fmt); 240 if (vasprintf(&msg, fmt, ap) == -1) 241 fatalx("yyerror vasprintf"); 242 va_end(ap); 243 log_warnx("%s:%d: %s", file->name, yylval.lineno, msg); 244 free(msg); 245 return (0); 246 } 247 248 int 249 kw_cmp(const void *k, const void *e) 250 { 251 return (strcmp(k, ((const struct keywords *)e)->k_name)); 252 } 253 254 int 255 lookup(char *s) 256 { 257 /* this has to be sorted always */ 258 static const struct keywords keywords[] = { 259 { "include", INCLUDE }, 260 { "inet4", INET4 }, 261 { "inet6", INET6 }, 262 { "listen", LISTEN }, 263 { "on", ON }, 264 { "port", PORT }, 265 { "socket", SOCKET }, 266 }; 267 const struct keywords *p; 268 269 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 270 sizeof(keywords[0]), kw_cmp); 271 272 if (p) 273 return (p->k_val); 274 else 275 return (STRING); 276 } 277 278 #define MAXPUSHBACK 128 279 280 unsigned char *parsebuf; 281 int parseindex; 282 unsigned char pushback_buffer[MAXPUSHBACK]; 283 int pushback_index = 0; 284 285 int 286 lgetc(int quotec) 287 { 288 int c, next; 289 290 if (parsebuf) { 291 /* Read character from the parsebuffer instead of input. */ 292 if (parseindex >= 0) { 293 c = parsebuf[parseindex++]; 294 if (c != '\0') 295 return (c); 296 parsebuf = NULL; 297 } else 298 parseindex++; 299 } 300 301 if (pushback_index) 302 return (pushback_buffer[--pushback_index]); 303 304 if (quotec) { 305 if ((c = getc(file->stream)) == EOF) { 306 yyerror("reached end of file while parsing " 307 "quoted string"); 308 if (file == topfile || popfile() == EOF) 309 return (EOF); 310 return (quotec); 311 } 312 return (c); 313 } 314 315 while ((c = getc(file->stream)) == '\\') { 316 next = getc(file->stream); 317 if (next != '\n') { 318 c = next; 319 break; 320 } 321 yylval.lineno = file->lineno; 322 file->lineno++; 323 } 324 325 while (c == EOF) { 326 if (file == topfile || popfile() == EOF) 327 return (EOF); 328 c = getc(file->stream); 329 } 330 return (c); 331 } 332 333 int 334 lungetc(int c) 335 { 336 if (c == EOF) 337 return (EOF); 338 if (parsebuf) { 339 parseindex--; 340 if (parseindex >= 0) 341 return (c); 342 } 343 if (pushback_index < MAXPUSHBACK-1) 344 return (pushback_buffer[pushback_index++] = c); 345 else 346 return (EOF); 347 } 348 349 int 350 findeol(void) 351 { 352 int c; 353 354 parsebuf = NULL; 355 pushback_index = 0; 356 357 /* skip to either EOF or the first real EOL */ 358 while (1) { 359 c = lgetc(0); 360 if (c == '\n') { 361 file->lineno++; 362 break; 363 } 364 if (c == EOF) 365 break; 366 } 367 return (ERROR); 368 } 369 370 int 371 yylex(void) 372 { 373 unsigned char buf[8096]; 374 unsigned char *p, *val; 375 int quotec, next, c; 376 int token; 377 378 top: 379 p = buf; 380 while ((c = lgetc(0)) == ' ' || c == '\t') 381 ; /* nothing */ 382 383 yylval.lineno = file->lineno; 384 if (c == '#') 385 while ((c = lgetc(0)) != '\n' && c != EOF) 386 ; /* nothing */ 387 if (c == '$' && parsebuf == NULL) { 388 while (1) { 389 if ((c = lgetc(0)) == EOF) 390 return (0); 391 392 if (p + 1 >= buf + sizeof(buf) - 1) { 393 yyerror("string too long"); 394 return (findeol()); 395 } 396 if (isalnum(c) || c == '_') { 397 *p++ = c; 398 continue; 399 } 400 *p = '\0'; 401 lungetc(c); 402 break; 403 } 404 val = symget(buf); 405 if (val == NULL) { 406 yyerror("macro '%s' not defined", buf); 407 return (findeol()); 408 } 409 parsebuf = val; 410 parseindex = 0; 411 goto top; 412 } 413 414 switch (c) { 415 case '\'': 416 case '"': 417 quotec = c; 418 while (1) { 419 if ((c = lgetc(quotec)) == EOF) 420 return (0); 421 if (c == '\n') { 422 file->lineno++; 423 continue; 424 } else if (c == '\\') { 425 if ((next = lgetc(quotec)) == EOF) 426 return (0); 427 if (next == quotec || next == ' ' || 428 next == '\t') 429 c = next; 430 else if (next == '\n') { 431 file->lineno++; 432 continue; 433 } else 434 lungetc(next); 435 } else if (c == quotec) { 436 *p = '\0'; 437 break; 438 } else if (c == '\0') { 439 yyerror("syntax error"); 440 return (findeol()); 441 } 442 if (p + 1 >= buf + sizeof(buf) - 1) { 443 yyerror("string too long"); 444 return (findeol()); 445 } 446 *p++ = c; 447 } 448 yylval.v.string = strdup(buf); 449 if (yylval.v.string == NULL) 450 err(1, "%s", __func__); 451 return (STRING); 452 } 453 454 #define allowed_to_end_number(x) \ 455 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 456 457 if (c == '-' || isdigit(c)) { 458 do { 459 *p++ = c; 460 if ((size_t)(p-buf) >= sizeof(buf)) { 461 yyerror("string too long"); 462 return (findeol()); 463 } 464 } while ((c = lgetc(0)) != EOF && isdigit(c)); 465 lungetc(c); 466 if (p == buf + 1 && buf[0] == '-') 467 goto nodigits; 468 if (c == EOF || allowed_to_end_number(c)) { 469 const char *errstr = NULL; 470 471 *p = '\0'; 472 yylval.v.number = strtonum(buf, LLONG_MIN, 473 LLONG_MAX, &errstr); 474 if (errstr) { 475 yyerror("\"%s\" invalid number: %s", 476 buf, errstr); 477 return (findeol()); 478 } 479 return (NUMBER); 480 } else { 481 nodigits: 482 while (p > buf + 1) 483 lungetc(*--p); 484 c = *--p; 485 if (c == '-') 486 return (c); 487 } 488 } 489 490 if (c == '=') { 491 if ((c = lgetc(0)) != EOF && c == '>') 492 return (ARROW); 493 lungetc(c); 494 c = '='; 495 } 496 497 #define allowed_in_string(x) \ 498 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 499 x != '{' && x != '}' && x != '<' && x != '>' && \ 500 x != '!' && x != '=' && x != '#' && \ 501 x != ',')) 502 503 if (isalnum(c) || c == ':' || c == '_') { 504 do { 505 *p++ = c; 506 if ((size_t)(p-buf) >= sizeof(buf)) { 507 yyerror("string too long"); 508 return (findeol()); 509 } 510 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 511 lungetc(c); 512 *p = '\0'; 513 if ((token = lookup(buf)) == STRING) 514 if ((yylval.v.string = strdup(buf)) == NULL) 515 err(1, "%s", __func__); 516 return (token); 517 } 518 if (c == '\n') { 519 yylval.lineno = file->lineno; 520 file->lineno++; 521 } 522 if (c == EOF) 523 return (0); 524 return (c); 525 } 526 527 int 528 check_file_secrecy(int fd, const char *fname) 529 { 530 struct stat st; 531 532 if (fstat(fd, &st)) { 533 log_warn("warn: cannot stat %s", fname); 534 return (-1); 535 } 536 if (st.st_uid != 0 && st.st_uid != getuid()) { 537 log_warnx("warn: %s: owner not root or current user", fname); 538 return (-1); 539 } 540 if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { 541 log_warnx("warn: %s: group/world readable/writeable", fname); 542 return (-1); 543 } 544 return (0); 545 } 546 547 struct file * 548 pushfile(const char *name, int secret) 549 { 550 struct file *nfile; 551 552 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 553 log_warn("%s", __func__); 554 return (NULL); 555 } 556 if ((nfile->name = strdup(name)) == NULL) { 557 log_warn("%s", __func__); 558 free(nfile); 559 return (NULL); 560 } 561 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 562 log_warn("%s: %s", __func__, nfile->name); 563 free(nfile->name); 564 free(nfile); 565 return (NULL); 566 } else if (secret && 567 check_file_secrecy(fileno(nfile->stream), nfile->name)) { 568 fclose(nfile->stream); 569 free(nfile->name); 570 free(nfile); 571 return (NULL); 572 } 573 nfile->lineno = 1; 574 TAILQ_INSERT_TAIL(&files, nfile, entry); 575 return (nfile); 576 } 577 578 int 579 popfile(void) 580 { 581 struct file *prev; 582 583 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 584 prev->errors += file->errors; 585 586 TAILQ_REMOVE(&files, file, entry); 587 fclose(file->stream); 588 free(file->name); 589 free(file); 590 file = prev; 591 return (file ? 0 : EOF); 592 } 593 594 struct lpd_conf * 595 parse_config(const char *filename, int verbose) 596 { 597 struct sym *sym, *next; 598 599 conf = calloc(1, sizeof(*conf)); 600 if (conf == NULL) 601 return NULL; 602 TAILQ_INIT(&conf->listeners); 603 604 errors = 0; 605 606 if ((file = pushfile(filename, 0)) == NULL) { 607 config_free(conf); 608 return NULL; 609 } 610 topfile = file; 611 612 /* 613 * parse configuration 614 */ 615 setservent(1); 616 yyparse(); 617 errors = file->errors; 618 popfile(); 619 endservent(); 620 621 /* Free macros and check which have not been used. */ 622 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { 623 if ((verbose) && !sym->used) 624 log_warnx("warning: macro '%s' not used\n", sym->nam); 625 if (!sym->persist) { 626 free(sym->nam); 627 free(sym->val); 628 TAILQ_REMOVE(&symhead, sym, entry); 629 free(sym); 630 } 631 } 632 633 if (errors) { 634 config_free(conf); 635 return NULL; 636 } 637 638 return conf; 639 } 640 641 int 642 symset(const char *nam, const char *val, int persist) 643 { 644 struct sym *sym; 645 646 TAILQ_FOREACH(sym, &symhead, entry) { 647 if (strcmp(nam, sym->nam) == 0) 648 break; 649 } 650 651 if (sym != NULL) { 652 if (sym->persist == 1) 653 return (0); 654 else { 655 free(sym->nam); 656 free(sym->val); 657 TAILQ_REMOVE(&symhead, sym, entry); 658 free(sym); 659 } 660 } 661 if ((sym = calloc(1, sizeof(*sym))) == NULL) 662 return (-1); 663 664 sym->nam = strdup(nam); 665 if (sym->nam == NULL) { 666 free(sym); 667 return (-1); 668 } 669 sym->val = strdup(val); 670 if (sym->val == NULL) { 671 free(sym->nam); 672 free(sym); 673 return (-1); 674 } 675 sym->used = 0; 676 sym->persist = persist; 677 TAILQ_INSERT_TAIL(&symhead, sym, entry); 678 return (0); 679 } 680 681 int 682 cmdline_symset(char *s) 683 { 684 char *sym, *val; 685 int ret; 686 687 if ((val = strrchr(s, '=')) == NULL) 688 return (-1); 689 sym = strndup(s, val - s); 690 if (sym == NULL) 691 errx(1, "%s: strndup", __func__); 692 ret = symset(sym, val + 1, 1); 693 free(sym); 694 695 return (ret); 696 } 697 698 char * 699 symget(const char *nam) 700 { 701 struct sym *sym; 702 703 TAILQ_FOREACH(sym, &symhead, entry) { 704 if (strcmp(nam, sym->nam) == 0) { 705 sym->used = 1; 706 return (sym->val); 707 } 708 } 709 return (NULL); 710 } 711 712 static void 713 config_free(struct lpd_conf *c) 714 { 715 struct listener *l; 716 717 while ((l = TAILQ_FIRST(&c->listeners))) { 718 TAILQ_REMOVE(&c->listeners, l, entry); 719 free(l); 720 } 721 free(c); 722 } 723 724 static void 725 create_listeners(struct listen_opts *lo) 726 { 727 if (local(lo)) 728 return; 729 if (interface(lo)) 730 return; 731 if (host_v4(lo)) 732 return; 733 if (host_v6(lo)) 734 return; 735 if (host_dns(lo)) 736 return; 737 738 errx(1, "invalid virtual ip or interface: %s", lo->ifx); 739 } 740 741 static void 742 config_listener(struct listener *l, struct listen_opts *lo) 743 { 744 l->sock = -1; 745 l->proto = lo->proto; 746 747 TAILQ_INSERT_TAIL(&conf->listeners, l, entry); 748 } 749 750 static int 751 local(struct listen_opts *lo) 752 { 753 struct sockaddr_un *sun; 754 struct listener *h; 755 756 if (lo->family != AF_UNSPEC && lo->family != AF_LOCAL) 757 return 0; 758 759 if (lo->ifx[0] != '/') 760 return 0; 761 762 h = calloc(1, sizeof(*h)); 763 sun = (struct sockaddr_un *)&h->ss; 764 sun->sun_len = sizeof(*sun); 765 sun->sun_family = AF_LOCAL; 766 if (strlcpy(sun->sun_path, lo->ifx, sizeof(sun->sun_path)) 767 >= sizeof(sun->sun_path)) 768 fatalx("path too long"); 769 770 config_listener(h, lo); 771 772 return (1); 773 } 774 775 static int 776 host_v4(struct listen_opts *lo) 777 { 778 struct in_addr ina; 779 struct sockaddr_in *sain; 780 struct listener *h; 781 782 if (lo->family != AF_UNSPEC && lo->family != AF_INET) 783 return (0); 784 785 memset(&ina, 0, sizeof(ina)); 786 if (inet_pton(AF_INET, lo->ifx, &ina) != 1) 787 return (0); 788 789 h = calloc(1, sizeof(*h)); 790 sain = (struct sockaddr_in *)&h->ss; 791 sain->sin_len = sizeof(struct sockaddr_in); 792 sain->sin_family = AF_INET; 793 sain->sin_addr.s_addr = ina.s_addr; 794 sain->sin_port = lo->port; 795 796 config_listener(h, lo); 797 798 return (1); 799 } 800 801 static int 802 host_v6(struct listen_opts *lo) 803 { 804 struct in6_addr ina6; 805 struct sockaddr_in6 *sin6; 806 struct listener *h; 807 808 if (lo->family != AF_UNSPEC && lo->family != AF_INET6) 809 return (0); 810 811 memset(&ina6, 0, sizeof(ina6)); 812 if (inet_pton(AF_INET6, lo->ifx, &ina6) != 1) 813 return (0); 814 815 h = calloc(1, sizeof(*h)); 816 sin6 = (struct sockaddr_in6 *)&h->ss; 817 sin6->sin6_len = sizeof(struct sockaddr_in6); 818 sin6->sin6_family = AF_INET6; 819 sin6->sin6_port = lo->port; 820 memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6)); 821 822 config_listener(h, lo); 823 824 return (1); 825 } 826 827 static int 828 host_dns(struct listen_opts *lo) 829 { 830 struct addrinfo hints, *res0, *res; 831 int error, cnt = 0; 832 struct sockaddr_in *sain; 833 struct sockaddr_in6 *sin6; 834 struct listener *h; 835 836 memset(&hints, 0, sizeof(hints)); 837 hints.ai_family = lo->family; 838 hints.ai_socktype = SOCK_STREAM; 839 hints.ai_flags = AI_ADDRCONFIG; 840 error = getaddrinfo(lo->ifx, NULL, &hints, &res0); 841 if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME) 842 return (0); 843 if (error) { 844 log_warnx("warn: host_dns: could not parse \"%s\": %s", lo->ifx, 845 gai_strerror(error)); 846 return (-1); 847 } 848 849 for (res = res0; res; res = res->ai_next) { 850 if (res->ai_family != AF_INET && 851 res->ai_family != AF_INET6) 852 continue; 853 h = calloc(1, sizeof(*h)); 854 h->ss.ss_family = res->ai_family; 855 if (res->ai_family == AF_INET) { 856 sain = (struct sockaddr_in *)&h->ss; 857 sain->sin_len = sizeof(struct sockaddr_in); 858 sain->sin_addr.s_addr = ((struct sockaddr_in *) 859 res->ai_addr)->sin_addr.s_addr; 860 sain->sin_port = lo->port; 861 } else { 862 sin6 = (struct sockaddr_in6 *)&h->ss; 863 sin6->sin6_len = sizeof(struct sockaddr_in6); 864 memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *) 865 res->ai_addr)->sin6_addr, sizeof(struct in6_addr)); 866 sin6->sin6_port = lo->port; 867 } 868 869 config_listener(h, lo); 870 871 cnt++; 872 } 873 874 freeaddrinfo(res0); 875 return (cnt); 876 } 877 878 static int 879 interface(struct listen_opts *lo) 880 { 881 struct ifaddrs *ifap, *p; 882 struct sockaddr_in *sain; 883 struct sockaddr_in6 *sin6; 884 struct listener *h; 885 int ret = 0; 886 887 if (getifaddrs(&ifap) == -1) 888 fatal("getifaddrs"); 889 890 for (p = ifap; p != NULL; p = p->ifa_next) { 891 if (p->ifa_addr == NULL) 892 continue; 893 if (strcmp(p->ifa_name, lo->ifx) != 0 && 894 !is_if_in_group(p->ifa_name, lo->ifx)) 895 continue; 896 if (lo->family != AF_UNSPEC && lo->family != p->ifa_addr->sa_family) 897 continue; 898 899 h = calloc(1, sizeof(*h)); 900 901 switch (p->ifa_addr->sa_family) { 902 case AF_INET: 903 sain = (struct sockaddr_in *)&h->ss; 904 *sain = *(struct sockaddr_in *)p->ifa_addr; 905 sain->sin_len = sizeof(struct sockaddr_in); 906 sain->sin_port = lo->port; 907 break; 908 909 case AF_INET6: 910 sin6 = (struct sockaddr_in6 *)&h->ss; 911 *sin6 = *(struct sockaddr_in6 *)p->ifa_addr; 912 sin6->sin6_len = sizeof(struct sockaddr_in6); 913 sin6->sin6_port = lo->port; 914 break; 915 916 default: 917 free(h); 918 continue; 919 } 920 921 config_listener(h, lo); 922 ret = 1; 923 } 924 925 freeifaddrs(ifap); 926 927 return ret; 928 } 929 930 static int 931 is_if_in_group(const char *ifname, const char *groupname) 932 { 933 unsigned int len; 934 struct ifgroupreq ifgr; 935 struct ifg_req *ifg; 936 int s; 937 int ret = 0; 938 939 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 940 err(1, "socket"); 941 942 memset(&ifgr, 0, sizeof(ifgr)); 943 if (strlcpy(ifgr.ifgr_name, ifname, IFNAMSIZ) >= IFNAMSIZ) 944 errx(1, "interface name too large"); 945 946 if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) { 947 if (errno == EINVAL || errno == ENOTTY) 948 goto end; 949 err(1, "SIOCGIFGROUP"); 950 } 951 952 len = ifgr.ifgr_len; 953 ifgr.ifgr_groups = calloc(len/sizeof(struct ifg_req), 954 sizeof(struct ifg_req)); 955 if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) 956 err(1, "SIOCGIFGROUP"); 957 958 ifg = ifgr.ifgr_groups; 959 for (; ifg && len >= sizeof(struct ifg_req); ifg++) { 960 len -= sizeof(struct ifg_req); 961 if (strcmp(ifg->ifgrq_group, groupname) == 0) { 962 ret = 1; 963 break; 964 } 965 } 966 free(ifgr.ifgr_groups); 967 968 end: 969 close(s); 970 return ret; 971 } 972