HS 2.0R2: Add routine for fetching OSU provider information
authorJouni Malinen <jouni@qca.qualcomm.com>
Fri, 25 Jan 2013 22:10:41 +0000 (00:10 +0200)
committerJouni Malinen <j@w1.fi>
Tue, 25 Feb 2014 23:24:23 +0000 (01:24 +0200)
The new wpa_cli fetch_osu command can be used to fetch information about
all OSU providers and write that to a text file with the icons in
separate files. cancel_osu_fetch command can be used to stop ongoing OSU
provider list fetch.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>

wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/ctrl_iface.c
wpa_supplicant/hs20_supplicant.c
wpa_supplicant/hs20_supplicant.h
wpa_supplicant/interworking.c
wpa_supplicant/wpa_cli.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index def3e83..343d7c1 100644 (file)
@@ -1999,6 +1999,7 @@ void wpa_config_free(struct wpa_config *config)
        os_free(config->ext_password_backend);
        os_free(config->sae_groups);
        wpabuf_free(config->ap_vendor_elements);
+       os_free(config->osu_dir);
        os_free(config);
 }
 
@@ -3464,6 +3465,7 @@ static const struct global_parse_data global_fields[] = {
        { INT(scan_cur_freq), 0 },
        { INT(sched_scan_interval), 0 },
        { INT(tdls_external_control), 0},
+       { STR(osu_dir), 0 },
 };
 
 #undef FUNC
index b64f414..9e06e70 100644 (file)
@@ -963,6 +963,15 @@ struct wpa_config {
        u8 ip_addr_mask[4];
        u8 ip_addr_start[4];
        u8 ip_addr_end[4];
+
+       /**
+        * osu_dir - OSU provider information directory
+        *
+        * If set, allow FETCH_OSU control interface command to be used to fetch
+        * OSU provider information into all APs and store the results in this
+        * directory.
+        */
+       char *osu_dir;
 };
 
 
index dc038fe..29a0973 100644 (file)
@@ -5197,6 +5197,7 @@ static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd)
                used++;
        icon = &cmd[used];
 
+       wpa_s->fetch_osu_icon_in_progress = 0;
        return hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_ICON_REQUEST),
                                  (u8 *) icon, os_strlen(icon));
 }
@@ -5495,6 +5496,10 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
        radio_remove_works(wpa_s, NULL, 1);
 
        wpa_s->next_ssid = NULL;
+
+#ifdef CONFIG_INTERWORKING
+       hs20_cancel_fetch_osu(wpa_s);
+#endif /* CONFIG_INTERWORKING */
 }
 
 
@@ -6129,6 +6134,11 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strncmp(buf, "HS20_ICON_REQUEST ", 18) == 0) {
                if (hs20_icon_request(wpa_s, buf + 18) < 0)
                        reply_len = -1;
+       } else if (os_strcmp(buf, "FETCH_OSU") == 0) {
+               if (hs20_fetch_osu(wpa_s) < 0)
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) {
+               hs20_cancel_fetch_osu(wpa_s);
 #endif /* CONFIG_HS20 */
        } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
        {
index d5a332f..4f1ce98 100644 (file)
@@ -18,6 +18,7 @@
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "config.h"
+#include "scan.h"
 #include "bss.h"
 #include "blacklist.h"
 #include "gas_query.h"
 #include "hs20_supplicant.h"
 
 
+#define OSU_MAX_ITEMS 10
+
+struct osu_lang_string {
+       char lang[4];
+       char text[253];
+};
+
+struct osu_icon {
+       u16 width;
+       u16 height;
+       char lang[4];
+       char icon_type[256];
+       char filename[256];
+       unsigned int id;
+       unsigned int failed:1;
+};
+
+struct osu_provider {
+       u8 bssid[ETH_ALEN];
+       u8 osu_ssid[32];
+       u8 osu_ssid_len;
+       char server_uri[256];
+       u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */
+       char osu_nai[256];
+       struct osu_lang_string friendly_name[OSU_MAX_ITEMS];
+       size_t friendly_name_count;
+       struct osu_lang_string serv_desc[OSU_MAX_ITEMS];
+       size_t serv_desc_count;
+       struct osu_icon icon[OSU_MAX_ITEMS];
+       size_t icon_count;
+};
+
+
 void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id)
 {
        u8 conf;
@@ -165,6 +199,107 @@ int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
 }
 
 
