Share a single str_starts() implementation
[mech_eap.git] / src / utils / common.c
index 257bb6d..68413b2 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "includes.h"
 
+#include "common/ieee802_11_defs.h"
 #include "common.h"
 
 
@@ -36,6 +37,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 +64,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((unsigned char) *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 +185,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 +248,60 @@ 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;
+}
+
+
+int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
+                        char sep)
+{
+       size_t i;
+       char *pos = buf, *end = buf + buf_size;
+       int ret;
+
+       if (buf_size == 0)
+               return 0;
+
+       for (i = 0; i < len; i++) {
+               ret = os_snprintf(pos, end - pos, "%02x%c",
+                                 data[i], sep);
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return pos - buf;
+               }
+               pos += ret;
+       }
+       pos[-1] = '\0';
+       return pos - buf;
+}
+
 
 static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data,
                                    size_t len, int uppercase)
@@ -195,7 +314,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 +469,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 +481,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;
@@ -379,7 +498,7 @@ void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len)
                        *txt++ = 't';
                        break;
                default:
-                       if (data[i] >= 32 && data[i] <= 127) {
+                       if (data[i] >= 32 && data[i] <= 126) {
                                *txt++ = data[i];
                        } else {
                                txt += os_snprintf(txt, end - txt, "\\x%02x",
@@ -427,7 +546,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':
@@ -491,7 +610,7 @@ size_t printf_decode(u8 *buf, size_t maxlen, const char *str)
  */
 const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len)
 {
-       static char ssid_txt[32 * 4 + 1];
+       static char ssid_txt[SSID_MAX_LEN * 4 + 1];
 
        if (ssid == NULL) {
                ssid_txt[0] = '\0';
@@ -578,18 +697,26 @@ int is_hex(const u8 *data, size_t len)
 }
 
 
-int find_first_bit(u32 value)
+int has_ctrl_char(const u8 *data, size_t len)
 {
-       int pos = 0;
+       size_t i;
 
-       while (value) {
-               if (value & 0x1)
-                       return pos;
-               value >>= 1;
-               pos++;
+       for (i = 0; i < len; i++) {
+               if (data[i] < 32 || data[i] == 127)
+                       return 1;
        }
+       return 0;
+}
 
-       return -1;
+
+int has_newline(const char *str)
+{
+       while (*str) {
+               if (*str == '\n' || *str == '\r')
+                       return 1;
+               str++;
+       }
+       return 0;
 }
 
 
@@ -726,7 +853,7 @@ char * freq_range_list_str(const struct wpa_freq_range_list *list)
                        res = os_snprintf(pos, end - pos, "%s%u-%u",
                                          i == 0 ? "" : ",",
                                          range->min, range->max);
-               if (res < 0 || res > end - pos) {
+               if (os_snprintf_error(end - pos, res)) {
                        os_free(buf);
                        return NULL;
                }
@@ -735,3 +862,341 @@ char * freq_range_list_str(const struct wpa_freq_range_list *list)
 
        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;
+}
+
+
+/**
+ * cstr_token - Get next token from const char string
+ * @str: a constant string to tokenize
+ * @delim: a string of delimiters
+ * @last: a pointer to a character following the returned token
+ *      It has to be set to NULL for the first call and passed for any
+ *      futher call.
+ * Returns: a pointer to token position in str or NULL
+ *
+ * This function is similar to str_token, but it can be used with both
+ * char and const char strings. Differences:
+ * - The str buffer remains unmodified
+ * - The returned token is not a NULL terminated string, but a token
+ *   position in str buffer. If a return value is not NULL a size
+ *   of the returned token could be calculated as (last - token).
+ */
+const char * cstr_token(const char *str, const char *delim, const char **last)
+{
+       const char *end, *token = str;
+
+       if (!str || !delim || !last)
+               return NULL;
+
+       if (*last)
+               token = *last;
+
+       while (*token && os_strchr(delim, *token))
+               token++;
+
+       if (!*token)
+               return NULL;
+
+       end = token + 1;
+
+       while (*end && !os_strchr(delim, *end))
+               end++;
+
+       *last = end;
+       return token;
+}
+
+
+/**
+ * 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 *token = (char *) cstr_token(str, delim, (const char **) context);
+
+       if (token && **context)
+               *(*context)++ = '\0';
+
+       return token;
+}
+
+
+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;
+}
+
+
+int is_ctrl_char(char c)
+{
+       return c > 0 && c < 32;
+}
+
+
+/**
+ * ssid_parse - Parse a string that contains SSID in hex or text format
+ * @buf: Input NULL terminated string that contains the SSID
+ * @ssid: Output SSID
+ * Returns: 0 on success, -1 otherwise
+ *
+ * The SSID has to be enclosed in double quotes for the text format or space
+ * or NULL terminated string of hex digits for the hex format. buf can include
+ * additional arguments after the SSID.
+ */
+int ssid_parse(const char *buf, struct wpa_ssid_value *ssid)
+{
+       char *tmp, *res, *end;
+       size_t len;
+
+       ssid->ssid_len = 0;
+
+       tmp = os_strdup(buf);
+       if (!tmp)
+               return -1;
+
+       if (*tmp != '"') {
+               end = os_strchr(tmp, ' ');
+               if (end)
+                       *end = '\0';
+       } else {
+               end = os_strchr(tmp + 1, '"');
+               if (!end) {
+                       os_free(tmp);
+                       return -1;
+               }
+
+               end[1] = '\0';
+       }
+
+       res = wpa_config_parse_string(tmp, &len);
+       if (res && len <= SSID_MAX_LEN) {
+               ssid->ssid_len = len;
+               os_memcpy(ssid->ssid, res, len);
+       }
+
+       os_free(tmp);
+       os_free(res);
+
+       return ssid->ssid_len ? 0 : -1;
+}
+
+
+int str_starts(const char *str, const char *start)
+{
+       return os_strncmp(str, start, os_strlen(start)) == 0;
+}