LCOV - code coverage report
Current view: top level - src/wps - wps_upnp_event.c (source / functions) Hit Total Coverage
Test: hostapd hwsim test run 1388338050 Lines: 103 178 57.9 %
Date: 2013-12-29 Functions: 11 13 84.6 %
Branches: 32 77 41.6 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * UPnP WPS Device - Event processing
       3                 :            :  * Copyright (c) 2000-2003 Intel Corporation
       4                 :            :  * Copyright (c) 2006-2007 Sony Corporation
       5                 :            :  * Copyright (c) 2008-2009 Atheros Communications
       6                 :            :  * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
       7                 :            :  *
       8                 :            :  * See wps_upnp.c for more details on licensing and code history.
       9                 :            :  */
      10                 :            : 
      11                 :            : #include "includes.h"
      12                 :            : #include <assert.h>
      13                 :            : 
      14                 :            : #include "common.h"
      15                 :            : #include "eloop.h"
      16                 :            : #include "uuid.h"
      17                 :            : #include "http_client.h"
      18                 :            : #include "wps_defs.h"
      19                 :            : #include "wps_upnp.h"
      20                 :            : #include "wps_upnp_i.h"
      21                 :            : 
      22                 :            : /*
      23                 :            :  * Event message generation (to subscribers)
      24                 :            :  *
      25                 :            :  * We make a separate copy for each message for each subscriber. This memory
      26                 :            :  * wasted could be limited (adding code complexity) by sharing copies, keeping
      27                 :            :  * a usage count and freeing when zero.
      28                 :            :  *
      29                 :            :  * Sending a message requires using a HTTP over TCP NOTIFY
      30                 :            :  * (like a PUT) which requires a number of states..
      31                 :            :  */
      32                 :            : 
      33                 :            : #define MAX_EVENTS_QUEUED 20   /* How far behind queued events */
      34                 :            : #define MAX_FAILURES 10 /* Drop subscription after this many failures */
      35                 :            : 
      36                 :            : /* How long to wait before sending event */
      37                 :            : #define EVENT_DELAY_SECONDS 0
      38                 :            : #define EVENT_DELAY_MSEC 0
      39                 :            : 
      40                 :            : /*
      41                 :            :  * Event information that we send to each subscriber is remembered in this
      42                 :            :  * struct. The event cannot be sent by simple UDP; it has to be sent by a HTTP
      43                 :            :  * over TCP transaction which requires various states.. It may also need to be
      44                 :            :  * retried at a different address (if more than one is available).
      45                 :            :  *
      46                 :            :  * TODO: As an optimization we could share data between subscribers.
      47                 :            :  */
      48                 :            : struct wps_event_ {
      49                 :            :         struct dl_list list;
      50                 :            :         struct subscription *s;         /* parent */
      51                 :            :         unsigned subscriber_sequence;   /* which event for this subscription*/
      52                 :            :         unsigned int retry;             /* which retry */
      53                 :            :         struct subscr_addr *addr;       /* address to connect to */
      54                 :            :         struct wpabuf *data;            /* event data to send */
      55                 :            :         struct http_client *http_event;
      56                 :            : };
      57                 :            : 
      58                 :            : 
      59                 :            : /* event_clean -- clean sockets etc. of event
      60                 :            :  * Leaves data, retry count etc. alone.
      61                 :            :  */
      62                 :         20 : static void event_clean(struct wps_event_ *e)
      63                 :            : {
      64         [ +  - ]:         20 :         if (e->s->current_event == e)
      65                 :         20 :                 e->s->current_event = NULL;
      66                 :         20 :         http_client_free(e->http_event);
      67                 :         20 :         e->http_event = NULL;
      68                 :         20 : }
      69                 :            : 
      70                 :            : 
      71                 :            : /* event_delete -- delete single unqueued event
      72                 :            :  * (be sure to dequeue first if need be)
      73                 :            :  */
      74                 :         20 : static void event_delete(struct wps_event_ *e)
      75                 :            : {
      76                 :         20 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Delete event %p", e);
      77                 :         20 :         event_clean(e);
      78                 :         20 :         wpabuf_free(e->data);
      79                 :         20 :         os_free(e);
      80                 :         20 : }
      81                 :            : 
      82                 :            : 
      83                 :            : /* event_dequeue -- get next event from the queue
      84                 :            :  * Returns NULL if empty.
      85                 :            :  */
      86                 :         23 : static struct wps_event_ *event_dequeue(struct subscription *s)
      87                 :            : {
      88                 :            :         struct wps_event_ *e;
      89         [ +  + ]:         23 :         e = dl_list_first(&s->event_queue, struct wps_event_, list);
      90         [ +  + ]:         23 :         if (e) {
      91                 :         20 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for "
      92                 :            :                            "subscription %p", e, s);
      93                 :         20 :                 dl_list_del(&e->list);
      94                 :            :         }
      95                 :         23 :         return e;
      96                 :            : }
      97                 :            : 
      98                 :            : 
      99                 :            : /* event_delete_all -- delete entire event queue and current event */
     100                 :          3 : void event_delete_all(struct subscription *s)
     101                 :            : {
     102                 :            :         struct wps_event_ *e;
     103         [ -  + ]:          3 :         while ((e = event_dequeue(s)) != NULL)
     104                 :          0 :                 event_delete(e);
     105         [ -  + ]:          3 :         if (s->current_event) {
     106                 :          0 :                 event_delete(s->current_event);
     107                 :            :                 /* will set: s->current_event = NULL;  */
     108                 :            :         }
     109                 :          3 : }
     110                 :            : 
     111                 :            : 
     112                 :            : /**
     113                 :            :  * event_retry - Called when we had a failure delivering event msg
     114                 :            :  * @e: Event
     115                 :            :  * @do_next_address: skip address e.g. on connect fail
     116                 :            :  */
     117                 :          0 : static void event_retry(struct wps_event_ *e, int do_next_address)
     118                 :            : {
     119                 :          0 :         struct subscription *s = e->s;
     120                 :          0 :         struct upnp_wps_device_sm *sm = s->sm;
     121                 :            : 
     122                 :          0 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p",
     123                 :            :                    e, s);
     124                 :          0 :         event_clean(e);
     125                 :            :         /* will set: s->current_event = NULL; */
     126                 :            : 
     127         [ #  # ]:          0 :         if (do_next_address) {
     128                 :          0 :                 e->retry++;
     129                 :          0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry);
     130                 :            :         }
     131         [ #  # ]:          0 :         if (e->retry >= dl_list_len(&s->addr_list)) {
     132                 :          0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
     133                 :          0 :                            "for %s", e->addr->domain_and_port);
     134                 :          0 :                 event_delete(e);
     135                 :          0 :                 s->last_event_failed = 1;
     136         [ #  # ]:          0 :                 if (!dl_list_empty(&s->event_queue))
     137                 :          0 :                         event_send_all_later(s->sm);
     138                 :          0 :                 return;
     139                 :            :         }
     140                 :          0 :         dl_list_add(&s->event_queue, &e->list);
     141                 :          0 :         event_send_all_later(sm);
     142                 :            : }
     143                 :            : 
     144                 :            : 
     145                 :         20 : static struct wpabuf * event_build_message(struct wps_event_ *e)
     146                 :            : {
     147                 :            :         struct wpabuf *buf;
     148                 :            :         char *b;
     149                 :            : 
     150                 :         20 :         buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
     151         [ -  + ]:         20 :         if (buf == NULL)
     152                 :          0 :                 return NULL;
     153                 :         20 :         wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
     154                 :         20 :         wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
     155                 :         20 :         wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
     156                 :         20 :         wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
     157                 :            :                        "NT: upnp:event\r\n"
     158                 :            :                        "NTS: upnp:propchange\r\n");
     159                 :         20 :         wpabuf_put_str(buf, "SID: uuid:");
     160                 :         20 :         b = wpabuf_put(buf, 0);
     161                 :         20 :         uuid_bin2str(e->s->uuid, b, 80);
     162                 :         20 :         wpabuf_put(buf, os_strlen(b));
     163                 :         20 :         wpabuf_put_str(buf, "\r\n");
     164                 :         20 :         wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
     165                 :         20 :         wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
     166                 :         20 :                       (int) wpabuf_len(e->data));
     167                 :         20 :         wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
     168                 :         20 :         wpabuf_put_buf(buf, e->data);
     169                 :         20 :         return buf;
     170                 :            : }
     171                 :            : 
     172                 :            : 
     173                 :          0 : static void event_addr_failure(struct wps_event_ *e)
     174                 :            : {
     175                 :          0 :         struct subscription *s = e->s;
     176                 :            : 
     177                 :          0 :         e->addr->num_failures++;
     178                 :          0 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s "
     179                 :            :                    "(num_failures=%u)",
     180                 :          0 :                    e, e->addr->domain_and_port, e->addr->num_failures);
     181                 :            : 
     182         [ #  # ]:          0 :         if (e->addr->num_failures < MAX_FAILURES) {
     183                 :            :                 /* Try other addresses, if available */
     184                 :          0 :                 event_retry(e, 1);
     185                 :          0 :                 return;
     186                 :            :         }
     187                 :            : 
     188                 :            :         /*
     189                 :            :          * If other side doesn't like what we say, forget about them.
     190                 :            :          * (There is no way to tell other side that we are dropping them...).
     191                 :            :          */
     192                 :          0 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription %p "
     193                 :          0 :                    "address %s due to errors", s, e->addr->domain_and_port);
     194                 :          0 :         dl_list_del(&e->addr->list);
     195                 :          0 :         subscr_addr_delete(e->addr);
     196                 :          0 :         e->addr = NULL;
     197                 :            : 
     198         [ #  # ]:          0 :         if (dl_list_empty(&s->addr_list)) {
     199                 :            :                 /* if we've given up on all addresses */
     200                 :          0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Removing subscription %p "
     201                 :            :                            "with no addresses", s);
     202                 :          0 :                 dl_list_del(&s->list);
     203                 :          0 :                 subscription_destroy(s);
     204                 :          0 :                 return;
     205                 :            :         }
     206                 :            : 
     207                 :            :         /* Try other addresses, if available */
     208                 :          0 :         event_retry(e, 0);
     209                 :            : }
     210                 :            : 
     211                 :            : 
     212                 :         20 : static void event_http_cb(void *ctx, struct http_client *c,
     213                 :            :                           enum http_client_event event)
     214                 :            : {
     215                 :         20 :         struct wps_event_ *e = ctx;
     216                 :         20 :         struct subscription *s = e->s;
     217                 :            : 
     218                 :         20 :         wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p "
     219                 :            :                    "event=%d", e, c, event);
     220   [ +  -  -  -  :         20 :         switch (event) {
                      - ]
     221                 :            :         case HTTP_CLIENT_OK:
     222                 :         20 :                 wpa_printf(MSG_DEBUG,
     223                 :            :                            "WPS UPnP: Got event %p reply OK from %s",
     224                 :         20 :                            e, e->addr->domain_and_port);
     225                 :         20 :                 e->addr->num_failures = 0;
     226                 :         20 :                 s->last_event_failed = 0;
     227                 :         20 :                 event_delete(e);
     228                 :            : 
     229                 :            :                 /* Schedule sending more if there is more to send */
     230         [ -  + ]:         20 :                 if (!dl_list_empty(&s->event_queue))
     231                 :          0 :                         event_send_all_later(s->sm);
     232                 :         20 :                 break;
     233                 :            :         case HTTP_CLIENT_FAILED:
     234                 :          0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure");
     235                 :          0 :                 event_addr_failure(e);
     236                 :          0 :                 break;
     237                 :            :         case HTTP_CLIENT_INVALID_REPLY:
     238                 :          0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid reply");
     239                 :          0 :                 event_addr_failure(e);
     240                 :          0 :                 break;
     241                 :            :         case HTTP_CLIENT_TIMEOUT:
     242                 :          0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
     243                 :          0 :                 event_addr_failure(e);
     244                 :          0 :                 break;
     245                 :            :         }
     246                 :         20 : }
     247                 :            : 
     248                 :            : 
     249                 :            : /* event_send_start -- prepare to send a event message to subscriber
     250                 :            :  *
     251                 :            :  * This gets complicated because:
     252                 :            :  * -- The message is sent via TCP and we have to keep the stream open
     253                 :            :  *      for 30 seconds to get a response... then close it.
     254                 :            :  * -- But we might have other event happen in the meantime...
     255                 :            :  *      we have to queue them, if we lose them then the subscriber will
     256                 :            :  *      be forced to unsubscribe and subscribe again.
     257                 :            :  * -- If multiple URLs are provided then we are supposed to try successive
     258                 :            :  *      ones after 30 second timeout.
     259                 :            :  * -- The URLs might use domain names instead of dotted decimal addresses,
     260                 :            :  *      and resolution of those may cause unwanted sleeping.
     261                 :            :  * -- Doing the initial TCP connect can take a while, so we have to come
     262                 :            :  *      back after connection and then send the data.
     263                 :            :  *
     264                 :            :  * Returns nonzero on error;
     265                 :            :  *
     266                 :            :  * Prerequisite: No current event send (s->current_event == NULL)
     267                 :            :  *      and non-empty queue.
     268                 :            :  */
     269                 :         20 : static int event_send_start(struct subscription *s)
     270                 :            : {
     271                 :            :         struct wps_event_ *e;
     272                 :            :         unsigned int itry;
     273                 :            :         struct wpabuf *buf;
     274                 :            : 
     275                 :            :         /*
     276                 :            :          * Assume we are called ONLY with no current event and ONLY with
     277                 :            :          * nonempty event queue and ONLY with at least one address to send to.
     278                 :            :          */
     279         [ -  + ]:         20 :         if (dl_list_empty(&s->addr_list))
     280                 :          0 :                 return -1;
     281         [ -  + ]:         20 :         if (s->current_event)
     282                 :          0 :                 return -1;
     283         [ -  + ]:         20 :         if (dl_list_empty(&s->event_queue))
     284                 :          0 :                 return -1;
     285                 :            : 
     286                 :         20 :         s->current_event = e = event_dequeue(s);
     287                 :            : 
     288                 :            :         /* Use address according to number of retries */
     289                 :         20 :         itry = 0;
     290         [ +  - ]:         20 :         dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list)
     291         [ +  - ]:         20 :                 if (itry++ == e->retry)
     292                 :         20 :                         break;
     293         [ -  + ]:         20 :         if (itry < e->retry)
     294                 :          0 :                 return -1;
     295                 :            : 
     296                 :         20 :         buf = event_build_message(e);
     297         [ -  + ]:         20 :         if (buf == NULL) {
     298                 :          0 :                 event_retry(e, 0);
     299                 :          0 :                 return -1;
     300                 :            :         }
     301                 :            : 
     302                 :         20 :         e->http_event = http_client_addr(&e->addr->saddr, buf, 0,
     303                 :            :                                          event_http_cb, e);
     304         [ -  + ]:         20 :         if (e->http_event == NULL) {
     305                 :          0 :                 wpabuf_free(buf);
     306                 :          0 :                 event_retry(e, 0);
     307                 :          0 :                 return -1;
     308                 :            :         }
     309                 :            : 
     310                 :         20 :         return 0;
     311                 :            : }
     312                 :            : 
     313                 :            : 
     314                 :            : /* event_send_all_later_handler -- actually send events as needed */
     315                 :         20 : static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
     316                 :            : {
     317                 :         20 :         struct upnp_wps_device_sm *sm = user_ctx;
     318                 :            :         struct subscription *s, *tmp;
     319                 :         20 :         int nerrors = 0;
     320                 :            : 
     321                 :         20 :         sm->event_send_all_queued = 0;
     322         [ +  + ]:         40 :         dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
     323                 :            :                               list) {
     324   [ +  -  +  - ]:         40 :                 if (s->current_event == NULL /* not busy */ &&
     325                 :         20 :                     !dl_list_empty(&s->event_queue) /* more to do */) {
     326         [ -  + ]:         20 :                         if (event_send_start(s))
     327                 :          0 :                                 nerrors++;
     328                 :            :                 }
     329                 :            :         }
     330                 :            : 
     331         [ -  + ]:         20 :         if (nerrors) {
     332                 :            :                 /* Try again later */
     333                 :          0 :                 event_send_all_later(sm);
     334                 :            :         }
     335                 :         20 : }
     336                 :            : 
     337                 :            : 
     338                 :            : /* event_send_all_later -- schedule sending events to all subscribers
     339                 :            :  * that need it.
     340                 :            :  * This avoids two problems:
     341                 :            :  * -- After getting a subscription, we should not send the first event
     342                 :            :  *      until after our reply is fully queued to be sent back,
     343                 :            :  * -- Possible stack depth or infinite recursion issues.
     344                 :            :  */
     345                 :         23 : void event_send_all_later(struct upnp_wps_device_sm *sm)
     346                 :            : {
     347                 :            :         /*
     348                 :            :          * The exact time in the future isn't too important. Waiting a bit
     349                 :            :          * might let us do several together.
     350                 :            :          */
     351         [ +  + ]:         23 :         if (sm->event_send_all_queued)
     352                 :         23 :                 return;
     353                 :         20 :         sm->event_send_all_queued = 1;
     354                 :         20 :         eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
     355                 :            :                                event_send_all_later_handler, NULL, sm);
     356                 :            : }
     357                 :            : 
     358                 :            : 
     359                 :            : /* event_send_stop_all -- cleanup */
     360                 :          4 : void event_send_stop_all(struct upnp_wps_device_sm *sm)
     361                 :            : {
     362         [ -  + ]:          4 :         if (sm->event_send_all_queued)
     363                 :          0 :                 eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
     364                 :          4 :         sm->event_send_all_queued = 0;
     365                 :          4 : }
     366                 :            : 
     367                 :            : 
     368                 :            : /**
     369                 :            :  * event_add - Add a new event to a queue
     370                 :            :  * @s: Subscription
     371                 :            :  * @data: Event data (is copied; caller retains ownership)
     372                 :            :  * @probereq: Whether this is a Probe Request event
     373                 :            :  * Returns: 0 on success, -1 on error, 1 on max event queue limit reached
     374                 :            :  */
     375                 :         20 : int event_add(struct subscription *s, const struct wpabuf *data, int probereq)
     376                 :            : {
     377                 :            :         struct wps_event_ *e;
     378                 :            :         unsigned int len;
     379                 :            : 
     380                 :         20 :         len = dl_list_len(&s->event_queue);
     381         [ -  + ]:         20 :         if (len >= MAX_EVENTS_QUEUED) {
     382                 :          0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
     383                 :            :                            "subscriber %p", s);
     384         [ #  # ]:          0 :                 if (probereq)
     385                 :          0 :                         return 1;
     386                 :            : 
     387                 :            :                 /* Drop oldest entry to allow EAP event to be stored. */
     388                 :          0 :                 e = event_dequeue(s);
     389         [ #  # ]:          0 :                 if (!e)
     390                 :          0 :                         return 1;
     391                 :          0 :                 event_delete(e);
     392                 :            :         }
     393                 :            : 
     394 [ -  + ][ #  # ]:         20 :         if (s->last_event_failed && probereq && len > 0) {
                 [ #  # ]
     395                 :            :                 /*
     396                 :            :                  * Avoid queuing frames for subscribers that may have left
     397                 :            :                  * without unsubscribing.
     398                 :            :                  */
     399                 :          0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe "
     400                 :            :                            "Request frames for subscription %p since last "
     401                 :            :                            "delivery failed", s);
     402                 :          0 :                 return -1;
     403                 :            :         }
     404                 :            : 
     405                 :         20 :         e = os_zalloc(sizeof(*e));
     406         [ -  + ]:         20 :         if (e == NULL)
     407                 :          0 :                 return -1;
     408                 :         20 :         dl_list_init(&e->list);
     409                 :         20 :         e->s = s;
     410                 :         20 :         e->data = wpabuf_dup(data);
     411         [ -  + ]:         20 :         if (e->data == NULL) {
     412                 :          0 :                 os_free(e);
     413                 :          0 :                 return -1;
     414                 :            :         }
     415                 :         20 :         e->subscriber_sequence = s->next_subscriber_sequence++;
     416         [ -  + ]:         20 :         if (s->next_subscriber_sequence == 0)
     417                 :          0 :                 s->next_subscriber_sequence++;
     418                 :         20 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p "
     419                 :            :                    "(queue len %u)", e, s, len + 1);
     420                 :         20 :         dl_list_add_tail(&s->event_queue, &e->list);
     421                 :         20 :         event_send_all_later(s->sm);
     422                 :         20 :         return 0;
     423                 :            : }

Generated by: LCOV version 1.9