diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index def3e838c..343d7c103 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -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 diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index b64f414bb..9e06e70a1 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -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; }; diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index dc038fecb..29a097315 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -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) { diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c index d5a332f60..4f1ce9858 100644 --- a/wpa_supplicant/hs20_supplicant.c +++ b/wpa_supplicant/hs20_supplicant.c @@ -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" @@ -25,6 +26,39 @@ #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,44 +383,14 @@ 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)); - - if (slen < 4) { - wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon " - "Binary File value from " MACSTR, MAC2STR(sa)); - break; + 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); } - - wpa_printf(MSG_DEBUG, "HS 2.0: Download Status Code %u", *pos); - 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)); - 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)); - break; - } - data_len = WPA_GET_BE16(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)); - break; - } - - wpa_printf(MSG_DEBUG, "Icon Binary Data: %u bytes", data_len); break; default: wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype); @@ -294,6 +399,444 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, } +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); +} + + +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]; + } + + /* 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]; + + /* 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; + } + + 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; + } + os_memcpy(icon->icon_type, pos2 + 1, pos2[0]); + pos2 += 1 + pos2[0]; + + 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]; + + 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); +} + + void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s, const char *url, u8 osu_method) { diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h index eb24b7427..978252892 100644 --- a/wpa_supplicant/hs20_supplicant.h +++ b/wpa_supplicant/hs20_supplicant.h @@ -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 */ diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index 937c4f7b8..0107ff153 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -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; diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index fd7f92fa3..069118304 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -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, " = 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, diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 71b6b63fd..95ab4af49 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -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 */ } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 765f5b6c5..92e937e1c 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -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;