fixing unchecked return values
[oweals/gnunet.git] / src / multicast / multicast_api.c
index b49d210323dbfa290adbe352f55944c7a0cc316e..b23320c72502a7078a621310c5992c0099872958 100644 (file)
      Boston, MA 02111-1307, USA.
 */
 
-/** 
+/**
  * @file multicast/multicast_api.c
  * @brief multicast service; establish tunnels to distant peers
  * @author Christian Grothoff
  * @author Gabor X Toth
  */
 #include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_signatures.h"
 #include "gnunet_multicast_service.h"
+#include "multicast.h"
 
-/** 
- * Opaque handle for a multicast group member.
+#define LOG(kind,...) GNUNET_log_from (kind, "multicast-api",__VA_ARGS__)
+
+
+/**
+ * Started origins.
+ * Group's pub_key_hash -> struct GNUNET_MULTICAST_Origin
  */
-struct GNUNET_MULTICAST_Member
+static struct GNUNET_CONTAINER_MultiHashMap *origins;
+
+/**
+ * Joined members.
+ * group_key_hash -> struct GNUNET_MULTICAST_Member
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *members;
+
+
+/**
+ * Handle for a request to send a message to all multicast group members
+ * (from the origin).
+ */
+struct GNUNET_MULTICAST_OriginMessageHandle
 {
+  GNUNET_MULTICAST_OriginTransmitNotify notify;
+  void *notify_cls;
+  struct GNUNET_MULTICAST_Origin *origin;
+
+  uint64_t message_id;
+  uint64_t group_generation;
+  uint64_t fragment_offset;
 };
 
 
-/** 
+struct GNUNET_MULTICAST_Group
+{
+  uint8_t is_origin;
+};
+
+/**
  * Handle for the origin of a multicast group.
  */
 struct GNUNET_MULTICAST_Origin
 {
-};
+  struct GNUNET_MULTICAST_Group grp;
+
+  struct GNUNET_MULTICAST_OriginMessageHandle msg_handle;
+  struct GNUNET_CRYPTO_EddsaPrivateKey priv_key;
+
+  GNUNET_MULTICAST_JoinCallback join_cb;
+  GNUNET_MULTICAST_MembershipTestCallback mem_test_cb;
+  GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb;
+  GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb;
+  GNUNET_MULTICAST_RequestCallback request_cb;
+  GNUNET_MULTICAST_MessageCallback message_cb;
+  void *cls;
 
+  uint64_t next_fragment_id;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
+  struct GNUNET_HashCode pub_key_hash;
+};
 
-GNUNET_NETWORK_STRUCT_BEGIN
 
-/** 
- * Header of a request from a member to the origin.
+/**
+ * Handle for a message to be delivered from a member to the origin.
  */
-struct GNUNET_MULTICAST_RequestHeader
+struct GNUNET_MULTICAST_MemberRequestHandle
 {
-  /** 
-   * Header for all requests from a member to the origin.
-   */
-  struct GNUNET_MessageHeader header;
+  GNUNET_MULTICAST_MemberTransmitNotify notify;
+  void *notify_cls;
+  struct GNUNET_MULTICAST_Member *member;
 
-  /**
-   * Public key of the sending member.
-   */
-  struct GNUNET_CRYPTO_EccPublicSignKey member_key;
+  uint64_t request_id;
+  uint64_t fragment_offset;
+};
 
-  /** 
-   * ECC signature of the request fragment.
-   *
-   * Signature must match the public key of the multicast group.
-   */
-  struct GNUNET_CRYPTO_EccSignature signature;
 
-  /** 
-   * Purpose for the signature and size of the signed data.
-   */
-  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+/**
+ * Handle for a multicast group member.
+ */
+struct GNUNET_MULTICAST_Member
+{
+  struct GNUNET_MULTICAST_Group grp;
+
+  struct GNUNET_MULTICAST_MemberRequestHandle req_handle;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey group_key;
+  struct GNUNET_CRYPTO_EddsaPrivateKey member_key;
+  struct GNUNET_PeerIdentity origin;
+  struct GNUNET_PeerIdentity relays;
+  uint32_t relay_count;
+  struct GNUNET_MessageHeader *join_request;
+  GNUNET_MULTICAST_JoinCallback join_cb;
+  GNUNET_MULTICAST_MembershipTestCallback member_test_cb;
+  GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb;
+  GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb;
+  GNUNET_MULTICAST_MessageCallback message_cb;
+  void *cls;
+
+  uint64_t next_fragment_id;
+  struct GNUNET_HashCode group_key_hash;
+};
 
