summaryrefslogtreecommitdiff
path: root/usr.sbin/dhcpd/confpars.c
diff options
context:
space:
mode:
authorHenning Brauer <henning@cvs.openbsd.org>2004-04-13 23:41:50 +0000
committerHenning Brauer <henning@cvs.openbsd.org>2004-04-13 23:41:50 +0000
commitd4e911c1cc791c196fcb9c9d13ec28a25e63053f (patch)
treeaa8ed389eb5fc83d380043d222e533186e278b0f /usr.sbin/dhcpd/confpars.c
parentdeece1dfb12ee6671c915bc387ca2298c3520269 (diff)
may the whacking begin
Diffstat (limited to 'usr.sbin/dhcpd/confpars.c')
-rw-r--r--usr.sbin/dhcpd/confpars.c1372
1 files changed, 1372 insertions, 0 deletions
diff --git a/usr.sbin/dhcpd/confpars.c b/usr.sbin/dhcpd/confpars.c
new file mode 100644
index 00000000000..fec0c61d274
--- /dev/null
+++ b/usr.sbin/dhcpd/confpars.c
@@ -0,0 +1,1372 @@
+/* confpars.c
+
+ Parser for dhcpd config file... */
+
+/*
+ * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
+ * Enterprises. To learn more about the Internet Software Consortium,
+ * see ``http://www.vix.com/isc''. To learn more about Vixie
+ * Enterprises, see ``http://www.vix.com''.
+ */
+
+#include "dhcpd.h"
+#include "dhctoken.h"
+
+static TIME parsed_time;
+
+/* conf-file :== parameters declarations EOF
+ parameters :== <nil> | parameter | parameters parameter
+ declarations :== <nil> | declaration | declarations declaration */
+
+int readconf ()
+{
+ FILE *cfile;
+ char *val;
+ int token;
+ int declaration = 0;
+
+ new_parse (path_dhcpd_conf);
+
+ /* Set up the initial dhcp option universe. */
+ initialize_universes ();
+
+ /* Set up the global defaults... */
+ root_group.default_lease_time = 43200; /* 12 hours. */
+ root_group.max_lease_time = 86400; /* 24 hours. */
+ root_group.bootp_lease_cutoff = MAX_TIME;
+ root_group.boot_unknown_clients = 1;
+ root_group.allow_bootp = 1;
+ root_group.allow_booting = 1;
+ root_group.authoritative = 1;
+
+ if ((cfile = fopen (path_dhcpd_conf, "r")) == NULL) {
+ error ("Can't open %s: %m", path_dhcpd_conf);
+ }
+
+ do {
+ token = peek_token (&val, cfile);
+ if (token == EOF)
+ break;
+ declaration = parse_statement (cfile, &root_group,
+ ROOT_GROUP,
+ (struct host_decl *)0,
+ declaration);
+ } while (1);
+ token = next_token (&val, cfile); /* Clear the peek buffer */
+ fclose(cfile);
+
+ return !warnings_occurred;
+}
+
+/* lease-file :== lease-declarations EOF
+ lease-statments :== <nil>
+ | lease-declaration
+ | lease-declarations lease-declaration */
+
+void read_leases ()
+{
+ FILE *cfile;
+ char *val;
+ int token;
+
+ new_parse (path_dhcpd_db);
+
+ /* Open the lease file. If we can't open it, fail. The reason
+ for this is that although on initial startup, the absence of
+ a lease file is perfectly benign, if dhcpd has been running
+ and this file is absent, it means that dhcpd tried and failed
+ to rewrite the lease database. If we proceed and the
+ problem which caused the rewrite to fail has been fixed, but no
+ human has corrected the database problem, then we are left
+ thinking that no leases have been assigned to anybody, which
+ could create severe network chaos. */
+ if ((cfile = fopen (path_dhcpd_db, "r")) == NULL) {
+ warn ("Can't open lease database %s: %m -- %s",
+ path_dhcpd_db,
+ "check for failed database rewrite attempt!");
+ warn ("Please read the dhcpd.leases manual page if you.");
+ error ("don't know what to do about this."); }
+
+ do {
+ token = next_token (&val, cfile);
+ if (token == EOF)
+ break;
+ if (token != LEASE) {
+ warn ("Corrupt lease file - possible data loss!");
+ skip_to_semi (cfile);
+ } else {
+ struct lease *lease;
+ lease = parse_lease_declaration (cfile);
+ if (lease)
+ enter_lease (lease);
+ else
+ parse_warn ("possibly corrupt lease file");
+ }
+
+ } while (1);
+ fclose(cfile);
+}
+
+/* statement :== parameter | declaration
+
+ parameter :== timestamp
+ | DEFAULT_LEASE_TIME lease_time
+ | MAX_LEASE_TIME lease_time
+ | DYNAMIC_BOOTP_LEASE_CUTOFF date
+ | DYNAMIC_BOOTP_LEASE_LENGTH lease_time
+ | BOOT_UNKNOWN_CLIENTS boolean
+ | ONE_LEASE_PER_CLIENT boolean
+ | GET_LEASE_HOSTNAMES boolean
+ | USE_HOST_DECL_NAME boolean
+ | NEXT_SERVER ip-addr-or-hostname SEMI
+ | option_parameter
+ | SERVER-IDENTIFIER ip-addr-or-hostname SEMI
+ | FILENAME string-parameter
+ | SERVER_NAME string-parameter
+ | hardware-parameter
+ | fixed-address-parameter
+ | ALLOW allow-deny-keyword
+ | DENY allow-deny-keyword
+ | USE_LEASE_ADDR_FOR_DEFAULT_ROUTE boolean
+
+ declaration :== host-declaration
+ | group-declaration
+ | shared-network-declaration
+ | subnet-declaration
+ | VENDOR_CLASS class-declaration
+ | USER_CLASS class-declaration
+ | RANGE address-range-declaration */
+
+int parse_statement (cfile, group, type, host_decl, declaration)
+ FILE *cfile;
+ struct group *group;
+ int type;
+ struct host_decl *host_decl;
+ int declaration;
+{
+ int token;
+ char *val;
+ struct shared_network *share;
+ char *t, *n;
+ struct tree *tree;
+ struct tree_cache *cache;
+ struct hardware hardware;
+
+ switch (next_token (&val, cfile)) {
+ case HOST:
+ if (type != HOST_DECL)
+ parse_host_declaration (cfile, group);
+ else {
+ parse_warn ("host declarations not allowed here.");
+ skip_to_semi (cfile);
+ }
+ return 1;
+
+ case GROUP:
+ if (type != HOST_DECL)
+ parse_group_declaration (cfile, group);
+ else {
+ parse_warn ("host declarations not allowed here.");
+ skip_to_semi (cfile);
+ }
+ return 1;
+
+ case TIMESTAMP:
+ parsed_time = parse_timestamp (cfile);
+ break;
+
+ case SHARED_NETWORK:
+ if (type == SHARED_NET_DECL ||
+ type == HOST_DECL ||
+ type == SUBNET_DECL) {
+ parse_warn ("shared-network parameters not %s.",
+ "allowed here");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ parse_shared_net_declaration (cfile, group);
+ return 1;
+
+ case SUBNET:
+ if (type == HOST_DECL || type == SUBNET_DECL) {
+ parse_warn ("subnet declarations not allowed here.");
+ skip_to_semi (cfile);
+ return 1;
+ }
+
+ /* If we're in a subnet declaration, just do the parse. */
+ if (group -> shared_network) {
+ parse_subnet_declaration (cfile,
+ group -> shared_network);
+ break;
+ }
+
+ /* Otherwise, cons up a fake shared network structure
+ and populate it with the lone subnet... */
+
+ share = new_shared_network ("parse_statement");
+ if (!share)
+ error ("No memory for shared subnet");
+ share -> group = clone_group (group, "parse_statement:subnet");
+ share -> group -> shared_network = share;
+
+ parse_subnet_declaration (cfile, share);
+
+ /* share -> subnets is the subnet we just parsed. */
+ if (share -> subnets) {
+ share -> interface =
+ share -> subnets -> interface;
+
+ /* Make the shared network name from network number. */
+ n = piaddr (share -> subnets -> net);
+ t = malloc (strlen (n) + 1);
+ if (!t)
+ error ("no memory for subnet name");
+ strlcpy (t, n, (strlen(n) + 1));
+ share -> name = t;
+
+ /* Copy the authoritative parameter from the subnet,
+ since there is no opportunity to declare it here. */
+ share -> group -> authoritative =
+ share -> subnets -> group -> authoritative;
+ enter_shared_network (share);
+ }
+ return 1;
+
+ case VENDOR_CLASS:
+ parse_class_declaration (cfile, group, 0);
+ return 1;
+
+ case USER_CLASS:
+ parse_class_declaration (cfile, group, 1);
+ return 1;
+
+ case DEFAULT_LEASE_TIME:
+ parse_lease_time (cfile, &group -> default_lease_time);
+ break;
+
+ case MAX_LEASE_TIME:
+ parse_lease_time (cfile, &group -> max_lease_time);
+ break;
+
+ case DYNAMIC_BOOTP_LEASE_CUTOFF:
+ group -> bootp_lease_cutoff = parse_date (cfile);
+ break;
+
+ case DYNAMIC_BOOTP_LEASE_LENGTH:
+ parse_lease_time (cfile, &group -> bootp_lease_length);
+ break;
+
+ case BOOT_UNKNOWN_CLIENTS:
+ if (type == HOST_DECL)
+ parse_warn ("boot-unknown-clients not allowed here.");
+ group -> boot_unknown_clients = parse_boolean (cfile);
+ break;
+
+ case ONE_LEASE_PER_CLIENT:
+ if (type == HOST_DECL)
+ parse_warn ("one-lease-per-client not allowed here.");
+ group -> one_lease_per_client = parse_boolean (cfile);
+ break;
+
+ case GET_LEASE_HOSTNAMES:
+ if (type == HOST_DECL)
+ parse_warn ("get-lease-hostnames not allowed here.");
+ group -> get_lease_hostnames = parse_boolean (cfile);
+ break;
+
+ case ALWAYS_REPLY_RFC1048:
+ group -> always_reply_rfc1048 = parse_boolean (cfile);
+ break;
+
+ case USE_HOST_DECL_NAMES:
+ if (type == HOST_DECL)
+ parse_warn ("use-host-decl-names not allowed here.");
+ group -> use_host_decl_names = parse_boolean (cfile);
+ break;
+
+ case USE_LEASE_ADDR_FOR_DEFAULT_ROUTE:
+ group -> use_lease_addr_for_default_route =
+ parse_boolean (cfile);
+ break;
+
+ case TOKEN_NOT:
+ token = next_token (&val, cfile);
+ switch (token) {
+ case AUTHORITATIVE:
+ if (type == HOST_DECL)
+ parse_warn ("authority makes no sense here.");
+ group -> authoritative = 0;
+ parse_semi (cfile);
+ break;
+ default:
+ parse_warn ("expecting assertion");
+ skip_to_semi (cfile);
+ break;
+ }
+ break;
+
+ case AUTHORITATIVE:
+ if (type == HOST_DECL)
+ parse_warn ("authority makes no sense here.");
+ group -> authoritative = 1;
+ parse_semi (cfile);
+ break;
+
+ case NEXT_SERVER:
+ tree = parse_ip_addr_or_hostname (cfile, 0);
+ if (!tree)
+ break;
+ cache = tree_cache (tree);
+ if (!tree_evaluate (cache))
+ error ("next-server is not known");
+ group -> next_server.len = 4;
+ memcpy (group -> next_server.iabuf,
+ cache -> value, group -> next_server.len);
+ parse_semi (cfile);
+ break;
+
+ case OPTION:
+ parse_option_param (cfile, group);
+ break;
+
+ case SERVER_IDENTIFIER:
+ tree = parse_ip_addr_or_hostname (cfile, 0);
+ if (!tree)
+ return declaration;
+ group -> options [DHO_DHCP_SERVER_IDENTIFIER] =
+ tree_cache (tree);
+ token = next_token (&val, cfile);
+ break;
+
+ case FILENAME:
+ group -> filename = parse_string (cfile);
+ break;
+
+ case SERVER_NAME:
+ group -> server_name = parse_string (cfile);
+ break;
+
+ case HARDWARE:
+ parse_hardware_param (cfile, &hardware);
+ if (host_decl)
+ host_decl -> interface = hardware;
+ else
+ parse_warn ("hardware address parameter %s",
+ "not allowed here.");
+ break;
+
+ case FIXED_ADDR:
+ cache = parse_fixed_addr_param (cfile);
+ if (host_decl)
+ host_decl -> fixed_addr = cache;
+ else
+ parse_warn ("fixed-address parameter not %s",
+ "allowed here.");
+ break;
+
+ case RANGE:
+ if (type != SUBNET_DECL || !group -> subnet) {
+ parse_warn ("range declaration not allowed here.");
+ skip_to_semi (cfile);
+ return declaration;
+ }
+ parse_address_range (cfile, group -> subnet);
+ return declaration;
+
+ case ALLOW:
+ parse_allow_deny (cfile, group, 1);
+ break;
+
+ case DENY:
+ parse_allow_deny (cfile, group, 0);
+ break;
+
+ default:
+ if (declaration)
+ parse_warn ("expecting a declaration.");
+ else
+ parse_warn ("expecting a parameter or declaration.");
+ skip_to_semi (cfile);
+ return declaration;
+ }
+
+ if (declaration) {
+ parse_warn ("parameters not allowed after first declaration.");
+ return 1;
+ }
+
+ return 0;
+}
+
+/* allow-deny-keyword :== BOOTP
+ | BOOTING
+ | DYNAMIC_BOOTP
+ | UNKNOWN_CLIENTS */
+
+void parse_allow_deny (cfile, group, flag)
+ FILE *cfile;
+ struct group *group;
+ int flag;
+{
+ int token;
+ char *val;
+
+ token = next_token (&val, cfile);
+ switch (token) {
+ case BOOTP:
+ group -> allow_bootp = flag;
+ break;
+
+ case BOOTING:
+ group -> allow_booting = flag;
+ break;
+
+ case DYNAMIC_BOOTP:
+ group -> dynamic_bootp = flag;
+ break;
+
+ case UNKNOWN_CLIENTS:
+ group -> boot_unknown_clients = flag;
+ break;
+
+ default:
+ parse_warn ("expecting allow/deny key");
+ skip_to_semi (cfile);
+ return;
+ }
+ parse_semi (cfile);
+}
+
+/* boolean :== ON SEMI | OFF SEMI | TRUE SEMI | FALSE SEMI */
+
+int parse_boolean (cfile)
+ FILE *cfile;
+{
+ int token;
+ char *val;
+ int rv;
+
+ token = next_token (&val, cfile);
+ if (!strcasecmp (val, "true")
+ || !strcasecmp (val, "on"))
+ rv = 1;
+ else if (!strcasecmp (val, "false")
+ || !strcasecmp (val, "off"))
+ rv = 0;
+ else {
+ parse_warn ("boolean value (true/false/on/off) expected");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ parse_semi (cfile);
+ return rv;
+}
+
+/* Expect a left brace; if there isn't one, skip over the rest of the
+ statement and return zero; otherwise, return 1. */
+
+int parse_lbrace (cfile)
+ FILE *cfile;
+{
+ int token;
+ char *val;
+
+ token = next_token (&val, cfile);
+ if (token != LBRACE) {
+ parse_warn ("expecting left brace.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ return 1;
+}
+
+
+/* host-declaration :== hostname RBRACE parameters declarations LBRACE */
+
+void parse_host_declaration (cfile, group)
+ FILE *cfile;
+ struct group *group;
+{
+ char *val;
+ int token;
+ struct host_decl *host;
+ char *name = parse_host_name (cfile);
+ int declaration = 0;
+
+ if (!name)
+ return;
+
+ host = (struct host_decl *)dmalloc (sizeof (struct host_decl),
+ "parse_host_declaration");
+ if (!host)
+ error ("can't allocate host decl struct %s.", name);
+
+ host -> name = name;
+ host -> group = clone_group (group, "parse_host_declaration");
+
+ if (!parse_lbrace (cfile))
+ return;
+
+ do {
+ token = peek_token (&val, cfile);
+ if (token == RBRACE) {
+ token = next_token (&val, cfile);
+ break;
+ }
+ if (token == EOF) {
+ token = next_token (&val, cfile);
+ parse_warn ("unexpected end of file");
+ break;
+ }
+ declaration = parse_statement (cfile, host -> group,
+ HOST_DECL, host,
+ declaration);
+ } while (1);
+
+ if (!host -> group -> options [DHO_HOST_NAME] &&
+ host -> group -> use_host_decl_names) {
+ host -> group -> options [DHO_HOST_NAME] =
+ new_tree_cache ("parse_host_declaration");
+ if (!host -> group -> options [DHO_HOST_NAME])
+ error ("can't allocate a tree cache for hostname.");
+ host -> group -> options [DHO_HOST_NAME] -> len =
+ strlen (name);
+ host -> group -> options [DHO_HOST_NAME] -> value =
+ (unsigned char *)name;
+ host -> group -> options [DHO_HOST_NAME] -> buf_size =
+ host -> group -> options [DHO_HOST_NAME] -> len;
+ host -> group -> options [DHO_HOST_NAME] -> timeout =
+ 0xFFFFFFFF;
+ host -> group -> options [DHO_HOST_NAME] -> tree =
+ (struct tree *)0;
+ }
+
+ enter_host (host);
+}
+
+/* class-declaration :== STRING LBRACE parameters declarations RBRACE
+*/
+
+void parse_class_declaration (cfile, group, type)
+ FILE *cfile;
+ struct group *group;
+ int type;
+{
+ char *val;
+ int token;
+ struct class *class;
+ int declaration = 0;
+
+ token = next_token (&val, cfile);
+ if (token != STRING) {
+ parse_warn ("Expecting class name");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ class = add_class (type, val);
+ if (!class)
+ error ("No memory for class %s.", val);
+ class -> group = clone_group (group, "parse_class_declaration");
+
+ if (!parse_lbrace (cfile))
+ return;
+
+ do {
+ token = peek_token (&val, cfile);
+ if (token == RBRACE) {
+ token = next_token (&val, cfile);
+ break;
+ } else if (token == EOF) {
+ token = next_token (&val, cfile);
+ parse_warn ("unexpected end of file");
+ break;
+ } else {
+ declaration = parse_statement (cfile, class -> group,
+ CLASS_DECL,
+ (struct host_decl *)0,
+ declaration);
+ }
+ } while (1);
+}
+
+/* shared-network-declaration :==
+ hostname LBRACE declarations parameters RBRACE */
+
+void parse_shared_net_declaration (cfile, group)
+ FILE *cfile;
+ struct group *group;
+{
+ char *val;
+ int token;
+ struct shared_network *share;
+ char *name;
+ int declaration = 0;
+
+ share = new_shared_network ("parse_shared_net_declaration");
+ if (!share)
+ error ("No memory for shared subnet");
+ share -> leases = (struct lease *)0;
+ share -> last_lease = (struct lease *)0;
+ share -> insertion_point = (struct lease *)0;
+ share -> next = (struct shared_network *)0;
+ share -> interface = (struct interface_info *)0;
+ share -> group = clone_group (group, "parse_shared_net_declaration");
+ share -> group -> shared_network = share;
+
+ /* Get the name of the shared network... */
+ token = peek_token (&val, cfile);
+ if (token == STRING) {
+ token = next_token (&val, cfile);
+
+ if (val [0] == 0) {
+ parse_warn ("zero-length shared network name");
+ val = "<no-name-given>";
+ }
+ name = malloc (strlen (val) + 1);
+ if (!name)
+ error ("no memory for shared network name");
+ strlcpy (name, val, strlen(val) + 1);
+ } else {
+ name = parse_host_name (cfile);
+ if (!name)
+ return;
+ }
+ share -> name = name;
+
+ if (!parse_lbrace (cfile))
+ return;
+
+ do {
+ token = peek_token (&val, cfile);
+ if (token == RBRACE) {
+ token = next_token (&val, cfile);
+ if (!share -> subnets) {
+ parse_warn ("empty shared-network decl");
+ return;
+ }
+ enter_shared_network (share);
+ return;
+ } else if (token == EOF) {
+ token = next_token (&val, cfile);
+ parse_warn ("unexpected end of file");
+ break;
+ }
+
+ declaration = parse_statement (cfile, share -> group,
+ SHARED_NET_DECL,
+ (struct host_decl *)0,
+ declaration);
+ } while (1);
+}
+
+/* subnet-declaration :==
+ net NETMASK netmask RBRACE parameters declarations LBRACE */
+
+void parse_subnet_declaration (cfile, share)
+ FILE *cfile;
+ struct shared_network *share;
+{
+ char *val;
+ int token;
+ struct subnet *subnet, *t, *u;
+ struct iaddr iaddr;
+ unsigned char addr [4];
+ int len = sizeof addr;
+ int declaration = 0;
+
+ subnet = new_subnet ("parse_subnet_declaration");
+ if (!subnet)
+ error ("No memory for new subnet");
+ subnet -> shared_network = share;
+ subnet -> group = clone_group (share -> group,
+ "parse_subnet_declaration");
+ subnet -> group -> subnet = subnet;
+
+ /* Get the network number... */
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return;
+ memcpy (iaddr.iabuf, addr, len);
+ iaddr.len = len;
+ subnet -> net = iaddr;
+
+ token = next_token (&val, cfile);
+ if (token != NETMASK) {
+ parse_warn ("Expecting netmask");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ /* Get the netmask... */
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return;
+ memcpy (iaddr.iabuf, addr, len);
+ iaddr.len = len;
+ subnet -> netmask = iaddr;
+
+ enter_subnet (subnet);
+
+ if (!parse_lbrace (cfile))
+ return;
+
+ do {
+ token = peek_token (&val, cfile);
+ if (token == RBRACE) {
+ token = next_token (&val, cfile);
+ break;
+ } else if (token == EOF) {
+ token = next_token (&val, cfile);
+ parse_warn ("unexpected end of file");
+ break;
+ }
+ declaration = parse_statement (cfile, subnet -> group,
+ SUBNET_DECL,
+ (struct host_decl *)0,
+ declaration);
+ } while (1);
+
+ /* If this subnet supports dynamic bootp, flag it so in the
+ shared_network containing it. */
+ if (subnet -> group -> dynamic_bootp)
+ share -> group -> dynamic_bootp = 1;
+ if (subnet -> group -> one_lease_per_client)
+ share -> group -> one_lease_per_client = 1;
+
+ /* Add the subnet to the list of subnets in this shared net. */
+ if (!share -> subnets)
+ share -> subnets = subnet;
+ else {
+ u = (struct subnet *)0;
+ for (t = share -> subnets; t; t = t -> next_sibling) {
+ if (subnet_inner_than (subnet, t, 0)) {
+ if (u)
+ u -> next_sibling = subnet;
+ else
+ share -> subnets = subnet;
+ subnet -> next_sibling = t;
+ return;
+ }
+ u = t;
+ }
+ u -> next_sibling = subnet;
+ }
+}
+
+/* group-declaration :== RBRACE parameters declarations LBRACE */
+
+void parse_group_declaration (cfile, group)
+ FILE *cfile;
+ struct group *group;
+{
+ char *val;
+ int token;
+ struct group *g;
+ int declaration = 0;
+
+ g = clone_group (group, "parse_group_declaration");
+
+ if (!parse_lbrace (cfile))
+ return;
+
+ do {
+ token = peek_token (&val, cfile);
+ if (token == RBRACE) {
+ token = next_token (&val, cfile);
+ break;
+ } else if (token == EOF) {
+ token = next_token (&val, cfile);
+ parse_warn ("unexpected end of file");
+ break;
+ }
+ declaration = parse_statement (cfile, g, GROUP_DECL,
+ (struct host_decl *)0,
+ declaration);
+ } while (1);
+}
+
+/* ip-addr-or-hostname :== ip-address | hostname
+ ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
+
+ Parse an ip address or a hostname. If uniform is zero, put in
+ a TREE_LIMIT node to catch hostnames that evaluate to more than
+ one IP address. */
+
+struct tree *parse_ip_addr_or_hostname (cfile, uniform)
+ FILE *cfile;
+ int uniform;
+{
+ char *val;
+ int token;
+ unsigned char addr [4];
+ int len = sizeof addr;
+ char *name;
+ struct tree *rv;
+
+ token = peek_token (&val, cfile);
+ if (is_identifier (token)) {
+ name = parse_host_name (cfile);
+ if (!name)
+ return (struct tree *)0;
+ rv = tree_host_lookup (name);
+ if (!uniform)
+ rv = tree_limit (rv, 4);
+ } else if (token == NUMBER) {
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return (struct tree *)0;
+ rv = tree_const (addr, len);
+ } else {
+ if (token != RBRACE && token != LBRACE)
+ token = next_token (&val, cfile);
+ parse_warn ("%s (%d): expecting IP address or hostname",
+ val, token);
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (struct tree *)0;
+ }
+
+ return rv;
+}
+
+
+/* fixed-addr-parameter :== ip-addrs-or-hostnames SEMI
+ ip-addrs-or-hostnames :== ip-addr-or-hostname
+ | ip-addrs-or-hostnames ip-addr-or-hostname */
+
+struct tree_cache *parse_fixed_addr_param (cfile)
+ FILE *cfile;
+{
+ char *val;
+ int token;
+ struct tree *tree = (struct tree *)0;
+ struct tree *tmp;
+
+ do {
+ tmp = parse_ip_addr_or_hostname (cfile, 0);
+ if (tree)
+ tree = tree_concat (tree, tmp);
+ else
+ tree = tmp;
+ token = peek_token (&val, cfile);
+ if (token == COMMA)
+ token = next_token (&val, cfile);
+ } while (token == COMMA);
+
+ if (!parse_semi (cfile))
+ return (struct tree_cache *)0;
+ return tree_cache (tree);
+}
+
+/* option_parameter :== identifier DOT identifier <syntax> SEMI
+ | identifier <syntax> SEMI
+
+ Option syntax is handled specially through format strings, so it
+ would be painful to come up with BNF for it. However, it always
+ starts as above and ends in a SEMI. */
+
+void parse_option_param (cfile, group)
+ FILE *cfile;
+ struct group *group;
+{
+ char *val;
+ int token;
+ unsigned char buf [4];
+ char *vendor;
+ char *fmt;
+ struct universe *universe;
+ struct option *option;
+ struct tree *tree = (struct tree *)0;
+ struct tree *t;
+
+ token = next_token (&val, cfile);
+ if (!is_identifier (token)) {
+ parse_warn ("expecting identifier after option keyword.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return;
+ }
+ vendor = malloc (strlen (val) + 1);
+ if (!vendor)
+ error ("no memory for vendor token.");
+ strlcpy (vendor, val, strlen(val) + 1);
+ token = peek_token (&val, cfile);
+ if (token == DOT) {
+ /* Go ahead and take the DOT token... */
+ token = next_token (&val, cfile);
+
+ /* The next token should be an identifier... */
+ token = next_token (&val, cfile);
+ if (!is_identifier (token)) {
+ parse_warn ("expecting identifier after '.'");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return;
+ }
+
+ /* Look up the option name hash table for the specified
+ vendor. */
+ universe = ((struct universe *)
+ hash_lookup (&universe_hash,
+ (unsigned char *)vendor, 0));
+ /* If it's not there, we can't parse the rest of the
+ declaration. */
+ if (!universe) {
+ parse_warn ("no vendor named %s.", vendor);
+ skip_to_semi (cfile);
+ return;
+ }
+ } else {
+ /* Use the default hash table, which contains all the
+ standard dhcp option names. */
+ val = vendor;
+ universe = &dhcp_universe;
+ }
+
+ /* Look up the actual option info... */
+ option = (struct option *)hash_lookup (universe -> hash,
+ (unsigned char *)val, 0);
+
+ /* If we didn't get an option structure, it's an undefined option. */
+ if (!option) {
+ if (val == vendor)
+ parse_warn ("no option named %s", val);
+ else
+ parse_warn ("no option named %s for vendor %s",
+ val, vendor);
+ skip_to_semi (cfile);
+ return;
+ }
+
+ /* Free the initial identifier token. */
+ free (vendor);
+
+ /* Parse the option data... */
+ do {
+ /* Set a flag if this is an array of a simple type (i.e.,
+ not an array of pairs of IP addresses, or something
+ like that. */
+ int uniform = option -> format [1] == 'A';
+
+ for (fmt = option -> format; *fmt; fmt++) {
+ if (*fmt == 'A')
+ break;
+ switch (*fmt) {
+ case 'X':
+ token = peek_token (&val, cfile);
+ if (token == NUMBER_OR_NAME ||
+ token == NUMBER) {
+ do {
+ token = next_token
+ (&val, cfile);
+ if (token != NUMBER
+ && token != NUMBER_OR_NAME)
+ goto need_number;
+ convert_num (buf, val, 16, 8);
+ tree = tree_concat
+ (tree,
+ tree_const (buf, 1));
+ token = peek_token
+ (&val, cfile);
+ if (token == COLON)
+ token = next_token
+ (&val, cfile);
+ } while (token == COLON);
+ } else if (token == STRING) {
+ token = next_token (&val, cfile);
+ tree = tree_concat
+ (tree,
+ tree_const ((unsigned char *)
+ val,
+ strlen (val)));
+ } else {
+ parse_warn ("expecting string %s.",
+ "or hexadecimal data");
+ skip_to_semi (cfile);
+ return;
+ }
+ break;
+
+ case 't': /* Text string... */
+ token = next_token (&val, cfile);
+ if (token != STRING
+ && !is_identifier (token)) {
+ parse_warn ("expecting string.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return;
+ }
+ tree = tree_concat
+ (tree,
+ tree_const ((unsigned char *)val,
+ strlen (val)));
+ break;
+
+ case 'I': /* IP address or hostname. */
+ t = parse_ip_addr_or_hostname (cfile, uniform);
+ if (!t)
+ return;
+ tree = tree_concat (tree, t);
+ break;
+
+ case 'L': /* Unsigned 32-bit integer... */
+ case 'l': /* Signed 32-bit integer... */
+ token = next_token (&val, cfile);
+ if (token != NUMBER) {
+ need_number:
+ parse_warn ("expecting number.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return;
+ }
+ convert_num (buf, val, 0, 32);
+ tree = tree_concat (tree, tree_const (buf, 4));
+ break;
+ case 's': /* Signed 16-bit integer. */
+ case 'S': /* Unsigned 16-bit integer. */
+ token = next_token (&val, cfile);
+ if (token != NUMBER)
+ goto need_number;
+ convert_num (buf, val, 0, 16);
+ tree = tree_concat (tree, tree_const (buf, 2));
+ break;
+ case 'b': /* Signed 8-bit integer. */
+ case 'B': /* Unsigned 8-bit integer. */
+ token = next_token (&val, cfile);
+ if (token != NUMBER)
+ goto need_number;
+ convert_num (buf, val, 0, 8);
+ tree = tree_concat (tree, tree_const (buf, 1));
+ break;
+ case 'f': /* Boolean flag. */
+ token = next_token (&val, cfile);
+ if (!is_identifier (token)) {
+ parse_warn ("expecting identifier.");
+ bad_flag:
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return;
+ }
+ if (!strcasecmp (val, "true")
+ || !strcasecmp (val, "on"))
+ buf [0] = 1;
+ else if (!strcasecmp (val, "false")
+ || !strcasecmp (val, "off"))
+ buf [0] = 0;
+ else {
+ parse_warn ("expecting boolean.");
+ goto bad_flag;
+ }
+ tree = tree_concat (tree, tree_const (buf, 1));
+ break;
+ default:
+ warn ("Bad format %c in parse_option_param.",
+ *fmt);
+ skip_to_semi (cfile);
+ return;
+ }
+ }
+ if (*fmt == 'A') {
+ token = peek_token (&val, cfile);
+ if (token == COMMA) {
+ token = next_token (&val, cfile);
+ continue;
+ }
+ break;
+ }
+ } while (*fmt == 'A');
+
+ token = next_token (&val, cfile);
+ if (token != SEMI) {
+ parse_warn ("semicolon expected.");
+ skip_to_semi (cfile);
+ return;
+ }
+ group -> options [option -> code] = tree_cache (tree);
+}
+
+/* timestamp :== date
+
+ Timestamps are actually not used in dhcpd.conf, which is a static file,
+ but rather in the database file and the journal file. (Okay, actually
+ they're not even used there yet). */
+
+TIME parse_timestamp (cfile)
+ FILE *cfile;
+{
+ TIME rv;
+
+ rv = parse_date (cfile);
+ return rv;
+}
+
+/* lease_declaration :== LEASE ip_address LBRACE lease_parameters RBRACE
+
+ lease_parameters :== <nil>
+ | lease_parameter
+ | lease_parameters lease_parameter
+
+ lease_parameter :== STARTS date
+ | ENDS date
+ | TIMESTAMP date
+ | HARDWARE hardware-parameter
+ | UID hex_numbers SEMI
+ | HOSTNAME hostname SEMI
+ | CLIENT_HOSTNAME hostname SEMI
+ | CLASS identifier SEMI
+ | DYNAMIC_BOOTP SEMI */
+
+struct lease *parse_lease_declaration (cfile)
+ FILE *cfile;
+{
+ char *val;
+ int token;
+ unsigned char addr [4];
+ int len = sizeof addr;
+ int seenmask = 0;
+ int seenbit;
+ char tbuf [32];
+ static struct lease lease;
+
+ /* Zap the lease structure... */
+ memset (&lease, 0, sizeof lease);
+
+ /* Get the address for which the lease has been issued. */
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return (struct lease *)0;
+ memcpy (lease.ip_addr.iabuf, addr, len);
+ lease.ip_addr.len = len;
+
+ if (!parse_lbrace (cfile))
+ return (struct lease *)0;
+
+ do {
+ token = next_token (&val, cfile);
+ if (token == RBRACE)
+ break;
+ else if (token == EOF) {
+ parse_warn ("unexpected end of file");
+ break;
+ }
+ strlcpy (tbuf, val, sizeof tbuf);
+
+ /* Parse any of the times associated with the lease. */
+ if (token == STARTS || token == ENDS || token == TIMESTAMP) {
+ TIME t;
+ t = parse_date (cfile);
+ switch (token) {
+ case STARTS:
+ seenbit = 1;
+ lease.starts = t;
+ break;
+
+ case ENDS:
+ seenbit = 2;
+ lease.ends = t;
+ break;
+
+ case TIMESTAMP:
+ seenbit = 4;
+ lease.timestamp = t;
+ break;
+
+ default:
+ /*NOTREACHED*/
+ seenbit = 0;
+ break;
+ }
+ } else {
+ switch (token) {
+ /* Colon-separated hexadecimal octets... */
+ case UID:
+ seenbit = 8;
+ token = peek_token (&val, cfile);
+ if (token == STRING) {
+ token = next_token (&val, cfile);
+ lease.uid_len = strlen (val);
+ lease.uid = (unsigned char *)
+ malloc (lease.uid_len);
+ if (!lease.uid) {
+ warn ("no space for uid");
+ return (struct lease *)0;
+ }
+ memcpy (lease.uid, val, lease.uid_len);
+ parse_semi (cfile);
+ } else {
+ lease.uid_len = 0;
+ lease.uid = parse_numeric_aggregate
+ (cfile, (unsigned char *)0,
+ &lease.uid_len, ':', 16, 8);
+ if (!lease.uid) {
+ warn ("no space for uid");
+ return (struct lease *)0;
+ }
+ if (lease.uid_len == 0) {
+ lease.uid = (unsigned char *)0;
+ parse_warn ("zero-length uid");
+ seenbit = 0;
+ break;
+ }
+ }
+ if (!lease.uid) {
+ error ("No memory for lease uid");
+ }
+ break;
+
+ case CLASS:
+ seenbit = 32;
+ token = next_token (&val, cfile);
+ if (!is_identifier (token)) {
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (struct lease *)0;
+ }
+ /* for now, we aren't using this. */
+ break;
+
+ case HARDWARE:
+ seenbit = 64;
+ parse_hardware_param (cfile,
+ &lease.hardware_addr);
+ break;
+
+ case DYNAMIC_BOOTP:
+ seenbit = 128;
+ lease.flags |= BOOTP_LEASE;
+ break;
+
+ case ABANDONED:
+ seenbit = 256;
+ lease.flags |= ABANDONED_LEASE;
+ break;
+
+ case HOSTNAME:
+ seenbit = 512;
+ token = peek_token (&val, cfile);
+ if (token == STRING)
+ lease.hostname = parse_string (cfile);
+ else
+ lease.hostname =
+ parse_host_name (cfile);
+ if (!lease.hostname) {
+ seenbit = 0;
+ return (struct lease *)0;
+ }
+ break;
+
+ case CLIENT_HOSTNAME:
+ seenbit = 1024;
+ token = peek_token (&val, cfile);
+ if (token == STRING)
+ lease.client_hostname =
+ parse_string (cfile);
+ else
+ lease.client_hostname =
+ parse_host_name (cfile);
+ break;
+
+ default:
+ skip_to_semi (cfile);
+ seenbit = 0;
+ return (struct lease *)0;
+ }
+
+ if (token != HARDWARE && token != STRING) {
+ token = next_token (&val, cfile);
+ if (token != SEMI) {
+ parse_warn ("semicolon expected.");
+ skip_to_semi (cfile);
+ return (struct lease *)0;
+ }
+ }
+ }
+ if (seenmask & seenbit) {
+ parse_warn ("Too many %s parameters in lease %s\n",
+ tbuf, piaddr (lease.ip_addr));
+ } else
+ seenmask |= seenbit;
+
+ } while (1);
+ return &lease;
+}
+
+/* address-range-declaration :== ip-address ip-address SEMI
+ | DYNAMIC_BOOTP ip-address ip-address SEMI */
+
+void parse_address_range (cfile, subnet)
+ FILE *cfile;
+ struct subnet *subnet;
+{
+ struct iaddr low, high;
+ unsigned char addr [4];
+ int len = sizeof addr;
+ int token;
+ char *val;
+ int dynamic = 0;
+
+ if ((token = peek_token (&val, cfile)) == DYNAMIC_BOOTP) {
+ token = next_token (&val, cfile);
+ subnet -> group -> dynamic_bootp = dynamic = 1;
+ }
+
+ /* Get the bottom address in the range... */
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return;
+ memcpy (low.iabuf, addr, len);
+ low.len = len;
+
+ /* Only one address? */
+ token = peek_token (&val, cfile);
+ if (token == SEMI)
+ high = low;
+ else {
+ /* Get the top address in the range... */
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return;
+ memcpy (high.iabuf, addr, len);
+ high.len = len;
+ }
+
+ token = next_token (&val, cfile);
+ if (token != SEMI) {
+ parse_warn ("semicolon expected.");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ /* Create the new address range... */
+ new_address_range (low, high, subnet, dynamic);
+}
+
+