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