2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008 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 applications/fs/ecrs/search.c
23 * @brief Helper functions for searching.
24 * @author Christian Grothoff
28 #include "gnunet_protocols.h"
29 #include "gnunet_fs_lib.h"
30 #include "gnunet_ecrs_lib.h"
31 #include "ecrs_core.h"
34 #define DEBUG_SEARCH GNUNET_NO
37 * Context for an individual search. Followed
38 * by keyCount keys of type GNUNET_HashCode.
42 struct PendingSearch *next;
44 struct GNUNET_ECRS_SearchContext *context;
47 * The key (for decryption)
49 GNUNET_HashCode decryptKey;
51 unsigned int keyCount;
54 * What type of query is it?
61 * Context for search operation.
63 struct GNUNET_ECRS_SearchContext
66 * Time when the cron-job was first started.
68 GNUNET_CronTime start;
71 * What is the global timeout?
73 GNUNET_CronTime timeout;
78 struct GNUNET_FS_SearchContext *sctx;
83 struct PendingSearch *queries;
85 GNUNET_ECRS_SearchResultProcessor spcb;
89 struct GNUNET_GE_Context *ectx;
91 struct GNUNET_GC_Configuration *cfg;
97 unsigned int anonymityLevel;
102 receive_response_callback (const GNUNET_HashCode * key,
103 const GNUNET_DatastoreValue * value,
104 void *cls, unsigned long long uid);
107 * Add a query to the SQC.
110 add_search (unsigned int type,
111 unsigned int keyCount,
112 const GNUNET_HashCode * keys,
113 const GNUNET_HashCode * dkey,
114 struct GNUNET_ECRS_SearchContext *sqc)
116 struct PendingSearch *ps;
119 GNUNET_malloc (sizeof (struct PendingSearch) +
120 sizeof (GNUNET_HashCode) * keyCount);
122 ps->keyCount = keyCount;
123 memcpy (&ps[1], keys, sizeof (GNUNET_HashCode) * keyCount);
124 ps->decryptKey = *dkey;
126 ps->next = sqc->queries;
128 GNUNET_FS_start_search (sqc->sctx,
134 &receive_response_callback, ps);
138 * Add the query that corresponds to the given URI
142 add_search_for_uri (const struct GNUNET_ECRS_URI *uri,
143 struct GNUNET_ECRS_SearchContext *sqc)
145 struct GNUNET_GE_Context *ectx = sqc->ectx;
151 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
152 _("CHK URI not allowed for search.\n"));
156 GNUNET_HashCode keys[2];
157 GNUNET_HashCode hk; /* hk = GNUNET_hash(identifier) */
158 GNUNET_HashCode hk2; /* hk2 = GNUNET_hash(hk) */
160 GNUNET_hash (uri->data.sks.identifier,
161 strlen (uri->data.sks.identifier), &hk);
162 GNUNET_hash (&hk, sizeof (GNUNET_HashCode), &hk2);
163 /* compute routing key keys[0] = H(key) ^ namespace */
164 GNUNET_hash_xor (&hk2, &uri->data.sks.namespace, &keys[0]);
165 keys[1] = uri->data.sks.namespace;
166 add_search (GNUNET_ECRS_BLOCKTYPE_SIGNED, 2, &keys[0], &hk, sqc);
172 GNUNET_HashCode query;
173 struct GNUNET_RSA_PrivateKey *pk;
174 GNUNET_RSA_PublicKey pub;
180 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
181 "Computing queries (this may take a while).\n");
183 for (i = 0; i < uri->data.ksk.keywordCount; i++)
185 keyword = uri->data.ksk.keywords[i];
186 /* first character of the keyword is
187 "+" or " " to indicate mandatory or
188 not -- ignore for hashing! */
189 GNUNET_hash (&keyword[1], strlen (&keyword[1]), &hc);
190 pk = GNUNET_RSA_create_key_from_hash (&hc);
191 GNUNET_RSA_get_public_key (pk, &pub);
192 GNUNET_hash (&pub, sizeof (GNUNET_RSA_PublicKey), &query);
193 add_search (GNUNET_ECRS_BLOCKTYPE_ANY, /* GNUNET_ECRS_BLOCKTYPE_KEYWORD, GNUNET_ECRS_BLOCKTYPE_NAMESPACE or GNUNET_ECRS_BLOCKTYPE_KEYWORD_FOR_NAMESPACE ok */
194 1, &query, &hc, sqc);
195 GNUNET_RSA_free_key (pk);
199 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
206 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
207 _("LOC URI not allowed for search.\n"));
210 GNUNET_GE_BREAK (ectx, 0);
211 /* unknown URI type */
217 * We found an GNUNET_EC_SBlock. Decode the meta-data and call
218 * the callback of the SQC with the root-URI for the namespace,
219 * together with the namespace advertisement. Also, if this is
220 * a result with updates, automatically start the search for
224 process_sblock_result (const GNUNET_EC_SBlock * sb,
225 const GNUNET_HashCode * key,
227 struct GNUNET_ECRS_SearchContext *sqc)
229 static GNUNET_HashCode allZeros;
230 struct GNUNET_GE_Context *ectx = sqc->ectx;
231 GNUNET_ECRS_FileInfo fi;
240 len = size - sizeof (GNUNET_EC_SBlock);
241 off = GNUNET_string_buffer_tokenize ((const char *) &sb[1],
245 GNUNET_GE_BREAK_OP (ectx, 0); /* sblock malformed */
246 return GNUNET_SYSERR;
248 fi.meta = GNUNET_meta_data_deserialize (ectx, &id[off], len - off);
251 GNUNET_GE_BREAK_OP (ectx, 0); /* sblock malformed */
252 return GNUNET_SYSERR;
254 isRoot = 0 == memcmp (&sb->identifier, &allZeros, sizeof (GNUNET_HashCode));
255 fi.uri = GNUNET_ECRS_string_to_uri (ectx, uris);
256 if ((isRoot) && (fi.uri == NULL))
258 fi.uri = GNUNET_malloc (sizeof (URI));
260 GNUNET_hash (&sb->subspace,
261 sizeof (GNUNET_RSA_PublicKey),
262 &fi.uri->data.sks.namespace);
263 fi.uri->data.sks.identifier = GNUNET_strdup (id);
267 GNUNET_GE_BREAK_OP (ectx, 0); /* sblock malformed */
268 GNUNET_meta_data_destroy (fi.meta);
269 return GNUNET_SYSERR;
271 if (sqc->spcb != NULL)
273 ret = sqc->spcb (&fi, key, isRoot, sqc->spcbClosure);
274 if (ret == GNUNET_SYSERR)
275 sqc->aborted = GNUNET_YES;
279 if ((strlen (id) > 0) && (strlen (uris) > 0))
281 updateURI.type = sks;
282 GNUNET_hash (&sb->subspace,
283 sizeof (GNUNET_RSA_PublicKey),
284 &updateURI.data.sks.namespace);
285 updateURI.data.sks.identifier = GNUNET_strdup (id);
286 add_search_for_uri (&updateURI, sqc);
287 GNUNET_free (updateURI.data.sks.identifier);
289 GNUNET_meta_data_destroy (fi.meta);
290 GNUNET_ECRS_uri_destroy (fi.uri);
295 * Process replies received in response to our
296 * queries. Verifies, decrypts and passes valid
297 * replies to the callback.
299 * @return GNUNET_SYSERR if the entry is malformed
302 receive_response_callback (const GNUNET_HashCode * key,
303 const GNUNET_DatastoreValue * value,
304 void *cls, unsigned long long uid)
306 struct PendingSearch *ps = cls;
307 struct GNUNET_ECRS_SearchContext *sqc = ps->context;
308 struct GNUNET_GE_Context *ectx = sqc->ectx;
310 GNUNET_ECRS_FileInfo fi;
313 GNUNET_HashCode query;
314 GNUNET_CronTime expiration;
316 expiration = GNUNET_ntohll (value->expiration_time);
317 if (expiration < GNUNET_get_time ())
318 return GNUNET_OK; /* expired, ignore! */
319 type = ntohl (value->type);
320 size = ntohl (value->size) - sizeof (GNUNET_DatastoreValue);
323 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
324 "Search received reply of type %u and size %u.\n", type,
328 GNUNET_EC_file_block_check_and_get_query (size,
329 (const GNUNET_EC_DBlock *)
330 &value[1], GNUNET_YES,
333 GNUNET_GE_BREAK_OP (NULL, 0);
334 return GNUNET_SYSERR;
336 if (!((0 == memcmp (&query,
337 (GNUNET_HashCode *) & ps[1], sizeof (GNUNET_HashCode)))
338 && ((ps->type == type) || (ps->type == GNUNET_ECRS_BLOCKTYPE_ANY))
340 GNUNET_EC_is_block_applicable_for_query (type, size,
341 (const GNUNET_EC_DBlock
342 *) &value[1], &query,
344 (GNUNET_HashCode *) &
347 return GNUNET_OK; /* not a match */
352 case GNUNET_ECRS_BLOCKTYPE_KEYWORD:
354 GNUNET_EC_KBlock *kb;
361 if (size < sizeof (GNUNET_EC_KBlock))
363 GNUNET_GE_BREAK_OP (NULL, 0);
364 return GNUNET_SYSERR;
366 kb = GNUNET_malloc (size);
367 memcpy (kb, &value[1], size);
370 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
371 GNUNET_GE_USER, GNUNET_hash_to_enc (&ps->decryptKey, &enc));
373 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
375 "Decrypting KBlock with key %s.\n", &enc);
377 GNUNET_ECRS_decryptInPlace (&ps->decryptKey,
378 &kb[1], size - sizeof (GNUNET_EC_KBlock));
379 j = sizeof (GNUNET_EC_KBlock);
380 while ((j < size) && (((const char *) kb)[j] != '\0'))
384 GNUNET_GE_BREAK_OP (ectx, 0); /* kblock malformed */
386 return GNUNET_SYSERR;
388 dstURI = (const char *) &kb[1];
390 fi.meta = GNUNET_meta_data_deserialize (ectx,
395 GNUNET_GE_BREAK_OP (ectx, 0); /* kblock malformed */
397 return GNUNET_SYSERR;
399 fi.uri = GNUNET_ECRS_string_to_uri (ectx, dstURI);
402 GNUNET_GE_BREAK_OP (ectx, 0); /* kblock malformed */
403 GNUNET_meta_data_destroy (fi.meta);
405 return GNUNET_SYSERR;
407 if (sqc->spcb != NULL)
409 ret = sqc->spcb (&fi,
410 &ps->decryptKey, GNUNET_NO, sqc->spcbClosure);
411 if (ret == GNUNET_SYSERR)
412 sqc->aborted = GNUNET_YES;
416 GNUNET_ECRS_uri_destroy (fi.uri);
417 GNUNET_meta_data_destroy (fi.meta);
421 case GNUNET_ECRS_BLOCKTYPE_SIGNED:
423 GNUNET_EC_SBlock *sb;
426 if (size < sizeof (GNUNET_EC_SBlock))
428 GNUNET_GE_BREAK_OP (ectx, 0); /* sblock malformed */
429 return GNUNET_SYSERR;
431 sb = GNUNET_malloc (size);
432 memcpy (sb, &value[1], size);
433 GNUNET_ECRS_decryptInPlace (&ps->decryptKey,
434 &sb[1], size - sizeof (GNUNET_EC_SBlock));
435 ret = process_sblock_result (sb, &ps->decryptKey, size, sqc);
439 case GNUNET_ECRS_BLOCKTYPE_KEYWORD_SIGNED:
441 GNUNET_EC_KSBlock *kb;
444 if (size < sizeof (GNUNET_EC_KSBlock))
446 GNUNET_GE_BREAK_OP (ectx, 0); /* ksblock malformed */
447 return GNUNET_SYSERR;
449 kb = GNUNET_malloc (size);
450 memcpy (kb, &value[1], size);
451 GNUNET_ECRS_decryptInPlace (&ps->decryptKey,
453 size - sizeof (GNUNET_EC_KBlock) -
454 sizeof (unsigned int));
456 process_sblock_result (&kb->sblock, &ps->decryptKey,
457 size - sizeof (GNUNET_EC_KSBlock) +
458 sizeof (GNUNET_EC_SBlock), sqc);
463 GNUNET_GE_BREAK_OP (ectx, 0);
470 * Start search for content.
472 * @param uri specifies the search parameters
473 * @param uri set to the URI of the uploaded file
475 struct GNUNET_ECRS_SearchContext *
476 GNUNET_ECRS_search_start (struct GNUNET_GE_Context *ectx,
477 struct GNUNET_GC_Configuration *cfg,
478 struct GNUNET_FS_SearchContext *sc,
479 const struct GNUNET_ECRS_URI *uri,
480 unsigned int anonymityLevel,
481 GNUNET_ECRS_SearchResultProcessor spcb,
484 struct GNUNET_ECRS_SearchContext *ctx;
486 if (GNUNET_YES == GNUNET_ECRS_uri_test_ksk (uri))
488 if (1 != GNUNET_ECRS_uri_get_keyword_count_from_ksk (uri))
493 if (GNUNET_YES != GNUNET_ECRS_uri_test_sks (uri))
496 ctx = GNUNET_malloc (sizeof (struct GNUNET_ECRS_SearchContext));
497 ctx->start = GNUNET_get_time ();
498 ctx->anonymityLevel = anonymityLevel;
503 ctx->spcbClosure = spcbClosure;
504 ctx->aborted = GNUNET_NO;
505 ctx->sctx = sc == NULL ? GNUNET_FS_create_search_context (ectx, cfg) : sc;
506 if (ctx->sctx == NULL)
511 ctx->my_sctx = (sc == NULL);
512 add_search_for_uri (uri, ctx);
517 * Stop search for content.
519 * @param uri specifies the search parameters
520 * @param uri set to the URI of the uploaded file
523 GNUNET_ECRS_search_stop (struct GNUNET_ECRS_SearchContext *ctx)
525 struct PendingSearch *pos;
527 while (ctx->queries != NULL)
530 ctx->queries = pos->next;
532 GNUNET_FS_stop_search (ctx->sctx, &receive_response_callback, pos);
536 GNUNET_FS_destroy_search_context (ctx->sctx);
541 * Search for content.
543 * @param timeout how long to wait (relative)
544 * @param uri specifies the search parameters
545 * @param uri set to the URI of the uploaded file
548 GNUNET_ECRS_search (struct GNUNET_GE_Context *ectx,
549 struct GNUNET_GC_Configuration *cfg,
550 const struct GNUNET_ECRS_URI *uri,
551 unsigned int anonymityLevel,
552 GNUNET_ECRS_SearchResultProcessor spcb,
553 void *spcbClosure, GNUNET_ECRS_TestTerminate tt,
556 struct GNUNET_ECRS_SearchContext *ctx;
559 GNUNET_ECRS_search_start (ectx, cfg, NULL,
560 uri, anonymityLevel, spcb, spcbClosure);
562 return GNUNET_SYSERR;
563 while (((NULL == tt) || (GNUNET_OK == tt (ttClosure)))
564 && (GNUNET_NO == GNUNET_shutdown_test ())
565 && (ctx->aborted == GNUNET_NO))
566 GNUNET_thread_sleep (100 * GNUNET_CRON_MILLISECONDS);
567 GNUNET_ECRS_search_stop (ctx);
572 /* end of search.c */