1 /* $OpenBSD: parse.y,v 1.65 2015/10/31 19:32:18 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 ntpd_conf *conf; 61 62 struct opts { 63 int weight; 64 int correction; 65 int stratum; 66 int rtable; 67 char *refstr; 68 } opts; 69 void opts_default(void); 70 71 typedef struct { 72 union { 73 int64_t number; 74 char *string; 75 struct ntp_addr_wrap *addr; 76 struct opts opts; 77 } v; 78 int lineno; 79 } YYSTYPE; 80 81 %} 82 83 %token LISTEN ON CONSTRAINT CONSTRAINTS FROM 84 %token SERVER SERVERS SENSOR CORRECTION RTABLE REFID STRATUM WEIGHT 85 %token ERROR 86 %token <v.string> STRING 87 %token <v.number> NUMBER 88 %type <v.addr> address url 89 %type <v.opts> listen_opts listen_opts_l listen_opt 90 %type <v.opts> server_opts server_opts_l server_opt 91 %type <v.opts> sensor_opts sensor_opts_l sensor_opt 92 %type <v.opts> correction 93 %type <v.opts> rtable 94 %type <v.opts> refid 95 %type <v.opts> stratum 96 %type <v.opts> weight 97 %% 98 99 grammar : /* empty */ 100 | grammar '\n' 101 | grammar main '\n' 102 | grammar error '\n' { file->errors++; } 103 ; 104 105 main : LISTEN ON address listen_opts { 106 struct listen_addr *la; 107 struct ntp_addr *h, *next; 108 109 if ((h = $3->a) == NULL && 110 (host_dns($3->name, &h) == -1 || !h)) { 111 yyerror("could not resolve \"%s\"", $3->name); 112 free($3->name); 113 free($3); 114 YYERROR; 115 } 116 117 for (; h != NULL; h = next) { 118 next = h->next; 119 la = calloc(1, sizeof(struct listen_addr)); 120 if (la == NULL) 121 fatal("listen on calloc"); 122 la->fd = -1; 123 la->rtable = $4.rtable; 124 memcpy(&la->sa, &h->ss, 125 sizeof(struct sockaddr_storage)); 126 TAILQ_INSERT_TAIL(&conf->listen_addrs, la, 127 entry); 128 free(h); 129 } 130 free($3->name); 131 free($3); 132 } 133 | SERVERS address server_opts { 134 struct ntp_peer *p; 135 struct ntp_addr *h, *next; 136 137 h = $2->a; 138 do { 139 if (h != NULL) { 140 next = h->next; 141 if (h->ss.ss_family != AF_INET && 142 h->ss.ss_family != AF_INET6) { 143 yyerror("IPv4 or IPv6 address " 144 "or hostname expected"); 145 free(h); 146 free($2->name); 147 free($2); 148 YYERROR; 149 } 150 h->next = NULL; 151 } else 152 next = NULL; 153 154 p = new_peer(); 155 p->weight = $3.weight; 156 p->addr = h; 157 p->addr_head.a = h; 158 p->addr_head.pool = 1; 159 p->addr_head.name = strdup($2->name); 160 if (p->addr_head.name == NULL) 161 fatal(NULL); 162 if (p->addr != NULL) 163 p->state = STATE_DNS_DONE; 164 TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry); 165 h = next; 166 } while (h != NULL); 167 168 free($2->name); 169 free($2); 170 } 171 | SERVER address server_opts { 172 struct ntp_peer *p; 173 struct ntp_addr *h, *next; 174 175 p = new_peer(); 176 for (h = $2->a; h != NULL; h = next) { 177 next = h->next; 178 if (h->ss.ss_family != AF_INET && 179 h->ss.ss_family != AF_INET6) { 180 yyerror("IPv4 or IPv6 address " 181 "or hostname expected"); 182 free(h); 183 free(p); 184 free($2->name); 185 free($2); 186 YYERROR; 187 } 188 h->next = p->addr; 189 p->addr = h; 190 } 191 192 p->weight = $3.weight; 193 p->addr_head.a = p->addr; 194 p->addr_head.pool = 0; 195 p->addr_head.name = strdup($2->name); 196 if (p->addr_head.name == NULL) 197 fatal(NULL); 198 if (p->addr != NULL) 199 p->state = STATE_DNS_DONE; 200 TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry); 201 free($2->name); 202 free($2); 203 } 204 | CONSTRAINTS FROM url { 205 struct constraint *p; 206 struct ntp_addr *h, *next; 207 208 h = $3->a; 209 do { 210 if (h != NULL) { 211 next = h->next; 212 if (h->ss.ss_family != AF_INET && 213 h->ss.ss_family != AF_INET6) { 214 yyerror("IPv4 or IPv6 address " 215 "or hostname expected"); 216 free(h); 217 free($3->name); 218 free($3->path); 219 free($3); 220 YYERROR; 221 } 222 h->next = NULL; 223 } else 224 next = NULL; 225 226 p = new_constraint(); 227 p->addr = h; 228 p->addr_head.a = h; 229 p->addr_head.pool = 1; 230 p->addr_head.name = strdup($3->name); 231 p->addr_head.path = strdup($3->path); 232 if (p->addr_head.name == NULL || 233 p->addr_head.path == NULL) 234 fatal(NULL); 235 if (p->addr != NULL) 236 p->state = STATE_DNS_DONE; 237 constraint_add(p); 238 h = next; 239 } while (h != NULL); 240 241 free($3->name); 242 free($3); 243 } 244 | CONSTRAINT FROM url { 245 struct constraint *p; 246 struct ntp_addr *h, *next; 247 248 p = new_constraint(); 249 for (h = $3->a; h != NULL; h = next) { 250 next = h->next; 251 if (h->ss.ss_family != AF_INET && 252 h->ss.ss_family != AF_INET6) { 253 yyerror("IPv4 or IPv6 address " 254 "or hostname expected"); 255 free(h); 256 free(p); 257 free($3->name); 258 free($3->path); 259 free($3); 260 YYERROR; 261 } 262 h->next = p->addr; 263 p->addr = h; 264 } 265 266 p->addr_head.a = p->addr; 267 p->addr_head.pool = 0; 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 free($3->name); 277 free($3); 278 } 279 | SENSOR STRING sensor_opts { 280 struct ntp_conf_sensor *s; 281 282 s = new_sensor($2); 283 s->weight = $3.weight; 284 s->correction = $3.correction; 285 s->refstr = $3.refstr; 286 s->stratum = $3.stratum; 287 free($2); 288 TAILQ_INSERT_TAIL(&conf->ntp_conf_sensors, s, entry); 289 } 290 ; 291 292 address : STRING { 293 if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) == 294 NULL) 295 fatal(NULL); 296 host($1, &$$->a); 297 $$->name = $1; 298 } 299 ; 300 301 url : STRING { 302 char *hname, *path; 303 304 if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) == 305 NULL) 306 fatal("calloc"); 307 308 if (strncmp("https://", $1, 309 strlen("https://")) != 0) { 310 host($1, &$$->a); 311 $$->name = $1; 312 } else { 313 hname = $1 + strlen("https://"); 314 315 path = hname + strcspn(hname, "/\\"); 316 if (*path != '\0') { 317 if (($$->path = strdup(path)) == NULL) 318 fatal("strdup"); 319 *path = '\0'; 320 } 321 host(hname, &$$->a); 322 if (($$->name = strdup(hname)) == NULL) 323 fatal("strdup"); 324 } 325 if ($$->path == NULL && 326 ($$->path = strdup("/")) == NULL) 327 fatal("strdup"); 328 } 329 ; 330 331 listen_opts : { opts_default(); } 332 listen_opts_l 333 { $$ = opts; } 334 | { opts_default(); $$ = opts; } 335 ; 336 listen_opts_l : listen_opts_l listen_opt 337 | listen_opt 338 ; 339 listen_opt : rtable 340 ; 341 342 server_opts : { opts_default(); } 343 server_opts_l 344 { $$ = opts; } 345 | { opts_default(); $$ = opts; } 346 ; 347 server_opts_l : server_opts_l server_opt 348 | server_opt 349 ; 350 server_opt : weight 351 ; 352 353 sensor_opts : { opts_default(); } 354 sensor_opts_l 355 { $$ = opts; } 356 | { opts_default(); $$ = opts; } 357 ; 358 sensor_opts_l : sensor_opts_l sensor_opt 359 | sensor_opt 360 ; 361 sensor_opt : correction 362 | refid 363 | stratum 364 | weight 365 ; 366 367 correction : CORRECTION NUMBER { 368 if ($2 < -127000000 || $2 > 127000000) { 369 yyerror("correction must be between " 370 "-127000000 and 127000000 microseconds"); 371 YYERROR; 372 } 373 opts.correction = $2; 374 } 375 ; 376 377 refid : REFID STRING { 378 size_t l = strlen($2); 379 380 if (l < 1 || l > 4) { 381 yyerror("refid must be 1 to 4 characters"); 382 free($2); 383 YYERROR; 384 } 385 opts.refstr = $2; 386 } 387 ; 388 389 stratum : STRATUM NUMBER { 390 if ($2 < 1 || $2 > 15) { 391 yyerror("stratum must be between " 392 "1 and 15"); 393 YYERROR; 394 } 395 opts.stratum = $2; 396 } 397 ; 398 399 weight : WEIGHT NUMBER { 400 if ($2 < 1 || $2 > 10) { 401 yyerror("weight must be between 1 and 10"); 402 YYERROR; 403 } 404 opts.weight = $2; 405 } 406 rtable : RTABLE NUMBER { 407 if ($2 < 0 || $2 > RT_TABLEID_MAX) { 408 yyerror("rtable must be between 1" 409 " and RT_TABLEID_MAX"); 410 YYERROR; 411 } 412 opts.rtable = $2; 413 } 414 ; 415 416 %% 417 418 void 419 opts_default(void) 420 { 421 memset(&opts, 0, sizeof opts); 422 opts.weight = 1; 423 opts.stratum = 1; 424 } 425 426 struct keywords { 427 const char *k_name; 428 int k_val; 429 }; 430 431 int 432 yyerror(const char *fmt, ...) 433 { 434 va_list ap; 435 char *msg; 436 437 file->errors++; 438 va_start(ap, fmt); 439 if (vasprintf(&msg, fmt, ap) == -1) 440 fatalx("yyerror vasprintf"); 441 va_end(ap); 442 log_warnx("%s:%d: %s", file->name, yylval.lineno, msg); 443 free(msg); 444 return (0); 445 } 446 447 int 448 kw_cmp(const void *k, const void *e) 449 { 450 return (strcmp(k, ((const struct keywords *)e)->k_name)); 451 } 452 453 int 454 lookup(char *s) 455 { 456 /* this has to be sorted always */ 457 static const struct keywords keywords[] = { 458 { "constraint", CONSTRAINT}, 459 { "constraints", CONSTRAINTS}, 460 { "correction", CORRECTION}, 461 { "from", FROM}, 462 { "listen", LISTEN}, 463 { "on", ON}, 464 { "refid", REFID}, 465 { "rtable", RTABLE}, 466 { "sensor", SENSOR}, 467 { "server", SERVER}, 468 { "servers", SERVERS}, 469 { "stratum", STRATUM}, 470 { "weight", WEIGHT} 471 }; 472 const struct keywords *p; 473 474 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 475 sizeof(keywords[0]), kw_cmp); 476 477 if (p) 478 return (p->k_val); 479 else 480 return (STRING); 481 } 482 483 #define MAXPUSHBACK 128 484 485 u_char *parsebuf; 486 int parseindex; 487 u_char pushback_buffer[MAXPUSHBACK]; 488 int pushback_index = 0; 489 490 int 491 lgetc(int quotec) 492 { 493 int c, next; 494 495 if (parsebuf) { 496 /* Read character from the parsebuffer instead of input. */ 497 if (parseindex >= 0) { 498 c = parsebuf[parseindex++]; 499 if (c != '\0') 500 return (c); 501 parsebuf = NULL; 502 } else 503 parseindex++; 504 } 505 506 if (pushback_index) 507 return (pushback_buffer[--pushback_index]); 508 509 if (quotec) { 510 if ((c = getc(file->stream)) == EOF) { 511 yyerror("reached end of file while parsing " 512 "quoted string"); 513 if (file == topfile || popfile() == EOF) 514 return (EOF); 515 return (quotec); 516 } 517 return (c); 518 } 519 520 while ((c = getc(file->stream)) == '\\') { 521 next = getc(file->stream); 522 if (next != '\n') { 523 c = next; 524 break; 525 } 526 yylval.lineno = file->lineno; 527 file->lineno++; 528 } 529 530 while (c == EOF) { 531 if (file == topfile || popfile() == EOF) 532 return (EOF); 533 c = getc(file->stream); 534 } 535 return (c); 536 } 537 538 int 539 lungetc(int c) 540 { 541 if (c == EOF) 542 return (EOF); 543 if (parsebuf) { 544 parseindex--; 545 if (parseindex >= 0) 546 return (c); 547 } 548 if (pushback_index < MAXPUSHBACK-1) 549 return (pushback_buffer[pushback_index++] = c); 550 else 551 return (EOF); 552 } 553 554 int 555 findeol(void) 556 { 557 int c; 558 559 parsebuf = NULL; 560 561 /* skip to either EOF or the first real EOL */ 562 while (1) { 563 if (pushback_index) 564 c = pushback_buffer[--pushback_index]; 565 else 566 c = lgetc(0); 567 if (c == '\n') { 568 file->lineno++; 569 break; 570 } 571 if (c == EOF) 572 break; 573 } 574 return (ERROR); 575 } 576 577 int 578 yylex(void) 579 { 580 u_char buf[8096]; 581 u_char *p; 582 int quotec, next, c; 583 int token; 584 585 p = buf; 586 while ((c = lgetc(0)) == ' ' || c == '\t') 587 ; /* nothing */ 588 589 yylval.lineno = file->lineno; 590 if (c == '#') 591 while ((c = lgetc(0)) != '\n' && c != EOF) 592 ; /* nothing */ 593 594 switch (c) { 595 case '\'': 596 case '"': 597 quotec = c; 598 while (1) { 599 if ((c = lgetc(quotec)) == EOF) 600 return (0); 601 if (c == '\n') { 602 file->lineno++; 603 continue; 604 } else if (c == '\\') { 605 if ((next = lgetc(quotec)) == EOF) 606 return (0); 607 if (next == quotec || c == ' ' || c == '\t') 608 c = next; 609 else if (next == '\n') { 610 file->lineno++; 611 continue; 612 } else 613 lungetc(next); 614 } else if (c == quotec) { 615 *p = '\0'; 616 break; 617 } else if (c == '\0') { 618 yyerror("syntax error"); 619 return (findeol()); 620 } 621 if (p + 1 >= buf + sizeof(buf) - 1) { 622 yyerror("string too long"); 623 return (findeol()); 624 } 625 *p++ = c; 626 } 627 yylval.v.string = strdup(buf); 628 if (yylval.v.string == NULL) 629 fatal("yylex: strdup"); 630 return (STRING); 631 } 632 633 #define allowed_to_end_number(x) \ 634 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 635 636 if (c == '-' || isdigit(c)) { 637 do { 638 *p++ = c; 639 if ((unsigned)(p-buf) >= sizeof(buf)) { 640 yyerror("string too long"); 641 return (findeol()); 642 } 643 } while ((c = lgetc(0)) != EOF && isdigit(c)); 644 lungetc(c); 645 if (p == buf + 1 && buf[0] == '-') 646 goto nodigits; 647 if (c == EOF || allowed_to_end_number(c)) { 648 const char *errstr = NULL; 649 650 *p = '\0'; 651 yylval.v.number = strtonum(buf, LLONG_MIN, 652 LLONG_MAX, &errstr); 653 if (errstr) { 654 yyerror("\"%s\" invalid number: %s", 655 buf, errstr); 656 return (findeol()); 657 } 658 return (NUMBER); 659 } else { 660 nodigits: 661 while (p > buf + 1) 662 lungetc(*--p); 663 c = *--p; 664 if (c == '-') 665 return (c); 666 } 667 } 668 669 #define allowed_in_string(x) \ 670 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 671 x != '{' && x != '}' && x != '<' && x != '>' && \ 672 x != '!' && x != '=' && x != '/' && x != '#' && \ 673 x != ',')) 674 675 if (isalnum(c) || c == ':' || c == '_' || c == '*') { 676 do { 677 *p++ = c; 678 if ((unsigned)(p-buf) >= sizeof(buf)) { 679 yyerror("string too long"); 680 return (findeol()); 681 } 682 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 683 lungetc(c); 684 *p = '\0'; 685 if ((token = lookup(buf)) == STRING) 686 if ((yylval.v.string = strdup(buf)) == NULL) 687 fatal("yylex: strdup"); 688 return (token); 689 } 690 if (c == '\n') { 691 yylval.lineno = file->lineno; 692 file->lineno++; 693 } 694 if (c == EOF) 695 return (0); 696 return (c); 697 } 698 699 struct file * 700 pushfile(const char *name) 701 { 702 struct file *nfile; 703 704 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 705 log_warn("malloc"); 706 return (NULL); 707 } 708 if ((nfile->name = strdup(name)) == NULL) { 709 log_warn("malloc"); 710 free(nfile); 711 return (NULL); 712 } 713 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 714 log_warn("%s", nfile->name); 715 free(nfile->name); 716 free(nfile); 717 return (NULL); 718 } 719 nfile->lineno = 1; 720 TAILQ_INSERT_TAIL(&files, nfile, entry); 721 return (nfile); 722 } 723 724 int 725 popfile(void) 726 { 727 struct file *prev; 728 729 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 730 prev->errors += file->errors; 731 732 TAILQ_REMOVE(&files, file, entry); 733 fclose(file->stream); 734 free(file->name); 735 free(file); 736 file = prev; 737 return (file ? 0 : EOF); 738 } 739 740 int 741 parse_config(const char *filename, struct ntpd_conf *xconf) 742 { 743 int errors = 0; 744 745 conf = xconf; 746 TAILQ_INIT(&conf->listen_addrs); 747 TAILQ_INIT(&conf->ntp_peers); 748 TAILQ_INIT(&conf->ntp_conf_sensors); 749 TAILQ_INIT(&conf->constraints); 750 751 if ((file = pushfile(filename)) == NULL) { 752 return (-1); 753 } 754 topfile = file; 755 756 yyparse(); 757 errors = file->errors; 758 popfile(); 759 760 return (errors ? -1 : 0); 761 } 762