1 /* $OpenBSD: parse_lease.y,v 1.1 2024/06/05 16:15:47 florian Exp $ */
2
3 /*
4 * Copyright (c) 2018, 2024 Florian Obser <florian@openbsd.org>
5 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
7 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
8 * Copyright (c) 2001 Markus Friedl. All rights reserved.
9 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
10 * Copyright (c) 2001 Theo de Raadt. All rights reserved.
11 *
12 * Permission to use, copy, modify, and distribute this software for any
13 * purpose with or without fee is hereby granted, provided that the above
14 * copyright notice and this permission notice appear in all copies.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 */
24
25 %{
26 #include <sys/types.h>
27 #include <sys/queue.h>
28 #include <sys/socket.h>
29 #include <sys/stat.h>
30
31 #include <net/if.h>
32
33 #include <netinet/in.h>
34
35 #include <arpa/inet.h>
36
37 #include <ctype.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <event.h>
41 #include <imsg.h>
42 #include <limits.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <syslog.h>
47 #include <unistd.h>
48 #include <vis.h>
49
50 #include "log.h"
51 #include "dhcp6leased.h"
52 #include "frontend.h"
53
54 extern TAILQ_HEAD(files, file) files;
55 extern struct file *file, *topfile;
56 int yyparse(void);
57 int yylex(void);
58 int yyerror(const char *, ...)
59 __attribute__((__format__ (printf, 1, 2)))
60 __attribute__((__nonnull__ (1)));
61 int pllookup(char *);
62
63 struct imsg_ifinfo *ifinfo;
64 static int errors;
65
66 typedef struct {
67 union {
68 int64_t number;
69 char *string;
70 } v;
71 int lineno;
72 } YYSTYPE;
73
74 %}
75
76 %token ERROR IAPD
77
78 %token <v.string> STRING
79 %token <v.number> NUMBER
80
81 %%
82
83 grammar : /* empty */
84 | grammar '\n'
85 | grammar ia_pd '\n'
86 | grammar error '\n' { file->errors++; }
87 ;
88
89 ia_pd : IAPD NUMBER STRING NUMBER {
90 if ($2 < 0 || $2 > MAX_IA) {
91 yyerror("invalid IA_ID %lld", $2);
92 free($3);
93 YYERROR;
94 }
95 if ($4 < 1 || $4 > 128) {
96 yyerror("invalid prefix length %lld", $4);
97 free($3);
98 ifinfo->pds[$2].prefix_len = 0;
99 YYERROR;
100 } else
101 ifinfo->pds[$2].prefix_len = $4;
102
103 if (inet_pton(AF_INET6, $3, &ifinfo->pds[$2].prefix)
104 != 1) {
105 yyerror("invalid prefix %s", $3);
106 free($3);
107 ifinfo->pds[$2].prefix_len = 0;
108 YYERROR;
109 }
110 free($3);
111 }
112 ;
113 %%
114
115 struct keywords {
116 const char *k_name;
117 int k_val;
118 };
119
120 int
yyerror(const char * fmt,...)121 yyerror(const char *fmt, ...)
122 {
123 va_list ap;
124 char *msg;
125
126 file->errors++;
127 va_start(ap, fmt);
128 if (vasprintf(&msg, fmt, ap) == -1)
129 fatalx("yyerror vasprintf");
130 va_end(ap);
131 logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
132 free(msg);
133 return (0);
134 }
135
136 int
pllookup(char * s)137 pllookup(char *s)
138 {
139 /* This has to be sorted always. */
140 static const struct keywords keywords[] = {
141 {"ia_pd", IAPD},
142 };
143 const struct keywords *p;
144
145 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
146 sizeof(keywords[0]), kw_cmp);
147
148 if (p)
149 return (p->k_val);
150 else
151 return (STRING);
152 }
153
154 int
yylex(void)155 yylex(void)
156 {
157 char buf[8096];
158 char *p;
159 int quotec, next, c;
160 int token;
161
162 p = buf;
163 while ((c = lgetc(0)) == ' ' || c == '\t')
164 ; /* nothing */
165
166 yylval.lineno = file->lineno;
167 if (c == '#')
168 while ((c = lgetc(0)) != '\n' && c != EOF)
169 ; /* nothing */
170 switch (c) {
171 case '\'':
172 case '"':
173 quotec = c;
174 while (1) {
175 if ((c = lgetc(quotec)) == EOF)
176 return (0);
177 if (c == '\n') {
178 file->lineno++;
179 continue;
180 } else if (c == '\\') {
181 if ((next = lgetc(quotec)) == EOF)
182 return (0);
183 if (next == quotec || next == ' ' ||
184 next == '\t')
185 c = next;
186 else if (next == '\n') {
187 file->lineno++;
188 continue;
189 } else
190 lungetc(next);
191 } else if (c == quotec) {
192 *p = '\0';
193 break;
194 } else if (c == '\0') {
195 yyerror("syntax error");
196 return (findeol());
197 }
198 if (p + 1 >= buf + sizeof(buf) - 1) {
199 yyerror("string too long");
200 return (findeol());
201 }
202 *p++ = c;
203 }
204 yylval.v.string = strdup(buf);
205 if (yylval.v.string == NULL)
206 err(1, "yylex: strdup");
207 return (STRING);
208 }
209
210 #define allowed_to_end_number(x) \
211 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
212
213 if (c == '-' || isdigit(c)) {
214 do {
215 *p++ = c;
216 if ((size_t)(p-buf) >= sizeof(buf)) {
217 yyerror("string too long");
218 return (findeol());
219 }
220 } while ((c = lgetc(0)) != EOF && isdigit(c));
221 lungetc(c);
222 if (p == buf + 1 && buf[0] == '-')
223 goto nodigits;
224 if (c == EOF || allowed_to_end_number(c)) {
225 const char *errstr = NULL;
226
227 *p = '\0';
228 yylval.v.number = strtonum(buf, LLONG_MIN,
229 LLONG_MAX, &errstr);
230 if (errstr) {
231 yyerror("\"%s\" invalid number: %s",
232 buf, errstr);
233 return (findeol());
234 }
235 return (NUMBER);
236 } else {
237 nodigits:
238 while (p > buf + 1)
239 lungetc((unsigned char)*--p);
240 c = (unsigned char)*--p;
241 if (c == '-')
242 return (c);
243 }
244 }
245
246 #define allowed_in_string(x) \
247 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
248 x != '{' && x != '}' && \
249 x != '!' && x != '=' && x != '#' && \
250 x != ','))
251
252 if (isalnum(c) || c == ':' || c == '_') {
253 do {
254 *p++ = c;
255 if ((size_t)(p-buf) >= sizeof(buf)) {
256 yyerror("string too long");
257 return (findeol());
258 }
259 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
260 lungetc(c);
261 *p = '\0';
262 if ((token = pllookup(buf)) == STRING)
263 if ((yylval.v.string = strdup(buf)) == NULL)
264 err(1, "yylex: strdup");
265 return (token);
266 }
267 if (c == '\n') {
268 yylval.lineno = file->lineno;
269 file->lineno++;
270 }
271 if (c == EOF)
272 return (0);
273 return (c);
274 }
275
276 void
parse_lease(const char * filename,struct imsg_ifinfo * imsg)277 parse_lease(const char *filename, struct imsg_ifinfo *imsg)
278 {
279 ifinfo = imsg;
280 file = pushfile(filename, 0);
281 if (file == NULL)
282 return;
283
284 topfile = file;
285
286 yyparse();
287 errors = file->errors;
288 popfile();
289 }
290