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 5147072Sbrooks/* 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/10/sbin/dhclient/clparse.c 315610 2017-03-20 03:06:41Z ngie $"); 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; 105147072Sbrooks 106147072Sbrooks if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) { 107147072Sbrooks do { 108147072Sbrooks token = peek_token(&val, cfile); 109147072Sbrooks if (token == EOF) 110147072Sbrooks break; 111147072Sbrooks parse_client_statement(cfile, NULL, &top_level_config); 112147072Sbrooks } while (1); 113147072Sbrooks token = next_token(&val, cfile); /* Clear the peek buffer */ 114147072Sbrooks fclose(cfile); 115147072Sbrooks } 116147072Sbrooks 117147072Sbrooks /* 118147072Sbrooks * Set up state and config structures for clients that don't 119147072Sbrooks * have per-interface configuration declarations. 120147072Sbrooks */ 121147072Sbrooks config = NULL; 122147072Sbrooks if (!ifi->client) { 123147072Sbrooks ifi->client = malloc(sizeof(struct client_state)); 124147072Sbrooks if (!ifi->client) 125147072Sbrooks error("no memory for client state."); 126147072Sbrooks memset(ifi->client, 0, sizeof(*(ifi->client))); 127147072Sbrooks } 128147072Sbrooks if (!ifi->client->config) { 129147072Sbrooks if (!config) { 130147072Sbrooks config = malloc(sizeof(struct client_config)); 131147072Sbrooks if (!config) 132147072Sbrooks error("no memory for client config."); 133147072Sbrooks memcpy(config, &top_level_config, 134147072Sbrooks sizeof(top_level_config)); 135147072Sbrooks } 136147072Sbrooks ifi->client->config = config; 137147072Sbrooks } 138147072Sbrooks 139147072Sbrooks return (!warnings_occurred); 140147072Sbrooks} 141147072Sbrooks 142147072Sbrooks/* 143147072Sbrooks * lease-file :== client-lease-statements EOF 144147072Sbrooks * client-lease-statements :== <nil> 145147072Sbrooks * | client-lease-statements LEASE client-lease-statement 146147072Sbrooks */ 147147072Sbrooksvoid 148147072Sbrooksread_client_leases(void) 149147072Sbrooks{ 150147072Sbrooks FILE *cfile; 151147072Sbrooks char *val; 152147072Sbrooks int token; 153147072Sbrooks 154147072Sbrooks new_parse(path_dhclient_db); 155147072Sbrooks 156147072Sbrooks /* Open the lease file. If we can't open it, just return - 157147072Sbrooks we can safely trust the server to remember our state. */ 158147072Sbrooks if ((cfile = fopen(path_dhclient_db, "r")) == NULL) 159147072Sbrooks return; 160147072Sbrooks do { 161147072Sbrooks token = next_token(&val, cfile); 162147072Sbrooks if (token == EOF) 163147072Sbrooks break; 164147072Sbrooks if (token != LEASE) { 165147072Sbrooks warning("Corrupt lease file - possible data loss!"); 166147072Sbrooks skip_to_semi(cfile); 167147072Sbrooks break; 168147072Sbrooks } else 169147072Sbrooks parse_client_lease_statement(cfile, 0); 170147072Sbrooks 171147072Sbrooks } while (1); 172147072Sbrooks fclose(cfile); 173147072Sbrooks} 174147072Sbrooks 175147072Sbrooks/* 176147072Sbrooks * client-declaration :== 177147072Sbrooks * SEND option-decl | 178147072Sbrooks * DEFAULT option-decl | 179147072Sbrooks * SUPERSEDE option-decl | 180147072Sbrooks * PREPEND option-decl | 181147072Sbrooks * APPEND option-decl | 182147072Sbrooks * hardware-declaration | 183147072Sbrooks * REQUEST option-list | 184147072Sbrooks * REQUIRE option-list | 185147072Sbrooks * TIMEOUT number | 186147072Sbrooks * RETRY number | 187147072Sbrooks * REBOOT number | 188147072Sbrooks * SELECT_TIMEOUT number | 189147072Sbrooks * SCRIPT string | 190147072Sbrooks * interface-declaration | 191147072Sbrooks * LEASE client-lease-statement | 192147072Sbrooks * ALIAS client-lease-statement 193147072Sbrooks */ 194147072Sbrooksvoid 195147072Sbrooksparse_client_statement(FILE *cfile, struct interface_info *ip, 196147072Sbrooks struct client_config *config) 197147072Sbrooks{ 198147072Sbrooks int token; 199147072Sbrooks char *val; 200147072Sbrooks struct option *option; 201147072Sbrooks 202147072Sbrooks switch (next_token(&val, cfile)) { 203147072Sbrooks case SEND: 204147072Sbrooks parse_option_decl(cfile, &config->send_options[0]); 205147072Sbrooks return; 206147072Sbrooks case DEFAULT: 207147072Sbrooks option = parse_option_decl(cfile, &config->defaults[0]); 208147072Sbrooks if (option) 209147072Sbrooks config->default_actions[option->code] = ACTION_DEFAULT; 210147072Sbrooks return; 211147072Sbrooks case SUPERSEDE: 212147072Sbrooks option = parse_option_decl(cfile, &config->defaults[0]); 213147072Sbrooks if (option) 214147072Sbrooks config->default_actions[option->code] = 215147072Sbrooks ACTION_SUPERSEDE; 216147072Sbrooks return; 217147072Sbrooks case APPEND: 218147072Sbrooks option = parse_option_decl(cfile, &config->defaults[0]); 219147072Sbrooks if (option) 220147072Sbrooks config->default_actions[option->code] = ACTION_APPEND; 221147072Sbrooks return; 222147072Sbrooks case PREPEND: 223147072Sbrooks option = parse_option_decl(cfile, &config->defaults[0]); 224147072Sbrooks if (option) 225147072Sbrooks config->default_actions[option->code] = ACTION_PREPEND; 226147072Sbrooks return; 227147072Sbrooks case MEDIA: 228147072Sbrooks parse_string_list(cfile, &config->media, 1); 229147072Sbrooks return; 230147072Sbrooks case HARDWARE: 231147072Sbrooks if (ip) 232147072Sbrooks parse_hardware_param(cfile, &ip->hw_address); 233147072Sbrooks else { 234147072Sbrooks parse_warn("hardware address parameter %s", 235147072Sbrooks "not allowed here."); 236147072Sbrooks skip_to_semi(cfile); 237147072Sbrooks } 238147072Sbrooks return; 239147072Sbrooks case REQUEST: 240147072Sbrooks config->requested_option_count = 241147072Sbrooks parse_option_list(cfile, config->requested_options); 242147072Sbrooks return; 243147072Sbrooks case REQUIRE: 244147072Sbrooks memset(config->required_options, 0, 245147072Sbrooks sizeof(config->required_options)); 246147072Sbrooks parse_option_list(cfile, config->required_options); 247147072Sbrooks return; 248147072Sbrooks case TIMEOUT: 249147072Sbrooks parse_lease_time(cfile, &config->timeout); 250147072Sbrooks return; 251147072Sbrooks case RETRY: 252147072Sbrooks parse_lease_time(cfile, &config->retry_interval); 253147072Sbrooks return; 254147072Sbrooks case SELECT_TIMEOUT: 255147072Sbrooks parse_lease_time(cfile, &config->select_interval); 256147072Sbrooks return; 257147072Sbrooks case REBOOT: 258147072Sbrooks parse_lease_time(cfile, &config->reboot_timeout); 259147072Sbrooks return; 260147072Sbrooks case BACKOFF_CUTOFF: 261147072Sbrooks parse_lease_time(cfile, &config->backoff_cutoff); 262147072Sbrooks return; 263147072Sbrooks case INITIAL_INTERVAL: 264147072Sbrooks parse_lease_time(cfile, &config->initial_interval); 265147072Sbrooks return; 266147072Sbrooks case SCRIPT: 267147072Sbrooks config->script_name = parse_string(cfile); 268147072Sbrooks return; 269147072Sbrooks case INTERFACE: 270147072Sbrooks if (ip) 271147072Sbrooks parse_warn("nested interface declaration."); 272147072Sbrooks parse_interface_declaration(cfile, config); 273147072Sbrooks return; 274147072Sbrooks case LEASE: 275147072Sbrooks parse_client_lease_statement(cfile, 1); 276147072Sbrooks return; 277147072Sbrooks case ALIAS: 278147072Sbrooks parse_client_lease_statement(cfile, 2); 279147072Sbrooks return; 280147072Sbrooks case REJECT: 281147072Sbrooks parse_reject_statement(cfile, config); 282147072Sbrooks return; 283147072Sbrooks default: 284147072Sbrooks parse_warn("expecting a statement."); 285147072Sbrooks skip_to_semi(cfile); 286147072Sbrooks break; 287147072Sbrooks } 288147072Sbrooks token = next_token(&val, cfile); 289147072Sbrooks if (token != SEMI) { 290147072Sbrooks parse_warn("semicolon expected."); 291147072Sbrooks skip_to_semi(cfile); 292147072Sbrooks } 293147072Sbrooks} 294147072Sbrooks 295147072Sbrooksint 296147072Sbrooksparse_X(FILE *cfile, u_int8_t *buf, int max) 297147072Sbrooks{ 298147072Sbrooks int token; 299147072Sbrooks char *val; 300147072Sbrooks int len; 301147072Sbrooks 302147072Sbrooks token = peek_token(&val, cfile); 303147072Sbrooks if (token == NUMBER_OR_NAME || token == NUMBER) { 304147072Sbrooks len = 0; 305147072Sbrooks do { 306147072Sbrooks token = next_token(&val, cfile); 307147072Sbrooks if (token != NUMBER && token != NUMBER_OR_NAME) { 308147072Sbrooks parse_warn("expecting hexadecimal constant."); 309147072Sbrooks skip_to_semi(cfile); 310147072Sbrooks return (0); 311147072Sbrooks } 312147072Sbrooks convert_num(&buf[len], val, 16, 8); 313147072Sbrooks if (len++ > max) { 314147072Sbrooks parse_warn("hexadecimal constant too long."); 315147072Sbrooks skip_to_semi(cfile); 316147072Sbrooks return (0); 317147072Sbrooks } 318147072Sbrooks token = peek_token(&val, cfile); 319147072Sbrooks if (token == COLON) 320147072Sbrooks token = next_token(&val, cfile); 321147072Sbrooks } while (token == COLON); 322147072Sbrooks val = (char *)buf; 323147072Sbrooks } else if (token == STRING) { 324147072Sbrooks token = next_token(&val, cfile); 325147072Sbrooks len = strlen(val); 326147072Sbrooks if (len + 1 > max) { 327147072Sbrooks parse_warn("string constant too long."); 328147072Sbrooks skip_to_semi(cfile); 329147072Sbrooks return (0); 330147072Sbrooks } 331147072Sbrooks memcpy(buf, val, len + 1); 332147072Sbrooks } else { 333147072Sbrooks parse_warn("expecting string or hexadecimal data"); 334147072Sbrooks skip_to_semi(cfile); 335147072Sbrooks return (0); 336147072Sbrooks } 337147072Sbrooks return (len); 338147072Sbrooks} 339147072Sbrooks 340147072Sbrooks/* 341147072Sbrooks * option-list :== option_name | 342147072Sbrooks * option_list COMMA option_name 343147072Sbrooks */ 344147072Sbrooksint 345147072Sbrooksparse_option_list(FILE *cfile, u_int8_t *list) 346147072Sbrooks{ 347147072Sbrooks int ix, i; 348147072Sbrooks int token; 349147072Sbrooks char *val; 350147072Sbrooks 351147072Sbrooks ix = 0; 352147072Sbrooks do { 353147072Sbrooks token = next_token(&val, cfile); 354147072Sbrooks if (!is_identifier(token)) { 355147072Sbrooks parse_warn("expected option name."); 356147072Sbrooks skip_to_semi(cfile); 357147072Sbrooks return (0); 358147072Sbrooks } 359147072Sbrooks for (i = 0; i < 256; i++) 360147072Sbrooks if (!strcasecmp(dhcp_options[i].name, val)) 361147072Sbrooks break; 362147072Sbrooks 363147072Sbrooks if (i == 256) { 364147072Sbrooks parse_warn("%s: unexpected option name.", val); 365147072Sbrooks skip_to_semi(cfile); 366147072Sbrooks return (0); 367147072Sbrooks } 368147072Sbrooks list[ix++] = i; 369147072Sbrooks if (ix == 256) { 370147072Sbrooks parse_warn("%s: too many options.", val); 371147072Sbrooks skip_to_semi(cfile); 372147072Sbrooks return (0); 373147072Sbrooks } 374147072Sbrooks token = next_token(&val, cfile); 375147072Sbrooks } while (token == COMMA); 376147072Sbrooks if (token != SEMI) { 377147072Sbrooks parse_warn("expecting semicolon."); 378147072Sbrooks skip_to_semi(cfile); 379147072Sbrooks return (0); 380147072Sbrooks } 381147072Sbrooks return (ix); 382147072Sbrooks} 383147072Sbrooks 384147072Sbrooks/* 385147072Sbrooks * interface-declaration :== 386147072Sbrooks * INTERFACE string LBRACE client-declarations RBRACE 387147072Sbrooks */ 388147072Sbrooksvoid 389147072Sbrooksparse_interface_declaration(FILE *cfile, struct client_config *outer_config) 390147072Sbrooks{ 391147072Sbrooks int token; 392147072Sbrooks char *val; 393147072Sbrooks struct interface_info *ip; 394147072Sbrooks 395147072Sbrooks token = next_token(&val, cfile); 396147072Sbrooks if (token != STRING) { 397147072Sbrooks parse_warn("expecting interface name (in quotes)."); 398147072Sbrooks skip_to_semi(cfile); 399147072Sbrooks return; 400147072Sbrooks } 401147072Sbrooks 402147072Sbrooks ip = interface_or_dummy(val); 403147072Sbrooks 404147072Sbrooks if (!ip->client) 405147072Sbrooks make_client_state(ip); 406147072Sbrooks 407147072Sbrooks if (!ip->client->config) 408147072Sbrooks make_client_config(ip, outer_config); 409147072Sbrooks 410147072Sbrooks token = next_token(&val, cfile); 411147072Sbrooks if (token != LBRACE) { 412147072Sbrooks parse_warn("expecting left brace."); 413147072Sbrooks skip_to_semi(cfile); 414147072Sbrooks return; 415147072Sbrooks } 416147072Sbrooks 417147072Sbrooks do { 418147072Sbrooks token = peek_token(&val, cfile); 419147072Sbrooks if (token == EOF) { 420147072Sbrooks parse_warn("unterminated interface declaration."); 421147072Sbrooks return; 422147072Sbrooks } 423147072Sbrooks if (token == RBRACE) 424147072Sbrooks break; 425147072Sbrooks parse_client_statement(cfile, ip, ip->client->config); 426147072Sbrooks } while (1); 427147072Sbrooks token = next_token(&val, cfile); 428147072Sbrooks} 429147072Sbrooks 430147072Sbrooksstruct interface_info * 431147072Sbrooksinterface_or_dummy(char *name) 432147072Sbrooks{ 433147072Sbrooks struct interface_info *ip; 434147072Sbrooks 435147072Sbrooks /* Find the interface (if any) that matches the name. */ 436147072Sbrooks if (!strcmp(ifi->name, name)) 437147072Sbrooks return (ifi); 438147072Sbrooks 439147072Sbrooks /* If it's not a real interface, see if it's on the dummy list. */ 440147072Sbrooks for (ip = dummy_interfaces; ip; ip = ip->next) 441147072Sbrooks if (!strcmp(ip->name, name)) 442147072Sbrooks return (ip); 443147072Sbrooks 444147072Sbrooks /* 445147072Sbrooks * If we didn't find an interface, make a dummy interface as a 446147072Sbrooks * placeholder. 447147072Sbrooks */ 448147072Sbrooks ip = malloc(sizeof(*ip)); 449147072Sbrooks if (!ip) 450147072Sbrooks error("Insufficient memory to record interface %s", name); 451147072Sbrooks memset(ip, 0, sizeof(*ip)); 452147072Sbrooks strlcpy(ip->name, name, IFNAMSIZ); 453147072Sbrooks ip->next = dummy_interfaces; 454147072Sbrooks dummy_interfaces = ip; 455147072Sbrooks return (ip); 456147072Sbrooks} 457147072Sbrooks 458147072Sbrooksvoid 459147072Sbrooksmake_client_state(struct interface_info *ip) 460147072Sbrooks{ 461147072Sbrooks ip->client = malloc(sizeof(*(ip->client))); 462147072Sbrooks if (!ip->client) 463147072Sbrooks error("no memory for state on %s", ip->name); 464147072Sbrooks memset(ip->client, 0, sizeof(*(ip->client))); 465147072Sbrooks} 466147072Sbrooks 467147072Sbrooksvoid 468147072Sbrooksmake_client_config(struct interface_info *ip, struct client_config *config) 469147072Sbrooks{ 470147072Sbrooks ip->client->config = malloc(sizeof(struct client_config)); 471147072Sbrooks if (!ip->client->config) 472147072Sbrooks error("no memory for config for %s", ip->name); 473147072Sbrooks memset(ip->client->config, 0, sizeof(*(ip->client->config))); 474147072Sbrooks memcpy(ip->client->config, config, sizeof(*config)); 475147072Sbrooks} 476147072Sbrooks 477147072Sbrooks/* 478147072Sbrooks * client-lease-statement :== 479147072Sbrooks * RBRACE client-lease-declarations LBRACE 480147072Sbrooks * 481147072Sbrooks * client-lease-declarations :== 482147072Sbrooks * <nil> | 483147072Sbrooks * client-lease-declaration | 484147072Sbrooks * client-lease-declarations client-lease-declaration 485147072Sbrooks */ 486147072Sbrooksvoid 487147072Sbrooksparse_client_lease_statement(FILE *cfile, int is_static) 488147072Sbrooks{ 489147072Sbrooks struct client_lease *lease, *lp, *pl; 490147072Sbrooks struct interface_info *ip; 491147072Sbrooks int token; 492147072Sbrooks char *val; 493147072Sbrooks 494147072Sbrooks token = next_token(&val, cfile); 495147072Sbrooks if (token != LBRACE) { 496147072Sbrooks parse_warn("expecting left brace."); 497147072Sbrooks skip_to_semi(cfile); 498147072Sbrooks return; 499147072Sbrooks } 500147072Sbrooks 501147072Sbrooks lease = malloc(sizeof(struct client_lease)); 502147072Sbrooks if (!lease) 503147072Sbrooks error("no memory for lease."); 504147072Sbrooks memset(lease, 0, sizeof(*lease)); 505147072Sbrooks lease->is_static = is_static; 506147072Sbrooks 507147072Sbrooks ip = NULL; 508147072Sbrooks 509147072Sbrooks do { 510147072Sbrooks token = peek_token(&val, cfile); 511147072Sbrooks if (token == EOF) { 512147072Sbrooks parse_warn("unterminated lease declaration."); 513315610Sngie free_client_lease(lease); 514147072Sbrooks return; 515147072Sbrooks } 516147072Sbrooks if (token == RBRACE) 517147072Sbrooks break; 518147072Sbrooks parse_client_lease_declaration(cfile, lease, &ip); 519147072Sbrooks } while (1); 520147072Sbrooks token = next_token(&val, cfile); 521147072Sbrooks 522147072Sbrooks /* If the lease declaration didn't include an interface 523147072Sbrooks * declaration that we recognized, it's of no use to us. 524147072Sbrooks */ 525147072Sbrooks if (!ip) { 526147072Sbrooks free_client_lease(lease); 527147072Sbrooks return; 528147072Sbrooks } 529147072Sbrooks 530147072Sbrooks /* Make sure there's a client state structure... */ 531147072Sbrooks if (!ip->client) 532147072Sbrooks make_client_state(ip); 533147072Sbrooks 534147072Sbrooks /* If this is an alias lease, it doesn't need to be sorted in. */ 535147072Sbrooks if (is_static == 2) { 536147072Sbrooks ip->client->alias = lease; 537147072Sbrooks return; 538147072Sbrooks } 539147072Sbrooks 540147072Sbrooks /* 541147072Sbrooks * The new lease may supersede a lease that's not the active 542147072Sbrooks * lease but is still on the lease list, so scan the lease list 543147072Sbrooks * looking for a lease with the same address, and if we find it, 544147072Sbrooks * toss it. 545147072Sbrooks */ 546147072Sbrooks pl = NULL; 547147072Sbrooks for (lp = ip->client->leases; lp; lp = lp->next) { 548147072Sbrooks if (lp->address.len == lease->address.len && 549147072Sbrooks !memcmp(lp->address.iabuf, lease->address.iabuf, 550147072Sbrooks lease->address.len)) { 551147072Sbrooks if (pl) 552147072Sbrooks pl->next = lp->next; 553147072Sbrooks else 554147072Sbrooks ip->client->leases = lp->next; 555147072Sbrooks free_client_lease(lp); 556147072Sbrooks break; 557147072Sbrooks } 558147072Sbrooks } 559147072Sbrooks 560147072Sbrooks /* 561147072Sbrooks * If this is a preloaded lease, just put it on the list of 562147072Sbrooks * recorded leases - don't make it the active lease. 563147072Sbrooks */ 564147072Sbrooks if (is_static) { 565147072Sbrooks lease->next = ip->client->leases; 566147072Sbrooks ip->client->leases = lease; 567147072Sbrooks return; 568147072Sbrooks } 569147072Sbrooks 570147072Sbrooks /* 571147072Sbrooks * The last lease in the lease file on a particular interface is 572147072Sbrooks * the active lease for that interface. Of course, we don't 573147072Sbrooks * know what the last lease in the file is until we've parsed 574147072Sbrooks * the whole file, so at this point, we assume that the lease we 575147072Sbrooks * just parsed is the active lease for its interface. If 576147072Sbrooks * there's already an active lease for the interface, and this 577147072Sbrooks * lease is for the same ip address, then we just toss the old 578147072Sbrooks * active lease and replace it with this one. If this lease is 579147072Sbrooks * for a different address, then if the old active lease has 580147072Sbrooks * expired, we dump it; if not, we put it on the list of leases 581147072Sbrooks * for this interface which are still valid but no longer 582147072Sbrooks * active. 583147072Sbrooks */ 584147072Sbrooks if (ip->client->active) { 585147072Sbrooks if (ip->client->active->expiry < cur_time) 586147072Sbrooks free_client_lease(ip->client->active); 587147072Sbrooks else if (ip->client->active->address.len == 588147072Sbrooks lease->address.len && 589147072Sbrooks !memcmp(ip->client->active->address.iabuf, 590147072Sbrooks lease->address.iabuf, lease->address.len)) 591147072Sbrooks free_client_lease(ip->client->active); 592147072Sbrooks else { 593147072Sbrooks ip->client->active->next = ip->client->leases; 594147072Sbrooks ip->client->leases = ip->client->active; 595147072Sbrooks } 596147072Sbrooks } 597147072Sbrooks ip->client->active = lease; 598147072Sbrooks 599147072Sbrooks /* Phew. */ 600147072Sbrooks} 601147072Sbrooks 602147072Sbrooks/* 603147072Sbrooks * client-lease-declaration :== 604147072Sbrooks * BOOTP | 605147072Sbrooks * INTERFACE string | 606147072Sbrooks * FIXED_ADDR ip_address | 607147072Sbrooks * FILENAME string | 608147072Sbrooks * SERVER_NAME string | 609147072Sbrooks * OPTION option-decl | 610147072Sbrooks * RENEW time-decl | 611147072Sbrooks * REBIND time-decl | 612147072Sbrooks * EXPIRE time-decl 613147072Sbrooks */ 614147072Sbrooksvoid 615147072Sbrooksparse_client_lease_declaration(FILE *cfile, struct client_lease *lease, 616147072Sbrooks struct interface_info **ipp) 617147072Sbrooks{ 618147072Sbrooks int token; 619147072Sbrooks char *val; 620147072Sbrooks struct interface_info *ip; 621147072Sbrooks 622147072Sbrooks switch (next_token(&val, cfile)) { 623147072Sbrooks case BOOTP: 624147072Sbrooks lease->is_bootp = 1; 625147072Sbrooks break; 626147072Sbrooks case INTERFACE: 627147072Sbrooks token = next_token(&val, cfile); 628147072Sbrooks if (token != STRING) { 629147072Sbrooks parse_warn("expecting interface name (in quotes)."); 630147072Sbrooks skip_to_semi(cfile); 631147072Sbrooks break; 632147072Sbrooks } 633147072Sbrooks ip = interface_or_dummy(val); 634147072Sbrooks *ipp = ip; 635147072Sbrooks break; 636147072Sbrooks case FIXED_ADDR: 637147072Sbrooks if (!parse_ip_addr(cfile, &lease->address)) 638147072Sbrooks return; 639147072Sbrooks break; 640147072Sbrooks case MEDIUM: 641147072Sbrooks parse_string_list(cfile, &lease->medium, 0); 642147072Sbrooks return; 643147072Sbrooks case FILENAME: 644147072Sbrooks lease->filename = parse_string(cfile); 645147072Sbrooks return; 646252506Sbms case NEXT_SERVER: 647252506Sbms if (!parse_ip_addr(cfile, &lease->nextserver)) 648252506Sbms return; 649252506Sbms break; 650147072Sbrooks case SERVER_NAME: 651147072Sbrooks lease->server_name = parse_string(cfile); 652147072Sbrooks return; 653147072Sbrooks case RENEW: 654147072Sbrooks lease->renewal = parse_date(cfile); 655147072Sbrooks return; 656147072Sbrooks case REBIND: 657147072Sbrooks lease->rebind = parse_date(cfile); 658147072Sbrooks return; 659147072Sbrooks case EXPIRE: 660147072Sbrooks lease->expiry = parse_date(cfile); 661147072Sbrooks return; 662147072Sbrooks case OPTION: 663147072Sbrooks parse_option_decl(cfile, lease->options); 664147072Sbrooks return; 665147072Sbrooks default: 666147072Sbrooks parse_warn("expecting lease declaration."); 667147072Sbrooks skip_to_semi(cfile); 668147072Sbrooks break; 669147072Sbrooks } 670147072Sbrooks token = next_token(&val, cfile); 671147072Sbrooks if (token != SEMI) { 672147072Sbrooks parse_warn("expecting semicolon."); 673147072Sbrooks skip_to_semi(cfile); 674147072Sbrooks } 675147072Sbrooks} 676147072Sbrooks 677147072Sbrooksstruct option * 678147072Sbrooksparse_option_decl(FILE *cfile, struct option_data *options) 679147072Sbrooks{ 680147072Sbrooks char *val; 681147072Sbrooks int token; 682147072Sbrooks u_int8_t buf[4]; 683147072Sbrooks u_int8_t hunkbuf[1024]; 684147072Sbrooks int hunkix = 0; 685147072Sbrooks char *vendor; 686147072Sbrooks char *fmt; 687147072Sbrooks struct universe *universe; 688147072Sbrooks struct option *option; 689147072Sbrooks struct iaddr ip_addr; 690147072Sbrooks u_int8_t *dp; 691147072Sbrooks int len; 692147072Sbrooks int nul_term = 0; 693147072Sbrooks 694147072Sbrooks token = next_token(&val, cfile); 695147072Sbrooks if (!is_identifier(token)) { 696147072Sbrooks parse_warn("expecting identifier after option keyword."); 697147072Sbrooks if (token != SEMI) 698147072Sbrooks skip_to_semi(cfile); 699147072Sbrooks return (NULL); 700147072Sbrooks } 701147072Sbrooks if ((vendor = strdup(val)) == NULL) 702147072Sbrooks error("no memory for vendor information."); 703147072Sbrooks 704147072Sbrooks token = peek_token(&val, cfile); 705147072Sbrooks if (token == DOT) { 706147072Sbrooks /* Go ahead and take the DOT token... */ 707147072Sbrooks token = next_token(&val, cfile); 708147072Sbrooks 709147072Sbrooks /* The next token should be an identifier... */ 710147072Sbrooks token = next_token(&val, cfile); 711147072Sbrooks if (!is_identifier(token)) { 712147072Sbrooks parse_warn("expecting identifier after '.'"); 713147072Sbrooks if (token != SEMI) 714147072Sbrooks skip_to_semi(cfile); 715315610Sngie free(vendor); 716147072Sbrooks return (NULL); 717147072Sbrooks } 718147072Sbrooks 719147072Sbrooks /* Look up the option name hash table for the specified 720147072Sbrooks vendor. */ 721147072Sbrooks universe = ((struct universe *)hash_lookup(&universe_hash, 722147072Sbrooks (unsigned char *)vendor, 0)); 723147072Sbrooks /* If it's not there, we can't parse the rest of the 724147072Sbrooks declaration. */ 725147072Sbrooks if (!universe) { 726147072Sbrooks parse_warn("no vendor named %s.", vendor); 727147072Sbrooks skip_to_semi(cfile); 728315610Sngie free(vendor); 729147072Sbrooks return (NULL); 730147072Sbrooks } 731147072Sbrooks } else { 732147072Sbrooks /* Use the default hash table, which contains all the 733147072Sbrooks standard dhcp option names. */ 734147072Sbrooks val = vendor; 735147072Sbrooks universe = &dhcp_universe; 736147072Sbrooks } 737147072Sbrooks 738147072Sbrooks /* Look up the actual option info... */ 739147072Sbrooks option = (struct option *)hash_lookup(universe->hash, 740147072Sbrooks (unsigned char *)val, 0); 741147072Sbrooks 742147072Sbrooks /* If we didn't get an option structure, it's an undefined option. */ 743147072Sbrooks if (!option) { 744147072Sbrooks if (val == vendor) 745147072Sbrooks parse_warn("no option named %s", val); 746147072Sbrooks else 747147072Sbrooks parse_warn("no option named %s for vendor %s", 748147072Sbrooks val, vendor); 749147072Sbrooks skip_to_semi(cfile); 750315610Sngie free(vendor); 751147072Sbrooks return (NULL); 752147072Sbrooks } 753147072Sbrooks 754147072Sbrooks /* Free the initial identifier token. */ 755147072Sbrooks free(vendor); 756147072Sbrooks 757147072Sbrooks /* Parse the option data... */ 758147072Sbrooks do { 759147072Sbrooks for (fmt = option->format; *fmt; fmt++) { 760147072Sbrooks if (*fmt == 'A') 761147072Sbrooks break; 762147072Sbrooks switch (*fmt) { 763147072Sbrooks case 'X': 764147072Sbrooks len = parse_X(cfile, &hunkbuf[hunkix], 765147072Sbrooks sizeof(hunkbuf) - hunkix); 766147072Sbrooks hunkix += len; 767147072Sbrooks break; 768147072Sbrooks case 't': /* Text string... */ 769147072Sbrooks token = next_token(&val, cfile); 770147072Sbrooks if (token != STRING) { 771147072Sbrooks parse_warn("expecting string."); 772147072Sbrooks skip_to_semi(cfile); 773147072Sbrooks return (NULL); 774147072Sbrooks } 775147072Sbrooks len = strlen(val); 776147072Sbrooks if (hunkix + len + 1 > sizeof(hunkbuf)) { 777147072Sbrooks parse_warn("option data buffer %s", 778147072Sbrooks "overflow"); 779147072Sbrooks skip_to_semi(cfile); 780147072Sbrooks return (NULL); 781147072Sbrooks } 782147072Sbrooks memcpy(&hunkbuf[hunkix], val, len + 1); 783147072Sbrooks nul_term = 1; 784147072Sbrooks hunkix += len; 785147072Sbrooks break; 786147072Sbrooks case 'I': /* IP address. */ 787147072Sbrooks if (!parse_ip_addr(cfile, &ip_addr)) 788147072Sbrooks return (NULL); 789147072Sbrooks len = ip_addr.len; 790147072Sbrooks dp = ip_addr.iabuf; 791147072Sbrooksalloc: 792147072Sbrooks if (hunkix + len > sizeof(hunkbuf)) { 793147072Sbrooks parse_warn("option data buffer " 794147072Sbrooks "overflow"); 795147072Sbrooks skip_to_semi(cfile); 796147072Sbrooks return (NULL); 797147072Sbrooks } 798147072Sbrooks memcpy(&hunkbuf[hunkix], dp, len); 799147072Sbrooks hunkix += len; 800147072Sbrooks break; 801147072Sbrooks case 'L': /* Unsigned 32-bit integer... */ 802147072Sbrooks case 'l': /* Signed 32-bit integer... */ 803147072Sbrooks token = next_token(&val, cfile); 804147072Sbrooks if (token != NUMBER) { 805147072Sbrooksneed_number: 806147072Sbrooks parse_warn("expecting number."); 807147072Sbrooks if (token != SEMI) 808147072Sbrooks skip_to_semi(cfile); 809147072Sbrooks return (NULL); 810147072Sbrooks } 811147072Sbrooks convert_num(buf, val, 0, 32); 812147072Sbrooks len = 4; 813147072Sbrooks dp = buf; 814147072Sbrooks goto alloc; 815147072Sbrooks case 's': /* Signed 16-bit integer. */ 816147072Sbrooks case 'S': /* Unsigned 16-bit integer. */ 817147072Sbrooks token = next_token(&val, cfile); 818147072Sbrooks if (token != NUMBER) 819147072Sbrooks goto need_number; 820147072Sbrooks convert_num(buf, val, 0, 16); 821147072Sbrooks len = 2; 822147072Sbrooks dp = buf; 823147072Sbrooks goto alloc; 824147072Sbrooks case 'b': /* Signed 8-bit integer. */ 825147072Sbrooks case 'B': /* Unsigned 8-bit integer. */ 826147072Sbrooks token = next_token(&val, cfile); 827147072Sbrooks if (token != NUMBER) 828147072Sbrooks goto need_number; 829147072Sbrooks convert_num(buf, val, 0, 8); 830147072Sbrooks len = 1; 831147072Sbrooks dp = buf; 832147072Sbrooks goto alloc; 833147072Sbrooks case 'f': /* Boolean flag. */ 834147072Sbrooks token = next_token(&val, cfile); 835147072Sbrooks if (!is_identifier(token)) { 836147072Sbrooks parse_warn("expecting identifier."); 837147072Sbrooksbad_flag: 838147072Sbrooks if (token != SEMI) 839147072Sbrooks skip_to_semi(cfile); 840147072Sbrooks return (NULL); 841147072Sbrooks } 842147072Sbrooks if (!strcasecmp(val, "true") || 843147072Sbrooks !strcasecmp(val, "on")) 844147072Sbrooks buf[0] = 1; 845147072Sbrooks else if (!strcasecmp(val, "false") || 846147072Sbrooks !strcasecmp(val, "off")) 847147072Sbrooks buf[0] = 0; 848147072Sbrooks else { 849147072Sbrooks parse_warn("expecting boolean."); 850147072Sbrooks goto bad_flag; 851147072Sbrooks } 852147072Sbrooks len = 1; 853147072Sbrooks dp = buf; 854147072Sbrooks goto alloc; 855147072Sbrooks default: 856147072Sbrooks warning("Bad format %c in parse_option_param.", 857147072Sbrooks *fmt); 858147072Sbrooks skip_to_semi(cfile); 859147072Sbrooks return (NULL); 860147072Sbrooks } 861147072Sbrooks } 862147072Sbrooks token = next_token(&val, cfile); 863147072Sbrooks } while (*fmt == 'A' && token == COMMA); 864147072Sbrooks 865147072Sbrooks if (token != SEMI) { 866147072Sbrooks parse_warn("semicolon expected."); 867147072Sbrooks skip_to_semi(cfile); 868147072Sbrooks return (NULL); 869147072Sbrooks } 870147072Sbrooks 871147072Sbrooks options[option->code].data = malloc(hunkix + nul_term); 872147072Sbrooks if (!options[option->code].data) 873147072Sbrooks error("out of memory allocating option data."); 874147072Sbrooks memcpy(options[option->code].data, hunkbuf, hunkix + nul_term); 875147072Sbrooks options[option->code].len = hunkix; 876147072Sbrooks return (option); 877147072Sbrooks} 878147072Sbrooks 879147072Sbrooksvoid 880147072Sbrooksparse_string_list(FILE *cfile, struct string_list **lp, int multiple) 881147072Sbrooks{ 882147072Sbrooks int token; 883147072Sbrooks char *val; 884228614Sdim size_t valsize; 885147072Sbrooks struct string_list *cur, *tmp; 886147072Sbrooks 887147072Sbrooks /* Find the last medium in the media list. */ 888147072Sbrooks if (*lp) 889147072Sbrooks for (cur = *lp; cur->next; cur = cur->next) 890147072Sbrooks ; /* nothing */ 891147072Sbrooks else 892147072Sbrooks cur = NULL; 893147072Sbrooks 894147072Sbrooks do { 895147072Sbrooks token = next_token(&val, cfile); 896147072Sbrooks if (token != STRING) { 897147072Sbrooks parse_warn("Expecting media options."); 898147072Sbrooks skip_to_semi(cfile); 899147072Sbrooks return; 900147072Sbrooks } 901147072Sbrooks 902228614Sdim valsize = strlen(val) + 1; 903228614Sdim tmp = new_string_list(valsize); 904147072Sbrooks if (tmp == NULL) 905147072Sbrooks error("no memory for string list entry."); 906228615Sdim memcpy(tmp->string, val, valsize); 907147072Sbrooks tmp->next = NULL; 908147072Sbrooks 909147072Sbrooks /* Store this medium at the end of the media list. */ 910147072Sbrooks if (cur) 911147072Sbrooks cur->next = tmp; 912147072Sbrooks else 913147072Sbrooks *lp = tmp; 914147072Sbrooks cur = tmp; 915147072Sbrooks 916147072Sbrooks token = next_token(&val, cfile); 917147072Sbrooks } while (multiple && token == COMMA); 918147072Sbrooks 919147072Sbrooks if (token != SEMI) { 920147072Sbrooks parse_warn("expecting semicolon."); 921147072Sbrooks skip_to_semi(cfile); 922147072Sbrooks } 923147072Sbrooks} 924147072Sbrooks 925147072Sbrooksvoid 926147072Sbrooksparse_reject_statement(FILE *cfile, struct client_config *config) 927147072Sbrooks{ 928147072Sbrooks int token; 929147072Sbrooks char *val; 930147072Sbrooks struct iaddr addr; 931147072Sbrooks struct iaddrlist *list; 932147072Sbrooks 933147072Sbrooks do { 934147072Sbrooks if (!parse_ip_addr(cfile, &addr)) { 935147072Sbrooks parse_warn("expecting IP address."); 936147072Sbrooks skip_to_semi(cfile); 937147072Sbrooks return; 938147072Sbrooks } 939147072Sbrooks 940147072Sbrooks list = malloc(sizeof(struct iaddrlist)); 941147072Sbrooks if (!list) 942147072Sbrooks error("no memory for reject list!"); 943147072Sbrooks 944147072Sbrooks list->addr = addr; 945147072Sbrooks list->next = config->reject_list; 946147072Sbrooks config->reject_list = list; 947147072Sbrooks 948147072Sbrooks token = next_token(&val, cfile); 949147072Sbrooks } while (token == COMMA); 950147072Sbrooks 951147072Sbrooks if (token != SEMI) { 952147072Sbrooks parse_warn("expecting semicolon."); 953147072Sbrooks skip_to_semi(cfile); 954147072Sbrooks } 955147072Sbrooks} 956