1 /* $OpenBSD: parse.y,v 1.47 2010/08/03 18:42:40 henning 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 file->lineno++; 473 continue; 474 } else 475 lungetc(next); 476 } else if (c == quotec) { 477 *p = '\0'; 478 break; 479 } 480 if (p + 1 >= buf + sizeof(buf) - 1) { 481 yyerror("string too long"); 482 return (findeol()); 483 } 484 *p++ = (char)c; 485 } 486 yylval.v.string = strdup(buf); 487 if (yylval.v.string == NULL) 488 fatal("yylex: strdup"); 489 return (STRING); 490 } 491 492 #define allowed_to_end_number(x) \ 493 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 494 495 if (c == '-' || isdigit(c)) { 496 do { 497 *p++ = c; 498 if ((unsigned)(p-buf) >= sizeof(buf)) { 499 yyerror("string too long"); 500 return (findeol()); 501 } 502 } while ((c = lgetc(0)) != EOF && isdigit(c)); 503 lungetc(c); 504 if (p == buf + 1 && buf[0] == '-') 505 goto nodigits; 506 if (c == EOF || allowed_to_end_number(c)) { 507 const char *errstr = NULL; 508 509 *p = '\0'; 510 yylval.v.number = strtonum(buf, LLONG_MIN, 511 LLONG_MAX, &errstr); 512 if (errstr) { 513 yyerror("\"%s\" invalid number: %s", 514 buf, errstr); 515 return (findeol()); 516 } 517 return (NUMBER); 518 } else { 519 nodigits: 520 while (p > buf + 1) 521 lungetc(*--p); 522 c = *--p; 523 if (c == '-') 524 return (c); 525 } 526 } 527 528 #define allowed_in_string(x) \ 529 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 530 x != '{' && x != '}' && x != '<' && x != '>' && \ 531 x != '!' && x != '=' && x != '/' && x != '#' && \ 532 x != ',')) 533 534 if (isalnum(c) || c == ':' || c == '_' || c == '*') { 535 do { 536 *p++ = c; 537 if ((unsigned)(p-buf) >= sizeof(buf)) { 538 yyerror("string too long"); 539 return (findeol()); 540 } 541 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 542 lungetc(c); 543 *p = '\0'; 544 if ((token = lookup(buf)) == STRING) 545 if ((yylval.v.string = strdup(buf)) == NULL) 546 fatal("yylex: strdup"); 547 return (token); 548 } 549 if (c == '\n') { 550 yylval.lineno = file->lineno; 551 file->lineno++; 552 } 553 if (c == EOF) 554 return (0); 555 return (c); 556 } 557 558 struct file * 559 pushfile(const char *name) 560 { 561 struct file *nfile; 562 563 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 564 log_warn("malloc"); 565 return (NULL); 566 } 567 if ((nfile->name = strdup(name)) == NULL) { 568 log_warn("malloc"); 569 free(nfile); 570 return (NULL); 571 } 572 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 573 log_warn("%s", nfile->name); 574 free(nfile->name); 575 free(nfile); 576 return (NULL); 577 } 578 nfile->lineno = 1; 579 TAILQ_INSERT_TAIL(&files, nfile, entry); 580 return (nfile); 581 } 582 583 int 584 popfile(void) 585 { 586 struct file *prev; 587 588 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 589 prev->errors += file->errors; 590 591 TAILQ_REMOVE(&files, file, entry); 592 fclose(file->stream); 593 free(file->name); 594 free(file); 595 file = prev; 596 return (file ? 0 : EOF); 597 } 598 599 int 600 parse_config(const char *filename, struct ntpd_conf *xconf) 601 { 602 int errors = 0; 603 604 conf = xconf; 605 TAILQ_INIT(&conf->listen_addrs); 606 TAILQ_INIT(&conf->ntp_peers); 607 TAILQ_INIT(&conf->ntp_conf_sensors); 608 609 if ((file = pushfile(filename)) == NULL) { 610 return (-1); 611 } 612 topfile = file; 613 614 yyparse(); 615 errors = file->errors; 616 popfile(); 617 618 return (errors ? -1 : 0); 619 } 620