* -- Needs renaming with module prefix to avoid polluting the debugger
* namespace and causing possible collisions with other static fncs
* and structure declarations when using the debugger.
- * -- The http error code generation is pretty bogus, hopefully noone cares.
+ * -- The http error code generation is pretty bogus, hopefully no one cares.
*
* Author: Ted Merrill, Atheros Communications, based upon earlier work
* as explained above and below.
#include "includes.h"
-#include <assert.h>
+#include <time.h>
#include <net/if.h>
#include <netdb.h>
#include <sys/ioctl.h>
/* Maximum number of Probe Request events per second */
#define MAX_EVENTS_PER_SEC 5
+
+static struct upnp_wps_device_sm *shared_upnp_device = NULL;
+
+
/* Write the current date/time per RFC */
void format_date(struct wpabuf *buf)
{
t = time(NULL);
date = gmtime(&t);
+ if (date == NULL)
+ return;
wpabuf_printf(buf, "%s, %02d %s %d %02d:%02d:%02d GMT",
&weekday_str[date->tm_wday * 4], date->tm_mday,
&month_str[date->tm_mon * 4], date->tm_year + 1900,
* use for constructing UUIDs for subscriptions. Presumably any method from
* rfc4122 is good enough; I've chosen random number method.
*/
-static void uuid_make(u8 uuid[UUID_LEN])
+static int uuid_make(u8 uuid[UUID_LEN])
{
- os_get_random(uuid, UUID_LEN);
+ if (os_get_random(uuid, UUID_LEN) < 0)
+ return -1;
/* Replace certain bits as specified in rfc4122 or X.667 */
uuid[6] &= 0x0f; uuid[6] |= (4 << 4); /* version 4 == random gen */
uuid[8] &= 0x3f; uuid[8] |= 0x80;
+
+ return 0;
}
int alloc_len;
char *scratch_mem = NULL;
char *mem;
- char *domain_and_port;
+ char *host;
char *delim;
char *path;
- char *domain;
int port = 80; /* port to send to (default is port 80) */
struct addrinfo hints;
struct addrinfo *result = NULL;
struct addrinfo *rp;
int rerr;
+ size_t host_len, path_len;
/* url MUST begin with http: */
if (url_len < 7 || os_strncasecmp(url, "http://", 7))
url += 7;
url_len -= 7;
- /* allocate memory for the extra stuff we need */
- alloc_len = 2 * (url_len + 1);
- scratch_mem = os_zalloc(alloc_len);
+ /* Make a copy of the string to allow modification during parsing */
+ scratch_mem = dup_binstr(url, url_len);
if (scratch_mem == NULL)
goto fail;
- mem = scratch_mem;
- os_strncpy(mem, url, url_len);
- wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", mem);
- domain_and_port = mem;
- mem += 1 + os_strlen(mem);
- delim = os_strchr(domain_and_port, '/');
- if (delim) {
- *delim++ = 0; /* null terminate domain and port */
- path = delim;
- } else {
- path = domain_and_port + os_strlen(domain_and_port);
- }
- domain = mem;
- strcpy(domain, domain_and_port);
- delim = os_strchr(domain, ':');
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem);
+ host = scratch_mem;
+ path = os_strchr(host, '/');
+ if (path)
+ *path++ = '\0'; /* null terminate host */
+
+ /* Process and remove optional port component */
+ delim = os_strchr(host, ':');
if (delim) {
- *delim++ = 0; /* null terminate domain */
- if (isdigit(*delim))
- port = atol(delim);
+ *delim = '\0'; /* null terminate host name for now */
+ if (isdigit(delim[1]))
+ port = atol(delim + 1);
}
/*
hints.ai_flags = 0;
#endif
hints.ai_protocol = 0; /* Any protocol? */
- rerr = getaddrinfo(domain, NULL /* fill in port ourselves */,
+ rerr = getaddrinfo(host, NULL /* fill in port ourselves */,
&hints, &result);
if (rerr) {
wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s",
- rerr, gai_strerror(rerr), domain);
+ rerr, gai_strerror(rerr), host);
goto fail;
}
+
+ if (delim)
+ *delim = ':'; /* Restore port */
+
+ host_len = os_strlen(host);
+ path_len = path ? os_strlen(path) : 0;
+ alloc_len = host_len + 1 + 1 + path_len + 1;
+
for (rp = result; rp; rp = rp->ai_next) {
struct subscr_addr *a;
a = os_zalloc(sizeof(*a) + alloc_len);
if (a == NULL)
- continue;
- mem = (void *) (a + 1);
+ break;
+ mem = (char *) (a + 1);
a->domain_and_port = mem;
- strcpy(mem, domain_and_port);
- mem += 1 + strlen(mem);
+ os_memcpy(mem, host, host_len);
+ mem += host_len + 1;
a->path = mem;
- if (path[0] != '/')
+ if (path == NULL || path[0] != '/')
*mem++ = '/';
- strcpy(mem, path);
- mem += 1 + os_strlen(mem);
+ if (path)
+ os_memcpy(mem, path, path_len);
os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
a->saddr.sin_port = htons(port);
}
-int send_wpabuf(int fd, struct wpabuf *buf)
-{
- wpa_printf(MSG_DEBUG, "WPS UPnP: Send %lu byte message",
- (unsigned long) wpabuf_len(buf));
- errno = 0;
- if (write(fd, wpabuf_head(buf), wpabuf_len(buf)) !=
- (int) wpabuf_len(buf)) {
- wpa_printf(MSG_ERROR, "WPS UPnP: Failed to send buffer: "
- "errno=%d (%s)",
- errno, strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-
static void wpabuf_put_property(struct wpabuf *buf, const char *name,
const char *value)
{
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
const char *format_tail = "</e:propertyset>\n";
- struct os_time now;
+ struct os_reltime now;
if (dl_list_empty(&sm->subscriptions)) {
/* optimize */
return;
}
- if (os_get_time(&now) == 0) {
+ if (os_get_reltime(&now) == 0) {
if (now.sec != sm->last_event_sec) {
sm->last_event_sec = now.sec;
sm->num_events_in_sec = 1;
*/
void subscription_destroy(struct subscription *s)
{
+ struct upnp_wps_device_interface *iface;
wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s);
subscr_addr_free_all(s);
event_delete_all(s);
- upnp_er_remove_notification(s);
+ dl_list_for_each(iface, &s->sm->interfaces,
+ struct upnp_wps_device_interface, list)
+ upnp_er_remove_notification(iface->wps->registrar, s);
os_free(s);
}
wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
wpabuf_put_be16(msg, WPS_NONCE_LEN);
wpabuf_put(msg, WPS_NONCE_LEN);
- wps_build_wfa_ext(msg, 0, NULL, 0);
+ if (wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
return msg;
}
struct subscription *s;
time_t now = time(NULL);
time_t expire = now + UPNP_SUBSCRIBE_SEC;
+ char str[80];
/* Get rid of expired subscriptions so we have room */
subscription_list_age(sm, now);
if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) {
s = dl_list_first(&sm->subscriptions, struct subscription,
list);
- wpa_printf(MSG_INFO, "WPS UPnP: Too many subscriptions, "
- "trashing oldest");
- dl_list_del(&s->list);
- subscription_destroy(s);
+ if (s) {
+ wpa_printf(MSG_INFO,
+ "WPS UPnP: Too many subscriptions, trashing oldest");
+ dl_list_del(&s->list);
+ subscription_destroy(s);
+ }
}
s = os_zalloc(sizeof(*s));
s->sm = sm;
s->timeout_time = expire;
- uuid_make(s->uuid);
+ if (uuid_make(s->uuid) < 0) {
+ subscription_destroy(s);
+ return NULL;
+ }
subscr_addr_list_create(s, callback_urls);
if (dl_list_empty(&s->addr_list)) {
wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in "
subscription_destroy(s);
return NULL;
}
- wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s",
- s, callback_urls);
+ uuid_bin2str(s->uuid, str, sizeof(str));
+ wpa_printf(MSG_DEBUG,
+ "WPS UPnP: Subscription %p (SID %s) started with %s",
+ s, str, callback_urls);
/* Schedule sending this */
event_send_all_later(sm);
return s;
}
-static void upnp_wps_free_subscriptions(struct dl_list *head)
+static void upnp_wps_free_subscriptions(struct dl_list *head,
+ struct wps_registrar *reg)
{
struct subscription *s, *tmp;
dl_list_for_each_safe(s, tmp, head, struct subscription, list) {
+ if (reg && s->reg != reg)
+ continue;
dl_list_del(&s->list);
subscription_destroy(s);
}
* upnp_wps_device_stop - Stop WPS UPnP operations on an interface
* @sm: WPS UPnP state machine from upnp_wps_device_init()
*/
-void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
+static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
{
if (!sm || !sm->started)
return;
wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device");
web_listener_stop(sm);
+ ssdp_listener_stop(sm);
upnp_wps_free_msearchreply(&sm->msearch_replies);
- upnp_wps_free_subscriptions(&sm->subscriptions);
+ upnp_wps_free_subscriptions(&sm->subscriptions, NULL);
advertisement_state_machine_stop(sm, 1);
if (sm->multicast_sd >= 0)
close(sm->multicast_sd);
sm->multicast_sd = -1;
- ssdp_listener_stop(sm);
sm->started = 0;
}
* @net_if: Selected network interface name
* Returns: 0 on success, -1 on failure
*/
-int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
+static int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
{
if (!sm || !net_if)
return -1;
}
+static struct upnp_wps_device_interface *
+upnp_wps_get_iface(struct upnp_wps_device_sm *sm, void *priv)
+{
+ struct upnp_wps_device_interface *iface;
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ if (iface->priv == priv)
+ return iface;
+ }
+ return NULL;
+}
+
+
/**
* upnp_wps_device_deinit - Deinitialize WPS UPnP
* @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @priv: External context data that was used in upnp_wps_device_init() call
*/
-void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm)
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv)
{
+ struct upnp_wps_device_interface *iface;
+
if (!sm)
return;
- upnp_wps_device_stop(sm);
-
- if (sm->peer.wps)
- wps_deinit(sm->peer.wps);
- os_free(sm->root_dir);
- os_free(sm->desc_url);
- os_free(sm->ctx->ap_pin);
- os_free(sm->ctx);
- os_free(sm);
+ iface = upnp_wps_get_iface(sm, priv);
+ if (iface == NULL) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: Could not find the interface "
+ "instance to deinit");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Deinit interface instance %p", iface);
+ if (dl_list_len(&sm->interfaces) == 1) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Deinitializing last instance "
+ "- free global device instance");
+ upnp_wps_device_stop(sm);
+ } else
+ upnp_wps_free_subscriptions(&sm->subscriptions,
+ iface->wps->registrar);
+ dl_list_del(&iface->list);
+
+ if (iface->peer.wps)
+ wps_deinit(iface->peer.wps);
+ os_free(iface->ctx->ap_pin);
+ os_free(iface->ctx);
+ os_free(iface);
+
+ if (dl_list_empty(&sm->interfaces)) {
+ os_free(sm->root_dir);
+ os_free(sm->desc_url);
+ os_free(sm);
+ shared_upnp_device = NULL;
+ }
}
* @ctx: callback table; we must eventually free it
* @wps: Pointer to longterm WPS context
* @priv: External context data that will be used in callbacks
+ * @net_if: Selected network interface name
* Returns: WPS UPnP state or %NULL on failure
*/
struct upnp_wps_device_sm *
upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
- void *priv)
+ void *priv, char *net_if)
{
struct upnp_wps_device_sm *sm;
+ struct upnp_wps_device_interface *iface;
+ int start = 0;
- sm = os_zalloc(sizeof(*sm));
- if (!sm) {
- wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init failed");
+ iface = os_zalloc(sizeof(*iface));
+ if (iface == NULL) {
+ os_free(ctx->ap_pin);
+ os_free(ctx);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface);
+
+ iface->ctx = ctx;
+ iface->wps = wps;
+ iface->priv = priv;
+
+ if (shared_upnp_device) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Share existing device "
+ "context");
+ sm = shared_upnp_device;
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Initialize device context");
+ sm = os_zalloc(sizeof(*sm));
+ if (!sm) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init "
+ "failed");
+ os_free(iface);
+ os_free(ctx->ap_pin);
+ os_free(ctx);
+ return NULL;
+ }
+ shared_upnp_device = sm;
+
+ dl_list_init(&sm->msearch_replies);
+ dl_list_init(&sm->subscriptions);
+ dl_list_init(&sm->interfaces);
+ start = 1;
+ }
+
+ dl_list_add(&sm->interfaces, &iface->list);
+
+ if (start && upnp_wps_device_start(sm, net_if)) {
+ upnp_wps_device_deinit(sm, priv);
return NULL;
}
- sm->ctx = ctx;
- sm->wps = wps;
- sm->priv = priv;
- dl_list_init(&sm->msearch_replies);
- dl_list_init(&sm->subscriptions);
return sm;
}
int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin)
{
+ struct upnp_wps_device_interface *iface;
if (sm == NULL)
return 0;
- os_free(sm->ctx->ap_pin);
- if (ap_pin) {
- sm->ctx->ap_pin = os_strdup(ap_pin);
- if (sm->ctx->ap_pin == NULL)
- return -1;
- } else
- sm->ctx->ap_pin = NULL;
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ os_free(iface->ctx->ap_pin);
+ if (ap_pin) {
+ iface->ctx->ap_pin = os_strdup(ap_pin);
+ if (iface->ctx->ap_pin == NULL)
+ return -1;
+ } else
+ iface->ctx->ap_pin = NULL;
+ }
return 0;
}