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