REST: nothing triggers rest
[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      SPDX-License-Identifier: AGPL3.0-or-later
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_neighbours.h"
30 #include "gnunet-service-dht_routing.h"
31 #include "gnunet-service-dht.h"
32
33 #define LOG(kind,...) GNUNET_log_from (kind, "dht-dhtcache",__VA_ARGS__)
34
35 /**
36  * How many "closest" results to we return for migration when
37  * asked (at most)?
38  */
39 #define NUM_CLOSEST 42
40
41 /**
42  * Handle to the datacache service (for inserting/retrieving data)
43  */
44 static struct GNUNET_DATACACHE_Handle *datacache;
45
46
47 /**
48  * Handle a datum we've received from another peer.  Cache if
49  * possible.
50  *
51  * @param expiration when will the reply expire
52  * @param key the query this reply is for
53  * @param put_path_length number of peers in @a put_path
54  * @param put_path path the reply took on put
55  * @param type type of the reply
56  * @param data_size number of bytes in @a data
57  * @param data application payload data
58  */
59 void
60 GDS_DATACACHE_handle_put (struct GNUNET_TIME_Absolute expiration,
61                           const struct GNUNET_HashCode *key,
62                           unsigned int put_path_length,
63                           const struct GNUNET_PeerIdentity *put_path,
64                           enum GNUNET_BLOCK_Type type,
65                           size_t data_size,
66                           const void *data)
67 {
68   int r;
69
70   if (NULL == datacache)
71   {
72     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
73                 _("%s request received, but have no datacache!\n"), "PUT");
74     return;
75   }
76   if (data_size >= GNUNET_MAX_MESSAGE_SIZE)
77   {
78     GNUNET_break (0);
79     return;
80   }
81   /* Put size is actual data size plus struct overhead plus path length (if any) */
82   GNUNET_STATISTICS_update (GDS_stats,
83                             gettext_noop ("# ITEMS stored in datacache"),
84                             1,
85                             GNUNET_NO);
86   r = GNUNET_DATACACHE_put (datacache,
87                             key,
88                             GNUNET_CRYPTO_hash_matching_bits (key,
89                                                               &my_identity_hash),
90                             data_size,
91                             data,
92                             type,
93                             expiration,
94                             put_path_length,
95                             put_path);
96   LOG (GNUNET_ERROR_TYPE_DEBUG,
97        "DATACACHE PUT for key %s [%u] completed (%d) after %u hops\n",
98        GNUNET_h2s (key),
99        data_size,
100        r,
101        put_path_length);
102 }
103
104
105 /**
106  * Context containing information about a GET request.
107  */
108 struct GetRequestContext
109 {
110   /**
111    * extended query (see gnunet_block_lib.h).
112    */
113   const void *xquery;
114
115   /**
116    * The key this request was about
117    */
118   struct GNUNET_HashCode key;
119
120   /**
121    * Block group to use to evaluate replies (updated)
122    */
123   struct GNUNET_BLOCK_Group *bg;
124
125   /**
126    * Function to call on results.
127    */
128   GDS_DATACACHE_GetCallback gc;
129
130   /**
131    * Closure for @e gc.
132    */
133   void *gc_cls;
134
135   /**
136    * Number of bytes in xquery.
137    */
138   size_t xquery_size;
139
140   /**
141    * Return value to give back.
142    */
143   enum GNUNET_BLOCK_EvaluationResult eval;
144
145 };
146
147
148 /**
149  * Iterator for local get request results,
150  *
151  * @param cls closure for iterator, a `struct GetRequestContext`
152  * @param exp when does this value expire?
153  * @param key the key this data is stored under
154  * @param data_size the size of the data identified by key
155  * @param data the actual data
156  * @param type the type of the @a data
157  * @param put_path_length number of peers in @a put_path
158  * @param put_path path the reply took on put
159  * @return #GNUNET_OK to continue iteration, anything else
160  * to stop iteration.
161  */
162 static int
163 datacache_get_iterator (void *cls,
164                         const struct GNUNET_HashCode *key,
165                         size_t data_size,
166                         const char *data,
167                         enum GNUNET_BLOCK_Type type,
168                         struct GNUNET_TIME_Absolute exp,
169                         unsigned int put_path_length,
170                         const struct GNUNET_PeerIdentity *put_path)
171 {
172   static char non_null;
173   struct GetRequestContext *ctx = cls;
174   enum GNUNET_BLOCK_EvaluationResult eval;
175
176   if (0 == GNUNET_TIME_absolute_get_remaining (exp).rel_value_us)
177   {
178     GNUNET_break (0); /* why does datacache return expired values? */
179     return GNUNET_OK; /* skip expired record */
180   }
181   if ( (NULL == data) &&
182        (0 == data_size) )
183     data = &non_null; /* point anywhere, but not to NULL */
184
185   eval
186     = GNUNET_BLOCK_evaluate (GDS_block_context,
187                              type,
188                              ctx->bg,
189                              GNUNET_BLOCK_EO_LOCAL_SKIP_CRYPTO,
190                              key,
191                              ctx->xquery,
192                              ctx->xquery_size,
193                              data,
194                              data_size);
195   LOG (GNUNET_ERROR_TYPE_DEBUG,
196        "Found reply for query %s in datacache, evaluation result is %d\n",
197        GNUNET_h2s (key),
198        (int) eval);
199   ctx->eval = eval;
200   switch (eval)
201   {
202   case GNUNET_BLOCK_EVALUATION_OK_MORE:
203   case GNUNET_BLOCK_EVALUATION_OK_LAST:
204     /* forward to local clients */
205     GNUNET_STATISTICS_update (GDS_stats,
206                               gettext_noop
207                               ("# Good RESULTS found in datacache"), 1,
208                               GNUNET_NO);
209     ctx->gc (ctx->gc_cls,
210              type,
211              exp,
212              key,
213              put_path_length, put_path,
214              0, NULL,
215              data, data_size);
216     break;
217   case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE:
218     GNUNET_STATISTICS_update (GDS_stats,
219                               gettext_noop ("# Duplicate RESULTS found in datacache"),
220                               1,
221                               GNUNET_NO);
222     break;
223   case GNUNET_BLOCK_EVALUATION_RESULT_INVALID:
224     GNUNET_STATISTICS_update (GDS_stats,
225                               gettext_noop ("# Invalid RESULTS found in datacache"),
226                               1,
227                               GNUNET_NO);
228     break;
229   case GNUNET_BLOCK_EVALUATION_RESULT_IRRELEVANT:
230     GNUNET_STATISTICS_update (GDS_stats,
231                               gettext_noop ("# Irrelevant RESULTS found in datacache"),
232                               1,
233                               GNUNET_NO);
234     break;
235   case GNUNET_BLOCK_EVALUATION_REQUEST_VALID:
236     GNUNET_break (0);
237     break;
238   case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID:
239     GNUNET_break_op (0);
240     return GNUNET_SYSERR;
241   case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED:
242     GNUNET_STATISTICS_update (GDS_stats,
243                               gettext_noop ("# Unsupported RESULTS found in datacache"),
244                               1,
245                               GNUNET_NO);
246     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
247                 _("Unsupported block type (%u) in local response!\n"),
248                 type);
249     break;
250   }
251   return (eval == GNUNET_BLOCK_EVALUATION_OK_LAST) ? GNUNET_NO : GNUNET_OK;
252 }
253
254
255 /**
256  * Handle a GET request we've received from another peer.
257  *
258  * @param key the query
259  * @param type requested data type
260  * @param xquery extended query
261  * @param xquery_size number of bytes in @a xquery
262  * @param bg block group to use for reply evaluation
263  * @param gc function to call on the results
264  * @param gc_cls closure for @a gc
265  * @return evaluation result for the local replies
266  */
267 enum GNUNET_BLOCK_EvaluationResult
268 GDS_DATACACHE_handle_get (const struct GNUNET_HashCode *key,
269                           enum GNUNET_BLOCK_Type type,
270                           const void *xquery,
271                           size_t xquery_size,
272                           struct GNUNET_BLOCK_Group *bg,
273                           GDS_DATACACHE_GetCallback gc,
274                           void *gc_cls)
275 {
276   struct GetRequestContext ctx;
277   unsigned int r;
278
279   if (NULL == datacache)
280     return GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
281   GNUNET_STATISTICS_update (GDS_stats,
282                             gettext_noop ("# GET requests given to datacache"),
283                             1,
284                             GNUNET_NO);
285   ctx.eval = GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
286   ctx.key = *key;
287   ctx.xquery = xquery;
288   ctx.xquery_size = xquery_size;
289   ctx.bg = bg;
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 */