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