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