xref: /freebsd/sbin/dhclient/conflex.c (revision 24bd614bbdda9cf8065489d0c6c3ea469c9c4392)
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  * 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 <= 81) {
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 (STRING);
255 }
256 
257 static int
258 read_number(int c, FILE *cfile)
259 {
260 	int	seenx = 0, i = 0, token = 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 = 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 = 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 int
313 intern(char *atom, int dfv)
314 {
315 	if (!isascii(atom[0]))
316 		return (dfv);
317 
318 	switch (tolower(atom[0])) {
319 	case 'a':
320 		if (!strcasecmp(atom + 1, "lways-reply-rfc1048"))
321 			return (ALWAYS_REPLY_RFC1048);
322 		if (!strcasecmp(atom + 1, "ppend"))
323 			return (APPEND);
324 		if (!strcasecmp(atom + 1, "llow"))
325 			return (ALLOW);
326 		if (!strcasecmp(atom + 1, "lias"))
327 			return (ALIAS);
328 		if (!strcasecmp(atom + 1, "bandoned"))
329 			return (ABANDONED);
330 		if (!strcasecmp(atom + 1, "uthoritative"))
331 			return (AUTHORITATIVE);
332 		break;
333 	case 'b':
334 		if (!strcasecmp(atom + 1, "ackoff-cutoff"))
335 			return (BACKOFF_CUTOFF);
336 		if (!strcasecmp(atom + 1, "ootp"))
337 			return (BOOTP);
338 		if (!strcasecmp(atom + 1, "ooting"))
339 			return (BOOTING);
340 		if (!strcasecmp(atom + 1, "oot-unknown-clients"))
341 			return (BOOT_UNKNOWN_CLIENTS);
342 	case 'c':
343 		if (!strcasecmp(atom + 1, "lass"))
344 			return (CLASS);
345 		if (!strcasecmp(atom + 1, "iaddr"))
346 			return (CIADDR);
347 		if (!strcasecmp(atom + 1, "lient-identifier"))
348 			return (CLIENT_IDENTIFIER);
349 		if (!strcasecmp(atom + 1, "lient-hostname"))
350 			return (CLIENT_HOSTNAME);
351 		break;
352 	case 'd':
353 		if (!strcasecmp(atom + 1, "omain"))
354 			return (DOMAIN);
355 		if (!strcasecmp(atom + 1, "eny"))
356 			return (DENY);
357 		if (!strncasecmp(atom + 1, "efault", 6)) {
358 			if (!atom[7])
359 				return (DEFAULT);
360 			if (!strcasecmp(atom + 7, "-lease-time"))
361 				return (DEFAULT_LEASE_TIME);
362 			break;
363 		}
364 		if (!strncasecmp(atom + 1, "ynamic-bootp", 12)) {
365 			if (!atom[13])
366 				return (DYNAMIC_BOOTP);
367 			if (!strcasecmp(atom + 13, "-lease-cutoff"))
368 				return (DYNAMIC_BOOTP_LEASE_CUTOFF);
369 			if (!strcasecmp(atom + 13, "-lease-length"))
370 				return (DYNAMIC_BOOTP_LEASE_LENGTH);
371 			break;
372 		}
373 		break;
374 	case 'e':
375 		if (!strcasecmp(atom + 1, "thernet"))
376 			return (ETHERNET);
377 		if (!strcasecmp(atom + 1, "nds"))
378 			return (ENDS);
379 		if (!strcasecmp(atom + 1, "xpire"))
380 			return (EXPIRE);
381 		break;
382 	case 'f':
383 		if (!strcasecmp(atom + 1, "ilename"))
384 			return (FILENAME);
385 		if (!strcasecmp(atom + 1, "ixed-address"))
386 			return (FIXED_ADDR);
387 		if (!strcasecmp(atom + 1, "ddi"))
388 			return (FDDI);
389 		break;
390 	case 'g':
391 		if (!strcasecmp(atom + 1, "iaddr"))
392 			return (GIADDR);
393 		if (!strcasecmp(atom + 1, "roup"))
394 			return (GROUP);
395 		if (!strcasecmp(atom + 1, "et-lease-hostnames"))
396 			return (GET_LEASE_HOSTNAMES);
397 		break;
398 	case 'h':
399 		if (!strcasecmp(atom + 1, "ost"))
400 			return (HOST);
401 		if (!strcasecmp(atom + 1, "ardware"))
402 			return (HARDWARE);
403 		if (!strcasecmp(atom + 1, "ostname"))
404 			return (HOSTNAME);
405 		break;
406 	case 'i':
407 		if (!strcasecmp(atom + 1, "nitial-interval"))
408 			return (INITIAL_INTERVAL);
409 		if (!strcasecmp(atom + 1, "nterface"))
410 			return (INTERFACE);
411 		break;
412 	case 'l':
413 		if (!strcasecmp(atom + 1, "ease"))
414 			return (LEASE);
415 		break;
416 	case 'm':
417 		if (!strcasecmp(atom + 1, "ax-lease-time"))
418 			return (MAX_LEASE_TIME);
419 		if (!strncasecmp(atom + 1, "edi", 3)) {
420 			if (!strcasecmp(atom + 4, "a"))
421 				return (MEDIA);
422 			if (!strcasecmp(atom + 4, "um"))
423 				return (MEDIUM);
424 			break;
425 		}
426 		break;
427 	case 'n':
428 		if (!strcasecmp(atom + 1, "ameserver"))
429 			return (NAMESERVER);
430 		if (!strcasecmp(atom + 1, "etmask"))
431 			return (NETMASK);
432 		if (!strcasecmp(atom + 1, "ext-server"))
433 			return (NEXT_SERVER);
434 		if (!strcasecmp(atom + 1, "ot"))
435 			return (TOKEN_NOT);
436 		break;
437 	case 'o':
438 		if (!strcasecmp(atom + 1, "ption"))
439 			return (OPTION);
440 		if (!strcasecmp(atom + 1, "ne-lease-per-client"))
441 			return (ONE_LEASE_PER_CLIENT);
442 		break;
443 	case 'p':
444 		if (!strcasecmp(atom + 1, "repend"))
445 			return (PREPEND);
446 		if (!strcasecmp(atom + 1, "acket"))
447 			return (PACKET);
448 		break;
449 	case 'r':
450 		if (!strcasecmp(atom + 1, "ange"))
451 			return (RANGE);
452 		if (!strcasecmp(atom + 1, "equest"))
453 			return (REQUEST);
454 		if (!strcasecmp(atom + 1, "equire"))
455 			return (REQUIRE);
456 		if (!strcasecmp(atom + 1, "etry"))
457 			return (RETRY);
458 		if (!strcasecmp(atom + 1, "enew"))
459 			return (RENEW);
460 		if (!strcasecmp(atom + 1, "ebind"))
461 			return (REBIND);
462 		if (!strcasecmp(atom + 1, "eboot"))
463 			return (REBOOT);
464 		if (!strcasecmp(atom + 1, "eject"))
465 			return (REJECT);
466 		break;
467 	case 's':
468 		if (!strcasecmp(atom + 1, "earch"))
469 			return (SEARCH);
470 		if (!strcasecmp(atom + 1, "tarts"))
471 			return (STARTS);
472 		if (!strcasecmp(atom + 1, "iaddr"))
473 			return (SIADDR);
474 		if (!strcasecmp(atom + 1, "ubnet"))
475 			return (SUBNET);
476 		if (!strcasecmp(atom + 1, "hared-network"))
477 			return (SHARED_NETWORK);
478 		if (!strcasecmp(atom + 1, "erver-name"))
479 			return (SERVER_NAME);
480 		if (!strcasecmp(atom + 1, "erver-identifier"))
481 			return (SERVER_IDENTIFIER);
482 		if (!strcasecmp(atom + 1, "elect-timeout"))
483 			return (SELECT_TIMEOUT);
484 		if (!strcasecmp(atom + 1, "end"))
485 			return (SEND);
486 		if (!strcasecmp(atom + 1, "cript"))
487 			return (SCRIPT);
488 		if (!strcasecmp(atom + 1, "upersede"))
489 			return (SUPERSEDE);
490 		break;
491 	case 't':
492 		if (!strcasecmp(atom + 1, "imestamp"))
493 			return (TIMESTAMP);
494 		if (!strcasecmp(atom + 1, "imeout"))
495 			return (TIMEOUT);
496 		if (!strcasecmp(atom + 1, "oken-ring"))
497 			return (TOKEN_RING);
498 		break;
499 	case 'u':
500 		if (!strncasecmp(atom + 1, "se", 2)) {
501 			if (!strcasecmp(atom + 3, "r-class"))
502 				return (USER_CLASS);
503 			if (!strcasecmp(atom + 3, "-host-decl-names"))
504 				return (USE_HOST_DECL_NAMES);
505 			if (!strcasecmp(atom + 3,
506 					 "-lease-addr-for-default-route"))
507 				return (USE_LEASE_ADDR_FOR_DEFAULT_ROUTE);
508 			break;
509 		}
510 		if (!strcasecmp(atom + 1, "id"))
511 			return (UID);
512 		if (!strcasecmp(atom + 1, "nknown-clients"))
513 			return (UNKNOWN_CLIENTS);
514 		break;
515 	case 'v':
516 		if (!strcasecmp(atom + 1, "endor-class"))
517 			return (VENDOR_CLASS);
518 		break;
519 	case 'y':
520 		if (!strcasecmp(atom + 1, "iaddr"))
521 			return (YIADDR);
522 		break;
523 	}
524 	return (dfv);
525 }
526