2 This file is part of GNUnet.
3 (C) 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/gnunet-service-fs.c
23 * @brief program that provides the file-sharing service
24 * @author Christian Grothoff
27 * - INDEX_START handling
28 * - INDEX_LIST handling
30 * - bloomfilter support (GET, CS-request with BF, etc.)
34 #include "gnunet_protocols.h"
35 #include "gnunet_core_service.h"
36 #include "gnunet_datastore_service.h"
37 #include "gnunet_util_lib.h"
41 * Our connection to the datastore.
43 static struct GNUNET_DATASTORE_Handle *dsh;
48 static struct GNUNET_SCHEDULER_Handle *sched;
53 const struct GNUNET_CONFIGURATION_Handle *cfg;
56 * Handle INDEX_START-message.
59 * @param client identification of the client
60 * @param message the actual message
63 handle_index_start (void *cls,
64 struct GNUNET_SERVER_Client *client,
65 const struct GNUNET_MessageHeader *message)
67 const struct IndexStartMessage *ism;
71 msize = ntohs(message->size);
72 if ( (msize <= sizeof (struct IndexStartMessage)) ||
73 ( ((const char *)message)[msize-1] != '\0') )
76 GNUNET_SERVER_receive_done (client,
80 ism = (const struct IndexStartMessage*) message;
81 fn = (const char*) &ism[1];
82 // FIXME: store fn, hash, check, respond to client, etc.
87 * Handle INDEX_LIST_GET-message.
90 * @param client identification of the client
91 * @param message the actual message
94 handle_index_list_get (void *cls,
95 struct GNUNET_SERVER_Client *client,
96 const struct GNUNET_MessageHeader *message)
98 struct GNUNET_SERVER_TransmitContext *tc;
99 struct IndexInfoMessage *iim;
100 char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
103 struct GNUNET_MessageHeader *msg;
105 tc = GNUNET_SERVER_transmit_context_create (client);
106 iim = (struct IndexInfoMessage*) buf;
111 // FIXME: read actual list of indexed files...
112 // iim->file_id = id;
114 slen = strlen (fn) + 1;
115 if (slen + sizeof (struct IndexInfoMessage) >
116 GNUNET_SERVER_MAX_MESSAGE_SIZE)
121 memcpy (&iim[1], fn, slen);
122 GNUNET_SERVER_transmit_context_append
125 sizeof (struct IndexInfoMessage)
126 - sizeof (struct GNUNET_MessageHeader) + slen,
127 GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_ENTRY);
129 GNUNET_SERVER_transmit_context_append (tc,
131 GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_END);
132 GNUNET_SERVER_transmit_context_run (tc,
133 GNUNET_TIME_UNIT_MINUTES);
138 * Handle UNINDEX-message.
141 * @param client identification of the client
142 * @param message the actual message
145 handle_unindex (void *cls,
146 struct GNUNET_SERVER_Client *client,
147 const struct GNUNET_MessageHeader *message)
149 const struct UnindexMessage *um;
150 struct GNUNET_SERVER_TransmitContext *tc;
152 um = (const struct UnindexMessage*) message;
154 tc = GNUNET_SERVER_transmit_context_create (client);
155 GNUNET_SERVER_transmit_context_append (tc,
157 GNUNET_MESSAGE_TYPE_FS_UNINDEX_OK);
158 GNUNET_SERVER_transmit_context_run (tc,
159 GNUNET_TIME_UNIT_MINUTES);
164 * Signature of a function that is called whenever a datastore
165 * request can be processed (or an entry put on the queue times out).
168 * @param ok GNUNET_OK if DS is ready, GNUNET_SYSERR on timeout
170 typedef void (*RequestFunction)(void *cls,
175 * Doubly-linked list of our requests for the datastore.
177 struct DatastoreRequestQueue
181 * This is a doubly-linked list.
183 struct DatastoreRequestQueue *next;
186 * This is a doubly-linked list.
188 struct DatastoreRequestQueue *prev;
191 * Function to call (will issue the request).
201 * When should this request time-out because we don't care anymore?
203 struct GNUNET_TIME_Absolute timeout;
206 * ID of task used for signaling timeout.
208 GNUNET_SCHEDULER_TaskIdentifier task;
214 * Head of request queue for the datastore, sorted by timeout.
216 static struct DatastoreRequestQueue *drq_head;
219 * Tail of request queue for the datastore.
221 static struct DatastoreRequestQueue *drq_tail;
225 * Run the next DS request in our
226 * queue, we're done with the current one.
231 struct DatastoreRequestQueue *e;
233 while (NULL != (e = drq_head))
235 if (0 != GNUNET_TIME_absolute_get_remaining (e->timeout).value)
237 if (e->task != GNUNET_SCHEDULER_NO_TASK)
238 GNUNET_SCHEDULER_cancel (sched, e->task);
239 GNUNET_CONTAINER_DLL_remove (drq_head, drq_tail, e);
240 e->req (e->req_cls, GNUNET_NO);
245 if (e->task != GNUNET_SCHEDULER_NO_TASK)
246 GNUNET_SCHEDULER_cancel (sched, e->task);
247 e->task = GNUNET_SCHEDULER_NO_TASK;
248 e->req (e->req_cls, GNUNET_YES);
249 GNUNET_CONTAINER_DLL_remove (drq_head, drq_tail, e);
255 * A datastore request had to be timed out.
257 * @param cls closure (of type "struct DatastoreRequestQueue*")
258 * @param tc task context, unused
261 timeout_ds_request (void *cls,
262 const struct GNUNET_SCHEDULER_TaskContext *tc)
264 struct DatastoreRequestQueue *e = cls;
266 e->task = GNUNET_SCHEDULER_NO_TASK;
267 GNUNET_CONTAINER_DLL_remove (drq_head, drq_tail, e);
268 e->req (e->req_cls, GNUNET_NO);
274 * Queue a request for the datastore.
276 * @param deadline by when the request should run
277 * @param fun function to call once the request can be run
278 * @param fun_cls closure for fun
280 static struct DatastoreRequestQueue *
281 queue_ds_request (struct GNUNET_TIME_Relative deadline,
285 struct DatastoreRequestQueue *e;
286 struct DatastoreRequestQueue *bef;
288 if (drq_head == NULL)
290 /* no other requests pending, run immediately */
291 fun (fun_cls, GNUNET_OK);
294 e = GNUNET_malloc (sizeof (struct DatastoreRequestQueue));
295 e->timeout = GNUNET_TIME_relative_to_absolute (deadline);
297 e->req_cls = fun_cls;
298 if (deadline.value == GNUNET_TIME_UNIT_FOREVER_REL.value)
300 /* local request, highest prio, put at head of queue
301 regardless of deadline */
307 while ( (NULL != bef) &&
308 (e->timeout.value < bef->timeout.value) )
311 GNUNET_CONTAINER_DLL_insert_after (drq_head, drq_tail, bef, e);
312 if (deadline.value == GNUNET_TIME_UNIT_FOREVER_REL.value)
314 e->task = GNUNET_SCHEDULER_add_delayed (sched,
316 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
317 GNUNET_SCHEDULER_NO_TASK,
326 * Closure for processing START_SEARCH messages from a client.
328 struct LocalGetContext
332 * This is a doubly-linked list.
334 struct LocalGetContext *next;
337 * This is a doubly-linked list.
339 struct LocalGetContext *prev;
342 * Client that initiated the search.
344 struct GNUNET_SERVER_Client *client;
347 * Array of results that we've already received
350 GNUNET_HashCode *results;
353 * Bloomfilter over all results (for fast query construction);
354 * NULL if we don't have any results.
356 struct GNUNET_CONTAINER_BloomFilter *results_bf;
359 * DS request associated with this operation.
361 struct DatastoreRequestQueue *req;
364 * Current result message to transmit to client (or NULL).
366 struct ContentMessage *result;
369 * Type of the content that we're looking for.
375 * Desired anonymity level.
377 uint32_t anonymity_level;
380 * Number of results actually stored in the results array.
382 unsigned int results_used;
385 * Size of the results array in memory.
387 unsigned int results_size;
390 * If the request is for a DBLOCK or IBLOCK, this is the identity of
391 * the peer that is known to have a response. Set to all-zeros if
392 * such a target is not known (note that even if OUR anonymity
393 * level is >0 we may happen to know the responder's identity;
394 * nevertheless, we should probably not use it for a DHT-lookup
395 * or similar blunt actions in order to avoid exposing ourselves).
397 * If the request is for an SBLOCK, this is the identity of the
398 * pseudonym to which the SBLOCK belongs.
400 * If the request is for a KBLOCK, "target" must be all zeros.
402 GNUNET_HashCode target;
405 * Hash of the keyword (aka query) for KBLOCKs; Hash of
406 * the CHK-encoded block for DBLOCKS and IBLOCKS (aka query)
407 * and hash of the identifier XORed with the target for
408 * SBLOCKS (aka query).
410 GNUNET_HashCode query;
416 * Head of doubly-linked LGC list.
418 static struct LocalGetContext *lgc_head;
421 * Tail of doubly-linked LGC list.
423 static struct LocalGetContext *lgc_tail;
427 * Free the state associated with a local get context.
429 * @param lgc the lgc to free
432 local_get_context_free (struct LocalGetContext *lgc)
434 GNUNET_CONTAINER_DLL_remove (lgc_head, lgc_tail, lgc);
435 GNUNET_SERVER_client_drop (lgc->client);
436 GNUNET_free_non_null (lgc->results);
437 if (lgc->results_bf != NULL)
438 GNUNET_CONTAINER_bloomfilter_free (lgc->results_bf);
439 if (lgc->req != NULL)
441 if (lgc->req->task != GNUNET_SCHEDULER_NO_TASK)
442 GNUNET_SCHEDULER_cancel (sched, lgc->req->task);
443 GNUNET_CONTAINER_DLL_remove (lgc_head, lgc_tail, lgc);
444 GNUNET_free (lgc->req);
451 * We're able to transmit the next (local) result to the client.
452 * Do it and ask the datastore for more. Or, on error, tell
453 * the datastore to stop giving us more.
455 * @param cls our closure (struct LocalGetContext)
456 * @param max maximum number of bytes we can transmit
457 * @param buf where to copy our message
458 * @return number of bytes copied to buf
461 transmit_local_result (void *cls,
465 struct LocalGetContext *lgc = cls;
471 GNUNET_free (lgc->result);
473 GNUNET_DATASTORE_get_next (dsh, GNUNET_NO);
476 msize = ntohs (lgc->result->header.size);
477 GNUNET_assert (max >= msize);
478 memcpy (buf, lgc->result, msize);
479 GNUNET_free (lgc->result);
481 GNUNET_DATASTORE_get_next (dsh, GNUNET_YES);
487 * We're processing (local) results for a search request
488 * from a (local) client. Pass applicable results to the
489 * client and if we are done either clean up (operation
490 * complete) or switch to P2P search (more results possible).
492 * @param cls our closure (struct LocalGetContext)
493 * @param key key for the content
494 * @param size number of bytes in data
495 * @param data content stored
496 * @param type type of the content
497 * @param priority priority of the content
498 * @param anonymity anonymity-level for the content
499 * @param expiration expiration time for the content
500 * @param uid unique identifier for the datum;
501 * maybe 0 if no unique identifier is available
504 process_local_get_result (void *cls,
505 const GNUNET_HashCode * key,
511 struct GNUNET_TIME_Absolute
515 struct LocalGetContext *lgc = cls;
520 /* no further results from datastore; continue
521 processing further requests from the client and
522 allow the next task to use the datastore; also,
523 switch to P2P requests or clean up our state. */
525 GNUNET_SERVER_receive_done (lgc->client,
527 if ( (lgc->results_used == 0) ||
528 (lgc->type == GNUNET_DATASTORE_BLOCKTYPE_KBLOCK) ||
529 (lgc->type == GNUNET_DATASTORE_BLOCKTYPE_SBLOCK) ||
530 (lgc->type == GNUNET_DATASTORE_BLOCKTYPE_SKBLOCK) )
532 // FIXME: initiate P2P search
535 /* got all possible results, clean up! */
536 local_get_context_free (lgc);
539 if (lgc->results_used == lgc->results_size)
541 GNUNET_array_grow (lgc->results,
543 lgc->results_size * 2 + 2);
544 if ( (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_DBLOCK) ||
545 (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_IBLOCK) )
547 // FIXME: possibly grow/create BF!
550 GNUNET_CRYPTO_hash (data,
552 &lgc->results[lgc->results_used++]);
553 if ( (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_DBLOCK) ||
554 (lgc->type != GNUNET_DATASTORE_BLOCKTYPE_IBLOCK) )
556 // FIXME: add result to BF!
558 msize = size + sizeof (struct ContentMessage);
559 GNUNET_assert (msize < GNUNET_SERVER_MAX_MESSAGE_SIZE);
560 lgc->result = GNUNET_malloc (msize);
561 lgc->result->header.size = htons (msize);
562 lgc->result->header.type = htons (GNUNET_MESSAGE_TYPE_FS_CONTENT);
563 lgc->result->type = htonl (type);
564 lgc->result->expiration = GNUNET_TIME_absolute_hton (expiration);
565 memcpy (&lgc->result[1],
568 GNUNET_SERVER_notify_transmit_ready (lgc->client,
570 GNUNET_TIME_UNIT_FOREVER_REL,
571 &transmit_local_result,
577 * We're processing a search request from a local
578 * client. Now it is our turn to query the datastore.
580 * @param cls our closure (struct LocalGetContext)
584 transmit_local_get (void *cls,
585 const struct GNUNET_SCHEDULER_TaskContext *tc)
587 struct LocalGetContext *lgc = cls;
589 GNUNET_DATASTORE_get (dsh,
592 &process_local_get_result,
594 GNUNET_TIME_UNIT_FOREVER_REL);
599 * We're processing a search request from a local
600 * client. Now it is our turn to query the datastore.
602 * @param cls our closure (struct LocalGetContext)
603 * @param ok did we succeed to queue for datastore access, should always be GNUNET_OK
606 transmit_local_get_ready (void *cls,
609 struct LocalGetContext *lgc = cls;
611 GNUNET_assert (GNUNET_OK == ok);
612 GNUNET_SCHEDULER_add_continuation (sched,
616 GNUNET_SCHEDULER_REASON_PREREQ_DONE);
621 * Handle START_SEARCH-message (search request from client).
624 * @param client identification of the client
625 * @param message the actual message
628 handle_start_search (void *cls,
629 struct GNUNET_SERVER_Client *client,
630 const struct GNUNET_MessageHeader *message)
632 const struct SearchMessage *sm;
633 struct LocalGetContext *lgc;
635 sm = (const struct SearchMessage*) message;
636 GNUNET_SERVER_client_keep (client);
637 lgc = GNUNET_malloc (sizeof (struct LocalGetContext));
638 lgc->client = client;
639 lgc->type = ntohl (sm->type);
640 lgc->anonymity_level = ntohl (sm->anonymity_level);
641 lgc->target = sm->target;
642 lgc->query = sm->query;
643 GNUNET_CONTAINER_DLL_insert (lgc_head, lgc_tail, lgc);
644 lgc->req = queue_ds_request (GNUNET_TIME_UNIT_FOREVER_REL,
645 &transmit_local_get_ready,
651 * List of handlers for the messages understood by this
654 static struct GNUNET_SERVER_MessageHandler handlers[] = {
655 {&handle_index_start, NULL,
656 GNUNET_MESSAGE_TYPE_FS_INDEX_START, 0},
657 {&handle_index_list_get, NULL,
658 GNUNET_MESSAGE_TYPE_FS_INDEX_LIST_GET, sizeof(struct GNUNET_MessageHeader) },
659 {&handle_unindex, NULL, GNUNET_MESSAGE_TYPE_FS_UNINDEX,
660 sizeof (struct UnindexMessage) },
661 {&handle_start_search, NULL, GNUNET_MESSAGE_TYPE_FS_START_SEARCH,
662 sizeof (struct SearchMessage) },
668 * A client disconnected. Remove all of its pending queries.
670 * @param cls closure, NULL
671 * @param client identification of the client
674 handle_client_disconnect (void *cls,
675 struct GNUNET_SERVER_Client
678 struct LocalGetContext *lgc;
681 while ( (NULL != lgc) &&
682 (lgc->client != client) )
685 return; /* not one of our clients */
686 local_get_context_free (lgc);
691 * Task run during shutdown.
697 shutdown_task (void *cls,
698 const struct GNUNET_SCHEDULER_TaskContext *tc)
700 GNUNET_DATASTORE_disconnect (dsh,
707 * Process fs requests.
710 * @param sched scheduler to use
711 * @param server the initialized server
712 * @param cfg configuration to use
716 struct GNUNET_SCHEDULER_Handle *s,
717 struct GNUNET_SERVER_Handle *server,
718 const struct GNUNET_CONFIGURATION_Handle *c)
722 dsh = GNUNET_DATASTORE_connect (cfg,
726 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
727 _("Failed to connect to datastore service.\n"));
730 GNUNET_SERVER_disconnect_notify (server,
731 &handle_client_disconnect,
733 GNUNET_SERVER_add_handlers (server, handlers);
734 // FIXME: also register with core to handle P2P messages!
735 GNUNET_SCHEDULER_add_delayed (sched,
737 GNUNET_SCHEDULER_PRIORITY_IDLE,
738 GNUNET_SCHEDULER_NO_TASK,
739 GNUNET_TIME_UNIT_FOREVER_REL,
746 * The main function for the fs service.
748 * @param argc number of arguments from the command line
749 * @param argv command line arguments
750 * @return 0 ok, 1 on error
753 main (int argc, char *const *argv)
756 GNUNET_SERVICE_run (argc,
758 "fs", &run, NULL, NULL, NULL)) ? 0 : 1;
761 /* end of gnunet-service-fs.c */