blob: a08583c4c20afb670504572dc913374159c6760a [file] [log] [blame]
#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;
}