wps_upnp_event.c revision 214734
1240116Smarcel/*
2240116Smarcel * UPnP WPS Device - Event processing
3240116Smarcel * Copyright (c) 2000-2003 Intel Corporation
4240116Smarcel * Copyright (c) 2006-2007 Sony Corporation
5240116Smarcel * Copyright (c) 2008-2009 Atheros Communications
6240116Smarcel * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
7240116Smarcel *
8240116Smarcel * See wps_upnp.c for more details on licensing and code history.
9240116Smarcel */
10240116Smarcel
11240116Smarcel#include "includes.h"
12240116Smarcel#include <assert.h>
13240116Smarcel
14240116Smarcel#include "common.h"
15240116Smarcel#include "eloop.h"
16240116Smarcel#include "uuid.h"
17240116Smarcel#include "http_client.h"
18240116Smarcel#include "wps_defs.h"
19240116Smarcel#include "wps_upnp.h"
20240116Smarcel#include "wps_upnp_i.h"
21240116Smarcel
22240116Smarcel/*
23240116Smarcel * Event message generation (to subscribers)
24240116Smarcel *
25240116Smarcel * We make a separate copy for each message for each subscriber. This memory
26240116Smarcel * wasted could be limited (adding code complexity) by sharing copies, keeping
27240116Smarcel * a usage count and freeing when zero.
28240116Smarcel *
29240116Smarcel * Sending a message requires using a HTTP over TCP NOTIFY
30240116Smarcel * (like a PUT) which requires a number of states..
31240116Smarcel */
32240116Smarcel
33240116Smarcel#define MAX_EVENTS_QUEUED 20   /* How far behind queued events */
34240116Smarcel#define EVENT_TIMEOUT_SEC 30   /* Drop sending event after timeout */
35240116Smarcel
36240116Smarcel/* How long to wait before sending event */
37240116Smarcel#define EVENT_DELAY_SECONDS 0
38240116Smarcel#define EVENT_DELAY_MSEC 0
39240116Smarcel
40240116Smarcel/*
41240116Smarcel * Event information that we send to each subscriber is remembered in this
42240116Smarcel * struct. The event cannot be sent by simple UDP; it has to be sent by a HTTP
43240116Smarcel * over TCP transaction which requires various states.. It may also need to be
44240116Smarcel * retried at a different address (if more than one is available).
45240116Smarcel *
46240116Smarcel * TODO: As an optimization we could share data between subscribers.
47240116Smarcel */
48240116Smarcelstruct wps_event_ {
49240116Smarcel	struct dl_list list;
50240116Smarcel	struct subscription *s;         /* parent */
51240116Smarcel	unsigned subscriber_sequence;   /* which event for this subscription*/
52240116Smarcel	unsigned int retry;             /* which retry */
53240116Smarcel	struct subscr_addr *addr;       /* address to connect to */
54240116Smarcel	struct wpabuf *data;            /* event data to send */
55240116Smarcel	struct http_client *http_event;
56240116Smarcel};
57240116Smarcel
58240116Smarcel
59240116Smarcel/* event_clean -- clean sockets etc. of event
60240116Smarcel * Leaves data, retry count etc. alone.
61240116Smarcel */
62240116Smarcelstatic void event_clean(struct wps_event_ *e)
63240116Smarcel{
64240116Smarcel	if (e->s->current_event == e)
65240116Smarcel		e->s->current_event = NULL;
66240116Smarcel	http_client_free(e->http_event);
67240116Smarcel	e->http_event = NULL;
68240116Smarcel}
69240116Smarcel
70240116Smarcel
71240116Smarcel/* event_delete -- delete single unqueued event
72240116Smarcel * (be sure to dequeue first if need be)
73240116Smarcel */
74240116Smarcelstatic void event_delete(struct wps_event_ *e)
75240116Smarcel{
76240116Smarcel	event_clean(e);
77240116Smarcel	wpabuf_free(e->data);
78240116Smarcel	os_free(e);
79240116Smarcel}
80240116Smarcel
81240116Smarcel
82240116Smarcel/* event_dequeue -- get next event from the queue
83240116Smarcel * Returns NULL if empty.
84240116Smarcel */
85240116Smarcelstatic struct wps_event_ *event_dequeue(struct subscription *s)
86240116Smarcel{
87240116Smarcel	struct wps_event_ *e;
88240116Smarcel	e = dl_list_first(&s->event_queue, struct wps_event_, list);
89240116Smarcel	if (e)
90240116Smarcel		dl_list_del(&e->list);
91240116Smarcel	return e;
92240116Smarcel}
93240116Smarcel
94240116Smarcel
95240116Smarcel/* event_delete_all -- delete entire event queue and current event */
96240116Smarcelvoid event_delete_all(struct subscription *s)
97240116Smarcel{
98240116Smarcel	struct wps_event_ *e;
99240116Smarcel	while ((e = event_dequeue(s)) != NULL)
100240116Smarcel		event_delete(e);
101240116Smarcel	if (s->current_event) {
102240116Smarcel		event_delete(s->current_event);
103240116Smarcel		/* will set: s->current_event = NULL;  */
104240116Smarcel	}
105240116Smarcel}
106240116Smarcel
107240116Smarcel
108240116Smarcel/**
109240116Smarcel * event_retry - Called when we had a failure delivering event msg
110240116Smarcel * @e: Event
111240116Smarcel * @do_next_address: skip address e.g. on connect fail
112240116Smarcel */
113240116Smarcelstatic void event_retry(struct wps_event_ *e, int do_next_address)
114240116Smarcel{
115240116Smarcel	struct subscription *s = e->s;
116240116Smarcel	struct upnp_wps_device_sm *sm = s->sm;
117240116Smarcel
118240116Smarcel	event_clean(e);
119240116Smarcel	/* will set: s->current_event = NULL; */
120240116Smarcel
121240116Smarcel	if (do_next_address)
122240116Smarcel		e->retry++;
123240116Smarcel	if (e->retry >= dl_list_len(&s->addr_list)) {
124240116Smarcel		wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
125240116Smarcel			   "for %s", e->addr->domain_and_port);
126240116Smarcel		return;
127240116Smarcel	}
128240116Smarcel	dl_list_add(&s->event_queue, &e->list);
129240116Smarcel	event_send_all_later(sm);
130240116Smarcel}
131240116Smarcel
132240116Smarcel
133240116Smarcelstatic struct wpabuf * event_build_message(struct wps_event_ *e)
134240116Smarcel{
135240116Smarcel	struct wpabuf *buf;
136240116Smarcel	char *b;
137240116Smarcel
138240116Smarcel	buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
139240116Smarcel	if (buf == NULL)
140240116Smarcel		return NULL;
141240116Smarcel	wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
142240116Smarcel	wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
143240116Smarcel	wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
144240116Smarcel	wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
145240116Smarcel		       "NT: upnp:event\r\n"
146240116Smarcel		       "NTS: upnp:propchange\r\n");
147240116Smarcel	wpabuf_put_str(buf, "SID: uuid:");
148240116Smarcel	b = wpabuf_put(buf, 0);
149240116Smarcel	uuid_bin2str(e->s->uuid, b, 80);
150240116Smarcel	wpabuf_put(buf, os_strlen(b));
151240116Smarcel	wpabuf_put_str(buf, "\r\n");
152240116Smarcel	wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
153240116Smarcel	wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
154240116Smarcel		      (int) wpabuf_len(e->data));
155240116Smarcel	wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
156240116Smarcel	wpabuf_put_buf(buf, e->data);
157240116Smarcel	return buf;
158240116Smarcel}
159240116Smarcel
160240116Smarcel
161240116Smarcelstatic void event_http_cb(void *ctx, struct http_client *c,
162240116Smarcel			  enum http_client_event event)
163240116Smarcel{
164240116Smarcel	struct wps_event_ *e = ctx;
165240116Smarcel	struct subscription *s = e->s;
166240116Smarcel
167240116Smarcel	switch (event) {
168240116Smarcel	case HTTP_CLIENT_OK:
169240116Smarcel		wpa_printf(MSG_DEBUG,
170240116Smarcel			   "WPS UPnP: Got event reply OK from "
171240116Smarcel			   "%s", e->addr->domain_and_port);
172240116Smarcel		event_delete(e);
173240116Smarcel
174240116Smarcel		/* Schedule sending more if there is more to send */
175240116Smarcel		if (!dl_list_empty(&s->event_queue))
176240116Smarcel			event_send_all_later(s->sm);
177240116Smarcel		break;
178240116Smarcel	case HTTP_CLIENT_FAILED:
179240116Smarcel	case HTTP_CLIENT_INVALID_REPLY:
180240116Smarcel		wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event to %s",
181240116Smarcel			   e->addr->domain_and_port);
182240116Smarcel
183240116Smarcel		/*
184240116Smarcel		 * If other side doesn't like what we say, forget about them.
185240116Smarcel		 * (There is no way to tell other side that we are dropping
186240116Smarcel		 * them...).
187240116Smarcel		 * Alternately, we could just do event_delete(e)
188240116Smarcel		 */
189240116Smarcel		wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to "
190240116Smarcel			   "errors");
191240116Smarcel		dl_list_del(&s->list);
192240116Smarcel		subscription_destroy(s);
193240116Smarcel		break;
194240116Smarcel	case HTTP_CLIENT_TIMEOUT:
195240116Smarcel		wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
196240116Smarcel		event_retry(e, 1);
197240116Smarcel	}
198240116Smarcel}
199240116Smarcel
200240116Smarcel
201240116Smarcel/* event_send_start -- prepare to send a event message to subscriber
202240116Smarcel *
203240116Smarcel * This gets complicated because:
204240116Smarcel * -- The message is sent via TCP and we have to keep the stream open
205240116Smarcel *      for 30 seconds to get a response... then close it.
206240116Smarcel * -- But we might have other event happen in the meantime...
207240116Smarcel *      we have to queue them, if we lose them then the subscriber will
208240116Smarcel *      be forced to unsubscribe and subscribe again.
209240116Smarcel * -- If multiple URLs are provided then we are supposed to try successive
210240116Smarcel *      ones after 30 second timeout.
211240116Smarcel * -- The URLs might use domain names instead of dotted decimal addresses,
212240116Smarcel *      and resolution of those may cause unwanted sleeping.
213240116Smarcel * -- Doing the initial TCP connect can take a while, so we have to come
214240116Smarcel *      back after connection and then send the data.
215240116Smarcel *
216240116Smarcel * Returns nonzero on error;
217240116Smarcel *
218240116Smarcel * Prerequisite: No current event send (s->current_event == NULL)
219240116Smarcel *      and non-empty queue.
220240116Smarcel */
221240116Smarcelstatic int event_send_start(struct subscription *s)
222240116Smarcel{
223240116Smarcel	struct wps_event_ *e;
224240116Smarcel	unsigned int itry;
225240116Smarcel	struct wpabuf *buf;
226240116Smarcel
227240116Smarcel	/*
228240116Smarcel	 * Assume we are called ONLY with no current event and ONLY with
229240116Smarcel	 * nonempty event queue and ONLY with at least one address to send to.
230240116Smarcel	 */
231240116Smarcel	assert(!dl_list_empty(&s->addr_list));
232240116Smarcel	assert(s->current_event == NULL);
233240116Smarcel	assert(!dl_list_empty(&s->event_queue));
234240116Smarcel
235240116Smarcel	s->current_event = e = event_dequeue(s);
236240116Smarcel
237240116Smarcel	/* Use address according to number of retries */
238240116Smarcel	itry = 0;
239240116Smarcel	dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list)
240240116Smarcel		if (itry++ == e->retry)
241240116Smarcel			break;
242240116Smarcel	if (itry < e->retry)
243240116Smarcel		return -1;
244240116Smarcel
245240116Smarcel	buf = event_build_message(e);
246240116Smarcel	if (buf == NULL) {
247240116Smarcel		event_retry(e, 0);
248240116Smarcel		return -1;
249240116Smarcel	}
250240116Smarcel
251240116Smarcel	e->http_event = http_client_addr(&e->addr->saddr, buf, 0,
252240116Smarcel					 event_http_cb, e);
253240116Smarcel	if (e->http_event == NULL) {
254240116Smarcel		wpabuf_free(buf);
255240116Smarcel		event_retry(e, 0);
256240116Smarcel		return -1;
257240116Smarcel	}
258240116Smarcel
259240116Smarcel	return 0;
260240116Smarcel}
261240116Smarcel
262240116Smarcel
263240116Smarcel/* event_send_all_later_handler -- actually send events as needed */
264240116Smarcelstatic void event_send_all_later_handler(void *eloop_data, void *user_ctx)
265240116Smarcel{
266240116Smarcel	struct upnp_wps_device_sm *sm = user_ctx;
267240116Smarcel	struct subscription *s, *tmp;
268240116Smarcel	int nerrors = 0;
269240116Smarcel
270240116Smarcel	sm->event_send_all_queued = 0;
271240116Smarcel	dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
272240116Smarcel			      list) {
273240116Smarcel		if (dl_list_empty(&s->addr_list)) {
274240116Smarcel			/* if we've given up on all addresses */
275240116Smarcel			wpa_printf(MSG_DEBUG, "WPS UPnP: Removing "
276240116Smarcel				   "subscription with no addresses");
277240116Smarcel			dl_list_del(&s->list);
278240116Smarcel			subscription_destroy(s);
279240116Smarcel		} else {
280240116Smarcel			if (s->current_event == NULL /* not busy */ &&
281240116Smarcel			    !dl_list_empty(&s->event_queue) /* more to do */) {
282240116Smarcel				if (event_send_start(s))
283240116Smarcel					nerrors++;
284240116Smarcel			}
285240116Smarcel		}
286240116Smarcel	}
287240116Smarcel
288240116Smarcel	if (nerrors) {
289240116Smarcel		/* Try again later */
290240116Smarcel		event_send_all_later(sm);
291240116Smarcel	}
292240116Smarcel}
293240116Smarcel
294240116Smarcel
295240116Smarcel/* event_send_all_later -- schedule sending events to all subscribers
296240116Smarcel * that need it.
297240116Smarcel * This avoids two problems:
298240116Smarcel * -- After getting a subscription, we should not send the first event
299240116Smarcel *      until after our reply is fully queued to be sent back,
300240116Smarcel * -- Possible stack depth or infinite recursion issues.
301240116Smarcel */
302240116Smarcelvoid event_send_all_later(struct upnp_wps_device_sm *sm)
303240116Smarcel{
304240116Smarcel	/*
305240116Smarcel	 * The exact time in the future isn't too important. Waiting a bit
306240116Smarcel	 * might let us do several together.
307240116Smarcel	 */
308240116Smarcel	if (sm->event_send_all_queued)
309240116Smarcel		return;
310240116Smarcel	sm->event_send_all_queued = 1;
311240116Smarcel	eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
312240116Smarcel			       event_send_all_later_handler, NULL, sm);
313240116Smarcel}
314240116Smarcel
315240116Smarcel
316240116Smarcel/* event_send_stop_all -- cleanup */
317240116Smarcelvoid event_send_stop_all(struct upnp_wps_device_sm *sm)
318240116Smarcel{
319240116Smarcel	if (sm->event_send_all_queued)
320240116Smarcel		eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
321240116Smarcel	sm->event_send_all_queued = 0;
322240116Smarcel}
323240116Smarcel
324240116Smarcel
325240116Smarcel/**
326240116Smarcel * event_add - Add a new event to a queue
327240116Smarcel * @s: Subscription
328240116Smarcel * @data: Event data (is copied; caller retains ownership)
329240116Smarcel * Returns: 0 on success, 1 on error
330240116Smarcel */
331240116Smarcelint event_add(struct subscription *s, const struct wpabuf *data)
332240116Smarcel{
333240116Smarcel	struct wps_event_ *e;
334240116Smarcel
335240116Smarcel	if (dl_list_len(&s->event_queue) >= MAX_EVENTS_QUEUED) {
336240116Smarcel		wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
337240116Smarcel			   "subscriber");
338240116Smarcel		return 1;
339240116Smarcel	}
340240116Smarcel
341240116Smarcel	e = os_zalloc(sizeof(*e));
342240116Smarcel	if (e == NULL)
343240116Smarcel		return 1;
344240116Smarcel	dl_list_init(&e->list);
345240116Smarcel	e->s = s;
346240116Smarcel	e->data = wpabuf_dup(data);
347240116Smarcel	if (e->data == NULL) {
348240116Smarcel		os_free(e);
349240116Smarcel		return 1;
350240116Smarcel	}
351240116Smarcel	e->subscriber_sequence = s->next_subscriber_sequence++;
352240116Smarcel	if (s->next_subscriber_sequence == 0)
353240116Smarcel		s->next_subscriber_sequence++;
354240116Smarcel	dl_list_add_tail(&s->event_queue, &e->list);
355240116Smarcel	event_send_all_later(s->sm);
356240116Smarcel	return 0;
357240116Smarcel}
358240116Smarcel