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