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