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 MulticastJoinRequestMessage *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 if (NULL != mem->join_decision)
210 GNUNET_free (mem->join_decision);
211 GNUNET_CONTAINER_multihashmap_remove (members, &grp->pub_key_hash, mem);
216 * Clean up group data structures after a client disconnected.
219 cleanup_group (struct Group *grp)
221 (GNUNET_YES == grp->is_origin)
222 ? cleanup_origin ((struct Origin *) grp)
223 : cleanup_member ((struct Member *) grp);
230 * Called whenever a client is disconnected.
232 * Frees our resources associated with that client.
234 * @param cls Closure.
235 * @param client Client handle.
238 client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
244 = GNUNET_SERVER_client_get_user_context (client, struct Group);
248 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
249 "%p User context is NULL in client_disconnect()\n", grp);
254 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
255 "%p Client (%s) disconnected from group %s\n",
256 grp, (GNUNET_YES == grp->is_origin) ? "origin" : "member",
257 GNUNET_h2s (&grp->pub_key_hash));
259 struct ClientList *cl = grp->clients_head;
262 if (cl->client == client)
264 GNUNET_CONTAINER_DLL_remove (grp->clients_head, grp->clients_tail, cl);
271 if (NULL == grp->clients_head)
272 { /* Last client disconnected. */
274 if (NULL != grp->tmit_head)
275 { /* Send pending messages via CADET before cleanup. */
276 transmit_message (grp);
288 * Send message to all clients connected to the group.
291 message_to_clients (const struct Group *grp,
292 const struct GNUNET_MessageHeader *msg)
294 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
295 "%p Sending message to clients.\n", grp);
297 struct ClientList *cl = grp->clients_head;
300 GNUNET_SERVER_notification_context_add (nc, cl->client);
301 GNUNET_SERVER_notification_context_unicast (nc, cl->client, msg, GNUNET_NO);
308 * Iterator callback for sending a message to origin clients.
311 origin_message_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
314 const struct GNUNET_MessageHeader *msg = cls;
315 struct Member *orig = origin;
317 message_to_clients (&orig->grp, msg);
323 * Iterator callback for sending a message to member clients.
326 member_message_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
329 const struct GNUNET_MessageHeader *msg = cls;
330 struct Member *mem = member;
332 if (NULL != mem->join_decision)
333 { /* Only send message to admitted members */
334 message_to_clients (&mem->grp, msg);
341 * Send message to all origin and member clients connected to the group.
343 * @param grp The group to send @a msg to.
344 * @param msg Message to send.
347 message_to_group (struct Group *grp, const struct GNUNET_MessageHeader *msg)
350 GNUNET_CONTAINER_multihashmap_get_multiple (origins, &grp->pub_key_hash,
351 origin_message_cb, (void *) msg);
353 GNUNET_CONTAINER_multihashmap_get_multiple (members, &grp->pub_key_hash,
354 member_message_cb, (void *) msg);
359 * Send message to all origin clients connected to the group.
361 * @param grp The group to send @a msg to.
362 * @param msg Message to send.
365 message_to_origin (struct Group *grp, const struct GNUNET_MessageHeader *msg)
368 GNUNET_CONTAINER_multihashmap_get_multiple (origins, &grp->pub_key_hash,
369 origin_message_cb, (void *) msg);
374 * Handle a connecting client starting an origin.
377 handle_origin_start (void *cls, struct GNUNET_SERVER_Client *client,
378 const struct GNUNET_MessageHeader *m)
380 const struct MulticastOriginStartMessage *
381 msg = (const struct MulticastOriginStartMessage *) m;
383 struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
384 struct GNUNET_HashCode pub_key_hash;
386 GNUNET_CRYPTO_eddsa_key_get_public (&msg->group_key, &pub_key);
387 GNUNET_CRYPTO_hash (&pub_key, sizeof (pub_key), &pub_key_hash);
390 orig = GNUNET_CONTAINER_multihashmap_get (origins, &pub_key_hash);
395 orig = GNUNET_new (struct Origin);
396 orig->priv_key = msg->group_key;
398 grp->is_origin = GNUNET_YES;
399 grp->pub_key = pub_key;
400 grp->pub_key_hash = pub_key_hash;
402 GNUNET_CONTAINER_multihashmap_put (origins, &grp->pub_key_hash, orig,
403 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
410 struct ClientList *cl = GNUNET_new (struct ClientList);
412 GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
414 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
415 "%p Client connected as origin to group %s.\n",
416 orig, GNUNET_h2s (&grp->pub_key_hash));
418 GNUNET_SERVER_client_set_user_context (client, grp);
419 GNUNET_SERVER_receive_done (client, GNUNET_OK);
424 * Handle a connecting client joining a group.
427 handle_member_join (void *cls, struct GNUNET_SERVER_Client *client,
428 const struct GNUNET_MessageHeader *m)
430 const struct MulticastMemberJoinMessage *
431 msg = (const struct MulticastMemberJoinMessage *) m;
433 struct GNUNET_CRYPTO_EddsaPublicKey mem_pub_key;
434 struct GNUNET_HashCode pub_key_hash, mem_pub_key_hash;
436 GNUNET_CRYPTO_eddsa_key_get_public (&msg->member_key, &mem_pub_key);
437 GNUNET_CRYPTO_hash (&mem_pub_key, sizeof (mem_pub_key), &mem_pub_key_hash);
438 GNUNET_CRYPTO_hash (&msg->group_key, sizeof (msg->group_key), &pub_key_hash);
440 struct GNUNET_CONTAINER_MultiHashMap *
441 grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members, &pub_key_hash);
442 struct Member *mem = NULL;
447 mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &mem_pub_key_hash);
451 mem = GNUNET_new (struct Member);
452 mem->priv_key = msg->member_key;
453 mem->pub_key = mem_pub_key;
454 mem->pub_key_hash = mem_pub_key_hash;
457 grp->is_origin = GNUNET_NO;
458 grp->pub_key = msg->group_key;
459 grp->pub_key_hash = pub_key_hash;
463 grp_mem = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
464 GNUNET_CONTAINER_multihashmap_put (group_members, &grp->pub_key_hash, grp_mem,
465 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
467 GNUNET_CONTAINER_multihashmap_put (grp_mem, &mem->pub_key_hash, mem,
468 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
469 GNUNET_CONTAINER_multihashmap_put (members, &grp->pub_key_hash, mem,
470 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
477 struct ClientList *cl = GNUNET_new (struct ClientList);
479 GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
481 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482 "%p Client connected to group %s..\n",
483 mem, GNUNET_h2s (&grp->pub_key_hash));
484 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
485 "%p ..as member %s.\n",
486 mem, GNUNET_h2s (&mem_pub_key_hash));
488 GNUNET_SERVER_client_set_user_context (client, grp);
490 if (NULL != mem->join_decision)
491 { /* Already got a join decision, send it to client. */
492 GNUNET_SERVER_notification_context_add (nc, client);
493 GNUNET_SERVER_notification_context_unicast (nc, client,
494 (struct GNUNET_MessageHeader *)
498 else if (grp->clients_head == grp->clients_tail)
499 { /* First client, send join request. */
500 struct GNUNET_PeerIdentity *relays = (struct GNUNET_PeerIdentity *) &msg[1];
501 uint32_t relay_count = ntohs (msg->relay_count);
502 struct GNUNET_MessageHeader *
503 join_msg = ((struct GNUNET_MessageHeader *)
504 ((char *) &msg[1]) + relay_count * sizeof (*relays));
505 uint16_t join_msg_size = ntohs (join_msg->size);
507 struct MulticastJoinRequestMessage *
508 req = GNUNET_malloc (sizeof (*req) + join_msg_size);
509 req->header.size = htons (sizeof (*req) + join_msg_size);
510 req->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST);
511 req->group_key = grp->pub_key;
512 GNUNET_CRYPTO_eddsa_key_get_public (&mem->priv_key, &req->member_key);
513 memcpy (&req[1], join_msg, join_msg_size);
515 req->purpose.size = htonl (sizeof (*req) + join_msg_size
516 - sizeof (req->header)
517 - sizeof (req->signature));
518 req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
520 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&mem->priv_key, &req->purpose,
523 /* FIXME: handle error */
527 if (NULL != mem->join_request)
528 GNUNET_free (mem->join_request);
529 mem->join_request = req;
532 == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
534 message_to_origin (grp, (struct GNUNET_MessageHeader *) mem->join_request);
538 /* FIXME: send join request to remote peers */
541 GNUNET_SERVER_receive_done (client, GNUNET_OK);
546 * Join decision from client.
549 handle_join_decision (void *cls, struct GNUNET_SERVER_Client *client,
550 const struct GNUNET_MessageHeader *m)
553 grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
554 const struct MulticastClientJoinDecisionMessage *
555 cl_dcsn = (const struct MulticastClientJoinDecisionMessage *) m;
557 struct GNUNET_PeerIdentity *relays = (struct GNUNET_PeerIdentity *) &cl_dcsn[1];
558 uint32_t relay_count = ntohs (cl_dcsn->relay_count);
560 struct GNUNET_MessageHeader *join_msg = NULL;
561 uint16_t join_msg_size = 0;
562 if (sizeof (*cl_dcsn) + relay_count * sizeof (*relays) + sizeof (*m)
565 join_msg = ((struct GNUNET_MessageHeader *)
566 ((char *) &cl_dcsn[1]) + relay_count * sizeof (*relays));
567 join_msg_size = ntohs (join_msg->size);
570 struct MulticastJoinDecisionMessage *
571 dcsn = GNUNET_malloc (sizeof (*dcsn) + join_msg_size);
572 dcsn->header.size = htons (sizeof (*dcsn) + join_msg_size);
573 dcsn->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION);
574 dcsn->is_admitted = cl_dcsn->is_admitted;
575 memcpy (&dcsn[1], join_msg, join_msg_size);
577 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
578 "%p Got join decision from client for group %s..\n",
579 grp, GNUNET_h2s (&grp->pub_key_hash));
582 == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
584 struct GNUNET_CONTAINER_MultiHashMap *
585 grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members,
589 struct GNUNET_HashCode member_key_hash;
590 GNUNET_CRYPTO_hash (&cl_dcsn->member_key, sizeof (cl_dcsn->member_key),
593 mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &member_key_hash);
594 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
595 "%p ..and member %s: %p\n",
596 grp, GNUNET_h2s (&member_key_hash), mem);
599 message_to_clients (grp, (struct GNUNET_MessageHeader *) dcsn);
600 if (GNUNET_YES == dcsn->is_admitted)
601 { /* Member admitted, store join_decision. */
602 mem->join_decision = dcsn;
605 { /* Refused entry, disconnect clients. */
607 struct ClientList *cl = mem->grp.clients_head;
610 GNUNET_SERVER_client_disconnect (cl->client);
619 /* FIXME: send join decision to remote peers */
621 GNUNET_SERVER_receive_done (client, GNUNET_OK);
625 * Incoming message from a client.
628 handle_multicast_message (void *cls, struct GNUNET_SERVER_Client *client,
629 const struct GNUNET_MessageHeader *m)
632 grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
633 GNUNET_assert (GNUNET_YES == grp->is_origin);
634 struct Origin *orig = (struct Origin *) grp;
635 struct GNUNET_MULTICAST_MessageHeader *
636 msg = (struct GNUNET_MULTICAST_MessageHeader *) m;
638 msg->fragment_id = GNUNET_htonll (orig->max_fragment_id++);
639 msg->purpose.size = htonl (sizeof (*msg) + ntohs (m->size)
640 - sizeof (msg->header)
641 - sizeof (msg->hop_counter)
642 - sizeof (msg->signature));
643 msg->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_MESSAGE);
645 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&orig->priv_key, &msg->purpose,
648 /* FIXME: handle error */
652 /* FIXME: send to remote members */
654 message_to_group (grp, m);
655 GNUNET_SERVER_receive_done (client, GNUNET_OK);
660 * Incoming request from a client.
663 handle_multicast_request (void *cls, struct GNUNET_SERVER_Client *client,
664 const struct GNUNET_MessageHeader *m)
667 grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
668 GNUNET_assert (GNUNET_NO == grp->is_origin);
669 struct Member *mem = (struct Member *) grp;
671 struct GNUNET_MULTICAST_RequestHeader *
672 req = (struct GNUNET_MULTICAST_RequestHeader *) m;
674 req->fragment_id = GNUNET_ntohll (mem->max_fragment_id++);
676 req->purpose.size = htonl (sizeof (*req) + ntohs (m->size)
677 - sizeof (req->header)
678 - sizeof (req->member_key)
679 - sizeof (req->signature));
680 req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
682 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&mem->priv_key, &req->purpose,
685 /* FIXME: handle error */
690 == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
692 message_to_origin (grp, m);
696 /* FIXME: send to remote origin */
698 GNUNET_SERVER_receive_done (client, GNUNET_OK);
702 * Process multicast requests.
705 * @param server the initialized server
706 * @param cfg configuration to use
709 run (void *cls, struct GNUNET_SERVER_Handle *server,
710 const struct GNUNET_CONFIGURATION_Handle *c)
712 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
713 { &handle_origin_start, NULL,
714 GNUNET_MESSAGE_TYPE_MULTICAST_ORIGIN_START, 0 },
716 { &handle_member_join, NULL,
717 GNUNET_MESSAGE_TYPE_MULTICAST_MEMBER_JOIN, 0 },
719 { &handle_join_decision, NULL,
720 GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION, 0 },
722 { &handle_multicast_message, NULL,
723 GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE, 0 },
725 { &handle_multicast_request, NULL,
726 GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST, 0 },
732 stats = GNUNET_STATISTICS_create ("multicast", cfg);
733 origins = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
734 members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
735 group_members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
736 nc = GNUNET_SERVER_notification_context_create (server, 1);
738 GNUNET_SERVER_add_handlers (server, handlers);
739 GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL);
740 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
746 * The main function for the multicast 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, argv, "multicast",
757 GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;
760 /* end of gnunet-service-multicast.c */