Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / src / utils / common.c
index 1b8ea80..660e9fc 100644 (file)
@@ -2,18 +2,13 @@
  * wpa_supplicant/hostapd / common helper functions, etc.
  * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
 
+#include "common/ieee802_11_defs.h"
 #include "common.h"
 
 
@@ -29,7 +24,7 @@ static int hex2num(char c)
 }
 
 
-static int hex2byte(const char *hex)
+int hex2byte(const char *hex)
 {
        int a, b;
        a = hex2num(*hex++);
@@ -42,6 +37,25 @@ static 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")
@@ -50,6 +64,54 @@ static int hex2byte(const char *hex)
  */
 int hwaddr_aton(const char *txt, u8 *addr)
 {
+       return hwaddr_parse(txt, addr) ? 0 : -1;
+}
+
+
+/**
+ * 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")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_compact_aton(const char *txt, u8 *addr)
+{
        int i;
 
        for (i = 0; i < 6; i++) {
@@ -62,14 +124,11 @@ int hwaddr_aton(const char *txt, u8 *addr)
                if (b < 0)
                        return -1;
                *addr++ = (a << 4) | b;
-               if (i < 5 && *txt++ != ':')
-                       return -1;
        }
 
        return 0;
 }
 
-
 /**
  * hwaddr_aton2 - Convert ASCII string to MAC address (in any known format)
  * @txt: MAC address as a string (e.g., 00:11:22:33:44:55 or 0011.2233.4455)
@@ -126,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
@@ -165,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)
@@ -177,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;
                }
@@ -326,6 +463,137 @@ TCHAR * wpa_strdup_tchar(const char *str)
 #endif /* CONFIG_NATIVE_WINDOWS */
 
 
+void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len)
+{
+       char *end = txt + maxlen;
+       size_t i;
+
+       for (i = 0; i < len; i++) {
+               if (txt + 4 >= end)
+                       break;
+
+               switch (data[i]) {
+               case '\"':
+                       *txt++ = '\\';
+                       *txt++ = '\"';
+                       break;
+               case '\\':
+                       *txt++ = '\\';
+                       *txt++ = '\\';
+                       break;
+               case '\033':
+                       *txt++ = '\\';
+                       *txt++ = 'e';
+                       break;
+               case '\n':
+                       *txt++ = '\\';
+                       *txt++ = 'n';
+                       break;
+               case '\r':
+                       *txt++ = '\\';
+                       *txt++ = 'r';
+                       break;
+               case '\t':
+                       *txt++ = '\\';
+                       *txt++ = 't';
+                       break;
+               default:
+                       if (data[i] >= 32 && data[i] <= 127) {
+                               *txt++ = data[i];
+                       } else {
+                               txt += os_snprintf(txt, end - txt, "\\x%02x",
+                                                  data[i]);
+                       }
+                       break;
+               }
+       }
+
+       *txt = '\0';
+}
+
+
+size_t printf_decode(u8 *buf, size_t maxlen, const char *str)
+{
+       const char *pos = str;
+       size_t len = 0;
+       int val;
+
+       while (*pos) {
+               if (len + 1 >= maxlen)
+                       break;
+               switch (*pos) {
+               case '\\':
+                       pos++;
+                       switch (*pos) {
+                       case '\\':
+                               buf[len++] = '\\';
+                               pos++;
+                               break;
+                       case '"':
+                               buf[len++] = '"';
+                               pos++;
+                               break;
+                       case 'n':
+                               buf[len++] = '\n';
+                               pos++;
+                               break;
+                       case 'r':
+                               buf[len++] = '\r';
+                               pos++;
+                               break;
+                       case 't':
+                               buf[len++] = '\t';
+                               pos++;
+                               break;
+                       case 'e':
+                               buf[len++] = '\033';
+                               pos++;
+                               break;
+                       case 'x':
+                               pos++;
+                               val = hex2byte(pos);
+                               if (val < 0) {
+                                       val = hex2num(*pos);
+                                       if (val < 0)
+                                               break;
+                                       buf[len++] = val;
+                                       pos++;
+                               } else {
+                                       buf[len++] = val;
+                                       pos += 2;
+                               }
+                               break;
+                       case '0':
+                       case '1':
+                       case '2':
+                       case '3':
+                       case '4':
+                       case '5':
+                       case '6':
+                       case '7':
+                               val = *pos++ - '0';
+                               if (*pos >= '0' && *pos <= '7')
+                                       val = val * 8 + (*pos++ - '0');
+                               if (*pos >= '0' && *pos <= '7')
+                                       val = val * 8 + (*pos++ - '0');
+                               buf[len++] = val;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               default:
+                       buf[len++] = *pos++;
+                       break;
+               }
+       }
+       if (maxlen > len)
+               buf[len] = '\0';
+
+       return len;
+}
+
+
 /**
  * wpa_ssid_txt - Convert SSID to a printable string
  * @ssid: SSID (32-octet string)
@@ -342,17 +610,14 @@ TCHAR * wpa_strdup_tchar(const char *str)
  */
 const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len)
 {
-       static char ssid_txt[33];
-       char *pos;
+       static char ssid_txt[SSID_MAX_LEN * 4 + 1];
 
-       if (ssid_len > 32)
-               ssid_len = 32;
-       os_memcpy(ssid_txt, ssid, ssid_len);
-       ssid_txt[ssid_len] = '\0';
-       for (pos = ssid_txt; *pos != '\0'; pos++) {
-               if ((u8) *pos < 32 || (u8) *pos >= 127)
-                       *pos = '_';
+       if (ssid == NULL) {
+               ssid_txt[0] = '\0';
+               return ssid_txt;
        }
+
+       printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len);
        return ssid_txt;
 }
 
