diff options
Diffstat (limited to 'src/utils/common.c')
-rw-r--r-- | src/utils/common.c | 496 |
1 files changed, 474 insertions, 22 deletions
diff --git a/src/utils/common.c b/src/utils/common.c index e63698491444..5fd795f3f303 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -36,6 +36,25 @@ int hex2byte(const char *hex) } +static const char * hwaddr_parse(const char *txt, u8 *addr) +{ + size_t i; + + for (i = 0; i < ETH_ALEN; i++) { + int a; + + a = hex2byte(txt); + if (a < 0) + return NULL; + txt += 2; + addr[i] = a; + if (i < ETH_ALEN - 1 && *txt++ != ':') + return NULL; + } + return txt; +} + + /** * hwaddr_aton - Convert ASCII string to MAC address (colon-delimited format) * @txt: MAC address as a string (e.g., "00:11:22:33:44:55") @@ -44,25 +63,46 @@ int hex2byte(const char *hex) */ int hwaddr_aton(const char *txt, u8 *addr) { - int i; + return hwaddr_parse(txt, addr) ? 0 : -1; +} - for (i = 0; i < 6; i++) { - int a, b; - a = hex2num(*txt++); - if (a < 0) - return -1; - b = hex2num(*txt++); - if (b < 0) - return -1; - *addr++ = (a << 4) | b; - if (i < 5 && *txt++ != ':') +/** + * hwaddr_masked_aton - Convert ASCII string with optional mask to MAC address (colon-delimited format) + * @txt: MAC address with optional mask as a string (e.g., "00:11:22:33:44:55/ff:ff:ff:ff:00:00") + * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes) + * @mask: Buffer for the MAC address mask (ETH_ALEN = 6 bytes) + * @maskable: Flag to indicate whether a mask is allowed + * Returns: 0 on success, -1 on failure (e.g., string not a MAC address) + */ +int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable) +{ + const char *r; + + /* parse address part */ + r = hwaddr_parse(txt, addr); + if (!r) + return -1; + + /* check for optional mask */ + if (*r == '\0' || isspace(*r)) { + /* no mask specified, assume default */ + os_memset(mask, 0xff, ETH_ALEN); + } else if (maskable && *r == '/') { + /* mask specified and allowed */ + r = hwaddr_parse(r + 1, mask); + /* parser error? */ + if (!r) return -1; + } else { + /* mask specified but not allowed or trailing garbage */ + return -1; } return 0; } + /** * hwaddr_compact_aton - Convert ASCII string to MAC address (no colon delimitors format) * @txt: MAC address as a string (e.g., "001122334455") @@ -144,6 +184,30 @@ int hexstr2bin(const char *hex, u8 *buf, size_t len) } +int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask) +{ + size_t i; + int print_mask = 0; + int res; + + for (i = 0; i < ETH_ALEN; i++) { + if (mask[i] != 0xff) { + print_mask = 1; + break; + } + } + + if (print_mask) + res = os_snprintf(buf, len, MACSTR "/" MACSTR, + MAC2STR(addr), MAC2STR(mask)); + else + res = os_snprintf(buf, len, MACSTR, MAC2STR(addr)); + if (os_snprintf_error(len, res)) + return -1; + return res; +} + + /** * inc_byte_array - Increment arbitrary length byte array by one * @counter: Pointer to byte array @@ -183,6 +247,35 @@ void wpa_get_ntp_timestamp(u8 *buf) os_memcpy(buf + 4, (u8 *) &tmp, 4); } +/** + * wpa_scnprintf - Simpler-to-use snprintf function + * @buf: Output buffer + * @size: Buffer size + * @fmt: format + * + * Simpler snprintf version that doesn't require further error checks - the + * return value only indicates how many bytes were actually written, excluding + * the NULL byte (i.e., 0 on error, size-1 if buffer is not big enough). + */ +int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...) +{ + va_list ap; + int ret; + + if (!size) + return 0; + + va_start(ap, fmt); + ret = vsnprintf(buf, size, fmt, ap); + va_end(ap); + + if (ret < 0) + return 0; + if ((size_t) ret >= size) + return size - 1; + + return ret; +} static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len, int uppercase) @@ -195,7 +288,7 @@ static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, for (i = 0; i < len; i++) { ret = os_snprintf(pos, end - pos, uppercase ? "%02X" : "%02x", data[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return pos - buf; } @@ -350,7 +443,7 @@ void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len) size_t i; for (i = 0; i < len; i++) { - if (txt + 4 > end) + if (txt + 4 >= end) break; switch (data[i]) { @@ -362,7 +455,7 @@ void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len) *txt++ = '\\'; *txt++ = '\\'; break; - case '\e': + case '\033': *txt++ = '\\'; *txt++ = 'e'; break; @@ -400,7 +493,7 @@ size_t printf_decode(u8 *buf, size_t maxlen, const char *str) int val; while (*pos) { - if (len == maxlen) + if (len + 1 >= maxlen) break; switch (*pos) { case '\\': @@ -427,7 +520,7 @@ size_t printf_decode(u8 *buf, size_t maxlen, const char *str) pos++; break; case 'e': - buf[len++] = '\e'; + buf[len++] = '\033'; pos++; break; case 'x': @@ -468,6 +561,8 @@ size_t printf_decode(u8 *buf, size_t maxlen, const char *str) break; } } + if (maxlen > len) + buf[len] = '\0'; return len; } @@ -517,11 +612,9 @@ char * wpa_config_parse_string(const char *value, size_t *len) if (pos == NULL || pos[1] != '\0') return NULL; *len = pos - value; - str = os_malloc(*len + 1); + str = dup_binstr(value, *len); if (str == NULL) return NULL; - os_memcpy(str, value, *len); - str[*len] = '\0'; return str; } else if (*value == 'P' && value[1] == '"') { const char *pos; @@ -532,11 +625,9 @@ char * wpa_config_parse_string(const char *value, size_t *len) if (pos == NULL || pos[1] != '\0') return NULL; tlen = pos - value; - tstr = os_malloc(tlen + 1); + tstr = dup_binstr(value, tlen); if (tstr == NULL) return NULL; - os_memcpy(tstr, value, tlen); - tstr[tlen] = '\0'; str = os_malloc(tlen + 1); if (str == NULL) { @@ -610,3 +701,364 @@ size_t merge_byte_arrays(u8 *res, size_t res_len, return len; } + + +char * dup_binstr(const void *src, size_t len) +{ + char *res; + + if (src == NULL) + return NULL; + res = os_malloc(len + 1); + if (res == NULL) + return NULL; + os_memcpy(res, src, len); + res[len] = '\0'; + + return res; +} + + +int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value) +{ + struct wpa_freq_range *freq = NULL, *n; + unsigned int count = 0; + const char *pos, *pos2, *pos3; + + /* + * Comma separated list of frequency ranges. + * For example: 2412-2432,2462,5000-6000 + */ + pos = value; + while (pos && pos[0]) { + n = os_realloc_array(freq, count + 1, + sizeof(struct wpa_freq_range)); + if (n == NULL) { + os_free(freq); + return -1; + } + freq = n; + freq[count].min = atoi(pos); + pos2 = os_strchr(pos, '-'); + pos3 = os_strchr(pos, ','); + if (pos2 && (!pos3 || pos2 < pos3)) { + pos2++; + freq[count].max = atoi(pos2); + } else + freq[count].max = freq[count].min; + pos = pos3; + if (pos) + pos++; + count++; + } + + os_free(res->range); + res->range = freq; + res->num = count; + + return 0; +} + + +int freq_range_list_includes(const struct wpa_freq_range_list *list, + unsigned int freq) +{ + unsigned int i; + + if (list == NULL) + return 0; + + for (i = 0; i < list->num; i++) { + if (freq >= list->range[i].min && freq <= list->range[i].max) + return 1; + } + + return 0; +} + + +char * freq_range_list_str(const struct wpa_freq_range_list *list) +{ + char *buf, *pos, *end; + size_t maxlen; + unsigned int i; + int res; + + if (list->num == 0) + return NULL; + + maxlen = list->num * 30; + buf = os_malloc(maxlen); + if (buf == NULL) + return NULL; + pos = buf; + end = buf + maxlen; + + for (i = 0; i < list->num; i++) { + struct wpa_freq_range *range = &list->range[i]; + + if (range->min == range->max) + res = os_snprintf(pos, end - pos, "%s%u", + i == 0 ? "" : ",", range->min); + else + res = os_snprintf(pos, end - pos, "%s%u-%u", + i == 0 ? "" : ",", + range->min, range->max); + if (os_snprintf_error(end - pos, res)) { + os_free(buf); + return NULL; + } + pos += res; + } + + return buf; +} + + +int int_array_len(const int *a) +{ + int i; + for (i = 0; a && a[i]; i++) + ; + return i; +} + + +void int_array_concat(int **res, const int *a) +{ + int reslen, alen, i; + int *n; + + reslen = int_array_len(*res); + alen = int_array_len(a); + + n = os_realloc_array(*res, reslen + alen + 1, sizeof(int)); + if (n == NULL) { + os_free(*res); + *res = NULL; + return; + } + for (i = 0; i <= alen; i++) + n[reslen + i] = a[i]; + *res = n; +} + + +static int freq_cmp(const void *a, const void *b) +{ + int _a = *(int *) a; + int _b = *(int *) b; + + if (_a == 0) + return 1; + if (_b == 0) + return -1; + return _a - _b; +} + + +void int_array_sort_unique(int *a) +{ + int alen; + int i, j; + + if (a == NULL) + return; + + alen = int_array_len(a); + qsort(a, alen, sizeof(int), freq_cmp); + + i = 0; + j = 1; + while (a[i] && a[j]) { + if (a[i] == a[j]) { + j++; + continue; + } + a[++i] = a[j++]; + } + if (a[i]) + i++; + a[i] = 0; +} + + +void int_array_add_unique(int **res, int a) +{ + int reslen; + int *n; + + for (reslen = 0; *res && (*res)[reslen]; reslen++) { + if ((*res)[reslen] == a) + return; /* already in the list */ + } + + n = os_realloc_array(*res, reslen + 2, sizeof(int)); + if (n == NULL) { + os_free(*res); + *res = NULL; + return; + } + + n[reslen] = a; + n[reslen + 1] = 0; + + *res = n; +} + + +void str_clear_free(char *str) +{ + if (str) { + size_t len = os_strlen(str); + os_memset(str, 0, len); + os_free(str); + } +} + + +void bin_clear_free(void *bin, size_t len) +{ + if (bin) { + os_memset(bin, 0, len); + os_free(bin); + } +} + + +int random_mac_addr(u8 *addr) +{ + if (os_get_random(addr, ETH_ALEN) < 0) + return -1; + addr[0] &= 0xfe; /* unicast */ + addr[0] |= 0x02; /* locally administered */ + return 0; +} + + +int random_mac_addr_keep_oui(u8 *addr) +{ + if (os_get_random(addr + 3, 3) < 0) + return -1; + addr[0] &= 0xfe; /* unicast */ + addr[0] |= 0x02; /* locally administered */ + return 0; +} + + +/** + * str_token - Get next token from a string + * @buf: String to tokenize. Note that the string might be modified. + * @delim: String of delimiters + * @context: Pointer to save our context. Should be initialized with + * NULL on the first call, and passed for any further call. + * Returns: The next token, NULL if there are no more valid tokens. + */ +char * str_token(char *str, const char *delim, char **context) +{ + char *end, *pos = str; + + if (*context) + pos = *context; + + while (*pos && os_strchr(delim, *pos)) + pos++; + if (!*pos) + return NULL; + + end = pos + 1; + while (*end && !os_strchr(delim, *end)) + end++; + + if (*end) + *end++ = '\0'; + + *context = end; + return pos; +} + + +size_t utf8_unescape(const char *inp, size_t in_size, + char *outp, size_t out_size) +{ + size_t res_size = 0; + + if (!inp || !outp) + return 0; + + if (!in_size) + in_size = os_strlen(inp); + + /* Advance past leading single quote */ + if (*inp == '\'' && in_size) { + inp++; + in_size--; + } + + while (in_size--) { + if (res_size >= out_size) + return 0; + + switch (*inp) { + case '\'': + /* Terminate on bare single quote */ + *outp = '\0'; + return res_size; + + case '\\': + if (!in_size--) + return 0; + inp++; + /* fall through */ + + default: + *outp++ = *inp++; + res_size++; + } + } + + /* NUL terminate if space allows */ + if (res_size < out_size) + *outp = '\0'; + + return res_size; +} + + +size_t utf8_escape(const char *inp, size_t in_size, + char *outp, size_t out_size) +{ + size_t res_size = 0; + + if (!inp || !outp) + return 0; + + /* inp may or may not be NUL terminated, but must be if 0 size + * is specified */ + if (!in_size) + in_size = os_strlen(inp); + + while (in_size--) { + if (res_size++ >= out_size) + return 0; + + switch (*inp) { + case '\\': + case '\'': + if (res_size++ >= out_size) + return 0; + *outp++ = '\\'; + /* fall through */ + + default: + *outp++ = *inp++; + break; + } + } + + /* NUL terminate if space allows */ + if (res_size < out_size) + *outp = '\0'; + + return res_size; +} |