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