+static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s,
+                                        const u8 *sa, const u8 *pos,
+                                        size_t slen)
+{
+       char fname[256];
+       int png;
+       FILE *f;
+       u16 data_len;
+
+       wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR " Icon Binary File",
+               MAC2STR(sa));
+
+       if (slen < 4) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+                       "value from " MACSTR, MAC2STR(sa));
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "HS 2.0: Download Status Code %u", *pos);
+       if (*pos != 0)
+               return -1;
+       pos++;
+       slen--;
+
+       if ((size_t) 1 + pos[0] > slen) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+                       "value from " MACSTR, MAC2STR(sa));
+               return -1;
+       }
+       wpa_hexdump_ascii(MSG_DEBUG, "Icon Type", pos + 1, pos[0]);
+       png = os_strncasecmp((char *) pos + 1, "image/png", 9) == 0;
+       slen -= 1 + pos[0];
+       pos += 1 + pos[0];
+
+       if (slen < 2) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+                       "value from " MACSTR, MAC2STR(sa));
+               return -1;
+       }
+       data_len = WPA_GET_LE16(pos);
+       pos += 2;
+       slen -= 2;
+
+       if (data_len > slen) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+                       "value from " MACSTR, MAC2STR(sa));
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "Icon Binary Data: %u bytes", data_len);
+       if (wpa_s->conf->osu_dir == NULL)
+               return -1;
+
+       wpa_s->osu_icon_id++;
+       if (wpa_s->osu_icon_id == 0)
+               wpa_s->osu_icon_id++;
+       snprintf(fname, sizeof(fname), "%s/osu-icon-%u.%s",
+                wpa_s->conf->osu_dir, wpa_s->osu_icon_id,
+                png ? "png" : "icon");
+       f = fopen(fname, "wb");
+       if (f == NULL)
+               return -1;
+       if (fwrite(pos, slen, 1, f) != 1) {
+               fclose(f);
+               unlink(fname);
+               return -1;
+       }
+       fclose(f);
+
+       wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP-ICON %s", fname);
+       return 0;
+}
+
+
+static void hs20_continue_icon_fetch(void *eloop_ctx, void *sock_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       if (wpa_s->fetch_osu_icon_in_progress)
+               hs20_next_osu_icon(wpa_s);
+}
+
+
+static void hs20_osu_icon_fetch_result(struct wpa_supplicant *wpa_s, int res)
+{
+       size_t i, j;
+       for (i = 0; i < wpa_s->osu_prov_count; i++) {
+               struct osu_provider *osu = &wpa_s->osu_prov[i];
+               for (j = 0; j < osu->icon_count; j++) {
+                       struct osu_icon *icon = &osu->icon[j];
+                       if (icon->id || icon->failed)
+                               continue;
+                       if (res < 0)
+                               icon->failed = 1;
+                       else
+                               icon->id = wpa_s->osu_icon_id;
+                       return;
+               }
+       }
+}
+
+
 void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
                                  const u8 *sa, const u8 *data, size_t slen)
 {
@@ -172,7 +307,7 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
        u8 subtype;
        struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
        struct wpa_bss_anqp *anqp = NULL;
-       u16 data_len;
+       int ret;
 
        if (slen < 2)
                return;
@@ -248,49 +383,457 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
                }
                break;
        case HS20_STYPE_ICON_BINARY_FILE:
-               wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
-                       " Icon Binary File", MAC2STR(sa));
+               ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen);
+               if (wpa_s->fetch_osu_icon_in_progress) {
+                       hs20_osu_icon_fetch_result(wpa_s, ret);
+                       eloop_cancel_timeout(hs20_continue_icon_fetch,
+                                            wpa_s, NULL);
+                       eloop_register_timeout(0, 0, hs20_continue_icon_fetch,
+                                              wpa_s, NULL);
+               }
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype);
+               break;
+       }
+}
+
+
+void hs20_notify_parse_done(struct wpa_supplicant *wpa_s)
+{
+       if (!wpa_s->fetch_osu_icon_in_progress)
+               return;
+       if (eloop_is_timeout_registered(hs20_continue_icon_fetch, wpa_s, NULL))
+               return;
+       /*
+        * We are going through icon fetch, but no icon response was received.
+        * Assume this means the current AP could not provide an answer to avoid
+        * getting stuck in fetch iteration.
+        */
+       hs20_icon_fetch_failed(wpa_s);
+}
+
 
