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
51 int lexline;
52 int lexchar;
53 char *token_line;
54 static char *prev_line;
55 static char *cur_line;
56 const char *tlname;
57 int eol_token;
58
59 static char line1[81];
60 static char line2[81];
61 static unsigned lpos;
62 static unsigned line;
63 static int tlpos;
64 static int tline;
65 static int token;
66 static int ugflag;
67 static char *tval;
68 static char tokbuf[1500];
69
70 static int get_char(FILE *);
71 static int get_token(FILE *);
72 static void skip_to_eol(FILE *);
73 static int read_string(FILE *);
74 static int read_number(int, FILE *);
75 static int read_num_or_name(int, FILE *);
76 static int intern(char *, int);
77
78 void
new_parse(const char * name)79 new_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
90 static int
get_char(FILE * cfile)91 get_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
118 static int
get_token(FILE * cfile)119 get_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
166 int
next_token(char ** rval,FILE * cfile)167 next_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
188 int
peek_token(char ** rval,FILE * cfile)189 peek_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
212 static void
skip_to_eol(FILE * cfile)213 skip_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
226 static int
read_string(FILE * cfile)227 read_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
262 static int
read_number(int c,FILE * cfile)263 read_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
290 static int
read_num_or_name(int c,FILE * cfile)291 read_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
318 static int
intern(char * atom,int dfv)319 intern(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