-  /** 
-   * Number of the request fragment, monotonically increasing.
-   */
-  uint64_t fragment_id GNUNET_PACKED;
 
-  /** 
-   * Byte offset of this @e fragment of the @e request.
-   */
-  uint64_t fragment_offset GNUNET_PACKED;
+/**
+ * Handle that identifies a join request.
+ *
+ * Used to match calls to #GNUNET_MULTICAST_JoinCallback to the
+ * corresponding calls to #GNUNET_MULTICAST_join_decision().
+ */
+struct GNUNET_MULTICAST_JoinHandle
+{
+};
 
-  /** 
-   * Number of the request this fragment belongs to.
-   *
-   * Set in GNUNET_MULTICAST_origin_to_all().
-   */
-  uint64_t request_id GNUNET_PACKED;
 
-  /**
-   * Flags for this request.
-   */
-  enum GNUNET_MULTICAST_MessageFlags flags GNUNET_PACKED;
+/**
+ * Handle to pass back for the answer of a membership test.
+ */
+struct GNUNET_MULTICAST_MembershipTestHandle
+{
+};
 
-  /* Followed by request body. */
+
+/**
+ * Opaque handle to a replay request from the multicast service.
+ */
+struct GNUNET_MULTICAST_ReplayHandle
+{
 };
 
-/** 
- * Header of a join request sent to the origin or another member.
+
+/**
+ * Handle for a replay request.
  */
-struct GNUNET_MULTICAST_JoinRequest
+struct GNUNET_MULTICAST_MemberReplayHandle
 {
-  /** 
-   * Header for the join request.
-   */
-  struct GNUNET_MessageHeader header;
+};
 
-  /** 
-   * ECC signature of the rest of the fields of the join request.
-   *
-   * Signature must match the public key of the joining member.
-   */
-  struct GNUNET_CRYPTO_EccSignature signature;
 
-  /** 
-   * Purpose for the signature and size of the signed data.
-   */
-  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+/**
+ * Iterator callback for calling message callbacks for all groups.
+ */
+static int
+message_callback (void *cls, const struct GNUNET_HashCode *chan_key_hash,
+                   void *group)
+{
+  const struct GNUNET_MessageHeader *msg = cls;
+  struct GNUNET_MULTICAST_Group *grp = group;
+
+  if (GNUNET_YES == grp->is_origin)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Calling origin's message callback "
+                "for a message of type %u and size %u.\n",
+              ntohs (msg->type), ntohs (msg->size));
+    struct GNUNET_MULTICAST_Origin *orig = (struct GNUNET_MULTICAST_Origin *) grp;
+    orig->message_cb (orig->cls, msg);
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Calling slave's message callback "
+                "for a message of type %u and size %u.\n",
+                ntohs (msg->type), ntohs (msg->size));
+    struct GNUNET_MULTICAST_Member *mem = (struct GNUNET_MULTICAST_Member *) grp;
+    mem->message_cb (mem->cls, msg);
+  }
+
+  return GNUNET_YES;
+}
 
-  /**
-   * Public key of the target group.
-   */
-  struct GNUNET_CRYPTO_EccPublicSignKey group_key;
 
