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