psycstore
[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   struct GetRequestContext *ctx = cls;
174   enum GNUNET_BLOCK_EvaluationResult eval;
175
176   eval
177     = GNUNET_BLOCK_evaluate (GDS_block_context,
178                              type,
179                              GNUNET_BLOCK_EO_LOCAL_SKIP_CRYPTO,
180                              key,
181                              ctx->reply_bf,
182                              ctx->reply_bf_mutator,
183                              ctx->xquery,
184                              ctx->xquery_size,
185                              data,
186                              data_size);
187   LOG (GNUNET_ERROR_TYPE_DEBUG,
188        "Found reply for query %s in datacache, evaluation result is %d\n",
189        GNUNET_h2s (key),
190        (int) eval);
191   ctx->eval = eval;
192   switch (eval)
193   {
194   case GNUNET_BLOCK_EVALUATION_OK_MORE:
195   case GNUNET_BLOCK_EVALUATION_OK_LAST:
196     /* forward to local clients */
197     GNUNET_STATISTICS_update (GDS_stats,
198                               gettext_noop
199                               ("# Good RESULTS found in datacache"), 1,
200                               GNUNET_NO);
201     ctx->gc (ctx->gc_cls,
202              type,
203              exp,
204              key,
205              put_path_length, put_path,
206              0, NULL,
207              data, data_size);
208     break;
209   case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE:
210     GNUNET_STATISTICS_update (GDS_stats,
211                               gettext_noop ("# Duplicate RESULTS found in datacache"),
212                               1,
213                               GNUNET_NO);
214     break;
215   case GNUNET_BLOCK_EVALUATION_RESULT_INVALID:
216     GNUNET_STATISTICS_update (GDS_stats,
217                               gettext_noop ("# Invalid RESULTS found in datacache"),
218                               1,
219                               GNUNET_NO);
220     break;
221   case GNUNET_BLOCK_EVALUATION_RESULT_IRRELEVANT:
222     GNUNET_STATISTICS_update (GDS_stats,
223                               gettext_noop ("# Irrelevant RESULTS found in datacache"),
224                               1,
225                               GNUNET_NO);
226     break;
227   case GNUNET_BLOCK_EVALUATION_REQUEST_VALID:
228     GNUNET_break (0);
229     break;
230   case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID:
231     GNUNET_break_op (0);
232     return GNUNET_SYSERR;
233   case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED:
234     GNUNET_STATISTICS_update (GDS_stats,
235                               gettext_noop ("# Unsupported RESULTS found in datacache"),
236                               1,
237                               GNUNET_NO);
238     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
239                 _("Unsupported block type (%u) in local response!\n"),
240                 type);
241     break;
242   }
243   return (eval == GNUNET_BLOCK_EVALUATION_OK_LAST) ? GNUNET_NO : GNUNET_OK;
244 }
245
246
247 /**
248  * Handle a GET request we've received from another peer.
249  *
250  * @param key the query
251  * @param type requested data type
252  * @param xquery extended query
253  * @param xquery_size number of bytes in @a xquery
254  * @param reply_bf where the reply bf is (to be) stored, possibly updated, can be NULL
255  * @param reply_bf_mutator mutation value for @a reply_bf
256  * @param gc function to call on the results
257  * @param gc_cls closure for @a gc
258  * @return evaluation result for the local replies
259  */
260 enum GNUNET_BLOCK_EvaluationResult
261 GDS_DATACACHE_handle_get (const struct GNUNET_HashCode *key,
262                           enum GNUNET_BLOCK_Type type,
263                           const void *xquery,
264                           size_t xquery_size,
265                           struct GNUNET_CONTAINER_BloomFilter **reply_bf,
266                           uint32_t reply_bf_mutator,
267                           GDS_DATACACHE_GetCallback gc,
268                           void *gc_cls)
269 {
270   struct GetRequestContext ctx;
271   unsigned int r;
272
273   if (NULL == datacache)
274     return GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
275   GNUNET_STATISTICS_update (GDS_stats,
276                             gettext_noop ("# GET requests given to datacache"),
277                             1,
278                             GNUNET_NO);
279   ctx.eval = GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
280   ctx.key = *key;
281   ctx.xquery = xquery;
282   ctx.xquery_size = xquery_size;
283   ctx.reply_bf = reply_bf;
284   ctx.reply_bf_mutator = reply_bf_mutator;
285   ctx.gc = gc;
286   ctx.gc_cls = gc_cls;
287   r = GNUNET_DATACACHE_get (datacache,
288                             key,
289                             type,
290                             &datacache_get_iterator,
291                             &ctx);
292   LOG (GNUNET_ERROR_TYPE_DEBUG,
293        "DATACACHE GET for key %s completed (%d). %u results found.\n",
294        GNUNET_h2s (key),
295        ctx.eval,
296        r);
297   return ctx.eval;
298 }
299
300
301 /**
302  * Function called with a random element from the datacache.
303  * Stores the key in the closure.
304  *
305  * @param cls a `struct GNUNET_HashCode *`, where to store the @a key
306  * @param key key for the content
307  * @param data_size number of bytes in @a data
308  * @param data content stored
309  * @param type type of the content
310  * @param exp when will the content expire?
311  * @param path_info_len number of entries in @a path_info
312  * @param path_info a path through the network
313  * @return #GNUNET_OK to continue iterating, #GNUNET_SYSERR to abort
314  */
315 static int
316 datacache_random_iterator (void *cls,
317                            const struct GNUNET_HashCode *key,
318                            size_t data_size,
319                            const char *data,
320                            enum GNUNET_BLOCK_Type type,
321                            struct GNUNET_TIME_Absolute exp,
322                            unsigned int path_info_len,
323                            const struct GNUNET_PeerIdentity *path_info)
324 {
325   struct GNUNET_HashCode *dest = cls;
326
327   *dest = *key;
328   return GNUNET_OK; /* should actually not matter which we return */
329 }
330
331
332 /**
333  * Obtain a random key from the datacache.
334  * Used by Whanau for load-balancing.
335  *
336  * @param[out] key where to store the key of a random element,
337  *             randomized by PRNG if datacache is empty
338  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the datacache is empty
339  */
340 int
341 GDS_DATACACHE_get_random_key (struct GNUNET_HashCode *key)
342 {
343   if (0 ==
344       GNUNET_DATACACHE_get_random (datacache,
345                                    &datacache_random_iterator,
346                                    key))
347   {
348     /* randomize key in this case */
349     GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
350                                       key);
351     return GNUNET_SYSERR;
352   }
353   return GNUNET_OK;
354 }
355
356
357 /**
358  * Closure for #datacache_get_successors_iterator().
359  */
360 struct SuccContext
361 {
362   /**
363    * Function to call on the result
364    */
365   GDS_DATACACHE_SuccessorCallback cb;
366
367   /**
368    * Closure for @e cb.
369    */
370   void *cb_cls;
371 };
372
373
374 /**
375  * Iterator for local get request results,
376  *
377  * @param cls closure with the `struct GNUNET_HashCode *` with the trail ID
378  * @param key the key this data is stored under
379  * @param size the size of the data identified by key
380  * @param data the actual data
381  * @param type the type of the data
382  * @param exp when does this value expire?
383  * @param put_path_length number of peers in @a put_path
384  * @param put_path path the reply took on put
385  * @return #GNUNET_OK to continue iteration, anything else
386  * to stop iteration.
387  */
388 static int
389 datacache_get_successors_iterator (void *cls,
390                                    const struct GNUNET_HashCode *key,
391                                    size_t size,
392                                    const char *data,
393                                    enum GNUNET_BLOCK_Type type,
394                                    struct GNUNET_TIME_Absolute exp,
395                                    unsigned int put_path_length,
396                                    const struct GNUNET_PeerIdentity *put_path)
397 {
398   const struct SuccContext *sc = cls;
399
400   /* NOTE: The datacache currently does not store the RO from
401      the original 'put', so we don't know the 'correct' option
402      at this point anymore.  Thus, we conservatively assume
403      that recording is desired (for now). */
404   sc->cb (sc->cb_cls,
405           GNUNET_DHT_RO_RECORD_ROUTE,
406           key,
407           type,
408           put_path_length, put_path,
409           exp,
410           data,
411           size);
412   return GNUNET_OK;
413 }
414
415
416 /**
417  * Handle a request for data close to a key that we have received from
418  * another peer.
419  *
420  * @param key the location at which the peer is looking for data that is close
421  * @param cb function to call with the result
422  * @param cb_cls closure for @a cb
423  */
424 void
425 GDS_DATACACHE_get_successors (const struct GNUNET_HashCode *key,
426                               GDS_DATACACHE_SuccessorCallback cb,
427                               void *cb_cls)
428 {
429   struct SuccContext sc;
430
431   sc.cb = cb;
432   sc.cb_cls = cb_cls;
433   (void) GNUNET_DATACACHE_get_closest (datacache,
434                                        key,
435                                        NUM_CLOSEST,
436                                        &datacache_get_successors_iterator,
437                                        &sc);
438 }
439
440
441 /**
442  * Initialize datacache subsystem.
443  */
444 void
445 GDS_DATACACHE_init ()
446 {
447   datacache = GNUNET_DATACACHE_create (GDS_cfg, "dhtcache");
448 }
449
450
451 /**
452  * Shutdown datacache subsystem.
453  */
454 void
455 GDS_DATACACHE_done ()
456 {
457   if (NULL != datacache)
458   {
459     GNUNET_DATACACHE_destroy (datacache);
460     datacache = NULL;
461   }
462 }
463
464
465 /* end of gnunet-service-dht_datacache.c */