Rewrite DHCP options parsing to conform to RFC 3396: options concatenation is
now fully supported.
This commit is contained in:
parent
d7103c887d
commit
13f5ab56e2
@ -219,18 +219,18 @@ static size_t send_client_ip(char *out, size_t olen, struct dhcpmsg *packet)
|
|||||||
static size_t send_cmd(char *out, size_t olen, struct dhcpmsg *packet,
|
static size_t send_cmd(char *out, size_t olen, struct dhcpmsg *packet,
|
||||||
uint8_t code)
|
uint8_t code)
|
||||||
{
|
{
|
||||||
char buf[256];
|
char buf[2048];
|
||||||
uint8_t *optdata, *olddata;
|
uint8_t optdata[MAX_DOPT_SIZE], olddata[MAX_DOPT_SIZE];
|
||||||
ssize_t optlen, oldlen;
|
ssize_t optlen, oldlen;
|
||||||
|
|
||||||
if (!packet)
|
if (!packet)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
memset(buf, '\0', sizeof buf);
|
memset(buf, '\0', sizeof buf);
|
||||||
optdata = get_option_data(packet, code, &optlen);
|
optlen = get_dhcp_opt(packet, code, optdata, sizeof optdata);
|
||||||
if (!optlen)
|
if (!optlen)
|
||||||
return 0;
|
return 0;
|
||||||
olddata = get_option_data(&cfg_packet, code, &oldlen);
|
oldlen = get_dhcp_opt(&cfg_packet, code, olddata, sizeof olddata);
|
||||||
if (oldlen == optlen && !memcmp(optdata, olddata, optlen))
|
if (oldlen == optlen && !memcmp(optdata, olddata, optlen))
|
||||||
return 0;
|
return 0;
|
||||||
if (ifchd_cmd(buf, sizeof buf, optdata, optlen, code) == -1)
|
if (ifchd_cmd(buf, sizeof buf, optdata, optlen, code) == -1)
|
||||||
|
159
ndhc/options.c
159
ndhc/options.c
@ -34,84 +34,83 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "ifch_proto.h"
|
#include "ifch_proto.h"
|
||||||
|
|
||||||
// Worker function for get_option_data(). Optlen will be set to the length
|
static int do_overload_value(uint8_t *buf, ssize_t blen, int overload)
|
||||||
// of the option data.
|
|
||||||
static uint8_t *do_get_option_data(uint8_t *buf, ssize_t buflen, int code,
|
|
||||||
char *overload, ssize_t *optlen)
|
|
||||||
{
|
{
|
||||||
// option bytes: [code][len]([data1][data2]..[dataLEN])
|
ssize_t i = 0;
|
||||||
*overload = 0;
|
while (i < blen) {
|
||||||
while (buflen > 0) {
|
if (buf[i] == DCODE_PADDING) {
|
||||||
// Advance over padding.
|
++i;
|
||||||
if (buf[0] == DCODE_PADDING) {
|
|
||||||
buflen--;
|
|
||||||
buf++;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (buf[i] == DCODE_END)
|
||||||
// We hit the end.
|
break;
|
||||||
if (buf[0] == DCODE_END) {
|
if (i >= blen - 2)
|
||||||
*optlen = 0;
|
break;
|
||||||
return NULL;
|
if (buf[i] == DCODE_OVERLOAD) {
|
||||||
|
if (buf[i+1] == 1) {
|
||||||
|
overload |= buf[i+2];
|
||||||
|
i += 3;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
i += buf[i+1] + 2;
|
||||||
buflen -= buf[1] + 2;
|
|
||||||
if (buflen < 0) {
|
|
||||||
log_warning("Bad option data: length would exceed options field size.");
|
|
||||||
*optlen = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buf[0] == code) {
|
|
||||||
*optlen = buf[1];
|
|
||||||
return buf + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buf[0] == DCODE_OVERLOAD) {
|
|
||||||
if (buf[1] == 1)
|
|
||||||
*overload |= buf[2];
|
|
||||||
// fall through
|
|
||||||
}
|
|
||||||
buf += buf[1] + 2;
|
|
||||||
}
|
}
|
||||||
// End of options field was unmarked: no option data
|
return overload;
|
||||||
*optlen = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: Never concatenates options. If this is added, refer to RFC3396.
|
static int overload_value(struct dhcpmsg *packet)
|
||||||
// Get an option with bounds checking (warning, result is not aligned)
|
|
||||||
// optlen will be equal to the length of the option data.
|
|
||||||
uint8_t *get_option_data(struct dhcpmsg *packet, int code, ssize_t *optlen)
|
|
||||||
{
|
{
|
||||||
uint8_t *option, *buf;
|
int ol = do_overload_value(packet->options, sizeof packet->options, 0);
|
||||||
ssize_t buflen;
|
if (ol & 1 && ol & 2)
|
||||||
char overload, parsed_ff = 0;
|
return ol;
|
||||||
|
if (ol & 1) {
|
||||||
buf = packet->options;
|
ol |= do_overload_value(packet->file, sizeof packet->file, ol);
|
||||||
buflen = sizeof packet->options;
|
return ol;
|
||||||
|
|
||||||
option = do_get_option_data(buf, buflen, code, &overload, optlen);
|
|
||||||
if (option)
|
|
||||||
return option;
|
|
||||||
|
|
||||||
if (overload & 1) {
|
|
||||||
parsed_ff = 1;
|
|
||||||
option = do_get_option_data(packet->file, sizeof packet->file,
|
|
||||||
code, &overload, optlen);
|
|
||||||
if (option)
|
|
||||||
return option;
|
|
||||||
}
|
}
|
||||||
if (overload & 2) {
|
if (ol & 2) {
|
||||||
option = do_get_option_data(packet->sname, sizeof packet->sname,
|
ol |= do_overload_value(packet->sname, sizeof packet->sname, ol);
|
||||||
code, &overload, optlen);
|
return ol;
|
||||||
if (option)
|
|
||||||
return option;
|
|
||||||
if (!parsed_ff && overload & 1)
|
|
||||||
option = do_get_option_data(packet->file, sizeof packet->file,
|
|
||||||
code, &overload, optlen);
|
|
||||||
}
|
}
|
||||||
return option;
|
return ol; // ol == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t do_get_dhcp_opt(uint8_t *sbuf, ssize_t slen, uint8_t code,
|
||||||
|
uint8_t *dbuf, ssize_t dlen, ssize_t didx)
|
||||||
|
{
|
||||||
|
ssize_t i = 0;
|
||||||
|
while (i < slen) {
|
||||||
|
if (sbuf[i] == DCODE_PADDING) {
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (sbuf[i] == DCODE_END)
|
||||||
|
break;
|
||||||
|
if (i >= slen - 2)
|
||||||
|
break;
|
||||||
|
if (sbuf[i] == code) {
|
||||||
|
if (dlen - didx < sbuf[i+1])
|
||||||
|
return didx;
|
||||||
|
memcpy(dbuf+didx, sbuf+i+2, sbuf[i+1]);
|
||||||
|
didx += sbuf[i+1];
|
||||||
|
}
|
||||||
|
i += sbuf[i+1] + 2;
|
||||||
|
}
|
||||||
|
return didx;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t get_dhcp_opt(struct dhcpmsg *packet, uint8_t code, uint8_t *dbuf,
|
||||||
|
ssize_t dlen)
|
||||||
|
{
|
||||||
|
int ol = overload_value(packet);
|
||||||
|
ssize_t didx = do_get_dhcp_opt(packet->options, sizeof packet->options,
|
||||||
|
code, dbuf, dlen, 0);
|
||||||
|
if (ol & 1)
|
||||||
|
didx += do_get_dhcp_opt(packet->file, sizeof packet->file, code,
|
||||||
|
dbuf, dlen, didx);
|
||||||
|
if (ol & 2)
|
||||||
|
didx += do_get_dhcp_opt(packet->sname, sizeof packet->sname, code,
|
||||||
|
dbuf, dlen, didx);
|
||||||
|
return didx;
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the position of the 'end' option
|
// return the position of the 'end' option
|
||||||
@ -297,9 +296,10 @@ uint32_t get_option_router(struct dhcpmsg *packet)
|
|||||||
{
|
{
|
||||||
ssize_t ol;
|
ssize_t ol;
|
||||||
uint32_t ret = 0;
|
uint32_t ret = 0;
|
||||||
uint8_t *od = get_option_data(packet, DCODE_ROUTER, &ol);
|
uint8_t buf[MAX_DOPT_SIZE];
|
||||||
|
ol = get_dhcp_opt(packet, DCODE_ROUTER, buf, sizeof buf);
|
||||||
if (ol == sizeof ret)
|
if (ol == sizeof ret)
|
||||||
memcpy(&ret, od, sizeof ret);
|
memcpy(&ret, buf, sizeof ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,22 +307,23 @@ uint8_t get_option_msgtype(struct dhcpmsg *packet)
|
|||||||
{
|
{
|
||||||
ssize_t ol;
|
ssize_t ol;
|
||||||
uint8_t ret = 0;
|
uint8_t ret = 0;
|
||||||
uint8_t *t = get_option_data(packet, DCODE_MSGTYPE, &ol);
|
uint8_t buf[MAX_DOPT_SIZE];
|
||||||
if (t)
|
ol = get_dhcp_opt(packet, DCODE_MSGTYPE, buf, sizeof buf);
|
||||||
ret = *t;
|
if (ol == sizeof ret)
|
||||||
|
ret = buf[0];
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t get_option_serverid(struct dhcpmsg *packet, int *found)
|
uint32_t get_option_serverid(struct dhcpmsg *packet, int *found)
|
||||||
{
|
{
|
||||||
ssize_t ol;
|
ssize_t ol;
|
||||||
uint8_t *t;
|
|
||||||
uint32_t ret = 0;
|
uint32_t ret = 0;
|
||||||
|
uint8_t buf[MAX_DOPT_SIZE];
|
||||||
*found = 0;
|
*found = 0;
|
||||||
t = get_option_data(packet, DCODE_SERVER_ID, &ol);
|
ol = get_dhcp_opt(packet, DCODE_SERVER_ID, buf, sizeof buf);
|
||||||
if (ol == sizeof ret) {
|
if (ol == sizeof ret) {
|
||||||
*found = 1;
|
*found = 1;
|
||||||
memcpy(&ret, t, sizeof ret);
|
memcpy(&ret, buf, sizeof ret);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -330,11 +331,11 @@ uint32_t get_option_serverid(struct dhcpmsg *packet, int *found)
|
|||||||
uint32_t get_option_leasetime(struct dhcpmsg *packet)
|
uint32_t get_option_leasetime(struct dhcpmsg *packet)
|
||||||
{
|
{
|
||||||
ssize_t ol;
|
ssize_t ol;
|
||||||
uint8_t *t;
|
|
||||||
uint32_t ret = 0;
|
uint32_t ret = 0;
|
||||||
t = get_option_data(packet, DCODE_LEASET, &ol);
|
uint8_t buf[MAX_DOPT_SIZE];
|
||||||
|
ol = get_dhcp_opt(packet, DCODE_LEASET, buf, sizeof buf);
|
||||||
if (ol == sizeof ret) {
|
if (ol == sizeof ret) {
|
||||||
memcpy(&ret, t, sizeof ret);
|
memcpy(&ret, buf, sizeof ret);
|
||||||
ret = ntohl(ret);
|
ret = ntohl(ret);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -54,7 +54,10 @@
|
|||||||
#define DCODE_CLIENT_ID 0x3d
|
#define DCODE_CLIENT_ID 0x3d
|
||||||
#define DCODE_END 0xff
|
#define DCODE_END 0xff
|
||||||
|
|
||||||
uint8_t *get_option_data(struct dhcpmsg *packet, int code, ssize_t *optlen);
|
#define MAX_DOPT_SIZE 500
|
||||||
|
|
||||||
|
ssize_t get_dhcp_opt(struct dhcpmsg *packet, uint8_t code, uint8_t *dbuf,
|
||||||
|
ssize_t dlen);
|
||||||
ssize_t get_end_option_idx(struct dhcpmsg *packet);
|
ssize_t get_end_option_idx(struct dhcpmsg *packet);
|
||||||
|
|
||||||
size_t add_option_request_list(struct dhcpmsg *packet);
|
size_t add_option_request_list(struct dhcpmsg *packet);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user