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, Jouni Malinen <j@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 EVENT_TIMEOUT_SEC 30   /* Drop sending event after timeout */
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 */
48struct 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 */
62static 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 */
74static void event_delete(struct wps_event_ *e)
75{
76	event_clean(e);
77	wpabuf_free(e->data);
78	os_free(e);
79}
80
81
82/* event_dequeue -- get next event from the queue
83 * Returns NULL if empty.
84 */
85static struct wps_event_ *event_dequeue(struct subscription *s)
86{
87	struct wps_event_ *e;
88	e = dl_list_first(&s->event_queue, struct wps_event_, list);
89	if (e)
90		dl_list_del(&e->list);
91	return e;
92}
93
94
95/* event_delete_all -- delete entire event queue and current event */
96void event_delete_all(struct subscription *s)
97{
98	struct wps_event_ *e;
99	while ((e = event_dequeue(s)) != NULL)
100		event_delete(e);
101	if (s->current_event) {
102		event_delete(s->current_event);
103		/* will set: s->current_event = NULL;  */
104	}
105}
106
107
108/**
109 * event_retry - Called when we had a failure delivering event msg
110 * @e: Event
111 * @do_next_address: skip address e.g. on connect fail
112 */
113static void event_retry(struct wps_event_ *e, int do_next_address)
114{
115	struct subscription *s = e->s;
116	struct upnp_wps_device_sm *sm = s->sm;
117
118	event_clean(e);
119	/* will set: s->current_event = NULL; */
120
121	if (do_next_address)
122		e->retry++;
123	if (e->retry >= dl_list_len(&s->addr_list)) {
124		wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
125			   "for %s", e->addr->domain_and_port);
126		return;
127	}
128	dl_list_add(&s->event_queue, &e->list);
129	event_send_all_later(sm);
130}
131
132
133static struct wpabuf * event_build_message(struct wps_event_ *e)
134{
135	struct wpabuf *buf;
136	char *b;
137
138	buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
139	if (buf == NULL)
140		return NULL;
141	wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
142	wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
143	wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
144	wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
145		       "NT: upnp:event\r\n"
146		       "NTS: upnp:propchange\r\n");
147	wpabuf_put_str(buf, "SID: uuid:");
148	b = wpabuf_put(buf, 0);
149	uuid_bin2str(e->s->uuid, b, 80);
150	wpabuf_put(buf, os_strlen(b));
151	wpabuf_put_str(buf, "\r\n");
152	wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
153	wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
154		      (int) wpabuf_len(e->data));
155	wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
156	wpabuf_put_buf(buf, e->data);
157	return buf;
158}
159
160
161static void event_http_cb(void *ctx, struct http_client *c,
162			  enum http_client_event event)
163{
164	struct wps_event_ *e = ctx;
165	struct subscription *s = e->s;
166
167	switch (event) {
168	case HTTP_CLIENT_OK:
169		wpa_printf(MSG_DEBUG,
170			   "WPS UPnP: Got event reply OK from "
171			   "%s", e->addr->domain_and_port);
172		event_delete(e);
173
174		/* Schedule sending more if there is more to send */
175		if (!dl_list_empty(&s->event_queue))
176			event_send_all_later(s->sm);
177		break;
178	case HTTP_CLIENT_FAILED:
179	case HTTP_CLIENT_INVALID_REPLY:
180		wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event to %s",
181			   e->addr->domain_and_port);
182
183		/*
184		 * If other side doesn't like what we say, forget about them.
185		 * (There is no way to tell other side that we are dropping
186		 * them...).
187		 * Alternately, we could just do event_delete(e)
188		 */
189		wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to "
190			   "errors");
191		dl_list_del(&s->list);
192		subscription_destroy(s);
193		break;
194	case HTTP_CLIENT_TIMEOUT:
195		wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
196		event_retry(e, 1);
197	}
198}
199
200
201/* event_send_start -- prepare to send a event message to subscriber
202 *
203 * This gets complicated because:
204 * -- The message is sent via TCP and we have to keep the stream open
205 *      for 30 seconds to get a response... then close it.
206 * -- But we might have other event happen in the meantime...
207 *      we have to queue them, if we lose them then the subscriber will
208 *      be forced to unsubscribe and subscribe again.
209 * -- If multiple URLs are provided then we are supposed to try successive
210 *      ones after 30 second timeout.
211 * -- The URLs might use domain names instead of dotted decimal addresses,
212 *      and resolution of those may cause unwanted sleeping.
213 * -- Doing the initial TCP connect can take a while, so we have to come
214 *      back after connection and then send the data.
215 *
216 * Returns nonzero on error;
217 *
218 * Prerequisite: No current event send (s->current_event == NULL)
219 *      and non-empty queue.
220 */
221static int event_send_start(struct subscription *s)
222{
223	struct wps_event_ *e;
224	unsigned int itry;
225	struct wpabuf *buf;
226
227	/*
228	 * Assume we are called ONLY with no current event and ONLY with
229	 * nonempty event queue and ONLY with at least one address to send to.
230	 */
231	assert(!dl_list_empty(&s->addr_list));
232	assert(s->current_event == NULL);
233	assert(!dl_list_empty(&s->event_queue));
234
235	s->current_event = e = event_dequeue(s);
236
237	/* Use address according to number of retries */
238	itry = 0;
239	dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list)
240		if (itry++ == e->retry)
241			break;
242	if (itry < e->retry)
243		return -1;
244
245	buf = event_build_message(e);
246	if (buf == NULL) {
247		event_retry(e, 0);
248		return -1;
249	}
250
251	e->http_event = http_client_addr(&e->addr->saddr, buf, 0,
252					 event_http_cb, e);
253	if (e->http_event == NULL) {
254		wpabuf_free(buf);
255		event_retry(e, 0);
256		return -1;
257	}
258
259	return 0;
260}
261
262
263/* event_send_all_later_handler -- actually send events as needed */
264static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
265{
266	struct upnp_wps_device_sm *sm = user_ctx;
267	struct subscription *s, *tmp;
268	int nerrors = 0;
269
270	sm->event_send_all_queued = 0;
271	dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
272			      list) {
273		if (dl_list_empty(&s->addr_list)) {
274			/* if we've given up on all addresses */
275			wpa_printf(MSG_DEBUG, "WPS UPnP: Removing "
276				   "subscription with no addresses");
277			dl_list_del(&s->list);
278			subscription_destroy(s);
279		} else {
280			if (s->current_event == NULL /* not busy */ &&
281			    !dl_list_empty(&s->event_queue) /* more to do */) {
282				if (event_send_start(s))
283					nerrors++;
284			}
285		}
286	}
287
288	if (nerrors) {
289		/* Try again later */
290		event_send_all_later(sm);
291	}
292}
293
294
295/* event_send_all_later -- schedule sending events to all subscribers
296 * that need it.
297 * This avoids two problems:
298 * -- After getting a subscription, we should not send the first event
299 *      until after our reply is fully queued to be sent back,
300 * -- Possible stack depth or infinite recursion issues.
301 */
302void event_send_all_later(struct upnp_wps_device_sm *sm)
303{
304	/*
305	 * The exact time in the future isn't too important. Waiting a bit
306	 * might let us do several together.
307	 */
308	if (sm->event_send_all_queued)
309		return;
310	sm->event_send_all_queued = 1;
311	eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
312			       event_send_all_later_handler, NULL, sm);
313}
314
315
316/* event_send_stop_all -- cleanup */
317void event_send_stop_all(struct upnp_wps_device_sm *sm)
318{
319	if (sm->event_send_all_queued)
320		eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
321	sm->event_send_all_queued = 0;
322}
323
324
325/**
326 * event_add - Add a new event to a queue
327 * @s: Subscription
328 * @data: Event data (is copied; caller retains ownership)
329 * Returns: 0 on success, 1 on error
330 */
331int event_add(struct subscription *s, const struct wpabuf *data)
332{
333	struct wps_event_ *e;
334
335	if (dl_list_len(&s->event_queue) >= MAX_EVENTS_QUEUED) {
336		wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
337			   "subscriber");
338		return 1;
339	}
340
341	e = os_zalloc(sizeof(*e));
342	if (e == NULL)
343		return 1;
344	dl_list_init(&e->list);
345	e->s = s;
346	e->data = wpabuf_dup(data);
347	if (e->data == NULL) {
348		os_free(e);
349		return 1;
350	}
351	e->subscriber_sequence = s->next_subscriber_sequence++;
352	if (s->next_subscriber_sequence == 0)
353		s->next_subscriber_sequence++;
354	dl_list_add_tail(&s->event_queue, &e->list);
355	event_send_all_later(s->sm);
356	return 0;
357}
358