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
53int lexline;
54int lexchar;
55char *token_line;
56static char *prev_line;
57static char *cur_line;
58const char *tlname;
59int eol_token;
60
61static char line1[81];
62static char line2[81];
63static unsigned lpos;
64static unsigned line;
65static int tlpos;
66static int tline;
67static int token;
68static int ugflag;
69static char *tval;
70static char tokbuf[1500];
71
72static int get_char(FILE *);
73static int get_token(FILE *);
74static void skip_to_eol(FILE *);
75static int read_string(FILE *);
76static int read_number(int, FILE *);
77static int read_num_or_name(int, FILE *);
78static int intern(char *, int);
79
80void
81new_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
92static int
93get_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
120static int
121get_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
168int
169next_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
190int
191peek_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
214static void
215skip_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
228static int
229read_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
264static int
265read_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
292static int
293read_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
320static int
321intern(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, "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		break;
528	case 'y':
529		if (!strcasecmp(atom + 1, "iaddr"))
530			return (YIADDR);
531		break;
532	}
533	return (dfv);
534}
535