xref: /openbsd-src/sbin/dhcp6leased/parse_lease.y (revision 763cddbde0e1b7af7c735976159183bf19089258)
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