diff options
author | brooks <brooks@FreeBSD.org> | 2005-06-07 04:05:09 +0000 |
---|---|---|
committer | brooks <brooks@FreeBSD.org> | 2005-06-07 04:05:09 +0000 |
commit | 9066b3f8349915bcffc1bbec3e08bc088b966f30 (patch) | |
tree | 15f626e3cc39d6ff210e3d5ceda54c655c46dbdc /sbin/dhclient/clparse.c | |
parent | 278fb1a5b03afd90c559d3c6ea91fa325b05b245 (diff) | |
download | FreeBSD-src-9066b3f8349915bcffc1bbec3e08bc088b966f30.zip FreeBSD-src-9066b3f8349915bcffc1bbec3e08bc088b966f30.tar.gz |
Import the OpenBSD dhclient as shipped with OpenBSD-3.7 (the tag
OPENBSD_3_7).
Diffstat (limited to 'sbin/dhclient/clparse.c')
-rw-r--r-- | sbin/dhclient/clparse.c | 938 |
1 files changed, 938 insertions, 0 deletions
diff --git a/sbin/dhclient/clparse.c b/sbin/dhclient/clparse.c new file mode 100644 index 0000000..2777902 --- /dev/null +++ b/sbin/dhclient/clparse.c @@ -0,0 +1,938 @@ +/* $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 "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_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); + } +} |