2 * Wi-Fi Protected Setup - External Registrar
3 * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
9 * Alternatively, this software may be distributed under the terms of BSD
12 * See README and COPYING for more details.
21 #include "http_client.h"
22 #include "http_server.h"
26 #include "wps_upnp_i.h"
30 * send notification of new AP device with wpa_msg
31 * re-send notifications with wpa_msg if ER re-started (to update wpa_gui-qt4)
32 * (also re-send SSDP M-SEARCH in this case to find new APs)
33 * parse UPnP event messages
36 static void wps_er_ap_timeout(void *eloop_data, void *user_ctx);
40 struct wps_er_ap *next;
44 struct http_client *http;
48 char *manufacturer_url;
49 char *model_description;
66 struct wps_registrar *reg;
68 char *mac_addr_text; /* mac addr of network i.f. we use */
69 u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
70 char *ip_addr_text; /* IP address of network i.f. we use */
71 unsigned ip_addr; /* IP address of network i.f. we use (host order) */
75 struct http_server *http_srv;
77 unsigned int next_ap_id;
81 static void wps_er_pin_needed_cb(void *ctx, const u8 *uuid_e,
82 const struct wps_device_data *dev)
84 wpa_printf(MSG_DEBUG, "WPS ER: PIN needed");
88 static struct wps_er_ap * wps_er_ap_get(struct wps_er *er,
92 for (ap = er->ap; ap; ap = ap->next) {
93 if (ap->addr.s_addr == addr->s_addr)
100 static struct wps_er_ap * wps_er_ap_get_id(struct wps_er *er, unsigned int id)
102 struct wps_er_ap *ap;
103 for (ap = er->ap; ap; ap = ap->next) {
111 static void wps_er_ap_free(struct wps_er *er, struct wps_er_ap *ap)
113 /* TODO: if ap->subscribed, unsubscribe from events if the AP is still
115 wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)",
116 inet_ntoa(ap->addr), ap->location);
117 eloop_cancel_timeout(wps_er_ap_timeout, er, ap);
118 os_free(ap->location);
119 http_client_free(ap->http);
121 os_free(ap->friendly_name);
122 os_free(ap->manufacturer);
123 os_free(ap->manufacturer_url);
124 os_free(ap->model_description);
125 os_free(ap->model_name);
126 os_free(ap->model_number);
127 os_free(ap->model_url);
128 os_free(ap->serial_number);
132 os_free(ap->scpd_url);
133 os_free(ap->control_url);
134 os_free(ap->event_sub_url);
140 static void wps_er_ap_timeout(void *eloop_data, void *user_ctx)
142 struct wps_er *er = eloop_data;
143 struct wps_er_ap *ap = user_ctx;
144 wpa_printf(MSG_DEBUG, "WPS ER: AP advertisement timed out");
145 wps_er_ap_free(er, ap);
149 static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c,
150 enum http_client_event event)
152 struct wps_er_ap *ap = ctx;
156 wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events");
158 case HTTP_CLIENT_FAILED:
159 case HTTP_CLIENT_INVALID_REPLY:
160 case HTTP_CLIENT_TIMEOUT:
161 wpa_printf(MSG_DEBUG, "WPS ER: Failed to subscribe to events");
164 http_client_free(ap->http);
169 static void wps_er_subscribe(struct wps_er_ap *ap)
172 struct sockaddr_in dst;
175 if (ap->event_sub_url == NULL) {
176 wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot "
181 wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot "
182 "send subscribe request");
186 url = http_client_url_parse(ap->event_sub_url, &dst, &path);
188 wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL");
192 req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000);
198 "SUBSCRIBE %s HTTP/1.1\r\n"
200 "CALLBACK: <http://%s:%d/event/%d>\r\n"
202 "TIMEOUT: Second-%d\r\n"
204 path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port),
205 ap->er->ip_addr_text, ap->er->http_port, ap->id, 1800);
207 wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Subscription request",
208 wpabuf_head(req), wpabuf_len(req));
210 ap->http = http_client_addr(&dst, req, 1000, wps_er_http_subscribe_cb,
212 if (ap->http == NULL)
217 static void wps_er_parse_device_description(struct wps_er_ap *ap,
218 struct wpabuf *reply)
220 /* Note: reply includes null termination after the buffer data */
221 const char *data = wpabuf_head(reply);
223 wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Device info",
224 wpabuf_head(reply), wpabuf_len(reply));
226 ap->friendly_name = xml_get_first_item(data, "friendlyName");
227 wpa_printf(MSG_DEBUG, "WPS ER: friendlyName='%s'", ap->friendly_name);
229 ap->manufacturer = xml_get_first_item(data, "manufacturer");
230 wpa_printf(MSG_DEBUG, "WPS ER: manufacturer='%s'", ap->manufacturer);
232 ap->manufacturer_url = xml_get_first_item(data, "manufacturerURL");
233 wpa_printf(MSG_DEBUG, "WPS ER: manufacturerURL='%s'",
234 ap->manufacturer_url);
236 ap->model_description = xml_get_first_item(data, "modelDescription");
237 wpa_printf(MSG_DEBUG, "WPS ER: modelDescription='%s'",
238 ap->model_description);
240 ap->model_name = xml_get_first_item(data, "modelName");
241 wpa_printf(MSG_DEBUG, "WPS ER: modelName='%s'", ap->model_name);
243 ap->model_number = xml_get_first_item(data, "modelNumber");
244 wpa_printf(MSG_DEBUG, "WPS ER: modelNumber='%s'", ap->model_number);
246 ap->model_url = xml_get_first_item(data, "modelURL");
247 wpa_printf(MSG_DEBUG, "WPS ER: modelURL='%s'", ap->model_url);
249 ap->serial_number = xml_get_first_item(data, "serialNumber");
250 wpa_printf(MSG_DEBUG, "WPS ER: serialNumber='%s'", ap->serial_number);
252 ap->udn = xml_get_first_item(data, "UDN");
253 wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn);
255 ap->upc = xml_get_first_item(data, "UPC");
256 wpa_printf(MSG_DEBUG, "WPS ER: UPC='%s'", ap->upc);
258 ap->scpd_url = http_link_update(
259 xml_get_first_item(data, "SCPDURL"), ap->location);
260 wpa_printf(MSG_DEBUG, "WPS ER: SCPDURL='%s'", ap->scpd_url);
262 ap->control_url = http_link_update(
263 xml_get_first_item(data, "controlURL"), ap->location);
264 wpa_printf(MSG_DEBUG, "WPS ER: controlURL='%s'", ap->control_url);
266 ap->event_sub_url = http_link_update(
267 xml_get_first_item(data, "eventSubURL"), ap->location);
268 wpa_printf(MSG_DEBUG, "WPS ER: eventSubURL='%s'", ap->event_sub_url);
272 static void wps_er_http_dev_desc_cb(void *ctx, struct http_client *c,
273 enum http_client_event event)
275 struct wps_er_ap *ap = ctx;
276 struct wpabuf *reply;
281 reply = http_client_get_body(c);
284 wps_er_parse_device_description(ap, reply);
287 case HTTP_CLIENT_FAILED:
288 case HTTP_CLIENT_INVALID_REPLY:
289 case HTTP_CLIENT_TIMEOUT:
290 wpa_printf(MSG_DEBUG, "WPS ER: Failed to fetch device info");
293 http_client_free(ap->http);
296 wps_er_subscribe(ap);
300 static void wps_er_ap_add(struct wps_er *er, struct in_addr *addr,
301 const char *location, int max_age)
303 struct wps_er_ap *ap;
305 ap = wps_er_ap_get(er, addr);
307 /* Update advertisement timeout */
308 eloop_cancel_timeout(wps_er_ap_timeout, er, ap);
309 eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap);
313 ap = os_zalloc(sizeof(*ap));
317 ap->id = ++er->next_ap_id;
318 ap->location = os_strdup(location);
319 if (ap->location == NULL) {
326 ap->addr.s_addr = addr->s_addr;
327 eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap);
329 wpa_printf(MSG_DEBUG, "WPS ER: Added AP entry for %s (%s)",
330 inet_ntoa(ap->addr), ap->location);
332 /* Fetch device description */
333 ap->http = http_client_url(ap->location, NULL, 10000,
334 wps_er_http_dev_desc_cb, ap);
338 static void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr)
340 struct wps_er_ap *prev = NULL, *ap = er->ap;
343 if (ap->addr.s_addr == addr->s_addr) {
345 prev->next = ap->next;
348 wps_er_ap_free(er, ap);
357 static void wps_er_ap_remove_all(struct wps_er *er)
359 struct wps_er_ap *prev, *ap;
367 wps_er_ap_free(er, prev);
372 static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx)
374 struct wps_er *er = eloop_ctx;
375 struct sockaddr_in addr; /* client address */
378 char buf[MULTICAST_MAX_READ], *pos, *pos2, *start;
379 int wfa = 0, byebye = 0;
381 char *location = NULL;
383 addr_len = sizeof(addr);
384 nread = recvfrom(sd, buf, sizeof(buf) - 1, 0,
385 (struct sockaddr *) &addr, &addr_len);
390 wpa_printf(MSG_DEBUG, "WPS ER: Received SSDP from %s",
391 inet_ntoa(addr.sin_addr));
392 wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Received SSDP contents",
395 if (sd == er->multicast_sd) {
396 /* Reply to M-SEARCH */
397 if (os_strncmp(buf, "HTTP/1.1 200 OK", 15) != 0)
398 return; /* unexpected response header */
400 /* Unsolicited message (likely NOTIFY or M-SEARCH) */
401 if (os_strncmp(buf, "NOTIFY ", 7) != 0)
402 return; /* only process notifications */
405 for (start = buf; start && *start; start = pos) {
406 pos = os_strchr(start, '\n');
412 if (os_strstr(start, "schemas-wifialliance-org:device:"
415 if (os_strstr(start, "schemas-wifialliance-org:service:"
418 if (os_strncasecmp(start, "LOCATION:", 9) == 0) {
420 while (*start == ' ')
423 } else if (os_strncasecmp(start, "NTS:", 4) == 0) {
424 if (os_strstr(start, "ssdp:byebye"))
426 } else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) {
428 while (*start == ' ')
430 pos2 = os_strstr(start, "max-age=");
434 max_age = atoi(pos2);
439 return; /* Not WPS advertisement/reply */
442 wps_er_ap_remove(er, &addr.sin_addr);
447 return; /* Unknown location */
450 return; /* No max-age reported */
452 wpa_printf(MSG_DEBUG, "WPS ER: AP discovered: %s "
453 "(packet source: %s max-age: %d)",
454 location, inet_ntoa(addr.sin_addr), max_age);
456 wps_er_ap_add(er, &addr.sin_addr, location, max_age);
460 static void wps_er_send_ssdp_msearch(struct wps_er *er)
463 struct sockaddr_in dest;
465 msg = wpabuf_alloc(500);
470 "M-SEARCH * HTTP/1.1\r\n"
471 "HOST: 239.255.255.250:1900\r\n"
472 "MAN: \"ssdp:discover\"\r\n"
474 "ST: urn:schemas-wifialliance-org:device:WFADevice:1"
478 os_memset(&dest, 0, sizeof(dest));
479 dest.sin_family = AF_INET;
480 dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
481 dest.sin_port = htons(UPNP_MULTICAST_PORT);
483 if (sendto(er->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
484 (struct sockaddr *) &dest, sizeof(dest)) < 0)
485 wpa_printf(MSG_DEBUG, "WPS ER: M-SEARCH sendto failed: "
486 "%d (%s)", errno, strerror(errno));
492 static void http_put_date(struct wpabuf *buf)
494 wpabuf_put_str(buf, "Date: ");
496 wpabuf_put_str(buf, "\r\n");
500 static void wps_er_http_resp_not_found(struct http_request *req)
503 buf = wpabuf_alloc(200);
505 http_request_deinit(req);
510 "HTTP/1.1 404 Not Found\r\n"
511 "Server: unspecified, UPnP/1.0, unspecified\r\n"
512 "Connection: close\r\n");
514 wpabuf_put_str(buf, "\r\n");
515 http_request_send_and_deinit(req, buf);
519 static void wps_er_http_resp_ok(struct http_request *req)
522 buf = wpabuf_alloc(200);
524 http_request_deinit(req);
529 "HTTP/1.1 200 OK\r\n"
530 "Server: unspecified, UPnP/1.0, unspecified\r\n"
531 "Connection: close\r\n"
532 "Content-Length: 0\r\n");
534 wpabuf_put_str(buf, "\r\n");
535 http_request_send_and_deinit(req, buf);
539 static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap,
543 struct wps_parse_attr attr;
545 wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - Probe Request - from "
546 MACSTR, MAC2STR(addr));
547 wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
548 "(TLVs from Probe Request)", msg);
550 if (wps_parse_msg(msg, &attr) < 0) {
551 wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
552 "WLANEvent message");
556 /* TODO: add STA table to the AP entry and wpa_msg indication if new
561 static void wps_er_process_wlanevent_eap(struct wps_er_ap *ap, const u8 *addr,
564 struct wps_parse_attr attr;
566 wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - EAP - from " MACSTR,
568 wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
569 "(TLVs from EAP-WSC)", msg);
571 if (wps_parse_msg(msg, &attr) < 0) {
572 wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
573 "WLANEvent message");
577 /* TODO: add STA table to the AP entry and wpa_msg indication if new
578 * STA; process message if it is part of ongoing protocol run */
582 static void wps_er_process_wlanevent(struct wps_er_ap *ap,
583 struct wpabuf *event)
587 u8 wlan_event_mac[ETH_ALEN];
590 wpa_hexdump(MSG_MSGDUMP, "WPS ER: Received WLANEvent",
591 wpabuf_head(event), wpabuf_len(event));
592 if (wpabuf_len(event) < 1 + 17) {
593 wpa_printf(MSG_DEBUG, "WPS ER: Too short WLANEvent");
597 data = wpabuf_mhead(event);
598 wlan_event_type = data[0];
599 if (hwaddr_aton((char *) data + 1, wlan_event_mac) < 0) {
600 wpa_printf(MSG_DEBUG, "WPS ER: Invalid WLANEventMAC in "
605 wpabuf_set(&msg, data + 1 + 17, wpabuf_len(event) - (1 + 17));
607 switch (wlan_event_type) {
609 wps_er_process_wlanevent_probe_req(ap, wlan_event_mac, &msg);
612 wps_er_process_wlanevent_eap(ap, wlan_event_mac, &msg);
615 wpa_printf(MSG_DEBUG, "WPS ER: Unknown WLANEventType %d",
622 static void wps_er_http_event(struct wps_er *er, struct http_request *req,
625 struct wps_er_ap *ap = wps_er_ap_get_id(er, ap_id);
626 struct wpabuf *event;
627 enum http_reply_code ret;
630 wpa_printf(MSG_DEBUG, "WPS ER: HTTP event from unknown AP id "
632 wps_er_http_resp_not_found(req);
635 wpa_printf(MSG_MSGDUMP, "WPS ER: HTTP event from AP id %u: %s",
636 ap_id, http_request_get_data(req));
638 event = xml_get_base64_item(http_request_get_data(req), "WLANEvent",
641 wpa_printf(MSG_DEBUG, "WPS ER: Could not extract WLANEvent "
642 "from the event notification");
644 * Reply with OK anyway to avoid getting unregistered from
647 wps_er_http_resp_ok(req);
651 wps_er_process_wlanevent(ap, event);
654 wps_er_http_resp_ok(req);
658 static void wps_er_http_notify(struct wps_er *er, struct http_request *req)
660 char *uri = http_request_get_uri(req);
662 if (os_strncmp(uri, "/event/", 7) == 0) {
663 wps_er_http_event(er, req, atoi(uri + 7));
665 wpa_printf(MSG_DEBUG, "WPS ER: Unknown HTTP NOTIFY for '%s'",
667 wps_er_http_resp_not_found(req);
672 static void wps_er_http_req(void *ctx, struct http_request *req)
674 struct wps_er *er = ctx;
675 struct sockaddr_in *cli = http_request_get_cli_addr(req);
676 enum httpread_hdr_type type = http_request_get_type(req);
679 wpa_printf(MSG_DEBUG, "WPS ER: HTTP request: '%s' (type %d) from "
681 http_request_get_uri(req), type,
682 inet_ntoa(cli->sin_addr), ntohs(cli->sin_port));
685 case HTTPREAD_HDR_TYPE_NOTIFY:
686 wps_er_http_notify(er, req);
689 wpa_printf(MSG_DEBUG, "WPS ER: Unsupported HTTP request type "
691 buf = wpabuf_alloc(200);
693 http_request_deinit(req);
697 "HTTP/1.1 501 Unimplemented\r\n"
698 "Connection: close\r\n");
700 wpabuf_put_str(buf, "\r\n");
701 http_request_send_and_deinit(req, buf);
708 wps_er_init(struct wps_context *wps, const char *ifname)
711 struct wps_registrar_config rcfg;
714 er = os_zalloc(sizeof(*er));
718 er->multicast_sd = -1;
721 os_strlcpy(er->ifname, ifname, sizeof(er->ifname));
722 os_memset(&rcfg, 0, sizeof(rcfg));
723 rcfg.pin_needed_cb = wps_er_pin_needed_cb;
726 er->reg = wps_registrar_init(wps, &rcfg);
727 if (er->reg == NULL) {
732 if (get_netif_info(ifname,
733 &er->ip_addr, &er->ip_addr_text,
734 er->mac_addr, &er->mac_addr_text)) {
735 wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
736 "for %s. Does it have IP address?", ifname);
741 if (add_ssdp_network(ifname)) {
746 er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr);
747 if (er->multicast_sd < 0) {
752 er->ssdp_sd = ssdp_listener_open();
753 if (er->ssdp_sd < 0) {
757 if (eloop_register_sock(er->multicast_sd, EVENT_TYPE_READ,
758 wps_er_ssdp_rx, er, NULL) ||
759 eloop_register_sock(er->ssdp_sd, EVENT_TYPE_READ,
760 wps_er_ssdp_rx, er, NULL)) {
765 addr.s_addr = er->ip_addr;
766 er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er);
767 if (er->http_srv == NULL) {
771 er->http_port = http_server_get_port(er->http_srv);
773 wpa_printf(MSG_DEBUG, "WPS ER: Start (ifname=%s ip_addr=%s "
775 er->ifname, er->ip_addr_text, er->mac_addr_text);
777 wps_er_send_ssdp_msearch(er);
783 void wps_er_deinit(struct wps_er *er)
787 http_server_deinit(er->http_srv);
788 wps_er_ap_remove_all(er);
789 if (er->multicast_sd >= 0) {
790 eloop_unregister_sock(er->multicast_sd, EVENT_TYPE_READ);
791 close(er->multicast_sd);
793 if (er->ssdp_sd >= 0) {
794 eloop_unregister_sock(er->ssdp_sd, EVENT_TYPE_READ);
797 wps_registrar_deinit(er->reg);
798 os_free(er->ip_addr_text);
799 os_free(er->mac_addr_text);