tighten formatting rules
[oweals/gnunet.git] / src / cadet / gnunet-service-cadet_dht.c
index 7d31603df8476670e86a24c7a3be93fbe5a15657..4e8ccbb0872a295ccba983007da71c25b94446f6 100644 (file)
@@ -1,80 +1,72 @@
 /*
      This file is part of GNUnet.
-     (C) 2013 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2013, 2017 GNUnet e.V.
 
-     GNUnet is free software; you can redistribute it and/or modify
-     it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 3, or (at your
-     option) any later version.
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     General Public License for more details.
+     Affero General Public License for more details.
 
-     You should have received a copy of the GNU General Public License
-     along with GNUnet; see the file COPYING.  If not, write to the
-     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-     Boston, MA 02111-1307, USA.
-*/
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @file cadet/gnunet-service-cadet_dht.c
+ * @brief Information we track per peer.
+ * @author Bartlomiej Polot
+ * @author Christian Grothoff
+ */
 
 #include "platform.h"
 #include "gnunet_util_lib.h"
-
 #include "gnunet_dht_service.h"
 #include "gnunet_statistics_service.h"
-
-#include "cadet_path.h"
+#include "gnunet-service-cadet.h"
 #include "gnunet-service-cadet_dht.h"
-#include "gnunet-service-cadet_peer.h"
 #include "gnunet-service-cadet_hello.h"
+#include "gnunet-service-cadet_peer.h"
+#include "gnunet-service-cadet_paths.h"
 
-#define LOG(level, ...) GNUNET_log_from (level,"cadet-dht",__VA_ARGS__)
+/**
+ * How long do we wait before first announcing our presence to the DHT.
+ * Used to wait for our HELLO to be available.  Note that we also get
+ * notifications when our HELLO is ready, so this is just the maximum
+ * we wait for the first notification.
+ */
+#define STARTUP_DELAY GNUNET_TIME_relative_multiply ( \
+    GNUNET_TIME_UNIT_MILLISECONDS, 500)
 
+/**
+ * How long do we wait after we get an updated HELLO before publishing?
+ * Allows for the HELLO to be updated again quickly, for example in
+ * case multiple addresses changed and we got a partial update.
+ */
+#define CHANGE_DELAY GNUNET_TIME_relative_multiply ( \
+    GNUNET_TIME_UNIT_MILLISECONDS, 100)
+
+
+#define LOG(level, ...) GNUNET_log_from (level, "cadet-dht", __VA_ARGS__)
 
-/******************************************************************************/
-/********************************   STRUCTS  **********************************/
-/******************************************************************************/
 
 /**
  * Handle for DHT searches.
  */
 struct GCD_search_handle
 {
-  /** DHT_GET handle. */
+  /**
+   * DHT_GET handle.
+   */
   struct GNUNET_DHT_GetHandle *dhtget;
-
-  /** Provided callback to call when a path is found. */
-  GCD_search_callback callback;
-
-  /** Provided closure. */
-  void *cls;
-
-  /** Peer ID searched for */
-  GNUNET_PEER_Id peer_id;
 };
 
 
-/******************************************************************************/
-/*******************************   GLOBALS  ***********************************/
-/******************************************************************************/
-
-/**
- * Global handle to the statistics service.
- */
-extern struct GNUNET_STATISTICS_Handle *stats;
-
-/**
- * Own ID (short value).
- */
-extern GNUNET_PEER_Id myid;
-
-/**
- * Own ID (full value).
- */
-extern struct GNUNET_PeerIdentity my_full_id;
-
 /**
  * Handle to use DHT.
  */
@@ -86,112 +78,19 @@ static struct GNUNET_DHT_Handle *dht_handle;
 static struct GNUNET_TIME_Relative id_announce_time;
 
 /**
- * DHT replication level, see DHT API: GNUNET_DHT_get_start, GNUNET_DHT_put.
+ * DHT replication level, see DHT API: #GNUNET_DHT_get_start(), #GNUNET_DHT_put().
  */
 static unsigned long long dht_replication_level;
 
 /**
  * Task to periodically announce itself in the network.
  */
