#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <limits.h>
#include <netdb.h>
#include <resolv.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "dhcp.h"
#include "tree.h"
#include "dhcpd.h"
#include "dhctoken.h"
#include "log.h"
int parse_cidr(FILE *, unsigned char *, unsigned char *);
int
readconf(void)
{
FILE *cfile;
char *val;
int token;
int declaration = 0;
new_parse(path_dhcpd_conf);
initialize_universes();
root_group.default_lease_time = 43200;
root_group.max_lease_time = 86400;
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;
root_group.echo_client_id = 1;
if ((cfile = fopen(path_dhcpd_conf, "r")) == NULL)
fatal("Can't open %s", path_dhcpd_conf);
do {
token = peek_token(&val, cfile);
if (token == EOF)
break;
declaration = parse_statement(cfile, &root_group,
ROOT_GROUP,
NULL,
declaration);
} while (1);
token = next_token(&val, cfile);
fclose(cfile);
return !warnings_occurred;
}
void
read_leases(void)
{
FILE *cfile;
char *val;
int token;
new_parse(path_dhcpd_db);
if ((cfile = fopen(path_dhcpd_db, "r")) == NULL) {
log_warn("Can't open lease database (%s)", path_dhcpd_db);
log_warnx("check for failed database rewrite attempt!");
log_warnx("Please read the dhcpd.leases manual page if you");
fatalx("don't know what to do about this.");
}
do {
token = next_token(&val, cfile);
if (token == EOF)
break;
if (token != TOK_LEASE) {
log_warnx("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);
}
int
parse_statement(FILE *cfile, struct group *group, int type,
struct host_decl *host_decl, int declaration)
{
int token;
char *val;
struct shared_network *share;
char *n;
struct tree *tree;
struct tree_cache *cache;
struct hardware hardware;
switch (next_token(&val, cfile)) {
case TOK_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 TOK_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 TOK_TIMESTAMP:
break;
case TOK_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 TOK_SUBNET:
if (type == HOST_DECL || type == SUBNET_DECL) {
parse_warn("subnet declarations not allowed here.");
skip_to_semi(cfile);
return 1;
}
if (group->shared_network) {
parse_subnet_declaration(cfile,
group->shared_network);
break;
}
share = calloc(1, sizeof(struct shared_network));
if (!share)
fatalx("No memory for shared subnet");
share->group = clone_group(group, "parse_statement:subnet");
share->group->shared_network = share;
parse_subnet_declaration(cfile, share);
if (share->subnets) {
share->interface =
share->subnets->interface;
n = piaddr(share->subnets->net);
share->name = strdup(n);
if (share->name == NULL)
fatalx("no memory for subnet name");
share->group->authoritative =
share->subnets->group->authoritative;
enter_shared_network(share);
}
return 1;
case TOK_VENDOR_CLASS:
parse_class_declaration(cfile, group, 0);
return 1;
case TOK_USER_CLASS:
parse_class_declaration(cfile, group, 1);
return 1;
case TOK_DEFAULT_LEASE_TIME:
parse_lease_time(cfile, &group->default_lease_time);
break;
case TOK_MAX_LEASE_TIME:
parse_lease_time(cfile, &group->max_lease_time);
break;
case TOK_DYNAMIC_BOOTP_LEASE_CUTOFF:
group->bootp_lease_cutoff = parse_date(cfile);
break;
case TOK_DYNAMIC_BOOTP_LEASE_LENGTH:
parse_lease_time(cfile, &group->bootp_lease_length);
break;
case TOK_BOOT_UNKNOWN_CLIENTS:
if (type == HOST_DECL)
parse_warn("boot-unknown-clients not allowed here.");
group->boot_unknown_clients = parse_boolean(cfile);
break;
case TOK_GET_LEASE_HOSTNAMES:
if (type == HOST_DECL)
parse_warn("get-lease-hostnames not allowed here.");
group->get_lease_hostnames = parse_boolean(cfile);
break;
case TOK_ALWAYS_REPLY_RFC1048:
group->always_reply_rfc1048 = parse_boolean(cfile);
break;
case TOK_ECHO_CLIENT_ID:
group->echo_client_id = parse_boolean(cfile);
break;
case TOK_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 TOK_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE:
group->use_lease_addr_for_default_route =
parse_boolean(cfile);
break;
case TOK_TOKEN_NOT:
token = next_token(&val, cfile);
switch (token) {
case TOK_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 TOK_AUTHORITATIVE:
if (type == HOST_DECL)
parse_warn("authority makes no sense here.");
group->authoritative = 1;
parse_semi(cfile);
break;
case TOK_NEXT_SERVER:
tree = parse_ip_addr_or_hostname(cfile, 0);
if (!tree)
break;
cache = tree_cache(tree);
if (!tree_evaluate (cache))
fatalx("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 TOK_OPTION:
parse_option_param(cfile, group);
break;
case TOK_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 TOK_FILENAME:
group->filename = parse_string(cfile);
break;
case TOK_SERVER_NAME:
group->server_name = parse_string(cfile);
break;
case TOK_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 TOK_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 TOK_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 TOK_ALLOW:
parse_allow_deny(cfile, group, 1);
break;
case TOK_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;
}
void
parse_allow_deny(FILE *cfile, struct group *group, int flag)
{
int token;
char *val;
token = next_token(&val, cfile);
switch (token) {
case TOK_BOOTP:
group->allow_bootp = flag;
break;
case TOK_BOOTING:
group->allow_booting = flag;
break;
case TOK_DYNAMIC_BOOTP:
group->dynamic_bootp = flag;
break;
case TOK_UNKNOWN_CLIENTS:
group->boot_unknown_clients = flag;
break;
default:
parse_warn("expecting allow/deny key");
skip_to_semi(cfile);
return;
}
parse_semi(cfile);
}
int
parse_boolean(FILE *cfile)
{
char *val;
int rv;
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;
}
int
parse_lbrace(FILE *cfile)
{
int token;
char *val;
token = next_token(&val, cfile);
if (token != '{') {
parse_warn("expecting left brace.");
skip_to_semi(cfile);
return 0;
}
return 1;
}
void
parse_host_declaration(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 = calloc(1, sizeof (struct host_decl));
if (!host)
fatalx("can't allocate host decl struct %s.", name);
host->name = name;
host->group = clone_group(group, "parse_host_declaration");
if (!parse_lbrace(cfile)) {
free(host->name);
free(host->group);
free(host);
return;
}
do {
token = peek_token(&val, cfile);
if (token == '}') {
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])
fatalx("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 = -1;
host->group->options[DHO_HOST_NAME]->tree =
NULL;
}
enter_host(host);
}
void
parse_class_declaration(FILE *cfile, struct group *group, int type)
{
char *val;
int token;
struct class *class;
int declaration = 0;
token = next_token(&val, cfile);
if (token != TOK_STRING) {
parse_warn("Expecting class name");
skip_to_semi(cfile);
return;
}
class = add_class (type, val);
if (!class)
fatalx("No memory for class %s.", val);
class->group = clone_group(group, "parse_class_declaration");
if (!parse_lbrace(cfile)) {
free(class->name);
free(class->group);
free(class);
return;
}
do {
token = peek_token(&val, cfile);
if (token == '}') {
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, NULL, declaration);
}
} while (1);
}
void
parse_shared_net_declaration(FILE *cfile, struct group *group)
{
char *val;
int token;
struct shared_network *share;
char *name;
int declaration = 0;
share = calloc(1, sizeof(struct shared_network));
if (!share)
fatalx("No memory for shared subnet");
share->leases = NULL;
share->last_lease = NULL;
share->insertion_point = NULL;
share->next = NULL;
share->interface = NULL;
share->group = clone_group(group, "parse_shared_net_declaration");
share->group->shared_network = share;
token = peek_token(&val, cfile);
if (token == TOK_STRING) {
token = next_token(&val, cfile);
if (val[0] == 0) {
parse_warn("zero-length shared network name");
val = "<no-name-given>";
}
name = strdup(val);
if (name == NULL)
fatalx("no memory for shared network name");
} else {
name = parse_host_name(cfile);
if (!name) {
free(share->group);
free(share);
return;
}
}
share->name = name;
if (!parse_lbrace(cfile)) {
free(share->group);
free(share->name);
free(share);
return;
}
do {
token = peek_token(&val, cfile);
if (token == '}') {
token = next_token(&val, cfile);
if (!share->subnets) {
free(share->group);
free(share->name);
free(share);
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, NULL, declaration);
} while (1);
}
void
parse_subnet_declaration(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 = calloc(1, sizeof(struct subnet));
if (!subnet)
fatalx("No memory for new subnet");
subnet->shared_network = share;
subnet->group = clone_group(share->group, "parse_subnet_declaration");
subnet->group->subnet = subnet;
if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) {
free(subnet->group);
free(subnet);
return;
}
memcpy(iaddr.iabuf, addr, len);
iaddr.len = len;
subnet->net = iaddr;
token = next_token(&val, cfile);
if (token != TOK_NETMASK) {
free(subnet->group);
free(subnet);
parse_warn("Expecting netmask");
skip_to_semi(cfile);
return;
}
if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) {
free(subnet->group);
free(subnet);
return;
}
memcpy(iaddr.iabuf, addr, len);
iaddr.len = len;
subnet->netmask = iaddr;
subnet->net = subnet_number(subnet->net, subnet->netmask);
enter_subnet(subnet);
if (!parse_lbrace(cfile))
return;
do {
token = peek_token(&val, cfile);
if (token == '}') {
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, NULL, declaration);
} while (1);
if (subnet->group->dynamic_bootp)
share->group->dynamic_bootp = 1;
if (!share->subnets)
share->subnets = subnet;
else {
u = NULL;
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;
}
}
void
parse_group_declaration(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)) {
free(g);
return;
}
do {
token = peek_token(&val, cfile);
if (token == '}') {
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, NULL,
declaration);
} while (1);
}
int
parse_cidr(FILE *cfile, unsigned char *subnet, unsigned char *subnetlen)
{
uint8_t cidr[5];
const char *errstr;
char *val;
long long numval;
unsigned int i;
int token;
memset(cidr, 0, sizeof(cidr));
i = 1;
do {
token = next_token(&val, cfile);
if (i == 0)
numval = strtonum(val, 0, 32, &errstr);
else
numval = strtonum(val, 0, UINT8_MAX, &errstr);
if (errstr != NULL)
break;
cidr[i++] = numval;
if (i == 1) {
memcpy(subnet, &cidr[1], 4);
*subnetlen = cidr[0];
return 1;
}
token = next_token(NULL, cfile);
if (token == '/')
i = 0;
if (i == sizeof(cidr))
break;
} while (token == '.' || token == '/');
parse_warn("expecting IPv4 CIDR block.");
if (token != ';')
skip_to_semi(cfile);
return 0;
}
struct tree *
parse_ip_addr_or_hostname(FILE *cfile, int uniform)
{
char *val;
int token;
unsigned char addr[4];
int len = sizeof addr;
char *name;
struct tree *rv;
struct hostent *h;
name = NULL;
h = NULL;
token = peek_token(&val, cfile);
if (is_identifier(token)) {
name = parse_host_name(cfile);
if (name)
h = gethostbyname(name);
if (name && h) {
rv = tree_const(h->h_addr_list[0], h->h_length);
if (!uniform)
rv = tree_limit(rv, 4);
return rv;
}
}
if (token == TOK_NUMBER_OR_NAME) {
if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) {
parse_warn("%s (%d): expecting IP address or hostname",
val, token);
return NULL;
}
rv = tree_const(addr, len);
} else {
if (token != '{' && token != '}')
token = next_token(&val, cfile);
parse_warn("%s (%d): expecting IP address or hostname",
val, token);
if (token != ';')
skip_to_semi(cfile);
return NULL;
}
return rv;
}
struct tree_cache *
parse_fixed_addr_param(FILE *cfile)
{
char *val;
int token;
struct tree *tree = NULL;
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 == ',')
token = next_token(&val, cfile);
} while (token == ',');
if (!parse_semi(cfile))
return NULL;
return tree_cache(tree);
}
void
parse_option_param(FILE *cfile, struct group *group)
{
char *val;
int token;
unsigned char buf[4];
unsigned char cprefix;
char *vendor;
char *fmt;
struct universe *universe;
struct option *option;
struct tree *tree = NULL;
struct tree *t;
token = next_token(&val, cfile);
if (!is_identifier(token)) {
parse_warn("expecting identifier after option keyword.");
if (token != ';')
skip_to_semi(cfile);
return;
}
vendor = strdup(val);
if (vendor == NULL)
fatalx("no memory for vendor token.");
token = peek_token(&val, cfile);
if (token == '.') {
token = next_token(&val, cfile);
token = next_token(&val, cfile);
if (!is_identifier(token)) {
parse_warn("expecting identifier after '.'");
if (token != ';')
skip_to_semi(cfile);
free(vendor);
return;
}
universe = ((struct universe *)hash_lookup(&universe_hash,
(unsigned char *)vendor, 0));
if (!universe) {
parse_warn("no vendor named %s.", vendor);
skip_to_semi(cfile);
free(vendor);
return;
}
} else {
val = vendor;
universe = &dhcp_universe;
}
option = (struct option *)hash_lookup(universe->hash,
(unsigned char *)val, 0);
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);
free(vendor);
return;
}
free(vendor);
do {
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 == TOK_NUMBER_OR_NAME) {
do {
token = next_token
(&val, cfile);
if (token !=
TOK_NUMBER_OR_NAME) {
parse_warn("expecting "
"hex number.");
if (token != ';')
skip_to_semi(
cfile);
return;
}
convert_num(buf, val, 16, 8);
tree = tree_concat(tree,
tree_const(buf, 1));
token = peek_token(&val,
cfile);
if (token == ':')
token =
next_token(&val,
cfile);
} while (token == ':');
} else if (token == TOK_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':
token = next_token(&val, cfile);
if (token != TOK_STRING
&& !is_identifier(token)) {
parse_warn("expecting string.");
if (token != ';')
skip_to_semi(cfile);
return;
}
tree = tree_concat(tree,
tree_const((unsigned char *)val,
strlen(val)));
break;
case 'I':
t = parse_ip_addr_or_hostname(cfile, uniform);
if (!t)
return;
tree = tree_concat(tree, t);
break;
case 'L':
case 'l':
token = next_token(&val, cfile);
if (token != TOK_NUMBER && token !=
TOK_NUMBER_OR_NAME) {
parse_warn("expecting number.");
if (token != ';')
skip_to_semi(cfile);
return;
}
convert_num(buf, val, 0, 32);
tree = tree_concat(tree, tree_const(buf, 4));
break;
case 's':
case 'S':
token = next_token(&val, cfile);
if (token != TOK_NUMBER && token !=
TOK_NUMBER_OR_NAME) {
parse_warn("expecting number.");
if (token != ';')
skip_to_semi(cfile);
return;
}
convert_num(buf, val, 0, 16);
tree = tree_concat(tree, tree_const(buf, 2));
break;
case 'b':
case 'B':
token = next_token(&val, cfile);
if (token != TOK_NUMBER && token !=
TOK_NUMBER_OR_NAME) {
parse_warn("expecting number.");
if (token != ';')
skip_to_semi(cfile);
return;
}
convert_num(buf, val, 0, 8);
tree = tree_concat(tree, tree_const(buf, 1));
break;
case 'f':
token = next_token(&val, cfile);
if (!is_identifier(token)) {
parse_warn("expecting identifier.");
if (token != ';')
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.");
if (token != ';')
skip_to_semi(cfile);
return;
}
tree = tree_concat(tree, tree_const(buf, 1));
break;
case 'C':
if (!parse_cidr(cfile, buf, &cprefix))
return;
tree = tree_concat(tree, tree_const(&cprefix,
sizeof(cprefix)));
if (cprefix > 0)
tree = tree_concat(tree, tree_const(
buf, (cprefix + 7) / 8));
break;
case 'D':
t = parse_domain_and_comp(cfile);
if (!t)
return;
tree = tree_concat(tree, t);
break;
default:
log_warnx("Bad format %c in "
"parse_option_param.", *fmt);
skip_to_semi(cfile);
return;
}
}
if (*fmt == 'A') {
token = peek_token(&val, cfile);
if (token == ',') {
token = next_token(&val, cfile);
continue;
}
break;
}
} while (*fmt == 'A');
token = next_token(&val, cfile);
if (token != ';') {
parse_warn("semicolon expected.");
skip_to_semi(cfile);
return;
}
group->options[option->code] = tree_cache(tree);
}
struct lease *
parse_lease_declaration(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;
memset(&lease, 0, sizeof lease);
if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8))
return NULL;
memcpy(lease.ip_addr.iabuf, addr, len);
lease.ip_addr.len = len;
if (!parse_lbrace(cfile))
return NULL;
do {
token = next_token(&val, cfile);
if (token == '}')
break;
else if (token == EOF) {
parse_warn("unexpected end of file");
break;
}
strlcpy(tbuf, val, sizeof tbuf);
if (token == TOK_STARTS || token == TOK_ENDS || token ==
TOK_TIMESTAMP) {
time_t t;
t = parse_date(cfile);
switch (token) {
case TOK_STARTS:
seenbit = 1;
lease.starts = t;
break;
case TOK_ENDS:
seenbit = 2;
lease.ends = t;
break;
case TOK_TIMESTAMP:
seenbit = 4;
lease.timestamp = t;
break;
default:
seenbit = 0;
break;
}
} else {
switch (token) {
case TOK_UID:
seenbit = 8;
token = peek_token(&val, cfile);
if (token == TOK_STRING) {
token = next_token(&val, cfile);
lease.uid_len = strlen(val);
lease.uid = malloc(lease.uid_len);
if (!lease.uid) {
log_warnx("no space for uid");
return NULL;
}
memcpy(lease.uid, val, lease.uid_len);
parse_semi(cfile);
} else {
lease.uid_len = 0;
lease.uid =
parse_numeric_aggregate(cfile,
NULL, &lease.uid_len, ':', 16, 8);
if (!lease.uid) {
log_warnx("no space for uid");
return NULL;
}
if (lease.uid_len == 0) {
lease.uid = NULL;
parse_warn("zero-length uid");
seenbit = 0;
break;
}
}
if (!lease.uid)
fatalx("No memory for lease uid");
break;
case TOK_CLASS:
seenbit = 32;
token = next_token(&val, cfile);
if (!is_identifier(token)) {
if (token != ';')
skip_to_semi(cfile);
return NULL;
}
break;
case TOK_HARDWARE:
seenbit = 64;
parse_hardware_param(cfile,
&lease.hardware_addr);
break;
case TOK_DYNAMIC_BOOTP:
seenbit = 128;
lease.flags |= BOOTP_LEASE;
break;
case TOK_ABANDONED:
seenbit = 256;
lease.flags |= ABANDONED_LEASE;
break;
case TOK_HOSTNAME:
seenbit = 512;
token = peek_token(&val, cfile);
if (token == TOK_STRING)
lease.hostname = parse_string(cfile);
else
lease.hostname =
parse_host_name(cfile);
if (!lease.hostname) {
seenbit = 0;
return NULL;
}
break;
case TOK_CLIENT_HOSTNAME:
seenbit = 1024;
token = peek_token(&val, cfile);
if (token == TOK_STRING)
lease.client_hostname =
parse_string(cfile);
else
lease.client_hostname =
parse_host_name(cfile);
break;
default:
skip_to_semi(cfile);
seenbit = 0;
return NULL;
}
if (token != TOK_HARDWARE && token != TOK_STRING) {
token = next_token(&val, cfile);
if (token != ';') {
parse_warn("semicolon expected.");
skip_to_semi(cfile);
return NULL;
}
}
}
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;
}
void
parse_address_range(FILE *cfile, struct subnet *subnet)
{
struct iaddr low, high;
unsigned char addr[4];
int len = sizeof addr, token, dynamic = 0;
char *val;
if ((token = peek_token(&val, cfile)) == TOK_DYNAMIC_BOOTP) {
token = next_token(&val, cfile);
subnet->group->dynamic_bootp = dynamic = 1;
}
if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8))
return;
memcpy(low.iabuf, addr, len);
low.len = len;
token = peek_token(&val, cfile);
if (token == ';')
high = low;
else {
if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8))
return;
memcpy(high.iabuf, addr, len);
high.len = len;
}
token = next_token(&val, cfile);
if (token != ';') {
parse_warn("semicolon expected.");
skip_to_semi(cfile);
return;
}
new_address_range(low, high, subnet, dynamic);
}
static void
push_domain_list(char ***domains, size_t *count, char *domain)
{
*domains = reallocarray(*domains, *count + 1, sizeof **domains);
if (!*domains)
fatalx("Can't allocate domain list");
(*domains)[*count] = domain;
++*count;
}
static void
free_domain_list(char **domains, size_t count)
{
size_t i;
for (i = 0; i < count; i++)
free(domains[i]);
free(domains);
}
struct tree *
parse_domain_and_comp(FILE *cfile)
{
struct tree *rv = NULL;
char **domains = NULL;
char *val, *domain;
unsigned char *buf = NULL;
unsigned char **bufptrs = NULL;
size_t bufsiz = 0, bufn = 0, count = 0, i;
int token = ';';
do {
if (token == ',')
token = next_token(&val, cfile);
token = next_token(&val, cfile);
if (token != TOK_STRING) {
parse_warn("string expected");
goto error;
}
domain = strdup(val);
if (domain == NULL)
fatalx("Can't allocate domain to compress");
push_domain_list(&domains, &count, domain);
bufsiz = bufsiz + 2 + strlen(domain);
token = peek_token(NULL, cfile);
} while (token == ',');
buf = malloc(bufsiz);
if (!buf)
fatalx("Can't allocate compressed domain buffer");
bufptrs = calloc(count + 1, sizeof *bufptrs);
if (!bufptrs)
fatalx("Can't allocate compressed pointer list");
bufptrs[0] = buf;
if (!(bufsiz <= INT_MAX))
fatalx("Size of compressed domain buffer too large");
for (i = 0; i < count; i++) {
int n;
n = dn_comp(domains[i], &buf[bufn], bufsiz - bufn, bufptrs,
&bufptrs[count + 1]);
if (n == -1)
fatalx("Can't compress domain");
bufn += (size_t)n;
}
rv = tree_const(buf, bufn);
error:
free_domain_list(domains, count);
free(buf);
free(bufptrs);
return rv;
}