Merge branch 'master' of ssh://gnunet.org/gnunet
[oweals/gnunet.git] / src / cadet / gnunet-service-cadet_dht.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2013 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, 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   /**
46    * DHT_GET handle.
47    */
48   struct GNUNET_DHT_GetHandle *dhtget;
49
50   /**
51    * Provided callback to call when a path is found.
52    */
53   GCD_search_callback callback;
54
55   /**
56    * Provided closure.
57    */
58   void *cls;
59
60   /**
61    * Peer ID searched for
62    */
63   GNUNET_PEER_Id peer_id;
64 };
65
66
67 /******************************************************************************/
68 /*******************************   GLOBALS  ***********************************/
69 /******************************************************************************/
70
71 /**
72  * Global handle to the statistics service.
73  */
74 extern struct GNUNET_STATISTICS_Handle *stats;
75
76 /**
77  * Own ID (short value).
78  */
79 extern GNUNET_PEER_Id myid;
80
81 /**
82  * Own ID (full value).
83  */
84 extern struct GNUNET_PeerIdentity my_full_id;
85
86 /**
87  * Handle to use DHT.
88  */
89 static struct GNUNET_DHT_Handle *dht_handle;
90
91 /**
92  * How often to PUT own ID in the DHT.
93  */
94 static struct GNUNET_TIME_Relative id_announce_time;
95
96 /**
97  * DHT replication level, see DHT API: GNUNET_DHT_get_start, GNUNET_DHT_put.
98  */
99 static unsigned long long dht_replication_level;
100
101 /**
102  * Task to periodically announce itself in the network.
103  */
104 static struct GNUNET_SCHEDULER_Task * announce_id_task;
105
106 /**
107  * Delay for the next ID announce.
108  */
109 static struct GNUNET_TIME_Relative announce_delay;
110
111 /**
112  * GET requests to stop on shutdown.
113  */
114 static struct GNUNET_CONTAINER_MultiHashMap32 *get_requests;
115
116 /******************************************************************************/
117 /********************************   STATIC  ***********************************/
118 /******************************************************************************/
119
120
121 /**
122  * Build a PeerPath from the paths returned from the DHT, reversing the paths
123  * to obtain a local peer -> destination path and interning the peer ids.
124  *
125  * @return Newly allocated and created path
126  *
127  * FIXME refactor and use build_path_from_peer_ids
128  */
129 static struct CadetPeerPath *
130 path_build_from_dht (const struct GNUNET_PeerIdentity *get_path,
131                      unsigned int get_path_length,
132                      const struct GNUNET_PeerIdentity *put_path,
133                      unsigned int put_path_length)
134 {
135   size_t size = get_path_length + put_path_length + 1;
136   struct GNUNET_PeerIdentity peers[size];
137   const struct GNUNET_PeerIdentity *peer;
138   struct CadetPeerPath *p;
139   unsigned int own_pos;
140   int i;
141
142   peers[0] = my_full_id;
143   LOG (GNUNET_ERROR_TYPE_DEBUG, "   GET has %d hops.\n", get_path_length);
144   for (i = 0 ; i < get_path_length; i++)
145   {
146     peer = &get_path[get_path_length - i - 1];
147     LOG (GNUNET_ERROR_TYPE_DEBUG, "   From GET: %s\n", GNUNET_i2s (peer));
148     peers[i + 1] = *peer;
149   }
150   for (i = 0 ; i < put_path_length; i++)
151   {
152     peer = &put_path[put_path_length - i - 1];
153     LOG (GNUNET_ERROR_TYPE_DEBUG, "   From PUT: %s\n", GNUNET_i2s (peer));
154     peers[i + get_path_length + 1] = *peer;
155   }
156   p = path_build_from_peer_ids (peers, size, myid, &own_pos);
157   return p;
158 }
159
160
161 /**
162  * Function to process paths received for a new peer addition. The recorded
163  * paths form the initial tunnel, which can be optimized later.
164  * Called on each result obtained for the DHT search.
165  *
166  * @param cls closure
167  * @param exp when will this value expire
168  * @param key key of the result
169  * @param get_path path of the get request
170  * @param get_path_length lenght of @a get_path
171  * @param put_path path of the put request
172  * @param put_path_length length of the @a put_path
173  * @param type type of the result
174  * @param size number of bytes in data
175  * @param data pointer to the result data
176  */
177 static void
178 dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp,
179                     const struct GNUNET_HashCode * key,
180                     const struct GNUNET_PeerIdentity *get_path,
181                     unsigned int get_path_length,
182                     const struct GNUNET_PeerIdentity *put_path,
183                     unsigned int put_path_length, enum GNUNET_BLOCK_Type type,
184                     size_t size, const void *data)
185 {
186   struct GCD_search_handle *h = cls;
187   struct GNUNET_HELLO_Message *hello;
188   struct CadetPeerPath *p;
189   struct CadetPeer *peer;
190   char *s;
191
192   p = path_build_from_dht (get_path, get_path_length,
193                            put_path, put_path_length);
194   if (NULL == p)
195   {
196     GNUNET_break_op (0);
197     return;
198   }
199
200   s = path_2s (p);
201   LOG (GNUNET_ERROR_TYPE_INFO,
202        "Got path from DHT: %s\n",
203        s);
204   GNUNET_free_non_null (s);
205
206   peer = GCP_get_short (p->peers[p->length - 1], GNUNET_YES);
207   LOG (GNUNET_ERROR_TYPE_DEBUG,
208        "Got HELLO for %s\n",
209        GCP_2s (peer));
210   h->callback (h->cls, p);
211   path_destroy (p);
212   hello = (struct GNUNET_HELLO_Message *) data;
213   GCP_set_hello (peer, hello);
214   GCP_try_connect (peer);
215 }
216
217
218 /**
219  * Periodically announce self id in the DHT
220  *
221  * @param cls closure
222  */
223 static void
224 announce_id (void *cls)
225 {
226   struct GNUNET_HashCode phash;
227   const struct GNUNET_HELLO_Message *hello;
228   size_t size;
229   struct GNUNET_TIME_Absolute expiration;
230   struct GNUNET_TIME_Relative next_put;
231
232   announce_id_task = NULL;
233   LOG (GNUNET_ERROR_TYPE_DEBUG, "Announce ID\n");
234   hello = GCH_get_mine ();
235   size = (NULL != hello) ? GNUNET_HELLO_size (hello) : 0;
236   if ( (NULL == hello) || (0 == size) )
237   {
238     /* Peerinfo gave us no hello yet, try again soon. */
239     LOG (GNUNET_ERROR_TYPE_INFO,
240          "  no hello, waiting!\n");
241     GNUNET_STATISTICS_update (stats,
242                               "# DHT announce skipped (no hello)",
243                               1,
244                               GNUNET_NO);
245     expiration = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
246                                            announce_delay);
247     announce_delay = GNUNET_TIME_STD_BACKOFF (announce_delay);
248   }
249   else
250   {
251     expiration = GNUNET_HELLO_get_last_expiration (hello);
252     announce_delay = GNUNET_TIME_UNIT_SECONDS;
253   }
254
255   LOG (GNUNET_ERROR_TYPE_DEBUG,
256        "Hello %p size: %u\n",
257        hello,
258        size);
259   if (NULL != hello)
260   {
261     GNUNET_STATISTICS_update (stats,
262                               "# DHT announce",
263                               1, GNUNET_NO);
264     memset (&phash,
265             0,
266             sizeof (phash));
267     GNUNET_memcpy (&phash,
268                    &my_full_id,
269                    sizeof (my_full_id));
270     GNUNET_DHT_put (dht_handle,   /* DHT handle */
271                     &phash,       /* Key to use */
272                     dht_replication_level,     /* Replication level */
273                     GNUNET_DHT_RO_RECORD_ROUTE
274                     | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,    /* DHT options */
275                     GNUNET_BLOCK_TYPE_DHT_HELLO,       /* Block type */
276                     size,  /* Size of the data */
277                     (const char *) hello, /* Data itself */
278                     expiration,  /* Data expiration */
279                     NULL,         /* Continuation */
280                     NULL);        /* Continuation closure */
281   }
282   /* Call again in id_announce_time, unless HELLO expires first,
283    * but wait at least 1s. */
284   next_put = GNUNET_TIME_absolute_get_remaining (expiration);
285   next_put = GNUNET_TIME_relative_min (next_put,
286                                        id_announce_time);
287   next_put = GNUNET_TIME_relative_max (next_put,
288                                        GNUNET_TIME_UNIT_SECONDS);
289   announce_id_task = GNUNET_SCHEDULER_add_delayed (next_put,
290                                                    &announce_id,
291                                                    cls);
292 }
293
294 /**
295  * Iterator over hash map entries and stop GET requests before disconnecting
296  * from the DHT.
297  *
298  * @param cls Closure (unused)
299  * @param key Current peer ID.
300  * @param value Value in the hash map (GCD_search_handle).
301  *
302  * @return #GNUNET_YES, we should continue to iterate,
303  */
304 int
305 stop_get (void *cls,
306           uint32_t key,
307           void *value)
308 {
309   struct GCD_search_handle *h = value;
310
311   GCD_search_stop (h);
312   return GNUNET_YES;
313 }
314
315
316 /******************************************************************************/
317 /********************************    API    ***********************************/
318 /******************************************************************************/
319
320 /**
321  * Initialize the DHT subsystem.
322  *
323  * @param c Configuration.
324  */
325 void
326 GCD_init (const struct GNUNET_CONFIGURATION_Handle *c)
327 {
328   LOG (GNUNET_ERROR_TYPE_DEBUG, "init\n");
329   if (GNUNET_OK !=
330       GNUNET_CONFIGURATION_get_value_number (c, "CADET",
331                                              "DHT_REPLICATION_LEVEL",
332                                              &dht_replication_level))
333   {
334     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, "CADET",
335                                "DHT_REPLICATION_LEVEL", "USING DEFAULT");
336     dht_replication_level = 3;
337   }
338
339   if (GNUNET_OK !=
340       GNUNET_CONFIGURATION_get_value_time (c, "CADET", "ID_ANNOUNCE_TIME",
341                                            &id_announce_time))
342   {
343     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, "CADET",
344                                "ID_ANNOUNCE_TIME", "MISSING");
345     GNUNET_SCHEDULER_shutdown ();
346     return;
347   }
348
349   dht_handle = GNUNET_DHT_connect (c, 64);
350   if (NULL == dht_handle)
351   {
352     GNUNET_break (0);
353   }
354
355   announce_delay = GNUNET_TIME_UNIT_SECONDS;
356   announce_id_task = GNUNET_SCHEDULER_add_now (&announce_id, NULL);
357   get_requests = GNUNET_CONTAINER_multihashmap32_create (32);
358 }
359
360
361 /**
362  * Shut down the DHT subsystem.
363  */
364 void
365 GCD_shutdown (void)
366 {
367   LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down DHT\n");
368   GNUNET_CONTAINER_multihashmap32_iterate (get_requests, &stop_get, NULL);
369   GNUNET_CONTAINER_multihashmap32_destroy (get_requests);
370   if (dht_handle != NULL)
371   {
372     GNUNET_DHT_disconnect (dht_handle);
373     dht_handle = NULL;
374   }
375   if (NULL != announce_id_task)
376   {
377     GNUNET_SCHEDULER_cancel (announce_id_task);
378     announce_id_task = NULL;
379   }
380 }
381
382 struct GCD_search_handle *
383 GCD_search (const struct GNUNET_PeerIdentity *peer_id,
384             GCD_search_callback callback, void *cls)
385 {
386   struct GNUNET_HashCode phash;
387   struct GCD_search_handle *h;
388
389   LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting DHT GET for peer %s\n",
390        GNUNET_i2s (peer_id));
391   GNUNET_STATISTICS_update (stats, "# DHT search", 1, GNUNET_NO);
392   memset (&phash, 0, sizeof (phash));
393   GNUNET_memcpy (&phash, peer_id, sizeof (*peer_id));
394   h = GNUNET_new (struct GCD_search_handle);
395   h->peer_id = GNUNET_PEER_intern (peer_id);
396   h->callback = callback;
397   h->cls = cls;
398   h->dhtget = GNUNET_DHT_get_start (dht_handle,    /* handle */
399                                     GNUNET_BLOCK_TYPE_DHT_HELLO, /* type */
400                                     &phash,     /* key to search */
401                                     dht_replication_level, /* replication level */
402                                     GNUNET_DHT_RO_RECORD_ROUTE |
403                                     GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
404                                     NULL,       /* xquery */
405                                     0,     /* xquery bits */
406                                     &dht_get_id_handler,
407                                     h);
408   GNUNET_CONTAINER_multihashmap32_put (get_requests,
409                                        h->peer_id,
410                                        h,
411                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
412   return h;
413 }
414
415
416 void
417 GCD_search_stop (struct GCD_search_handle *h)
418 {
419   GNUNET_break (GNUNET_OK ==
420                 GNUNET_CONTAINER_multihashmap32_remove (get_requests,
421                                                         h->peer_id, h));
422   GNUNET_DHT_get_stop (h->dhtget);
423   GNUNET_free (h);
424 }