xref: /freebsd/contrib/wpa/src/wps/wps_upnp_event.c (revision f05cddf940dbfc5b657f5e9beb9de2c31e509e5b)
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
6*f05cddf9SRui 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 */
34*f05cddf9SRui 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  */
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  */
743157ba21SRui Paulo static void event_delete(struct wps_event_ *e)
7539beb93cSSam Leffler {
76*f05cddf9SRui 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  */
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);
90*f05cddf9SRui Paulo 	if (e) {
91*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for "
92*f05cddf9SRui Paulo 			   "subscription %p", e, s);
93e28a4053SRui Paulo 		dl_list_del(&e->list);
94*f05cddf9SRui Paulo 	}
9539beb93cSSam Leffler 	return e;
9639beb93cSSam Leffler }
9739beb93cSSam Leffler 
9839beb93cSSam Leffler 
9939beb93cSSam Leffler /* event_delete_all -- delete entire event queue and current event */
10039beb93cSSam Leffler void 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  */
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 
122*f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p",
123*f05cddf9SRui Paulo 		   e, s);
12439beb93cSSam Leffler 	event_clean(e);
12539beb93cSSam Leffler 	/* will set: s->current_event = NULL; */
12639beb93cSSam Leffler 
127*f05cddf9SRui Paulo 	if (do_next_address) {
12839beb93cSSam Leffler 		e->retry++;
129*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry);
130*f05cddf9SRui 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);
134*f05cddf9SRui Paulo 		event_delete(e);
135*f05cddf9SRui Paulo 		s->last_event_failed = 1;
136*f05cddf9SRui Paulo 		if (!dl_list_empty(&s->event_queue))
137*f05cddf9SRui Paulo 			event_send_all_later(s->sm);
13839beb93cSSam Leffler 		return;
13939beb93cSSam Leffler 	}
140e28a4053SRui Paulo 	dl_list_add(&s->event_queue, &e->list);
14139beb93cSSam Leffler 	event_send_all_later(sm);
14239beb93cSSam Leffler }
14339beb93cSSam Leffler 
14439beb93cSSam Leffler 
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 
15039beb93cSSam Leffler 	buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
151e28a4053SRui Paulo 	if (buf == NULL)
152e28a4053SRui Paulo 		return NULL;
15339beb93cSSam Leffler 	wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
15439beb93cSSam Leffler 	wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
15539beb93cSSam Leffler 	wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
15639beb93cSSam Leffler 	wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
15739beb93cSSam Leffler 		       "NT: upnp:event\r\n"
15839beb93cSSam Leffler 		       "NTS: upnp:propchange\r\n");
15939beb93cSSam Leffler 	wpabuf_put_str(buf, "SID: uuid:");
16039beb93cSSam Leffler 	b = wpabuf_put(buf, 0);
161e28a4053SRui Paulo 	uuid_bin2str(e->s->uuid, b, 80);
16239beb93cSSam Leffler 	wpabuf_put(buf, os_strlen(b));
16339beb93cSSam Leffler 	wpabuf_put_str(buf, "\r\n");
16439beb93cSSam Leffler 	wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
16539beb93cSSam Leffler 	wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
16639beb93cSSam Leffler 		      (int) wpabuf_len(e->data));
16739beb93cSSam Leffler 	wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
16839beb93cSSam Leffler 	wpabuf_put_buf(buf, e->data);
169e28a4053SRui Paulo 	return buf;
17039beb93cSSam Leffler }
17139beb93cSSam Leffler 
17239beb93cSSam Leffler 
173*f05cddf9SRui Paulo static void event_addr_failure(struct wps_event_ *e)
174*f05cddf9SRui Paulo {
175*f05cddf9SRui Paulo 	struct subscription *s = e->s;
176*f05cddf9SRui Paulo 
177*f05cddf9SRui Paulo 	e->addr->num_failures++;
178*f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s "
179*f05cddf9SRui Paulo 		   "(num_failures=%u)",
180*f05cddf9SRui Paulo 		   e, e->addr->domain_and_port, e->addr->num_failures);
181*f05cddf9SRui Paulo 
182*f05cddf9SRui Paulo 	if (e->addr->num_failures < MAX_FAILURES) {
183*f05cddf9SRui Paulo 		/* Try other addresses, if available */
184*f05cddf9SRui Paulo 		event_retry(e, 1);
185*f05cddf9SRui Paulo 		return;
186*f05cddf9SRui Paulo 	}
187*f05cddf9SRui Paulo 
188*f05cddf9SRui Paulo 	/*
189*f05cddf9SRui Paulo 	 * If other side doesn't like what we say, forget about them.
190*f05cddf9SRui Paulo 	 * (There is no way to tell other side that we are dropping them...).
191*f05cddf9SRui Paulo 	 */
192*f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription %p "
193*f05cddf9SRui Paulo 		   "address %s due to errors", s, e->addr->domain_and_port);
194*f05cddf9SRui Paulo 	dl_list_del(&e->addr->list);
195*f05cddf9SRui Paulo 	subscr_addr_delete(e->addr);
196*f05cddf9SRui Paulo 	e->addr = NULL;
197*f05cddf9SRui Paulo 
198*f05cddf9SRui Paulo 	if (dl_list_empty(&s->addr_list)) {
199*f05cddf9SRui Paulo 		/* if we've given up on all addresses */
200*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS UPnP: Removing subscription %p "
201*f05cddf9SRui Paulo 			   "with no addresses", s);
202*f05cddf9SRui Paulo 		dl_list_del(&s->list);
203*f05cddf9SRui Paulo 		subscription_destroy(s);
204*f05cddf9SRui Paulo 		return;
205*f05cddf9SRui Paulo 	}
206*f05cddf9SRui Paulo 
207*f05cddf9SRui Paulo 	/* Try other addresses, if available */
208*f05cddf9SRui Paulo 	event_retry(e, 0);
209*f05cddf9SRui Paulo }
210*f05cddf9SRui Paulo 
211*f05cddf9SRui Paulo 
212e28a4053SRui Paulo static void event_http_cb(void *ctx, struct http_client *c,
213e28a4053SRui Paulo 			  enum http_client_event event)
214e28a4053SRui Paulo {
215e28a4053SRui Paulo 	struct wps_event_ *e = ctx;
216e28a4053SRui Paulo 	struct subscription *s = e->s;
217e28a4053SRui Paulo 
218*f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p "
219*f05cddf9SRui Paulo 		   "event=%d", e, c, event);
220e28a4053SRui Paulo 	switch (event) {
221e28a4053SRui Paulo 	case HTTP_CLIENT_OK:
222e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG,
223*f05cddf9SRui Paulo 			   "WPS UPnP: Got event %p reply OK from %s",
224*f05cddf9SRui Paulo 			   e, e->addr->domain_and_port);
225*f05cddf9SRui Paulo 		e->addr->num_failures = 0;
226*f05cddf9SRui Paulo 		s->last_event_failed = 0;
227e28a4053SRui Paulo 		event_delete(e);
228e28a4053SRui Paulo 
22939beb93cSSam Leffler 		/* Schedule sending more if there is more to send */
230e28a4053SRui Paulo 		if (!dl_list_empty(&s->event_queue))
23139beb93cSSam Leffler 			event_send_all_later(s->sm);
232e28a4053SRui Paulo 		break;
233e28a4053SRui Paulo 	case HTTP_CLIENT_FAILED:
234*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure");
235*f05cddf9SRui Paulo 		event_addr_failure(e);
236*f05cddf9SRui Paulo 		break;
237e28a4053SRui Paulo 	case HTTP_CLIENT_INVALID_REPLY:
238*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid reply");
239*f05cddf9SRui Paulo 		event_addr_failure(e);
240e28a4053SRui Paulo 		break;
241e28a4053SRui Paulo 	case HTTP_CLIENT_TIMEOUT:
242e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
243*f05cddf9SRui Paulo 		event_addr_failure(e);
244*f05cddf9SRui Paulo 		break;
245e28a4053SRui Paulo 	}
24639beb93cSSam Leffler }
24739beb93cSSam Leffler 
24839beb93cSSam Leffler 
24939beb93cSSam Leffler /* event_send_start -- prepare to send a event message to subscriber
25039beb93cSSam Leffler  *
25139beb93cSSam Leffler  * This gets complicated because:
25239beb93cSSam Leffler  * -- The message is sent via TCP and we have to keep the stream open
25339beb93cSSam Leffler  *      for 30 seconds to get a response... then close it.
25439beb93cSSam Leffler  * -- But we might have other event happen in the meantime...
25539beb93cSSam Leffler  *      we have to queue them, if we lose them then the subscriber will
25639beb93cSSam Leffler  *      be forced to unsubscribe and subscribe again.
25739beb93cSSam Leffler  * -- If multiple URLs are provided then we are supposed to try successive
25839beb93cSSam Leffler  *      ones after 30 second timeout.
25939beb93cSSam Leffler  * -- The URLs might use domain names instead of dotted decimal addresses,
26039beb93cSSam Leffler  *      and resolution of those may cause unwanted sleeping.
26139beb93cSSam Leffler  * -- Doing the initial TCP connect can take a while, so we have to come
26239beb93cSSam Leffler  *      back after connection and then send the data.
26339beb93cSSam Leffler  *
26439beb93cSSam Leffler  * Returns nonzero on error;
26539beb93cSSam Leffler  *
26639beb93cSSam Leffler  * Prerequisite: No current event send (s->current_event == NULL)
26739beb93cSSam Leffler  *      and non-empty queue.
26839beb93cSSam Leffler  */
26939beb93cSSam Leffler static int event_send_start(struct subscription *s)
27039beb93cSSam Leffler {
27139beb93cSSam Leffler 	struct wps_event_ *e;
272e28a4053SRui Paulo 	unsigned int itry;
273e28a4053SRui Paulo 	struct wpabuf *buf;
27439beb93cSSam Leffler 
27539beb93cSSam Leffler 	/*
27639beb93cSSam Leffler 	 * Assume we are called ONLY with no current event and ONLY with
27739beb93cSSam Leffler 	 * nonempty event queue and ONLY with at least one address to send to.
27839beb93cSSam Leffler 	 */
279*f05cddf9SRui Paulo 	if (dl_list_empty(&s->addr_list))
280*f05cddf9SRui Paulo 		return -1;
281*f05cddf9SRui Paulo 	if (s->current_event)
282*f05cddf9SRui Paulo 		return -1;
283*f05cddf9SRui Paulo 	if (dl_list_empty(&s->event_queue))
284*f05cddf9SRui Paulo 		return -1;
28539beb93cSSam Leffler 
28639beb93cSSam Leffler 	s->current_event = e = event_dequeue(s);
28739beb93cSSam Leffler 
288e28a4053SRui Paulo 	/* Use address according to number of retries */
289e28a4053SRui Paulo 	itry = 0;
290e28a4053SRui Paulo 	dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list)
291e28a4053SRui Paulo 		if (itry++ == e->retry)
292e28a4053SRui Paulo 			break;
293e28a4053SRui Paulo 	if (itry < e->retry)
294e28a4053SRui Paulo 		return -1;
29539beb93cSSam Leffler 
296e28a4053SRui Paulo 	buf = event_build_message(e);
297e28a4053SRui Paulo 	if (buf == NULL) {
29839beb93cSSam Leffler 		event_retry(e, 0);
29939beb93cSSam Leffler 		return -1;
30039beb93cSSam Leffler 	}
301e28a4053SRui Paulo 
302e28a4053SRui Paulo 	e->http_event = http_client_addr(&e->addr->saddr, buf, 0,
303e28a4053SRui Paulo 					 event_http_cb, e);
304e28a4053SRui Paulo 	if (e->http_event == NULL) {
305e28a4053SRui Paulo 		wpabuf_free(buf);
30639beb93cSSam Leffler 		event_retry(e, 0);
30739beb93cSSam Leffler 		return -1;
30839beb93cSSam Leffler 	}
309e28a4053SRui Paulo 
31039beb93cSSam Leffler 	return 0;
31139beb93cSSam Leffler }
31239beb93cSSam Leffler 
31339beb93cSSam Leffler 
31439beb93cSSam Leffler /* event_send_all_later_handler -- actually send events as needed */
3153157ba21SRui Paulo static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
31639beb93cSSam Leffler {
31739beb93cSSam Leffler 	struct upnp_wps_device_sm *sm = user_ctx;
318e28a4053SRui Paulo 	struct subscription *s, *tmp;
31939beb93cSSam Leffler 	int nerrors = 0;
32039beb93cSSam Leffler 
32139beb93cSSam Leffler 	sm->event_send_all_queued = 0;
322e28a4053SRui Paulo 	dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
323e28a4053SRui Paulo 			      list) {
32439beb93cSSam Leffler 		if (s->current_event == NULL /* not busy */ &&
325e28a4053SRui Paulo 		    !dl_list_empty(&s->event_queue) /* more to do */) {
32639beb93cSSam Leffler 			if (event_send_start(s))
32739beb93cSSam Leffler 				nerrors++;
32839beb93cSSam Leffler 		}
32939beb93cSSam Leffler 	}
33039beb93cSSam Leffler 
33139beb93cSSam Leffler 	if (nerrors) {
33239beb93cSSam Leffler 		/* Try again later */
33339beb93cSSam Leffler 		event_send_all_later(sm);
33439beb93cSSam Leffler 	}
33539beb93cSSam Leffler }
33639beb93cSSam Leffler 
33739beb93cSSam Leffler 
33839beb93cSSam Leffler /* event_send_all_later -- schedule sending events to all subscribers
33939beb93cSSam Leffler  * that need it.
34039beb93cSSam Leffler  * This avoids two problems:
34139beb93cSSam Leffler  * -- After getting a subscription, we should not send the first event
34239beb93cSSam Leffler  *      until after our reply is fully queued to be sent back,
34339beb93cSSam Leffler  * -- Possible stack depth or infinite recursion issues.
34439beb93cSSam Leffler  */
34539beb93cSSam Leffler void event_send_all_later(struct upnp_wps_device_sm *sm)
34639beb93cSSam Leffler {
34739beb93cSSam Leffler 	/*
34839beb93cSSam Leffler 	 * The exact time in the future isn't too important. Waiting a bit
34939beb93cSSam Leffler 	 * might let us do several together.
35039beb93cSSam Leffler 	 */
35139beb93cSSam Leffler 	if (sm->event_send_all_queued)
35239beb93cSSam Leffler 		return;
35339beb93cSSam Leffler 	sm->event_send_all_queued = 1;
35439beb93cSSam Leffler 	eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
35539beb93cSSam Leffler 			       event_send_all_later_handler, NULL, sm);
35639beb93cSSam Leffler }
35739beb93cSSam Leffler 
35839beb93cSSam Leffler 
35939beb93cSSam Leffler /* event_send_stop_all -- cleanup */
36039beb93cSSam Leffler void event_send_stop_all(struct upnp_wps_device_sm *sm)
36139beb93cSSam Leffler {
36239beb93cSSam Leffler 	if (sm->event_send_all_queued)
36339beb93cSSam Leffler 		eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
36439beb93cSSam Leffler 	sm->event_send_all_queued = 0;
36539beb93cSSam Leffler }
36639beb93cSSam Leffler 
36739beb93cSSam Leffler 
36839beb93cSSam Leffler /**
36939beb93cSSam Leffler  * event_add - Add a new event to a queue
37039beb93cSSam Leffler  * @s: Subscription
37139beb93cSSam Leffler  * @data: Event data (is copied; caller retains ownership)
372*f05cddf9SRui Paulo  * @probereq: Whether this is a Probe Request event
373*f05cddf9SRui Paulo  * Returns: 0 on success, -1 on error, 1 on max event queue limit reached
37439beb93cSSam Leffler  */
375*f05cddf9SRui Paulo int event_add(struct subscription *s, const struct wpabuf *data, int probereq)
37639beb93cSSam Leffler {
37739beb93cSSam Leffler 	struct wps_event_ *e;
378*f05cddf9SRui Paulo 	unsigned int len;
37939beb93cSSam Leffler 
380*f05cddf9SRui Paulo 	len = dl_list_len(&s->event_queue);
381*f05cddf9SRui Paulo 	if (len >= MAX_EVENTS_QUEUED) {
38239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
383*f05cddf9SRui Paulo 			   "subscriber %p", s);
384*f05cddf9SRui Paulo 		if (probereq)
38539beb93cSSam Leffler 			return 1;
386*f05cddf9SRui Paulo 
387*f05cddf9SRui Paulo 		/* Drop oldest entry to allow EAP event to be stored. */
388*f05cddf9SRui Paulo 		e = event_dequeue(s);
389*f05cddf9SRui Paulo 		if (!e)
390*f05cddf9SRui Paulo 			return 1;
391*f05cddf9SRui Paulo 		event_delete(e);
392*f05cddf9SRui Paulo 	}
393*f05cddf9SRui Paulo 
394*f05cddf9SRui Paulo 	if (s->last_event_failed && probereq && len > 0) {
395*f05cddf9SRui Paulo 		/*
396*f05cddf9SRui Paulo 		 * Avoid queuing frames for subscribers that may have left
397*f05cddf9SRui Paulo 		 * without unsubscribing.
398*f05cddf9SRui Paulo 		 */
399*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe "
400*f05cddf9SRui Paulo 			   "Request frames for subscription %p since last "
401*f05cddf9SRui Paulo 			   "delivery failed", s);
402*f05cddf9SRui Paulo 		return -1;
40339beb93cSSam Leffler 	}
40439beb93cSSam Leffler 
40539beb93cSSam Leffler 	e = os_zalloc(sizeof(*e));
40639beb93cSSam Leffler 	if (e == NULL)
407*f05cddf9SRui 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);
413*f05cddf9SRui 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++;
418*f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p "
419*f05cddf9SRui Paulo 		   "(queue len %u)", e, s, len + 1);
420e28a4053SRui Paulo 	dl_list_add_tail(&s->event_queue, &e->list);
42139beb93cSSam Leffler 	event_send_all_later(s->sm);
42239beb93cSSam Leffler 	return 0;
42339beb93cSSam Leffler }
424