Enforce stronger type checking on DHCP option values. Fix some endianness

issues as well.
This commit is contained in:
Nicholas J. Kain 2011-07-02 04:45:11 -04:00
parent cfd9822252
commit 46ed7f5998
4 changed files with 67 additions and 25 deletions

View File

@ -569,7 +569,7 @@ static struct dhcpmsg init_packet(char type, uint32_t xid)
.options[0] = DHCP_END,
.xid = xid,
};
add_u32_option(&packet, DHCP_MESSAGE_TYPE, type);
add_u8_option(&packet, DHCP_MESSAGE_TYPE, type);
memcpy(packet.chaddr, client_config.arp, 6);
add_option_clientid(&packet);
add_option_hostname(&packet);
@ -581,7 +581,7 @@ int send_discover(struct client_state_t *cs)
struct dhcpmsg packet = init_packet(DHCPDISCOVER, cs->xid);
if (cs->clientAddr)
add_u32_option(&packet, DHCP_REQUESTED_IP, cs->clientAddr);
add_u32_option(&packet, DHCP_MAX_SIZE,
add_u16_option(&packet, DHCP_MAX_SIZE,
htons(sizeof(struct ip_udp_dhcp_packet)));
add_option_request_list(&packet);
add_option_vendor(&packet);
@ -594,7 +594,7 @@ int send_selecting(struct client_state_t *cs)
struct dhcpmsg packet = init_packet(DHCPREQUEST, cs->xid);
add_u32_option(&packet, DHCP_REQUESTED_IP, cs->clientAddr);
add_u32_option(&packet, DHCP_SERVER_ID, cs->serverAddr);
add_u32_option(&packet, DHCP_MAX_SIZE,
add_u16_option(&packet, DHCP_MAX_SIZE,
htons(sizeof(struct ip_udp_dhcp_packet)));
add_option_request_list(&packet);
add_option_vendor(&packet);
@ -607,7 +607,7 @@ int send_renew(struct client_state_t *cs)
{
struct dhcpmsg packet = init_packet(DHCPREQUEST, cs->xid);
packet.ciaddr = cs->clientAddr;
add_u32_option(&packet, DHCP_MAX_SIZE,
add_u16_option(&packet, DHCP_MAX_SIZE,
htons(sizeof(struct ip_udp_dhcp_packet)));
add_option_request_list(&packet);
add_option_vendor(&packet);
@ -620,7 +620,7 @@ int send_rebind(struct client_state_t *cs)
struct dhcpmsg packet = init_packet(DHCPREQUEST, cs->xid);
packet.ciaddr = cs->clientAddr;
add_u32_option(&packet, DHCP_REQUESTED_IP, cs->clientAddr);
add_u32_option(&packet, DHCP_MAX_SIZE,
add_u16_option(&packet, DHCP_MAX_SIZE,
htons(sizeof(struct ip_udp_dhcp_packet)));
add_option_request_list(&packet);
add_option_vendor(&packet);

View File

@ -60,8 +60,8 @@ struct dhcpmsg {
uint8_t chaddr[16]; // Client MAC address
uint8_t sname[64]; // Server host name (optional); null-terminated string
uint8_t file[128]; // boot file name, null-terminated string
uint32_t cookie;
uint8_t options[308]; /* 312 - cookie */
uint32_t cookie; // Magic number cookie that starts DHCP options
uint8_t options[308]; // Size of options excluding the cookie.
};
struct ip_udp_dhcp_packet {

View File

@ -227,7 +227,7 @@ size_t add_option_string(struct dhcpmsg *packet, uint8_t code, char *str,
return 0;
}
size_t end = get_end_option_idx(packet);
ssize_t end = get_end_option_idx(packet);
if (end == -1) {
log_warning("add_option_string: Buffer has no DHCP_END marker");
return 0;
@ -243,30 +243,70 @@ size_t add_option_string(struct dhcpmsg *packet, uint8_t code, char *str,
return len;
}
// XXX: length=1 and length=2 will fail if data is big-endian.
size_t add_u32_option(struct dhcpmsg *packet, uint8_t code, uint32_t data)
static ssize_t add_option_check(struct dhcpmsg *packet, uint8_t code,
uint8_t rlen)
{
size_t length = option_length(code);
if (!length) {
log_warning("add_u32_option: option code 0x%02x has 0 length", code);
return 0;
if (length != rlen) {
log_warning("add_u%01u_option: length mismatch code=0x%02x len=%01u",
rlen*8, code, length);
return -1;
}
size_t end = get_end_option_idx(packet);
ssize_t end = get_end_option_idx(packet);
if (end == -1) {
log_warning("add_u32_option: Buffer has no DHCP_END marker");
return 0;
log_warning("add_u%01u_option: Buffer has no DHCP_END marker", rlen*8);
return -1;
}
if (end + 2 + length >= sizeof packet->options) {
log_warning("add_u32_option: No space for option 0x%02x", code);
return 0;
if (end + 2 + rlen >= sizeof packet->options) {
log_warning("add_u%01u_option: No space for option 0x%02x",
rlen*8, code);
return -1;
}
return end;
}
size_t add_u8_option(struct dhcpmsg *packet, uint8_t code, uint8_t data)
{
ssize_t end = add_option_check(packet, code, 1);
if (end < 0)
return 0;
packet->options[end] = code;
packet->options[end+1] = length;
// XXX: this is broken: it's alignment-safe, but the endianness is wrong
memcpy(packet->options + end + 2, &data, length);
packet->options[end+length+2] = DHCP_END;
return length+2;
packet->options[end+1] = 1;
packet->options[end+2] = data;
packet->options[end+3] = DHCP_END;
return 3;
}
// Data should be in network byte order.
size_t add_u16_option(struct dhcpmsg *packet, uint8_t code, uint16_t data)
{
ssize_t end = add_option_check(packet, code, 2);
if (end < 0)
return 0;
uint8_t *dp = (uint8_t *)&data;
packet->options[end] = code;
packet->options[end+1] = 2;
packet->options[end+2] = dp[0];
packet->options[end+3] = dp[1];
packet->options[end+4] = DHCP_END;
return 4;
}
// Data should be in network byte order.
size_t add_u32_option(struct dhcpmsg *packet, uint8_t code, uint32_t data)
{
ssize_t end = add_option_check(packet, code, 4);
if (end < 0)
return 0;
uint8_t *dp = (uint8_t *)&data;
packet->options[end] = code;
packet->options[end+1] = 4;
packet->options[end+2] = dp[0];
packet->options[end+3] = dp[1];
packet->options[end+4] = dp[2];
packet->options[end+5] = dp[3];
packet->options[end+6] = DHCP_END;
return 6;
}
// Add a paramater request list for stubborn DHCP servers

View File

@ -87,6 +87,8 @@ uint8_t *get_option_data(struct dhcpmsg *packet, int code, ssize_t *optlen);
ssize_t get_end_option_idx(struct dhcpmsg *packet);
size_t add_option_string(struct dhcpmsg *packet, uint8_t code, char *str,
size_t slen);
size_t add_u8_option(struct dhcpmsg *packet, uint8_t code, uint8_t data);
size_t add_u16_option(struct dhcpmsg *packet, uint8_t code, uint16_t data);
size_t add_u32_option(struct dhcpmsg *packet, uint8_t code, uint32_t data);
size_t add_option_request_list(struct dhcpmsg *packet);