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 <sys/cdefs.h>
44__FBSDID("$FreeBSD: stable/11/sbin/dhclient/conflex.c 332602 2018-04-16 16:23:32Z asomers $");
45
46#include <ctype.h>
47
48#include "dhcpd.h"
49#include "dhctoken.h"
50
51int lexline;
52int lexchar;
53char *token_line;
54char *prev_line;
55char *cur_line;
56const char *tlname;
57int eol_token;
58
59static char line1[81];
60static char line2[81];
61static unsigned lpos;
62static unsigned line;
63static int tlpos;
64static int tline;
65static int token;
66static int ugflag;
67static char *tval;
68static char tokbuf[1500];
69
70static int get_char(FILE *);
71static int get_token(FILE *);
72static void skip_to_eol(FILE *);
73static int read_string(FILE *);
74static int read_number(int, FILE *);
75static int read_num_or_name(int, FILE *);
76static int intern(char *, int);
77
78void
79new_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
90static int
91get_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
118static int
119get_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
166int
167next_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
188int
189peek_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
212static void
213skip_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
226static int
227read_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
262static int
263read_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
290static int
291read_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
318static int
319intern(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	case 'c':
349		if (!strcasecmp(atom + 1, "lass"))
350			return (CLASS);
351		if (!strcasecmp(atom + 1, "iaddr"))
352			return (CIADDR);
353		if (!strcasecmp(atom + 1, "lient-identifier"))
354			return (CLIENT_IDENTIFIER);
355		if (!strcasecmp(atom + 1, "lient-hostname"))
356			return (CLIENT_HOSTNAME);
357		break;
358	case 'd':
359		if (!strcasecmp(atom + 1, "omain"))
360			return (DOMAIN);
361		if (!strcasecmp(atom + 1, "eny"))
362			return (DENY);
363		if (!strncasecmp(atom + 1, "efault", 6)) {
364			if (!atom[7])
365				return (DEFAULT);
366			if (!strcasecmp(atom + 7, "-lease-time"))
367				return (DEFAULT_LEASE_TIME);
368			break;
369		}
370		if (!strncasecmp(atom + 1, "ynamic-bootp", 12)) {
371			if (!atom[13])
372				return (DYNAMIC_BOOTP);
373			if (!strcasecmp(atom + 13, "-lease-cutoff"))
374				return (DYNAMIC_BOOTP_LEASE_CUTOFF);
375			if (!strcasecmp(atom + 13, "-lease-length"))
376				return (DYNAMIC_BOOTP_LEASE_LENGTH);
377			break;
378		}
379		break;
380	case 'e':
381		if (!strcasecmp(atom + 1, "thernet"))
382			return (ETHERNET);
383		if (!strcasecmp(atom + 1, "nds"))
384			return (ENDS);
385		if (!strcasecmp(atom + 1, "xpire"))
386			return (EXPIRE);
387		break;
388	case 'f':
389		if (!strcasecmp(atom + 1, "ilename"))
390			return (FILENAME);
391		if (!strcasecmp(atom + 1, "ixed-address"))
392			return (FIXED_ADDR);
393		if (!strcasecmp(atom + 1, "ddi"))
394			return (FDDI);
395		break;
396	case 'g':
397		if (!strcasecmp(atom + 1, "iaddr"))
398			return (GIADDR);
399		if (!strcasecmp(atom + 1, "roup"))
400			return (GROUP);
401		if (!strcasecmp(atom + 1, "et-lease-hostnames"))
402			return (GET_LEASE_HOSTNAMES);
403		break;
404	case 'h':
405		if (!strcasecmp(atom + 1, "ost"))
406			return (HOST);
407		if (!strcasecmp(atom + 1, "ardware"))
408			return (HARDWARE);
409		if (!strcasecmp(atom + 1, "ostname"))
410			return (HOSTNAME);
411		break;
412	case 'i':
413		if (!strcasecmp(atom + 1, "nitial-interval"))
414			return (INITIAL_INTERVAL);
415		if (!strcasecmp(atom + 1, "nterface"))
416			return (INTERFACE);
417		break;
418	case 'l':
419		if (!strcasecmp(atom + 1, "ease"))
420			return (LEASE);
421		break;
422	case 'm':
423		if (!strcasecmp(atom + 1, "ax-lease-time"))
424			return (MAX_LEASE_TIME);
425		if (!strncasecmp(atom + 1, "edi", 3)) {
426			if (!strcasecmp(atom + 4, "a"))
427				return (MEDIA);
428			if (!strcasecmp(atom + 4, "um"))
429				return (MEDIUM);
430			break;
431		}
432		break;
433	case 'n':
434		if (!strcasecmp(atom + 1, "ameserver"))
435			return (NAMESERVER);
436		if (!strcasecmp(atom + 1, "etmask"))
437			return (NETMASK);
438		if (!strcasecmp(atom + 1, "ext-server"))
439			return (NEXT_SERVER);
440		if (!strcasecmp(atom + 1, "ot"))
441			return (TOKEN_NOT);
442		break;
443	case 'o':
444		if (!strcasecmp(atom + 1, "ption"))
445			return (OPTION);
446		if (!strcasecmp(atom + 1, "ne-lease-per-client"))
447			return (ONE_LEASE_PER_CLIENT);
448		break;
449	case 'p':
450		if (!strcasecmp(atom + 1, "repend"))
451			return (PREPEND);
452		if (!strcasecmp(atom + 1, "acket"))
453			return (PACKET);
454		break;
455	case 'r':
456		if (!strcasecmp(atom + 1, "ange"))
457			return (RANGE);
458		if (!strcasecmp(atom + 1, "equest"))
459			return (REQUEST);
460		if (!strcasecmp(atom + 1, "equire"))
461			return (REQUIRE);
462		if (!strcasecmp(atom + 1, "etry"))
463			return (RETRY);
464		if (!strcasecmp(atom + 1, "enew"))
465			return (RENEW);
466		if (!strcasecmp(atom + 1, "ebind"))
467			return (REBIND);
468		if (!strcasecmp(atom + 1, "eboot"))
469			return (REBOOT);
470		if (!strcasecmp(atom + 1, "eject"))
471			return (REJECT);
472		break;
473	case 's':
474		if (!strcasecmp(atom + 1, "earch"))
475			return (SEARCH);
476		if (!strcasecmp(atom + 1, "tarts"))
477			return (STARTS);
478		if (!strcasecmp(atom + 1, "iaddr"))
479			return (SIADDR);
480		if (!strcasecmp(atom + 1, "ubnet"))
481			return (SUBNET);
482		if (!strcasecmp(atom + 1, "hared-network"))
483			return (SHARED_NETWORK);
484		if (!strcasecmp(atom + 1, "erver-name"))
485			return (SERVER_NAME);
486		if (!strcasecmp(atom + 1, "erver-identifier"))
487			return (SERVER_IDENTIFIER);
488		if (!strcasecmp(atom + 1, "elect-timeout"))
489			return (SELECT_TIMEOUT);
490		if (!strcasecmp(atom + 1, "end"))
491			return (SEND);
492		if (!strcasecmp(atom + 1, "cript"))
493			return (SCRIPT);
494		if (!strcasecmp(atom + 1, "upersede"))
495			return (SUPERSEDE);
496		break;
497	case 't':
498		if (!strcasecmp(atom + 1, "imestamp"))
499			return (TIMESTAMP);
500		if (!strcasecmp(atom + 1, "imeout"))
501			return (TIMEOUT);
502		if (!strcasecmp(atom + 1, "oken-ring"))
503			return (TOKEN_RING);
504		break;
505	case 'u':
506		if (!strncasecmp(atom + 1, "se", 2)) {
507			if (!strcasecmp(atom + 3, "r-class"))
508				return (USER_CLASS);
509			if (!strcasecmp(atom + 3, "-host-decl-names"))
510				return (USE_HOST_DECL_NAMES);
511			if (!strcasecmp(atom + 3,
512					 "-lease-addr-for-default-route"))
513				return (USE_LEASE_ADDR_FOR_DEFAULT_ROUTE);
514			break;
515		}
516		if (!strcasecmp(atom + 1, "id"))
517			return (UID);
518		if (!strcasecmp(atom + 1, "nknown-clients"))
519			return (UNKNOWN_CLIENTS);
520		break;
521	case 'v':
522		if (!strcasecmp(atom + 1, "endor-class"))
523			return (VENDOR_CLASS);
524		break;
525	case 'y':
526		if (!strcasecmp(atom + 1, "iaddr"))
527			return (YIADDR);
528		break;
529	}
530	return (dfv);
531}
532