* Copyright (c) 2000-2003 Intel Corporation
* Copyright (c) 2006-2007 Sony Corporation
* Copyright (c) 2008-2009 Atheros Communications
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
* See below for more details on licensing and code history.
*/
* -- 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.
- * -- Just what should be in the first event message sent after subscription
- * for the WLANEvent field? If i pass it empty, Vista replies with OK
- * but apparently barfs on the message.
* -- The http error code generation is pretty bogus, hopefully noone cares.
*
* Author: Ted Merrill, Atheros Communications, based upon earlier work
#define MAX_SUBSCRIPTIONS 4 /* how many subscribing clients we handle */
#define MAX_ADDR_PER_SUBSCRIPTION 8
+/* Maximum number of Probe Request events per second */
+#define MAX_EVENTS_PER_SEC 5
/* Write the current date/time per RFC */
void format_date(struct wpabuf *buf)
/* subscr_addr_delete -- delete single unlinked subscriber address
* (be sure to unlink first if need be)
*/
-static void subscr_addr_delete(struct subscr_addr *a)
+void subscr_addr_delete(struct subscr_addr *a)
{
/*
* Note: do NOT free domain_and_port or path because they point to
}
-/* subscr_addr_unlink -- unlink subscriber address from linked list */
-static void subscr_addr_unlink(struct subscription *s, struct subscr_addr *a)
-{
- struct subscr_addr **listp = &s->addr_list;
- s->n_addr--;
- a->next->prev = a->prev;
- a->prev->next = a->next;
- if (*listp == a) {
- if (a == a->next) {
- /* last in queue */
- *listp = NULL;
- assert(s->n_addr == 0);
- } else {
- *listp = a->next;
- }
- }
-}
-
-
/* subscr_addr_free_all -- unlink and delete list of subscriber addresses. */
static void subscr_addr_free_all(struct subscription *s)
{
- struct subscr_addr **listp = &s->addr_list;
- struct subscr_addr *a;
- while ((a = *listp) != NULL) {
- subscr_addr_unlink(s, a);
+ struct subscr_addr *a, *tmp;
+ dl_list_for_each_safe(a, tmp, &s->addr_list, struct subscr_addr, list)
+ {
+ dl_list_del(&a->list);
subscr_addr_delete(a);
}
}
-/* subscr_addr_link -- add subscriber address to list of addresses */
-static void subscr_addr_link(struct subscription *s, struct subscr_addr *a)
-{
- struct subscr_addr **listp = &s->addr_list;
- s->n_addr++;
- if (*listp == NULL) {
- *listp = a->next = a->prev = a;
- } else {
- a->next = *listp;
- a->prev = (*listp)->prev;
- a->prev->next = a;
- a->next->prev = a;
- }
-}
-
-
/* subscr_addr_add_url -- add address(es) for one url to subscription */
-static void subscr_addr_add_url(struct subscription *s, const char *url)
+static void subscr_addr_add_url(struct subscription *s, const char *url,
+ size_t url_len)
{
int alloc_len;
char *scratch_mem = NULL;
struct addrinfo *result = NULL;
struct addrinfo *rp;
int rerr;
- struct subscr_addr *a = NULL;
/* url MUST begin with http: */
- if (os_strncasecmp(url, "http://", 7))
+ if (url_len < 7 || os_strncasecmp(url, "http://", 7))
goto fail;
url += 7;
+ url_len -= 7;
/* allocate memory for the extra stuff we need */
- alloc_len = (2 * (os_strlen(url) + 1));
+ alloc_len = 2 * (url_len + 1);
scratch_mem = os_zalloc(alloc_len);
if (scratch_mem == NULL)
goto fail;
mem = scratch_mem;
- strcpy(mem, url);
+ 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, '/');
}
domain = mem;
strcpy(domain, domain_and_port);
- delim = strchr(domain, ':');
+ delim = os_strchr(domain, ':');
if (delim) {
*delim++ = 0; /* null terminate domain */
if (isdigit(*delim))
goto fail;
}
for (rp = result; rp; rp = rp->ai_next) {
+ struct subscr_addr *a;
+
/* Limit no. of address to avoid denial of service attack */
- if (s->n_addr >= MAX_ADDR_PER_SUBSCRIPTION) {
+ if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) {
wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: "
"Ignoring excessive addresses");
break;
a = os_zalloc(sizeof(*a) + alloc_len);
if (a == NULL)
continue;
- a->s = s;
mem = (void *) (a + 1);
a->domain_and_port = mem;
strcpy(mem, domain_and_port);
if (path[0] != '/')
*mem++ = '/';
strcpy(mem, path);
- mem += 1 + strlen(mem);
+ mem += 1 + os_strlen(mem);
os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
a->saddr.sin_port = htons(port);
- subscr_addr_link(s, a);
- a = NULL; /* don't free it below */
+ dl_list_add(&s->addr_list, &a->list);
}
fail:
if (result)
freeaddrinfo(result);
os_free(scratch_mem);
- os_free(a);
}
static void subscr_addr_list_create(struct subscription *s,
const char *url_list)
{
- char *end;
+ const char *end;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Parsing URL list '%s'", url_list);
for (;;) {
while (*url_list == ' ' || *url_list == '\t')
url_list++;
end = os_strchr(url_list, '>');
if (end == NULL)
break;
- *end++ = 0;
- subscr_addr_add_url(s, url_list);
- url_list = end;
+ subscr_addr_add_url(s, url_list, end - url_list);
+ url_list = end + 1;
}
}
/* Enqueue event message for all subscribers */
struct wpabuf *buf; /* holds event message */
int buf_size = 0;
- struct subscription *s;
+ struct subscription *s, *tmp;
/* Actually, utf-8 is the default, but it doesn't hurt to specify it */
const char *format_head =
"<?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;
- if (sm->subscriptions == NULL) {
+ if (dl_list_empty(&sm->subscriptions)) {
/* optimize */
return;
}
+ if (os_get_time(&now) == 0) {
+ if (now.sec != sm->last_event_sec) {
+ sm->last_event_sec = now.sec;
+ sm->num_events_in_sec = 1;
+ } else {
+ sm->num_events_in_sec++;
+ /*
+ * In theory, this should apply to all WLANEvent
+ * notifications, but EAP messages are of much higher
+ * priority and Probe Request notifications should not
+ * be allowed to drop EAP messages, so only throttle
+ * Probe Request notifications.
+ */
+ if (sm->num_events_in_sec > MAX_EVENTS_PER_SEC &&
+ sm->wlanevent_type ==
+ UPNP_WPS_WLANEVENT_TYPE_PROBE) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Throttle "
+ "event notifications (%u seen "
+ "during one second)",
+ sm->num_events_in_sec);
+ return;
+ }
+ }
+ }
+
/* Determine buffer size needed first */
buf_size += os_strlen(format_head);
buf_size += 50 + 2 * os_strlen("WLANEvent");
wpa_printf(MSG_MSGDUMP, "WPS UPnP: WLANEvent message:\n%s",
(char *) wpabuf_head(buf));
- s = sm->subscriptions;
- do {
- if (event_add(s, buf)) {
- struct subscription *s_old = s;
+ dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
+ list) {
+ if (event_add(s, buf, sm->wlanevent_type ==
+ UPNP_WPS_WLANEVENT_TYPE_PROBE) == 1) {
wpa_printf(MSG_INFO, "WPS UPnP: Dropping "
- "subscriber due to event backlog");
- s = s_old->next;
- subscription_unlink(s_old);
- subscription_destroy(s_old);
- } else {
- s = s->next;
+ "subscriber %p due to event backlog", s);
+ dl_list_del(&s->list);
+ subscription_destroy(s);
}
- } while (s != sm->subscriptions);
+ }
wpabuf_free(buf);
}
* This is the result of an incoming HTTP over TCP SUBSCRIBE request.
*/
-/* subscription_unlink -- remove from the active list */
-void subscription_unlink(struct subscription *s)
-{
- struct upnp_wps_device_sm *sm = s->sm;
-
- if (s->next == s) {
- /* only one? */
- sm->subscriptions = NULL;
- } else {
- if (sm->subscriptions == s)
- sm->subscriptions = s->next;
- s->next->prev = s->prev;
- s->prev->next = s->next;
- }
- sm->n_subscriptions--;
-}
-
-
-/* subscription_link_to_end -- link to end of active list
- * (should have high expiry time!)
- */
-static void subscription_link_to_end(struct subscription *s)
-{
- struct upnp_wps_device_sm *sm = s->sm;
-
- if (sm->subscriptions) {
- s->next = sm->subscriptions;
- s->prev = s->next->prev;
- s->prev->next = s;
- s->next->prev = s;
- } else {
- sm->subscriptions = s->next = s->prev = s;
- }
- sm->n_subscriptions++;
-}
-
-
/* subscription_destroy -- destroy an unlinked subscription
* Be sure to unlink first if necessary.
*/
void subscription_destroy(struct subscription *s)
{
wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s);
- if (s->addr_list)
- subscr_addr_free_all(s);
+ subscr_addr_free_all(s);
event_delete_all(s);
+ upnp_er_remove_notification(s);
os_free(s);
}
/* subscription_list_age -- remove expired subscriptions */
static void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now)
{
- struct subscription *s;
- while ((s = sm->subscriptions) != NULL && s->timeout_time < now) {
+ struct subscription *s, *tmp;
+ dl_list_for_each_safe(s, tmp, &sm->subscriptions,
+ struct subscription, list) {
+ if (s->timeout_time > now)
+ break;
wpa_printf(MSG_DEBUG, "WPS UPnP: Removing aged subscription");
- subscription_unlink(s);
+ dl_list_del(&s->list);
subscription_destroy(s);
}
}
struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
const u8 uuid[UUID_LEN])
{
- struct subscription *s0 = sm->subscriptions;
- struct subscription *s = s0;
-
- if (s0 == NULL)
- return NULL;
- do {
+ struct subscription *s;
+ dl_list_for_each(s, &sm->subscriptions, struct subscription, list) {
if (os_memcmp(s->uuid, uuid, UUID_LEN) == 0)
return s; /* Found match */
- s = s->next;
- } while (s != s0);
-
+ }
return NULL;
}
+static struct wpabuf * build_fake_wsc_ack(void)
+{
+ struct wpabuf *msg = wpabuf_alloc(100);
+ if (msg == NULL)
+ return NULL;
+ wpabuf_put_u8(msg, UPNP_WPS_WLANEVENT_TYPE_EAP);
+ wpabuf_put_str(msg, "00:00:00:00:00:00");
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_ACK)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ /* Enrollee Nonce */
+ wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
+ wpabuf_put_be16(msg, WPS_NONCE_LEN);
+ wpabuf_put(msg, WPS_NONCE_LEN);
+ /* Registrar Nonce */
+ 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);
+ return msg;
+}
+
+
/* subscription_first_event -- send format/queue event that is automatically
* sent on a new subscription.
*/
"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
const char *tail = "</e:propertyset>\n";
char txt[10];
+ int ret;
+
+ if (s->sm->wlanevent == NULL) {
+ /*
+ * There has been no events before the subscription. However,
+ * UPnP device architecture specification requires all the
+ * evented variables to be included, so generate a dummy event
+ * for this particular case using a WSC_ACK and all-zeros
+ * nonces. The ER (UPnP control point) will ignore this, but at
+ * least it will learn that WLANEvent variable will be used in
+ * event notifications in the future.
+ */
+ struct wpabuf *msg;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Use a fake WSC_ACK as the "
+ "initial WLANEvent");
+ msg = build_fake_wsc_ack();
+ if (msg) {
+ s->sm->wlanevent = (char *)
+ base64_encode(wpabuf_head(msg),
+ wpabuf_len(msg), NULL);
+ wpabuf_free(msg);
+ }
+ }
wlan_event = s->sm->wlanevent;
if (wlan_event == NULL || *wlan_event == '\0') {
}
buf = wpabuf_alloc(500 + os_strlen(wlan_event));
if (buf == NULL)
- return 1;
+ return -1;
wpabuf_put_str(buf, head);
wpabuf_put_property(buf, "STAStatus", "1");
wpabuf_put_property(buf, "WLANEvent", wlan_event);
wpabuf_put_str(buf, tail);
- if (event_add(s, buf)) {
+ ret = event_add(s, buf, 0);
+ if (ret) {
wpabuf_free(buf);
- return 1;
+ return ret;
}
wpabuf_free(buf);
/**
- * subscription_start - Rremember a UPnP control point to send events to.
+ * subscription_start - Remember a UPnP control point to send events to.
* @sm: WPS UPnP state machine from upnp_wps_device_init()
- * @callback_urls: malloc' mem given to the subscription
+ * @callback_urls: Callback URLs
* Returns: %NULL on error, or pointer to new subscription structure.
*/
struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
- char *callback_urls)
+ const char *callback_urls)
{
struct subscription *s;
time_t now = time(NULL);
subscription_list_age(sm, now);
/* If too many subscriptions, remove oldest */
- if (sm->n_subscriptions >= MAX_SUBSCRIPTIONS) {
- s = sm->subscriptions;
+ 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");
- subscription_unlink(s);
+ dl_list_del(&s->list);
subscription_destroy(s);
}
s = os_zalloc(sizeof(*s));
if (s == NULL)
return NULL;
+ dl_list_init(&s->addr_list);
+ dl_list_init(&s->event_queue);
s->sm = sm;
s->timeout_time = expire;
uuid_make(s->uuid);
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 "
+ "'%s' - drop subscription", callback_urls);
+ subscription_destroy(s);
+ return NULL;
+ }
+
/* Add to end of list, since it has the highest expiration time */
- subscription_link_to_end(s);
+ dl_list_add_tail(&sm->subscriptions, &s->list);
/* Queue up immediate event message (our last event)
* as required by UPnP spec.
*/
if (subscription_first_event(s)) {
wpa_printf(MSG_INFO, "WPS UPnP: Dropping subscriber due to "
"event backlog");
- subscription_unlink(s);
+ dl_list_del(&s->list);
subscription_destroy(s);
return NULL;
}
wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s",
s, callback_urls);
- os_free(callback_urls);
/* Schedule sending this */
event_send_all_later(sm);
return s;
if (s == NULL)
return NULL;
wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewed");
- subscription_unlink(s);
+ dl_list_del(&s->list);
s->timeout_time = expire;
/* add back to end of list, since it now has highest expiry */
- subscription_link_to_end(s);
+ dl_list_add_tail(&sm->subscriptions, &s->list);
return s;
}
os_free(sm->wlanevent);
sm->wlanevent = val;
+ sm->wlanevent_type = ev_type;
upnp_wps_device_send_event(sm);
ret = 0;
}
-#ifdef __FreeBSD__
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <sys/sysctl.h>
#include <net/route.h>
#include <net/if_dl.h>
* @ip_addr: Buffer for returning IP address in network byte order
* @ip_addr_text: Buffer for returning a pointer to allocated IP address text
* @mac: Buffer for returning MAC address
- * @mac_addr_text: Buffer for returning allocated MAC address text
* Returns: 0 on success, -1 on failure
*/
int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
- u8 mac[ETH_ALEN], char **mac_addr_text)
+ u8 mac[ETH_ALEN])
{
struct ifreq req;
int sock = -1;
struct in_addr in_addr;
*ip_addr_text = os_zalloc(16);
- *mac_addr_text = os_zalloc(18);
- if (*ip_addr_text == NULL || *mac_addr_text == NULL)
+ if (*ip_addr_text == NULL)
goto fail;
sock = socket(AF_INET, SOCK_DGRAM, 0);
goto fail;
}
os_memcpy(mac, req.ifr_addr.sa_data, 6);
-#elif defined(__FreeBSD__)
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
if (eth_get(net_if, mac) < 0) {
wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address");
goto fail;
#else
#error MAC address fetch not implemented
#endif
- os_snprintf(*mac_addr_text, 18, MACSTR, MAC2STR(mac));
close(sock);
return 0;
close(sock);
os_free(*ip_addr_text);
*ip_addr_text = NULL;
- os_free(*mac_addr_text);
- *mac_addr_text = NULL;
return -1;
}
+static void upnp_wps_free_msearchreply(struct dl_list *head)
+{
+ struct advertisement_state_machine *a, *tmp;
+ dl_list_for_each_safe(a, tmp, head, struct advertisement_state_machine,
+ list)
+ msearchreply_state_machine_stop(a);
+}
+
+
+static void upnp_wps_free_subscriptions(struct dl_list *head)
+{
+ struct subscription *s, *tmp;
+ dl_list_for_each_safe(s, tmp, head, struct subscription, list) {
+ 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()
wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device");
web_listener_stop(sm);
- while (sm->web_connections)
- web_connection_stop(sm->web_connections);
- while (sm->msearch_replies)
- msearchreply_state_machine_stop(sm->msearch_replies);
- while (sm->subscriptions) {
- struct subscription *s = sm->subscriptions;
- subscription_unlink(s);
- subscription_destroy(s);
- }
+ upnp_wps_free_msearchreply(&sm->msearch_replies);
+ upnp_wps_free_subscriptions(&sm->subscriptions);
- advertisement_state_machine_stop(sm);
- /* TODO: send byebye notifications */
+ advertisement_state_machine_stop(sm, 1);
event_send_stop_all(sm);
os_free(sm->wlanevent);
sm->wlanevent = NULL;
- os_free(sm->net_if);
- sm->net_if = NULL;
- os_free(sm->mac_addr_text);
- sm->mac_addr_text = NULL;
os_free(sm->ip_addr_text);
sm->ip_addr_text = NULL;
if (sm->multicast_sd >= 0)
if (sm->started)
upnp_wps_device_stop(sm);
- sm->net_if = strdup(net_if);
sm->multicast_sd = -1;
sm->ssdp_sd = -1;
sm->started = 1;
goto fail;
/* Determine which IP and mac address we're using */
- if (get_netif_info(net_if,
- &sm->ip_addr, &sm->ip_addr_text,
- sm->mac_addr, &sm->mac_addr_text)) {
+ if (get_netif_info(net_if, &sm->ip_addr, &sm->ip_addr_text,
+ sm->mac_addr)) {
wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
"for %s. Does it have IP address?", net_if);
goto fail;
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);
}
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_subscribers(struct upnp_wps_device_sm *sm)
{
- return sm->subscriptions != NULL;
+ return !dl_list_empty(&sm->subscriptions);
+}
+
+
+int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin)
+{
+ 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;
+
+ return 0;
}