|
|
- /* $OpenBSD: parse.y,v 1.71 2019/02/13 22:57:08 deraadt Exp $ */
-
- /*
- * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
- * Copyright (c) 2001 Markus Friedl. All rights reserved.
- * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
- * Copyright (c) 2001 Theo de Raadt. All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
- %{
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
-
- #include <ctype.h>
- #include <errno.h>
- #include <limits.h>
- #include <stdarg.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <syslog.h>
-
- #include "ntpd.h"
-
- TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
- static struct file {
- TAILQ_ENTRY(file) entry;
- FILE *stream;
- char *name;
- int lineno;
- int errors;
- } *file, *topfile;
- struct file *pushfile(const char *);
- int popfile(void);
- int yyparse(void);
- int yylex(void);
- int yyerror(const char *, ...)
- __attribute__((__format__ (printf, 1, 2)))
- __attribute__((__nonnull__ (1)));
- int kw_cmp(const void *, const void *);
- int lookup(char *);
- int lgetc(int);
- int lungetc(int);
- int findeol(void);
-
- struct ntpd_conf *conf;
- struct sockaddr_in query_addr4;
- struct sockaddr_in6 query_addr6;
-
- struct opts {
- int weight;
- int correction;
- int stratum;
- int rtable;
- char *refstr;
- } opts;
- void opts_default(void);
-
- typedef struct {
- union {
- int64_t number;
- char *string;
- struct ntp_addr_wrap *addr;
- struct opts opts;
- } v;
- int lineno;
- } YYSTYPE;
-
- %}
-
- %token LISTEN ON CONSTRAINT CONSTRAINTS FROM QUERY
- %token SERVER SERVERS SENSOR CORRECTION RTABLE REFID STRATUM WEIGHT
- %token ERROR
- %token <v.string> STRING
- %token <v.number> NUMBER
- %type <v.addr> address url
- %type <v.opts> listen_opts listen_opts_l listen_opt
- %type <v.opts> server_opts server_opts_l server_opt
- %type <v.opts> sensor_opts sensor_opts_l sensor_opt
- %type <v.opts> correction
- %type <v.opts> rtable
- %type <v.opts> refid
- %type <v.opts> stratum
- %type <v.opts> weight
- %%
-
- grammar : /* empty */
- | grammar '\n'
- | grammar main '\n'
- | grammar error '\n' { file->errors++; }
- ;
-
- main : LISTEN ON address listen_opts {
- struct listen_addr *la;
- struct ntp_addr *h, *next;
-
- if ((h = $3->a) == NULL &&
- (host_dns($3->name, &h) == -1 || !h)) {
- yyerror("could not resolve \"%s\"", $3->name);
- free($3->name);
- free($3);
- YYERROR;
- }
-
- for (; h != NULL; h = next) {
- next = h->next;
- la = calloc(1, sizeof(struct listen_addr));
- if (la == NULL)
- fatal("listen on calloc");
- la->fd = -1;
- la->rtable = $4.rtable;
- memcpy(&la->sa, &h->ss,
- sizeof(struct sockaddr_storage));
- TAILQ_INSERT_TAIL(&conf->listen_addrs, la,
- entry);
- free(h);
- }
- free($3->name);
- free($3);
- }
- | QUERY FROM STRING {
- struct sockaddr_in sin4;
- struct sockaddr_in6 sin6;
-
- memset(&sin4, 0, sizeof(sin4));
- sin4.sin_family = AF_INET;
- sin4.sin_len = sizeof(struct sockaddr_in);
- memset(&sin6, 0, sizeof(sin6));
- sin6.sin6_family = AF_INET6;
- sin6.sin6_len = sizeof(struct sockaddr_in6);
-
- if (inet_pton(AF_INET, $3, &sin4.sin_addr) == 1)
- memcpy(&query_addr4, &sin4, sin4.sin_len);
- else if (inet_pton(AF_INET6, $3, &sin6.sin6_addr) == 1)
- memcpy(&query_addr6, &sin6, sin6.sin6_len);
- else {
- yyerror("invalid IPv4 or IPv6 address: %s\n",
- $3);
- free($3);
- YYERROR;
- }
-
- free($3);
- }
- | SERVERS address server_opts {
- struct ntp_peer *p;
- struct ntp_addr *h, *next;
-
- h = $2->a;
- do {
- if (h != NULL) {
- next = h->next;
- if (h->ss.ss_family != AF_INET &&
- h->ss.ss_family != AF_INET6) {
- yyerror("IPv4 or IPv6 address "
- "or hostname expected");
- free(h);
- free($2->name);
- free($2);
- YYERROR;
- }
- h->next = NULL;
- } else
- next = NULL;
-
- p = new_peer();
- p->weight = $3.weight;
- p->query_addr4 = query_addr4;
- p->query_addr6 = query_addr6;
- p->addr = h;
- p->addr_head.a = h;
- p->addr_head.pool = 1;
- p->addr_head.name = strdup($2->name);
- if (p->addr_head.name == NULL)
- fatal(NULL);
- if (p->addr != NULL)
- p->state = STATE_DNS_DONE;
- TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
- h = next;
- } while (h != NULL);
-
- free($2->name);
- free($2);
- }
- | SERVER address server_opts {
- struct ntp_peer *p;
- struct ntp_addr *h, *next;
-
- p = new_peer();
- for (h = $2->a; h != NULL; h = next) {
- next = h->next;
- if (h->ss.ss_family != AF_INET &&
- h->ss.ss_family != AF_INET6) {
- yyerror("IPv4 or IPv6 address "
- "or hostname expected");
- free(h);
- free(p);
- free($2->name);
- free($2);
- YYERROR;
- }
- h->next = p->addr;
- p->addr = h;
- }
-
- p->weight = $3.weight;
- p->query_addr4 = query_addr4;
- p->query_addr6 = query_addr6;
- p->addr_head.a = p->addr;
- p->addr_head.pool = 0;
- p->addr_head.name = strdup($2->name);
- if (p->addr_head.name == NULL)
- fatal(NULL);
- if (p->addr != NULL)
- p->state = STATE_DNS_DONE;
- TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
- free($2->name);
- free($2);
- }
- | CONSTRAINTS FROM url {
- struct constraint *p;
- struct ntp_addr *h, *next;
-
- h = $3->a;
- do {
- if (h != NULL) {
- next = h->next;
- if (h->ss.ss_family != AF_INET &&
- h->ss.ss_family != AF_INET6) {
- yyerror("IPv4 or IPv6 address "
- "or hostname expected");
- free(h);
- free($3->name);
- free($3->path);
- free($3);
- YYERROR;
- }
- h->next = NULL;
- } else
- next = NULL;
-
- p = new_constraint();
- p->addr = h;
- p->addr_head.a = h;
- p->addr_head.pool = 1;
- p->addr_head.name = strdup($3->name);
- p->addr_head.path = strdup($3->path);
- if (p->addr_head.name == NULL ||
- p->addr_head.path == NULL)
- fatal(NULL);
- if (p->addr != NULL)
- p->state = STATE_DNS_DONE;
- constraint_add(p);
- h = next;
- } while (h != NULL);
-
- free($3->name);
- free($3);
- }
- | CONSTRAINT FROM url {
- struct constraint *p;
- struct ntp_addr *h, *next;
-
- p = new_constraint();
- for (h = $3->a; h != NULL; h = next) {
- next = h->next;
- if (h->ss.ss_family != AF_INET &&
- h->ss.ss_family != AF_INET6) {
- yyerror("IPv4 or IPv6 address "
- "or hostname expected");
- free(h);
- free(p);
- free($3->name);
- free($3->path);
- free($3);
- YYERROR;
- }
- h->next = p->addr;
- p->addr = h;
- }
-
- p->addr_head.a = p->addr;
- p->addr_head.pool = 0;
- p->addr_head.name = strdup($3->name);
- p->addr_head.path = strdup($3->path);
- if (p->addr_head.name == NULL ||
- p->addr_head.path == NULL)
- fatal(NULL);
- if (p->addr != NULL)
- p->state = STATE_DNS_DONE;
- constraint_add(p);
- free($3->name);
- free($3);
- }
- | SENSOR STRING sensor_opts {
- struct ntp_conf_sensor *s;
-
- s = new_sensor($2);
- s->weight = $3.weight;
- s->correction = $3.correction;
- s->refstr = $3.refstr;
- s->stratum = $3.stratum;
- free($2);
- TAILQ_INSERT_TAIL(&conf->ntp_conf_sensors, s, entry);
- }
- ;
-
- address : STRING {
- if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) ==
- NULL)
- fatal(NULL);
- host($1, &$$->a);
- $$->name = $1;
- }
- ;
-
- url : STRING {
- char *hname, *path;
-
- if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) ==
- NULL)
- fatal("calloc");
-
- if (strncmp("https://", $1,
- strlen("https://")) != 0) {
- host($1, &$$->a);
- $$->name = $1;
- } else {
- hname = $1 + strlen("https://");
-
- path = hname + strcspn(hname, "/\\");
- if (*path != '\0') {
- if (($$->path = strdup(path)) == NULL)
- fatal("strdup");
- *path = '\0';
- }
- host(hname, &$$->a);
- if (($$->name = strdup(hname)) == NULL)
- fatal("strdup");
- }
- if ($$->path == NULL &&
- ($$->path = strdup("/")) == NULL)
- fatal("strdup");
- }
- ;
-
- listen_opts : { opts_default(); }
- listen_opts_l
- { $$ = opts; }
- | { opts_default(); $$ = opts; }
- ;
- listen_opts_l : listen_opts_l listen_opt
- | listen_opt
- ;
- listen_opt : rtable
- ;
-
- server_opts : { opts_default(); }
- server_opts_l
- { $$ = opts; }
- | { opts_default(); $$ = opts; }
- ;
- server_opts_l : server_opts_l server_opt
- | server_opt
- ;
- server_opt : weight
- ;
-
- sensor_opts : { opts_default(); }
- sensor_opts_l
- { $$ = opts; }
- | { opts_default(); $$ = opts; }
- ;
- sensor_opts_l : sensor_opts_l sensor_opt
- | sensor_opt
- ;
- sensor_opt : correction
- | refid
- | stratum
- | weight
- ;
-
- correction : CORRECTION NUMBER {
- if ($2 < -127000000 || $2 > 127000000) {
- yyerror("correction must be between "
- "-127000000 and 127000000 microseconds");
- YYERROR;
- }
- opts.correction = $2;
- }
- ;
-
- refid : REFID STRING {
- size_t l = strlen($2);
-
- if (l < 1 || l > 4) {
- yyerror("refid must be 1 to 4 characters");
- free($2);
- YYERROR;
- }
- opts.refstr = $2;
- }
- ;
-
- stratum : STRATUM NUMBER {
- if ($2 < 1 || $2 > 15) {
- yyerror("stratum must be between "
- "1 and 15");
- YYERROR;
- }
- opts.stratum = $2;
- }
- ;
-
- weight : WEIGHT NUMBER {
- if ($2 < 1 || $2 > 10) {
- yyerror("weight must be between 1 and 10");
- YYERROR;
- }
- opts.weight = $2;
- }
- rtable : RTABLE NUMBER {
- if ($2 < 0 || $2 > RT_TABLEID_MAX) {
- yyerror("rtable must be between 1"
- " and RT_TABLEID_MAX");
- YYERROR;
- }
- opts.rtable = $2;
- }
- ;
-
- %%
-
- void
- opts_default(void)
- {
- memset(&opts, 0, sizeof opts);
- opts.weight = 1;
- opts.stratum = 1;
- }
-
- struct keywords {
- const char *k_name;
- int k_val;
- };
-
- int
- yyerror(const char *fmt, ...)
- {
- va_list ap;
- char *msg;
-
- file->errors++;
- va_start(ap, fmt);
- if (vasprintf(&msg, fmt, ap) == -1)
- fatalx("yyerror vasprintf");
- va_end(ap);
- log_warnx("%s:%d: %s", file->name, yylval.lineno, msg);
- free(msg);
- return (0);
- }
-
- int
- kw_cmp(const void *k, const void *e)
- {
- return (strcmp(k, ((const struct keywords *)e)->k_name));
- }
-
- int
- lookup(char *s)
- {
- /* this has to be sorted always */
- static const struct keywords keywords[] = {
- { "constraint", CONSTRAINT},
- { "constraints", CONSTRAINTS},
- { "correction", CORRECTION},
- { "from", FROM},
- { "listen", LISTEN},
- { "on", ON},
- { "query", QUERY},
- { "refid", REFID},
- { "rtable", RTABLE},
- { "sensor", SENSOR},
- { "server", SERVER},
- { "servers", SERVERS},
- { "stratum", STRATUM},
- { "weight", WEIGHT}
- };
- const struct keywords *p;
-
- p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
- sizeof(keywords[0]), kw_cmp);
-
- if (p)
- return (p->k_val);
- else
- return (STRING);
- }
-
- #define MAXPUSHBACK 128
-
- u_char *parsebuf;
- int parseindex;
- u_char pushback_buffer[MAXPUSHBACK];
- int pushback_index = 0;
-
- int
- lgetc(int quotec)
- {
- int c, next;
-
- if (parsebuf) {
- /* Read character from the parsebuffer instead of input. */
- if (parseindex >= 0) {
- c = parsebuf[parseindex++];
- if (c != '\0')
- return (c);
- parsebuf = NULL;
- } else
- parseindex++;
- }
-
- if (pushback_index)
- return (pushback_buffer[--pushback_index]);
-
- if (quotec) {
- if ((c = getc(file->stream)) == EOF) {
- yyerror("reached end of file while parsing "
- "quoted string");
- if (file == topfile || popfile() == EOF)
- return (EOF);
- return (quotec);
- }
- return (c);
- }
-
- while ((c = getc(file->stream)) == '\\') {
- next = getc(file->stream);
- if (next != '\n') {
- c = next;
- break;
- }
- yylval.lineno = file->lineno;
- file->lineno++;
- }
-
- while (c == EOF) {
- if (file == topfile || popfile() == EOF)
- return (EOF);
- c = getc(file->stream);
- }
- return (c);
- }
-
- int
- lungetc(int c)
- {
- if (c == EOF)
- return (EOF);
- if (parsebuf) {
- parseindex--;
- if (parseindex >= 0)
- return (c);
- }
- if (pushback_index < MAXPUSHBACK-1)
- return (pushback_buffer[pushback_index++] = c);
- else
- return (EOF);
- }
-
- int
- findeol(void)
- {
- int c;
-
- parsebuf = NULL;
-
- /* skip to either EOF or the first real EOL */
- while (1) {
- if (pushback_index)
- c = pushback_buffer[--pushback_index];
- else
- c = lgetc(0);
- if (c == '\n') {
- file->lineno++;
- break;
- }
- if (c == EOF)
- break;
- }
- return (ERROR);
- }
-
- int
- yylex(void)
- {
- u_char buf[8096];
- u_char *p;
- int quotec, next, c;
- int token;
-
- p = buf;
- while ((c = lgetc(0)) == ' ' || c == '\t')
- ; /* nothing */
-
- yylval.lineno = file->lineno;
- if (c == '#')
- while ((c = lgetc(0)) != '\n' && c != EOF)
- ; /* nothing */
-
- switch (c) {
- case '\'':
- case '"':
- quotec = c;
- while (1) {
- if ((c = lgetc(quotec)) == EOF)
- return (0);
- if (c == '\n') {
- file->lineno++;
- continue;
- } else if (c == '\\') {
- if ((next = lgetc(quotec)) == EOF)
- return (0);
- if (next == quotec || next == ' ' ||
- next == '\t')
- c = next;
- else if (next == '\n') {
- file->lineno++;
- continue;
- } else
- lungetc(next);
- } else if (c == quotec) {
- *p = '\0';
- break;
- } else if (c == '\0') {
- yyerror("syntax error");
- return (findeol());
- }
- if (p + 1 >= buf + sizeof(buf) - 1) {
- yyerror("string too long");
- return (findeol());
- }
- *p++ = c;
- }
- yylval.v.string = strdup(buf);
- if (yylval.v.string == NULL)
- fatal("yylex: strdup");
- return (STRING);
- }
-
- #define allowed_to_end_number(x) \
- (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
-
- if (c == '-' || isdigit(c)) {
- do {
- *p++ = c;
- if ((size_t)(p-buf) >= sizeof(buf)) {
- yyerror("string too long");
- return (findeol());
- }
- } while ((c = lgetc(0)) != EOF && isdigit(c));
- lungetc(c);
- if (p == buf + 1 && buf[0] == '-')
- goto nodigits;
- if (c == EOF || allowed_to_end_number(c)) {
- const char *errstr = NULL;
-
- *p = '\0';
- yylval.v.number = strtonum(buf, LLONG_MIN,
- LLONG_MAX, &errstr);
- if (errstr) {
- yyerror("\"%s\" invalid number: %s",
- buf, errstr);
- return (findeol());
- }
- return (NUMBER);
- } else {
- nodigits:
- while (p > buf + 1)
- lungetc(*--p);
- c = *--p;
- if (c == '-')
- return (c);
- }
- }
-
- #define allowed_in_string(x) \
- (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
- x != '{' && x != '}' && x != '<' && x != '>' && \
- x != '!' && x != '=' && x != '/' && x != '#' && \
- x != ','))
-
- if (isalnum(c) || c == ':' || c == '_' || c == '*') {
- do {
- *p++ = c;
- if ((size_t)(p-buf) >= sizeof(buf)) {
- yyerror("string too long");
- return (findeol());
- }
- } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
- lungetc(c);
- *p = '\0';
- if ((token = lookup(buf)) == STRING)
- if ((yylval.v.string = strdup(buf)) == NULL)
- fatal("yylex: strdup");
- return (token);
- }
- if (c == '\n') {
- yylval.lineno = file->lineno;
- file->lineno++;
- }
- if (c == EOF)
- return (0);
- return (c);
- }
-
- struct file *
- pushfile(const char *name)
- {
- struct file *nfile;
-
- if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
- log_warn("%s", __func__);
- return (NULL);
- }
- if ((nfile->name = strdup(name)) == NULL) {
- log_warn("%s", __func__);
- free(nfile);
- return (NULL);
- }
- if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
- log_warn("%s: %s", __func__, nfile->name);
- free(nfile->name);
- free(nfile);
- return (NULL);
- }
- nfile->lineno = 1;
- TAILQ_INSERT_TAIL(&files, nfile, entry);
- return (nfile);
- }
-
- int
- popfile(void)
- {
- struct file *prev;
-
- if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
- prev->errors += file->errors;
-
- TAILQ_REMOVE(&files, file, entry);
- fclose(file->stream);
- free(file->name);
- free(file);
- file = prev;
- return (file ? 0 : EOF);
- }
-
- int
- parse_config(const char *filename, struct ntpd_conf *xconf)
- {
- int errors = 0;
-
- conf = xconf;
- TAILQ_INIT(&conf->listen_addrs);
- TAILQ_INIT(&conf->ntp_peers);
- TAILQ_INIT(&conf->ntp_conf_sensors);
- TAILQ_INIT(&conf->constraints);
-
- if ((file = pushfile(filename)) == NULL) {
- return (-1);
- }
- topfile = file;
-
- yyparse();
- errors = file->errors;
- popfile();
-
- return (errors ? -1 : 0);
- }
|