multicast,psyc,social: remove DEBUG #defines from tests to keep `make check` working
[oweals/gnunet.git] / src / multicast / gnunet-service-multicast.c
index 47c8bce363680a21353b221c1957d09bb228dad3..1b8e5c8b6f106e4737cdbe4c88e12dafea29f236 100644 (file)
  */
 #include "platform.h"
 #include "gnunet_util_lib.h"
+#include "gnunet_signatures.h"
+#include "gnunet_statistics_service.h"
+#include "gnunet_core_service.h"
+#include "gnunet_multicast_service.h"
+#include "multicast.h"
+
+/**
+ * Handle to our current configuration.
+ */
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Server handle.
+ */
+static struct GNUNET_SERVER_Handle *server;
+
+/**
+ * Core handle.
+ * Only used during initialization.
+ */
+static struct GNUNET_CORE_Handle *core;
+
+/**
+ * Identity of this peer.
+ */
+static struct GNUNET_PeerIdentity this_peer;
+
+/**
+ * Handle to the statistics service.
+ */
+static struct GNUNET_STATISTICS_Handle *stats;
+/**
+ * Notification context, simplifies client broadcasts.
+ */
+static struct GNUNET_SERVER_NotificationContext *nc;
+
+/**
+ * All connected origins.
+ * Group's pub_key_hash -> struct Origin
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *origins;
+
+/**
+ * All connected members.
+ * Group's pub_key_hash -> struct Member
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *members;
+
+/**
+ * Connected members per group.
+ * Group's pub_key_hash -> Member's pub_key -> struct Member
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *group_members;
+
+/**
+ * List of connected clients.
+ */
+struct ClientList
+{
+  struct ClientList *prev;
+  struct ClientList *next;
+  struct GNUNET_SERVER_Client *client;
+};
+
+/**
+ * Common part of the client context for both an origin and member.
+ */
+struct Group
+{
+  struct ClientList *clients_head;
+  struct ClientList *clients_tail;
+
+  /**
+   * Public key of the group.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
+
+  /**
+   * Hash of @a pub_key.
+   */
+  struct GNUNET_HashCode pub_key_hash;
+
+  /**
+   * Is this an origin (#GNUNET_YES), or member (#GNUNET_NO)?
+   */
+  uint8_t is_origin;
+
+  /**
+   * Is the client disconnected? #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t disconnected;
+};
+
+
+/**
+ * Client context for a group's origin.
+ */
+struct Origin
+{
+  struct Group grp;
+
+  /**
+   * Private key of the group.
+   */
+  struct GNUNET_CRYPTO_EddsaPrivateKey priv_key;
+
+  /**
+   * Last message fragment ID sent to the group.
+   */
+  uint64_t max_fragment_id;
+};
+
+
+/**
+ * Client context for a group member.
+ */
+struct Member
+{
+  struct Group grp;
+
+  /**
+   * Private key of the member.
+   */
+  struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
+
+  /**
+   * Public key of the member.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
+
+  /**
+   * Hash of @a pub_key.
+   */
+  struct GNUNET_HashCode pub_key_hash;
+
+  /**
+   * Join request sent to the origin / members.
+   */
+  struct MulticastJoinRequestMessage *join_req;
+
+  /**
+   * Join decision sent in reply to our request.
+   *
+   * Only a positive decision is stored here, in case of a negative decision the
+   * client is disconnected.
+   */
+  struct MulticastJoinDecisionMessageHeader *join_dcsn;
+
+  /**
+   * Last request fragment ID sent to the origin.
+   */
+  uint64_t max_fragment_id;
+};
 
 
 /**
  * @param tc unused
  */
 static void
-cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  /* FIXME: do clean up here */
+  if (NULL != core)
+  {
+    GNUNET_CORE_disconnect (core);
+    core = NULL;
+  }
+  if (NULL != stats)
+  {
+    GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
+    stats = NULL;
+  }
+  /* FIXME: do more clean up here */
+}
+
+/**
+ * Clean up origin data structures after a client disconnected.
+ */
+static void
+cleanup_origin (struct Origin *orig)
+{
+  struct Group *grp = &orig->grp;
+  GNUNET_CONTAINER_multihashmap_remove (origins, &grp->pub_key_hash, orig);
 }
 
 
 /**
- * Handle a connecting client starting an origin.
+ * Clean up member data structures after a client disconnected.
+ */
+static void
+cleanup_member (struct Member *mem)
+{
+  struct Group *grp = &mem->grp;
+  struct GNUNET_CONTAINER_MultiHashMap *
+    grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members,
+                                                 &grp->pub_key_hash);
+  GNUNET_assert (NULL != grp_mem);
+  GNUNET_CONTAINER_multihashmap_remove (grp_mem, &mem->pub_key_hash, mem);
+
+  if (0 == GNUNET_CONTAINER_multihashmap_size (grp_mem))
+  {
+    GNUNET_CONTAINER_multihashmap_remove (group_members, &grp->pub_key_hash,
+                                          grp_mem);
+    GNUNET_CONTAINER_multihashmap_destroy (grp_mem);
+  }
+  if (NULL != mem->join_dcsn)
+  {
+    GNUNET_free (mem->join_dcsn);
+    mem->join_dcsn = NULL;
+  }
+  GNUNET_CONTAINER_multihashmap_remove (members, &grp->pub_key_hash, mem);
+}
+
+
+/**
+ * Clean up group data structures after a client disconnected.
  */
 static void
