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