-  /**
-   * Public key of the joining member.
-   */
-  struct GNUNET_CRYPTO_EccPublicSignKey member_key;
+/**
+ * Handle a multicast message from the service.
+ *
+ * Call message callbacks of all origins and members of the destination group.
+ *
+ * @param grp Destination group of the message.
+ * @param msg The message.
+ */
+static void
+handle_multicast_message (struct GNUNET_MULTICAST_Group *grp,
+                          const struct GNUNET_MULTICAST_MessageHeader *msg)
+{
+  struct GNUNET_HashCode *hash;
+
+  if (GNUNET_YES == grp->is_origin)
+  {
+    struct GNUNET_MULTICAST_Origin *orig = (struct GNUNET_MULTICAST_Origin *) grp;
+    hash = &orig->pub_key_hash;
+  }
+  else
+  {
+    struct GNUNET_MULTICAST_Member *mem = (struct GNUNET_MULTICAST_Member *) grp;
+    hash = &mem->group_key_hash;
+  }
+
+  if (origins != NULL)
+    GNUNET_CONTAINER_multihashmap_get_multiple (origins, hash, message_callback,
+                                                (void *) msg);
+  if (members != NULL)
+    GNUNET_CONTAINER_multihashmap_get_multiple (members, hash, message_callback,
+                                                (void *) msg);
+}
 
-  /**
-   * Peer identity of the joining member.
-   */
-  struct GNUNET_PeerIdentity member_peer;
 
-  /* Followed by request body. */
-};
+/**
+ * Iterator callback for calling request callbacks of origins.
+ */
+static int
+request_callback (void *cls, const struct GNUNET_HashCode *chan_key_hash,
+                  void *origin)
+{
+  const struct GNUNET_MULTICAST_RequestHeader *req = cls;
+  struct GNUNET_MULTICAST_Origin *orig = origin;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Calling request callback for a request of type %u and size %u.\n",
+              ntohs (req->header.type), ntohs (req->header.size));
 
-GNUNET_NETWORK_STRUCT_END
+  orig->request_cb (orig->cls, &req->member_key,
+                    (const struct GNUNET_MessageHeader *) req, 0);
+  return GNUNET_YES;
+}
 
 
-/** 
- * Handle that identifies a join request.
+/**
+ * Handle a multicast request from the service.
  *
- * Used to match calls to #GNUNET_MULTICAST_JoinCallback to the
- * corresponding calls to #GNUNET_MULTICAST_join_decision().
+ * Call request callbacks of all origins of the destination group.
+ *
+ * @param grp Destination group of the message.
+ * @param msg The message.
  */
-struct GNUNET_MULTICAST_JoinHandle
+static void
+handle_multicast_request (const struct GNUNET_HashCode *group_key_hash,
+                          const struct GNUNET_MULTICAST_RequestHeader *req)
 {
-};
+  if (NULL != origins)
+    GNUNET_CONTAINER_multihashmap_get_multiple (origins, group_key_hash,
+                                                request_callback, (void *) req);
+}
 
 
-/** 
+/**
  * Function to call with the decision made for a join request.
  *
  * Must be called once and only once in response to an invocation of the
@@ -184,15 +306,7 @@ GNUNET_MULTICAST_join_decision (struct GNUNET_MULTICAST_JoinHandle *jh,
 }
 
 
-/** 
- * Handle to pass back for the answer of a membership test.
- */
-struct GNUNET_MULTICAST_MembershipTestHandle
-{
-};
-
-
-/** 
+/**
  * Call informing multicast about the decision taken for a membership test.
  *
  * @param mth Handle that was given for the query.
@@ -206,15 +320,7 @@ GNUNET_MULTICAST_membership_test_result (struct GNUNET_MULTICAST_MembershipTestH
 }
 
 
-/** 
- * Opaque handle to a replay request from the multicast service.
- */
-struct GNUNET_MULTICAST_ReplayHandle
-{
-};
-
-
-/** 
+/**
  * Replay a message fragment for the multicast group.
  *
  * @param rh Replay handle identifying which replay operation was requested.
@@ -229,7 +335,7 @@ GNUNET_MULTICAST_replay_response (struct GNUNET_MULTICAST_ReplayHandle *rh,
 }
 
 
-/** 
+/**
  * Indicate the end of the replay session.
  *
  * Invalidates the replay handle.
@@ -242,7 +348,7 @@ GNUNET_MULTICAST_replay_response_end (struct GNUNET_MULTICAST_ReplayHandle *rh)
 }
 
 
-/** 
+/**
  * Replay a message for the multicast group.
  *
  * @param rh Replay handle identifying which replay operation was requested.
@@ -257,7 +363,7 @@ GNUNET_MULTICAST_replay_response2 (struct GNUNET_MULTICAST_ReplayHandle *rh,
 }
 
 
-/** 
+/**
  * Start a multicast group.
  *
  * Will advertise the origin in the P2P overlay network under the respective
@@ -272,10 +378,10 @@ GNUNET_MULTICAST_replay_response2 (struct GNUNET_MULTICAST_ReplayHandle *rh,
  * @param cfg Configuration to use.
  * @param priv_key ECC key that will be used to sign messages for this
  *        multicast session; public key is used to identify the multicast group;
- * @param last_fragment_id Last fragment ID to continue counting fragments from
+ * @param next_fragment_id Next fragment ID to continue counting fragments from
  *        when restarting the origin.  0 for a new group.
  * @param join_cb Function called to approve / disapprove joining of a peer.
- * @param test_cb Function multicast can use to test group membership.
+ * @param mem_test_cb Function multicast can use to test group membership.
  * @param replay_frag_cb Function that can be called to replay a message fragment.
  * @param replay_msg_cb Function that can be called to replay a message.
  * @param request_cb Function called with message fragments from group members.
@@ -287,30 +393,119 @@ GNUNET_MULTICAST_replay_response2 (struct GNUNET_MULTICAST_ReplayHandle *rh,
  */
 struct GNUNET_MULTICAST_Origin *
 GNUNET_MULTICAST_origin_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                               const struct GNUNET_CRYPTO_EccPrivateKey *priv_key,
