xref: /freebsd/sbin/dhclient/conflex.c (revision 911f0260390e18cf85f3dbf2c719b593efdc1e3c)
1 /*	$OpenBSD: conflex.c,v 1.7 2004/09/15 19:02:38 deraadt Exp $	*/
2 
3 /* Lexical scanner for dhcpd config file... */
4 
5 /*-
6  * SPDX-License-Identifier: BSD-3-Clause
7  *
8  * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of The Internet Software Consortium nor the names
21  *    of its contributors may be used to endorse or promote products derived
22  *    from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
25  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
26  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
29  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
32  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
33  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
35  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  * This software has been written for the Internet Software Consortium
39  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
40  * Enterprises.  To learn more about the Internet Software Consortium,
41  * see ``http://www.vix.com/isc''.  To learn more about Vixie
42  * Enterprises, see ``http://www.vix.com''.
43  */
44 
45 #include <sys/cdefs.h>
46 __FBSDID("$FreeBSD$");
47 
48 #include <ctype.h>
49 
50 #include "dhcpd.h"
51 #include "dhctoken.h"
52 
53 int lexline;
54 int lexchar;
55 char *token_line;
56 static char *prev_line;
57 static char *cur_line;
58 const char *tlname;
59 int eol_token;
60 
61 static char line1[81];
62 static char line2[81];
63 static unsigned lpos;
64 static unsigned line;
65 static int tlpos;
66 static int tline;
67 static int token;
68 static int ugflag;
69 static char *tval;
70 static char tokbuf[1500];
71 
72 static int get_char(FILE *);
73 static int get_token(FILE *);
74 static void skip_to_eol(FILE *);
75 static int read_string(FILE *);
76 static int read_number(int, FILE *);
77 static int read_num_or_name(int, FILE *);
78 static int intern(char *, int);
79 
80 void
81 new_parse(const char *name)
82 {
83 	tlname = name;
84 	lpos = line = 1;
85 	cur_line = line1;
86 	prev_line = line2;
87 	token_line = cur_line;
88 	cur_line[0] = prev_line[0] = 0;
89 	warnings_occurred = 0;
90 }
91 
92 static int
93 get_char(FILE *cfile)
94 {
95 	int c = getc(cfile);
96 	if (!ugflag) {
97 		if (c == '\n') {
98 			if (cur_line == line1) {
99 				cur_line = line2;
100 				prev_line = line1;
101 			} else {
102 				cur_line = line1;
103 				prev_line = line2;
104 			}
105 			line++;
106 			lpos = 1;
107 			cur_line[0] = 0;
108 		} else if (c != EOF) {
109 			if (lpos < sizeof(line1)) {
110 				cur_line[lpos - 1] = c;
111 				cur_line[lpos] = 0;
112 			}
113 			lpos++;
114 		}
115 	} else
116 		ugflag = 0;
117 	return (c);
118 }
119 
120 static int
121 get_token(FILE *cfile)
122 {
123 	int		c, ttok;
124 	static char	tb[2];
125 	int		l, p;
126 
127 	do {
128 		l = line;
129 		p = lpos;
130 
131 		c = get_char(cfile);
132 
133 		if (!(c == '\n' && eol_token) && isascii(c) && isspace(c))
134 			continue;
135 		if (c == '#') {
136 			skip_to_eol(cfile);
137 			continue;
138 		}
139 		if (c == '"') {
140 			lexline = l;
141 			lexchar = p;
142 			ttok = read_string(cfile);
143 			break;
144 		}
145 		if ((isascii(c) && isdigit(c)) || c == '-') {
146 			lexline = l;
147 			lexchar = p;
148 			ttok = read_number(c, cfile);
149 			break;
150 		} else if (isascii(c) && isalpha(c)) {
151 			lexline = l;
152 			lexchar = p;
153 			ttok = read_num_or_name(c, cfile);
154 			break;
155 		} else {
156 			lexline = l;
157 			lexchar = p;
158 			tb[0] = c;
159 			tb[1] = 0;
160 			tval = tb;
161 			ttok = c;
162 			break;
163 		}
164 	} while (1);
165 	return (ttok);
166 }
167 
168 int
169 next_token(char **rval, FILE *cfile)
170 {
171 	int	rv;
172 
173 	if (token) {
174 		if (lexline != tline)
175 			token_line = cur_line;
176 		lexchar = tlpos;
177 		lexline = tline;
178 		rv = token;
179 		token = 0;
180 	} else {
181 		rv = get_token(cfile);
182 		token_line = cur_line;
183 	}
184 	if (rval)
185 		*rval = tval;
186 
187 	return (rv);
188 }
189 
190 int
191 peek_token(char **rval, FILE *cfile)
192 {
193 	int	x;
194 
195 	if (!token) {
196 		tlpos = lexchar;
197 		tline = lexline;
198 		token = get_token(cfile);
199 		if (lexline != tline)
200 			token_line = prev_line;
201 		x = lexchar;
202 		lexchar = tlpos;
203 		tlpos = x;
204 		x = lexline;
205 		lexline = tline;
206 		tline = x;
207 	}
208 	if (rval)
209 		*rval = tval;
210 
211 	return (token);
212 }
213 
214 static void
215 skip_to_eol(FILE *cfile)
216 {
217 	int	c;
218 
219 	do {
220 		c = get_char(cfile);
221 		if (c == EOF)
222 			return;
223 		if (c == '\n')
224 			return;
225 	} while (1);
226 }
227 
228 static int
229 read_string(FILE *cfile)
230 {
231 	int	c, bs = 0;
232 	unsigned i;
233 
234 	for (i = 0; i < sizeof(tokbuf); i++) {
235 		c = get_char(cfile);
236 		if (c == EOF) {
237 			parse_warn("eof in string constant");
238 			break;
239 		}
240 		if (bs) {
241 			bs = 0;
242 			i--;
243 			tokbuf[i] = c;
244 		} else if (c == '\\')
245 			bs = 1;
246 		else if (c == '"')
247 			break;
248 		else
249 			tokbuf[i] = c;
250 	}
251 	/*
252 	 * Normally, I'd feel guilty about this, but we're talking about
253 	 * strings that'll fit in a DHCP packet here...
254 	 */
255 	if (i == sizeof(tokbuf)) {
256 		parse_warn("string constant larger than internal buffer");
257 		i--;
258 	}
259 	tokbuf[i] = 0;
260 	tval = tokbuf;
261 	return (STRING);
262 }
263 
264 static int
265 read_number(int c, FILE *cfile)
266 {
267 	int	seenx = 0, _token = NUMBER;
268 	unsigned i = 0;
269 
270 	tokbuf[i++] = c;
271 	for (; i < sizeof(tokbuf); i++) {
272 		c = get_char(cfile);
273 		if (!seenx && c == 'x')
274 			seenx = 1;
275 		else if (!isascii(c) || !isxdigit(c)) {
276 			ungetc(c, cfile);
277 			ugflag = 1;
278 			break;
279 		}
280 		tokbuf[i] = c;
281 	}
282 	if (i == sizeof(tokbuf)) {
283 		parse_warn("numeric token larger than internal buffer");
284 		i--;
285 	}
286 	tokbuf[i] = 0;
287 	tval = tokbuf;
288 
289 	return (_token);
290 }
291 
292 static int
293 read_num_or_name(int c, FILE *cfile)
294 {
295 	unsigned i = 0;
296 	int	rv = NUMBER_OR_NAME;
297 
298 	tokbuf[i++] = c;
299 	for (; i < sizeof(tokbuf); i++) {
300 		c = get_char(cfile);
301 		if (!isascii(c) || (c != '-' && c != '_' && !isalnum(c))) {
302 			ungetc(c, cfile);
303 			ugflag = 1;
304 			break;
305 		}
306 		if (!isxdigit(c))
307 			rv = NAME;
308 		tokbuf[i] = c;
309 	}
310 	if (i == sizeof(tokbuf)) {
311 		parse_warn("token larger than internal buffer");
312 		i--;
313 	}
314 	tokbuf[i] = 0;
315 	tval = tokbuf;
316 
317 	return (intern(tval, rv));
318 }
319 
320 static int
321 intern(char *atom, int dfv)
322 {
323 	if (!isascii(atom[0]))
324 		return (dfv);
325 
326 	switch (tolower(atom[0])) {
327 	case 'a':
328 		if (!strcasecmp(atom + 1, "lways-reply-rfc1048"))
329 			return (ALWAYS_REPLY_RFC1048);
330 		if (!strcasecmp(atom + 1, "ppend"))
331 			return (APPEND);
332 		if (!strcasecmp(atom + 1, "llow"))
333 			return (ALLOW);
334 		if (!strcasecmp(atom + 1, "lias"))
335 			return (ALIAS);
336 		if (!strcasecmp(atom + 1, "bandoned"))
337 			return (ABANDONED);
338 		if (!strcasecmp(atom + 1, "uthoritative"))
339 			return (AUTHORITATIVE);
340 		break;
341 	case 'b':
342 		if (!strcasecmp(atom + 1, "ackoff-cutoff"))
343 			return (BACKOFF_CUTOFF);
344 		if (!strcasecmp(atom + 1, "ootp"))
345 			return (BOOTP);
346 		if (!strcasecmp(atom + 1, "ooting"))
347 			return (BOOTING);
348 		if (!strcasecmp(atom + 1, "oot-unknown-clients"))
349 			return (BOOT_UNKNOWN_CLIENTS);
350 		break;
351 	case 'c':
352 		if (!strcasecmp(atom + 1, "lass"))
353 			return (CLASS);
354 		if (!strcasecmp(atom + 1, "iaddr"))
355 			return (CIADDR);
356 		if (!strcasecmp(atom + 1, "lient-identifier"))
357 			return (CLIENT_IDENTIFIER);
358 		if (!strcasecmp(atom + 1, "lient-hostname"))
359 			return (CLIENT_HOSTNAME);
360 		break;
361 	case 'd':
362 		if (!strcasecmp(atom + 1, "omain"))
363 			return (DOMAIN);
364 		if (!strcasecmp(atom + 1, "eny"))
365 			return (DENY);
366 		if (!strncasecmp(atom + 1, "efault", 6)) {
367 			if (!atom[7])
368 				return (DEFAULT);
369 			if (!strcasecmp(atom + 7, "-lease-time"))
370 				return (DEFAULT_LEASE_TIME);
371 			break;
372 		}
373 		if (!strncasecmp(atom + 1, "ynamic-bootp", 12)) {
374 			if (!atom[13])
375 				return (DYNAMIC_BOOTP);
376 			if (!strcasecmp(atom + 13, "-lease-cutoff"))
377 				return (DYNAMIC_BOOTP_LEASE_CUTOFF);
378 			if (!strcasecmp(atom + 13, "-lease-length"))
379 				return (DYNAMIC_BOOTP_LEASE_LENGTH);
380 			break;
381 		}
382 		break;
383 	case 'e':
384 		if (!strcasecmp(atom + 1, "thernet"))
385 			return (ETHERNET);
386 		if (!strcasecmp(atom + 1, "nds"))
387 			return (ENDS);
388 		if (!strcasecmp(atom + 1, "xpire"))
389 			return (EXPIRE);
390 		break;
391 	case 'f':
392 		if (!strcasecmp(atom + 1, "ilename"))
393 			return (FILENAME);
394 		if (!strcasecmp(atom + 1, "ixed-address"))
395 			return (FIXED_ADDR);
396 		if (!strcasecmp(atom + 1, "ddi"))
397 			return (FDDI);
398 		break;
399 	case 'g':
400 		if (!strcasecmp(atom + 1, "iaddr"))
401 			return (GIADDR);
402 		if (!strcasecmp(atom + 1, "roup"))
403 			return (GROUP);
404 		if (!strcasecmp(atom + 1, "et-lease-hostnames"))
405 			return (GET_LEASE_HOSTNAMES);
406 		break;
407 	case 'h':
408 		if (!strcasecmp(atom + 1, "ost"))
409 			return (HOST);
410 		if (!strcasecmp(atom + 1, "ardware"))
411 			return (HARDWARE);
412 		if (!strcasecmp(atom + 1, "ostname"))
413 			return (HOSTNAME);
414 		break;
415 	case 'i':
416 		if (!strcasecmp(atom + 1, "gnore"))
417 			return (IGNORE);
418 		if (!strcasecmp(atom + 1, "nitial-interval"))
419 			return (INITIAL_INTERVAL);
420 		if (!strcasecmp(atom + 1, "nterface"))
421 			return (INTERFACE);
422 		break;
423 	case 'l':
424 		if (!strcasecmp(atom + 1, "ease"))
425 			return (LEASE);
426 		break;
427 	case 'm':
428 		if (!strcasecmp(atom + 1, "ax-lease-time"))
429 			return (MAX_LEASE_TIME);
430 		if (!strncasecmp(atom + 1, "edi", 3)) {
431 			if (!strcasecmp(atom + 4, "a"))
432 				return (MEDIA);
433 			if (!strcasecmp(atom + 4, "um"))
434 				return (MEDIUM);
435 			break;
436 		}
437 		break;
438 	case 'n':
439 		if (!strcasecmp(atom + 1, "ameserver"))
440 			return (NAMESERVER);
441 		if (!strcasecmp(atom + 1, "etmask"))
442 			return (NETMASK);
443 		if (!strcasecmp(atom + 1, "ext-server"))
444 			return (NEXT_SERVER);
445 		if (!strcasecmp(atom + 1, "ot"))
446 			return (TOKEN_NOT);
447 		break;
448 	case 'o':
449 		if (!strcasecmp(atom + 1, "ption"))
450 			return (OPTION);
451 		if (!strcasecmp(atom + 1, "ne-lease-per-client"))
452 			return (ONE_LEASE_PER_CLIENT);
453 		break;
454 	case 'p':
455 		if (!strcasecmp(atom + 1, "repend"))
456 			return (PREPEND);
457 		if (!strcasecmp(atom + 1, "acket"))
458 			return (PACKET);
459 		break;
460 	case 'r':
461 		if (!strcasecmp(atom + 1, "ange"))
462 			return (RANGE);
463 		if (!strcasecmp(atom + 1, "equest"))
464 			return (REQUEST);
465 		if (!strcasecmp(atom + 1, "equire"))
466 			return (REQUIRE);
467 		if (!strcasecmp(atom + 1, "etry"))
468 			return (RETRY);
469 		if (!strcasecmp(atom + 1, "enew"))
470 			return (RENEW);
471 		if (!strcasecmp(atom + 1, "ebind"))
472 			return (REBIND);
473 		if (!strcasecmp(atom + 1, "eboot"))
474 			return (REBOOT);
475 		if (!strcasecmp(atom + 1, "eject"))
476 			return (REJECT);
477 		break;
478 	case 's':
479 		if (!strcasecmp(atom + 1, "earch"))
480 			return (SEARCH);
481 		if (!strcasecmp(atom + 1, "tarts"))
482 			return (STARTS);
483 		if (!strcasecmp(atom + 1, "iaddr"))
484 			return (SIADDR);
485 		if (!strcasecmp(atom + 1, "ubnet"))
486 			return (SUBNET);
487 		if (!strcasecmp(atom + 1, "hared-network"))
488 			return (SHARED_NETWORK);
489 		if (!strcasecmp(atom + 1, "erver-name"))
490 			return (SERVER_NAME);
491 		if (!strcasecmp(atom + 1, "erver-identifier"))
492 			return (SERVER_IDENTIFIER);
493 		if (!strcasecmp(atom + 1, "elect-timeout"))
494 			return (SELECT_TIMEOUT);
495 		if (!strcasecmp(atom + 1, "end"))
496 			return (SEND);
497 		if (!strcasecmp(atom + 1, "cript"))
498 			return (SCRIPT);
499 		if (!strcasecmp(atom + 1, "upersede"))
500 			return (SUPERSEDE);
501 		break;
502 	case 't':
503 		if (!strcasecmp(atom + 1, "imestamp"))
504 			return (TIMESTAMP);
505 		if (!strcasecmp(atom + 1, "imeout"))
506 			return (TIMEOUT);
507 		if (!strcasecmp(atom + 1, "oken-ring"))
508 			return (TOKEN_RING);
509 		break;
510 	case 'u':
511 		if (!strncasecmp(atom + 1, "se", 2)) {
512 			if (!strcasecmp(atom + 3, "r-class"))
513 				return (USER_CLASS);
514 			if (!strcasecmp(atom + 3, "-host-decl-names"))
515 				return (USE_HOST_DECL_NAMES);
516 			if (!strcasecmp(atom + 3,
517 					 "-lease-addr-for-default-route"))
518 				return (USE_LEASE_ADDR_FOR_DEFAULT_ROUTE);
519 			break;
520 		}
521 		if (!strcasecmp(atom + 1, "id"))
522 			return (UID);
523 		if (!strcasecmp(atom + 1, "nknown-clients"))
524 			return (UNKNOWN_CLIENTS);
525 		break;
526 	case 'v':
527 		if (!strcasecmp(atom + 1, "endor-class"))
528 			return (VENDOR_CLASS);
529 		if (!strcasecmp(atom + 1, "lan-pcp"))
530 			return (VLAN_PCP);
531 		break;
532 	case 'y':
533 		if (!strcasecmp(atom + 1, "iaddr"))
534 			return (YIADDR);
535 		break;
536 	}
537 	return (dfv);
538 }
539