d2a0412bb6010792bd012ca24f803c9e491a3316
[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; implements multicast groups using CADET connections.
24  * @author Christian Grothoff
25  * @author Gabor X Toth
26  */
27
28 #include "platform.h"
29 #include "gnunet_util_lib.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  * Handle for a request to send a message to all multicast group members
38  * (from the origin).
39  */
40 struct GNUNET_MULTICAST_OriginTransmitHandle
41 {
42   GNUNET_MULTICAST_OriginTransmitNotify notify;
43   void *notify_cls;
44   struct GNUNET_MULTICAST_Origin *origin;
45
46   uint64_t message_id;
47   uint64_t group_generation;
48   uint64_t fragment_offset;
49 };
50
51
52 /**
53  * Handle for a message to be delivered from a member to the origin.
54  */
55 struct GNUNET_MULTICAST_MemberTransmitHandle
56 {
57   GNUNET_MULTICAST_MemberTransmitNotify notify;
58   void *notify_cls;
59   struct GNUNET_MULTICAST_Member *member;
60
61   uint64_t request_id;
62   uint64_t fragment_offset;
63 };
64
65
66 struct GNUNET_MULTICAST_Group
67 {
68   /**
69    * Configuration to use.
70    */
71   const struct GNUNET_CONFIGURATION_Handle *cfg;
72
73   /**
74    * Client connection to the service.
75    */
76   struct GNUNET_CLIENT_MANAGER_Connection *client;
77
78   /**
79    * Message to send on reconnect.
80    */
81   struct GNUNET_MessageHeader *connect_msg;
82
83   GNUNET_MULTICAST_JoinRequestCallback join_req_cb;
84   GNUNET_MULTICAST_MembershipTestCallback member_test_cb;
85   GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb;
86   GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb;
87   GNUNET_MULTICAST_MessageCallback message_cb;
88   void *cb_cls;
89
90   /**
91    * Are we currently transmitting a message?
92    */
93   uint8_t in_transmit;
94
95   /**
96    * Is this the origin or a member?
97    */
98   uint8_t is_origin;
99 };
100
101
102 /**
103  * Handle for the origin of a multicast group.
104  */
105 struct GNUNET_MULTICAST_Origin
106 {
107   struct GNUNET_MULTICAST_Group grp;
108   struct GNUNET_MULTICAST_OriginTransmitHandle tmit;
109
110   GNUNET_MULTICAST_RequestCallback request_cb;
111 };
112
113
114 /**
115  * Handle for a multicast group member.
116  */
117 struct GNUNET_MULTICAST_Member
118 {
119   struct GNUNET_MULTICAST_Group grp;
120   struct GNUNET_MULTICAST_MemberTransmitHandle tmit;
121
122   GNUNET_MULTICAST_JoinDecisionCallback join_dcsn_cb;
123
124   uint64_t next_fragment_id;
125 };
126
127
128 /**
129  * Handle that identifies a join request.
130  *
131  * Used to match calls to #GNUNET_MULTICAST_JoinCallback to the
132  * corresponding calls to #GNUNET_MULTICAST_join_decision().
133  */
134 struct GNUNET_MULTICAST_JoinHandle
135 {
136   struct GNUNET_MULTICAST_Group *group;
137
138   /**
139    * Public key of the member requesting join.
140    */
141   struct GNUNET_CRYPTO_EcdsaPublicKey member_key;
142
143   /**
144    * Peer identity of the member requesting join.
145    */
146   struct GNUNET_PeerIdentity member_peer;
147 };
148
149
150 /**
151  * Handle to pass back for the answer of a membership test.
152  */
153 struct GNUNET_MULTICAST_MembershipTestHandle
154 {
155 };
156
157
158 /**
159  * Opaque handle to a replay request from the multicast service.
160  */
161 struct GNUNET_MULTICAST_ReplayHandle
162 {
163 };
164
165
166 /**
167  * Handle for a replay request.
168  */
169 struct GNUNET_MULTICAST_MemberReplayHandle
170 {
171 };
172
173
174 /**
175  * Send first message to the service after connecting.
176  */
177 static void
178 group_send_connect_msg (struct GNUNET_MULTICAST_Group *grp)
179 {
180   uint16_t cmsg_size = ntohs (grp->connect_msg->size);
181   struct GNUNET_MessageHeader * cmsg = GNUNET_malloc (cmsg_size);
182   memcpy (cmsg, grp->connect_msg, cmsg_size);
183   GNUNET_CLIENT_MANAGER_transmit_now (grp->client, cmsg);
184 }
185
186
187 /**
188  * Got disconnected from service.  Reconnect.
189  */
190 static void
191 group_recv_disconnect (void *cls,
192                         struct GNUNET_CLIENT_MANAGER_Connection *client,
193                         const struct GNUNET_MessageHeader *msg)
194 {
195   struct GNUNET_MULTICAST_Group *
196     grp = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*grp));
197   GNUNET_CLIENT_MANAGER_reconnect (client);
198   group_send_connect_msg (grp);
199 }
200
201
202 /**
203  * Receive join request from service.
204  */
205 static void
206 group_recv_join_request (void *cls,
207                           struct GNUNET_CLIENT_MANAGER_Connection *client,
208                           const struct GNUNET_MessageHeader *msg)
209 {
210   struct GNUNET_MULTICAST_Group *
211     grp = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*grp));
212
213   const struct MulticastJoinRequestMessage *
214     jreq = (const struct MulticastJoinRequestMessage *) msg;
215
216   struct GNUNET_MULTICAST_JoinHandle *jh = GNUNET_malloc (sizeof (*jh));
217   jh->group = grp;
218   jh->member_key = jreq->member_key;
219   jh->member_peer = jreq->member_peer;
220
221   const struct GNUNET_MessageHeader *jmsg = NULL;
222   if (sizeof (*jreq) + sizeof (*jmsg) <= ntohs (jreq->header.size))
223     jmsg = (const struct GNUNET_MessageHeader *) &jreq[1];
224
225   if (NULL != grp->join_req_cb)
226     grp->join_req_cb (grp->cb_cls, &jreq->member_key, jmsg, jh);
227 }
228
229
230 /**
231  * Receive multicast message from service.
232  */
233 static void
234 group_recv_message (void *cls,
235                     struct GNUNET_CLIENT_MANAGER_Connection *client,
236                     const struct GNUNET_MessageHeader *msg)
237 {
238   struct GNUNET_MULTICAST_Group *
239     grp = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*grp));
240   struct GNUNET_MULTICAST_MessageHeader *
241     mmsg = (struct GNUNET_MULTICAST_MessageHeader *) msg;
242
243   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
244               "Calling message callback with a message of size %u.\n",
245               ntohs (mmsg->header.size));
246
247   if (NULL != grp->message_cb)
248     grp->message_cb (grp->cb_cls, mmsg);
249 }
250
251
252 /**
253  * Origin receives uniquest request from a member.
254  */
255 static void
256 origin_recv_request (void *cls,
257                      struct GNUNET_CLIENT_MANAGER_Connection *client,
258                      const struct GNUNET_MessageHeader *msg)
259 {
260   struct GNUNET_MULTICAST_Group *grp;
261   struct GNUNET_MULTICAST_Origin *
262     orig = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*grp));
263   grp = &orig->grp;
264   struct GNUNET_MULTICAST_RequestHeader *
265     req = (struct GNUNET_MULTICAST_RequestHeader *) msg;
266
267   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
268               "Calling request callback with a request of size %u.\n",
269               ntohs (req->header.size));
270
271   if (NULL != orig->request_cb)
272     orig->request_cb (grp->cb_cls, req);
273 }
274
275
276 /**
277  * Member receives join decision.
278  */
279 static void
280 member_recv_join_decision (void *cls,
281                            struct GNUNET_CLIENT_MANAGER_Connection *client,
282                            const struct GNUNET_MessageHeader *msg)
283 {
284   struct GNUNET_MULTICAST_Group *grp;
285   struct GNUNET_MULTICAST_Member *
286     mem = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*grp));
287   grp = &mem->grp;
288
289   const struct MulticastJoinDecisionMessageHeader *
290     hdcsn = (const struct MulticastJoinDecisionMessageHeader *) msg;
291   const struct MulticastJoinDecisionMessage *
292     dcsn = (const struct MulticastJoinDecisionMessage *) &hdcsn[1];
293
294   uint16_t dcsn_size = ntohs (dcsn->header.size);
295   int is_admitted = ntohl (dcsn->is_admitted);
296
297   const struct GNUNET_MessageHeader *join_resp = NULL;
298   uint16_t join_resp_size = 0;
299
300   uint16_t relay_count = ntohl (dcsn->relay_count);
301   const struct GNUNET_PeerIdentity *relays = NULL;
302   uint16_t relay_size = relay_count * sizeof (*relays);
303   if (0 < relay_count && dcsn_size < sizeof (*dcsn) + relay_size)
304     relays = (struct GNUNET_PeerIdentity *) &dcsn[1];
305
306   if (sizeof (*dcsn) + relay_size + sizeof (*join_resp) <= dcsn_size)
307   {
308     join_resp = (const struct GNUNET_MessageHeader *) &dcsn[1];
309     join_resp_size = ntohs (join_resp->size);
310   }
311   if (dcsn_size < sizeof (*dcsn) + relay_size + join_resp_size)
312   {
313     LOG (GNUNET_ERROR_TYPE_DEBUG,
314          "Received invalid join decision message from multicast.\n");
315     GNUNET_break_op (0);
316     is_admitted = GNUNET_SYSERR;
317   }
318
319   if (NULL != mem->join_dcsn_cb)
320     mem->join_dcsn_cb (grp->cb_cls, is_admitted, &hdcsn->peer,
321                        relay_count, relays, join_resp);
322
323   if (GNUNET_YES != is_admitted)
324     GNUNET_MULTICAST_member_part (mem);
325 }
326
327
328 /**
329  * Message handlers for an origin.
330  */
331 static struct GNUNET_CLIENT_MANAGER_MessageHandler origin_handlers[] =
332 {
333   { &group_recv_disconnect, NULL, 0, 0, GNUNET_NO },
334
335   { &group_recv_message, NULL,
336     GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE,
337     sizeof (struct GNUNET_MULTICAST_MessageHeader), GNUNET_YES },
338
339   { &origin_recv_request, NULL,
340     GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST,
341     sizeof (struct GNUNET_MULTICAST_RequestHeader), GNUNET_YES },
342
343   { &group_recv_join_request, NULL,
344     GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST,
345     sizeof (struct MulticastJoinRequestMessage), GNUNET_YES },
346
347   { NULL, NULL, 0, 0, GNUNET_NO }
348 };
349
350
351 /**
352  * Message handlers for a member.
353  */
354 static struct GNUNET_CLIENT_MANAGER_MessageHandler member_handlers[] =
355 {
356   { &group_recv_disconnect, NULL, 0, 0, GNUNET_NO },
357
358   { &group_recv_message, NULL,
359     GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE,
360     sizeof (struct GNUNET_MULTICAST_MessageHeader), GNUNET_YES },
361
362   { &group_recv_join_request, NULL,
363     GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST,
364     sizeof (struct MulticastJoinRequestMessage), GNUNET_YES },
365
366   { &member_recv_join_decision, NULL,
367     GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION,
368     sizeof (struct MulticastJoinDecisionMessage), GNUNET_YES },
369
370   { NULL, NULL, 0, 0, GNUNET_NO }
371 };
372
373
374 /**
375  * Function to call with the decision made for a join request.
376  *
377  * Must be called once and only once in response to an invocation of the
378  * #GNUNET_MULTICAST_JoinRequestCallback.
379  *
380  * @param join  Join request handle.
381  * @param is_admitted  #GNUNET_YES    if the join is approved,
382  *                     #GNUNET_NO     if it is disapproved,
383  *                     #GNUNET_SYSERR if we cannot answer the request.
384  * @param relay_count Number of relays given.
385  * @param relays Array of suggested peers that might be useful relays to use
386  *        when joining the multicast group (essentially a list of peers that
387  *        are already part of the multicast group and might thus be willing
388  *        to help with routing).  If empty, only this local peer (which must
389  *        be the multicast origin) is a good candidate for building the
390  *        multicast tree.  Note that it is unnecessary to specify our own
391  *        peer identity in this array.
392  * @param join_resp  Message to send in response to the joining peer;
393  *        can also be used to redirect the peer to a different group at the
394  *        application layer; this response is to be transmitted to the
395  *        peer that issued the request even if admission is denied.
396  */
397 struct GNUNET_MULTICAST_ReplayHandle *
398 GNUNET_MULTICAST_join_decision (struct GNUNET_MULTICAST_JoinHandle *join,
399                                 int is_admitted,
400                                 uint16_t relay_count,
401                                 const struct GNUNET_PeerIdentity *relays,
402                                 const struct GNUNET_MessageHeader *join_resp)
403 {
404   struct GNUNET_MULTICAST_Group *grp = join->group;
405   uint16_t join_resp_size = (NULL != join_resp) ? ntohs (join_resp->size) : 0;
406   uint16_t relay_size = relay_count * sizeof (*relays);
407
408   struct MulticastJoinDecisionMessageHeader * hdcsn;
409   struct MulticastJoinDecisionMessage *dcsn;
410   hdcsn = GNUNET_malloc (sizeof (*hdcsn) + sizeof (*dcsn)
411                          + relay_size + join_resp_size);
412   hdcsn->header.size = htons (sizeof (*hdcsn) + sizeof (*dcsn)
413                               + relay_size + join_resp_size);
414   hdcsn->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION);
415   hdcsn->member_key = join->member_key;
416   hdcsn->peer = join->member_peer;
417
418   dcsn = (struct MulticastJoinDecisionMessage *) &hdcsn[1];
419   dcsn->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION);
420   dcsn->header.size = htons (sizeof (*dcsn) + relay_size + join_resp_size);
421   dcsn->is_admitted = htonl (is_admitted);
422   dcsn->relay_count = htonl (relay_count);
423   if (0 < relay_size)
424     memcpy (&dcsn[1], relays, relay_size);
425   if (0 < join_resp_size)
426     memcpy (((char *) &dcsn[1]) + relay_size, join_resp, join_resp_size);
427
428   GNUNET_CLIENT_MANAGER_transmit (grp->client, &hdcsn->header);
429   GNUNET_free (join);
430   return NULL;
431 }
432
433
434 /**
435  * Call informing multicast about the decision taken for a membership test.
436  *
437  * @param mth Handle that was given for the query.
438  * @param result #GNUNET_YES if peer was a member, #GNUNET_NO if peer was not a member,
439  *        #GNUNET_SYSERR if we cannot answer the membership test.
440  */
441 void
442 GNUNET_MULTICAST_membership_test_result (struct GNUNET_MULTICAST_MembershipTestHandle *mth,
443                                          int result)
444 {
445 }
446
447
448 /**
449  * Replay a message fragment for the multicast group.
450  *
451  * @param rh Replay handle identifying which replay operation was requested.
452  * @param msg Replayed message fragment, NULL if unknown/error.
453  * @param ec Error code.
454  */
455 void
456 GNUNET_MULTICAST_replay_response (struct GNUNET_MULTICAST_ReplayHandle *rh,
457                                   const struct GNUNET_MessageHeader *msg,
458                                   enum GNUNET_MULTICAST_ReplayErrorCode ec)
459 {
460 }
461
462
463 /**
464  * Indicate the end of the replay session.
465  *
466  * Invalidates the replay handle.
467  *
468  * @param rh Replay session to end.
469  */
470 void
471 GNUNET_MULTICAST_replay_response_end (struct GNUNET_MULTICAST_ReplayHandle *rh)
472 {
473 }
474
475
476 /**
477  * Replay a message for the multicast group.
478  *
479  * @param rh Replay handle identifying which replay operation was requested.
480  * @param notify Function to call to get the message.
481  * @param notify_cls Closure for @a notify.
482  */
483 void
484 GNUNET_MULTICAST_replay_response2 (struct GNUNET_MULTICAST_ReplayHandle *rh,
485                                    GNUNET_MULTICAST_ReplayTransmitNotify notify,
486                                    void *notify_cls)
487 {
488 }
489
490
491 /**
492  * Start a multicast group.
493  *
494  * Will advertise the origin in the P2P overlay network under the respective
495  * public key so that other peer can find this peer to join it.  Peers that
496  * issue GNUNET_MULTICAST_member_join() can then transmit a join request to
497  * either an existing group member or to the origin.  If the joining is
498  * approved, the member is cleared for @e replay and will begin to receive
499  * messages transmitted to the group.  If joining is disapproved, the failed
500  * candidate will be given a response.  Members in the group can send messages
501  * to the origin (one at a time).
502  *
503  * @param cfg  Configuration to use.
504  * @param priv_key  ECC key that will be used to sign messages for this
505  *        multicast session; public key is used to identify the multicast group;
506  * @param max_fragment_id  Maximum fragment ID already sent to the group.
507  *        0 for a new group.
508  * @param join_request_cb Function called to approve / disapprove joining of a peer.
509  * @param member_test_cb  Function multicast can use to test group membership.
510  * @param replay_frag_cb  Function that can be called to replay a message fragment.
511  * @param replay_msg_cb  Function that can be called to replay a message.
512  * @param request_cb  Function called with message fragments from group members.
513  * @param message_cb  Function called with the message fragments sent to the
514  *        network by GNUNET_MULTICAST_origin_to_all().  These message fragments
515  *        should be stored for answering replay requests later.
516  * @param cls  Closure for the various callbacks that follow.
517  *
518  * @return Handle for the origin, NULL on error.
519  */
520 struct GNUNET_MULTICAST_Origin *
521 GNUNET_MULTICAST_origin_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
522                                const struct GNUNET_CRYPTO_EddsaPrivateKey *priv_key,
523                                uint64_t max_fragment_id,
524                                GNUNET_MULTICAST_JoinRequestCallback join_request_cb,
525                                GNUNET_MULTICAST_MembershipTestCallback member_test_cb,
526                                GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb,
527                                GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb,
528                                GNUNET_MULTICAST_RequestCallback request_cb,
529                                GNUNET_MULTICAST_MessageCallback message_cb,
530                                void *cls)
531 {
532   struct GNUNET_MULTICAST_Origin *orig = GNUNET_malloc (sizeof (*orig));
533   struct GNUNET_MULTICAST_Group *grp = &orig->grp;
534   struct MulticastOriginStartMessage *start = GNUNET_malloc (sizeof (*start));
535
536   start->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_ORIGIN_START);
537   start->header.size = htons (sizeof (*start));
538   start->max_fragment_id = max_fragment_id;
539   memcpy (&start->group_key, priv_key, sizeof (*priv_key));
540
541   grp->connect_msg = (struct GNUNET_MessageHeader *) start;
542   grp->is_origin = GNUNET_YES;
543   grp->cfg = cfg;
544
545   grp->cb_cls = cls;
546   grp->join_req_cb = join_request_cb;
547   grp->member_test_cb = member_test_cb;
548   grp->replay_frag_cb = replay_frag_cb;
549   grp->replay_msg_cb = replay_msg_cb;
550   grp->message_cb = message_cb;
551
552   orig->request_cb = request_cb;
553
554   grp->client = GNUNET_CLIENT_MANAGER_connect (cfg, "multicast", origin_handlers);
555   GNUNET_CLIENT_MANAGER_set_user_context_ (grp->client, orig, sizeof (*grp));
556   group_send_connect_msg (grp);
557
558   return orig;
559 }
560
561
562 /**
563  * Stop a multicast group.
564  *
565  * @param origin Multicast group to stop.
566  */
567 void
568 GNUNET_MULTICAST_origin_stop (struct GNUNET_MULTICAST_Origin *orig)
569 {
570   GNUNET_CLIENT_MANAGER_disconnect (orig->grp.client, GNUNET_YES);
571   GNUNET_free (orig);
572 }
573
574
575 static void
576 origin_to_all (struct GNUNET_MULTICAST_Origin *orig)
577 {
578   LOG (GNUNET_ERROR_TYPE_DEBUG, "origin_to_all()\n");
579   struct GNUNET_MULTICAST_Group *grp = &orig->grp;
580   struct GNUNET_MULTICAST_OriginTransmitHandle *tmit = &orig->tmit;
581
582   size_t buf_size = GNUNET_MULTICAST_FRAGMENT_MAX_SIZE;
583   struct GNUNET_MULTICAST_MessageHeader *msg = GNUNET_malloc (buf_size);
584   int ret = tmit->notify (tmit->notify_cls, &buf_size, &msg[1]);
585
586   if (! (GNUNET_YES == ret || GNUNET_NO == ret)
587       || GNUNET_MULTICAST_FRAGMENT_MAX_SIZE < buf_size)
588   {
589     LOG (GNUNET_ERROR_TYPE_ERROR,
590          "OriginTransmitNotify() returned error or invalid message size.\n");
591     /* FIXME: handle error */
592     GNUNET_free (msg);
593     return;
594   }
595
596   if (GNUNET_NO == ret && 0 == buf_size)
597   {
598     GNUNET_free (msg);
599     return; /* Transmission paused. */
600   }
601
602   msg->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
603   msg->header.size = htons (sizeof (*msg) + buf_size);
604   msg->message_id = GNUNET_htonll (tmit->message_id);
605   msg->group_generation = tmit->group_generation;
606   msg->fragment_offset = GNUNET_htonll (tmit->fragment_offset);
607   tmit->fragment_offset += sizeof (*msg) + buf_size;
608
609   GNUNET_CLIENT_MANAGER_transmit (grp->client, &msg->header);
610 }
611
612
613 /**
614  * Send a message to the multicast group.
615  *
616  * @param orig  Handle to the multicast group.
617  * @param message_id  Application layer ID for the message.  Opaque to multicast.
618  * @param group_generation  Group generation of the message.
619  *                          Documented in struct GNUNET_MULTICAST_MessageHeader.
620  * @param notify  Function to call to get the message.
621  * @param notify_cls  Closure for @a notify.
622  *
623  * @return Message handle on success,
624  *         NULL on error (i.e. another request is already pending).
625  */
626 struct GNUNET_MULTICAST_OriginTransmitHandle *
627 GNUNET_MULTICAST_origin_to_all (struct GNUNET_MULTICAST_Origin *orig,
628                                 uint64_t message_id,
629                                 uint64_t group_generation,
630                                 GNUNET_MULTICAST_OriginTransmitNotify notify,
631                                 void *notify_cls)
632 {
633 /* FIXME
634   if (GNUNET_YES == orig->grp.in_transmit)
635     return NULL;
636   orig->grp.in_transmit = GNUNET_YES;
637 */
638
639   struct GNUNET_MULTICAST_OriginTransmitHandle *tmit = &orig->tmit;
640   tmit->origin = orig;
641   tmit->message_id = message_id;
642   tmit->group_generation = group_generation;
643   tmit->notify = notify;
644   tmit->notify_cls = notify_cls;
645
646   origin_to_all (orig);
647   return tmit;
648 }
649
650
651 /**
652  * Resume message transmission to multicast group.
653  *
654  * @param th  Transmission to cancel.
655  */
656 void
657 GNUNET_MULTICAST_origin_to_all_resume (struct GNUNET_MULTICAST_OriginTransmitHandle *th)
658 {
659   origin_to_all (th->origin);
660 }
661
662
663 /**
664  * Cancel request for message transmission to multicast group.
665  *
666  * @param th  Transmission to cancel.
667  */
668 void
669 GNUNET_MULTICAST_origin_to_all_cancel (struct GNUNET_MULTICAST_OriginTransmitHandle *th)
670 {
671 }
672
673
674 /**
675  * Join a multicast group.
676  *
677  * The entity joining is always the local peer.  Further information about the
678  * candidate can be provided in the @a join_request message.  If the join fails, the
679  * @a message_cb is invoked with a (failure) response and then with NULL.  If
680  * the join succeeds, outstanding (state) messages and ongoing multicast
681  * messages will be given to the @a message_cb until the member decides to part
682  * the group.  The @a test_cb and @a replay_cb functions may be called at
683  * anytime by the multicast service to support relaying messages to other
684  * members of the group.
685  *
686  * @param cfg Configuration to use.
687  * @param group_key ECC public key that identifies the group to join.
688  * @param member_key ECC key that identifies the member and used to sign
689  *        requests sent to the origin.
690  * @param origin Peer ID of the origin to send unicast requsets to.  If NULL,
691  *        unicast requests are sent back via multiple hops on the reverse path
692  *        of multicast messages.
693  * @param relay_count Number of peers in the @a relays array.
694  * @param relays Peer identities of members of the group, which serve as relays
695  *        and can be used to join the group at. and send the @a join_request to.
696  *        If empty, the @a join_request is sent directly to the @a origin.
697  * @param join_msg  Application-dependent join message to be passed to the peer
698  *        @a origin.
699  * @param join_request_cb Function called to approve / disapprove joining of a peer.
700  * @param join_decision_cb Function called to inform about the join decision.
701  * @param member_test_cb Function multicast can use to test group membership.
702  * @param replay_frag_cb Function that can be called to replay message fragments
703  *        this peer already knows from this group. NULL if this
704  *        client is unable to support replay.
705  * @param replay_msg_cb Function that can be called to replay message fragments
706  *        this peer already knows from this group. NULL if this
707  *        client is unable to support replay.
708  * @param message_cb Function to be called for all message fragments we
709  *        receive from the group, excluding those our @a replay_cb
710  *        already has.
711  * @param cls Closure for callbacks.
712  * @return Handle for the member, NULL on error.
713  */
714 struct GNUNET_MULTICAST_Member *
715 GNUNET_MULTICAST_member_join (const struct GNUNET_CONFIGURATION_Handle *cfg,
716                               const struct GNUNET_CRYPTO_EddsaPublicKey *group_key,
717                               const struct GNUNET_CRYPTO_EcdsaPrivateKey *member_key,
718                               const struct GNUNET_PeerIdentity *origin,
719                               uint16_t relay_count,
720                               const struct GNUNET_PeerIdentity *relays,
721                               const struct GNUNET_MessageHeader *join_msg,
722                               GNUNET_MULTICAST_JoinRequestCallback join_request_cb,
723                               GNUNET_MULTICAST_JoinDecisionCallback join_decision_cb,
724                               GNUNET_MULTICAST_MembershipTestCallback member_test_cb,
725                               GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb,
726                               GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb,
727                               GNUNET_MULTICAST_MessageCallback message_cb,
728                               void *cls)
729 {
730   struct GNUNET_MULTICAST_Member *mem = GNUNET_malloc (sizeof (*mem));
731   struct GNUNET_MULTICAST_Group *grp = &mem->grp;
732
733   uint16_t relay_size = relay_count * sizeof (*relays);
734   uint16_t join_msg_size = (NULL != join_msg) ? ntohs (join_msg->size) : 0;
735   struct MulticastMemberJoinMessage *
736     join = GNUNET_malloc (sizeof (*join) + relay_size + join_msg_size);
737   join->header.size = htons (sizeof (*join) + relay_size + join_msg_size);
738   join->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MEMBER_JOIN);
739   join->group_key = *group_key;
740   join->member_key = *member_key;
741   join->origin = *origin;
742   if (0 < relay_size)
743     memcpy (&join[1], relays, relay_size);
744   if (0 < join_msg_size)
745     memcpy (((char *) &join[1]) + relay_size, join_msg, join_msg_size);
746
747   grp->connect_msg = (struct GNUNET_MessageHeader *) join;
748   grp->is_origin = GNUNET_NO;
749   grp->cfg = cfg;
750
751   mem->join_dcsn_cb = join_decision_cb;
752   grp->join_req_cb = join_request_cb;
753   grp->member_test_cb = member_test_cb;
754   grp->replay_frag_cb = replay_frag_cb;
755   grp->message_cb = message_cb;
756   grp->cb_cls = cls;
757
758   grp->client = GNUNET_CLIENT_MANAGER_connect (cfg, "multicast", member_handlers);
759   GNUNET_CLIENT_MANAGER_set_user_context_ (grp->client, mem, sizeof (*grp));
760   group_send_connect_msg (grp);
761
762   return mem;
763 }
764
765
766 /**
767  * Part a multicast group.
768  *
769  * Disconnects from all group members and invalidates the @a member handle.
770  *
771  * An application-dependent part message can be transmitted beforehand using
772  * #GNUNET_MULTICAST_member_to_origin())
773  *
774  * @param member Membership handle.
775  */
776 void
777 GNUNET_MULTICAST_member_part (struct GNUNET_MULTICAST_Member *mem)
778 {
779   GNUNET_CLIENT_MANAGER_disconnect (mem->grp.client, GNUNET_YES);
780   GNUNET_free (mem);
781 }
782
783
784 /**
785  * Request a fragment to be replayed by fragment ID.
786  *
787  * Useful if messages below the @e max_known_fragment_id given when joining are
788  * needed and not known to the client.
789  *
790  * @param member Membership handle.
791  * @param fragment_id ID of a message fragment that this client would like to
792           see replayed.
793  * @param flags Additional flags for the replay request.  It is used and defined
794  *        by the replay callback.  FIXME: which replay callback? FIXME: use enum?
795  *        FIXME: why not pass reply cb here?
796  * @return Replay request handle, NULL on error.
797  */
798 struct GNUNET_MULTICAST_MemberReplayHandle *
799 GNUNET_MULTICAST_member_replay_fragment (struct GNUNET_MULTICAST_Member *member,
800                                          uint64_t fragment_id,
801                                          uint64_t flags)
802 {
803   return NULL;
804 }
805
806
807 /**
808  * Request a message fragment to be replayed.
809  *
810  * Useful if messages below the @e max_known_fragment_id given when joining are
811  * needed and not known to the client.
812  *
813  * @param member Membership handle.
814  * @param message_id ID of the message this client would like to see replayed.
815  * @param fragment_offset Offset of the fragment within the message to replay.
816  * @param flags Additional flags for the replay request.  It is used & defined
817  *        by the replay callback.
818  * @param result_cb Function to be called for the replayed message.
819  * @param result_cb_cls Closure for @a result_cb.
820  * @return Replay request handle, NULL on error.
821  */
822 struct GNUNET_MULTICAST_MemberReplayHandle *
823 GNUNET_MULTICAST_member_replay_message (struct GNUNET_MULTICAST_Member *member,
824                                         uint64_t message_id,
825                                         uint64_t fragment_offset,
826                                         uint64_t flags,
827                                         GNUNET_MULTICAST_ResultCallback result_cb,
828                                         void *result_cb_cls)
829 {
830   return NULL;
831 }
832
833
834 /**
835  * Cancel a replay request.
836  *
837  * @param rh Request to cancel.
838  */
839 void
840 GNUNET_MULTICAST_member_replay_cancel (struct GNUNET_MULTICAST_MemberReplayHandle *rh)
841 {
842 }
843
844
845 static void
846 member_to_origin (struct GNUNET_MULTICAST_Member *mem)
847 {
848   LOG (GNUNET_ERROR_TYPE_DEBUG, "member_to_origin()\n");
849   struct GNUNET_MULTICAST_Group *grp = &mem->grp;
850   struct GNUNET_MULTICAST_MemberTransmitHandle *tmit = &mem->tmit;
851
852   size_t buf_size = GNUNET_MULTICAST_FRAGMENT_MAX_SIZE;
853   struct GNUNET_MULTICAST_RequestHeader *req = GNUNET_malloc (buf_size);
854   int ret = tmit->notify (tmit->notify_cls, &buf_size, &req[1]);
855
856   if (! (GNUNET_YES == ret || GNUNET_NO == ret)
857       || GNUNET_MULTICAST_FRAGMENT_MAX_SIZE < buf_size)
858   {
859     LOG (GNUNET_ERROR_TYPE_ERROR,
860          "MemberTransmitNotify() returned error or invalid message size.\n");
861     /* FIXME: handle error */
862     GNUNET_free (req);
863     return;
864   }
865
866   if (GNUNET_NO == ret && 0 == buf_size)
867   {
868     /* Transmission paused. */
869     GNUNET_free (req);
870     return;
871   }
872
873   req->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST);
874   req->header.size = htons (sizeof (*req) + buf_size);
875   req->request_id = GNUNET_htonll (tmit->request_id);
876   req->fragment_offset = GNUNET_ntohll (tmit->fragment_offset);
877   tmit->fragment_offset += sizeof (*req) + buf_size;
878
879   GNUNET_CLIENT_MANAGER_transmit (grp->client, &req->header);
880 }
881
882
883 /**
884  * Send a message to the origin of the multicast group.
885  *
886  * @param mem Membership handle.
887  * @param request_id Application layer ID for the request.  Opaque to multicast.
888  * @param notify Callback to call to get the message.
889  * @param notify_cls Closure for @a notify.
890  * @return Handle to cancel request, NULL on error (i.e. request already pending).
891  */
892 struct GNUNET_MULTICAST_MemberTransmitHandle *
893 GNUNET_MULTICAST_member_to_origin (struct GNUNET_MULTICAST_Member *mem,
894                                    uint64_t request_id,
895                                    GNUNET_MULTICAST_MemberTransmitNotify notify,
896                                    void *notify_cls)
897 {
898 /* FIXME
899   if (GNUNET_YES == mem->grp.in_transmit)
900     return NULL;
901   mem->grp.in_transmit = GNUNET_YES;
902 */
903
904   struct GNUNET_MULTICAST_MemberTransmitHandle *tmit = &mem->tmit;
905   tmit->member = mem;
906   tmit->request_id = request_id;
907   tmit->notify = notify;
908   tmit->notify_cls = notify_cls;
909
910   member_to_origin (mem);
911   return tmit;
912 }
913
914
915 /**
916  * Resume message transmission to origin.
917  *
918  * @param th  Transmission to cancel.
919  */
920 void
921 GNUNET_MULTICAST_member_to_origin_resume (struct GNUNET_MULTICAST_MemberTransmitHandle *th)
922 {
923
924 }
925
926
927 /**
928  * Cancel request for message transmission to origin.
929  *
930  * @param th  Transmission to cancel.
931  */
932 void
933 GNUNET_MULTICAST_member_to_origin_cancel (struct GNUNET_MULTICAST_MemberTransmitHandle *th)
934 {
935 }
936
937
938 /* end of multicast_api.c */