-follow naming and coding style conventions
[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 (key, 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                                        &((struct RegexBlock *)copy)->key, copy,
424                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)
425                );
426   len = ntohl (block->n_proof);
427   {
428     char proof[len + 1];
429
430     memcpy (proof, &block[1], len);
431     proof[len] = '\0';
432     if (GNUNET_OK != REGEX_INTERNAL_check_proof (proof, key))
433     {
434       GNUNET_break_op (0);
435       return;
436     }
437   }
438   len = strlen (info->description);
439   if (len == ctx->position) // String processed
440   {
441     if (GNUNET_YES == ntohl (block->accepting))
442     {
443       regex_find_path (key, ctx);
444     }
445     else
446     {
447       LOG (GNUNET_ERROR_TYPE_INFO, "  block not accepting!\n");
448       // FIXME REGEX this block not successful, wait for more? start timeout?
449     }
450     return;
451   }
452   regex_next_edge (block, size, ctx);
453 }
454
455
456 /**
457  * Iterator over found existing mesh regex blocks that match an ongoing search.
458  *
459  * @param cls Closure (current context)-
460  * @param key Current key code (key for cached block).
461  * @param value Value in the hash map (cached RegexBlock).
462  * @return GNUNET_YES: we should always continue to iterate.
463  */
464 static int
465 regex_result_iterator (void *cls,
466                        const struct GNUNET_HashCode * key,
467                        void *value)
468 {
469   struct RegexBlock *block = value;
470   struct RegexSearchContext *ctx = cls;
471
472   if (GNUNET_YES == ntohl(block->accepting) &&
473       ctx->position == strlen (ctx->info->description))
474   {
475     LOG (GNUNET_ERROR_TYPE_INFO, " * Found accepting known block\n");
476     regex_find_path (key, ctx);
477     return GNUNET_YES; // We found an accept state!
478   }
479   LOG (GNUNET_ERROR_TYPE_DEBUG, "* %u, %u, [%u]\n",
480        ctx->position, strlen(ctx->info->description),
481        ntohl(block->accepting));
482
483   regex_next_edge (block, SIZE_MAX, ctx);
484
485   GNUNET_STATISTICS_update (ctx->info->stats, "# regex mesh blocks iterated",
486                             1, GNUNET_NO);
487
488   return GNUNET_YES;
489 }
490
491
492 /**
493  * Iterator over edges in a regex block retrieved from the DHT.
494  *
495  * @param cls Closure (context of the search).
496  * @param token Token that follows to next state.
497  * @param len Lenght of token.
498  * @param key Hash of next state.
499  *
500  * @return GNUNET_YES if should keep iterating, GNUNET_NO otherwise.
501  */
502 static int
503 regex_edge_iterator (void *cls,
504                      const char *token,
505                      size_t len,
506                      const struct GNUNET_HashCode *key)
507 {
508   struct RegexSearchContext *ctx = cls;
509   struct REGEX_INTERNAL_Search *info = ctx->info;
510   const char *current;
511   size_t current_len;
512
513   GNUNET_STATISTICS_update (info->stats, "# regex edges iterated",
514                             1, GNUNET_NO);
515
516   LOG (GNUNET_ERROR_TYPE_DEBUG, "*    Start of regex edge iterator\n");
517   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     descr : %s\n", info->description);
518   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     posit : %u\n", ctx->position);
519   current = &info->description[ctx->position];
520   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     currt : %s\n", current);
521   current_len = strlen (info->description) - ctx->position;
522   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     ctlen : %u\n", current_len);
523   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     tklen : %u\n", len);
524   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     token : %.*s\n", len, token);
525   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     nextk : %s\n", GNUNET_h2s(key));
526   if (len > current_len)
527   {
528     LOG (GNUNET_ERROR_TYPE_DEBUG, "*     Token too long, END\n");
529     return GNUNET_YES; // Token too long, wont match
530   }
531   if (0 != strncmp (current, token, len))
532   {
533     LOG (GNUNET_ERROR_TYPE_DEBUG, "*     Token doesn't match, END\n");
534     return GNUNET_YES; // Token doesn't match
535   }
536
537   if (len > ctx->longest_match)
538   {
539     LOG (GNUNET_ERROR_TYPE_DEBUG, "*     Token is longer, KEEP\n");
540     ctx->longest_match = len;
541     ctx->hash = *key;
542   }
543   else
544   {
545     LOG (GNUNET_ERROR_TYPE_DEBUG, "*     Token is not longer, IGNORE\n");
546   }
547
548   LOG (GNUNET_ERROR_TYPE_DEBUG, "*    End of regex edge iterator\n");
549   return GNUNET_YES;
550 }
551
552
553 /**
554  * Jump to the next edge, with the longest matching token.
555  *
556  * @param block Block found in the DHT.
557  * @param size Size of the block.
558  * @param ctx Context of the search.
559  *
560  * @return GNUNET_YES if should keep iterating, GNUNET_NO otherwise.
561  */
562 static void
563 regex_next_edge (const struct RegexBlock *block,
564                  size_t size,
565                  struct RegexSearchContext *ctx)
566 {
567   struct RegexSearchContext *new_ctx;
568   struct REGEX_INTERNAL_Search *info = ctx->info;
569   struct GNUNET_DHT_GetHandle *get_h;
570   struct GNUNET_HashCode *hash;
571   const char *rest;
572   int result;
573
574   /* Find the longest match for the current string position, 
575    * among tokens in the given block */
576   ctx->longest_match = 0;
577   result = REGEX_BLOCK_iterate (block, size,
578                                        &regex_edge_iterator, ctx);
579   GNUNET_break (GNUNET_OK == result);
580
581   /* Did anything match? */
582   if (0 == ctx->longest_match)
583   {
584     LOG (GNUNET_ERROR_TYPE_DEBUG, "  no match in block\n");
585     return;
586   }
587
588   hash = &ctx->hash;
589   new_ctx = GNUNET_malloc (sizeof (struct RegexSearchContext));
590   new_ctx->info = info;
591   new_ctx->position = ctx->position + ctx->longest_match;
592   GNUNET_array_append (info->contexts, info->n_contexts, new_ctx);
593
594   /* Check whether we already have a DHT GET running for it */
595   if (GNUNET_YES ==
596       GNUNET_CONTAINER_multihashmap_contains (info->dht_get_handles, hash))
597   {
598     LOG (GNUNET_ERROR_TYPE_DEBUG, "*     GET for %s running, END\n",
599          GNUNET_h2s (hash));
600     GNUNET_CONTAINER_multihashmap_get_multiple (info->dht_get_results,
601                                                 hash,
602                                                 &regex_result_iterator,
603                                                 new_ctx);
604     return; /* We are already looking for it */
605   }
606
607   GNUNET_STATISTICS_update (info->stats, "# regex nodes traversed",
608                             1, GNUNET_NO);
609
610   /* Start search in DHT */
611   LOG (GNUNET_ERROR_TYPE_INFO, "   looking for %s\n", GNUNET_h2s (hash));
612   rest = &new_ctx->info->description[new_ctx->position];
613   get_h = 
614       GNUNET_DHT_get_start (info->dht,    /* handle */
615                             GNUNET_BLOCK_TYPE_REGEX, /* type */
616                             hash,     /* key to search */
617                             DHT_REPLICATION, /* replication level */
618                             DHT_OPT,
619                             rest, /* xquery */
620                             // FIXME add BLOOMFILTER to exclude filtered peers
621                             strlen(rest) + 1,     /* xquery bits */
622                             // FIXME add BLOOMFILTER SIZE
623                             &dht_get_string_handler, new_ctx);
624   if (GNUNET_OK !=
625       GNUNET_CONTAINER_multihashmap_put(info->dht_get_handles,
626                                         hash,
627                                         get_h,
628                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
629   {
630     GNUNET_break (0);
631     return;
632   }
633 }
634
635
636 struct REGEX_INTERNAL_Search *
637 REGEX_INTERNAL_search (struct GNUNET_DHT_Handle *dht,
638                      const char *string,
639                      REGEX_INTERNAL_Found callback,
640                      void *callback_cls,
641                      struct GNUNET_STATISTICS_Handle *stats)
642 {
643   struct REGEX_INTERNAL_Search *h;
644   struct GNUNET_DHT_GetHandle *get_h;
645   struct RegexSearchContext *ctx;
646   struct GNUNET_HashCode key;
647   size_t size;
648   size_t len;
649
650   /* Initialize handle */
651   LOG (GNUNET_ERROR_TYPE_INFO, "REGEX_INTERNAL_search: %s\n", string);
652   GNUNET_assert (NULL != dht);
653   GNUNET_assert (NULL != callback);
654   h = GNUNET_malloc (sizeof (struct REGEX_INTERNAL_Search));
655   h->dht = dht;
656   h->description = GNUNET_strdup (string);
657   h->callback = callback;
658   h->callback_cls = callback_cls;
659   h->stats = stats;
660   h->dht_get_handles = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO);
661   h->dht_get_results = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_YES);
662
663   /* Initialize context */
664   len = strlen (string);
665   size = REGEX_INTERNAL_get_first_key (string, len, &key);
666   ctx = GNUNET_malloc (sizeof (struct RegexSearchContext));
667   ctx->position = size;
668   ctx->info = h;
669   GNUNET_array_append (h->contexts, h->n_contexts, ctx);
670   LOG (GNUNET_ERROR_TYPE_DEBUG, "  consumed %u bits out of %u\n", size, len);
671   LOG (GNUNET_ERROR_TYPE_INFO, "   looking for %s\n", GNUNET_h2s (&key));
672
673   /* Start search in DHT */
674   get_h = GNUNET_DHT_get_start (h->dht,    /* handle */
675                                 GNUNET_BLOCK_TYPE_REGEX, /* type */
676                                 &key,     /* key to search */
677                                 DHT_REPLICATION, /* replication level */
678                                 DHT_OPT,
679                                 &h->description[size],           /* xquery */
680                                 // FIXME add BLOOMFILTER to exclude filtered peers
681                                 len + 1 - size,                /* xquery bits */
682                                 // FIXME add BLOOMFILTER SIZE
683                                 &dht_get_string_handler, ctx);
684   GNUNET_break (
685     GNUNET_OK ==
686     GNUNET_CONTAINER_multihashmap_put (h->dht_get_handles,
687                                        &key,
688                                        get_h,
689                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)
690                );
691
692   return h;
693 }
694
695
696 /**
697  * Iterator over hash map entries to cancel DHT GET requests after a
698  * successful connect_by_string.
699  *
700  * @param cls Closure (unused).
701  * @param key Current key code (unused).
702  * @param value Value in the hash map (get handle).
703  * @return GNUNET_YES if we should continue to iterate,
704  *         GNUNET_NO if not.
705  */
706 static int
707 regex_cancel_dht_get (void *cls,
708                       const struct GNUNET_HashCode * key,
709                       void *value)
710 {
711   struct GNUNET_DHT_GetHandle *h = value;
712
713   GNUNET_DHT_get_stop (h);
714   return GNUNET_YES;
715 }
716
717
718 /**
719  * Iterator over hash map entries to free MeshRegexBlocks stored during the
720  * search for connect_by_string.
721  *
722  * @param cls Closure (unused).
723  * @param key Current key code (unused).
724  * @param value MeshRegexBlock in the hash map.
725  * @return GNUNET_YES if we should continue to iterate,
726  *         GNUNET_NO if not.
727  */
728 static int
729 regex_free_result (void *cls,
730                    const struct GNUNET_HashCode * key,
731                    void *value)
732 {
733   GNUNET_free (value);
734   return GNUNET_YES;
735 }
736
737
738 /**
739  * Cancel an ongoing regex search in the DHT and free all resources.
740  *
741  * @param h the search context.
742  */
743 void
744 REGEX_INTERNAL_search_cancel (struct REGEX_INTERNAL_Search *h)
745 {
746   unsigned int i;
747
748   GNUNET_free (h->description);
749   GNUNET_CONTAINER_multihashmap_iterate (h->dht_get_handles,
750                                          &regex_cancel_dht_get, NULL);
751   GNUNET_CONTAINER_multihashmap_iterate (h->dht_get_results,
752                                          &regex_free_result, NULL);
753   GNUNET_CONTAINER_multihashmap_destroy (h->dht_get_results);
754   GNUNET_CONTAINER_multihashmap_destroy (h->dht_get_handles);
755   if (0 < h->n_contexts)
756   {
757     for (i = 0; i < h->n_contexts; i++)
758       GNUNET_free (h->contexts[i]);
759     GNUNET_free (h->contexts);
760   }
761   GNUNET_free (h);
762 }
763
764
765 /* end of regex_internal_dht.c */