-handle_origin_start (void *cls, struct GNUNET_SERVER_Client *client,
-                     const struct GNUNET_MessageHeader *msg)
+cleanup_group (struct Group *grp)
 {
+  (GNUNET_YES == grp->is_origin)
+    ? cleanup_origin ((struct Origin *) grp)
+    : cleanup_member ((struct Member *) grp);
 
+  GNUNET_free (grp);
 }
 
 
 /**
- * Handle a client stopping an origin.
+ * Called whenever a client is disconnected.
+ *
+ * Frees our resources associated with that client.
+ *
+ * @param cls  Closure.
+ * @param client  Client handle.
  */
 static void
-handle_origin_stop (void *cls, struct GNUNET_SERVER_Client *client,
+client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
+{
+  if (NULL == client)
+    return;
+
+  struct Group *grp
+    = GNUNET_SERVER_client_get_user_context (client, struct Group);
+
+  if (NULL == grp)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p User context is NULL in client_disconnect()\n", grp);
+    GNUNET_assert (0);
+    return;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Client (%s) disconnected from group %s\n",
+              grp, (GNUNET_YES == grp->is_origin) ? "origin" : "member",
+              GNUNET_h2s (&grp->pub_key_hash));
+
+  struct ClientList *cl = grp->clients_head;
+  while (NULL != cl)
+  {
+    if (cl->client == client)
+    {
+      GNUNET_CONTAINER_DLL_remove (grp->clients_head, grp->clients_tail, cl);
+      GNUNET_free (cl);
+      break;
+    }
+    cl = cl->next;
+  }
+
+  if (NULL == grp->clients_head)
+  { /* Last client disconnected. */
+#if FIXME
+    if (NULL != grp->tmit_head)
+    { /* Send pending messages via CADET before cleanup. */
+      transmit_message (grp);
+    }
+    else
+#endif
+    {
+      cleanup_group (grp);
+    }
+  }
+}
+
+
+/**
+ * Send message to all clients connected to the group.
+ */
+static void
+message_to_clients (const struct Group *grp,
                     const struct GNUNET_MessageHeader *msg)
 {
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "%p Sending message to clients.\n", grp);
+
+  struct ClientList *cl = grp->clients_head;
+  while (NULL != cl)
+  {
+    GNUNET_SERVER_notification_context_add (nc, cl->client);
+    GNUNET_SERVER_notification_context_unicast (nc, cl->client, msg, GNUNET_NO);
+    cl = cl->next;
+  }
+}
+
 
