25af614afc94b959036ed16a4382262ab94811fd
[oweals/gnunet.git] / src / multicast / multicast_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012, 2013 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19 */
20
21 /**
22  * @file multicast/multicast_api.c
23  * @brief multicast service; establish tunnels to distant peers
24  * @author Christian Grothoff
25  * @author Gabor X Toth
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_signatures.h"
30 #include "gnunet_multicast_service.h"
31 #include "multicast.h"
32
33 #define LOG(kind,...) GNUNET_log_from (kind, "multicast-api",__VA_ARGS__)
34
35
36 /**
37  * Started origins.
38  * Group's pub_key_hash -> struct GNUNET_MULTICAST_Origin
39  */
40 static struct GNUNET_CONTAINER_MultiHashMap *origins;
41
42 /**
43  * Joined members.
44  * group_key_hash -> struct GNUNET_MULTICAST_Member
45  */
46 static struct GNUNET_CONTAINER_MultiHashMap *members;
47
48
49 /**
50  * Handle for a request to send a message to all multicast group members
51  * (from the origin).
52  */
53 struct GNUNET_MULTICAST_OriginMessageHandle
54 {
55   GNUNET_MULTICAST_OriginTransmitNotify notify;
56   void *notify_cls;
57   struct GNUNET_MULTICAST_Origin *origin;
58
59   uint64_t message_id;
60   uint64_t group_generation;
61   uint64_t fragment_offset;
62 };
63
64
65 struct GNUNET_MULTICAST_Group
66 {
67   uint8_t is_origin;
68 };
69
70 /**
71  * Handle for the origin of a multicast group.
72  */
73 struct GNUNET_MULTICAST_Origin
74 {
75   struct GNUNET_MULTICAST_Group grp;
76
77   struct GNUNET_MULTICAST_OriginMessageHandle msg_handle;
78   struct GNUNET_CRYPTO_EddsaPrivateKey priv_key;
79
80   GNUNET_MULTICAST_JoinCallback join_cb;
81   GNUNET_MULTICAST_MembershipTestCallback mem_test_cb;
82   GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb;
83   GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb;
84   GNUNET_MULTICAST_RequestCallback request_cb;
85   GNUNET_MULTICAST_MessageCallback message_cb;
86   void *cls;
87
88   uint64_t next_fragment_id;
89
90   struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
91   struct GNUNET_HashCode pub_key_hash;
92 };
93
94
95 /**
96  * Handle for a message to be delivered from a member to the origin.
97  */
98 struct GNUNET_MULTICAST_MemberRequestHandle
99 {
100   GNUNET_MULTICAST_MemberTransmitNotify notify;
101   void *notify_cls;
102   struct GNUNET_MULTICAST_Member *member;
103
104   uint64_t request_id;
105   uint64_t fragment_offset;
106 };
107
108
109 /**
110  * Handle for a multicast group member.
111  */
112 struct GNUNET_MULTICAST_Member
113 {
114   struct GNUNET_MULTICAST_Group grp;
115
116   struct GNUNET_MULTICAST_MemberRequestHandle req_handle;
117
118   struct GNUNET_CRYPTO_EddsaPublicKey group_key;
119   struct GNUNET_CRYPTO_EddsaPrivateKey member_key;
120   struct GNUNET_PeerIdentity origin;
121   struct GNUNET_PeerIdentity relays;
122   uint32_t relay_count;
123   struct GNUNET_MessageHeader *join_request;
124   GNUNET_MULTICAST_JoinCallback join_cb;
125   GNUNET_MULTICAST_MembershipTestCallback member_test_cb;
126   GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb;
127   GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb;
128   GNUNET_MULTICAST_MessageCallback message_cb;
129   void *cls;
130
131   uint64_t next_fragment_id;
132   struct GNUNET_HashCode group_key_hash;
133 };
134
135
136 /**
137  * Handle that identifies a join request.
138  *
139  * Used to match calls to #GNUNET_MULTICAST_JoinCallback to the
140  * corresponding calls to #GNUNET_MULTICAST_join_decision().
141  */
142 struct GNUNET_MULTICAST_JoinHandle
143 {
144 };
145
146
147 /**
148  * Handle to pass back for the answer of a membership test.
149  */
150 struct GNUNET_MULTICAST_MembershipTestHandle
151 {
152 };
153
154
155 /**
156  * Opaque handle to a replay request from the multicast service.
157  */
158 struct GNUNET_MULTICAST_ReplayHandle
159 {
160 };
161
162
163 /**
164  * Handle for a replay request.
165  */
166 struct GNUNET_MULTICAST_MemberReplayHandle
167 {
168 };
169
170
171 /**
172  * Iterator callback for calling message callbacks for all groups.
173  */
174 static int
175 message_callback (void *cls, const struct GNUNET_HashCode *chan_key_hash,
176                    void *group)
177 {
178   const struct GNUNET_MessageHeader *msg = cls;
179   struct GNUNET_MULTICAST_Group *grp = group;
180
181   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
182               "Calling message callback for a message of type %u and size %u.\n",
183               ntohs (msg->type), ntohs (msg->size));
184
185   if (GNUNET_YES == grp->is_origin)
186   {
187     struct GNUNET_MULTICAST_Origin *orig = (struct GNUNET_MULTICAST_Origin *) grp;
188     orig->message_cb (orig->cls, msg);
189   }
190   else
191   {
192     struct GNUNET_MULTICAST_Member *mem = (struct GNUNET_MULTICAST_Member *) grp;
193     mem->message_cb (mem->cls, msg);
194   }
195
196   return GNUNET_YES;
197 }
198
199
200 /**
201  * Handle a multicast message from the service.
202  *
203  * Call message callbacks of all origins and members of the destination group.
204  *
205  * @param grp Destination group of the message.
206  * @param msg The message.
207  */
208 static void
209 handle_multicast_message (struct GNUNET_MULTICAST_Group *grp,
210                           const struct GNUNET_MULTICAST_MessageHeader *msg)
211 {
212   struct GNUNET_HashCode *hash;
213
214   if (GNUNET_YES == grp->is_origin)
215   {
216     struct GNUNET_MULTICAST_Origin *orig = (struct GNUNET_MULTICAST_Origin *) grp;
217     hash = &orig->pub_key_hash;
218   }
219   else
220   {
221     struct GNUNET_MULTICAST_Member *mem = (struct GNUNET_MULTICAST_Member *) grp;
222     hash = &mem->group_key_hash;
223   }
224
225   if (origins != NULL)
226     GNUNET_CONTAINER_multihashmap_get_multiple (origins, hash, message_callback,
227                                                 (void *) msg);
228   if (members != NULL)
229     GNUNET_CONTAINER_multihashmap_get_multiple (members, hash, message_callback,
230                                                 (void *) msg);
231 }
232
233
234 /**
235  * Iterator callback for calling request callbacks of origins.
236  */
237 static int
238 request_callback (void *cls, const struct GNUNET_HashCode *chan_key_hash,
239                   void *origin)
240 {
241   const struct GNUNET_MULTICAST_RequestHeader *req = cls;
242   struct GNUNET_MULTICAST_Origin *orig = origin;
243
244   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
245               "Calling request callback for a request of type %u and size %u.\n",
246               ntohs (req->header.type), ntohs (req->header.size));
247
248   orig->request_cb (orig->cls, &req->member_key,
249                     (const struct GNUNET_MessageHeader *) req, 0);
250   return GNUNET_YES;
251 }
252
253
254 /**
255  * Handle a multicast request from the service.
256  *
257  * Call request callbacks of all origins of the destination group.
258  *
259  * @param grp Destination group of the message.
260  * @param msg The message.
261  */
262 static void
263 handle_multicast_request (const struct GNUNET_HashCode *group_key_hash,
264                           const struct GNUNET_MULTICAST_RequestHeader *req)
265 {
266   if (NULL != origins)
267     GNUNET_CONTAINER_multihashmap_get_multiple (origins, group_key_hash,
268                                                 request_callback, (void *) req);
269 }
270
271
272 /**
273  * Function to call with the decision made for a join request.
274  *
275  * Must be called once and only once in response to an invocation of the
276  * #GNUNET_MULTICAST_JoinCallback.
277  *
278  * @param jh Join request handle.
279  * @param is_admitted #GNUNET_YES if joining is approved,
280  *        #GNUNET_NO if it is disapproved
281  * @param relay_count Number of relays given.
282  * @param relays Array of suggested peers that might be useful relays to use
283  *        when joining the multicast group (essentially a list of peers that
284  *        are already part of the multicast group and might thus be willing
285  *        to help with routing).  If empty, only this local peer (which must
286  *        be the multicast origin) is a good candidate for building the
287  *        multicast tree.  Note that it is unnecessary to specify our own
288  *        peer identity in this array.
289  * @param join_response Message to send in response to the joining peer;
290  *        can also be used to redirect the peer to a different group at the
291  *        application layer; this response is to be transmitted to the
292  *        peer that issued the request even if admission is denied.
293  */
294 struct GNUNET_MULTICAST_ReplayHandle *
295 GNUNET_MULTICAST_join_decision (struct GNUNET_MULTICAST_JoinHandle *jh,
296                                 int is_admitted,
297                                 unsigned int relay_count,
298                                 const struct GNUNET_PeerIdentity *relays,
299                                 const struct GNUNET_MessageHeader *join_response)
300 {
301   return NULL;
302 }
303
304
305 /**
306  * Call informing multicast about the decision taken for a membership test.
307  *
308  * @param mth Handle that was given for the query.
309  * @param result #GNUNET_YES if peer was a member, #GNUNET_NO if peer was not a member,
310  *        #GNUNET_SYSERR if we cannot answer the membership test.
311  */
312 void
313 GNUNET_MULTICAST_membership_test_result (struct GNUNET_MULTICAST_MembershipTestHandle *mth,
314                                          int result)
315 {
316 }
317
318
319 /**
320  * Replay a message fragment for the multicast group.
321  *
322  * @param rh Replay handle identifying which replay operation was requested.
323  * @param msg Replayed message fragment, NULL if unknown/error.
324  * @param ec Error code.
325  */
326 void
327 GNUNET_MULTICAST_replay_response (struct GNUNET_MULTICAST_ReplayHandle *rh,
328                                   const struct GNUNET_MessageHeader *msg,
329                                   enum GNUNET_MULTICAST_ReplayErrorCode ec)
330 {
331 }
332
333
334 /**
335  * Indicate the end of the replay session.
336  *
337  * Invalidates the replay handle.
338  *
339  * @param rh Replay session to end.
340  */
341 void
342 GNUNET_MULTICAST_replay_response_end (struct GNUNET_MULTICAST_ReplayHandle *rh)
343 {
344 }
345
346
347 /**
348  * Replay a message for the multicast group.
349  *
350  * @param rh Replay handle identifying which replay operation was requested.
351  * @param notify Function to call to get the message.
352  * @param notify_cls Closure for @a notify.
353  */
354 void
355 GNUNET_MULTICAST_replay_response2 (struct GNUNET_MULTICAST_ReplayHandle *rh,
356                                    GNUNET_MULTICAST_ReplayTransmitNotify notify,
357                                    void *notify_cls)
358 {
359 }
360
361
362 /**
363  * Start a multicast group.
364  *
365  * Will advertise the origin in the P2P overlay network under the respective
366  * public key so that other peer can find this peer to join it.  Peers that
367  * issue GNUNET_MULTICAST_member_join() can then transmit a join request to
368  * either an existing group member or to the origin.  If the joining is
369  * approved, the member is cleared for @e replay and will begin to receive
370  * messages transmitted to the group.  If joining is disapproved, the failed
371  * candidate will be given a response.  Members in the group can send messages
372  * to the origin (one at a time).
373  *
374  * @param cfg Configuration to use.
375  * @param priv_key ECC key that will be used to sign messages for this
376  *        multicast session; public key is used to identify the multicast group;
377  * @param next_fragment_id Next fragment ID to continue counting fragments from
378  *        when restarting the origin.  0 for a new group.
379  * @param join_cb Function called to approve / disapprove joining of a peer.
380  * @param mem_test_cb Function multicast can use to test group membership.
381  * @param replay_frag_cb Function that can be called to replay a message fragment.
382  * @param replay_msg_cb Function that can be called to replay a message.
383  * @param request_cb Function called with message fragments from group members.
384  * @param message_cb Function called with the message fragments sent to the
385  *        network by GNUNET_MULTICAST_origin_to_all().  These message fragments
386  *        should be stored for answering replay requests later.
387  * @param cls Closure for the various callbacks that follow.
388  * @return Handle for the origin, NULL on error.
389  */
390 struct GNUNET_MULTICAST_Origin *
391 GNUNET_MULTICAST_origin_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
392                                const struct GNUNET_CRYPTO_EddsaPrivateKey *priv_key,
393                                uint64_t next_fragment_id,
394                                GNUNET_MULTICAST_JoinCallback join_cb,
395                                GNUNET_MULTICAST_MembershipTestCallback mem_test_cb,
396                                GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb,
397                                GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb,
398                                GNUNET_MULTICAST_RequestCallback request_cb,
399                                GNUNET_MULTICAST_MessageCallback message_cb,
400                                void *cls)
401 {
402   struct GNUNET_MULTICAST_Origin *orig = GNUNET_malloc (sizeof (*orig));
403   orig->grp.is_origin = GNUNET_YES;
404   orig->priv_key = *priv_key;
405   orig->next_fragment_id = next_fragment_id;
406   orig->join_cb = join_cb;
407   orig->mem_test_cb = mem_test_cb;
408   orig->replay_frag_cb = replay_frag_cb;
409   orig->replay_msg_cb = replay_msg_cb;
410   orig->request_cb = request_cb;
411   orig->message_cb = message_cb;
412   orig->cls = cls;
413
414   GNUNET_CRYPTO_eddsa_key_get_public (&orig->priv_key, &orig->pub_key);
415   GNUNET_CRYPTO_hash (&orig->pub_key, sizeof (orig->pub_key),
416                       &orig->pub_key_hash);
417
418   if (NULL == origins)
419     origins = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
420
421   GNUNET_CONTAINER_multihashmap_put (origins, &orig->pub_key_hash, orig,
422                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
423
424   /* FIXME: send ORIGIN_START to service */
425
426   return orig;
427 }
428
429
430 /**
431  * Stop a multicast group.
432  *
433  * @param origin Multicast group to stop.
434  */
435 void
436 GNUNET_MULTICAST_origin_stop (struct GNUNET_MULTICAST_Origin *orig)
437 {
438   GNUNET_CONTAINER_multihashmap_remove (origins, &orig->pub_key_hash, orig);
439   GNUNET_free (orig);
440 }
441
442
443 /* FIXME: for now just call clients' callbacks
444  *        without sending anything to multicast. */
445 static void
446 schedule_origin_to_all (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
447 {
448   LOG (GNUNET_ERROR_TYPE_DEBUG, "schedule_origin_to_all()\n");
449   struct GNUNET_MULTICAST_Origin *orig = cls;
450   struct GNUNET_MULTICAST_OriginMessageHandle *mh = &orig->msg_handle;
451
452   size_t buf_size = GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD;
453   char buf[GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD] = "";
454   struct GNUNET_MULTICAST_MessageHeader *msg
455     = (struct GNUNET_MULTICAST_MessageHeader *) buf;
456   int ret = mh->notify (mh->notify_cls, &buf_size, &msg[1]);
457
458   if (! (GNUNET_YES == ret || GNUNET_NO == ret)
459       || GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < buf_size)
460   {
461     LOG (GNUNET_ERROR_TYPE_ERROR,
462          "OriginTransmitNotify() returned error or invalid message size.\n");
463     /* FIXME: handle error */
464     return;
465   }
466
467   if (GNUNET_NO == ret && 0 == buf_size)
468     return; /* Transmission paused. */
469
470   msg->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
471   msg->header.size = htons (sizeof (*msg) + buf_size);
472   msg->message_id = GNUNET_htonll (mh->message_id);
473   msg->group_generation = mh->group_generation;
474
475   /* FIXME: add fragment ID and signature in the service instead of here */
476   msg->fragment_id = GNUNET_ntohll (orig->next_fragment_id++);
477   msg->fragment_offset = GNUNET_ntohll (mh->fragment_offset);
478   mh->fragment_offset += sizeof (*msg) + buf_size;
479   msg->purpose.size = htonl (sizeof (*msg) + buf_size
480                              - sizeof (msg->header)
481                              - sizeof (msg->hop_counter)
482                              - sizeof (msg->signature));
483   msg->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_MESSAGE);
484
485   if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&orig->priv_key, &msg->purpose,
486                                            &msg->signature))
487   {
488     /* FIXME: handle error */
489     return;
490   }
491
492   /* FIXME: send msg to the service and only then call handle_multicast_message
493    *        with the returned signed message.
494    */
495   handle_multicast_message (&orig->grp, msg);
496
497   if (GNUNET_NO == ret)
498     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
499                                   (GNUNET_TIME_UNIT_SECONDS, 1),
500                                   schedule_origin_to_all, orig);
501 }
502
503
504 /**
505  * Send a message to the multicast group.
506  *
507  * @param origin Handle to the multicast group.
508  * @param message_id Application layer ID for the message.  Opaque to multicast.
509  * @param group_generation Group generation of the message.  Documented in
510  *             `struct GNUNET_MULTICAST_MessageHeader`.
511  * @param notify Function to call to get the message.
512  * @param notify_cls Closure for @a notify.
513  * @return NULL on error (i.e. request already pending).
514  */
515 struct GNUNET_MULTICAST_OriginMessageHandle *
516 GNUNET_MULTICAST_origin_to_all (struct GNUNET_MULTICAST_Origin *origin,
517                                 uint64_t message_id,
518                                 uint64_t group_generation,
519                                 GNUNET_MULTICAST_OriginTransmitNotify notify,
520                                 void *notify_cls)
521 {
522   struct GNUNET_MULTICAST_OriginMessageHandle *mh = &origin->msg_handle;
523   mh->origin = origin;
524   mh->message_id = message_id;
525   mh->group_generation = group_generation;
526   mh->notify = notify;
527   mh->notify_cls = notify_cls;
528
529   /* FIXME: remove delay, it's there only for testing */
530   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
531                                 (GNUNET_TIME_UNIT_SECONDS, 1),
532                                 schedule_origin_to_all, origin);
533   return &origin->msg_handle;
534 }
535
536
537 /**
538  * Resume message transmission to multicast group.
539  *
540  * @param mh Request to cancel.
541  */
542 void
543 GNUNET_MULTICAST_origin_to_all_resume (struct GNUNET_MULTICAST_OriginMessageHandle *mh)
544 {
545   GNUNET_SCHEDULER_add_now (schedule_origin_to_all, mh->origin);
546 }
547
548
549 /**
550  * Cancel request for message transmission to multicast group.
551  *
552  * @param mh Request to cancel.
553  */
554 void
555 GNUNET_MULTICAST_origin_to_all_cancel (struct GNUNET_MULTICAST_OriginMessageHandle *mh)
556 {
557 }
558
559
560 /**
561  * Join a multicast group.
562  *
563  * The entity joining is always the local peer.  Further information about the
564  * candidate can be provided in the @a join_request message.  If the join fails, the
565  * @a message_cb is invoked with a (failure) response and then with NULL.  If
566  * the join succeeds, outstanding (state) messages and ongoing multicast
567  * messages will be given to the @a message_cb until the member decides to part
568  * the group.  The @a test_cb and @a replay_cb functions may be called at
569  * anytime by the multicast service to support relaying messages to other
570  * members of the group.
571  *
572  * @param cfg Configuration to use.
573  * @param group_key ECC public key that identifies the group to join.
574  * @param member_key ECC key that identifies the member and used to sign
575  *        requests sent to the origin.
576  * @param origin Peer ID of the origin to send unicast requsets to.  If NULL,
577  *        unicast requests are sent back via multiple hops on the reverse path
578  *        of multicast messages.
579  * @param relay_count Number of peers in the @a relays array.
580  * @param relays Peer identities of members of the group, which serve as relays
581  *        and can be used to join the group at. and send the @a join_request to.
582  *        If empty, the @a join_request is sent directly to the @a origin.
583  * @param join_request  Application-dependent join request to be passed to the peer
584  *        @a relay (might, for example, contain a user, bind user
585  *        identity/pseudonym to peer identity, application-level message to
586  *        origin, etc.).
587  * @param join_cb Function called to approve / disapprove joining of a peer.
588  * @param mem_test_cb Function multicast can use to test group membership.
589  * @param replay_frag_cb Function that can be called to replay message fragments
590  *        this peer already knows from this group. NULL if this
591  *        client is unable to support replay.
592  * @param replay_msg_cb Function that can be called to replay message fragments
593  *        this peer already knows from this group. NULL if this
594  *        client is unable to support replay.
595  * @param message_cb Function to be called for all message fragments we
596  *        receive from the group, excluding those our @a replay_cb
597  *        already has.
598  * @param cls Closure for callbacks.
599  * @return Handle for the member, NULL on error.
600  */
601 struct GNUNET_MULTICAST_Member *
602 GNUNET_MULTICAST_member_join (const struct GNUNET_CONFIGURATION_Handle *cfg,
603                               const struct GNUNET_CRYPTO_EddsaPublicKey *group_key,
604                               const struct GNUNET_CRYPTO_EddsaPrivateKey *member_key,
605                               const struct GNUNET_PeerIdentity *origin,
606                               uint32_t relay_count,
607                               const struct GNUNET_PeerIdentity *relays,
608                               const struct GNUNET_MessageHeader *join_request,
609                               GNUNET_MULTICAST_JoinCallback join_cb,
610                               GNUNET_MULTICAST_MembershipTestCallback member_test_cb,
611                               GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb,
612                               GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb,
613                               GNUNET_MULTICAST_MessageCallback message_cb,
614                               void *cls)
615 {
616   struct GNUNET_MULTICAST_Member *mem = GNUNET_malloc (sizeof (*mem));
617   mem->group_key = *group_key;
618   mem->member_key = *member_key;
619   mem->origin = *origin;
620   mem->relay_count = relay_count;
621   mem->relays = *relays;
622   mem->join_cb = join_cb;
623   mem->member_test_cb = member_test_cb;
624   mem->replay_frag_cb = replay_frag_cb;
625   mem->message_cb = message_cb;
626   mem->cls = cls;
627
628   if (NULL != join_request)
629   {
630     uint16_t size = ntohs (join_request->size);
631     mem->join_request = GNUNET_malloc (size);
632     memcpy (mem->join_request, join_request, size);
633   }
634
635   GNUNET_CRYPTO_hash (&mem->group_key, sizeof (mem->group_key), &mem->group_key_hash);
636
637   if (NULL == members)
638     members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
639
640   GNUNET_CONTAINER_multihashmap_put (members, &mem->group_key_hash, mem,
641                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
642
643   /* FIXME: send MEMBER_JOIN to service */
644
645   return mem;
646 }
647
648
649 /**
650  * Part a multicast group.
651  *
652  * Disconnects from all group members and invalidates the @a member handle.
653  *
654  * An application-dependent part message can be transmitted beforehand using
655  * #GNUNET_MULTICAST_member_to_origin())
656  *
657  * @param member Membership handle.
658  */
659 void
660 GNUNET_MULTICAST_member_part (struct GNUNET_MULTICAST_Member *mem)
661 {
662   GNUNET_CONTAINER_multihashmap_remove (members, &mem->group_key_hash, mem);
663   GNUNET_free (mem);
664 }
665
666
667 /**
668  * Request a fragment to be replayed by fragment ID.
669  *
670  * Useful if messages below the @e max_known_fragment_id given when joining are
671  * needed and not known to the client.
672  *
673  * @param member Membership handle.
674  * @param fragment_id ID of a message fragment that this client would like to
675           see replayed.
676  * @param flags Additional flags for the replay request.  It is used and defined
677  *        by the replay callback.  FIXME: which replay callback? FIXME: use enum?
678  *        FIXME: why not pass reply cb here?
679  * @return Replay request handle, NULL on error.
680  */
681 struct GNUNET_MULTICAST_MemberReplayHandle *
682 GNUNET_MULTICAST_member_replay_fragment (struct GNUNET_MULTICAST_Member *member,
683                                          uint64_t fragment_id,
684                                          uint64_t flags)
685 {
686   return NULL;
687 }
688
689
690 /**
691  * Request a message fragment to be replayed.
692  *
693  * Useful if messages below the @e max_known_fragment_id given when joining are
694  * needed and not known to the client.
695  *
696  * @param member Membership handle.
697  * @param message_id ID of the message this client would like to see replayed.
698  * @param fragment_offset Offset of the fragment within the message to replay.
699  * @param flags Additional flags for the replay request.  It is used & defined
700  *        by the replay callback.
701  * @param result_cb Function to be called for the replayed message.
702  * @param result_cb_cls Closure for @a result_cb.
703  * @return Replay request handle, NULL on error.
704  */
705 struct GNUNET_MULTICAST_MemberReplayHandle *
706 GNUNET_MULTICAST_member_replay_message (struct GNUNET_MULTICAST_Member *member,
707                                         uint64_t message_id,
708                                         uint64_t fragment_offset,
709                                         uint64_t flags,
710                                         GNUNET_MULTICAST_ResultCallback result_cb,
711                                         void *result_cb_cls)
712 {
713   return NULL;
714 }
715
716
717 /**
718  * Cancel a replay request.
719  *
720  * @param rh Request to cancel.
721  */
722 void
723 GNUNET_MULTICAST_member_replay_cancel (struct GNUNET_MULTICAST_MemberReplayHandle *rh)
724 {
725 }
726
727
728 /* FIXME: for now just send back to the client what it sent. */
729 static void
730 schedule_member_to_origin (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
731 {
732   LOG (GNUNET_ERROR_TYPE_DEBUG, "schedule_member_to_origin()\n");
733   struct GNUNET_MULTICAST_Member *mem = cls;
734   struct GNUNET_MULTICAST_MemberRequestHandle *rh = &mem->req_handle;
735
736   size_t buf_size = GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD;
737   char buf[GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD] = "";
738   struct GNUNET_MULTICAST_RequestHeader *req
739     = (struct GNUNET_MULTICAST_RequestHeader *) buf;
740   int ret = rh->notify (rh->notify_cls, &buf_size, &req[1]);
741
742   if (! (GNUNET_YES == ret || GNUNET_NO == ret)
743       || GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < buf_size)
744   {
745     LOG (GNUNET_ERROR_TYPE_ERROR,
746          "MemberTransmitNotify() returned error or invalid message size.\n");
747     /* FIXME: handle error */
748     return;
749   }
750
751   if (GNUNET_NO == ret && 0 == buf_size)
752     return; /* Transmission paused. */
753
754   req->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST);
755   req->header.size = htons (sizeof (*req) + buf_size);
756   req->request_id = GNUNET_htonll (rh->request_id);
757
758   /* FIXME: add fragment ID and signature in the service instead of here */
759   req->fragment_id = GNUNET_ntohll (mem->next_fragment_id++);
760   req->fragment_offset = GNUNET_ntohll (rh->fragment_offset);
761   rh->fragment_offset += sizeof (*req) + buf_size;
762   req->purpose.size = htonl (sizeof (*req) + buf_size
763                              - sizeof (req->header)
764                              - sizeof (req->member_key)
765                              - sizeof (req->signature));
766   req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_MESSAGE);
767
768   if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&mem->member_key, &req->purpose,
769                                            &req->signature))
770   {
771     /* FIXME: handle error */
772     return;
773   }
774
775   /* FIXME: send req to the service and only then call handle_multicast_request
776    *        with the returned request.
777    */
778   handle_multicast_request (&mem->group_key_hash, req);
779
780   if (GNUNET_NO == ret)
781     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
782                                   (GNUNET_TIME_UNIT_SECONDS, 1),
783                                   schedule_member_to_origin, mem);
784 }
785
786
787 /**
788  * Send a message to the origin of the multicast group.
789  *
790  * @param member Membership handle.
791  * @param request_id Application layer ID for the request.  Opaque to multicast.
792  * @param notify Callback to call to get the message.
793  * @param notify_cls Closure for @a notify.
794  * @return Handle to cancel request, NULL on error (i.e. request already pending).
795  */
796 struct GNUNET_MULTICAST_MemberRequestHandle *
797 GNUNET_MULTICAST_member_to_origin (struct GNUNET_MULTICAST_Member *member,
798                                    uint64_t request_id,
799                                    GNUNET_MULTICAST_MemberTransmitNotify notify,
800                                    void *notify_cls)
801 {
802   struct GNUNET_MULTICAST_MemberRequestHandle *rh = &member->req_handle;
803   rh->member = member;
804   rh->request_id = request_id;
805   rh->notify = notify;
806   rh->notify_cls = notify_cls;
807
808   /* FIXME: remove delay, it's there only for testing */
809   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
810                                 (GNUNET_TIME_UNIT_SECONDS, 1),
811                                 schedule_member_to_origin, member);
812   return &member->req_handle;
813 }
814
815
816 /**
817  * Resume message transmission to origin.
818  *
819  * @param rh Request to cancel.
820  */
821 void
822 GNUNET_MULTICAST_member_to_origin_resume (struct GNUNET_MULTICAST_MemberRequestHandle *rh)
823 {
824
825 }
826
827
828 /**
829  * Cancel request for message transmission to origin.
830  *
831  * @param rh Request to cancel.
832  */
833 void
834 GNUNET_MULTICAST_member_to_origin_cancel (struct GNUNET_MULTICAST_MemberRequestHandle *rh)
835 {
836 }
837
838
839 /* end of multicast_api.c */