aboutsummaryrefslogtreecommitdiff
path: root/src/fs/fs_search.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs/fs_search.c')
-rw-r--r--src/fs/fs_search.c572
1 files changed, 572 insertions, 0 deletions
diff --git a/src/fs/fs_search.c b/src/fs/fs_search.c
new file mode 100644
index 0000000000..56240af695
--- /dev/null
+++ b/src/fs/fs_search.c
@@ -0,0 +1,572 @@
+/*
+ This file is part of GNUnet.
+ (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/fs/ecrs/search.c
+ * @brief Helper functions for searching.
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "gnunet_protocols.h"
+#include "gnunet_fs_lib.h"
+#include "gnunet_ecrs_lib.h"
+#include "ecrs_core.h"
+#include "ecrs.h"
+
+#define DEBUG_SEARCH GNUNET_NO
+
+/**
+ * Context for an individual search. Followed
+ * by keyCount keys of type GNUNET_HashCode.
+ */
+struct PendingSearch
+{
+ struct PendingSearch *next;
+
+ struct GNUNET_ECRS_SearchContext *context;
+
+ /**
+ * The key (for decryption)
+ */
+ GNUNET_HashCode decryptKey;
+
+ unsigned int keyCount;
+
+ /**
+ * What type of query is it?
+ */
+ unsigned int type;
+
+};
+
+/**
+ * Context for search operation.
+ */
+struct GNUNET_ECRS_SearchContext
+{
+ /**
+ * Time when the cron-job was first started.
+ */
+ GNUNET_CronTime start;
+
+ /**
+ * What is the global timeout?
+ */
+ GNUNET_CronTime timeout;
+
+ /**
+ * Search context
+ */
+ struct GNUNET_FS_SearchContext *sctx;
+
+ /**
+ * Active searches.
+ */
+ struct PendingSearch *queries;
+
+ GNUNET_ECRS_SearchResultProcessor spcb;
+
+ void *spcbClosure;
+
+ struct GNUNET_GE_Context *ectx;
+
+ struct GNUNET_GC_Configuration *cfg;
+
+ int aborted;
+
+ int my_sctx;
+
+ unsigned int anonymityLevel;
+
+};
+
+static int
+receive_response_callback (const GNUNET_HashCode * key,
+ const GNUNET_DatastoreValue * value,
+ void *cls, unsigned long long uid);
+
+/**
+ * Add a query to the SQC.
+ */
+static void
+add_search (unsigned int type,
+ unsigned int keyCount,
+ const GNUNET_HashCode * keys,
+ const GNUNET_HashCode * dkey,
+ struct GNUNET_ECRS_SearchContext *sqc)
+{
+ struct PendingSearch *ps;
+
+ ps =
+ GNUNET_malloc (sizeof (struct PendingSearch) +
+ sizeof (GNUNET_HashCode) * keyCount);
+ ps->type = type;
+ ps->keyCount = keyCount;
+ memcpy (&ps[1], keys, sizeof (GNUNET_HashCode) * keyCount);
+ ps->decryptKey = *dkey;
+ ps->context = sqc;
+ ps->next = sqc->queries;
+ sqc->queries = ps;
+ GNUNET_FS_start_search (sqc->sctx,
+ NULL,
+ type,
+ keyCount,
+ keys,
+ sqc->anonymityLevel,
+ &receive_response_callback, ps);
+}
+
+/**
+ * Add the query that corresponds to the given URI
+ * to the SQC.
+ */
+static void
+add_search_for_uri (const struct GNUNET_ECRS_URI *uri,
+ struct GNUNET_ECRS_SearchContext *sqc)
+{
+ struct GNUNET_GE_Context *ectx = sqc->ectx;
+
+ switch (uri->type)
+ {
+ case chk:
+ GNUNET_GE_LOG (ectx,
+ GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
+ _("CHK URI not allowed for search.\n"));
+ break;
+ case sks:
+ {
+ GNUNET_HashCode keys[2];
+ GNUNET_HashCode hk; /* hk = GNUNET_hash(identifier) */
+ GNUNET_HashCode hk2; /* hk2 = GNUNET_hash(hk) */
+
+ GNUNET_hash (uri->data.sks.identifier,
+ strlen (uri->data.sks.identifier), &hk);
+ GNUNET_hash (&hk, sizeof (GNUNET_HashCode), &hk2);
+ /* compute routing key keys[0] = H(key) ^ namespace */
+ GNUNET_hash_xor (&hk2, &uri->data.sks.namespace, &keys[0]);
+ keys[1] = uri->data.sks.namespace;
+ add_search (GNUNET_ECRS_BLOCKTYPE_SIGNED, 2, &keys[0], &hk, sqc);
+ break;
+ }
+ case ksk:
+ {
+ GNUNET_HashCode hc;
+ GNUNET_HashCode query;
+ struct GNUNET_RSA_PrivateKey *pk;
+ GNUNET_RSA_PublicKey pub;
+ int i;
+ const char *keyword;
+
+#if DEBUG_SEARCH
+ GNUNET_GE_LOG (ectx,
+ GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
+ "Computing queries (this may take a while).\n");
+#endif
+ for (i = 0; i < uri->data.ksk.keywordCount; i++)
+ {
+ keyword = uri->data.ksk.keywords[i];
+ /* first character of the keyword is
+ "+" or " " to indicate mandatory or
+ not -- ignore for hashing! */
+ GNUNET_hash (&keyword[1], strlen (&keyword[1]), &hc);
+ pk = GNUNET_RSA_create_key_from_hash (&hc);
+ GNUNET_RSA_get_public_key (pk, &pub);
+ GNUNET_hash (&pub, sizeof (GNUNET_RSA_PublicKey), &query);
+ add_search (GNUNET_ECRS_BLOCKTYPE_ANY, /* GNUNET_ECRS_BLOCKTYPE_KEYWORD, GNUNET_ECRS_BLOCKTYPE_NAMESPACE or GNUNET_ECRS_BLOCKTYPE_KEYWORD_FOR_NAMESPACE ok */
+ 1, &query, &hc, sqc);
+ GNUNET_RSA_free_key (pk);
+ }
+#if DEBUG_SEARCH
+ GNUNET_GE_LOG (ectx,
+ GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
+ "Queries ready.\n");
+#endif
+ break;
+ }
+ case loc:
+ GNUNET_GE_LOG (ectx,
+ GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
+ _("LOC URI not allowed for search.\n"));
+ break;
+ default:
+ GNUNET_GE_BREAK (ectx, 0);
+ /* unknown URI type */
+ break;
+ }
+}
+
+/**
+ * We found an GNUNET_EC_SBlock. Decode the meta-data and call
+ * the callback of the SQC with the root-URI for the namespace,
+ * together with the namespace advertisement. Also, if this is
+ * a result with updates, automatically start the search for
+ * updates.
+ */
+static int
+process_sblock_result (const GNUNET_EC_SBlock * sb,
+ const GNUNET_HashCode * key,
+ unsigned int size,
+ struct GNUNET_ECRS_SearchContext *sqc)
+{
+ static GNUNET_HashCode allZeros;
+ struct GNUNET_GE_Context *ectx = sqc->ectx;
+ GNUNET_ECRS_FileInfo fi;
+ URI updateURI;
+ int ret;
+ const char *id;
+ const char *uris;
+ unsigned int len;
+ unsigned int off;
+ int isRoot;
+
+ len = size - sizeof (GNUNET_EC_SBlock);
+ off = GNUNET_string_buffer_tokenize ((const char *) &sb[1],
+ len, 2, &id, &uris);
+ if (off == 0)
+ {
+ GNUNET_GE_BREAK_OP (ectx, 0); /* sblock malformed */
+ return GNUNET_SYSERR;
+ }
+ fi.meta = GNUNET_meta_data_deserialize (ectx, &id[off], len - off);
+ if (fi.meta == NULL)
+ {
+ GNUNET_GE_BREAK_OP (ectx, 0); /* sblock malformed */
+ return GNUNET_SYSERR;
+ }
+ isRoot = 0 == memcmp (&sb->identifier, &allZeros, sizeof (GNUNET_HashCode));
+ fi.uri = GNUNET_ECRS_string_to_uri (ectx, uris);
+ if ((isRoot) && (fi.uri == NULL))
+ {
+ fi.uri = GNUNET_malloc (sizeof (URI));
+ fi.uri->type = sks;
+ GNUNET_hash (&sb->subspace,
+ sizeof (GNUNET_RSA_PublicKey),
+ &fi.uri->data.sks.namespace);
+ fi.uri->data.sks.identifier = GNUNET_strdup (id);
+ }
+ if (fi.uri == NULL)
+ {
+ GNUNET_GE_BREAK_OP (ectx, 0); /* sblock malformed */
+ GNUNET_meta_data_destroy (fi.meta);
+ return GNUNET_SYSERR;
+ }
+ if (sqc->spcb != NULL)
+ {
+ ret = sqc->spcb (&fi, key, isRoot, sqc->spcbClosure);
+ if (ret == GNUNET_SYSERR)
+ sqc->aborted = GNUNET_YES;
+ }
+ else
+ ret = GNUNET_OK;
+ if ((strlen (id) > 0) && (strlen (uris) > 0))
+ {
+ updateURI.type = sks;
+ GNUNET_hash (&sb->subspace,
+ sizeof (GNUNET_RSA_PublicKey),
+ &updateURI.data.sks.namespace);
+ updateURI.data.sks.identifier = GNUNET_strdup (id);
+ add_search_for_uri (&updateURI, sqc);
+ GNUNET_free (updateURI.data.sks.identifier);
+ }
+ GNUNET_meta_data_destroy (fi.meta);
+ GNUNET_ECRS_uri_destroy (fi.uri);
+ return ret;
+}
+
+/**
+ * Process replies received in response to our
+ * queries. Verifies, decrypts and passes valid
+ * replies to the callback.
+ *
+ * @return GNUNET_SYSERR if the entry is malformed
+ */
+static int
+receive_response_callback (const GNUNET_HashCode * key,
+ const GNUNET_DatastoreValue * value,
+ void *cls, unsigned long long uid)
+{
+ struct PendingSearch *ps = cls;
+ struct GNUNET_ECRS_SearchContext *sqc = ps->context;
+ struct GNUNET_GE_Context *ectx = sqc->ectx;
+ unsigned int type;
+ GNUNET_ECRS_FileInfo fi;
+ unsigned int size;
+ int ret;
+ GNUNET_HashCode query;
+ GNUNET_CronTime expiration;
+
+ expiration = GNUNET_ntohll (value->expiration_time);
+ if (expiration < GNUNET_get_time ())
+ return GNUNET_OK; /* expired, ignore! */
+ type = ntohl (value->type);
+ size = ntohl (value->size) - sizeof (GNUNET_DatastoreValue);
+#if DEBUG_SEARCH
+ GNUNET_GE_LOG (ectx,
+ GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
+ "Search received reply of type %u and size %u.\n", type,
+ size);
+#endif
+ if (GNUNET_OK !=
+ GNUNET_EC_file_block_check_and_get_query (size,
+ (const GNUNET_EC_DBlock *)
+ &value[1], GNUNET_YES,
+ &query))
+ {
+ GNUNET_GE_BREAK_OP (NULL, 0);
+ return GNUNET_SYSERR;
+ }
+ if (!((0 == memcmp (&query,
+ (GNUNET_HashCode *) & ps[1], sizeof (GNUNET_HashCode)))
+ && ((ps->type == type) || (ps->type == GNUNET_ECRS_BLOCKTYPE_ANY))
+ && (GNUNET_YES ==
+ GNUNET_EC_is_block_applicable_for_query (type, size,
+ (const GNUNET_EC_DBlock
+ *) &value[1], &query,
+ ps->keyCount,
+ (GNUNET_HashCode *) &
+ ps[1]))))
+ {
+ return GNUNET_OK; /* not a match */
+ }
+
+ switch (type)
+ {
+ case GNUNET_ECRS_BLOCKTYPE_KEYWORD:
+ {
+ GNUNET_EC_KBlock *kb;
+ const char *dstURI;
+#if DEBUG_SEARCH
+ GNUNET_EncName enc;
+#endif
+ int j;
+
+ if (size < sizeof (GNUNET_EC_KBlock))
+ {
+ GNUNET_GE_BREAK_OP (NULL, 0);
+ return GNUNET_SYSERR;
+ }
+ kb = GNUNET_malloc (size);
+ memcpy (kb, &value[1], size);
+#if DEBUG_SEARCH
+ IF_GELOG (ectx,
+ GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
+ GNUNET_GE_USER, GNUNET_hash_to_enc (&ps->decryptKey, &enc));
+ GNUNET_GE_LOG (ectx,
+ GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
+ GNUNET_GE_USER,
+ "Decrypting KBlock with key %s.\n", &enc);
+#endif
+ GNUNET_ECRS_decryptInPlace (&ps->decryptKey,
+ &kb[1], size - sizeof (GNUNET_EC_KBlock));
+ j = sizeof (GNUNET_EC_KBlock);
+ while ((j < size) && (((const char *) kb)[j] != '\0'))
+ j++;
+ if (j == size)
+ {
+ GNUNET_GE_BREAK_OP (ectx, 0); /* kblock malformed */
+ GNUNET_free (kb);
+ return GNUNET_SYSERR;
+ }
+ dstURI = (const char *) &kb[1];
+ j++;
+ fi.meta = GNUNET_meta_data_deserialize (ectx,
+ &((const char *)
+ kb)[j], size - j);
+ if (fi.meta == NULL)
+ {
+ GNUNET_GE_BREAK_OP (ectx, 0); /* kblock malformed */
+ GNUNET_free (kb);
+ return GNUNET_SYSERR;
+ }
+ fi.uri = GNUNET_ECRS_string_to_uri (ectx, dstURI);
+ if (fi.uri == NULL)
+ {
+ GNUNET_GE_BREAK_OP (ectx, 0); /* kblock malformed */
+ GNUNET_meta_data_destroy (fi.meta);
+ GNUNET_free (kb);
+ return GNUNET_SYSERR;
+ }
+ if (sqc->spcb != NULL)
+ {
+ ret = sqc->spcb (&fi,
+ &ps->decryptKey, GNUNET_NO, sqc->spcbClosure);
+ if (ret == GNUNET_SYSERR)
+ sqc->aborted = GNUNET_YES;
+ }
+ else
+ ret = GNUNET_OK;
+ GNUNET_ECRS_uri_destroy (fi.uri);
+ GNUNET_meta_data_destroy (fi.meta);
+ GNUNET_free (kb);
+ return ret;
+ }
+ case GNUNET_ECRS_BLOCKTYPE_SIGNED:
+ {
+ GNUNET_EC_SBlock *sb;
+ int ret;
+
+ if (size < sizeof (GNUNET_EC_SBlock))
+ {
+ GNUNET_GE_BREAK_OP (ectx, 0); /* sblock malformed */
+ return GNUNET_SYSERR;
+ }
+ sb = GNUNET_malloc (size);
+ memcpy (sb, &value[1], size);
+ GNUNET_ECRS_decryptInPlace (&ps->decryptKey,
+ &sb[1], size - sizeof (GNUNET_EC_SBlock));
+ ret = process_sblock_result (sb, &ps->decryptKey, size, sqc);
+ GNUNET_free (sb);
+ return ret;
+ }
+ case GNUNET_ECRS_BLOCKTYPE_KEYWORD_SIGNED:
+ {
+ GNUNET_EC_KSBlock *kb;
+ int ret;
+
+ if (size < sizeof (GNUNET_EC_KSBlock))
+ {
+ GNUNET_GE_BREAK_OP (ectx, 0); /* ksblock malformed */
+ return GNUNET_SYSERR;
+ }
+ kb = GNUNET_malloc (size);
+ memcpy (kb, &value[1], size);
+ GNUNET_ECRS_decryptInPlace (&ps->decryptKey,
+ &kb->sblock,
+ size - sizeof (GNUNET_EC_KBlock) -
+ sizeof (unsigned int));
+ ret =
+ process_sblock_result (&kb->sblock, &ps->decryptKey,
+ size - sizeof (GNUNET_EC_KSBlock) +
+ sizeof (GNUNET_EC_SBlock), sqc);
+ GNUNET_free (kb);
+ return ret;
+ }
+ default:
+ GNUNET_GE_BREAK_OP (ectx, 0);
+ break;
+ } /* end switch */
+ return GNUNET_OK;
+}
+
+/**
+ * Start search for content.
+ *
+ * @param uri specifies the search parameters
+ * @param uri set to the URI of the uploaded file
+ */
+struct GNUNET_ECRS_SearchContext *
+GNUNET_ECRS_search_start (struct GNUNET_GE_Context *ectx,
+ struct GNUNET_GC_Configuration *cfg,
+ struct GNUNET_FS_SearchContext *sc,
+ const struct GNUNET_ECRS_URI *uri,
+ unsigned int anonymityLevel,
+ GNUNET_ECRS_SearchResultProcessor spcb,
+ void *spcbClosure)
+{
+ struct GNUNET_ECRS_SearchContext *ctx;
+
+ if (GNUNET_YES == GNUNET_ECRS_uri_test_ksk (uri))
+ {
+ if (1 != GNUNET_ECRS_uri_get_keyword_count_from_ksk (uri))
+ return NULL;
+ }
+ else
+ {
+ if (GNUNET_YES != GNUNET_ECRS_uri_test_sks (uri))
+ return NULL;
+ }
+ ctx = GNUNET_malloc (sizeof (struct GNUNET_ECRS_SearchContext));
+ ctx->start = GNUNET_get_time ();
+ ctx->anonymityLevel = anonymityLevel;
+ ctx->ectx = ectx;
+ ctx->cfg = cfg;
+ ctx->queries = NULL;
+ ctx->spcb = spcb;
+ ctx->spcbClosure = spcbClosure;
+ ctx->aborted = GNUNET_NO;
+ ctx->sctx = sc == NULL ? GNUNET_FS_create_search_context (ectx, cfg) : sc;
+ if (ctx->sctx == NULL)
+ {
+ GNUNET_free (ctx);
+ return NULL;
+ }
+ ctx->my_sctx = (sc == NULL);
+ add_search_for_uri (uri, ctx);
+ return ctx;
+}
+
+/**
+ * Stop search for content.
+ *
+ * @param uri specifies the search parameters
+ * @param uri set to the URI of the uploaded file
+ */
+void
+GNUNET_ECRS_search_stop (struct GNUNET_ECRS_SearchContext *ctx)
+{
+ struct PendingSearch *pos;
+
+ while (ctx->queries != NULL)
+ {
+ pos = ctx->queries;
+ ctx->queries = pos->next;
+ if (!ctx->my_sctx)
+ GNUNET_FS_stop_search (ctx->sctx, &receive_response_callback, pos);
+ GNUNET_free (pos);
+ }
+ if (ctx->my_sctx)
+ GNUNET_FS_destroy_search_context (ctx->sctx);
+ GNUNET_free (ctx);
+}
+
+/**
+ * Search for content.
+ *
+ * @param timeout how long to wait (relative)
+ * @param uri specifies the search parameters
+ * @param uri set to the URI of the uploaded file
+ */
+int
+GNUNET_ECRS_search (struct GNUNET_GE_Context *ectx,
+ struct GNUNET_GC_Configuration *cfg,
+ const struct GNUNET_ECRS_URI *uri,
+ unsigned int anonymityLevel,
+ GNUNET_ECRS_SearchResultProcessor spcb,
+ void *spcbClosure, GNUNET_ECRS_TestTerminate tt,
+ void *ttClosure)
+{
+ struct GNUNET_ECRS_SearchContext *ctx;
+
+ ctx =
+ GNUNET_ECRS_search_start (ectx, cfg, NULL,
+ uri, anonymityLevel, spcb, spcbClosure);
+ if (ctx == NULL)
+ return GNUNET_SYSERR;
+ while (((NULL == tt) || (GNUNET_OK == tt (ttClosure)))
+ && (GNUNET_NO == GNUNET_shutdown_test ())
+ && (ctx->aborted == GNUNET_NO))
+ GNUNET_thread_sleep (100 * GNUNET_CRON_MILLISECONDS);
+ GNUNET_ECRS_search_stop (ctx);
+ return GNUNET_OK;
+}
+
+
+/* end of search.c */