/* $OpenBSD: conflex.c,v 1.48 2017/11/06 13:27:19 krw Exp $ */ /* Lexical scanner for dhclient config file. */ /* * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of The Internet Software Consortium nor the names * of its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This software has been written for the Internet Software Consortium * by Ted Lemon in cooperation with Vixie * Enterprises. To learn more about the Internet Software Consortium, * see ``http://www.vix.com/isc''. To learn more about Vixie * Enterprises, see ``http://www.vix.com''. */ #include #include #include #include #include #include #include #include #include #include #include #include "dhcp.h" #include "dhcpd.h" #include "dhctoken.h" #include "log.h" int lexline; int lexchar; char *token_line; char *tlname; static char line1[81]; static char line2[81]; static char *prev_line; static char *cur_line; static int lpos; static int line; static int tlpos; static int tline; static int token; static int ugflag; static char *tval; static char tokbuf[1500]; static void eol(void); static void skip_to_eol(FILE *); static int get_char(FILE *); static int get_token(FILE *); static int read_string(FILE *); static int read_num_or_name(int, FILE *); static int intern(char *, int); void new_parse(char *name) { /* * Initialize all parsing state, as we are starting to parse a * new file, 'name'. */ memset(line1, 0, sizeof(line1)); memset(line2, 0, sizeof(line2)); memset(tokbuf, 0, sizeof(tokbuf)); lpos = line = 1; tlpos = tline = token = ugflag = 0; tval = NULL; lexline = lexchar = 0; cur_line = line1; prev_line = line2; token_line = cur_line; tlname = name; } /* * eol() increments the lexical line. * * It is split from get_char() because read_num_or_name() does *not* * want the lexical line incremented when a '\n' ends the token assembly. * Instead, it ungetc()'s the '\n' for the next token parse to deal with. * Incrementing the lexical line in that case causes parse_warn() to * generate messages that display a blank line instead of the offending * token in context. * * Invoccations of get_char() wanting to increment the lexical line on '\n' * must call eol(). */ static void eol(void) { if (cur_line == line1) { cur_line = line2; prev_line = line1; } else { cur_line = line1; prev_line = line2; } line++; lpos = 1; cur_line[0] = 0; } static int get_char(FILE *cfile) { int c; c = getc(cfile); if (ugflag == 0) { if (c != EOF && c != '\n') { if ((unsigned int)lpos < sizeof(line1)) { cur_line[lpos - 1] = c; cur_line[lpos] = 0; } lpos++; } } else ugflag = 0; return c; } static int get_token(FILE *cfile) { static char tb[2]; int c, ttok; int l, p, u; u = ugflag; for (;;) { l = line; p = lpos - u; u = 0; c = get_char(cfile); if (isascii(c) != 0 && isspace(c) != 0) { if (c == '\n') eol(); continue; } if (c == '#') { skip_to_eol(cfile); continue; } lexline = l; lexchar = p; if (c == '"') { ttok = read_string(cfile); break; } else if (c == '-' || (isascii(c) != 0 && isalnum(c) != 0)) { ttok = read_num_or_name(c, cfile); break; } else { tb[0] = c; tb[1] = 0; tval = tb; ttok = c; break; } } return ttok; } int next_token(char **rval, FILE *cfile) { int rv; if (token != 0) { if (lexline != tline) token_line = cur_line; lexchar = tlpos; lexline = tline; rv = token; token = 0; } else { rv = get_token(cfile); token_line = cur_line; } if (rval != 0) *rval = tval; return rv; } int peek_token(char **rval, FILE *cfile) { int x; if (token == 0) { tlpos = lexchar; tline = lexline; token = get_token(cfile); if (lexline != tline) token_line = prev_line; x = lexchar; lexchar = tlpos; tlpos = x; x = lexline; lexline = tline; tline = x; } if (rval != 0) *rval = tval; return token; } static void skip_to_eol(FILE *cfile) { int c; for (;;) { c = get_char(cfile); if (c == EOF) return; if (c == '\n') { eol(); return; } } } static int read_string(FILE *cfile) { int i, c, bs; /* * Read in characters until an un-escaped '"' is encountered. */ bs = i = 0; while ((c = get_char(cfile)) != EOF) { if (c == '"' && bs == 0) break; if (c == '\n') eol(); tokbuf[i++] = c; if (bs != 0) bs = 0; else if (c == '\\') bs = 1; if (i == sizeof(tokbuf) - 1) break; } if (bs == 1) i--; if (c == EOF) parse_warn("eof in string constant"); else if (c != '"') parse_warn("string constant too long"); tokbuf[i] = '\0'; tval = tokbuf; return TOK_STRING; } static int read_num_or_name(int c, FILE *cfile) { unsigned int i, xdigits; int rv; xdigits = (isxdigit(c) != 0) ? 1 : 0; tokbuf[0] = c; for (i = 1; i < sizeof(tokbuf); i++) { c = get_char(cfile); if (isascii(c) == 0 || (c != '-' && c != '_' && isalnum(c) == 0)) { /* N.B.: Do not call eol()! '\n' is put back. */ ungetc(c, cfile); ugflag = 1; break; } if (isxdigit(c) != 0) xdigits++; tokbuf[i] = c; } if (i == sizeof(tokbuf)) { parse_warn("token larger than internal buffer"); i--; c = tokbuf[i]; if (isxdigit(c) != 0) xdigits--; } tokbuf[i] = 0; tval = tokbuf; c = (unsigned int)tokbuf[0]; if (c == '-') rv = TOK_NUMBER; else rv = intern(tval, TOK_NUMBER_OR_NAME); if (rv == TOK_NUMBER_OR_NAME && xdigits != i) rv = TOK_NAME; return rv; } static const struct keywords { const char *k_name; int k_val; } keywords[] = { { "append", TOK_APPEND }, { "backoff-cutoff", TOK_BACKOFF_CUTOFF }, { "bootp", TOK_BOOTP }, { "default", TOK_DEFAULT }, { "epoch", TOK_EPOCH }, { "expire", TOK_EXPIRE }, { "filename", TOK_FILENAME }, { "fixed-address", TOK_FIXED_ADDR }, { "ignore", TOK_IGNORE }, { "initial-interval", TOK_INITIAL_INTERVAL }, { "interface", TOK_INTERFACE }, { "lease", TOK_LEASE }, { "link-timeout", TOK_LINK_TIMEOUT }, { "next-server", TOK_NEXT_SERVER }, { "option", TOK_OPTION }, { "prepend", TOK_PREPEND }, { "rebind", TOK_REBIND }, { "reboot", TOK_REBOOT }, { "reject", TOK_REJECT }, { "renew", TOK_RENEW }, { "request", TOK_REQUEST }, { "require", TOK_REQUIRE }, { "retry", TOK_RETRY }, { "select-timeout", TOK_SELECT_TIMEOUT }, { "send", TOK_SEND }, { "server-name", TOK_SERVER_NAME }, { "ssid", TOK_SSID }, { "supersede", TOK_SUPERSEDE }, { "timeout", TOK_TIMEOUT } }; int kw_cmp(const void *k, const void *e); int kw_cmp(const void *k, const void *e) { return strcasecmp(k, ((const struct keywords *)e)->k_name); } static int intern(char *atom, int dfv) { const struct keywords *p; p = bsearch(atom, keywords, sizeof(keywords)/sizeof(keywords[0]), sizeof(keywords[0]), kw_cmp); if (p != NULL) return p->k_val; return dfv; }