diff options
author | Martin Lewis <martin.lewis.x84@gmail.com> | 2020-06-23 15:25:08 -0500 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2020-06-29 14:57:02 +0200 |
commit | acdc8eed89399a9fa5e7478ee40b928c65e3ab4e (patch) | |
tree | 406e5c338b8d22c895cb16bdee9151202efe0ead /networking/udhcp | |
parent | fc2ce04a38ebfb03f9aeff205979786839cd5a7c (diff) | |
download | busybox-w32-acdc8eed89399a9fa5e7478ee40b928c65e3ab4e.tar.gz busybox-w32-acdc8eed89399a9fa5e7478ee40b928c65e3ab4e.tar.bz2 busybox-w32-acdc8eed89399a9fa5e7478ee40b928c65e3ab4e.zip |
udhcp: add option scanner
Added an option scanner to udhcp to enable iteration over packet options.
Signed-off-by: Martin Lewis <martin.lewis.x84@gmail.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'networking/udhcp')
-rw-r--r-- | networking/udhcp/common.c | 96 | ||||
-rw-r--r-- | networking/udhcp/common.h | 8 |
2 files changed, 62 insertions, 42 deletions
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c index 16bf69707..20d843bab 100644 --- a/networking/udhcp/common.c +++ b/networking/udhcp/common.c | |||
@@ -15,7 +15,7 @@ const uint8_t MAC_BCAST_ADDR[6] ALIGN2 = { | |||
15 | }; | 15 | }; |
16 | 16 | ||
17 | #if ENABLE_UDHCPC || ENABLE_UDHCPD | 17 | #if ENABLE_UDHCPC || ENABLE_UDHCPD |
18 | /* Supported options are easily added here. | 18 | /* Supported options are easily added here, they need to be sorted. |
19 | * See RFC2132 for more options. | 19 | * See RFC2132 for more options. |
20 | * OPTION_REQ: these options are requested by udhcpc (unless -o). | 20 | * OPTION_REQ: these options are requested by udhcpc (unless -o). |
21 | */ | 21 | */ |
@@ -222,79 +222,91 @@ unsigned FAST_FUNC udhcp_option_idx(const char *name, const char *option_strings | |||
222 | } | 222 | } |
223 | } | 223 | } |
224 | 224 | ||
225 | /* Get an option with bounds checking (warning, result is not aligned) */ | 225 | /* Initialize state to be used between subsequent udhcp_scan_options calls */ |
226 | uint8_t* FAST_FUNC udhcp_get_option(struct dhcp_packet *packet, int code) | 226 | void FAST_FUNC init_scan_state(struct dhcp_packet *packet, struct dhcp_scan_state *scan_state) |
227 | { | ||
228 | scan_state->overload = 0; | ||
229 | scan_state->rem = sizeof(packet->options); | ||
230 | scan_state->optionptr = packet->options; | ||
231 | } | ||
232 | |||
233 | /* Iterate over packet's options, each call returning the next option. | ||
234 | * scan_state needs to be initialized with init_scan_state beforehand. | ||
235 | * Warning, result is not aligned. */ | ||
236 | uint8_t* FAST_FUNC udhcp_scan_options(struct dhcp_packet *packet, struct dhcp_scan_state *scan_state) | ||
227 | { | 237 | { |
228 | uint8_t *optionptr; | ||
229 | int len; | 238 | int len; |
230 | int rem; | ||
231 | int overload = 0; | ||
232 | enum { | 239 | enum { |
233 | FILE_FIELD101 = FILE_FIELD * 0x101, | 240 | FILE_FIELD101 = FILE_FIELD * 0x101, |
234 | SNAME_FIELD101 = SNAME_FIELD * 0x101, | 241 | SNAME_FIELD101 = SNAME_FIELD * 0x101, |
235 | }; | 242 | }; |
236 | 243 | ||
237 | /* option bytes: [code][len][data1][data2]..[dataLEN] */ | 244 | /* option bytes: [code][len][data1][data2]..[dataLEN] */ |
238 | optionptr = packet->options; | ||
239 | rem = sizeof(packet->options); | ||
240 | while (1) { | 245 | while (1) { |
241 | if (rem <= 0) { | 246 | if (scan_state->rem <= 0) { |
242 | complain: | 247 | complain: |
243 | bb_simple_error_msg("bad packet, malformed option field"); | 248 | bb_simple_error_msg("bad packet, malformed option field"); |
244 | return NULL; | 249 | return NULL; |
245 | } | 250 | } |
246 | 251 | ||
247 | /* DHCP_PADDING and DHCP_END have no [len] byte */ | 252 | /* DHCP_PADDING and DHCP_END have no [len] byte */ |
248 | if (optionptr[OPT_CODE] == DHCP_PADDING) { | 253 | if (scan_state->optionptr[OPT_CODE] == DHCP_PADDING) { |
249 | rem--; | 254 | scan_state->rem--; |
250 | optionptr++; | 255 | scan_state->optionptr++; |
251 | continue; | 256 | continue; |
252 | } | 257 | } |
253 | if (optionptr[OPT_CODE] == DHCP_END) { | 258 | if (scan_state->optionptr[OPT_CODE] == DHCP_END) { |
254 | if ((overload & FILE_FIELD101) == FILE_FIELD) { | 259 | if ((scan_state->overload & FILE_FIELD101) == FILE_FIELD) { |
255 | /* can use packet->file, and didn't look at it yet */ | 260 | /* can use packet->file, and didn't look at it yet */ |
256 | overload |= FILE_FIELD101; /* "we looked at it" */ | 261 | scan_state->overload |= FILE_FIELD101; /* "we looked at it" */ |
257 | optionptr = packet->file; | 262 | scan_state->optionptr = packet->file; |
258 | rem = sizeof(packet->file); | 263 | scan_state->rem = sizeof(packet->file); |
259 | continue; | 264 | continue; |
260 | } | 265 | } |
261 | if ((overload & SNAME_FIELD101) == SNAME_FIELD) { | 266 | if ((scan_state->overload & SNAME_FIELD101) == SNAME_FIELD) { |
262 | /* can use packet->sname, and didn't look at it yet */ | 267 | /* can use packet->sname, and didn't look at it yet */ |
263 | overload |= SNAME_FIELD101; /* "we looked at it" */ | 268 | scan_state->overload |= SNAME_FIELD101; /* "we looked at it" */ |
264 | optionptr = packet->sname; | 269 | scan_state->optionptr = packet->sname; |
265 | rem = sizeof(packet->sname); | 270 | scan_state->rem = sizeof(packet->sname); |
266 | continue; | 271 | continue; |
267 | } | 272 | } |
268 | break; | 273 | break; |
269 | } | 274 | } |
270 | 275 | ||
271 | if (rem <= OPT_LEN) | 276 | if (scan_state->rem <= OPT_LEN) |
272 | goto complain; /* complain and return NULL */ | 277 | goto complain; /* complain and return NULL */ |
273 | len = 2 + optionptr[OPT_LEN]; | 278 | len = 2 + scan_state->optionptr[OPT_LEN]; |
274 | rem -= len; | 279 | scan_state->rem -= len; |
275 | if (rem < 0) | 280 | /* So far no valid option with length 0 known. */ |
281 | if (scan_state->rem < 0 || scan_state->optionptr[OPT_LEN] == 0) | ||
276 | goto complain; /* complain and return NULL */ | 282 | goto complain; /* complain and return NULL */ |
277 | 283 | ||
278 | if (optionptr[OPT_CODE] == code) { | 284 | if (scan_state->optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) { |
279 | if (optionptr[OPT_LEN] == 0) { | 285 | if (len >= 3) |
280 | /* So far no valid option with length 0 known. | 286 | scan_state->overload |= scan_state->optionptr[OPT_DATA]; |
281 | * Having this check means that searching | 287 | } else { |
282 | * for DHCP_MESSAGE_TYPE need not worry | 288 | uint8_t *return_ptr = scan_state->optionptr; |
283 | * that returned pointer might be unsafe | 289 | scan_state->optionptr += len; |
284 | * to dereference. | 290 | return return_ptr; |
285 | */ | ||
286 | goto complain; /* complain and return NULL */ | ||
287 | } | ||
288 | log_option("option found", optionptr); | ||
289 | return optionptr + OPT_DATA; | ||
290 | } | 291 | } |
292 | scan_state->optionptr += len; | ||
293 | } | ||
291 | 294 | ||
292 | if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) { | 295 | return NULL; |
293 | if (len >= 3) | 296 | } |
294 | overload |= optionptr[OPT_DATA]; | 297 | |
295 | /* fall through */ | 298 | /* Get an option with bounds checking (warning, result is not aligned) */ |
299 | uint8_t* FAST_FUNC udhcp_get_option(struct dhcp_packet *packet, int code) | ||
300 | { | ||
301 | uint8_t *optptr; | ||
302 | struct dhcp_scan_state scan_state; | ||
303 | |||
304 | init_scan_state(packet, &scan_state); | ||
305 | while ((optptr = udhcp_scan_options(packet, &scan_state)) != NULL) { | ||
306 | if (optptr[OPT_CODE] == code) { | ||
307 | log_option("option found", optptr); | ||
308 | return optptr + OPT_DATA; | ||
296 | } | 309 | } |
297 | optionptr += len; | ||
298 | } | 310 | } |
299 | 311 | ||
300 | /* log3 because udhcpc uses it a lot - very noisy */ | 312 | /* log3 because udhcpc uses it a lot - very noisy */ |
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h index 6214db06a..81c1dcbdc 100644 --- a/networking/udhcp/common.h +++ b/networking/udhcp/common.h | |||
@@ -107,6 +107,12 @@ enum { | |||
107 | OPTION_LIST = 0x20, | 107 | OPTION_LIST = 0x20, |
108 | }; | 108 | }; |
109 | 109 | ||
110 | struct dhcp_scan_state { | ||
111 | int overload; | ||
112 | int rem; | ||
113 | uint8_t *optionptr; | ||
114 | }; | ||
115 | |||
110 | /* DHCP option codes (partial list). See RFC 2132 and | 116 | /* DHCP option codes (partial list). See RFC 2132 and |
111 | * http://www.iana.org/assignments/bootp-dhcp-parameters/ | 117 | * http://www.iana.org/assignments/bootp-dhcp-parameters/ |
112 | * Commented out options are handled by common option machinery, | 118 | * Commented out options are handled by common option machinery, |
@@ -206,6 +212,8 @@ extern const uint8_t dhcp_option_lengths[] ALIGN1; | |||
206 | 212 | ||
207 | unsigned FAST_FUNC udhcp_option_idx(const char *name, const char *option_strings); | 213 | unsigned FAST_FUNC udhcp_option_idx(const char *name, const char *option_strings); |
208 | 214 | ||
215 | void init_scan_state(struct dhcp_packet *packet, struct dhcp_scan_state *scan_state) FAST_FUNC; | ||
216 | uint8_t *udhcp_scan_options(struct dhcp_packet *packet, struct dhcp_scan_state *scan_state) FAST_FUNC; | ||
209 | uint8_t *udhcp_get_option(struct dhcp_packet *packet, int code) FAST_FUNC; | 217 | uint8_t *udhcp_get_option(struct dhcp_packet *packet, int code) FAST_FUNC; |
210 | /* Same as above + ensures that option length is 4 bytes | 218 | /* Same as above + ensures that option length is 4 bytes |
211 | * (returns NULL if size is different) | 219 | * (returns NULL if size is different) |