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: stable/12/sbin/dhclient/conflex.c 370261 2021-08-02 15:06:06Z kp $");
47
48 #include <ctype.h>
49
50 #include "dhcpd.h"
51 #include "dhctoken.h"
52
53 int lexline;
54 int lexchar;
55 char *token_line;
56 static char *prev_line;
57 static char *cur_line;
58 const char *tlname;
59 int eol_token;
60
61 static char line1[81];
62 static char line2[81];
63 static unsigned lpos;
64 static unsigned line;
65 static int tlpos;
66 static int tline;
67 static int token;
68 static int ugflag;
69 static char *tval;
70 static char tokbuf[1500];
71
72 static int get_char(FILE *);
73 static int get_token(FILE *);
74 static void skip_to_eol(FILE *);
75 static int read_string(FILE *);
76 static int read_number(int, FILE *);
77 static int read_num_or_name(int, FILE *);
78 static int intern(char *, int);
79
80 void
new_parse(const char * name)81 new_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
92 static int
get_char(FILE * cfile)93 get_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
120 static int
get_token(FILE * cfile)121 get_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
168 int
next_token(char ** rval,FILE * cfile)169 next_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
190 int
peek_token(char ** rval,FILE * cfile)191 peek_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
214 static void
skip_to_eol(FILE * cfile)215 skip_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
228 static int
read_string(FILE * cfile)229 read_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
264 static int
read_number(int c,FILE * cfile)265 read_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
292 static int
read_num_or_name(int c,FILE * cfile)293 read_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
320 static int
intern(char * atom,int dfv)321 intern(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 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