-               if (slen < 4) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon "
-                               "Binary File value from " MACSTR, MAC2STR(sa));
+static void hs20_free_osu_prov_entry(struct osu_provider *prov)
+{
+}
+
+
+void hs20_free_osu_prov(struct wpa_supplicant *wpa_s)
+{
+       size_t i;
+       for (i = 0; i < wpa_s->osu_prov_count; i++)
+               hs20_free_osu_prov_entry(&wpa_s->osu_prov[i]);
+       os_free(wpa_s->osu_prov);
+       wpa_s->osu_prov = NULL;
+       wpa_s->osu_prov_count = 0;
+}
+
+
+static void hs20_osu_fetch_done(struct wpa_supplicant *wpa_s)
+{
+       char fname[256];
+       FILE *f;
+       size_t i, j;
+
+       wpa_s->fetch_osu_info = 0;
+       wpa_s->fetch_osu_icon_in_progress = 0;
+
+       if (wpa_s->conf->osu_dir == NULL) {
+               hs20_free_osu_prov(wpa_s);
+               wpa_s->fetch_anqp_in_progress = 0;
+               return;
+       }
+
+       snprintf(fname, sizeof(fname), "%s/osu-providers.txt",
+                wpa_s->conf->osu_dir);
+       f = fopen(fname, "w");
+       if (f == NULL) {
+               hs20_free_osu_prov(wpa_s);
+               return;
+       }
+       for (i = 0; i < wpa_s->osu_prov_count; i++) {
+               struct osu_provider *osu = &wpa_s->osu_prov[i];
+               if (i > 0)
+                       fprintf(f, "\n");
+               fprintf(f, "OSU-PROVIDER " MACSTR "\n"
+                       "uri=%s\n"
+                       "methods=%08x\n",
+                       MAC2STR(osu->bssid), osu->server_uri, osu->osu_methods);
+               if (osu->osu_ssid_len) {
+                       fprintf(f, "osu_ssid=%s\n",
+                               wpa_ssid_txt(osu->osu_ssid,
+                                            osu->osu_ssid_len));
+               }
+               if (osu->osu_nai[0])
+                       fprintf(f, "osu_nai=%s\n", osu->osu_nai);
+               for (j = 0; j < osu->friendly_name_count; j++) {
+                       fprintf(f, "friendly_name=%s:%s\n",
+                               osu->friendly_name[j].lang,
+                               osu->friendly_name[j].text);
+               }
+               for (j = 0; j < osu->serv_desc_count; j++) {
+                       fprintf(f, "desc=%s:%s\n",
+                               osu->serv_desc[j].lang,
+                               osu->serv_desc[j].text);
+               }
+               for (j = 0; j < osu->icon_count; j++) {
+                       struct osu_icon *icon = &osu->icon[j];
+                       if (icon->failed)
+                               continue; /* could not fetch icon */
+                       fprintf(f, "icon=%u:%u:%u:%s:%s:%s\n",
+                               icon->id, icon->width, icon->height, icon->lang,
+                               icon->icon_type, icon->filename);
+               }
+       }
+       fclose(f);
+       hs20_free_osu_prov(wpa_s);
+
+       wpa_msg(wpa_s, MSG_INFO, "OSU provider fetch completed");
+       wpa_s->fetch_anqp_in_progress = 0;
+}
+
+
+void hs20_next_osu_icon(struct wpa_supplicant *wpa_s)
+{
+       size_t i, j;
+
+       wpa_printf(MSG_DEBUG, "HS 2.0: Ready to fetch next icon");
+
+       for (i = 0; i < wpa_s->osu_prov_count; i++) {
+               struct osu_provider *osu = &wpa_s->osu_prov[i];
+               for (j = 0; j < osu->icon_count; j++) {
+                       struct osu_icon *icon = &osu->icon[j];
+                       if (icon->id || icon->failed)
+                               continue;
+
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Try to fetch icon '%s' "
+                                  "from " MACSTR, icon->filename,
+                                  MAC2STR(osu->bssid));
+                       if (hs20_anqp_send_req(wpa_s, osu->bssid,
+                                              BIT(HS20_STYPE_ICON_REQUEST),
+                                              (u8 *) icon->filename,
+                                              os_strlen(icon->filename)) < 0) {
+                               icon->failed = 1;
+                               continue;
+                       }
+                       return;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "HS 2.0: No more icons to fetch");
+       hs20_osu_fetch_done(wpa_s);
+}
+
+
+static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+                             const u8 *osu_ssid, u8 osu_ssid_len,
+                             const u8 *pos, size_t len)
+{
+       struct osu_provider *prov;
+       const u8 *end = pos + len;
+       u16 len2;
+       const u8 *pos2;
+
+       wpa_hexdump(MSG_DEBUG, "HS 2.0: Parsing OSU Provider", pos, len);
+       prov = os_realloc_array(wpa_s->osu_prov,
+                               wpa_s->osu_prov_count + 1,
+                               sizeof(*prov));
+       if (prov == NULL)
+               return;
+       wpa_s->osu_prov = prov;
+       prov = &prov[wpa_s->osu_prov_count];
+       os_memset(prov, 0, sizeof(*prov));
+
+       os_memcpy(prov->bssid, bss->bssid, ETH_ALEN);
+       os_memcpy(prov->osu_ssid, osu_ssid, osu_ssid_len);
+       prov->osu_ssid_len = osu_ssid_len;
+
+       /* OSU Friendly Name Length */
+       if (pos + 2 > end) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+                          "Friendly Name Length");
+               return;
+       }
+       len2 = WPA_GET_LE16(pos);
+       pos += 2;
+       if (pos + len2 > end) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+                          "Friendly Name Duples");
+               return;
+       }
+       pos2 = pos;
+       pos += len2;
+
+       /* OSU Friendly Name Duples */
+       while (pos2 + 4 <= pos && prov->friendly_name_count < OSU_MAX_ITEMS) {
+               struct osu_lang_string *f;
+               if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) {
+                       wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name");
                        break;
                }
