LCOV - code coverage report
Current view: top level - src/wps - wps_upnp_event.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1443382998 Lines: 165 176 93.8 %
Date: 2015-09-27 Functions: 13 13 100.0 %

          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         811 : static void event_clean(struct wps_event_ *e)
      63             : {
      64         811 :         if (e->s->current_event == e)
      65         764 :                 e->s->current_event = NULL;
      66         811 :         http_client_free(e->http_event);
      67         811 :         e->http_event = NULL;
      68         811 : }
      69             : 
      70             : 
      71             : /* event_delete -- delete single unqueued event
      72             :  * (be sure to dequeue first if need be)
      73             :  */
      74         703 : static void event_delete(struct wps_event_ *e)
      75             : {
      76         703 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Delete event %p", e);
      77         703 :         event_clean(e);
      78         703 :         wpabuf_free(e->data);
      79         703 :         os_free(e);
      80         703 : }
      81             : 
      82             : 
      83             : /* event_dequeue -- get next event from the queue
      84             :  * Returns NULL if empty.
      85             :  */
      86         819 : static struct wps_event_ *event_dequeue(struct subscription *s)
      87             : {
      88             :         struct wps_event_ *e;
      89         819 :         e = dl_list_first(&s->event_queue, struct wps_event_, list);
      90         819 :         if (e) {
      91         775 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for "
      92             :                            "subscription %p", e, s);
      93         775 :                 dl_list_del(&e->list);
      94             :         }
      95         819 :         return e;
      96             : }
      97             : 
      98             : 
      99             : /* event_delete_all -- delete entire event queue and current event */
     100          44 : void event_delete_all(struct subscription *s)
     101             : {
     102             :         struct wps_event_ *e;
     103          98 :         while ((e = event_dequeue(s)) != NULL)
     104          10 :                 event_delete(e);
     105          44 :         if (s->current_event) {
     106           2 :                 event_delete(s->current_event);
     107             :                 /* will set: s->current_event = NULL;  */
     108             :         }
     109          44 : }
     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         108 : static void event_retry(struct wps_event_ *e, int do_next_address)
     118             : {
     119         108 :         struct subscription *s = e->s;
     120         108 :         struct upnp_wps_device_sm *sm = s->sm;
     121             : 
     122         108 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p",
     123             :                    e, s);
     124         108 :         event_clean(e);
     125             :         /* will set: s->current_event = NULL; */
     126             : 
     127         108 :         if (do_next_address) {
     128          99 :                 e->retry++;
     129          99 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry);
     130             :         }
     131         108 :         if (e->retry >= dl_list_len(&s->addr_list)) {
     132          36 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
     133          36 :                            "for %s", e->addr->domain_and_port);
     134          36 :                 event_delete(e);
     135          36 :                 s->last_event_failed = 1;
     136          36 :                 if (!dl_list_empty(&s->event_queue))
     137          14 :                         event_send_all_later(s->sm);
     138         144 :                 return;
     139             :         }
     140          72 :         dl_list_add(&s->event_queue, &e->list);
     141          72 :         event_send_all_later(sm);
     142             : }
     143             : 
     144             : 
     145         764 : static struct wpabuf * event_build_message(struct wps_event_ *e)
     146             : {
     147             :         struct wpabuf *buf;
     148             :         char *b;
     149             : 
     150         764 :         buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
     151         764 :         if (buf == NULL)
     152           1 :                 return NULL;
     153         763 :         wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
     154         763 :         wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
     155         763 :         wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
     156         763 :         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         763 :         wpabuf_put_str(buf, "SID: uuid:");
     160         763 :         b = wpabuf_put(buf, 0);
     161         763 :         uuid_bin2str(e->s->uuid, b, 80);
     162         763 :         wpabuf_put(buf, os_strlen(b));
     163         763 :         wpabuf_put_str(buf, "\r\n");
     164         763 :         wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
     165         763 :         wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
     166         763 :                       (int) wpabuf_len(e->data));
     167         763 :         wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
     168         763 :         wpabuf_put_buf(buf, e->data);
     169         763 :         return buf;
     170             : }
     171             : 
     172             : 
     173         108 : static void event_addr_failure(struct wps_event_ *e)
     174             : {
     175         108 :         struct subscription *s = e->s;
     176             : 
     177         108 :         e->addr->num_failures++;
     178         216 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s "
     179             :                    "(num_failures=%u)",
     180         216 :                    e, e->addr->domain_and_port, e->addr->num_failures);
     181             : 
     182         108 :         if (e->addr->num_failures < MAX_FAILURES) {
     183             :                 /* Try other addresses, if available */
     184          99 :                 event_retry(e, 1);
     185          99 :                 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           9 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription %p "
     193           9 :                    "address %s due to errors", s, e->addr->domain_and_port);
     194           9 :         dl_list_del(&e->addr->list);
     195           9 :         subscr_addr_delete(e->addr);
     196           9 :         e->addr = NULL;
     197             : 
     198           9 :         if (dl_list_empty(&s->addr_list)) {
     199             :                 /* if we've given up on all addresses */
     200           2 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Removing subscription %p "
     201             :                            "with no addresses", s);
     202           2 :                 dl_list_del(&s->list);
     203           2 :                 subscription_destroy(s);
     204           2 :                 return;
     205             :         }
     206             : 
     207             :         /* Try other addresses, if available */
     208           7 :         event_retry(e, 0);
     209             : }
     210             : 
     211             : 
     212         762 : static void event_http_cb(void *ctx, struct http_client *c,
     213             :                           enum http_client_event event)
     214             : {
     215         762 :         struct wps_event_ *e = ctx;
     216         762 :         struct subscription *s = e->s;
     217             : 
     218         762 :         wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p "
     219             :                    "event=%d", e, c, event);
     220         762 :         switch (event) {
     221             :         case HTTP_CLIENT_OK:
     222         654 :                 wpa_printf(MSG_DEBUG,
     223             :                            "WPS UPnP: Got event %p reply OK from %s",
     224         654 :                            e, e->addr->domain_and_port);
     225         654 :                 e->addr->num_failures = 0;
     226         654 :                 s->last_event_failed = 0;
     227         654 :                 event_delete(e);
     228             : 
     229             :                 /* Schedule sending more if there is more to send */
     230         654 :                 if (!dl_list_empty(&s->event_queue))
     231          11 :                         event_send_all_later(s->sm);
     232         654 :                 break;
     233             :         case HTTP_CLIENT_FAILED:
     234         108 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure");
     235         108 :                 event_addr_failure(e);
     236         108 :                 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         762 : }
     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         764 : 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        1528 :         if (dl_list_empty(&s->addr_list) ||
     280        1528 :             s->current_event ||
     281         764 :             dl_list_empty(&s->event_queue))
     282           0 :                 return -1;
     283             : 
     284         764 :         s->current_event = e = event_dequeue(s);
     285             : 
     286             :         /* Use address according to number of retries */
     287         764 :         itry = 0;
     288        1016 :         dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list)
     289        1016 :                 if (itry++ == e->retry)
     290         764 :                         break;
     291         764 :         if (itry < e->retry)
     292           0 :                 return -1;
     293             : 
     294         764 :         buf = event_build_message(e);
     295         764 :         if (buf == NULL) {
     296           1 :                 event_retry(e, 0);
     297           1 :                 return -1;
     298             :         }
     299             : 
     300         763 :         e->http_event = http_client_addr(&e->addr->saddr, buf, 0,
     301             :                                          event_http_cb, e);
     302         763 :         if (e->http_event == NULL) {
     303           1 :                 wpabuf_free(buf);
     304           1 :                 event_retry(e, 0);
     305           1 :                 return -1;
     306             :         }
     307             : 
     308         762 :         return 0;
     309             : }
     310             : 
     311             : 
     312             : /* event_send_all_later_handler -- actually send events as needed */
     313         787 : static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
     314             : {
     315         787 :         struct upnp_wps_device_sm *sm = user_ctx;
     316             :         struct subscription *s, *tmp;
     317         787 :         int nerrors = 0;
     318             : 
     319         787 :         sm->event_send_all_queued = 0;
     320        1604 :         dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
     321             :                               list) {
     322        1601 :                 if (s->current_event == NULL /* not busy */ &&
     323         784 :                     !dl_list_empty(&s->event_queue) /* more to do */) {
     324         764 :                         if (event_send_start(s))
     325           2 :                                 nerrors++;
     326             :                 }
     327             :         }
     328             : 
     329         787 :         if (nerrors) {
     330             :                 /* Try again later */
     331           2 :                 event_send_all_later(sm);
     332             :         }
     333         787 : }
     334             : 
     335             : 
     336             : /* event_send_all_later -- schedule sending events to all subscribers
     337             :  * that need it.
     338             :  * This avoids two problems:
     339             :  * -- After getting a subscription, we should not send the first event
     340             :  *      until after our reply is fully queued to be sent back,
     341             :  * -- Possible stack depth or infinite recursion issues.
     342             :  */
     343         839 : void event_send_all_later(struct upnp_wps_device_sm *sm)
     344             : {
     345             :         /*
     346             :          * The exact time in the future isn't too important. Waiting a bit
     347             :          * might let us do several together.
     348             :          */
     349         839 :         if (sm->event_send_all_queued)
     350         891 :                 return;
     351         787 :         sm->event_send_all_queued = 1;
     352         787 :         eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
     353             :                                event_send_all_later_handler, NULL, sm);
     354             : }
     355             : 
     356             : 
     357             : /* event_send_stop_all -- cleanup */
     358          37 : void event_send_stop_all(struct upnp_wps_device_sm *sm)
     359             : {
     360          37 :         if (sm->event_send_all_queued)
     361           0 :                 eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
     362          37 :         sm->event_send_all_queued = 0;
     363          37 : }
     364             : 
     365             : 
     366             : /**
     367             :  * event_add - Add a new event to a queue
     368             :  * @s: Subscription
     369             :  * @data: Event data (is copied; caller retains ownership)
     370             :  * @probereq: Whether this is a Probe Request event
     371             :  * Returns: 0 on success, -1 on error, 1 on max event queue limit reached
     372             :  */
     373         714 : int event_add(struct subscription *s, const struct wpabuf *data, int probereq)
     374             : {
     375             :         struct wps_event_ *e;
     376             :         unsigned int len;
     377             : 
     378         714 :         len = dl_list_len(&s->event_queue);
     379         714 :         if (len >= MAX_EVENTS_QUEUED) {
     380           8 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
     381             :                            "subscriber %p", s);
     382           8 :                 if (probereq)
     383           7 :                         return 1;
     384             : 
     385             :                 /* Drop oldest entry to allow EAP event to be stored. */
     386           1 :                 e = event_dequeue(s);
     387           1 :                 if (!e)
     388           0 :                         return 1;
     389           1 :                 event_delete(e);
     390             :         }
     391             : 
     392         707 :         if (s->last_event_failed && probereq && len > 0) {
     393             :                 /*
     394             :                  * Avoid queuing frames for subscribers that may have left
     395             :                  * without unsubscribing.
     396             :                  */
     397           1 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe "
     398             :                            "Request frames for subscription %p since last "
     399             :                            "delivery failed", s);
     400           1 :                 return -1;
     401             :         }
     402             : 
     403         706 :         e = os_zalloc(sizeof(*e));
     404         706 :         if (e == NULL)
     405           2 :                 return -1;
     406         704 :         dl_list_init(&e->list);
     407         704 :         e->s = s;
     408         704 :         e->data = wpabuf_dup(data);
     409         704 :         if (e->data == NULL) {
     410           1 :                 os_free(e);
     411           1 :                 return -1;
     412             :         }
     413         703 :         e->subscriber_sequence = s->next_subscriber_sequence++;
     414         703 :         if (s->next_subscriber_sequence == 0)
     415           0 :                 s->next_subscriber_sequence++;
     416         703 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p "
     417             :                    "(queue len %u)", e, s, len + 1);
     418         703 :         dl_list_add_tail(&s->event_queue, &e->list);
     419         703 :         event_send_all_later(s->sm);
     420         703 :         return 0;
     421             : }

Generated by: LCOV version 1.10