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 1422976643 Lines: 132 178 74.2 %
Date: 2015-02-03 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          67 : static void event_clean(struct wps_event_ *e)
      63             : {
      64          67 :         if (e->s->current_event == e)
      65          64 :                 e->s->current_event = NULL;
      66          67 :         http_client_free(e->http_event);
      67          67 :         e->http_event = NULL;
      68          67 : }
      69             : 
      70             : 
      71             : /* event_delete -- delete single unqueued event
      72             :  * (be sure to dequeue first if need be)
      73             :  */
      74          57 : static void event_delete(struct wps_event_ *e)
      75             : {
      76          57 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Delete event %p", e);
      77          57 :         event_clean(e);
      78          57 :         wpabuf_free(e->data);
      79          57 :         os_free(e);
      80          57 : }
      81             : 
      82             : 
      83             : /* event_dequeue -- get next event from the queue
      84             :  * Returns NULL if empty.
      85             :  */
      86          76 : static struct wps_event_ *event_dequeue(struct subscription *s)
      87             : {
      88             :         struct wps_event_ *e;
      89          76 :         e = dl_list_first(&s->event_queue, struct wps_event_, list);
      90          76 :         if (e) {
      91          64 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for "
      92             :                            "subscription %p", e, s);
      93          64 :                 dl_list_del(&e->list);
      94             :         }
      95          76 :         return e;
      96             : }
      97             : 
      98             : 
      99             : /* event_delete_all -- delete entire event queue and current event */
     100          12 : void event_delete_all(struct subscription *s)
     101             : {
     102             :         struct wps_event_ *e;
     103          24 :         while ((e = event_dequeue(s)) != NULL)
     104           0 :                 event_delete(e);
     105          12 :         if (s->current_event) {
     106           0 :                 event_delete(s->current_event);
     107             :                 /* will set: s->current_event = NULL;  */
     108             :         }
     109          12 : }
     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          10 : static void event_retry(struct wps_event_ *e, int do_next_address)
     118             : {
     119          10 :         struct subscription *s = e->s;
     120          10 :         struct upnp_wps_device_sm *sm = s->sm;
     121             : 
     122          10 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p",
     123             :                    e, s);
     124          10 :         event_clean(e);
     125             :         /* will set: s->current_event = NULL; */
     126             : 
     127          10 :         if (do_next_address) {
     128          10 :                 e->retry++;
     129          10 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry);
     130             :         }
     131          10 :         if (e->retry >= dl_list_len(&s->addr_list)) {
     132           3 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
     133           3 :                            "for %s", e->addr->domain_and_port);
     134           3 :                 event_delete(e);
     135           3 :                 s->last_event_failed = 1;
     136           3 :                 if (!dl_list_empty(&s->event_queue))
     137           0 :                         event_send_all_later(s->sm);
     138          13 :                 return;
     139             :         }
     140           7 :         dl_list_add(&s->event_queue, &e->list);
     141           7 :         event_send_all_later(sm);
     142             : }
     143             : 
     144             : 
     145          64 : static struct wpabuf * event_build_message(struct wps_event_ *e)
     146             : {
     147             :         struct wpabuf *buf;
     148             :         char *b;
     149             : 
     150          64 :         buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
     151          64 :         if (buf == NULL)
     152           0 :                 return NULL;
     153          64 :         wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
     154          64 :         wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
     155          64 :         wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
     156          64 :         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          64 :         wpabuf_put_str(buf, "SID: uuid:");
     160          64 :         b = wpabuf_put(buf, 0);
     161          64 :         uuid_bin2str(e->s->uuid, b, 80);
     162          64 :         wpabuf_put(buf, os_strlen(b));
     163          64 :         wpabuf_put_str(buf, "\r\n");
     164          64 :         wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
     165          64 :         wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
     166          64 :                       (int) wpabuf_len(e->data));
     167          64 :         wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
     168          64 :         wpabuf_put_buf(buf, e->data);
     169          64 :         return buf;
     170             : }
     171             : 
     172             : 
     173          10 : static void event_addr_failure(struct wps_event_ *e)
     174             : {
     175          10 :         struct subscription *s = e->s;
     176             : 
     177          10 :         e->addr->num_failures++;
     178          20 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s "
     179             :                    "(num_failures=%u)",
     180          20 :                    e, e->addr->domain_and_port, e->addr->num_failures);
     181             : 
     182          10 :         if (e->addr->num_failures < MAX_FAILURES) {
     183             :                 /* Try other addresses, if available */
     184          10 :                 event_retry(e, 1);
     185          10 :                 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          64 : static void event_http_cb(void *ctx, struct http_client *c,
     213             :                           enum http_client_event event)
     214             : {
     215          64 :         struct wps_event_ *e = ctx;
     216          64 :         struct subscription *s = e->s;
     217             : 
     218          64 :         wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p "
     219             :                    "event=%d", e, c, event);
     220          64 :         switch (event) {
     221             :         case HTTP_CLIENT_OK:
     222          54 :                 wpa_printf(MSG_DEBUG,
     223             :                            "WPS UPnP: Got event %p reply OK from %s",
     224          54 :                            e, e->addr->domain_and_port);
     225          54 :                 e->addr->num_failures = 0;
     226          54 :                 s->last_event_failed = 0;
     227          54 :                 event_delete(e);
     228             : 
     229             :                 /* Schedule sending more if there is more to send */
     230          54 :                 if (!dl_list_empty(&s->event_queue))
     231           1 :                         event_send_all_later(s->sm);
     232          54 :                 break;
     233             :         case HTTP_CLIENT_FAILED:
     234          10 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure");
     235          10 :                 event_addr_failure(e);
     236          10 :                 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          64 : }
     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          64 : 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          64 :         if (dl_list_empty(&s->addr_list))
     280           0 :                 return -1;
     281          64 :         if (s->current_event)
     282           0 :                 return -1;
     283          64 :         if (dl_list_empty(&s->event_queue))
     284           0 :                 return -1;
     285             : 
     286          64 :         s->current_event = e = event_dequeue(s);
     287             : 
     288             :         /* Use address according to number of retries */
     289          64 :         itry = 0;
     290          92 :         dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list)
     291          92 :                 if (itry++ == e->retry)
     292          64 :                         break;
     293          64 :         if (itry < e->retry)
     294           0 :                 return -1;
     295             : 
     296          64 :         buf = event_build_message(e);
     297          64 :         if (buf == NULL) {
     298           0 :                 event_retry(e, 0);
     299           0 :                 return -1;
     300             :         }
     301             : 
     302          64 :         e->http_event = http_client_addr(&e->addr->saddr, buf, 0,
     303             :                                          event_http_cb, e);
     304          64 :         if (e->http_event == NULL) {
     305           0 :                 wpabuf_free(buf);
     306           0 :                 event_retry(e, 0);
     307           0 :                 return -1;
     308             :         }
     309             : 
     310          64 :         return 0;
     311             : }
     312             : 
     313             : 
     314             : /* event_send_all_later_handler -- actually send events as needed */
     315          65 : static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
     316             : {
     317          65 :         struct upnp_wps_device_sm *sm = user_ctx;
     318             :         struct subscription *s, *tmp;
     319          65 :         int nerrors = 0;
     320             : 
     321          65 :         sm->event_send_all_queued = 0;
     322         130 :         dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
     323             :                               list) {
     324         129 :                 if (s->current_event == NULL /* not busy */ &&
     325          64 :                     !dl_list_empty(&s->event_queue) /* more to do */) {
     326          64 :                         if (event_send_start(s))
     327           0 :                                 nerrors++;
     328             :                 }
     329             :         }
     330             : 
     331          65 :         if (nerrors) {
     332             :                 /* Try again later */
     333           0 :                 event_send_all_later(sm);
     334             :         }
     335          65 : }
     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          77 : 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          77 :         if (sm->event_send_all_queued)
     352          89 :                 return;
     353          65 :         sm->event_send_all_queued = 1;
     354          65 :         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          16 : void event_send_stop_all(struct upnp_wps_device_sm *sm)
     361             : {
     362          16 :         if (sm->event_send_all_queued)
     363           0 :                 eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
     364          16 :         sm->event_send_all_queued = 0;
     365          16 : }
     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          57 : int event_add(struct subscription *s, const struct wpabuf *data, int probereq)
     376             : {
     377             :         struct wps_event_ *e;
     378             :         unsigned int len;
     379             : 
     380          57 :         len = dl_list_len(&s->event_queue);
     381          57 :         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          57 :         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          57 :         e = os_zalloc(sizeof(*e));
     406          57 :         if (e == NULL)
     407           0 :                 return -1;
     408          57 :         dl_list_init(&e->list);
     409          57 :         e->s = s;
     410          57 :         e->data = wpabuf_dup(data);
     411          57 :         if (e->data == NULL) {
     412           0 :                 os_free(e);
     413           0 :                 return -1;
     414             :         }
     415          57 :         e->subscriber_sequence = s->next_subscriber_sequence++;
     416          57 :         if (s->next_subscriber_sequence == 0)
     417           0 :                 s->next_subscriber_sequence++;
     418          57 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p "
     419             :                    "(queue len %u)", e, s, len + 1);
     420          57 :         dl_list_add_tail(&s->event_queue, &e->list);
     421          57 :         event_send_all_later(s->sm);
     422          57 :         return 0;
     423             : }

Generated by: LCOV version 1.10