+               f = &prov->friendly_name[prov->friendly_name_count++];
+               os_memcpy(f->lang, pos2 + 1, 3);
+               os_memcpy(f->text, pos2 + 1 + 3, pos2[0] - 3);
+               pos2 += 1 + pos2[0];
+       }
 
-               wpa_printf(MSG_DEBUG, "HS 2.0: Download Status Code %u", *pos);
-               pos++;
-               slen--;
+       /* OSU Server URI */
+       if (pos + 1 > end || pos + 1 + pos[0] > end) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server "
+                          "URI");
+               return;
+       }
+       os_memcpy(prov->server_uri, pos + 1, pos[0]);
+       pos += 1 + pos[0];
 
-               if ((size_t) 1 + pos[0] > slen) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon "
-                               "Binary File value from " MACSTR, MAC2STR(sa));
+       /* OSU Method list */
+       if (pos + 1 > end || pos + 1 + pos[0] > end) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
+                          "list");
+               return;
+       }
+       pos2 = pos + 1;
+       pos += 1 + pos[0];
+       while (pos2 < pos) {
+               if (*pos2 < 32)
+                       prov->osu_methods |= BIT(*pos2);
+               pos2++;
+       }
+
+       /* Icons Available Length */
+       if (pos + 2 > end) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
+                          "Available Length");
+               return;
+       }
+       len2 = WPA_GET_LE16(pos);
+       pos += 2;
+       if (pos + len2 > end) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
+                          "Available");
+               return;
+       }
+       pos2 = pos;
+       pos += len2;
+
+       /* Icons Available */
+       while (pos2 < pos) {
+               struct osu_icon *icon = &prov->icon[prov->icon_count];
+               if (pos2 + 2 + 2 + 3 + 1 + 1 > pos) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata");
                        break;
                }
-               wpa_hexdump_ascii(MSG_DEBUG, "Icon Type", pos + 1, pos[0]);
-               slen -= 1 + pos[0];
-               pos += 1 + pos[0];
 
-               if (slen < 2) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon "
-                               "Binary File value from " MACSTR, MAC2STR(sa));
+               icon->width = WPA_GET_LE16(pos2);
+               pos2 += 2;
+               icon->height = WPA_GET_LE16(pos2);
+               pos2 += 2;
+               os_memcpy(icon->lang, pos2, 3);
+               pos2 += 3;
+
+               if (pos2 + 1 + pos2[0] > pos) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type");
                        break;
                }
