/*
This file is part of GNUnet.
- (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009 Christian Grothoff (and other contributing authors)
+ (C) 2001-2006, 2008-2012 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
#include "gnunet_constants.h"
#include "gnunet_fs_service.h"
#include "gnunet_protocols.h"
-#include "fs.h"
+#include "fs_api.h"
-#define DEBUG_SEARCH GNUNET_NO
+#define DEBUG_SEARCH GNUNET_EXTRA_LOGGING
+
+/**
+ * Number of availability trials we perform per search result.
+ */
+#define AVAILABILITY_TRIALS_MAX 8
/**
* Fill in all of the generic fields for a search event and
struct GNUNET_FS_SearchResult *sr)
{
struct GNUNET_FS_ProgressInfo pi;
-
+
pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE;
pi.value.search.specifics.update.cctx = sr->client_info;
pi.value.search.specifics.update.meta = sr->meta;
{
struct GNUNET_FS_ProgressInfo pi;
- pi.status = GNUNET_FS_STATUS_SEARCH_START;
+ pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE;
pi.value.search.specifics.update.cctx = sr->client_info;
pi.value.search.specifics.update.meta = sr->meta;
pi.value.search.specifics.update.uri = sr->uri;
pi.value.search.specifics.update.availability_certainty =
sr->availability_trials;
pi.value.search.specifics.update.applicability_rank = sr->optional_support;
- sr->sc->client_info = GNUNET_FS_search_make_status_ (&pi, sr->sc);
+ sr->client_info = GNUNET_FS_search_make_status_ (&pi, sr->sc);
GNUNET_FS_search_start_probe_ (sr);
}
{
struct GNUNET_FS_SearchResult *sr = cls;
+ sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
sr->availability_trials++;
+ GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
+ sr->probe_ctx = NULL;
GNUNET_FS_search_result_sync_ (sr);
signal_probe_result (sr);
}
{
struct GNUNET_FS_SearchResult *sr = cls;
+ sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
sr->availability_trials++;
sr->availability_success++;
+ GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
+ sr->probe_ctx = NULL;
GNUNET_FS_search_result_sync_ (sr);
signal_probe_result (sr);
}
sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
}
sr->probe_cancel_task =
- GNUNET_SCHEDULER_add_delayed (sr->remaining_probe_time,
- &probe_success_handler, sr);
+ GNUNET_SCHEDULER_add_now (&probe_success_handler, sr);
break;
case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
struct GNUNET_FS_SearchResult *sr;
struct GetResultContext grc;
int is_new;
+ unsigned int koff;
/* check if new */
+ GNUNET_assert (NULL != sc);
GNUNET_FS_uri_to_key (uri, &key);
if (GNUNET_SYSERR ==
GNUNET_CONTAINER_multihashmap_get_multiple (ent->results, &key,
sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
sr->mandatory_missing = sc->mandatory_count;
sr->key = key;
+ sr->keyword_bitmap = GNUNET_malloc ((sc->uri->data.ksk.keywordCount + 7) / 8); /* round up, count bits */
GNUNET_CONTAINER_multihashmap_put (sc->master_result_map, &key, sr,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
}
{
GNUNET_CONTAINER_meta_data_merge (sr->meta, meta);
}
+ koff = ent - sc->requests;
+ GNUNET_assert ( (ent >= sc->requests) && (koff < sc->uri->data.ksk.keywordCount));
+ sr->keyword_bitmap[koff / 8] |= (1 << (koff % 8));
/* check if mandatory satisfied */
if (ent->mandatory)
sr->mandatory_missing--;
struct GNUNET_FS_SearchResult *sr;
/* check if new */
+ GNUNET_assert (NULL != sc);
GNUNET_FS_uri_to_key (uri, &key);
GNUNET_CRYPTO_hash_xor (&uri->data.chk.chk.key, &uri->data.chk.chk.query,
&key);
/**
- * Process a keyword-search result.
+ * Decrypt a block using a 'keyword' as the passphrase. Given the
+ * KSK public key derived from the keyword, this function looks up
+ * the original keyword in the search context and decrypts the
+ * given ciphertext block.
*
- * @param sc our search context
- * @param kb the kblock
- * @param size size of kb
+ * @param sc search context with the keywords
+ * @param public_key public key to use to lookup the keyword
+ * @param edata encrypted data
+ * @param edata_size number of bytes in 'edata' (and 'data')
+ * @param data where to store the plaintext
+ * @return keyword index on success, GNUNET_SYSERR on error (no such
+ * keyword, internal error)
*/
-static void
-process_kblock (struct GNUNET_FS_SearchContext *sc, const struct KBlock *kb,
- size_t size)
-{
- unsigned int i;
- size_t j;
+static int
+decrypt_block_with_keyword (const struct GNUNET_FS_SearchContext *sc,
+ const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key,
+ const void *edata,
+ size_t edata_size,
+ char *data)
+{
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;
+ int i;
- GNUNET_CRYPTO_hash (&kb->keyspace,
+ GNUNET_CRYPTO_hash (public_key,
sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
&q);
/* find key */
{
/* oops, does not match any of our keywords!? */
GNUNET_break (0);
- return;
+ return GNUNET_SYSERR;
}
/* decrypt */
GNUNET_CRYPTO_hash_to_aes_key (&sc->requests[i].key, &skey, &iv);
if (-1 ==
- GNUNET_CRYPTO_aes_decrypt (&kb[1], size - sizeof (struct KBlock), &skey,
- &iv, pt))
+ GNUNET_CRYPTO_aes_decrypt (edata, edata_size, &skey,
+ &iv, data))
{
GNUNET_break (0);
- return;
+ return GNUNET_SYSERR;
}
+ return i;
+}
+
+
+/**
+ * Process a keyword-search result.
+ *
+ * @param sc our search context
+ * @param kb the kblock
+ * @param size size of kb
+ */
+static void
+process_kblock (struct GNUNET_FS_SearchContext *sc, const struct KBlock *kb,
+ size_t size)
+{
+ size_t j;
+ char pt[size - sizeof (struct KBlock)];
+ const char *eos;
+ struct GNUNET_CONTAINER_MetaData *meta;
+ struct GNUNET_FS_Uri *uri;
+ char *emsg;
+ int i;
+
+ if (-1 == (i = decrypt_block_with_keyword (sc,
+ &kb->keyspace,
+ &kb[1],
+ size - sizeof (struct KBlock),
+ pt)))
+ return;
/* parse */
eos = memchr (pt, 0, sizeof (pt));
if (NULL == eos)
process_nblock (struct GNUNET_FS_SearchContext *sc, const struct NBlock *nb,
size_t size)
{
- unsigned int i;
size_t j;
- GNUNET_HashCode q;
char pt[size - sizeof (struct NBlock)];
- struct GNUNET_CRYPTO_AesSessionKey skey;
- struct GNUNET_CRYPTO_AesInitializationVector iv;
const char *eos;
struct GNUNET_CONTAINER_MetaData *meta;
struct GNUNET_FS_Uri *uri;
char *uris;
+ int i;
- GNUNET_CRYPTO_hash (&nb->keyspace,
- sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
- &q);
- /* find key */
- for (i = 0; i < sc->uri->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);
+ if (-1 == (i = decrypt_block_with_keyword (sc,
+ &nb->keyspace,
+ &nb[1],
+ size - sizeof (struct NBlock),
+ pt)))
return;
- }
- /* decrypt */
- GNUNET_CRYPTO_hash_to_aes_key (&sc->requests[i].key, &skey, &iv);
- if (-1 ==
- GNUNET_CRYPTO_aes_decrypt (&nb[1], size - sizeof (struct NBlock), &skey,
- &iv, pt))
- {
- GNUNET_break (0);
- return;
- }
/* parse */
eos = memchr (pt, 0, sizeof (pt));
if (NULL == eos)
receive_results (void *cls, const struct GNUNET_MessageHeader *msg)
{
struct GNUNET_FS_SearchContext *sc = cls;
- const struct PutMessage *cm;
+ const struct ClientPutMessage *cm;
uint16_t msize;
if ((NULL == msg) || (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_PUT) ||
- (ntohs (msg->size) <= sizeof (struct PutMessage)))
+ (ntohs (msg->size) <= sizeof (struct ClientPutMessage)))
{
try_reconnect (sc);
return;
msize = ntohs (msg->size);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Receiving %u bytes of result from fs service\n", msize);
- cm = (const struct PutMessage *) msg;
+ cm = (const struct ClientPutMessage *) msg;
process_result (sc, ntohl (cm->type),
GNUNET_TIME_absolute_ntoh (cm->expiration), &cm[1],
- msize - sizeof (struct PutMessage));
+ msize - sizeof (struct ClientPutMessage));
/* continue receiving */
GNUNET_CLIENT_receive (sc->client, &receive_results, sc,
GNUNET_TIME_UNIT_FOREVER_REL);
struct GNUNET_FS_SearchContext *sc;
/**
- * URI the search result must match, NULL for any
+ * Keyword offset the search result must match (0 for SKS)
*/
- struct GNUNET_FS_Uri *uri;
+ unsigned int keyword_offset;
};
/**
- * Iterating over the known results, pick those
- * matching the given result range and store
- * their keys at 'xoff'.
+ * Iterating over the known results, pick those matching the given
+ * result range and store their keys at 'xoff'.
*
* @param cls the 'struct MessageBuilderContext'
* @param key key for a result
struct MessageBuilderContext *mbc = cls;
struct GNUNET_FS_SearchResult *sr = value;
- if ((mbc->uri != NULL) &&
- (GNUNET_YES != GNUNET_FS_uri_test_equal (mbc->uri, sr->uri)))
- return GNUNET_OK;
+ if ( (sr->keyword_bitmap != NULL) &&
+ (0 == (sr->keyword_bitmap[mbc->keyword_offset / 8] & (1 << (mbc->keyword_offset % 8)))) )
+ return GNUNET_OK; /* have no match for this keyword yet */
if (mbc->skip_cnt > 0)
{
mbc->skip_cnt--;
struct MessageBuilderContext *mbc = cls;
struct GNUNET_FS_SearchResult *sr = value;
- if ((mbc->uri != NULL) &&
- (GNUNET_YES != GNUNET_FS_uri_test_equal (mbc->uri, sr->uri)))
- return GNUNET_OK;
+ if ( (sr->keyword_bitmap != NULL) &&
+ (0 == (sr->keyword_bitmap[mbc->keyword_offset / 8] & (1 << (mbc->keyword_offset % 8)))) )
+ return GNUNET_OK; /* have no match for this keyword yet */
mbc->put_cnt++;
return GNUNET_OK;
}
GNUNET_HashCode key;
GNUNET_HashCode idh;
unsigned int sqms;
+ uint32_t options;
if (NULL == buf)
{
sm = buf;
sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
mbc.xoff = (GNUNET_HashCode *) & sm[1];
+ options = SEARCH_MESSAGE_OPTION_NONE;
+ if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
+ options |= SEARCH_MESSAGE_OPTION_LOOPBACK_ONLY;
if (GNUNET_FS_uri_test_ksk (sc->uri))
{
msize = sizeof (struct SearchMessage);
GNUNET_assert (size >= msize);
- mbc.uri = NULL;
+ mbc.keyword_offset = sc->keyword_offset;
mbc.put_cnt = 0;
GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
&find_result_set, &mbc);
GNUNET_assert (mbc.put_cnt > 0);
sm->header.size = htons (msize);
- if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
- sm->options = htonl (1);
- else
- sm->options = htonl (0);
sm->type = htonl (GNUNET_BLOCK_TYPE_ANY);
sm->anonymity_level = htonl (sc->anonymity);
memset (&sm->target, 0, sizeof (GNUNET_HashCode));
GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
&build_result_set, &mbc);
sm->header.size = htons (msize);
+ GNUNET_assert (sqms >= sc->search_request_map_offset);
if (sqms != sc->search_request_map_offset)
{
/* more requesting to be done... */
+ sm->options = htonl (options | SEARCH_MESSAGE_OPTION_CONTINUED);
schedule_transmit_search_request (sc);
return msize;
}
+ sm->options = htonl (options);
sc->keyword_offset++;
if (sc->uri->data.ksk.keywordCount != sc->keyword_offset)
{
GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri));
msize = sizeof (struct SearchMessage);
GNUNET_assert (size >= msize);
- if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
- sm->options = htonl (1);
- else
- sm->options = htonl (0);
sm->type = htonl (GNUNET_BLOCK_TYPE_FS_SBLOCK);
sm->anonymity_level = htonl (sc->anonymity);
sm->target = sc->uri->data.sks.namespace;
mbc.put_cnt = (size - msize) / sizeof (GNUNET_HashCode);
sqms = GNUNET_CONTAINER_multihashmap_size (sc->master_result_map);
mbc.put_cnt = GNUNET_MIN (mbc.put_cnt, sqms - mbc.skip_cnt);
- mbc.uri = NULL;
+ mbc.keyword_offset = 0;
if (sc->search_request_map_offset < sqms)
GNUNET_assert (mbc.put_cnt > 0);
msize += sizeof (GNUNET_HashCode) * mbc.put_cnt;
GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
&build_result_set, &mbc);
sm->header.size = htons (msize);
+ GNUNET_assert (sqms >= sc->search_request_map_offset);
if (sqms != sc->search_request_map_offset)
{
/* more requesting to be done... */
+ sm->options = htonl (options | SEARCH_MESSAGE_OPTION_CONTINUED);
schedule_transmit_search_request (sc);
return msize;
}
+ sm->options = htonl (options);
}
GNUNET_CLIENT_receive (sc->client, &receive_results, sc,
GNUNET_TIME_UNIT_FOREVER_REL);
GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
+ GNUNET_free_non_null (sr->keyword_bitmap);
GNUNET_free (sr);
return GNUNET_OK;
}
}
+/**
+ * Signal stop for the given search result.
+ *
+ * @param cls the global FS handle
+ * @param key the key for the search result (unused)
+ * @param value the search result to free
+ * @return GNUNET_OK
+ */
+static int
+search_result_stop (void *cls, const GNUNET_HashCode * key, void *value)
+{
+ struct GNUNET_FS_SearchContext *sc = cls;
+ struct GNUNET_FS_SearchResult *sr = value;
+ struct GNUNET_FS_ProgressInfo pi;
+
+ pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
+ pi.value.search.specifics.result_stopped.cctx = sr->client_info;
+ pi.value.search.specifics.result_stopped.meta = sr->meta;
+ pi.value.search.specifics.result_stopped.uri = sr->uri;
+ sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
+ return GNUNET_OK;
+}
+
+
/**
* Free the given search result.
*
GNUNET_FS_search_stop (sr->update_search);
GNUNET_assert (sr->update_search == NULL);
}
- pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
- pi.value.search.specifics.result_stopped.cctx = sr->client_info;
- pi.value.search.specifics.result_stopped.meta = sr->meta;
- pi.value.search.specifics.result_stopped.uri = sr->uri;
- sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
GNUNET_break (NULL == sr->client_info);
GNUNET_free_non_null (sr->serialization);
GNUNET_FS_uri_destroy (sr->uri);
GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
+ GNUNET_free_non_null (sr->keyword_bitmap);
GNUNET_free (sr);
return GNUNET_OK;
}
if (sc->top != NULL)
GNUNET_FS_end_top (sc->h, sc->top);
+ GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
+ &search_result_stop, sc);
if (sc->psearch_result != NULL)
sc->psearch_result->update_search = NULL;
- GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
- &search_result_free, sc);
if (sc->serialization != NULL)
{
GNUNET_FS_remove_sync_file_ (sc->h,
GNUNET_SCHEDULER_cancel (sc->task);
if (NULL != sc->client)
GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
+ GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
+ &search_result_free, sc);
GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
if (sc->requests != NULL)
{