parse.y revision 1.15
1/* $OpenBSD: parse.y,v 1.15 2013/06/03 16:53:49 claudio Exp $ */ 2 3/* 4 * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> 5 * Copyright (c) 2004 Ryan McBride <mcbride@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 <netinet/in.h> 29#include <arpa/inet.h> 30#include <ctype.h> 31#include <err.h> 32#include <errno.h> 33#include <unistd.h> 34#include <ifaddrs.h> 35#include <net/if_types.h> 36#include <limits.h> 37#include <stdarg.h> 38#include <stdio.h> 39#include <string.h> 40#include <syslog.h> 41 42#include "ldp.h" 43#include "ldpd.h" 44#include "ldpe.h" 45#include "log.h" 46 47TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 48static struct file { 49 TAILQ_ENTRY(file) entry; 50 FILE *stream; 51 char *name; 52 int lineno; 53 int errors; 54} *file, *topfile; 55struct file *pushfile(const char *, int); 56int popfile(void); 57int check_file_secrecy(int, const char *); 58int yyparse(void); 59int yylex(void); 60int yyerror(const char *, ...); 61int kw_cmp(const void *, const void *); 62int lookup(char *); 63int lgetc(int); 64int lungetc(int); 65int findeol(void); 66 67TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 68struct sym { 69 TAILQ_ENTRY(sym) entry; 70 int used; 71 int persist; 72 char *nam; 73 char *val; 74}; 75 76int symset(const char *, const char *, int); 77char *symget(const char *); 78 79void clear_config(struct ldpd_conf *xconf); 80u_int32_t get_rtr_id(void); 81int host(const char *, struct in_addr *, struct in_addr *); 82 83static struct ldpd_conf *conf; 84static int errors = 0; 85 86struct iface *iface = NULL; 87 88struct config_defaults { 89 u_int16_t holdtime; 90 u_int16_t hello_interval; 91}; 92 93struct config_defaults globaldefs; 94struct config_defaults ifacedefs; 95struct config_defaults *defs; 96 97struct iface *conf_get_if(struct kif *); 98 99typedef struct { 100 union { 101 int64_t number; 102 char *string; 103 } v; 104 int lineno; 105} YYSTYPE; 106 107%} 108 109%token INTERFACE ROUTERID FIBUPDATE 110%token HOLDTIME HELLOINTERVAL KEEPALIVE 111%token DISTRIBUTION RETENTION ADVERTISEMENT 112%token EXTTAG 113%token HELLOINTERVAL 114%token YES NO 115%token ERROR 116%token <v.string> STRING 117%token <v.number> NUMBER 118%type <v.number> yesno 119%type <v.string> string 120 121%% 122 123grammar : /* empty */ 124 | grammar '\n' 125 | grammar conf_main '\n' 126 | grammar varset '\n' 127 | grammar interface '\n' 128 | grammar error '\n' { file->errors++; } 129 ; 130 131string : string STRING { 132 if (asprintf(&$$, "%s %s", $1, $2) == -1) { 133 free($1); 134 free($2); 135 yyerror("string: asprintf"); 136 YYERROR; 137 } 138 free($1); 139 free($2); 140 } 141 | STRING 142 ; 143 144yesno : YES { $$ = 1; } 145 | NO { $$ = 0; } 146 ; 147 148varset : STRING '=' string { 149 if (conf->opts & LDPD_OPT_VERBOSE) 150 printf("%s = \"%s\"\n", $1, $3); 151 if (symset($1, $3, 0) == -1) 152 fatal("cannot store variable"); 153 free($1); 154 free($3); 155 } 156 ; 157 158conf_main : ROUTERID STRING { 159 if (!inet_aton($2, &conf->rtr_id)) { 160 yyerror("error parsing router-id"); 161 free($2); 162 YYERROR; 163 } 164 free($2); 165 } 166 | FIBUPDATE yesno { 167 if ($2 == 0) 168 conf->flags |= LDPD_FLAG_NO_FIB_UPDATE; 169 else 170 conf->flags &= ~LDPD_FLAG_NO_FIB_UPDATE; 171 } 172 | DISTRIBUTION STRING { 173 conf->mode &= ~(MODE_DIST_INDEPENDENT | 174 MODE_DIST_ORDERED); 175 176 if (!strcmp($2, "independent")) 177 conf->mode |= MODE_DIST_INDEPENDENT; 178 else if (!strcmp($2, "ordered")) 179 conf->mode |= MODE_DIST_ORDERED; 180 else { 181 yyerror("unknown distribution type"); 182 free($2); 183 YYERROR; 184 } 185 } 186 | RETENTION STRING { 187 conf->mode &= ~(MODE_RET_CONSERVATIVE | 188 MODE_RET_LIBERAL); 189 190 if (!strcmp($2, "conservative")) 191 conf->mode |= MODE_RET_CONSERVATIVE; 192 else if (!strcmp($2, "liberal")) 193 conf->mode |= MODE_RET_LIBERAL; 194 else { 195 yyerror("unknown retention type"); 196 free($2); 197 YYERROR; 198 } 199 } 200 | ADVERTISEMENT STRING { 201 conf->mode &= ~(MODE_ADV_ONDEMAND | 202 MODE_ADV_UNSOLICITED); 203 204 if (!strcmp($2, "ondemand")) 205 conf->mode |= MODE_ADV_ONDEMAND; 206 else if (!strcmp($2, "unsolicited")) 207 conf->mode |= MODE_ADV_UNSOLICITED; 208 else { 209 yyerror("unknown retention type"); 210 free($2); 211 YYERROR; 212 } 213 } 214 | KEEPALIVE NUMBER { 215 if ($2 < MIN_KEEPALIVE || 216 $2 > MAX_KEEPALIVE) { 217 yyerror("keepalive out of range (%d-%d)", 218 MIN_KEEPALIVE, MAX_KEEPALIVE); 219 YYERROR; 220 } 221 conf->keepalive = $2; 222 } 223 | defaults 224 ; 225defaults : HOLDTIME NUMBER { 226 if ($2 < MIN_HOLDTIME || 227 $2 > MAX_HOLDTIME) { 228 yyerror("holdtime out of range (%d-%d)", 229 MIN_HOLDTIME, MAX_HOLDTIME); 230 YYERROR; 231 } 232 defs->holdtime = $2; 233 } 234 | HELLOINTERVAL NUMBER { 235 if ($2 < MIN_HELLO_INTERVAL || 236 $2 > MAX_HELLO_INTERVAL) { 237 yyerror("hello-interval out of range (%d-%d)", 238 MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL); 239 YYERROR; 240 } 241 defs->hello_interval = $2; 242 } 243 ; 244 245optnl : '\n' optnl 246 | 247 ; 248 249nl : '\n' optnl /* one newline or more */ 250 ; 251 252interface : INTERFACE STRING { 253 struct kif *kif; 254 255 if ((kif = kif_findname($2)) == NULL) { 256 yyerror("unknown interface %s", $2); 257 free($2); 258 YYERROR; 259 } 260 free($2); 261 iface = conf_get_if(kif); 262 if (iface == NULL) 263 YYERROR; 264 if (iface->media_type == IFT_LOOP || 265 iface->media_type == IFT_CARP) { 266 yyerror("unsupported interface type on " 267 "interface %s", iface->name); 268 YYERROR; 269 } 270 LIST_INSERT_HEAD(&conf->iface_list, iface, entry); 271 272 memcpy(&ifacedefs, defs, sizeof(ifacedefs)); 273 defs = &ifacedefs; 274 } interface_block { 275 iface->holdtime = defs->holdtime; 276 iface->hello_interval = defs->hello_interval; 277 iface = NULL; 278 279 defs = &globaldefs; 280 } 281 ; 282 283interface_block : '{' optnl interfaceopts_l '}' 284 | '{' optnl '}' 285 | /* nothing */ 286 ; 287 288interfaceopts_l : interfaceopts_l defaults nl 289 | defaults optnl 290 ; 291 292%% 293 294struct keywords { 295 const char *k_name; 296 int k_val; 297}; 298 299int 300yyerror(const char *fmt, ...) 301{ 302 va_list ap; 303 char *nfmt; 304 305 file->errors++; 306 va_start(ap, fmt); 307 if (asprintf(&nfmt, "%s:%d: %s", file->name, yylval.lineno, fmt) == -1) 308 fatalx("yyerror asprintf"); 309 vlog(LOG_CRIT, nfmt, ap); 310 va_end(ap); 311 free(nfmt); 312 return (0); 313} 314 315int 316kw_cmp(const void *k, const void *e) 317{ 318 return (strcmp(k, ((const struct keywords *)e)->k_name)); 319} 320 321int 322lookup(char *s) 323{ 324 /* this has to be sorted always */ 325 static const struct keywords keywords[] = { 326 {"advertisement", ADVERTISEMENT}, 327 {"distribution", DISTRIBUTION}, 328 {"external-tag", EXTTAG}, 329 {"fib-update", FIBUPDATE}, 330 {"hello-interval", HELLOINTERVAL}, 331 {"holdtime", HOLDTIME}, 332 {"interface", INTERFACE}, 333 {"keepalive", KEEPALIVE}, 334 {"retention", RETENTION}, 335 {"router-id", ROUTERID}, 336 {"yes", YES} 337 }; 338 const struct keywords *p; 339 340 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 341 sizeof(keywords[0]), kw_cmp); 342 343 if (p) 344 return (p->k_val); 345 else 346 return (STRING); 347} 348 349#define MAXPUSHBACK 128 350 351char *parsebuf; 352int parseindex; 353char pushback_buffer[MAXPUSHBACK]; 354int pushback_index = 0; 355 356int 357lgetc(int quotec) 358{ 359 int c, next; 360 361 if (parsebuf) { 362 /* Read character from the parsebuffer instead of input. */ 363 if (parseindex >= 0) { 364 c = parsebuf[parseindex++]; 365 if (c != '\0') 366 return (c); 367 parsebuf = NULL; 368 } else 369 parseindex++; 370 } 371 372 if (pushback_index) 373 return (pushback_buffer[--pushback_index]); 374 375 if (quotec) { 376 if ((c = getc(file->stream)) == EOF) { 377 yyerror("reached end of file while parsing " 378 "quoted string"); 379 if (file == topfile || popfile() == EOF) 380 return (EOF); 381 return (quotec); 382 } 383 return (c); 384 } 385 386 while ((c = getc(file->stream)) == '\\') { 387 next = getc(file->stream); 388 if (next != '\n') { 389 c = next; 390 break; 391 } 392 yylval.lineno = file->lineno; 393 file->lineno++; 394 } 395 396 while (c == EOF) { 397 if (file == topfile || popfile() == EOF) 398 return (EOF); 399 c = getc(file->stream); 400 } 401 return (c); 402} 403 404int 405lungetc(int c) 406{ 407 if (c == EOF) 408 return (EOF); 409 if (parsebuf) { 410 parseindex--; 411 if (parseindex >= 0) 412 return (c); 413 } 414 if (pushback_index < MAXPUSHBACK-1) 415 return (pushback_buffer[pushback_index++] = c); 416 else 417 return (EOF); 418} 419 420int 421findeol(void) 422{ 423 int c; 424 425 parsebuf = NULL; 426 pushback_index = 0; 427 428 /* skip to either EOF or the first real EOL */ 429 while (1) { 430 c = lgetc(0); 431 if (c == '\n') { 432 file->lineno++; 433 break; 434 } 435 if (c == EOF) 436 break; 437 } 438 return (ERROR); 439} 440 441int 442yylex(void) 443{ 444 char buf[8096]; 445 char *p, *val; 446 int quotec, next, c; 447 int token; 448 449top: 450 p = buf; 451 while ((c = lgetc(0)) == ' ' || c == '\t') 452 ; /* nothing */ 453 454 yylval.lineno = file->lineno; 455 if (c == '#') 456 while ((c = lgetc(0)) != '\n' && c != EOF) 457 ; /* nothing */ 458 if (c == '$' && parsebuf == NULL) { 459 while (1) { 460 if ((c = lgetc(0)) == EOF) 461 return (0); 462 463 if (p + 1 >= buf + sizeof(buf) - 1) { 464 yyerror("string too long"); 465 return (findeol()); 466 } 467 if (isalnum(c) || c == '_') { 468 *p++ = (char)c; 469 continue; 470 } 471 *p = '\0'; 472 lungetc(c); 473 break; 474 } 475 val = symget(buf); 476 if (val == NULL) { 477 yyerror("macro '%s' not defined", buf); 478 return (findeol()); 479 } 480 parsebuf = val; 481 parseindex = 0; 482 goto top; 483 } 484 485 switch (c) { 486 case '\'': 487 case '"': 488 quotec = c; 489 while (1) { 490 if ((c = lgetc(quotec)) == EOF) 491 return (0); 492 if (c == '\n') { 493 file->lineno++; 494 continue; 495 } else if (c == '\\') { 496 if ((next = lgetc(quotec)) == EOF) 497 return (0); 498 if (next == quotec || c == ' ' || c == '\t') 499 c = next; 500 else if (next == '\n') { 501 file->lineno++; 502 continue; 503 } else 504 lungetc(next); 505 } else if (c == quotec) { 506 *p = '\0'; 507 break; 508 } 509 if (p + 1 >= buf + sizeof(buf) - 1) { 510 yyerror("string too long"); 511 return (findeol()); 512 } 513 *p++ = (char)c; 514 } 515 yylval.v.string = strdup(buf); 516 if (yylval.v.string == NULL) 517 err(1, "yylex: strdup"); 518 return (STRING); 519 } 520 521#define allowed_to_end_number(x) \ 522 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 523 524 if (c == '-' || isdigit(c)) { 525 do { 526 *p++ = c; 527 if ((unsigned)(p-buf) >= sizeof(buf)) { 528 yyerror("string too long"); 529 return (findeol()); 530 } 531 } while ((c = lgetc(0)) != EOF && isdigit(c)); 532 lungetc(c); 533 if (p == buf + 1 && buf[0] == '-') 534 goto nodigits; 535 if (c == EOF || allowed_to_end_number(c)) { 536 const char *errstr = NULL; 537 538 *p = '\0'; 539 yylval.v.number = strtonum(buf, LLONG_MIN, 540 LLONG_MAX, &errstr); 541 if (errstr) { 542 yyerror("\"%s\" invalid number: %s", 543 buf, errstr); 544 return (findeol()); 545 } 546 return (NUMBER); 547 } else { 548nodigits: 549 while (p > buf + 1) 550 lungetc(*--p); 551 c = *--p; 552 if (c == '-') 553 return (c); 554 } 555 } 556 557#define allowed_in_string(x) \ 558 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 559 x != '{' && x != '}' && \ 560 x != '!' && x != '=' && x != '#' && \ 561 x != ',')) 562 563 if (isalnum(c) || c == ':' || c == '_') { 564 do { 565 *p++ = c; 566 if ((unsigned)(p-buf) >= sizeof(buf)) { 567 yyerror("string too long"); 568 return (findeol()); 569 } 570 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 571 lungetc(c); 572 *p = '\0'; 573 if ((token = lookup(buf)) == STRING) 574 if ((yylval.v.string = strdup(buf)) == NULL) 575 err(1, "yylex: strdup"); 576 return (token); 577 } 578 if (c == '\n') { 579 yylval.lineno = file->lineno; 580 file->lineno++; 581 } 582 if (c == EOF) 583 return (0); 584 return (c); 585} 586 587int 588check_file_secrecy(int fd, const char *fname) 589{ 590 struct stat st; 591 592 if (fstat(fd, &st)) { 593 log_warn("cannot stat %s", fname); 594 return (-1); 595 } 596 if (st.st_uid != 0 && st.st_uid != getuid()) { 597 log_warnx("%s: owner not root or current user", fname); 598 return (-1); 599 } 600 if (st.st_mode & (S_IRWXG | S_IRWXO)) { 601 log_warnx("%s: group/world readable/writeable", fname); 602 return (-1); 603 } 604 return (0); 605} 606 607struct file * 608pushfile(const char *name, int secret) 609{ 610 struct file *nfile; 611 612 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 613 log_warn("malloc"); 614 return (NULL); 615 } 616 if ((nfile->name = strdup(name)) == NULL) { 617 log_warn("strdup"); 618 free(nfile); 619 return (NULL); 620 } 621 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 622 log_warn("%s", nfile->name); 623 free(nfile->name); 624 free(nfile); 625 return (NULL); 626 } else if (secret && 627 check_file_secrecy(fileno(nfile->stream), nfile->name)) { 628 fclose(nfile->stream); 629 free(nfile->name); 630 free(nfile); 631 return (NULL); 632 } 633 nfile->lineno = 1; 634 TAILQ_INSERT_TAIL(&files, nfile, entry); 635 return (nfile); 636} 637 638int 639popfile(void) 640{ 641 struct file *prev; 642 643 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 644 prev->errors += file->errors; 645 646 TAILQ_REMOVE(&files, file, entry); 647 fclose(file->stream); 648 free(file->name); 649 free(file); 650 file = prev; 651 return (file ? 0 : EOF); 652} 653 654struct ldpd_conf * 655parse_config(char *filename, int opts) 656{ 657 struct sym *sym, *next; 658 659 if ((conf = calloc(1, sizeof(struct ldpd_conf))) == NULL) 660 fatal("parse_config"); 661 conf->opts = opts; 662 conf->keepalive = DEFAULT_KEEPALIVE; 663 664 bzero(&globaldefs, sizeof(globaldefs)); 665 defs = &globaldefs; 666 defs->holdtime = DEFAULT_HOLDTIME; 667 defs->hello_interval = DEFAULT_HELLO_INTERVAL; 668 669 conf->mode = (MODE_DIST_INDEPENDENT | MODE_RET_LIBERAL | 670 MODE_ADV_UNSOLICITED); 671 672 if ((file = pushfile(filename, !(conf->opts & LDPD_OPT_NOACTION))) == NULL) { 673 free(conf); 674 return (NULL); 675 } 676 topfile = file; 677 678 yyparse(); 679 errors = file->errors; 680 popfile(); 681 682 /* Free macros and check which have not been used. */ 683 for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { 684 next = TAILQ_NEXT(sym, entry); 685 if ((conf->opts & LDPD_OPT_VERBOSE2) && !sym->used) 686 fprintf(stderr, "warning: macro '%s' not " 687 "used\n", sym->nam); 688 if (!sym->persist) { 689 free(sym->nam); 690 free(sym->val); 691 TAILQ_REMOVE(&symhead, sym, entry); 692 free(sym); 693 } 694 } 695 696 /* free global config defaults */ 697 if (errors) { 698 clear_config(conf); 699 return (NULL); 700 } 701 702 if (conf->rtr_id.s_addr == 0) 703 conf->rtr_id.s_addr = get_rtr_id(); 704 705 return (conf); 706} 707 708int 709symset(const char *nam, const char *val, int persist) 710{ 711 struct sym *sym; 712 713 for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); 714 sym = TAILQ_NEXT(sym, entry)) 715 ; /* nothing */ 716 717 if (sym != NULL) { 718 if (sym->persist == 1) 719 return (0); 720 else { 721 free(sym->nam); 722 free(sym->val); 723 TAILQ_REMOVE(&symhead, sym, entry); 724 free(sym); 725 } 726 } 727 if ((sym = calloc(1, sizeof(*sym))) == NULL) 728 return (-1); 729 730 sym->nam = strdup(nam); 731 if (sym->nam == NULL) { 732 free(sym); 733 return (-1); 734 } 735 sym->val = strdup(val); 736 if (sym->val == NULL) { 737 free(sym->nam); 738 free(sym); 739 return (-1); 740 } 741 sym->used = 0; 742 sym->persist = persist; 743 TAILQ_INSERT_TAIL(&symhead, sym, entry); 744 return (0); 745} 746 747int 748cmdline_symset(char *s) 749{ 750 char *sym, *val; 751 int ret; 752 size_t len; 753 754 if ((val = strrchr(s, '=')) == NULL) 755 return (-1); 756 757 len = strlen(s) - strlen(val) + 1; 758 if ((sym = malloc(len)) == NULL) 759 errx(1, "cmdline_symset: malloc"); 760 761 strlcpy(sym, s, len); 762 763 ret = symset(sym, val + 1, 1); 764 free(sym); 765 766 return (ret); 767} 768 769char * 770symget(const char *nam) 771{ 772 struct sym *sym; 773 774 TAILQ_FOREACH(sym, &symhead, entry) 775 if (strcmp(nam, sym->nam) == 0) { 776 sym->used = 1; 777 return (sym->val); 778 } 779 return (NULL); 780} 781 782struct iface * 783conf_get_if(struct kif *kif) 784{ 785 struct iface *i; 786 787 LIST_FOREACH(i, &conf->iface_list, entry) { 788 if (i->ifindex == kif->ifindex) { 789 yyerror("interface %s already configured", 790 kif->ifname); 791 return (NULL); 792 } 793 } 794 795 i = if_new(kif); 796 797 return (i); 798} 799 800void 801clear_config(struct ldpd_conf *xconf) 802{ 803 struct iface *i; 804 805 while ((i = LIST_FIRST(&conf->iface_list)) != NULL) { 806 LIST_REMOVE(i, entry); 807 if_del(i); 808 } 809 810 free(xconf); 811} 812 813u_int32_t 814get_rtr_id(void) 815{ 816 struct ifaddrs *ifap, *ifa; 817 u_int32_t ip = 0, cur, localnet; 818 819 localnet = htonl(INADDR_LOOPBACK & IN_CLASSA_NET); 820 821 if (getifaddrs(&ifap) == -1) 822 fatal("getifaddrs"); 823 824 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 825 if (strncmp(ifa->ifa_name, "carp", 4) == 0) 826 continue; 827 if (ifa->ifa_addr->sa_family != AF_INET) 828 continue; 829 cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; 830 if ((cur & localnet) == localnet) /* skip 127/8 */ 831 continue; 832 if (cur > ip || ip == 0) 833 ip = cur; 834 } 835 freeifaddrs(ifap); 836 837 if (ip == 0) 838 fatal("router-id is 0.0.0.0"); 839 840 return (ip); 841} 842 843int 844host(const char *s, struct in_addr *addr, struct in_addr *mask) 845{ 846 struct in_addr ina; 847 int bits = 32; 848 849 bzero(&ina, sizeof(struct in_addr)); 850 if (strrchr(s, '/') != NULL) { 851 if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1) 852 return (0); 853 } else { 854 if (inet_pton(AF_INET, s, &ina) != 1) 855 return (0); 856 } 857 858 addr->s_addr = ina.s_addr; 859 mask->s_addr = prefixlen2mask(bits); 860 861 return (1); 862} 863