From: Christian Grothoff Date: Fri, 4 Sep 2009 08:12:54 +0000 (+0000) Subject: towards search X-Git-Tag: initial-import-from-subversion-38251~23534 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=f8bfdee4d3a12ccc63b4f340b3032ae3b0ee5fd1;p=oweals%2Fgnunet.git towards search --- diff --git a/TODO b/TODO index 49b1fc73b..209a0c9f3 100644 --- a/TODO +++ b/TODO @@ -36,8 +36,6 @@ PHASE #2: (Goal: recover basic file-sharing functionality) * HOSTLIST: - implement testcases * FS (basic anonymous FS only) - - design network structs (CS) - + search/download, response - implement basic FS library - keyword-search - download (need search to be done) @@ -61,8 +59,13 @@ PHASE #2: (Goal: recover basic file-sharing functionality) ~ collection + directory API * new webpage + - lcov + - buildbot + - migrate Mantis? + - download links on Drupal? + - run peer + - configure hostlist - install on proper server - - activate as ng.gnunet.org => Deploy(able) development network diff --git a/src/fs/fs.h b/src/fs/fs.h index 7df395e86..6e793bf8d 100644 --- a/src/fs/fs.h +++ b/src/fs/fs.h @@ -582,11 +582,71 @@ struct GNUNET_FS_UnindexContext }; +/** + * Information we keep for each keyword in + * a keyword search. + */ +struct SearchRequestEntry +{ + /** + * Hash of the original keyword, also known as the + * key (for decrypting the KBlock). + */ + GNUNET_HashCode key; + + /** + * Hash of the public key, also known as the query. + */ + GNUNET_HashCode query; +}; + + /** * Handle for controlling a search. */ struct GNUNET_FS_SearchContext { + /** + * Handle to the global FS context. + */ + struct GNUNET_FS_Handle *h; + + /** + * List of keywords that we're looking for. + */ + struct GNUNET_FS_Uri *uri; + + /** + * Connection to the FS service. + */ + struct GNUNET_CLIENT_Connection *client; + + /** + * Per-keyword information for a keyword search. + */ + struct SearchRequestEntry *requests; + + /** + * When did we start? + */ + struct GNUNET_TIME_Absolute start_time; + + /** + * ID of a task that is using this struct + * and that must be cancelled when the search + * is being stopped (if not GNUNET_SCHEDULER_NO_TASK). + * Used for the task that adds some artificial + * delay when trying to reconnect to the FS + * service. + */ + GNUNET_SCHEDULER_TaskIdentifier task; + + /** + * Anonymity level for the search. + */ + unsigned int anonymity; + + }; @@ -630,7 +690,7 @@ struct OnDemandBlock * At which offset should we be able to find * this on-demand encoded block? */ - uint64_t offset; + uint64_t offset GNUNET_PACKED; }; @@ -733,7 +793,7 @@ struct IndexStartMessage * OS does not support this, in which case the service must do a * full hash recomputation. */ - uint32_t device; + uint32_t device GNUNET_PACKED; /** * Inode of the file on the given device, as seen by the client @@ -741,7 +801,7 @@ struct IndexStartMessage * support this, in which case the service must do a full hash * recomputation. */ - uint64_t inode; + uint64_t inode GNUNET_PACKED; /** * Hash of the file that we would like to index. @@ -767,6 +827,11 @@ struct IndexInfoMessage */ struct GNUNET_MessageHeader header; + /** + * Always zero. + */ + uint32_t reserved GNUNET_PACKED; + /** * Hash of the indexed file. */ @@ -800,7 +865,7 @@ struct UnindexMessage /** * Always zero. */ - uint32_t reserved; + uint32_t reserved GNUNET_PACKED; /** * Hash of the file that we will unindex. @@ -810,6 +875,93 @@ struct UnindexMessage }; +/** + * Message sent from a GNUnet (fs) search + * activity to the gnunet-fs-service to + * start a search. + */ +struct SearchMessage +{ + + /** + * Message type will be + * GNUNET_MESSAGE_TYPE_FS_START_SEARCH. + */ + struct GNUNET_MessageHeader header; + + /** + * Should be zero. + */ + int32_t reserved GNUNET_PACKED; + + /** + * Type of the content that we're looking for. + * 0 for any. + */ + uint32_t type GNUNET_PACKED; + + /** + * Desired anonymity level, big-endian. + */ + uint32_t anonymity_level GNUNET_PACKED; + + /** + * If the request is for a DBLOCK or IBLOCK, this is the identity of + * the peer that is known to have a response. Set to all-zeros if + * such a target is not known (note that even if OUR anonymity + * level is >0 we may happen to know the responder's identity; + * nevertheless, we should probably not use it for a DHT-lookup + * or similar blunt actions in order to avoid exposing ourselves). + *

