diff options
Diffstat (limited to 'src/wps/wps_upnp_event.c')
-rw-r--r-- | src/wps/wps_upnp_event.c | 347 |
1 files changed, 85 insertions, 262 deletions
diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c index 4122a87..ae5efdb 100644 --- a/src/wps/wps_upnp_event.c +++ b/src/wps/wps_upnp_event.c @@ -10,12 +10,11 @@ #include "includes.h" #include <assert.h> -#include <fcntl.h> #include "common.h" #include "eloop.h" #include "uuid.h" -#include "httpread.h" +#include "http_client.h" #include "wps_defs.h" #include "wps_upnp.h" #include "wps_upnp_i.h" @@ -47,42 +46,25 @@ * TODO: As an optimization we could share data between subscribers. */ struct wps_event_ { - struct wps_event_ *next; - struct wps_event_ *prev; /* double linked list */ + struct dl_list list; struct subscription *s; /* parent */ unsigned subscriber_sequence; /* which event for this subscription*/ - int retry; /* which retry */ + unsigned int retry; /* which retry */ struct subscr_addr *addr; /* address to connect to */ struct wpabuf *data; /* event data to send */ - /* The following apply while we are sending an event message. */ - int sd; /* -1 or socket descriptor for open connection */ - int sd_registered; /* nonzero if we must cancel registration */ - struct httpread *hread; /* NULL or open connection for event msg */ + struct http_client *http_event; }; -static void event_timeout_handler(void *eloop_data, void *user_ctx); - /* event_clean -- clean sockets etc. of event * Leaves data, retry count etc. alone. */ static void event_clean(struct wps_event_ *e) { - if (e->s->current_event == e) { - eloop_cancel_timeout(event_timeout_handler, NULL, e); + if (e->s->current_event == e) e->s->current_event = NULL; - } - if (e->sd_registered) { - eloop_unregister_sock(e->sd, EVENT_TYPE_WRITE); - e->sd_registered = 0; - } - if (e->sd != -1) { - close(e->sd); - e->sd = -1; - } - if (e->hread) - httpread_destroy(e->hread); - e->hread = NULL; + http_client_free(e->http_event); + e->http_event = NULL; } @@ -102,63 +84,14 @@ static void event_delete(struct wps_event_ *e) */ static struct wps_event_ *event_dequeue(struct subscription *s) { - struct wps_event_ **event_head = &s->event_queue; - struct wps_event_ *e = *event_head; - if (e == NULL) - return NULL; - e->next->prev = e->prev; - e->prev->next = e->next; - if (*event_head == e) { - if (e == e->next) { - /* last in queue */ - *event_head = NULL; - } else { - *event_head = e->next; - } - } - s->n_queue--; - e->next = e->prev = NULL; - /* but parent "s" is still valid */ + struct wps_event_ *e; + e = dl_list_first(&s->event_queue, struct wps_event_, list); + if (e) + dl_list_del(&e->list); return e; } -/* event_enqueue_at_end -- add event to end of queue */ -static void event_enqueue_at_end(struct subscription *s, struct wps_event_ *e) -{ - struct wps_event_ **event_head = &s->event_queue; - if (*event_head == NULL) { - *event_head = e->next = e->prev = e; - } else { - e->next = *event_head; - e->prev = e->next->prev; - e->prev->next = e; - e->next->prev = e; - } - s->n_queue++; -} - - -/* event_enqueue_at_begin -- add event to begin of queue - * (appropriate for retrying event only) - */ -static void event_enqueue_at_begin(struct subscription *s, - struct wps_event_ *e) -{ - struct wps_event_ **event_head = &s->event_queue; - if (*event_head == NULL) { - *event_head = e->next = e->prev = e; - } else { - e->prev = *event_head; - e->next = e->prev->next; - e->prev->next = e; - e->next->prev = e; - *event_head = e; - } - s->n_queue++; -} - - /* event_delete_all -- delete entire event queue and current event */ void event_delete_all(struct subscription *s) { @@ -187,114 +120,24 @@ static void event_retry(struct wps_event_ *e, int do_next_address) if (do_next_address) e->retry++; - if (e->retry >= s->n_addr) { + if (e->retry >= dl_list_len(&s->addr_list)) { wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event " "for %s", e->addr->domain_and_port); return; } - event_enqueue_at_begin(s, e); + dl_list_add(&s->event_queue, &e->list); event_send_all_later(sm); } -/* called if the overall event-sending process takes too long */ -static void event_timeout_handler(void *eloop_data, void *user_ctx) +static struct wpabuf * event_build_message(struct wps_event_ *e) { - struct wps_event_ *e = user_ctx; - struct subscription *s = e->s; - - assert(e == s->current_event); - - wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout"); - event_retry(e, 1); -} - - -/* event_got_response_handler -- called back when http response is received. */ -static void event_got_response_handler(struct httpread *handle, void *cookie, - enum httpread_event en) -{ - struct wps_event_ *e = cookie; - struct subscription *s = e->s; - struct upnp_wps_device_sm *sm = s->sm; - struct httpread *hread = e->hread; - int reply_code = 0; - - assert(e == s->current_event); - eloop_cancel_timeout(event_timeout_handler, NULL, e); - - if (en == HTTPREAD_EVENT_FILE_READY) { - if (httpread_hdr_type_get(hread) == HTTPREAD_HDR_TYPE_REPLY) { - reply_code = httpread_reply_code_get(hread); - if (reply_code == HTTP_OK) { - wpa_printf(MSG_DEBUG, - "WPS UPnP: Got event reply OK from " - "%s", e->addr->domain_and_port); - event_delete(e); - goto send_more; - } else { - wpa_printf(MSG_DEBUG, "WPS UPnP: Got event " - "error reply code %d from %s", - reply_code, - e->addr->domain_and_port); - goto bad; - } - } else { - wpa_printf(MSG_DEBUG, "WPS UPnP: Got bogus event " - "response %d from %s", en, - e->addr->domain_and_port); - } - } else { - wpa_printf(MSG_DEBUG, "WPS UPnP: Event response timeout/fail " - "for %s", e->addr->domain_and_port); - goto bad; - } - event_retry(e, 1); - goto send_more; - -send_more: - /* Schedule sending more if there is more to send */ - if (s->event_queue) - event_send_all_later(sm); - return; - -bad: - /* - * If other side doesn't like what we say, forget about them. - * (There is no way to tell other side that we are dropping - * them...). - * Alternately, we could just do event_delete(e) - */ - wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to errors"); - subscription_unlink(s); - subscription_destroy(s); -} - - -/* event_send_tx_ready -- actually write event message - * - * Prequisite: subscription socket descriptor has become ready to - * write (because connection to subscriber has been made). - * - * It is also possible that we are called because the connect has failed; - * it is possible to test for this, or we can just go ahead and then - * the write will fail. - */ -static void event_send_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct wps_event_ *e = sock_ctx; - struct subscription *s = e->s; struct wpabuf *buf; char *b; - assert(e == s->current_event); - assert(e->sd == sock); - buf = wpabuf_alloc(1000 + wpabuf_len(e->data)); - if (buf == NULL) { - event_retry(e, 0); - goto bad; - } + if (buf == NULL) + return NULL; wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path); wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n"); wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port); @@ -303,7 +146,7 @@ static void event_send_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) "NTS: upnp:propchange\r\n"); wpabuf_put_str(buf, "SID: uuid:"); b = wpabuf_put(buf, 0); - uuid_bin2str(s->uuid, b, 80); + uuid_bin2str(e->s->uuid, b, 80); wpabuf_put(buf, os_strlen(b)); wpabuf_put_str(buf, "\r\n"); wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence); @@ -311,45 +154,47 @@ static void event_send_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) (int) wpabuf_len(e->data)); wpabuf_put_str(buf, "\r\n"); /* terminating empty line */ wpabuf_put_buf(buf, e->data); + return buf; +} - /* Since the message size is pretty small, we should be - * able to get the operating system to buffer what we give it - * and not have to come back again later to write more... - */ -#if 0 - /* we could: Turn blocking back on? */ - fcntl(e->sd, F_SETFL, 0); -#endif - wpa_printf(MSG_DEBUG, "WPS UPnP: Sending event to %s", - e->addr->domain_and_port); - if (send_wpabuf(e->sd, buf) < 0) { - event_retry(e, 1); - goto bad; - } - wpabuf_free(buf); - buf = NULL; - if (e->sd_registered) { - e->sd_registered = 0; - eloop_unregister_sock(e->sd, EVENT_TYPE_WRITE); - } - /* Set up to read the reply */ - e->hread = httpread_create(e->sd, event_got_response_handler, - e /* cookie */, - 0 /* no data expected */, - EVENT_TIMEOUT_SEC); - if (e->hread == NULL) { - wpa_printf(MSG_ERROR, "WPS UPnP: httpread_create failed"); - event_retry(e, 0); - goto bad; - } - return; +static void event_http_cb(void *ctx, struct http_client *c, + enum http_client_event event) +{ + struct wps_event_ *e = ctx; + struct subscription *s = e->s; -bad: - /* Schedule sending more if there is more to send */ - if (s->event_queue) - event_send_all_later(s->sm); - wpabuf_free(buf); + switch (event) { + case HTTP_CLIENT_OK: + wpa_printf(MSG_DEBUG, + "WPS UPnP: Got event reply OK from " + "%s", e->addr->domain_and_port); + event_delete(e); + + /* Schedule sending more if there is more to send */ + if (!dl_list_empty(&s->event_queue)) + event_send_all_later(s->sm); + break; + case HTTP_CLIENT_FAILED: + case HTTP_CLIENT_INVALID_REPLY: + wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event to %s", + e->addr->domain_and_port); + + /* + * If other side doesn't like what we say, forget about them. + * (There is no way to tell other side that we are dropping + * them...). + * Alternately, we could just do event_delete(e) + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to " + "errors"); + dl_list_del(&s->list); + subscription_destroy(s); + break; + case HTTP_CLIENT_TIMEOUT: + wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout"); + event_retry(e, 1); + } } @@ -376,57 +221,41 @@ bad: static int event_send_start(struct subscription *s) { struct wps_event_ *e; - int itry; + unsigned int itry; + struct wpabuf *buf; /* * Assume we are called ONLY with no current event and ONLY with * nonempty event queue and ONLY with at least one address to send to. */ - assert(s->addr_list != NULL); + assert(!dl_list_empty(&s->addr_list)); assert(s->current_event == NULL); - assert(s->event_queue != NULL); + assert(!dl_list_empty(&s->event_queue)); s->current_event = e = event_dequeue(s); - /* Use address acc. to no. of retries */ - e->addr = s->addr_list; - for (itry = 0; itry < e->retry; itry++) - e->addr = e->addr->next; - - e->sd = socket(AF_INET, SOCK_STREAM, 0); - if (e->sd < 0) { - event_retry(e, 0); + /* Use address according to number of retries */ + itry = 0; + dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list) + if (itry++ == e->retry) + break; + if (itry < e->retry) return -1; - } - /* set non-blocking so we don't sleep waiting for connection */ - if (fcntl(e->sd, F_SETFL, O_NONBLOCK) != 0) { - event_retry(e, 0); - return -1; - } - /* - * Start the connect. It might succeed immediately but more likely will - * return errno EINPROGRESS. - */ - if (connect(e->sd, (struct sockaddr *) &e->addr->saddr, - sizeof(e->addr->saddr))) { - if (errno != EINPROGRESS) { - event_retry(e, 1); - return -1; - } - } - /* Call back when ready for writing (or on failure...). */ - if (eloop_register_sock(e->sd, EVENT_TYPE_WRITE, event_send_tx_ready, - NULL, e)) { + + buf = event_build_message(e); + if (buf == NULL) { event_retry(e, 0); return -1; } - e->sd_registered = 1; - /* Don't wait forever! */ - if (eloop_register_timeout(EVENT_TIMEOUT_SEC, 0, event_timeout_handler, - NULL, e)) { + + e->http_event = http_client_addr(&e->addr->saddr, buf, 0, + event_http_cb, e); + if (e->http_event == NULL) { + wpabuf_free(buf); event_retry(e, 0); return -1; } + return 0; } @@ -435,32 +264,26 @@ static int event_send_start(struct subscription *s) static void event_send_all_later_handler(void *eloop_data, void *user_ctx) { struct upnp_wps_device_sm *sm = user_ctx; - struct subscription *s; - struct subscription *s_old; + struct subscription *s, *tmp; int nerrors = 0; sm->event_send_all_queued = 0; - s = sm->subscriptions; - if (s == NULL) - return; - do { - if (s->addr_list == NULL) { + dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription, + list) { + if (dl_list_empty(&s->addr_list)) { /* if we've given up on all addresses */ wpa_printf(MSG_DEBUG, "WPS UPnP: Removing " "subscription with no addresses"); - s_old = s; - s = s_old->next; - subscription_unlink(s_old); - subscription_destroy(s_old); + dl_list_del(&s->list); + subscription_destroy(s); } else { if (s->current_event == NULL /* not busy */ && - s->event_queue != NULL /* more to do */) { + !dl_list_empty(&s->event_queue) /* more to do */) { if (event_send_start(s)) nerrors++; } - s = s->next; } - } while (sm->subscriptions != NULL && s != sm->subscriptions); + } if (nerrors) { /* Try again later */ @@ -509,7 +332,7 @@ int event_add(struct subscription *s, const struct wpabuf *data) { struct wps_event_ *e; - if (s->n_queue >= MAX_EVENTS_QUEUED) { + if (dl_list_len(&s->event_queue) >= MAX_EVENTS_QUEUED) { wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for " "subscriber"); return 1; @@ -518,8 +341,8 @@ int event_add(struct subscription *s, const struct wpabuf *data) e = os_zalloc(sizeof(*e)); if (e == NULL) return 1; + dl_list_init(&e->list); e->s = s; - e->sd = -1; e->data = wpabuf_dup(data); if (e->data == NULL) { os_free(e); @@ -528,7 +351,7 @@ int event_add(struct subscription *s, const struct wpabuf *data) e->subscriber_sequence = s->next_subscriber_sequence++; if (s->next_subscriber_sequence == 0) s->next_subscriber_sequence++; - event_enqueue_at_end(s, e); + dl_list_add_tail(&s->event_queue, &e->list); event_send_all_later(s->sm); return 0; } |