+/**
+ * Iterator callback for sending a message to origin clients.
+ */
+static int
+origin_message_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
+                   void *origin)
+{
+  const struct GNUNET_MessageHeader *msg = cls;
+  struct Member *orig = origin;
+
+  message_to_clients (&orig->grp, msg);
+  return GNUNET_YES;
 }
 
 
 /**
- * Handle a connecting client joining a group.
+ * Iterator callback for sending a message to member clients.
+ */
+static int
+member_message_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
+                   void *member)
+{
+  const struct GNUNET_MessageHeader *msg = cls;
+  struct Member *mem = member;
+
+  if (NULL != mem->join_dcsn)
+  { /* Only send message to admitted members */
+    message_to_clients (&mem->grp, msg);
+  }
+  return GNUNET_YES;
+}
+
+
+/**
+ * Send message to all origin and member clients connected to the group.
+ *
+ * @param grp  The group to send @a msg to.
+ * @param msg  Message to send.
  */
 static void
-handle_member_join (void *cls, struct GNUNET_SERVER_Client *client,
-                     const struct GNUNET_MessageHeader *msg)
+message_to_group (struct Group *grp, const struct GNUNET_MessageHeader *msg)
 {
+  if (origins != NULL)
+    GNUNET_CONTAINER_multihashmap_get_multiple (origins, &grp->pub_key_hash,
+                                                origin_message_cb, (void *) msg);
+  if (members != NULL)
+    GNUNET_CONTAINER_multihashmap_get_multiple (members, &grp->pub_key_hash,
+                                                member_message_cb, (void *) msg);
+}
+
 
+/**
+ * Send message to all origin clients connected to the group.
+ *
+ * @param grp  The group to send @a msg to.
+ * @param msg  Message to send.
+ */
+static void
+message_to_origin (struct Group *grp, const struct GNUNET_MessageHeader *msg)
+{
+  if (origins != NULL)
+    GNUNET_CONTAINER_multihashmap_get_multiple (origins, &grp->pub_key_hash,
+                                                origin_message_cb, (void *) msg);
 }
 
 
 /**
- * Handle a client parting a group.
+ * Handle a connecting client starting an origin.
  */
 static void
