WPS ER: Subscribe to UPnP events
This adds code to start a HTTP server and to subscribe to UPnP events from each discovered WPS AP. The event messages are received, but there is not yet any code to actually parse the contents of the event.
This commit is contained in:
parent
875a4e5936
commit
097c7b3723
3 changed files with 193 additions and 19 deletions
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
162
src/wps/wps_er.c
162
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: <http://%s:%d/event/%d>\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);
|
||||
|
|
Loading…
Reference in a new issue