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$"); 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."); 513147072Sbrooks return; 514147072Sbrooks } 515147072Sbrooks if (token == RBRACE) 516147072Sbrooks break; 517147072Sbrooks parse_client_lease_declaration(cfile, lease, &ip); 518147072Sbrooks } while (1); 519147072Sbrooks token = next_token(&val, cfile); 520147072Sbrooks 521147072Sbrooks /* If the lease declaration didn't include an interface 522147072Sbrooks * declaration that we recognized, it's of no use to us. 523147072Sbrooks */ 524147072Sbrooks if (!ip) { 525147072Sbrooks free_client_lease(lease); 526147072Sbrooks return; 527147072Sbrooks } 528147072Sbrooks 529147072Sbrooks /* Make sure there's a client state structure... */ 530147072Sbrooks if (!ip->client) 531147072Sbrooks make_client_state(ip); 532147072Sbrooks 533147072Sbrooks /* If this is an alias lease, it doesn't need to be sorted in. */ 534147072Sbrooks if (is_static == 2) { 535147072Sbrooks ip->client->alias = lease; 536147072Sbrooks return; 537147072Sbrooks } 538147072Sbrooks 539147072Sbrooks /* 540147072Sbrooks * The new lease may supersede a lease that's not the active 541147072Sbrooks * lease but is still on the lease list, so scan the lease list 542147072Sbrooks * looking for a lease with the same address, and if we find it, 543147072Sbrooks * toss it. 544147072Sbrooks */ 545147072Sbrooks pl = NULL; 546147072Sbrooks for (lp = ip->client->leases; lp; lp = lp->next) { 547147072Sbrooks if (lp->address.len == lease->address.len && 548147072Sbrooks !memcmp(lp->address.iabuf, lease->address.iabuf, 549147072Sbrooks lease->address.len)) { 550147072Sbrooks if (pl) 551147072Sbrooks pl->next = lp->next; 552147072Sbrooks else 553147072Sbrooks ip->client->leases = lp->next; 554147072Sbrooks free_client_lease(lp); 555147072Sbrooks break; 556147072Sbrooks } 557147072Sbrooks } 558147072Sbrooks 559147072Sbrooks /* 560147072Sbrooks * If this is a preloaded lease, just put it on the list of 561147072Sbrooks * recorded leases - don't make it the active lease. 562147072Sbrooks */ 563147072Sbrooks if (is_static) { 564147072Sbrooks lease->next = ip->client->leases; 565147072Sbrooks ip->client->leases = lease; 566147072Sbrooks return; 567147072Sbrooks } 568147072Sbrooks 569147072Sbrooks /* 570147072Sbrooks * The last lease in the lease file on a particular interface is 571147072Sbrooks * the active lease for that interface. Of course, we don't 572147072Sbrooks * know what the last lease in the file is until we've parsed 573147072Sbrooks * the whole file, so at this point, we assume that the lease we 574147072Sbrooks * just parsed is the active lease for its interface. If 575147072Sbrooks * there's already an active lease for the interface, and this 576147072Sbrooks * lease is for the same ip address, then we just toss the old 577147072Sbrooks * active lease and replace it with this one. If this lease is 578147072Sbrooks * for a different address, then if the old active lease has 579147072Sbrooks * expired, we dump it; if not, we put it on the list of leases 580147072Sbrooks * for this interface which are still valid but no longer 581147072Sbrooks * active. 582147072Sbrooks */ 583147072Sbrooks if (ip->client->active) { 584147072Sbrooks if (ip->client->active->expiry < cur_time) 585147072Sbrooks free_client_lease(ip->client->active); 586147072Sbrooks else if (ip->client->active->address.len == 587147072Sbrooks lease->address.len && 588147072Sbrooks !memcmp(ip->client->active->address.iabuf, 589147072Sbrooks lease->address.iabuf, lease->address.len)) 590147072Sbrooks free_client_lease(ip->client->active); 591147072Sbrooks else { 592147072Sbrooks ip->client->active->next = ip->client->leases; 593147072Sbrooks ip->client->leases = ip->client->active; 594147072Sbrooks } 595147072Sbrooks } 596147072Sbrooks ip->client->active = lease; 597147072Sbrooks 598147072Sbrooks /* Phew. */ 599147072Sbrooks} 600147072Sbrooks 601147072Sbrooks/* 602147072Sbrooks * client-lease-declaration :== 603147072Sbrooks * BOOTP | 604147072Sbrooks * INTERFACE string | 605147072Sbrooks * FIXED_ADDR ip_address | 606147072Sbrooks * FILENAME string | 607147072Sbrooks * SERVER_NAME string | 608147072Sbrooks * OPTION option-decl | 609147072Sbrooks * RENEW time-decl | 610147072Sbrooks * REBIND time-decl | 611147072Sbrooks * EXPIRE time-decl 612147072Sbrooks */ 613147072Sbrooksvoid 614147072Sbrooksparse_client_lease_declaration(FILE *cfile, struct client_lease *lease, 615147072Sbrooks struct interface_info **ipp) 616147072Sbrooks{ 617147072Sbrooks int token; 618147072Sbrooks char *val; 619147072Sbrooks struct interface_info *ip; 620147072Sbrooks 621147072Sbrooks switch (next_token(&val, cfile)) { 622147072Sbrooks case BOOTP: 623147072Sbrooks lease->is_bootp = 1; 624147072Sbrooks break; 625147072Sbrooks case INTERFACE: 626147072Sbrooks token = next_token(&val, cfile); 627147072Sbrooks if (token != STRING) { 628147072Sbrooks parse_warn("expecting interface name (in quotes)."); 629147072Sbrooks skip_to_semi(cfile); 630147072Sbrooks break; 631147072Sbrooks } 632147072Sbrooks ip = interface_or_dummy(val); 633147072Sbrooks *ipp = ip; 634147072Sbrooks break; 635147072Sbrooks case FIXED_ADDR: 636147072Sbrooks if (!parse_ip_addr(cfile, &lease->address)) 637147072Sbrooks return; 638147072Sbrooks break; 639147072Sbrooks case MEDIUM: 640147072Sbrooks parse_string_list(cfile, &lease->medium, 0); 641147072Sbrooks return; 642147072Sbrooks case FILENAME: 643147072Sbrooks lease->filename = parse_string(cfile); 644147072Sbrooks return; 645252506Sbms case NEXT_SERVER: 646252506Sbms if (!parse_ip_addr(cfile, &lease->nextserver)) 647252506Sbms return; 648252506Sbms break; 649147072Sbrooks case SERVER_NAME: 650147072Sbrooks lease->server_name = parse_string(cfile); 651147072Sbrooks return; 652147072Sbrooks case RENEW: 653147072Sbrooks lease->renewal = parse_date(cfile); 654147072Sbrooks return; 655147072Sbrooks case REBIND: 656147072Sbrooks lease->rebind = parse_date(cfile); 657147072Sbrooks return; 658147072Sbrooks case EXPIRE: 659147072Sbrooks lease->expiry = parse_date(cfile); 660147072Sbrooks return; 661147072Sbrooks case OPTION: 662147072Sbrooks parse_option_decl(cfile, lease->options); 663147072Sbrooks return; 664147072Sbrooks default: 665147072Sbrooks parse_warn("expecting lease declaration."); 666147072Sbrooks skip_to_semi(cfile); 667147072Sbrooks break; 668147072Sbrooks } 669147072Sbrooks token = next_token(&val, cfile); 670147072Sbrooks if (token != SEMI) { 671147072Sbrooks parse_warn("expecting semicolon."); 672147072Sbrooks skip_to_semi(cfile); 673147072Sbrooks } 674147072Sbrooks} 675147072Sbrooks 676147072Sbrooksstruct option * 677147072Sbrooksparse_option_decl(FILE *cfile, struct option_data *options) 678147072Sbrooks{ 679147072Sbrooks char *val; 680147072Sbrooks int token; 681147072Sbrooks u_int8_t buf[4]; 682147072Sbrooks u_int8_t hunkbuf[1024]; 683147072Sbrooks int hunkix = 0; 684147072Sbrooks char *vendor; 685147072Sbrooks char *fmt; 686147072Sbrooks struct universe *universe; 687147072Sbrooks struct option *option; 688147072Sbrooks struct iaddr ip_addr; 689147072Sbrooks u_int8_t *dp; 690147072Sbrooks int len; 691147072Sbrooks int nul_term = 0; 692147072Sbrooks 693147072Sbrooks token = next_token(&val, cfile); 694147072Sbrooks if (!is_identifier(token)) { 695147072Sbrooks parse_warn("expecting identifier after option keyword."); 696147072Sbrooks if (token != SEMI) 697147072Sbrooks skip_to_semi(cfile); 698147072Sbrooks return (NULL); 699147072Sbrooks } 700147072Sbrooks if ((vendor = strdup(val)) == NULL) 701147072Sbrooks error("no memory for vendor information."); 702147072Sbrooks 703147072Sbrooks token = peek_token(&val, cfile); 704147072Sbrooks if (token == DOT) { 705147072Sbrooks /* Go ahead and take the DOT token... */ 706147072Sbrooks token = next_token(&val, cfile); 707147072Sbrooks 708147072Sbrooks /* The next token should be an identifier... */ 709147072Sbrooks token = next_token(&val, cfile); 710147072Sbrooks if (!is_identifier(token)) { 711147072Sbrooks parse_warn("expecting identifier after '.'"); 712147072Sbrooks if (token != SEMI) 713147072Sbrooks skip_to_semi(cfile); 714147072Sbrooks return (NULL); 715147072Sbrooks } 716147072Sbrooks 717147072Sbrooks /* Look up the option name hash table for the specified 718147072Sbrooks vendor. */ 719147072Sbrooks universe = ((struct universe *)hash_lookup(&universe_hash, 720147072Sbrooks (unsigned char *)vendor, 0)); 721147072Sbrooks /* If it's not there, we can't parse the rest of the 722147072Sbrooks declaration. */ 723147072Sbrooks if (!universe) { 724147072Sbrooks parse_warn("no vendor named %s.", vendor); 725147072Sbrooks skip_to_semi(cfile); 726147072Sbrooks return (NULL); 727147072Sbrooks } 728147072Sbrooks } else { 729147072Sbrooks /* Use the default hash table, which contains all the 730147072Sbrooks standard dhcp option names. */ 731147072Sbrooks val = vendor; 732147072Sbrooks universe = &dhcp_universe; 733147072Sbrooks } 734147072Sbrooks 735147072Sbrooks /* Look up the actual option info... */ 736147072Sbrooks option = (struct option *)hash_lookup(universe->hash, 737147072Sbrooks (unsigned char *)val, 0); 738147072Sbrooks 739147072Sbrooks /* If we didn't get an option structure, it's an undefined option. */ 740147072Sbrooks if (!option) { 741147072Sbrooks if (val == vendor) 742147072Sbrooks parse_warn("no option named %s", val); 743147072Sbrooks else 744147072Sbrooks parse_warn("no option named %s for vendor %s", 745147072Sbrooks val, vendor); 746147072Sbrooks skip_to_semi(cfile); 747147072Sbrooks return (NULL); 748147072Sbrooks } 749147072Sbrooks 750147072Sbrooks /* Free the initial identifier token. */ 751147072Sbrooks free(vendor); 752147072Sbrooks 753147072Sbrooks /* Parse the option data... */ 754147072Sbrooks do { 755147072Sbrooks for (fmt = option->format; *fmt; fmt++) { 756147072Sbrooks if (*fmt == 'A') 757147072Sbrooks break; 758147072Sbrooks switch (*fmt) { 759147072Sbrooks case 'X': 760147072Sbrooks len = parse_X(cfile, &hunkbuf[hunkix], 761147072Sbrooks sizeof(hunkbuf) - hunkix); 762147072Sbrooks hunkix += len; 763147072Sbrooks break; 764147072Sbrooks case 't': /* Text string... */ 765147072Sbrooks token = next_token(&val, cfile); 766147072Sbrooks if (token != STRING) { 767147072Sbrooks parse_warn("expecting string."); 768147072Sbrooks skip_to_semi(cfile); 769147072Sbrooks return (NULL); 770147072Sbrooks } 771147072Sbrooks len = strlen(val); 772147072Sbrooks if (hunkix + len + 1 > sizeof(hunkbuf)) { 773147072Sbrooks parse_warn("option data buffer %s", 774147072Sbrooks "overflow"); 775147072Sbrooks skip_to_semi(cfile); 776147072Sbrooks return (NULL); 777147072Sbrooks } 778147072Sbrooks memcpy(&hunkbuf[hunkix], val, len + 1); 779147072Sbrooks nul_term = 1; 780147072Sbrooks hunkix += len; 781147072Sbrooks break; 782147072Sbrooks case 'I': /* IP address. */ 783147072Sbrooks if (!parse_ip_addr(cfile, &ip_addr)) 784147072Sbrooks return (NULL); 785147072Sbrooks len = ip_addr.len; 786147072Sbrooks dp = ip_addr.iabuf; 787147072Sbrooksalloc: 788147072Sbrooks if (hunkix + len > sizeof(hunkbuf)) { 789147072Sbrooks parse_warn("option data buffer " 790147072Sbrooks "overflow"); 791147072Sbrooks skip_to_semi(cfile); 792147072Sbrooks return (NULL); 793147072Sbrooks } 794147072Sbrooks memcpy(&hunkbuf[hunkix], dp, len); 795147072Sbrooks hunkix += len; 796147072Sbrooks break; 797147072Sbrooks case 'L': /* Unsigned 32-bit integer... */ 798147072Sbrooks case 'l': /* Signed 32-bit integer... */ 799147072Sbrooks token = next_token(&val, cfile); 800147072Sbrooks if (token != NUMBER) { 801147072Sbrooksneed_number: 802147072Sbrooks parse_warn("expecting number."); 803147072Sbrooks if (token != SEMI) 804147072Sbrooks skip_to_semi(cfile); 805147072Sbrooks return (NULL); 806147072Sbrooks } 807147072Sbrooks convert_num(buf, val, 0, 32); 808147072Sbrooks len = 4; 809147072Sbrooks dp = buf; 810147072Sbrooks goto alloc; 811147072Sbrooks case 's': /* Signed 16-bit integer. */ 812147072Sbrooks case 'S': /* Unsigned 16-bit integer. */ 813147072Sbrooks token = next_token(&val, cfile); 814147072Sbrooks if (token != NUMBER) 815147072Sbrooks goto need_number; 816147072Sbrooks convert_num(buf, val, 0, 16); 817147072Sbrooks len = 2; 818147072Sbrooks dp = buf; 819147072Sbrooks goto alloc; 820147072Sbrooks case 'b': /* Signed 8-bit integer. */ 821147072Sbrooks case 'B': /* Unsigned 8-bit integer. */ 822147072Sbrooks token = next_token(&val, cfile); 823147072Sbrooks if (token != NUMBER) 824147072Sbrooks goto need_number; 825147072Sbrooks convert_num(buf, val, 0, 8); 826147072Sbrooks len = 1; 827147072Sbrooks dp = buf; 828147072Sbrooks goto alloc; 829147072Sbrooks case 'f': /* Boolean flag. */ 830147072Sbrooks token = next_token(&val, cfile); 831147072Sbrooks if (!is_identifier(token)) { 832147072Sbrooks parse_warn("expecting identifier."); 833147072Sbrooksbad_flag: 834147072Sbrooks if (token != SEMI) 835147072Sbrooks skip_to_semi(cfile); 836147072Sbrooks return (NULL); 837147072Sbrooks } 838147072Sbrooks if (!strcasecmp(val, "true") || 839147072Sbrooks !strcasecmp(val, "on")) 840147072Sbrooks buf[0] = 1; 841147072Sbrooks else if (!strcasecmp(val, "false") || 842147072Sbrooks !strcasecmp(val, "off")) 843147072Sbrooks buf[0] = 0; 844147072Sbrooks else { 845147072Sbrooks parse_warn("expecting boolean."); 846147072Sbrooks goto bad_flag; 847147072Sbrooks } 848147072Sbrooks len = 1; 849147072Sbrooks dp = buf; 850147072Sbrooks goto alloc; 851147072Sbrooks default: 852147072Sbrooks warning("Bad format %c in parse_option_param.", 853147072Sbrooks *fmt); 854147072Sbrooks skip_to_semi(cfile); 855147072Sbrooks return (NULL); 856147072Sbrooks } 857147072Sbrooks } 858147072Sbrooks token = next_token(&val, cfile); 859147072Sbrooks } while (*fmt == 'A' && token == COMMA); 860147072Sbrooks 861147072Sbrooks if (token != SEMI) { 862147072Sbrooks parse_warn("semicolon expected."); 863147072Sbrooks skip_to_semi(cfile); 864147072Sbrooks return (NULL); 865147072Sbrooks } 866147072Sbrooks 867147072Sbrooks options[option->code].data = malloc(hunkix + nul_term); 868147072Sbrooks if (!options[option->code].data) 869147072Sbrooks error("out of memory allocating option data."); 870147072Sbrooks memcpy(options[option->code].data, hunkbuf, hunkix + nul_term); 871147072Sbrooks options[option->code].len = hunkix; 872147072Sbrooks return (option); 873147072Sbrooks} 874147072Sbrooks 875147072Sbrooksvoid 876147072Sbrooksparse_string_list(FILE *cfile, struct string_list **lp, int multiple) 877147072Sbrooks{ 878147072Sbrooks int token; 879147072Sbrooks char *val; 880228614Sdim size_t valsize; 881147072Sbrooks struct string_list *cur, *tmp; 882147072Sbrooks 883147072Sbrooks /* Find the last medium in the media list. */ 884147072Sbrooks if (*lp) 885147072Sbrooks for (cur = *lp; cur->next; cur = cur->next) 886147072Sbrooks ; /* nothing */ 887147072Sbrooks else 888147072Sbrooks cur = NULL; 889147072Sbrooks 890147072Sbrooks do { 891147072Sbrooks token = next_token(&val, cfile); 892147072Sbrooks if (token != STRING) { 893147072Sbrooks parse_warn("Expecting media options."); 894147072Sbrooks skip_to_semi(cfile); 895147072Sbrooks return; 896147072Sbrooks } 897147072Sbrooks 898228614Sdim valsize = strlen(val) + 1; 899228614Sdim tmp = new_string_list(valsize); 900147072Sbrooks if (tmp == NULL) 901147072Sbrooks error("no memory for string list entry."); 902228615Sdim memcpy(tmp->string, val, valsize); 903147072Sbrooks tmp->next = NULL; 904147072Sbrooks 905147072Sbrooks /* Store this medium at the end of the media list. */ 906147072Sbrooks if (cur) 907147072Sbrooks cur->next = tmp; 908147072Sbrooks else 909147072Sbrooks *lp = tmp; 910147072Sbrooks cur = tmp; 911147072Sbrooks 912147072Sbrooks token = next_token(&val, cfile); 913147072Sbrooks } while (multiple && token == COMMA); 914147072Sbrooks 915147072Sbrooks if (token != SEMI) { 916147072Sbrooks parse_warn("expecting semicolon."); 917147072Sbrooks skip_to_semi(cfile); 918147072Sbrooks } 919147072Sbrooks} 920147072Sbrooks 921147072Sbrooksvoid 922147072Sbrooksparse_reject_statement(FILE *cfile, struct client_config *config) 923147072Sbrooks{ 924147072Sbrooks int token; 925147072Sbrooks char *val; 926147072Sbrooks struct iaddr addr; 927147072Sbrooks struct iaddrlist *list; 928147072Sbrooks 929147072Sbrooks do { 930147072Sbrooks if (!parse_ip_addr(cfile, &addr)) { 931147072Sbrooks parse_warn("expecting IP address."); 932147072Sbrooks skip_to_semi(cfile); 933147072Sbrooks return; 934147072Sbrooks } 935147072Sbrooks 936147072Sbrooks list = malloc(sizeof(struct iaddrlist)); 937147072Sbrooks if (!list) 938147072Sbrooks error("no memory for reject list!"); 939147072Sbrooks 940147072Sbrooks list->addr = addr; 941147072Sbrooks list->next = config->reject_list; 942147072Sbrooks config->reject_list = list; 943147072Sbrooks 944147072Sbrooks token = next_token(&val, cfile); 945147072Sbrooks } while (token == COMMA); 946147072Sbrooks 947147072Sbrooks if (token != SEMI) { 948147072Sbrooks parse_warn("expecting semicolon."); 949147072Sbrooks skip_to_semi(cfile); 950147072Sbrooks } 951147072Sbrooks} 952