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