xref: /openbsd-src/usr.sbin/dhcpd/conflex.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: conflex.c,v 1.9 2006/12/17 18:03:33 stevesk Exp $	*/
2 
3 /* Lexical scanner for dhcpd config file... */
4 
5 /*
6  * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of The Internet Software Consortium nor the names
19  *    of its contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * This software has been written for the Internet Software Consortium
37  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38  * Enterprises.  To learn more about the Internet Software Consortium,
39  * see ``http://www.vix.com/isc''.  To learn more about Vixie
40  * Enterprises, see ``http://www.vix.com''.
41  */
42 
43 #include <ctype.h>
44 
45 #include "dhcpd.h"
46 #include "dhctoken.h"
47 
48 int lexline;
49 int lexchar;
50 char *token_line;
51 char *prev_line;
52 char *cur_line;
53 char *tlname;
54 int eol_token;
55 
56 static char line1[81];
57 static char line2[81];
58 static int lpos;
59 static int line;
60 static int tlpos;
61 static int tline;
62 static int token;
63 static int ugflag;
64 static char *tval;
65 static char tokbuf[1500];
66 
67 static int get_char(FILE *);
68 static int get_token(FILE *);
69 static void skip_to_eol(FILE *);
70 static int read_string(FILE *);
71 static int read_number(int, FILE *);
72 static int read_num_or_name(int, FILE *);
73 static int intern(char *, int);
74 
75 void
76 new_parse(char *name)
77 {
78 	tlname = name;
79 	lpos = line = 1;
80 	cur_line = line1;
81 	prev_line = line2;
82 	token_line = cur_line;
83 	cur_line[0] = prev_line[0] = 0;
84 	warnings_occurred = 0;
85 }
86 
87 static int
88 get_char(FILE *cfile)
89 {
90 	int c = getc(cfile);
91 	if (!ugflag) {
92 		if (c == '\n') {
93 			if (cur_line == line1) {
94 				cur_line = line2;
95 				prev_line = line1;
96 			} else {
97 				cur_line = line2;
98 				prev_line = line1;
99 			}
100 			line++;
101 			lpos = 1;
102 			cur_line[0] = 0;
103 		} else if (c != EOF) {
104 			if (lpos < sizeof(line1)) {
105 				cur_line[lpos - 1] = c;
106 				cur_line[lpos] = 0;
107 			}
108 			lpos++;
109 		}
110 	} else
111 		ugflag = 0;
112 	return (c);
113 }
114 
115 static int
116 get_token(FILE *cfile)
117 {
118 	int		c, ttok;
119 	static char	tb[2];
120 	int		l, p;
121 
122 	do {
123 		l = line;
124 		p = lpos;
125 
126 		c = get_char(cfile);
127 
128 		if (!(c == '\n' && eol_token) && isascii(c) && isspace(c))
129 			continue;
130 		if (c == '#') {
131 			skip_to_eol(cfile);
132 			continue;
133 		}
134 		if (c == '"') {
135 			lexline = l;
136 			lexchar = p;
137 			ttok = read_string(cfile);
138 			break;
139 		}
140 		if ((isascii(c) && isdigit(c)) || c == '-') {
141 			lexline = l;
142 			lexchar = p;
143 			ttok = read_number(c, cfile);
144 			break;
145 		} else if (isascii(c) && isalpha(c)) {
146 			lexline = l;
147 			lexchar = p;
148 			ttok = read_num_or_name(c, cfile);
149 			break;
150 		} else {
151 			lexline = l;
152 			lexchar = p;
153 			tb[0] = c;
154 			tb[1] = 0;
155 			tval = tb;
156 			ttok = c;
157 			break;
158 		}
159 	} while (1);
160 	return (ttok);
161 }
162 
163 int
164 next_token(char **rval, FILE *cfile)
165 {
166 	int	rv;
167 
168 	if (token) {
169 		if (lexline != tline)
170 			token_line = cur_line;
171 		lexchar = tlpos;
172 		lexline = tline;
173 		rv = token;
174 		token = 0;
175 	} else {
176 		rv = get_token(cfile);
177 		token_line = cur_line;
178 	}
179 	if (rval)
180 		*rval = tval;
181 
182 	return (rv);
183 }
184 
185 int
186 peek_token(char **rval, FILE *cfile)
187 {
188 	int	x;
189 
190 	if (!token) {
191 		tlpos = lexchar;
192 		tline = lexline;
193 		token = get_token(cfile);
194 		if (lexline != tline)
195 			token_line = prev_line;
196 		x = lexchar;
197 		lexchar = tlpos;
198 		tlpos = x;
199 		x = lexline;
200 		lexline = tline;
201 		tline = x;
202 	}
203 	if (rval)
204 		*rval = tval;
205 
206 	return (token);
207 }
208 
209 static void
210 skip_to_eol(FILE *cfile)
211 {
212 	int	c;
213 
214 	do {
215 		c = get_char(cfile);
216 		if (c == EOF)
217 			return;
218 		if (c == '\n')
219 			return;
220 	} while (1);
221 }
222 
223 static int
224 read_string(FILE *cfile)
225 {
226 	int	i, c, bs = 0;
227 
228 	for (i = 0; i < sizeof(tokbuf); i++) {
229 		c = get_char(cfile);
230 		if (c == EOF) {
231 			parse_warn("eof in string constant");
232 			break;
233 		}
234 		if (bs) {
235 			bs = 0;
236 			tokbuf[i] = c;
237 		} else if (c == '\\')
238 			bs = 1;
239 		else if (c == '"')
240 			break;
241 		else
242 			tokbuf[i] = c;
243 	}
244 	/*
245 	 * Normally, I'd feel guilty about this, but we're talking about
246 	 * strings that'll fit in a DHCP packet here...
247 	 */
248 	if (i == sizeof(tokbuf)) {
249 		parse_warn("string constant larger than internal buffer");
250 		i--;
251 	}
252 	tokbuf[i] = 0;
253 	tval = tokbuf;
254 	return (TOK_STRING);
255 }
256 
257 static int
258 read_number(int c, FILE *cfile)
259 {
260 	int	seenx = 0, i = 0, token = TOK_NUMBER;
261 
262 	tokbuf[i++] = c;
263 	for (; i < sizeof(tokbuf); i++) {
264 		c = get_char(cfile);
265 		if (!seenx && c == 'x')
266 			seenx = 1;
267 		else if (!isascii(c) || !isxdigit(c)) {
268 			ungetc(c, cfile);
269 			ugflag = 1;
270 			break;
271 		}
272 		tokbuf[i] = c;
273 	}
274 	if (i == sizeof(tokbuf)) {
275 		parse_warn("numeric token larger than internal buffer");
276 		i--;
277 	}
278 	tokbuf[i] = 0;
279 	tval = tokbuf;
280 
281 	return (token);
282 }
283 
284 static int
285 read_num_or_name(int c, FILE *cfile)
286 {
287 	int	i = 0;
288 	int	rv = TOK_NUMBER_OR_NAME;
289 
290 	tokbuf[i++] = c;
291 	for (; i < sizeof(tokbuf); i++) {
292 		c = get_char(cfile);
293 		if (!isascii(c) || (c != '-' && c != '_' && !isalnum(c))) {
294 			ungetc(c, cfile);
295 			ugflag = 1;
296 			break;
297 		}
298 		if (!isxdigit(c))
299 			rv = TOK_NAME;
300 		tokbuf[i] = c;
301 	}
302 	if (i == sizeof(tokbuf)) {
303 		parse_warn("token larger than internal buffer");
304 		i--;
305 	}
306 	tokbuf[i] = 0;
307 	tval = tokbuf;
308 
309 	return (intern(tval, rv));
310 }
311 
312 static const struct keywords {
313 	const char	*k_name;
314 	int		k_val;
315 } keywords[] = {
316 	{ "abandoned",				TOK_ABANDONED },
317 	{ "allow",				TOK_ALLOW },
318 	{ "always-reply-rfc1048",		TOK_ALWAYS_REPLY_RFC1048 },
319 	{ "authoritative",			TOK_AUTHORITATIVE },
320 	{ "booting",				TOK_BOOTING },
321 	{ "bootp",				TOK_BOOTP },
322 	{ "class",				TOK_CLASS },
323 	{ "client-hostname",			TOK_CLIENT_HOSTNAME },
324 	{ "default-lease-time",			TOK_DEFAULT_LEASE_TIME },
325 	{ "deny",				TOK_DENY },
326 	{ "domain",				TOK_DOMAIN },
327 	{ "dynamic-bootp",			TOK_DYNAMIC_BOOTP },
328 	{ "dynamic-bootp-lease-cutoff",		TOK_DYNAMIC_BOOTP_LEASE_CUTOFF },
329 	{ "dynamic-bootp-lease-length",		TOK_DYNAMIC_BOOTP_LEASE_LENGTH },
330 	{ "ends",				TOK_ENDS },
331 	{ "ethernet",				TOK_ETHERNET },
332 	{ "fddi",				TOK_FDDI },
333 	{ "filename",				TOK_FILENAME },
334 	{ "fixed-address",			TOK_FIXED_ADDR },
335 	{ "get-lease-hostnames",		TOK_GET_LEASE_HOSTNAMES },
336 	{ "group",				TOK_GROUP },
337 	{ "hardware",				TOK_HARDWARE },
338 	{ "host",				TOK_HOST },
339 	{ "hostname",				TOK_HOSTNAME },
340 	{ "lease",				TOK_LEASE },
341 	{ "max-lease-time",			TOK_MAX_LEASE_TIME },
342 	{ "netmask",				TOK_NETMASK },
343 	{ "next-server",			TOK_NEXT_SERVER },
344 	{ "not",				TOK_TOKEN_NOT },
345 	{ "option",				TOK_OPTION },
346 	{ "range",				TOK_RANGE },
347 	{ "server-identifier",			TOK_SERVER_IDENTIFIER },
348 	{ "server-name",			TOK_SERVER_NAME },
349 	{ "shared-network",			TOK_SHARED_NETWORK },
350 	{ "starts",				TOK_STARTS },
351 	{ "subnet",				TOK_SUBNET },
352 	{ "timeout",				TOK_TIMEOUT },
353 	{ "timestamp",				TOK_TIMESTAMP },
354 	{ "token-ring",				TOK_TOKEN_RING },
355 	{ "uid",				TOK_UID },
356 	{ "unknown-clients",			TOK_UNKNOWN_CLIENTS },
357 	{ "use-host-decl-names",		TOK_USE_HOST_DECL_NAMES },
358 	{ "use-lease-addr-for-default-route",	TOK_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE },
359 	{ "user-class",				TOK_USER_CLASS },
360 	{ "vendor-class",			TOK_VENDOR_CLASS }
361 };
362 
363 int
364 kw_cmp(const void *k, const void *e)
365 {
366 	return (strcasecmp(k, ((const struct keywords *)e)->k_name));
367 }
368 
369 static int
370 intern(char *atom, int dfv)
371 {
372 	const struct keywords *p;
373 
374 	p = bsearch(atom, keywords, sizeof(keywords)/sizeof(keywords[0]),
375 	    sizeof(keywords[0]), kw_cmp);
376 	if (p)
377 		return (p->k_val);
378 	return (dfv);
379 }
380