-               data_len = WPA_GET_BE16(pos);
-               pos += 2;
-               slen -= 2;
+               os_memcpy(icon->icon_type, pos2 + 1, pos2[0]);
+               pos2 += 1 + pos2[0];
 
-               if (data_len > slen) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon "
-                               "Binary File value from " MACSTR, MAC2STR(sa));
+               if (pos2 + 1 + pos2[0] > pos) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
+                                  "Filename");
                        break;
                }
+               os_memcpy(icon->filename, pos2 + 1, pos2[0]);
+               pos2 += 1 + pos2[0];
 
-               wpa_printf(MSG_DEBUG, "Icon Binary Data: %u bytes", data_len);
-               break;
-       default:
-               wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype);
-               break;
+               prov->icon_count++;
        }
+
+       /* OSU_NAI */
+       if (pos + 1 > end || pos + 1 + pos[0] > end) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
+               return;
+       }
+       os_memcpy(prov->osu_nai, pos + 1, pos[0]);
+       pos += 1 + pos[0];
+
+       /* OSU Service Description Length */
+       if (pos + 2 > end) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+                          "Service Description Length");
+               return;
+       }
+       len2 = WPA_GET_LE16(pos);
+       pos += 2;
+       if (pos + len2 > end) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+                          "Service Description Duples");
+               return;
+       }
+       pos2 = pos;
+       pos += len2;
+
+       /* OSU Service Description Duples */
+       while (pos2 + 4 <= pos && prov->serv_desc_count < OSU_MAX_ITEMS) {
+               struct osu_lang_string *f;
+               if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) {
+                       wpa_printf(MSG_DEBUG, "Invalid OSU Service "
+                                  "Description");
+                       break;
+               }
+               f = &prov->serv_desc[prov->serv_desc_count++];
+               os_memcpy(f->lang, pos2 + 1, 3);
+               os_memcpy(f->text, pos2 + 1 + 3, pos2[0] - 3);
+               pos2 += 1 + pos2[0];
+       }
+
+       wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR,
+                  MAC2STR(bss->bssid));
+       wpa_s->osu_prov_count++;
+}
+
+
+void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_bss *bss;
+       struct wpabuf *prov_anqp;
+       const u8 *pos, *end;
+       u16 len;
+       const u8 *osu_ssid;
+       u8 osu_ssid_len;
+       u8 num_providers;
+
+       hs20_free_osu_prov(wpa_s);
+
+       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+               if (bss->anqp == NULL)
+                       continue;
+               prov_anqp = bss->anqp->hs20_osu_providers_list;
+               if (prov_anqp == NULL)
+                       continue;
+               wpa_printf(MSG_DEBUG, "HS 2.0: Parsing OSU Providers list from "
+                          MACSTR, MAC2STR(bss->bssid));
+               wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers list",
+                               prov_anqp);
+               pos = wpabuf_head(prov_anqp);
+               end = pos + wpabuf_len(prov_anqp);
+
+               /* OSU SSID */
+               if (pos + 1 > end)
+                       continue;
+               if (pos + 1 + pos[0] > end) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
+                                  "OSU SSID");
+                       continue;
+               }
+               osu_ssid_len = *pos++;
+               if (osu_ssid_len > 32) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Invalid OSU SSID "
+                                  "Length %u", osu_ssid_len);
+                       continue;
+               }
+               osu_ssid = pos;
+               pos += osu_ssid_len;
+
+               if (pos + 1 > end) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
+                                  "Number of OSU Providers");
+                       continue;
+               }
+               num_providers = *pos++;
+               wpa_printf(MSG_DEBUG, "HS 2.0: Number of OSU Providers: %u",
+                          num_providers);
+
+               /* OSU Providers */
+               while (pos + 2 < end && num_providers > 0) {
+                       num_providers--;
+                       len = WPA_GET_LE16(pos);
+                       pos += 2;
+                       if (pos + len > end)
+                               break;
+                       hs20_osu_add_prov(wpa_s, bss, osu_ssid,
+                                         osu_ssid_len, pos, len);
+                       pos += len;
+               }
+
+               if (pos != end) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Ignored %d bytes of "
+                                  "extra data after OSU Providers",
+                                  (int) (end - pos));
+               }
+       }
+
+       wpa_s->fetch_osu_icon_in_progress = 1;
+       hs20_next_osu_icon(wpa_s);
+}
+
+
+static void hs20_osu_scan_res_handler(struct wpa_supplicant *wpa_s,
+                                     struct wpa_scan_results *scan_res)
+{
+       wpa_printf(MSG_DEBUG, "OSU provisioning fetch scan completed");
+       wpa_s->network_select = 0;
+       wpa_s->fetch_all_anqp = 1;
+       wpa_s->fetch_osu_info = 1;
+       wpa_s->fetch_osu_icon_in_progress = 0;
+
+       interworking_start_fetch_anqp(wpa_s);
+}
+
+
+int hs20_fetch_osu(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+                          "interface disabled");
+               return -1;
+       }
+
+       if (wpa_s->scanning) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+                          "scanning");
+               return -1;
+       }
+
+       if (wpa_s->conf->osu_dir == NULL) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+                          "osu_dir not configured");
+               return -1;
+       }
+
+       if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+                          "fetch in progress (%d, %d)",
+                          wpa_s->fetch_anqp_in_progress,
+                          wpa_s->network_select);
+               return -1;
+       }
+
+       wpa_msg(wpa_s, MSG_INFO, "Starting OSU provisioning information fetch");
+       wpa_s->scan_req = MANUAL_SCAN_REQ;
+       wpa_s->scan_res_handler = hs20_osu_scan_res_handler;
+       wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+       return 0;
+}
+
+
+void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s)
+{
+       wpa_printf(MSG_DEBUG, "Cancel OSU fetch");
+       interworking_stop_fetch_anqp(wpa_s);
+       wpa_s->network_select = 0;
+       wpa_s->fetch_osu_info = 0;
+       wpa_s->fetch_osu_icon_in_progress = 0;
+}
+
+
+void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s)
+{
+       hs20_osu_icon_fetch_result(wpa_s, -1);
+       eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
+       eloop_register_timeout(0, 0, hs20_continue_icon_fetch, wpa_s, NULL);
 }
 
 
