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