Rewrite DHCP options parsing to conform to RFC 3396: options concatenation is
now fully supported.
This commit is contained in:
		@@ -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,
 | 
			
		||||
                       uint8_t code)
 | 
			
		||||
{
 | 
			
		||||
    char buf[256];
 | 
			
		||||
    uint8_t *optdata, *olddata;
 | 
			
		||||
    char buf[2048];
 | 
			
		||||
    uint8_t optdata[MAX_DOPT_SIZE], olddata[MAX_DOPT_SIZE];
 | 
			
		||||
    ssize_t optlen, oldlen;
 | 
			
		||||
 | 
			
		||||
    if (!packet)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    memset(buf, '\0', sizeof buf);
 | 
			
		||||
    optdata = get_option_data(packet, code, &optlen);
 | 
			
		||||
    optlen = get_dhcp_opt(packet, code, optdata, sizeof optdata);
 | 
			
		||||
    if (!optlen)
 | 
			
		||||
        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))
 | 
			
		||||
        return 0;
 | 
			
		||||
    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 "ifch_proto.h"
 | 
			
		||||
 | 
			
		||||
// Worker function for get_option_data().  Optlen will be set to the length
 | 
			
		||||
// of the option data.
 | 
			
		||||
static uint8_t *do_get_option_data(uint8_t *buf, ssize_t buflen, int code,
 | 
			
		||||
                                   char *overload, ssize_t *optlen)
 | 
			
		||||
static int do_overload_value(uint8_t *buf, ssize_t blen, int overload)
 | 
			
		||||
{
 | 
			
		||||
    // option bytes: [code][len]([data1][data2]..[dataLEN])
 | 
			
		||||
    *overload = 0;
 | 
			
		||||
    while (buflen > 0) {
 | 
			
		||||
        // Advance over padding.
 | 
			
		||||
        if (buf[0] == DCODE_PADDING) {
 | 
			
		||||
            buflen--;
 | 
			
		||||
            buf++;
 | 
			
		||||
    ssize_t i = 0;
 | 
			
		||||
    while (i < blen) {
 | 
			
		||||
        if (buf[i] == DCODE_PADDING) {
 | 
			
		||||
            ++i;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // We hit the end.
 | 
			
		||||
        if (buf[0] == DCODE_END) {
 | 
			
		||||
            *optlen = 0;
 | 
			
		||||
            return NULL;
 | 
			
		||||
        if (buf[i] == DCODE_END)
 | 
			
		||||
            break;
 | 
			
		||||
        if (i >= blen - 2)
 | 
			
		||||
            break;
 | 
			
		||||
        if (buf[i] == DCODE_OVERLOAD) {
 | 
			
		||||
            if (buf[i+1] == 1) {
 | 
			
		||||
                overload |= buf[i+2];
 | 
			
		||||
                i += 3;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
        i += buf[i+1] + 2;
 | 
			
		||||
    }
 | 
			
		||||
    // End of options field was unmarked: no option data
 | 
			
		||||
    *optlen = 0;
 | 
			
		||||
    return NULL;
 | 
			
		||||
    return overload;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// XXX: Never concatenates options.  If this is added, refer to RFC3396.
 | 
			
		||||
// 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)
 | 
			
		||||
static int overload_value(struct dhcpmsg *packet)
 | 
			
		||||
{
 | 
			
		||||
    uint8_t *option, *buf;
 | 
			
		||||
    ssize_t buflen;
 | 
			
		||||
    char overload, parsed_ff = 0;
 | 
			
		||||
 | 
			
		||||
    buf = packet->options;
 | 
			
		||||
    buflen = sizeof packet->options;
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    int ol = do_overload_value(packet->options, sizeof packet->options, 0);
 | 
			
		||||
    if (ol & 1 && ol & 2)
 | 
			
		||||
        return ol;
 | 
			
		||||
    if (ol & 1) {
 | 
			
		||||
        ol |= do_overload_value(packet->file, sizeof packet->file, ol);
 | 
			
		||||
        return ol;
 | 
			
		||||
    }
 | 
			
		||||
    if (overload & 2) {
 | 
			
		||||
        option = do_get_option_data(packet->sname, sizeof packet->sname,
 | 
			
		||||
                                    code, &overload, optlen);
 | 
			
		||||
        if (option)
 | 
			
		||||
            return option;
 | 
			
		||||
        if (!parsed_ff && overload & 1)
 | 
			
		||||
            option = do_get_option_data(packet->file, sizeof packet->file,
 | 
			
		||||
                                        code, &overload, optlen);
 | 
			
		||||
    if (ol & 2) {
 | 
			
		||||
        ol |= do_overload_value(packet->sname, sizeof packet->sname, ol);
 | 
			
		||||
        return ol;
 | 
			
		||||
    }
 | 
			
		||||
    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
 | 
			
		||||
@@ -297,9 +296,10 @@ uint32_t get_option_router(struct dhcpmsg *packet)
 | 
			
		||||
{
 | 
			
		||||
    ssize_t ol;
 | 
			
		||||
    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)
 | 
			
		||||
        memcpy(&ret, od, sizeof ret);
 | 
			
		||||
        memcpy(&ret, buf, sizeof ret);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -307,22 +307,23 @@ uint8_t get_option_msgtype(struct dhcpmsg *packet)
 | 
			
		||||
{
 | 
			
		||||
    ssize_t ol;
 | 
			
		||||
    uint8_t ret = 0;
 | 
			
		||||
    uint8_t *t = get_option_data(packet, DCODE_MSGTYPE, &ol);
 | 
			
		||||
    if (t)
 | 
			
		||||
        ret = *t;
 | 
			
		||||
    uint8_t buf[MAX_DOPT_SIZE];
 | 
			
		||||
    ol = get_dhcp_opt(packet, DCODE_MSGTYPE, buf, sizeof buf);
 | 
			
		||||
    if (ol == sizeof ret)
 | 
			
		||||
        ret = buf[0];
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t get_option_serverid(struct dhcpmsg *packet, int *found)
 | 
			
		||||
{
 | 
			
		||||
    ssize_t ol;
 | 
			
		||||
    uint8_t *t;
 | 
			
		||||
    uint32_t ret = 0;
 | 
			
		||||
    uint8_t buf[MAX_DOPT_SIZE];
 | 
			
		||||
    *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) {
 | 
			
		||||
        *found = 1;
 | 
			
		||||
        memcpy(&ret, t, sizeof ret);
 | 
			
		||||
        memcpy(&ret, buf, sizeof 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)
 | 
			
		||||
{
 | 
			
		||||
    ssize_t ol;
 | 
			
		||||
    uint8_t *t;
 | 
			
		||||
    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) {
 | 
			
		||||
        memcpy(&ret, t, sizeof ret);
 | 
			
		||||
        memcpy(&ret, buf, sizeof ret);
 | 
			
		||||
        ret = ntohl(ret);
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,10 @@
 | 
			
		||||
#define DCODE_CLIENT_ID    0x3d
 | 
			
		||||
#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);
 | 
			
		||||
 | 
			
		||||
size_t add_option_request_list(struct dhcpmsg *packet);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user