diff options
Diffstat (limited to 'com32/lib/dhcppack.c')
-rw-r--r-- | com32/lib/dhcppack.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/com32/lib/dhcppack.c b/com32/lib/dhcppack.c new file mode 100644 index 00000000..a08583c4 --- /dev/null +++ b/com32/lib/dhcppack.c @@ -0,0 +1,166 @@ +#include <stdlib.h> +#include <errno.h> +#include <string.h> +// #include <arpa/inet.h> +#include <netinet/in.h> + +// #include "dhcp.h" +#include <dhcp.h> + +/* + * Pack DHCP options into an option field, without overload support. + * On return, len contains the number of active bytes, and the full + * field is zero-padded. + * + * Options which are successfully placed have their length zeroed out. + */ +static int dhcp_pack_field_zero(void *field, size_t *len, + struct dhcp_option opt[256]) +{ + int i; + size_t xlen, plen; + const uint8_t *p; + uint8_t *q = field; + size_t spc = *len; + int err = 0; + + if (!*len) + return ENOSPC; + + for (i = 1; i < 255; i++) { + if (opt[i].len < 0) + continue; + + /* We need to handle the 0 case as well as > 255 */ + if (opt[i].len <= 255) + xlen = opt[i].len + 2; + else + xlen = opt[i].len + 2*((opt[i].len+254)/255); + + p = opt[i].data; + + if (xlen >= spc) { + /* This option doesn't fit... */ + err++; + continue; + } + + xlen = opt[i].len; + do { + *q++ = i; + *q++ = plen = xlen > 255 ? 255 : xlen; + if (plen) + memcpy(q, p, plen); + q += plen; + p += plen; + spc -= plen+2; + xlen -= plen; + } while (xlen); + + opt[i].len = -1; + } + + *q++ = 255; /* End marker */ + memset(q, 0, spc); /* Zero-pad the rest of the field */ + + *len = xlen = q - (uint8_t *)field; + return err; +} + +/* + * Pack DHCP options into an option field, without overload support. + * On return, len contains the number of active bytes, and the full + * field is zero-padded. + * + * Use this to encode encapsulated option fields. + */ +int dhcp_pack_field(void *field, size_t *len, + struct dhcp_option opt[256]) +{ + struct dhcp_option ox[256]; + + memcpy(ox, opt, sizeof ox); + return dhcp_pack_field_zero(field, len, ox); +} + +/* + * Pack DHCP options into a packet. + * Apply overloading if (and only if) the "file" or "sname" option + * doesn't fit in the respective dedicated fields. + */ +int dhcp_pack_packet(void *packet, size_t *len, + const struct dhcp_option opt[256]) +{ + struct dhcp_packet *pkt = packet; + size_t spc = *len; + uint8_t overload; + struct dhcp_option ox[256]; + uint8_t *q; + int err; + + if (spc < sizeof(struct dhcp_packet)) + return ENOSPC; /* Buffer impossibly small */ + + pkt->magic = htonl(DHCP_VENDOR_MAGIC); + + memcpy(ox, opt, sizeof ox); + + /* Figure out if we should do overloading or not */ + overload = 0; + + if (opt[67].len > 128) + overload |= 1; + else + ox[67].len = -1; + + if (opt[66].len > 64) + overload |= 2; + else + ox[66].len = -1; + + /* Kill any passed-in overload option */ + ox[52].len = -1; + + q = pkt->options; + spc -= 240; + + /* Force option 53 (DHCP packet type) first */ + if (ox[53].len == 1) { + *q++ = 53; + *q++ = 1; + *q++ = *(uint8_t *)ox[53].data; + spc -= 3; + ox[53].len = -1; + } + + /* Follow with the overload option, if applicable */ + if (overload) { + *q++ = 52; + *q++ = 1; + *q++ = overload; + spc -= 3; + } + + err = dhcp_pack_field_zero(q, &spc, ox); + *len = spc + (q-(uint8_t *)packet); + + if (overload & 1) { + spc = 128; + err = dhcp_pack_field_zero(pkt->file, &spc, ox); + } else { + memset(pkt->file, 0, 128); + if (opt[67].len > 0) + memcpy(pkt->file, opt[67].data, opt[67].len); + } + + if (overload & 2) { + spc = 64; + err = dhcp_pack_field_zero(pkt->sname, &spc, ox); + } else { + memset(pkt->sname, 0, 64); + if (opt[66].len > 0) + memcpy(pkt->sname, opt[66].data, opt[66].len); + } + + return err; +} |