xref: /freebsd/contrib/wpa/src/wps/wps_upnp_event.c (revision c1d255d3ffdbe447de3ab875bf4e7d7accc5bfc5)
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
6f05cddf9SRui Paulo  * Copyright (c) 2009-2010, 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 
1439beb93cSSam Leffler #include "common.h"
1539beb93cSSam Leffler #include "eloop.h"
1639beb93cSSam Leffler #include "uuid.h"
17e28a4053SRui Paulo #include "http_client.h"
1839beb93cSSam Leffler #include "wps_defs.h"
1939beb93cSSam Leffler #include "wps_upnp.h"
2039beb93cSSam Leffler #include "wps_upnp_i.h"
2139beb93cSSam Leffler 
2239beb93cSSam Leffler /*
2339beb93cSSam Leffler  * Event message generation (to subscribers)
2439beb93cSSam Leffler  *
2539beb93cSSam Leffler  * We make a separate copy for each message for each subscriber. This memory
2639beb93cSSam Leffler  * wasted could be limited (adding code complexity) by sharing copies, keeping
2739beb93cSSam Leffler  * a usage count and freeing when zero.
2839beb93cSSam Leffler  *
2939beb93cSSam Leffler  * Sending a message requires using a HTTP over TCP NOTIFY
3039beb93cSSam Leffler  * (like a PUT) which requires a number of states..
3139beb93cSSam Leffler  */
3239beb93cSSam Leffler 
3339beb93cSSam Leffler #define MAX_EVENTS_QUEUED 20   /* How far behind queued events */
34f05cddf9SRui Paulo #define MAX_FAILURES 10 /* Drop subscription after this many failures */
3539beb93cSSam Leffler 
3639beb93cSSam Leffler /* How long to wait before sending event */
3739beb93cSSam Leffler #define EVENT_DELAY_SECONDS 0
3839beb93cSSam Leffler #define EVENT_DELAY_MSEC 0
3939beb93cSSam Leffler 
4039beb93cSSam Leffler /*
4139beb93cSSam Leffler  * Event information that we send to each subscriber is remembered in this
4239beb93cSSam Leffler  * struct. The event cannot be sent by simple UDP; it has to be sent by a HTTP
4339beb93cSSam Leffler  * over TCP transaction which requires various states.. It may also need to be
4439beb93cSSam Leffler  * retried at a different address (if more than one is available).
4539beb93cSSam Leffler  *
4639beb93cSSam Leffler  * TODO: As an optimization we could share data between subscribers.
4739beb93cSSam Leffler  */
4839beb93cSSam Leffler struct wps_event_ {
49e28a4053SRui Paulo 	struct dl_list list;
5039beb93cSSam Leffler 	struct subscription *s;         /* parent */
5139beb93cSSam Leffler 	unsigned subscriber_sequence;   /* which event for this subscription*/
52e28a4053SRui Paulo 	unsigned int retry;             /* which retry */
5339beb93cSSam Leffler 	struct subscr_addr *addr;       /* address to connect to */
5439beb93cSSam Leffler 	struct wpabuf *data;            /* event data to send */
55e28a4053SRui Paulo 	struct http_client *http_event;
5639beb93cSSam Leffler };
5739beb93cSSam Leffler 
5839beb93cSSam Leffler 
5939beb93cSSam Leffler /* event_clean -- clean sockets etc. of event
6039beb93cSSam Leffler  * Leaves data, retry count etc. alone.
6139beb93cSSam Leffler  */
event_clean(struct wps_event_ * e)6239beb93cSSam Leffler static void event_clean(struct wps_event_ *e)
6339beb93cSSam Leffler {
64e28a4053SRui Paulo 	if (e->s->current_event == e)
6539beb93cSSam Leffler 		e->s->current_event = NULL;
66e28a4053SRui Paulo 	http_client_free(e->http_event);
67e28a4053SRui Paulo 	e->http_event = NULL;
6839beb93cSSam Leffler }
6939beb93cSSam Leffler 
7039beb93cSSam Leffler 
7139beb93cSSam Leffler /* event_delete -- delete single unqueued event
7239beb93cSSam Leffler  * (be sure to dequeue first if need be)
7339beb93cSSam Leffler  */
event_delete(struct wps_event_ * e)743157ba21SRui Paulo static void event_delete(struct wps_event_ *e)
7539beb93cSSam Leffler {
76f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS UPnP: Delete event %p", e);
7739beb93cSSam Leffler 	event_clean(e);
7839beb93cSSam Leffler 	wpabuf_free(e->data);
7939beb93cSSam Leffler 	os_free(e);
8039beb93cSSam Leffler }
8139beb93cSSam Leffler 
8239beb93cSSam Leffler 
8339beb93cSSam Leffler /* event_dequeue -- get next event from the queue
8439beb93cSSam Leffler  * Returns NULL if empty.
8539beb93cSSam Leffler  */
event_dequeue(struct subscription * s)8639beb93cSSam Leffler static struct wps_event_ *event_dequeue(struct subscription *s)
8739beb93cSSam Leffler {
88e28a4053SRui Paulo 	struct wps_event_ *e;
89e28a4053SRui Paulo 	e = dl_list_first(&s->event_queue, struct wps_event_, list);
90f05cddf9SRui Paulo 	if (e) {
91f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for "
92f05cddf9SRui Paulo 			   "subscription %p", e, s);
93e28a4053SRui Paulo 		dl_list_del(&e->list);
94f05cddf9SRui Paulo 	}
9539beb93cSSam Leffler 	return e;
9639beb93cSSam Leffler }
9739beb93cSSam Leffler 
9839beb93cSSam Leffler 
99*c1d255d3SCy Schubert /* wps_upnp_event_delete_all -- delete entire event queue and current event */
wps_upnp_event_delete_all(struct subscription * s)100*c1d255d3SCy Schubert void wps_upnp_event_delete_all(struct subscription *s)
10139beb93cSSam Leffler {
10239beb93cSSam Leffler 	struct wps_event_ *e;
10339beb93cSSam Leffler 	while ((e = event_dequeue(s)) != NULL)
10439beb93cSSam Leffler 		event_delete(e);
10539beb93cSSam Leffler 	if (s->current_event) {
10639beb93cSSam Leffler 		event_delete(s->current_event);
10739beb93cSSam Leffler 		/* will set: s->current_event = NULL;  */
10839beb93cSSam Leffler 	}
10939beb93cSSam Leffler }
11039beb93cSSam Leffler 
11139beb93cSSam Leffler 
11239beb93cSSam Leffler /**
11339beb93cSSam Leffler  * event_retry - Called when we had a failure delivering event msg
11439beb93cSSam Leffler  * @e: Event
11539beb93cSSam Leffler  * @do_next_address: skip address e.g. on connect fail
11639beb93cSSam Leffler  */
event_retry(struct wps_event_ * e,int do_next_address)11739beb93cSSam Leffler static void event_retry(struct wps_event_ *e, int do_next_address)
11839beb93cSSam Leffler {
11939beb93cSSam Leffler 	struct subscription *s = e->s;
12039beb93cSSam Leffler 	struct upnp_wps_device_sm *sm = s->sm;
12139beb93cSSam Leffler 
122f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p",
123f05cddf9SRui Paulo 		   e, s);
12439beb93cSSam Leffler 	event_clean(e);
12539beb93cSSam Leffler 	/* will set: s->current_event = NULL; */
12639beb93cSSam Leffler 
127f05cddf9SRui Paulo 	if (do_next_address) {
12839beb93cSSam Leffler 		e->retry++;
129f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry);
130f05cddf9SRui Paulo 	}
131e28a4053SRui Paulo 	if (e->retry >= dl_list_len(&s->addr_list)) {
13239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
13339beb93cSSam Leffler 			   "for %s", e->addr->domain_and_port);
134f05cddf9SRui Paulo 		event_delete(e);
135f05cddf9SRui Paulo 		s->last_event_failed = 1;
136f05cddf9SRui Paulo 		if (!dl_list_empty(&s->event_queue))
137*c1d255d3SCy Schubert 			wps_upnp_event_send_all_later(s->sm);
13839beb93cSSam Leffler 		return;
13939beb93cSSam Leffler 	}
140e28a4053SRui Paulo 	dl_list_add(&s->event_queue, &e->list);
141*c1d255d3SCy Schubert 	wps_upnp_event_send_all_later(sm);
14239beb93cSSam Leffler }
14339beb93cSSam Leffler 
14439beb93cSSam Leffler 
event_build_message(struct wps_event_ * e)145e28a4053SRui Paulo static struct wpabuf * event_build_message(struct wps_event_ *e)
14639beb93cSSam Leffler {
14739beb93cSSam Leffler 	struct wpabuf *buf;
14839beb93cSSam Leffler 	char *b;
14939beb93cSSam Leffler 
150b266d2f2SCy Schubert 	buf = wpabuf_alloc(1000 + os_strlen(e->addr->path) +
151b266d2f2SCy Schubert 			   wpabuf_len(e->data));
152e28a4053SRui Paulo 	if (buf == NULL)
153e28a4053SRui Paulo 		return NULL;
15439beb93cSSam Leffler 	wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
15539beb93cSSam Leffler 	wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
15639beb93cSSam Leffler 	wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
15739beb93cSSam Leffler 	wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
15839beb93cSSam Leffler 		       "NT: upnp:event\r\n"
15939beb93cSSam Leffler 		       "NTS: upnp:propchange\r\n");
16039beb93cSSam Leffler 	wpabuf_put_str(buf, "SID: uuid:");
16139beb93cSSam Leffler 	b = wpabuf_put(buf, 0);
162e28a4053SRui Paulo 	uuid_bin2str(e->s->uuid, b, 80);
16339beb93cSSam Leffler 	wpabuf_put(buf, os_strlen(b));
16439beb93cSSam Leffler 	wpabuf_put_str(buf, "\r\n");
16539beb93cSSam Leffler 	wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
16639beb93cSSam Leffler 	wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
16739beb93cSSam Leffler 		      (int) wpabuf_len(e->data));
16839beb93cSSam Leffler 	wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
16939beb93cSSam Leffler 	wpabuf_put_buf(buf, e->data);
170e28a4053SRui Paulo 	return buf;
17139beb93cSSam Leffler }
17239beb93cSSam Leffler 
17339beb93cSSam Leffler 
event_addr_failure(struct wps_event_ * e)174f05cddf9SRui Paulo static void event_addr_failure(struct wps_event_ *e)
175f05cddf9SRui Paulo {
176f05cddf9SRui Paulo 	struct subscription *s = e->s;
177f05cddf9SRui Paulo 
178f05cddf9SRui Paulo 	e->addr->num_failures++;
179f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s "
180f05cddf9SRui Paulo 		   "(num_failures=%u)",
181f05cddf9SRui Paulo 		   e, e->addr->domain_and_port, e->addr->num_failures);
182f05cddf9SRui Paulo 
183f05cddf9SRui Paulo 	if (e->addr->num_failures < MAX_FAILURES) {
184f05cddf9SRui Paulo 		/* Try other addresses, if available */
185f05cddf9SRui Paulo 		event_retry(e, 1);
186f05cddf9SRui Paulo 		return;
187f05cddf9SRui Paulo 	}
188f05cddf9SRui Paulo 
189f05cddf9SRui Paulo 	/*
190f05cddf9SRui Paulo 	 * If other side doesn't like what we say, forget about them.
191f05cddf9SRui Paulo 	 * (There is no way to tell other side that we are dropping them...).
192f05cddf9SRui Paulo 	 */
193f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription %p "
194f05cddf9SRui Paulo 		   "address %s due to errors", s, e->addr->domain_and_port);
195f05cddf9SRui Paulo 	dl_list_del(&e->addr->list);
196f05cddf9SRui Paulo 	subscr_addr_delete(e->addr);
197f05cddf9SRui Paulo 	e->addr = NULL;
198f05cddf9SRui Paulo 
199f05cddf9SRui Paulo 	if (dl_list_empty(&s->addr_list)) {
200f05cddf9SRui Paulo 		/* if we've given up on all addresses */
201f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS UPnP: Removing subscription %p "
202f05cddf9SRui Paulo 			   "with no addresses", s);
203f05cddf9SRui Paulo 		dl_list_del(&s->list);
204f05cddf9SRui Paulo 		subscription_destroy(s);
205f05cddf9SRui Paulo 		return;
206f05cddf9SRui Paulo 	}
207f05cddf9SRui Paulo 
208f05cddf9SRui Paulo 	/* Try other addresses, if available */
209f05cddf9SRui Paulo 	event_retry(e, 0);
210f05cddf9SRui Paulo }
211f05cddf9SRui Paulo 
212f05cddf9SRui Paulo 
event_http_cb(void * ctx,struct http_client * c,enum http_client_event event)213e28a4053SRui Paulo static void event_http_cb(void *ctx, struct http_client *c,
214e28a4053SRui Paulo 			  enum http_client_event event)
215e28a4053SRui Paulo {
216e28a4053SRui Paulo 	struct wps_event_ *e = ctx;
217e28a4053SRui Paulo 	struct subscription *s = e->s;
218e28a4053SRui Paulo 
219f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p "
220f05cddf9SRui Paulo 		   "event=%d", e, c, event);
221e28a4053SRui Paulo 	switch (event) {
222e28a4053SRui Paulo 	case HTTP_CLIENT_OK:
223e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG,
224f05cddf9SRui Paulo 			   "WPS UPnP: Got event %p reply OK from %s",
225f05cddf9SRui Paulo 			   e, e->addr->domain_and_port);
226f05cddf9SRui Paulo 		e->addr->num_failures = 0;
227f05cddf9SRui Paulo 		s->last_event_failed = 0;
228e28a4053SRui Paulo 		event_delete(e);
229e28a4053SRui Paulo 
23039beb93cSSam Leffler 		/* Schedule sending more if there is more to send */
231e28a4053SRui Paulo 		if (!dl_list_empty(&s->event_queue))
232*c1d255d3SCy Schubert 			wps_upnp_event_send_all_later(s->sm);
233e28a4053SRui Paulo 		break;
234e28a4053SRui Paulo 	case HTTP_CLIENT_FAILED:
235f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure");
236f05cddf9SRui Paulo 		event_addr_failure(e);
237f05cddf9SRui Paulo 		break;
238e28a4053SRui Paulo 	case HTTP_CLIENT_INVALID_REPLY:
239f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid reply");
240f05cddf9SRui Paulo 		event_addr_failure(e);
241e28a4053SRui Paulo 		break;
242e28a4053SRui Paulo 	case HTTP_CLIENT_TIMEOUT:
243e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
244f05cddf9SRui Paulo 		event_addr_failure(e);
245f05cddf9SRui Paulo 		break;
246e28a4053SRui Paulo 	}
24739beb93cSSam Leffler }
24839beb93cSSam Leffler 
24939beb93cSSam Leffler 
25039beb93cSSam Leffler /* event_send_start -- prepare to send a event message to subscriber
25139beb93cSSam Leffler  *
25239beb93cSSam Leffler  * This gets complicated because:
25339beb93cSSam Leffler  * -- The message is sent via TCP and we have to keep the stream open
25439beb93cSSam Leffler  *      for 30 seconds to get a response... then close it.
25539beb93cSSam Leffler  * -- But we might have other event happen in the meantime...
25639beb93cSSam Leffler  *      we have to queue them, if we lose them then the subscriber will
25739beb93cSSam Leffler  *      be forced to unsubscribe and subscribe again.
25839beb93cSSam Leffler  * -- If multiple URLs are provided then we are supposed to try successive
25939beb93cSSam Leffler  *      ones after 30 second timeout.
26039beb93cSSam Leffler  * -- The URLs might use domain names instead of dotted decimal addresses,
26139beb93cSSam Leffler  *      and resolution of those may cause unwanted sleeping.
26239beb93cSSam Leffler  * -- Doing the initial TCP connect can take a while, so we have to come
26339beb93cSSam Leffler  *      back after connection and then send the data.
26439beb93cSSam Leffler  *
26539beb93cSSam Leffler  * Returns nonzero on error;
26639beb93cSSam Leffler  *
26739beb93cSSam Leffler  * Prerequisite: No current event send (s->current_event == NULL)
26839beb93cSSam Leffler  *      and non-empty queue.
26939beb93cSSam Leffler  */
event_send_start(struct subscription * s)27039beb93cSSam Leffler static int event_send_start(struct subscription *s)
27139beb93cSSam Leffler {
27239beb93cSSam Leffler 	struct wps_event_ *e;
273e28a4053SRui Paulo 	unsigned int itry;
274e28a4053SRui Paulo 	struct wpabuf *buf;
27539beb93cSSam Leffler 
27639beb93cSSam Leffler 	/*
27739beb93cSSam Leffler 	 * Assume we are called ONLY with no current event and ONLY with
27839beb93cSSam Leffler 	 * nonempty event queue and ONLY with at least one address to send to.
27939beb93cSSam Leffler 	 */
280325151a3SRui Paulo 	if (dl_list_empty(&s->addr_list) ||
281325151a3SRui Paulo 	    s->current_event ||
282325151a3SRui Paulo 	    dl_list_empty(&s->event_queue))
283f05cddf9SRui Paulo 		return -1;
28439beb93cSSam Leffler 
28539beb93cSSam Leffler 	s->current_event = e = event_dequeue(s);
28639beb93cSSam Leffler 
287e28a4053SRui Paulo 	/* Use address according to number of retries */
288e28a4053SRui Paulo 	itry = 0;
289e28a4053SRui Paulo 	dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list)
290e28a4053SRui Paulo 		if (itry++ == e->retry)
291e28a4053SRui Paulo 			break;
292e28a4053SRui Paulo 	if (itry < e->retry)
293e28a4053SRui Paulo 		return -1;
29439beb93cSSam Leffler 
295e28a4053SRui Paulo 	buf = event_build_message(e);
296e28a4053SRui Paulo 	if (buf == NULL) {
2977d748adcSCy Schubert 		event_addr_failure(e);
29839beb93cSSam Leffler 		return -1;
29939beb93cSSam Leffler 	}
300e28a4053SRui Paulo 
301e28a4053SRui Paulo 	e->http_event = http_client_addr(&e->addr->saddr, buf, 0,
302e28a4053SRui Paulo 					 event_http_cb, e);
303e28a4053SRui Paulo 	if (e->http_event == NULL) {
304e28a4053SRui Paulo 		wpabuf_free(buf);
3057d748adcSCy Schubert 		event_addr_failure(e);
30639beb93cSSam Leffler 		return -1;
30739beb93cSSam Leffler 	}
308e28a4053SRui Paulo 
30939beb93cSSam Leffler 	return 0;
31039beb93cSSam Leffler }
31139beb93cSSam Leffler 
31239beb93cSSam Leffler 
31339beb93cSSam Leffler /* event_send_all_later_handler -- actually send events as needed */
event_send_all_later_handler(void * eloop_data,void * user_ctx)3143157ba21SRui Paulo static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
31539beb93cSSam Leffler {
31639beb93cSSam Leffler 	struct upnp_wps_device_sm *sm = user_ctx;
317e28a4053SRui Paulo 	struct subscription *s, *tmp;
31839beb93cSSam Leffler 	int nerrors = 0;
31939beb93cSSam Leffler 
32039beb93cSSam Leffler 	sm->event_send_all_queued = 0;
321e28a4053SRui Paulo 	dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
322e28a4053SRui Paulo 			      list) {
32339beb93cSSam Leffler 		if (s->current_event == NULL /* not busy */ &&
324e28a4053SRui Paulo 		    !dl_list_empty(&s->event_queue) /* more to do */) {
32539beb93cSSam Leffler 			if (event_send_start(s))
32639beb93cSSam Leffler 				nerrors++;
32739beb93cSSam Leffler 		}
32839beb93cSSam Leffler 	}
32939beb93cSSam Leffler 
33039beb93cSSam Leffler 	if (nerrors) {
33139beb93cSSam Leffler 		/* Try again later */
332*c1d255d3SCy Schubert 		wps_upnp_event_send_all_later(sm);
33339beb93cSSam Leffler 	}
33439beb93cSSam Leffler }
33539beb93cSSam Leffler 
33639beb93cSSam Leffler 
337*c1d255d3SCy Schubert /* wps_upnp_event_send_all_later -- schedule sending events to all subscribers
33839beb93cSSam Leffler  * that need it.
33939beb93cSSam Leffler  * This avoids two problems:
34039beb93cSSam Leffler  * -- After getting a subscription, we should not send the first event
34139beb93cSSam Leffler  *      until after our reply is fully queued to be sent back,
34239beb93cSSam Leffler  * -- Possible stack depth or infinite recursion issues.
34339beb93cSSam Leffler  */
wps_upnp_event_send_all_later(struct upnp_wps_device_sm * sm)344*c1d255d3SCy Schubert void wps_upnp_event_send_all_later(struct upnp_wps_device_sm *sm)
34539beb93cSSam Leffler {
34639beb93cSSam Leffler 	/*
34739beb93cSSam Leffler 	 * The exact time in the future isn't too important. Waiting a bit
34839beb93cSSam Leffler 	 * might let us do several together.
34939beb93cSSam Leffler 	 */
35039beb93cSSam Leffler 	if (sm->event_send_all_queued)
35139beb93cSSam Leffler 		return;
35239beb93cSSam Leffler 	sm->event_send_all_queued = 1;
35339beb93cSSam Leffler 	eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
35439beb93cSSam Leffler 			       event_send_all_later_handler, NULL, sm);
35539beb93cSSam Leffler }
35639beb93cSSam Leffler 
35739beb93cSSam Leffler 
358*c1d255d3SCy Schubert /* wps_upnp_event_send_stop_all -- cleanup */
wps_upnp_event_send_stop_all(struct upnp_wps_device_sm * sm)359*c1d255d3SCy Schubert void wps_upnp_event_send_stop_all(struct upnp_wps_device_sm *sm)
36039beb93cSSam Leffler {
36139beb93cSSam Leffler 	if (sm->event_send_all_queued)
36239beb93cSSam Leffler 		eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
36339beb93cSSam Leffler 	sm->event_send_all_queued = 0;
36439beb93cSSam Leffler }
36539beb93cSSam Leffler 
36639beb93cSSam Leffler 
36739beb93cSSam Leffler /**
368*c1d255d3SCy Schubert  * wps_upnp_event_add - Add a new event to a queue
36939beb93cSSam Leffler  * @s: Subscription
37039beb93cSSam Leffler  * @data: Event data (is copied; caller retains ownership)
371f05cddf9SRui Paulo  * @probereq: Whether this is a Probe Request event
372f05cddf9SRui Paulo  * Returns: 0 on success, -1 on error, 1 on max event queue limit reached
37339beb93cSSam Leffler  */
wps_upnp_event_add(struct subscription * s,const struct wpabuf * data,int probereq)374*c1d255d3SCy Schubert int wps_upnp_event_add(struct subscription *s, const struct wpabuf *data,
375*c1d255d3SCy Schubert 		       int probereq)
37639beb93cSSam Leffler {
37739beb93cSSam Leffler 	struct wps_event_ *e;
378f05cddf9SRui Paulo 	unsigned int len;
37939beb93cSSam Leffler 
380f05cddf9SRui Paulo 	len = dl_list_len(&s->event_queue);
381f05cddf9SRui Paulo 	if (len >= MAX_EVENTS_QUEUED) {
38239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
383f05cddf9SRui Paulo 			   "subscriber %p", s);
384f05cddf9SRui Paulo 		if (probereq)
38539beb93cSSam Leffler 			return 1;
386f05cddf9SRui Paulo 
387f05cddf9SRui Paulo 		/* Drop oldest entry to allow EAP event to be stored. */
388f05cddf9SRui Paulo 		e = event_dequeue(s);
389f05cddf9SRui Paulo 		if (!e)
390f05cddf9SRui Paulo 			return 1;
391f05cddf9SRui Paulo 		event_delete(e);
392f05cddf9SRui Paulo 	}
393f05cddf9SRui Paulo 
394f05cddf9SRui Paulo 	if (s->last_event_failed && probereq && len > 0) {
395f05cddf9SRui Paulo 		/*
396f05cddf9SRui Paulo 		 * Avoid queuing frames for subscribers that may have left
397f05cddf9SRui Paulo 		 * without unsubscribing.
398f05cddf9SRui Paulo 		 */
399f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe "
400f05cddf9SRui Paulo 			   "Request frames for subscription %p since last "
401f05cddf9SRui Paulo 			   "delivery failed", s);
402f05cddf9SRui Paulo 		return -1;
40339beb93cSSam Leffler 	}
40439beb93cSSam Leffler 
40539beb93cSSam Leffler 	e = os_zalloc(sizeof(*e));
40639beb93cSSam Leffler 	if (e == NULL)
407f05cddf9SRui Paulo 		return -1;
408e28a4053SRui Paulo 	dl_list_init(&e->list);
40939beb93cSSam Leffler 	e->s = s;
41039beb93cSSam Leffler 	e->data = wpabuf_dup(data);
41139beb93cSSam Leffler 	if (e->data == NULL) {
41239beb93cSSam Leffler 		os_free(e);
413f05cddf9SRui Paulo 		return -1;
41439beb93cSSam Leffler 	}
41539beb93cSSam Leffler 	e->subscriber_sequence = s->next_subscriber_sequence++;
41639beb93cSSam Leffler 	if (s->next_subscriber_sequence == 0)
41739beb93cSSam Leffler 		s->next_subscriber_sequence++;
418f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p "
419f05cddf9SRui Paulo 		   "(queue len %u)", e, s, len + 1);
420e28a4053SRui Paulo 	dl_list_add_tail(&s->event_queue, &e->list);
421*c1d255d3SCy Schubert 	wps_upnp_event_send_all_later(s->sm);
42239beb93cSSam Leffler 	return 0;
42339beb93cSSam Leffler }
424