xref: /freebsd/contrib/wpa/src/wps/wps_upnp_event.c (revision 39beb93c3f8bdbf72a61fda42300b5ebed7390c8)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * UPnP WPS Device - Event processing
339beb93cSSam Leffler  * Copyright (c) 2000-2003 Intel Corporation
439beb93cSSam Leffler  * Copyright (c) 2006-2007 Sony Corporation
539beb93cSSam Leffler  * Copyright (c) 2008-2009 Atheros Communications
639beb93cSSam Leffler  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
739beb93cSSam Leffler  *
839beb93cSSam Leffler  * See wps_upnp.c for more details on licensing and code history.
939beb93cSSam Leffler  */
1039beb93cSSam Leffler 
1139beb93cSSam Leffler #include "includes.h"
1239beb93cSSam Leffler #include <assert.h>
1339beb93cSSam Leffler #include <fcntl.h>
1439beb93cSSam Leffler 
1539beb93cSSam Leffler #include "common.h"
1639beb93cSSam Leffler #include "eloop.h"
1739beb93cSSam Leffler #include "uuid.h"
1839beb93cSSam Leffler #include "httpread.h"
1939beb93cSSam Leffler #include "wps_defs.h"
2039beb93cSSam Leffler #include "wps_upnp.h"
2139beb93cSSam Leffler #include "wps_upnp_i.h"
2239beb93cSSam Leffler 
2339beb93cSSam Leffler /*
2439beb93cSSam Leffler  * Event message generation (to subscribers)
2539beb93cSSam Leffler  *
2639beb93cSSam Leffler  * We make a separate copy for each message for each subscriber. This memory
2739beb93cSSam Leffler  * wasted could be limited (adding code complexity) by sharing copies, keeping
2839beb93cSSam Leffler  * a usage count and freeing when zero.
2939beb93cSSam Leffler  *
3039beb93cSSam Leffler  * Sending a message requires using a HTTP over TCP NOTIFY
3139beb93cSSam Leffler  * (like a PUT) which requires a number of states..
3239beb93cSSam Leffler  */
3339beb93cSSam Leffler 
3439beb93cSSam Leffler #define MAX_EVENTS_QUEUED 20   /* How far behind queued events */
3539beb93cSSam Leffler #define EVENT_TIMEOUT_SEC 30   /* Drop sending event after timeout */
3639beb93cSSam Leffler 
3739beb93cSSam Leffler /* How long to wait before sending event */
3839beb93cSSam Leffler #define EVENT_DELAY_SECONDS 0
3939beb93cSSam Leffler #define EVENT_DELAY_MSEC 0
4039beb93cSSam Leffler 
4139beb93cSSam Leffler /*
4239beb93cSSam Leffler  * Event information that we send to each subscriber is remembered in this
4339beb93cSSam Leffler  * struct. The event cannot be sent by simple UDP; it has to be sent by a HTTP
4439beb93cSSam Leffler  * over TCP transaction which requires various states.. It may also need to be
4539beb93cSSam Leffler  * retried at a different address (if more than one is available).
4639beb93cSSam Leffler  *
4739beb93cSSam Leffler  * TODO: As an optimization we could share data between subscribers.
4839beb93cSSam Leffler  */
4939beb93cSSam Leffler struct wps_event_ {
5039beb93cSSam Leffler 	struct wps_event_ *next;
5139beb93cSSam Leffler 	struct wps_event_ *prev;        /* double linked list */
5239beb93cSSam Leffler 	struct subscription *s;         /* parent */
5339beb93cSSam Leffler 	unsigned subscriber_sequence;   /* which event for this subscription*/
5439beb93cSSam Leffler 	int retry;                      /* which retry */
5539beb93cSSam Leffler 	struct subscr_addr *addr;       /* address to connect to */
5639beb93cSSam Leffler 	struct wpabuf *data;            /* event data to send */
5739beb93cSSam Leffler 	/* The following apply while we are sending an event message. */
5839beb93cSSam Leffler 	int sd;            /* -1 or socket descriptor for open connection */
5939beb93cSSam Leffler 	int sd_registered;        /* nonzero if we must cancel registration */
6039beb93cSSam Leffler 	struct httpread *hread; /* NULL or open connection for event msg */
6139beb93cSSam Leffler };
6239beb93cSSam Leffler 
6339beb93cSSam Leffler 
6439beb93cSSam Leffler static void event_timeout_handler(void *eloop_data, void *user_ctx);
6539beb93cSSam Leffler 
6639beb93cSSam Leffler /* event_clean -- clean sockets etc. of event
6739beb93cSSam Leffler  * Leaves data, retry count etc. alone.
6839beb93cSSam Leffler  */
6939beb93cSSam Leffler static void event_clean(struct wps_event_ *e)
7039beb93cSSam Leffler {
7139beb93cSSam Leffler 	if (e->s->current_event == e) {
7239beb93cSSam Leffler 		eloop_cancel_timeout(event_timeout_handler, NULL, e);
7339beb93cSSam Leffler 		e->s->current_event = NULL;
7439beb93cSSam Leffler 	}
7539beb93cSSam Leffler 	if (e->sd_registered) {
7639beb93cSSam Leffler 		eloop_unregister_sock(e->sd, EVENT_TYPE_WRITE);
7739beb93cSSam Leffler 		e->sd_registered = 0;
7839beb93cSSam Leffler 	}
7939beb93cSSam Leffler 	if (e->sd != -1) {
8039beb93cSSam Leffler 		close(e->sd);
8139beb93cSSam Leffler 		e->sd = -1;
8239beb93cSSam Leffler 	}
8339beb93cSSam Leffler 	if (e->hread)
8439beb93cSSam Leffler 		httpread_destroy(e->hread);
8539beb93cSSam Leffler 	e->hread = NULL;
8639beb93cSSam Leffler }
8739beb93cSSam Leffler 
8839beb93cSSam Leffler 
8939beb93cSSam Leffler /* event_delete -- delete single unqueued event
9039beb93cSSam Leffler  * (be sure to dequeue first if need be)
9139beb93cSSam Leffler  */
9239beb93cSSam Leffler void event_delete(struct wps_event_ *e)
9339beb93cSSam Leffler {
9439beb93cSSam Leffler 	event_clean(e);
9539beb93cSSam Leffler 	wpabuf_free(e->data);
9639beb93cSSam Leffler 	os_free(e);
9739beb93cSSam Leffler }
9839beb93cSSam Leffler 
9939beb93cSSam Leffler 
10039beb93cSSam Leffler /* event_dequeue -- get next event from the queue
10139beb93cSSam Leffler  * Returns NULL if empty.
10239beb93cSSam Leffler  */
10339beb93cSSam Leffler static struct wps_event_ *event_dequeue(struct subscription *s)
10439beb93cSSam Leffler {
10539beb93cSSam Leffler 	struct wps_event_ **event_head = &s->event_queue;
10639beb93cSSam Leffler 	struct wps_event_ *e = *event_head;
10739beb93cSSam Leffler 	if (e == NULL)
10839beb93cSSam Leffler 		return NULL;
10939beb93cSSam Leffler 	e->next->prev = e->prev;
11039beb93cSSam Leffler 	e->prev->next = e->next;
11139beb93cSSam Leffler 	if (*event_head == e) {
11239beb93cSSam Leffler 		if (e == e->next) {
11339beb93cSSam Leffler 			/* last in queue */
11439beb93cSSam Leffler 			*event_head = NULL;
11539beb93cSSam Leffler 		} else {
11639beb93cSSam Leffler 			*event_head = e->next;
11739beb93cSSam Leffler 		}
11839beb93cSSam Leffler 	}
11939beb93cSSam Leffler 	s->n_queue--;
12039beb93cSSam Leffler 	e->next = e->prev = NULL;
12139beb93cSSam Leffler 	/* but parent "s" is still valid */
12239beb93cSSam Leffler 	return e;
12339beb93cSSam Leffler }
12439beb93cSSam Leffler 
12539beb93cSSam Leffler 
12639beb93cSSam Leffler /* event_enqueue_at_end -- add event to end of queue */
12739beb93cSSam Leffler static void event_enqueue_at_end(struct subscription *s, struct wps_event_ *e)
12839beb93cSSam Leffler {
12939beb93cSSam Leffler 	struct wps_event_ **event_head = &s->event_queue;
13039beb93cSSam Leffler 	if (*event_head == NULL) {
13139beb93cSSam Leffler 		*event_head = e->next = e->prev = e;
13239beb93cSSam Leffler 	} else {
13339beb93cSSam Leffler 		e->next = *event_head;
13439beb93cSSam Leffler 		e->prev = e->next->prev;
13539beb93cSSam Leffler 		e->prev->next = e;
13639beb93cSSam Leffler 		e->next->prev = e;
13739beb93cSSam Leffler 	}
13839beb93cSSam Leffler 	s->n_queue++;
13939beb93cSSam Leffler }
14039beb93cSSam Leffler 
14139beb93cSSam Leffler 
14239beb93cSSam Leffler /* event_enqueue_at_begin -- add event to begin of queue
14339beb93cSSam Leffler  * (appropriate for retrying event only)
14439beb93cSSam Leffler  */
14539beb93cSSam Leffler static void event_enqueue_at_begin(struct subscription *s,
14639beb93cSSam Leffler 				   struct wps_event_ *e)
14739beb93cSSam Leffler {
14839beb93cSSam Leffler 	struct wps_event_ **event_head = &s->event_queue;
14939beb93cSSam Leffler 	if (*event_head == NULL) {
15039beb93cSSam Leffler 		*event_head = e->next = e->prev = e;
15139beb93cSSam Leffler 	} else {
15239beb93cSSam Leffler 		e->prev = *event_head;
15339beb93cSSam Leffler 		e->next = e->prev->next;
15439beb93cSSam Leffler 		e->prev->next = e;
15539beb93cSSam Leffler 		e->next->prev = e;
15639beb93cSSam Leffler 		*event_head = e;
15739beb93cSSam Leffler 	}
15839beb93cSSam Leffler 	s->n_queue++;
15939beb93cSSam Leffler }
16039beb93cSSam Leffler 
16139beb93cSSam Leffler 
16239beb93cSSam Leffler /* event_delete_all -- delete entire event queue and current event */
16339beb93cSSam Leffler void event_delete_all(struct subscription *s)
16439beb93cSSam Leffler {
16539beb93cSSam Leffler 	struct wps_event_ *e;
16639beb93cSSam Leffler 	while ((e = event_dequeue(s)) != NULL)
16739beb93cSSam Leffler 		event_delete(e);
16839beb93cSSam Leffler 	if (s->current_event) {
16939beb93cSSam Leffler 		event_delete(s->current_event);
17039beb93cSSam Leffler 		/* will set: s->current_event = NULL;  */
17139beb93cSSam Leffler 	}
17239beb93cSSam Leffler }
17339beb93cSSam Leffler 
17439beb93cSSam Leffler 
17539beb93cSSam Leffler /**
17639beb93cSSam Leffler  * event_retry - Called when we had a failure delivering event msg
17739beb93cSSam Leffler  * @e: Event
17839beb93cSSam Leffler  * @do_next_address: skip address e.g. on connect fail
17939beb93cSSam Leffler  */
18039beb93cSSam Leffler static void event_retry(struct wps_event_ *e, int do_next_address)
18139beb93cSSam Leffler {
18239beb93cSSam Leffler 	struct subscription *s = e->s;
18339beb93cSSam Leffler 	struct upnp_wps_device_sm *sm = s->sm;
18439beb93cSSam Leffler 
18539beb93cSSam Leffler 	event_clean(e);
18639beb93cSSam Leffler 	/* will set: s->current_event = NULL; */
18739beb93cSSam Leffler 
18839beb93cSSam Leffler 	if (do_next_address)
18939beb93cSSam Leffler 		e->retry++;
19039beb93cSSam Leffler 	if (e->retry >= s->n_addr) {
19139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
19239beb93cSSam Leffler 			   "for %s", e->addr->domain_and_port);
19339beb93cSSam Leffler 		return;
19439beb93cSSam Leffler 	}
19539beb93cSSam Leffler 	event_enqueue_at_begin(s, e);
19639beb93cSSam Leffler 	event_send_all_later(sm);
19739beb93cSSam Leffler }
19839beb93cSSam Leffler 
19939beb93cSSam Leffler 
20039beb93cSSam Leffler /* called if the overall event-sending process takes too long */
20139beb93cSSam Leffler static void event_timeout_handler(void *eloop_data, void *user_ctx)
20239beb93cSSam Leffler {
20339beb93cSSam Leffler 	struct wps_event_ *e = user_ctx;
20439beb93cSSam Leffler 	struct subscription *s = e->s;
20539beb93cSSam Leffler 
20639beb93cSSam Leffler 	assert(e == s->current_event);
20739beb93cSSam Leffler 
20839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
20939beb93cSSam Leffler 	event_retry(e, 1);
21039beb93cSSam Leffler }
21139beb93cSSam Leffler 
21239beb93cSSam Leffler 
21339beb93cSSam Leffler /* event_got_response_handler -- called back when http response is received. */
21439beb93cSSam Leffler static void event_got_response_handler(struct httpread *handle, void *cookie,
21539beb93cSSam Leffler 				       enum httpread_event en)
21639beb93cSSam Leffler {
21739beb93cSSam Leffler 	struct wps_event_ *e = cookie;
21839beb93cSSam Leffler 	struct subscription *s = e->s;
21939beb93cSSam Leffler 	struct upnp_wps_device_sm *sm = s->sm;
22039beb93cSSam Leffler 	struct httpread *hread = e->hread;
22139beb93cSSam Leffler 	int reply_code = 0;
22239beb93cSSam Leffler 
22339beb93cSSam Leffler 	assert(e == s->current_event);
22439beb93cSSam Leffler 	eloop_cancel_timeout(event_timeout_handler, NULL, e);
22539beb93cSSam Leffler 
22639beb93cSSam Leffler 	if (en == HTTPREAD_EVENT_FILE_READY) {
22739beb93cSSam Leffler 		if (httpread_hdr_type_get(hread) == HTTPREAD_HDR_TYPE_REPLY) {
22839beb93cSSam Leffler 			reply_code = httpread_reply_code_get(hread);
22939beb93cSSam Leffler 			if (reply_code == HTTP_OK) {
23039beb93cSSam Leffler 				wpa_printf(MSG_DEBUG,
23139beb93cSSam Leffler 					   "WPS UPnP: Got event reply OK from "
23239beb93cSSam Leffler 					   "%s", e->addr->domain_and_port);
23339beb93cSSam Leffler 				event_delete(e);
23439beb93cSSam Leffler 				goto send_more;
23539beb93cSSam Leffler 			} else {
23639beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "WPS UPnP: Got event "
23739beb93cSSam Leffler 					   "error reply code %d from %s",
23839beb93cSSam Leffler 					   reply_code,
23939beb93cSSam Leffler 					   e->addr->domain_and_port);
24039beb93cSSam Leffler 				goto bad;
24139beb93cSSam Leffler 			}
24239beb93cSSam Leffler 		} else {
24339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS UPnP: Got bogus event "
24439beb93cSSam Leffler 				   "response %d from %s", en,
24539beb93cSSam Leffler 				   e->addr->domain_and_port);
24639beb93cSSam Leffler 		}
24739beb93cSSam Leffler 	} else {
24839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS UPnP: Event response timeout/fail "
24939beb93cSSam Leffler 			   "for %s", e->addr->domain_and_port);
25039beb93cSSam Leffler 		goto bad;
25139beb93cSSam Leffler 	}
25239beb93cSSam Leffler 	event_retry(e, 1);
25339beb93cSSam Leffler 	goto send_more;
25439beb93cSSam Leffler 
25539beb93cSSam Leffler send_more:
25639beb93cSSam Leffler 	/* Schedule sending more if there is more to send */
25739beb93cSSam Leffler 	if (s->event_queue)
25839beb93cSSam Leffler 		event_send_all_later(sm);
25939beb93cSSam Leffler 	return;
26039beb93cSSam Leffler 
26139beb93cSSam Leffler bad:
26239beb93cSSam Leffler 	/*
26339beb93cSSam Leffler 	 * If other side doesn't like what we say, forget about them.
26439beb93cSSam Leffler 	 * (There is no way to tell other side that we are dropping
26539beb93cSSam Leffler 	 * them...).
26639beb93cSSam Leffler 	 * Alternately, we could just do event_delete(e)
26739beb93cSSam Leffler 	 */
26839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to errors");
26939beb93cSSam Leffler 	subscription_unlink(s);
27039beb93cSSam Leffler 	subscription_destroy(s);
27139beb93cSSam Leffler }
27239beb93cSSam Leffler 
27339beb93cSSam Leffler 
27439beb93cSSam Leffler /* event_send_tx_ready -- actually write event message
27539beb93cSSam Leffler  *
27639beb93cSSam Leffler  * Prequisite: subscription socket descriptor has become ready to
27739beb93cSSam Leffler  * write (because connection to subscriber has been made).
27839beb93cSSam Leffler  *
27939beb93cSSam Leffler  * It is also possible that we are called because the connect has failed;
28039beb93cSSam Leffler  * it is possible to test for this, or we can just go ahead and then
28139beb93cSSam Leffler  * the write will fail.
28239beb93cSSam Leffler  */
28339beb93cSSam Leffler static void event_send_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
28439beb93cSSam Leffler {
28539beb93cSSam Leffler 	struct wps_event_ *e = sock_ctx;
28639beb93cSSam Leffler 	struct subscription *s = e->s;
28739beb93cSSam Leffler 	struct wpabuf *buf;
28839beb93cSSam Leffler 	char *b;
28939beb93cSSam Leffler 
29039beb93cSSam Leffler 	assert(e == s->current_event);
29139beb93cSSam Leffler 	assert(e->sd == sock);
29239beb93cSSam Leffler 
29339beb93cSSam Leffler 	buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
29439beb93cSSam Leffler 	if (buf == NULL) {
29539beb93cSSam Leffler 		event_retry(e, 0);
29639beb93cSSam Leffler 		goto bad;
29739beb93cSSam Leffler 	}
29839beb93cSSam Leffler 	wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
29939beb93cSSam Leffler 	wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
30039beb93cSSam Leffler 	wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
30139beb93cSSam Leffler 	wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
30239beb93cSSam Leffler 		       "NT: upnp:event\r\n"
30339beb93cSSam Leffler 		       "NTS: upnp:propchange\r\n");
30439beb93cSSam Leffler 	wpabuf_put_str(buf, "SID: uuid:");
30539beb93cSSam Leffler 	b = wpabuf_put(buf, 0);
30639beb93cSSam Leffler 	uuid_bin2str(s->uuid, b, 80);
30739beb93cSSam Leffler 	wpabuf_put(buf, os_strlen(b));
30839beb93cSSam Leffler 	wpabuf_put_str(buf, "\r\n");
30939beb93cSSam Leffler 	wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
31039beb93cSSam Leffler 	wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
31139beb93cSSam Leffler 		      (int) wpabuf_len(e->data));
31239beb93cSSam Leffler 	wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
31339beb93cSSam Leffler 	wpabuf_put_buf(buf, e->data);
31439beb93cSSam Leffler 
31539beb93cSSam Leffler 	/* Since the message size is pretty small, we should be
31639beb93cSSam Leffler 	 * able to get the operating system to buffer what we give it
31739beb93cSSam Leffler 	 * and not have to come back again later to write more...
31839beb93cSSam Leffler 	 */
31939beb93cSSam Leffler #if 0
32039beb93cSSam Leffler 	/* we could: Turn blocking back on? */
32139beb93cSSam Leffler 	fcntl(e->sd, F_SETFL, 0);
32239beb93cSSam Leffler #endif
32339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS UPnP: Sending event to %s",
32439beb93cSSam Leffler 		   e->addr->domain_and_port);
32539beb93cSSam Leffler 	if (send_wpabuf(e->sd, buf) < 0) {
32639beb93cSSam Leffler 		event_retry(e, 1);
32739beb93cSSam Leffler 		goto bad;
32839beb93cSSam Leffler 	}
32939beb93cSSam Leffler 	wpabuf_free(buf);
33039beb93cSSam Leffler 	buf = NULL;
33139beb93cSSam Leffler 
33239beb93cSSam Leffler 	if (e->sd_registered) {
33339beb93cSSam Leffler 		e->sd_registered = 0;
33439beb93cSSam Leffler 		eloop_unregister_sock(e->sd, EVENT_TYPE_WRITE);
33539beb93cSSam Leffler 	}
33639beb93cSSam Leffler 	/* Set up to read the reply */
33739beb93cSSam Leffler 	e->hread = httpread_create(e->sd, event_got_response_handler,
33839beb93cSSam Leffler 				   e /* cookie */,
33939beb93cSSam Leffler 				   0 /* no data expected */,
34039beb93cSSam Leffler 				   EVENT_TIMEOUT_SEC);
34139beb93cSSam Leffler 	if (e->hread == NULL) {
34239beb93cSSam Leffler 		wpa_printf(MSG_ERROR, "WPS UPnP: httpread_create failed");
34339beb93cSSam Leffler 		event_retry(e, 0);
34439beb93cSSam Leffler 		goto bad;
34539beb93cSSam Leffler 	}
34639beb93cSSam Leffler 	return;
34739beb93cSSam Leffler 
34839beb93cSSam Leffler bad:
34939beb93cSSam Leffler 	/* Schedule sending more if there is more to send */
35039beb93cSSam Leffler 	if (s->event_queue)
35139beb93cSSam Leffler 		event_send_all_later(s->sm);
35239beb93cSSam Leffler 	wpabuf_free(buf);
35339beb93cSSam Leffler }
35439beb93cSSam Leffler 
35539beb93cSSam Leffler 
35639beb93cSSam Leffler /* event_send_start -- prepare to send a event message to subscriber
35739beb93cSSam Leffler  *
35839beb93cSSam Leffler  * This gets complicated because:
35939beb93cSSam Leffler  * -- The message is sent via TCP and we have to keep the stream open
36039beb93cSSam Leffler  *      for 30 seconds to get a response... then close it.
36139beb93cSSam Leffler  * -- But we might have other event happen in the meantime...
36239beb93cSSam Leffler  *      we have to queue them, if we lose them then the subscriber will
36339beb93cSSam Leffler  *      be forced to unsubscribe and subscribe again.
36439beb93cSSam Leffler  * -- If multiple URLs are provided then we are supposed to try successive
36539beb93cSSam Leffler  *      ones after 30 second timeout.
36639beb93cSSam Leffler  * -- The URLs might use domain names instead of dotted decimal addresses,
36739beb93cSSam Leffler  *      and resolution of those may cause unwanted sleeping.
36839beb93cSSam Leffler  * -- Doing the initial TCP connect can take a while, so we have to come
36939beb93cSSam Leffler  *      back after connection and then send the data.
37039beb93cSSam Leffler  *
37139beb93cSSam Leffler  * Returns nonzero on error;
37239beb93cSSam Leffler  *
37339beb93cSSam Leffler  * Prerequisite: No current event send (s->current_event == NULL)
37439beb93cSSam Leffler  *      and non-empty queue.
37539beb93cSSam Leffler  */
37639beb93cSSam Leffler static int event_send_start(struct subscription *s)
37739beb93cSSam Leffler {
37839beb93cSSam Leffler 	struct wps_event_ *e;
37939beb93cSSam Leffler 	int itry;
38039beb93cSSam Leffler 
38139beb93cSSam Leffler 	/*
38239beb93cSSam Leffler 	 * Assume we are called ONLY with no current event and ONLY with
38339beb93cSSam Leffler 	 * nonempty event queue and ONLY with at least one address to send to.
38439beb93cSSam Leffler 	 */
38539beb93cSSam Leffler 	assert(s->addr_list != NULL);
38639beb93cSSam Leffler 	assert(s->current_event == NULL);
38739beb93cSSam Leffler 	assert(s->event_queue != NULL);
38839beb93cSSam Leffler 
38939beb93cSSam Leffler 	s->current_event = e = event_dequeue(s);
39039beb93cSSam Leffler 
39139beb93cSSam Leffler 	/* Use address acc. to no. of retries */
39239beb93cSSam Leffler 	e->addr = s->addr_list;
39339beb93cSSam Leffler 	for (itry = 0; itry < e->retry; itry++)
39439beb93cSSam Leffler 		e->addr = e->addr->next;
39539beb93cSSam Leffler 
39639beb93cSSam Leffler 	e->sd = socket(AF_INET, SOCK_STREAM, 0);
39739beb93cSSam Leffler 	if (e->sd < 0) {
39839beb93cSSam Leffler 		event_retry(e, 0);
39939beb93cSSam Leffler 		return -1;
40039beb93cSSam Leffler 	}
40139beb93cSSam Leffler 	/* set non-blocking so we don't sleep waiting for connection */
40239beb93cSSam Leffler 	if (fcntl(e->sd, F_SETFL, O_NONBLOCK) != 0) {
40339beb93cSSam Leffler 		event_retry(e, 0);
40439beb93cSSam Leffler 		return -1;
40539beb93cSSam Leffler 	}
40639beb93cSSam Leffler 	/*
40739beb93cSSam Leffler 	 * Start the connect. It might succeed immediately but more likely will
40839beb93cSSam Leffler 	 * return errno EINPROGRESS.
40939beb93cSSam Leffler 	 */
41039beb93cSSam Leffler 	if (connect(e->sd, (struct sockaddr *) &e->addr->saddr,
41139beb93cSSam Leffler 		    sizeof(e->addr->saddr))) {
41239beb93cSSam Leffler 		if (errno != EINPROGRESS) {
41339beb93cSSam Leffler 			event_retry(e, 1);
41439beb93cSSam Leffler 			return -1;
41539beb93cSSam Leffler 		}
41639beb93cSSam Leffler 	}
41739beb93cSSam Leffler 	/* Call back when ready for writing (or on failure...). */
41839beb93cSSam Leffler 	if (eloop_register_sock(e->sd, EVENT_TYPE_WRITE, event_send_tx_ready,
41939beb93cSSam Leffler 				NULL, e)) {
42039beb93cSSam Leffler 		event_retry(e, 0);
42139beb93cSSam Leffler 		return -1;
42239beb93cSSam Leffler 	}
42339beb93cSSam Leffler 	e->sd_registered = 1;
42439beb93cSSam Leffler 	/* Don't wait forever! */
42539beb93cSSam Leffler 	if (eloop_register_timeout(EVENT_TIMEOUT_SEC, 0, event_timeout_handler,
42639beb93cSSam Leffler 				   NULL, e)) {
42739beb93cSSam Leffler 		event_retry(e, 0);
42839beb93cSSam Leffler 		return -1;
42939beb93cSSam Leffler 	}
43039beb93cSSam Leffler 	return 0;
43139beb93cSSam Leffler }
43239beb93cSSam Leffler 
43339beb93cSSam Leffler 
43439beb93cSSam Leffler /* event_send_all_later_handler -- actually send events as needed */
43539beb93cSSam Leffler void event_send_all_later_handler(void *eloop_data, void *user_ctx)
43639beb93cSSam Leffler {
43739beb93cSSam Leffler 	struct upnp_wps_device_sm *sm = user_ctx;
43839beb93cSSam Leffler 	struct subscription *s;
43939beb93cSSam Leffler 	struct subscription *s_old;
44039beb93cSSam Leffler 	int nerrors = 0;
44139beb93cSSam Leffler 
44239beb93cSSam Leffler 	sm->event_send_all_queued = 0;
44339beb93cSSam Leffler 	s = sm->subscriptions;
44439beb93cSSam Leffler 	if (s == NULL)
44539beb93cSSam Leffler 		return;
44639beb93cSSam Leffler 	do {
44739beb93cSSam Leffler 		if (s->addr_list == NULL) {
44839beb93cSSam Leffler 			/* if we've given up on all addresses */
44939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPS UPnP: Removing "
45039beb93cSSam Leffler 				   "subscription with no addresses");
45139beb93cSSam Leffler 			s_old = s;
45239beb93cSSam Leffler 			s = s_old->next;
45339beb93cSSam Leffler 			subscription_unlink(s_old);
45439beb93cSSam Leffler 			subscription_destroy(s_old);
45539beb93cSSam Leffler 		} else {
45639beb93cSSam Leffler 			if (s->current_event == NULL /* not busy */ &&
45739beb93cSSam Leffler 			    s->event_queue != NULL /* more to do */) {
45839beb93cSSam Leffler 				if (event_send_start(s))
45939beb93cSSam Leffler 					nerrors++;
46039beb93cSSam Leffler 			}
46139beb93cSSam Leffler 			s = s->next;
46239beb93cSSam Leffler 		}
46339beb93cSSam Leffler 	} while (sm->subscriptions != NULL && s != sm->subscriptions);
46439beb93cSSam Leffler 
46539beb93cSSam Leffler 	if (nerrors) {
46639beb93cSSam Leffler 		/* Try again later */
46739beb93cSSam Leffler 		event_send_all_later(sm);
46839beb93cSSam Leffler 	}
46939beb93cSSam Leffler }
47039beb93cSSam Leffler 
47139beb93cSSam Leffler 
47239beb93cSSam Leffler /* event_send_all_later -- schedule sending events to all subscribers
47339beb93cSSam Leffler  * that need it.
47439beb93cSSam Leffler  * This avoids two problems:
47539beb93cSSam Leffler  * -- After getting a subscription, we should not send the first event
47639beb93cSSam Leffler  *      until after our reply is fully queued to be sent back,
47739beb93cSSam Leffler  * -- Possible stack depth or infinite recursion issues.
47839beb93cSSam Leffler  */
47939beb93cSSam Leffler void event_send_all_later(struct upnp_wps_device_sm *sm)
48039beb93cSSam Leffler {
48139beb93cSSam Leffler 	/*
48239beb93cSSam Leffler 	 * The exact time in the future isn't too important. Waiting a bit
48339beb93cSSam Leffler 	 * might let us do several together.
48439beb93cSSam Leffler 	 */
48539beb93cSSam Leffler 	if (sm->event_send_all_queued)
48639beb93cSSam Leffler 		return;
48739beb93cSSam Leffler 	sm->event_send_all_queued = 1;
48839beb93cSSam Leffler 	eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
48939beb93cSSam Leffler 			       event_send_all_later_handler, NULL, sm);
49039beb93cSSam Leffler }
49139beb93cSSam Leffler 
49239beb93cSSam Leffler 
49339beb93cSSam Leffler /* event_send_stop_all -- cleanup */
49439beb93cSSam Leffler void event_send_stop_all(struct upnp_wps_device_sm *sm)
49539beb93cSSam Leffler {
49639beb93cSSam Leffler 	if (sm->event_send_all_queued)
49739beb93cSSam Leffler 		eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
49839beb93cSSam Leffler 	sm->event_send_all_queued = 0;
49939beb93cSSam Leffler }
50039beb93cSSam Leffler 
50139beb93cSSam Leffler 
50239beb93cSSam Leffler /**
50339beb93cSSam Leffler  * event_add - Add a new event to a queue
50439beb93cSSam Leffler  * @s: Subscription
50539beb93cSSam Leffler  * @data: Event data (is copied; caller retains ownership)
50639beb93cSSam Leffler  * Returns: 0 on success, 1 on error
50739beb93cSSam Leffler  */
50839beb93cSSam Leffler int event_add(struct subscription *s, const struct wpabuf *data)
50939beb93cSSam Leffler {
51039beb93cSSam Leffler 	struct wps_event_ *e;
51139beb93cSSam Leffler 
51239beb93cSSam Leffler 	if (s->n_queue >= MAX_EVENTS_QUEUED) {
51339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
51439beb93cSSam Leffler 			   "subscriber");
51539beb93cSSam Leffler 		return 1;
51639beb93cSSam Leffler 	}
51739beb93cSSam Leffler 
51839beb93cSSam Leffler 	e = os_zalloc(sizeof(*e));
51939beb93cSSam Leffler 	if (e == NULL)
52039beb93cSSam Leffler 		return 1;
52139beb93cSSam Leffler 	e->s = s;
52239beb93cSSam Leffler 	e->sd = -1;
52339beb93cSSam Leffler 	e->data = wpabuf_dup(data);
52439beb93cSSam Leffler 	if (e->data == NULL) {
52539beb93cSSam Leffler 		os_free(e);
52639beb93cSSam Leffler 		return 1;
52739beb93cSSam Leffler 	}
52839beb93cSSam Leffler 	e->subscriber_sequence = s->next_subscriber_sequence++;
52939beb93cSSam Leffler 	if (s->next_subscriber_sequence == 0)
53039beb93cSSam Leffler 		s->next_subscriber_sequence++;
53139beb93cSSam Leffler 	event_enqueue_at_end(s, e);
53239beb93cSSam Leffler 	event_send_all_later(s->sm);
53339beb93cSSam Leffler 	return 0;
53439beb93cSSam Leffler }
535