summaryrefslogtreecommitdiffstats
path: root/freebsd/sbin/dhclient/clparse.c
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2013-10-09 22:42:09 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2013-10-10 09:06:58 +0200
commitbceabc95c1c85d793200446fa85f1ddc6313ea29 (patch)
tree973c8bd8deca9fd69913f2895cc91e0e6114d46c /freebsd/sbin/dhclient/clparse.c
parentAdd FreeBSD sources as a submodule (diff)
downloadrtems-libbsd-bceabc95c1c85d793200446fa85f1ddc6313ea29.tar.bz2
Move files to match FreeBSD layout
Diffstat (limited to 'freebsd/sbin/dhclient/clparse.c')
-rw-r--r--freebsd/sbin/dhclient/clparse.c943
1 files changed, 943 insertions, 0 deletions
diff --git a/freebsd/sbin/dhclient/clparse.c b/freebsd/sbin/dhclient/clparse.c
new file mode 100644
index 00000000..5d7084e7
--- /dev/null
+++ b/freebsd/sbin/dhclient/clparse.c
@@ -0,0 +1,943 @@
+/* $OpenBSD: clparse.c,v 1.18 2004/09/15 18:15:18 henning Exp $ */
+
+/* Parser for dhclient config and lease files... */
+
+/*
+ * Copyright (c) 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "dhcpd.h"
+#include "dhctoken.h"
+
+struct client_config top_level_config;
+struct interface_info *dummy_interfaces;
+extern struct interface_info *ifi;
+
+char client_script_name[] = "/sbin/dhclient-script";
+
+/*
+ * client-conf-file :== client-declarations EOF
+ * client-declarations :== <nil>
+ * | client-declaration
+ * | client-declarations client-declaration
+ */
+int
+read_client_conf(void)
+{
+ FILE *cfile;
+ char *val;
+ int token;
+ struct client_config *config;
+
+ new_parse(path_dhclient_conf);
+
+ /* Set up the initial dhcp option universe. */
+ initialize_universes();
+
+ /* Initialize the top level client configuration. */
+ memset(&top_level_config, 0, sizeof(top_level_config));
+
+ /* Set some defaults... */
+ top_level_config.timeout = 60;
+ top_level_config.select_interval = 0;
+ top_level_config.reboot_timeout = 10;
+ top_level_config.retry_interval = 300;
+ top_level_config.backoff_cutoff = 15;
+ top_level_config.initial_interval = 3;
+ top_level_config.bootp_policy = ACCEPT;
+ top_level_config.script_name = client_script_name;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_SUBNET_MASK;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_BROADCAST_ADDRESS;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_TIME_OFFSET;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_CLASSLESS_ROUTES;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_ROUTERS;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_DOMAIN_NAME;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] =
+ DHO_DOMAIN_NAME_SERVERS;
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_HOST_NAME;
+
+ if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
+ do {
+ token = peek_token(&val, cfile);
+ if (token == EOF)
+ break;
+ parse_client_statement(cfile, NULL, &top_level_config);
+ } while (1);
+ token = next_token(&val, cfile); /* Clear the peek buffer */
+ fclose(cfile);
+ }
+
+ /*
+ * Set up state and config structures for clients that don't
+ * have per-interface configuration declarations.
+ */
+ config = NULL;
+ if (!ifi->client) {
+ ifi->client = malloc(sizeof(struct client_state));
+ if (!ifi->client)
+ error("no memory for client state.");
+ memset(ifi->client, 0, sizeof(*(ifi->client)));
+ }
+ if (!ifi->client->config) {
+ if (!config) {
+ config = malloc(sizeof(struct client_config));
+ if (!config)
+ error("no memory for client config.");
+ memcpy(config, &top_level_config,
+ sizeof(top_level_config));
+ }
+ ifi->client->config = config;
+ }
+
+ return (!warnings_occurred);
+}
+
+/*
+ * lease-file :== client-lease-statements EOF
+ * client-lease-statements :== <nil>
+ * | client-lease-statements LEASE client-lease-statement
+ */
+void
+read_client_leases(void)
+{
+ FILE *cfile;
+ char *val;
+ int token;
+
+ new_parse(path_dhclient_db);
+
+ /* Open the lease file. If we can't open it, just return -
+ we can safely trust the server to remember our state. */
+ if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
+ return;
+ do {
+ token = next_token(&val, cfile);
+ if (token == EOF)
+ break;
+ if (token != LEASE) {
+ warning("Corrupt lease file - possible data loss!");
+ skip_to_semi(cfile);
+ break;
+ } else
+ parse_client_lease_statement(cfile, 0);
+
+ } while (1);
+ fclose(cfile);
+}
+
+/*
+ * client-declaration :==
+ * SEND option-decl |
+ * DEFAULT option-decl |
+ * SUPERSEDE option-decl |
+ * PREPEND option-decl |
+ * APPEND option-decl |
+ * hardware-declaration |
+ * REQUEST option-list |
+ * REQUIRE option-list |
+ * TIMEOUT number |
+ * RETRY number |
+ * REBOOT number |
+ * SELECT_TIMEOUT number |
+ * SCRIPT string |
+ * interface-declaration |
+ * LEASE client-lease-statement |
+ * ALIAS client-lease-statement
+ */
+void
+parse_client_statement(FILE *cfile, struct interface_info *ip,
+ struct client_config *config)
+{
+ int token;
+ char *val;
+ struct option *option;
+
+ switch (next_token(&val, cfile)) {
+ case SEND:
+ parse_option_decl(cfile, &config->send_options[0]);
+ return;
+ case DEFAULT:
+ option = parse_option_decl(cfile, &config->defaults[0]);
+ if (option)
+ config->default_actions[option->code] = ACTION_DEFAULT;
+ return;
+ case SUPERSEDE:
+ option = parse_option_decl(cfile, &config->defaults[0]);
+ if (option)
+ config->default_actions[option->code] =
+ ACTION_SUPERSEDE;
+ return;
+ case APPEND:
+ option = parse_option_decl(cfile, &config->defaults[0]);
+ if (option)
+ config->default_actions[option->code] = ACTION_APPEND;
+ return;
+ case PREPEND:
+ option = parse_option_decl(cfile, &config->defaults[0]);
+ if (option)
+ config->default_actions[option->code] = ACTION_PREPEND;
+ return;
+ case MEDIA:
+ parse_string_list(cfile, &config->media, 1);
+ return;
+ case HARDWARE:
+ if (ip)
+ parse_hardware_param(cfile, &ip->hw_address);
+ else {
+ parse_warn("hardware address parameter %s",
+ "not allowed here.");
+ skip_to_semi(cfile);
+ }
+ return;
+ case REQUEST:
+ config->requested_option_count =
+ parse_option_list(cfile, config->requested_options);
+ return;
+ case REQUIRE:
+ memset(config->required_options, 0,
+ sizeof(config->required_options));
+ parse_option_list(cfile, config->required_options);
+ return;
+ case TIMEOUT:
+ parse_lease_time(cfile, &config->timeout);
+ return;
+ case RETRY:
+ parse_lease_time(cfile, &config->retry_interval);
+ return;
+ case SELECT_TIMEOUT:
+ parse_lease_time(cfile, &config->select_interval);
+ return;
+ case REBOOT:
+ parse_lease_time(cfile, &config->reboot_timeout);
+ return;
+ case BACKOFF_CUTOFF:
+ parse_lease_time(cfile, &config->backoff_cutoff);
+ return;
+ case INITIAL_INTERVAL:
+ parse_lease_time(cfile, &config->initial_interval);
+ return;
+ case SCRIPT:
+ config->script_name = parse_string(cfile);
+ return;
+ case INTERFACE:
+ if (ip)
+ parse_warn("nested interface declaration.");
+ parse_interface_declaration(cfile, config);
+ return;
+ case LEASE:
+ parse_client_lease_statement(cfile, 1);
+ return;
+ case ALIAS:
+ parse_client_lease_statement(cfile, 2);
+ return;
+ case REJECT:
+ parse_reject_statement(cfile, config);
+ return;
+ default:
+ parse_warn("expecting a statement.");
+ skip_to_semi(cfile);
+ break;
+ }
+ token = next_token(&val, cfile);
+ if (token != SEMI) {
+ parse_warn("semicolon expected.");
+ skip_to_semi(cfile);
+ }
+}
+
+int
+parse_X(FILE *cfile, u_int8_t *buf, int max)
+{
+ int token;
+ char *val;
+ int len;
+
+ token = peek_token(&val, cfile);
+ if (token == NUMBER_OR_NAME || token == NUMBER) {
+ len = 0;
+ do {
+ token = next_token(&val, cfile);
+ if (token != NUMBER && token != NUMBER_OR_NAME) {
+ parse_warn("expecting hexadecimal constant.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ convert_num(&buf[len], val, 16, 8);
+ if (len++ > max) {
+ parse_warn("hexadecimal constant too long.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ token = peek_token(&val, cfile);
+ if (token == COLON)
+ token = next_token(&val, cfile);
+ } while (token == COLON);
+ val = (char *)buf;
+ } else if (token == STRING) {
+ token = next_token(&val, cfile);
+ len = strlen(val);
+ if (len + 1 > max) {
+ parse_warn("string constant too long.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ memcpy(buf, val, len + 1);
+ } else {
+ parse_warn("expecting string or hexadecimal data");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ return (len);
+}
+
+/*
+ * option-list :== option_name |
+ * option_list COMMA option_name
+ */
+int
+parse_option_list(FILE *cfile, u_int8_t *list)
+{
+ int ix, i;
+ int token;
+ char *val;
+
+ ix = 0;
+ do {
+ token = next_token(&val, cfile);
+ if (!is_identifier(token)) {
+ parse_warn("expected option name.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ for (i = 0; i < 256; i++)
+ if (!strcasecmp(dhcp_options[i].name, val))
+ break;
+
+ if (i == 256) {
+ parse_warn("%s: unexpected option name.", val);
+ skip_to_semi(cfile);
+ return (0);
+ }
+ list[ix++] = i;
+ if (ix == 256) {
+ parse_warn("%s: too many options.", val);
+ skip_to_semi(cfile);
+ return (0);
+ }
+ token = next_token(&val, cfile);
+ } while (token == COMMA);
+ if (token != SEMI) {
+ parse_warn("expecting semicolon.");
+ skip_to_semi(cfile);
+ return (0);
+ }
+ return (ix);
+}
+
+/*
+ * interface-declaration :==
+ * INTERFACE string LBRACE client-declarations RBRACE
+ */
+void
+parse_interface_declaration(FILE *cfile, struct client_config *outer_config)
+{
+ int token;
+ char *val;
+ struct interface_info *ip;
+
+ token = next_token(&val, cfile);
+ if (token != STRING) {
+ parse_warn("expecting interface name (in quotes).");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ ip = interface_or_dummy(val);
+
+ if (!ip->client)
+ make_client_state(ip);
+
+ if (!ip->client->config)
+ make_client_config(ip, outer_config);
+
+ token = next_token(&val, cfile);
+ if (token != LBRACE) {
+ parse_warn("expecting left brace.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ do {
+ token = peek_token(&val, cfile);
+ if (token == EOF) {
+ parse_warn("unterminated interface declaration.");
+ return;
+ }
+ if (token == RBRACE)
+ break;
+ parse_client_statement(cfile, ip, ip->client->config);
+ } while (1);
+ token = next_token(&val, cfile);
+}
+
+struct interface_info *
+interface_or_dummy(char *name)
+{
+ struct interface_info *ip;
+
+ /* Find the interface (if any) that matches the name. */
+ if (!strcmp(ifi->name, name))
+ return (ifi);
+
+ /* If it's not a real interface, see if it's on the dummy list. */
+ for (ip = dummy_interfaces; ip; ip = ip->next)
+ if (!strcmp(ip->name, name))
+ return (ip);
+
+ /*
+ * If we didn't find an interface, make a dummy interface as a
+ * placeholder.
+ */
+ ip = malloc(sizeof(*ip));
+ if (!ip)
+ error("Insufficient memory to record interface %s", name);
+ memset(ip, 0, sizeof(*ip));
+ strlcpy(ip->name, name, IFNAMSIZ);
+ ip->next = dummy_interfaces;
+ dummy_interfaces = ip;
+ return (ip);
+}
+
+void
+make_client_state(struct interface_info *ip)
+{
+ ip->client = malloc(sizeof(*(ip->client)));
+ if (!ip->client)
+ error("no memory for state on %s", ip->name);
+ memset(ip->client, 0, sizeof(*(ip->client)));
+}
+
+void
+make_client_config(struct interface_info *ip, struct client_config *config)
+{
+ ip->client->config = malloc(sizeof(struct client_config));
+ if (!ip->client->config)
+ error("no memory for config for %s", ip->name);
+ memset(ip->client->config, 0, sizeof(*(ip->client->config)));
+ memcpy(ip->client->config, config, sizeof(*config));
+}
+
+/*
+ * client-lease-statement :==
+ * RBRACE client-lease-declarations LBRACE
+ *
+ * client-lease-declarations :==
+ * <nil> |
+ * client-lease-declaration |
+ * client-lease-declarations client-lease-declaration
+ */
+void
+parse_client_lease_statement(FILE *cfile, int is_static)
+{
+ struct client_lease *lease, *lp, *pl;
+ struct interface_info *ip;
+ int token;
+ char *val;
+
+ token = next_token(&val, cfile);
+ if (token != LBRACE) {
+ parse_warn("expecting left brace.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ lease = malloc(sizeof(struct client_lease));
+ if (!lease)
+ error("no memory for lease.");
+ memset(lease, 0, sizeof(*lease));
+ lease->is_static = is_static;
+
+ ip = NULL;
+
+ do {
+ token = peek_token(&val, cfile);
+ if (token == EOF) {
+ parse_warn("unterminated lease declaration.");
+ return;
+ }
+ if (token == RBRACE)
+ break;
+ parse_client_lease_declaration(cfile, lease, &ip);
+ } while (1);
+ token = next_token(&val, cfile);
+
+ /* If the lease declaration didn't include an interface
+ * declaration that we recognized, it's of no use to us.
+ */
+ if (!ip) {
+ free_client_lease(lease);
+ return;
+ }
+
+ /* Make sure there's a client state structure... */
+ if (!ip->client)
+ make_client_state(ip);
+
+ /* If this is an alias lease, it doesn't need to be sorted in. */
+ if (is_static == 2) {
+ ip->client->alias = lease;
+ return;
+ }
+
+ /*
+ * The new lease may supersede a lease that's not the active
+ * lease but is still on the lease list, so scan the lease list
+ * looking for a lease with the same address, and if we find it,
+ * toss it.
+ */
+ pl = NULL;
+ for (lp = ip->client->leases; lp; lp = lp->next) {
+ if (lp->address.len == lease->address.len &&
+ !memcmp(lp->address.iabuf, lease->address.iabuf,
+ lease->address.len)) {
+ if (pl)
+ pl->next = lp->next;
+ else
+ ip->client->leases = lp->next;
+ free_client_lease(lp);
+ break;
+ }
+ }
+
+ /*
+ * If this is a preloaded lease, just put it on the list of
+ * recorded leases - don't make it the active lease.
+ */
+ if (is_static) {
+ lease->next = ip->client->leases;
+ ip->client->leases = lease;
+ return;
+ }
+
+ /*
+ * The last lease in the lease file on a particular interface is
+ * the active lease for that interface. Of course, we don't
+ * know what the last lease in the file is until we've parsed
+ * the whole file, so at this point, we assume that the lease we
+ * just parsed is the active lease for its interface. If
+ * there's already an active lease for the interface, and this
+ * lease is for the same ip address, then we just toss the old
+ * active lease and replace it with this one. If this lease is
+ * for a different address, then if the old active lease has
+ * expired, we dump it; if not, we put it on the list of leases
+ * for this interface which are still valid but no longer
+ * active.
+ */
+ if (ip->client->active) {
+ if (ip->client->active->expiry < cur_time)
+ free_client_lease(ip->client->active);
+ else if (ip->client->active->address.len ==
+ lease->address.len &&
+ !memcmp(ip->client->active->address.iabuf,
+ lease->address.iabuf, lease->address.len))
+ free_client_lease(ip->client->active);
+ else {
+ ip->client->active->next = ip->client->leases;
+ ip->client->leases = ip->client->active;
+ }
+ }
+ ip->client->active = lease;
+
+ /* Phew. */
+}
+
+/*
+ * client-lease-declaration :==
+ * BOOTP |
+ * INTERFACE string |
+ * FIXED_ADDR ip_address |
+ * FILENAME string |
+ * SERVER_NAME string |
+ * OPTION option-decl |
+ * RENEW time-decl |
+ * REBIND time-decl |
+ * EXPIRE time-decl
+ */
+void
+parse_client_lease_declaration(FILE *cfile, struct client_lease *lease,
+ struct interface_info **ipp)
+{
+ int token;
+ char *val;
+ struct interface_info *ip;
+
+ switch (next_token(&val, cfile)) {
+ case BOOTP:
+ lease->is_bootp = 1;
+ break;
+ case INTERFACE:
+ token = next_token(&val, cfile);
+ if (token != STRING) {
+ parse_warn("expecting interface name (in quotes).");
+ skip_to_semi(cfile);
+ break;
+ }
+ ip = interface_or_dummy(val);
+ *ipp = ip;
+ break;
+ case FIXED_ADDR:
+ if (!parse_ip_addr(cfile, &lease->address))
+ return;
+ break;
+ case MEDIUM:
+ parse_string_list(cfile, &lease->medium, 0);
+ return;
+ case FILENAME:
+ lease->filename = parse_string(cfile);
+ return;
+ case SERVER_NAME:
+ lease->server_name = parse_string(cfile);
+ return;
+ case RENEW:
+ lease->renewal = parse_date(cfile);
+ return;
+ case REBIND:
+ lease->rebind = parse_date(cfile);
+ return;
+ case EXPIRE:
+ lease->expiry = parse_date(cfile);
+ return;
+ case OPTION:
+ parse_option_decl(cfile, lease->options);
+ return;
+ default:
+ parse_warn("expecting lease declaration.");
+ skip_to_semi(cfile);
+ break;
+ }
+ token = next_token(&val, cfile);
+ if (token != SEMI) {
+ parse_warn("expecting semicolon.");
+ skip_to_semi(cfile);
+ }
+}
+
+struct option *
+parse_option_decl(FILE *cfile, struct option_data *options)
+{
+ char *val;
+ int token;
+ u_int8_t buf[4];
+ u_int8_t hunkbuf[1024];
+ int hunkix = 0;
+ char *vendor;
+ char *fmt;
+ struct universe *universe;
+ struct option *option;
+ struct iaddr ip_addr;
+ u_int8_t *dp;
+ int len;
+ int nul_term = 0;
+
+ token = next_token(&val, cfile);
+ if (!is_identifier(token)) {
+ parse_warn("expecting identifier after option keyword.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ if ((vendor = strdup(val)) == NULL)
+ error("no memory for vendor information.");
+
+ 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 (NULL);
+ }
+
+ /* 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 (NULL);
+ }
+ } 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 (NULL);
+ }
+
+ /* Free the initial identifier token. */
+ free(vendor);
+
+ /* Parse the option data... */
+ do {
+ for (fmt = option->format; *fmt; fmt++) {
+ if (*fmt == 'A')
+ break;
+ switch (*fmt) {
+ case 'X':
+ len = parse_X(cfile, &hunkbuf[hunkix],
+ sizeof(hunkbuf) - hunkix);
+ hunkix += len;
+ break;
+ case 't': /* Text string... */
+ token = next_token(&val, cfile);
+ if (token != STRING) {
+ parse_warn("expecting string.");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ len = strlen(val);
+ if (hunkix + len + 1 > sizeof(hunkbuf)) {
+ parse_warn("option data buffer %s",
+ "overflow");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ memcpy(&hunkbuf[hunkix], val, len + 1);
+ nul_term = 1;
+ hunkix += len;
+ break;
+ case 'I': /* IP address. */
+ if (!parse_ip_addr(cfile, &ip_addr))
+ return (NULL);
+ len = ip_addr.len;
+ dp = ip_addr.iabuf;
+alloc:
+ if (hunkix + len > sizeof(hunkbuf)) {
+ parse_warn("option data buffer "
+ "overflow");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ memcpy(&hunkbuf[hunkix], dp, len);
+ hunkix += len;
+ 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 (NULL);
+ }
+ convert_num(buf, val, 0, 32);
+ len = 4;
+ dp = buf;
+ goto alloc;
+ 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);
+ len = 2;
+ dp = buf;
+ goto alloc;
+ 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);
+ len = 1;
+ dp = buf;
+ goto alloc;
+ 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 (NULL);
+ }
+ 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;
+ }
+ len = 1;
+ dp = buf;
+ goto alloc;
+ default:
+ warning("Bad format %c in parse_option_param.",
+ *fmt);
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+ }
+ token = next_token(&val, cfile);
+ } while (*fmt == 'A' && token == COMMA);
+
+ if (token != SEMI) {
+ parse_warn("semicolon expected.");
+ skip_to_semi(cfile);
+ return (NULL);
+ }
+
+ options[option->code].data = malloc(hunkix + nul_term);
+ if (!options[option->code].data)
+ error("out of memory allocating option data.");
+ memcpy(options[option->code].data, hunkbuf, hunkix + nul_term);
+ options[option->code].len = hunkix;
+ return (option);
+}
+
+void
+parse_string_list(FILE *cfile, struct string_list **lp, int multiple)
+{
+ int token;
+ char *val;
+ struct string_list *cur, *tmp;
+
+ /* Find the last medium in the media list. */
+ if (*lp)
+ for (cur = *lp; cur->next; cur = cur->next)
+ ; /* nothing */
+ else
+ cur = NULL;
+
+ do {
+ token = next_token(&val, cfile);
+ if (token != STRING) {
+ parse_warn("Expecting media options.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ tmp = new_string_list(strlen(val) + 1);
+ if (tmp == NULL)
+ error("no memory for string list entry.");
+ strlcpy(tmp->string, val, strlen(val) + 1);
+ tmp->next = NULL;
+
+ /* Store this medium at the end of the media list. */
+ if (cur)
+ cur->next = tmp;
+ else
+ *lp = tmp;
+ cur = tmp;
+
+ token = next_token(&val, cfile);
+ } while (multiple && token == COMMA);
+
+ if (token != SEMI) {
+ parse_warn("expecting semicolon.");
+ skip_to_semi(cfile);
+ }
+}
+
+void
+parse_reject_statement(FILE *cfile, struct client_config *config)
+{
+ int token;
+ char *val;
+ struct iaddr addr;
+ struct iaddrlist *list;
+
+ do {
+ if (!parse_ip_addr(cfile, &addr)) {
+ parse_warn("expecting IP address.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ list = malloc(sizeof(struct iaddrlist));
+ if (!list)
+ error("no memory for reject list!");
+
+ list->addr = addr;
+ list->next = config->reject_list;
+ config->reject_list = list;
+
+ token = next_token(&val, cfile);
+ } while (token == COMMA);
+
+ if (token != SEMI) {
+ parse_warn("expecting semicolon.");
+ skip_to_semi(cfile);
+ }
+}