wps_upnp_event.c

Go to the documentation of this file.
00001 
00012 #include "includes.h"
00013 #include <assert.h>
00014 
00015 #include "common.h"
00016 #include "eloop.h"
00017 #include "uuid.h"
00018 #include "http_client.h"
00019 #include "wps_defs.h"
00020 #include "wps_upnp.h"
00021 #include "wps_upnp_i.h"
00022 
00023 /*
00024  * Event message generation (to subscribers)
00025  *
00026  * We make a separate copy for each message for each subscriber. This memory
00027  * wasted could be limited (adding code complexity) by sharing copies, keeping
00028  * a usage count and freeing when zero.
00029  *
00030  * Sending a message requires using a HTTP over TCP NOTIFY
00031  * (like a PUT) which requires a number of states..
00032  */
00033 
00034 #define MAX_EVENTS_QUEUED 20   /* How far behind queued events */
00035 #define EVENT_TIMEOUT_SEC 30   /* Drop sending event after timeout */
00036 
00037 /* How long to wait before sending event */
00038 #define EVENT_DELAY_SECONDS 0
00039 #define EVENT_DELAY_MSEC 0
00040 
00041 /*
00042  * Event information that we send to each subscriber is remembered in this
00043  * struct. The event cannot be sent by simple UDP; it has to be sent by a HTTP
00044  * over TCP transaction which requires various states.. It may also need to be
00045  * retried at a different address (if more than one is available).
00046  *
00047  * TODO: As an optimization we could share data between subscribers.
00048  */
00049 struct wps_event_ {
00050         struct wps_event_ *next;
00051         struct wps_event_ *prev;        /* double linked list */
00052         struct subscription *s;         /* parent */
00053         unsigned subscriber_sequence;   /* which event for this subscription*/
00054         int retry;                      /* which retry */
00055         struct subscr_addr *addr;       /* address to connect to */
00056         struct wpabuf *data;            /* event data to send */
00057         struct http_client *http_event;
00058 };
00059 
00060 
00061 /* event_clean -- clean sockets etc. of event
00062  * Leaves data, retry count etc. alone.
00063  */
00064 static void event_clean(struct wps_event_ *e)
00065 {
00066         if (e->s->current_event == e)
00067                 e->s->current_event = NULL;
00068         http_client_free(e->http_event);
00069         e->http_event = NULL;
00070 }
00071 
00072 
00073 /* event_delete -- delete single unqueued event
00074  * (be sure to dequeue first if need be)
00075  */
00076 static void event_delete(struct wps_event_ *e)
00077 {
00078         event_clean(e);
00079         wpabuf_free(e->data);
00080         os_free(e);
00081 }
00082 
00083 
00084 /* event_dequeue -- get next event from the queue
00085  * Returns NULL if empty.
00086  */
00087 static struct wps_event_ *event_dequeue(struct subscription *s)
00088 {
00089         struct wps_event_ **event_head = &s->event_queue;
00090         struct wps_event_ *e = *event_head;
00091         if (e == NULL)
00092                 return NULL;
00093         e->next->prev = e->prev;
00094         e->prev->next = e->next;
00095         if (*event_head == e) {
00096                 if (e == e->next) {
00097                         /* last in queue */
00098                         *event_head = NULL;
00099                 } else {
00100                         *event_head = e->next;
00101                 }
00102         }
00103         s->n_queue--;
00104         e->next = e->prev = NULL;
00105         /* but parent "s" is still valid */
00106         return e;
00107 }
00108 
00109 
00110 /* event_enqueue_at_end -- add event to end of queue */
00111 static void event_enqueue_at_end(struct subscription *s, struct wps_event_ *e)
00112 {
00113         struct wps_event_ **event_head = &s->event_queue;
00114         if (*event_head == NULL) {
00115                 *event_head = e->next = e->prev = e;
00116         } else {
00117                 e->next = *event_head;
00118                 e->prev = e->next->prev;
00119                 e->prev->next = e;
00120                 e->next->prev = e;
00121         }
00122         s->n_queue++;
00123 }
00124 
00125 
00126 /* event_enqueue_at_begin -- add event to begin of queue
00127  * (appropriate for retrying event only)
00128  */
00129 static void event_enqueue_at_begin(struct subscription *s,
00130                                    struct wps_event_ *e)
00131 {
00132         struct wps_event_ **event_head = &s->event_queue;
00133         if (*event_head == NULL) {
00134                 *event_head = e->next = e->prev = e;
00135         } else {
00136                 e->prev = *event_head;
00137                 e->next = e->prev->next;
00138                 e->prev->next = e;
00139                 e->next->prev = e;
00140                 *event_head = e;
00141         }
00142         s->n_queue++;
00143 }
00144 
00145 
00146 /* event_delete_all -- delete entire event queue and current event */
00147 void event_delete_all(struct subscription *s)
00148 {
00149         struct wps_event_ *e;
00150         while ((e = event_dequeue(s)) != NULL)
00151                 event_delete(e);
00152         if (s->current_event) {
00153                 event_delete(s->current_event);
00154                 /* will set: s->current_event = NULL;  */
00155         }
00156 }
00157 
00158 
00165 static void event_retry(struct wps_event_ *e, int do_next_address)
00166 {
00167         struct subscription *s = e->s;
00168         struct upnp_wps_device_sm *sm = s->sm;
00169 
00170         event_clean(e);
00171         /* will set: s->current_event = NULL; */
00172 
00173         if (do_next_address)
00174                 e->retry++;
00175         if (e->retry >= s->n_addr) {
00176                 wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
00177                            "for %s", e->addr->domain_and_port);
00178                 return;
00179         }
00180         event_enqueue_at_begin(s, e);
00181         event_send_all_later(sm);
00182 }
00183 
00184 
00185 static struct wpabuf * event_build_message(struct wps_event_ *e)
00186 {
00187         struct wpabuf *buf;
00188         char *b;
00189 
00190         buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
00191         if (buf == NULL)
00192                 return NULL;
00193         wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
00194         wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
00195         wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
00196         wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
00197                        "NT: upnp:event\r\n"
00198                        "NTS: upnp:propchange\r\n");
00199         wpabuf_put_str(buf, "SID: uuid:");
00200         b = wpabuf_put(buf, 0);
00201         uuid_bin2str(e->s->uuid, b, 80);
00202         wpabuf_put(buf, os_strlen(b));
00203         wpabuf_put_str(buf, "\r\n");
00204         wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
00205         wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
00206                       (int) wpabuf_len(e->data));
00207         wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
00208         wpabuf_put_buf(buf, e->data);
00209         return buf;
00210 }
00211 
00212 
00213 static void event_http_cb(void *ctx, struct http_client *c,
00214                           enum http_client_event event)
00215 {
00216         struct wps_event_ *e = ctx;
00217         struct subscription *s = e->s;
00218 
00219         switch (event) {
00220         case HTTP_CLIENT_OK:
00221                 wpa_printf(MSG_DEBUG,
00222                            "WPS UPnP: Got event reply OK from "
00223                            "%s", e->addr->domain_and_port);
00224                 event_delete(e);
00225 
00226                 /* Schedule sending more if there is more to send */
00227                 if (s->event_queue)
00228                         event_send_all_later(s->sm);
00229                 break;
00230         case HTTP_CLIENT_FAILED:
00231         case HTTP_CLIENT_INVALID_REPLY:
00232                 wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event to %s",
00233                            e->addr->domain_and_port);
00234 
00235                 /*
00236                  * If other side doesn't like what we say, forget about them.
00237                  * (There is no way to tell other side that we are dropping
00238                  * them...).
00239                  * Alternately, we could just do event_delete(e)
00240                  */
00241                 wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to "
00242                            "errors");
00243                 subscription_unlink(s);
00244                 subscription_destroy(s);
00245                 break;
00246         case HTTP_CLIENT_TIMEOUT:
00247                 wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
00248                 event_retry(e, 1);
00249         }
00250 }
00251 
00252 
00253 /* event_send_start -- prepare to send a event message to subscriber
00254  *
00255  * This gets complicated because:
00256  * -- The message is sent via TCP and we have to keep the stream open
00257  *      for 30 seconds to get a response... then close it.
00258  * -- But we might have other event happen in the meantime...
00259  *      we have to queue them, if we lose them then the subscriber will
00260  *      be forced to unsubscribe and subscribe again.
00261  * -- If multiple URLs are provided then we are supposed to try successive
00262  *      ones after 30 second timeout.
00263  * -- The URLs might use domain names instead of dotted decimal addresses,
00264  *      and resolution of those may cause unwanted sleeping.
00265  * -- Doing the initial TCP connect can take a while, so we have to come
00266  *      back after connection and then send the data.
00267  *
00268  * Returns nonzero on error;
00269  *
00270  * Prerequisite: No current event send (s->current_event == NULL)
00271  *      and non-empty queue.
00272  */
00273 static int event_send_start(struct subscription *s)
00274 {
00275         struct wps_event_ *e;
00276         int itry;
00277         struct wpabuf *buf;
00278 
00279         /*
00280          * Assume we are called ONLY with no current event and ONLY with
00281          * nonempty event queue and ONLY with at least one address to send to.
00282          */
00283         assert(s->addr_list != NULL);
00284         assert(s->current_event == NULL);
00285         assert(s->event_queue != NULL);
00286 
00287         s->current_event = e = event_dequeue(s);
00288 
00289         /* Use address acc. to no. of retries */
00290         e->addr = s->addr_list;
00291         for (itry = 0; itry < e->retry; itry++)
00292                 e->addr = e->addr->next;
00293 
00294         buf = event_build_message(e);
00295         if (buf == NULL) {
00296                 event_retry(e, 0);
00297                 return -1;
00298         }
00299 
00300         e->http_event = http_client_addr(&e->addr->saddr, buf, 0,
00301                                          event_http_cb, e);
00302         if (e->http_event == NULL) {
00303                 wpabuf_free(buf);
00304                 event_retry(e, 0);
00305                 return -1;
00306         }
00307 
00308         return 0;
00309 }
00310 
00311 
00312 /* event_send_all_later_handler -- actually send events as needed */
00313 static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
00314 {
00315         struct upnp_wps_device_sm *sm = user_ctx;
00316         struct subscription *s;
00317         struct subscription *s_old;
00318         int nerrors = 0;
00319 
00320         sm->event_send_all_queued = 0;
00321         s = sm->subscriptions;
00322         if (s == NULL)
00323                 return;
00324         do {
00325                 if (s->addr_list == NULL) {
00326                         /* if we've given up on all addresses */
00327                         wpa_printf(MSG_DEBUG, "WPS UPnP: Removing "
00328                                    "subscription with no addresses");
00329                         s_old = s;
00330                         s = s_old->next;
00331                         subscription_unlink(s_old);
00332                         subscription_destroy(s_old);
00333                 } else {
00334                         if (s->current_event == NULL /* not busy */ &&
00335                             s->event_queue != NULL /* more to do */) {
00336                                 if (event_send_start(s))
00337                                         nerrors++;
00338                         }
00339                         s = s->next;
00340                 }
00341         } while (sm->subscriptions != NULL && s != sm->subscriptions);
00342 
00343         if (nerrors) {
00344                 /* Try again later */
00345                 event_send_all_later(sm);
00346         }
00347 }
00348 
00349 
00350 /* event_send_all_later -- schedule sending events to all subscribers
00351  * that need it.
00352  * This avoids two problems:
00353  * -- After getting a subscription, we should not send the first event
00354  *      until after our reply is fully queued to be sent back,
00355  * -- Possible stack depth or infinite recursion issues.
00356  */
00357 void event_send_all_later(struct upnp_wps_device_sm *sm)
00358 {
00359         /*
00360          * The exact time in the future isn't too important. Waiting a bit
00361          * might let us do several together.
00362          */
00363         if (sm->event_send_all_queued)
00364                 return;
00365         sm->event_send_all_queued = 1;
00366         eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
00367                                event_send_all_later_handler, NULL, sm);
00368 }
00369 
00370 
00371 /* event_send_stop_all -- cleanup */
00372 void event_send_stop_all(struct upnp_wps_device_sm *sm)
00373 {
00374         if (sm->event_send_all_queued)
00375                 eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
00376         sm->event_send_all_queued = 0;
00377 }
00378 
00379 
00387 int event_add(struct subscription *s, const struct wpabuf *data)
00388 {
00389         struct wps_event_ *e;
00390 
00391         if (s->n_queue >= MAX_EVENTS_QUEUED) {
00392                 wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
00393                            "subscriber");
00394                 return 1;
00395         }
00396 
00397         e = os_zalloc(sizeof(*e));
00398         if (e == NULL)
00399                 return 1;
00400         e->s = s;
00401         e->data = wpabuf_dup(data);
00402         if (e->data == NULL) {
00403                 os_free(e);
00404                 return 1;
00405         }
00406         e->subscriber_sequence = s->next_subscriber_sequence++;
00407         if (s->next_subscriber_sequence == 0)
00408                 s->next_subscriber_sequence++;
00409         event_enqueue_at_end(s, e);
00410         event_send_all_later(s->sm);
00411         return 0;
00412 }
00413 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines

Generated on Sat Nov 21 23:16:55 2009 for hostapd by  doxygen 1.6.1