hush/networking/udhcp/script.c
Denys Vlasenko 0f62c4d065 udhcpd: remove five more options which do not make sense or not supported
requestip, vendorclass, clientid are client-side variables,
          they do not make sense as udhcpd opts
dhcptype  is the packet type (not interesting, it's always 5)
userclass needs parser support in order to work

function                                             old     new   delta
dhcp_options                                          68      66      -2
read_opt                                             865     859      -6
dhcp_option_strings                                  253     203     -50
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 0/3 up/down: 0/-58)             Total: -58 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2010-03-20 07:12:21 +01:00

286 lines
7.0 KiB
C

/* vi: set sw=4 ts=4: */
/* script.c
*
* Functions to call the DHCP client notification scripts
*
* Russ Dill <Russ.Dill@asu.edu> July 2001
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "common.h"
#include "dhcpc.h"
#include "options.h"
/* get a rough idea of how long an option will be (rounding up...) */
static const uint8_t len_of_option_as_string[] = {
[OPTION_IP] = sizeof("255.255.255.255 "),
[OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2,
[OPTION_STATIC_ROUTES]= sizeof("255.255.255.255/32 255.255.255.255 "),
[OPTION_STRING] = 1,
#if ENABLE_FEATURE_UDHCP_RFC3397
[OPTION_STR1035] = 1,
#endif
[OPTION_BOOLEAN] = sizeof("yes "),
[OPTION_U8] = sizeof("255 "),
[OPTION_U16] = sizeof("65535 "),
[OPTION_S16] = sizeof("-32768 "),
[OPTION_U32] = sizeof("4294967295 "),
[OPTION_S32] = sizeof("-2147483684 "),
};
/* note: ip is a pointer to an IP in network order, possibly misaliged */
static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
{
return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
}
/* really simple implementation, just count the bits */
static int mton(uint32_t mask)
{
int i = 0;
mask = ntohl(mask); /* 111110000-like bit pattern */
while (mask) {
i++;
mask <<= 1;
}
return i;
}
/* Create "opt_name=opt_value" string */
static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_option *type_p, const char *opt_name)
{
unsigned upper_length;
int len, type, optlen;
uint16_t val_u16;
int16_t val_s16;
uint32_t val_u32;
int32_t val_s32;
char *dest, *ret;
/* option points to OPT_DATA, need to go back and get OPT_LEN */
len = option[OPT_LEN - OPT_DATA];
type = type_p->flags & TYPE_MASK;
optlen = dhcp_option_lengths[type];
upper_length = len_of_option_as_string[type] * (len / optlen);
dest = ret = xmalloc(upper_length + strlen(opt_name) + 2);
dest += sprintf(ret, "%s=", opt_name);
while (len >= optlen) {
switch (type) {
case OPTION_IP_PAIR:
dest += sprint_nip(dest, "", option);
*dest++ = '/';
option += 4;
optlen = 4;
case OPTION_IP:
dest += sprint_nip(dest, "", option);
// TODO: it can be a list only if (type_p->flags & OPTION_LIST).
// Should we bail out/warn if we see multi-ip option which is
// not allowed to be such? For example, DHCP_BROADCAST...
break;
case OPTION_BOOLEAN:
dest += sprintf(dest, *option ? "yes" : "no");
break;
case OPTION_U8:
dest += sprintf(dest, "%u", *option);
break;
case OPTION_U16:
move_from_unaligned16(val_u16, option);
dest += sprintf(dest, "%u", ntohs(val_u16));
break;
case OPTION_S16:
move_from_unaligned16(val_s16, option);
dest += sprintf(dest, "%d", ntohs(val_s16));
break;
case OPTION_U32:
move_from_unaligned32(val_u32, option);
dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32));
break;
case OPTION_S32:
move_from_unaligned32(val_s32, option);
dest += sprintf(dest, "%ld", (long) ntohl(val_s32));
break;
case OPTION_STRING:
memcpy(dest, option, len);
dest[len] = '\0';
return ret; /* Short circuit this case */
case OPTION_STATIC_ROUTES: {
/* Option binary format:
* mask [one byte, 0..32]
* ip [big endian, 0..4 bytes depending on mask]
* router [big endian, 4 bytes]
* may be repeated
*
* We convert it to a string "IP/MASK ROUTER IP2/MASK2 ROUTER2"
*/
const char *pfx = "";
while (len >= 1 + 4) { /* mask + 0-byte ip + router */
uint32_t nip;
uint8_t *p;
unsigned mask;
int bytes;
mask = *option++;
if (mask > 32)
break;
len--;
nip = 0;
p = (void*) &nip;
bytes = (mask + 7) / 8; /* 0 -> 0, 1..8 -> 1, 9..16 -> 2 etc */
while (--bytes >= 0) {
*p++ = *option++;
len--;
}
if (len < 4)
break;
/* print ip/mask */
dest += sprint_nip(dest, pfx, (void*) &nip);
pfx = " ";
dest += sprintf(dest, "/%u ", mask);
/* print router */
dest += sprint_nip(dest, "", option);
option += 4;
len -= 4;
}
return ret;
}
#if ENABLE_FEATURE_UDHCP_RFC3397
case OPTION_STR1035:
/* unpack option into dest; use ret for prefix (i.e., "optname=") */
dest = dname_dec(option, len, ret);
if (dest) {
free(ret);
return dest;
}
/* error. return "optname=" string */
return ret;
#endif
}
option += optlen;
len -= optlen;
if (len <= 0)
break;
*dest++ = ' ';
*dest = '\0';
}
return ret;
}
/* put all the parameters into the environment */
static char **fill_envp(struct dhcp_packet *packet)
{
int num_options = 0;
int i;
char **envp, **curr;
const char *opt_name;
uint8_t *temp;
uint8_t over = 0;
if (packet) {
for (i = 0; dhcp_options[i].code; i++) {
if (get_option(packet, dhcp_options[i].code)) {
num_options++;
if (dhcp_options[i].code == DHCP_SUBNET)
num_options++; /* for mton */
}
}
if (packet->siaddr_nip)
num_options++;
temp = get_option(packet, DHCP_OPTION_OVERLOAD);
if (temp)
over = *temp;
if (!(over & FILE_FIELD) && packet->file[0])
num_options++;
if (!(over & SNAME_FIELD) && packet->sname[0])
num_options++;
}
curr = envp = xzalloc(sizeof(char *) * (num_options + 3));
*curr = xasprintf("interface=%s", client_config.interface);
putenv(*curr++);
if (packet == NULL)
return envp;
*curr = xmalloc(sizeof("ip=255.255.255.255"));
sprint_nip(*curr, "ip=", (uint8_t *) &packet->yiaddr);
putenv(*curr++);
opt_name = dhcp_option_strings;
i = 0;
while (*opt_name) {
temp = get_option(packet, dhcp_options[i].code);
if (!temp)
goto next;
*curr = xmalloc_optname_optval(temp, &dhcp_options[i], opt_name);
putenv(*curr++);
/* Fill in a subnet bits option for things like /24 */
if (dhcp_options[i].code == DHCP_SUBNET) {
uint32_t subnet;
move_from_unaligned32(subnet, temp);
*curr = xasprintf("mask=%d", mton(subnet));
putenv(*curr++);
}
next:
opt_name += strlen(opt_name) + 1;
i++;
}
if (packet->siaddr_nip) {
*curr = xmalloc(sizeof("siaddr=255.255.255.255"));
sprint_nip(*curr, "siaddr=", (uint8_t *) &packet->siaddr_nip);
putenv(*curr++);
}
if (!(over & FILE_FIELD) && packet->file[0]) {
/* watch out for invalid packets */
packet->file[sizeof(packet->file) - 1] = '\0';
*curr = xasprintf("boot_file=%s", packet->file);
putenv(*curr++);
}
if (!(over & SNAME_FIELD) && packet->sname[0]) {
/* watch out for invalid packets */
packet->sname[sizeof(packet->sname) - 1] = '\0';
*curr = xasprintf("sname=%s", packet->sname);
putenv(*curr++);
}
return envp;
}
/* Call a script with a par file and env vars */
void FAST_FUNC udhcp_run_script(struct dhcp_packet *packet, const char *name)
{
char **envp, **curr;
char *argv[3];
if (client_config.script == NULL)
return;
envp = fill_envp(packet);
/* call script */
log1("Executing %s %s", client_config.script, name);
argv[0] = (char*) client_config.script;
argv[1] = (char*) name;
argv[2] = NULL;
spawn_and_wait(argv);
for (curr = envp; *curr; curr++) {
log2(" %s", *curr);
bb_unsetenv(*curr);
free(*curr);
}
free(envp);
}