index eb24b74..9782528 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -19,10 +19,18 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
 int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
                    struct wpa_bss *bss);
 int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+void hs20_notify_parse_done(struct wpa_supplicant *wpa_s);
 
 void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
                                      const char *url, u8 osu_method);
 void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
                                    u16 reauth_delay, const char *url);
 
+void hs20_free_osu_prov(struct wpa_supplicant *wpa_s);
+void hs20_next_osu_icon(struct wpa_supplicant *wpa_s);
+void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s);
+int hs20_fetch_osu(struct wpa_supplicant *wpa_s);
+void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s);
+void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s);
+
 #endif /* HS20_SUPPLICANT_H */
index 937c4f7..0107ff1 100644 (file)
@@ -102,6 +102,9 @@ static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
 {
        struct wpa_supplicant *wpa_s = ctx;
 
+       wpa_printf(MSG_DEBUG, "ANQP: Response callback dst=" MACSTR
+                  " dialog_token=%u result=%d status_code=%u",
+                  MAC2STR(dst), dialog_token, result, status_code);
        anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
                     status_code);
        interworking_next_anqp_fetch(wpa_s);
@@ -1970,8 +1973,21 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
        int found = 0;
        const u8 *ie;
 
-       if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress)
+       wpa_printf(MSG_DEBUG, "Interworking: next_anqp_fetch - "
+                  "fetch_anqp_in_progress=%d fetch_osu_icon_in_progress=%d",
+                  wpa_s->fetch_anqp_in_progress,
+                  wpa_s->fetch_osu_icon_in_progress);
+
+       if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress) {
+               wpa_printf(MSG_DEBUG, "Interworking: Stop next-ANQP-fetch");
                return;
+       }
+
+       if (wpa_s->fetch_osu_icon_in_progress) {
+               wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)");
+               hs20_next_osu_icon(wpa_s);
+               return;
+       }
 
        dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
                if (!(bss->caps & IEEE80211_CAP_ESS))