-static GNUNET_SCHEDULER_TaskIdentifier announce_id_task;
+static struct GNUNET_SCHEDULER_Task *announce_id_task;
 
 /**
- * GET requests to stop on shutdown.
+ * Delay for the next ID announce.
  */
-static struct GNUNET_CONTAINER_MultiHashMap32 *get_requests;
-
-/******************************************************************************/
-/********************************   STATIC  ***********************************/
-/******************************************************************************/
-
-
-/**
- * Build a PeerPath from the paths returned from the DHT, reversing the paths
- * to obtain a local peer -> destination path and interning the peer ids.
- *
- * @return Newly allocated and created path
- *
- * FIXME refactor and use build_path_from_peer_ids
- */
-static struct CadetPeerPath *
-path_build_from_dht (const struct GNUNET_PeerIdentity *get_path,
-                     unsigned int get_path_length,
-                     const struct GNUNET_PeerIdentity *put_path,
-                     unsigned int put_path_length)
-{
-  struct CadetPeerPath *p;
-  GNUNET_PEER_Id id;
-  int i;
-
-  p = path_new (1);
-  p->peers[0] = myid;
-  GNUNET_PEER_change_rc (myid, 1);
-  i = get_path_length;
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "   GET has %d hops.\n", i);
-  for (i--; i >= 0; i--)
-  {
-    id = GNUNET_PEER_intern (&get_path[i]);
-    if (p->length > 0 && id == p->peers[p->length - 1])
-    {
-      LOG (GNUNET_ERROR_TYPE_DEBUG, "   Optimizing 1 hop out.\n");
-      GNUNET_PEER_change_rc (id, -1);
-    }
-    else
-    {
-      LOG (GNUNET_ERROR_TYPE_DEBUG, "   Adding from GET: %s.\n",
-                  GNUNET_i2s (&get_path[i]));
-      p->length++;
-      p->peers = GNUNET_realloc (p->peers, sizeof (GNUNET_PEER_Id) * p->length);
-      p->peers[p->length - 1] = id;
-    }
-  }
-  i = put_path_length;
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "   PUT has %d hops.\n", i);
-  for (i--; i >= 0; i--)
-  {
-    id = GNUNET_PEER_intern (&put_path[i]);
-    if (id == myid)
-    {
-      /* PUT path went through us, so discard the path up until now and start
-       * from here to get a much shorter (and loop-free) path.
-       */
-      path_destroy (p);
-      p = path_new (0);
-    }
-    if (p->length > 0 && id == p->peers[p->length - 1])
-    {
-      LOG (GNUNET_ERROR_TYPE_DEBUG, "   Optimizing 1 hop out.\n");
-      GNUNET_PEER_change_rc (id, -1);
-    }
-    else
-    {
-      LOG (GNUNET_ERROR_TYPE_DEBUG, "   Adding from PUT: %s.\n",
-                  GNUNET_i2s (&put_path[i]));
-      p->length++;
-      p->peers = GNUNET_realloc (p->peers, sizeof (GNUNET_PEER_Id) * p->length);
-      p->peers[p->length - 1] = id;
-    }
-  }
-#if CADET_DEBUG
-  if (get_path_length > 0)
-    LOG (GNUNET_ERROR_TYPE_DEBUG, "   (first of GET: %s)\n",
-                GNUNET_i2s (&get_path[0]));
-  if (put_path_length > 0)
-    LOG (GNUNET_ERROR_TYPE_DEBUG, "   (first of PUT: %s)\n",
-                GNUNET_i2s (&put_path[0]));
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "   In total: %d hops\n",
-              p->length);
-  for (i = 0; i < p->length; i++)
-  {
-    struct GNUNET_PeerIdentity peer_id;
-
-    GNUNET_PEER_resolve (p->peers[i], &peer_id);
-    LOG (GNUNET_ERROR_TYPE_DEBUG, "       %u: %s\n", p->peers[i],
-                GNUNET_i2s (&peer_id));
-  }
-#endif
-  return p;
-}
+static struct GNUNET_TIME_Relative announce_delay;
 
 
 /**
@@ -203,41 +102,43 @@ path_build_from_dht (const struct GNUNET_PeerIdentity *get_path,
  * @param exp when will this value expire
  * @param key key of the result
  * @param get_path path of the get request
- * @param get_path_length lenght of get_path
+ * @param get_path_length lenght of @a get_path
  * @param put_path path of the put request
- * @param put_path_length length of the put_path
+ * @param put_path_length length of the @a put_path
  * @param type type of the result
  * @param size number of bytes in data
  * @param data pointer to the result data
  */
 static void
 dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp,
