/* $NetBSD: conflex.c,v 1.2.6.1 2024/02/29 11:39:17 martin Exp $ */ /* conflex.c Lexical scanner for dhcpd config file... */ /* * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * PO Box 360 * Newmarket, NH 03857 USA * * https://www.isc.org/ * */ #include __RCSID("$NetBSD: conflex.c,v 1.2.6.1 2024/02/29 11:39:17 martin Exp $"); #include "dhcpd.h" #include static int get_char (struct parse *); static void unget_char(struct parse *, int); static void skip_to_eol (struct parse *); static enum dhcp_token read_whitespace(int c, struct parse *cfile); static enum dhcp_token read_string (struct parse *); static enum dhcp_token read_number (int, struct parse *); static enum dhcp_token read_num_or_name (int, struct parse *); static enum dhcp_token intern (char *, enum dhcp_token); isc_result_t new_parse (cfile, file, inbuf, buflen, name, eolp) struct parse **cfile; int file; char *inbuf; unsigned buflen; const char *name; int eolp; { isc_result_t status = ISC_R_SUCCESS; struct parse *tmp; tmp = dmalloc(sizeof(struct parse), MDL); if (tmp == NULL) { return (ISC_R_NOMEMORY); } /* * We don't need to initialize things to zero here, since * dmalloc() returns memory that is set to zero. */ tmp->tlname = name; tmp->lpos = tmp -> line = 1; tmp->cur_line = tmp->line1; tmp->prev_line = tmp->line2; tmp->token_line = tmp->cur_line; tmp->cur_line[0] = tmp->prev_line[0] = 0; tmp->file = file; tmp->eol_token = eolp; if (inbuf != NULL) { tmp->inbuf = inbuf; tmp->buflen = buflen; tmp->bufsiz = 0; } else { struct stat sb; if (fstat(file, &sb) < 0) { status = ISC_R_IOERROR; goto cleanup; } if (sb.st_size == 0) goto cleanup; tmp->bufsiz = tmp->buflen = (size_t) sb.st_size; tmp->inbuf = mmap(NULL, tmp->bufsiz, PROT_READ, MAP_SHARED, file, 0); if (tmp->inbuf == MAP_FAILED) { status = ISC_R_IOERROR; goto cleanup; } } *cfile = tmp; return (ISC_R_SUCCESS); cleanup: dfree(tmp, MDL); return (status); } isc_result_t end_parse (cfile) struct parse **cfile; { /* "Memory" config files have no file. */ if ((*cfile)->file != -1) { munmap((*cfile)->inbuf, (*cfile)->bufsiz); close((*cfile)->file); } if ((*cfile)->saved_state != NULL) { dfree((*cfile)->saved_state, MDL); } dfree(*cfile, MDL); *cfile = NULL; return ISC_R_SUCCESS; } /* * Save the current state of the parser. * * Only one state may be saved. Any previous saved state is * lost. */ isc_result_t save_parse_state(struct parse *cfile) { /* * Free any previous saved state. */ if (cfile->saved_state != NULL) { dfree(cfile->saved_state, MDL); } /* * Save our current state. */ cfile->saved_state = dmalloc(sizeof(struct parse), MDL); if (cfile->saved_state == NULL) { return ISC_R_NOMEMORY; } memcpy(cfile->saved_state, cfile, sizeof(*cfile)); return ISC_R_SUCCESS; } /* * Return the parser to the previous saved state. * * You must call save_parse_state() every time before calling * restore_parse_state(). * * Note: When the read function callback is in use in ldap mode, * a call to get_char() may reallocate the buffer and will append * config data to the buffer until a state restore. * Do not restore to the (freed) pointer and size, but use new one. */ isc_result_t restore_parse_state(struct parse *cfile) { struct parse *saved_state; #if defined(LDAP_CONFIGURATION) char *inbuf = cfile->inbuf; size_t size = cfile->bufsiz; #endif if (cfile->saved_state == NULL) { return DHCP_R_NOTYET; } saved_state = cfile->saved_state; memcpy(cfile, saved_state, sizeof(*cfile)); dfree(saved_state, MDL); cfile->saved_state = NULL; #if defined(LDAP_CONFIGURATION) cfile->inbuf = inbuf; cfile->bufsiz = size; #endif return ISC_R_SUCCESS; } static int get_char (cfile) struct parse *cfile; { /* My kingdom for WITH... */ int c; if (cfile->bufix == cfile->buflen) { #if !defined(LDAP_CONFIGURATION) c = EOF; #else /* defined(LDAP_CONFIGURATION) */ if (cfile->read_function != NULL) c = cfile->read_function(cfile); else c = EOF; #endif } else { c = cfile->inbuf [cfile->bufix]; cfile->bufix++; } if (!cfile->ugflag) { if (c == EOL) { if (cfile->cur_line == cfile->line1) { cfile->cur_line = cfile->line2; cfile->prev_line = cfile->line1; } else { cfile->cur_line = cfile->line1; cfile->prev_line = cfile->line2; } cfile->line++; cfile->lpos = 1; cfile->cur_line [0] = 0; } else if (c != EOF) { if (cfile->lpos <= 80) { cfile->cur_line [cfile->lpos - 1] = c; cfile->cur_line [cfile->lpos] = 0; } cfile->lpos++; } } else cfile->ugflag = 0; return c; } /* * Return a character to our input buffer. */ static void unget_char(struct parse *cfile, int c) { if (c != EOF) { cfile->bufix--; cfile->ugflag = 1; /* do not put characters into our error buffer on the next call to get_char() */ } } /* * GENERAL NOTE ABOUT TOKENS * * We normally only want non-whitespace tokens. There are some * circumstances where we *do* want to see whitespace (for example * when parsing IPv6 addresses). * * Generally we use the next_token() function to read tokens. This * in turn calls get_next_token, which does *not* return tokens for * whitespace. Rather, it skips these. * * When we need to see whitespace, we us next_raw_token(), which also * returns the WHITESPACE token. * * The peek_token() and peek_raw_token() functions work as expected. * * Warning: if you invoke peek_token(), then if there is a whitespace * token, it will be lost, and subsequent use of next_raw_token() or * peek_raw_token() will NOT see it. */ static enum dhcp_token get_raw_token(struct parse *cfile) { int c; enum dhcp_token ttok; static char tb [2]; int l, p; do { l = cfile -> line; p = cfile -> lpos; c = get_char (cfile); if (!((c == '\n') && cfile->eol_token) && isascii(c) && isspace(c)) { ttok = read_whitespace(c, cfile); break; } if (c == '#') { skip_to_eol (cfile); continue; } if (c == '"') { cfile -> lexline = l; cfile -> lexchar = p; ttok = read_string (cfile); break; } if ((isascii (c) && isdigit (c)) || c == '-') { cfile -> lexline = l; cfile -> lexchar = p; ttok = read_number (c, cfile); break; } else if (isascii (c) && isalpha (c)) { cfile -> lexline = l; cfile -> lexchar = p; ttok = read_num_or_name (c, cfile); break; } else if (c == EOF) { ttok = END_OF_FILE; cfile -> tlen = 0; break; } else { cfile -> lexline = l; cfile -> lexchar = p; tb [0] = c; tb [1] = 0; cfile -> tval = tb; cfile -> tlen = 1; ttok = c; break; } } while (1); return ttok; } /* * The get_next_token() function consumes the next token and * returns it to the caller. * * Since the code is almost the same for "normal" and "raw" * input, we pass a flag to alter the way it works. */ static enum dhcp_token get_next_token(const char **rval, unsigned *rlen, struct parse *cfile, isc_boolean_t raw) { int rv; if (cfile -> token) { if (cfile -> lexline != cfile -> tline) cfile -> token_line = cfile -> cur_line; cfile -> lexchar = cfile -> tlpos; cfile -> lexline = cfile -> tline; rv = cfile -> token; cfile -> token = 0; } else { rv = get_raw_token(cfile); cfile -> token_line = cfile -> cur_line; } if (!raw) { while (rv == WHITESPACE) { rv = get_raw_token(cfile); cfile->token_line = cfile->cur_line; } } if (rval) *rval = cfile -> tval; if (rlen) *rlen = cfile -> tlen; #ifdef DEBUG_TOKENS fprintf (stderr, "%s:%d ", cfile -> tval, rv); #endif return rv; } /* * Get the next token from cfile and return it. * * If rval is non-NULL, set the pointer it contains to * the contents of the token. * * If rlen is non-NULL, set the integer it contains to * the length of the token. */ enum dhcp_token next_token(const char **rval, unsigned *rlen, struct parse *cfile) { return get_next_token(rval, rlen, cfile, ISC_FALSE); } /* * The same as the next_token() function above, but will return space * as the WHITESPACE token. */ enum dhcp_token next_raw_token(const char **rval, unsigned *rlen, struct parse *cfile) { return get_next_token(rval, rlen, cfile, ISC_TRUE); } /* * The do_peek_token() function checks the next token without * consuming it, and returns it to the caller. * * Since the code is almost the same for "normal" and "raw" * input, we pass a flag to alter the way it works. (See the * warning in the GENERAL NOTES ABOUT TOKENS above though.) */ static enum dhcp_token do_peek_token(const char **rval, unsigned int *rlen, struct parse *cfile, isc_boolean_t raw) { int x; if (!cfile->token || (!raw && (cfile->token == WHITESPACE))) { cfile -> tlpos = cfile -> lexchar; cfile -> tline = cfile -> lexline; do { cfile->token = get_raw_token(cfile); } while (!raw && (cfile->token == WHITESPACE)); if (cfile -> lexline != cfile -> tline) cfile -> token_line = cfile -> prev_line; x = cfile -> lexchar; cfile -> lexchar = cfile -> tlpos; cfile -> tlpos = x; x = cfile -> lexline; cfile -> lexline = cfile -> tline; cfile -> tline = x; } if (rval) *rval = cfile -> tval; if (rlen) *rlen = cfile -> tlen; #ifdef DEBUG_TOKENS fprintf (stderr, "(%s:%d) ", cfile -> tval, cfile -> token); #endif return cfile -> token; } /* * Get the next token from cfile and return it, leaving it for a * subsequent call to next_token(). * * Note that it WILL consume whitespace tokens. * * If rval is non-NULL, set the pointer it contains to * the contents of the token. * * If rlen is non-NULL, set the integer it contains to * the length of the token. */ enum dhcp_token peek_token(const char **rval, unsigned *rlen, struct parse *cfile) { return do_peek_token(rval, rlen, cfile, ISC_FALSE); } /* * The same as the peek_token() function above, but will return space * as the WHITESPACE token. */ enum dhcp_token peek_raw_token(const char **rval, unsigned *rlen, struct parse *cfile) { return do_peek_token(rval, rlen, cfile, ISC_TRUE); } static void skip_to_eol (cfile) struct parse *cfile; { int c; do { c = get_char (cfile); if (c == EOF) return; if (c == EOL) { return; } } while (1); } static enum dhcp_token read_whitespace(int c, struct parse *cfile) { int ofs; /* * Read as much whitespace as we have available. */ ofs = 0; do { if (ofs >= (sizeof(cfile->tokbuf) - 1)) { /* * As the file includes a huge amount of whitespace, * it's probably broken. * Print out a warning and bail out. */ parse_warn(cfile, "whitespace too long, buffer overflow."); log_fatal("Exiting"); } cfile->tokbuf[ofs++] = c; c = get_char(cfile); if (c == EOF) return END_OF_FILE; } while (!((c == '\n') && cfile->eol_token) && isascii(c) && isspace(c)); /* * Put the last (non-whitespace) character back. */ unget_char(cfile, c); /* * Return our token. */ cfile->tokbuf[ofs] = '\0'; cfile->tlen = ofs; cfile->tval = cfile->tokbuf; return WHITESPACE; } static enum dhcp_token read_string (cfile) struct parse *cfile; { int i; int bs = 0; int c; int value = 0; int hex = 0; for (i = 0; i < sizeof cfile -> tokbuf; i++) { again: c = get_char (cfile); if (c == EOF) { parse_warn (cfile, "eof in string constant"); break; } if (bs == 1) { switch (c) { case 't': cfile -> tokbuf [i] = '\t'; break; case 'r': cfile -> tokbuf [i] = '\r'; break; case 'n': cfile -> tokbuf [i] = '\n'; break; case 'b': cfile -> tokbuf [i] = '\b'; break; case '0': case '1': case '2': case '3': hex = 0; value = c - '0'; ++bs; goto again; case 'x': hex = 1; value = 0; ++bs; goto again; default: cfile -> tokbuf [i] = c; break; } bs = 0; } else if (bs > 1) { if (hex) { if (c >= '0' && c <= '9') { value = value * 16 + (c - '0'); } else if (c >= 'a' && c <= 'f') { value = value * 16 + (c - 'a' + 10); } else if (c >= 'A' && c <= 'F') { value = value * 16 + (c - 'A' + 10); } else { parse_warn (cfile, "invalid hex digit: %x", c); bs = 0; continue; } if (++bs == 4) { cfile -> tokbuf [i] = value; bs = 0; } else goto again; } else { if (c >= '0' && c <= '7') { value = value * 8 + (c - '0'); } else { if (value != 0) { parse_warn (cfile, "invalid octal digit %x", c); continue; } else cfile -> tokbuf [i] = 0; bs = 0; } if (++bs == 4) { cfile -> tokbuf [i] = value; bs = 0; } else goto again; } } else if (c == '\\') { bs = 1; goto again; } else if (c == '"') break; else cfile -> tokbuf [i] = c; } /* Normally, I'd feel guilty about this, but we're talking about strings that'll fit in a DHCP packet here... */ if (i == sizeof cfile -> tokbuf) { parse_warn (cfile, "string constant larger than internal buffer"); --i; } cfile -> tokbuf [i] = 0; cfile -> tlen = i; cfile -> tval = cfile -> tokbuf; return STRING; } static enum dhcp_token read_number (c, cfile) int c; struct parse *cfile; { int i = 0; int token = NUMBER; cfile -> tokbuf [i++] = c; for (; i < sizeof cfile -> tokbuf; i++) { c = get_char (cfile); /* Promote NUMBER -> NUMBER_OR_NAME -> NAME, never demote. * Except in the case of '0x' syntax hex, which gets called * a NAME at '0x', and returned to NUMBER_OR_NAME once it's * verified to be at least 0xf or less. */ switch(isascii(c) ? token : BREAK) { case NUMBER: if(isdigit(c)) break; /* FALLTHROUGH */ case NUMBER_OR_NAME: if(isxdigit(c)) { token = NUMBER_OR_NAME; break; } /* FALLTHROUGH */ case NAME: if((i == 2) && isxdigit(c) && (cfile->tokbuf[0] == '0') && ((cfile->tokbuf[1] == 'x') || (cfile->tokbuf[1] == 'X'))) { token = NUMBER_OR_NAME; break; } else if(((c == '-') || (c == '_') || isalnum(c))) { token = NAME; break; } /* FALLTHROUGH */ case BREAK: /* At this point c is either EOF or part of the next * token. If not EOF, rewind the file one byte so * the next token is read from there. */ unget_char(cfile, c); goto end_read; default: log_fatal("read_number():%s:%d: impossible case", MDL); } cfile -> tokbuf [i] = c; } if (i == sizeof cfile -> tokbuf) { parse_warn (cfile, "numeric token larger than internal buffer"); --i; } end_read: cfile -> tokbuf [i] = 0; cfile -> tlen = i; cfile -> tval = cfile -> tokbuf; /* * If this entire token from start to finish was "-", such as * the middle parameter in "42 - 7", return just the MINUS token. */ if ((i == 1) && (cfile->tokbuf[i] == '-')) return MINUS; else return token; } static enum dhcp_token read_num_or_name (c, cfile) int c; struct parse *cfile; { int i = 0; enum dhcp_token rv = NUMBER_OR_NAME; cfile -> tokbuf [i++] = c; for (; i < sizeof cfile -> tokbuf; i++) { c = get_char (cfile); if (!isascii (c) || (c != '-' && c != '_' && !isalnum (c))) { unget_char(cfile, c); break; } if (!isxdigit (c)) rv = NAME; cfile -> tokbuf [i] = c; } if (i == sizeof cfile -> tokbuf) { parse_warn (cfile, "token larger than internal buffer"); --i; } cfile -> tokbuf [i] = 0; cfile -> tlen = i; cfile -> tval = cfile -> tokbuf; return intern(cfile->tval, rv); } static enum dhcp_token intern(char *atom, enum dhcp_token dfv) { if (!isascii(atom[0])) return dfv; switch (tolower((unsigned char)atom[0])) { case '-': if (atom [1] == 0) return MINUS; break; case 'a': if (!strcasecmp(atom + 1, "bandoned")) return TOKEN_ABANDONED; if (!strcasecmp(atom + 1, "ctive")) return TOKEN_ACTIVE; if (!strncasecmp(atom + 1, "dd", 2)) { if (atom[3] == '\0') return TOKEN_ADD; else if (!strcasecmp(atom + 3, "ress")) return ADDRESS; break; } if (!strcasecmp(atom + 1, "fter")) return AFTER; if (isascii(atom[1]) && (tolower((unsigned char)atom[1]) == 'l')) { if (!strcasecmp(atom + 2, "gorithm")) return ALGORITHM; if (!strcasecmp(atom + 2, "ias")) return ALIAS; if (isascii(atom[2]) && (tolower((unsigned char)atom[2]) == 'l')) { if (atom[3] == '\0') return ALL; else if (!strcasecmp(atom + 3, "ow")) return ALLOW; break; } if (!strcasecmp(atom + 2, "so")) return TOKEN_ALSO; break; } if (isascii(atom[1]) && (tolower((unsigned char)atom[1]) == 'n')) { if (!strcasecmp(atom + 2, "d")) return AND; if (!strcasecmp(atom + 2, "ycast-mac")) return ANYCAST_MAC; break; } if (!strcasecmp(atom + 1, "ppend")) return APPEND; if (!strcasecmp(atom + 1, "rray")) return ARRAY; if (isascii(atom[1]) && (tolower((unsigned char)atom[1]) == 't')) { if (atom[2] == '\0') return AT; if (!strcasecmp(atom + 2, "sfp")) return ATSFP; break; } if (!strcasecmp(atom + 1, "uthoring-byte-order")) return AUTHORING_BYTE_ORDER; if (!strncasecmp(atom + 1, "ut", 2)) { if (isascii(atom[3]) && (tolower((unsigned char)atom[3]) == 'h')) { if (!strncasecmp(atom + 4, "enticat", 7)) { if (!strcasecmp(atom + 11, "ed")) return AUTHENTICATED; if (!strcasecmp(atom + 11, "ion")) return AUTHENTICATION; break; } if (!strcasecmp(atom + 4, "oritative")) return AUTHORITATIVE; break; } if (!strcasecmp(atom + 3, "o-partner-down")) return AUTO_PARTNER_DOWN; break; } break; case 'b': if (!strcasecmp (atom + 1, "ackup")) return TOKEN_BACKUP; if (!strcasecmp (atom + 1, "ootp")) return TOKEN_BOOTP; if (!strcasecmp (atom + 1, "inding")) return BINDING; if (!strcasecmp (atom + 1, "inary-to-ascii")) return BINARY_TO_ASCII; if (!strcasecmp (atom + 1, "ackoff-cutoff")) return BACKOFF_CUTOFF; if (!strcasecmp (atom + 1, "ooting")) return BOOTING; if (!strcasecmp (atom + 1, "oot-unknown-clients")) return BOOT_UNKNOWN_CLIENTS; if (!strcasecmp (atom + 1, "reak")) return BREAK; if (!strcasecmp (atom + 1, "illing")) return BILLING; if (!strcasecmp (atom + 1, "oolean")) return BOOLEAN; if (!strcasecmp (atom + 1, "alance")) return BALANCE; if (!strcasecmp (atom + 1, "ound")) return BOUND; if (!strcasecmp(atom+1, "ig-endian")) { return TOKEN_BIG_ENDIAN; } break; case 'c': if (!strcasecmp(atom + 1, "ase")) return CASE; if (!strcasecmp(atom + 1, "heck")) return CHECK; if (!strcasecmp(atom + 1, "iaddr")) return CIADDR; if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 'l') { if (!strcasecmp(atom + 2, "ass")) return CLASS; if (!strncasecmp(atom + 2, "ient", 4)) { if (!strcasecmp(atom + 6, "s")) return CLIENTS; if (atom[6] == '-') { if (!strcasecmp(atom + 7, "hostname")) return CLIENT_HOSTNAME; if (!strcasecmp(atom + 7, "identifier")) return CLIENT_IDENTIFIER; if (!strcasecmp(atom + 7, "state")) return CLIENT_STATE; if (!strcasecmp(atom + 7, "updates")) return CLIENT_UPDATES; break; } break; } if (!strcasecmp(atom + 2, "ose")) return TOKEN_CLOSE; if (!strcasecmp(atom + 2, "tt")) return CLTT; break; } if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 'o') { if (!strcasecmp(atom + 2, "de")) return CODE; if (isascii(atom[2]) && tolower((unsigned char)atom[2]) == 'm') { if (!strcasecmp(atom + 3, "mit")) return COMMIT; if (!strcasecmp(atom + 3, "munications-interrupted")) return COMMUNICATIONS_INTERRUPTED; if (!strcasecmp(atom + 3, "pressed")) return COMPRESSED; break; } if (isascii(atom[2]) && tolower((unsigned char)atom[2]) == 'n') { if (!strcasecmp(atom + 3, "cat")) return CONCAT; if (!strcasecmp(atom + 3, "fig-option")) return CONFIG_OPTION; if (!strcasecmp(atom + 3, "flict-done")) return CONFLICT_DONE; if (!strcasecmp(atom + 3, "nect")) return CONNECT; break; } break; } if (!strcasecmp(atom + 1, "reate")) return TOKEN_CREATE; break; case 'd': if (!strcasecmp(atom + 1, "b-time-format")) return DB_TIME_FORMAT; if (!strcasecmp (atom + 1, "omain")) return DOMAIN; if (!strncasecmp (atom + 1, "omain-", 6)) { if (!strcasecmp(atom + 7, "name")) return DOMAIN_NAME; if (!strcasecmp(atom + 7, "list")) return DOMAIN_LIST; } if (!strcasecmp (atom + 1, "o-forward-updates")) return DO_FORWARD_UPDATE; /* do-forward-update is included for historical reasons */ if (!strcasecmp (atom + 1, "o-forward-update")) return DO_FORWARD_UPDATE; if (!strcasecmp (atom + 1, "ebug")) return TOKEN_DEBUG; if (!strcasecmp (atom + 1, "eny")) return DENY; if (!strcasecmp (atom + 1, "eleted")) return TOKEN_DELETED; if (!strcasecmp (atom + 1, "elete")) return TOKEN_DELETE; if (!strncasecmp (atom + 1, "efault", 6)) { if (!atom [7]) return DEFAULT; if (!strcasecmp(atom + 7, "-duid")) return DEFAULT_DUID; if (!strcasecmp (atom + 7, "-lease-time")) return DEFAULT_LEASE_TIME; break; } if (!strncasecmp (atom + 1, "ynamic", 6)) { if (!atom [7]) return DYNAMIC; if (!strncasecmp (atom + 7, "-bootp", 6)) { if (!atom [13]) return DYNAMIC_BOOTP; if (!strcasecmp (atom + 13, "-lease-cutoff")) return DYNAMIC_BOOTP_LEASE_CUTOFF; if (!strcasecmp (atom + 13, "-lease-length")) return DYNAMIC_BOOTP_LEASE_LENGTH; break; } } if (!strcasecmp (atom + 1, "uplicates")) return DUPLICATES; if (!strcasecmp (atom + 1, "eclines")) return DECLINES; if (!strncasecmp (atom + 1, "efine", 5)) { if (!strcasecmp (atom + 6, "d")) return DEFINED; if (!atom [6]) return DEFINE; } if (!strcasecmp (atom + 1, "isconnect")) return DISCONNECT; break; case 'e': if (isascii (atom [1]) && tolower((unsigned char)atom[1]) == 'x') { if (!strcasecmp (atom + 2, "tract-int")) return EXTRACT_INT; if (!strcasecmp (atom + 2, "ists")) return EXISTS; if (!strcasecmp (atom + 2, "piry")) return EXPIRY; if (!strcasecmp (atom + 2, "pire")) return EXPIRE; if (!strcasecmp (atom + 2, "pired")) return TOKEN_EXPIRED; } if (!strcasecmp (atom + 1, "ncode-int")) return ENCODE_INT; if (!strcasecmp(atom + 1, "poch")) return EPOCH; if (!strcasecmp (atom + 1, "thernet")) return ETHERNET; if (!strcasecmp (atom + 1, "nds")) return ENDS; if (!strncasecmp (atom + 1, "ls", 2)) { if (!strcasecmp (atom + 3, "e")) return ELSE; if (!strcasecmp (atom + 3, "if")) return ELSIF; break; } if (!strcasecmp (atom + 1, "rror")) return ERROR; if (!strcasecmp (atom + 1, "val")) return EVAL; if (!strcasecmp (atom + 1, "ncapsulate")) return ENCAPSULATE; if (!strcasecmp(atom + 1, "xecute")) return EXECUTE; if (!strcasecmp(atom+1, "n")) { return EN; } break; case 'f': if (!strcasecmp (atom + 1, "atal")) return FATAL; if (!strcasecmp (atom + 1, "ilename")) return FILENAME; if (!strcasecmp (atom + 1, "ixed-address")) return FIXED_ADDR; if (!strcasecmp (atom + 1, "ixed-address6")) return FIXED_ADDR6; if (!strcasecmp (atom + 1, "ixed-prefix6")) return FIXED_PREFIX6; if (!strcasecmp (atom + 1, "ddi")) return TOKEN_FDDI; if (!strcasecmp (atom + 1, "ormerr")) return NS_FORMERR; if (!strcasecmp (atom + 1, "unction")) return FUNCTION; if (!strcasecmp (atom + 1, "ailover")) return FAILOVER; if (!strcasecmp (atom + 1, "ree")) return TOKEN_FREE; break; case 'g': if (!strncasecmp(atom + 1, "et", 2)) { if (!strcasecmp(atom + 3, "-lease-hostnames")) return GET_LEASE_HOSTNAMES; if (!strcasecmp(atom + 3, "hostbyname")) return GETHOSTBYNAME; if (!strcasecmp(atom + 3, "hostname")) return GETHOSTNAME; break; } if (!strcasecmp (atom + 1, "iaddr")) return GIADDR; if (!strcasecmp (atom + 1, "roup")) return GROUP; break; case 'h': if (!strcasecmp(atom + 1, "ash")) return HASH; if (!strcasecmp (atom + 1, "ba")) return HBA; if (!strcasecmp (atom + 1, "ost")) return HOST; if (!strcasecmp (atom + 1, "ost-decl-name")) return HOST_DECL_NAME; if (!strcasecmp(atom + 1, "ost-identifier")) return HOST_IDENTIFIER; if (!strcasecmp (atom + 1, "ardware")) return HARDWARE; if (!strcasecmp (atom + 1, "ostname")) return HOSTNAME; if (!strcasecmp (atom + 1, "elp")) return TOKEN_HELP; if (!strcasecmp (atom + 1, "ex")) { return TOKEN_HEX; } break; case 'i': if (!strcasecmp(atom+1, "a-na")) return IA_NA; if (!strcasecmp(atom+1, "a-ta")) return IA_TA; if (!strcasecmp(atom+1, "a-pd")) return IA_PD; if (!strcasecmp(atom+1, "aaddr")) return IAADDR; if (!strcasecmp(atom+1, "aprefix")) return IAPREFIX; if (!strcasecmp (atom + 1, "nclude")) return INCLUDE; if (!strcasecmp (atom + 1, "nteger")) return INTEGER; if (!strcasecmp (atom + 1, "nfiniband")) return TOKEN_INFINIBAND; if (!strcasecmp (atom + 1, "nfinite")) return INFINITE; if (!strcasecmp (atom + 1, "nfo")) return INFO; if (!strcasecmp (atom + 1, "p-address")) return IP_ADDRESS; if (!strcasecmp (atom + 1, "p6-address")) return IP6_ADDRESS; if (!strcasecmp (atom + 1, "nitial-interval")) return INITIAL_INTERVAL; if (!strcasecmp (atom + 1, "nitial-delay")) return INITIAL_DELAY; if (!strcasecmp (atom + 1, "nterface")) return INTERFACE; if (!strcasecmp (atom + 1, "dentifier")) return IDENTIFIER; if (!strcasecmp (atom + 1, "f")) return IF; if (!strcasecmp (atom + 1, "s")) return IS; if (!strcasecmp (atom + 1, "gnore")) return IGNORE; break; case 'k': if (!strncasecmp (atom + 1, "nown", 4)) { if (!strcasecmp (atom + 5, "-clients")) return KNOWN_CLIENTS; if (!atom[5]) return KNOWN; break; } if (!strcasecmp (atom + 1, "ey")) return KEY; if (!strcasecmp (atom + 1, "ey-algorithm")) return KEY_ALGORITHM; break; case 'l': if (!strcasecmp (atom + 1, "case")) return LCASE; if (!strcasecmp (atom + 1, "ease")) return LEASE; if (!strcasecmp(atom + 1, "ease6")) return LEASE6; if (!strcasecmp (atom + 1, "eased-address")) return LEASED_ADDRESS; if (!strcasecmp (atom + 1, "ease-time")) return LEASE_TIME; if (!strcasecmp(atom + 1, "easequery")) return LEASEQUERY; if (!strcasecmp(atom + 1, "ength")) return LENGTH; if (!strcasecmp (atom + 1, "imit")) return LIMIT; if (!strcasecmp (atom + 1, "et")) return LET; if (!strcasecmp (atom + 1, "oad")) return LOAD; if (!strcasecmp(atom + 1, "ocal")) return LOCAL; if (!strcasecmp (atom + 1, "og")) return LOG; if (!strcasecmp(atom+1, "lt")) { return LLT; } if (!strcasecmp(atom+1, "l")) { return LL; } if (!strcasecmp(atom+1, "ittle-endian")) { return TOKEN_LITTLE_ENDIAN; } if (!strcasecmp (atom + 1, "ease-id-format")) { return LEASE_ID_FORMAT; } break; case 'm': if (!strncasecmp (atom + 1, "ax", 2)) { if (!atom [3]) return TOKEN_MAX; if (!strcasecmp (atom + 3, "-balance")) return MAX_BALANCE; if (!strncasecmp (atom + 3, "-lease-", 7)) { if (!strcasecmp(atom + 10, "misbalance")) return MAX_LEASE_MISBALANCE; if (!strcasecmp(atom + 10, "ownership")) return MAX_LEASE_OWNERSHIP; if (!strcasecmp(atom + 10, "time")) return MAX_LEASE_TIME; } if (!strcasecmp(atom + 3, "-life")) return MAX_LIFE; if (!strcasecmp (atom + 3, "-transmit-idle")) return MAX_TRANSMIT_IDLE; if (!strcasecmp (atom + 3, "-response-delay")) return MAX_RESPONSE_DELAY; if (!strcasecmp (atom + 3, "-unacked-updates")) return MAX_UNACKED_UPDATES; } if (!strncasecmp (atom + 1, "in-", 3)) { if (!strcasecmp (atom + 4, "balance")) return MIN_BALANCE; if (!strcasecmp (atom + 4, "lease-time")) return MIN_LEASE_TIME; if (!strcasecmp (atom + 4, "secs")) return MIN_SECS; break; } if (!strncasecmp (atom + 1, "edi", 3)) { if (!strcasecmp (atom + 4, "a")) return MEDIA; if (!strcasecmp (atom + 4, "um")) return MEDIUM; break; } if (!strcasecmp (atom + 1, "atch")) return MATCH; if (!strcasecmp (atom + 1, "embers")) return MEMBERS; if (!strcasecmp (atom + 1, "y")) return MY; if (!strcasecmp (atom + 1, "clt")) return MCLT; break; case 'n': if (!strcasecmp (atom + 1, "ormal")) return NORMAL; if (!strcasecmp (atom + 1, "ameserver")) return NAMESERVER; if (!strcasecmp (atom + 1, "etmask")) return NETMASK; if (!strcasecmp (atom + 1, "ever")) return NEVER; if (!strcasecmp (atom + 1, "ext-server")) return NEXT_SERVER; if (!strcasecmp (atom + 1, "ot")) return TOKEN_NOT; if (!strcasecmp (atom + 1, "o")) return TOKEN_NO; if (!strcasecmp (atom + 1, "oerror")) return NS_NOERROR; if (!strcasecmp (atom + 1, "otauth")) return NS_NOTAUTH; if (!strcasecmp (atom + 1, "otimp")) return NS_NOTIMP; if (!strcasecmp (atom + 1, "otzone")) return NS_NOTZONE; if (!strcasecmp (atom + 1, "xdomain")) return NS_NXDOMAIN; if (!strcasecmp (atom + 1, "xrrset")) return NS_NXRRSET; if (!strcasecmp (atom + 1, "ull")) return TOKEN_NULL; if (!strcasecmp (atom + 1, "ext")) return TOKEN_NEXT; if (!strcasecmp (atom + 1, "ew")) return TOKEN_NEW; break; case 'o': if (!strcasecmp (atom + 1, "mapi")) return OMAPI; if (!strcasecmp (atom + 1, "r")) return OR; if (!strcasecmp (atom + 1, "n")) return ON; if (!strcasecmp (atom + 1, "pen")) return TOKEN_OPEN; if (!strcasecmp (atom + 1, "ption")) return OPTION; if (!strcasecmp (atom + 1, "ne-lease-per-client")) return ONE_LEASE_PER_CLIENT; if (!strcasecmp (atom + 1, "f")) return OF; if (!strcasecmp (atom + 1, "wner")) return OWNER; if (!strcasecmp (atom + 1, "ctal")) { return TOKEN_OCTAL; } break; case 'p': if (!strcasecmp (atom + 1, "arse-vendor-option")) return PARSE_VENDOR_OPT; if (!strcasecmp (atom + 1, "repend")) return PREPEND; if (!strcasecmp(atom + 1, "referred-life")) return PREFERRED_LIFE; if (!strcasecmp (atom + 1, "acket")) return PACKET; if (!strcasecmp (atom + 1, "ool")) return POOL; if (!strcasecmp (atom + 1, "ool6")) return POOL6; if (!strcasecmp (atom + 1, "refix6")) return PREFIX6; if (!strcasecmp (atom + 1, "seudo")) return PSEUDO; if (!strcasecmp (atom + 1, "eer")) return PEER; if (!strcasecmp (atom + 1, "rimary")) return PRIMARY; if (!strcasecmp (atom + 1, "rimary6")) return PRIMARY6; if (!strncasecmp (atom + 1, "artner", 6)) { if (!atom [7]) return PARTNER; if (!strcasecmp (atom + 7, "-down")) return PARTNER_DOWN; } if (!strcasecmp (atom + 1, "ort")) return PORT; if (!strcasecmp (atom + 1, "otential-conflict")) return POTENTIAL_CONFLICT; if (!strcasecmp (atom + 1, "ick-first-value") || !strcasecmp (atom + 1, "ick")) return PICK; if (!strcasecmp (atom + 1, "aused")) return PAUSED; break; case 'r': if (!strcasecmp(atom + 1, "ange")) return RANGE; if (!strcasecmp(atom + 1, "ange6")) return RANGE6; if (isascii(atom[1]) && (tolower((unsigned char)atom[1]) == 'e')) { if (!strcasecmp(atom + 2, "bind")) return REBIND; if (!strcasecmp(atom + 2, "boot")) return REBOOT; if (!strcasecmp(atom + 2, "contact-interval")) return RECONTACT_INTERVAL; if (!strncasecmp(atom + 2, "cover", 5)) { if (atom[7] == '\0') return RECOVER; if (!strcasecmp(atom + 7, "-done")) return RECOVER_DONE; if (!strcasecmp(atom + 7, "-wait")) return RECOVER_WAIT; break; } if (!strcasecmp(atom + 2, "fresh")) return REFRESH; if (!strcasecmp(atom + 2, "fused")) return NS_REFUSED; if (!strcasecmp(atom + 2, "ject")) return REJECT; if (!strcasecmp(atom + 2, "lease")) return RELEASE; if (!strcasecmp(atom + 2, "leased")) return TOKEN_RELEASED; if (!strcasecmp(atom + 2, "move")) return REMOVE; if (!strcasecmp(atom + 2, "new")) return RENEW; if (!strcasecmp(atom + 2, "quest")) return REQUEST; if (!strcasecmp(atom + 2, "quire")) return REQUIRE; if (isascii(atom[2]) && (tolower((unsigned char)atom[2]) == 's')) { if (!strcasecmp(atom + 3, "erved")) return TOKEN_RESERVED; if (!strcasecmp(atom + 3, "et")) return TOKEN_RESET; if (!strcasecmp(atom + 3, "olution-interrupted")) return RESOLUTION_INTERRUPTED; break; } if (!strcasecmp(atom + 2, "try")) return RETRY; if (!strcasecmp(atom + 2, "turn")) return RETURN; if (!strcasecmp(atom + 2, "verse")) return REVERSE; if (!strcasecmp(atom + 2, "wind")) return REWIND; break; } break; case 's': if (!strcasecmp(atom + 1, "cript")) return SCRIPT; if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 'e') { if (!strcasecmp(atom + 2, "arch")) return SEARCH; if (isascii(atom[2]) && tolower((unsigned char)atom[2]) == 'c') { if (!strncasecmp(atom + 3, "ond", 3)) { if (!strcasecmp(atom + 6, "ary")) return SECONDARY; if (!strcasecmp(atom + 6, "ary6")) return SECONDARY6; if (!strcasecmp(atom + 6, "s")) return SECONDS; break; } if (!strcasecmp(atom + 3, "ret")) return SECRET; break; } if (!strncasecmp(atom + 2, "lect", 4)) { if (atom[6] == '\0') return SELECT; if (!strcasecmp(atom + 6, "-timeout")) return SELECT_TIMEOUT; break; } if (!strcasecmp(atom + 2, "nd")) return SEND; if (!strncasecmp(atom + 2, "rv", 2)) { if (!strncasecmp(atom + 4, "er", 2)) { if (atom[6] == '\0') return TOKEN_SERVER; if (atom[6] == '-') { if (!strcasecmp(atom + 7, "duid")) return SERVER_DUID; if (!strcasecmp(atom + 7, "name")) return SERVER_NAME; if (!strcasecmp(atom + 7, "identifier")) return SERVER_IDENTIFIER; break; } break; } if (!strcasecmp(atom + 4, "fail")) return NS_SERVFAIL; break; } if (!strcasecmp(atom + 2, "t")) return TOKEN_SET; break; } if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 'h') { if (!strcasecmp(atom + 2, "ared-network")) return SHARED_NETWORK; if (!strcasecmp(atom + 2, "utdown")) return SHUTDOWN; break; } if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 'i') { if (!strcasecmp(atom + 2, "addr")) return SIADDR; if (!strcasecmp(atom + 2, "gned")) return SIGNED; if (!strcasecmp(atom + 2, "ze")) return SIZE; break; } if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 'p') { if (isascii(atom[2]) && tolower((unsigned char)atom[2]) == 'a') { if (!strcasecmp(atom + 3, "ce")) return SPACE; if (!strcasecmp(atom + 3, "wn")) return SPAWN; break; } if (!strcasecmp(atom + 2, "lit")) return SPLIT; break; } if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 't') { if (isascii(atom[2]) && tolower((unsigned char)atom[2]) == 'a') { if(!strncasecmp(atom + 3, "rt", 2)) { if (!strcasecmp(atom + 5, "s")) return STARTS; if (!strcasecmp(atom + 5, "up")) return STARTUP; break; } if (isascii(atom[3]) && tolower((unsigned char)atom[3]) == 't') { if (!strcasecmp(atom + 4, "e")) return STATE; if (!strcasecmp(atom + 4, "ic")) return STATIC; break; } } if (!strcasecmp(atom + 2, "ring")) return STRING_TOKEN; break; } if (!strncasecmp(atom + 1, "ub", 2)) { if (!strcasecmp(atom + 3, "class")) return SUBCLASS; if (!strcasecmp(atom + 3, "net")) return SUBNET; if (!strcasecmp(atom + 3, "net6")) return SUBNET6; if (!strcasecmp(atom + 3, "string")) return SUBSTRING; break; } if (isascii(atom[1]) && tolower((unsigned char)atom[1]) == 'u') { if (!strcasecmp(atom + 2, "ffix")) return SUFFIX; if (!strcasecmp(atom + 2, "persede")) return SUPERSEDE; } if (!strcasecmp(atom + 1, "witch")) return SWITCH; break; case 't': if (!strcasecmp (atom + 1, "imestamp")) return TIMESTAMP; if (!strcasecmp (atom + 1, "imeout")) return TIMEOUT; if (!strcasecmp (atom + 1, "oken-ring")) return TOKEN_RING; if (!strcasecmp (atom + 1, "ext")) return TEXT; if (!strcasecmp (atom + 1, "stp")) return TSTP; if (!strcasecmp (atom + 1, "sfp")) return TSFP; if (!strcasecmp (atom + 1, "ransmission")) return TRANSMISSION; if (!strcasecmp(atom + 1, "emporary")) return TEMPORARY; break; case 'u': if (!strcasecmp (atom + 1, "case")) return UCASE; if (!strcasecmp (atom + 1, "nset")) return UNSET; if (!strcasecmp (atom + 1, "nsigned")) return UNSIGNED; if (!strcasecmp (atom + 1, "id")) return UID; if (!strncasecmp (atom + 1, "se", 2)) { if (!strcasecmp (atom + 3, "r-class")) return USER_CLASS; if (!strcasecmp (atom + 3, "-host-decl-names")) return USE_HOST_DECL_NAMES; if (!strcasecmp (atom + 3, "-lease-addr-for-default-route")) return USE_LEASE_ADDR_FOR_DEFAULT_ROUTE; break; } if (!strncasecmp (atom + 1, "nknown", 6)) { if (!strcasecmp (atom + 7, "-clients")) return UNKNOWN_CLIENTS; if (!strcasecmp (atom + 7, "-state")) return UNKNOWN_STATE; if (!atom [7]) return UNKNOWN; break; } if (!strcasecmp (atom + 1, "nauthenticated")) return UNAUTHENTICATED; if (!strcasecmp (atom + 1, "pdate")) return UPDATE; break; case 'v': if (!strcasecmp (atom + 1, "6relay")) return V6RELAY; if (!strcasecmp (atom + 1, "6relopt")) return V6RELOPT; if (!strcasecmp (atom + 1, "endor-class")) return VENDOR_CLASS; if (!strcasecmp (atom + 1, "endor")) return VENDOR; break; case 'w': if (!strcasecmp (atom + 1, "ith")) return WITH; if (!strcasecmp(atom + 1, "idth")) return WIDTH; break; case 'y': if (!strcasecmp (atom + 1, "iaddr")) return YIADDR; if (!strcasecmp (atom + 1, "xdomain")) return NS_YXDOMAIN; if (!strcasecmp (atom + 1, "xrrset")) return NS_YXRRSET; break; case 'z': if (!strcasecmp (atom + 1, "erolen")) return ZEROLEN; if (!strcasecmp (atom + 1, "one")) return ZONE; break; } return dfv; }