udhcp: add option scanner
No binary (build failed)
—
(log yok)
| @@ -15,7 +15,7 @@ const uint8_t MAC_BCAST_ADDR[6] ALIGN2 = { | |
| }; | |
| #if ENABLE_UDHCPC || ENABLE_UDHCPD | |
| - | /* Supported options are easily added here. |
| + | /* Supported options are easily added here, they need to be sorted. |
| * See RFC2132 for more options. | |
| * OPTION_REQ: these options are requested by udhcpc (unless -o). | |
| */ | |
| @@ -222,79 +222,91 @@ unsigned FAST_FUNC udhcp_option_idx(const char *name, const char *option_strings | |
| } | |
| } | |
| - | /* Get an option with bounds checking (warning, result is not aligned) */ |
| - | uint8_t* FAST_FUNC udhcp_get_option(struct dhcp_packet *packet, int code) |
| + | /* Initialize state to be used between subsequent udhcp_scan_options calls */ |
| + | void FAST_FUNC init_scan_state(struct dhcp_packet *packet, struct dhcp_scan_state *scan_state) |
| + | { |
| + | scan_state->overload = 0; |
| + | scan_state->rem = sizeof(packet->options); |
| + | scan_state->optionptr = packet->options; |
| + | } |
| + | |
| + | /* Iterate over packet's options, each call returning the next option. |
| + | * scan_state needs to be initialized with init_scan_state beforehand. |
| + | * Warning, result is not aligned. */ |
| + | uint8_t* FAST_FUNC udhcp_scan_options(struct dhcp_packet *packet, struct dhcp_scan_state *scan_state) |
| { | |
| - | uint8_t *optionptr; |
| int len; | |
| - | int rem; |
| - | int overload = 0; |
| enum { | |
| FILE_FIELD101 = FILE_FIELD * 0x101, | |
| SNAME_FIELD101 = SNAME_FIELD * 0x101, | |
| }; | |
| /* option bytes: [code][len][data1][data2]..[dataLEN] */ | |
| - | optionptr = packet->options; |
| - | rem = sizeof(packet->options); |
| while (1) { | |
| - | if (rem <= 0) { |
| + | if (scan_state->rem <= 0) { |
| complain: | |
| bb_simple_error_msg("bad packet, malformed option field"); | |
| return NULL; | |
| } | |
| /* DHCP_PADDING and DHCP_END have no [len] byte */ | |
| - | if (optionptr[OPT_CODE] == DHCP_PADDING) { |
| - | rem--; |
| - | optionptr++; |
| + | if (scan_state->optionptr[OPT_CODE] == DHCP_PADDING) { |
| + | scan_state->rem--; |
| + | scan_state->optionptr++; |
| continue; | |
| } | |
| - | if (optionptr[OPT_CODE] == DHCP_END) { |
| - | if ((overload & FILE_FIELD101) == FILE_FIELD) { |
| + | if (scan_state->optionptr[OPT_CODE] == DHCP_END) { |
| + | if ((scan_state->overload & FILE_FIELD101) == FILE_FIELD) { |
| /* can use packet->file, and didn't look at it yet */ | |
| - | overload |= FILE_FIELD101; /* "we looked at it" */ |
| - | optionptr = packet->file; |
| - | rem = sizeof(packet->file); |
| + | scan_state->overload |= FILE_FIELD101; /* "we looked at it" */ |
| + | scan_state->optionptr = packet->file; |
| + | scan_state->rem = sizeof(packet->file); |
| continue; | |
| } | |
| - | if ((overload & SNAME_FIELD101) == SNAME_FIELD) { |
| + | if ((scan_state->overload & SNAME_FIELD101) == SNAME_FIELD) { |
| /* can use packet->sname, and didn't look at it yet */ | |
| - | overload |= SNAME_FIELD101; /* "we looked at it" */ |
| - | optionptr = packet->sname; |
| - | rem = sizeof(packet->sname); |
| + | scan_state->overload |= SNAME_FIELD101; /* "we looked at it" */ |
| + | scan_state->optionptr = packet->sname; |
| + | scan_state->rem = sizeof(packet->sname); |
| continue; | |
| } | |
| break; | |
| } | |
| - | if (rem <= OPT_LEN) |
| + | if (scan_state->rem <= OPT_LEN) |
| goto complain; /* complain and return NULL */ | |
| - | len = 2 + optionptr[OPT_LEN]; |
| - | rem -= len; |
| - | if (rem < 0) |
| + | len = 2 + scan_state->optionptr[OPT_LEN]; |
| + | scan_state->rem -= len; |
| + | /* So far no valid option with length 0 known. */ |
| + | if (scan_state->rem < 0 || scan_state->optionptr[OPT_LEN] == 0) |
| goto complain; /* complain and return NULL */ | |
| - | if (optionptr[OPT_CODE] == code) { |
| - | if (optionptr[OPT_LEN] == 0) { |
| - | /* So far no valid option with length 0 known. |
| - | * Having this check means that searching |
| - | * for DHCP_MESSAGE_TYPE need not worry |
| - | * that returned pointer might be unsafe |
| - | * to dereference. |
| - | */ |
| - | goto complain; /* complain and return NULL */ |
| - | } |
| - | log_option("option found", optionptr); |
| - | return optionptr + OPT_DATA; |
| + | if (scan_state->optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) { |
| + | if (len >= 3) |
| + | scan_state->overload |= scan_state->optionptr[OPT_DATA]; |
| + | } else { |
| + | uint8_t *return_ptr = scan_state->optionptr; |
| + | scan_state->optionptr += len; |
| + | return return_ptr; |
| } | |
| + | scan_state->optionptr += len; |
| + | } |
| - | if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) { |
| - | if (len >= 3) |
| - | overload |= optionptr[OPT_DATA]; |
| - | /* fall through */ |
| + | return NULL; |
| + | } |
| + | |
| + | /* Get an option with bounds checking (warning, result is not aligned) */ |
| + | uint8_t* FAST_FUNC udhcp_get_option(struct dhcp_packet *packet, int code) |
| + | { |
| + | uint8_t *optptr; |
| + | struct dhcp_scan_state scan_state; |
| + | |
| + | init_scan_state(packet, &scan_state); |
| + | while ((optptr = udhcp_scan_options(packet, &scan_state)) != NULL) { |
| + | if (optptr[OPT_CODE] == code) { |
| + | log_option("option found", optptr); |
| + | return optptr + OPT_DATA; |
| } | |
| - | optionptr += len; |
| } | |
| /* log3 because udhcpc uses it a lot - very noisy */ | |
| @@ -107,6 +107,12 @@ enum { | |
| OPTION_LIST = 0x20, | |
| }; | |
| + | struct dhcp_scan_state { |
| + | int overload; |
| + | int rem; |
| + | uint8_t *optionptr; |
| + | }; |
| + | |
| /* DHCP option codes (partial list). See RFC 2132 and | |
| * http://www.iana.org/assignments/bootp-dhcp-parameters/ | |
| * Commented out options are handled by common option machinery, | |
| @@ -206,6 +212,8 @@ extern const uint8_t dhcp_option_lengths[] ALIGN1; | |
| unsigned FAST_FUNC udhcp_option_idx(const char *name, const char *option_strings); | |
| + | void init_scan_state(struct dhcp_packet *packet, struct dhcp_scan_state *scan_state) FAST_FUNC; |
| + | uint8_t *udhcp_scan_options(struct dhcp_packet *packet, struct dhcp_scan_state *scan_state) FAST_FUNC; |
| uint8_t *udhcp_get_option(struct dhcp_packet *packet, int code) FAST_FUNC; | |
| /* Same as above + ensures that option length is 4 bytes | |
| * (returns NULL if size is different) | |