2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009 Christian Grothoff (and other contributing authors)
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 2, or (at your
8 option) any later version.
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.
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.
22 * @file fs/fs_search.c
23 * @brief Helper functions for searching.
24 * @author Christian Grothoff
27 * - aggregate and process results (FSUI-style)
28 * - call progress callbacks
29 * - make operations persistent (can wait)
30 * - add support for pushing "already seen" information
31 * to FS service for bloomfilter (can wait)
35 #include "gnunet_constants.h"
36 #include "gnunet_fs_service.h"
37 #include "gnunet_protocols.h"
40 #define DEBUG_SEARCH GNUNET_YES
44 * We have received a KSK result. Check
45 * how it fits in with the overall query
46 * and notify the client accordingly.
48 * @param sc context for the overall query
49 * @param ent entry for the specific keyword
50 * @param uri the URI that was found
51 * @param meta metadata associated with the URI
52 * under the "ent" keyword
55 process_ksk_result (struct GNUNET_FS_SearchContext *sc,
56 struct SearchRequestEntry *ent,
57 const struct GNUNET_FS_Uri *uri,
58 const struct GNUNET_CONTAINER_MetaData *meta)
60 // FIXME: check if new
61 // FIXME: check if mandatory satisfied
62 // FIXME: notify client!
67 * We have received an SKS result. Start
68 * searching for updates and notify the
69 * client if it is a new result.
71 * @param sc context for the overall query
72 * @param id_update identifier for updates, NULL for none
73 * @param uri the URI that was found
74 * @param meta metadata associated with the URI
77 process_sks_result (struct GNUNET_FS_SearchContext *sc,
78 const char *id_update,
79 const struct GNUNET_FS_Uri *uri,
80 const struct GNUNET_CONTAINER_MetaData *meta)
82 // FIXME: check if new
83 // FIXME: notify client
85 if (strlen (id_update) > 0)
87 // FIXME: search for updates!
90 GNUNET_hash (&sb->subspace,
91 sizeof (GNUNET_RSA_PublicKey),
92 &updateURI.data.sks.namespace);
93 updateURI.data.sks.identifier = GNUNET_strdup (id);
94 add_search_for_uri (&updateURI, sqc);
101 * Process a keyword-search result.
103 * @param sc our search context
104 * @param kb the kblock
105 * @param size size of kb
108 process_kblock (struct GNUNET_FS_SearchContext *sc,
109 const struct KBlock *kb,
115 char pt[size - sizeof (struct KBlock)];
116 struct GNUNET_CRYPTO_AesSessionKey skey;
117 struct GNUNET_CRYPTO_AesInitializationVector iv;
119 struct GNUNET_CONTAINER_MetaData *meta;
120 struct GNUNET_FS_Uri *uri;
123 GNUNET_CRYPTO_hash (&kb->keyspace,
124 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
127 for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
129 &sc->requests[i].query,
130 sizeof (GNUNET_HashCode)))
132 if (i == sc->uri->data.ksk.keywordCount)
134 /* oops, does not match any of our keywords!? */
139 GNUNET_CRYPTO_hash_to_aes_key (&sc->requests[i].key, &skey, &iv);
140 GNUNET_CRYPTO_aes_encrypt (&kb[1],
141 size - sizeof (struct KBlock),
146 eos = memchr (pt, 0, sizeof (pt));
153 meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j],
157 GNUNET_break_op (0); /* kblock malformed */
160 uri = GNUNET_FS_uri_parse (pt, &emsg);
163 GNUNET_break_op (0); /* kblock malformed */
164 GNUNET_free_non_null (emsg);
165 GNUNET_CONTAINER_meta_data_destroy (meta);
169 process_ksk_result (sc, &sc->requests[i], uri, meta);
172 GNUNET_CONTAINER_meta_data_destroy (meta);
173 GNUNET_FS_uri_destroy (uri);
178 * Process a namespace-search result.
180 * @param sc our search context
181 * @param sb the sblock
182 * @param size size of sb
185 process_sblock (struct GNUNET_FS_SearchContext *sc,
186 const struct SBlock *sb,
189 size_t len = size - sizeof (struct SBlock);
191 struct GNUNET_CRYPTO_AesSessionKey skey;
192 struct GNUNET_CRYPTO_AesInitializationVector iv;
193 struct GNUNET_FS_Uri *uri;
194 struct GNUNET_CONTAINER_MetaData *meta;
203 identifier = sc->uri->data.sks.identifier;
204 GNUNET_CRYPTO_hash (identifier,
207 GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
208 GNUNET_CRYPTO_aes_encrypt (&sb[1],
214 off = GNUNET_STRINGS_buffer_tokenize (pt,
221 GNUNET_break_op (0); /* sblock malformed */
224 meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[off],
228 GNUNET_break_op (0); /* sblock malformed */
231 uri = GNUNET_FS_uri_parse (uris, &emsg);
234 GNUNET_break_op (0); /* sblock malformed */
235 GNUNET_free_non_null (emsg);
236 GNUNET_CONTAINER_meta_data_destroy (meta);
240 process_sks_result (sc, id, uri, meta);
242 GNUNET_FS_uri_destroy (uri);
243 GNUNET_CONTAINER_meta_data_destroy (meta);
248 * Process a search result.
250 * @param sc our search context
251 * @param type type of the result
252 * @param expiration when it will expire
253 * @param data the (encrypted) response
254 * @param size size of data
257 process_result (struct GNUNET_FS_SearchContext *sc,
259 struct GNUNET_TIME_Absolute expiration,
263 if (GNUNET_TIME_absolute_get_duration (expiration).value > 0)
264 return; /* result expired */
267 case GNUNET_DATASTORE_BLOCKTYPE_KBLOCK:
268 if (! GNUNET_FS_uri_test_ksk (sc->uri))
273 if (sizeof (struct KBlock) > size)
278 process_kblock (sc, data, size);
280 case GNUNET_DATASTORE_BLOCKTYPE_SBLOCK:
281 if (! GNUNET_FS_uri_test_ksk (sc->uri))
286 if (sizeof (struct SBlock) > size)
291 process_sblock (sc, data, size);
293 case GNUNET_DATASTORE_BLOCKTYPE_ANY:
294 case GNUNET_DATASTORE_BLOCKTYPE_DBLOCK:
295 case GNUNET_DATASTORE_BLOCKTYPE_ONDEMAND:
296 case GNUNET_DATASTORE_BLOCKTYPE_IBLOCK:
300 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
301 _("Got result with unknown block type `%d', ignoring"),
309 * Shutdown any existing connection to the FS
310 * service and try to establish a fresh one
311 * (and then re-transmit our search request).
313 * @param sc the search to reconnec
316 try_reconnect (struct GNUNET_FS_SearchContext *sc);
320 * Type of a function to call when we receive a message
324 * @param msg message received, NULL on timeout or fatal error
327 receive_results (void *cls,
328 const struct GNUNET_MessageHeader * msg)
330 struct GNUNET_FS_SearchContext *sc = cls;
331 const struct ContentMessage *cm;
334 if ( (NULL == msg) ||
335 (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_CONTENT) ||
336 (ntohs (msg->size) <= sizeof (struct ContentMessage)) )
341 msize = ntohs (msg->size);
342 cm = (const struct ContentMessage*) msg;
345 GNUNET_TIME_absolute_ntoh (cm->expiration),
347 msize - sizeof (struct ContentMessage));
348 /* continue receiving */
349 GNUNET_CLIENT_receive (sc->client,
352 GNUNET_TIME_UNIT_FOREVER_REL);
357 * We're ready to transmit the search request to the
358 * file-sharing service. Do it.
361 * @param size number of bytes available in buf
362 * @param buf where the callee should write the message
363 * @return number of bytes written to buf
366 transmit_search_request (void *cls,
370 struct GNUNET_FS_SearchContext *sc = cls;
372 struct SearchMessage *sm;
375 const char *identifier;
378 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
379 struct GNUNET_CRYPTO_RsaPrivateKey *pk;
386 if (GNUNET_FS_uri_test_ksk (sc->uri))
388 msize = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount;
389 GNUNET_assert (size >= msize);
391 memset (sm, 0, msize);
392 sc->requests = GNUNET_malloc (sizeof (struct SearchRequestEntry) *
393 sc->uri->data.ksk.keywordCount);
394 for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
396 sm[i].header.size = htons (sizeof (struct SearchMessage));
397 sm[i].header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
398 sm[i].anonymity_level = htonl (sc->anonymity);
399 keyword = &sc->uri->data.ksk.keywords[i][1];
401 GNUNET_CRYPTO_hash (keyword, strlen (keyword), &hc);
402 pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&hc);
403 GNUNET_CRYPTO_rsa_key_get_public (pk, &pub);
404 GNUNET_CRYPTO_rsa_key_free (pk);
405 GNUNET_CRYPTO_hash (&pub,
406 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
408 sc->requests[i].query = sm[i].query;
409 GNUNET_CRYPTO_hash (keyword,
411 &sc->requests[i].key);
416 msize = sizeof (struct SearchMessage);
417 GNUNET_assert (size >= msize);
419 memset (sm, 0, msize);
420 sm->header.size = htons (sizeof (struct SearchMessage));
421 sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
422 sm->anonymity_level = htonl (sc->anonymity);
423 sm->target = sc->uri->data.sks.namespace;
424 identifier = sc->uri->data.sks.identifier;
425 GNUNET_CRYPTO_hash (identifier,
428 GNUNET_CRYPTO_hash_xor (&idh,
432 GNUNET_CLIENT_receive (sc->client,
435 GNUNET_TIME_UNIT_FOREVER_REL);
441 * Reconnect to the FS service and transmit
444 * @param cls our search context
448 do_reconnect (void *cls,
449 const struct GNUNET_SCHEDULER_TaskContext *tc)
451 struct GNUNET_FS_SearchContext *sc = cls;
452 struct GNUNET_CLIENT_Connection *client;
455 sc->task = GNUNET_SCHEDULER_NO_TASK;
456 client = GNUNET_CLIENT_connect (sc->h->sched,
465 if (GNUNET_FS_uri_test_ksk (sc->uri))
466 size = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount;
468 size = sizeof (struct SearchMessage);
469 GNUNET_CLIENT_notify_transmit_ready (client,
471 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
472 &transmit_search_request,
478 * Shutdown any existing connection to the FS
479 * service and try to establish a fresh one
480 * (and then re-transmit our search request).
482 * @param sc the search to reconnec
485 try_reconnect (struct GNUNET_FS_SearchContext *sc)
487 if (NULL != sc->client)
489 GNUNET_CLIENT_disconnect (sc->client);
493 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
495 GNUNET_SCHEDULER_PRIORITY_IDLE,
496 GNUNET_SCHEDULER_NO_TASK,
497 GNUNET_TIME_UNIT_SECONDS,
504 * Start search for content.
506 * @param h handle to the file sharing subsystem
507 * @param uri specifies the search parameters; can be
508 * a KSK URI or an SKS URI.
509 * @param anonymity desired level of anonymity
510 * @return context that can be used to control the search
512 struct GNUNET_FS_SearchContext *
513 GNUNET_FS_search_start (struct GNUNET_FS_Handle *h,
514 const struct GNUNET_FS_Uri *uri,
515 unsigned int anonymity)
517 struct GNUNET_FS_SearchContext *sc;
518 struct GNUNET_CLIENT_Connection *client;
521 if (GNUNET_FS_uri_test_ksk (uri))
523 size = sizeof (struct SearchMessage) * uri->data.ksk.keywordCount;
527 GNUNET_assert (GNUNET_FS_uri_test_sks (uri));
528 size = sizeof (struct SearchMessage);
530 if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
532 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
533 _("Too many keywords specified for a single search."));
536 client = GNUNET_CLIENT_connect (sc->h->sched,
541 sc = GNUNET_malloc (sizeof(struct GNUNET_FS_SearchContext));
543 sc->uri = GNUNET_FS_uri_dup (uri);
544 sc->anonymity = anonymity;
545 sc->start_time = GNUNET_TIME_absolute_get ();
547 // FIXME: call callback!
548 GNUNET_CLIENT_notify_transmit_ready (client,
550 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
551 &transmit_search_request,
560 * @param sc context for the search that should be paused
563 GNUNET_FS_search_pause (struct GNUNET_FS_SearchContext *sc)
565 if (sc->task != GNUNET_SCHEDULER_NO_TASK)
566 GNUNET_SCHEDULER_cancel (sc->h->sched,
568 sc->task = GNUNET_SCHEDULER_NO_TASK;
569 if (NULL != sc->client)
570 GNUNET_CLIENT_disconnect (sc->client);
572 // FIXME: make persistent!
573 // FIXME: call callback!
578 * Continue paused search.
580 * @param sc context for the search that should be resumed
583 GNUNET_FS_search_continue (struct GNUNET_FS_SearchContext *sc)
585 GNUNET_assert (sc->client == NULL);
586 GNUNET_assert (sc->task == GNUNET_SCHEDULER_NO_TASK);
587 do_reconnect (sc, NULL);
588 // FIXME: make persistent!
589 // FIXME: call callback!
594 * Stop search for content.
596 * @param sc context for the search that should be stopped
599 GNUNET_FS_search_stop (struct GNUNET_FS_SearchContext *sc)
601 // FIXME: make un-persistent!
602 // FIXME: call callback!
603 if (sc->task != GNUNET_SCHEDULER_NO_TASK)
604 GNUNET_SCHEDULER_cancel (sc->h->sched,
606 if (NULL != sc->client)
607 GNUNET_CLIENT_disconnect (sc->client);
608 GNUNET_free_non_null (sc->requests);
609 GNUNET_FS_uri_destroy (sc->uri);
613 /* end of fs_search.c */