#include "regex_block_lib.h"
#include "gnunet_dht_service.h"
#include "gnunet_statistics_service.h"
+#include "gnunet_constants.h"
+#include "gnunet_signatures.h"
+
#define LOG(kind,...) GNUNET_log_from (kind,"regex-dht",__VA_ARGS__)
-/* FIXME: OPTION (API, CONFIG) */
#define DHT_REPLICATION 5
#define DHT_TTL GNUNET_TIME_UNIT_HOURS
-#define DEBUG_DHT GNUNET_NO
-
-#if DEBUG_DHT
-#define DHT_OPT GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE | GNUNET_DHT_RO_RECORD_ROUTE
-#else
#define DHT_OPT GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE
-#endif
+
+/**
+ * Handle to store cached data about a regex announce.
+ */
struct REGEX_INTERNAL_Announcement
{
/**
struct REGEX_INTERNAL_Automaton* dfa;
/**
- * Identity under which to announce the regex.
+ * Our private key.
*/
- struct GNUNET_PeerIdentity id;
+ const struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
/**
* Optional statistics handle to report usage. Can be NULL.
struct REGEX_INTERNAL_Announcement *h = cls;
struct RegexBlock *block;
size_t size;
+ unsigned int i;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
+ LOG (GNUNET_ERROR_TYPE_INFO,
"DHT PUT for state %s with proof `%s' and %u edges\n",
GNUNET_h2s (key),
proof,
num_edges);
+ for (i = 0; i < num_edges; i++)
+ {
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ " edge %s towards %s (%s)\n",
+ edges[i].label,
+ GNUNET_h2s (&edges[i].destination),
+ proof);
+ }
if (GNUNET_YES == accepting)
{
- struct RegexAccept block;
+ struct RegexAcceptBlock ab;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
+ LOG (GNUNET_ERROR_TYPE_INFO,
"State %s is accepting, putting own id\n",
- GNUNET_h2s(key));
- size = sizeof (block);
- block.key = *key;
- block.id = h->id;
+ GNUNET_h2s (key));
+ size = sizeof (struct RegexAcceptBlock);
+ ab.purpose.size = ntohl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
+ sizeof (struct GNUNET_TIME_AbsoluteNBO) +
+ sizeof (struct GNUNET_HashCode));
+ ab.purpose.purpose = ntohl (GNUNET_SIGNATURE_PURPOSE_REGEX_ACCEPT);
+ ab.expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_DHT_MAX_EXPIRATION));
+ ab.key = *key;
+ GNUNET_CRYPTO_eddsa_key_get_public (h->priv,
+ &ab.peer.public_key);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_sign (h->priv,
+ &ab.purpose,
+ &ab.signature));
+
GNUNET_STATISTICS_update (h->stats, "# regex accepting blocks stored",
1, GNUNET_NO);
GNUNET_STATISTICS_update (h->stats, "# regex accepting block bytes stored",
- sizeof (block), GNUNET_NO);
+ sizeof (struct RegexAcceptBlock), GNUNET_NO);
(void)
GNUNET_DHT_put (h->dht, key,
DHT_REPLICATION,
DHT_OPT | GNUNET_DHT_RO_RECORD_ROUTE,
GNUNET_BLOCK_TYPE_REGEX_ACCEPT,
size,
- &block,
+ &ab,
GNUNET_TIME_relative_to_absolute (DHT_TTL),
DHT_TTL,
NULL, NULL);
}
block = REGEX_BLOCK_create (proof,
- num_edges, edges,
- accepting,
- &size);
+ num_edges, edges,
+ accepting,
+ &size);
(void)
GNUNET_DHT_put (h->dht, key,
DHT_REPLICATION,
DHT_OPT,
- GNUNET_BLOCK_TYPE_REGEX,
- size, block,
+ GNUNET_BLOCK_TYPE_REGEX,
+ size, block,
GNUNET_TIME_relative_to_absolute (DHT_TTL),
DHT_TTL,
NULL, NULL);
}
+/**
+ * Announce a regular expression: put all states of the automaton in the DHT.
+ * Does not free resources, must call REGEX_INTERNAL_announce_cancel for that.
+ *
+ * @param dht An existing and valid DHT service handle. CANNOT be NULL.
+ * @param priv our private key, must remain valid until the announcement is cancelled
+ * @param regex Regular expression to announce.
+ * @param compression How many characters per edge can we squeeze?
+ * @param stats Optional statistics handle to report usage. Can be NULL.
+ *
+ * @return Handle to reuse o free cached resources.
+ * Must be freed by calling REGEX_INTERNAL_announce_cancel.
+ */
struct REGEX_INTERNAL_Announcement *
REGEX_INTERNAL_announce (struct GNUNET_DHT_Handle *dht,
- const struct GNUNET_PeerIdentity *id,
- const char *regex,
- uint16_t compression,
- struct GNUNET_STATISTICS_Handle *stats)
+ const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
+ const char *regex,
+ uint16_t compression,
+ struct GNUNET_STATISTICS_Handle *stats)
{
struct REGEX_INTERNAL_Announcement *h;
h->regex = regex;
h->dht = dht;
h->stats = stats;
- h->id = *id;
- h->dfa = REGEX_INTERNAL_construct_dfa (regex,
- strlen (regex),
- compression);
+ h->priv = priv;
+ h->dfa = REGEX_INTERNAL_construct_dfa (regex, strlen (regex), compression);
REGEX_INTERNAL_reannounce (h);
return h;
}
+/**
+ * Announce again a regular expression previously announced.
+ * Does use caching to speed up process.
+ *
+ * @param h Handle returned by a previous REGEX_INTERNAL_announce call.
+ */
void
REGEX_INTERNAL_reannounce (struct REGEX_INTERNAL_Announcement *h)
{
GNUNET_assert (NULL != h->dfa); /* make sure to call announce first */
- LOG (GNUNET_ERROR_TYPE_INFO, "REGEX_INTERNAL_reannounce: %.60s\n", h->regex);
- LOG (GNUNET_ERROR_TYPE_DEBUG, " full: %s\n", h->regex);
- REGEX_INTERNAL_iterate_all_edges (h->dfa, ®ex_iterator, h);
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ "REGEX_INTERNAL_reannounce: %s\n",
+ h->regex);
+ REGEX_INTERNAL_iterate_reachable_edges (h->dfa, ®ex_iterator, h);
}
+/**
+ * Clear all cached data used by a regex announce.
+ * Does not close DHT connection.
+ *
+ * @param h Handle returned by a previous REGEX_INTERNAL_announce call.
+ */
void
REGEX_INTERNAL_announce_cancel (struct REGEX_INTERNAL_Announcement *h)
{
*/
struct RegexSearchContext
{
- /**
- * Part of the description already consumed by
- * this particular search branch.
- */
+ /**
+ * Part of the description already consumed by
+ * this particular search branch.
+ */
size_t position;
- /**
- * Information about the search.
- */
+ /**
+ * Information about the search.
+ */
struct REGEX_INTERNAL_Search *info;
- /**
- * We just want to look for one edge, the longer the better.
- * Keep its length.
- */
+ /**
+ * We just want to look for one edge, the longer the better.
+ * Keep its length.
+ */
unsigned int longest_match;
- /**
- * Destination hash of the longest match.
- */
+ /**
+ * Destination hash of the longest match.
+ */
struct GNUNET_HashCode hash;
};
+/**
+ * Type of values in 'dht_get_results'.
+ */
+struct Result
+{
+ /**
+ * Number of bytes in data.
+ */
+ size_t size;
+
+ /**
+ * The raw result data.
+ */
+ const void *data;
+};
+
+
/**
* Struct to keep information of searches of services described by a regex
* using a user-provided string service description.
*/
struct REGEX_INTERNAL_Search
{
- /**
- * DHT handle to use, must be initialized externally.
- */
+ /**
+ * DHT handle to use, must be initialized externally.
+ */
struct GNUNET_DHT_Handle *dht;
- /**
- * Optional statistics handle to report usage. Can be NULL.
- */
+ /**
+ * Optional statistics handle to report usage. Can be NULL.
+ */
struct GNUNET_STATISTICS_Handle *stats;
- /**
- * User provided description of the searched service.
- */
+ /**
+ * User provided description of the searched service.
+ */
char *description;
- /**
- * Running DHT GETs.
- */
+ /**
+ * Running DHT GETs.
+ */
struct GNUNET_CONTAINER_MultiHashMap *dht_get_handles;
- /**
- * Results from running DHT GETs.
- */
+ /**
+ * Results from running DHT GETs, values are of type
+ * 'struct Result'.
+ */
struct GNUNET_CONTAINER_MultiHashMap *dht_get_results;
- /**
- * Contexts, for each running DHT GET. Free all on end of search.
- */
+ /**
+ * Contexts, for each running DHT GET. Free all on end of search.
+ */
struct RegexSearchContext **contexts;
- /**
- * Number of contexts (branches/steps in search).
- */
+ /**
+ * Number of contexts (branches/steps in search).
+ */
unsigned int n_contexts;
-
+
/**
* @param callback Callback for found peers.
*/
};
-
/**
* Jump to the next edge, with the longest matching token.
*
* @param block Block found in the DHT.
* @param size Size of the block.
* @param ctx Context of the search.
- *
- * @return GNUNET_YES if should keep iterating, GNUNET_NO otherwise.
*/
static void
regex_next_edge (const struct RegexBlock *block,
*/
static void
dht_get_string_accept_handler (void *cls, struct GNUNET_TIME_Absolute exp,
- const struct GNUNET_HashCode * key,
+ const struct GNUNET_HashCode *key,
const struct GNUNET_PeerIdentity *get_path,
unsigned int get_path_length,
const struct GNUNET_PeerIdentity *put_path,
enum GNUNET_BLOCK_Type type,
size_t size, const void *data)
{
- const struct RegexAccept *block = data;
+ const struct RegexAcceptBlock *block = data;
struct RegexSearchContext *ctx = cls;
struct REGEX_INTERNAL_Search *info = ctx->info;
- LOG (GNUNET_ERROR_TYPE_DEBUG, "Got regex results from DHT!\n");
- LOG (GNUNET_ERROR_TYPE_INFO, " accept for %s (key %s)\n",
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Regex result accept for %s (key %s)\n",
info->description, GNUNET_h2s(key));
- GNUNET_STATISTICS_update (info->stats, "# regex accepting blocks found",
+ GNUNET_STATISTICS_update (info->stats,
+ "# regex accepting blocks found",
1, GNUNET_NO);
- GNUNET_STATISTICS_update (info->stats, "# regex accepting block bytes found",
+ GNUNET_STATISTICS_update (info->stats,
+ "# regex accepting block bytes found",
size, GNUNET_NO);
-
info->callback (info->callback_cls,
- &block->id,
+ &block->peer,
get_path, get_path_length,
put_path, put_path_length);
}
/**
* Find a path to a peer that offers a regex servcie compatible
* with a given string.
- *
+ *
* @param key The key of the accepting state.
* @param ctx Context containing info about the string, tunnel, etc.
*/
{
struct GNUNET_DHT_GetHandle *get_h;
- LOG (GNUNET_ERROR_TYPE_DEBUG, "Found peer by service\n");
- LOG (GNUNET_ERROR_TYPE_INFO, " find accept for %s\n", GNUNET_h2s (key));
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "regex finds path for %s\n",
+ GNUNET_h2s (key));
get_h = GNUNET_DHT_get_start (ctx->info->dht, /* handle */
GNUNET_BLOCK_TYPE_REGEX_ACCEPT, /* type */
key, /* key to search */
const struct RegexBlock *block = data;
struct RegexSearchContext *ctx = cls;
struct REGEX_INTERNAL_Search *info = ctx->info;
- void *copy;
size_t len;
- char *datastore;
-
-#if DEBUG_DHT
- if ( (NULL != put_path) &&
- (0 != put_path_length) )
- {
- datastore = GNUNET_strdup (GNUNET_i2s (&put_path[put_path_length - 1]));
- }
- else
- {
- GNUNET_asprintf (&datastore, "?? %u/%u", put_path_length, get_path_length);
- }
-#else
- datastore = GNUNET_strdup ("N/A");
-#endif
-
- LOG (GNUNET_ERROR_TYPE_INFO, " DHT GET result for %s (%s) at %s\n",
- GNUNET_h2s (key), ctx->info->description, datastore);
- GNUNET_free (datastore);
-
- copy = GNUNET_malloc (size);
- memcpy (copy, data, size);
- GNUNET_break (
- GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_put (info->dht_get_results,
- key, copy,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)
- );
+ struct Result *copy;
+
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ "DHT GET result for %s (%s)\n",
+ GNUNET_h2s (key), ctx->info->description);
+ copy = GNUNET_malloc (sizeof (struct Result) + size);
+ copy->size = size;
+ copy->data = ©[1];
+ memcpy (©[1], block, size);
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (info->dht_get_results,
+ key, copy,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
len = strlen (info->description);
if (len == ctx->position) // String processed
{
- if (GNUNET_YES == ntohs (block->is_accepting))
+ if (GNUNET_YES == GNUNET_BLOCK_is_accepting (block, size))
{
regex_find_path (key, ctx);
}
const struct GNUNET_HashCode * key,
void *value)
{
- struct RegexBlock *block = value;
+ struct Result *result = value;
+ const struct RegexBlock *block = result->data;
struct RegexSearchContext *ctx = cls;
- if (GNUNET_YES == ntohs (block->is_accepting) &&
- ctx->position == strlen (ctx->info->description))
+ if ( (GNUNET_YES ==
+ GNUNET_BLOCK_is_accepting (block, result->size)) &&
+ (ctx->position == strlen (ctx->info->description)) )
{
- LOG (GNUNET_ERROR_TYPE_INFO, " * Found accepting known block\n");
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ "Found accepting known block\n");
regex_find_path (key, ctx);
return GNUNET_YES; // We found an accept state!
}
- LOG (GNUNET_ERROR_TYPE_DEBUG, "* %u, %u, [%u]\n",
- ctx->position, strlen(ctx->info->description),
- ntohs (block->is_accepting));
-
- regex_next_edge (block, SIZE_MAX, ctx);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "* %u, %u, [%u]\n",
+ ctx->position,
+ strlen (ctx->info->description),
+ GNUNET_BLOCK_is_accepting (block, result->size));
+ regex_next_edge (block, result->size, ctx);
GNUNET_STATISTICS_update (ctx->info->stats, "# regex mesh blocks iterated",
1, GNUNET_NO);
GNUNET_STATISTICS_update (info->stats, "# regex edges iterated",
1, GNUNET_NO);
-
- LOG (GNUNET_ERROR_TYPE_DEBUG, "* Start of regex edge iterator\n");
- LOG (GNUNET_ERROR_TYPE_DEBUG, "* descr : %s\n", info->description);
- LOG (GNUNET_ERROR_TYPE_DEBUG, "* posit : %u\n", ctx->position);
current = &info->description[ctx->position];
- LOG (GNUNET_ERROR_TYPE_DEBUG, "* currt : %s\n", current);
current_len = strlen (info->description) - ctx->position;
- LOG (GNUNET_ERROR_TYPE_DEBUG, "* ctlen : %u\n", current_len);
- LOG (GNUNET_ERROR_TYPE_DEBUG, "* tklen : %u\n", len);
- LOG (GNUNET_ERROR_TYPE_DEBUG, "* token : %.*s\n", len, token);
- LOG (GNUNET_ERROR_TYPE_DEBUG, "* nextk : %s\n", GNUNET_h2s(key));
if (len > current_len)
{
- LOG (GNUNET_ERROR_TYPE_DEBUG, "* Token too long, END\n");
- return GNUNET_YES; // Token too long, wont match
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Token too long, END\n");
+ return GNUNET_YES;
}
if (0 != strncmp (current, token, len))
{
- LOG (GNUNET_ERROR_TYPE_DEBUG, "* Token doesn't match, END\n");
- return GNUNET_YES; // Token doesn't match
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Token doesn't match, END\n");
+ return GNUNET_YES;
}
if (len > ctx->longest_match)
{
- LOG (GNUNET_ERROR_TYPE_DEBUG, "* Token is longer, KEEP\n");
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Token is longer, KEEP\n");
ctx->longest_match = len;
ctx->hash = *key;
}
else
{
- LOG (GNUNET_ERROR_TYPE_DEBUG, "* Token is not longer, IGNORE\n");
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Token is not longer, IGNORE\n");
}
LOG (GNUNET_ERROR_TYPE_DEBUG, "* End of regex edge iterator\n");
* @param block Block found in the DHT.
* @param size Size of the block.
* @param ctx Context of the search.
- *
- * @return GNUNET_YES if should keep iterating, GNUNET_NO otherwise.
*/
static void
regex_next_edge (const struct RegexBlock *block,
const char *rest;
int result;
- /* Find the longest match for the current string position,
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Next edge\n");
+ /* Find the longest match for the current string position,
* among tokens in the given block */
ctx->longest_match = 0;
result = REGEX_BLOCK_iterate (block, size,
- ®ex_edge_iterator, ctx);
+ ®ex_edge_iterator, ctx);
GNUNET_break (GNUNET_OK == result);
/* Did anything match? */
if (0 == ctx->longest_match)
{
- LOG (GNUNET_ERROR_TYPE_DEBUG, " no match in block\n");
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "no match in block\n");
return;
}
if (GNUNET_YES ==
GNUNET_CONTAINER_multihashmap_contains (info->dht_get_handles, hash))
{
- LOG (GNUNET_ERROR_TYPE_DEBUG, "* GET for %s running, END\n",
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "GET for %s running, END\n",
GNUNET_h2s (hash));
GNUNET_CONTAINER_multihashmap_get_multiple (info->dht_get_results,
hash,
GNUNET_STATISTICS_update (info->stats, "# regex nodes traversed",
1, GNUNET_NO);
- /* Start search in DHT */
- LOG (GNUNET_ERROR_TYPE_INFO, " looking for %s\n", GNUNET_h2s (hash));
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ "looking for %s\n",
+ GNUNET_h2s (hash));
rest = &new_ctx->info->description[new_ctx->position];
- get_h =
+ get_h =
GNUNET_DHT_get_start (info->dht, /* handle */
GNUNET_BLOCK_TYPE_REGEX, /* type */
hash, /* key to search */
DHT_REPLICATION, /* replication level */
DHT_OPT,
rest, /* xquery */
- // FIXME add BLOOMFILTER to exclude filtered peers
- strlen(rest) + 1, /* xquery bits */
- // FIXME add BLOOMFILTER SIZE
+ strlen (rest) + 1, /* xquery bits */
&dht_get_string_handler, new_ctx);
if (GNUNET_OK !=
GNUNET_CONTAINER_multihashmap_put(info->dht_get_handles,
}
+/**
+ * Search for a peer offering a regex matching certain string in the DHT.
+ * The search runs until REGEX_INTERNAL_search_cancel is called, even if results
+ * are returned.
+ *
+ * @param dht An existing and valid DHT service handle.
+ * @param string String to match against the regexes in the DHT.
+ * @param callback Callback for found peers.
+ * @param callback_cls Closure for @c callback.
+ * @param stats Optional statistics handle to report usage. Can be NULL.
+ *
+ * @return Handle to stop search and free resources.
+ * Must be freed by calling REGEX_INTERNAL_search_cancel.
+ */
struct REGEX_INTERNAL_Search *
REGEX_INTERNAL_search (struct GNUNET_DHT_Handle *dht,
const char *string,
/* Initialize context */
len = strlen (string);
size = REGEX_INTERNAL_get_first_key (string, len, &key);
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ " initial key for %s: %s (%.*s)\n",
+ string, GNUNET_h2s (&key), size, string);
ctx = GNUNET_malloc (sizeof (struct RegexSearchContext));
ctx->position = size;
ctx->info = h;
GNUNET_array_append (h->contexts, h->n_contexts, ctx);
- LOG (GNUNET_ERROR_TYPE_DEBUG, " consumed %u bits out of %u\n", size, len);
- LOG (GNUNET_ERROR_TYPE_INFO, " looking for %s\n", GNUNET_h2s (&key));
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "consumed %u bits out of %u, now looking for %s\n",
+ size, len,
+ GNUNET_h2s (&key));
/* Start search in DHT */
get_h = GNUNET_DHT_get_start (h->dht, /* handle */