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