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_core_service.h"
31 #include "gnunet_multicast_service.h"
32 #include "multicast.h"
35 * Handle to our current configuration.
37 static const struct GNUNET_CONFIGURATION_Handle *cfg;
42 static struct GNUNET_SERVER_Handle *server;
46 * Only used during initialization.
48 static struct GNUNET_CORE_Handle *core;
51 * Identity of this peer.
53 static struct GNUNET_PeerIdentity this_peer;
56 * Handle to the statistics service.
58 static struct GNUNET_STATISTICS_Handle *stats;
60 * Notification context, simplifies client broadcasts.
62 static struct GNUNET_SERVER_NotificationContext *nc;
65 * All connected origins.
66 * Group's pub_key_hash -> struct Origin
68 static struct GNUNET_CONTAINER_MultiHashMap *origins;
71 * All connected members.
72 * Group's pub_key_hash -> struct Member
74 static struct GNUNET_CONTAINER_MultiHashMap *members;
77 * Connected members per group.
78 * Group's pub_key_hash -> Member's pub_key -> struct Member
80 static struct GNUNET_CONTAINER_MultiHashMap *group_members;
83 * List of connected clients.
87 struct ClientList *prev;
88 struct ClientList *next;
89 struct GNUNET_SERVER_Client *client;
93 * Common part of the client context for both an origin and member.
97 struct ClientList *clients_head;
98 struct ClientList *clients_tail;
101 * Public key of the group.
103 struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
106 * Hash of @a pub_key.
108 struct GNUNET_HashCode pub_key_hash;
111 * Is this an origin (#GNUNET_YES), or member (#GNUNET_NO)?
116 * Is the client disconnected? #GNUNET_YES or #GNUNET_NO
118 uint8_t disconnected;
123 * Client context for a group's origin.
130 * Private key of the group.
132 struct GNUNET_CRYPTO_EddsaPrivateKey priv_key;
135 * Last message fragment ID sent to the group.
137 uint64_t max_fragment_id;
142 * Client context for a group member.
149 * Private key of the member.
151 struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
154 * Public key of the member.
156 struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
159 * Hash of @a pub_key.
161 struct GNUNET_HashCode pub_key_hash;
164 * Join request sent to the origin / members.
166 struct MulticastJoinRequestMessage *join_req;
169 * Join decision sent in reply to our request.
171 * Only a positive decision is stored here, in case of a negative decision the
172 * client is disconnected.
174 struct MulticastJoinDecisionMessageHeader *join_dcsn;
177 * Last request fragment ID sent to the origin.
179 uint64_t max_fragment_id;
184 * Task run during shutdown.
190 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
194 GNUNET_CORE_disconnect (core);
199 GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
202 /* FIXME: do more clean up here */
206 * Clean up origin data structures after a client disconnected.
209 cleanup_origin (struct Origin *orig)
211 struct Group *grp = &orig->grp;
212 GNUNET_CONTAINER_multihashmap_remove (origins, &grp->pub_key_hash, orig);
217 * Clean up member data structures after a client disconnected.
220 cleanup_member (struct Member *mem)
222 struct Group *grp = &mem->grp;
223 struct GNUNET_CONTAINER_MultiHashMap *
224 grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members,
226 GNUNET_assert (NULL != grp_mem);
227 GNUNET_CONTAINER_multihashmap_remove (grp_mem, &mem->pub_key_hash, mem);
229 if (0 == GNUNET_CONTAINER_multihashmap_size (grp_mem))
231 GNUNET_CONTAINER_multihashmap_remove (group_members, &grp->pub_key_hash,
233 GNUNET_CONTAINER_multihashmap_destroy (grp_mem);
235 if (NULL != mem->join_dcsn)
237 GNUNET_free (mem->join_dcsn);
238 mem->join_dcsn = NULL;
240 GNUNET_CONTAINER_multihashmap_remove (members, &grp->pub_key_hash, mem);
245 * Clean up group data structures after a client disconnected.
248 cleanup_group (struct Group *grp)
250 (GNUNET_YES == grp->is_origin)
251 ? cleanup_origin ((struct Origin *) grp)
252 : cleanup_member ((struct Member *) grp);
259 * Called whenever a client is disconnected.
261 * Frees our resources associated with that client.
263 * @param cls Closure.
264 * @param client Client handle.
267 client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
273 = GNUNET_SERVER_client_get_user_context (client, struct Group);
277 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
278 "%p User context is NULL in client_disconnect()\n", grp);
283 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
284 "%p Client (%s) disconnected from group %s\n",
285 grp, (GNUNET_YES == grp->is_origin) ? "origin" : "member",
286 GNUNET_h2s (&grp->pub_key_hash));
288 struct ClientList *cl = grp->clients_head;
291 if (cl->client == client)
293 GNUNET_CONTAINER_DLL_remove (grp->clients_head, grp->clients_tail, cl);
300 if (NULL == grp->clients_head)
301 { /* Last client disconnected. */
303 if (NULL != grp->tmit_head)
304 { /* Send pending messages via CADET before cleanup. */
305 transmit_message (grp);
317 * Send message to all clients connected to the group.
320 message_to_clients (const struct Group *grp,
321 const struct GNUNET_MessageHeader *msg)
323 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
324 "%p Sending message to clients.\n", grp);
326 struct ClientList *cl = grp->clients_head;
329 GNUNET_SERVER_notification_context_add (nc, cl->client);
330 GNUNET_SERVER_notification_context_unicast (nc, cl->client, msg, GNUNET_NO);
337 * Iterator callback for sending a message to origin clients.
340 origin_message_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
343 const struct GNUNET_MessageHeader *msg = cls;
344 struct Member *orig = origin;
346 message_to_clients (&orig->grp, msg);
352 * Iterator callback for sending a message to member clients.
355 member_message_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
358 const struct GNUNET_MessageHeader *msg = cls;
359 struct Member *mem = member;
361 if (NULL != mem->join_dcsn)
362 { /* Only send message to admitted members */
363 message_to_clients (&mem->grp, msg);
370 * Send message to all origin and member clients connected to the group.
372 * @param grp The group to send @a msg to.
373 * @param msg Message to send.
376 message_to_group (struct Group *grp, const struct GNUNET_MessageHeader *msg)
379 GNUNET_CONTAINER_multihashmap_get_multiple (origins, &grp->pub_key_hash,
380 origin_message_cb, (void *) msg);
382 GNUNET_CONTAINER_multihashmap_get_multiple (members, &grp->pub_key_hash,
383 member_message_cb, (void *) msg);
388 * Send message to all origin clients connected to the group.
390 * @param grp The group to send @a msg to.
391 * @param msg Message to send.
394 message_to_origin (struct Group *grp, const struct GNUNET_MessageHeader *msg)
397 GNUNET_CONTAINER_multihashmap_get_multiple (origins, &grp->pub_key_hash,
398 origin_message_cb, (void *) msg);
403 * Handle a connecting client starting an origin.
406 client_origin_start (void *cls, struct GNUNET_SERVER_Client *client,
407 const struct GNUNET_MessageHeader *m)
409 const struct MulticastOriginStartMessage *
410 msg = (const struct MulticastOriginStartMessage *) m;
412 struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
413 struct GNUNET_HashCode pub_key_hash;
415 GNUNET_CRYPTO_eddsa_key_get_public (&msg->group_key, &pub_key);
416 GNUNET_CRYPTO_hash (&pub_key, sizeof (pub_key), &pub_key_hash);
419 orig = GNUNET_CONTAINER_multihashmap_get (origins, &pub_key_hash);
424 orig = GNUNET_new (struct Origin);
425 orig->priv_key = msg->group_key;
426 orig->max_fragment_id = GNUNET_ntohll (msg->max_fragment_id);
428 grp->is_origin = GNUNET_YES;
429 grp->pub_key = pub_key;
430 grp->pub_key_hash = pub_key_hash;
432 GNUNET_CONTAINER_multihashmap_put (origins, &grp->pub_key_hash, orig,
433 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
440 struct ClientList *cl = GNUNET_new (struct ClientList);
442 GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
444 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
445 "%p Client connected as origin to group %s.\n",
446 orig, GNUNET_h2s (&grp->pub_key_hash));
448 GNUNET_SERVER_client_set_user_context (client, grp);
449 GNUNET_SERVER_receive_done (client, GNUNET_OK);
454 * Handle a connecting client joining a group.
457 client_member_join (void *cls, struct GNUNET_SERVER_Client *client,
458 const struct GNUNET_MessageHeader *m)
460 const struct MulticastMemberJoinMessage *
461 msg = (const struct MulticastMemberJoinMessage *) m;
462 uint16_t msg_size = ntohs (msg->header.size);
464 struct GNUNET_CRYPTO_EcdsaPublicKey mem_pub_key;
465 struct GNUNET_HashCode pub_key_hash, mem_pub_key_hash;
467 GNUNET_CRYPTO_ecdsa_key_get_public (&msg->member_key, &mem_pub_key);
468 GNUNET_CRYPTO_hash (&mem_pub_key, sizeof (mem_pub_key), &mem_pub_key_hash);
469 GNUNET_CRYPTO_hash (&msg->group_key, sizeof (msg->group_key), &pub_key_hash);
471 struct GNUNET_CONTAINER_MultiHashMap *
472 grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members, &pub_key_hash);
473 struct Member *mem = NULL;
478 mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &mem_pub_key_hash);
482 mem = GNUNET_new (struct Member);
483 mem->priv_key = msg->member_key;
484 mem->pub_key = mem_pub_key;
485 mem->pub_key_hash = mem_pub_key_hash;
486 mem->max_fragment_id = 0; // FIXME
489 grp->is_origin = GNUNET_NO;
490 grp->pub_key = msg->group_key;
491 grp->pub_key_hash = pub_key_hash;
495 grp_mem = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
496 GNUNET_CONTAINER_multihashmap_put (group_members, &grp->pub_key_hash, grp_mem,
497 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
499 GNUNET_CONTAINER_multihashmap_put (grp_mem, &mem->pub_key_hash, mem,
500 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
501 GNUNET_CONTAINER_multihashmap_put (members, &grp->pub_key_hash, mem,
502 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
509 struct ClientList *cl = GNUNET_new (struct ClientList);
511 GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
513 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
514 "%p Client connected to group %s..\n",
515 mem, GNUNET_h2s (&grp->pub_key_hash));
516 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
517 "%p ..as member %s.\n",
518 mem, GNUNET_h2s (&mem_pub_key_hash));
520 GNUNET_SERVER_client_set_user_context (client, grp);
522 if (NULL != mem->join_dcsn)
523 { /* Already got a join decision, send it to client. */
524 GNUNET_SERVER_notification_context_add (nc, client);
525 GNUNET_SERVER_notification_context_unicast (nc, client,
526 (struct GNUNET_MessageHeader *)
530 else if (grp->clients_head == grp->clients_tail)
531 { /* First client of the group, send join request. */
532 struct GNUNET_PeerIdentity *relays = (struct GNUNET_PeerIdentity *) &msg[1];
533 uint32_t relay_count = ntohs (msg->relay_count);
534 uint16_t relay_size = relay_count * sizeof (*relays);
535 struct GNUNET_MessageHeader *join_msg = NULL;
536 uint16_t join_msg_size = 0;
537 if (sizeof (*msg) + relay_size + sizeof (struct GNUNET_MessageHeader)
540 join_msg = (struct GNUNET_MessageHeader *)
541 (((char *) &msg[1]) + relay_size);
542 join_msg_size = ntohs (join_msg->size);
544 if (sizeof (*msg) + relay_size + join_msg_size != msg_size)
547 GNUNET_SERVER_client_disconnect (client);
551 struct MulticastJoinRequestMessage *
552 req = GNUNET_malloc (sizeof (*req) + join_msg_size);
553 req->header.size = htons (sizeof (*req) + join_msg_size);
554 req->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST);
555 req->group_key = grp->pub_key;
556 req->member_peer = this_peer;
557 GNUNET_CRYPTO_ecdsa_key_get_public (&mem->priv_key, &req->member_key);
558 if (0 < join_msg_size)
559 memcpy (&req[1], join_msg, join_msg_size);
561 req->purpose.size = htonl (sizeof (*req) + join_msg_size
562 - sizeof (req->header)
563 - sizeof (req->signature));
564 req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
566 if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_sign (&mem->priv_key, &req->purpose,
569 /* FIXME: handle error */
573 if (NULL != mem->join_req)
574 GNUNET_free (mem->join_req);
578 == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
580 message_to_origin (grp, (struct GNUNET_MessageHeader *) mem->join_req);
584 /* FIXME: send join request to remote peers */
587 GNUNET_SERVER_receive_done (client, GNUNET_OK);
592 * Join decision from client.
595 client_join_decision (void *cls, struct GNUNET_SERVER_Client *client,
596 const struct GNUNET_MessageHeader *m)
599 grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
600 const struct MulticastJoinDecisionMessageHeader *
601 hdcsn = (const struct MulticastJoinDecisionMessageHeader *) m;
602 const struct MulticastJoinDecisionMessage *
603 dcsn = (const struct MulticastJoinDecisionMessage *) &hdcsn[1];
605 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
606 "%p Got join decision from client for group %s..\n",
607 grp, GNUNET_h2s (&grp->pub_key_hash));
610 == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
612 struct GNUNET_CONTAINER_MultiHashMap *
613 grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members,
617 struct GNUNET_HashCode member_key_hash;
618 GNUNET_CRYPTO_hash (&hdcsn->member_key, sizeof (hdcsn->member_key),
621 mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &member_key_hash);
622 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
623 "%p ..and member %s: %p\n",
624 grp, GNUNET_h2s (&member_key_hash), mem);
627 message_to_clients (&mem->grp, (struct GNUNET_MessageHeader *) hdcsn);
628 if (GNUNET_YES == ntohl (dcsn->is_admitted))
629 { /* Member admitted, store join_decision. */
630 uint16_t dcsn_size = ntohs (dcsn->header.size);
631 mem->join_dcsn = GNUNET_malloc (dcsn_size);
632 memcpy (mem->join_dcsn, dcsn, dcsn_size);
635 { /* Refused entry, disconnect clients. */
636 struct ClientList *cl = mem->grp.clients_head;
639 struct GNUNET_SERVER_Client *client = cl->client;
641 GNUNET_SERVER_client_disconnect (client);
649 /* FIXME: send join decision to hdcsn->peer */
651 GNUNET_SERVER_receive_done (client, GNUNET_OK);
655 * Incoming message from a client.
658 client_multicast_message (void *cls, struct GNUNET_SERVER_Client *client,
659 const struct GNUNET_MessageHeader *m)
662 grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
663 GNUNET_assert (GNUNET_YES == grp->is_origin);
664 struct Origin *orig = (struct Origin *) grp;
665 struct GNUNET_MULTICAST_MessageHeader *
666 msg = (struct GNUNET_MULTICAST_MessageHeader *) m;
668 msg->fragment_id = GNUNET_htonll (++orig->max_fragment_id);
669 msg->purpose.size = htonl (sizeof (*msg) + ntohs (m->size)
670 - sizeof (msg->header)
671 - sizeof (msg->hop_counter)
672 - sizeof (msg->signature));
673 msg->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_MESSAGE);
675 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&orig->priv_key, &msg->purpose,
678 /* FIXME: handle error */
682 /* FIXME: send to remote members */
684 message_to_group (grp, m);
685 GNUNET_SERVER_receive_done (client, GNUNET_OK);
690 * Incoming request from a client.
693 client_multicast_request (void *cls, struct GNUNET_SERVER_Client *client,
694 const struct GNUNET_MessageHeader *m)
697 grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
698 GNUNET_assert (GNUNET_NO == grp->is_origin);
699 struct Member *mem = (struct Member *) grp;
701 struct GNUNET_MULTICAST_RequestHeader *
702 req = (struct GNUNET_MULTICAST_RequestHeader *) m;
704 req->fragment_id = GNUNET_ntohll (++mem->max_fragment_id);
705 req->purpose.size = htonl (sizeof (*req) + ntohs (m->size)
706 - sizeof (req->header)
707 - sizeof (req->member_key)
708 - sizeof (req->signature));
709 req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
711 if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_sign (&mem->priv_key, &req->purpose,
714 /* FIXME: handle error */
719 == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
721 message_to_origin (grp, m);
725 /* FIXME: send to remote origin */
727 GNUNET_SERVER_receive_done (client, GNUNET_OK);
732 * Connected to core service.
735 core_connected_cb (void *cls, const struct GNUNET_PeerIdentity *my_identity)
737 this_peer = *my_identity;
739 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
740 { &client_origin_start, NULL,
741 GNUNET_MESSAGE_TYPE_MULTICAST_ORIGIN_START, 0 },
743 { &client_member_join, NULL,
744 GNUNET_MESSAGE_TYPE_MULTICAST_MEMBER_JOIN, 0 },
746 { &client_join_decision, NULL,
747 GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION, 0 },
749 { &client_multicast_message, NULL,
750 GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE, 0 },
752 { &client_multicast_request, NULL,
753 GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST, 0 },
758 stats = GNUNET_STATISTICS_create ("multicast", cfg);
759 origins = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
760 members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
761 group_members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
762 nc = GNUNET_SERVER_notification_context_create (server, 1);
764 GNUNET_SERVER_add_handlers (server, handlers);
765 GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL);
766 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
775 * @param server the initialized server
776 * @param cfg configuration to use
779 run (void *cls, struct GNUNET_SERVER_Handle *srv,
780 const struct GNUNET_CONFIGURATION_Handle *c)
784 core = GNUNET_CORE_connect (cfg, NULL, core_connected_cb, NULL, NULL,
785 NULL, GNUNET_NO, NULL, GNUNET_NO, NULL);
790 * The main function for the multicast service.
792 * @param argc number of arguments from the command line
793 * @param argv command line arguments
794 * @return 0 ok, 1 on error
797 main (int argc, char *const *argv)
800 GNUNET_SERVICE_run (argc, argv, "multicast",
801 GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;
804 /* end of gnunet-service-multicast.c */