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