-                    const struct GNUNET_HashCode * key,
+                    const struct GNUNET_HashCode *key,
                     const struct GNUNET_PeerIdentity *get_path,
                     unsigned int get_path_length,
                     const struct GNUNET_PeerIdentity *put_path,
-                    unsigned int put_path_length, enum GNUNET_BLOCK_Type type,
-                    size_t size, const void *data)
+                    unsigned int put_path_length,
+                    enum GNUNET_BLOCK_Type type,
+                    size_t size,
+                    const void *data)
 {
-  struct GCD_search_handle *h = cls;
-  struct GNUNET_HELLO_Message *hello;
-  struct CadetPeerPath *p;
+  const struct GNUNET_HELLO_Message *hello = data;
   struct CadetPeer *peer;
-  char *s;
-
-  p = path_build_from_dht (get_path, get_path_length,
-                           put_path, put_path_length);
-  s = path_2s (p);
-  LOG (GNUNET_ERROR_TYPE_INFO, "Got path from DHT: %s\n", s);
-  GNUNET_free_non_null (s);
-  peer = GCP_get_short (p->peers[p->length - 1]);
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "Got HELLO for %s\n", GCP_2s (peer));
-  h->callback (h->cls, p);
-  path_destroy (p);
-  hello = (struct GNUNET_HELLO_Message *) data;
-  GCP_set_hello (peer, hello);
-  GCP_try_connect (peer);
-  return;
+
+  GCPP_try_path_from_dht (get_path,
+                          get_path_length,
+                          put_path,
+                          put_path_length);
+  if ((size >= sizeof(struct GNUNET_HELLO_Message)) &&
+      (ntohs (hello->header.size) == size) &&
+      (size == GNUNET_HELLO_size (hello)))
+  {
+    peer = GCP_get (&put_path[0],
+                    GNUNET_YES);
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Got HELLO for %s\n",
+         GCP_2s (peer));
+    GCP_set_hello (peer,
+                   hello);
+  }
 }
 
 
@@ -245,44 +146,58 @@ dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp,
  * Periodically announce self id in the DHT
  *
  * @param cls closure
- * @param tc task context
  */
 static void
-announce_id (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+announce_id (void *cls)
 {
   struct GNUNET_HashCode phash;
   const struct GNUNET_HELLO_Message *hello;
   size_t size;
   struct GNUNET_TIME_Absolute expiration;
-  struct GNUNET_TIME_Relative retry_time;
+  struct GNUNET_TIME_Relative next_put;
 
-  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+  hello = GCH_get_mine ();
+  size = (NULL != hello) ? GNUNET_HELLO_size (hello) : 0;
+  if (0 == size)
   {
-    announce_id_task = GNUNET_SCHEDULER_NO_TASK;
-    return;
+    expiration = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
+                                           announce_delay);
+    announce_delay = GNUNET_TIME_STD_BACKOFF (announce_delay);
   }
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "Announce ID\n");
-
-  /* TODO
-   * - Set data expiration in function of X
-   * - Adapt X to churn
-   */
-  hello = GCH_get_mine ();
-  if (NULL == hello || (size = GNUNET_HELLO_size (hello)) == 0)
+  else
   {
-    /* Peerinfo gave us no hello yet, try again in a second. */
-    announce_id_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
-                                                     &announce_id, cls);
-    LOG (GNUNET_ERROR_TYPE_DEBUG, "  no hello, waiting!\n");
-    return;
+    expiration = GNUNET_HELLO_get_last_expiration (hello);
+    announce_delay = GNUNET_TIME_UNIT_SECONDS;
   }
