return 0;
}
+
+static int hs20_parse_osu_ssid(struct hostapd_bss_config *bss,
+ char *pos, int line)
+{
+ size_t slen;
+ char *str;
+
+ str = wpa_config_parse_string(pos, &slen);
+ if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos);
+ return -1;
+ }
+
+ os_memcpy(bss->osu_ssid, str, slen);
+ bss->osu_ssid_len = slen;
+ os_free(str);
+
+ return 0;
+}
+
+
+static int hs20_parse_osu_server_uri(struct hostapd_bss_config *bss,
+ char *pos, int line)
+{
+ struct hs20_osu_provider *p;
+
+ p = os_realloc_array(bss->hs20_osu_providers,
+ bss->hs20_osu_providers_count + 1, sizeof(*p));
+ if (p == NULL)
+ return -1;
+
+ bss->hs20_osu_providers = p;
+ bss->last_osu = &bss->hs20_osu_providers[bss->hs20_osu_providers_count];
+ bss->hs20_osu_providers_count++;
+ os_memset(bss->last_osu, 0, sizeof(*p));
+ bss->last_osu->server_uri = os_strdup(pos);
+
+ return 0;
+}
+
+
+static int hs20_parse_osu_friendly_name(struct hostapd_bss_config *bss,
+ char *pos, int line)
+{
+ if (bss->last_osu == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+ return -1;
+ }
+
+ if (parse_lang_string(&bss->last_osu->friendly_name,
+ &bss->last_osu->friendly_name_count, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid osu_friendly_name '%s'",
+ line, pos);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int hs20_parse_osu_nai(struct hostapd_bss_config *bss,
+ char *pos, int line)
+{
+ if (bss->last_osu == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+ return -1;
+ }
+
+ os_free(bss->last_osu->osu_nai);
+ bss->last_osu->osu_nai = os_strdup(pos);
+ if (bss->last_osu->osu_nai == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos,
+ int line)
+{
+ if (bss->last_osu == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+ return -1;
+ }
+
+ if (hostapd_parse_intlist(&bss->last_osu->method_list, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid osu_method_list", line);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int hs20_parse_osu_icon(struct hostapd_bss_config *bss, char *pos,
+ int line)
+{
+ char **n;
+ struct hs20_osu_provider *p = bss->last_osu;
+
+ if (p == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+ return -1;
+ }
+
+ n = os_realloc_array(p->icons, p->icons_count + 1, sizeof(char *));
+ if (n == NULL)
+ return -1;
+ p->icons = n;
+ p->icons[p->icons_count] = os_strdup(pos);
+ if (p->icons[p->icons_count] == NULL)
+ return -1;
+ p->icons_count++;
+
+ return 0;
+}
+
+
+static int hs20_parse_osu_service_desc(struct hostapd_bss_config *bss,
+ char *pos, int line)
+{
+ if (bss->last_osu == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+ return -1;
+ }
+
+ if (parse_lang_string(&bss->last_osu->service_desc,
+ &bss->last_osu->service_desc_count, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid osu_service_desc '%s'",
+ line, pos);
+ return -1;
+ }
+
+ return 0;
+}
+
#endif /* CONFIG_HS20 */
errors++;
return errors;
}
+ } else if (os_strcmp(buf, "osu_ssid") == 0) {
+ if (hs20_parse_osu_ssid(bss, pos, line) < 0)
+ errors++;
+ } else if (os_strcmp(buf, "osu_server_uri") == 0) {
+ if (hs20_parse_osu_server_uri(bss, pos, line) < 0)
+ errors++;
+ } else if (os_strcmp(buf, "osu_friendly_name") == 0) {
+ if (hs20_parse_osu_friendly_name(bss, pos, line) < 0)
+ errors++;
+ } else if (os_strcmp(buf, "osu_nai") == 0) {
+ if (hs20_parse_osu_nai(bss, pos, line) < 0)
+ errors++;
+ } else if (os_strcmp(buf, "osu_method_list") == 0) {
+ if (hs20_parse_osu_method_list(bss, pos, line) < 0)
+ errors++;
+ } else if (os_strcmp(buf, "osu_icon") == 0) {
+ if (hs20_parse_osu_icon(bss, pos, line) < 0)
+ errors++;
+ } else if (os_strcmp(buf, "osu_service_desc") == 0) {
+ if (hs20_parse_osu_service_desc(bss, pos, line) < 0)
+ errors++;
#endif /* CONFIG_HS20 */
#ifdef CONFIG_TESTING_OPTIONS
#define PARSE_TEST_PROBABILITY(_val) \
#hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png
#hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png
+# OSU SSID (see ssid2 for format description)
+# This is the SSID used for all OSU connections to all the listed OSU Providers.
+#osu_ssid="example"
+
+# OSU Providers
+# One or more sets of following parameter. Each OSU provider is started by the
+# mandatory osu_server_uri item. The other parameters add information for the
+# last added OSU provider.
+#
+#osu_server_uri=https://example.com/osu/
+#osu_friendly_name=eng:Example operator
+#osu_friendly_name=fin:Esimerkkipalveluntarjoaja
+#osu_nai=anonymous@example.com
+#osu_method_list=1 0
+#osu_icon=icon32
+#osu_icon=icon64
+#osu_service_desc=eng:Example services
+#osu_service_desc=fin:Esimerkkipalveluja
+#
+#osu_server_uri=...
+
##### TESTING OPTIONS #########################################################
#
# The options in this section are only available when the build configuration
os_free(conf->hs20_connection_capability);
os_free(conf->hs20_operating_class);
os_free(conf->hs20_icons);
+ if (conf->hs20_osu_providers) {
+ size_t i;
+ for (i = 0; i < conf->hs20_osu_providers_count; i++) {
+ struct hs20_osu_provider *p;
+ size_t j;
+ p = &conf->hs20_osu_providers[i];
+ os_free(p->friendly_name);
+ os_free(p->server_uri);
+ os_free(p->method_list);
+ for (j = 0; j < p->icons_count; j++)
+ os_free(p->icons[j]);
+ os_free(p->icons);
+ os_free(p->osu_nai);
+ os_free(p->service_desc);
+ }
+ os_free(conf->hs20_osu_providers);
+ }
#endif /* CONFIG_HS20 */
wpabuf_free(conf->vendor_elements);
char file[256];
} *hs20_icons;
size_t hs20_icons_count;
+ u8 osu_ssid[HOSTAPD_MAX_SSID_LEN];
+ size_t osu_ssid_len;
+ struct hs20_osu_provider {
+ unsigned int friendly_name_count;
+ struct hostapd_lang_string *friendly_name;
+ char *server_uri;
+ int *method_list;
+ char **icons;
+ size_t icons_count;
+ char *osu_nai;
+ unsigned int service_desc_count;
+ struct hostapd_lang_string *service_desc;
+ } *hs20_osu_providers, *last_osu;
+ size_t hs20_osu_providers_count;
unsigned int hs20_deauth_req_timeout;
#endif /* CONFIG_HS20 */
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_osu_providers_count)
+ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
if (hapd->conf->hs20_icons_count)
wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
gas_anqp_set_element_len(buf, len);
}
+static void anqp_add_osu_provider(struct wpabuf *buf,
+ struct hostapd_bss_config *bss,
+ struct hs20_osu_provider *p)
+{
+ u8 *len, *len2, *count;
+ unsigned int i;
+
+ len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
+
+ /* OSU Friendly Name Duples */
+ len2 = wpabuf_put(buf, 2);
+ for (i = 0; i < p->friendly_name_count; i++) {
+ struct hostapd_lang_string *s = &p->friendly_name[i];
+ wpabuf_put_u8(buf, 3 + s->name_len);
+ wpabuf_put_data(buf, s->lang, 3);
+ wpabuf_put_data(buf, s->name, s->name_len);
+ }
+ WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+ /* OSU Server URI */
+ if (p->server_uri) {
+ wpabuf_put_u8(buf, os_strlen(p->server_uri));
+ wpabuf_put_str(buf, p->server_uri);
+ } else
+ wpabuf_put_u8(buf, 0);
+
+ /* OSU Method List */
+ count = wpabuf_put(buf, 1);
+ for (i = 0; p->method_list[i] >= 0; i++)
+ wpabuf_put_u8(buf, p->method_list[i]);
+ *count = i;
+
+ /* Icons Available */
+ len2 = wpabuf_put(buf, 2);
+ for (i = 0; i < p->icons_count; i++) {
+ size_t j;
+ struct hs20_icon *icon = NULL;
+
+ for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
+ if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
+ 0)
+ icon = &bss->hs20_icons[j];
+ }
+ if (!icon)
+ continue; /* icon info not found */
+
+ wpabuf_put_le16(buf, icon->width);
+ wpabuf_put_le16(buf, icon->height);
+ wpabuf_put_data(buf, icon->language, 3);
+ wpabuf_put_u8(buf, os_strlen(icon->type));
+ wpabuf_put_str(buf, icon->type);
+ wpabuf_put_u8(buf, os_strlen(icon->name));
+ wpabuf_put_str(buf, icon->name);
+ }
+ WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+ /* OSU_NAI */
+ if (p->osu_nai) {
+ wpabuf_put_u8(buf, os_strlen(p->osu_nai));
+ wpabuf_put_str(buf, p->osu_nai);
+ } else
+ wpabuf_put_u8(buf, 0);
+
+ /* OSU Service Description Duples */
+ len2 = wpabuf_put(buf, 2);
+ for (i = 0; i < p->service_desc_count; i++) {
+ struct hostapd_lang_string *s = &p->service_desc[i];
+ wpabuf_put_u8(buf, 3 + s->name_len);
+ wpabuf_put_data(buf, s->lang, 3);
+ wpabuf_put_data(buf, s->name, s->name_len);
+ }
+ WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+ WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+}
+
+
+static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->hs20_osu_providers_count) {
+ size_t i;
+ u8 *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_OSU_PROVIDERS_LIST);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+
+ /* OSU SSID */
+ wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
+ wpabuf_put_data(buf, hapd->conf->osu_ssid,
+ hapd->conf->osu_ssid_len);
+
+ /* Number of OSU Providers */
+ wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
+
+ for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
+ anqp_add_osu_provider(
+ buf, hapd->conf,
+ &hapd->conf->hs20_osu_providers[i]);
+ }
+
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
struct wpabuf *buf,
const u8 *name, size_t name_len)
anqp_add_connection_capability(hapd, buf);
if (request & ANQP_REQ_OPERATING_CLASS)
anqp_add_operating_class(hapd, buf);
+ if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
+ anqp_add_osu_providers_list(hapd, buf);
if (request & ANQP_REQ_ICON_REQUEST)
anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
#endif /* CONFIG_HS20 */
hapd->conf->hs20_operating_class != NULL,
0, 0, qi);
break;
+ case HS20_STYPE_OSU_PROVIDERS_LIST:
+ set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
+ hapd->conf->hs20_osu_providers_count, 0, 0, qi);
+ break;
default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
subtype);
/*
* Generic advertisement service (GAS) server
- * 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.
(0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY)
#define ANQP_REQ_OPERATING_CLASS \
(0x10000 << HS20_STYPE_OPERATING_CLASS)
+#define ANQP_REQ_OSU_PROVIDERS_LIST \
+ (0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
#define ANQP_REQ_ICON_REQUEST \
(0x10000 << HS20_STYPE_ICON_REQUEST)