Home | History | Annotate | Download | only in wps
      1 /*
      2  * UPnP WPS Device - Event processing
      3  * Copyright (c) 2000-2003 Intel Corporation
      4  * Copyright (c) 2006-2007 Sony Corporation
      5  * Copyright (c) 2008-2009 Atheros Communications
      6  * Copyright (c) 2009-2010, Jouni Malinen <j (at) w1.fi>
      7  *
      8  * See wps_upnp.c for more details on licensing and code history.
      9  */
     10 
     11 #include "includes.h"
     12 #include <assert.h>
     13 
     14 #include "common.h"
     15 #include "eloop.h"
     16 #include "uuid.h"
     17 #include "http_client.h"
     18 #include "wps_defs.h"
     19 #include "wps_upnp.h"
     20 #include "wps_upnp_i.h"
     21 
     22 /*
     23  * Event message generation (to subscribers)
     24  *
     25  * We make a separate copy for each message for each subscriber. This memory
     26  * wasted could be limited (adding code complexity) by sharing copies, keeping
     27  * a usage count and freeing when zero.
     28  *
     29  * Sending a message requires using a HTTP over TCP NOTIFY
     30  * (like a PUT) which requires a number of states..
     31  */
     32 
     33 #define MAX_EVENTS_QUEUED 20   /* How far behind queued events */
     34 #define MAX_FAILURES 10 /* Drop subscription after this many failures */
     35 
     36 /* How long to wait before sending event */
     37 #define EVENT_DELAY_SECONDS 0
     38 #define EVENT_DELAY_MSEC 0
     39 
     40 /*
     41  * Event information that we send to each subscriber is remembered in this
     42  * struct. The event cannot be sent by simple UDP; it has to be sent by a HTTP
     43  * over TCP transaction which requires various states.. It may also need to be
     44  * retried at a different address (if more than one is available).
     45  *
     46  * TODO: As an optimization we could share data between subscribers.
     47  */
     48 struct wps_event_ {
     49 	struct dl_list list;
     50 	struct subscription *s;         /* parent */
     51 	unsigned subscriber_sequence;   /* which event for this subscription*/
     52 	unsigned int retry;             /* which retry */
     53 	struct subscr_addr *addr;       /* address to connect to */
     54 	struct wpabuf *data;            /* event data to send */
     55 	struct http_client *http_event;
     56 };
     57 
     58 
     59 /* event_clean -- clean sockets etc. of event
     60  * Leaves data, retry count etc. alone.
     61  */
     62 static void event_clean(struct wps_event_ *e)
     63 {
     64 	if (e->s->current_event == e)
     65 		e->s->current_event = NULL;
     66 	http_client_free(e->http_event);
     67 	e->http_event = NULL;
     68 }
     69 
     70 
     71 /* event_delete -- delete single unqueued event
     72  * (be sure to dequeue first if need be)
     73  */
     74 static void event_delete(struct wps_event_ *e)
     75 {
     76 	wpa_printf(MSG_DEBUG, "WPS UPnP: Delete event %p", e);
     77 	event_clean(e);
     78 	wpabuf_free(e->data);
     79 	os_free(e);
     80 }
     81 
     82 
     83 /* event_dequeue -- get next event from the queue
     84  * Returns NULL if empty.
     85  */
     86 static struct wps_event_ *event_dequeue(struct subscription *s)
     87 {
     88 	struct wps_event_ *e;
     89 	e = dl_list_first(&s->event_queue, struct wps_event_, list);
     90 	if (e) {
     91 		wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for "
     92 			   "subscription %p", e, s);
     93 		dl_list_del(&e->list);
     94 	}
     95 	return e;
     96 }
     97 
     98 
     99 /* event_delete_all -- delete entire event queue and current event */
    100 void event_delete_all(struct subscription *s)
    101 {
    102 	struct wps_event_ *e;
    103 	while ((e = event_dequeue(s)) != NULL)
    104 		event_delete(e);
    105 	if (s->current_event) {
    106 		event_delete(s->current_event);
    107 		/* will set: s->current_event = NULL;  */
    108 	}
    109 }
    110 
    111 
    112 /**
    113  * event_retry - Called when we had a failure delivering event msg
    114  * @e: Event
    115  * @do_next_address: skip address e.g. on connect fail
    116  */
    117 static void event_retry(struct wps_event_ *e, int do_next_address)
    118 {
    119 	struct subscription *s = e->s;
    120 	struct upnp_wps_device_sm *sm = s->sm;
    121 
    122 	wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p",
    123 		   e, s);
    124 	event_clean(e);
    125 	/* will set: s->current_event = NULL; */
    126 
    127 	if (do_next_address) {
    128 		e->retry++;
    129 		wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry);
    130 	}
    131 	if (e->retry >= dl_list_len(&s->addr_list)) {
    132 		wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
    133 			   "for %s", e->addr->domain_and_port);
    134 		event_delete(e);
    135 		s->last_event_failed = 1;
    136 		if (!dl_list_empty(&s->event_queue))
    137 			event_send_all_later(s->sm);
    138 		return;
    139 	}
    140 	dl_list_add(&s->event_queue, &e->list);
    141 	event_send_all_later(sm);
    142 }
    143 
    144 
    145 static struct wpabuf * event_build_message(struct wps_event_ *e)
    146 {
    147 	struct wpabuf *buf;
    148 	char *b;
    149 
    150 	buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
    151 	if (buf == NULL)
    152 		return NULL;
    153 	wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
    154 	wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
    155 	wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
    156 	wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
    157 		       "NT: upnp:event\r\n"
    158 		       "NTS: upnp:propchange\r\n");
    159 	wpabuf_put_str(buf, "SID: uuid:");
    160 	b = wpabuf_put(buf, 0);
    161 	uuid_bin2str(e->s->uuid, b, 80);
    162 	wpabuf_put(buf, os_strlen(b));
    163 	wpabuf_put_str(buf, "\r\n");
    164 	wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
    165 	wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
    166 		      (int) wpabuf_len(e->data));
    167 	wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
    168 	wpabuf_put_buf(buf, e->data);
    169 	return buf;
    170 }
    171 
    172 
    173 static void event_addr_failure(struct wps_event_ *e)
    174 {
    175 	struct subscription *s = e->s;
    176 
    177 	e->addr->num_failures++;
    178 	wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s "
    179 		   "(num_failures=%u)",
    180 		   e, e->addr->domain_and_port, e->addr->num_failures);
    181 
    182 	if (e->addr->num_failures < MAX_FAILURES) {
    183 		/* Try other addresses, if available */
    184 		event_retry(e, 1);
    185 		return;
    186 	}
    187 
    188 	/*
    189 	 * If other side doesn't like what we say, forget about them.
    190 	 * (There is no way to tell other side that we are dropping them...).
    191 	 */
    192 	wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription %p "
    193 		   "address %s due to errors", s, e->addr->domain_and_port);
    194 	dl_list_del(&e->addr->list);
    195 	subscr_addr_delete(e->addr);
    196 	e->addr = NULL;
    197 
    198 	if (dl_list_empty(&s->addr_list)) {
    199 		/* if we've given up on all addresses */
    200 		wpa_printf(MSG_DEBUG, "WPS UPnP: Removing subscription %p "
    201 			   "with no addresses", s);
    202 		dl_list_del(&s->list);
    203 		subscription_destroy(s);
    204 		return;
    205 	}
    206 
    207 	/* Try other addresses, if available */
    208 	event_retry(e, 0);
    209 }
    210 
    211 
    212 static void event_http_cb(void *ctx, struct http_client *c,
    213 			  enum http_client_event event)
    214 {
    215 	struct wps_event_ *e = ctx;
    216 	struct subscription *s = e->s;
    217 
    218 	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p "
    219 		   "event=%d", e, c, event);
    220 	switch (event) {
    221 	case HTTP_CLIENT_OK:
    222 		wpa_printf(MSG_DEBUG,
    223 			   "WPS UPnP: Got event %p reply OK from %s",
    224 			   e, e->addr->domain_and_port);
    225 		e->addr->num_failures = 0;
    226 		s->last_event_failed = 0;
    227 		event_delete(e);
    228 
    229 		/* Schedule sending more if there is more to send */
    230 		if (!dl_list_empty(&s->event_queue))
    231 			event_send_all_later(s->sm);
    232 		break;
    233 	case HTTP_CLIENT_FAILED:
    234 		wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure");
    235 		event_addr_failure(e);
    236 		break;
    237 	case HTTP_CLIENT_INVALID_REPLY:
    238 		wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid reply");
    239 		event_addr_failure(e);
    240 		break;
    241 	case HTTP_CLIENT_TIMEOUT:
    242 		wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
    243 		event_addr_failure(e);
    244 		break;
    245 	}
    246 }
    247 
    248 
    249 /* event_send_start -- prepare to send a event message to subscriber
    250  *
    251  * This gets complicated because:
    252  * -- The message is sent via TCP and we have to keep the stream open
    253  *      for 30 seconds to get a response... then close it.
    254  * -- But we might have other event happen in the meantime...
    255  *      we have to queue them, if we lose them then the subscriber will
    256  *      be forced to unsubscribe and subscribe again.
    257  * -- If multiple URLs are provided then we are supposed to try successive
    258  *      ones after 30 second timeout.
    259  * -- The URLs might use domain names instead of dotted decimal addresses,
    260  *      and resolution of those may cause unwanted sleeping.
    261  * -- Doing the initial TCP connect can take a while, so we have to come
    262  *      back after connection and then send the data.
    263  *
    264  * Returns nonzero on error;
    265  *
    266  * Prerequisite: No current event send (s->current_event == NULL)
    267  *      and non-empty queue.
    268  */
    269 static int event_send_start(struct subscription *s)
    270 {
    271 	struct wps_event_ *e;
    272 	unsigned int itry;
    273 	struct wpabuf *buf;
    274 
    275 	/*
    276 	 * Assume we are called ONLY with no current event and ONLY with
    277 	 * nonempty event queue and ONLY with at least one address to send to.
    278 	 */
    279 	if (dl_list_empty(&s->addr_list) ||
    280 	    s->current_event ||
    281 	    dl_list_empty(&s->event_queue))
    282 		return -1;
    283 
    284 	s->current_event = e = event_dequeue(s);
    285 
    286 	/* Use address according to number of retries */
    287 	itry = 0;
    288 	dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list)
    289 		if (itry++ == e->retry)
    290 			break;
    291 	if (itry < e->retry)
    292 		return -1;
    293 
    294 	buf = event_build_message(e);
    295 	if (buf == NULL) {
    296 		event_retry(e, 0);
    297 		return -1;
    298 	}
    299 
    300 	e->http_event = http_client_addr(&e->addr->saddr, buf, 0,
    301 					 event_http_cb, e);
    302 	if (e->http_event == NULL) {
    303 		wpabuf_free(buf);
    304 		event_retry(e, 0);
    305 		return -1;
    306 	}
    307 
    308 	return 0;
    309 }
    310 
    311 
    312 /* event_send_all_later_handler -- actually send events as needed */
    313 static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
    314 {
    315 	struct upnp_wps_device_sm *sm = user_ctx;
    316 	struct subscription *s, *tmp;
    317 	int nerrors = 0;
    318 
    319 	sm->event_send_all_queued = 0;
    320 	dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
    321 			      list) {
    322 		if (s->current_event == NULL /* not busy */ &&
    323 		    !dl_list_empty(&s->event_queue) /* more to do */) {
    324 			if (event_send_start(s))
    325 				nerrors++;
    326 		}
    327 	}
    328 
    329 	if (nerrors) {
    330 		/* Try again later */
    331 		event_send_all_later(sm);
    332 	}
    333 }
    334 
    335 
    336 /* event_send_all_later -- schedule sending events to all subscribers
    337  * that need it.
    338  * This avoids two problems:
    339  * -- After getting a subscription, we should not send the first event
    340  *      until after our reply is fully queued to be sent back,
    341  * -- Possible stack depth or infinite recursion issues.
    342  */
    343 void event_send_all_later(struct upnp_wps_device_sm *sm)
    344 {
    345 	/*
    346 	 * The exact time in the future isn't too important. Waiting a bit
    347 	 * might let us do several together.
    348 	 */
    349 	if (sm->event_send_all_queued)
    350 		return;
    351 	sm->event_send_all_queued = 1;
    352 	eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
    353 			       event_send_all_later_handler, NULL, sm);
    354 }
    355 
    356 
    357 /* event_send_stop_all -- cleanup */
    358 void event_send_stop_all(struct upnp_wps_device_sm *sm)
    359 {
    360 	if (sm->event_send_all_queued)
    361 		eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
    362 	sm->event_send_all_queued = 0;
    363 }
    364 
    365 
    366 /**
    367  * event_add - Add a new event to a queue
    368  * @s: Subscription
    369  * @data: Event data (is copied; caller retains ownership)
    370  * @probereq: Whether this is a Probe Request event
    371  * Returns: 0 on success, -1 on error, 1 on max event queue limit reached
    372  */
    373 int event_add(struct subscription *s, const struct wpabuf *data, int probereq)
    374 {
    375 	struct wps_event_ *e;
    376 	unsigned int len;
    377 
    378 	len = dl_list_len(&s->event_queue);
    379 	if (len >= MAX_EVENTS_QUEUED) {
    380 		wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
    381 			   "subscriber %p", s);
    382 		if (probereq)
    383 			return 1;
    384 
    385 		/* Drop oldest entry to allow EAP event to be stored. */
    386 		e = event_dequeue(s);
    387 		if (!e)
    388 			return 1;
    389 		event_delete(e);
    390 	}
    391 
    392 	if (s->last_event_failed && probereq && len > 0) {
    393 		/*
    394 		 * Avoid queuing frames for subscribers that may have left
    395 		 * without unsubscribing.
    396 		 */
    397 		wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe "
    398 			   "Request frames for subscription %p since last "
    399 			   "delivery failed", s);
    400 		return -1;
    401 	}
    402 
    403 	e = os_zalloc(sizeof(*e));
    404 	if (e == NULL)
    405 		return -1;
    406 	dl_list_init(&e->list);
    407 	e->s = s;
    408 	e->data = wpabuf_dup(data);
    409 	if (e->data == NULL) {
    410 		os_free(e);
    411 		return -1;
    412 	}
    413 	e->subscriber_sequence = s->next_subscriber_sequence++;
    414 	if (s->next_subscriber_sequence == 0)
    415 		s->next_subscriber_sequence++;
    416 	wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p "
    417 		   "(queue len %u)", e, s, len + 1);
    418 	dl_list_add_tail(&s->event_queue, &e->list);
    419 	event_send_all_later(s->sm);
    420 	return 0;
    421 }
    422