+ * If the request is for an SBLOCK, this is the identity of the + * pseudonym to which the SBLOCK belongs. + *

+ * If the request is for a KBLOCK, "target" must be all zeros. + */ + GNUNET_HashCode target; + + /** + * Hash of the keyword (aka query) for KBLOCKs; Hash of + * the CHK-encoded block for DBLOCKS and IBLOCKS (aka query) + * and hash of the identifier XORed with the target for + * SBLOCKS (aka query). + */ + GNUNET_HashCode query; + +}; + + +/** + * Response from FS service with a result for + * a previous FS search. Note that queries + * for DBLOCKS and IBLOCKS that have received + * a single response are considered done. + */ +struct ContentMessage +{ + + /** + * Message type will be + * GNUNET_MESSAGE_TYPE_FS_CONTENT. + */ + struct GNUNET_MessageHeader header; + + /** + * Type of the content that was found, + * should never be 0. + */ + uint32_t type GNUNET_PACKED; + + /** + * When will this result expire? + */ + struct GNUNET_TIME_AbsoluteNBO expiration; + + /* followed by the actual block of data */ + +}; + + #endif diff --git a/src/fs/fs_search.c b/src/fs/fs_search.c index 51d1fbc1f..e855a4d30 100644 --- a/src/fs/fs_search.c +++ b/src/fs/fs_search.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008 Christian Grothoff (and other contributing authors) + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009 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 @@ -22,604 +22,592 @@ * @file fs/fs_search.c * @brief Helper functions for searching. * @author Christian Grothoff + * + * TODO: + * - aggregate and process results (FSUI-style) + * - call progress callbacks + * - make operations persistent (can wait) + * - add support for pushing "already seen" information + * to FS service for bloomfilter (can wait) */ #include "platform.h" +#include "gnunet_constants.h" #include "gnunet_fs_service.h" +#include "gnunet_protocols.h" #include "fs.h" #define DEBUG_SEARCH GNUNET_YES /** - * Start search for content. + * We have received a KSK result. Check + * how it fits in with the overall query + * and notify the client accordingly. * - * @param h handle to the file sharing subsystem - * @param uri specifies the search parameters; can be - * a KSK URI or an SKS URI. - * @param anonymity desired level of anonymity - * @return context that can be used to control the search + * @param sc context for the overall query + * @param ent entry for the specific keyword + * @param uri the URI that was found + * @param meta metadata associated with the URI + * under the "ent" keyword */ -struct GNUNET_FS_SearchContext * -GNUNET_FS_search_start (struct GNUNET_FS_Handle *h, - const struct GNUNET_FS_Uri *uri, - unsigned int anonymity) +static void +process_ksk_result (struct GNUNET_FS_SearchContext *sc, + struct SearchRequestEntry *ent, + const struct GNUNET_FS_Uri *uri, + const struct GNUNET_CONTAINER_MetaData *meta) { - return NULL; + // FIXME: check if new + // FIXME: check if mandatory satisfied + // FIXME: notify client! } /** - * Pause search. + * We have received an SKS result. Start + * searching for updates and notify the + * client if it is a new result. * - * @param sc context for the search that should be paused - */ -void -GNUNET_FS_search_pause (struct GNUNET_FS_SearchContext *sc) + * @param sc context for the overall query + * @param id_update identifier for updates, NULL for none + * @param uri the URI that was found + * @param meta metadata associated with the URI + */ +static void +process_sks_result (struct GNUNET_FS_SearchContext *sc, + const char *id_update, + const struct GNUNET_FS_Uri *uri, + const struct GNUNET_CONTAINER_MetaData *meta) { + // FIXME: check if new + // FIXME: notify client + + if (strlen (id_update) > 0) + { + // FIXME: search for updates! +#if 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); +#endif + } } + /** - * Continue paused search. + * Process a keyword-search result. * - * @param sc context for the search that should be resumed + * @param sc our search context + * @param kb the kblock + * @param size size of kb */ -void -GNUNET_FS_search_continue (struct GNUNET_FS_SearchContext *sc) +static void +process_kblock (struct GNUNET_FS_SearchContext *sc, + const struct KBlock *kb, + size_t size) { + unsigned int i; + size_t j; + GNUNET_HashCode q; + char pt[size - sizeof (struct KBlock)]; + struct GNUNET_CRYPTO_AesSessionKey skey; + struct GNUNET_CRYPTO_AesInitializationVector iv; + const char *eos; + struct GNUNET_CONTAINER_MetaData *meta; + struct GNUNET_FS_Uri *uri; + char *emsg; + + GNUNET_CRYPTO_hash (&kb->keyspace, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &q); + /* find key */ + for (i=0;iuri->data.ksk.keywordCount;i++) + if (0 == memcmp (&q, + &sc->requests[i].query, + sizeof (GNUNET_HashCode))) + break; + if (i == sc->uri->data.ksk.keywordCount) + { + /* oops, does not match any of our keywords!? */ + GNUNET_break (0); + return; + } + /* decrypt */ + GNUNET_CRYPTO_hash_to_aes_key (&sc->requests[i].key, &skey, &iv); + GNUNET_CRYPTO_aes_encrypt (&kb[1], + size - sizeof (struct KBlock), + &skey, + &iv, + pt); + /* parse */ + eos = memchr (pt, 0, sizeof (pt)); + if (NULL == eos) + { + GNUNET_break_op (0); + return; + } + j = eos - pt + 1; + meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j], + sizeof (pt) - j); + if (meta == NULL) + { + GNUNET_break_op (0); /* kblock malformed */ + return; + } + uri = GNUNET_FS_uri_parse (pt, &emsg); + if (uri == NULL) + { + GNUNET_break_op (0); /* kblock malformed */ + GNUNET_free_non_null (emsg); + GNUNET_CONTAINER_meta_data_destroy (meta); + return; + } + /* process */ + process_ksk_result (sc, &sc->requests[i], uri, meta); + + /* clean up */ + GNUNET_CONTAINER_meta_data_destroy (meta); + GNUNET_FS_uri_destroy (uri); } /** - * Stop search for content. + * Process a namespace-search result. * - * @param sc context for the search that should be stopped + * @param sc our search context + * @param sb the sblock + * @param size size of sb */ -void -GNUNET_FS_search_stop (struct GNUNET_FS_SearchContext *sc) +static void +process_sblock (struct GNUNET_FS_SearchContext *sc, + const struct SBlock *sb, + size_t size) { + size_t len = size - sizeof (struct SBlock); + char pt[len]; + struct GNUNET_CRYPTO_AesSessionKey skey; + struct GNUNET_CRYPTO_AesInitializationVector iv; + struct GNUNET_FS_Uri *uri; + struct GNUNET_CONTAINER_MetaData *meta; + const char *id; + const char *uris; + size_t off; + char *emsg; + GNUNET_HashCode key; + char *identifier; + + /* decrypt */ + identifier = sc->uri->data.sks.identifier; + GNUNET_CRYPTO_hash (identifier, + strlen (identifier), + &key); + GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv); + GNUNET_CRYPTO_aes_encrypt (&sb[1], + len, + &skey, + &iv, + pt); + /* parse */ + off = GNUNET_STRINGS_buffer_tokenize (pt, + len, + 2, + &id, + &uris); + if (off == 0) + { + GNUNET_break_op (0); /* sblock malformed */ + return; + } + meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[off], + len - off); + if (meta == NULL) + { + GNUNET_break_op (0); /* sblock malformed */ + return; + } + uri = GNUNET_FS_uri_parse (uris, &emsg); + if (uri == NULL) + { + GNUNET_break_op (0); /* sblock malformed */ + GNUNET_free_non_null (emsg); + GNUNET_CONTAINER_meta_data_destroy (meta); + return; + } + /* process */ + process_sks_result (sc, id, uri, meta); + /* clean up */ + GNUNET_FS_uri_destroy (uri); + GNUNET_CONTAINER_meta_data_destroy (meta); } - - -#if 0 - /** - * Context for an individual search. Followed - * by keyCount keys of type GNUNET_HashCode. + * Process a search result. + * + * @param sc our search context + * @param type type of the result + * @param expiration when it will expire + * @param data the (encrypted) response + * @param size size of data */ -struct PendingSearch +static void +process_result (struct GNUNET_FS_SearchContext *sc, + uint32_t type, + struct GNUNET_TIME_Absolute expiration, + const void *data, + size_t size) { - 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; + if (GNUNET_TIME_absolute_get_duration (expiration).value > 0) + return; /* result expired */ + switch (type) + { + case GNUNET_DATASTORE_BLOCKTYPE_KBLOCK: + if (! GNUNET_FS_uri_test_ksk (sc->uri)) + { + GNUNET_break (0); + return; + } + if (sizeof (struct KBlock) > size) + { + GNUNET_break_op (0); + return; + } + process_kblock (sc, data, size); + break; + case GNUNET_DATASTORE_BLOCKTYPE_SBLOCK: + if (! GNUNET_FS_uri_test_ksk (sc->uri)) + { + GNUNET_break (0); + return; + } + if (sizeof (struct SBlock) > size) + { + GNUNET_break_op (0); + return; + } + process_sblock (sc, data, size); + break; + case GNUNET_DATASTORE_BLOCKTYPE_ANY: + case GNUNET_DATASTORE_BLOCKTYPE_DBLOCK: + case GNUNET_DATASTORE_BLOCKTYPE_ONDEMAND: + case GNUNET_DATASTORE_BLOCKTYPE_IBLOCK: + GNUNET_break (0); + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Got result with unknown block type `%d', ignoring"), + type); + break; + } +} -}; /** - * Context for search operation. + * Shutdown any existing connection to the FS + * service and try to establish a fresh one + * (and then re-transmit our search request). + * + * @param sc the search to reconnec */ -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; +static void +try_reconnect (struct GNUNET_FS_SearchContext *sc); - 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. + * Type of a function to call when we receive a message + * from the service. + * + * @param cls closure + * @param msg message received, NULL on timeout or fatal error */ -static void -add_search_for_uri (const struct GNUNET_ECRS_URI *uri, - struct GNUNET_ECRS_SearchContext *sqc) +static void +receive_results (void *cls, + const struct GNUNET_MessageHeader * msg) { - struct GNUNET_GE_Context *ectx = sqc->ectx; + struct GNUNET_FS_SearchContext *sc = cls; + const struct ContentMessage *cm; + uint16_t msize; - switch (uri->type) + if ( (NULL == msg) || + (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_CONTENT) || + (ntohs (msg->size) <= sizeof (struct ContentMessage)) ) { - 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; + try_reconnect (sc); + return; } + msize = ntohs (msg->size); + cm = (const struct ContentMessage*) msg; + process_result (sc, + ntohl (cm->type), + GNUNET_TIME_absolute_ntoh (cm->expiration), + &cm[1], + msize - sizeof (struct ContentMessage)); + /* continue receiving */ + GNUNET_CLIENT_receive (sc->client, + &receive_results, + sc, + GNUNET_TIME_UNIT_FOREVER_REL); } + /** - * 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. + * We're ready to transmit the search request to the + * file-sharing service. Do it. + * + * @param cls closure + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf */ -static int -process_sblock_result (const GNUNET_EC_SBlock * sb, - const GNUNET_HashCode * key, - unsigned int size, - struct GNUNET_ECRS_SearchContext *sqc) +static size_t +transmit_search_request (void *cls, + size_t size, + void *buf) { - 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) + struct GNUNET_FS_SearchContext *sc = cls; + size_t msize; + struct SearchMessage *sm; + unsigned int i; + const char *keyword; + const char *identifier; + GNUNET_HashCode idh; + GNUNET_HashCode hc; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub; + struct GNUNET_CRYPTO_RsaPrivateKey *pk; + + if (NULL == buf) { - GNUNET_GE_BREAK_OP (ectx, 0); /* sblock malformed */ - return GNUNET_SYSERR; + try_reconnect (sc); + return 0; } - fi.meta = GNUNET_meta_data_deserialize (ectx, &id[off], len - off); - if (fi.meta == NULL) + if (GNUNET_FS_uri_test_ksk (sc->uri)) { - 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; + msize = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount; + GNUNET_assert (size >= msize); + sm = buf; + memset (sm, 0, msize); + sc->requests = GNUNET_malloc (sizeof (struct SearchRequestEntry) * + sc->uri->data.ksk.keywordCount); + for (i=0;iuri->data.ksk.keywordCount;i++) + { + sm[i].header.size = htons (sizeof (struct SearchMessage)); + sm[i].header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH); + sm[i].anonymity_level = htonl (sc->anonymity); + keyword = &sc->uri->data.ksk.keywords[i][1]; + + GNUNET_CRYPTO_hash (keyword, strlen (keyword), &hc); + pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&hc); + GNUNET_CRYPTO_rsa_key_get_public (pk, &pub); + GNUNET_CRYPTO_rsa_key_free (pk); + GNUNET_CRYPTO_hash (&pub, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &sm[i].query); + sc->requests[i].query = sm[i].query; + GNUNET_CRYPTO_hash (keyword, + strlen (keyword), + &sc->requests[i].key); + } } 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); + msize = sizeof (struct SearchMessage); + GNUNET_assert (size >= msize); + sm = buf; + memset (sm, 0, msize); + sm->header.size = htons (sizeof (struct SearchMessage)); + sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH); + sm->anonymity_level = htonl (sc->anonymity); + sm->target = sc->uri->data.sks.namespace; + identifier = sc->uri->data.sks.identifier; + GNUNET_CRYPTO_hash (identifier, + strlen (identifier), + &idh); + GNUNET_CRYPTO_hash_xor (&idh, + &sm->target, + &sm->query); } - GNUNET_meta_data_destroy (fi.meta); - GNUNET_ECRS_uri_destroy (fi.uri); - return ret; + GNUNET_CLIENT_receive (sc->client, + &receive_results, + sc, + GNUNET_TIME_UNIT_FOREVER_REL); + return msize; } + /** - * Process replies received in response to our - * queries. Verifies, decrypts and passes valid - * replies to the callback. + * Reconnect to the FS service and transmit + * our queries NOW. * - * @return GNUNET_SYSERR if the entry is malformed + * @param cls our search context + * @param tc unused */ -static int -receive_response_callback (const GNUNET_HashCode * key, - const GNUNET_DatastoreValue * value, - void *cls, unsigned long long uid) +static void +do_reconnect (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { - 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)) + struct GNUNET_FS_SearchContext *sc = cls; + struct GNUNET_CLIENT_Connection *client; + size_t size; + + sc->task = GNUNET_SCHEDULER_NO_TASK; + client = GNUNET_CLIENT_connect (sc->h->sched, + "fs", + sc->h->cfg); + if (NULL == client) { - 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 */ + try_reconnect (sc); + return; } + sc->client = client; + if (GNUNET_FS_uri_test_ksk (sc->uri)) + size = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount; + else + size = sizeof (struct SearchMessage); + GNUNET_CLIENT_notify_transmit_ready (client, + size, + GNUNET_CONSTANTS_SERVICE_TIMEOUT, + &transmit_search_request, + sc); +} - switch (type) + +/** + * Shutdown any existing connection to the FS + * service and try to establish a fresh one + * (and then re-transmit our search request). + * + * @param sc the search to reconnec + */ +static void +try_reconnect (struct GNUNET_FS_SearchContext *sc) +{ + if (NULL != sc->client) { - 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; + GNUNET_CLIENT_disconnect (sc->client); + sc->client = NULL; + } + sc->task + = GNUNET_SCHEDULER_add_delayed (sc->h->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_IDLE, + GNUNET_SCHEDULER_NO_TASK, + GNUNET_TIME_UNIT_SECONDS, + &do_reconnect, + sc); } + /** * Start search for content. * - * @param uri specifies the search parameters - * @param uri set to the URI of the uploaded file + * @param h handle to the file sharing subsystem + * @param uri specifies the search parameters; can be + * a KSK URI or an SKS URI. + * @param anonymity desired level of anonymity + * @return context that can be used to control the search */ -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_FS_SearchContext * +GNUNET_FS_search_start (struct GNUNET_FS_Handle *h, + const struct GNUNET_FS_Uri *uri, + unsigned int anonymity) { - struct GNUNET_ECRS_SearchContext *ctx; + struct GNUNET_FS_SearchContext *sc; + struct GNUNET_CLIENT_Connection *client; + size_t size; - if (GNUNET_YES == GNUNET_ECRS_uri_test_ksk (uri)) + if (GNUNET_FS_uri_test_ksk (uri)) { - if (1 != GNUNET_ECRS_uri_get_keyword_count_from_ksk (uri)) - return NULL; + size = sizeof (struct SearchMessage) * uri->data.ksk.keywordCount; } else { - if (GNUNET_YES != GNUNET_ECRS_uri_test_sks (uri)) - return NULL; + GNUNET_assert (GNUNET_FS_uri_test_sks (uri)); + size = sizeof (struct SearchMessage); } - 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) + if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE) { - GNUNET_free (ctx); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Too many keywords specified for a single search.")); return NULL; } - ctx->my_sctx = (sc == NULL); - add_search_for_uri (uri, ctx); - return ctx; + client = GNUNET_CLIENT_connect (sc->h->sched, + "fs", + sc->h->cfg); + if (NULL == client) + return NULL; + sc = GNUNET_malloc (sizeof(struct GNUNET_FS_SearchContext)); + sc->h = h; + sc->uri = GNUNET_FS_uri_dup (uri); + sc->anonymity = anonymity; + sc->start_time = GNUNET_TIME_absolute_get (); + sc->client = client; + // FIXME: call callback! + GNUNET_CLIENT_notify_transmit_ready (client, + size, + GNUNET_CONSTANTS_SERVICE_TIMEOUT, + &transmit_search_request, + sc); + return sc; } + /** - * Stop search for content. + * Pause search. * - * @param uri specifies the search parameters - * @param uri set to the URI of the uploaded file + * @param sc context for the search that should be paused */ -void -GNUNET_ECRS_search_stop (struct GNUNET_ECRS_SearchContext *ctx) +void +GNUNET_FS_search_pause (struct GNUNET_FS_SearchContext *sc) { - 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); + if (sc->task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (sc->h->sched, + sc->task); + sc->task = GNUNET_SCHEDULER_NO_TASK; + if (NULL != sc->client) + GNUNET_CLIENT_disconnect (sc->client); + sc->client = NULL; + // FIXME: make persistent! + // FIXME: call callback! } + /** - * Search for content. + * Continue paused search. * - * @param timeout how long to wait (relative) - * @param uri specifies the search parameters - * @param uri set to the URI of the uploaded file + * @param sc context for the search that should be resumed */ -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) +void +GNUNET_FS_search_continue (struct GNUNET_FS_SearchContext *sc) { - 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; + GNUNET_assert (sc->client == NULL); + GNUNET_assert (sc->task == GNUNET_SCHEDULER_NO_TASK); + do_reconnect (sc, NULL); + // FIXME: make persistent! + // FIXME: call callback! } -#endif + +/** + * Stop search for content. + * + * @param sc context for the search that should be stopped + */ +void +GNUNET_FS_search_stop (struct GNUNET_FS_SearchContext *sc) +{ + // FIXME: make un-persistent! + // FIXME: call callback! + if (sc->task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (sc->h->sched, + sc->task); + if (NULL != sc->client) + GNUNET_CLIENT_disconnect (sc->client); + GNUNET_free_non_null (sc->requests); + GNUNET_FS_uri_destroy (sc->uri); + GNUNET_free (sc); +} /* end of fs_search.c */ diff --git a/src/fs/fs_unindex.c b/src/fs/fs_unindex.c index fb72c0167..23779bc97 100644 --- a/src/fs/fs_unindex.c +++ b/src/fs/fs_unindex.c @@ -78,54 +78,6 @@ unindex_reader (void *cls, } -/** - * Function called asking for the current (encoded) - * block to be processed. After processing the - * client should either call "GNUNET_FS_tree_encode_next" - * or (on error) "GNUNET_FS_tree_encode_finish". - * - * @param cls closure - * @param query the query for the block (key for lookup in the datastore) - * @param offset offset of the block - * @param type type of the block (IBLOCK or DBLOCK) - * @param block the (encrypted) block - * @param block_size size of block (in bytes) - */ -static void -unindex_process (void *cls, - const GNUNET_HashCode *query, - uint64_t offset, - unsigned int type, - const void *block, - uint16_t block_size) -{ - struct GNUNET_FS_UnindexContext *uc = cls; - uint32_t size; - const void *data; - struct OnDemandBlock odb; - - if (type != GNUNET_DATASTORE_BLOCKTYPE_DBLOCK) - { - size = block_size; - data = block; - } - else /* on-demand encoded DBLOCK */ - { - size = sizeof(struct OnDemandBlock); - odb.offset = offset; - odb.file_id = uc->file_id; - data = &odb; - } - GNUNET_DATASTORE_remove (uc->dsh, - query, - block_size, - block, - &process_cont, - uc, - GNUNET_CONSTANTS_SERVICE_TIMEOUT); -} - - /** * Fill in all of the generic fields for * an unindex event. @@ -223,7 +175,7 @@ process_cont (void *cls, if (success == GNUNET_SYSERR) { signal_unindex_error (uc, - emsg); + msg); return; } @@ -231,6 +183,54 @@ process_cont (void *cls, } +/** + * Function called asking for the current (encoded) + * block to be processed. After processing the + * client should either call "GNUNET_FS_tree_encode_next" + * or (on error) "GNUNET_FS_tree_encode_finish". + * + * @param cls closure + * @param query the query for the block (key for lookup in the datastore) + * @param offset offset of the block + * @param type type of the block (IBLOCK or DBLOCK) + * @param block the (encrypted) block + * @param block_size size of block (in bytes) + */ +static void +unindex_process (void *cls, + const GNUNET_HashCode *query, + uint64_t offset, + unsigned int type, + const void *block, + uint16_t block_size) +{ + struct GNUNET_FS_UnindexContext *uc = cls; + uint32_t size; + const void *data; + struct OnDemandBlock odb; + + if (type != GNUNET_DATASTORE_BLOCKTYPE_DBLOCK) + { + size = block_size; + data = block; + } + else /* on-demand encoded DBLOCK */ + { + size = sizeof(struct OnDemandBlock); + odb.offset = offset; + odb.file_id = uc->file_id; + data = &odb; + } + GNUNET_DATASTORE_remove (uc->dsh, + query, + block_size, + block, + &process_cont, + uc, + GNUNET_CONSTANTS_SERVICE_TIMEOUT); +} + + /** * Function called when the tree encoder has * processed all blocks. Clean up. diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index ad8e28bd1..664b10636 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h @@ -409,6 +409,16 @@ extern "C" */ #define GNUNET_MESSAGE_TYPE_FS_UNINDEX_OK 135 +/** + * Client asks FS service to start a (keyword) search. + */ +#define GNUNET_MESSAGE_TYPE_FS_START_SEARCH 136 + +/** + * FS service has found content matching this client's + * request. + */ +#define GNUNET_MESSAGE_TYPE_FS_CONTENT 137 /* TODO: