Merge from vendor branch NTPD:
[dragonfly.git] / contrib / ntpd / parse.y
1 /*      $OpenBSD: src/usr.sbin/ntpd/parse.y,v 1.22 2004/11/05 14:28:29 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 <string.h>
34 #include <syslog.h>
35
36 #include "ntpd.h"
37
38 static struct ntpd_conf         *conf;
39 static FILE                     *fin = NULL;
40 static int                       lineno = 1;
41 static int                       errors = 0;
42 char                            *infile;
43
44 int      yyerror(const char *, ...);
45 int      yyparse(void);
46 int      kw_cmp(const void *, const void *);
47 int      lookup(char *);
48 int      lgetc(FILE *);
49 int      lungetc(int);
50 int      findeol(void);
51 int      yylex(void);
52
53 typedef struct {
54         union {
55                 u_int32_t                number;
56                 char                    *string;
57                 struct ntp_addr_wrap    *addr;
58         } v;
59         int lineno;
60 } YYSTYPE;
61
62 %}
63
64 %token  LISTEN ON
65 %token  SERVER SERVERS
66 %token  ERROR
67 %token  <v.string>              STRING
68 %type   <v.addr>                address
69 %%
70
71 grammar         : /* empty */
72                 | grammar '\n'
73                 | grammar conf_main '\n'
74                 | grammar error '\n'            { errors++; }
75                 ;
76
77 conf_main       : LISTEN ON address     {
78                         struct listen_addr      *la;
79                         struct ntp_addr         *h, *next;
80
81                         if ($3->a == NULL) {
82                                 yyerror("cannot resolve \"%s\"", $3->name);
83                                 free($3->name);
84                                 free($3);
85                                 YYERROR;
86                         }
87
88                         for (h = $3->a; h != NULL; h = next) {
89                                 next = h->next;
90                                 if (h->ss.ss_family == AF_UNSPEC) {
91                                         conf->listen_all = 1;
92                                         free(h);
93                                         continue;
94                                 }
95                                 la = calloc(1, sizeof(struct listen_addr));
96                                 if (la == NULL)
97                                         fatal("listen on calloc");
98                                 la->fd = -1;
99                                 memcpy(&la->sa, &h->ss,
100                                     sizeof(struct sockaddr_storage));
101                                 TAILQ_INSERT_TAIL(&conf->listen_addrs, la,
102                                     entry);
103                                 free(h);
104                         }
105                         free($3->name);
106                         free($3);
107                 }
108                 | SERVERS address       {
109                         struct ntp_peer         *p;
110                         struct ntp_addr         *h, *next;
111
112                         h = $2->a;
113                         do {
114                                 if (h != NULL) {
115                                         next = h->next;
116                                         if (h->ss.ss_family != AF_INET &&
117                                             h->ss.ss_family != AF_INET6) {
118                                                 yyerror("IPv4 or IPv6 address "
119                                                     "or hostname expected");
120                                                 free(h);
121                                                 free($2->name);
122                                                 free($2);
123                                                 YYERROR;
124                                         }
125                                         h->next = NULL;
126                                 } else
127                                         next = NULL;
128
129                                 p = new_peer();
130                                 p->addr = h;
131                                 p->addr_head.a = h;
132                                 p->addr_head.pool = 1;
133                                 p->addr_head.name = strdup($2->name);
134                                 if (p->addr_head.name == NULL)
135                                         fatal(NULL);
136                                 TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
137
138                                 h = next;
139                         } while (h != NULL);
140
141                         free($2->name);
142                         free($2);
143                 }
144                 | SERVER address        {
145                         struct ntp_peer         *p;
146                         struct ntp_addr         *h, *next;
147
148                         p = new_peer();
149                         for (h = $2->a; h != NULL; h = next) {
150                                 next = h->next;
151                                 if (h->ss.ss_family != AF_INET &&
152                                     h->ss.ss_family != AF_INET6) {
153                                         yyerror("IPv4 or IPv6 address "
154                                             "or hostname expected");
155                                         free(h);
156                                         free(p);
157                                         free($2->name);
158                                         free($2);
159                                         YYERROR;
160                                 }
161                                 h->next = p->addr;
162                                 p->addr = h;
163                         }
164
165                         p->addr_head.a = p->addr;
166                         p->addr_head.pool = 0;
167                         p->addr_head.name = strdup($2->name);
168                         if (p->addr_head.name == NULL)
169                                 fatal(NULL);
170                         TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
171                         free($2->name);
172                         free($2);
173                 }
174                 ;
175
176 address         : STRING                {
177                         if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) ==
178                             NULL)
179                                 fatal(NULL);
180                         if (host($1, &$$->a) == -1) {
181                                 yyerror("could not parse address spec \"%s\"",
182                                     $1);
183                                 free($1);
184                                 free($$);
185                                 YYERROR;
186                         }
187                         $$->name = $1;
188                 }
189                 ;
190
191 %%
192
193 struct keywords {
194         const char      *k_name;
195         int              k_val;
196 };
197
198 int
199 yyerror(const char *fmt, ...)
200 {
201         va_list          ap;
202         char            *nfmt;
203
204         errors = 1;
205         va_start(ap, fmt);
206         if (asprintf(&nfmt, "%s:%d: %s", infile, yylval.lineno, fmt) == -1)
207                 fatalx("yyerror asprintf");
208         vlog(LOG_CRIT, nfmt, ap);
209         va_end(ap);
210         free(nfmt);
211         return (0);
212 }
213
214 int
215 kw_cmp(const void *k, const void *e)
216 {
217         return (strcmp(k, ((const struct keywords *)e)->k_name));
218 }
219
220 int
221 lookup(char *s)
222 {
223         /* this has to be sorted always */
224         static const struct keywords keywords[] = {
225                 { "listen",             LISTEN},
226                 { "on",                 ON},
227                 { "server",             SERVER},
228                 { "servers",            SERVERS}
229         };
230         const struct keywords   *p;
231
232         p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
233             sizeof(keywords[0]), kw_cmp);
234
235         if (p)
236                 return (p->k_val);
237         else
238                 return (STRING);
239 }
240
241 #define MAXPUSHBACK     128
242
243 char    *parsebuf;
244 int      parseindex;
245 char     pushback_buffer[MAXPUSHBACK];
246 int      pushback_index = 0;
247
248 int
249 lgetc(FILE *f)
250 {
251         int     c, next;
252
253         if (parsebuf) {
254                 /* Read character from the parsebuffer instead of input. */
255                 if (parseindex >= 0) {
256                         c = parsebuf[parseindex++];
257                         if (c != '\0')
258                                 return (c);
259                         parsebuf = NULL;
260                 } else
261                         parseindex++;
262         }
263
264         if (pushback_index)
265                 return (pushback_buffer[--pushback_index]);
266
267         while ((c = getc(f)) == '\\') {
268                 next = getc(f);
269                 if (next != '\n') {
270                         if (isspace(next))
271                                 yyerror("whitespace after \\");
272                         ungetc(next, f);
273                         break;
274                 }
275                 yylval.lineno = lineno;
276                 lineno++;
277         }
278         if (c == '\t' || c == ' ') {
279                 /* Compress blanks to a single space. */
280                 do {
281                         c = getc(f);
282                 } while (c == '\t' || c == ' ');
283                 ungetc(c, f);
284                 c = ' ';
285         }
286
287         return (c);
288 }
289
290 int
291 lungetc(int c)
292 {
293         if (c == EOF)
294                 return (EOF);
295         if (parsebuf) {
296                 parseindex--;
297                 if (parseindex >= 0)
298                         return (c);
299         }
300         if (pushback_index < MAXPUSHBACK-1)
301                 return (pushback_buffer[pushback_index++] = c);
302         else
303                 return (EOF);
304 }
305
306 int
307 findeol(void)
308 {
309         int     c;
310
311         parsebuf = NULL;
312         pushback_index = 0;
313
314         /* skip to either EOF or the first real EOL */
315         while (1) {
316                 c = lgetc(fin);
317                 if (c == '\n') {
318                         lineno++;
319                         break;
320                 }
321                 if (c == EOF)
322                         break;
323         }
324         return (ERROR);
325 }
326
327 int
328 yylex(void)
329 {
330         char     buf[8096];
331         char    *p;
332         int      endc, c;
333         int      token;
334
335         p = buf;
336         while ((c = lgetc(fin)) == ' ')
337                 ; /* nothing */
338
339         yylval.lineno = lineno;
340         if (c == '#')
341                 while ((c = lgetc(fin)) != '\n' && c != EOF)
342                         ; /* nothing */
343
344         switch (c) {
345         case '\'':
346         case '"':
347                 endc = c;
348                 while (1) {
349                         if ((c = lgetc(fin)) == EOF)
350                                 return (0);
351                         if (c == endc) {
352                                 *p = '\0';
353                                 break;
354                         }
355                         if (c == '\n') {
356                                 lineno++;
357                                 continue;
358                         }
359                         if (p + 1 >= buf + sizeof(buf) - 1) {
360                                 yyerror("string too long");
361                                 return (findeol());
362                         }
363                         *p++ = (char)c;
364                 }
365                 yylval.v.string = strdup(buf);
366                 if (yylval.v.string == NULL)
367                         fatal("yylex: strdup");
368                 return (STRING);
369         }
370
371 #define allowed_in_string(x) \
372         (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
373         x != '{' && x != '}' && x != '<' && x != '>' && \
374         x != '!' && x != '=' && x != '/' && x != '#' && \
375         x != ','))
376
377         if (isalnum(c) || c == ':' || c == '_' || c == '*') {
378                 do {
379                         *p++ = c;
380                         if ((unsigned)(p-buf) >= sizeof(buf)) {
381                                 yyerror("string too long");
382                                 return (findeol());
383                         }
384                 } while ((c = lgetc(fin)) != EOF && (allowed_in_string(c)));
385                 lungetc(c);
386                 *p = '\0';
387                 if ((token = lookup(buf)) == STRING)
388                         if ((yylval.v.string = strdup(buf)) == NULL)
389                                 fatal("yylex: strdup");
390                 return (token);
391         }
392         if (c == '\n') {
393                 yylval.lineno = lineno;
394                 lineno++;
395         }
396         if (c == EOF)
397                 return (0);
398         return (c);
399 }
400
401 int
402 parse_config(char *filename, struct ntpd_conf *xconf)
403 {
404         conf = xconf;
405         lineno = 1;
406         errors = 0;
407         TAILQ_INIT(&conf->listen_addrs);
408         TAILQ_INIT(&conf->ntp_peers);
409
410         if ((fin = fopen(filename, "r")) == NULL) {
411                 log_warn("%s", filename);
412                 return (-1);
413         }
414         infile = filename;
415
416         yyparse();
417
418         fclose(fin);
419
420         return (errors ? -1 : 0);
421 }