adding a GNUNET_memcmp_priv for constant-time comparing of data; fixes #6152 (modulo...
[oweals/gnunet.git] / src / regex / regex_internal_dht.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2012, 2015 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 src/regex/regex_internal_dht.c
22  * @brief library to announce regexes in the network and match strings
23  * against published regexes.
24  * @author Bartlomiej Polot
25  */
26 #include "platform.h"
27 #include "regex_internal_lib.h"
28 #include "regex_block_lib.h"
29 #include "gnunet_dht_service.h"
30 #include "gnunet_statistics_service.h"
31 #include "gnunet_constants.h"
32 #include "gnunet_signatures.h"
33
34
35 #define LOG(kind, ...) GNUNET_log_from (kind, "regex-dht", __VA_ARGS__)
36
37 /**
38  * DHT replication level to use.
39  */
40 #define DHT_REPLICATION 5
41
42 /**
43  * DHT record lifetime to use.
44  */
45 #define DHT_TTL         GNUNET_TIME_UNIT_HOURS
46
47 /**
48  * DHT options to set.
49  */
50 #define DHT_OPT         GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE
51
52
53 /**
54  * Handle to store cached data about a regex announce.
55  */
56 struct REGEX_INTERNAL_Announcement
57 {
58   /**
59    * DHT handle to use, must be initialized externally.
60    */
61   struct GNUNET_DHT_Handle *dht;
62
63   /**
64    * Regular expression.
65    */
66   const char *regex;
67
68   /**
69    * Automaton representation of the regex (expensive to build).
70    */
71   struct REGEX_INTERNAL_Automaton *dfa;
72
73   /**
74    * Our private key.
75    */
76   const struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
77
78   /**
79    * Optional statistics handle to report usage. Can be NULL.
80    */
81   struct GNUNET_STATISTICS_Handle *stats;
82 };
83
84
85 /**
86  * Regex callback iterator to store own service description in the DHT.
87  *
88  * @param cls closure.
89  * @param key hash for current state.
90  * @param proof proof for current state.
91  * @param accepting #GNUNET_YES if this is an accepting state, #GNUNET_NO if not.
92  * @param num_edges number of edges leaving current state.
93  * @param edges edges leaving current state.
94  */
95 static void
96 regex_iterator (void *cls,
97                 const struct GNUNET_HashCode *key,
98                 const char *proof,
99                 int accepting,
100                 unsigned int num_edges,
101                 const struct REGEX_BLOCK_Edge *edges)
102 {
103   struct REGEX_INTERNAL_Announcement *h = cls;
104   struct RegexBlock *block;
105   size_t size;
106   unsigned int i;
107
108   LOG (GNUNET_ERROR_TYPE_INFO,
109        "DHT PUT for state %s with proof `%s' and %u edges:\n",
110        GNUNET_h2s (key),
111        proof,
112        num_edges);
113   for (i = 0; i < num_edges; i++)
114   {
115     LOG (GNUNET_ERROR_TYPE_INFO,
116          "Edge %u `%s' towards %s\n",
117          i,
118          edges[i].label,
119          GNUNET_h2s (&edges[i].destination));
120   }
121   if (GNUNET_YES == accepting)
122   {
123     struct RegexAcceptBlock ab;
124
125     LOG (GNUNET_ERROR_TYPE_INFO,
126          "State %s is accepting, putting own id\n",
127          GNUNET_h2s (key));
128     size = sizeof(struct RegexAcceptBlock);
129     ab.purpose.size = ntohl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
130                              + sizeof(struct GNUNET_TIME_AbsoluteNBO)
131                              + sizeof(struct GNUNET_HashCode));
132     ab.purpose.purpose = ntohl (GNUNET_SIGNATURE_PURPOSE_REGEX_ACCEPT);
133     ab.expiration_time = GNUNET_TIME_absolute_hton (
134       GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_DHT_MAX_EXPIRATION));
135     ab.key = *key;
136     GNUNET_CRYPTO_eddsa_key_get_public (h->priv,
137                                         &ab.peer.public_key);
138     GNUNET_assert (GNUNET_OK ==
139                    GNUNET_CRYPTO_eddsa_sign (h->priv,
140                                              &ab.purpose,
141                                              &ab.signature));
142
143     GNUNET_STATISTICS_update (h->stats, "# regex accepting blocks stored",
144                               1, GNUNET_NO);
145     GNUNET_STATISTICS_update (h->stats, "# regex accepting block bytes stored",
146                               sizeof(struct RegexAcceptBlock), GNUNET_NO);
147     (void)
148     GNUNET_DHT_put (h->dht, key,
149                     DHT_REPLICATION,
150                     DHT_OPT | GNUNET_DHT_RO_RECORD_ROUTE,
151                     GNUNET_BLOCK_TYPE_REGEX_ACCEPT,
152                     size,
153                     &ab,
154                     GNUNET_TIME_relative_to_absolute (DHT_TTL),
155                     NULL, NULL);
156   }
157   block = REGEX_BLOCK_create (proof,
158                               num_edges,
159                               edges,
160                               accepting,
161                               &size);
162   if (NULL == block)
163     return;
164   (void) GNUNET_DHT_put (h->dht,
165                          key,
166                          DHT_REPLICATION,
167                          DHT_OPT,
168                          GNUNET_BLOCK_TYPE_REGEX,
169                          size,
170                          block,
171                          GNUNET_TIME_relative_to_absolute (DHT_TTL),
172                          NULL,
173                          NULL);
174   GNUNET_STATISTICS_update (h->stats,
175                             "# regex blocks stored",
176                             1,
177                             GNUNET_NO);
178   GNUNET_STATISTICS_update (h->stats,
179                             "# regex block bytes stored",
180                             size,
181                             GNUNET_NO);
182   GNUNET_free (block);
183 }
184
185
186 /**
187  * Announce a regular expression: put all states of the automaton in the DHT.
188  * Does not free resources, must call #REGEX_INTERNAL_announce_cancel() for that.
189  *
190  * @param dht An existing and valid DHT service handle. CANNOT be NULL.
191  * @param priv our private key, must remain valid until the announcement is cancelled
192  * @param regex Regular expression to announce.
193  * @param compression How many characters per edge can we squeeze?
194  * @param stats Optional statistics handle to report usage. Can be NULL.
195  * @return Handle to reuse o free cached resources.
196  *         Must be freed by calling #REGEX_INTERNAL_announce_cancel().
197  */
198 struct REGEX_INTERNAL_Announcement *
199 REGEX_INTERNAL_announce (struct GNUNET_DHT_Handle *dht,
200                          const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
201                          const char *regex,
202                          uint16_t compression,
203                          struct GNUNET_STATISTICS_Handle *stats)
204 {
205   struct REGEX_INTERNAL_Announcement *h;
206
207   GNUNET_assert (NULL != dht);
208   h = GNUNET_new (struct REGEX_INTERNAL_Announcement);
209   h->regex = regex;
210   h->dht = dht;
211   h->stats = stats;
212   h->priv = priv;
213   h->dfa = REGEX_INTERNAL_construct_dfa (regex, strlen (regex), compression);
214   REGEX_INTERNAL_reannounce (h);
215   return h;
216 }
217
218
219 /**
220  * Announce again a regular expression previously announced.
221  * Does use caching to speed up process.
222  *
223  * @param h Handle returned by a previous #REGEX_INTERNAL_announce call().
224  */
225 void
226 REGEX_INTERNAL_reannounce (struct REGEX_INTERNAL_Announcement *h)
227 {
228   GNUNET_assert (NULL != h->dfa);  /* make sure to call announce first */
229   LOG (GNUNET_ERROR_TYPE_INFO,
230        "REGEX_INTERNAL_reannounce: %s\n",
231        h->regex);
232   REGEX_INTERNAL_iterate_reachable_edges (h->dfa,
233                                           &regex_iterator,
234                                           h);
235 }
236
237
238 /**
239  * Clear all cached data used by a regex announce.
240  * Does not close DHT connection.
241  *
242  * @param h Handle returned by a previous #REGEX_INTERNAL_announce() call.
243  */
244 void
245 REGEX_INTERNAL_announce_cancel (struct REGEX_INTERNAL_Announcement *h)
246 {
247   REGEX_INTERNAL_automaton_destroy (h->dfa);
248   GNUNET_free (h);
249 }
250
251
252 /******************************************************************************/
253
254
255 /**
256  * Struct to keep state of running searches that have consumed a part of
257  * the inital string.
258  */
259 struct RegexSearchContext
260 {
261   /**
262    * Part of the description already consumed by
263    * this particular search branch.
264    */
265   size_t position;
266
267   /**
268    * Information about the search.
269    */
270   struct REGEX_INTERNAL_Search *info;
271
272   /**
273    * We just want to look for one edge, the longer the better.
274    * Keep its length.
275    */
276   unsigned int longest_match;
277
278   /**
279    * Destination hash of the longest match.
280    */
281   struct GNUNET_HashCode hash;
282 };
283
284
285 /**
286  * Type of values in `dht_get_results`.
287  */
288 struct Result
289 {
290   /**
291    * Number of bytes in data.
292    */
293   size_t size;
294
295   /**
296    * The raw result data.
297    */
298   const void *data;
299 };
300
301
302 /**
303  * Struct to keep information of searches of services described by a regex
304  * using a user-provided string service description.
305  */
306 struct REGEX_INTERNAL_Search
307 {
308   /**
309    * DHT handle to use, must be initialized externally.
310    */
311   struct GNUNET_DHT_Handle *dht;
312
313   /**
314    * Optional statistics handle to report usage. Can be NULL.
315    */
316   struct GNUNET_STATISTICS_Handle *stats;
317
318   /**
319    * User provided description of the searched service.
320    */
321   char *description;
322
323   /**
324    * Running DHT GETs.
325    */
326   struct GNUNET_CONTAINER_MultiHashMap *dht_get_handles;
327
328   /**
329    * Results from running DHT GETs, values are of type
330    * 'struct Result'.
331    */
332   struct GNUNET_CONTAINER_MultiHashMap *dht_get_results;
333
334   /**
335    * Contexts, for each running DHT GET. Free all on end of search.
336    */
337   struct RegexSearchContext **contexts;
338
339   /**
340    * Number of contexts (branches/steps in search).
341    */
342   unsigned int n_contexts;
343
344   /**
345    * @param callback Callback for found peers.
346    */
347   REGEX_INTERNAL_Found callback;
348
349   /**
350    * @param callback_cls Closure for @c callback.
351    */
352   void *callback_cls;
353 };
354
355
356 /**
357  * Jump to the next edge, with the longest matching token.
358  *
359  * @param block Block found in the DHT.
360  * @param size Size of the block.
361  * @param ctx Context of the search.
362  */
363 static void
364 regex_next_edge (const struct RegexBlock *block,
365                  size_t size,
366                  struct RegexSearchContext *ctx);
367
368
369 /**
370  * Function to process DHT string to regex matching.
371  * Called on each result obtained for the DHT search.
372  *
373  * @param cls Closure (search context).
374  * @param exp When will this value expire.
375  * @param key Key of the result.
376  * @param get_path Path of the get request.
377  * @param get_path_length Lenght of get_path.
378  * @param put_path Path of the put request.
379  * @param put_path_length Length of the put_path.
380  * @param type Type of the result.
381  * @param size Number of bytes in data.
382  * @param data Pointer to the result data.
383  */
384 static void
385 dht_get_string_accept_handler (void *cls, struct GNUNET_TIME_Absolute exp,
386                                const struct GNUNET_HashCode *key,
387                                const struct GNUNET_PeerIdentity *get_path,
388                                unsigned int get_path_length,
389                                const struct GNUNET_PeerIdentity *put_path,
390                                unsigned int put_path_length,
391                                enum GNUNET_BLOCK_Type type,
392                                size_t size, const void *data)
393 {
394   const struct RegexAcceptBlock *block = data;
395   struct RegexSearchContext *ctx = cls;
396   struct REGEX_INTERNAL_Search *info = ctx->info;
397
398   LOG (GNUNET_ERROR_TYPE_DEBUG,
399        "Regex result accept for %s (key %s)\n",
400        info->description, GNUNET_h2s (key));
401
402   GNUNET_STATISTICS_update (info->stats,
403                             "# regex accepting blocks found",
404                             1, GNUNET_NO);
405   GNUNET_STATISTICS_update (info->stats,
406                             "# regex accepting block bytes found",
407                             size, GNUNET_NO);
408   info->callback (info->callback_cls,
409                   &block->peer,
410                   get_path, get_path_length,
411                   put_path, put_path_length);
412 }
413
414
415 /**
416  * Find a path to a peer that offers a regex service compatible
417  * with a given string.
418  *
419  * @param key The key of the accepting state.
420  * @param ctx Context containing info about the string, tunnel, etc.
421  */
422 static void
423 regex_find_path (const struct GNUNET_HashCode *key,
424                  struct RegexSearchContext *ctx)
425 {
426   struct GNUNET_DHT_GetHandle *get_h;
427
428   LOG (GNUNET_ERROR_TYPE_DEBUG,
429        "Accept state found, now searching for paths to %s\n",
430        GNUNET_h2s (key),
431        (unsigned int) ctx->position);
432   get_h = GNUNET_DHT_get_start (ctx->info->dht,     /* handle */
433                                 GNUNET_BLOCK_TYPE_REGEX_ACCEPT, /* type */
434                                 key,     /* key to search */
435                                 DHT_REPLICATION, /* replication level */
436                                 DHT_OPT | GNUNET_DHT_RO_RECORD_ROUTE,
437                                 NULL, /* xquery */       // FIXME BLOOMFILTER
438                                 0, /* xquery bits */     // FIXME BLOOMFILTER SIZE
439                                 &dht_get_string_accept_handler, ctx);
440   GNUNET_break (GNUNET_OK ==
441                 GNUNET_CONTAINER_multihashmap_put (ctx->info->dht_get_handles,
442                                                    key,
443                                                    get_h,
444                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
445 }
446
447
448 /**
449  * Function to process DHT string to regex matching.
450  * Called on each result obtained for the DHT search.
451  *
452  * @param cls closure (search context)
453  * @param exp when will this value expire
454  * @param key key of the result
455  * @param get_path path of the get request (not used)
456  * @param get_path_length length of @a get_path (not used)
457  * @param put_path path of the put request (not used)
458  * @param put_path_length length of the @a put_path (not used)
459  * @param type type of the result
460  * @param size number of bytes in data
461  * @param data pointer to the result data
462  *
463  * TODO: re-issue the request after certain time? cancel after X results?
464  */
465 static void
466 dht_get_string_handler (void *cls, struct GNUNET_TIME_Absolute exp,
467                         const struct GNUNET_HashCode *key,
468                         const struct GNUNET_PeerIdentity *get_path,
469                         unsigned int get_path_length,
470                         const struct GNUNET_PeerIdentity *put_path,
471                         unsigned int put_path_length,
472                         enum GNUNET_BLOCK_Type type,
473                         size_t size, const void *data)
474 {
475   const struct RegexBlock *block = data;
476   struct RegexSearchContext *ctx = cls;
477   struct REGEX_INTERNAL_Search *info = ctx->info;
478   size_t len;
479   struct Result *copy;
480
481   LOG (GNUNET_ERROR_TYPE_INFO,
482        "DHT GET result for %s (%s)\n",
483        GNUNET_h2s (key), ctx->info->description);
484   copy = GNUNET_malloc (sizeof(struct Result) + size);
485   copy->size = size;
486   copy->data = &copy[1];
487   GNUNET_memcpy (&copy[1], block, size);
488   GNUNET_break (GNUNET_OK ==
489                 GNUNET_CONTAINER_multihashmap_put (info->dht_get_results,
490                                                    key, copy,
491                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
492   len = strlen (info->description);
493   if (len == ctx->position) // String processed
494   {
495     if (GNUNET_YES == GNUNET_BLOCK_is_accepting (block, size))
496     {
497       regex_find_path (key, ctx);
498     }
499     else
500     {
501       LOG (GNUNET_ERROR_TYPE_INFO, "block not accepting!\n");
502       /* FIXME REGEX this block not successful, wait for more? start timeout? */
503     }
504     return;
505   }
506   regex_next_edge (block, size, ctx);
507 }
508
509
510 /**
511  * Iterator over found existing cadet regex blocks that match an ongoing search.
512  *
513  * @param cls Closure (current context)-
514  * @param key Current key code (key for cached block).
515  * @param value Value in the hash map (cached RegexBlock).
516  * @return #GNUNET_YES: we should always continue to iterate.
517  */
518 static int
519 regex_result_iterator (void *cls,
520                        const struct GNUNET_HashCode *key,
521                        void *value)
522 {
523   struct Result *result = value;
524   const struct RegexBlock *block = result->data;
525   struct RegexSearchContext *ctx = cls;
526
527   if ((GNUNET_YES ==
528        GNUNET_BLOCK_is_accepting (block, result->size)) &&
529       (ctx->position == strlen (ctx->info->description)))
530   {
531     LOG (GNUNET_ERROR_TYPE_INFO,
532          "Found accepting known block\n");
533     regex_find_path (key, ctx);
534     return GNUNET_YES;   // We found an accept state!
535   }
536   LOG (GNUNET_ERROR_TYPE_DEBUG,
537        "* %u, %u, [%u]\n",
538        ctx->position,
539        strlen (ctx->info->description),
540        GNUNET_BLOCK_is_accepting (block, result->size));
541   regex_next_edge (block, result->size, ctx);
542
543   GNUNET_STATISTICS_update (ctx->info->stats, "# regex cadet blocks iterated",
544                             1, GNUNET_NO);
545
546   return GNUNET_YES;
547 }
548
549
550 /**
551  * Iterator over edges in a regex block retrieved from the DHT.
552  *
553  * @param cls Closure (context of the search).
554  * @param token Token that follows to next state.
555  * @param len Lenght of token.
556  * @param key Hash of next state.
557  * @return #GNUNET_YES if should keep iterating, #GNUNET_NO otherwise.
558  */
559 static int
560 regex_edge_iterator (void *cls,
561                      const char *token,
562                      size_t len,
563                      const struct GNUNET_HashCode *key)
564 {
565   struct RegexSearchContext *ctx = cls;
566   struct REGEX_INTERNAL_Search *info = ctx->info;
567   const char *current;
568   size_t current_len;
569
570   GNUNET_STATISTICS_update (info->stats, "# regex edges iterated",
571                             1, GNUNET_NO);
572   current = &info->description[ctx->position];
573   current_len = strlen (info->description) - ctx->position;
574   if (len > current_len)
575   {
576     LOG (GNUNET_ERROR_TYPE_DEBUG, "Token too long, END\n");
577     return GNUNET_YES;
578   }
579   if (0 != strncmp (current, token, len))
580   {
581     LOG (GNUNET_ERROR_TYPE_DEBUG, "Token doesn't match, END\n");
582     return GNUNET_YES;
583   }
584
585   if (len > ctx->longest_match)
586   {
587     LOG (GNUNET_ERROR_TYPE_DEBUG, "Token is longer, KEEP\n");
588     ctx->longest_match = len;
589     ctx->hash = *key;
590   }
591   else
592   {
593     LOG (GNUNET_ERROR_TYPE_DEBUG, "Token is not longer, IGNORE\n");
594   }
595
596   LOG (GNUNET_ERROR_TYPE_DEBUG, "*    End of regex edge iterator\n");
597   return GNUNET_YES;
598 }
599
600
601 /**
602  * Jump to the next edge, with the longest matching token.
603  *
604  * @param block Block found in the DHT.
605  * @param size Size of the block.
606  * @param ctx Context of the search.
607  */
608 static void
609 regex_next_edge (const struct RegexBlock *block,
610                  size_t size,
611                  struct RegexSearchContext *ctx)
612 {
613   struct RegexSearchContext *new_ctx;
614   struct REGEX_INTERNAL_Search *info = ctx->info;
615   struct GNUNET_DHT_GetHandle *get_h;
616   struct GNUNET_HashCode *hash;
617   const char *rest;
618   int result;
619
620   LOG (GNUNET_ERROR_TYPE_DEBUG, "Next edge\n");
621   /* Find the longest match for the current string position,
622    * among tokens in the given block */
623   ctx->longest_match = 0;
624   result = REGEX_BLOCK_iterate (block, size,
625                                 &regex_edge_iterator, ctx);
626   GNUNET_break (GNUNET_OK == result);
627
628   /* Did anything match? */
629   if (0 == ctx->longest_match)
630   {
631     LOG (GNUNET_ERROR_TYPE_DEBUG,
632          "no match in block\n");
633     return;
634   }
635
636   hash = &ctx->hash;
637   new_ctx = GNUNET_new (struct RegexSearchContext);
638   new_ctx->info = info;
639   new_ctx->position = ctx->position + ctx->longest_match;
640   GNUNET_array_append (info->contexts, info->n_contexts, new_ctx);
641
642   /* Check whether we already have a DHT GET running for it */
643   if (GNUNET_YES ==
644       GNUNET_CONTAINER_multihashmap_contains (info->dht_get_handles, hash))
645   {
646     LOG (GNUNET_ERROR_TYPE_DEBUG,
647          "GET for %s running, END\n",
648          GNUNET_h2s (hash));
649     GNUNET_CONTAINER_multihashmap_get_multiple (info->dht_get_results,
650                                                 hash,
651                                                 &regex_result_iterator,
652                                                 new_ctx);
653     return;   /* We are already looking for it */
654   }
655
656   GNUNET_STATISTICS_update (info->stats, "# regex nodes traversed",
657                             1, GNUNET_NO);
658
659   LOG (GNUNET_ERROR_TYPE_DEBUG,
660        "Following edges at %s for offset %u in `%s'\n",
661        GNUNET_h2s (hash),
662        (unsigned int) ctx->position,
663        info->description);
664   rest = &new_ctx->info->description[new_ctx->position];
665   get_h =
666     GNUNET_DHT_get_start (info->dht,       /* handle */
667                           GNUNET_BLOCK_TYPE_REGEX,   /* type */
668                           hash,       /* key to search */
669                           DHT_REPLICATION,   /* replication level */
670                           DHT_OPT,
671                           rest,   /* xquery */
672                           strlen (rest) + 1,       /* xquery bits */
673                           &dht_get_string_handler, new_ctx);
674   if (GNUNET_OK !=
675       GNUNET_CONTAINER_multihashmap_put (info->dht_get_handles,
676                                          hash,
677                                          get_h,
678                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
679   {
680     GNUNET_break (0);
681     return;
682   }
683 }
684
685
686 /**
687  * Search for a peer offering a regex matching certain string in the DHT.
688  * The search runs until #REGEX_INTERNAL_search_cancel() is called, even if results
689  * are returned.
690  *
691  * @param dht An existing and valid DHT service handle.
692  * @param string String to match against the regexes in the DHT.
693  * @param callback Callback for found peers.
694  * @param callback_cls Closure for @c callback.
695  * @param stats Optional statistics handle to report usage. Can be NULL.
696  * @return Handle to stop search and free resources.
697  *         Must be freed by calling #REGEX_INTERNAL_search_cancel().
698  */
699 struct REGEX_INTERNAL_Search *
700 REGEX_INTERNAL_search (struct GNUNET_DHT_Handle *dht,
701                        const char *string,
702                        REGEX_INTERNAL_Found callback,
703                        void *callback_cls,
704                        struct GNUNET_STATISTICS_Handle *stats)
705 {
706   struct REGEX_INTERNAL_Search *h;
707   struct GNUNET_DHT_GetHandle *get_h;
708   struct RegexSearchContext *ctx;
709   struct GNUNET_HashCode key;
710   size_t size;
711   size_t len;
712
713   /* Initialize handle */
714   GNUNET_assert (NULL != dht);
715   GNUNET_assert (NULL != callback);
716   h = GNUNET_new (struct REGEX_INTERNAL_Search);
717   h->dht = dht;
718   h->description = GNUNET_strdup (string);
719   h->callback = callback;
720   h->callback_cls = callback_cls;
721   h->stats = stats;
722   h->dht_get_handles = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO);
723   h->dht_get_results = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO);
724
725   /* Initialize context */
726   len = strlen (string);
727   size = REGEX_INTERNAL_get_first_key (string, len, &key);
728   LOG (GNUNET_ERROR_TYPE_INFO,
729        "Initial key for `%s' is %s (based on `%.*s')\n",
730        string,
731        GNUNET_h2s (&key),
732        size,
733        string);
734   ctx = GNUNET_new (struct RegexSearchContext);
735   ctx->position = size;
736   ctx->info = h;
737   GNUNET_array_append (h->contexts,
738                        h->n_contexts,
739                        ctx);
740   /* Start search in DHT */
741   get_h = GNUNET_DHT_get_start (h->dht,     /* handle */
742                                 GNUNET_BLOCK_TYPE_REGEX, /* type */
743                                 &key,     /* key to search */
744                                 DHT_REPLICATION, /* replication level */
745                                 DHT_OPT,
746                                 &h->description[size],           /* xquery */
747                                 // FIXME add BLOOMFILTER to exclude filtered peers
748                                 len + 1 - size,                /* xquery bits */
749                                 // FIXME add BLOOMFILTER SIZE
750                                 &dht_get_string_handler, ctx);
751   GNUNET_break (
752     GNUNET_OK ==
753     GNUNET_CONTAINER_multihashmap_put (h->dht_get_handles,
754                                        &key,
755                                        get_h,
756                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)
757     );
758
759   return h;
760 }
761
762
763 /**
764  * Iterator over hash map entries to cancel DHT GET requests after a
765  * successful connect_by_string.
766  *
767  * @param cls Closure (unused).
768  * @param key Current key code (unused).
769  * @param value Value in the hash map (get handle).
770  * @return #GNUNET_YES if we should continue to iterate,
771  *         #GNUNET_NO if not.
772  */
773 static int
774 regex_cancel_dht_get (void *cls,
775                       const struct GNUNET_HashCode *key,
776                       void *value)
777 {
778   struct GNUNET_DHT_GetHandle *h = value;
779
780   GNUNET_DHT_get_stop (h);
781   return GNUNET_YES;
782 }
783
784
785 /**
786  * Iterator over hash map entries to free CadetRegexBlocks stored during the
787  * search for connect_by_string.
788  *
789  * @param cls Closure (unused).
790  * @param key Current key code (unused).
791  * @param value CadetRegexBlock in the hash map.
792  * @return #GNUNET_YES if we should continue to iterate,
793  *         #GNUNET_NO if not.
794  */
795 static int
796 regex_free_result (void *cls,
797                    const struct GNUNET_HashCode *key,
798                    void *value)
799 {
800   GNUNET_free (value);
801   return GNUNET_YES;
802 }
803
804
805 /**
806  * Cancel an ongoing regex search in the DHT and free all resources.
807  *
808  * @param h the search context.
809  */
810 void
811 REGEX_INTERNAL_search_cancel (struct REGEX_INTERNAL_Search *h)
812 {
813   unsigned int i;
814
815   GNUNET_free (h->description);
816   GNUNET_CONTAINER_multihashmap_iterate (h->dht_get_handles,
817                                          &regex_cancel_dht_get, NULL);
818   GNUNET_CONTAINER_multihashmap_iterate (h->dht_get_results,
819                                          &regex_free_result, NULL);
820   GNUNET_CONTAINER_multihashmap_destroy (h->dht_get_results);
821   GNUNET_CONTAINER_multihashmap_destroy (h->dht_get_handles);
822   if (0 < h->n_contexts)
823   {
824     for (i = 0; i < h->n_contexts; i++)
825       GNUNET_free (h->contexts[i]);
826     GNUNET_free (h->contexts);
827   }
828   GNUNET_free (h);
829 }
830
831
832 /* end of regex_internal_dht.c */