-fix off-by-1
[oweals/gnunet.git] / src / dht / gnunet-service-wdht_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 /**
22  * @file dht/gnunet-service-wdht_datacache.c
23  * @brief GNUnet DHT service's datacache integration
24  * @author Christian Grothoff
25  * @author Nathan Evans
26  */
27 #include "platform.h"
28 #include "gnunet_datacache_lib.h"
29 #include "gnunet-service-wdht_clients.h"
30 #include "gnunet-service-wdht_datacache.h"
31 #include "gnunet-service-wdht_neighbours.h"
32 #include "gnunet-service-dht.h"
33
34 #define LOG(kind,...) GNUNET_log_from (kind, "dht-dtcache",__VA_ARGS__)
35
36 #define DEBUG(...)                                           \
37   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
38
39 /**
40  * How many "closest" results to we return for migration when
41  * asked (at most)?
42  */
43 #define NUM_CLOSEST 42
44
45 /**
46  * Handle to the datacache service (for inserting/retrieving data)
47  */
48 static struct GNUNET_DATACACHE_Handle *datacache;
49
50
51 /**
52  * Handle a datum we've received from another peer.  Cache if
53  * possible.
54  *
55  * @param expiration when will the reply expire
56  * @param key the query this reply is for
57  * @param put_path_length number of peers in @a put_path
58  * @param put_path path the reply took on put
59  * @param get_path_length number of peers in @a get_path
60  * @param get_path path the reply took on get
61  * @param type type of the reply
62  * @param data_size number of bytes in @a data
63  * @param data application payload data
64  */
65 void
66 GDS_DATACACHE_handle_put (struct GNUNET_TIME_Absolute expiration,
67                           const struct GNUNET_HashCode *key,
68                           unsigned int put_path_length,
69                           const struct GNUNET_PeerIdentity *put_path,
70                           unsigned int get_path_length,
71                           const struct GNUNET_PeerIdentity *get_path,
72                           enum GNUNET_BLOCK_Type type,
73                           size_t data_size,
74                           const void *data)
75 {
76   int r;
77   struct GNUNET_PeerIdentity path[get_path_length + put_path_length];
78
79   if (NULL == datacache)
80   {
81     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
82                 _("PUT request received, but have no datacache!\n"));
83     return;
84   }
85   if (data_size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
86   {
87     GNUNET_break (0);
88     return;
89   }
90   memcpy (path,
91           put_path,
92           put_path_length * sizeof (struct GNUNET_PeerIdentity));
93   memcpy (&path[put_path_length],
94           get_path,
95           get_path_length * sizeof (struct GNUNET_PeerIdentity));
96   /* Put size is actual data size plus struct overhead plus path length (if any) */
97   r = GNUNET_DATACACHE_put (datacache,
98                             key,
99                             data_size,
100                             data,
101                             type,
102                             expiration,
103                             get_path_length + put_path_length,
104                             path);
105   if (GNUNET_OK == r)
106     GNUNET_STATISTICS_update (GDS_stats,
107                               gettext_noop ("# ITEMS stored in datacache"), 1,
108                               GNUNET_NO);
109   LOG (GNUNET_ERROR_TYPE_DEBUG,
110        "DATACACHE PUT for key %s [%u] completed (%d) after %u hops\n",
111        GNUNET_h2s (key),
112        data_size,
113        r,
114        put_path_length + get_path_length);
115 }
116
117
118 /**
119  * Context containing information about a GET request.
120  */
121 struct GetRequestContext
122 {
123   /**
124    * extended query (see gnunet_block_lib.h).
125    */
126   const void *xquery;
127
128   /**
129    * Bloomfilter to filter out duplicate replies (updated)
130    */
131   struct GNUNET_CONTAINER_BloomFilter **reply_bf;
132
133   /**
134    * The key this request was about
135    */
136   struct GNUNET_HashCode key;
137
138   /**
139    * The trail this request was for
140    */
141   const struct GNUNET_HashCode *trail_id;
142
143   /**
144    * Number of bytes in @e xquery.
145    */
146   size_t xquery_size;
147
148   /**
149    * Mutator value for the @e reply_bf, see gnunet_block_lib.h
150    */
151   uint32_t reply_bf_mutator;
152
153   /**
154    * Return value to give back.
155    */
156   enum GNUNET_BLOCK_EvaluationResult eval;
157
158   /**
159    * Routing options of the GET.
160    */
161   enum GNUNET_DHT_RouteOption options;
162
163 };
164
165
166 /**
167  * Iterator for local get request results,
168  *
169  * @param cls closure for iterator, a `struct GetRequestContext`
170  * @param key the key this data is stored under
171  * @param size the size of the data identified by key
172  * @param data the actual data
173  * @param type the type of the data
174  * @param exp when does this value expire?
175  * @param put_path_length number of peers in @a put_path
176  * @param put_path path the reply took on put
177  * @return #GNUNET_OK to continue iteration, anything else
178  * to stop iteration.
179  */
180 static int
181 datacache_get_iterator (void *cls,
182                         const struct GNUNET_HashCode *key,
183                         size_t size,
184                         const char *data,
185                         enum GNUNET_BLOCK_Type type,
186                         struct GNUNET_TIME_Absolute exp,
187                         unsigned int put_path_length,
188                         const struct GNUNET_PeerIdentity *put_path)
189 {
190   struct GetRequestContext *ctx = cls;
191   enum GNUNET_BLOCK_EvaluationResult eval;
192
193   eval =
194       GNUNET_BLOCK_evaluate (GDS_block_context,
195                              type,
196                              GNUNET_BLOCK_EO_NONE,
197                              key,
198                              ctx->reply_bf,
199                              ctx->reply_bf_mutator,
200                              ctx->xquery,
201                              ctx->xquery_size,
202                              data,
203                              size);
204   LOG (GNUNET_ERROR_TYPE_DEBUG,
205        "Found reply for query %s in datacache, evaluation result is %d\n",
206        GNUNET_h2s (key), (int) eval);
207   ctx->eval = eval;
208
209   switch (eval)
210   {
211   case GNUNET_BLOCK_EVALUATION_OK_MORE:
212   case GNUNET_BLOCK_EVALUATION_OK_LAST:
213     /* forward to local clients */
214     GNUNET_STATISTICS_update (GDS_stats,
215                               gettext_noop
216                               ("# Good RESULTS found in datacache"), 1,
217                               GNUNET_NO);
218     GDS_NEIGHBOURS_send_get_result (ctx->trail_id,
219                                     ctx->options,
220                                     key,
221                                     type,
222                                     put_path_length,
223                                     put_path,
224                                     exp,
225                                     data,
226                                     size);
227     break;
228   case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE:
229     GNUNET_STATISTICS_update (GDS_stats,
230                               gettext_noop
231                               ("# Duplicate RESULTS found in datacache"), 1,
232                               GNUNET_NO);
233     break;
234   case GNUNET_BLOCK_EVALUATION_RESULT_INVALID:
235     GNUNET_STATISTICS_update (GDS_stats,
236                               gettext_noop
237                               ("# Invalid RESULTS found in datacache"), 1,
238                               GNUNET_NO);
239     break;
240   case GNUNET_BLOCK_EVALUATION_RESULT_IRRELEVANT:
241     GNUNET_STATISTICS_update (GDS_stats,
242                               gettext_noop
243                               ("# Irrelevant RESULTS found in datacache"), 1,
244                               GNUNET_NO);
245     break;
246   case GNUNET_BLOCK_EVALUATION_REQUEST_VALID:
247     GNUNET_break (0);
248     break;
249   case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID:
250     GNUNET_break_op (0);
251     return GNUNET_SYSERR;
252   case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED:
253     GNUNET_STATISTICS_update (GDS_stats,
254                               gettext_noop
255                               ("# Unsupported RESULTS found in datacache"), 1,
256                               GNUNET_NO);
257     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
258                 _("Unsupported block type (%u) in local response!\n"), type);
259     break;
260   }
261
262   return (eval == GNUNET_BLOCK_EVALUATION_OK_LAST) ? GNUNET_NO : GNUNET_OK;
263 }
264
265
266 /**
267  * Handle a GET request we've received from another peer.
268  *
269  * @param trail_id trail identifying where to send the result to, NULL for us
270  * @param options routing options (to be passed along)
271  * @param key the query
272  * @param type requested data type
273  * @param xquery extended query
274  * @param xquery_size number of bytes in @a xquery
275  * @param reply_bf where the reply bf is (to be) stored, possibly updated, can be NULL
276  * @param reply_bf_mutator mutation value for @a reply_bf
277  * @return evaluation result for the local replies
278  */
279 enum GNUNET_BLOCK_EvaluationResult
280 GDS_DATACACHE_handle_get (const struct GNUNET_HashCode *trail_id,
281                           enum GNUNET_DHT_RouteOption options,
282                           const struct GNUNET_HashCode *key,
283                           enum GNUNET_BLOCK_Type type,
284                           const void *xquery,
285                           size_t xquery_size,
286                           struct GNUNET_CONTAINER_BloomFilter **reply_bf,
287                           uint32_t reply_bf_mutator)
288 {
289   struct GetRequestContext ctx;
290   unsigned int r;
291
292   if (NULL == datacache)
293     return GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
294   GNUNET_STATISTICS_update (GDS_stats,
295                             gettext_noop ("# GET requests given to datacache"),
296                             1, GNUNET_NO);
297   ctx.eval = GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
298   ctx.trail_id = trail_id;
299   ctx.options = options;
300   ctx.key = *key;
301   ctx.xquery = xquery;
302   ctx.xquery_size = xquery_size;
303   ctx.reply_bf = reply_bf;
304   ctx.reply_bf_mutator = reply_bf_mutator;
305   r = GNUNET_DATACACHE_get (datacache,
306                             key,
307                             type,
308                             &datacache_get_iterator,
309                             &ctx);
310   DEBUG ("DATACACHE_GET for key %s completed (%d). %u results found.\n",
311          GNUNET_h2s (key),
312          ctx.eval,
313          r);
314   return ctx.eval;
315 }
316
317
318 /**
319  * Function called with a random element from the datacache.
320  * Stores the key in the closure.
321  *
322  * @param cls a `struct GNUNET_HashCode *`, where to store the @a key
323  * @param key key for the content
324  * @param data_size number of bytes in @a data
325  * @param data content stored
326  * @param type type of the content
327  * @param exp when will the content expire?
328  * @param path_info_len number of entries in @a path_info
329  * @param path_info a path through the network
330  * @return #GNUNET_OK to continue iterating, #GNUNET_SYSERR to abort
331  */
332 static int
333 datacache_random_iterator (void *cls,
334                            const struct GNUNET_HashCode *key,
335                            size_t data_size,
336                            const char *data,
337                            enum GNUNET_BLOCK_Type type,
338                            struct GNUNET_TIME_Absolute exp,
339                            unsigned int path_info_len,
340                            const struct GNUNET_PeerIdentity *path_info)
341 {
342   struct GNUNET_HashCode *dest = cls;
343
344   *dest = *key;
345   return GNUNET_OK; /* should actually not matter which we return */
346 }
347
348
349 /**
350  * Obtain a random key from the datacache.
351  * Used by Whanau for load-balancing.
352  *
353  * @param[out] key where to store the key of a random element,
354  *             randomized by PRNG if datacache is empty
355  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the datacache is empty
356  */
357 int
358 GDS_DATACACHE_get_random_key (struct GNUNET_HashCode *key)
359 {
360   if (0 ==
361       GNUNET_DATACACHE_get_random (datacache,
362                                    &datacache_random_iterator,
363                                    key))
364   {
365     /* randomize key in this case */
366     GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
367                                       key);
368     return GNUNET_SYSERR;
369   }
370   return GNUNET_OK;
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 GNUNET_HashCode *trail_id = 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   GDS_NEIGHBOURS_send_get_result (trail_id,
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 trail_id trail where the reply needs to be send to
421  * @param key the location at which the peer is looking for data that is close
422  */
423 void
424 GDS_DATACACHE_get_successors (const struct GNUNET_HashCode *trail_id,
425                               const struct GNUNET_HashCode *key)
426 {
427   (void) GNUNET_DATACACHE_get_closest (datacache,
428                                        key,
429                                        NUM_CLOSEST,
430                                        &datacache_get_successors_iterator,
431                                        (void *) trail_id);
432 }
433
434
435 /**
436  * Initialize datacache subsystem.
437  */
438 void
439 GDS_DATACACHE_init ()
440 {
441   datacache = GNUNET_DATACACHE_create (GDS_cfg, "dhtcache");
442 }
443
444
445 /**
446  * Shutdown datacache subsystem.
447  */
448 void
449 GDS_DATACACHE_done ()
450 {
451   if (NULL != datacache)
452   {
453     GNUNET_DATACACHE_destroy (datacache);
454     datacache = NULL;
455   }
456 }
457
458
459 /* end of gnunet-service-wdht_datacache.c */