-  expiration = GNUNET_HELLO_get_last_expiration (hello);
-  retry_time = GNUNET_TIME_absolute_get_remaining (expiration);
 
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "Hello %p size: %u\n", hello, size);
-  memset (&phash, 0, sizeof (phash));
-  memcpy (&phash, &my_full_id, sizeof (my_full_id));
-  GNUNET_DHT_put (dht_handle,   /* DHT handle */
+  /* Call again in id_announce_time, unless HELLO expires first,
+   * but wait at least 1s. */
+  next_put
+    = GNUNET_TIME_absolute_get_remaining (expiration);
+  next_put
+    = GNUNET_TIME_relative_min (next_put,
+                                id_announce_time);
+  next_put
+    = GNUNET_TIME_relative_max (next_put,
+                                GNUNET_TIME_UNIT_SECONDS);
+  announce_id_task
+    = GNUNET_SCHEDULER_add_delayed (next_put,
+                                    &announce_id,
+                                    cls);
+  GNUNET_STATISTICS_update (stats,
+                            "# DHT announce",
+                            1,
+                            GNUNET_NO);
+  memset (&phash,
+          0,
+          sizeof(phash));
+  GNUNET_memcpy (&phash,
+                 &my_full_id,
+                 sizeof(my_full_id));
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Announcing my HELLO (%u bytes) in the DHT\n",
+       size);
+  GNUNET_DHT_put (dht_handle,    /* DHT handle */
                   &phash,       /* Key to use */
                   dht_replication_level,     /* Replication level */
                   GNUNET_DHT_RO_RECORD_ROUTE
@@ -291,39 +206,28 @@ announce_id (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
                   size,  /* Size of the data */
                   (const char *) hello, /* Data itself */
                   expiration,  /* Data expiration */
-                  retry_time, /* Retry time */
                   NULL,         /* Continuation */
                   NULL);        /* Continuation closure */
-  announce_id_task =
-      GNUNET_SCHEDULER_add_delayed (id_announce_time, &announce_id, cls);
 }
 
+
 /**
- * Iterator over hash map entries and stop GET requests before disconnecting
- * from the DHT.
- *
- * @param cls Closure (unused)
- * @param key Current peer ID.
- * @param value Value in the hash map (GCD_search_handle).
- *
- * @return #GNUNET_YES, we should continue to iterate,
+ * Function called by the HELLO subsystem whenever OUR hello
+ * changes. Re-triggers the DHT PUT immediately.
  */
-int
-stop_get (void *cls,
-          uint32_t key,
-          void *value)
+void
+GCD_hello_update ()
 {
-  struct GCD_search_handle *h = value;
-
-  GCD_search_stop (h);
-  return GNUNET_YES;
+  if (NULL == announce_id_task)
+    return; /* too early */
+  GNUNET_SCHEDULER_cancel (announce_id_task);
+  announce_id_task
+    = GNUNET_SCHEDULER_add_delayed (CHANGE_DELAY,
+                                    &announce_id,
+                                    NULL);
 }
 
 