-handle_member_part (void *cls, struct GNUNET_SERVER_Client *client,
-                    const struct GNUNET_MessageHeader *msg)
+client_origin_start (void *cls, struct GNUNET_SERVER_Client *client,
+                     const struct GNUNET_MessageHeader *m)
 {
+  const struct MulticastOriginStartMessage *
+    msg = (const struct MulticastOriginStartMessage *) m;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
+  struct GNUNET_HashCode pub_key_hash;
+
+  GNUNET_CRYPTO_eddsa_key_get_public (&msg->group_key, &pub_key);
+  GNUNET_CRYPTO_hash (&pub_key, sizeof (pub_key), &pub_key_hash);
+
+  struct Origin *
+    orig = GNUNET_CONTAINER_multihashmap_get (origins, &pub_key_hash);
+  struct Group *grp;
+
+  if (NULL == orig)
+  {
+    orig = GNUNET_new (struct Origin);
+    orig->priv_key = msg->group_key;
+    orig->max_fragment_id = GNUNET_ntohll (msg->max_fragment_id);
+    grp = &orig->grp;
+    grp->is_origin = GNUNET_YES;
+    grp->pub_key = pub_key;
+    grp->pub_key_hash = pub_key_hash;
+
+    GNUNET_CONTAINER_multihashmap_put (origins, &grp->pub_key_hash, orig,
+                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  }
+  else
+  {
+    grp = &orig->grp;
+  }
+
+  struct ClientList *cl = GNUNET_new (struct ClientList);
+  cl->client = client;
+  GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Client connected as origin to group %s.\n",
+              orig, GNUNET_h2s (&grp->pub_key_hash));
+
+  GNUNET_SERVER_client_set_user_context (client, grp);
+  GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
 
+
+/**
+ * Handle a connecting client joining a group.
+ */
+static void
+client_member_join (void *cls, struct GNUNET_SERVER_Client *client,
+                    const struct GNUNET_MessageHeader *m)
+{
+  const struct MulticastMemberJoinMessage *
+    msg = (const struct MulticastMemberJoinMessage *) m;
+  uint16_t msg_size = ntohs (msg->header.size);
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey mem_pub_key;
+  struct GNUNET_HashCode pub_key_hash, mem_pub_key_hash;
+
+  GNUNET_CRYPTO_ecdsa_key_get_public (&msg->member_key, &mem_pub_key);
+  GNUNET_CRYPTO_hash (&mem_pub_key, sizeof (mem_pub_key), &mem_pub_key_hash);
+  GNUNET_CRYPTO_hash (&msg->group_key, sizeof (msg->group_key), &pub_key_hash);
+
+  struct GNUNET_CONTAINER_MultiHashMap *
+    grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members, &pub_key_hash);
+  struct Member *mem = NULL;
+  struct Group *grp;
+
+  if (NULL != grp_mem)
+  {
+    mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &mem_pub_key_hash);
+  }
+  if (NULL == mem)
+  {
+    mem = GNUNET_new (struct Member);
+    mem->priv_key = msg->member_key;
+    mem->pub_key = mem_pub_key;
+    mem->pub_key_hash = mem_pub_key_hash;
+    mem->max_fragment_id = 0; // FIXME
+
+    grp = &mem->grp;
+    grp->is_origin = GNUNET_NO;
+    grp->pub_key = msg->group_key;
+    grp->pub_key_hash = pub_key_hash;
+
+    if (NULL == grp_mem)
+    {
+      grp_mem = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+      GNUNET_CONTAINER_multihashmap_put (group_members, &grp->pub_key_hash, grp_mem,
+                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+    }
+    GNUNET_CONTAINER_multihashmap_put (grp_mem, &mem->pub_key_hash, mem,
+                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+    GNUNET_CONTAINER_multihashmap_put (members, &grp->pub_key_hash, mem,
+                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+  }
+  else
+  {
+    grp = &mem->grp;
+  }
+
+  struct ClientList *cl = GNUNET_new (struct ClientList);
+  cl->client = client;
+  GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Client connected to group %s..\n",
+              mem, GNUNET_h2s (&grp->pub_key_hash));
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p ..as member %s.\n",
+              mem, GNUNET_h2s (&mem_pub_key_hash));
+
+  GNUNET_SERVER_client_set_user_context (client, grp);
+
+  if (NULL != mem->join_dcsn)
+  { /* Already got a join decision, send it to client. */
+    GNUNET_SERVER_notification_context_add (nc, client);
+    GNUNET_SERVER_notification_context_unicast (nc, client,
+                                                (struct GNUNET_MessageHeader *)
+                                                mem->join_dcsn,
+                                                GNUNET_NO);
+  }
+  else if (grp->clients_head == grp->clients_tail)
+  { /* First client of the group, send join request. */
+    struct GNUNET_PeerIdentity *relays = (struct GNUNET_PeerIdentity *) &msg[1];
+    uint32_t relay_count = ntohs (msg->relay_count);
+    uint16_t relay_size = relay_count * sizeof (*relays);
+    struct GNUNET_MessageHeader *join_msg = NULL;
+    uint16_t join_msg_size = 0;
+    if (sizeof (*msg) + relay_size + sizeof (struct GNUNET_MessageHeader)
+        <= msg_size)
+    {
+      join_msg = (struct GNUNET_MessageHeader *)
+        (((char *) &msg[1]) + relay_size);
+      join_msg_size = ntohs (join_msg->size);
+    }
+    if (sizeof (*msg) + relay_size + join_msg_size != msg_size)
+    {
+      GNUNET_break (0);
+      GNUNET_SERVER_client_disconnect (client);
+      return;
+    }
+
+    struct MulticastJoinRequestMessage *
+      req = GNUNET_malloc (sizeof (*req) + join_msg_size);
+    req->header.size = htons (sizeof (*req) + join_msg_size);
+    req->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST);
+    req->group_key = grp->pub_key;
+    req->member_peer = this_peer;
+    GNUNET_CRYPTO_ecdsa_key_get_public (&mem->priv_key, &req->member_key);
+    if (0 < join_msg_size)
+      memcpy (&req[1], join_msg, join_msg_size);
+
+    req->purpose.size = htonl (sizeof (*req) + join_msg_size
+                               - sizeof (req->header)
+                               - sizeof (req->signature));
+    req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
+
+    if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_sign (&mem->priv_key, &req->purpose,
+                                               &req->signature))
+    {
+      /* FIXME: handle error */
+      GNUNET_assert (0);
+    }
+
+    if (NULL != mem->join_req)
+      GNUNET_free (mem->join_req);
+    mem->join_req = req;
+
+    if (GNUNET_YES
+        == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
+    { /* Local origin */
+      message_to_origin (grp, (struct GNUNET_MessageHeader *) mem->join_req);
+    }
+    else
+    {
+      /* FIXME: send join request to remote peers */
+    }
+  }
+  GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
 
 /**
- * Incoming message from a client.
+ * Join decision from client.
  */
 static void