-                               uint64_t last_fragment_id,
+                               const struct GNUNET_CRYPTO_EddsaPrivateKey *priv_key,
+                               uint64_t next_fragment_id,
                                GNUNET_MULTICAST_JoinCallback join_cb,
-                               GNUNET_MULTICAST_MembershipTestCallback test_cb,
+                               GNUNET_MULTICAST_MembershipTestCallback mem_test_cb,
                                GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb,
                                GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb,
                                GNUNET_MULTICAST_RequestCallback request_cb,
                                GNUNET_MULTICAST_MessageCallback message_cb,
                                void *cls)
 {
-  return NULL;
+  struct GNUNET_MULTICAST_Origin *orig = GNUNET_malloc (sizeof (*orig));
+  orig->grp.is_origin = GNUNET_YES;
+  orig->priv_key = *priv_key;
+  orig->next_fragment_id = next_fragment_id;
+  orig->join_cb = join_cb;
+  orig->mem_test_cb = mem_test_cb;
+  orig->replay_frag_cb = replay_frag_cb;
+  orig->replay_msg_cb = replay_msg_cb;
+  orig->request_cb = request_cb;
+  orig->message_cb = message_cb;
+  orig->cls = cls;
+
+  GNUNET_CRYPTO_eddsa_key_get_public (&orig->priv_key, &orig->pub_key);
+  GNUNET_CRYPTO_hash (&orig->pub_key, sizeof (orig->pub_key),
+                      &orig->pub_key_hash);
+
+  if (NULL == origins)
+    origins = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+
+  GNUNET_CONTAINER_multihashmap_put (origins, &orig->pub_key_hash, orig,
+                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+
+  /* FIXME: send ORIGIN_START to service */
+
+  return orig;
 }
 
 
-/** 
- * Handle for a request to send a message to all multicast group members
- * (from the origin).
+/**
+ * Stop a multicast group.
+ *
+ * @param origin Multicast group to stop.
  */
-struct GNUNET_MULTICAST_OriginMessageHandle
+void
+GNUNET_MULTICAST_origin_stop (struct GNUNET_MULTICAST_Origin *orig)
 {
-};
+  GNUNET_CONTAINER_multihashmap_remove (origins, &orig->pub_key_hash, orig);
+  GNUNET_free (orig);
+}
+
+
+/* FIXME: for now just call clients' callbacks
+ *        without sending anything to multicast. */
+static void
+schedule_origin_to_all (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "schedule_origin_to_all()\n");
+  struct GNUNET_MULTICAST_Origin *orig = cls;
+  struct GNUNET_MULTICAST_OriginMessageHandle *mh = &orig->msg_handle;
+
+  size_t buf_size = GNUNET_MULTICAST_FRAGMENT_MAX_SIZE;
+  char buf[GNUNET_MULTICAST_FRAGMENT_MAX_SIZE] = "";
+  struct GNUNET_MULTICAST_MessageHeader *msg
+    = (struct GNUNET_MULTICAST_MessageHeader *) buf;
+  int ret = mh->notify (mh->notify_cls, &buf_size, &msg[1]);
+
+  if (! (GNUNET_YES == ret || GNUNET_NO == ret)
+      || GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < buf_size)
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "OriginTransmitNotify() returned error or invalid message size.\n");
+    /* FIXME: handle error */
+    return;
+  }
+
+  if (GNUNET_NO == ret && 0 == buf_size)
+    return; /* Transmission paused. */
+
+  msg->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
+  msg->header.size = htons (sizeof (*msg) + buf_size);
+  msg->message_id = GNUNET_htonll (mh->message_id);
+  msg->group_generation = mh->group_generation;
+
+  /* FIXME: add fragment ID and signature in the service instead of here */
+  msg->fragment_id = GNUNET_ntohll (orig->next_fragment_id++);
+  msg->fragment_offset = GNUNET_ntohll (mh->fragment_offset);
+  mh->fragment_offset += sizeof (*msg) + buf_size;
+  msg->purpose.size = htonl (sizeof (*msg) + buf_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 */
+    return;
+  }
+
+  /* FIXME: send msg to the service and only then call handle_multicast_message
+   *        with the returned signed message.
+   */
+  handle_multicast_message (&orig->grp, msg);
+
+  if (GNUNET_NO == ret)
+    GNUNET_SCHEDULER_add_delayed (
+      GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1),
+      schedule_origin_to_all, orig);
+}
 
 
-/** 
+/**
  * Send a message to the multicast group.
  *
  * @param origin Handle to the multicast group.
@@ -328,11 +523,22 @@ GNUNET_MULTICAST_origin_to_all (struct GNUNET_MULTICAST_Origin *origin,
                                 GNUNET_MULTICAST_OriginTransmitNotify notify,
                                 void *notify_cls)
 {
-  return NULL;
+  struct GNUNET_MULTICAST_OriginMessageHandle *mh = &origin->msg_handle;
+  mh->origin = origin;
+  mh->message_id = message_id;
+  mh->group_generation = group_generation;
+  mh->notify = notify;
+  mh->notify_cls = notify_cls;
+
+  /* add some delay for testing */
+  GNUNET_SCHEDULER_add_delayed (
+    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1),
+    schedule_origin_to_all, origin);
+  return &origin->msg_handle;
 }
 
 
