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