Adding a function pick_random_friend ()
[oweals/gnunet.git] / src / dht / gnunet-service-wdht_datacache.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2010, 2011, 2015 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  * @file dht/gnunet-service-wdht_datacache.c
23  * @brief GNUnet DHT service's datacache integration
24  * @author Christian Grothoff
25  * @author Nathan Evans
26  */
27 #include "platform.h"
28 #include "gnunet_datacache_lib.h"
29 #include "gnunet-service-wdht_clients.h"
30 #include "gnunet-service-wdht_datacache.h"
31 #include "gnunet-service-wdht_neighbours.h"
32 #include "gnunet-service-dht.h"
33
34 #define LOG(kind,...) GNUNET_log_from (kind, "dht-dtcache",__VA_ARGS__)
35
36 #define DEBUG(...)                                           \
37   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
38
39 /**
40  * How many "closest" results to we return for migration when
41  * asked (at most)?
42  */
43 #define NUM_CLOSEST 42
44
45 /**
46  * Handle to the datacache service (for inserting/retrieving data)
47  */
48 static struct GNUNET_DATACACHE_Handle *datacache;
49
50
51 /**
52  * Handle a datum we've received from another peer.  Cache if
53  * possible.
54  *
55  * @param expiration when will the reply expire
56  * @param key the query this reply is for
57  * @param put_path_length number of peers in @a put_path
58  * @param put_path path the reply took on put
59  * @param get_path_length number of peers in @a get_path
60  * @param get_path path the reply took on get
61  * @param type type of the reply
62  * @param data_size number of bytes in @a data
63  * @param data application payload data
64  */
65 void
66 GDS_DATACACHE_handle_put (struct GNUNET_TIME_Absolute expiration,
67                           const struct GNUNET_HashCode *key,
68                           unsigned int put_path_length,
69                           const struct GNUNET_PeerIdentity *put_path,
70                           unsigned int get_path_length,
71                           const struct GNUNET_PeerIdentity *get_path,
72                           enum GNUNET_BLOCK_Type type,
73                           size_t data_size,
74                           const void *data)
75 {
76   int r;
77   struct GNUNET_PeerIdentity path[get_path_length + put_path_length];
78
79   if (NULL == datacache)
80   {
81     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
82                 _("PUT request received, but have no datacache!\n"));
83     return;
84   }
85   if (data_size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
86   {
87     GNUNET_break (0);
88     return;
89   }
90   memcpy (path,
91           put_path,
92           put_path_length * sizeof (struct GNUNET_PeerIdentity));
93   memcpy (&path[put_path_length],
94           get_path,
95           get_path_length * sizeof (struct GNUNET_PeerIdentity));
96   /* Put size is actual data size plus struct overhead plus path length (if any) */
97   r = GNUNET_DATACACHE_put (datacache,
98                             key,
99                             data_size,
100                             data,
101                             type,
102                             expiration,
103                             get_path_length + put_path_length,
104                             path);
105   if (GNUNET_OK == r)
106     GNUNET_STATISTICS_update (GDS_stats,
107                               gettext_noop ("# ITEMS stored in datacache"), 1,
108                               GNUNET_NO);
109   LOG (GNUNET_ERROR_TYPE_DEBUG,
110        "DATACACHE PUT for key %s [%u] completed (%d) after %u hops\n",
111        GNUNET_h2s (key),
112        data_size,
113        r,
114        put_path_length + get_path_length);
115 }
116
117
118 /**
119  * Context containing information about a GET request.
120  */
121 struct GetRequestContext
122 {
123   /**
124    * extended query (see gnunet_block_lib.h).
125    */
126   const void *xquery;
127
128   /**
129    * Bloomfilter to filter out duplicate replies (updated)
130    */
131   struct GNUNET_CONTAINER_BloomFilter **reply_bf;
132
133   /**
134    * The key this request was about
135    */
136   struct GNUNET_HashCode key;
137
138   /**
139    * The trail this request was for
140    */
141   const struct GNUNET_HashCode *trail_id;
142
143   /**
144    * Number of bytes in @e xquery.
145    */
146   size_t xquery_size;
147
148   /**
149    * Mutator value for the @e reply_bf, see gnunet_block_lib.h
150    */
151   uint32_t reply_bf_mutator;
152
153   /**
154    * Return value to give back.
155    */
156   enum GNUNET_BLOCK_EvaluationResult eval;
157
158 };
159
160
161 /**
162  * Iterator for local get request results,
163  *
164  * @param cls closure for iterator, a `struct GetRequestContext`
165  * @param key the key this data is stored under
166  * @param size the size of the data identified by key
167  * @param data the actual data
168  * @param type the type of the data
169  * @param exp when does this value expire?
170  * @param put_path_length number of peers in @a put_path
171  * @param put_path path the reply took on put
172  * @return #GNUNET_OK to continue iteration, anything else
173  * to stop iteration.
174  */
175 static int
176 datacache_get_iterator (void *cls,
177                         const struct GNUNET_HashCode *key,
178                         size_t size,
179                         const char *data,
180                         enum GNUNET_BLOCK_Type type,
181                         struct GNUNET_TIME_Absolute exp,
182                         unsigned int put_path_length,
183                         const struct GNUNET_PeerIdentity *put_path)
184 {
185   struct GetRequestContext *ctx = cls;
186   enum GNUNET_BLOCK_EvaluationResult eval;
187
188   eval =
189       GNUNET_BLOCK_evaluate (GDS_block_context,
190                              type,
191                              GNUNET_BLOCK_EO_NONE,
192                              key,
193                              ctx->reply_bf,
194                              ctx->reply_bf_mutator,
195                              ctx->xquery,
196                              ctx->xquery_size,
197                              data,
198                              size);
199   LOG (GNUNET_ERROR_TYPE_DEBUG,
200        "Found reply for query %s in datacache, evaluation result is %d\n",
201        GNUNET_h2s (key), (int) eval);
202   ctx->eval = eval;
203
204   switch (eval)
205   {
206   case GNUNET_BLOCK_EVALUATION_OK_MORE:
207   case GNUNET_BLOCK_EVALUATION_OK_LAST:
208     /* forward to local clients */
209     GNUNET_STATISTICS_update (GDS_stats,
210                               gettext_noop
211                               ("# Good RESULTS found in datacache"), 1,
212                               GNUNET_NO);
213     GDS_NEIGHBOURS_send_get_result (ctx->trail_id,
214                                     key,
215                                     type,
216                                     put_path_length,
217                                     put_path,
218                                     exp,
219                                     data,
220                                     size);
221     break;
222   case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE:
223     GNUNET_STATISTICS_update (GDS_stats,
224                               gettext_noop
225                               ("# Duplicate RESULTS found in datacache"), 1,
226                               GNUNET_NO);
227     break;
228   case GNUNET_BLOCK_EVALUATION_RESULT_INVALID:
229     GNUNET_STATISTICS_update (GDS_stats,
230                               gettext_noop
231                               ("# Invalid RESULTS found in datacache"), 1,
232                               GNUNET_NO);
233     break;
234   case GNUNET_BLOCK_EVALUATION_RESULT_IRRELEVANT:
235     GNUNET_STATISTICS_update (GDS_stats,
236                               gettext_noop
237                               ("# Irrelevant RESULTS found in datacache"), 1,
238                               GNUNET_NO);
239     break;
240   case GNUNET_BLOCK_EVALUATION_REQUEST_VALID:
241     GNUNET_break (0);
242     break;
243   case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID:
244     GNUNET_break_op (0);
245     return GNUNET_SYSERR;
246   case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED:
247     GNUNET_STATISTICS_update (GDS_stats,
248                               gettext_noop
249                               ("# Unsupported RESULTS found in datacache"), 1,
250                               GNUNET_NO);
251     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
252                 _("Unsupported block type (%u) in local response!\n"), type);
253     break;
254   }
255
256   return (eval == GNUNET_BLOCK_EVALUATION_OK_LAST) ? GNUNET_NO : GNUNET_OK;
257 }
258
259
260 /**
261  * Handle a GET request we've received from another peer.
262  *
263  * @param trail_id trail identifying where to send the result to, NULL for us
264  * @param key the query
265  * @param type requested data type
266  * @param xquery extended query
267  * @param xquery_size number of bytes in @a xquery
268  * @param reply_bf where the reply bf is (to be) stored, possibly updated, can be NULL
269  * @param reply_bf_mutator mutation value for @a reply_bf
270  * @return evaluation result for the local replies
271  */
272 enum GNUNET_BLOCK_EvaluationResult
273 GDS_DATACACHE_handle_get (const struct GNUNET_HashCode *trail_id,
274                           const struct GNUNET_HashCode *key,
275                           enum GNUNET_BLOCK_Type type,
276                           const void *xquery,
277                           size_t xquery_size,
278                           struct GNUNET_CONTAINER_BloomFilter **reply_bf,
279                           uint32_t reply_bf_mutator)
280 {
281   struct GetRequestContext ctx;
282   unsigned int r;
283
284   if (NULL == datacache)
285     return GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
286   GNUNET_STATISTICS_update (GDS_stats,
287                             gettext_noop ("# GET requests given to datacache"),
288                             1, GNUNET_NO);
289   ctx.eval = GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
290   ctx.trail_id = trail_id;
291   ctx.key = *key;
292   ctx.xquery = xquery;
293   ctx.xquery_size = xquery_size;
294   ctx.reply_bf = reply_bf;
295   ctx.reply_bf_mutator = reply_bf_mutator;
296   r = GNUNET_DATACACHE_get (datacache,
297                             key,
298                             type,
299                             &datacache_get_iterator,
300                             &ctx);
301   DEBUG ("DATACACHE_GET for key %s completed (%d). %u results found.\n",
302          GNUNET_h2s (key),
303          ctx.eval,
304          r);
305   return ctx.eval;
306 }
307
308
309 /**
310  * Function called with a random element from the datacache.
311  * Stores the key in the closure.
312  *
313  * @param cls a `struct GNUNET_HashCode *`, where to store the @a key
314  * @param key key for the content
315  * @param data_size number of bytes in @a data
316  * @param data content stored
317  * @param type type of the content
318  * @param exp when will the content expire?
319  * @param path_info_len number of entries in @a path_info
320  * @param path_info a path through the network
321  * @return #GNUNET_OK to continue iterating, #GNUNET_SYSERR to abort
322  */
323 static int
324 datacache_random_iterator (void *cls,
325                            const struct GNUNET_HashCode *key,
326                            size_t data_size,
327                            const char *data,
328                            enum GNUNET_BLOCK_Type type,
329                            struct GNUNET_TIME_Absolute exp,
330                            unsigned int path_info_len,
331                            const struct GNUNET_PeerIdentity *path_info)
332 {
333   struct GNUNET_HashCode *dest = cls;
334
335   *dest = *key;
336   return GNUNET_OK; /* should actually not matter which we return */
337 }
338
339
340 /**
341  * Obtain a random key from the datacache.
342  * Used by Whanau for load-balancing.
343  *
344  * @param[out] key where to store the key of a random element,
345  *             randomized by PRNG if datacache is empty
346  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the datacache is empty
347  */
348 int
349 GDS_DATACACHE_get_random_key (struct GNUNET_HashCode *key)
350 {
351   if (0 ==
352       GNUNET_DATACACHE_get_random (datacache,
353                                    &datacache_random_iterator,
354                                    key))
355   {
356     /* randomize key in this case */
357     GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
358                                       key);
359     return GNUNET_SYSERR;
360   }
361   return GNUNET_OK;
362 }
363
364 /**
365  * Iterator for local get request results,
366  *
367  * @param cls closure with the `struct GNUNET_HashCode *` with the trail ID
368  * @param key the key this data is stored under
369  * @param size the size of the data identified by key
370  * @param data the actual data
371  * @param type the type of the data
372  * @param exp when does this value expire?
373  * @param put_path_length number of peers in @a put_path
374  * @param put_path path the reply took on put
375  * @return #GNUNET_OK to continue iteration, anything else
376  * to stop iteration.
377  */
378 static int
379 datacache_get_successors_iterator (void *cls,
380                                    const struct GNUNET_HashCode *key,
381                                    size_t size,
382                                    const char *data,
383                                    enum GNUNET_BLOCK_Type type,
384                                    struct GNUNET_TIME_Absolute exp,
385                                    unsigned int put_path_length,
386                                    const struct GNUNET_PeerIdentity *put_path)
387 {
388   const struct GNUNET_HashCode *trail_id = cls;
389
390   GDS_NEIGHBOURS_send_get_result (trail_id,
391                                   key,
392                                   type,
393                                   put_path_length, put_path,
394                                   exp,
395                                   data,
396                                   size);
397   return GNUNET_OK;
398 }
399
400
401 /**
402  * Handle a request for data close to a key that we have received from
403  * another peer.
404  *
405  * @param trail_id trail where the reply needs to be send to
406  * @param key the location at which the peer is looking for data that is close
407  */
408 void
409 GDS_DATACACHE_get_successors (const struct GNUNET_HashCode *trail_id,
410                               const struct GNUNET_HashCode *key)
411 {
412   (void) GNUNET_DATACACHE_get_closest (datacache,
413                                        key,
414                                        NUM_CLOSEST,
415                                        &datacache_get_successors_iterator,
416                                        (void *) trail_id);
417 }
418
419
420 /**
421  * Initialize datacache subsystem.
422  */
423 void
424 GDS_DATACACHE_init ()
425 {
426   datacache = GNUNET_DATACACHE_create (GDS_cfg, "dhtcache");
427 }
428
429
430 /**
431  * Shutdown datacache subsystem.
432  */
433 void
434 GDS_DATACACHE_done ()
435 {
436   if (NULL != datacache)
437   {
438     GNUNET_DATACACHE_destroy (datacache);
439     datacache = NULL;
440   }
441 }
442
443
444 /* end of gnunet-service-wdht_datacache.c */