-/** 
+/**
  * Resume message transmission to multicast group.
  *
  * @param mh Request to cancel.
@@ -340,11 +546,11 @@ GNUNET_MULTICAST_origin_to_all (struct GNUNET_MULTICAST_Origin *origin,
 void
 GNUNET_MULTICAST_origin_to_all_resume (struct GNUNET_MULTICAST_OriginMessageHandle *mh)
 {
-
+  GNUNET_SCHEDULER_add_now (schedule_origin_to_all, mh->origin);
 }
 
 
-/** 
+/**
  * Cancel request for message transmission to multicast group.
  *
  * @param mh Request to cancel.
@@ -355,18 +561,7 @@ GNUNET_MULTICAST_origin_to_all_cancel (struct GNUNET_MULTICAST_OriginMessageHand
 }
 
 
-/** 
- * Stop a multicast group.
- *
- * @param origin Multicast group to stop.
- */
-void
-GNUNET_MULTICAST_origin_stop (struct GNUNET_MULTICAST_Origin *origin)
-{
-}
-
-
-/** 
+/**
  * Join a multicast group.
  *
  * The entity joining is always the local peer.  Further information about the
@@ -394,7 +589,7 @@ GNUNET_MULTICAST_origin_stop (struct GNUNET_MULTICAST_Origin *origin)
  *        identity/pseudonym to peer identity, application-level message to
  *        origin, etc.).
  * @param join_cb Function called to approve / disapprove joining of a peer.
- * @param test_cb Function multicast can use to test group membership.
+ * @param mem_test_cb Function multicast can use to test group membership.
  * @param replay_frag_cb Function that can be called to replay message fragments
  *        this peer already knows from this group. NULL if this
  *        client is unable to support replay.
@@ -409,32 +604,71 @@ GNUNET_MULTICAST_origin_stop (struct GNUNET_MULTICAST_Origin *origin)
  */
 struct GNUNET_MULTICAST_Member *
 GNUNET_MULTICAST_member_join (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                              const struct GNUNET_CRYPTO_EccPublicSignKey *group_key,
-                              const struct GNUNET_CRYPTO_EccPrivateKey *member_key,
+                              const struct GNUNET_CRYPTO_EddsaPublicKey *group_key,
+                              const struct GNUNET_CRYPTO_EddsaPrivateKey *member_key,
                               const struct GNUNET_PeerIdentity *origin,
                               uint32_t relay_count,
                               const struct GNUNET_PeerIdentity *relays,
                               const struct GNUNET_MessageHeader *join_request,
                               GNUNET_MULTICAST_JoinCallback join_cb,
-                              GNUNET_MULTICAST_MembershipTestCallback test_cb,
+                              GNUNET_MULTICAST_MembershipTestCallback member_test_cb,
                               GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb,
                               GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb,
                               GNUNET_MULTICAST_MessageCallback message_cb,
                               void *cls)
 {
-  return NULL;
+  struct GNUNET_MULTICAST_Member *mem = GNUNET_malloc (sizeof (*mem));
+  mem->group_key = *group_key;
+  mem->member_key = *member_key;
+  mem->origin = *origin;
+  mem->relay_count = relay_count;
+  mem->relays = *relays;
+  mem->join_cb = join_cb;
+  mem->member_test_cb = member_test_cb;
+  mem->replay_frag_cb = replay_frag_cb;
+  mem->message_cb = message_cb;
+  mem->cls = cls;
+
+  if (NULL != join_request)
+  {
+    uint16_t size = ntohs (join_request->size);
+    mem->join_request = GNUNET_malloc (size);
+    memcpy (mem->join_request, join_request, size);
+  }
+
+  GNUNET_CRYPTO_hash (&mem->group_key, sizeof (mem->group_key), &mem->group_key_hash);
+
+  if (NULL == members)
+    members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+
+  GNUNET_CONTAINER_multihashmap_put (members, &mem->group_key_hash, mem,
+                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+
+  /* FIXME: send MEMBER_JOIN to service */
+
+  return mem;
 }
 
 
-/** 
- * Handle for a replay request.
+/**
+ * Part a multicast group.
+ *
+ * Disconnects from all group members and invalidates the @a member handle.
+ *
+ * An application-dependent part message can be transmitted beforehand using
+ * #GNUNET_MULTICAST_member_to_origin())
+ *
+ * @param member Membership handle.
  */
-struct GNUNET_MULTICAST_MemberReplayHandle
+void
+GNUNET_MULTICAST_member_part (struct GNUNET_MULTICAST_Member *mem)
 {
-};
+  GNUNET_CONTAINER_multihashmap_remove (members, &mem->group_key_hash, mem);
+  GNUNET_free (mem);
+}
 
 
-/** 
+/**
  * Request a fragment to be replayed by fragment ID.
  *
  * Useful if messages below the @e max_known_fragment_id given when joining are
@@ -457,7 +691,7 @@ GNUNET_MULTICAST_member_replay_fragment (struct GNUNET_MULTICAST_Member *member,
 }
 
 
-/** 
+/**
  * Request a message fragment to be replayed.
  *
  * Useful if messages below the @e max_known_fragment_id given when joining are
@@ -484,7 +718,7 @@ GNUNET_MULTICAST_member_replay_message (struct GNUNET_MULTICAST_Member *member,
 }
 
 
-/** 
+/**
  * Cancel a replay request.
  *
  * @param rh Request to cancel.
@@ -495,50 +729,95 @@ GNUNET_MULTICAST_member_replay_cancel (struct GNUNET_MULTICAST_MemberReplayHandl
 }
 
 
-/** 
- * Part a multicast group.
- *
- * Disconnects from all group members and invalidates the @a member handle.
- *
- * An application-dependent part message can be transmitted beforehand using
- * #GNUNET_MULTICAST_member_to_origin())
- *
- * @param member Membership handle.
- */
-void
-GNUNET_MULTICAST_member_part (struct GNUNET_MULTICAST_Member *member)
+/* FIXME: for now just send back to the client what it sent. */
+static void
+schedule_member_to_origin (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-}
-
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "schedule_member_to_origin()\n");
+  struct GNUNET_MULTICAST_Member *mem = cls;
+  struct GNUNET_MULTICAST_MemberRequestHandle *rh = &mem->req_handle;
+
+  size_t buf_size = GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD;
+  char buf[GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD] = "";
+  struct GNUNET_MULTICAST_RequestHeader *req
+    = (struct GNUNET_MULTICAST_RequestHeader *) buf;
+  int ret = rh->notify (rh->notify_cls, &buf_size, &req[1]);
+
+  if (! (GNUNET_YES == ret || GNUNET_NO == ret)
+      || GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < buf_size)
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "MemberTransmitNotify() returned error or invalid message size.\n");
+    /* FIXME: handle error */
+    return;
+  }
+
+  if (GNUNET_NO == ret && 0 == buf_size)
+    return; /* Transmission paused. */
+
+  req->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST);
+  req->header.size = htons (sizeof (*req) + buf_size);
+  req->request_id = GNUNET_htonll (rh->request_id);
+
+  /* FIXME: add fragment ID and signature in the service instead of here */
+  req->fragment_id = GNUNET_ntohll (mem->next_fragment_id++);
+  req->fragment_offset = GNUNET_ntohll (rh->fragment_offset);
+  rh->fragment_offset += sizeof (*req) + buf_size;
+  req->purpose.size = htonl (sizeof (*req) + buf_size
+                             - sizeof (req->header)
+                             - sizeof (req->member_key)
+                             - sizeof (req->signature));
+  req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_MESSAGE);
+
+  if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&mem->member_key, &req->purpose,
+                                           &req->signature))
+  {
+    /* FIXME: handle error */
+    return;
+  }
+
+  /* FIXME: send req to the service and only then call handle_multicast_request
+   *        with the returned request.
+   */
+  handle_multicast_request (&mem->group_key_hash, req);
 
