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 3, 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 multicast/gnunet-service-multicast.c
23 * @brief program that does multicast
24 * @author Christian Grothoff
27 #include "gnunet_util_lib.h"
28 #include "gnunet_signatures.h"
29 #include "gnunet_statistics_service.h"
30 #include "gnunet_multicast_service.h"
31 #include "multicast.h"
34 * Handle to our current configuration.
36 static const struct GNUNET_CONFIGURATION_Handle *cfg;
39 * Handle to the statistics service.
41 static struct GNUNET_STATISTICS_Handle *stats;
43 * Notification context, simplifies client broadcasts.
45 static struct GNUNET_SERVER_NotificationContext *nc;
48 * All connected origins.
49 * Group's pub_key_hash -> struct Origin
51 static struct GNUNET_CONTAINER_MultiHashMap *origins;
54 * All connected members.
55 * Group's pub_key_hash -> struct Member
57 static struct GNUNET_CONTAINER_MultiHashMap *members;
60 * Connected members per group.
61 * Group's pub_key_hash -> Member's pub_key -> struct Member
63 static struct GNUNET_CONTAINER_MultiHashMap *group_members;
67 * List of connected clients.
71 struct ClientList *prev;
72 struct ClientList *next;
73 struct GNUNET_SERVER_Client *client;
77 * Common part of the client context for both an origin and member.
81 struct ClientList *clients_head;
82 struct ClientList *clients_tail;
85 * Public key of the group.
87 struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
92 struct GNUNET_HashCode pub_key_hash;
95 * Is this an origin (#GNUNET_YES), or member (#GNUNET_NO)?
100 * Is the client disconnected? #GNUNET_YES or #GNUNET_NO
102 uint8_t disconnected;
107 * Client context for a group's origin.
114 * Private key of the group.
116 struct GNUNET_CRYPTO_EddsaPrivateKey priv_key;
119 * Last message fragment ID sent to the group.
121 uint64_t max_fragment_id;
126 * Client context for a group member.
133 * Private key of the member.
135 struct GNUNET_CRYPTO_EddsaPrivateKey priv_key;
138 * Public key of the member.
140 struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
143 * Hash of @a pub_key.
145 struct GNUNET_HashCode pub_key_hash;
148 * Join request sent to the origin / members.
150 struct GNUNET_MULTICAST_JoinRequest *join_request;
153 * Join decision sent in reply to our request.
155 * Only a positive decision is stored here, in case of a negative decision the
156 * client is disconnected.
158 struct MulticastJoinDecisionMessage *join_decision;
161 * Last request fragment ID sent to the origin.
163 uint64_t max_fragment_id;
168 * Task run during shutdown.
174 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
176 /* FIXME: do clean up here */
180 * Clean up origin data structures after a client disconnected.
183 cleanup_origin (struct Origin *orig)
185 struct Group *grp = &orig->grp;
186 GNUNET_CONTAINER_multihashmap_remove (origins, &grp->pub_key_hash, orig);
191 * Clean up member data structures after a client disconnected.
194 cleanup_member (struct Member *mem)
196 struct Group *grp = &mem->grp;
197 struct GNUNET_CONTAINER_MultiHashMap *
198 grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members,
200 GNUNET_assert (NULL != grp_mem);
201 GNUNET_CONTAINER_multihashmap_remove (grp_mem, &mem->pub_key_hash, mem);
203 if (0 == GNUNET_CONTAINER_multihashmap_size (grp_mem))
205 GNUNET_CONTAINER_multihashmap_remove (group_members, &grp->pub_key_hash,
207 GNUNET_CONTAINER_multihashmap_destroy (grp_mem);
209 GNUNET_CONTAINER_multihashmap_remove (members, &grp->pub_key_hash, mem);
214 * Clean up group data structures after a client disconnected.
217 cleanup_group (struct Group *grp)
219 (GNUNET_YES == grp->is_origin)
220 ? cleanup_origin ((struct Origin *) grp)
221 : cleanup_member ((struct Member *) grp);
228 * Called whenever a client is disconnected.
230 * Frees our resources associated with that client.
232 * @param cls Closure.
233 * @param client Client handle.
236 client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
242 = GNUNET_SERVER_client_get_user_context (client, struct Group);
246 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
247 "%p User context is NULL in client_disconnect()\n", grp);
252 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
253 "%p Client (%s) disconnected from group %s\n",
254 grp, (GNUNET_YES == grp->is_origin) ? "origin" : "member",
255 GNUNET_h2s (&grp->pub_key_hash));
257 struct ClientList *cl = grp->clients_head;
260 if (cl->client == client)
262 GNUNET_CONTAINER_DLL_remove (grp->clients_head, grp->clients_tail, cl);
269 if (NULL == grp->clients_head)
270 { /* Last client disconnected. */
272 if (NULL != grp->tmit_head)
273 { /* Send pending messages via CADET before cleanup. */
274 transmit_message (grp);
286 * Send message to all clients connected to the group.
289 message_to_clients (const struct Group *grp,
290 const struct GNUNET_MessageHeader *msg)
292 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
293 "%p Sending message to clients.\n", grp);
295 struct ClientList *cl = grp->clients_head;
298 GNUNET_SERVER_notification_context_add (nc, cl->client);
299 GNUNET_SERVER_notification_context_unicast (nc, cl->client, msg, GNUNET_NO);
306 * Iterator callback for sending a message to origin clients.
309 origin_message_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
312 const struct GNUNET_MessageHeader *msg = cls;
313 struct Member *orig = origin;
315 message_to_clients (&orig->grp, msg);
321 * Iterator callback for sending a message to member clients.
324 member_message_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
327 const struct GNUNET_MessageHeader *msg = cls;
328 struct Member *mem = member;
330 if (NULL != mem->join_decision)
331 { /* Only send message to admitted members */
332 message_to_clients (&mem->grp, msg);
339 * Send message to all origin and member clients connected to the group.
341 * @param grp The group to send @a msg to.
342 * @param msg Message to send.
345 message_to_group (struct Group *grp, const struct GNUNET_MessageHeader *msg)
348 GNUNET_CONTAINER_multihashmap_get_multiple (origins, &grp->pub_key_hash,
349 origin_message_cb, (void *) msg);
351 GNUNET_CONTAINER_multihashmap_get_multiple (members, &grp->pub_key_hash,
352 member_message_cb, (void *) msg);
357 * Send message to all origin clients connected to the group.
359 * @param grp The group to send @a msg to.
360 * @param msg Message to send.
363 message_to_origin (struct Group *grp, const struct GNUNET_MessageHeader *msg)
366 GNUNET_CONTAINER_multihashmap_get_multiple (origins, &grp->pub_key_hash,
367 origin_message_cb, (void *) msg);
372 * Handle a connecting client starting an origin.
375 handle_origin_start (void *cls, struct GNUNET_SERVER_Client *client,
376 const struct GNUNET_MessageHeader *m)
378 const struct MulticastOriginStartMessage *
379 msg = (const struct MulticastOriginStartMessage *) m;
381 struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
382 struct GNUNET_HashCode pub_key_hash;
384 GNUNET_CRYPTO_eddsa_key_get_public (&msg->group_key, &pub_key);
385 GNUNET_CRYPTO_hash (&pub_key, sizeof (pub_key), &pub_key_hash);
388 orig = GNUNET_CONTAINER_multihashmap_get (origins, &pub_key_hash);
393 orig = GNUNET_new (struct Origin);
394 orig->priv_key = msg->group_key;
396 grp->is_origin = GNUNET_YES;
397 grp->pub_key = pub_key;
398 grp->pub_key_hash = pub_key_hash;
400 GNUNET_CONTAINER_multihashmap_put (origins, &grp->pub_key_hash, orig,
401 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
408 struct ClientList *cl = GNUNET_new (struct ClientList);
410 GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
412 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
413 "%p Client connected as origin to group %s.\n",
414 orig, GNUNET_h2s (&grp->pub_key_hash));
416 GNUNET_SERVER_client_set_user_context (client, grp);
417 GNUNET_SERVER_receive_done (client, GNUNET_OK);
422 * Handle a connecting client joining a group.
425 handle_member_join (void *cls, struct GNUNET_SERVER_Client *client,
426 const struct GNUNET_MessageHeader *m)
428 struct MulticastMemberJoinMessage *
429 msg = (struct MulticastMemberJoinMessage *) m;
431 struct GNUNET_CRYPTO_EddsaPublicKey mem_pub_key;
432 struct GNUNET_HashCode pub_key_hash, mem_pub_key_hash;
434 GNUNET_CRYPTO_eddsa_key_get_public (&msg->member_key, &mem_pub_key);
435 GNUNET_CRYPTO_hash (&mem_pub_key, sizeof (mem_pub_key), &mem_pub_key_hash);
436 GNUNET_CRYPTO_hash (&msg->group_key, sizeof (msg->group_key), &pub_key_hash);
438 struct GNUNET_CONTAINER_MultiHashMap *
439 grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members, &pub_key_hash);
440 struct Member *mem = NULL;
445 grp_mem = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
446 GNUNET_CONTAINER_multihashmap_put (group_members, &pub_key_hash, grp_mem,
447 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
451 mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &mem_pub_key_hash);
456 mem = GNUNET_new (struct Member);
457 mem->priv_key = msg->member_key;
458 mem->pub_key = mem_pub_key;
459 mem->pub_key_hash = mem_pub_key_hash;
462 grp->is_origin = GNUNET_NO;
463 grp->pub_key = msg->group_key;
464 grp->pub_key_hash = pub_key_hash;
466 GNUNET_CONTAINER_multihashmap_put (grp_mem, &mem_pub_key_hash, mem,
467 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
468 GNUNET_CONTAINER_multihashmap_put (members, &grp->pub_key_hash, mem,
469 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
476 struct ClientList *cl = GNUNET_new (struct ClientList);
478 GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
480 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
481 "%p Client connected as member to group %s.\n",
482 mem, GNUNET_h2s (&grp->pub_key_hash));
484 GNUNET_SERVER_client_set_user_context (client, grp);
486 if (NULL != mem->join_decision)
487 { /* Already got a join decision, send it to client. */
488 GNUNET_SERVER_notification_context_add (nc, client);
489 GNUNET_SERVER_notification_context_unicast (nc, client,
490 (struct GNUNET_MessageHeader *)
494 else if (grp->clients_head == grp->clients_tail)
495 { /* First client, send join request. */
496 struct GNUNET_PeerIdentity *relays = (struct GNUNET_PeerIdentity *) &msg[1];
497 uint32_t relay_count = ntohs (msg->relay_count);
498 struct GNUNET_MessageHeader *
499 join_req = ((struct GNUNET_MessageHeader *)
500 ((char *) &msg[1]) + relay_count * sizeof (*relays));
501 uint16_t join_req_size = ntohs (join_req->size);
503 struct MulticastJoinRequestMessage *
504 req = GNUNET_malloc (sizeof (*req) + join_req_size);
505 req->header.size = htons (sizeof (*req) + join_req_size);
506 req->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST);
507 req->group_key = grp->pub_key;
508 GNUNET_CRYPTO_eddsa_key_get_public (&mem->priv_key, &req->member_key);
509 memcpy (&req[1], join_req, join_req_size);
511 req->purpose.size = htonl (sizeof (*req) + join_req_size
512 - sizeof (req->header)
513 - sizeof (req->signature));
514 req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
516 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&mem->priv_key, &req->purpose,
519 /* FIXME: handle error */
523 if (NULL != mem->join_request)
524 GNUNET_free (mem->join_request);
525 mem->join_request = req;
528 == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
530 message_to_origin (grp, (struct GNUNET_MessageHeader *) mem->join_request);
534 /* FIXME: send join request to remote origin / members */
537 GNUNET_SERVER_receive_done (client, GNUNET_OK);
542 * Incoming message from a client.
545 handle_multicast_message (void *cls, struct GNUNET_SERVER_Client *client,
546 const struct GNUNET_MessageHeader *m)
549 grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
550 GNUNET_assert (GNUNET_YES == grp->is_origin);
551 struct Origin *orig = (struct Origin *) grp;
552 struct GNUNET_MULTICAST_MessageHeader *
553 msg = (struct GNUNET_MULTICAST_MessageHeader *) m;
555 msg->fragment_id = GNUNET_htonll (orig->max_fragment_id++);
556 msg->purpose.size = htonl (sizeof (*msg) + ntohs (m->size)
557 - sizeof (msg->header)
558 - sizeof (msg->hop_counter)
559 - sizeof (msg->signature));
560 msg->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_MESSAGE);
562 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&orig->priv_key, &msg->purpose,
565 /* FIXME: handle error */
569 /* FIXME: send to remote members */
571 message_to_group (grp, m);
572 GNUNET_SERVER_receive_done (client, GNUNET_OK);
577 * Incoming request from a client.
580 handle_multicast_request (void *cls, struct GNUNET_SERVER_Client *client,
581 const struct GNUNET_MessageHeader *m)
584 grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
585 GNUNET_assert (GNUNET_NO == grp->is_origin);
586 struct Member *mem = (struct Member *) grp;
588 struct GNUNET_MULTICAST_RequestHeader *
589 req = (struct GNUNET_MULTICAST_RequestHeader *) m;
591 req->fragment_id = GNUNET_ntohll (mem->max_fragment_id++);
593 req->purpose.size = htonl (sizeof (*req) + ntohs (m->size)
594 - sizeof (req->header)
595 - sizeof (req->member_key)
596 - sizeof (req->signature));
597 req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
599 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&mem->priv_key, &req->purpose,
602 /* FIXME: handle error */
607 == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
609 message_to_origin (grp, m);
613 /* FIXME: send to remote origin */
615 GNUNET_SERVER_receive_done (client, GNUNET_OK);
619 * Process multicast requests.
622 * @param server the initialized server
623 * @param cfg configuration to use
626 run (void *cls, struct GNUNET_SERVER_Handle *server,
627 const struct GNUNET_CONFIGURATION_Handle *c)
629 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
630 { &handle_origin_start, NULL,
631 GNUNET_MESSAGE_TYPE_MULTICAST_ORIGIN_START, 0 },
633 { &handle_member_join, NULL,
634 GNUNET_MESSAGE_TYPE_MULTICAST_MEMBER_JOIN, 0 },
636 { &handle_multicast_message, NULL,
637 GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE, 0 },
639 { &handle_multicast_request, NULL,
640 GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST, 0 },
646 stats = GNUNET_STATISTICS_create ("multicast", cfg);
647 origins = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
648 members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
649 group_members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
650 nc = GNUNET_SERVER_notification_context_create (server, 1);
652 GNUNET_SERVER_add_handlers (server, handlers);
653 GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL);
654 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
660 * The main function for the multicast service.
662 * @param argc number of arguments from the command line
663 * @param argv command line arguments
664 * @return 0 ok, 1 on error
667 main (int argc, char *const *argv)
670 GNUNET_SERVICE_run (argc, argv, "multicast",
671 GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;
674 /* end of gnunet-service-multicast.c */