-/******************************************************************************/
-/********************************    API    ***********************************/
-/******************************************************************************/
-
 /**
  * Initialize the DHT subsystem.
  *
@@ -332,34 +236,40 @@ stop_get (void *cls,
 void
 GCD_init (const struct GNUNET_CONFIGURATION_Handle *c)
 {
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "init\n");
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_number (c, "CADET", "DHT_REPLICATION_LEVEL",
+      GNUNET_CONFIGURATION_get_value_number (c,
+                                             "CADET",
+                                             "DHT_REPLICATION_LEVEL",
                                              &dht_replication_level))
   {
     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
-                               "CADET", "DHT_REPLICATION_LEVEL", "USING DEFAULT");
+                               "CADET",
+                               "DHT_REPLICATION_LEVEL",
+                               "USING DEFAULT");
     dht_replication_level = 3;
   }
 
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_time (c, "CADET", "ID_ANNOUNCE_TIME",
+      GNUNET_CONFIGURATION_get_value_time (c,
+                                           "CADET",
+                                           "ID_ANNOUNCE_TIME",
                                            &id_announce_time))
   {
     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                               "CADET", "ID_ANNOUNCE_TIME", "MISSING");
+                               "CADET",
+                               "ID_ANNOUNCE_TIME",
+                               "MISSING");
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
 
-  dht_handle = GNUNET_DHT_connect (c, 64);
-  if (NULL == dht_handle)
-  {
-    GNUNET_break (0);
-  }
-
-  announce_id_task = GNUNET_SCHEDULER_add_now (&announce_id, NULL);
-  get_requests = GNUNET_CONTAINER_multihashmap32_create (32);
+  dht_handle = GNUNET_DHT_connect (c,
+                                   64);
+  GNUNET_break (NULL != dht_handle);
+  announce_delay = GNUNET_TIME_UNIT_SECONDS;
+  announce_id_task = GNUNET_SCHEDULER_add_delayed (STARTUP_DELAY,
+                                                   &announce_id,
+                                                   NULL);
 }
 
 
@@ -369,55 +279,75 @@ GCD_init (const struct GNUNET_CONFIGURATION_Handle *c)
 void
 GCD_shutdown (void)
 {
-  GNUNET_CONTAINER_multihashmap32_iterate (get_requests, &stop_get, NULL);
-  GNUNET_CONTAINER_multihashmap32_destroy (get_requests);
-  if (dht_handle != NULL)
+  if (NULL != dht_handle)
   {
     GNUNET_DHT_disconnect (dht_handle);
     dht_handle = NULL;
   }
-  if (GNUNET_SCHEDULER_NO_TASK != announce_id_task)
+  if (NULL != announce_id_task)
   {
     GNUNET_SCHEDULER_cancel (announce_id_task);
-    announce_id_task = GNUNET_SCHEDULER_NO_TASK;
+    announce_id_task = NULL;
   }
 }
 
+
+/**
+ * Search DHT for paths to @a peeR_id
+ *
+ * @param peer_id peer to search for
+ * @return handle to abort search
+ */
 struct GCD_search_handle *
-GCD_search (const struct GNUNET_PeerIdentity *peer_id,
-            GCD_search_callback callback, void *cls)
+GCD_search (const struct GNUNET_PeerIdentity *peer_id)
 {
   struct GNUNET_HashCode phash;
   struct GCD_search_handle *h;
 
-  LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "  Starting DHT GET for peer %s\n", GNUNET_i2s (peer_id));
-  memset (&phash, 0, sizeof (phash));
-  memcpy (&phash, peer_id, sizeof (*peer_id));
+  GNUNET_STATISTICS_update (stats,
+                            "# DHT search",
+                            1,
+                            GNUNET_NO);
+  memset (&phash,
+          0,
+          sizeof(phash));
+  GNUNET_memcpy (&phash,
+                 peer_id,
+                 sizeof(*peer_id));
+
   h = GNUNET_new (struct GCD_search_handle);
-  h->peer_id = GNUNET_PEER_intern (peer_id);
-  h->callback = callback;
-  h->cls = cls;
-  h->dhtget = GNUNET_DHT_get_start (dht_handle,    /* handle */
+  h->dhtget = GNUNET_DHT_get_start (dht_handle,     /* handle */
                                     GNUNET_BLOCK_TYPE_DHT_HELLO, /* type */
                                     &phash,     /* key to search */
                                     dht_replication_level, /* replication level */
-                                    GNUNET_DHT_RO_RECORD_ROUTE |
-                                    GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
+                                    GNUNET_DHT_RO_RECORD_ROUTE
+                                    GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
                                     NULL,       /* xquery */
                                     0,     /* xquery bits */
-                                    &dht_get_id_handler, h);
-  GNUNET_CONTAINER_multihashmap32_put (get_requests, h->peer_id, h,
-                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+                                    &dht_get_id_handler,
+                                    h);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Starting DHT GET for peer %s (%p)\n",
+       GNUNET_i2s (peer_id),
+       h);
   return h;
 }
 
+
+/**
+ * Stop DHT search started with #GCD_search().
+ *
+ * @param h handle to search to stop
+ */
 void
 GCD_search_stop (struct GCD_search_handle *h)
 {
-  GNUNET_break (GNUNET_OK ==
-                GNUNET_CONTAINER_multihashmap32_remove (get_requests,
-                                                        h->peer_id, h));
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Stopping DHT GET %p\n",
+       h);
   GNUNET_DHT_get_stop (h->dhtget);
   GNUNET_free (h);
 }
+
+
+/* end of gnunet-service-cadet_dht.c */