| #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; |
| } |