dhcp: stop using magic constants; use (htonl(CONST) != a) - it's smaller
[oweals/busybox.git] / networking / udhcp / options.c
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 */
13 const 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 */
55 const 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). */
72 uint8_t *get_option(struct dhcpMessage *packet, int code)
73 {
74         int i, length;
75         uint8_t *optionptr;
76         int over = 0;
77         int curr = OPTION_FIELD;
78
79         optionptr = packet->options;
80         i = 0;
81         length = sizeof(packet->options);
82         while (1) {
83                 if (i >= length) {
84                         bb_error_msg("bogus packet, option fields too long");
85                         return NULL;
86                 }
87                 if (optionptr[i + OPT_CODE] == code) {
88                         if (i + 1 + optionptr[i + OPT_LEN] >= length) {
89                                 bb_error_msg("bogus packet, option fields too long");
90                                 return NULL;
91                         }
92                         return optionptr + i + 2;
93                 }
94                 switch (optionptr[i + OPT_CODE]) {
95                 case DHCP_PADDING:
96                         i++;
97                         break;
98                 case DHCP_OPTION_OVER:
99                         if (i + 1 + optionptr[i + OPT_LEN] >= length) {
100                                 bb_error_msg("bogus packet, option fields too long");
101                                 return NULL;
102                         }
103                         over = optionptr[i + 3];
104                         i += optionptr[OPT_LEN] + 2;
105                         break;
106                 case DHCP_END:
107                         if (curr == OPTION_FIELD && (over & FILE_FIELD)) {
108                                 optionptr = packet->file;
109                                 i = 0;
110                                 length = sizeof(packet->file);
111                                 curr = FILE_FIELD;
112                         } else if (curr == FILE_FIELD && (over & SNAME_FIELD)) {
113                                 optionptr = packet->sname;
114                                 i = 0;
115                                 length = sizeof(packet->sname);
116                                 curr = SNAME_FIELD;
117                         } else
118                                 return NULL;
119                         break;
120                 default:
121                         i += optionptr[OPT_LEN + i] + 2;
122                 }
123         }
124         return NULL;
125 }
126
127
128 /* return the position of the 'end' option (no bounds checking) */
129 int end_option(uint8_t *optionptr)
130 {
131         int i = 0;
132
133         while (optionptr[i] != DHCP_END) {
134                 if (optionptr[i] == DHCP_PADDING) i++;
135                 else i += optionptr[i + OPT_LEN] + 2;
136         }
137         return i;
138 }
139
140
141 /* add an option string to the options (an option string contains an option code,
142  * length, then data) */
143 int add_option_string(uint8_t *optionptr, uint8_t *string)
144 {
145         int end = end_option(optionptr);
146
147         /* end position + string length + option code/length + end option */
148         if (end + string[OPT_LEN] + 2 + 1 >= 308) {
149                 bb_error_msg("option 0x%02x did not fit into the packet",
150                                 string[OPT_CODE]);
151                 return 0;
152         }
153         DEBUG("adding option 0x%02x", string[OPT_CODE]);
154         memcpy(optionptr + end, string, string[OPT_LEN] + 2);
155         optionptr[end + string[OPT_LEN] + 2] = DHCP_END;
156         return string[OPT_LEN] + 2;
157 }
158
159
160 /* add a one to four byte option to a packet */
161 int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data)
162 {
163         const struct dhcp_option *dh;
164
165         for (dh = dhcp_options; dh->code; dh++) {
166                 if (dh->code == code) {
167                         uint8_t option[6], len;
168
169                         option[OPT_CODE] = code;
170                         len = option_lengths[dh->flags & TYPE_MASK];
171                         option[OPT_LEN] = len;
172                         if (BB_BIG_ENDIAN) data <<= 8 * (4 - len);
173                         /* This memcpy is for broken processors which can't
174                          * handle a simple unaligned 32-bit assignment */
175                         memcpy(&option[OPT_DATA], &data, 4);
176                         return add_option_string(optionptr, option);
177                 }
178         }
179
180         bb_error_msg("cannot add option 0x%02x", code);
181         return 0;
182 }