diff --git a/src/wps/http_client.c b/src/wps/http_client.c index d5c1efc31..d11649d70 100644 --- a/src/wps/http_client.c +++ b/src/wps/http_client.c @@ -193,25 +193,17 @@ struct http_client * http_client_addr(struct sockaddr_in *dst, } -struct http_client * http_client_url(const char *url, - struct wpabuf *req, size_t max_response, - void (*cb)(void *ctx, - struct http_client *c, - enum http_client_event event), - void *cb_ctx) +char * http_client_url_parse(const char *url, struct sockaddr_in *dst, + char **ret_path) { - struct sockaddr_in dst; - struct http_client *c; char *u, *addr, *port, *path; - struct wpabuf *req_buf = NULL; - if (os_strncmp(url, "http://", 7) != 0) - return NULL; u = os_strdup(url); if (u == NULL) return NULL; - os_memset(&dst, 0, sizeof(dst)); - dst.sin_family = AF_INET; + + os_memset(dst, 0, sizeof(*dst)); + dst->sin_family = AF_INET; addr = u + 7; path = os_strchr(addr, '/'); port = os_strchr(addr, ':'); @@ -225,7 +217,7 @@ struct http_client * http_client_url(const char *url, if (port) *port++ = '\0'; - if (inet_aton(addr, &dst.sin_addr) == 0) { + if (inet_aton(addr, &dst->sin_addr) == 0) { /* TODO: name lookup */ wpa_printf(MSG_DEBUG, "HTTP: Unsupported address in URL '%s' " "(addr='%s' port='%s')", @@ -235,15 +227,39 @@ struct http_client * http_client_url(const char *url, } if (port) - dst.sin_port = htons(atoi(port)); + dst->sin_port = htons(atoi(port)); else - dst.sin_port = htons(80); + dst->sin_port = htons(80); if (*path == '\0') { /* remove temporary nul termination for address */ *path = '/'; } + *ret_path = path; + + return u; +} + + +struct http_client * http_client_url(const char *url, + struct wpabuf *req, size_t max_response, + void (*cb)(void *ctx, + struct http_client *c, + enum http_client_event event), + void *cb_ctx) +{ + struct sockaddr_in dst; + struct http_client *c; + char *u, *path; + struct wpabuf *req_buf = NULL; + + if (os_strncmp(url, "http://", 7) != 0) + return NULL; + u = http_client_url_parse(url, &dst, &path); + if (u == NULL) + return NULL; + if (req == NULL) { req_buf = wpabuf_alloc(os_strlen(url) + 1000); if (req_buf == NULL) { diff --git a/src/wps/http_client.h b/src/wps/http_client.h index 53f8ec8ec..6d03c2f69 100644 --- a/src/wps/http_client.h +++ b/src/wps/http_client.h @@ -24,6 +24,8 @@ enum http_client_event { HTTP_CLIENT_INVALID_REPLY, }; +char * http_client_url_parse(const char *url, struct sockaddr_in *dst, + char **path); struct http_client * http_client_addr(struct sockaddr_in *dst, struct wpabuf *req, size_t max_response, void (*cb)(void *ctx, diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c index a9f932abf..bfa27e2a3 100644 --- a/src/wps/wps_er.c +++ b/src/wps/wps_er.c @@ -17,7 +17,9 @@ #include "common.h" #include "uuid.h" #include "eloop.h" +#include "httpread.h" #include "http_client.h" +#include "http_server.h" #include "upnp_xml.h" #include "wps_i.h" #include "wps_upnp.h" @@ -25,7 +27,6 @@ /* TODO: - * start own HTTP server for receiving events * send notification of new AP device with wpa_msg * re-send notifications with wpa_msg if ER re-started (to update wpa_gui-qt4) * (also re-send SSDP M-SEARCH in this case to find new APs) @@ -37,6 +38,7 @@ static void wps_er_ap_timeout(void *eloop_data, void *user_ctx); struct wps_er_ap { struct wps_er_ap *next; + struct wps_er *er; struct in_addr addr; char *location; struct http_client *http; @@ -55,6 +57,9 @@ struct wps_er_ap { char *scpd_url; char *control_url; char *event_sub_url; + + int subscribed; + unsigned int id; }; struct wps_er { @@ -67,6 +72,9 @@ struct wps_er { int multicast_sd; int ssdp_sd; struct wps_er_ap *ap; + struct http_server *http_srv; + int http_port; + unsigned int next_ap_id; }; @@ -89,8 +97,21 @@ static struct wps_er_ap * wps_er_ap_get(struct wps_er *er, } +static struct wps_er_ap * wps_er_ap_get_id(struct wps_er *er, unsigned int id) +{ + struct wps_er_ap *ap; + for (ap = er->ap; ap; ap = ap->next) { + if (ap->id == id) + break; + } + return ap; +} + + static void wps_er_ap_free(struct wps_er *er, struct wps_er_ap *ap) { + /* TODO: if ap->subscribed, unsubscribe from events if the AP is still + * alive */ wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)", inet_ntoa(ap->addr), ap->location); eloop_cancel_timeout(wps_er_ap_timeout, er, ap); @@ -125,6 +146,74 @@ static void wps_er_ap_timeout(void *eloop_data, void *user_ctx) } +static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c, + enum http_client_event event) +{ + struct wps_er_ap *ap = ctx; + + switch (event) { + case HTTP_CLIENT_OK: + wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events"); + break; + case HTTP_CLIENT_FAILED: + case HTTP_CLIENT_INVALID_REPLY: + case HTTP_CLIENT_TIMEOUT: + wpa_printf(MSG_DEBUG, "WPS ER: Failed to subscribe to events"); + break; + } + http_client_free(ap->http); + ap->http = NULL; +} + + +static void wps_er_subscribe(struct wps_er_ap *ap) +{ + struct wpabuf *req; + struct sockaddr_in dst; + char *url, *path; + + if (ap->event_sub_url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot " + "subscribe"); + return; + } + if (ap->http) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot " + "send subscribe request"); + return; + } + + url = http_client_url_parse(ap->event_sub_url, &dst, &path); + if (url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL"); + return; + } + + req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000); + if (req == NULL) { + os_free(url); + return; + } + wpabuf_printf(req, + "SUBSCRIBE %s HTTP/1.1\r\n" + "HOST: %s:%d\r\n" + "CALLBACK: \r\n" + "NT: upnp:event\r\n" + "TIMEOUT: Second-%d\r\n" + "\r\n", + path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port), + ap->er->ip_addr_text, ap->er->http_port, ap->id, 1800); + os_free(url); + wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Subscription request", + wpabuf_head(req), wpabuf_len(req)); + + ap->http = http_client_addr(&dst, req, 1000, wps_er_http_subscribe_cb, + ap); + if (ap->http == NULL) + wpabuf_free(req); +} + + static void wps_er_parse_device_description(struct wps_er_ap *ap, struct wpabuf *reply) { @@ -177,8 +266,6 @@ static void wps_er_parse_device_description(struct wps_er_ap *ap, ap->event_sub_url = http_link_update( xml_get_first_item(data, "eventSubURL"), ap->location); wpa_printf(MSG_DEBUG, "WPS ER: eventSubURL='%s'", ap->event_sub_url); - - /* TODO: subscribe for events */ } @@ -187,6 +274,7 @@ static void wps_er_http_dev_desc_cb(void *ctx, struct http_client *c, { struct wps_er_ap *ap = ctx; struct wpabuf *reply; + int subscribe = 0; switch (event) { case HTTP_CLIENT_OK: @@ -194,6 +282,7 @@ static void wps_er_http_dev_desc_cb(void *ctx, struct http_client *c, if (reply == NULL) break; wps_er_parse_device_description(ap, reply); + subscribe = 1; break; case HTTP_CLIENT_FAILED: case HTTP_CLIENT_INVALID_REPLY: @@ -203,6 +292,8 @@ static void wps_er_http_dev_desc_cb(void *ctx, struct http_client *c, } http_client_free(ap->http); ap->http = NULL; + if (subscribe) + wps_er_subscribe(ap); } @@ -222,6 +313,8 @@ static void wps_er_ap_add(struct wps_er *er, struct in_addr *addr, ap = os_zalloc(sizeof(*ap)); if (ap == NULL) return; + ap->er = er; + ap->id = ++er->next_ap_id; ap->location = os_strdup(location); if (ap->location == NULL) { os_free(ap); @@ -396,11 +489,65 @@ static void wps_er_send_ssdp_msearch(struct wps_er *er) } +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); + if (ap == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: HTTP event from unknown AP id " + "%u", ap_id); + 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); +} + + +static void wps_er_http_notify(struct wps_er *er, struct http_request *req) +{ + char *uri = http_request_get_uri(req); + + if (os_strncmp(uri, "/event/", 7) == 0) { + wps_er_http_event(er, req, atoi(uri + 7)); + } else { + wpa_printf(MSG_DEBUG, "WPS ER: Unknown HTTP NOTIFY for '%s'", + uri); + http_request_deinit(req); + } +} + + +static void wps_er_http_req(void *ctx, struct http_request *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); + wpa_printf(MSG_DEBUG, "WPS ER: HTTP request: '%s' (type %d) from " + "%s:%d", + http_request_get_uri(req), type, + inet_ntoa(cli->sin_addr), ntohs(cli->sin_port)); + + switch (type) { + case HTTPREAD_HDR_TYPE_NOTIFY: + wps_er_http_notify(er, req); + break; + default: + wpa_printf(MSG_DEBUG, "WPS ER: Unsupported HTTP request type " + "%d", type); + http_request_deinit(req); + break; + } +} + + struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname) { struct wps_er *er; struct wps_registrar_config rcfg; + struct in_addr addr; er = os_zalloc(sizeof(*er)); if (er == NULL) @@ -453,6 +600,14 @@ wps_er_init(struct wps_context *wps, const char *ifname) return NULL; } + addr.s_addr = er->ip_addr; + er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er); + if (er->http_srv == NULL) { + wps_er_deinit(er); + return NULL; + } + er->http_port = http_server_get_port(er->http_srv); + wpa_printf(MSG_DEBUG, "WPS ER: Start (ifname=%s ip_addr=%s " "mac_addr=%s)", er->ifname, er->ip_addr_text, er->mac_addr_text); @@ -467,6 +622,7 @@ void wps_er_deinit(struct wps_er *er) { if (er == NULL) return; + http_server_deinit(er->http_srv); wps_er_ap_remove_all(er); if (er->multicast_sd >= 0) { eloop_unregister_sock(er->multicast_sd, EVENT_TYPE_READ);