2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011, 2015, 2017 GNUnet e.V.
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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
21 * @file dht/gnunet-service-dht_datacache.c
22 * @brief GNUnet DHT service's datacache integration
23 * @author Christian Grothoff
24 * @author Nathan Evans
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"
33 #define LOG(kind, ...) GNUNET_log_from (kind, "dht-dhtcache", __VA_ARGS__)
36 * How many "closest" results to we return for migration when
39 #define NUM_CLOSEST 42
42 * Handle to the datacache service (for inserting/retrieving data)
44 static struct GNUNET_DATACACHE_Handle *datacache;
48 * Handle a datum we've received from another peer. Cache if
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
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,
70 if (NULL == datacache)
72 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
73 _ ("%s request received, but have no datacache!\n"), "PUT");
76 if (data_size >= GNUNET_MAX_MESSAGE_SIZE)
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"),
86 r = GNUNET_DATACACHE_put (datacache,
88 GNUNET_CRYPTO_hash_matching_bits (key,
96 LOG (GNUNET_ERROR_TYPE_DEBUG,
97 "DATACACHE PUT for key %s [%u] completed (%d) after %u hops\n",
106 * Context containing information about a GET request.
108 struct GetRequestContext
111 * extended query (see gnunet_block_lib.h).
116 * The key this request was about
118 struct GNUNET_HashCode key;
121 * Block group to use to evaluate replies (updated)
123 struct GNUNET_BLOCK_Group *bg;
126 * Function to call on results.
128 GDS_DATACACHE_GetCallback gc;
136 * Number of bytes in xquery.
141 * Return value to give back.
143 enum GNUNET_BLOCK_EvaluationResult eval;
148 * Iterator for local get request results,
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
162 datacache_get_iterator (void *cls,
163 const struct GNUNET_HashCode *key,
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)
171 static char non_null;
172 struct GetRequestContext *ctx = cls;
173 enum GNUNET_BLOCK_EvaluationResult eval;
175 if (0 == GNUNET_TIME_absolute_get_remaining (exp).rel_value_us)
177 GNUNET_break (0); /* why does datacache return expired values? */
178 return GNUNET_OK; /* skip expired record */
180 if ((NULL == data) &&
182 data = &non_null; /* point anywhere, but not to NULL */
185 = GNUNET_BLOCK_evaluate (GDS_block_context,
188 GNUNET_BLOCK_EO_LOCAL_SKIP_CRYPTO,
194 LOG (GNUNET_ERROR_TYPE_DEBUG,
195 "Found reply for query %s in datacache, evaluation result is %d\n",
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,
206 ("# Good RESULTS found in datacache"), 1,
208 ctx->gc (ctx->gc_cls,
212 put_path_length, put_path,
217 case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE:
218 GNUNET_STATISTICS_update (GDS_stats,
220 "# Duplicate RESULTS found in datacache"),
225 case GNUNET_BLOCK_EVALUATION_RESULT_INVALID:
226 GNUNET_STATISTICS_update (GDS_stats,
228 "# Invalid RESULTS found in datacache"),
233 case GNUNET_BLOCK_EVALUATION_RESULT_IRRELEVANT:
234 GNUNET_STATISTICS_update (GDS_stats,
236 "# Irrelevant RESULTS found in datacache"),
241 case GNUNET_BLOCK_EVALUATION_REQUEST_VALID:
245 case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID:
247 return GNUNET_SYSERR;
249 case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED:
250 GNUNET_STATISTICS_update (GDS_stats,
252 "# Unsupported RESULTS found in datacache"),
255 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
256 _ ("Unsupported block type (%u) in local response!\n"),
260 return (eval == GNUNET_BLOCK_EVALUATION_OK_LAST) ? GNUNET_NO : GNUNET_OK;
265 * Handle a GET request we've received from another peer.
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
276 enum GNUNET_BLOCK_EvaluationResult
277 GDS_DATACACHE_handle_get (const struct GNUNET_HashCode *key,
278 enum GNUNET_BLOCK_Type type,
281 struct GNUNET_BLOCK_Group *bg,
282 GDS_DATACACHE_GetCallback gc,
285 struct GetRequestContext ctx;
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"),
294 ctx.eval = GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
297 ctx.xquery_size = xquery_size;
301 r = GNUNET_DATACACHE_get (datacache,
304 &datacache_get_iterator,
306 LOG (GNUNET_ERROR_TYPE_DEBUG,
307 "DATACACHE GET for key %s completed (%d). %u results found.\n",
316 * Function called with a random element from the datacache.
317 * Stores the key in the closure.
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
330 datacache_random_iterator (void *cls,
331 const struct GNUNET_HashCode *key,
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)
339 struct GNUNET_HashCode *dest = cls;
342 return GNUNET_OK; /* should actually not matter which we return */
347 * Obtain a random key from the datacache.
348 * Used by Whanau for load-balancing.
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
355 GDS_DATACACHE_get_random_key (struct GNUNET_HashCode *key)
358 GNUNET_DATACACHE_get_random (datacache,
359 &datacache_random_iterator,
362 /* randomize key in this case */
363 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
365 return GNUNET_SYSERR;
372 * Closure for #datacache_get_successors_iterator().
377 * Function to call on the result
379 GDS_DATACACHE_SuccessorCallback cb;
389 * Iterator for local get request results,
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
403 datacache_get_successors_iterator (void *cls,
404 const struct GNUNET_HashCode *key,
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)
412 const struct SuccContext *sc = cls;
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). */
419 GNUNET_DHT_RO_RECORD_ROUTE,
422 put_path_length, put_path,
431 * Handle a request for data close to a key that we have received from
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
439 GDS_DATACACHE_get_successors (const struct GNUNET_HashCode *key,
440 GDS_DATACACHE_SuccessorCallback cb,
443 struct SuccContext sc;
447 (void) GNUNET_DATACACHE_get_closest (datacache,
450 &datacache_get_successors_iterator,
456 * Initialize datacache subsystem.
459 GDS_DATACACHE_init ()
461 datacache = GNUNET_DATACACHE_create (GDS_cfg, "dhtcache");
466 * Shutdown datacache subsystem.
469 GDS_DATACACHE_done ()
471 if (NULL != datacache)
473 GNUNET_DATACACHE_destroy (datacache);
479 /* end of gnunet-service-dht_datacache.c */