-handle_multicast_message (void *cls, struct GNUNET_SERVER_Client *client,
-                         const struct GNUNET_MessageHeader *msg)
+client_join_decision (void *cls, struct GNUNET_SERVER_Client *client,
+                      const struct GNUNET_MessageHeader *m)
 {
+  struct Group *
+    grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
+  const struct MulticastJoinDecisionMessageHeader *
+    hdcsn = (const struct MulticastJoinDecisionMessageHeader *) m;
+  const struct MulticastJoinDecisionMessage *
+    dcsn = (const struct MulticastJoinDecisionMessage *) &hdcsn[1];
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Got join decision from client for group %s..\n",
+              grp, GNUNET_h2s (&grp->pub_key_hash));
+
+  if (GNUNET_YES
+      == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
+  { /* Local origin */
+    struct GNUNET_CONTAINER_MultiHashMap *
+      grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members,
+                                                   &grp->pub_key_hash);
+    if (NULL != grp_mem)
+    {
+      struct GNUNET_HashCode member_key_hash;
+      GNUNET_CRYPTO_hash (&hdcsn->member_key, sizeof (hdcsn->member_key),
+                          &member_key_hash);
+      struct Member *
+        mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &member_key_hash);
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "%p ..and member %s: %p\n",
+                  grp, GNUNET_h2s (&member_key_hash), mem);
+      if (NULL != mem)
+      {
+        message_to_clients (&mem->grp, (struct GNUNET_MessageHeader *) hdcsn);
+        if (GNUNET_YES == ntohl (dcsn->is_admitted))
+        { /* Member admitted, store join_decision. */
+          uint16_t dcsn_size = ntohs (dcsn->header.size);
+          mem->join_dcsn = GNUNET_malloc (dcsn_size);
+          memcpy (mem->join_dcsn, dcsn, dcsn_size);
+        }
+        else
+        { /* Refused entry, disconnect clients. */
+          struct ClientList *cl = mem->grp.clients_head;
+          while (NULL != cl)
+          {
+            struct GNUNET_SERVER_Client *client = cl->client;
+            cl = cl->next;
+            GNUNET_SERVER_client_disconnect (client);
+          }
+        }
+      }
+    }
+  }
+  else
+  {
+    /* FIXME: send join decision to hdcsn->peer */
+  }
+  GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
 
+/**
+ * Incoming message from a client.
+ */
+static void
+client_multicast_message (void *cls, struct GNUNET_SERVER_Client *client,
+                          const struct GNUNET_MessageHeader *m)
+{
+  struct Group *
+    grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
+  GNUNET_assert (GNUNET_YES == grp->is_origin);
+  struct Origin *orig = (struct Origin *) grp;
+  struct GNUNET_MULTICAST_MessageHeader *
+    msg = (struct GNUNET_MULTICAST_MessageHeader *) m;
+
+  msg->fragment_id = GNUNET_htonll (++orig->max_fragment_id);
+  msg->purpose.size = htonl (sizeof (*msg) + ntohs (m->size)
+                             - sizeof (msg->header)
+                             - sizeof (msg->hop_counter)
+                             - sizeof (msg->signature));
+  msg->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_MESSAGE);
+
+  if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&orig->priv_key, &msg->purpose,
+                                             &msg->signature))
+  {
+    /* FIXME: handle error */
+    GNUNET_assert (0);
+  }
+
+  /* FIXME: send to remote members */
+
+  message_to_group (grp, m);
+  GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
 
