1 /*
2  * Copyright (c) 2017 by Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
14  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  *
16  *   Internet Systems Consortium, Inc.
17  *   950 Charter Street
18  *   Redwood City, CA 94063
19  *   <info@isc.org>
20  *   https://www.isc.org/
21  *
22  */
23 
24 /* From common/conflex.c */
25 
26 #include "keama.h"
27 
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/mman.h>
31 #include <assert.h>
32 #include <ctype.h>
33 #include <fcntl.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 
38 static int get_char(struct parse *);
39 static void unget_char(struct parse *, int);
40 static void skip_to_eol(struct parse *);
41 static enum dhcp_token read_whitespace(int c, struct parse *cfile);
42 static enum dhcp_token read_string(struct parse *);
43 static enum dhcp_token read_number(int, struct parse *);
44 static enum dhcp_token read_num_or_name(int, struct parse *);
45 static enum dhcp_token intern(char *, enum dhcp_token);
46 
47 struct parse *
new_parse(int file,char * inbuf,size_t buflen,const char * name,int eolp)48 new_parse(int file, char *inbuf, size_t buflen, const char *name, int eolp)
49 {
50 	struct parse *tmp;
51 
52 	tmp = (struct parse *)malloc(sizeof(struct parse));
53 	assert(tmp != NULL);
54 	memset(tmp, 0, sizeof(struct parse));
55 
56 	TAILQ_INSERT_TAIL(&parses, tmp);
57 
58 	tmp->tlname = name;
59 	tmp->lpos = tmp->line = 1;
60 	tmp->cur_line = tmp->line1;
61 	tmp->prev_line = tmp->line2;
62 	tmp->token_line = tmp->cur_line;
63 	tmp->cur_line[0] = tmp->prev_line[0] = 0;
64 	tmp->file = file;
65 	tmp->eol_token = eolp;
66 	TAILQ_INIT(&tmp->comments);
67 
68 	if (inbuf != NULL) {
69 		tmp->inbuf = inbuf;
70 		tmp->buflen = buflen;
71 		tmp->bufsiz = 0;
72 	} else {
73 		struct stat sb;
74 
75 		if (fstat(file, &sb) < 0) {
76 			fprintf(stderr, "can't stat input\n");
77 			exit(1);
78 		}
79 
80 		if (sb.st_size == 0)
81 			return tmp;
82 
83 		tmp->bufsiz = tmp->buflen = (size_t) sb.st_size;
84 		tmp->inbuf = mmap(NULL, tmp->bufsiz, PROT_READ, MAP_SHARED,
85 				  file, 0);
86 
87 		if (tmp->inbuf == MAP_FAILED) {
88 			fprintf(stderr, "can't map input\n");
89 			exit(1);
90 		}
91 	}
92 
93 	return tmp;
94 }
95 
96 void
end_parse(struct parse * cfile)97 end_parse(struct parse *cfile)
98 {
99 	/* "Memory" config files have no file. */
100 	if (cfile->file != -1) {
101 		munmap(cfile->inbuf, cfile->bufsiz);
102 		close(cfile->file);
103 	}
104 
105 	while (TAILQ_NEXT(cfile) != NULL) {
106 		struct parse *saved_state;
107 
108 		saved_state = TAILQ_NEXT(cfile);
109 		TAILQ_REMOVE(&parses, saved_state);
110 		free(saved_state);
111 	}
112 
113 	cfile->stack_size = 0;
114 	if (cfile->stack != NULL)
115 		free(cfile->stack);
116 	cfile->stack = NULL;
117 	TAILQ_REMOVE(&parses, cfile);
118 	free(cfile);
119 }
120 
121 /*
122  * Save the current state of the parser.
123  *
124  * Only one state may be saved. Any previous saved state is
125  * lost.
126  */
127 void
save_parse_state(struct parse * cfile)128 save_parse_state(struct parse *cfile) {
129 	struct parse *tmp;
130 
131 	/*
132 	 * Free any previous saved states.
133 	 */
134 	while (TAILQ_NEXT(cfile) != NULL) {
135 		struct parse *saved_state;
136 
137 		saved_state = TAILQ_NEXT(cfile);
138 		TAILQ_REMOVE(&parses, saved_state);
139 		free(saved_state);
140 	}
141 
142 	/*
143 	 * Save our current state.
144 	 */
145 	tmp = (struct parse *)malloc(sizeof(struct parse));
146 	if (tmp == NULL)
147 		parse_error(cfile, "can't allocate state to be saved");
148 	memset(tmp, 0, sizeof(struct parse));
149 	/* save up to comments field */
150 	memcpy(tmp, cfile, (size_t)&(((struct parse *)0)->comments));
151 	TAILQ_INSERT_AFTER(&parses, cfile, tmp);
152 }
153 
154 /*
155  * Return the parser to the previous saved state.
156  *
157  * You must call save_parse_state() every time before calling
158  * restore_parse_state().
159  */
160 void
restore_parse_state(struct parse * cfile)161 restore_parse_state(struct parse *cfile) {
162 	struct parse *saved_state;
163 
164 	if (TAILQ_NEXT(cfile) == NULL)
165 		parse_error(cfile, "can't find saved state");
166 
167 	saved_state = TAILQ_NEXT(cfile);
168 	TAILQ_REMOVE(&parses, saved_state);
169 	/* restore up to comments field */
170 	memcpy(cfile, saved_state, (size_t)&(((struct parse *)0)->comments));
171 	free(saved_state);
172 }
173 
174 static int
get_char(struct parse * cfile)175 get_char(struct parse *cfile)
176 {
177 	/* My kingdom for WITH... */
178 	int c;
179 
180 	if (cfile->bufix == cfile->buflen) {
181 		c = EOF;
182 	} else {
183 		c = cfile->inbuf[cfile->bufix];
184 		cfile->bufix++;
185 	}
186 
187 	if (!cfile->ugflag) {
188 		if (c == EOL) {
189 			if (cfile->cur_line == cfile->line1) {
190 				cfile->cur_line = cfile->line2;
191 				cfile->prev_line = cfile->line1;
192 			} else {
193 				cfile->cur_line = cfile->line1;
194 				cfile->prev_line = cfile->line2;
195 			}
196 			cfile->line++;
197 			cfile->lpos = 1;
198 			cfile->cur_line[0] = 0;
199 		} else if (c != EOF) {
200 			if (cfile->lpos <= 80) {
201 				cfile->cur_line[cfile->lpos - 1] = c;
202 				cfile->cur_line[cfile->lpos] = 0;
203 			}
204 			cfile->lpos++;
205 		}
206 	} else
207 		cfile->ugflag = 0;
208 	return c;
209 }
210 
211 /*
212  * Return a character to our input buffer.
213  */
214 static void
unget_char(struct parse * cfile,int c)215 unget_char(struct parse *cfile, int c) {
216 	if (c != EOF) {
217 		cfile->bufix--;
218 		cfile->ugflag = 1;	/* do not put characters into
219 					   our error buffer on the next
220 					   call to get_char() */
221 	}
222 }
223 
224 /*
225  * GENERAL NOTE ABOUT TOKENS
226  *
227  * We normally only want non-whitespace tokens. There are some
228  * circumstances where we *do* want to see whitespace (for example
229  * when parsing IPv6 addresses).
230  *
231  * Generally we use the next_token() function to read tokens. This
232  * in turn calls get_next_token, which does *not* return tokens for
233  * whitespace. Rather, it skips these.
234  *
235  * When we need to see whitespace, we us next_raw_token(), which also
236  * returns the WHITESPACE token.
237  *
238  * The peek_token() and peek_raw_token() functions work as expected.
239  *
240  * Warning: if you invoke peek_token(), then if there is a whitespace
241  * token, it will be lost, and subsequent use of next_raw_token() or
242  * peek_raw_token() will NOT see it.
243  */
244 
245 static enum dhcp_token
get_raw_token(struct parse * cfile)246 get_raw_token(struct parse *cfile) {
247 	int c;
248 	enum dhcp_token ttok;
249 	static char tb[2];
250 	int l, p;
251 
252 	for (;;) {
253 		l = cfile->line;
254 		p = cfile->lpos;
255 
256 		c = get_char(cfile);
257 		if (!((c == '\n') && cfile->eol_token) &&
258 		    isascii(c) && isspace(c)) {
259 		    	ttok = read_whitespace(c, cfile);
260 			break;
261 		}
262 		if (c == '#') {
263 			skip_to_eol(cfile);
264 			continue;
265 		}
266 		if (c == '"') {
267 			cfile->lexline = l;
268 			cfile->lexchar = p;
269 			ttok = read_string(cfile);
270 			break;
271 		}
272 		if ((isascii(c) && isdigit(c)) || c == '-') {
273 			cfile->lexline = l;
274 			cfile->lexchar = p;
275 			ttok = read_number(c, cfile);
276 			break;
277 		} else if (isascii(c) && isalpha(c)) {
278 			cfile->lexline = l;
279 			cfile->lexchar = p;
280 			ttok = read_num_or_name(c, cfile);
281 			break;
282 		} else if (c == EOF) {
283 			ttok = END_OF_FILE;
284 			cfile->tlen = 0;
285 			break;
286 		} else {
287 			cfile->lexline = l;
288 			cfile->lexchar = p;
289 			tb[0] = c;
290 			tb[1] = 0;
291 			cfile->tval = tb;
292 			cfile->tlen = 1;
293 			ttok = c;
294 			break;
295 		}
296 	}
297 	return ttok;
298 }
299 
300 /*
301  * The get_next_token() function consumes the next token and
302  * returns it to the caller.
303  *
304  * Since the code is almost the same for "normal" and "raw"
305  * input, we pass a flag to alter the way it works.
306  */
307 
308 static enum dhcp_token
get_next_token(const char ** rval,unsigned * rlen,struct parse * cfile,isc_boolean_t raw)309 get_next_token(const char **rval, unsigned *rlen,
310 	       struct parse *cfile, isc_boolean_t raw) {
311 	int rv;
312 
313 	if (cfile->token) {
314 		if (cfile->lexline != cfile->tline)
315 			cfile->token_line = cfile->cur_line;
316 		cfile->lexchar = cfile->tlpos;
317 		cfile->lexline = cfile->tline;
318 		rv = cfile->token;
319 		cfile->token = 0;
320 	} else {
321 		rv = get_raw_token(cfile);
322 		cfile->token_line = cfile->cur_line;
323 	}
324 
325 	if (!raw) {
326 		while (rv == WHITESPACE) {
327 			rv = get_raw_token(cfile);
328 			cfile->token_line = cfile->cur_line;
329 		}
330 	}
331 
332 	if (rval)
333 		*rval = cfile->tval;
334 	if (rlen)
335 		*rlen = cfile->tlen;
336 	return rv;
337 }
338 
339 /*
340  * Get the next token from cfile and return it.
341  *
342  * If rval is non-NULL, set the pointer it contains to
343  * the contents of the token.
344  *
345  * If rlen is non-NULL, set the integer it contains to
346  * the length of the token.
347  */
348 
349 enum dhcp_token
next_token(const char ** rval,unsigned * rlen,struct parse * cfile)350 next_token(const char **rval, unsigned *rlen, struct parse *cfile) {
351 	return get_next_token(rval, rlen, cfile, ISC_FALSE);
352 }
353 
354 
355 /*
356  * The same as the next_token() function above, but will return space
357  * as the WHITESPACE token.
358  */
359 
360 enum dhcp_token
next_raw_token(const char ** rval,unsigned * rlen,struct parse * cfile)361 next_raw_token(const char **rval, unsigned *rlen, struct parse *cfile) {
362 	return get_next_token(rval, rlen, cfile, ISC_TRUE);
363 }
364 
365 
366 /*
367  * The do_peek_token() function checks the next token without
368  * consuming it, and returns it to the caller.
369  *
370  * Since the code is almost the same for "normal" and "raw"
371  * input, we pass a flag to alter the way it works. (See the
372  * warning in the GENERAL NOTES ABOUT TOKENS above though.)
373  */
374 
375 enum dhcp_token
do_peek_token(const char ** rval,unsigned int * rlen,struct parse * cfile,isc_boolean_t raw)376 do_peek_token(const char **rval, unsigned int *rlen,
377 	      struct parse *cfile, isc_boolean_t raw) {
378 	int x;
379 
380 	if (!cfile->token || (!raw && (cfile->token == WHITESPACE))) {
381 		cfile->tlpos = cfile->lexchar;
382 		cfile->tline = cfile->lexline;
383 
384 		do {
385 			cfile->token = get_raw_token(cfile);
386 		} while (!raw && (cfile->token == WHITESPACE));
387 
388 		if (cfile->lexline != cfile->tline)
389 			cfile->token_line = cfile->prev_line;
390 
391 		x = cfile->lexchar;
392 		cfile->lexchar = cfile->tlpos;
393 		cfile->tlpos = x;
394 
395 		x = cfile->lexline;
396 		cfile->lexline = cfile->tline;
397 		cfile->tline = x;
398 	}
399 	if (rval)
400 		*rval = cfile->tval;
401 	if (rlen)
402 		*rlen = cfile->tlen;
403 	return cfile->token;
404 }
405 
406 
407 /*
408  * Get the next token from cfile and return it, leaving it for a
409  * subsequent call to next_token().
410  *
411  * Note that it WILL consume whitespace tokens.
412  *
413  * If rval is non-NULL, set the pointer it contains to
414  * the contents of the token.
415  *
416  * If rlen is non-NULL, set the integer it contains to
417  * the length of the token.
418  */
419 
420 enum dhcp_token
peek_token(const char ** rval,unsigned * rlen,struct parse * cfile)421 peek_token(const char **rval, unsigned *rlen, struct parse *cfile) {
422 	return do_peek_token(rval, rlen, cfile, ISC_FALSE);
423 }
424 
425 
426 /*
427  * The same as the peek_token() function above, but will return space
428  * as the WHITESPACE token.
429  */
430 
431 enum dhcp_token
peek_raw_token(const char ** rval,unsigned * rlen,struct parse * cfile)432 peek_raw_token(const char **rval, unsigned *rlen, struct parse *cfile) {
433 	return do_peek_token(rval, rlen, cfile, ISC_TRUE);
434 }
435 
436 /*
437  * The comment up to but not including EOL is saved.
438  */
439 
440 static void
skip_to_eol(struct parse * cfile)441 skip_to_eol(struct parse *cfile)
442 {
443 	char buf[128];
444 	unsigned cc = 0;
445 	struct comment *comment;
446 
447 	memset(buf, 0, sizeof(buf));
448 	buf[0] = '#';
449 	for (;;) {
450 		int c;
451 
452 		c = get_char(cfile);
453 		if (c == EOF)
454 			break;
455 		if (c == EOL) {
456 			break;
457 		}
458 		if (++cc < sizeof(buf) - 1)
459 			buf[cc] = c;
460 	}
461 	comment = createComment(buf);
462 	TAILQ_INSERT_TAIL(&cfile->comments, comment);
463 }
464 
465 static enum dhcp_token
read_whitespace(int c,struct parse * cfile)466 read_whitespace(int c, struct parse *cfile) {
467 	int ofs;
468 
469 	/*
470 	 * Read as much whitespace as we have available.
471 	 */
472 	ofs = 0;
473 	do {
474 		if (ofs >= (sizeof(cfile->tokbuf) - 1)) {
475 			/*
476 			 * As the file includes a huge amount of whitespace,
477 			 * it's probably broken.
478 			 * Print out a warning and bail out.
479 			 */
480 			parse_error(cfile,
481 				    "whitespace too long, buffer overflow.");
482 		}
483 		cfile->tokbuf[ofs++] = c;
484 		c = get_char(cfile);
485 		if (c == EOF)
486 			return END_OF_FILE;
487 	} while (!((c == '\n') && cfile->eol_token) &&
488 		 isascii(c) && isspace(c));
489 
490 	/*
491 	 * Put the last (non-whitespace) character back.
492 	 */
493 	unget_char(cfile, c);
494 
495 	/*
496 	 * Return our token.
497 	 */
498 	cfile->tokbuf[ofs] = '\0';
499 	cfile->tlen = ofs;
500 	cfile->tval = cfile->tokbuf;
501 	return WHITESPACE;
502 }
503 
504 static enum dhcp_token
read_string(struct parse * cfile)505 read_string(struct parse *cfile)
506 {
507 	unsigned i;
508 	int bs = 0;
509 	int c;
510 	int value = 0;
511 	int hex = 0;
512 
513 	for (i = 0; i < sizeof(cfile->tokbuf); i++) {
514 	      again:
515 		c = get_char(cfile);
516 		if (c == EOF)
517 			parse_error(cfile, "eof in string constant");
518 		if (bs == 1) {
519 			switch (c) {
520 			case 't':
521 				cfile->tokbuf[i] = '\t';
522 				break;
523 			case 'r':
524 				cfile->tokbuf[i] = '\r';
525 				break;
526 			case 'n':
527 				cfile->tokbuf[i] = '\n';
528 				break;
529 			case 'b':
530 				cfile->tokbuf[i] = '\b';
531 				break;
532 			case '0':
533 			case '1':
534 			case '2':
535 			case '3':
536 				hex = 0;
537 				value = c - '0';
538 				++bs;
539 				goto again;
540 			case 'x':
541 				hex = 1;
542 				value = 0;
543 				++bs;
544 				goto again;
545 			default:
546 				cfile->tokbuf[i] = c;
547 				break;
548 			}
549 			bs = 0;
550 		} else if (bs > 1) {
551 			if (hex) {
552 				if (c >= '0' && c <= '9') {
553 					value = value * 16 + (c - '0');
554 				} else if (c >= 'a' && c <= 'f') {
555 					value = value * 16 + (c - 'a' + 10);
556 				} else if (c >= 'A' && c <= 'F') {
557 					value = value * 16 + (c - 'A' + 10);
558 				} else
559 					parse_error(cfile,
560 						    "invalid hex digit: %x",
561 						    c);
562 				if (++bs == 4) {
563 					cfile->tokbuf[i] = value;
564 					bs = 0;
565 				} else
566 					goto again;
567 			} else {
568 				if (c >= '0' && c <= '7') {
569 					value = value * 8 + (c - '0');
570 				} else {
571 				    if (value != 0)
572 					parse_error(cfile,
573 						    "invalid octal digit %x",
574 						    c);
575 				    else
576 					cfile->tokbuf[i] = 0;
577 				    bs = 0;
578 				}
579 				if (++bs == 4) {
580 					cfile->tokbuf[i] = value;
581 					bs = 0;
582 				} else
583 					goto again;
584 			}
585 		} else if (c == '\\') {
586 			bs = 1;
587 			goto again;
588 		} else if (c == '"')
589 			break;
590 		else
591 			cfile->tokbuf[i] = c;
592 	}
593 	/* Normally, I'd feel guilty about this, but we're talking about
594 	   strings that'll fit in a DHCP packet here... */
595 	if (i == sizeof(cfile->tokbuf))
596 		parse_error(cfile,
597 			    "string constant larger than internal buffer");
598 	cfile->tokbuf[i] = 0;
599 	cfile->tlen = i;
600 	cfile->tval = cfile->tokbuf;
601 	return STRING;
602 }
603 
604 static enum dhcp_token
read_number(int c,struct parse * cfile)605 read_number(int c, struct parse *cfile)
606 {
607 	unsigned i = 0;
608 	int token = NUMBER;
609 
610 	cfile->tokbuf[i++] = c;
611 	for (; i < sizeof(cfile->tokbuf); i++) {
612 		c = get_char(cfile);
613 
614 		/* Promote NUMBER->NUMBER_OR_NAME->NAME, never demote.
615 		 * Except in the case of '0x' syntax hex, which gets called
616 		 * a NAME at '0x', and returned to NUMBER_OR_NAME once it's
617 		 * verified to be at least 0xf or less.
618 		 */
619 		switch (isascii(c) ? token : BREAK) {
620 		case NUMBER:
621 			if (isdigit(c))
622 				break;
623 			/* FALLTHROUGH */
624 		case NUMBER_OR_NAME:
625 			if (isxdigit(c)) {
626 				token = NUMBER_OR_NAME;
627 				break;
628 			}
629 			/* FALLTHROUGH */
630 		case NAME:
631 			if ((i == 2) && isxdigit(c) &&
632 				(cfile->tokbuf[0] == '0') &&
633 				((cfile->tokbuf[1] == 'x') ||
634 				 (cfile->tokbuf[1] == 'X'))) {
635 				token = NUMBER_OR_NAME;
636 				break;
637 			} else if (((c == '-') || (c == '_') || isalnum(c))) {
638 				token = NAME;
639 				break;
640 			}
641 			/* FALLTHROUGH */
642 		case BREAK:
643 			/* At this point c is either EOF or part of the next
644 			 * token.  If not EOF, rewind the file one byte so
645 			 * the next token is read from there.
646 			 */
647 			unget_char(cfile, c);
648 			goto end_read;
649 
650 		default:
651 			parse_error(cfile,
652 				    "read_number(): impossible case");
653 		}
654 
655 		cfile->tokbuf[i] = c;
656 	}
657 
658 	if (i == sizeof(cfile->tokbuf))
659 		parse_error(cfile,
660 			    "numeric token larger than internal buffer");
661 
662   end_read:
663 	cfile->tokbuf[i] = 0;
664 	cfile->tlen = i;
665 	cfile->tval = cfile->tokbuf;
666 
667 	/*
668 	 * If this entire token from start to finish was "-", such as
669 	 * the middle parameter in "42 - 7", return just the MINUS token.
670 	 */
671 	if ((i == 1) && (cfile->tokbuf[i] == '-'))
672 		return MINUS;
673 	else
674 		return token;
675 }
676 
677 static enum dhcp_token
read_num_or_name(int c,struct parse * cfile)678 read_num_or_name(int c, struct parse *cfile)
679 {
680 	unsigned i = 0;
681 	enum dhcp_token rv = NUMBER_OR_NAME;
682 
683 	cfile->tokbuf[i++] = c;
684 	for (; i < sizeof(cfile->tokbuf); i++) {
685 		c = get_char(cfile);
686 		if (!isascii(c) ||
687 		    (c != '-' && c != '_' && !isalnum(c))) {
688 		    	unget_char(cfile, c);
689 			break;
690 		}
691 		if (!isxdigit(c))
692 			rv = NAME;
693 		cfile->tokbuf[i] = c;
694 	}
695 	if (i == sizeof(cfile->tokbuf))
696 		parse_error(cfile, "token larger than internal buffer");
697 	cfile->tokbuf[i] = 0;
698 	cfile->tlen = i;
699 	cfile->tval = cfile->tokbuf;
700 	return intern(cfile->tval, rv);
701 }
702 
703 static enum dhcp_token
intern(char * atom,enum dhcp_token dfv)704 intern(char *atom, enum dhcp_token dfv) {
705 	if (!isascii(atom[0]))
706 		return dfv;
707 
708 	switch (tolower((unsigned char)atom[0])) {
709 	case '-':
710 		if (atom [1] == 0)
711 			return MINUS;
712 		break;
713 
714 	case 'a':
715 		if (!strcasecmp(atom + 1, "bandoned"))
716 			return TOKEN_ABANDONED;
717 		if (!strcasecmp(atom + 1, "ctive"))
718 			return TOKEN_ACTIVE;
719 		if (!strncasecmp(atom + 1, "dd", 2)) {
720 			if (atom[3] == '\0')
721 				return TOKEN_ADD;
722 			else if (!strcasecmp(atom + 3, "ress"))
723 				return ADDRESS;
724 			break;
725 		}
726 		if (!strcasecmp(atom + 1, "fter"))
727 			return AFTER;
728 		if (isascii(atom[1]) &&
729 		    (tolower((unsigned char)atom[1]) == 'l')) {
730 			if (!strcasecmp(atom + 2, "gorithm"))
731 				return ALGORITHM;
732 			if (!strcasecmp(atom + 2, "ias"))
733 				return ALIAS;
734 			if (isascii(atom[2]) &&
735 			    (tolower((unsigned char)atom[2]) == 'l')) {
736 				if (atom[3] == '\0')
737 					return ALL;
738 				else if (!strcasecmp(atom + 3, "ow"))
739 					return ALLOW;
740 				break;
741 			}
742 			if (!strcasecmp(atom + 2, "so"))
743 				return TOKEN_ALSO;
744 			break;
745 		}
746 		if (isascii(atom[1]) &&
747 		    (tolower((unsigned char)atom[1]) == 'n')) {
748 			if (!strcasecmp(atom + 2, "d"))
749 				return AND;
750 			if (!strcasecmp(atom + 2, "ycast-mac"))
751 				return ANYCAST_MAC;
752 			break;
753 		}
754 		if (!strcasecmp(atom + 1, "ppend"))
755 			return APPEND;
756 		if (!strcasecmp(atom + 1, "rray"))
757 			return ARRAY;
758 		if (isascii(atom[1]) &&
759 		    (tolower((unsigned char)atom[1]) == 't')) {
760 			if (atom[2] == '\0')
761 				return AT;
762 			if (!strcasecmp(atom + 2, "sfp"))
763 				return ATSFP;
764 			break;
765 		}
766 		if (!strcasecmp(atom + 1, "uthoring-byte-order"))
767 			return AUTHORING_BYTE_ORDER;
768 		if (!strncasecmp(atom + 1, "ut", 2)) {
769 			if (isascii(atom[3]) &&
770 			    (tolower((unsigned char)atom[3]) == 'h')) {
771 				if (!strncasecmp(atom + 4, "enticat", 7)) {
772 					if (!strcasecmp(atom + 11, "ed"))
773 						return AUTHENTICATED;
774 					if (!strcasecmp(atom + 11, "ion"))
775 						return AUTHENTICATION;
776 					break;
777 				}
778 				if (!strcasecmp(atom + 4, "oritative"))
779 					return AUTHORITATIVE;
780 				break;
781 			}
782 			if (!strcasecmp(atom + 3, "o-partner-down"))
783 				return AUTO_PARTNER_DOWN;
784 			break;
785 		}
786 		break;
787 	case 'b':
788 		if (!strcasecmp(atom + 1, "ackup"))
789 			return TOKEN_BACKUP;
790 		if (!strcasecmp(atom + 1, "ootp"))
791 			return TOKEN_BOOTP;
792 		if (!strcasecmp(atom + 1, "inding"))
793 			return BINDING;
794 		if (!strcasecmp(atom + 1, "inary-to-ascii"))
795 			return BINARY_TO_ASCII;
796 		if (!strcasecmp(atom + 1, "ackoff-cutoff"))
797 			return BACKOFF_CUTOFF;
798 		if (!strcasecmp(atom + 1, "ooting"))
799 			return BOOTING;
800 		if (!strcasecmp(atom + 1, "oot-unknown-clients"))
801 			return BOOT_UNKNOWN_CLIENTS;
802 		if (!strcasecmp(atom + 1, "reak"))
803 			return BREAK;
804 		if (!strcasecmp(atom + 1, "illing"))
805 			return BILLING;
806 		if (!strcasecmp(atom + 1, "oolean"))
807 			return BOOLEAN;
808 		if (!strcasecmp(atom + 1, "alance"))
809 			return BALANCE;
810 		if (!strcasecmp(atom + 1, "ound"))
811 			return BOUND;
812 		if (!strcasecmp(atom+1, "ig-endian")) {
813 			return TOKEN_BIG_ENDIAN;
814 		}
815 		break;
816 	case 'c':
817 		if (!strcasecmp(atom + 1, "ase"))
818 			return CASE;
819 		if (!strcasecmp(atom + 1, "heck"))
820 			return CHECK;
821 		if (!strcasecmp(atom + 1, "iaddr"))
822 			return CIADDR;
823 		if (isascii(atom[1]) &&
824 		    tolower((unsigned char)atom[1]) == 'l') {
825 			if (!strcasecmp(atom + 2, "ass"))
826 				return CLASS;
827 			if (!strncasecmp(atom + 2, "ient", 4)) {
828 				if (!strcasecmp(atom + 6, "s"))
829 					return CLIENTS;
830 				if (atom[6] == '-') {
831 					if (!strcasecmp(atom + 7, "hostname"))
832 						return CLIENT_HOSTNAME;
833 					if (!strcasecmp(atom + 7, "identifier"))
834 						return CLIENT_IDENTIFIER;
835 					if (!strcasecmp(atom + 7, "state"))
836 						return CLIENT_STATE;
837 					if (!strcasecmp(atom + 7, "updates"))
838 						return CLIENT_UPDATES;
839 					break;
840 				}
841 				break;
842 			}
843 			if (!strcasecmp(atom + 2, "ose"))
844 				return TOKEN_CLOSE;
845 			if (!strcasecmp(atom + 2, "tt"))
846 				return CLTT;
847 			break;
848 		}
849 		if (isascii(atom[1]) &&
850 		    tolower((unsigned char)atom[1]) == 'o') {
851 			if (!strcasecmp(atom + 2, "de"))
852 				return CODE;
853 			if (isascii(atom[2]) &&
854 			    tolower((unsigned char)atom[2]) == 'm') {
855 				if (!strcasecmp(atom + 3, "mit"))
856 					return COMMIT;
857 				if (!strcasecmp(atom + 3,
858 						"munications-interrupted"))
859 					return COMMUNICATIONS_INTERRUPTED;
860 				if (!strcasecmp(atom + 3, "pressed"))
861 					return COMPRESSED;
862 				break;
863 			}
864 			if (isascii(atom[2]) &&
865 			    tolower((unsigned char)atom[2]) == 'n') {
866 				if (!strcasecmp(atom + 3, "cat"))
867 					return CONCAT;
868 				if (!strcasecmp(atom + 3, "fig-option"))
869 					return CONFIG_OPTION;
870 				if (!strcasecmp(atom + 3, "flict-done"))
871 					return CONFLICT_DONE;
872 				if (!strcasecmp(atom + 3, "nect"))
873 					return CONNECT;
874 				break;
875 			}
876 			break;
877 		}
878 		if (!strcasecmp(atom + 1, "reate"))
879 			return TOKEN_CREATE;
880 		break;
881 	case 'd':
882 		if (!strcasecmp(atom + 1, "b-time-format"))
883 			return DB_TIME_FORMAT;
884 		if (!strcasecmp(atom + 1, "omain"))
885 			return DOMAIN;
886 		if (!strncasecmp(atom + 1, "omain-", 6)) {
887 			if (!strcasecmp(atom + 7, "name"))
888 				return DOMAIN_NAME;
889 			if (!strcasecmp(atom + 7, "list"))
890 				return DOMAIN_LIST;
891 		}
892 		if (!strcasecmp(atom + 1, "o-forward-updates"))
893 			return DO_FORWARD_UPDATE;
894 		/* do-forward-update is included for historical reasons */
895 		if (!strcasecmp(atom + 1, "o-forward-update"))
896 			return DO_FORWARD_UPDATE;
897 		if (!strcasecmp(atom + 1, "ebug"))
898 			return TOKEN_DEBUG;
899 		if (!strcasecmp(atom + 1, "eny"))
900 			return DENY;
901 		if (!strcasecmp(atom + 1, "eleted"))
902 			return TOKEN_DELETED;
903 		if (!strcasecmp(atom + 1, "elete"))
904 			return TOKEN_DELETE;
905 		if (!strncasecmp(atom + 1, "efault", 6)) {
906 			if (!atom [7])
907 				return DEFAULT;
908 			if (!strcasecmp(atom + 7, "-duid"))
909 				return DEFAULT_DUID;
910 			if (!strcasecmp(atom + 7, "-lease-time"))
911 				return DEFAULT_LEASE_TIME;
912 			break;
913 		}
914 		if (!strncasecmp(atom + 1, "ynamic", 6)) {
915 			if (!atom [7])
916 				return DYNAMIC;
917 			if (!strncasecmp(atom + 7, "-bootp", 6)) {
918 				if (!atom [13])
919 					return DYNAMIC_BOOTP;
920 				if (!strcasecmp(atom + 13, "-lease-cutoff"))
921 					return DYNAMIC_BOOTP_LEASE_CUTOFF;
922 				if (!strcasecmp(atom + 13, "-lease-length"))
923 					return DYNAMIC_BOOTP_LEASE_LENGTH;
924 				break;
925 			}
926 		}
927 		if (!strcasecmp(atom + 1, "uplicates"))
928 			return DUPLICATES;
929 		if (!strcasecmp(atom + 1, "eclines"))
930 			return DECLINES;
931 		if (!strncasecmp(atom + 1, "efine", 5)) {
932 			if (!strcasecmp(atom + 6, "d"))
933 				return DEFINED;
934 			if (!atom [6])
935 				return DEFINE;
936 		}
937 		break;
938 	case 'e':
939 		if (isascii(atom [1]) &&
940 		    tolower((unsigned char)atom[1]) == 'x') {
941 			if (!strcasecmp(atom + 2, "tract-int"))
942 				return EXTRACT_INT;
943 			if (!strcasecmp(atom + 2, "ists"))
944 				return EXISTS;
945 			if (!strcasecmp(atom + 2, "piry"))
946 				return EXPIRY;
947 			if (!strcasecmp(atom + 2, "pire"))
948 				return EXPIRE;
949 			if (!strcasecmp(atom + 2, "pired"))
950 				return TOKEN_EXPIRED;
951 		}
952 		if (!strcasecmp(atom + 1, "ncode-int"))
953 			return ENCODE_INT;
954 		if (!strcasecmp(atom + 1, "poch"))
955 			return EPOCH;
956 		if (!strcasecmp(atom + 1, "thernet"))
957 			return ETHERNET;
958 		if (!strcasecmp(atom + 1, "nds"))
959 			return ENDS;
960 		if (!strncasecmp(atom + 1, "ls", 2)) {
961 			if (!strcasecmp(atom + 3, "e"))
962 				return ELSE;
963 			if (!strcasecmp(atom + 3, "if"))
964 				return ELSIF;
965 			break;
966 		}
967 		if (!strcasecmp(atom + 1, "rror"))
968 			return ERROR;
969 		if (!strcasecmp(atom + 1, "val"))
970 			return EVAL;
971 		if (!strcasecmp(atom + 1, "ncapsulate"))
972 			return ENCAPSULATE;
973 		if (!strcasecmp(atom + 1, "xecute"))
974 			return EXECUTE;
975 		if (!strcasecmp(atom+1, "n")) {
976 			return EN;
977 		}
978 		break;
979 	case 'f':
980 		if (!strcasecmp(atom + 1, "atal"))
981 			return FATAL;
982 		if (!strcasecmp(atom + 1, "ilename"))
983 			return FILENAME;
984 		if (!strcasecmp(atom + 1, "ixed-address"))
985 			return FIXED_ADDR;
986 		if (!strcasecmp(atom + 1, "ixed-address6"))
987 			return FIXED_ADDR6;
988 		if (!strcasecmp(atom + 1, "ixed-prefix6"))
989 			return FIXED_PREFIX6;
990 		if (!strcasecmp(atom + 1, "ddi"))
991 			return TOKEN_FDDI;
992 		if (!strcasecmp(atom + 1, "ormerr"))
993 			return NS_FORMERR;
994 		if (!strcasecmp(atom + 1, "unction"))
995 			return FUNCTION;
996 		if (!strcasecmp(atom + 1, "ailover"))
997 			return FAILOVER;
998 		if (!strcasecmp(atom + 1, "ree"))
999 			return TOKEN_FREE;
1000 		break;
1001 	case 'g':
1002 		if (!strncasecmp(atom + 1, "et", 2)) {
1003 			if (!strcasecmp(atom + 3, "-lease-hostnames"))
1004 				return GET_LEASE_HOSTNAMES;
1005 			if (!strcasecmp(atom + 3, "hostbyname"))
1006 				return GETHOSTBYNAME;
1007 			if (!strcasecmp(atom + 3, "hostname"))
1008 				return GETHOSTNAME;
1009 			break;
1010 		}
1011 		if (!strcasecmp(atom + 1, "iaddr"))
1012 			return GIADDR;
1013 		if (!strcasecmp(atom + 1, "roup"))
1014 			return GROUP;
1015 		break;
1016 	case 'h':
1017 		if (!strcasecmp(atom + 1, "ash"))
1018 			return HASH;
1019 		if (!strcasecmp(atom + 1, "ba"))
1020 			return HBA;
1021 		if (!strcasecmp(atom + 1, "ost"))
1022 			return HOST;
1023 		if (!strcasecmp(atom + 1, "ost-decl-name"))
1024 			return HOST_DECL_NAME;
1025 		if (!strcasecmp(atom + 1, "ost-identifier"))
1026 			return HOST_IDENTIFIER;
1027 		if (!strcasecmp(atom + 1, "ardware"))
1028 			return HARDWARE;
1029 		if (!strcasecmp(atom + 1, "ostname"))
1030 			return HOSTNAME;
1031 		if (!strcasecmp(atom + 1, "elp"))
1032 			return TOKEN_HELP;
1033 		if (!strcasecmp(atom + 1, "ex")) {
1034 			return TOKEN_HEX;
1035 		}
1036 		break;
1037 	case 'i':
1038 	      	if (!strcasecmp(atom+1, "a-na"))
1039 			return IA_NA;
1040 	      	if (!strcasecmp(atom+1, "a-ta"))
1041 			return IA_TA;
1042 	      	if (!strcasecmp(atom+1, "a-pd"))
1043 			return IA_PD;
1044 	      	if (!strcasecmp(atom+1, "aaddr"))
1045 			return IAADDR;
1046 	      	if (!strcasecmp(atom+1, "aprefix"))
1047 			return IAPREFIX;
1048 		if (!strcasecmp(atom + 1, "nclude"))
1049 			return INCLUDE;
1050 		if (!strcasecmp(atom + 1, "nteger"))
1051 			return INTEGER;
1052 		if (!strcasecmp(atom  + 1, "nfiniband"))
1053 			return TOKEN_INFINIBAND;
1054 		if (!strcasecmp(atom + 1, "nfinite"))
1055 			return INFINITE;
1056 		if (!strcasecmp(atom + 1, "nfo"))
1057 			return INFO;
1058 		if (!strcasecmp(atom + 1, "p-address"))
1059 			return IP_ADDRESS;
1060 		if (!strcasecmp(atom + 1, "p6-address"))
1061 			return IP6_ADDRESS;
1062 		if (!strcasecmp(atom + 1, "nitial-interval"))
1063 			return INITIAL_INTERVAL;
1064 		if (!strcasecmp(atom + 1, "nitial-delay"))
1065 			return INITIAL_DELAY;
1066 		if (!strcasecmp(atom + 1, "nterface"))
1067 			return INTERFACE;
1068 		if (!strcasecmp(atom + 1, "dentifier"))
1069 			return IDENTIFIER;
1070 		if (!strcasecmp(atom + 1, "f"))
1071 			return IF;
1072 		if (!strcasecmp(atom + 1, "s"))
1073 			return IS;
1074 		if (!strcasecmp(atom + 1, "gnore"))
1075 			return IGNORE;
1076 		break;
1077 	case 'k':
1078 		if (!strncasecmp(atom + 1, "nown", 4)) {
1079 			if (!strcasecmp(atom + 5, "-clients"))
1080 				return KNOWN_CLIENTS;
1081 			if (!atom[5])
1082 				return KNOWN;
1083 			break;
1084 		}
1085 		if (!strcasecmp(atom + 1, "ey"))
1086 			return KEY;
1087 		if (!strcasecmp (atom + 1, "ey-algorithm"))
1088 			return KEY_ALGORITHM;
1089 		break;
1090 	case 'l':
1091 		if (!strcasecmp(atom + 1, "case"))
1092 			return LCASE;
1093 		if (!strcasecmp(atom + 1, "ease"))
1094 			return LEASE;
1095 		if (!strcasecmp(atom + 1, "ease6"))
1096 			return LEASE6;
1097 		if (!strcasecmp(atom + 1, "eased-address"))
1098 			return LEASED_ADDRESS;
1099 		if (!strcasecmp(atom + 1, "ease-time"))
1100 			return LEASE_TIME;
1101 		if (!strcasecmp(atom + 1, "easequery"))
1102 			return LEASEQUERY;
1103 		if (!strcasecmp(atom + 1, "ength"))
1104 			return LENGTH;
1105 		if (!strcasecmp(atom + 1, "imit"))
1106 			return LIMIT;
1107 		if (!strcasecmp(atom + 1, "et"))
1108 			return LET;
1109 		if (!strcasecmp(atom + 1, "oad"))
1110 			return LOAD;
1111 		if (!strcasecmp(atom + 1, "ocal"))
1112 			return LOCAL;
1113 		if (!strcasecmp(atom + 1, "og"))
1114 			return LOG;
1115 		if (!strcasecmp(atom+1, "lt")) {
1116 			return LLT;
1117 		}
1118 		if (!strcasecmp(atom+1, "l")) {
1119 			return LL;
1120 		}
1121 		if (!strcasecmp(atom+1, "ittle-endian")) {
1122 			return TOKEN_LITTLE_ENDIAN;
1123 		}
1124 		if (!strcasecmp(atom + 1, "ease-id-format")) {
1125 			return LEASE_ID_FORMAT;
1126 		}
1127 		break;
1128 	case 'm':
1129 		if (!strncasecmp(atom + 1, "ax", 2)) {
1130 			if (!atom [3])
1131 				return TOKEN_MAX;
1132 			if (!strcasecmp(atom + 3, "-balance"))
1133 				return MAX_BALANCE;
1134 			if (!strncasecmp(atom + 3, "-lease-", 7)) {
1135 				if (!strcasecmp(atom + 10, "misbalance"))
1136 					return MAX_LEASE_MISBALANCE;
1137 				if (!strcasecmp(atom + 10, "ownership"))
1138 					return MAX_LEASE_OWNERSHIP;
1139 				if (!strcasecmp(atom + 10, "time"))
1140 					return MAX_LEASE_TIME;
1141 			}
1142 			if (!strcasecmp(atom + 3, "-life"))
1143 				return MAX_LIFE;
1144 			if (!strcasecmp(atom + 3, "-transmit-idle"))
1145 				return MAX_TRANSMIT_IDLE;
1146 			if (!strcasecmp(atom + 3, "-response-delay"))
1147 				return MAX_RESPONSE_DELAY;
1148 			if (!strcasecmp(atom + 3, "-unacked-updates"))
1149 				return MAX_UNACKED_UPDATES;
1150 		}
1151 		if (!strncasecmp(atom + 1, "in-", 3)) {
1152 			if (!strcasecmp(atom + 4, "balance"))
1153 				return MIN_BALANCE;
1154 			if (!strcasecmp(atom + 4, "lease-time"))
1155 				return MIN_LEASE_TIME;
1156 			if (!strcasecmp(atom + 4, "secs"))
1157 				return MIN_SECS;
1158 			break;
1159 		}
1160 		if (!strncasecmp(atom + 1, "edi", 3)) {
1161 			if (!strcasecmp(atom + 4, "a"))
1162 				return MEDIA;
1163 			if (!strcasecmp(atom + 4, "um"))
1164 				return MEDIUM;
1165 			break;
1166 		}
1167 		if (!strcasecmp(atom + 1, "atch"))
1168 			return MATCH;
1169 		if (!strcasecmp(atom + 1, "embers"))
1170 			return MEMBERS;
1171 		if (!strcasecmp(atom + 1, "y"))
1172 			return MY;
1173 		if (!strcasecmp(atom + 1, "clt"))
1174 			return MCLT;
1175 		break;
1176 	case 'n':
1177 		if (!strcasecmp(atom + 1, "ormal"))
1178 			return NORMAL;
1179 		if (!strcasecmp(atom + 1, "ameserver"))
1180 			return NAMESERVER;
1181 		if (!strcasecmp(atom + 1, "etmask"))
1182 			return NETMASK;
1183 		if (!strcasecmp(atom + 1, "ever"))
1184 			return NEVER;
1185 		if (!strcasecmp(atom + 1, "ext-server"))
1186 			return NEXT_SERVER;
1187 		if (!strcasecmp(atom + 1, "ot"))
1188 			return TOKEN_NOT;
1189 		if (!strcasecmp(atom + 1, "o"))
1190 			return TOKEN_NO;
1191 		if (!strcasecmp(atom + 1, "oerror"))
1192 			return NS_NOERROR;
1193 		if (!strcasecmp(atom + 1, "otauth"))
1194 			return NS_NOTAUTH;
1195 		if (!strcasecmp(atom + 1, "otimp"))
1196 			return NS_NOTIMP;
1197 		if (!strcasecmp(atom + 1, "otzone"))
1198 			return NS_NOTZONE;
1199 		if (!strcasecmp(atom + 1, "xdomain"))
1200 			return NS_NXDOMAIN;
1201 		if (!strcasecmp(atom + 1, "xrrset"))
1202 			return NS_NXRRSET;
1203 		if (!strcasecmp(atom + 1, "ull"))
1204 			return TOKEN_NULL;
1205 		if (!strcasecmp(atom + 1, "ext"))
1206 			return TOKEN_NEXT;
1207 		if (!strcasecmp(atom + 1, "ew"))
1208 			return TOKEN_NEW;
1209 		break;
1210 	case 'o':
1211 		if (!strcasecmp(atom + 1, "mapi"))
1212 			return OMAPI;
1213 		if (!strcasecmp(atom + 1, "r"))
1214 			return OR;
1215 		if (!strcasecmp(atom + 1, "n"))
1216 			return ON;
1217 		if (!strcasecmp(atom + 1, "pen"))
1218 			return TOKEN_OPEN;
1219 		if (!strcasecmp(atom + 1, "ption"))
1220 			return OPTION;
1221 		if (!strcasecmp(atom + 1, "ne-lease-per-client"))
1222 			return ONE_LEASE_PER_CLIENT;
1223 		if (!strcasecmp(atom + 1, "f"))
1224 			return OF;
1225 		if (!strcasecmp(atom + 1, "wner"))
1226 			return OWNER;
1227 		if (!strcasecmp(atom + 1, "ctal")) {
1228 			return TOKEN_OCTAL;
1229 		}
1230 		break;
1231 	case 'p':
1232 		if (!strcasecmp(atom + 1, "arse-vendor-option"))
1233 			return PARSE_VENDOR_OPT;
1234 		if (!strcasecmp(atom + 1, "repend"))
1235 			return PREPEND;
1236 		if (!strcasecmp(atom + 1, "referred-life"))
1237 			return PREFERRED_LIFE;
1238 		if (!strcasecmp(atom + 1, "acket"))
1239 			return PACKET;
1240 		if (!strcasecmp(atom + 1, "ool"))
1241 			return POOL;
1242 		if (!strcasecmp(atom + 1, "ool6"))
1243 			return POOL6;
1244 		if (!strcasecmp(atom + 1, "refix6"))
1245 			return PREFIX6;
1246 		if (!strcasecmp(atom + 1, "seudo"))
1247 			return PSEUDO;
1248 		if (!strcasecmp(atom + 1, "eer"))
1249 			return PEER;
1250 		if (!strcasecmp(atom + 1, "rimary"))
1251 			return PRIMARY;
1252 		if (!strcasecmp(atom + 1, "rimary6"))
1253 			return PRIMARY6;
1254 		if (!strncasecmp(atom + 1, "artner", 6)) {
1255 			if (!atom [7])
1256 				return PARTNER;
1257 			if (!strcasecmp(atom + 7, "-down"))
1258 				return PARTNER_DOWN;
1259 		}
1260 		if (!strcasecmp(atom + 1, "ort"))
1261 			return PORT;
1262 		if (!strcasecmp(atom + 1, "otential-conflict"))
1263 			return POTENTIAL_CONFLICT;
1264 		if (!strcasecmp(atom + 1, "ick-first-value") ||
1265 		    !strcasecmp(atom + 1, "ick"))
1266 			return PICK;
1267 		if (!strcasecmp(atom + 1, "aused"))
1268 			return PAUSED;
1269 		break;
1270 	case 'r':
1271 		if (!strcasecmp(atom + 1, "ange"))
1272 			return RANGE;
1273 		if (!strcasecmp(atom + 1, "ange6"))
1274 			return RANGE6;
1275 		if (isascii(atom[1]) &&
1276 		    (tolower((unsigned char)atom[1]) == 'e')) {
1277 			if (!strcasecmp(atom + 2, "bind"))
1278 				return REBIND;
1279 			if (!strcasecmp(atom + 2, "boot"))
1280 				return REBOOT;
1281 			if (!strcasecmp(atom + 2, "contact-interval"))
1282 				return RECONTACT_INTERVAL;
1283 			if (!strncasecmp(atom + 2, "cover", 5)) {
1284 				if (atom[7] == '\0')
1285 					return RECOVER;
1286 				if (!strcasecmp(atom + 7, "-done"))
1287 					return RECOVER_DONE;
1288 				if (!strcasecmp(atom + 7, "-wait"))
1289 					return RECOVER_WAIT;
1290 				break;
1291 			}
1292 			if (!strcasecmp(atom + 2, "fresh"))
1293 				return REFRESH;
1294 			if (!strcasecmp(atom + 2, "fused"))
1295 				return NS_REFUSED;
1296 			if (!strcasecmp(atom + 2, "ject"))
1297 				return REJECT;
1298 			if (!strcasecmp(atom + 2, "lease"))
1299 				return RELEASE;
1300 			if (!strcasecmp(atom + 2, "leased"))
1301 				return TOKEN_RELEASED;
1302 			if (!strcasecmp(atom + 2, "move"))
1303 				return REMOVE;
1304 			if (!strcasecmp(atom + 2, "new"))
1305 				return RENEW;
1306 			if (!strcasecmp(atom + 2, "quest"))
1307 				return REQUEST;
1308 			if (!strcasecmp(atom + 2, "quire"))
1309 				return REQUIRE;
1310 			if (isascii(atom[2]) &&
1311 			    (tolower((unsigned char)atom[2]) == 's')) {
1312 				if (!strcasecmp(atom + 3, "erved"))
1313 					return TOKEN_RESERVED;
1314 				if (!strcasecmp(atom + 3, "et"))
1315 					return TOKEN_RESET;
1316 				if (!strcasecmp(atom + 3,
1317 						"olution-interrupted"))
1318 					return RESOLUTION_INTERRUPTED;
1319 				break;
1320 			}
1321 			if (!strcasecmp(atom + 2, "try"))
1322 				return RETRY;
1323 			if (!strcasecmp(atom + 2, "turn"))
1324 				return RETURN;
1325 			if (!strcasecmp(atom + 2, "verse"))
1326 				return REVERSE;
1327 			if (!strcasecmp(atom + 2, "wind"))
1328 				return REWIND;
1329 			break;
1330 		}
1331 		break;
1332 	case 's':
1333 		if (!strcasecmp(atom + 1, "cript"))
1334 			return SCRIPT;
1335 		if (isascii(atom[1]) &&
1336 		    tolower((unsigned char)atom[1]) == 'e') {
1337 			if (!strcasecmp(atom + 2, "arch"))
1338 				return SEARCH;
1339 			if (isascii(atom[2]) &&
1340 			    tolower((unsigned char)atom[2]) == 'c') {
1341 				if (!strncasecmp(atom + 3, "ond", 3)) {
1342                                         if (!strcasecmp(atom + 6, "ary"))
1343 						return SECONDARY;
1344                                         if (!strcasecmp(atom + 6, "ary6"))
1345 						return SECONDARY6;
1346                                         if (!strcasecmp(atom + 6, "s"))
1347                                                 return SECONDS;
1348 					break;
1349 				}
1350                                 if (!strcasecmp(atom + 3, "ret"))
1351                                         return SECRET;
1352 				break;
1353 			}
1354 			if (!strncasecmp(atom + 2, "lect", 4)) {
1355                                 if (atom[6] == '\0')
1356                                         return SELECT;
1357                                 if (!strcasecmp(atom + 6, "-timeout"))
1358                                         return SELECT_TIMEOUT;
1359 				break;
1360 			}
1361                         if (!strcasecmp(atom + 2, "nd"))
1362                                 return SEND;
1363 			if (!strncasecmp(atom + 2, "rv", 2)) {
1364 				if (!strncasecmp(atom + 4, "er", 2)) {
1365                                         if (atom[6] == '\0')
1366                                                 return TOKEN_SERVER;
1367 					if (atom[6] == '-') {
1368 						if (!strcasecmp(atom + 7,
1369 								"duid"))
1370 							return SERVER_DUID;
1371                                                 if (!strcasecmp(atom + 7,
1372 								"name"))
1373                                                         return SERVER_NAME;
1374                                                 if (!strcasecmp(atom + 7,
1375 								"identifier"))
1376                                                       return SERVER_IDENTIFIER;
1377 						break;
1378 					}
1379 					break;
1380 				}
1381                                 if (!strcasecmp(atom + 4, "fail"))
1382                                         return NS_SERVFAIL;
1383 				break;
1384 			}
1385                         if (!strcasecmp(atom + 2, "t"))
1386                                 return TOKEN_SET;
1387 			break;
1388 		}
1389 		if (isascii(atom[1]) &&
1390 		    tolower((unsigned char)atom[1]) == 'h') {
1391                         if (!strcasecmp(atom + 2, "ared-network"))
1392                                 return SHARED_NETWORK;
1393                         if (!strcasecmp(atom + 2, "utdown"))
1394                                 return SHUTDOWN;
1395 			break;
1396 		}
1397 		if (isascii(atom[1]) &&
1398 		    tolower((unsigned char)atom[1]) == 'i') {
1399                         if (!strcasecmp(atom + 2, "addr"))
1400                                 return SIADDR;
1401                         if (!strcasecmp(atom + 2, "gned"))
1402                                 return SIGNED;
1403                         if (!strcasecmp(atom + 2, "ze"))
1404                                 return SIZE;
1405 			break;
1406 		}
1407 		if (isascii(atom[1]) &&
1408 		    tolower((unsigned char)atom[1]) == 'p') {
1409 			if (isascii(atom[2]) &&
1410 			    tolower((unsigned char)atom[2]) == 'a') {
1411                                 if (!strcasecmp(atom + 3, "ce"))
1412                                         return SPACE;
1413                                 if (!strcasecmp(atom + 3, "wn"))
1414                                         return SPAWN;
1415 				break;
1416 			}
1417                         if (!strcasecmp(atom + 2, "lit"))
1418                                 return SPLIT;
1419 			break;
1420 		}
1421 		if (isascii(atom[1]) &&
1422 		    tolower((unsigned char)atom[1]) == 't') {
1423 			if (isascii(atom[2]) &&
1424 			    tolower((unsigned char)atom[2]) == 'a') {
1425 				if (!strncasecmp(atom + 3, "rt", 2)) {
1426                                          if (!strcasecmp(atom + 5, "s"))
1427                                                  return STARTS;
1428                                          if (!strcasecmp(atom + 5, "up"))
1429                                                  return STARTUP;
1430 					break;
1431 				}
1432 				if (isascii(atom[3]) &&
1433 				    tolower((unsigned char)atom[3]) == 't') {
1434                                         if (!strcasecmp(atom + 4, "e"))
1435                                                 return STATE;
1436                                         if (!strcasecmp(atom + 4, "ic"))
1437                                                 return STATIC;
1438 					break;
1439 				}
1440 			}
1441                         if (!strcasecmp(atom + 2, "ring"))
1442                                 return STRING_TOKEN;
1443 			break;
1444 		}
1445                 if (!strncasecmp(atom + 1, "ub", 2)) {
1446                         if (!strcasecmp(atom + 3, "class"))
1447                                 return SUBCLASS;
1448                         if (!strcasecmp(atom + 3, "net"))
1449                                 return SUBNET;
1450                         if (!strcasecmp(atom + 3, "net6"))
1451                                 return SUBNET6;
1452                         if (!strcasecmp(atom + 3, "string"))
1453                                 return SUBSTRING;
1454                         break;
1455                 }
1456 		if (isascii(atom[1]) &&
1457 		    tolower((unsigned char)atom[1]) == 'u') {
1458                         if (!strcasecmp(atom + 2, "ffix"))
1459                                 return SUFFIX;
1460                         if (!strcasecmp(atom + 2, "persede"))
1461                                 return SUPERSEDE;
1462 		}
1463                 if (!strcasecmp(atom + 1, "witch"))
1464                         return SWITCH;
1465 		break;
1466 	case 't':
1467 		if (!strcasecmp(atom + 1, "imestamp"))
1468 			return TIMESTAMP;
1469 		if (!strcasecmp(atom + 1, "imeout"))
1470 			return TIMEOUT;
1471 		if (!strcasecmp(atom + 1, "oken-ring"))
1472 			return TOKEN_RING;
1473 		if (!strcasecmp(atom + 1, "ext"))
1474 			return TEXT;
1475 		if (!strcasecmp(atom + 1, "stp"))
1476 			return TSTP;
1477 		if (!strcasecmp(atom + 1, "sfp"))
1478 			return TSFP;
1479 		if (!strcasecmp(atom + 1, "ransmission"))
1480 			return TRANSMISSION;
1481 		if (!strcasecmp(atom + 1, "emporary"))
1482 			return TEMPORARY;
1483 		break;
1484 	case 'u':
1485 		if (!strcasecmp(atom + 1, "case"))
1486 			return UCASE;
1487 		if (!strcasecmp(atom + 1, "nset"))
1488 			return UNSET;
1489 		if (!strcasecmp(atom + 1, "nsigned"))
1490 			return UNSIGNED;
1491 		if (!strcasecmp(atom + 1, "id"))
1492 			return UID;
1493 		if (!strncasecmp(atom + 1, "se", 2)) {
1494 			if (!strcasecmp(atom + 3, "r-class"))
1495 				return USER_CLASS;
1496 			if (!strcasecmp(atom + 3, "-host-decl-names"))
1497 				return USE_HOST_DECL_NAMES;
1498 			if (!strcasecmp(atom + 3,
1499 					 "-lease-addr-for-default-route"))
1500 				return USE_LEASE_ADDR_FOR_DEFAULT_ROUTE;
1501 			break;
1502 		}
1503 		if (!strncasecmp(atom + 1, "nknown", 6)) {
1504 			if (!strcasecmp(atom + 7, "-clients"))
1505 				return UNKNOWN_CLIENTS;
1506 			if (!strcasecmp(atom + 7, "-state"))
1507 				return UNKNOWN_STATE;
1508 			if (!atom [7])
1509 				return UNKNOWN;
1510 			break;
1511 		}
1512 		if (!strcasecmp(atom + 1, "nauthenticated"))
1513 			return UNAUTHENTICATED;
1514 		if (!strcasecmp(atom + 1, "pdate"))
1515 			return UPDATE;
1516 		break;
1517 	case 'v':
1518 		if (!strcasecmp(atom + 1, "6relay"))
1519 			return V6RELAY;
1520 		if (!strcasecmp(atom + 1, "6relopt"))
1521 			return V6RELOPT;
1522 		if (!strcasecmp(atom + 1, "endor-class"))
1523 			return VENDOR_CLASS;
1524 		if (!strcasecmp(atom + 1, "endor"))
1525 			return VENDOR;
1526 		break;
1527 	case 'w':
1528 		if (!strcasecmp(atom + 1, "ith"))
1529 			return WITH;
1530 		if (!strcasecmp(atom + 1, "idth"))
1531 			return WIDTH;
1532 		break;
1533 	case 'y':
1534 		if (!strcasecmp(atom + 1, "iaddr"))
1535 			return YIADDR;
1536 		if (!strcasecmp(atom + 1, "xdomain"))
1537 			return NS_YXDOMAIN;
1538 		if (!strcasecmp(atom + 1, "xrrset"))
1539 			return NS_YXRRSET;
1540 		break;
1541 	case 'z':
1542 		if (!strcasecmp(atom + 1, "erolen"))
1543 			return ZEROLEN;
1544 		if (!strcasecmp(atom + 1, "one"))
1545 			return ZONE;
1546 		break;
1547 	}
1548 	return dfv;
1549 }
1550 
1551