xref: /freebsd/sbin/dhclient/conflex.c (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
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 #include <ctype.h>
47 
48 #include "dhcpd.h"
49 #include "dhctoken.h"
50 
51 int lexline;
52 int lexchar;
53 char *token_line;
54 static char *prev_line;
55 static char *cur_line;
56 const char *tlname;
57 int eol_token;
58 
59 static char line1[81];
60 static char line2[81];
61 static unsigned lpos;
62 static unsigned line;
63 static int tlpos;
64 static int tline;
65 static int token;
66 static int ugflag;
67 static char *tval;
68 static char tokbuf[1500];
69 
70 static int get_char(FILE *);
71 static int get_token(FILE *);
72 static void skip_to_eol(FILE *);
73 static int read_string(FILE *);
74 static int read_number(int, FILE *);
75 static int read_num_or_name(int, FILE *);
76 static int intern(char *, int);
77 
78 void
79 new_parse(const char *name)
80 {
81 	tlname = name;
82 	lpos = line = 1;
83 	cur_line = line1;
84 	prev_line = line2;
85 	token_line = cur_line;
86 	cur_line[0] = prev_line[0] = 0;
87 	warnings_occurred = 0;
88 }
89 
90 static int
91 get_char(FILE *cfile)
92 {
93 	int c = getc(cfile);
94 	if (!ugflag) {
95 		if (c == '\n') {
96 			if (cur_line == line1) {
97 				cur_line = line2;
98 				prev_line = line1;
99 			} else {
100 				cur_line = line1;
101 				prev_line = line2;
102 			}
103 			line++;
104 			lpos = 1;
105 			cur_line[0] = 0;
106 		} else if (c != EOF) {
107 			if (lpos < sizeof(line1)) {
108 				cur_line[lpos - 1] = c;
109 				cur_line[lpos] = 0;
110 			}
111 			lpos++;
112 		}
113 	} else
114 		ugflag = 0;
115 	return (c);
116 }
117 
118 static int
119 get_token(FILE *cfile)
120 {
121 	int		c, ttok;
122 	static char	tb[2];
123 	int		l, p;
124 
125 	do {
126 		l = line;
127 		p = lpos;
128 
129 		c = get_char(cfile);
130 
131 		if (!(c == '\n' && eol_token) && isascii(c) && isspace(c))
132 			continue;
133 		if (c == '#') {
134 			skip_to_eol(cfile);
135 			continue;
136 		}
137 		if (c == '"') {
138 			lexline = l;
139 			lexchar = p;
140 			ttok = read_string(cfile);
141 			break;
142 		}
143 		if ((isascii(c) && isdigit(c)) || c == '-') {
144 			lexline = l;
145 			lexchar = p;
146 			ttok = read_number(c, cfile);
147 			break;
148 		} else if (isascii(c) && isalpha(c)) {
149 			lexline = l;
150 			lexchar = p;
151 			ttok = read_num_or_name(c, cfile);
152 			break;
153 		} else {
154 			lexline = l;
155 			lexchar = p;
156 			tb[0] = c;
157 			tb[1] = 0;
158 			tval = tb;
159 			ttok = c;
160 			break;
161 		}
162 	} while (1);
163 	return (ttok);
164 }
165 
166 int
167 next_token(char **rval, FILE *cfile)
168 {
169 	int	rv;
170 
171 	if (token) {
172 		if (lexline != tline)
173 			token_line = cur_line;
174 		lexchar = tlpos;
175 		lexline = tline;
176 		rv = token;
177 		token = 0;
178 	} else {
179 		rv = get_token(cfile);
180 		token_line = cur_line;
181 	}
182 	if (rval)
183 		*rval = tval;
184 
185 	return (rv);
186 }
187 
188 int
189 peek_token(char **rval, FILE *cfile)
190 {
191 	int	x;
192 
193 	if (!token) {
194 		tlpos = lexchar;
195 		tline = lexline;
196 		token = get_token(cfile);
197 		if (lexline != tline)
198 			token_line = prev_line;
199 		x = lexchar;
200 		lexchar = tlpos;
201 		tlpos = x;
202 		x = lexline;
203 		lexline = tline;
204 		tline = x;
205 	}
206 	if (rval)
207 		*rval = tval;
208 
209 	return (token);
210 }
211 
212 static void
213 skip_to_eol(FILE *cfile)
214 {
215 	int	c;
216 
217 	do {
218 		c = get_char(cfile);
219 		if (c == EOF)
220 			return;
221 		if (c == '\n')
222 			return;
223 	} while (1);
224 }
225 
226 static int
227 read_string(FILE *cfile)
228 {
229 	int	c, bs = 0;
230 	unsigned i;
231 
232 	for (i = 0; i < sizeof(tokbuf); i++) {
233 		c = get_char(cfile);
234 		if (c == EOF) {
235 			parse_warn("eof in string constant");
236 			break;
237 		}
238 		if (bs) {
239 			bs = 0;
240 			i--;
241 			tokbuf[i] = c;
242 		} else if (c == '\\')
243 			bs = 1;
244 		else if (c == '"')
245 			break;
246 		else
247 			tokbuf[i] = c;
248 	}
249 	/*
250 	 * Normally, I'd feel guilty about this, but we're talking about
251 	 * strings that'll fit in a DHCP packet here...
252 	 */
253 	if (i == sizeof(tokbuf)) {
254 		parse_warn("string constant larger than internal buffer");
255 		i--;
256 	}
257 	tokbuf[i] = 0;
258 	tval = tokbuf;
259 	return (STRING);
260 }
261 
262 static int
263 read_number(int c, FILE *cfile)
264 {
265 	int	seenx = 0, _token = NUMBER;
266 	unsigned i = 0;
267 
268 	tokbuf[i++] = c;
269 	for (; i < sizeof(tokbuf); i++) {
270 		c = get_char(cfile);
271 		if (!seenx && c == 'x')
272 			seenx = 1;
273 		else if (!isascii(c) || !isxdigit(c)) {
274 			ungetc(c, cfile);
275 			ugflag = 1;
276 			break;
277 		}
278 		tokbuf[i] = c;
279 	}
280 	if (i == sizeof(tokbuf)) {
281 		parse_warn("numeric token larger than internal buffer");
282 		i--;
283 	}
284 	tokbuf[i] = 0;
285 	tval = tokbuf;
286 
287 	return (_token);
288 }
289 
290 static int
291 read_num_or_name(int c, FILE *cfile)
292 {
293 	unsigned i = 0;
294 	int	rv = NUMBER_OR_NAME;
295 
296 	tokbuf[i++] = c;
297 	for (; i < sizeof(tokbuf); i++) {
298 		c = get_char(cfile);
299 		if (!isascii(c) || (c != '-' && c != '_' && !isalnum(c))) {
300 			ungetc(c, cfile);
301 			ugflag = 1;
302 			break;
303 		}
304 		if (!isxdigit(c))
305 			rv = NAME;
306 		tokbuf[i] = c;
307 	}
308 	if (i == sizeof(tokbuf)) {
309 		parse_warn("token larger than internal buffer");
310 		i--;
311 	}
312 	tokbuf[i] = 0;
313 	tval = tokbuf;
314 
315 	return (intern(tval, rv));
316 }
317 
318 static int
319 intern(char *atom, int dfv)
320 {
321 	if (!isascii(atom[0]))
322 		return (dfv);
323 
324 	switch (tolower(atom[0])) {
325 	case 'a':
326 		if (!strcasecmp(atom + 1, "lways-reply-rfc1048"))
327 			return (ALWAYS_REPLY_RFC1048);
328 		if (!strcasecmp(atom + 1, "ppend"))
329 			return (APPEND);
330 		if (!strcasecmp(atom + 1, "llow"))
331 			return (ALLOW);
332 		if (!strcasecmp(atom + 1, "lias"))
333 			return (ALIAS);
334 		if (!strcasecmp(atom + 1, "bandoned"))
335 			return (ABANDONED);
336 		if (!strcasecmp(atom + 1, "uthoritative"))
337 			return (AUTHORITATIVE);
338 		break;
339 	case 'b':
340 		if (!strcasecmp(atom + 1, "ackoff-cutoff"))
341 			return (BACKOFF_CUTOFF);
342 		if (!strcasecmp(atom + 1, "ootp"))
343 			return (BOOTP);
344 		if (!strcasecmp(atom + 1, "ooting"))
345 			return (BOOTING);
346 		if (!strcasecmp(atom + 1, "oot-unknown-clients"))
347 			return (BOOT_UNKNOWN_CLIENTS);
348 		break;
349 	case 'c':
350 		if (!strcasecmp(atom + 1, "lass"))
351 			return (CLASS);
352 		if (!strcasecmp(atom + 1, "iaddr"))
353 			return (CIADDR);
354 		if (!strcasecmp(atom + 1, "lient-identifier"))
355 			return (CLIENT_IDENTIFIER);
356 		if (!strcasecmp(atom + 1, "lient-hostname"))
357 			return (CLIENT_HOSTNAME);
358 		break;
359 	case 'd':
360 		if (!strcasecmp(atom + 1, "omain"))
361 			return (DOMAIN);
362 		if (!strcasecmp(atom + 1, "eny"))
363 			return (DENY);
364 		if (!strncasecmp(atom + 1, "efault", 6)) {
365 			if (!atom[7])
366 				return (DEFAULT);
367 			if (!strcasecmp(atom + 7, "-lease-time"))
368 				return (DEFAULT_LEASE_TIME);
369 			break;
370 		}
371 		if (!strncasecmp(atom + 1, "ynamic-bootp", 12)) {
372 			if (!atom[13])
373 				return (DYNAMIC_BOOTP);
374 			if (!strcasecmp(atom + 13, "-lease-cutoff"))
375 				return (DYNAMIC_BOOTP_LEASE_CUTOFF);
376 			if (!strcasecmp(atom + 13, "-lease-length"))
377 				return (DYNAMIC_BOOTP_LEASE_LENGTH);
378 			break;
379 		}
380 		break;
381 	case 'e':
382 		if (!strcasecmp(atom + 1, "thernet"))
383 			return (ETHERNET);
384 		if (!strcasecmp(atom + 1, "nds"))
385 			return (ENDS);
386 		if (!strcasecmp(atom + 1, "xpire"))
387 			return (EXPIRE);
388 		break;
389 	case 'f':
390 		if (!strcasecmp(atom + 1, "ilename"))
391 			return (FILENAME);
392 		if (!strcasecmp(atom + 1, "ixed-address"))
393 			return (FIXED_ADDR);
394 		if (!strcasecmp(atom + 1, "ddi"))
395 			return (FDDI);
396 		break;
397 	case 'g':
398 		if (!strcasecmp(atom + 1, "iaddr"))
399 			return (GIADDR);
400 		if (!strcasecmp(atom + 1, "roup"))
401 			return (GROUP);
402 		if (!strcasecmp(atom + 1, "et-lease-hostnames"))
403 			return (GET_LEASE_HOSTNAMES);
404 		break;
405 	case 'h':
406 		if (!strcasecmp(atom + 1, "ost"))
407 			return (HOST);
408 		if (!strcasecmp(atom + 1, "ardware"))
409 			return (HARDWARE);
410 		if (!strcasecmp(atom + 1, "ostname"))
411 			return (HOSTNAME);
412 		break;
413 	case 'i':
414 		if (!strcasecmp(atom + 1, "gnore"))
415 			return (IGNORE);
416 		if (!strcasecmp(atom + 1, "nitial-interval"))
417 			return (INITIAL_INTERVAL);
418 		if (!strcasecmp(atom + 1, "nterface"))
419 			return (INTERFACE);
420 		break;
421 	case 'l':
422 		if (!strcasecmp(atom + 1, "ease"))
423 			return (LEASE);
424 		break;
425 	case 'm':
426 		if (!strcasecmp(atom + 1, "ax-lease-time"))
427 			return (MAX_LEASE_TIME);
428 		if (!strncasecmp(atom + 1, "edi", 3)) {
429 			if (!strcasecmp(atom + 4, "a"))
430 				return (MEDIA);
431 			if (!strcasecmp(atom + 4, "um"))
432 				return (MEDIUM);
433 			break;
434 		}
435 		break;
436 	case 'n':
437 		if (!strcasecmp(atom + 1, "ameserver"))
438 			return (NAMESERVER);
439 		if (!strcasecmp(atom + 1, "etmask"))
440 			return (NETMASK);
441 		if (!strcasecmp(atom + 1, "ext-server"))
442 			return (NEXT_SERVER);
443 		if (!strcasecmp(atom + 1, "ot"))
444 			return (TOKEN_NOT);
445 		break;
446 	case 'o':
447 		if (!strcasecmp(atom + 1, "ption"))
448 			return (OPTION);
449 		if (!strcasecmp(atom + 1, "ne-lease-per-client"))
450 			return (ONE_LEASE_PER_CLIENT);
451 		break;
452 	case 'p':
453 		if (!strcasecmp(atom + 1, "repend"))
454 			return (PREPEND);
455 		if (!strcasecmp(atom + 1, "acket"))
456 			return (PACKET);
457 		break;
458 	case 'r':
459 		if (!strcasecmp(atom + 1, "ange"))
460 			return (RANGE);
461 		if (!strcasecmp(atom + 1, "equest"))
462 			return (REQUEST);
463 		if (!strcasecmp(atom + 1, "equire"))
464 			return (REQUIRE);
465 		if (!strcasecmp(atom + 1, "etry"))
466 			return (RETRY);
467 		if (!strcasecmp(atom + 1, "enew"))
468 			return (RENEW);
469 		if (!strcasecmp(atom + 1, "ebind"))
470 			return (REBIND);
471 		if (!strcasecmp(atom + 1, "eboot"))
472 			return (REBOOT);
473 		if (!strcasecmp(atom + 1, "eject"))
474 			return (REJECT);
475 		break;
476 	case 's':
477 		if (!strcasecmp(atom + 1, "earch"))
478 			return (SEARCH);
479 		if (!strcasecmp(atom + 1, "tarts"))
480 			return (STARTS);
481 		if (!strcasecmp(atom + 1, "iaddr"))
482 			return (SIADDR);
483 		if (!strcasecmp(atom + 1, "ubnet"))
484 			return (SUBNET);
485 		if (!strcasecmp(atom + 1, "hared-network"))
486 			return (SHARED_NETWORK);
487 		if (!strcasecmp(atom + 1, "erver-name"))
488 			return (SERVER_NAME);
489 		if (!strcasecmp(atom + 1, "erver-identifier"))
490 			return (SERVER_IDENTIFIER);
491 		if (!strcasecmp(atom + 1, "elect-timeout"))
492 			return (SELECT_TIMEOUT);
493 		if (!strcasecmp(atom + 1, "end"))
494 			return (SEND);
495 		if (!strcasecmp(atom + 1, "cript"))
496 			return (SCRIPT);
497 		if (!strcasecmp(atom + 1, "upersede"))
498 			return (SUPERSEDE);
499 		break;
500 	case 't':
501 		if (!strcasecmp(atom + 1, "imestamp"))
502 			return (TIMESTAMP);
503 		if (!strcasecmp(atom + 1, "imeout"))
504 			return (TIMEOUT);
505 		if (!strcasecmp(atom + 1, "oken-ring"))
506 			return (TOKEN_RING);
507 		break;
508 	case 'u':
509 		if (!strncasecmp(atom + 1, "se", 2)) {
510 			if (!strcasecmp(atom + 3, "r-class"))
511 				return (USER_CLASS);
512 			if (!strcasecmp(atom + 3, "-host-decl-names"))
513 				return (USE_HOST_DECL_NAMES);
514 			if (!strcasecmp(atom + 3,
515 					 "-lease-addr-for-default-route"))
516 				return (USE_LEASE_ADDR_FOR_DEFAULT_ROUTE);
517 			break;
518 		}
519 		if (!strcasecmp(atom + 1, "id"))
520 			return (UID);
521 		if (!strcasecmp(atom + 1, "nknown-clients"))
522 			return (UNKNOWN_CLIENTS);
523 		break;
524 	case 'v':
525 		if (!strcasecmp(atom + 1, "endor-class"))
526 			return (VENDOR_CLASS);
527 		if (!strcasecmp(atom + 1, "lan-pcp"))
528 			return (VLAN_PCP);
529 		break;
530 	case 'y':
531 		if (!strcasecmp(atom + 1, "iaddr"))
532 			return (YIADDR);
533 		break;
534 	}
535 	return (dfv);
536 }
537