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