2 |
/* |
/* |
3 |
* options.c -- DHCP server option packet tools |
* options.c -- DHCP server option packet tools |
4 |
* Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 |
* Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 |
5 |
|
* |
6 |
|
* Licensed under GPLv2, see file LICENSE in this tarball for details. |
7 |
*/ |
*/ |
8 |
|
|
9 |
#include "common.h" |
#include "common.h" |
48 |
#if ENABLE_FEATURE_UDHCP_RFC3397 |
#if ENABLE_FEATURE_UDHCP_RFC3397 |
49 |
{ OPTION_STR1035 | OPTION_LIST , 0x77 }, /* search */ |
{ OPTION_STR1035 | OPTION_LIST , 0x77 }, /* search */ |
50 |
#endif |
#endif |
51 |
|
{ OPTION_STATIC_ROUTES , 0x79 }, /* DHCP_STATIC_ROUTES */ |
52 |
/* MSIE's "Web Proxy Autodiscovery Protocol" support */ |
/* MSIE's "Web Proxy Autodiscovery Protocol" support */ |
53 |
{ OPTION_STRING , 0xfc }, /* wpad */ |
{ OPTION_STRING , 0xfc }, /* wpad */ |
54 |
|
|
57 |
* with "option XXX YYY" syntax in dhcpd config file. */ |
* with "option XXX YYY" syntax in dhcpd config file. */ |
58 |
|
|
59 |
{ OPTION_U16 , 0x39 }, /* DHCP_MAX_SIZE */ |
{ OPTION_U16 , 0x39 }, /* DHCP_MAX_SIZE */ |
60 |
{ } /* zeroed terminating entry */ |
{ 0, 0 } /* zeroed terminating entry */ |
61 |
}; |
}; |
62 |
|
|
63 |
/* Used for converting options from incoming packets to env variables |
/* Used for converting options from incoming packets to env variables |
98 |
#if ENABLE_FEATURE_UDHCP_RFC3397 |
#if ENABLE_FEATURE_UDHCP_RFC3397 |
99 |
"search" "\0" |
"search" "\0" |
100 |
#endif |
#endif |
101 |
|
"staticroutes" "\0" /* DHCP_STATIC_ROUTES */ |
102 |
/* MSIE's "Web Proxy Autodiscovery Protocol" support */ |
/* MSIE's "Web Proxy Autodiscovery Protocol" support */ |
103 |
"wpad" "\0" |
"wpad" "\0" |
104 |
; |
; |
117 |
[OPTION_U16] = 2, |
[OPTION_U16] = 2, |
118 |
[OPTION_S16] = 2, |
[OPTION_S16] = 2, |
119 |
[OPTION_U32] = 4, |
[OPTION_U32] = 4, |
120 |
[OPTION_S32] = 4 |
[OPTION_S32] = 4, |
121 |
|
/* Just like OPTION_STRING, we use minimum length here */ |
122 |
|
[OPTION_STATIC_ROUTES] = 5, |
123 |
}; |
}; |
124 |
|
|
125 |
|
|
126 |
/* get an option with bounds checking (warning, not aligned). */ |
#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2 |
127 |
uint8_t* FAST_FUNC get_option(struct dhcpMessage *packet, int code) |
static void log_option(const char *pfx, const uint8_t *opt) |
128 |
|
{ |
129 |
|
if (dhcp_verbose >= 2) { |
130 |
|
char buf[256 * 2 + 2]; |
131 |
|
*bin2hex(buf, (void*) (opt + OPT_DATA), opt[OPT_LEN]) = '\0'; |
132 |
|
bb_info_msg("%s: 0x%02x %s", pfx, opt[OPT_CODE], buf); |
133 |
|
} |
134 |
|
} |
135 |
|
#else |
136 |
|
# define log_option(pfx, opt) ((void)0) |
137 |
|
#endif |
138 |
|
|
139 |
|
|
140 |
|
/* get an option with bounds checking (warning, result is not aligned). */ |
141 |
|
uint8_t* FAST_FUNC get_option(struct dhcp_packet *packet, int code) |
142 |
{ |
{ |
|
int i, length; |
|
143 |
uint8_t *optionptr; |
uint8_t *optionptr; |
144 |
int over = 0; |
int len; |
145 |
int curr = OPTION_FIELD; |
int rem; |
146 |
|
int overload = 0; |
147 |
|
enum { |
148 |
|
FILE_FIELD101 = FILE_FIELD * 0x101, |
149 |
|
SNAME_FIELD101 = SNAME_FIELD * 0x101, |
150 |
|
}; |
151 |
|
|
152 |
|
/* option bytes: [code][len][data1][data2]..[dataLEN] */ |
153 |
optionptr = packet->options; |
optionptr = packet->options; |
154 |
i = 0; |
rem = sizeof(packet->options); |
|
length = sizeof(packet->options); |
|
155 |
while (1) { |
while (1) { |
156 |
if (i >= length) { |
if (rem <= 0) { |
157 |
bb_error_msg("bogus packet, option fields too long"); |
bb_error_msg("bogus packet, malformed option field"); |
158 |
return NULL; |
return NULL; |
159 |
} |
} |
160 |
if (optionptr[i + OPT_CODE] == code) { |
if (optionptr[OPT_CODE] == DHCP_PADDING) { |
161 |
if (i + 1 + optionptr[i + OPT_LEN] >= length) { |
rem--; |
162 |
bb_error_msg("bogus packet, option fields too long"); |
optionptr++; |
163 |
return NULL; |
continue; |
|
} |
|
|
return optionptr + i + 2; |
|
164 |
} |
} |
165 |
switch (optionptr[i + OPT_CODE]) { |
if (optionptr[OPT_CODE] == DHCP_END) { |
166 |
case DHCP_PADDING: |
if ((overload & FILE_FIELD101) == FILE_FIELD) { |
167 |
i++; |
/* can use packet->file, and didn't look at it yet */ |
168 |
break; |
overload |= FILE_FIELD101; /* "we looked at it" */ |
|
case DHCP_OPTION_OVER: |
|
|
if (i + 1 + optionptr[i + OPT_LEN] >= length) { |
|
|
bb_error_msg("bogus packet, option fields too long"); |
|
|
return NULL; |
|
|
} |
|
|
over = optionptr[i + 3]; |
|
|
i += optionptr[OPT_LEN] + 2; |
|
|
break; |
|
|
case DHCP_END: |
|
|
if (curr == OPTION_FIELD && (over & FILE_FIELD)) { |
|
169 |
optionptr = packet->file; |
optionptr = packet->file; |
170 |
i = 0; |
rem = sizeof(packet->file); |
171 |
length = sizeof(packet->file); |
continue; |
172 |
curr = FILE_FIELD; |
} |
173 |
} else if (curr == FILE_FIELD && (over & SNAME_FIELD)) { |
if ((overload & SNAME_FIELD101) == SNAME_FIELD) { |
174 |
|
/* can use packet->sname, and didn't look at it yet */ |
175 |
|
overload |= SNAME_FIELD101; /* "we looked at it" */ |
176 |
optionptr = packet->sname; |
optionptr = packet->sname; |
177 |
i = 0; |
rem = sizeof(packet->sname); |
178 |
length = sizeof(packet->sname); |
continue; |
179 |
curr = SNAME_FIELD; |
} |
|
} else |
|
|
return NULL; |
|
180 |
break; |
break; |
|
default: |
|
|
i += optionptr[OPT_LEN + i] + 2; |
|
181 |
} |
} |
182 |
|
len = 2 + optionptr[OPT_LEN]; |
183 |
|
rem -= len; |
184 |
|
if (rem < 0) |
185 |
|
continue; /* complain and return NULL */ |
186 |
|
|
187 |
|
if (optionptr[OPT_CODE] == code) { |
188 |
|
log_option("Option found", optionptr); |
189 |
|
return optionptr + OPT_DATA; |
190 |
|
} |
191 |
|
|
192 |
|
if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) { |
193 |
|
overload |= optionptr[OPT_DATA]; |
194 |
|
/* fall through */ |
195 |
|
} |
196 |
|
optionptr += len; |
197 |
} |
} |
198 |
|
|
199 |
|
/* log3 because udhcpc uses it a lot - very noisy */ |
200 |
|
log3("Option 0x%02x not found", code); |
201 |
return NULL; |
return NULL; |
202 |
} |
} |
203 |
|
|
208 |
int i = 0; |
int i = 0; |
209 |
|
|
210 |
while (optionptr[i] != DHCP_END) { |
while (optionptr[i] != DHCP_END) { |
211 |
if (optionptr[i] == DHCP_PADDING) |
if (optionptr[i] != DHCP_PADDING) |
212 |
i++; |
i += optionptr[i + OPT_LEN] + 1; |
213 |
else |
i++; |
|
i += optionptr[i + OPT_LEN] + 2; |
|
214 |
} |
} |
215 |
return i; |
return i; |
216 |
} |
} |
217 |
|
|
218 |
|
|
219 |
/* add an option string to the options (an option string contains an option code, |
/* add an option string to the options */ |
220 |
* length, then data) */ |
/* option bytes: [code][len][data1][data2]..[dataLEN] */ |
221 |
int FAST_FUNC add_option_string(uint8_t *optionptr, uint8_t *string) |
int FAST_FUNC add_option_string(uint8_t *optionptr, uint8_t *string) |
222 |
{ |
{ |
223 |
int end = end_option(optionptr); |
int end = end_option(optionptr); |
228 |
string[OPT_CODE]); |
string[OPT_CODE]); |
229 |
return 0; |
return 0; |
230 |
} |
} |
231 |
DEBUG("adding option 0x%02x", string[OPT_CODE]); |
log_option("Adding option", string); |
232 |
memcpy(optionptr + end, string, string[OPT_LEN] + 2); |
memcpy(optionptr + end, string, string[OPT_LEN] + 2); |
233 |
optionptr[end + string[OPT_LEN] + 2] = DHCP_END; |
optionptr[end + string[OPT_LEN] + 2] = DHCP_END; |
234 |
return string[OPT_LEN] + 2; |
return string[OPT_LEN] + 2; |
249 |
option[OPT_LEN] = len; |
option[OPT_LEN] = len; |
250 |
if (BB_BIG_ENDIAN) |
if (BB_BIG_ENDIAN) |
251 |
data <<= 8 * (4 - len); |
data <<= 8 * (4 - len); |
252 |
/* This memcpy is for processors which can't |
/* Assignment is unaligned! */ |
253 |
* handle a simple unaligned 32-bit assignment */ |
move_to_unaligned32(&option[OPT_DATA], data); |
|
memcpy(&option[OPT_DATA], &data, 4); |
|
254 |
return add_option_string(optionptr, option); |
return add_option_string(optionptr, option); |
255 |
} |
} |
256 |
} |
} |
257 |
|
|
258 |
bb_error_msg("cannot add option 0x%02x", code); |
bb_error_msg("can't add option 0x%02x", code); |
259 |
return 0; |
return 0; |
260 |
} |
} |