@@ -99,51 +690,102 @@ handle_multicast_message (void *cls, struct GNUNET_SERVER_Client *client,
  * Incoming request from a client.
  */
 static void
-handle_multicast_request (void *cls, struct GNUNET_SERVER_Client *client,
-                         const struct GNUNET_MessageHeader *msg)
+client_multicast_request (void *cls, struct GNUNET_SERVER_Client *client,
+                          const struct GNUNET_MessageHeader *m)
 {
-
+  struct Group *
+    grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
+  GNUNET_assert (GNUNET_NO == grp->is_origin);
+  struct Member *mem = (struct Member *) grp;
+
+  struct GNUNET_MULTICAST_RequestHeader *
+    req = (struct GNUNET_MULTICAST_RequestHeader *) m;
+
+  req->fragment_id = GNUNET_ntohll (++mem->max_fragment_id);
+  req->purpose.size = htonl (sizeof (*req) + ntohs (m->size)
+                             - sizeof (req->header)
+                             - sizeof (req->member_key)
+                             - sizeof (req->signature));
+  req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
+
+  if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_sign (&mem->priv_key, &req->purpose,
+                                             &req->signature))
+  {
+    /* FIXME: handle error */
+    GNUNET_assert (0);
+  }
+
+  if (GNUNET_YES
+      == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
+  { /* Local origin */
+    message_to_origin (grp, m);
+  }
+  else
+  {
+    /* FIXME: send to remote origin */
+  }
+  GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
 
+
 /**
- * Process multicast requests.
- *
- * @param cls closure
- * @param server the initialized server
- * @param cfg configuration to use
+ * Connected to core service.
  */
 static void
-run (void *cls, struct GNUNET_SERVER_Handle *server,
-     const struct GNUNET_CONFIGURATION_Handle *cfg)
+core_connected_cb  (void *cls, const struct GNUNET_PeerIdentity *my_identity)
 {
+  this_peer = *my_identity;
+
   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
-    { &handle_origin_start, NULL,
+    { &client_origin_start, NULL,
       GNUNET_MESSAGE_TYPE_MULTICAST_ORIGIN_START, 0 },
 
-    { &handle_origin_stop, NULL,
-      GNUNET_MESSAGE_TYPE_MULTICAST_ORIGIN_STOP, 0 },
-
-    { &handle_member_join, NULL,
+    { &client_member_join, NULL,
       GNUNET_MESSAGE_TYPE_MULTICAST_MEMBER_JOIN, 0 },
 
-    { &handle_member_part, NULL,
-      GNUNET_MESSAGE_TYPE_MULTICAST_MEMBER_PART, 0 },
+    { &client_join_decision, NULL,
+      GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION, 0 },
 
-    { &handle_multicast_message, NULL,
+    { &client_multicast_message, NULL,
       GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE, 0 },
 
-    { &handle_multicast_request, NULL,
+    { &client_multicast_request, NULL,
       GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST, 0 },
 
     {NULL, NULL, 0, 0}
   };
-  /* FIXME: do setup here */
+
+  stats = GNUNET_STATISTICS_create ("multicast", cfg);
+  origins = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+  members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+  group_members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+  nc = GNUNET_SERVER_notification_context_create (server, 1);
+
   GNUNET_SERVER_add_handlers (server, handlers);
-  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
+  GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL);
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
                                 NULL);
 }
 
 
+/**
+ * Service started.
+ *
+ * @param cls closure
+ * @param server the initialized server
+ * @param cfg configuration to use
+ */
+static void
+run (void *cls, struct GNUNET_SERVER_Handle *srv,
+     const struct GNUNET_CONFIGURATION_Handle *c)
+{
+  cfg = c;
+  server = srv;
+  core = GNUNET_CORE_connect (cfg, NULL, core_connected_cb, NULL, NULL,
+                              NULL, GNUNET_NO, NULL, GNUNET_NO, NULL);
+}
+
+
 /**
  * The main function for the multicast service.
  *