#include "dhcpd.h"
#include "dhctoken.h"
int
read_client_conf(void)
{
FILE *cfile;
int token;
new_parse(path_dhclient_conf);
config->link_timeout = 30;
config->timeout = 60;
config->select_interval = 0;
config->reboot_timeout = 10;
config->retry_interval = 300;
config->backoff_cutoff = 15;
config->initial_interval = 3;
config->bootp_policy = ACCEPT;
config->script_name = _PATH_DHCLIENT_SCRIPT;
config->requested_options
[config->requested_option_count++] = DHO_SUBNET_MASK;
config->requested_options
[config->requested_option_count++] = DHO_BROADCAST_ADDRESS;
config->requested_options
[config->requested_option_count++] = DHO_TIME_OFFSET;
config->requested_options
[config->requested_option_count++] = DHO_CLASSLESS_ROUTES;
config->requested_options
[config->requested_option_count++] = DHO_ROUTERS;
config->requested_options
[config->requested_option_count++] = DHO_DOMAIN_NAME;
config->requested_options
[config->requested_option_count++] = DHO_DOMAIN_NAME_SERVERS;
config->requested_options
[config->requested_option_count++] = DHO_HOST_NAME;
if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
do {
token = peek_token(NULL, cfile);
if (token == EOF)
break;
parse_client_statement(cfile);
} while (1);
token = next_token(NULL, cfile);
fclose(cfile);
}
return (!warnings_occurred);
}
void
read_client_leases(void)
{
FILE *cfile;
int token;
new_parse(path_dhclient_db);
if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
return;
do {
token = next_token(NULL, cfile);
if (token == EOF)
break;
if (token != TOK_LEASE) {
warning("Corrupt lease file - possible data loss!");
skip_to_semi(cfile);
break;
} else
parse_client_lease_statement(cfile, 0);
} while (1);
fclose(cfile);
}
void
parse_client_statement(FILE *cfile)
{
u_int8_t ignorelist[256];
int token, code, count, i;
switch (next_token(NULL, cfile)) {
case TOK_SEND:
parse_option_decl(cfile, &config->send_options[0]);
return;
case TOK_DEFAULT:
code = parse_option_decl(cfile, &config->defaults[0]);
if (code != -1)
config->default_actions[code] = ACTION_DEFAULT;
return;
case TOK_SUPERSEDE:
code = parse_option_decl(cfile, &config->defaults[0]);
if (code != -1)
config->default_actions[code] = ACTION_SUPERSEDE;
return;
case TOK_IGNORE:
count = parse_option_list(cfile, ignorelist);
for (i = 0; i < count; i++)
config->default_actions[ignorelist[i]] = ACTION_IGNORE;
return;
case TOK_APPEND:
code = parse_option_decl(cfile, &config->defaults[0]);
if (code != -1)
config->default_actions[code] = ACTION_APPEND;
return;
case TOK_PREPEND:
code = parse_option_decl(cfile, &config->defaults[0]);
if (code != -1)
config->default_actions[code] = ACTION_PREPEND;
return;
case TOK_MEDIA:
skip_to_semi(cfile);
return;
case TOK_HARDWARE:
parse_hardware_param(cfile, &ifi->hw_address);
return;
case TOK_REQUEST:
config->requested_option_count =
parse_option_list(cfile, config->requested_options);
return;
case TOK_REQUIRE:
memset(config->required_options, 0,
sizeof(config->required_options));
parse_option_list(cfile, config->required_options);
return;
case TOK_LINK_TIMEOUT:
parse_lease_time(cfile, &config->link_timeout);
return;
case TOK_TIMEOUT:
parse_lease_time(cfile, &config->timeout);
return;
case TOK_RETRY:
parse_lease_time(cfile, &config->retry_interval);
return;
case TOK_SELECT_TIMEOUT:
parse_lease_time(cfile, &config->select_interval);
return;
case TOK_REBOOT:
parse_lease_time(cfile, &config->reboot_timeout);
return;
case TOK_BACKOFF_CUTOFF:
parse_lease_time(cfile, &config->backoff_cutoff);
return;
case TOK_INITIAL_INTERVAL:
parse_lease_time(cfile, &config->initial_interval);
return;
case TOK_SCRIPT:
config->script_name = parse_string(cfile);
return;
case TOK_INTERFACE:
parse_interface_declaration(cfile);
return;
case TOK_LEASE:
parse_client_lease_statement(cfile, 1);
return;
case TOK_ALIAS:
skip_to_semi(cfile);
return;
case TOK_REJECT:
parse_reject_statement(cfile);
return;
default:
parse_warn("expecting a statement.");
skip_to_semi(cfile);
break;
}
token = next_token(NULL, cfile);
if (token != ';') {
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 == TOK_NUMBER_OR_NAME || token == TOK_NUMBER) {
len = 0;
do {
token = next_token(&val, cfile);
if (token != TOK_NUMBER && token != TOK_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 == ':')
token = next_token(&val, cfile);
} while (token == ':');
val = (char *)buf;
} else if (token == TOK_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);
}
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 == ',');
if (token != ';') {
parse_warn("expecting semicolon.");
skip_to_semi(cfile);
return (0);
}
return (ix);
}
void
parse_interface_declaration(FILE *cfile)
{
char *val;
int token;
token = next_token(&val, cfile);
if (token != TOK_STRING) {
parse_warn("expecting interface name (in quotes).");
skip_to_semi(cfile);
return;
}
if (strcmp(ifi->name, val) != 0) {
skip_to_semi(cfile);
return;
}
token = next_token(&val, cfile);
if (token != '{') {
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 == '}')
break;
parse_client_statement(cfile);
} while (1);
token = next_token(&val, cfile);
}
void
parse_client_lease_statement(FILE *cfile, int is_static)
{
struct client_lease *lease, *lp, *pl;
int token;
token = next_token(NULL, cfile);
if (token != '{') {
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;
do {
token = peek_token(NULL, cfile);
if (token == EOF) {
parse_warn("unterminated lease declaration.");
return;
}
if (token == '}')
break;
parse_client_lease_declaration(cfile, lease);
} while (1);
token = next_token(NULL, cfile);
if (!ifi) {
free_client_lease(lease);
return;
}
pl = NULL;
for (lp = client->leases; lp; lp = lp->next) {
if (addr_eq(lp->address, lease->address)) {
if (pl)
pl->next = lp->next;
else
client->leases = lp->next;
free_client_lease(lp);
break;
} else
pl = lp;
}
if (is_static) {
lease->next = client->leases;
client->leases = lease;
return;
}
if (client->active) {
if (client->active->expiry < time(NULL))
free_client_lease(client->active);
else if (addr_eq(client->active->address, lease->address))
free_client_lease(client->active);
else {
client->active->next = client->leases;
client->leases = client->active;
}
}
client->active = lease;
}
void
parse_client_lease_declaration(FILE *cfile, struct client_lease *lease)
{
char *val;
int token;
switch (next_token(&val, cfile)) {
case TOK_BOOTP:
lease->is_bootp = 1;
break;
case TOK_INTERFACE:
token = next_token(&val, cfile);
if (token != TOK_STRING) {
parse_warn("expecting interface name (in quotes).");
skip_to_semi(cfile);
break;
}
if (strcmp(ifi->name, val) != 0) {
parse_warn("wrong interface name. Expecting '%s'.",
ifi->name);
skip_to_semi(cfile);
break;
}
break;
case TOK_FIXED_ADDR:
if (!parse_ip_addr(cfile, &lease->address))
return;
break;
case TOK_MEDIUM:
skip_to_semi(cfile);
return;
case TOK_FILENAME:
lease->filename = parse_string(cfile);
return;
case TOK_SERVER_NAME:
lease->server_name = parse_string(cfile);
return;
case TOK_RENEW:
lease->renewal = parse_date(cfile);
return;
case TOK_REBIND:
lease->rebind = parse_date(cfile);
return;
case TOK_EXPIRE:
lease->expiry = parse_date(cfile);
return;
case TOK_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 != ';') {
parse_warn("expecting semicolon.");
skip_to_semi(cfile);
}
}
int
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 *fmt;
struct iaddr ip_addr;
u_int8_t *dp;
int len, code;
int nul_term = 0;
token = next_token(&val, cfile);
if (!is_identifier(token)) {
parse_warn("expecting identifier after option keyword.");
if (token != ';')
skip_to_semi(cfile);
return (-1);
}
fmt = NULL;
for (code = 0; code < 256; code++)
if (strcmp(dhcp_options[code].name, val) == 0)
break;
if (code > 255) {
parse_warn("no option named %s", val);
skip_to_semi(cfile);
return (-1);
}
do {
for (fmt = dhcp_options[code].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':
token = next_token(&val, cfile);
if (token != TOK_STRING) {
parse_warn("expecting string.");
skip_to_semi(cfile);
return (-1);
}
len = strlen(val);
if (hunkix + len + 1 > sizeof(hunkbuf)) {
parse_warn("option data buffer %s",
"overflow");
skip_to_semi(cfile);
return (-1);
}
memcpy(&hunkbuf[hunkix], val, len + 1);
nul_term = 1;
hunkix += len;
break;
case 'I':
if (!parse_ip_addr(cfile, &ip_addr))
return (-1);
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 (-1);
}
memcpy(&hunkbuf[hunkix], dp, len);
hunkix += len;
break;
case 'L':
case 'l':
token = next_token(&val, cfile);
if (token != TOK_NUMBER) {
need_number:
parse_warn("expecting number.");
if (token != ';')
skip_to_semi(cfile);
return (-1);
}
convert_num(buf, val, 0, 32);
len = 4;
dp = buf;
goto alloc;
case 's':
case 'S':
token = next_token(&val, cfile);
if (token != TOK_NUMBER)
goto need_number;
convert_num(buf, val, 0, 16);
len = 2;
dp = buf;
goto alloc;
case 'b':
case 'B':
token = next_token(&val, cfile);
if (token != TOK_NUMBER)
goto need_number;
convert_num(buf, val, 0, 8);
len = 1;
dp = buf;
goto alloc;
case 'f':
token = next_token(&val, cfile);
if (!is_identifier(token)) {
parse_warn("expecting identifier.");
bad_flag:
if (token != ';')
skip_to_semi(cfile);
return (-1);
}
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 (-1);
}
}
token = next_token(&val, cfile);
} while (*fmt == 'A' && token == ',');
if (token != ';') {
parse_warn("semicolon expected.");
skip_to_semi(cfile);
return (-1);
}
options[code].data = malloc(hunkix + nul_term);
if (!options[code].data)
error("out of memory allocating option data.");
memcpy(options[code].data, hunkbuf, hunkix + nul_term);
options[code].len = hunkix;
return (code);
}
void
parse_reject_statement(FILE *cfile)
{
struct iaddrlist *list;
struct iaddr addr;
int token;
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(NULL, cfile);
} while (token == ',');
if (token != ';') {
parse_warn("expecting semicolon.");
skip_to_semi(cfile);
}
}