1 /* $OpenBSD: parse.y,v 1.78 2021/10/15 15:01:28 naddy Exp $ */ 2 3 /* 4 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 5 * Copyright (c) 2001 Markus Friedl. All rights reserved. 6 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 7 * Copyright (c) 2001 Theo de Raadt. All rights reserved. 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 %{ 23 #include <sys/types.h> 24 #include <sys/socket.h> 25 #include <netinet/in.h> 26 #include <arpa/inet.h> 27 28 #include <ctype.h> 29 #include <errno.h> 30 #include <limits.h> 31 #include <stdarg.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <syslog.h> 36 37 #include "ntpd.h" 38 39 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 40 static struct file { 41 TAILQ_ENTRY(file) entry; 42 FILE *stream; 43 char *name; 44 int lineno; 45 int errors; 46 } *file, *topfile; 47 struct file *pushfile(const char *); 48 int popfile(void); 49 int yyparse(void); 50 int yylex(void); 51 int yyerror(const char *, ...) 52 __attribute__((__format__ (printf, 1, 2))) 53 __attribute__((__nonnull__ (1))); 54 int kw_cmp(const void *, const void *); 55 int lookup(char *); 56 int lgetc(int); 57 int lungetc(int); 58 int findeol(void); 59 60 struct sockaddr_in query_addr4; 61 struct sockaddr_in6 query_addr6; 62 int poolseqnum; 63 64 struct opts { 65 int weight; 66 int correction; 67 int stratum; 68 int rtable; 69 int trusted; 70 char *refstr; 71 } opts; 72 void opts_default(void); 73 74 typedef struct { 75 union { 76 int64_t number; 77 char *string; 78 struct ntp_addr_wrap *addr; 79 struct opts opts; 80 } v; 81 int lineno; 82 } YYSTYPE; 83 84 %} 85 86 %token LISTEN ON CONSTRAINT CONSTRAINTS FROM QUERY TRUSTED 87 %token SERVER SERVERS SENSOR CORRECTION RTABLE REFID STRATUM WEIGHT 88 %token ERROR 89 %token <v.string> STRING 90 %token <v.number> NUMBER 91 %type <v.addr> address url urllist 92 %type <v.opts> listen_opts listen_opts_l listen_opt 93 %type <v.opts> server_opts server_opts_l server_opt 94 %type <v.opts> sensor_opts sensor_opts_l sensor_opt 95 %type <v.opts> correction 96 %type <v.opts> rtable 97 %type <v.opts> refid 98 %type <v.opts> stratum 99 %type <v.opts> weight 100 %type <v.opts> trusted 101 %% 102 103 grammar : /* empty */ 104 | grammar '\n' 105 | grammar main '\n' 106 | grammar error '\n' { file->errors++; } 107 ; 108 109 main : LISTEN ON address listen_opts { 110 struct listen_addr *la; 111 struct ntp_addr *h, *next; 112 113 if ((h = $3->a) == NULL && 114 (host_dns($3->name, 0, &h) == -1 || !h)) { 115 yyerror("could not resolve \"%s\"", $3->name); 116 free($3->name); 117 free($3); 118 YYERROR; 119 } 120 121 for (; h != NULL; h = next) { 122 next = h->next; 123 la = calloc(1, sizeof(struct listen_addr)); 124 if (la == NULL) 125 fatal("listen on calloc"); 126 la->fd = -1; 127 la->rtable = $4.rtable; 128 memcpy(&la->sa, &h->ss, 129 sizeof(struct sockaddr_storage)); 130 TAILQ_INSERT_TAIL(&conf->listen_addrs, la, 131 entry); 132 free(h); 133 } 134 free($3->name); 135 free($3); 136 } 137 | QUERY FROM STRING { 138 struct sockaddr_in sin4; 139 struct sockaddr_in6 sin6; 140 141 memset(&sin4, 0, sizeof(sin4)); 142 sin4.sin_family = AF_INET; 143 sin4.sin_len = sizeof(struct sockaddr_in); 144 memset(&sin6, 0, sizeof(sin6)); 145 sin6.sin6_family = AF_INET6; 146 sin6.sin6_len = sizeof(struct sockaddr_in6); 147 148 if (inet_pton(AF_INET, $3, &sin4.sin_addr) == 1) 149 memcpy(&query_addr4, &sin4, sin4.sin_len); 150 else if (inet_pton(AF_INET6, $3, &sin6.sin6_addr) == 1) 151 memcpy(&query_addr6, &sin6, sin6.sin6_len); 152 else { 153 yyerror("invalid IPv4 or IPv6 address: %s\n", 154 $3); 155 free($3); 156 YYERROR; 157 } 158 159 free($3); 160 } 161 | SERVERS address server_opts { 162 struct ntp_peer *p; 163 struct ntp_addr *h, *next; 164 165 h = $2->a; 166 do { 167 if (h != NULL) { 168 next = h->next; 169 if (h->ss.ss_family != AF_INET && 170 h->ss.ss_family != AF_INET6) { 171 yyerror("IPv4 or IPv6 address " 172 "or hostname expected"); 173 free(h); 174 free($2->name); 175 free($2); 176 YYERROR; 177 } 178 h->next = NULL; 179 } else 180 next = NULL; 181 182 p = new_peer(); 183 p->weight = $3.weight; 184 p->trusted = $3.trusted; 185 conf->trusted_peers = conf->trusted_peers || 186 $3.trusted; 187 p->query_addr4 = query_addr4; 188 p->query_addr6 = query_addr6; 189 p->addr = h; 190 p->addr_head.a = h; 191 p->addr_head.pool = ++poolseqnum; 192 p->addr_head.name = strdup($2->name); 193 if (p->addr_head.name == NULL) 194 fatal(NULL); 195 if (p->addr != NULL) 196 p->state = STATE_DNS_DONE; 197 TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry); 198 h = next; 199 } while (h != NULL); 200 201 free($2->name); 202 free($2); 203 } 204 | SERVER address server_opts { 205 struct ntp_peer *p; 206 struct ntp_addr *h, *next; 207 208 p = new_peer(); 209 for (h = $2->a; h != NULL; h = next) { 210 next = h->next; 211 if (h->ss.ss_family != AF_INET && 212 h->ss.ss_family != AF_INET6) { 213 yyerror("IPv4 or IPv6 address " 214 "or hostname expected"); 215 free(h); 216 free(p); 217 free($2->name); 218 free($2); 219 YYERROR; 220 } 221 h->next = p->addr; 222 p->addr = h; 223 } 224 225 p->weight = $3.weight; 226 p->trusted = $3.trusted; 227 conf->trusted_peers = conf->trusted_peers || 228 $3.trusted; 229 p->query_addr4 = query_addr4; 230 p->query_addr6 = query_addr6; 231 p->addr_head.a = p->addr; 232 p->addr_head.pool = 0; 233 p->addr_head.name = strdup($2->name); 234 if (p->addr_head.name == NULL) 235 fatal(NULL); 236 if (p->addr != NULL) 237 p->state = STATE_DNS_DONE; 238 TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry); 239 free($2->name); 240 free($2); 241 } 242 | CONSTRAINTS FROM url { 243 struct constraint *p; 244 struct ntp_addr *h, *next; 245 246 h = $3->a; 247 do { 248 if (h != NULL) { 249 next = h->next; 250 if (h->ss.ss_family != AF_INET && 251 h->ss.ss_family != AF_INET6) { 252 yyerror("IPv4 or IPv6 address " 253 "or hostname expected"); 254 free(h); 255 free($3->name); 256 free($3->path); 257 free($3); 258 YYERROR; 259 } 260 h->next = NULL; 261 } else 262 next = NULL; 263 264 p = new_constraint(); 265 p->addr = h; 266 p->addr_head.a = h; 267 p->addr_head.pool = ++poolseqnum; 268 p->addr_head.name = strdup($3->name); 269 p->addr_head.path = strdup($3->path); 270 if (p->addr_head.name == NULL || 271 p->addr_head.path == NULL) 272 fatal(NULL); 273 if (p->addr != NULL) 274 p->state = STATE_DNS_DONE; 275 constraint_add(p); 276 h = next; 277 } while (h != NULL); 278 279 free($3->name); 280 free($3); 281 } 282 | CONSTRAINT FROM urllist { 283 struct constraint *p; 284 struct ntp_addr *h, *next; 285 286 p = new_constraint(); 287 for (h = $3->a; h != NULL; h = next) { 288 next = h->next; 289 if (h->ss.ss_family != AF_INET && 290 h->ss.ss_family != AF_INET6) { 291 yyerror("IPv4 or IPv6 address " 292 "or hostname expected"); 293 free(h); 294 free(p); 295 free($3->name); 296 free($3->path); 297 free($3); 298 YYERROR; 299 } 300 h->next = p->addr; 301 p->addr = h; 302 } 303 304 p->addr_head.a = p->addr; 305 p->addr_head.pool = 0; 306 p->addr_head.name = strdup($3->name); 307 p->addr_head.path = strdup($3->path); 308 if (p->addr_head.name == NULL || 309 p->addr_head.path == NULL) 310 fatal(NULL); 311 if (p->addr != NULL) 312 p->state = STATE_DNS_DONE; 313 constraint_add(p); 314 free($3->name); 315 free($3); 316 } 317 | SENSOR STRING sensor_opts { 318 struct ntp_conf_sensor *s; 319 320 s = new_sensor($2); 321 s->weight = $3.weight; 322 s->correction = $3.correction; 323 s->refstr = $3.refstr; 324 s->stratum = $3.stratum; 325 s->trusted = $3.trusted; 326 conf->trusted_sensors = conf->trusted_sensors || 327 $3.trusted; 328 free($2); 329 TAILQ_INSERT_TAIL(&conf->ntp_conf_sensors, s, entry); 330 } 331 ; 332 333 address : STRING { 334 if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) == 335 NULL) 336 fatal(NULL); 337 host($1, &$$->a); 338 $$->name = $1; 339 } 340 ; 341 342 urllist : urllist address { 343 struct ntp_addr *p, *q = NULL; 344 struct in_addr ina; 345 struct in6_addr in6a; 346 347 if (inet_pton(AF_INET, $2->name, &ina) != 1 && 348 inet_pton(AF_INET6, $2->name, &in6a) != 1) { 349 yyerror("url can only be followed by IP " 350 "addresses"); 351 free($2->name); 352 free($2); 353 YYERROR; 354 } 355 p = $2->a; 356 while (p != NULL) { 357 q = p; 358 p = p->next; 359 } 360 if (q != NULL) { 361 q->next = $1->a; 362 $1->a = $2->a; 363 free($2); 364 } 365 $$ = $1; 366 } 367 | url { 368 $$ = $1; 369 } 370 ; 371 372 url : STRING { 373 char *hname, *path; 374 375 if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) == 376 NULL) 377 fatal("calloc"); 378 379 if (strncmp("https://", $1, 380 strlen("https://")) != 0) { 381 host($1, &$$->a); 382 $$->name = $1; 383 } else { 384 hname = $1 + strlen("https://"); 385 386 path = hname + strcspn(hname, "/\\"); 387 if (*path != '\0') { 388 if (($$->path = strdup(path)) == NULL) 389 fatal("strdup"); 390 *path = '\0'; 391 } 392 host(hname, &$$->a); 393 if (($$->name = strdup(hname)) == NULL) 394 fatal("strdup"); 395 } 396 if ($$->path == NULL && 397 ($$->path = strdup("/")) == NULL) 398 fatal("strdup"); 399 } 400 ; 401 402 listen_opts : { opts_default(); } 403 listen_opts_l 404 { $$ = opts; } 405 | { opts_default(); $$ = opts; } 406 ; 407 listen_opts_l : listen_opts_l listen_opt 408 | listen_opt 409 ; 410 listen_opt : rtable 411 ; 412 413 server_opts : { opts_default(); } 414 server_opts_l 415 { $$ = opts; } 416 | { opts_default(); $$ = opts; } 417 ; 418 server_opts_l : server_opts_l server_opt 419 | server_opt 420 ; 421 server_opt : weight 422 | trusted 423 ; 424 425 sensor_opts : { opts_default(); } 426 sensor_opts_l 427 { $$ = opts; } 428 | { opts_default(); $$ = opts; } 429 ; 430 sensor_opts_l : sensor_opts_l sensor_opt 431 | sensor_opt 432 ; 433 sensor_opt : correction 434 | refid 435 | stratum 436 | weight 437 | trusted 438 ; 439 440 correction : CORRECTION NUMBER { 441 if ($2 < -127000000 || $2 > 127000000) { 442 yyerror("correction must be between " 443 "-127000000 and 127000000 microseconds"); 444 YYERROR; 445 } 446 opts.correction = $2; 447 } 448 ; 449 450 refid : REFID STRING { 451 size_t l = strlen($2); 452 453 if (l < 1 || l > 4) { 454 yyerror("refid must be 1 to 4 characters"); 455 free($2); 456 YYERROR; 457 } 458 opts.refstr = $2; 459 } 460 ; 461 462 stratum : STRATUM NUMBER { 463 if ($2 < 1 || $2 > 15) { 464 yyerror("stratum must be between " 465 "1 and 15"); 466 YYERROR; 467 } 468 opts.stratum = $2; 469 } 470 ; 471 472 weight : WEIGHT NUMBER { 473 if ($2 < 1 || $2 > 10) { 474 yyerror("weight must be between 1 and 10"); 475 YYERROR; 476 } 477 opts.weight = $2; 478 } 479 rtable : RTABLE NUMBER { 480 if ($2 < 0 || $2 > RT_TABLEID_MAX) { 481 yyerror("rtable must be between 1" 482 " and RT_TABLEID_MAX"); 483 YYERROR; 484 } 485 opts.rtable = $2; 486 } 487 ; 488 489 trusted : TRUSTED { 490 opts.trusted = 1; 491 } 492 493 %% 494 495 void 496 opts_default(void) 497 { 498 memset(&opts, 0, sizeof opts); 499 opts.weight = 1; 500 opts.stratum = 1; 501 } 502 503 struct keywords { 504 const char *k_name; 505 int k_val; 506 }; 507 508 int 509 yyerror(const char *fmt, ...) 510 { 511 va_list ap; 512 char *msg; 513 514 file->errors++; 515 va_start(ap, fmt); 516 if (vasprintf(&msg, fmt, ap) == -1) 517 fatalx("yyerror vasprintf"); 518 va_end(ap); 519 log_warnx("%s:%d: %s", file->name, yylval.lineno, msg); 520 free(msg); 521 return (0); 522 } 523 524 int 525 kw_cmp(const void *k, const void *e) 526 { 527 return (strcmp(k, ((const struct keywords *)e)->k_name)); 528 } 529 530 int 531 lookup(char *s) 532 { 533 /* this has to be sorted always */ 534 static const struct keywords keywords[] = { 535 { "constraint", CONSTRAINT}, 536 { "constraints", CONSTRAINTS}, 537 { "correction", CORRECTION}, 538 { "from", FROM}, 539 { "listen", LISTEN}, 540 { "on", ON}, 541 { "query", QUERY}, 542 { "refid", REFID}, 543 { "rtable", RTABLE}, 544 { "sensor", SENSOR}, 545 { "server", SERVER}, 546 { "servers", SERVERS}, 547 { "stratum", STRATUM}, 548 { "trusted", TRUSTED}, 549 { "weight", WEIGHT} 550 }; 551 const struct keywords *p; 552 553 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 554 sizeof(keywords[0]), kw_cmp); 555 556 if (p) 557 return (p->k_val); 558 else 559 return (STRING); 560 } 561 562 #define MAXPUSHBACK 128 563 564 char *parsebuf; 565 int parseindex; 566 char pushback_buffer[MAXPUSHBACK]; 567 int pushback_index = 0; 568 569 int 570 lgetc(int quotec) 571 { 572 int c, next; 573 574 if (parsebuf) { 575 /* Read character from the parsebuffer instead of input. */ 576 if (parseindex >= 0) { 577 c = (unsigned char)parsebuf[parseindex++]; 578 if (c != '\0') 579 return (c); 580 parsebuf = NULL; 581 } else 582 parseindex++; 583 } 584 585 if (pushback_index) 586 return ((unsigned char)pushback_buffer[--pushback_index]); 587 588 if (quotec) { 589 if ((c = getc(file->stream)) == EOF) { 590 yyerror("reached end of file while parsing " 591 "quoted string"); 592 if (file == topfile || popfile() == EOF) 593 return (EOF); 594 return (quotec); 595 } 596 return (c); 597 } 598 599 while ((c = getc(file->stream)) == '\\') { 600 next = getc(file->stream); 601 if (next != '\n') { 602 c = next; 603 break; 604 } 605 yylval.lineno = file->lineno; 606 file->lineno++; 607 } 608 609 while (c == EOF) { 610 if (file == topfile || popfile() == EOF) 611 return (EOF); 612 c = getc(file->stream); 613 } 614 return (c); 615 } 616 617 int 618 lungetc(int c) 619 { 620 if (c == EOF) 621 return (EOF); 622 if (parsebuf) { 623 parseindex--; 624 if (parseindex >= 0) 625 return (c); 626 } 627 if (pushback_index + 1 >= MAXPUSHBACK) 628 return (EOF); 629 pushback_buffer[pushback_index++] = c; 630 return (c); 631 } 632 633 int 634 findeol(void) 635 { 636 int c; 637 638 parsebuf = NULL; 639 640 /* skip to either EOF or the first real EOL */ 641 while (1) { 642 if (pushback_index) 643 c = (unsigned char)pushback_buffer[--pushback_index]; 644 else 645 c = lgetc(0); 646 if (c == '\n') { 647 file->lineno++; 648 break; 649 } 650 if (c == EOF) 651 break; 652 } 653 return (ERROR); 654 } 655 656 int 657 yylex(void) 658 { 659 char buf[8096]; 660 char *p; 661 int quotec, next, c; 662 int token; 663 664 p = buf; 665 while ((c = lgetc(0)) == ' ' || c == '\t') 666 ; /* nothing */ 667 668 yylval.lineno = file->lineno; 669 if (c == '#') 670 while ((c = lgetc(0)) != '\n' && c != EOF) 671 ; /* nothing */ 672 673 switch (c) { 674 case '\'': 675 case '"': 676 quotec = c; 677 while (1) { 678 if ((c = lgetc(quotec)) == EOF) 679 return (0); 680 if (c == '\n') { 681 file->lineno++; 682 continue; 683 } else if (c == '\\') { 684 if ((next = lgetc(quotec)) == EOF) 685 return (0); 686 if (next == quotec || next == ' ' || 687 next == '\t') 688 c = next; 689 else if (next == '\n') { 690 file->lineno++; 691 continue; 692 } else 693 lungetc(next); 694 } else if (c == quotec) { 695 *p = '\0'; 696 break; 697 } else if (c == '\0') { 698 yyerror("syntax error"); 699 return (findeol()); 700 } 701 if (p + 1 >= buf + sizeof(buf) - 1) { 702 yyerror("string too long"); 703 return (findeol()); 704 } 705 *p++ = c; 706 } 707 yylval.v.string = strdup(buf); 708 if (yylval.v.string == NULL) 709 fatal("yylex: strdup"); 710 return (STRING); 711 } 712 713 #define allowed_to_end_number(x) \ 714 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 715 716 if (c == '-' || isdigit(c)) { 717 do { 718 *p++ = c; 719 if ((size_t)(p-buf) >= sizeof(buf)) { 720 yyerror("string too long"); 721 return (findeol()); 722 } 723 } while ((c = lgetc(0)) != EOF && isdigit(c)); 724 lungetc(c); 725 if (p == buf + 1 && buf[0] == '-') 726 goto nodigits; 727 if (c == EOF || allowed_to_end_number(c)) { 728 const char *errstr = NULL; 729 730 *p = '\0'; 731 yylval.v.number = strtonum(buf, LLONG_MIN, 732 LLONG_MAX, &errstr); 733 if (errstr) { 734 yyerror("\"%s\" invalid number: %s", 735 buf, errstr); 736 return (findeol()); 737 } 738 return (NUMBER); 739 } else { 740 nodigits: 741 while (p > buf + 1) 742 lungetc((unsigned char)*--p); 743 c = (unsigned char)*--p; 744 if (c == '-') 745 return (c); 746 } 747 } 748 749 #define allowed_in_string(x) \ 750 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 751 x != '{' && x != '}' && x != '<' && x != '>' && \ 752 x != '!' && x != '=' && x != '/' && x != '#' && \ 753 x != ',')) 754 755 if (isalnum(c) || c == ':' || c == '_' || c == '*') { 756 do { 757 *p++ = c; 758 if ((size_t)(p-buf) >= sizeof(buf)) { 759 yyerror("string too long"); 760 return (findeol()); 761 } 762 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 763 lungetc(c); 764 *p = '\0'; 765 if ((token = lookup(buf)) == STRING) 766 if ((yylval.v.string = strdup(buf)) == NULL) 767 fatal("yylex: strdup"); 768 return (token); 769 } 770 if (c == '\n') { 771 yylval.lineno = file->lineno; 772 file->lineno++; 773 } 774 if (c == EOF) 775 return (0); 776 return (c); 777 } 778 779 struct file * 780 pushfile(const char *name) 781 { 782 struct file *nfile; 783 784 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 785 log_warn("%s", __func__); 786 return (NULL); 787 } 788 if ((nfile->name = strdup(name)) == NULL) { 789 log_warn("%s", __func__); 790 free(nfile); 791 return (NULL); 792 } 793 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 794 log_warn("%s: %s", __func__, nfile->name); 795 free(nfile->name); 796 free(nfile); 797 return (NULL); 798 } 799 nfile->lineno = 1; 800 TAILQ_INSERT_TAIL(&files, nfile, entry); 801 return (nfile); 802 } 803 804 int 805 popfile(void) 806 { 807 struct file *prev; 808 809 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 810 prev->errors += file->errors; 811 812 TAILQ_REMOVE(&files, file, entry); 813 fclose(file->stream); 814 free(file->name); 815 free(file); 816 file = prev; 817 return (file ? 0 : EOF); 818 } 819 820 int 821 parse_config(const char *filename, struct ntpd_conf *xconf) 822 { 823 int errors = 0; 824 825 conf = xconf; 826 TAILQ_INIT(&conf->listen_addrs); 827 TAILQ_INIT(&conf->ntp_peers); 828 TAILQ_INIT(&conf->ntp_conf_sensors); 829 TAILQ_INIT(&conf->constraints); 830 831 if ((file = pushfile(filename)) == NULL) { 832 return (-1); 833 } 834 topfile = file; 835 836 yyparse(); 837 errors = file->errors; 838 popfile(); 839 840 return (errors ? -1 : 0); 841 } 842