1 /* $OpenBSD: conflex.c,v 1.9 2006/12/17 18:03:33 stevesk 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 <ctype.h> 44 45 #include "dhcpd.h" 46 #include "dhctoken.h" 47 48 int lexline; 49 int lexchar; 50 char *token_line; 51 char *prev_line; 52 char *cur_line; 53 char *tlname; 54 int eol_token; 55 56 static char line1[81]; 57 static char line2[81]; 58 static int lpos; 59 static int line; 60 static int tlpos; 61 static int tline; 62 static int token; 63 static int ugflag; 64 static char *tval; 65 static char tokbuf[1500]; 66 67 static int get_char(FILE *); 68 static int get_token(FILE *); 69 static void skip_to_eol(FILE *); 70 static int read_string(FILE *); 71 static int read_number(int, FILE *); 72 static int read_num_or_name(int, FILE *); 73 static int intern(char *, int); 74 75 void 76 new_parse(char *name) 77 { 78 tlname = name; 79 lpos = line = 1; 80 cur_line = line1; 81 prev_line = line2; 82 token_line = cur_line; 83 cur_line[0] = prev_line[0] = 0; 84 warnings_occurred = 0; 85 } 86 87 static int 88 get_char(FILE *cfile) 89 { 90 int c = getc(cfile); 91 if (!ugflag) { 92 if (c == '\n') { 93 if (cur_line == line1) { 94 cur_line = line2; 95 prev_line = line1; 96 } else { 97 cur_line = line2; 98 prev_line = line1; 99 } 100 line++; 101 lpos = 1; 102 cur_line[0] = 0; 103 } else if (c != EOF) { 104 if (lpos < sizeof(line1)) { 105 cur_line[lpos - 1] = c; 106 cur_line[lpos] = 0; 107 } 108 lpos++; 109 } 110 } else 111 ugflag = 0; 112 return (c); 113 } 114 115 static int 116 get_token(FILE *cfile) 117 { 118 int c, ttok; 119 static char tb[2]; 120 int l, p; 121 122 do { 123 l = line; 124 p = lpos; 125 126 c = get_char(cfile); 127 128 if (!(c == '\n' && eol_token) && isascii(c) && isspace(c)) 129 continue; 130 if (c == '#') { 131 skip_to_eol(cfile); 132 continue; 133 } 134 if (c == '"') { 135 lexline = l; 136 lexchar = p; 137 ttok = read_string(cfile); 138 break; 139 } 140 if ((isascii(c) && isdigit(c)) || c == '-') { 141 lexline = l; 142 lexchar = p; 143 ttok = read_number(c, cfile); 144 break; 145 } else if (isascii(c) && isalpha(c)) { 146 lexline = l; 147 lexchar = p; 148 ttok = read_num_or_name(c, cfile); 149 break; 150 } else { 151 lexline = l; 152 lexchar = p; 153 tb[0] = c; 154 tb[1] = 0; 155 tval = tb; 156 ttok = c; 157 break; 158 } 159 } while (1); 160 return (ttok); 161 } 162 163 int 164 next_token(char **rval, FILE *cfile) 165 { 166 int rv; 167 168 if (token) { 169 if (lexline != tline) 170 token_line = cur_line; 171 lexchar = tlpos; 172 lexline = tline; 173 rv = token; 174 token = 0; 175 } else { 176 rv = get_token(cfile); 177 token_line = cur_line; 178 } 179 if (rval) 180 *rval = tval; 181 182 return (rv); 183 } 184 185 int 186 peek_token(char **rval, FILE *cfile) 187 { 188 int x; 189 190 if (!token) { 191 tlpos = lexchar; 192 tline = lexline; 193 token = get_token(cfile); 194 if (lexline != tline) 195 token_line = prev_line; 196 x = lexchar; 197 lexchar = tlpos; 198 tlpos = x; 199 x = lexline; 200 lexline = tline; 201 tline = x; 202 } 203 if (rval) 204 *rval = tval; 205 206 return (token); 207 } 208 209 static void 210 skip_to_eol(FILE *cfile) 211 { 212 int c; 213 214 do { 215 c = get_char(cfile); 216 if (c == EOF) 217 return; 218 if (c == '\n') 219 return; 220 } while (1); 221 } 222 223 static int 224 read_string(FILE *cfile) 225 { 226 int i, c, bs = 0; 227 228 for (i = 0; i < sizeof(tokbuf); i++) { 229 c = get_char(cfile); 230 if (c == EOF) { 231 parse_warn("eof in string constant"); 232 break; 233 } 234 if (bs) { 235 bs = 0; 236 tokbuf[i] = c; 237 } else if (c == '\\') 238 bs = 1; 239 else if (c == '"') 240 break; 241 else 242 tokbuf[i] = c; 243 } 244 /* 245 * Normally, I'd feel guilty about this, but we're talking about 246 * strings that'll fit in a DHCP packet here... 247 */ 248 if (i == sizeof(tokbuf)) { 249 parse_warn("string constant larger than internal buffer"); 250 i--; 251 } 252 tokbuf[i] = 0; 253 tval = tokbuf; 254 return (TOK_STRING); 255 } 256 257 static int 258 read_number(int c, FILE *cfile) 259 { 260 int seenx = 0, i = 0, token = TOK_NUMBER; 261 262 tokbuf[i++] = c; 263 for (; i < sizeof(tokbuf); i++) { 264 c = get_char(cfile); 265 if (!seenx && c == 'x') 266 seenx = 1; 267 else if (!isascii(c) || !isxdigit(c)) { 268 ungetc(c, cfile); 269 ugflag = 1; 270 break; 271 } 272 tokbuf[i] = c; 273 } 274 if (i == sizeof(tokbuf)) { 275 parse_warn("numeric token larger than internal buffer"); 276 i--; 277 } 278 tokbuf[i] = 0; 279 tval = tokbuf; 280 281 return (token); 282 } 283 284 static int 285 read_num_or_name(int c, FILE *cfile) 286 { 287 int i = 0; 288 int rv = TOK_NUMBER_OR_NAME; 289 290 tokbuf[i++] = c; 291 for (; i < sizeof(tokbuf); i++) { 292 c = get_char(cfile); 293 if (!isascii(c) || (c != '-' && c != '_' && !isalnum(c))) { 294 ungetc(c, cfile); 295 ugflag = 1; 296 break; 297 } 298 if (!isxdigit(c)) 299 rv = TOK_NAME; 300 tokbuf[i] = c; 301 } 302 if (i == sizeof(tokbuf)) { 303 parse_warn("token larger than internal buffer"); 304 i--; 305 } 306 tokbuf[i] = 0; 307 tval = tokbuf; 308 309 return (intern(tval, rv)); 310 } 311 312 static const struct keywords { 313 const char *k_name; 314 int k_val; 315 } keywords[] = { 316 { "abandoned", TOK_ABANDONED }, 317 { "allow", TOK_ALLOW }, 318 { "always-reply-rfc1048", TOK_ALWAYS_REPLY_RFC1048 }, 319 { "authoritative", TOK_AUTHORITATIVE }, 320 { "booting", TOK_BOOTING }, 321 { "bootp", TOK_BOOTP }, 322 { "class", TOK_CLASS }, 323 { "client-hostname", TOK_CLIENT_HOSTNAME }, 324 { "default-lease-time", TOK_DEFAULT_LEASE_TIME }, 325 { "deny", TOK_DENY }, 326 { "domain", TOK_DOMAIN }, 327 { "dynamic-bootp", TOK_DYNAMIC_BOOTP }, 328 { "dynamic-bootp-lease-cutoff", TOK_DYNAMIC_BOOTP_LEASE_CUTOFF }, 329 { "dynamic-bootp-lease-length", TOK_DYNAMIC_BOOTP_LEASE_LENGTH }, 330 { "ends", TOK_ENDS }, 331 { "ethernet", TOK_ETHERNET }, 332 { "fddi", TOK_FDDI }, 333 { "filename", TOK_FILENAME }, 334 { "fixed-address", TOK_FIXED_ADDR }, 335 { "get-lease-hostnames", TOK_GET_LEASE_HOSTNAMES }, 336 { "group", TOK_GROUP }, 337 { "hardware", TOK_HARDWARE }, 338 { "host", TOK_HOST }, 339 { "hostname", TOK_HOSTNAME }, 340 { "lease", TOK_LEASE }, 341 { "max-lease-time", TOK_MAX_LEASE_TIME }, 342 { "netmask", TOK_NETMASK }, 343 { "next-server", TOK_NEXT_SERVER }, 344 { "not", TOK_TOKEN_NOT }, 345 { "option", TOK_OPTION }, 346 { "range", TOK_RANGE }, 347 { "server-identifier", TOK_SERVER_IDENTIFIER }, 348 { "server-name", TOK_SERVER_NAME }, 349 { "shared-network", TOK_SHARED_NETWORK }, 350 { "starts", TOK_STARTS }, 351 { "subnet", TOK_SUBNET }, 352 { "timeout", TOK_TIMEOUT }, 353 { "timestamp", TOK_TIMESTAMP }, 354 { "token-ring", TOK_TOKEN_RING }, 355 { "uid", TOK_UID }, 356 { "unknown-clients", TOK_UNKNOWN_CLIENTS }, 357 { "use-host-decl-names", TOK_USE_HOST_DECL_NAMES }, 358 { "use-lease-addr-for-default-route", TOK_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE }, 359 { "user-class", TOK_USER_CLASS }, 360 { "vendor-class", TOK_VENDOR_CLASS } 361 }; 362 363 int 364 kw_cmp(const void *k, const void *e) 365 { 366 return (strcasecmp(k, ((const struct keywords *)e)->k_name)); 367 } 368 369 static int 370 intern(char *atom, int dfv) 371 { 372 const struct keywords *p; 373 374 p = bsearch(atom, keywords, sizeof(keywords)/sizeof(keywords[0]), 375 sizeof(keywords[0]), kw_cmp); 376 if (p) 377 return (p->k_val); 378 return (dfv); 379 } 380