@@ -361,3 +626,500 @@ void * __hide_aliasing_typecast(void *foo)
 {
        return foo;
 }
+
+
+char * wpa_config_parse_string(const char *value, size_t *len)
+{
+       if (*value == '"') {
+               const char *pos;
+               char *str;
+               value++;
+               pos = os_strrchr(value, '"');
+               if (pos == NULL || pos[1] != '\0')
+                       return NULL;
+               *len = pos - value;
+               str = dup_binstr(value, *len);
+               if (str == NULL)
+                       return NULL;
+               return str;
+       } else if (*value == 'P' && value[1] == '"') {
+               const char *pos;
+               char *tstr, *str;
+               size_t tlen;
+               value += 2;
+               pos = os_strrchr(value, '"');
+               if (pos == NULL || pos[1] != '\0')
+                       return NULL;
+               tlen = pos - value;
+               tstr = dup_binstr(value, tlen);
+               if (tstr == NULL)
+                       return NULL;
+
+               str = os_malloc(tlen + 1);
+               if (str == NULL) {
+                       os_free(tstr);
+                       return NULL;
+               }
+
+               *len = printf_decode((u8 *) str, tlen + 1, tstr);
+               os_free(tstr);
+
+               return str;
+       } else {
+               u8 *str;
+               size_t tlen, hlen = os_strlen(value);
+               if (hlen & 1)
+                       return NULL;
+               tlen = hlen / 2;
+               str = os_malloc(tlen + 1);
+               if (str == NULL)
+                       return NULL;
+               if (hexstr2bin(value, str, tlen)) {
+                       os_free(str);
+                       return NULL;
+               }
+               str[tlen] = '\0';
+               *len = tlen;
+               return (char *) str;
+       }
+}
+
+
+int is_hex(const u8 *data, size_t len)
+{
+       size_t i;
+
+       for (i = 0; i < len; i++) {
+               if (data[i] < 32 || data[i] >= 127)
+                       return 1;
+       }
+       return 0;
+}
+
+
+size_t merge_byte_arrays(u8 *res, size_t res_len,
+                        const u8 *src1, size_t src1_len,
+                        const u8 *src2, size_t src2_len)
+{
+       size_t len = 0;
+
+       os_memset(res, 0, res_len);
+
+       if (src1) {
+               if (src1_len >= res_len) {
+                       os_memcpy(res, src1, res_len);
+                       return res_len;
+               }
+
+               os_memcpy(res, src1, src1_len);
+               len += src1_len;
+       }
+
+       if (src2) {
+               if (len + src2_len >= res_len) {
+                       os_memcpy(res + len, src2, res_len - len);
+                       return res_len;
+               }
+
+               os_memcpy(res + len, src2, src2_len);
+               len += src2_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;
+}
+
+
+/**
+ * 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;
+}