}
return 0;
}
+
+
+static int hs20_parse_icon(struct hostapd_bss_config *bss, char *pos)
+{
+ struct hs20_icon *icon;
+ char *end;
+
+ icon = os_realloc_array(bss->hs20_icons, bss->hs20_icons_count + 1,
+ sizeof(struct hs20_icon));
+ if (icon == NULL)
+ return -1;
+ bss->hs20_icons = icon;
+ icon = &bss->hs20_icons[bss->hs20_icons_count];
+ os_memset(icon, 0, sizeof(*icon));
+
+ icon->width = atoi(pos);
+ pos = os_strchr(pos, ':');
+ if (pos == NULL)
+ return -1;
+ pos++;
+
+ icon->height = atoi(pos);
+ pos = os_strchr(pos, ':');
+ if (pos == NULL)
+ return -1;
+ pos++;
+
+ end = os_strchr(pos, ':');
+ if (end == NULL || end - pos > 3)
+ return -1;
+ os_memcpy(icon->language, pos, end - pos);
+ pos = end + 1;
+
+ end = os_strchr(pos, ':');
+ if (end == NULL || end - pos > 255)
+ return -1;
+ os_memcpy(icon->type, pos, end - pos);
+ pos = end + 1;
+
+ end = os_strchr(pos, ':');
+ if (end == NULL || end - pos > 255)
+ return -1;
+ os_memcpy(icon->name, pos, end - pos);
+ pos = end + 1;
+
+ if (os_strlen(pos) > 255)
+ return -1;
+ os_memcpy(icon->file, pos, os_strlen(pos));
+
+ bss->hs20_icons_count++;
+
+ return 0;
+}
+
#endif /* CONFIG_HS20 */
os_free(bss->hs20_operating_class);
bss->hs20_operating_class = oper_class;
bss->hs20_operating_class_len = oper_class_len;
+ } else if (os_strcmp(buf, "hs20_icon") == 0) {
+ if (hs20_parse_icon(bss, pos) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid "
+ "hs20_icon '%s'", line, pos);
+ errors++;
+ return errors;
+ }
#endif /* CONFIG_HS20 */
#ifdef CONFIG_TESTING_OPTIONS
#define PARSE_TEST_PROBABILITY(_val) \
wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
if (hapd->conf->hs20_operating_class)
wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
+ if (hapd->conf->hs20_icons_count)
+ wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
gas_anqp_set_element_len(buf, len);
}
#endif /* CONFIG_HS20 */
}
}
+
+static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
+ struct wpabuf *buf,
+ const u8 *name, size_t name_len)
+{
+ struct hs20_icon *icon;
+ size_t i;
+ u8 *len;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
+ name, name_len);
+ for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
+ icon = &hapd->conf->hs20_icons[i];
+ if (name_len == os_strlen(icon->name) &&
+ os_memcmp(name, icon->name, name_len) == 0)
+ break;
+ }
+
+ if (i < hapd->conf->hs20_icons_count)
+ icon = &hapd->conf->hs20_icons[i];
+ else
+ icon = NULL;
+
+ len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+
+ if (icon) {
+ char *data;
+ size_t data_len;
+
+ data = os_readfile(icon->file, &data_len);
+ if (data == NULL || data_len > 65535) {
+ wpabuf_put_u8(buf, 2); /* Download Status:
+ * Unspecified file error */
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_le16(buf, 0);
+ } else {
+ wpabuf_put_u8(buf, 0); /* Download Status: Success */
+ wpabuf_put_u8(buf, os_strlen(icon->type));
+ wpabuf_put_str(buf, icon->type);
+ wpabuf_put_le16(buf, data_len);
+ wpabuf_put_data(buf, data, data_len);
+ }
+ os_free(data);
+ } else {
+ wpabuf_put_u8(buf, 1); /* Download Status: File not found */
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_le16(buf, 0);
+ }
+
+ gas_anqp_set_element_len(buf, len);
+}
+
#endif /* CONFIG_HS20 */
gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
unsigned int request,
struct gas_dialog_info *di,
- const u8 *home_realm, size_t home_realm_len)
+ const u8 *home_realm, size_t home_realm_len,
+ const u8 *icon_name, size_t icon_name_len)
{
struct wpabuf *buf;
+ size_t len;
+
+ len = 1400;
+ if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
+ len += 1000;
+ if (request & ANQP_REQ_ICON_REQUEST)
+ len += 65536;
- buf = wpabuf_alloc(1400);
+ buf = wpabuf_alloc(len);
if (buf == NULL)
return NULL;
anqp_add_connection_capability(hapd, buf);
if (request & ANQP_REQ_OPERATING_CLASS)
anqp_add_operating_class(hapd, buf);
+ if (request & ANQP_REQ_ICON_REQUEST)
+ anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
#endif /* CONFIG_HS20 */
return buf;
unsigned int remote_request;
const u8 *home_realm_query;
size_t home_realm_query_len;
+ const u8 *icon_name;
+ size_t icon_name_len;
u16 remote_delay;
};
}
+static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
+ const u8 *pos, const u8 *end,
+ struct anqp_query_info *qi)
+{
+ qi->request |= ANQP_REQ_ICON_REQUEST;
+ qi->icon_name = pos;
+ qi->icon_name_len = end - pos;
+ if (hapd->conf->hs20_icons_count) {
+ wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
+ "(local)");
+ } else {
+ wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
+ "available");
+ }
+}
+
+
static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
const u8 *pos, const u8 *end,
struct anqp_query_info *qi)
case HS20_STYPE_NAI_HOME_REALM_QUERY:
rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
break;
+ case HS20_STYPE_ICON_REQUEST:
+ rx_anqp_hs_icon_request(hapd, pos, end, qi);
+ break;
default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
"%u", subtype);
buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
qi->home_realm_query,
- qi->home_realm_query_len);
+ qi->home_realm_query_len,
+ qi->icon_name, qi->icon_name_len);
wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
buf);
if (!buf)
if (dialog->sd_resp == NULL) {
buf = gas_serv_build_gas_resp_payload(hapd,
dialog->all_requested,
- dialog, NULL, 0);
+ dialog, NULL, 0, NULL, 0);
wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
buf);
if (!buf)
buf = gas_serv_build_gas_resp_payload(hapd,
dialog->all_requested,
- dialog, NULL, 0);
+ dialog, NULL, 0, NULL, 0);
wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
buf);
if (!buf)