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 * 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/cdefs.h>
44 __FBSDID("$FreeBSD$");
45
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 char *prev_line;
55 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 case 'c':
349 if (!strcasecmp(atom + 1, "lass"))
350 return (CLASS);
351 if (!strcasecmp(atom + 1, "iaddr"))
352 return (CIADDR);
353 if (!strcasecmp(atom + 1, "lient-identifier"))
354 return (CLIENT_IDENTIFIER);
355 if (!strcasecmp(atom + 1, "lient-hostname"))
356 return (CLIENT_HOSTNAME);
357 break;
358 case 'd':
359 if (!strcasecmp(atom + 1, "omain"))
360 return (DOMAIN);
361 if (!strcasecmp(atom + 1, "eny"))
362 return (DENY);
363 if (!strncasecmp(atom + 1, "efault", 6)) {
364 if (!atom[7])
365 return (DEFAULT);
366 if (!strcasecmp(atom + 7, "-lease-time"))
367 return (DEFAULT_LEASE_TIME);
368 break;
369 }
370 if (!strncasecmp(atom + 1, "ynamic-bootp", 12)) {
371 if (!atom[13])
372 return (DYNAMIC_BOOTP);
373 if (!strcasecmp(atom + 13, "-lease-cutoff"))
374 return (DYNAMIC_BOOTP_LEASE_CUTOFF);
375 if (!strcasecmp(atom + 13, "-lease-length"))
376 return (DYNAMIC_BOOTP_LEASE_LENGTH);
377 break;
378 }
379 break;
380 case 'e':
381 if (!strcasecmp(atom + 1, "thernet"))
382 return (ETHERNET);
383 if (!strcasecmp(atom + 1, "nds"))
384 return (ENDS);
385 if (!strcasecmp(atom + 1, "xpire"))
386 return (EXPIRE);
387 break;
388 case 'f':
389 if (!strcasecmp(atom + 1, "ilename"))
390 return (FILENAME);
391 if (!strcasecmp(atom + 1, "ixed-address"))
392 return (FIXED_ADDR);
393 if (!strcasecmp(atom + 1, "ddi"))
394 return (FDDI);
395 break;
396 case 'g':
397 if (!strcasecmp(atom + 1, "iaddr"))
398 return (GIADDR);
399 if (!strcasecmp(atom + 1, "roup"))
400 return (GROUP);
401 if (!strcasecmp(atom + 1, "et-lease-hostnames"))
402 return (GET_LEASE_HOSTNAMES);
403 break;
404 case 'h':
405 if (!strcasecmp(atom + 1, "ost"))
406 return (HOST);
407 if (!strcasecmp(atom + 1, "ardware"))
408 return (HARDWARE);
409 if (!strcasecmp(atom + 1, "ostname"))
410 return (HOSTNAME);
411 break;
412 case 'i':
413 if (!strcasecmp(atom + 1, "nitial-interval"))
414 return (INITIAL_INTERVAL);
415 if (!strcasecmp(atom + 1, "nterface"))
416 return (INTERFACE);
417 break;
418 case 'l':
419 if (!strcasecmp(atom + 1, "ease"))
420 return (LEASE);
421 break;
422 case 'm':
423 if (!strcasecmp(atom + 1, "ax-lease-time"))
424 return (MAX_LEASE_TIME);
425 if (!strncasecmp(atom + 1, "edi", 3)) {
426 if (!strcasecmp(atom + 4, "a"))
427 return (MEDIA);
428 if (!strcasecmp(atom + 4, "um"))
429 return (MEDIUM);
430 break;
431 }
432 break;
433 case 'n':
434 if (!strcasecmp(atom + 1, "ameserver"))
435 return (NAMESERVER);
436 if (!strcasecmp(atom + 1, "etmask"))
437 return (NETMASK);
438 if (!strcasecmp(atom + 1, "ext-server"))
439 return (NEXT_SERVER);
440 if (!strcasecmp(atom + 1, "ot"))
441 return (TOKEN_NOT);
442 break;
443 case 'o':
444 if (!strcasecmp(atom + 1, "ption"))
445 return (OPTION);
446 if (!strcasecmp(atom + 1, "ne-lease-per-client"))
447 return (ONE_LEASE_PER_CLIENT);
448 break;
449 case 'p':
450 if (!strcasecmp(atom + 1, "repend"))
451 return (PREPEND);
452 if (!strcasecmp(atom + 1, "acket"))
453 return (PACKET);
454 break;
455 case 'r':
456 if (!strcasecmp(atom + 1, "ange"))
457 return (RANGE);
458 if (!strcasecmp(atom + 1, "equest"))
459 return (REQUEST);
460 if (!strcasecmp(atom + 1, "equire"))
461 return (REQUIRE);
462 if (!strcasecmp(atom + 1, "etry"))
463 return (RETRY);
464 if (!strcasecmp(atom + 1, "enew"))
465 return (RENEW);
466 if (!strcasecmp(atom + 1, "ebind"))
467 return (REBIND);
468 if (!strcasecmp(atom + 1, "eboot"))
469 return (REBOOT);
470 if (!strcasecmp(atom + 1, "eject"))
471 return (REJECT);
472 break;
473 case 's':
474 if (!strcasecmp(atom + 1, "earch"))
475 return (SEARCH);
476 if (!strcasecmp(atom + 1, "tarts"))
477 return (STARTS);
478 if (!strcasecmp(atom + 1, "iaddr"))
479 return (SIADDR);
480 if (!strcasecmp(atom + 1, "ubnet"))
481 return (SUBNET);
482 if (!strcasecmp(atom + 1, "hared-network"))
483 return (SHARED_NETWORK);
484 if (!strcasecmp(atom + 1, "erver-name"))
485 return (SERVER_NAME);
486 if (!strcasecmp(atom + 1, "erver-identifier"))
487 return (SERVER_IDENTIFIER);
488 if (!strcasecmp(atom + 1, "elect-timeout"))
489 return (SELECT_TIMEOUT);
490 if (!strcasecmp(atom + 1, "end"))
491 return (SEND);
492 if (!strcasecmp(atom + 1, "cript"))
493 return (SCRIPT);
494 if (!strcasecmp(atom + 1, "upersede"))
495 return (SUPERSEDE);
496 break;
497 case 't':
498 if (!strcasecmp(atom + 1, "imestamp"))
499 return (TIMESTAMP);
500 if (!strcasecmp(atom + 1, "imeout"))
501 return (TIMEOUT);
502 if (!strcasecmp(atom + 1, "oken-ring"))
503 return (TOKEN_RING);
504 break;
505 case 'u':
506 if (!strncasecmp(atom + 1, "se", 2)) {
507 if (!strcasecmp(atom + 3, "r-class"))
508 return (USER_CLASS);
509 if (!strcasecmp(atom + 3, "-host-decl-names"))
510 return (USE_HOST_DECL_NAMES);
511 if (!strcasecmp(atom + 3,
512 "-lease-addr-for-default-route"))
513 return (USE_LEASE_ADDR_FOR_DEFAULT_ROUTE);
514 break;
515 }
516 if (!strcasecmp(atom + 1, "id"))
517 return (UID);
518 if (!strcasecmp(atom + 1, "nknown-clients"))
519 return (UNKNOWN_CLIENTS);
520 break;
521 case 'v':
522 if (!strcasecmp(atom + 1, "endor-class"))
523 return (VENDOR_CLASS);
524 break;
525 case 'y':
526 if (!strcasecmp(atom + 1, "iaddr"))
527 return (YIADDR);
528 break;
529 }
530 return (dfv);
531 }
532