f18e868e3211d180b37ca885c092027090f53880
[oweals/gnunet.git] / src / cadet / gnunet-service-cadet_dht.c
1 /*
2      This file is part of GNUnet.
3      (C) 2013 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21
22 #include "platform.h"
23 #include "gnunet_util_lib.h"
24
25 #include "gnunet_dht_service.h"
26 #include "gnunet_statistics_service.h"
27
28 #include "cadet_path.h"
29 #include "gnunet-service-cadet_dht.h"
30 #include "gnunet-service-cadet_peer.h"
31 #include "gnunet-service-cadet_hello.h"
32
33 #define LOG(level, ...) GNUNET_log_from (level,"cadet-dht",__VA_ARGS__)
34
35
36 /******************************************************************************/
37 /********************************   STRUCTS  **********************************/
38 /******************************************************************************/
39
40 /**
41  * Handle for DHT searches.
42  */
43 struct GCD_search_handle
44 {
45   /** DHT_GET handle. */
46   struct GNUNET_DHT_GetHandle *dhtget;
47
48   /** Provided callback to call when a path is found. */
49   GCD_search_callback callback;
50
51   /** Provided closure. */
52   void *cls;
53
54   /** Peer ID searched for */
55   GNUNET_PEER_Id peer_id;
56 };
57
58
59 /******************************************************************************/
60 /*******************************   GLOBALS  ***********************************/
61 /******************************************************************************/
62
63 /**
64  * Global handle to the statistics service.
65  */
66 extern struct GNUNET_STATISTICS_Handle *stats;
67
68 /**
69  * Own ID (short value).
70  */
71 extern GNUNET_PEER_Id myid;
72
73 /**
74  * Own ID (full value).
75  */
76 extern struct GNUNET_PeerIdentity my_full_id;
77
78 /**
79  * Handle to use DHT.
80  */
81 static struct GNUNET_DHT_Handle *dht_handle;
82
83 /**
84  * How often to PUT own ID in the DHT.
85  */
86 static struct GNUNET_TIME_Relative id_announce_time;
87
88 /**
89  * DHT replication level, see DHT API: GNUNET_DHT_get_start, GNUNET_DHT_put.
90  */
91 static unsigned long long dht_replication_level;
92
93 /**
94  * Task to periodically announce itself in the network.
95  */
96 static GNUNET_SCHEDULER_TaskIdentifier announce_id_task;
97
98 /**
99  * GET requests to stop on shutdown.
100  */
101 static struct GNUNET_CONTAINER_MultiHashMap32 *get_requests;
102
103 /******************************************************************************/
104 /********************************   STATIC  ***********************************/
105 /******************************************************************************/
106
107
108 /**
109  * Build a PeerPath from the paths returned from the DHT, reversing the paths
110  * to obtain a local peer -> destination path and interning the peer ids.
111  *
112  * @return Newly allocated and created path
113  *
114  * FIXME refactor and use build_path_from_peer_ids
115  */
116 static struct CadetPeerPath *
117 path_build_from_dht (const struct GNUNET_PeerIdentity *get_path,
118                      unsigned int get_path_length,
119                      const struct GNUNET_PeerIdentity *put_path,
120                      unsigned int put_path_length)
121 {
122   size_t size = get_path_length + put_path_length;
123   struct GNUNET_PeerIdentity peers[size];
124   const struct GNUNET_PeerIdentity *peer;
125   struct CadetPeerPath *p;
126   unsigned int own_pos;
127   int i;
128
129   LOG (GNUNET_ERROR_TYPE_DEBUG, "   GET has %d hops.\n", get_path_length);
130   for (i = 0 ; i < get_path_length; i++)
131   {
132     peer = &get_path[get_path_length - i - 1];
133     LOG (GNUNET_ERROR_TYPE_DEBUG, "   From GET: %s\n", GNUNET_i2s (peer));
134     peers[i] = *peer;
135   }
136   for (i = 0 ; i < put_path_length; i++)
137   {
138     peer = &put_path[put_path_length - i - 1];
139     LOG (GNUNET_ERROR_TYPE_DEBUG, "   From PUT: %s\n", GNUNET_i2s (peer));
140     peers[i + get_path_length] = *peer;
141   }
142   p = path_build_from_peer_ids (peers, size, myid, &own_pos);
143   return p;
144 }
145
146
147 /**
148  * Function to process paths received for a new peer addition. The recorded
149  * paths form the initial tunnel, which can be optimized later.
150  * Called on each result obtained for the DHT search.
151  *
152  * @param cls closure
153  * @param exp when will this value expire
154  * @param key key of the result
155  * @param get_path path of the get request
156  * @param get_path_length lenght of get_path
157  * @param put_path path of the put request
158  * @param put_path_length length of the put_path
159  * @param type type of the result
160  * @param size number of bytes in data
161  * @param data pointer to the result data
162  */
163 static void
164 dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp,
165                     const struct GNUNET_HashCode * key,
166                     const struct GNUNET_PeerIdentity *get_path,
167                     unsigned int get_path_length,
168                     const struct GNUNET_PeerIdentity *put_path,
169                     unsigned int put_path_length, enum GNUNET_BLOCK_Type type,
170                     size_t size, const void *data)
171 {
172   struct GCD_search_handle *h = cls;
173   struct GNUNET_HELLO_Message *hello;
174   struct CadetPeerPath *p;
175   struct CadetPeer *peer;
176   char *s;
177
178   p = path_build_from_dht (get_path, get_path_length,
179                            put_path, put_path_length);
180   s = path_2s (p);
181   LOG (GNUNET_ERROR_TYPE_INFO, "Got path from DHT: %s\n", s);
182   GNUNET_free_non_null (s);
183   peer = GCP_get_short (p->peers[p->length - 1]);
184   LOG (GNUNET_ERROR_TYPE_DEBUG, "Got HELLO for %s\n", GCP_2s (peer));
185   h->callback (h->cls, p);
186   path_destroy (p);
187   hello = (struct GNUNET_HELLO_Message *) data;
188   GCP_set_hello (peer, hello);
189   GCP_try_connect (peer);
190   return;
191 }
192
193
194 /**
195  * Periodically announce self id in the DHT
196  *
197  * @param cls closure
198  * @param tc task context
199  */
200 static void
201 announce_id (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
202 {
203   struct GNUNET_HashCode phash;
204   const struct GNUNET_HELLO_Message *hello;
205   size_t size;
206   struct GNUNET_TIME_Absolute expiration;
207   struct GNUNET_TIME_Relative retry_time;
208
209   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
210   {
211     announce_id_task = GNUNET_SCHEDULER_NO_TASK;
212     return;
213   }
214   LOG (GNUNET_ERROR_TYPE_DEBUG, "Announce ID\n");
215
216   /* TODO
217    * - Set data expiration in function of X
218    * - Adapt X to churn
219    */
220   hello = GCH_get_mine ();
221   if (NULL == hello || (size = GNUNET_HELLO_size (hello)) == 0)
222   {
223     /* Peerinfo gave us no hello yet, try again in a second. */
224     announce_id_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
225                                                      &announce_id, cls);
226     LOG (GNUNET_ERROR_TYPE_DEBUG, "  no hello, waiting!\n");
227     return;
228   }
229   expiration = GNUNET_HELLO_get_last_expiration (hello);
230   retry_time = GNUNET_TIME_absolute_get_remaining (expiration);
231
232   LOG (GNUNET_ERROR_TYPE_DEBUG, "Hello %p size: %u\n", hello, size);
233   memset (&phash, 0, sizeof (phash));
234   memcpy (&phash, &my_full_id, sizeof (my_full_id));
235   GNUNET_DHT_put (dht_handle,   /* DHT handle */
236                   &phash,       /* Key to use */
237                   dht_replication_level,     /* Replication level */
238                   GNUNET_DHT_RO_RECORD_ROUTE
239                   | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,    /* DHT options */
240                   GNUNET_BLOCK_TYPE_DHT_HELLO,       /* Block type */
241                   size,  /* Size of the data */
242                   (const char *) hello, /* Data itself */
243                   expiration,  /* Data expiration */
244                   retry_time, /* Retry time */
245                   NULL,         /* Continuation */
246                   NULL);        /* Continuation closure */
247   announce_id_task =
248       GNUNET_SCHEDULER_add_delayed (id_announce_time, &announce_id, cls);
249 }
250
251 /**
252  * Iterator over hash map entries and stop GET requests before disconnecting
253  * from the DHT.
254  *
255  * @param cls Closure (unused)
256  * @param key Current peer ID.
257  * @param value Value in the hash map (GCD_search_handle).
258  *
259  * @return #GNUNET_YES, we should continue to iterate,
260  */
261 int
262 stop_get (void *cls,
263           uint32_t key,
264           void *value)
265 {
266   struct GCD_search_handle *h = value;
267
268   GCD_search_stop (h);
269   return GNUNET_YES;
270 }
271
272
273 /******************************************************************************/
274 /********************************    API    ***********************************/
275 /******************************************************************************/
276
277 /**
278  * Initialize the DHT subsystem.
279  *
280  * @param c Configuration.
281  */
282 void
283 GCD_init (const struct GNUNET_CONFIGURATION_Handle *c)
284 {
285   LOG (GNUNET_ERROR_TYPE_DEBUG, "init\n");
286   if (GNUNET_OK !=
287       GNUNET_CONFIGURATION_get_value_number (c, "CADET", "DHT_REPLICATION_LEVEL",
288                                              &dht_replication_level))
289   {
290     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
291                                "CADET", "DHT_REPLICATION_LEVEL", "USING DEFAULT");
292     dht_replication_level = 3;
293   }
294
295   if (GNUNET_OK !=
296       GNUNET_CONFIGURATION_get_value_time (c, "CADET", "ID_ANNOUNCE_TIME",
297                                            &id_announce_time))
298   {
299     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
300                                "CADET", "ID_ANNOUNCE_TIME", "MISSING");
301     GNUNET_SCHEDULER_shutdown ();
302     return;
303   }
304
305   dht_handle = GNUNET_DHT_connect (c, 64);
306   if (NULL == dht_handle)
307   {
308     GNUNET_break (0);
309   }
310
311   announce_id_task = GNUNET_SCHEDULER_add_now (&announce_id, NULL);
312   get_requests = GNUNET_CONTAINER_multihashmap32_create (32);
313 }
314
315
316 /**
317  * Shut down the DHT subsystem.
318  */
319 void
320 GCD_shutdown (void)
321 {
322   GNUNET_CONTAINER_multihashmap32_iterate (get_requests, &stop_get, NULL);
323   GNUNET_CONTAINER_multihashmap32_destroy (get_requests);
324   if (dht_handle != NULL)
325   {
326     GNUNET_DHT_disconnect (dht_handle);
327     dht_handle = NULL;
328   }
329   if (GNUNET_SCHEDULER_NO_TASK != announce_id_task)
330   {
331     GNUNET_SCHEDULER_cancel (announce_id_task);
332     announce_id_task = GNUNET_SCHEDULER_NO_TASK;
333   }
334 }
335
336 struct GCD_search_handle *
337 GCD_search (const struct GNUNET_PeerIdentity *peer_id,
338             GCD_search_callback callback, void *cls)
339 {
340   struct GNUNET_HashCode phash;
341   struct GCD_search_handle *h;
342
343   LOG (GNUNET_ERROR_TYPE_DEBUG,
344        "  Starting DHT GET for peer %s\n", GNUNET_i2s (peer_id));
345   memset (&phash, 0, sizeof (phash));
346   memcpy (&phash, peer_id, sizeof (*peer_id));
347   h = GNUNET_new (struct GCD_search_handle);
348   h->peer_id = GNUNET_PEER_intern (peer_id);
349   h->callback = callback;
350   h->cls = cls;
351   h->dhtget = GNUNET_DHT_get_start (dht_handle,    /* handle */
352                                     GNUNET_BLOCK_TYPE_DHT_HELLO, /* type */
353                                     &phash,     /* key to search */
354                                     dht_replication_level, /* replication level */
355                                     GNUNET_DHT_RO_RECORD_ROUTE |
356                                     GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
357                                     NULL,       /* xquery */
358                                     0,     /* xquery bits */
359                                     &dht_get_id_handler, h);
360   GNUNET_CONTAINER_multihashmap32_put (get_requests, h->peer_id, h,
361                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
362   return h;
363 }
364
365 void
366 GCD_search_stop (struct GCD_search_handle *h)
367 {
368   GNUNET_break (GNUNET_OK ==
369                 GNUNET_CONTAINER_multihashmap32_remove (get_requests,
370                                                         h->peer_id, h));
371   GNUNET_DHT_get_stop (h->dhtget);
372   GNUNET_free (h);
373 }