@@ -2005,6 +2021,11 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
        }
 
        if (found == 0) {
+               if (wpa_s->fetch_osu_info) {
+                       wpa_printf(MSG_DEBUG, "Interworking: Next icon");
+                       hs20_osu_icon_fetch(wpa_s);
+                       return;
+               }
                wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
                wpa_s->fetch_anqp_in_progress = 0;
                if (wpa_s->network_select)
@@ -2032,6 +2053,7 @@ int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
 
        wpa_s->network_select = 0;
        wpa_s->fetch_all_anqp = 1;
+       wpa_s->fetch_osu_info = 0;
 
        interworking_start_fetch_anqp(wpa_s);
 
@@ -2229,14 +2251,22 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
        u16 slen;
        struct wpa_bss *bss = NULL, *tmp;
 
-       if (result != GAS_QUERY_SUCCESS)
+       wpa_printf(MSG_DEBUG, "Interworking: anqp_resp_cb dst=" MACSTR
+                  " dialog_token=%u result=%d status_code=%u",
+                  MAC2STR(dst), dialog_token, result, status_code);
+       if (result != GAS_QUERY_SUCCESS) {
+               if (wpa_s->fetch_osu_icon_in_progress)
+                       hs20_icon_fetch_failed(wpa_s);
                return;
+       }
 
        pos = wpabuf_head(adv_proto);
        if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
            pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
                wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
                           "Protocol in response");
+               if (wpa_s->fetch_osu_icon_in_progress)
+                       hs20_icon_fetch_failed(wpa_s);
                return;
        }
 
@@ -2276,6 +2306,8 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
                                                slen);
                pos += slen;
        }
+
+       hs20_notify_parse_done(wpa_s);
 }
 
 
@@ -2296,6 +2328,7 @@ int interworking_select(struct wpa_supplicant *wpa_s, int auto_select,
        wpa_s->auto_network_select = 0;
        wpa_s->auto_select = !!auto_select;
        wpa_s->fetch_all_anqp = 0;
+       wpa_s->fetch_osu_info = 0;
        wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
                   "selection");
        wpa_s->scan_res_handler = interworking_scan_res_handler;
index fd7f92f..0691183 100644 (file)
@@ -2316,6 +2316,19 @@ static int wpa_cli_cmd_hs20_icon_request(struct wpa_ctrl *ctrl, int argc,
        return wpa_ctrl_command(ctrl, cmd);
 }
 
+
+static int wpa_cli_cmd_fetch_osu(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "FETCH_OSU");
+}
+
+
+static int wpa_cli_cmd_cancel_fetch_osu(struct wpa_ctrl *ctrl, int argc,
+                                       char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "CANCEL_FETCH_OSU");
+}
+
 #endif /* CONFIG_HS20 */
 
 
@@ -2852,6 +2865,11 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "hs20_icon_request", wpa_cli_cmd_hs20_icon_request,
          wpa_cli_complete_bss, cli_cmd_flag_none,
          "<addr> <icon name> = get Hotspot 2.0 OSU icon" },
+       { "fetch_osu", wpa_cli_cmd_fetch_osu, NULL, cli_cmd_flag_none,
+         "= fetch OSU provider information from all APs" },
+       { "cancel_fetch_osu", wpa_cli_cmd_cancel_fetch_osu, NULL,
+         cli_cmd_flag_none,
+         "= cancel fetch_osu command" },
 #endif /* CONFIG_HS20 */
        { "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, NULL,
          cli_cmd_flag_none,
index 71b6b63..95ab4af 100644 (file)
@@ -483,6 +483,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 
        os_free(wpa_s->last_scan_res);
        wpa_s->last_scan_res = NULL;
+
+#ifdef CONFIG_HS20
+       hs20_free_osu_prov(wpa_s);
+#endif /* CONFIG_HS20 */
 }
 
 
index 765f5b6..92e937e 100644 (file)
@@ -771,7 +771,12 @@ struct wpa_supplicant {
        unsigned int auto_select:1;
        unsigned int auto_network_select:1;
        unsigned int fetch_all_anqp:1;
+       unsigned int fetch_osu_info:1;
+       unsigned int fetch_osu_icon_in_progress:1;
        struct wpa_bss *interworking_gas_bss;
+       unsigned int osu_icon_id;
+       struct osu_provider *osu_prov;
+       size_t osu_prov_count;
 #endif /* CONFIG_INTERWORKING */
        unsigned int drv_capa_known;