*/
static void wps_er_ap_timeout(void *eloop_data, void *user_ctx);
+static void wps_er_sta_timeout(void *eloop_data, void *user_ctx);
+struct wps_er_sta {
+ struct wps_er_sta *next;
+ struct wps_er_ap *ap;
+ u8 addr[ETH_ALEN];
+ u16 config_methods;
+ u8 uuid[WPS_UUID_LEN];
+ u8 pri_dev_type[8];
+ u16 dev_passwd_id;
+ int m1_received;
+ char *manufacturer;
+ char *model_name;
+ char *model_number;
+ char *serial_number;
+ char *dev_name;
+ struct wps_data *wps;
+ struct http_client *http;
+};
+
struct wps_er_ap {
struct wps_er_ap *next;
struct wps_er *er;
+ struct wps_er_sta *sta; /* list of STAs/Enrollees using this AP */
struct in_addr addr;
char *location;
struct http_client *http;
};
struct wps_er {
- struct wps_registrar *reg;
+ struct wps_context *wps;
char ifname[17];
char *mac_addr_text; /* mac addr of network i.f. we use */
u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
};
+static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr)
+{
+ struct wps_er_sta *sta = ap->sta;
+ while (sta) {
+ if (os_memcmp(sta->addr, addr, ETH_ALEN) == 0)
+ return sta;
+ sta = sta->next;
+ }
+ return NULL;
+}
+
+
+static void wps_er_sta_free(struct wps_er_sta *sta)
+{
+ if (sta->wps)
+ wps_deinit(sta->wps);
+ os_free(sta->manufacturer);
+ os_free(sta->model_name);
+ os_free(sta->model_number);
+ os_free(sta->serial_number);
+ os_free(sta->dev_name);
+ http_client_free(sta->http);
+ eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
+ os_free(sta);
+}
+
+
+static void wps_er_sta_remove_all(struct wps_er_ap *ap)
+{
+ struct wps_er_sta *prev, *sta;
+
+ sta = ap->sta;
+ ap->sta = NULL;
+
+ while (sta) {
+ prev = sta;
+ sta = sta->next;
+ wps_er_sta_free(prev);
+ }
+}
+
+
static void wps_er_pin_needed_cb(void *ctx, const u8 *uuid_e,
const struct wps_device_data *dev)
{
os_free(ap->control_url);
os_free(ap->event_sub_url);
+ wps_er_sta_remove_all(ap);
+
os_free(ap);
}
}
+static void http_put_date(struct wpabuf *buf)
+{
+ wpabuf_put_str(buf, "Date: ");
+ format_date(buf);
+ wpabuf_put_str(buf, "\r\n");
+}
+
+
+static void wps_er_http_resp_not_found(struct http_request *req)
+{
+ struct wpabuf *buf;
+ buf = wpabuf_alloc(200);
+ if (buf == NULL) {
+ http_request_deinit(req);
+ return;
+ }
+
+ wpabuf_put_str(buf,
+ "HTTP/1.1 404 Not Found\r\n"
+ "Server: unspecified, UPnP/1.0, unspecified\r\n"
+ "Connection: close\r\n");
+ http_put_date(buf);
+ wpabuf_put_str(buf, "\r\n");
+ http_request_send_and_deinit(req, buf);
+}
+
+
+static void wps_er_http_resp_ok(struct http_request *req)
+{
+ struct wpabuf *buf;
+ buf = wpabuf_alloc(200);
+ if (buf == NULL) {
+ http_request_deinit(req);
+ return;
+ }
+
+ wpabuf_put_str(buf,
+ "HTTP/1.1 200 OK\r\n"
+ "Server: unspecified, UPnP/1.0, unspecified\r\n"
+ "Connection: close\r\n"
+ "Content-Length: 0\r\n");
+ http_put_date(buf);
+ wpabuf_put_str(buf, "\r\n");
+ http_request_send_and_deinit(req, buf);
+}
+
+
+static void wps_er_sta_timeout(void *eloop_data, void *user_ctx)
+{
+ struct wps_er_sta *sta = eloop_data;
+ wpa_printf(MSG_DEBUG, "WPS ER: STA entry timed out");
+ wps_er_sta_free(sta);
+}
+
+
+static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap,
+ const u8 *addr,
+ struct wps_parse_attr *attr,
+ int probe_req)
+{
+ struct wps_er_sta *sta = wps_er_sta_get(ap, addr);
+
+ if (sta == NULL) {
+ sta = os_zalloc(sizeof(*sta));
+ if (sta == NULL)
+ return NULL;
+ os_memcpy(sta->addr, addr, ETH_ALEN);
+ sta->ap = ap;
+ sta->next = ap->sta;
+ ap->sta = sta;
+ }
+
+ if (!probe_req)
+ sta->m1_received = 1;
+
+ if (attr->config_methods && (!probe_req || !sta->m1_received))
+ sta->config_methods = WPA_GET_BE16(attr->config_methods);
+ if (attr->uuid_e && (!probe_req || !sta->m1_received))
+ os_memcpy(sta->uuid, attr->uuid_e, WPS_UUID_LEN);
+ if (attr->primary_dev_type && (!probe_req || !sta->m1_received))
+ os_memcpy(sta->pri_dev_type, attr->primary_dev_type, 8);
+ if (attr->dev_password_id && (!probe_req || !sta->m1_received))
+ sta->dev_passwd_id = WPA_GET_BE16(attr->dev_password_id);
+
+ if (attr->manufacturer) {
+ os_free(sta->manufacturer);
+ sta->manufacturer = os_malloc(attr->manufacturer_len + 1);
+ if (sta->manufacturer) {
+ os_memcpy(sta->manufacturer, attr->manufacturer,
+ attr->manufacturer_len);
+ sta->manufacturer[attr->manufacturer_len] = '\0';
+ }
+ }
+
+ if (attr->model_name) {
+ os_free(sta->model_name);
+ sta->model_name = os_malloc(attr->model_name_len + 1);
+ if (sta->model_name) {
+ os_memcpy(sta->model_name, attr->model_name,
+ attr->model_name_len);
+ sta->model_name[attr->model_name_len] = '\0';
+ }
+ }
+
+ if (attr->model_number) {
+ os_free(sta->model_number);
+ sta->model_number = os_malloc(attr->model_number_len + 1);
+ if (sta->model_number) {
+ os_memcpy(sta->model_number, attr->model_number,
+ attr->model_number_len);
+ sta->model_number[attr->model_number_len] = '\0';
+ }
+ }
+
+ if (attr->serial_number) {
+ os_free(sta->serial_number);
+ sta->serial_number = os_malloc(attr->serial_number_len + 1);
+ if (sta->serial_number) {
+ os_memcpy(sta->serial_number, attr->serial_number,
+ attr->serial_number_len);
+ sta->serial_number[attr->serial_number_len] = '\0';
+ }
+ }
+
+ if (attr->dev_name) {
+ os_free(sta->dev_name);
+ sta->dev_name = os_malloc(attr->dev_name_len + 1);
+ if (sta->dev_name) {
+ os_memcpy(sta->dev_name, attr->dev_name,
+ attr->dev_name_len);
+ sta->dev_name[attr->dev_name_len] = '\0';
+ }
+ }
+
+ eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
+ eloop_register_timeout(300, 0, wps_er_sta_timeout, sta, NULL);
+
+ /* TODO: wpa_msg indication if new STA */
+
+ return sta;
+}
+
+
+static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap,
+ const u8 *addr,
+ struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - Probe Request - from "
+ MACSTR, MAC2STR(addr));
+ wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
+ "(TLVs from Probe Request)", msg);
+
+ if (wps_parse_msg(msg, &attr) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
+ "WLANEvent message");
+ return;
+ }
+
+ wps_er_add_sta_data(ap, addr, &attr, 1);
+}
+
+
+static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg)
+{
+ /* TODO: send msg as UPnP POST: PutWLANResponse(NewMessage,
+ * NewWLANEventType, NewWLANEventMAC) */
+
+ wpabuf_free(msg);
+}
+
+
+static void wps_er_sta_process(struct wps_er_sta *sta, struct wpabuf *msg)
+{
+ enum wps_process_res res;
+
+ res = wps_process_msg(sta->wps, WSC_MSG, msg);
+ if (res == WPS_CONTINUE) {
+ enum wsc_op_code op_code;
+ struct wpabuf *next = wps_get_msg(sta->wps, &op_code);
+ if (next)
+ wps_er_sta_send_msg(sta, next);
+ }
+}
+
+
+static void wps_er_sta_start(struct wps_er_sta *sta, struct wpabuf *msg)
+{
+ struct wps_config cfg;
+
+ if (sta->wps)
+ wps_deinit(sta->wps);
+
+ os_memset(&cfg, 0, sizeof(cfg));
+ cfg.wps = sta->ap->er->wps;
+ cfg.registrar = 1;
+ cfg.peer_addr = sta->addr;
+
+ sta->wps = wps_init(&cfg);
+ if (sta->wps == NULL)
+ return;
+
+ wps_er_sta_process(sta, msg);
+}
+
+
+static void wps_er_process_wlanevent_eap(struct wps_er_ap *ap, const u8 *addr,
+ struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+ struct wps_er_sta *sta;
+
+ wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - EAP - from " MACSTR,
+ MAC2STR(addr));
+ wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
+ "(TLVs from EAP-WSC)", msg);
+
+ if (wps_parse_msg(msg, &attr) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
+ "WLANEvent message");
+ return;
+ }
+
+ sta = wps_er_add_sta_data(ap, addr, &attr, 0);
+
+ if (attr.msg_type && *attr.msg_type == WPS_M1)
+ wps_er_sta_start(sta, msg);
+ else if (sta->wps)
+ wps_er_sta_process(sta, msg);
+}
+
+
+static void wps_er_process_wlanevent(struct wps_er_ap *ap,
+ struct wpabuf *event)
+{
+ u8 *data;
+ u8 wlan_event_type;
+ u8 wlan_event_mac[ETH_ALEN];
+ struct wpabuf msg;
+
+ wpa_hexdump(MSG_MSGDUMP, "WPS ER: Received WLANEvent",
+ wpabuf_head(event), wpabuf_len(event));
+ if (wpabuf_len(event) < 1 + 17) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Too short WLANEvent");
+ return;
+ }
+
+ data = wpabuf_mhead(event);
+ wlan_event_type = data[0];
+ if (hwaddr_aton((char *) data + 1, wlan_event_mac) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Invalid WLANEventMAC in "
+ "WLANEvent");
+ return;
+ }
+
+ wpabuf_set(&msg, data + 1 + 17, wpabuf_len(event) - (1 + 17));
+
+ switch (wlan_event_type) {
+ case 1:
+ wps_er_process_wlanevent_probe_req(ap, wlan_event_mac, &msg);
+ break;
+ case 2:
+ wps_er_process_wlanevent_eap(ap, wlan_event_mac, &msg);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS ER: Unknown WLANEventType %d",
+ wlan_event_type);
+ break;
+ }
+}
+
+
static void wps_er_http_event(struct wps_er *er, struct http_request *req,
unsigned int ap_id)
{
struct wps_er_ap *ap = wps_er_ap_get_id(er, ap_id);
+ struct wpabuf *event;
+ enum http_reply_code ret;
+
if (ap == NULL) {
wpa_printf(MSG_DEBUG, "WPS ER: HTTP event from unknown AP id "
"%u", ap_id);
+ wps_er_http_resp_not_found(req);
return;
}
wpa_printf(MSG_MSGDUMP, "WPS ER: HTTP event from AP id %u: %s",
ap_id, http_request_get_data(req));
- /* TODO */
- http_request_deinit(req);
+
+ event = xml_get_base64_item(http_request_get_data(req), "WLANEvent",
+ &ret);
+ if (event == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Could not extract WLANEvent "
+ "from the event notification");
+ /*
+ * Reply with OK anyway to avoid getting unregistered from
+ * events.
+ */
+ wps_er_http_resp_ok(req);
+ return;
+ }
+
+ wps_er_process_wlanevent(ap, event);
+
+ wpabuf_free(event);
+ wps_er_http_resp_ok(req);
}
} else {
wpa_printf(MSG_DEBUG, "WPS ER: Unknown HTTP NOTIFY for '%s'",
uri);
- http_request_deinit(req);
+ wps_er_http_resp_not_found(req);
}
}
struct wps_er *er = ctx;
struct sockaddr_in *cli = http_request_get_cli_addr(req);
enum httpread_hdr_type type = http_request_get_type(req);
+ struct wpabuf *buf;
+
wpa_printf(MSG_DEBUG, "WPS ER: HTTP request: '%s' (type %d) from "
"%s:%d",
http_request_get_uri(req), type,
default:
wpa_printf(MSG_DEBUG, "WPS ER: Unsupported HTTP request type "
"%d", type);
- http_request_deinit(req);
+ buf = wpabuf_alloc(200);
+ if (buf == NULL) {
+ http_request_deinit(req);
+ return;
+ }
+ wpabuf_put_str(buf,
+ "HTTP/1.1 501 Unimplemented\r\n"
+ "Connection: close\r\n");
+ http_put_date(buf);
+ wpabuf_put_str(buf, "\r\n");
+ http_request_send_and_deinit(req, buf);
break;
}
}
er->ssdp_sd = -1;
os_strlcpy(er->ifname, ifname, sizeof(er->ifname));
+ er->wps = wps;
os_memset(&rcfg, 0, sizeof(rcfg));
rcfg.pin_needed_cb = wps_er_pin_needed_cb;
rcfg.cb_ctx = er;
- er->reg = wps_registrar_init(wps, &rcfg);
- if (er->reg == NULL) {
- wps_er_deinit(er);
- return NULL;
- }
-
if (get_netif_info(ifname,
&er->ip_addr, &er->ip_addr_text,
er->mac_addr, &er->mac_addr_text)) {
eloop_unregister_sock(er->ssdp_sd, EVENT_TYPE_READ);
close(er->ssdp_sd);
}
- wps_registrar_deinit(er->reg);
os_free(er->ip_addr_text);
os_free(er->mac_addr_text);
os_free(er);