-/** 
- * Handle for a message to be delivered from a member to the origin.
- */
-struct GNUNET_MULTICAST_MemberRequestHandle
-{
-};
+  if (GNUNET_NO == ret)
+    GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
+                                  (GNUNET_TIME_UNIT_SECONDS, 1),
+                                  schedule_member_to_origin, mem);
+}
 
 
-/** 
+/**
  * Send a message to the origin of the multicast group.
- * 
+ *
  * @param member Membership handle.
- * @param message_id Application layer ID for the message.  Opaque to multicast.
+ * @param request_id Application layer ID for the request.  Opaque to multicast.
  * @param notify Callback to call to get the message.
  * @param notify_cls Closure for @a notify.
  * @return Handle to cancel request, NULL on error (i.e. request already pending).
  */
 struct GNUNET_MULTICAST_MemberRequestHandle *
 GNUNET_MULTICAST_member_to_origin (struct GNUNET_MULTICAST_Member *member,
-                                   uint64_t message_id,
+                                   uint64_t request_id,
                                    GNUNET_MULTICAST_MemberTransmitNotify notify,
                                    void *notify_cls)
 {
-  return NULL;
+  struct GNUNET_MULTICAST_MemberRequestHandle *rh = &member->req_handle;
+  rh->member = member;
+  rh->request_id = request_id;
+  rh->notify = notify;
+  rh->notify_cls = notify_cls;
+
+  /* FIXME: remove delay, it's there only for testing */
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
+                                (GNUNET_TIME_UNIT_SECONDS, 1),
+                                schedule_member_to_origin, member);
+  return &member->req_handle;
 }
 
 
-/** 
+/**
  * Resume message transmission to origin.
  *
  * @param rh Request to cancel.
@@ -550,7 +829,7 @@ GNUNET_MULTICAST_member_to_origin_resume (struct GNUNET_MULTICAST_MemberRequestH
 }
 
 
-/** 
+/**
  * Cancel request for message transmission to origin.
  *
  * @param rh Request to cancel.