1/* vi: set sw=4 ts=4: */ 2/* 3 * options.c -- DHCP server option packet tools 4 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 5 */ 6 7#include "common.h" 8#include "dhcpd.h" 9#include "options.h" 10 11 12/* supported options are easily added here */ 13const struct dhcp_option dhcp_options[] = { 14 /* name[12] flags code */ 15 {"subnet", OPTION_IP | OPTION_REQ, 0x01}, 16 {"timezone", OPTION_S32, 0x02}, 17 {"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03}, 18 {"timesvr", OPTION_IP | OPTION_LIST, 0x04}, 19 {"namesvr", OPTION_IP | OPTION_LIST, 0x05}, 20 {"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06}, 21 {"logsvr", OPTION_IP | OPTION_LIST, 0x07}, 22 {"cookiesvr", OPTION_IP | OPTION_LIST, 0x08}, 23 {"lprsvr", OPTION_IP | OPTION_LIST, 0x09}, 24 {"hostname", OPTION_STRING | OPTION_REQ, 0x0c}, 25 {"bootsize", OPTION_U16, 0x0d}, 26 {"domain", OPTION_STRING | OPTION_LIST | OPTION_REQ, 0x0f}, 27 {"swapsvr", OPTION_IP, 0x10}, 28 {"rootpath", OPTION_STRING, 0x11}, 29 {"ipttl", OPTION_U8, 0x17}, 30 {"mtu", OPTION_U16, 0x1a}, 31 {"broadcast", OPTION_IP | OPTION_REQ, 0x1c}, 32 {"nisdomain", OPTION_STRING | OPTION_REQ, 0x28}, 33 {"nissrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x29}, 34 {"ntpsrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a}, 35 {"wins", OPTION_IP | OPTION_LIST, 0x2c}, 36 {"requestip", OPTION_IP, 0x32}, 37 {"lease", OPTION_U32, 0x33}, 38 {"dhcptype", OPTION_U8, 0x35}, 39 {"serverid", OPTION_IP, 0x36}, 40 {"message", OPTION_STRING, 0x38}, 41 {"vendorclass", OPTION_STRING, 0x3C}, 42 {"clientid", OPTION_STRING, 0x3D}, 43 {"tftp", OPTION_STRING, 0x42}, 44 {"bootfile", OPTION_STRING, 0x43}, 45 {"userclass", OPTION_STRING, 0x4D}, 46#if ENABLE_FEATURE_RFC3397 47 {"search", OPTION_STR1035 | OPTION_LIST | OPTION_REQ, 0x77}, 48#endif 49 /* MSIE's "Web Proxy Autodiscovery Protocol" support */ 50 {"wpad", OPTION_STRING, 0xfc}, 51 {"", 0x00, 0x00} 52}; 53 54/* Lengths of the different option types */ 55const unsigned char option_lengths[] ALIGN1 = { 56 [OPTION_IP] = 4, 57 [OPTION_IP_PAIR] = 8, 58 [OPTION_BOOLEAN] = 1, 59 [OPTION_STRING] = 1, 60#if ENABLE_FEATURE_RFC3397 61 [OPTION_STR1035] = 1, 62#endif 63 [OPTION_U8] = 1, 64 [OPTION_U16] = 2, 65 [OPTION_S16] = 2, 66 [OPTION_U32] = 4, 67 [OPTION_S32] = 4 68}; 69 70 71/* get an option with bounds checking (warning, not aligned). */ 72uint8_t *get_option(struct dhcpMessage *packet, int code) 73{ 74 int i, length; 75 uint8_t *optionptr; 76 int over = 0, done = 0, curr = OPTION_FIELD; 77 78 optionptr = packet->options; 79 i = 0; 80 length = 308; 81 while (!done) { 82 if (i >= length) { 83 bb_error_msg("bogus packet, option fields too long"); 84 return NULL; 85 } 86 if (optionptr[i + OPT_CODE] == code) { 87 if (i + 1 + optionptr[i + OPT_LEN] >= length) { 88 bb_error_msg("bogus packet, option fields too long"); 89 return NULL; 90 } 91 return optionptr + i + 2; 92 } 93 switch (optionptr[i + OPT_CODE]) { 94 case DHCP_PADDING: 95 i++; 96 break; 97 case DHCP_OPTION_OVER: 98 if (i + 1 + optionptr[i + OPT_LEN] >= length) { 99 bb_error_msg("bogus packet, option fields too long"); 100 return NULL; 101 } 102 over = optionptr[i + 3]; 103 i += optionptr[OPT_LEN] + 2; 104 break; 105 case DHCP_END: 106 if (curr == OPTION_FIELD && over & FILE_FIELD) { 107 optionptr = packet->file; 108 i = 0; 109 length = 128; 110 curr = FILE_FIELD; 111 } else if (curr == FILE_FIELD && over & SNAME_FIELD) { 112 optionptr = packet->sname; 113 i = 0; 114 length = 64; 115 curr = SNAME_FIELD; 116 } else done = 1; 117 break; 118 default: 119 i += optionptr[OPT_LEN + i] + 2; 120 } 121 } 122 return NULL; 123} 124 125 126/* return the position of the 'end' option (no bounds checking) */ 127int end_option(uint8_t *optionptr) 128{ 129 int i = 0; 130 131 while (optionptr[i] != DHCP_END) { 132 if (optionptr[i] == DHCP_PADDING) i++; 133 else i += optionptr[i + OPT_LEN] + 2; 134 } 135 return i; 136} 137 138 139/* add an option string to the options (an option string contains an option code, 140 * length, then data) */ 141int add_option_string(uint8_t *optionptr, uint8_t *string) 142{ 143 int end = end_option(optionptr); 144 145 /* end position + string length + option code/length + end option */ 146 if (end + string[OPT_LEN] + 2 + 1 >= 308) { 147 bb_error_msg("option 0x%02x did not fit into the packet", 148 string[OPT_CODE]); 149 return 0; 150 } 151 DEBUG("adding option 0x%02x", string[OPT_CODE]); 152 memcpy(optionptr + end, string, string[OPT_LEN] + 2); 153 optionptr[end + string[OPT_LEN] + 2] = DHCP_END; 154 return string[OPT_LEN] + 2; 155} 156 157 158/* add a one to four byte option to a packet */ 159int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data) 160{ 161 const struct dhcp_option *dh; 162 163 for (dh = dhcp_options; dh->code; dh++) { 164 if (dh->code == code) { 165 uint8_t option[6], len; 166 167 option[OPT_CODE] = code; 168 len = option_lengths[dh->flags & TYPE_MASK]; 169 option[OPT_LEN] = len; 170 if (BB_BIG_ENDIAN) data <<= 8 * (4 - len); 171 /* This memcpy is for broken processors which can't 172 * handle a simple unaligned 32-bit assignment */ 173 memcpy(&option[OPT_DATA], &data, 4); 174 return add_option_string(optionptr, option); 175 } 176 } 177 178 bb_error_msg("cannot add option 0x%02x", code); 179 return 0; 180} 181