1#define _GNU_SOURCE		/* For strnlen() */
2#include <stdlib.h>
3#include <errno.h>
4#include <string.h>
5// #include <arpa/inet.h>
6#include <netinet/in.h>
7
8// #include "dhcp.h"
9#include <dhcp.h>
10
11/*
12 * Unpack DHCP options from a field.  Assumes opt is pre-initalized
13 * (to all zero in the common case.)
14 */
15int dhcp_unpack_field(const void *field, size_t len,
16		      struct dhcp_option opt[256])
17{
18	const uint8_t *p = field;
19	int err = 0;
20
21	while (len > 1) {
22		uint8_t op;
23		size_t xlen;
24
25		op = *p++; len--;
26		if (op == 0)
27			continue;
28		else if (op == 255)
29			break;
30
31		xlen = *p++; len--;
32		if (xlen > len)
33			break;
34		if (opt[op].len < 0)
35			opt[op].len = 0;
36		if (xlen) {
37			opt[op].data = realloc(opt[op].data,
38					       opt[op].len + xlen + 1);
39			if (!opt[op].data) {
40				err = ENOMEM;
41				continue;
42			}
43			memcpy((char *)opt[op].data + opt[op].len, p, xlen);
44			opt[op].len += xlen;
45			/* Null-terminate as a courtesy to users */
46			*((char *)opt[op].data + opt[op].len) = 0;
47			p += xlen;
48			len -= xlen;
49		}
50	}
51
52	return err;
53}
54
55/*
56 * Unpack a DHCP packet, with overload support.  Do not use this
57 * to unpack an encapsulated option set.
58 */
59int dhcp_unpack_packet(const void *packet, size_t len,
60		       struct dhcp_option opt[256])
61{
62	const struct dhcp_packet *pkt = packet;
63	int err;
64	uint8_t overload;
65	int i;
66
67	if (len < 240 || pkt->magic != htonl(DHCP_VENDOR_MAGIC))
68		return EINVAL;	/* Bogus packet */
69
70	for (i = 0; i < 256; i++) {
71		opt[i].len = -1; /* Option not present */
72		opt[i].data = NULL;
73	}
74
75	err = dhcp_unpack_field(pkt->options, len-240, opt);
76
77	overload = 0;
78	if (opt[52].len == 1) {
79		overload = *(uint8_t *)opt[52].data;
80		free(opt[52].data);
81		opt[52].len = -1;
82		opt[52].data = NULL;
83	}
84
85	if (overload & 1) {
86		err |= dhcp_unpack_field(pkt->file, 128, opt);
87	} else {
88		opt[67].len  = strnlen((const char *)pkt->file, 128);
89		if (opt[67].len) {
90			opt[67].data = malloc(opt[67].len + 1);
91			if (opt[67].data) {
92				memcpy(opt[67].data, pkt->file, opt[67].len);
93				*((char *)opt[67].data + opt[67].len) = 0;
94			} else {
95				err |= ENOMEM;
96			}
97		}
98	}
99
100	if (overload & 2) {
101		err |= dhcp_unpack_field(pkt->sname, 64, opt);
102	} else {
103		opt[66].len  = strnlen((const char *)pkt->sname, 64);
104		if (opt[66].len) {
105			opt[66].data = malloc(opt[66].len + 1);
106			if (opt[66].data) {
107				memcpy(opt[66].data, pkt->file, opt[66].len);
108				*((char *)opt[66].data + opt[66].len) = 0;
109			} else {
110				err |= ENOMEM;
111			}
112		}
113	}
114
115	return err;
116}
117