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
51int lexline;
52int lexchar;
53char *token_line;
54static char *prev_line;
55static char *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		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