xref: /openbsd/usr.sbin/dhcpd/conflex.c (revision a51e9872)
1 /*	$OpenBSD: conflex.c,v 1.19 2017/04/24 14:58:36 krw 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/types.h>
44 #include <sys/socket.h>
45 
46 #include <net/if.h>
47 
48 #include <netinet/in.h>
49 
50 #include <ctype.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 
55 #include "dhcp.h"
56 #include "tree.h"
57 #include "dhcpd.h"
58 #include "dhctoken.h"
59 
60 int lexline;
61 int lexchar;
62 char *token_line;
63 char *prev_line;
64 char *cur_line;
65 char *tlname;
66 int eol_token;
67 
68 static char line1[81];
69 static char line2[81];
70 static int lpos;
71 static int line;
72 static int tlpos;
73 static int tline;
74 static int token;
75 static int ugflag;
76 static char *tval;
77 static char tokbuf[1500];
78 
79 static int get_char(FILE *);
80 static int get_token(FILE *);
81 static void skip_to_eol(FILE *);
82 static int read_string(FILE *);
83 static int read_num_or_name(int, FILE *);
84 static int intern(char *, int);
85 static int kw_cmp(const void *, const void *);
86 
87 void
new_parse(char * name)88 new_parse(char *name)
89 {
90 	tlname = name;
91 	lpos = line = 1;
92 	cur_line = line1;
93 	prev_line = line2;
94 	token_line = cur_line;
95 	cur_line[0] = prev_line[0] = 0;
96 	warnings_occurred = 0;
97 }
98 
99 static int
get_char(FILE * cfile)100 get_char(FILE *cfile)
101 {
102 	int c = getc(cfile);
103 	if (!ugflag) {
104 		if (c == '\n') {
105 			if (cur_line == line1) {
106 				cur_line = line2;
107 				prev_line = line1;
108 			} else {
109 				cur_line = line1;
110 				prev_line = line2;
111 			}
112 			line++;
113 			lpos = 1;
114 			cur_line[0] = 0;
115 		} else if (c != EOF) {
116 			if (lpos < sizeof(line1)) {
117 				cur_line[lpos - 1] = c;
118 				cur_line[lpos] = 0;
119 			}
120 			lpos++;
121 		}
122 	} else
123 		ugflag = 0;
124 	return (c);
125 }
126 
127 static int
get_token(FILE * cfile)128 get_token(FILE *cfile)
129 {
130 	int		c, ttok;
131 	static char	tb[2];
132 	int		l, p;
133 
134 	do {
135 		l = line;
136 		p = lpos;
137 
138 		c = get_char(cfile);
139 
140 		if (!(c == '\n' && eol_token) && isascii(c) && isspace(c))
141 			continue;
142 		if (c == '#') {
143 			skip_to_eol(cfile);
144 			continue;
145 		}
146 		lexline = l;
147 		lexchar = p;
148 		if (c == '"') {
149 			ttok = read_string(cfile);
150 			break;
151 		} else if (c == '-' || (isascii(c) && isalnum(c))) {
152 			ttok = read_num_or_name(c, cfile);
153 			break;
154 		} else {
155 			tb[0] = c;
156 			tb[1] = 0;
157 			tval = tb;
158 			ttok = c;
159 			break;
160 		}
161 	} while (1);
162 	return (ttok);
163 }
164 
165 int
next_token(char ** rval,FILE * cfile)166 next_token(char **rval, FILE *cfile)
167 {
168 	int	rv;
169 
170 	if (token) {
171 		if (lexline != tline)
172 			token_line = cur_line;
173 		lexchar = tlpos;
174 		lexline = tline;
175 		rv = token;
176 		token = 0;
177 	} else {
178 		rv = get_token(cfile);
179 		token_line = cur_line;
180 	}
181 	if (rval)
182 		*rval = tval;
183 
184 	return (rv);
185 }
186 
187 int
peek_token(char ** rval,FILE * cfile)188 peek_token(char **rval, FILE *cfile)
189 {
190 	int	x;
191 
192 	if (!token) {
193 		tlpos = lexchar;
194 		tline = lexline;
195 		token = get_token(cfile);
196 		if (lexline != tline)
197 			token_line = prev_line;
198 		x = lexchar;
199 		lexchar = tlpos;
200 		tlpos = x;
201 		x = lexline;
202 		lexline = tline;
203 		tline = x;
204 	}
205 	if (rval)
206 		*rval = tval;
207 
208 	return (token);
209 }
210 
211 static void
skip_to_eol(FILE * cfile)212 skip_to_eol(FILE *cfile)
213 {
214 	int	c;
215 
216 	do {
217 		c = get_char(cfile);
218 		if (c == EOF)
219 			return;
220 		if (c == '\n')
221 			return;
222 	} while (1);
223 }
224 
225 static int
read_string(FILE * cfile)226 read_string(FILE *cfile)
227 {
228 	int i, c, bs;
229 
230 	bs = i = 0;
231 	do {
232 		c = get_char(cfile);
233 		if (bs)
234 			bs = 0;
235 		else if (c == '\\')
236 			bs = 1;
237 
238 		if (c != '"' && c != EOF && bs == 0)
239 			tokbuf[i++] = c;
240 
241 	} while (i < (sizeof(tokbuf) - 1) && c != EOF && c != '"');
242 
243 	if (c == EOF)
244 		parse_warn("eof in string constant");
245 	else if (c != '"')
246 		parse_warn("string constant larger than internal buffer");
247 
248 	tokbuf[i] = 0;
249 	tval = tokbuf;
250 
251 	return (TOK_STRING);
252 }
253 
254 static int
read_num_or_name(int c,FILE * cfile)255 read_num_or_name(int c, FILE *cfile)
256 {
257 	int i, rv, xdigits;
258 
259 	xdigits = isxdigit(c) ? 1 : 0;
260 
261 	tokbuf[0] = c;
262 	for (i = 1; i < sizeof(tokbuf); i++) {
263 		c = get_char(cfile);
264 		if (!isascii(c) || (c != '-' && c != '_' && !isalnum(c))) {
265 			ungetc(c, cfile);
266 			ugflag = 1;
267 			break;
268 		}
269 		if (isxdigit(c))
270 			xdigits++;
271 		tokbuf[i] = c;
272 	}
273 	if (i == sizeof(tokbuf)) {
274 		parse_warn("token larger than internal buffer");
275 		i--;
276 		c = tokbuf[i];
277 		if (isxdigit(c))
278 			xdigits--;
279 	}
280 	tokbuf[i] = 0;
281 	tval = tokbuf;
282 
283 	c = (unsigned int)tokbuf[0];
284 
285 	if (c == '-')
286 		rv = TOK_NUMBER;
287 	else
288 		rv = intern(tval, TOK_NUMBER_OR_NAME);
289 
290 	if (rv == TOK_NUMBER_OR_NAME && xdigits != i)
291 		rv = TOK_NAME;
292 
293 	return (rv);
294 }
295 
296 static const struct keywords {
297 	const char	*k_name;
298 	int		k_val;
299 } keywords[] = {
300 	{ "abandoned",			TOK_ABANDONED },
301 	{ "allow",			TOK_ALLOW },
302 	{ "always-reply-rfc1048",	TOK_ALWAYS_REPLY_RFC1048 },
303 	{ "authoritative",		TOK_AUTHORITATIVE },
304 	{ "booting",			TOK_BOOTING },
305 	{ "bootp",			TOK_BOOTP },
306 	{ "class",			TOK_CLASS },
307 	{ "client-hostname",		TOK_CLIENT_HOSTNAME },
308 	{ "default-lease-time",		TOK_DEFAULT_LEASE_TIME },
309 	{ "deny",			TOK_DENY },
310 	{ "domain",			TOK_DOMAIN },
311 	{ "dynamic-bootp",		TOK_DYNAMIC_BOOTP },
312 	{ "dynamic-bootp-lease-cutoff",	TOK_DYNAMIC_BOOTP_LEASE_CUTOFF },
313 	{ "dynamic-bootp-lease-length",	TOK_DYNAMIC_BOOTP_LEASE_LENGTH },
314 	{ "echo-client-id",		TOK_ECHO_CLIENT_ID },
315 	{ "ends",			TOK_ENDS },
316 	{ "ethernet",			TOK_ETHERNET },
317 	{ "filename",			TOK_FILENAME },
318 	{ "fixed-address",		TOK_FIXED_ADDR },
319 	{ "get-lease-hostnames",	TOK_GET_LEASE_HOSTNAMES },
320 	{ "group",			TOK_GROUP },
321 	{ "hardware",			TOK_HARDWARE },
322 	{ "host",			TOK_HOST },
323 	{ "hostname",			TOK_HOSTNAME },
324 	{ "ipsec-tunnel",		TOK_IPSEC_TUNNEL },
325 	{ "lease",			TOK_LEASE },
326 	{ "max-lease-time",		TOK_MAX_LEASE_TIME },
327 	{ "netmask",			TOK_NETMASK },
328 	{ "next-server",		TOK_NEXT_SERVER },
329 	{ "not",			TOK_TOKEN_NOT },
330 	{ "option",			TOK_OPTION },
331 	{ "range",			TOK_RANGE },
332 	{ "server-identifier",		TOK_SERVER_IDENTIFIER },
333 	{ "server-name",		TOK_SERVER_NAME },
334 	{ "shared-network",		TOK_SHARED_NETWORK },
335 	{ "starts",			TOK_STARTS },
336 	{ "subnet",			TOK_SUBNET },
337 	{ "timeout",			TOK_TIMEOUT },
338 	{ "timestamp",			TOK_TIMESTAMP },
339 	{ "uid",			TOK_UID },
340 	{ "unknown-clients",		TOK_UNKNOWN_CLIENTS },
341 	{ "use-host-decl-names",	TOK_USE_HOST_DECL_NAMES },
342 	{ "use-lease-addr-for-default-route",
343 					TOK_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE },
344 	{ "user-class",			TOK_USER_CLASS },
345 	{ "vendor-class",		TOK_VENDOR_CLASS }
346 };
347 
348 static int
kw_cmp(const void * k,const void * e)349 kw_cmp(const void *k, const void *e)
350 {
351 	return (strcasecmp(k, ((const struct keywords *)e)->k_name));
352 }
353 
354 static int
intern(char * atom,int dfv)355 intern(char *atom, int dfv)
356 {
357 	const struct keywords *p;
358 
359 	p = bsearch(atom, keywords, sizeof(keywords)/sizeof(keywords[0]),
360 	    sizeof(keywords[0]), kw_cmp);
361 	if (p)
362 		return (p->k_val);
363 	return (dfv);
364 }
365