00dfb9d4699f2414f03d7c4c295d222bbe278f24
[oweals/gnunet.git] / src / mesh / gnunet-service-mesh_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 "mesh_path.h"
29 #include "gnunet-service-mesh_dht.h"
30 #include "gnunet-service-mesh_peer.h"
31
32 #define LOG(level, ...) GNUNET_log_from (level,"mesh-dht",__VA_ARGS__)
33
34
35 /******************************************************************************/
36 /********************************   STRUCTS  **********************************/
37 /******************************************************************************/
38
39 /**
40  * Handle for DHT searches.
41  */
42 struct GMD_search_handle
43 {
44   /** DHT_GET handle. */
45   struct GNUNET_DHT_GetHandle *dhtget;
46
47   /** Provided callback to call when a path is found. */
48   GMD_search_callback callback;
49
50   /** Provided closure. */
51   void *cls;
52 };
53
54
55 /******************************************************************************/
56 /*******************************   GLOBALS  ***********************************/
57 /******************************************************************************/
58
59 /**
60  * Global handle to the statistics service.
61  */
62 extern struct GNUNET_STATISTICS_Handle *stats;
63
64 /**
65  * Handle to use DHT.
66  */
67 static struct GNUNET_DHT_Handle *dht_handle;
68
69 /**
70  * How often to PUT own ID in the DHT.
71  */
72 static struct GNUNET_TIME_Relative id_announce_time;
73
74 /**
75  * DHT replication level, see DHT API: GNUNET_DHT_get_start, GNUNET_DHT_put.
76  */
77 static unsigned long long dht_replication_level;
78
79 /**
80  * Task to periodically announce itself in the network.
81  */
82 static GNUNET_SCHEDULER_TaskIdentifier announce_id_task;
83
84 /**
85  * Own ID (short value).
86  */
87 static GNUNET_PEER_Id short_id;
88
89 /**
90  * Own ID (full value).
91  */
92 static struct GNUNET_PeerIdentity *full_id;
93
94 /**
95  * Own private key.
96  */
97 static struct GNUNET_CRYPTO_EddsaPrivateKey *private_key;
98
99
100 /******************************************************************************/
101 /********************************   STATIC  ***********************************/
102 /******************************************************************************/
103
104
105 /**
106  * Build a PeerPath from the paths returned from the DHT, reversing the paths
107  * to obtain a local peer -> destination path and interning the peer ids.
108  *
109  * @return Newly allocated and created path
110  */
111 static struct MeshPeerPath *
112 path_build_from_dht (const struct GNUNET_PeerIdentity *get_path,
113                      unsigned int get_path_length,
114                      const struct GNUNET_PeerIdentity *put_path,
115                      unsigned int put_path_length)
116 {
117   struct MeshPeerPath *p;
118   GNUNET_PEER_Id id;
119   int i;
120
121   p = path_new (1);
122   p->peers[0] = myid;
123   GNUNET_PEER_change_rc (myid, 1);
124   i = get_path_length;
125   LOG (GNUNET_ERROR_TYPE_DEBUG, "   GET has %d hops.\n", i);
126   for (i--; i >= 0; i--)
127   {
128     id = GNUNET_PEER_intern (&get_path[i]);
129     if (p->length > 0 && id == p->peers[p->length - 1])
130     {
131       LOG (GNUNET_ERROR_TYPE_DEBUG, "   Optimizing 1 hop out.\n");
132       GNUNET_PEER_change_rc (id, -1);
133     }
134     else
135     {
136       LOG (GNUNET_ERROR_TYPE_DEBUG, "   Adding from GET: %s.\n",
137                   GNUNET_i2s (&get_path[i]));
138       p->length++;
139       p->peers = GNUNET_realloc (p->peers, sizeof (GNUNET_PEER_Id) * p->length);
140       p->peers[p->length - 1] = id;
141     }
142   }
143   i = put_path_length;
144   LOG (GNUNET_ERROR_TYPE_DEBUG, "   PUT has %d hops.\n", i);
145   for (i--; i >= 0; i--)
146   {
147     id = GNUNET_PEER_intern (&put_path[i]);
148     if (id == myid)
149     {
150       /* PUT path went through us, so discard the path up until now and start
151        * from here to get a much shorter (and loop-free) path.
152        */
153       path_destroy (p);
154       p = path_new (0);
155     }
156     if (p->length > 0 && id == p->peers[p->length - 1])
157     {
158       LOG (GNUNET_ERROR_TYPE_DEBUG, "   Optimizing 1 hop out.\n");
159       GNUNET_PEER_change_rc (id, -1);
160     }
161     else
162     {
163       LOG (GNUNET_ERROR_TYPE_DEBUG, "   Adding from PUT: %s.\n",
164                   GNUNET_i2s (&put_path[i]));
165       p->length++;
166       p->peers = GNUNET_realloc (p->peers, sizeof (GNUNET_PEER_Id) * p->length);
167       p->peers[p->length - 1] = id;
168     }
169   }
170 #if MESH_DEBUG
171   if (get_path_length > 0)
172     LOG (GNUNET_ERROR_TYPE_DEBUG, "   (first of GET: %s)\n",
173                 GNUNET_i2s (&get_path[0]));
174   if (put_path_length > 0)
175     LOG (GNUNET_ERROR_TYPE_DEBUG, "   (first of PUT: %s)\n",
176                 GNUNET_i2s (&put_path[0]));
177   LOG (GNUNET_ERROR_TYPE_DEBUG, "   In total: %d hops\n",
178               p->length);
179   for (i = 0; i < p->length; i++)
180   {
181     struct GNUNET_PeerIdentity peer_id;
182
183     GNUNET_PEER_resolve (p->peers[i], &peer_id);
184     LOG (GNUNET_ERROR_TYPE_DEBUG, "       %u: %s\n", p->peers[i],
185                 GNUNET_i2s (&peer_id));
186   }
187 #endif
188   return p;
189 }
190
191
192 /**
193  * Function to process paths received for a new peer addition. The recorded
194  * paths form the initial tunnel, which can be optimized later.
195  * Called on each result obtained for the DHT search.
196  *
197  * @param cls closure
198  * @param exp when will this value expire
199  * @param key key of the result
200  * @param get_path path of the get request
201  * @param get_path_length lenght of get_path
202  * @param put_path path of the put request
203  * @param put_path_length length of the put_path
204  * @param type type of the result
205  * @param size number of bytes in data
206  * @param data pointer to the result data
207  */
208 static void
209 dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp,
210                     const struct GNUNET_HashCode * key,
211                     const struct GNUNET_PeerIdentity *get_path,
212                     unsigned int get_path_length,
213                     const struct GNUNET_PeerIdentity *put_path,
214                     unsigned int put_path_length, enum GNUNET_BLOCK_Type type,
215                     size_t size, const void *data)
216 {
217   struct GMD_search_handle *h = cls;
218   struct MeshPeerPath *p;
219
220   LOG (GNUNET_ERROR_TYPE_DEBUG, "Got results!\n");
221   p = path_build_from_dht (get_path, get_path_length,
222                            put_path, put_path_length);
223   h->callback (h->cls, p);
224   path_destroy (p);
225   return;
226 }
227
228
229 /**
230  * Periodically announce self id in the DHT
231  *
232  * @param cls closure
233  * @param tc task context
234  */
235 static void
236 announce_id (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
237 {
238   struct PBlock block;
239   struct GNUNET_HashCode phash;
240
241   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
242   {
243     announce_id_task = GNUNET_SCHEDULER_NO_TASK;
244     return;
245   }
246
247   /* TODO
248    * - Set data expiration in function of X
249    * - Adapt X to churn
250    */
251   block.id = *full_id;
252   GNUNET_CRYPTO_hash (full_id, sizeof (struct GNUNET_PeerIdentity), &phash);
253   GNUNET_DHT_put (dht_handle,   /* DHT handle */
254                   &phash,       /* Key to use */
255                   dht_replication_level,     /* Replication level */
256                   GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,    /* DHT options */
257                   GNUNET_BLOCK_TYPE_MESH_PEER,       /* Block type */
258                   sizeof (block),  /* Size of the data */
259                   (const char *) &block, /* Data itself */
260                   GNUNET_TIME_UNIT_FOREVER_ABS,  /* Data expiration */
261                   GNUNET_TIME_UNIT_FOREVER_REL, /* Retry time */
262                   NULL,         /* Continuation */
263                   NULL);        /* Continuation closure */
264   announce_id_task =
265       GNUNET_SCHEDULER_add_delayed (id_announce_time, &announce_id, cls);
266 }
267
268
269 /******************************************************************************/
270 /********************************    API    ***********************************/
271 /******************************************************************************/
272
273 /**
274  * Initialize the DHT subsystem.
275  *
276  * @param c Configuration.
277  * @param peer_id Local peer ID (must remain valid during all execution time).
278  */
279 void
280 GMD_init (const struct GNUNET_CONFIGURATION_Handle *c,
281           struct GNUNET_PeerIdentity *peer_id)
282 {
283   full_id = peer_id;
284   if (GNUNET_OK !=
285       GNUNET_CONFIGURATION_get_value_number (c, "MESH", "DHT_REPLICATION_LEVEL",
286                                              &dht_replication_level))
287   {
288     LOG_config_invalid (GNUNET_ERROR_TYPE_WARNING,
289                                "MESH", "DHT_REPLICATION_LEVEL", "USING DEFAULT");
290     dht_replication_level = 3;
291   }
292
293   if (GNUNET_OK !=
294       GNUNET_CONFIGURATION_get_value_time (c, "MESH", "ID_ANNOUNCE_TIME",
295                                            &id_announce_time))
296   {
297     LOG_config_invalid (GNUNET_ERROR_TYPE_ERROR,
298                                "MESH", "ID_ANNOUNCE_TIME", "MISSING");
299     GNUNET_SCHEDULER_shutdown ();
300     return;
301   }
302
303   dht_handle = GNUNET_DHT_connect (c, 64);
304   if (NULL == dht_handle)
305   {
306     GNUNET_break (0);
307   }
308
309   announce_id_task = GNUNET_SCHEDULER_add_now (&announce_id, NULL);
310 }
311
312
313 /**
314  * Shut down the DHT subsystem.
315  */
316 void
317 GMD_shutdown(void )
318 {
319   if (dht_handle != NULL)
320   {
321     GNUNET_DHT_disconnect (dht_handle);
322     dht_handle = NULL;
323   }
324   if (GNUNET_SCHEDULER_NO_TASK != announce_id_task)
325   {
326     GNUNET_SCHEDULER_cancel (announce_id_task);
327     announce_id_task = GNUNET_SCHEDULER_NO_TASK;
328   }
329 }
330
331 struct GMD_search_handle *
332 GMD_search (const struct GNUNET_PeerIdentity *peer_id,
333             GMD_search_callback callback, void *cls)
334 {
335   struct GNUNET_HashCode phash;
336   struct GMD_search_handle *h;
337
338   LOG (GNUNET_ERROR_TYPE_DEBUG,
339        "  Starting DHT GET for peer %s\n", GNUNET_i2s (peer_id));
340   GNUNET_CRYPTO_hash (peer_id, sizeof (struct GNUNET_PeerIdentity), &phash);
341   h = GNUNET_new (struct GMD_search_handle);
342   h->cls = cls;
343   h->dhtget = GNUNET_DHT_get_start (dht_handle,    /* handle */
344                                     GNUNET_BLOCK_TYPE_MESH_PEER, /* type */
345                                     &phash,     /* key to search */
346                                     dht_replication_level, /* replication level */
347                                     GNUNET_DHT_RO_RECORD_ROUTE |
348                                     GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
349                                     NULL,       /* xquery */
350                                     0,     /* xquery bits */
351                                     &dht_get_id_handler, h);
352   return h;
353 }
354
355 void
356 GMD_search_stop (struct GMD_search_handle *h)
357 {
358   GNUNET_DHT_get_stop (h->dhtget);
359   GNUNET_free (h);
360 }