1147072Sbrooks/* $OpenBSD: clparse.c,v 1.18 2004/09/15 18:15:18 henning Exp $ */ 2147072Sbrooks 3147072Sbrooks/* Parser for dhclient config and lease files... */ 4147072Sbrooks 5331722Seadler/* 6147072Sbrooks * Copyright (c) 1997 The Internet Software Consortium. 7147072Sbrooks * All rights reserved. 8147072Sbrooks * 9147072Sbrooks * Redistribution and use in source and binary forms, with or without 10147072Sbrooks * modification, are permitted provided that the following conditions 11147072Sbrooks * are met: 12147072Sbrooks * 13147072Sbrooks * 1. Redistributions of source code must retain the above copyright 14147072Sbrooks * notice, this list of conditions and the following disclaimer. 15147072Sbrooks * 2. Redistributions in binary form must reproduce the above copyright 16147072Sbrooks * notice, this list of conditions and the following disclaimer in the 17147072Sbrooks * documentation and/or other materials provided with the distribution. 18147072Sbrooks * 3. Neither the name of The Internet Software Consortium nor the names 19147072Sbrooks * of its contributors may be used to endorse or promote products derived 20147072Sbrooks * from this software without specific prior written permission. 21147072Sbrooks * 22147072Sbrooks * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 23147072Sbrooks * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 24147072Sbrooks * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 25147072Sbrooks * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26147072Sbrooks * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 27147072Sbrooks * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28147072Sbrooks * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29147072Sbrooks * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 30147072Sbrooks * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 31147072Sbrooks * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32147072Sbrooks * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 33147072Sbrooks * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34147072Sbrooks * SUCH DAMAGE. 35147072Sbrooks * 36147072Sbrooks * This software has been written for the Internet Software Consortium 37147072Sbrooks * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 38147072Sbrooks * Enterprises. To learn more about the Internet Software Consortium, 39147072Sbrooks * see ``http://www.vix.com/isc''. To learn more about Vixie 40147072Sbrooks * Enterprises, see ``http://www.vix.com''. 41147072Sbrooks */ 42147072Sbrooks 43149399Sbrooks#include <sys/cdefs.h> 44149399Sbrooks__FBSDID("$FreeBSD: stable/11/sbin/dhclient/clparse.c 332602 2018-04-16 16:23:32Z asomers $"); 45149399Sbrooks 46147072Sbrooks#include "dhcpd.h" 47147072Sbrooks#include "dhctoken.h" 48147072Sbrooks 49147072Sbrooksstruct client_config top_level_config; 50147072Sbrooksstruct interface_info *dummy_interfaces; 51147072Sbrooksextern struct interface_info *ifi; 52147072Sbrooks 53147072Sbrookschar client_script_name[] = "/sbin/dhclient-script"; 54147072Sbrooks 55147072Sbrooks/* 56147072Sbrooks * client-conf-file :== client-declarations EOF 57147072Sbrooks * client-declarations :== <nil> 58147072Sbrooks * | client-declaration 59147072Sbrooks * | client-declarations client-declaration 60147072Sbrooks */ 61147072Sbrooksint 62147072Sbrooksread_client_conf(void) 63147072Sbrooks{ 64147072Sbrooks FILE *cfile; 65147072Sbrooks char *val; 66147072Sbrooks int token; 67147072Sbrooks struct client_config *config; 68147072Sbrooks 69147072Sbrooks new_parse(path_dhclient_conf); 70147072Sbrooks 71147072Sbrooks /* Set up the initial dhcp option universe. */ 72147072Sbrooks initialize_universes(); 73147072Sbrooks 74147072Sbrooks /* Initialize the top level client configuration. */ 75147072Sbrooks memset(&top_level_config, 0, sizeof(top_level_config)); 76147072Sbrooks 77147072Sbrooks /* Set some defaults... */ 78147072Sbrooks top_level_config.timeout = 60; 79147072Sbrooks top_level_config.select_interval = 0; 80147072Sbrooks top_level_config.reboot_timeout = 10; 81147072Sbrooks top_level_config.retry_interval = 300; 82147072Sbrooks top_level_config.backoff_cutoff = 15; 83147072Sbrooks top_level_config.initial_interval = 3; 84147072Sbrooks top_level_config.bootp_policy = ACCEPT; 85147072Sbrooks top_level_config.script_name = client_script_name; 86147072Sbrooks top_level_config.requested_options 87147072Sbrooks [top_level_config.requested_option_count++] = DHO_SUBNET_MASK; 88147072Sbrooks top_level_config.requested_options 89147072Sbrooks [top_level_config.requested_option_count++] = DHO_BROADCAST_ADDRESS; 90147072Sbrooks top_level_config.requested_options 91147072Sbrooks [top_level_config.requested_option_count++] = DHO_TIME_OFFSET; 92147072Sbrooks top_level_config.requested_options 93166602Semaste [top_level_config.requested_option_count++] = DHO_CLASSLESS_ROUTES; 94166602Semaste top_level_config.requested_options 95147072Sbrooks [top_level_config.requested_option_count++] = DHO_ROUTERS; 96147072Sbrooks top_level_config.requested_options 97147072Sbrooks [top_level_config.requested_option_count++] = DHO_DOMAIN_NAME; 98147072Sbrooks top_level_config.requested_options 99147072Sbrooks [top_level_config.requested_option_count++] = 100147072Sbrooks DHO_DOMAIN_NAME_SERVERS; 101147072Sbrooks top_level_config.requested_options 102147072Sbrooks [top_level_config.requested_option_count++] = DHO_HOST_NAME; 103228259Sdumbbell top_level_config.requested_options 104228259Sdumbbell [top_level_config.requested_option_count++] = DHO_DOMAIN_SEARCH; 105331179Seadler top_level_config.requested_options 106331179Seadler [top_level_config.requested_option_count++] = DHO_INTERFACE_MTU; 107147072Sbrooks 108147072Sbrooks if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) { 109147072Sbrooks do { 110147072Sbrooks token = peek_token(&val, cfile); 111147072Sbrooks if (token == EOF) 112147072Sbrooks break; 113147072Sbrooks parse_client_statement(cfile, NULL, &top_level_config); 114147072Sbrooks } while (1); 115147072Sbrooks token = next_token(&val, cfile); /* Clear the peek buffer */ 116147072Sbrooks fclose(cfile); 117147072Sbrooks } 118147072Sbrooks 119147072Sbrooks /* 120147072Sbrooks * Set up state and config structures for clients that don't 121147072Sbrooks * have per-interface configuration declarations. 122147072Sbrooks */ 123147072Sbrooks config = NULL; 124147072Sbrooks if (!ifi->client) { 125147072Sbrooks ifi->client = malloc(sizeof(struct client_state)); 126147072Sbrooks if (!ifi->client) 127147072Sbrooks error("no memory for client state."); 128147072Sbrooks memset(ifi->client, 0, sizeof(*(ifi->client))); 129147072Sbrooks } 130147072Sbrooks if (!ifi->client->config) { 131147072Sbrooks if (!config) { 132147072Sbrooks config = malloc(sizeof(struct client_config)); 133147072Sbrooks if (!config) 134147072Sbrooks error("no memory for client config."); 135147072Sbrooks memcpy(config, &top_level_config, 136147072Sbrooks sizeof(top_level_config)); 137147072Sbrooks } 138147072Sbrooks ifi->client->config = config; 139147072Sbrooks } 140147072Sbrooks 141147072Sbrooks return (!warnings_occurred); 142147072Sbrooks} 143147072Sbrooks 144147072Sbrooks/* 145147072Sbrooks * lease-file :== client-lease-statements EOF 146147072Sbrooks * client-lease-statements :== <nil> 147147072Sbrooks * | client-lease-statements LEASE client-lease-statement 148147072Sbrooks */ 149147072Sbrooksvoid 150147072Sbrooksread_client_leases(void) 151147072Sbrooks{ 152147072Sbrooks FILE *cfile; 153147072Sbrooks char *val; 154147072Sbrooks int token; 155147072Sbrooks 156147072Sbrooks new_parse(path_dhclient_db); 157147072Sbrooks 158147072Sbrooks /* Open the lease file. If we can't open it, just return - 159147072Sbrooks we can safely trust the server to remember our state. */ 160147072Sbrooks if ((cfile = fopen(path_dhclient_db, "r")) == NULL) 161147072Sbrooks return; 162147072Sbrooks do { 163147072Sbrooks token = next_token(&val, cfile); 164147072Sbrooks if (token == EOF) 165147072Sbrooks break; 166147072Sbrooks if (token != LEASE) { 167147072Sbrooks warning("Corrupt lease file - possible data loss!"); 168147072Sbrooks skip_to_semi(cfile); 169147072Sbrooks break; 170147072Sbrooks } else 171147072Sbrooks parse_client_lease_statement(cfile, 0); 172147072Sbrooks 173147072Sbrooks } while (1); 174147072Sbrooks fclose(cfile); 175147072Sbrooks} 176147072Sbrooks 177147072Sbrooks/* 178147072Sbrooks * client-declaration :== 179147072Sbrooks * SEND option-decl | 180147072Sbrooks * DEFAULT option-decl | 181147072Sbrooks * SUPERSEDE option-decl | 182147072Sbrooks * PREPEND option-decl | 183147072Sbrooks * APPEND option-decl | 184147072Sbrooks * hardware-declaration | 185147072Sbrooks * REQUEST option-list | 186147072Sbrooks * REQUIRE option-list | 187147072Sbrooks * TIMEOUT number | 188147072Sbrooks * RETRY number | 189147072Sbrooks * REBOOT number | 190147072Sbrooks * SELECT_TIMEOUT number | 191147072Sbrooks * SCRIPT string | 192147072Sbrooks * interface-declaration | 193147072Sbrooks * LEASE client-lease-statement | 194147072Sbrooks * ALIAS client-lease-statement 195147072Sbrooks */ 196147072Sbrooksvoid 197147072Sbrooksparse_client_statement(FILE *cfile, struct interface_info *ip, 198147072Sbrooks struct client_config *config) 199147072Sbrooks{ 200147072Sbrooks int token; 201147072Sbrooks char *val; 202147072Sbrooks struct option *option; 203147072Sbrooks 204147072Sbrooks switch (next_token(&val, cfile)) { 205147072Sbrooks case SEND: 206147072Sbrooks parse_option_decl(cfile, &config->send_options[0]); 207147072Sbrooks return; 208147072Sbrooks case DEFAULT: 209147072Sbrooks option = parse_option_decl(cfile, &config->defaults[0]); 210147072Sbrooks if (option) 211147072Sbrooks config->default_actions[option->code] = ACTION_DEFAULT; 212147072Sbrooks return; 213147072Sbrooks case SUPERSEDE: 214147072Sbrooks option = parse_option_decl(cfile, &config->defaults[0]); 215147072Sbrooks if (option) 216147072Sbrooks config->default_actions[option->code] = 217147072Sbrooks ACTION_SUPERSEDE; 218147072Sbrooks return; 219147072Sbrooks case APPEND: 220147072Sbrooks option = parse_option_decl(cfile, &config->defaults[0]); 221147072Sbrooks if (option) 222147072Sbrooks config->default_actions[option->code] = ACTION_APPEND; 223147072Sbrooks return; 224147072Sbrooks case PREPEND: 225147072Sbrooks option = parse_option_decl(cfile, &config->defaults[0]); 226147072Sbrooks if (option) 227147072Sbrooks config->default_actions[option->code] = ACTION_PREPEND; 228147072Sbrooks return; 229147072Sbrooks case MEDIA: 230147072Sbrooks parse_string_list(cfile, &config->media, 1); 231147072Sbrooks return; 232147072Sbrooks case HARDWARE: 233147072Sbrooks if (ip) 234147072Sbrooks parse_hardware_param(cfile, &ip->hw_address); 235147072Sbrooks else { 236147072Sbrooks parse_warn("hardware address parameter %s", 237147072Sbrooks "not allowed here."); 238147072Sbrooks skip_to_semi(cfile); 239147072Sbrooks } 240147072Sbrooks return; 241147072Sbrooks case REQUEST: 242147072Sbrooks config->requested_option_count = 243147072Sbrooks parse_option_list(cfile, config->requested_options); 244147072Sbrooks return; 245147072Sbrooks case REQUIRE: 246147072Sbrooks memset(config->required_options, 0, 247147072Sbrooks sizeof(config->required_options)); 248147072Sbrooks parse_option_list(cfile, config->required_options); 249147072Sbrooks return; 250147072Sbrooks case TIMEOUT: 251147072Sbrooks parse_lease_time(cfile, &config->timeout); 252147072Sbrooks return; 253147072Sbrooks case RETRY: 254147072Sbrooks parse_lease_time(cfile, &config->retry_interval); 255147072Sbrooks return; 256147072Sbrooks case SELECT_TIMEOUT: 257147072Sbrooks parse_lease_time(cfile, &config->select_interval); 258147072Sbrooks return; 259147072Sbrooks case REBOOT: 260147072Sbrooks parse_lease_time(cfile, &config->reboot_timeout); 261147072Sbrooks return; 262147072Sbrooks case BACKOFF_CUTOFF: 263147072Sbrooks parse_lease_time(cfile, &config->backoff_cutoff); 264147072Sbrooks return; 265147072Sbrooks case INITIAL_INTERVAL: 266147072Sbrooks parse_lease_time(cfile, &config->initial_interval); 267147072Sbrooks return; 268147072Sbrooks case SCRIPT: 269147072Sbrooks config->script_name = parse_string(cfile); 270147072Sbrooks return; 271147072Sbrooks case INTERFACE: 272147072Sbrooks if (ip) 273147072Sbrooks parse_warn("nested interface declaration."); 274147072Sbrooks parse_interface_declaration(cfile, config); 275147072Sbrooks return; 276147072Sbrooks case LEASE: 277147072Sbrooks parse_client_lease_statement(cfile, 1); 278147072Sbrooks return; 279147072Sbrooks case ALIAS: 280147072Sbrooks parse_client_lease_statement(cfile, 2); 281147072Sbrooks return; 282147072Sbrooks case REJECT: 283147072Sbrooks parse_reject_statement(cfile, config); 284147072Sbrooks return; 285147072Sbrooks default: 286147072Sbrooks parse_warn("expecting a statement."); 287147072Sbrooks skip_to_semi(cfile); 288147072Sbrooks break; 289147072Sbrooks } 290147072Sbrooks token = next_token(&val, cfile); 291147072Sbrooks if (token != SEMI) { 292147072Sbrooks parse_warn("semicolon expected."); 293147072Sbrooks skip_to_semi(cfile); 294147072Sbrooks } 295147072Sbrooks} 296147072Sbrooks 297327854Sasomersunsigned 298327854Sasomersparse_X(FILE *cfile, u_int8_t *buf, unsigned max) 299147072Sbrooks{ 300147072Sbrooks int token; 301147072Sbrooks char *val; 302327854Sasomers unsigned len; 303147072Sbrooks 304147072Sbrooks token = peek_token(&val, cfile); 305147072Sbrooks if (token == NUMBER_OR_NAME || token == NUMBER) { 306147072Sbrooks len = 0; 307147072Sbrooks do { 308147072Sbrooks token = next_token(&val, cfile); 309147072Sbrooks if (token != NUMBER && token != NUMBER_OR_NAME) { 310147072Sbrooks parse_warn("expecting hexadecimal constant."); 311147072Sbrooks skip_to_semi(cfile); 312147072Sbrooks return (0); 313147072Sbrooks } 314147072Sbrooks convert_num(&buf[len], val, 16, 8); 315147072Sbrooks if (len++ > max) { 316147072Sbrooks parse_warn("hexadecimal constant too long."); 317147072Sbrooks skip_to_semi(cfile); 318147072Sbrooks return (0); 319147072Sbrooks } 320147072Sbrooks token = peek_token(&val, cfile); 321147072Sbrooks if (token == COLON) 322147072Sbrooks token = next_token(&val, cfile); 323147072Sbrooks } while (token == COLON); 324147072Sbrooks val = (char *)buf; 325147072Sbrooks } else if (token == STRING) { 326147072Sbrooks token = next_token(&val, cfile); 327147072Sbrooks len = strlen(val); 328147072Sbrooks if (len + 1 > max) { 329147072Sbrooks parse_warn("string constant too long."); 330147072Sbrooks skip_to_semi(cfile); 331147072Sbrooks return (0); 332147072Sbrooks } 333147072Sbrooks memcpy(buf, val, len + 1); 334147072Sbrooks } else { 335147072Sbrooks parse_warn("expecting string or hexadecimal data"); 336147072Sbrooks skip_to_semi(cfile); 337147072Sbrooks return (0); 338147072Sbrooks } 339147072Sbrooks return (len); 340147072Sbrooks} 341147072Sbrooks 342147072Sbrooks/* 343147072Sbrooks * option-list :== option_name | 344147072Sbrooks * option_list COMMA option_name 345147072Sbrooks */ 346147072Sbrooksint 347147072Sbrooksparse_option_list(FILE *cfile, u_int8_t *list) 348147072Sbrooks{ 349147072Sbrooks int ix, i; 350147072Sbrooks int token; 351147072Sbrooks char *val; 352147072Sbrooks 353147072Sbrooks ix = 0; 354147072Sbrooks do { 355147072Sbrooks token = next_token(&val, cfile); 356147072Sbrooks if (!is_identifier(token)) { 357147072Sbrooks parse_warn("expected option name."); 358147072Sbrooks skip_to_semi(cfile); 359147072Sbrooks return (0); 360147072Sbrooks } 361147072Sbrooks for (i = 0; i < 256; i++) 362147072Sbrooks if (!strcasecmp(dhcp_options[i].name, val)) 363147072Sbrooks break; 364147072Sbrooks 365147072Sbrooks if (i == 256) { 366147072Sbrooks parse_warn("%s: unexpected option name.", val); 367147072Sbrooks skip_to_semi(cfile); 368147072Sbrooks return (0); 369147072Sbrooks } 370147072Sbrooks list[ix++] = i; 371147072Sbrooks if (ix == 256) { 372147072Sbrooks parse_warn("%s: too many options.", val); 373147072Sbrooks skip_to_semi(cfile); 374147072Sbrooks return (0); 375147072Sbrooks } 376147072Sbrooks token = next_token(&val, cfile); 377147072Sbrooks } while (token == COMMA); 378147072Sbrooks if (token != SEMI) { 379147072Sbrooks parse_warn("expecting semicolon."); 380147072Sbrooks skip_to_semi(cfile); 381147072Sbrooks return (0); 382147072Sbrooks } 383147072Sbrooks return (ix); 384147072Sbrooks} 385147072Sbrooks 386147072Sbrooks/* 387147072Sbrooks * interface-declaration :== 388147072Sbrooks * INTERFACE string LBRACE client-declarations RBRACE 389147072Sbrooks */ 390147072Sbrooksvoid 391147072Sbrooksparse_interface_declaration(FILE *cfile, struct client_config *outer_config) 392147072Sbrooks{ 393147072Sbrooks int token; 394147072Sbrooks char *val; 395147072Sbrooks struct interface_info *ip; 396147072Sbrooks 397147072Sbrooks token = next_token(&val, cfile); 398147072Sbrooks if (token != STRING) { 399147072Sbrooks parse_warn("expecting interface name (in quotes)."); 400147072Sbrooks skip_to_semi(cfile); 401147072Sbrooks return; 402147072Sbrooks } 403147072Sbrooks 404147072Sbrooks ip = interface_or_dummy(val); 405147072Sbrooks 406147072Sbrooks if (!ip->client) 407147072Sbrooks make_client_state(ip); 408147072Sbrooks 409147072Sbrooks if (!ip->client->config) 410147072Sbrooks make_client_config(ip, outer_config); 411147072Sbrooks 412147072Sbrooks token = next_token(&val, cfile); 413147072Sbrooks if (token != LBRACE) { 414147072Sbrooks parse_warn("expecting left brace."); 415147072Sbrooks skip_to_semi(cfile); 416147072Sbrooks return; 417147072Sbrooks } 418147072Sbrooks 419147072Sbrooks do { 420147072Sbrooks token = peek_token(&val, cfile); 421147072Sbrooks if (token == EOF) { 422147072Sbrooks parse_warn("unterminated interface declaration."); 423147072Sbrooks return; 424147072Sbrooks } 425147072Sbrooks if (token == RBRACE) 426147072Sbrooks break; 427147072Sbrooks parse_client_statement(cfile, ip, ip->client->config); 428147072Sbrooks } while (1); 429147072Sbrooks token = next_token(&val, cfile); 430147072Sbrooks} 431147072Sbrooks 432147072Sbrooksstruct interface_info * 433147072Sbrooksinterface_or_dummy(char *name) 434147072Sbrooks{ 435147072Sbrooks struct interface_info *ip; 436147072Sbrooks 437147072Sbrooks /* Find the interface (if any) that matches the name. */ 438147072Sbrooks if (!strcmp(ifi->name, name)) 439147072Sbrooks return (ifi); 440147072Sbrooks 441147072Sbrooks /* If it's not a real interface, see if it's on the dummy list. */ 442147072Sbrooks for (ip = dummy_interfaces; ip; ip = ip->next) 443147072Sbrooks if (!strcmp(ip->name, name)) 444147072Sbrooks return (ip); 445147072Sbrooks 446147072Sbrooks /* 447147072Sbrooks * If we didn't find an interface, make a dummy interface as a 448147072Sbrooks * placeholder. 449147072Sbrooks */ 450147072Sbrooks ip = malloc(sizeof(*ip)); 451147072Sbrooks if (!ip) 452147072Sbrooks error("Insufficient memory to record interface %s", name); 453147072Sbrooks memset(ip, 0, sizeof(*ip)); 454147072Sbrooks strlcpy(ip->name, name, IFNAMSIZ); 455147072Sbrooks ip->next = dummy_interfaces; 456147072Sbrooks dummy_interfaces = ip; 457147072Sbrooks return (ip); 458147072Sbrooks} 459147072Sbrooks 460147072Sbrooksvoid 461147072Sbrooksmake_client_state(struct interface_info *ip) 462147072Sbrooks{ 463147072Sbrooks ip->client = malloc(sizeof(*(ip->client))); 464147072Sbrooks if (!ip->client) 465147072Sbrooks error("no memory for state on %s", ip->name); 466147072Sbrooks memset(ip->client, 0, sizeof(*(ip->client))); 467147072Sbrooks} 468147072Sbrooks 469147072Sbrooksvoid 470147072Sbrooksmake_client_config(struct interface_info *ip, struct client_config *config) 471147072Sbrooks{ 472147072Sbrooks ip->client->config = malloc(sizeof(struct client_config)); 473147072Sbrooks if (!ip->client->config) 474147072Sbrooks error("no memory for config for %s", ip->name); 475147072Sbrooks memset(ip->client->config, 0, sizeof(*(ip->client->config))); 476147072Sbrooks memcpy(ip->client->config, config, sizeof(*config)); 477147072Sbrooks} 478147072Sbrooks 479147072Sbrooks/* 480147072Sbrooks * client-lease-statement :== 481147072Sbrooks * RBRACE client-lease-declarations LBRACE 482147072Sbrooks * 483147072Sbrooks * client-lease-declarations :== 484147072Sbrooks * <nil> | 485147072Sbrooks * client-lease-declaration | 486147072Sbrooks * client-lease-declarations client-lease-declaration 487147072Sbrooks */ 488147072Sbrooksvoid 489147072Sbrooksparse_client_lease_statement(FILE *cfile, int is_static) 490147072Sbrooks{ 491147072Sbrooks struct client_lease *lease, *lp, *pl; 492147072Sbrooks struct interface_info *ip; 493147072Sbrooks int token; 494147072Sbrooks char *val; 495147072Sbrooks 496147072Sbrooks token = next_token(&val, cfile); 497147072Sbrooks if (token != LBRACE) { 498147072Sbrooks parse_warn("expecting left brace."); 499147072Sbrooks skip_to_semi(cfile); 500147072Sbrooks return; 501147072Sbrooks } 502147072Sbrooks 503147072Sbrooks lease = malloc(sizeof(struct client_lease)); 504147072Sbrooks if (!lease) 505147072Sbrooks error("no memory for lease."); 506147072Sbrooks memset(lease, 0, sizeof(*lease)); 507147072Sbrooks lease->is_static = is_static; 508147072Sbrooks 509147072Sbrooks ip = NULL; 510147072Sbrooks 511147072Sbrooks do { 512147072Sbrooks token = peek_token(&val, cfile); 513147072Sbrooks if (token == EOF) { 514147072Sbrooks parse_warn("unterminated lease declaration."); 515315609Sngie free_client_lease(lease); 516147072Sbrooks return; 517147072Sbrooks } 518147072Sbrooks if (token == RBRACE) 519147072Sbrooks break; 520147072Sbrooks parse_client_lease_declaration(cfile, lease, &ip); 521147072Sbrooks } while (1); 522147072Sbrooks token = next_token(&val, cfile); 523147072Sbrooks 524147072Sbrooks /* If the lease declaration didn't include an interface 525147072Sbrooks * declaration that we recognized, it's of no use to us. 526147072Sbrooks */ 527147072Sbrooks if (!ip) { 528147072Sbrooks free_client_lease(lease); 529147072Sbrooks return; 530147072Sbrooks } 531147072Sbrooks 532147072Sbrooks /* Make sure there's a client state structure... */ 533147072Sbrooks if (!ip->client) 534147072Sbrooks make_client_state(ip); 535147072Sbrooks 536147072Sbrooks /* If this is an alias lease, it doesn't need to be sorted in. */ 537147072Sbrooks if (is_static == 2) { 538147072Sbrooks ip->client->alias = lease; 539147072Sbrooks return; 540147072Sbrooks } 541147072Sbrooks 542147072Sbrooks /* 543147072Sbrooks * The new lease may supersede a lease that's not the active 544147072Sbrooks * lease but is still on the lease list, so scan the lease list 545147072Sbrooks * looking for a lease with the same address, and if we find it, 546147072Sbrooks * toss it. 547147072Sbrooks */ 548147072Sbrooks pl = NULL; 549147072Sbrooks for (lp = ip->client->leases; lp; lp = lp->next) { 550147072Sbrooks if (lp->address.len == lease->address.len && 551147072Sbrooks !memcmp(lp->address.iabuf, lease->address.iabuf, 552147072Sbrooks lease->address.len)) { 553147072Sbrooks if (pl) 554147072Sbrooks pl->next = lp->next; 555147072Sbrooks else 556147072Sbrooks ip->client->leases = lp->next; 557147072Sbrooks free_client_lease(lp); 558147072Sbrooks break; 559147072Sbrooks } 560147072Sbrooks } 561147072Sbrooks 562147072Sbrooks /* 563147072Sbrooks * If this is a preloaded lease, just put it on the list of 564147072Sbrooks * recorded leases - don't make it the active lease. 565147072Sbrooks */ 566147072Sbrooks if (is_static) { 567147072Sbrooks lease->next = ip->client->leases; 568147072Sbrooks ip->client->leases = lease; 569147072Sbrooks return; 570147072Sbrooks } 571147072Sbrooks 572147072Sbrooks /* 573147072Sbrooks * The last lease in the lease file on a particular interface is 574147072Sbrooks * the active lease for that interface. Of course, we don't 575147072Sbrooks * know what the last lease in the file is until we've parsed 576147072Sbrooks * the whole file, so at this point, we assume that the lease we 577147072Sbrooks * just parsed is the active lease for its interface. If 578147072Sbrooks * there's already an active lease for the interface, and this 579147072Sbrooks * lease is for the same ip address, then we just toss the old 580147072Sbrooks * active lease and replace it with this one. If this lease is 581147072Sbrooks * for a different address, then if the old active lease has 582147072Sbrooks * expired, we dump it; if not, we put it on the list of leases 583147072Sbrooks * for this interface which are still valid but no longer 584147072Sbrooks * active. 585147072Sbrooks */ 586147072Sbrooks if (ip->client->active) { 587147072Sbrooks if (ip->client->active->expiry < cur_time) 588147072Sbrooks free_client_lease(ip->client->active); 589147072Sbrooks else if (ip->client->active->address.len == 590147072Sbrooks lease->address.len && 591147072Sbrooks !memcmp(ip->client->active->address.iabuf, 592147072Sbrooks lease->address.iabuf, lease->address.len)) 593147072Sbrooks free_client_lease(ip->client->active); 594147072Sbrooks else { 595147072Sbrooks ip->client->active->next = ip->client->leases; 596147072Sbrooks ip->client->leases = ip->client->active; 597147072Sbrooks } 598147072Sbrooks } 599147072Sbrooks ip->client->active = lease; 600147072Sbrooks 601147072Sbrooks /* Phew. */ 602147072Sbrooks} 603147072Sbrooks 604147072Sbrooks/* 605147072Sbrooks * client-lease-declaration :== 606147072Sbrooks * BOOTP | 607147072Sbrooks * INTERFACE string | 608147072Sbrooks * FIXED_ADDR ip_address | 609147072Sbrooks * FILENAME string | 610147072Sbrooks * SERVER_NAME string | 611147072Sbrooks * OPTION option-decl | 612147072Sbrooks * RENEW time-decl | 613147072Sbrooks * REBIND time-decl | 614147072Sbrooks * EXPIRE time-decl 615147072Sbrooks */ 616147072Sbrooksvoid 617147072Sbrooksparse_client_lease_declaration(FILE *cfile, struct client_lease *lease, 618147072Sbrooks struct interface_info **ipp) 619147072Sbrooks{ 620147072Sbrooks int token; 621147072Sbrooks char *val; 622147072Sbrooks struct interface_info *ip; 623147072Sbrooks 624147072Sbrooks switch (next_token(&val, cfile)) { 625147072Sbrooks case BOOTP: 626147072Sbrooks lease->is_bootp = 1; 627147072Sbrooks break; 628147072Sbrooks case INTERFACE: 629147072Sbrooks token = next_token(&val, cfile); 630147072Sbrooks if (token != STRING) { 631147072Sbrooks parse_warn("expecting interface name (in quotes)."); 632147072Sbrooks skip_to_semi(cfile); 633147072Sbrooks break; 634147072Sbrooks } 635147072Sbrooks ip = interface_or_dummy(val); 636147072Sbrooks *ipp = ip; 637147072Sbrooks break; 638147072Sbrooks case FIXED_ADDR: 639147072Sbrooks if (!parse_ip_addr(cfile, &lease->address)) 640147072Sbrooks return; 641147072Sbrooks break; 642147072Sbrooks case MEDIUM: 643147072Sbrooks parse_string_list(cfile, &lease->medium, 0); 644147072Sbrooks return; 645147072Sbrooks case FILENAME: 646147072Sbrooks lease->filename = parse_string(cfile); 647147072Sbrooks return; 648252506Sbms case NEXT_SERVER: 649252506Sbms if (!parse_ip_addr(cfile, &lease->nextserver)) 650252506Sbms return; 651252506Sbms break; 652147072Sbrooks case SERVER_NAME: 653147072Sbrooks lease->server_name = parse_string(cfile); 654147072Sbrooks return; 655147072Sbrooks case RENEW: 656147072Sbrooks lease->renewal = parse_date(cfile); 657147072Sbrooks return; 658147072Sbrooks case REBIND: 659147072Sbrooks lease->rebind = parse_date(cfile); 660147072Sbrooks return; 661147072Sbrooks case EXPIRE: 662147072Sbrooks lease->expiry = parse_date(cfile); 663147072Sbrooks return; 664147072Sbrooks case OPTION: 665147072Sbrooks parse_option_decl(cfile, lease->options); 666147072Sbrooks return; 667147072Sbrooks default: 668147072Sbrooks parse_warn("expecting lease declaration."); 669147072Sbrooks skip_to_semi(cfile); 670147072Sbrooks break; 671147072Sbrooks } 672147072Sbrooks token = next_token(&val, cfile); 673147072Sbrooks if (token != SEMI) { 674147072Sbrooks parse_warn("expecting semicolon."); 675147072Sbrooks skip_to_semi(cfile); 676147072Sbrooks } 677147072Sbrooks} 678147072Sbrooks 679147072Sbrooksstruct option * 680147072Sbrooksparse_option_decl(FILE *cfile, struct option_data *options) 681147072Sbrooks{ 682147072Sbrooks char *val; 683147072Sbrooks int token; 684147072Sbrooks u_int8_t buf[4]; 685147072Sbrooks u_int8_t hunkbuf[1024]; 686327854Sasomers unsigned hunkix = 0; 687147072Sbrooks char *vendor; 688332602Sasomers const char *fmt; 689147072Sbrooks struct universe *universe; 690147072Sbrooks struct option *option; 691147072Sbrooks struct iaddr ip_addr; 692147072Sbrooks u_int8_t *dp; 693327854Sasomers unsigned len; 694147072Sbrooks int nul_term = 0; 695147072Sbrooks 696147072Sbrooks token = next_token(&val, cfile); 697147072Sbrooks if (!is_identifier(token)) { 698147072Sbrooks parse_warn("expecting identifier after option keyword."); 699147072Sbrooks if (token != SEMI) 700147072Sbrooks skip_to_semi(cfile); 701147072Sbrooks return (NULL); 702147072Sbrooks } 703147072Sbrooks if ((vendor = strdup(val)) == NULL) 704147072Sbrooks error("no memory for vendor information."); 705147072Sbrooks 706147072Sbrooks token = peek_token(&val, cfile); 707147072Sbrooks if (token == DOT) { 708147072Sbrooks /* Go ahead and take the DOT token... */ 709147072Sbrooks token = next_token(&val, cfile); 710147072Sbrooks 711147072Sbrooks /* The next token should be an identifier... */ 712147072Sbrooks token = next_token(&val, cfile); 713147072Sbrooks if (!is_identifier(token)) { 714147072Sbrooks parse_warn("expecting identifier after '.'"); 715147072Sbrooks if (token != SEMI) 716147072Sbrooks skip_to_semi(cfile); 717315609Sngie free(vendor); 718147072Sbrooks return (NULL); 719147072Sbrooks } 720147072Sbrooks 721147072Sbrooks /* Look up the option name hash table for the specified 722147072Sbrooks vendor. */ 723147072Sbrooks universe = ((struct universe *)hash_lookup(&universe_hash, 724147072Sbrooks (unsigned char *)vendor, 0)); 725147072Sbrooks /* If it's not there, we can't parse the rest of the 726147072Sbrooks declaration. */ 727147072Sbrooks if (!universe) { 728147072Sbrooks parse_warn("no vendor named %s.", vendor); 729147072Sbrooks skip_to_semi(cfile); 730315609Sngie free(vendor); 731147072Sbrooks return (NULL); 732147072Sbrooks } 733147072Sbrooks } else { 734147072Sbrooks /* Use the default hash table, which contains all the 735147072Sbrooks standard dhcp option names. */ 736147072Sbrooks val = vendor; 737147072Sbrooks universe = &dhcp_universe; 738147072Sbrooks } 739147072Sbrooks 740147072Sbrooks /* Look up the actual option info... */ 741147072Sbrooks option = (struct option *)hash_lookup(universe->hash, 742147072Sbrooks (unsigned char *)val, 0); 743147072Sbrooks 744147072Sbrooks /* If we didn't get an option structure, it's an undefined option. */ 745147072Sbrooks if (!option) { 746147072Sbrooks if (val == vendor) 747147072Sbrooks parse_warn("no option named %s", val); 748147072Sbrooks else 749147072Sbrooks parse_warn("no option named %s for vendor %s", 750147072Sbrooks val, vendor); 751147072Sbrooks skip_to_semi(cfile); 752315609Sngie free(vendor); 753147072Sbrooks return (NULL); 754147072Sbrooks } 755147072Sbrooks 756147072Sbrooks /* Free the initial identifier token. */ 757147072Sbrooks free(vendor); 758147072Sbrooks 759147072Sbrooks /* Parse the option data... */ 760147072Sbrooks do { 761147072Sbrooks for (fmt = option->format; *fmt; fmt++) { 762147072Sbrooks if (*fmt == 'A') 763147072Sbrooks break; 764147072Sbrooks switch (*fmt) { 765147072Sbrooks case 'X': 766147072Sbrooks len = parse_X(cfile, &hunkbuf[hunkix], 767147072Sbrooks sizeof(hunkbuf) - hunkix); 768147072Sbrooks hunkix += len; 769147072Sbrooks break; 770147072Sbrooks case 't': /* Text string... */ 771147072Sbrooks token = next_token(&val, cfile); 772147072Sbrooks if (token != STRING) { 773147072Sbrooks parse_warn("expecting string."); 774147072Sbrooks skip_to_semi(cfile); 775147072Sbrooks return (NULL); 776147072Sbrooks } 777147072Sbrooks len = strlen(val); 778147072Sbrooks if (hunkix + len + 1 > sizeof(hunkbuf)) { 779147072Sbrooks parse_warn("option data buffer %s", 780147072Sbrooks "overflow"); 781147072Sbrooks skip_to_semi(cfile); 782147072Sbrooks return (NULL); 783147072Sbrooks } 784147072Sbrooks memcpy(&hunkbuf[hunkix], val, len + 1); 785147072Sbrooks nul_term = 1; 786147072Sbrooks hunkix += len; 787147072Sbrooks break; 788147072Sbrooks case 'I': /* IP address. */ 789147072Sbrooks if (!parse_ip_addr(cfile, &ip_addr)) 790147072Sbrooks return (NULL); 791147072Sbrooks len = ip_addr.len; 792147072Sbrooks dp = ip_addr.iabuf; 793147072Sbrooksalloc: 794147072Sbrooks if (hunkix + len > sizeof(hunkbuf)) { 795147072Sbrooks parse_warn("option data buffer " 796147072Sbrooks "overflow"); 797147072Sbrooks skip_to_semi(cfile); 798147072Sbrooks return (NULL); 799147072Sbrooks } 800147072Sbrooks memcpy(&hunkbuf[hunkix], dp, len); 801147072Sbrooks hunkix += len; 802147072Sbrooks break; 803147072Sbrooks case 'L': /* Unsigned 32-bit integer... */ 804147072Sbrooks case 'l': /* Signed 32-bit integer... */ 805147072Sbrooks token = next_token(&val, cfile); 806147072Sbrooks if (token != NUMBER) { 807147072Sbrooksneed_number: 808147072Sbrooks parse_warn("expecting number."); 809147072Sbrooks if (token != SEMI) 810147072Sbrooks skip_to_semi(cfile); 811147072Sbrooks return (NULL); 812147072Sbrooks } 813147072Sbrooks convert_num(buf, val, 0, 32); 814147072Sbrooks len = 4; 815147072Sbrooks dp = buf; 816147072Sbrooks goto alloc; 817147072Sbrooks case 's': /* Signed 16-bit integer. */ 818147072Sbrooks case 'S': /* Unsigned 16-bit integer. */ 819147072Sbrooks token = next_token(&val, cfile); 820147072Sbrooks if (token != NUMBER) 821147072Sbrooks goto need_number; 822147072Sbrooks convert_num(buf, val, 0, 16); 823147072Sbrooks len = 2; 824147072Sbrooks dp = buf; 825147072Sbrooks goto alloc; 826147072Sbrooks case 'b': /* Signed 8-bit integer. */ 827147072Sbrooks case 'B': /* Unsigned 8-bit integer. */ 828147072Sbrooks token = next_token(&val, cfile); 829147072Sbrooks if (token != NUMBER) 830147072Sbrooks goto need_number; 831147072Sbrooks convert_num(buf, val, 0, 8); 832147072Sbrooks len = 1; 833147072Sbrooks dp = buf; 834147072Sbrooks goto alloc; 835147072Sbrooks case 'f': /* Boolean flag. */ 836147072Sbrooks token = next_token(&val, cfile); 837147072Sbrooks if (!is_identifier(token)) { 838147072Sbrooks parse_warn("expecting identifier."); 839147072Sbrooksbad_flag: 840147072Sbrooks if (token != SEMI) 841147072Sbrooks skip_to_semi(cfile); 842147072Sbrooks return (NULL); 843147072Sbrooks } 844147072Sbrooks if (!strcasecmp(val, "true") || 845147072Sbrooks !strcasecmp(val, "on")) 846147072Sbrooks buf[0] = 1; 847147072Sbrooks else if (!strcasecmp(val, "false") || 848147072Sbrooks !strcasecmp(val, "off")) 849147072Sbrooks buf[0] = 0; 850147072Sbrooks else { 851147072Sbrooks parse_warn("expecting boolean."); 852147072Sbrooks goto bad_flag; 853147072Sbrooks } 854147072Sbrooks len = 1; 855147072Sbrooks dp = buf; 856147072Sbrooks goto alloc; 857147072Sbrooks default: 858147072Sbrooks warning("Bad format %c in parse_option_param.", 859147072Sbrooks *fmt); 860147072Sbrooks skip_to_semi(cfile); 861147072Sbrooks return (NULL); 862147072Sbrooks } 863147072Sbrooks } 864147072Sbrooks token = next_token(&val, cfile); 865147072Sbrooks } while (*fmt == 'A' && token == COMMA); 866147072Sbrooks 867147072Sbrooks if (token != SEMI) { 868147072Sbrooks parse_warn("semicolon expected."); 869147072Sbrooks skip_to_semi(cfile); 870147072Sbrooks return (NULL); 871147072Sbrooks } 872147072Sbrooks 873147072Sbrooks options[option->code].data = malloc(hunkix + nul_term); 874147072Sbrooks if (!options[option->code].data) 875147072Sbrooks error("out of memory allocating option data."); 876147072Sbrooks memcpy(options[option->code].data, hunkbuf, hunkix + nul_term); 877147072Sbrooks options[option->code].len = hunkix; 878147072Sbrooks return (option); 879147072Sbrooks} 880147072Sbrooks 881147072Sbrooksvoid 882147072Sbrooksparse_string_list(FILE *cfile, struct string_list **lp, int multiple) 883147072Sbrooks{ 884147072Sbrooks int token; 885147072Sbrooks char *val; 886228614Sdim size_t valsize; 887147072Sbrooks struct string_list *cur, *tmp; 888147072Sbrooks 889147072Sbrooks /* Find the last medium in the media list. */ 890147072Sbrooks if (*lp) 891147072Sbrooks for (cur = *lp; cur->next; cur = cur->next) 892147072Sbrooks ; /* nothing */ 893147072Sbrooks else 894147072Sbrooks cur = NULL; 895147072Sbrooks 896147072Sbrooks do { 897147072Sbrooks token = next_token(&val, cfile); 898147072Sbrooks if (token != STRING) { 899147072Sbrooks parse_warn("Expecting media options."); 900147072Sbrooks skip_to_semi(cfile); 901147072Sbrooks return; 902147072Sbrooks } 903147072Sbrooks 904228614Sdim valsize = strlen(val) + 1; 905228614Sdim tmp = new_string_list(valsize); 906147072Sbrooks if (tmp == NULL) 907147072Sbrooks error("no memory for string list entry."); 908228615Sdim memcpy(tmp->string, val, valsize); 909147072Sbrooks tmp->next = NULL; 910147072Sbrooks 911147072Sbrooks /* Store this medium at the end of the media list. */ 912147072Sbrooks if (cur) 913147072Sbrooks cur->next = tmp; 914147072Sbrooks else 915147072Sbrooks *lp = tmp; 916147072Sbrooks cur = tmp; 917147072Sbrooks 918147072Sbrooks token = next_token(&val, cfile); 919147072Sbrooks } while (multiple && token == COMMA); 920147072Sbrooks 921147072Sbrooks if (token != SEMI) { 922147072Sbrooks parse_warn("expecting semicolon."); 923147072Sbrooks skip_to_semi(cfile); 924147072Sbrooks } 925147072Sbrooks} 926147072Sbrooks 927147072Sbrooksvoid 928147072Sbrooksparse_reject_statement(FILE *cfile, struct client_config *config) 929147072Sbrooks{ 930147072Sbrooks int token; 931147072Sbrooks char *val; 932147072Sbrooks struct iaddr addr; 933147072Sbrooks struct iaddrlist *list; 934147072Sbrooks 935147072Sbrooks do { 936147072Sbrooks if (!parse_ip_addr(cfile, &addr)) { 937147072Sbrooks parse_warn("expecting IP address."); 938147072Sbrooks skip_to_semi(cfile); 939147072Sbrooks return; 940147072Sbrooks } 941147072Sbrooks 942147072Sbrooks list = malloc(sizeof(struct iaddrlist)); 943147072Sbrooks if (!list) 944147072Sbrooks error("no memory for reject list!"); 945147072Sbrooks 946147072Sbrooks list->addr = addr; 947147072Sbrooks list->next = config->reject_list; 948147072Sbrooks config->reject_list = list; 949147072Sbrooks 950147072Sbrooks token = next_token(&val, cfile); 951147072Sbrooks } while (token == COMMA); 952147072Sbrooks 953147072Sbrooks if (token != SEMI) { 954147072Sbrooks parse_warn("expecting semicolon."); 955147072Sbrooks skip_to_semi(cfile); 956147072Sbrooks } 957147072Sbrooks} 958