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 639beb93cSSam Leffler * Copyright (c) 2009, 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 #include <fcntl.h> 1439beb93cSSam Leffler 1539beb93cSSam Leffler #include "common.h" 1639beb93cSSam Leffler #include "eloop.h" 1739beb93cSSam Leffler #include "uuid.h" 1839beb93cSSam Leffler #include "httpread.h" 1939beb93cSSam Leffler #include "wps_defs.h" 2039beb93cSSam Leffler #include "wps_upnp.h" 2139beb93cSSam Leffler #include "wps_upnp_i.h" 2239beb93cSSam Leffler 2339beb93cSSam Leffler /* 2439beb93cSSam Leffler * Event message generation (to subscribers) 2539beb93cSSam Leffler * 2639beb93cSSam Leffler * We make a separate copy for each message for each subscriber. This memory 2739beb93cSSam Leffler * wasted could be limited (adding code complexity) by sharing copies, keeping 2839beb93cSSam Leffler * a usage count and freeing when zero. 2939beb93cSSam Leffler * 3039beb93cSSam Leffler * Sending a message requires using a HTTP over TCP NOTIFY 3139beb93cSSam Leffler * (like a PUT) which requires a number of states.. 3239beb93cSSam Leffler */ 3339beb93cSSam Leffler 3439beb93cSSam Leffler #define MAX_EVENTS_QUEUED 20 /* How far behind queued events */ 3539beb93cSSam Leffler #define EVENT_TIMEOUT_SEC 30 /* Drop sending event after timeout */ 3639beb93cSSam Leffler 3739beb93cSSam Leffler /* How long to wait before sending event */ 3839beb93cSSam Leffler #define EVENT_DELAY_SECONDS 0 3939beb93cSSam Leffler #define EVENT_DELAY_MSEC 0 4039beb93cSSam Leffler 4139beb93cSSam Leffler /* 4239beb93cSSam Leffler * Event information that we send to each subscriber is remembered in this 4339beb93cSSam Leffler * struct. The event cannot be sent by simple UDP; it has to be sent by a HTTP 4439beb93cSSam Leffler * over TCP transaction which requires various states.. It may also need to be 4539beb93cSSam Leffler * retried at a different address (if more than one is available). 4639beb93cSSam Leffler * 4739beb93cSSam Leffler * TODO: As an optimization we could share data between subscribers. 4839beb93cSSam Leffler */ 4939beb93cSSam Leffler struct wps_event_ { 5039beb93cSSam Leffler struct wps_event_ *next; 5139beb93cSSam Leffler struct wps_event_ *prev; /* double linked list */ 5239beb93cSSam Leffler struct subscription *s; /* parent */ 5339beb93cSSam Leffler unsigned subscriber_sequence; /* which event for this subscription*/ 5439beb93cSSam Leffler int retry; /* which retry */ 5539beb93cSSam Leffler struct subscr_addr *addr; /* address to connect to */ 5639beb93cSSam Leffler struct wpabuf *data; /* event data to send */ 5739beb93cSSam Leffler /* The following apply while we are sending an event message. */ 5839beb93cSSam Leffler int sd; /* -1 or socket descriptor for open connection */ 5939beb93cSSam Leffler int sd_registered; /* nonzero if we must cancel registration */ 6039beb93cSSam Leffler struct httpread *hread; /* NULL or open connection for event msg */ 6139beb93cSSam Leffler }; 6239beb93cSSam Leffler 6339beb93cSSam Leffler 6439beb93cSSam Leffler static void event_timeout_handler(void *eloop_data, void *user_ctx); 6539beb93cSSam Leffler 6639beb93cSSam Leffler /* event_clean -- clean sockets etc. of event 6739beb93cSSam Leffler * Leaves data, retry count etc. alone. 6839beb93cSSam Leffler */ 6939beb93cSSam Leffler static void event_clean(struct wps_event_ *e) 7039beb93cSSam Leffler { 7139beb93cSSam Leffler if (e->s->current_event == e) { 7239beb93cSSam Leffler eloop_cancel_timeout(event_timeout_handler, NULL, e); 7339beb93cSSam Leffler e->s->current_event = NULL; 7439beb93cSSam Leffler } 7539beb93cSSam Leffler if (e->sd_registered) { 7639beb93cSSam Leffler eloop_unregister_sock(e->sd, EVENT_TYPE_WRITE); 7739beb93cSSam Leffler e->sd_registered = 0; 7839beb93cSSam Leffler } 7939beb93cSSam Leffler if (e->sd != -1) { 8039beb93cSSam Leffler close(e->sd); 8139beb93cSSam Leffler e->sd = -1; 8239beb93cSSam Leffler } 8339beb93cSSam Leffler if (e->hread) 8439beb93cSSam Leffler httpread_destroy(e->hread); 8539beb93cSSam Leffler e->hread = NULL; 8639beb93cSSam Leffler } 8739beb93cSSam Leffler 8839beb93cSSam Leffler 8939beb93cSSam Leffler /* event_delete -- delete single unqueued event 9039beb93cSSam Leffler * (be sure to dequeue first if need be) 9139beb93cSSam Leffler */ 9239beb93cSSam Leffler void event_delete(struct wps_event_ *e) 9339beb93cSSam Leffler { 9439beb93cSSam Leffler event_clean(e); 9539beb93cSSam Leffler wpabuf_free(e->data); 9639beb93cSSam Leffler os_free(e); 9739beb93cSSam Leffler } 9839beb93cSSam Leffler 9939beb93cSSam Leffler 10039beb93cSSam Leffler /* event_dequeue -- get next event from the queue 10139beb93cSSam Leffler * Returns NULL if empty. 10239beb93cSSam Leffler */ 10339beb93cSSam Leffler static struct wps_event_ *event_dequeue(struct subscription *s) 10439beb93cSSam Leffler { 10539beb93cSSam Leffler struct wps_event_ **event_head = &s->event_queue; 10639beb93cSSam Leffler struct wps_event_ *e = *event_head; 10739beb93cSSam Leffler if (e == NULL) 10839beb93cSSam Leffler return NULL; 10939beb93cSSam Leffler e->next->prev = e->prev; 11039beb93cSSam Leffler e->prev->next = e->next; 11139beb93cSSam Leffler if (*event_head == e) { 11239beb93cSSam Leffler if (e == e->next) { 11339beb93cSSam Leffler /* last in queue */ 11439beb93cSSam Leffler *event_head = NULL; 11539beb93cSSam Leffler } else { 11639beb93cSSam Leffler *event_head = e->next; 11739beb93cSSam Leffler } 11839beb93cSSam Leffler } 11939beb93cSSam Leffler s->n_queue--; 12039beb93cSSam Leffler e->next = e->prev = NULL; 12139beb93cSSam Leffler /* but parent "s" is still valid */ 12239beb93cSSam Leffler return e; 12339beb93cSSam Leffler } 12439beb93cSSam Leffler 12539beb93cSSam Leffler 12639beb93cSSam Leffler /* event_enqueue_at_end -- add event to end of queue */ 12739beb93cSSam Leffler static void event_enqueue_at_end(struct subscription *s, struct wps_event_ *e) 12839beb93cSSam Leffler { 12939beb93cSSam Leffler struct wps_event_ **event_head = &s->event_queue; 13039beb93cSSam Leffler if (*event_head == NULL) { 13139beb93cSSam Leffler *event_head = e->next = e->prev = e; 13239beb93cSSam Leffler } else { 13339beb93cSSam Leffler e->next = *event_head; 13439beb93cSSam Leffler e->prev = e->next->prev; 13539beb93cSSam Leffler e->prev->next = e; 13639beb93cSSam Leffler e->next->prev = e; 13739beb93cSSam Leffler } 13839beb93cSSam Leffler s->n_queue++; 13939beb93cSSam Leffler } 14039beb93cSSam Leffler 14139beb93cSSam Leffler 14239beb93cSSam Leffler /* event_enqueue_at_begin -- add event to begin of queue 14339beb93cSSam Leffler * (appropriate for retrying event only) 14439beb93cSSam Leffler */ 14539beb93cSSam Leffler static void event_enqueue_at_begin(struct subscription *s, 14639beb93cSSam Leffler struct wps_event_ *e) 14739beb93cSSam Leffler { 14839beb93cSSam Leffler struct wps_event_ **event_head = &s->event_queue; 14939beb93cSSam Leffler if (*event_head == NULL) { 15039beb93cSSam Leffler *event_head = e->next = e->prev = e; 15139beb93cSSam Leffler } else { 15239beb93cSSam Leffler e->prev = *event_head; 15339beb93cSSam Leffler e->next = e->prev->next; 15439beb93cSSam Leffler e->prev->next = e; 15539beb93cSSam Leffler e->next->prev = e; 15639beb93cSSam Leffler *event_head = e; 15739beb93cSSam Leffler } 15839beb93cSSam Leffler s->n_queue++; 15939beb93cSSam Leffler } 16039beb93cSSam Leffler 16139beb93cSSam Leffler 16239beb93cSSam Leffler /* event_delete_all -- delete entire event queue and current event */ 16339beb93cSSam Leffler void event_delete_all(struct subscription *s) 16439beb93cSSam Leffler { 16539beb93cSSam Leffler struct wps_event_ *e; 16639beb93cSSam Leffler while ((e = event_dequeue(s)) != NULL) 16739beb93cSSam Leffler event_delete(e); 16839beb93cSSam Leffler if (s->current_event) { 16939beb93cSSam Leffler event_delete(s->current_event); 17039beb93cSSam Leffler /* will set: s->current_event = NULL; */ 17139beb93cSSam Leffler } 17239beb93cSSam Leffler } 17339beb93cSSam Leffler 17439beb93cSSam Leffler 17539beb93cSSam Leffler /** 17639beb93cSSam Leffler * event_retry - Called when we had a failure delivering event msg 17739beb93cSSam Leffler * @e: Event 17839beb93cSSam Leffler * @do_next_address: skip address e.g. on connect fail 17939beb93cSSam Leffler */ 18039beb93cSSam Leffler static void event_retry(struct wps_event_ *e, int do_next_address) 18139beb93cSSam Leffler { 18239beb93cSSam Leffler struct subscription *s = e->s; 18339beb93cSSam Leffler struct upnp_wps_device_sm *sm = s->sm; 18439beb93cSSam Leffler 18539beb93cSSam Leffler event_clean(e); 18639beb93cSSam Leffler /* will set: s->current_event = NULL; */ 18739beb93cSSam Leffler 18839beb93cSSam Leffler if (do_next_address) 18939beb93cSSam Leffler e->retry++; 19039beb93cSSam Leffler if (e->retry >= s->n_addr) { 19139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event " 19239beb93cSSam Leffler "for %s", e->addr->domain_and_port); 19339beb93cSSam Leffler return; 19439beb93cSSam Leffler } 19539beb93cSSam Leffler event_enqueue_at_begin(s, e); 19639beb93cSSam Leffler event_send_all_later(sm); 19739beb93cSSam Leffler } 19839beb93cSSam Leffler 19939beb93cSSam Leffler 20039beb93cSSam Leffler /* called if the overall event-sending process takes too long */ 20139beb93cSSam Leffler static void event_timeout_handler(void *eloop_data, void *user_ctx) 20239beb93cSSam Leffler { 20339beb93cSSam Leffler struct wps_event_ *e = user_ctx; 20439beb93cSSam Leffler struct subscription *s = e->s; 20539beb93cSSam Leffler 20639beb93cSSam Leffler assert(e == s->current_event); 20739beb93cSSam Leffler 20839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout"); 20939beb93cSSam Leffler event_retry(e, 1); 21039beb93cSSam Leffler } 21139beb93cSSam Leffler 21239beb93cSSam Leffler 21339beb93cSSam Leffler /* event_got_response_handler -- called back when http response is received. */ 21439beb93cSSam Leffler static void event_got_response_handler(struct httpread *handle, void *cookie, 21539beb93cSSam Leffler enum httpread_event en) 21639beb93cSSam Leffler { 21739beb93cSSam Leffler struct wps_event_ *e = cookie; 21839beb93cSSam Leffler struct subscription *s = e->s; 21939beb93cSSam Leffler struct upnp_wps_device_sm *sm = s->sm; 22039beb93cSSam Leffler struct httpread *hread = e->hread; 22139beb93cSSam Leffler int reply_code = 0; 22239beb93cSSam Leffler 22339beb93cSSam Leffler assert(e == s->current_event); 22439beb93cSSam Leffler eloop_cancel_timeout(event_timeout_handler, NULL, e); 22539beb93cSSam Leffler 22639beb93cSSam Leffler if (en == HTTPREAD_EVENT_FILE_READY) { 22739beb93cSSam Leffler if (httpread_hdr_type_get(hread) == HTTPREAD_HDR_TYPE_REPLY) { 22839beb93cSSam Leffler reply_code = httpread_reply_code_get(hread); 22939beb93cSSam Leffler if (reply_code == HTTP_OK) { 23039beb93cSSam Leffler wpa_printf(MSG_DEBUG, 23139beb93cSSam Leffler "WPS UPnP: Got event reply OK from " 23239beb93cSSam Leffler "%s", e->addr->domain_and_port); 23339beb93cSSam Leffler event_delete(e); 23439beb93cSSam Leffler goto send_more; 23539beb93cSSam Leffler } else { 23639beb93cSSam Leffler wpa_printf(MSG_DEBUG, "WPS UPnP: Got event " 23739beb93cSSam Leffler "error reply code %d from %s", 23839beb93cSSam Leffler reply_code, 23939beb93cSSam Leffler e->addr->domain_and_port); 24039beb93cSSam Leffler goto bad; 24139beb93cSSam Leffler } 24239beb93cSSam Leffler } else { 24339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "WPS UPnP: Got bogus event " 24439beb93cSSam Leffler "response %d from %s", en, 24539beb93cSSam Leffler e->addr->domain_and_port); 24639beb93cSSam Leffler } 24739beb93cSSam Leffler } else { 24839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "WPS UPnP: Event response timeout/fail " 24939beb93cSSam Leffler "for %s", e->addr->domain_and_port); 25039beb93cSSam Leffler goto bad; 25139beb93cSSam Leffler } 25239beb93cSSam Leffler event_retry(e, 1); 25339beb93cSSam Leffler goto send_more; 25439beb93cSSam Leffler 25539beb93cSSam Leffler send_more: 25639beb93cSSam Leffler /* Schedule sending more if there is more to send */ 25739beb93cSSam Leffler if (s->event_queue) 25839beb93cSSam Leffler event_send_all_later(sm); 25939beb93cSSam Leffler return; 26039beb93cSSam Leffler 26139beb93cSSam Leffler bad: 26239beb93cSSam Leffler /* 26339beb93cSSam Leffler * If other side doesn't like what we say, forget about them. 26439beb93cSSam Leffler * (There is no way to tell other side that we are dropping 26539beb93cSSam Leffler * them...). 26639beb93cSSam Leffler * Alternately, we could just do event_delete(e) 26739beb93cSSam Leffler */ 26839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to errors"); 26939beb93cSSam Leffler subscription_unlink(s); 27039beb93cSSam Leffler subscription_destroy(s); 27139beb93cSSam Leffler } 27239beb93cSSam Leffler 27339beb93cSSam Leffler 27439beb93cSSam Leffler /* event_send_tx_ready -- actually write event message 27539beb93cSSam Leffler * 27639beb93cSSam Leffler * Prequisite: subscription socket descriptor has become ready to 27739beb93cSSam Leffler * write (because connection to subscriber has been made). 27839beb93cSSam Leffler * 27939beb93cSSam Leffler * It is also possible that we are called because the connect has failed; 28039beb93cSSam Leffler * it is possible to test for this, or we can just go ahead and then 28139beb93cSSam Leffler * the write will fail. 28239beb93cSSam Leffler */ 28339beb93cSSam Leffler static void event_send_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) 28439beb93cSSam Leffler { 28539beb93cSSam Leffler struct wps_event_ *e = sock_ctx; 28639beb93cSSam Leffler struct subscription *s = e->s; 28739beb93cSSam Leffler struct wpabuf *buf; 28839beb93cSSam Leffler char *b; 28939beb93cSSam Leffler 29039beb93cSSam Leffler assert(e == s->current_event); 29139beb93cSSam Leffler assert(e->sd == sock); 29239beb93cSSam Leffler 29339beb93cSSam Leffler buf = wpabuf_alloc(1000 + wpabuf_len(e->data)); 29439beb93cSSam Leffler if (buf == NULL) { 29539beb93cSSam Leffler event_retry(e, 0); 29639beb93cSSam Leffler goto bad; 29739beb93cSSam Leffler } 29839beb93cSSam Leffler wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path); 29939beb93cSSam Leffler wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n"); 30039beb93cSSam Leffler wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port); 30139beb93cSSam Leffler wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n" 30239beb93cSSam Leffler "NT: upnp:event\r\n" 30339beb93cSSam Leffler "NTS: upnp:propchange\r\n"); 30439beb93cSSam Leffler wpabuf_put_str(buf, "SID: uuid:"); 30539beb93cSSam Leffler b = wpabuf_put(buf, 0); 30639beb93cSSam Leffler uuid_bin2str(s->uuid, b, 80); 30739beb93cSSam Leffler wpabuf_put(buf, os_strlen(b)); 30839beb93cSSam Leffler wpabuf_put_str(buf, "\r\n"); 30939beb93cSSam Leffler wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence); 31039beb93cSSam Leffler wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n", 31139beb93cSSam Leffler (int) wpabuf_len(e->data)); 31239beb93cSSam Leffler wpabuf_put_str(buf, "\r\n"); /* terminating empty line */ 31339beb93cSSam Leffler wpabuf_put_buf(buf, e->data); 31439beb93cSSam Leffler 31539beb93cSSam Leffler /* Since the message size is pretty small, we should be 31639beb93cSSam Leffler * able to get the operating system to buffer what we give it 31739beb93cSSam Leffler * and not have to come back again later to write more... 31839beb93cSSam Leffler */ 31939beb93cSSam Leffler #if 0 32039beb93cSSam Leffler /* we could: Turn blocking back on? */ 32139beb93cSSam Leffler fcntl(e->sd, F_SETFL, 0); 32239beb93cSSam Leffler #endif 32339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "WPS UPnP: Sending event to %s", 32439beb93cSSam Leffler e->addr->domain_and_port); 32539beb93cSSam Leffler if (send_wpabuf(e->sd, buf) < 0) { 32639beb93cSSam Leffler event_retry(e, 1); 32739beb93cSSam Leffler goto bad; 32839beb93cSSam Leffler } 32939beb93cSSam Leffler wpabuf_free(buf); 33039beb93cSSam Leffler buf = NULL; 33139beb93cSSam Leffler 33239beb93cSSam Leffler if (e->sd_registered) { 33339beb93cSSam Leffler e->sd_registered = 0; 33439beb93cSSam Leffler eloop_unregister_sock(e->sd, EVENT_TYPE_WRITE); 33539beb93cSSam Leffler } 33639beb93cSSam Leffler /* Set up to read the reply */ 33739beb93cSSam Leffler e->hread = httpread_create(e->sd, event_got_response_handler, 33839beb93cSSam Leffler e /* cookie */, 33939beb93cSSam Leffler 0 /* no data expected */, 34039beb93cSSam Leffler EVENT_TIMEOUT_SEC); 34139beb93cSSam Leffler if (e->hread == NULL) { 34239beb93cSSam Leffler wpa_printf(MSG_ERROR, "WPS UPnP: httpread_create failed"); 34339beb93cSSam Leffler event_retry(e, 0); 34439beb93cSSam Leffler goto bad; 34539beb93cSSam Leffler } 34639beb93cSSam Leffler return; 34739beb93cSSam Leffler 34839beb93cSSam Leffler bad: 34939beb93cSSam Leffler /* Schedule sending more if there is more to send */ 35039beb93cSSam Leffler if (s->event_queue) 35139beb93cSSam Leffler event_send_all_later(s->sm); 35239beb93cSSam Leffler wpabuf_free(buf); 35339beb93cSSam Leffler } 35439beb93cSSam Leffler 35539beb93cSSam Leffler 35639beb93cSSam Leffler /* event_send_start -- prepare to send a event message to subscriber 35739beb93cSSam Leffler * 35839beb93cSSam Leffler * This gets complicated because: 35939beb93cSSam Leffler * -- The message is sent via TCP and we have to keep the stream open 36039beb93cSSam Leffler * for 30 seconds to get a response... then close it. 36139beb93cSSam Leffler * -- But we might have other event happen in the meantime... 36239beb93cSSam Leffler * we have to queue them, if we lose them then the subscriber will 36339beb93cSSam Leffler * be forced to unsubscribe and subscribe again. 36439beb93cSSam Leffler * -- If multiple URLs are provided then we are supposed to try successive 36539beb93cSSam Leffler * ones after 30 second timeout. 36639beb93cSSam Leffler * -- The URLs might use domain names instead of dotted decimal addresses, 36739beb93cSSam Leffler * and resolution of those may cause unwanted sleeping. 36839beb93cSSam Leffler * -- Doing the initial TCP connect can take a while, so we have to come 36939beb93cSSam Leffler * back after connection and then send the data. 37039beb93cSSam Leffler * 37139beb93cSSam Leffler * Returns nonzero on error; 37239beb93cSSam Leffler * 37339beb93cSSam Leffler * Prerequisite: No current event send (s->current_event == NULL) 37439beb93cSSam Leffler * and non-empty queue. 37539beb93cSSam Leffler */ 37639beb93cSSam Leffler static int event_send_start(struct subscription *s) 37739beb93cSSam Leffler { 37839beb93cSSam Leffler struct wps_event_ *e; 37939beb93cSSam Leffler int itry; 38039beb93cSSam Leffler 38139beb93cSSam Leffler /* 38239beb93cSSam Leffler * Assume we are called ONLY with no current event and ONLY with 38339beb93cSSam Leffler * nonempty event queue and ONLY with at least one address to send to. 38439beb93cSSam Leffler */ 38539beb93cSSam Leffler assert(s->addr_list != NULL); 38639beb93cSSam Leffler assert(s->current_event == NULL); 38739beb93cSSam Leffler assert(s->event_queue != NULL); 38839beb93cSSam Leffler 38939beb93cSSam Leffler s->current_event = e = event_dequeue(s); 39039beb93cSSam Leffler 39139beb93cSSam Leffler /* Use address acc. to no. of retries */ 39239beb93cSSam Leffler e->addr = s->addr_list; 39339beb93cSSam Leffler for (itry = 0; itry < e->retry; itry++) 39439beb93cSSam Leffler e->addr = e->addr->next; 39539beb93cSSam Leffler 39639beb93cSSam Leffler e->sd = socket(AF_INET, SOCK_STREAM, 0); 39739beb93cSSam Leffler if (e->sd < 0) { 39839beb93cSSam Leffler event_retry(e, 0); 39939beb93cSSam Leffler return -1; 40039beb93cSSam Leffler } 40139beb93cSSam Leffler /* set non-blocking so we don't sleep waiting for connection */ 40239beb93cSSam Leffler if (fcntl(e->sd, F_SETFL, O_NONBLOCK) != 0) { 40339beb93cSSam Leffler event_retry(e, 0); 40439beb93cSSam Leffler return -1; 40539beb93cSSam Leffler } 40639beb93cSSam Leffler /* 40739beb93cSSam Leffler * Start the connect. It might succeed immediately but more likely will 40839beb93cSSam Leffler * return errno EINPROGRESS. 40939beb93cSSam Leffler */ 41039beb93cSSam Leffler if (connect(e->sd, (struct sockaddr *) &e->addr->saddr, 41139beb93cSSam Leffler sizeof(e->addr->saddr))) { 41239beb93cSSam Leffler if (errno != EINPROGRESS) { 41339beb93cSSam Leffler event_retry(e, 1); 41439beb93cSSam Leffler return -1; 41539beb93cSSam Leffler } 41639beb93cSSam Leffler } 41739beb93cSSam Leffler /* Call back when ready for writing (or on failure...). */ 41839beb93cSSam Leffler if (eloop_register_sock(e->sd, EVENT_TYPE_WRITE, event_send_tx_ready, 41939beb93cSSam Leffler NULL, e)) { 42039beb93cSSam Leffler event_retry(e, 0); 42139beb93cSSam Leffler return -1; 42239beb93cSSam Leffler } 42339beb93cSSam Leffler e->sd_registered = 1; 42439beb93cSSam Leffler /* Don't wait forever! */ 42539beb93cSSam Leffler if (eloop_register_timeout(EVENT_TIMEOUT_SEC, 0, event_timeout_handler, 42639beb93cSSam Leffler NULL, e)) { 42739beb93cSSam Leffler event_retry(e, 0); 42839beb93cSSam Leffler return -1; 42939beb93cSSam Leffler } 43039beb93cSSam Leffler return 0; 43139beb93cSSam Leffler } 43239beb93cSSam Leffler 43339beb93cSSam Leffler 43439beb93cSSam Leffler /* event_send_all_later_handler -- actually send events as needed */ 43539beb93cSSam Leffler void event_send_all_later_handler(void *eloop_data, void *user_ctx) 43639beb93cSSam Leffler { 43739beb93cSSam Leffler struct upnp_wps_device_sm *sm = user_ctx; 43839beb93cSSam Leffler struct subscription *s; 43939beb93cSSam Leffler struct subscription *s_old; 44039beb93cSSam Leffler int nerrors = 0; 44139beb93cSSam Leffler 44239beb93cSSam Leffler sm->event_send_all_queued = 0; 44339beb93cSSam Leffler s = sm->subscriptions; 44439beb93cSSam Leffler if (s == NULL) 44539beb93cSSam Leffler return; 44639beb93cSSam Leffler do { 44739beb93cSSam Leffler if (s->addr_list == NULL) { 44839beb93cSSam Leffler /* if we've given up on all addresses */ 44939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "WPS UPnP: Removing " 45039beb93cSSam Leffler "subscription with no addresses"); 45139beb93cSSam Leffler s_old = s; 45239beb93cSSam Leffler s = s_old->next; 45339beb93cSSam Leffler subscription_unlink(s_old); 45439beb93cSSam Leffler subscription_destroy(s_old); 45539beb93cSSam Leffler } else { 45639beb93cSSam Leffler if (s->current_event == NULL /* not busy */ && 45739beb93cSSam Leffler s->event_queue != NULL /* more to do */) { 45839beb93cSSam Leffler if (event_send_start(s)) 45939beb93cSSam Leffler nerrors++; 46039beb93cSSam Leffler } 46139beb93cSSam Leffler s = s->next; 46239beb93cSSam Leffler } 46339beb93cSSam Leffler } while (sm->subscriptions != NULL && s != sm->subscriptions); 46439beb93cSSam Leffler 46539beb93cSSam Leffler if (nerrors) { 46639beb93cSSam Leffler /* Try again later */ 46739beb93cSSam Leffler event_send_all_later(sm); 46839beb93cSSam Leffler } 46939beb93cSSam Leffler } 47039beb93cSSam Leffler 47139beb93cSSam Leffler 47239beb93cSSam Leffler /* event_send_all_later -- schedule sending events to all subscribers 47339beb93cSSam Leffler * that need it. 47439beb93cSSam Leffler * This avoids two problems: 47539beb93cSSam Leffler * -- After getting a subscription, we should not send the first event 47639beb93cSSam Leffler * until after our reply is fully queued to be sent back, 47739beb93cSSam Leffler * -- Possible stack depth or infinite recursion issues. 47839beb93cSSam Leffler */ 47939beb93cSSam Leffler void event_send_all_later(struct upnp_wps_device_sm *sm) 48039beb93cSSam Leffler { 48139beb93cSSam Leffler /* 48239beb93cSSam Leffler * The exact time in the future isn't too important. Waiting a bit 48339beb93cSSam Leffler * might let us do several together. 48439beb93cSSam Leffler */ 48539beb93cSSam Leffler if (sm->event_send_all_queued) 48639beb93cSSam Leffler return; 48739beb93cSSam Leffler sm->event_send_all_queued = 1; 48839beb93cSSam Leffler eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC, 48939beb93cSSam Leffler event_send_all_later_handler, NULL, sm); 49039beb93cSSam Leffler } 49139beb93cSSam Leffler 49239beb93cSSam Leffler 49339beb93cSSam Leffler /* event_send_stop_all -- cleanup */ 49439beb93cSSam Leffler void event_send_stop_all(struct upnp_wps_device_sm *sm) 49539beb93cSSam Leffler { 49639beb93cSSam Leffler if (sm->event_send_all_queued) 49739beb93cSSam Leffler eloop_cancel_timeout(event_send_all_later_handler, NULL, sm); 49839beb93cSSam Leffler sm->event_send_all_queued = 0; 49939beb93cSSam Leffler } 50039beb93cSSam Leffler 50139beb93cSSam Leffler 50239beb93cSSam Leffler /** 50339beb93cSSam Leffler * event_add - Add a new event to a queue 50439beb93cSSam Leffler * @s: Subscription 50539beb93cSSam Leffler * @data: Event data (is copied; caller retains ownership) 50639beb93cSSam Leffler * Returns: 0 on success, 1 on error 50739beb93cSSam Leffler */ 50839beb93cSSam Leffler int event_add(struct subscription *s, const struct wpabuf *data) 50939beb93cSSam Leffler { 51039beb93cSSam Leffler struct wps_event_ *e; 51139beb93cSSam Leffler 51239beb93cSSam Leffler if (s->n_queue >= MAX_EVENTS_QUEUED) { 51339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for " 51439beb93cSSam Leffler "subscriber"); 51539beb93cSSam Leffler return 1; 51639beb93cSSam Leffler } 51739beb93cSSam Leffler 51839beb93cSSam Leffler e = os_zalloc(sizeof(*e)); 51939beb93cSSam Leffler if (e == NULL) 52039beb93cSSam Leffler return 1; 52139beb93cSSam Leffler e->s = s; 52239beb93cSSam Leffler e->sd = -1; 52339beb93cSSam Leffler e->data = wpabuf_dup(data); 52439beb93cSSam Leffler if (e->data == NULL) { 52539beb93cSSam Leffler os_free(e); 52639beb93cSSam Leffler return 1; 52739beb93cSSam Leffler } 52839beb93cSSam Leffler e->subscriber_sequence = s->next_subscriber_sequence++; 52939beb93cSSam Leffler if (s->next_subscriber_sequence == 0) 53039beb93cSSam Leffler s->next_subscriber_sequence++; 53139beb93cSSam Leffler event_enqueue_at_end(s, e); 53239beb93cSSam Leffler event_send_all_later(s->sm); 53339beb93cSSam Leffler return 0; 53439beb93cSSam Leffler } 535