aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Platen <tplaten@posteo.de>2022-07-20 20:26:59 +0200
committerTobias Platen <tplaten@posteo.de>2022-07-20 20:26:59 +0200
commit12db287bf58f2076a65bc34382ce07ec9fe76e38 (patch)
tree89ae191a5c25c701f5fd734f738ac25f7561bdba
parent6a60c7d436f5c4c47752ee54a0980583f0c3fe37 (diff)
downloadgnunet-12db287bf58f2076a65bc34382ce07ec9fe76e38.tar.gz
gnunet-12db287bf58f2076a65bc34382ce07ec9fe76e38.zip
inital import of multicast from secusharedev/tplaten/multicast_messenger
-rw-r--r--configure.ac2
-rw-r--r--src/Makefile.am1
-rw-r--r--src/include/gnunet_multicast_service.h925
-rw-r--r--src/multicast/.gitignore7
-rw-r--r--src/multicast/Makefile.am75
-rw-r--r--src/multicast/gnunet-multicast.c79
-rw-r--r--src/multicast/gnunet-service-multicast.c2236
-rw-r--r--src/multicast/multicast.conf.in21
-rw-r--r--src/multicast/multicast.h303
-rw-r--r--src/multicast/multicast_api.c1399
-rw-r--r--src/multicast/test_multicast.c758
-rw-r--r--src/multicast/test_multicast_2peers.c520
-rw-r--r--src/multicast/test_multicast_multipeer.c643
13 files changed, 6969 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index 20258d622..9b46874ab 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1435,6 +1435,8 @@ src/abd/abd.conf
1435src/reclaim/Makefile 1435src/reclaim/Makefile
1436src/messenger/Makefile 1436src/messenger/Makefile
1437src/messenger/messenger.conf 1437src/messenger/messenger.conf
1438src/multicast/Makefile
1439src/multicast/multicast.conf
1438pkgconfig/Makefile 1440pkgconfig/Makefile
1439pkgconfig/gnunetarm.pc 1441pkgconfig/gnunetarm.pc
1440pkgconfig/gnunetats.pc 1442pkgconfig/gnunetats.pc
diff --git a/src/Makefile.am b/src/Makefile.am
index d7c0b51f0..b84d35b38 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -93,5 +93,6 @@ SUBDIRS = \
93 secretsharing \ 93 secretsharing \
94 reclaim \ 94 reclaim \
95 messenger \ 95 messenger \
96 multicast \
96 $(EXP_DIR) \ 97 $(EXP_DIR) \
97 integration-tests 98 integration-tests
diff --git a/src/include/gnunet_multicast_service.h b/src/include/gnunet_multicast_service.h
new file mode 100644
index 000000000..58fca0b2e
--- /dev/null
+++ b/src/include/gnunet_multicast_service.h
@@ -0,0 +1,925 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19*/
20
21/**
22 * @author Gabor X Toth
23 * @author Christian Grothoff
24 *
25 * @file
26 * Multicast service; multicast messaging via CADET
27 *
28 * @defgroup multicast Multicast service
29 * Multicast messaging via CADET.
30 * @{
31 */
32
33#ifndef GNUNET_MULTICAST_SERVICE_H
34#define GNUNET_MULTICAST_SERVICE_H
35
36#ifdef __cplusplus
37extern "C"
38{
39#if 0 /* keep Emacsens' auto-indent happy */
40}
41#endif
42#endif
43
44#include "gnunet_util_lib.h"
45#include "gnunet_transport_service.h"
46
47/**
48 * Version number of GNUnet-multicast API.
49 */
50#define GNUNET_MULTICAST_VERSION 0x00000000
51
52/**
53 * Opaque handle for a multicast group member.
54 */
55struct GNUNET_MULTICAST_Member;
56
57/**
58 * Handle for the origin of a multicast group.
59 */
60struct GNUNET_MULTICAST_Origin;
61
62
63enum GNUNET_MULTICAST_MessageFlags
64{
65 /**
66 * First fragment of a message.
67 */
68 GNUNET_MULTICAST_MESSAGE_FIRST_FRAGMENT = 1 << 0,
69
70 /**
71 * Last fragment of a message.
72 */
73 GNUNET_MULTICAST_MESSAGE_LAST_FRAGMENT = 1 << 1,
74
75 /**
76 * OR'ed flags if message is not fragmented.
77 */
78 GNUNET_MULTICAST_MESSAGE_NOT_FRAGMENTED
79 = GNUNET_MULTICAST_MESSAGE_FIRST_FRAGMENT
80 | GNUNET_MULTICAST_MESSAGE_LAST_FRAGMENT,
81
82 /**
83 * Historic message, used only locally when replaying messages from local
84 * storage.
85 */
86 GNUNET_MULTICAST_MESSAGE_HISTORIC = 1 << 30
87
88};
89
90
91GNUNET_NETWORK_STRUCT_BEGIN
92
93/**
94 * Header of a multicast message fragment.
95 *
96 * This format is public as the replay mechanism must replay message fragments using the
97 * same format. This is needed as we want to integrity-check message fragments within
98 * the multicast layer to avoid multicasting mal-formed messages.
99 */
100struct GNUNET_MULTICAST_MessageHeader
101{
102
103 /**
104 * Header for all multicast message fragments from the origin.
105 */
106 struct GNUNET_MessageHeader header;
107
108 /**
109 * Number of hops this message fragment has taken since the origin.
110 *
111 * Helpful to determine shortest paths to the origin among honest peers for
112 * unicast requests from members. Updated at each hop and thus not signed and
113 * not secure.
114 */
115 uint32_t hop_counter GNUNET_PACKED;
116
117 /**
118 * ECC signature of the message fragment.
119 *
120 * Signature must match the public key of the multicast group.
121 */
122 struct GNUNET_CRYPTO_EddsaSignature signature;
123
124 /**
125 * Purpose for the signature and size of the signed data.
126 */
127 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
128
129 /**
130 * Number of the message fragment, monotonically increasing starting from 1.
131 */
132 uint64_t fragment_id GNUNET_PACKED;
133
134 /**
135 * Byte offset of this @e fragment of the @e message.
136 */
137 uint64_t fragment_offset GNUNET_PACKED;
138
139 /**
140 * Number of the message this fragment belongs to.
141 *
142 * Set in GNUNET_MULTICAST_origin_to_all().
143 */
144 uint64_t message_id GNUNET_PACKED;
145
146 /**
147 * Counter that monotonically increases whenever a member parts the group.
148 *
149 * Set in GNUNET_MULTICAST_origin_to_all().
150 *
151 * It has significance in case of replay requests: when a member has missed
152 * messages and gets a replay request: in this case if the @a group_generation
153 * is still the same before and after the missed messages, it means that no
154 * @e join or @e part operations happened during the missed messages.
155 */
156 uint64_t group_generation GNUNET_PACKED;
157
158 /**
159 * Flags for this message fragment.
160 *
161 * @see enum GNUNET_MULTICAST_MessageFlags
162 */
163 uint32_t flags GNUNET_PACKED;
164
165 /* Followed by message body. */
166};
167
168
169/**
170 * Header of a request from a member to the origin.
171 */
172struct GNUNET_MULTICAST_RequestHeader
173{
174 /**
175 * Header for all requests from a member to the origin.
176 */
177 struct GNUNET_MessageHeader header;
178
179 /**
180 * Public key of the sending member.
181 */
182 struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
183
184 /**
185 * ECC signature of the request fragment.
186 *
187 * Signature must match the public key of the multicast group.
188 */
189 struct GNUNET_CRYPTO_EcdsaSignature signature;
190
191 /**
192 * Purpose for the signature and size of the signed data.
193 */
194 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
195
196 /**
197 * Number of the request fragment.
198 * Monotonically increasing from 1.
199 */
200 uint64_t fragment_id GNUNET_PACKED;
201
202 /**
203 * Byte offset of this @e fragment of the @e request.
204 */
205 uint64_t fragment_offset GNUNET_PACKED;
206
207 /**
208 * Number of the request this fragment belongs to.
209 *
210 * Set in GNUNET_MULTICAST_origin_to_all().
211 */
212 uint64_t request_id GNUNET_PACKED;
213
214 /**
215 * Flags for this request.
216 */
217 enum GNUNET_MULTICAST_MessageFlags flags GNUNET_PACKED;
218
219 /* Followed by request body. */
220};
221
222GNUNET_NETWORK_STRUCT_END
223
224
225/**
226 * Maximum size of a multicast message fragment.
227 */
228#define GNUNET_MULTICAST_FRAGMENT_MAX_SIZE (63 * 1024)
229
230#define GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD \
231 (GNUNET_MULTICAST_FRAGMENT_MAX_SIZE \
232 - sizeof (struct GNUNET_MULTICAST_MessageHeader))
233
234
235/**
236 * Handle that identifies a join request.
237 *
238 * Used to match calls to #GNUNET_MULTICAST_JoinRequestCallback to the
239 * corresponding calls to #GNUNET_MULTICAST_join_decision().
240 */
241struct GNUNET_MULTICAST_JoinHandle;
242
243
244/**
245 * Function to call with the decision made for a join request.
246 *
247 * Must be called once and only once in response to an invocation of the
248 * #GNUNET_MULTICAST_JoinRequestCallback.
249 *
250 * @param jh
251 * Join request handle.
252 * @param is_admitted
253 * #GNUNET_YES if the join is approved,
254 * #GNUNET_NO if it is disapproved,
255 * #GNUNET_SYSERR if we cannot answer the request.
256 * @param relay_count
257 * Number of relays given.
258 * @param relays
259 * Array of suggested peers that might be useful relays to use
260 * when joining the multicast group (essentially a list of peers that
261 * are already part of the multicast group and might thus be willing
262 * to help with routing). If empty, only this local peer (which must
263 * be the multicast origin) is a good candidate for building the
264 * multicast tree. Note that it is unnecessary to specify our own
265 * peer identity in this array.
266 * @param join_resp
267 * Message to send in response to the joining peer;
268 * can also be used to redirect the peer to a different group at the
269 * application layer; this response is to be transmitted to the
270 * peer that issued the request even if admission is denied.
271 */
272struct GNUNET_MULTICAST_ReplayHandle *
273GNUNET_MULTICAST_join_decision (struct GNUNET_MULTICAST_JoinHandle *jh,
274 int is_admitted,
275 uint16_t relay_count,
276 const struct GNUNET_PeerIdentity *relays,
277 const struct GNUNET_MessageHeader *join_resp);
278
279
280/**
281 * Method called whenever another peer wants to join the multicast group.
282 *
283 * Implementations of this function must call GNUNET_MULTICAST_join_decision()
284 * with the decision.
285 *
286 * @param cls
287 * Closure.
288 * @param member_pub_key
289 * Public key of the member requesting join.
290 * @param join_msg
291 * Application-dependent join message from the new member.
292 * @param jh
293 * Join handle to pass to GNUNET_MULTICAST_join_decison().
294 */
295typedef void
296(*GNUNET_MULTICAST_JoinRequestCallback) (void *cls,
297 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
298 const struct GNUNET_MessageHeader *join_msg,
299 struct GNUNET_MULTICAST_JoinHandle *jh);
300
301
302/**
303 * Method called to inform about the decision in response to a join request.
304 *
305 * If @a is_admitted is not #GNUNET_YES, then the multicast service disconnects
306 * the client and the multicast member handle returned by
307 * GNUNET_MULTICAST_member_join() is invalidated.
308 *
309 * @param cls
310 * Closure.
311 * @param is_admitted
312 * #GNUNET_YES or #GNUNET_NO or #GNUNET_SYSERR
313 * @param peer
314 * The peer we are connected to and the join decision is from.
315 * @param relay_count
316 * Number of peers in the @a relays array.
317 * @param relays
318 * Peer identities of members of the group, which serve as relays
319 * and can be used to join the group at. If empty, only the origin can
320 * be used to connect to the group.
321 * @param join_msg
322 * Application-dependent join message from the origin.
323 */
324typedef void
325(*GNUNET_MULTICAST_JoinDecisionCallback) (void *cls,
326 int is_admitted,
327 const struct GNUNET_PeerIdentity *peer,
328 uint16_t relay_count,
329 const struct GNUNET_PeerIdentity *relays,
330 const struct GNUNET_MessageHeader *join_msg);
331
332
333/**
334 * Function called whenever a group member has transmitted a request
335 * to the origin (other than joining or leaving).
336 *
337 * FIXME: need to distinguish between origin cancelling a message (some fragments
338 * were sent, then the rest 'discarded') and the case where we got disconnected;
339 * right now, both would mean 'msg' is NULL, but they could be quite different...
340 * So the semantics from the receiver side of
341 * GNUNET_MULTICAST_member_to_origin_cancel() are not clear here. Maybe we
342 * should do something with the flags in this case?
343 *
344 * @param cls
345 * Closure (set from GNUNET_MULTICAST_origin_start).
346 * @param sender
347 * Identity of the sender.
348 * @param req
349 * Request to the origin.
350 * @param flags
351 * Flags for the request.
352 */
353typedef void
354(*GNUNET_MULTICAST_RequestCallback) (void *cls,
355 const struct GNUNET_MULTICAST_RequestHeader *req);
356
357
358/**
359 * Function called whenever a group member is receiving a message fragment from
360 * the origin.
361 *
362 * If admission to the group is denied, this function is called once with the
363 * response of the @e origin (as given to GNUNET_MULTICAST_join_decision()) and
364 * then a second time with NULL to indicate that the connection failed for good.
365 *
366 * FIXME: need to distinguish between origin cancelling a message (some fragments
367 * were sent, then the rest 'discarded') and the case where we got disconnected;
368 * right now, both would mean 'msg' is NULL, but they could be quite different...
369 * So the semantics from the receiver side of
370 * GNUNET_MULTICAST_origin_to_all_cancel() are not clear here.
371 *
372 * @param cls
373 * Closure (set from GNUNET_MULTICAST_member_join())
374 * @param msg
375 * Message from the origin, NULL if the origin shut down
376 * (or we were kicked out, and we should thus call
377 * GNUNET_MULTICAST_member_part() next)
378 */
379typedef void
380(*GNUNET_MULTICAST_MessageCallback) (void *cls,
381 const struct GNUNET_MULTICAST_MessageHeader *msg);
382
383
384/**
385 * Opaque handle to a replay request from the multicast service.
386 */
387struct GNUNET_MULTICAST_ReplayHandle;
388
389
390/**
391 * Functions with this signature are called whenever the multicast service needs
392 * a message fragment to be replayed by fragment_id.
393 *
394 * Implementations of this function MUST call GNUNET_MULTICAST_replay() ONCE
395 * (with a message or an error); however, if the origin is destroyed or the
396 * group is left, the replay handle must no longer be used.
397 *
398 * @param cls
399 * Closure (set from GNUNET_MULTICAST_origin_start()
400 * or GNUNET_MULTICAST_member_join()).
401 * @param member_pub_key
402 * The member requesting replay.
403 * @param fragment_id
404 * Which message fragment should be replayed.
405 * @param flags
406 * Flags for the replay.
407 * @param rh
408 * Handle to pass to message transmit function.
409 */
410typedef void
411(*GNUNET_MULTICAST_ReplayFragmentCallback) (void *cls,
412 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
413 uint64_t fragment_id,
414 uint64_t flags,
415 struct GNUNET_MULTICAST_ReplayHandle *rh);
416
417/**
418 * Functions with this signature are called whenever the multicast service needs
419 * a message fragment to be replayed by message_id and fragment_offset.
420 *
421 * Implementations of this function MUST call GNUNET_MULTICAST_replay() ONCE
422 * (with a message or an error); however, if the origin is destroyed or the
423 * group is left, the replay handle must no longer be used.
424 *
425 * @param cls
426 * Closure (set from GNUNET_MULTICAST_origin_start()
427 * or GNUNET_MULTICAST_member_join()).
428 * @param member_pub_key
429 * The member requesting replay.
430 * @param message_id
431 * Which message should be replayed.
432 * @param fragment_offset
433 * Offset of the fragment within of @a message_id to be replayed.
434 * @param flags
435 * Flags for the replay.
436 * @param rh
437 * Handle to pass to message transmit function.
438 */
439typedef void
440(*GNUNET_MULTICAST_ReplayMessageCallback) (void *cls,
441 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
442 uint64_t message_id,
443 uint64_t fragment_offset,
444 uint64_t flags,
445 struct GNUNET_MULTICAST_ReplayHandle *rh);
446
447
448/**
449 * Possible error codes during replay.
450 */
451enum GNUNET_MULTICAST_ReplayErrorCode
452{
453
454 /**
455 * Everything is fine.
456 */
457 GNUNET_MULTICAST_REC_OK = 0,
458
459 /**
460 * Message fragment not found in the message store.
461 *
462 * Either discarded if it is too old, or not arrived yet if this member has
463 * missed some messages.
464 */
465 GNUNET_MULTICAST_REC_NOT_FOUND = 1,
466
467 /**
468 * Fragment ID counter was larger than the highest counter this
469 * replay function has ever encountered; thus it is likely the
470 * origin never sent it and we're at the HEAD of the multicast
471 * stream as far as this node is concerned.
472 *
473 * FIXME: needed?
474 */
475 GNUNET_MULTICAST_REC_PAST_HEAD = 2,
476
477 /**
478 * Access is denied to the requested fragment, membership test did not pass.
479 */
480 GNUNET_MULTICAST_REC_ACCESS_DENIED = 3,
481
482 /**
483 * Internal error (i.e. database error). Try some other peer.
484 */
485 GNUNET_MULTICAST_REC_INTERNAL_ERROR = 4
486
487};
488
489
490/**
491 * Replay a message fragment for the multicast group.
492 *
493 * @param rh
494 * Replay handle identifying which replay operation was requested.
495 * @param msg
496 * Replayed message fragment, NULL if not found / an error occurred.
497 * @param ec
498 * Error code. See enum GNUNET_MULTICAST_ReplayErrorCode
499 * If not #GNUNET_MULTICAST_REC_OK, the replay handle is invalidated.
500 */
501void
502GNUNET_MULTICAST_replay_response (struct GNUNET_MULTICAST_ReplayHandle *rh,
503 const struct GNUNET_MessageHeader *msg,
504 enum GNUNET_MULTICAST_ReplayErrorCode ec);
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 */
514void
515GNUNET_MULTICAST_replay_response_end (struct GNUNET_MULTICAST_ReplayHandle *rh);
516
517
518/**
519 * Function called to provide data for a transmission for a replay.
520 *
521 * @see GNUNET_MULTICAST_replay2()
522 */
523typedef int
524(*GNUNET_MULTICAST_ReplayTransmitNotify) (void *cls,
525 size_t *data_size,
526 void *data);
527
528
529/**
530 * Replay a message for the multicast group.
531 *
532 * @param rh
533 * Replay handle identifying which replay operation was requested.
534 * @param notify
535 * Function to call to get the message.
536 * @param notify_cls
537 * Closure for @a notify.
538 */
539void
540GNUNET_MULTICAST_replay_response2 (struct GNUNET_MULTICAST_ReplayHandle *rh,
541 GNUNET_MULTICAST_ReplayTransmitNotify notify,
542 void *notify_cls);
543
544
545/**
546 * Start a multicast group.
547 *
548 * Peers that issue GNUNET_MULTICAST_member_join() can transmit a join request
549 * to either an existing group member or to the origin. If the joining is
550 * approved, the member is cleared for @e replay and will begin to receive
551 * messages transmitted to the group. If joining is disapproved, the failed
552 * candidate will be given a response. Members in the group can send messages
553 * to the origin.
554 *
555 * TODO: This function could optionally offer to advertise the origin in the
556 * P2P overlay network(where?) under the respective public key so that other
557 * peers can find an alternate PeerId to join it. Higher level protocols may
558 * however provide other means of solving the problem of the offline host
559 * (see secushare specs about that) and therefore merely need a way to provide
560 * a list of possible PeerIds.
561 *
562 * @param cfg
563 * Configuration to use.
564 * @param priv_key
565 * ECC key that will be used to sign messages for this
566 * multicast session; public key is used to identify the multicast group;
567 * @param max_fragment_id
568 * Maximum fragment ID already sent to the group.
569 * 0 for a new group.
570 * @param join_request_cb
571 * Function called to approve / disapprove joining of a peer.
572 * @param replay_frag_cb
573 * Function that can be called to replay a message fragment.
574 * @param replay_msg_cb
575 * Function that can be called to replay a message.
576 * @param request_cb
577 * Function called with message fragments from group members.
578 * @param message_cb
579 * Function called with the message fragments sent to the
580 * network by GNUNET_MULTICAST_origin_to_all(). These message fragments
581 * should be stored for answering replay requests later.
582 * @param cls
583 * Closure for the various callbacks that follow.
584 *
585 * @return Handle for the origin, NULL on error.
586 */
587struct GNUNET_MULTICAST_Origin *
588GNUNET_MULTICAST_origin_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
589 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv_key,
590 uint64_t max_fragment_id,
591 GNUNET_MULTICAST_JoinRequestCallback join_request_cb,
592 GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb,
593 GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb,
594 GNUNET_MULTICAST_RequestCallback request_cb,
595 GNUNET_MULTICAST_MessageCallback message_cb,
596 void *cls);
597
598/**
599 * Function called to provide data for a transmission from the origin to all
600 * members.
601 *
602 * Note that returning #GNUNET_OK or #GNUNET_SYSERR (but not #GNUNET_NO)
603 * invalidates the respective transmission handle.
604 *
605 * @param cls
606 * Closure.
607 * @param[in,out] data_size
608 * Initially set to the number of bytes available in
609 * @a data, should be set to the number of bytes written to data.
610 * @param[out] data
611 * Where to write the body of the message to give to the
612 * method. The function must copy at most @a data_size bytes to @a data.
613 *
614 * @return #GNUNET_SYSERR on error (fatal, aborts transmission)
615 * #GNUNET_NO on success, if more data is to be transmitted later.
616 * Should be used if @a data_size was not big enough to take all the
617 * data. If 0 is returned in @a data_size the transmission is paused,
618 * and can be resumed with GNUNET_MULTICAST_origin_to_all_resume().
619 * #GNUNET_YES if this completes the transmission (all data supplied)
620 * @deprecated should move to MQ-style API!
621 */
622typedef int
623(*GNUNET_MULTICAST_OriginTransmitNotify) (void *cls,
624 size_t *data_size,
625 void *data);
626
627
628/**
629 * Handle for a request to send a message to all multicast group members
630 * (from the origin).
631 */
632struct GNUNET_MULTICAST_OriginTransmitHandle;
633
634
635/**
636 * Send a message to the multicast group.
637 *
638 * @param origin
639 * Handle to the multicast group.
640 * @param message_id
641 * Application layer ID for the message. Opaque to multicast.
642 * @param group_generation
643 * Group generation of the message. Documented in
644 * struct GNUNET_MULTICAST_MessageHeader.
645 * @param notify
646 * Function to call to get the message.
647 * @param notify_cls
648 * Closure for @a notify.
649 *
650 * @return NULL on error (i.e. request already pending).
651 * @deprecated should move to MQ-style API!
652 */
653struct GNUNET_MULTICAST_OriginTransmitHandle *
654GNUNET_MULTICAST_origin_to_all (struct GNUNET_MULTICAST_Origin *origin,
655 uint64_t message_id,
656 uint64_t group_generation,
657 GNUNET_MULTICAST_OriginTransmitNotify notify,
658 void *notify_cls);
659
660
661
662/**
663 * Resume message transmission to multicast group.
664 *
665 * @param th Transmission to cancel.
666 */
667void
668GNUNET_MULTICAST_origin_to_all_resume (struct GNUNET_MULTICAST_OriginTransmitHandle *th);
669
670
671/**
672 * Cancel request for message transmission to multicast group.
673 *
674 * @param th Transmission to cancel.
675 */
676void
677GNUNET_MULTICAST_origin_to_all_cancel (struct GNUNET_MULTICAST_OriginTransmitHandle *th);
678
679
680/**
681 * Stop a multicast group.
682 *
683 * @param origin Multicast group to stop.
684 */
685void
686GNUNET_MULTICAST_origin_stop (struct GNUNET_MULTICAST_Origin *origin,
687 GNUNET_ContinuationCallback stop_cb,
688 void *stop_cls);
689
690
691/**
692 * Join a multicast group.
693 *
694 * The entity joining is always the local peer. Further information about the
695 * candidate can be provided in @a join_msg. If the join fails, the
696 * @a message_cb is invoked with a (failure) response and then with NULL. If
697 * the join succeeds, outstanding (state) messages and ongoing multicast
698 * messages will be given to the @a message_cb until the member decides to part
699 * the group. The @a mem_test_cb and @a replay_cb functions may be called at
700 * anytime by the multicast service to support relaying messages to other
701 * members of the group.
702 *
703 * @param cfg
704 * Configuration to use.
705 * @param group_key
706 * ECC public key that identifies the group to join.
707 * @param member_pub_key
708 * ECC key that identifies the member
709 * and used to sign requests sent to the origin.
710 * @param origin
711 * Peer ID of the origin to send unicast requsets to. If NULL,
712 * unicast requests are sent back via multiple hops on the reverse path
713 * of multicast messages.
714 * @param relay_count
715 * Number of peers in the @a relays array.
716 * @param relays
717 * Peer identities of members of the group, which serve as relays
718 * and can be used to join the group at. and send the @a join_request to.
719 * If empty, the @a join_request is sent directly to the @a origin.
720 * @param join_msg
721 * Application-dependent join message to be passed to the peer @a origin.
722 * @param join_request_cb
723 * Function called to approve / disapprove joining of a peer.
724 * @param join_decision_cb
725 * Function called to inform about the join decision.
726 * @param replay_frag_cb
727 * Function that can be called to replay message fragments
728 * this peer already knows from this group. NULL if this
729 * client is unable to support replay.
730 * @param replay_msg_cb
731 * Function that can be called to replay message fragments
732 * this peer already knows from this group. NULL if this
733 * client is unable to support replay.
734 * @param message_cb
735 * Function to be called for all message fragments we
736 * receive from the group, excluding those our @a replay_cb
737 * already has.
738 * @param cls
739 * Closure for callbacks.
740 *
741 * @return Handle for the member, NULL on error.
742 */
743struct GNUNET_MULTICAST_Member *
744GNUNET_MULTICAST_member_join (const struct GNUNET_CONFIGURATION_Handle *cfg,
745 const struct GNUNET_CRYPTO_EddsaPublicKey *group_key,
746 const struct GNUNET_CRYPTO_EcdsaPrivateKey *member_pub_key,
747 const struct GNUNET_PeerIdentity *origin,
748 uint16_t relay_count,
749 const struct GNUNET_PeerIdentity *relays,
750 const struct GNUNET_MessageHeader *join_request,
751 GNUNET_MULTICAST_JoinRequestCallback join_request_cb,
752 GNUNET_MULTICAST_JoinDecisionCallback join_decision_cb,
753 GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb,
754 GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb,
755 GNUNET_MULTICAST_MessageCallback message_cb,
756 void *cls);
757
758/**
759 * Handle for a replay request.
760 */
761struct GNUNET_MULTICAST_MemberReplayHandle;
762
763
764/**
765 * Request a fragment to be replayed by fragment ID.
766 *
767 * Useful if messages below the @e max_known_fragment_id given when joining are
768 * needed and not known to the client.
769 *
770 * @param member
771 * Membership handle.
772 * @param fragment_id
773 * ID of a message fragment that this client would like to see replayed.
774 * @param flags
775 * Additional flags for the replay request.
776 * It is used and defined by GNUNET_MULTICAST_ReplayFragmentCallback
777 *
778 * @return Replay request handle, NULL on error.
779 */
780struct GNUNET_MULTICAST_MemberReplayHandle *
781GNUNET_MULTICAST_member_replay_fragment (struct GNUNET_MULTICAST_Member *member,
782 uint64_t fragment_id,
783 uint64_t flags);
784
785
786/**
787 * Request a message fr to be replayed.
788 *
789 * Useful if messages below the @e max_known_fragment_id given when joining are
790 * needed and not known to the client.
791 *
792 * @param member
793 * Membership handle.
794 * @param message_id
795 * ID of the message this client would like to see replayed.
796 * @param fragment_offset
797 * Offset of the fragment within the message to replay.
798 * @param flags
799 * Additional flags for the replay request.
800 * It is used & defined by GNUNET_MULTICAST_ReplayMessageCallback
801 *
802 * @return Replay request handle, NULL on error.
803 */
804struct GNUNET_MULTICAST_MemberReplayHandle *
805GNUNET_MULTICAST_member_replay_message (struct GNUNET_MULTICAST_Member *member,
806 uint64_t message_id,
807 uint64_t fragment_offset,
808 uint64_t flags);
809
810
811/**
812 * Cancel a replay request.
813 *
814 * @param rh
815 * Request to cancel.
816 */
817void
818GNUNET_MULTICAST_member_replay_cancel (struct GNUNET_MULTICAST_MemberReplayHandle *rh);
819
820
821/**
822 * Part a multicast group.
823 *
824 * Disconnects from all group members and invalidates the @a member handle.
825 *
826 * An application-dependent part message can be transmitted beforehand using
827 * #GNUNET_MULTICAST_member_to_origin())
828 *
829 * @param member
830 * Membership handle.
831 */
832void
833GNUNET_MULTICAST_member_part (struct GNUNET_MULTICAST_Member *member,
834 GNUNET_ContinuationCallback part_cb,
835 void *part_cls);
836
837
838/**
839 * Function called to provide data for a transmission from a member to the origin.
840 *
841 * Note that returning #GNUNET_OK or #GNUNET_SYSERR (but not #GNUNET_NO)
842 * invalidates the respective transmission handle.
843 *
844 * @param cls
845 * Closure.
846 * @param[in,out] data_size
847 * Initially set to the number of bytes available in
848 * @a data, should be set to the number of bytes written to data.
849 * @param[out] data
850 * Where to write the body of the message to give to the
851 * method. The function must copy at most @a data_size bytes to @a data.
852 *
853 * @return #GNUNET_SYSERR on error (fatal, aborts transmission)
854 * #GNUNET_NO on success, if more data is to be transmitted later.
855 * Should be used if @a data_size was not big enough to take all the
856 * data. If 0 is returned in @a data_size the transmission is paused,
857 * and can be resumed with GNUNET_MULTICAST_member_to_origin_resume().
858 * #GNUNET_YES if this completes the transmission (all data supplied)
859 * @deprecated should move to MQ-style API!
860 */
861typedef int
862(*GNUNET_MULTICAST_MemberTransmitNotify) (void *cls,
863 size_t *data_size,
864 void *data);
865
866
867/**
868 * Handle for a message to be delivered from a member to the origin.
869 */
870struct GNUNET_MULTICAST_MemberTransmitHandle;
871
872
873/**
874 * Send a message to the origin of the multicast group.
875 *
876 * @param member
877 * Membership handle.
878 * @param request_id
879 * Application layer ID for the request. Opaque to multicast.
880 * @param notify
881 * Callback to call to get the message.
882 * @param notify_cls
883 * Closure for @a notify.
884 *
885 * @return Handle to cancel request, NULL on error (i.e. request already pending).
886 * @deprecated should move to MQ-style API!
887 */
888struct GNUNET_MULTICAST_MemberTransmitHandle *
889GNUNET_MULTICAST_member_to_origin (struct GNUNET_MULTICAST_Member *member,
890 uint64_t request_id,
891 GNUNET_MULTICAST_MemberTransmitNotify notify,
892 void *notify_cls);
893
894
895/**
896 * Resume message transmission to origin.
897 *
898 * @param th
899 * Transmission to cancel.
900 */
901void
902GNUNET_MULTICAST_member_to_origin_resume (struct GNUNET_MULTICAST_MemberTransmitHandle *th);
903
904
905/**
906 * Cancel request for message transmission to origin.
907 *
908 * @param th
909 * Transmission to cancel.
910 */
911void
912GNUNET_MULTICAST_member_to_origin_cancel (struct GNUNET_MULTICAST_MemberTransmitHandle *th);
913
914
915#if 0 /* keep Emacsens' auto-indent happy */
916{
917#endif
918#ifdef __cplusplus
919}
920#endif
921
922/* ifndef GNUNET_MULTICAST_SERVICE_H */
923#endif
924
925/** @} */ /* end of group */
diff --git a/src/multicast/.gitignore b/src/multicast/.gitignore
new file mode 100644
index 000000000..a97844e81
--- /dev/null
+++ b/src/multicast/.gitignore
@@ -0,0 +1,7 @@
1gnunet-service-multicast
2gnunet-multicast
3test_multicast
4test_multicast_multipeer
5test_multicast_2peers
6test_multicast_multipeer_line
7test_multicast_multipeer_star
diff --git a/src/multicast/Makefile.am b/src/multicast/Makefile.am
new file mode 100644
index 000000000..0e1c77c41
--- /dev/null
+++ b/src/multicast/Makefile.am
@@ -0,0 +1,75 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include \
3 $(GNUNET_CPPFLAGS)
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8pkgcfg_DATA = \
9 multicast.conf
10
11if USE_COVERAGE
12 AM_CFLAGS = -fprofile-arcs -ftest-coverage
13endif
14
15lib_LTLIBRARIES = libgnunetmulticast.la
16
17libgnunetmulticast_la_SOURCES = \
18 multicast_api.c multicast.h
19libgnunetmulticast_la_LIBADD = \
20 -lgnunetutil \
21 $(GN_LIBINTL) $(XLIB)
22libgnunetmulticast_la_LDFLAGS = \
23 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
24 -version-info 0:0:0
25
26
27bin_PROGRAMS = \
28 gnunet-multicast
29
30libexec_PROGRAMS = \
31 gnunet-service-multicast \
32 $(EXP_LIBEXEC)
33
34gnunet_multicast_SOURCES = \
35 gnunet-multicast.c
36gnunet_multicast_LDADD = \
37 -lgnunetutil \
38 $(GN_LIBINTL)
39
40gnunet_service_multicast_SOURCES = \
41 gnunet-service-multicast.c
42gnunet_service_multicast_LDADD = \
43 -lgnunetutil \
44 -lgnunetcadet \
45 -lgnunetstatistics \
46 $(GN_LIBINTL)
47
48check_PROGRAMS = \
49 test_multicast \
50 test_multicast_multipeer_star \
51 test_multicast_multipeer_line
52
53if ENABLE_TEST_RUN
54AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@}; export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH; unset XDG_DATA_HOME; unset XDG_CONFIG_HOME;
55TESTS = $(check_PROGRAMS)
56endif
57
58test_multicast_SOURCES = \
59 test_multicast.c
60test_multicast_LDADD = \
61 libgnunetmulticast.la \
62 -lgnunettesting \
63 -lgnunetutil
64test_multicast_multipeer_star_SOURCES = \
65 test_multicast_multipeer.c
66test_multicast_multipeer_star_LDADD = \
67 libgnunetmulticast.la \
68 -lgnunettestbed \
69 -lgnunetutil
70test_multicast_multipeer_line_SOURCES = \
71 test_multicast_multipeer.c
72test_multicast_multipeer_line_LDADD = \
73 libgnunetmulticast.la \
74 -lgnunettestbed \
75 -lgnunetutil
diff --git a/src/multicast/gnunet-multicast.c b/src/multicast/gnunet-multicast.c
new file mode 100644
index 000000000..802600487
--- /dev/null
+++ b/src/multicast/gnunet-multicast.c
@@ -0,0 +1,79 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19*/
20
21/**
22 * @file multicast/gnunet-multicast.c
23 * @brief multicast for writing a tool
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28/* #include "gnunet_multicast_service.h" */
29
30/**
31 * Final status code.
32 */
33static int ret;
34
35/**
36 * Main function that will be run by the scheduler.
37 *
38 * @param cls closure
39 * @param args remaining command-line arguments
40 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
41 * @param cfg configuration
42 */
43static void
44run (void *cls, char *const *args, const char *cfgfile,
45 const struct GNUNET_CONFIGURATION_Handle *cfg)
46{
47 /* main code here */
48 puts( gettext_noop ("This command doesn't do anything yet.") );
49 ret = -1;
50}
51
52
53/**
54 * The main function.
55 *
56 * @param argc number of arguments from the command line
57 * @param argv command line arguments
58 * @return 0 ok, 1 on error
59 */
60int
61main (int argc, char *const *argv)
62{
63 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
64 /* FIMXE: add options here */
65 GNUNET_GETOPT_OPTION_END
66 };
67 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
68 return 2;
69
70 ret = (GNUNET_OK ==
71 GNUNET_PROGRAM_run (argc, argv, "gnunet-multicast",
72 gettext_noop ("This command doesn't do anything yet."),
73 options, &run,
74 NULL)) ? ret : 1;
75 //XXX GNUNET_free ((void*) argv);
76 return ret;
77}
78
79/* end of gnunet-multicast.c */
diff --git a/src/multicast/gnunet-service-multicast.c b/src/multicast/gnunet-service-multicast.c
new file mode 100644
index 000000000..236a7c8cd
--- /dev/null
+++ b/src/multicast/gnunet-service-multicast.c
@@ -0,0 +1,2236 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19*/
20
21/**
22 * @file multicast/gnunet-service-multicast.c
23 * @brief program that does multicast
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_signatures.h"
30#include "gnunet_util_lib.h"
31#include "gnunet_signatures.h"
32#include "gnunet_applications.h"
33#include "gnunet_statistics_service.h"
34#include "gnunet_cadet_service.h"
35#include "gnunet_multicast_service.h"
36#include "multicast.h"
37
38/**
39 * Handle to our current configuration.
40 */
41static const struct GNUNET_CONFIGURATION_Handle *cfg;
42
43/**
44 * Service handle.
45 */
46static struct GNUNET_SERVICE_Handle *service;
47
48/**
49 * CADET handle.
50 */
51static struct GNUNET_CADET_Handle *cadet;
52
53/**
54 * Identity of this peer.
55 */
56static struct GNUNET_PeerIdentity this_peer;
57
58/**
59 * Handle to the statistics service.
60 */
61static struct GNUNET_STATISTICS_Handle *stats;
62
63/**
64 * All connected origin clients.
65 * Group's pub_key_hash -> struct Origin * (uniq)
66 */
67static struct GNUNET_CONTAINER_MultiHashMap *origins;
68
69/**
70 * All connected member clients.
71 * Group's pub_key_hash -> struct Member * (multi)
72 */
73static struct GNUNET_CONTAINER_MultiHashMap *members;
74
75/**
76 * Connected member clients per group.
77 * Group's pub_key_hash -> Member's pub_key_hash (uniq) -> struct Member * (uniq)
78 */
79static struct GNUNET_CONTAINER_MultiHashMap *group_members;
80
81/**
82 * Incoming CADET channels with connected children in the tree.
83 * Group's pub_key_hash -> struct Channel * (multi)
84 */
85static struct GNUNET_CONTAINER_MultiHashMap *channels_in;
86
87/**
88 * Outgoing CADET channels connecting to parents in the tree.
89 * Group's pub_key_hash -> struct Channel * (multi)
90 */
91static struct GNUNET_CONTAINER_MultiHashMap *channels_out;
92
93/**
94 * Incoming replay requests from CADET.
95 * Group's pub_key_hash ->
96 * H(fragment_id, message_id, fragment_offset, flags) -> struct Channel *
97 */
98static struct GNUNET_CONTAINER_MultiHashMap *replay_req_cadet;
99
100/**
101 * Incoming replay requests from clients.
102 * Group's pub_key_hash ->
103 * H(fragment_id, message_id, fragment_offset, flags) -> struct GNUNET_SERVICE_Client *
104 */
105static struct GNUNET_CONTAINER_MultiHashMap *replay_req_client;
106
107
108/**
109 * Join status of a remote peer.
110 */
111enum JoinStatus
112{
113 JOIN_REFUSED = -1,
114 JOIN_NOT_ASKED = 0,
115 JOIN_WAITING = 1,
116 JOIN_ADMITTED = 2,
117};
118
119enum ChannelDirection
120{
121 DIR_INCOMING = 0,
122 DIR_OUTGOING = 1,
123};
124
125
126/**
127 * Context for a CADET channel.
128 */
129struct Channel
130{
131 /**
132 * Group the channel belongs to.
133 *
134 * Only set for outgoing channels.
135 */
136 struct Group *group;
137
138 /**
139 * CADET channel.
140 */
141 struct GNUNET_CADET_Channel *channel;
142
143 // FIXME: not used
144 /**
145 * CADET transmission handle.
146 */
147 struct GNUNET_CADET_TransmitHandle *tmit_handle;
148
149 /**
150 * Public key of the target group.
151 */
152 struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key;
153
154 /**
155 * Hash of @a group_pub_key.
156 */
157 struct GNUNET_HashCode group_pub_hash;
158
159 /**
160 * Public key of the joining member.
161 */
162 struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
163
164 /**
165 * Remote peer identity.
166 */
167 struct GNUNET_PeerIdentity peer;
168
169 /**
170 * Current window size, set by cadet_notify_window_change()
171 */
172 int32_t window_size;
173
174 /**
175 * Is the connection established?
176 */
177 int8_t is_connected;
178
179 /**
180 * Is the remote peer admitted to the group?
181 * @see enum JoinStatus
182 */
183 int8_t join_status;
184
185 /**
186 * Number of messages waiting to be sent to CADET.
187 */
188 uint8_t msgs_pending;
189
190 /**
191 * Channel direction.
192 * @see enum ChannelDirection
193 */
194 uint8_t direction;
195};
196
197
198/**
199 * List of connected clients.
200 */
201struct ClientList
202{
203 struct ClientList *prev;
204 struct ClientList *next;
205 struct GNUNET_SERVICE_Client *client;
206};
207
208
209/**
210 * Client context for an origin or member.
211 */
212struct Group
213{
214 struct ClientList *clients_head;
215 struct ClientList *clients_tail;
216
217 /**
218 * Public key of the group.
219 */
220 struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
221
222 /**
223 * Hash of @a pub_key.
224 */
225 struct GNUNET_HashCode pub_key_hash;
226
227 /**
228 * CADET port hash.
229 */
230 struct GNUNET_HashCode cadet_port_hash;
231
232 /**
233 * Is the client disconnected? #GNUNET_YES or #GNUNET_NO
234 */
235 uint8_t is_disconnected;
236
237 /**
238 * Is this an origin (#GNUNET_YES), or member (#GNUNET_NO)?
239 */
240 uint8_t is_origin;
241
242 union {
243 struct Origin *origin;
244 struct Member *member;
245 };
246};
247
248
249/**
250* Client context for a group's origin.
251 */
252struct Origin
253{
254 struct Group group;
255
256 /**
257 * Private key of the group.
258 */
259 struct GNUNET_CRYPTO_EddsaPrivateKey priv_key;
260
261 /**
262 * CADET port.
263 */
264 struct GNUNET_CADET_Port *cadet_port;
265
266 /**
267 * Last message fragment ID sent to the group.
268 */
269 uint64_t max_fragment_id;
270};
271
272
273/**
274 * Client context for a group member.
275 */
276struct Member
277{
278 struct Group group;
279
280 /**
281 * Private key of the member.
282 */
283 struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
284
285 /**
286 * Public key of the member.
287 */
288 struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
289
290 /**
291 * Hash of @a pub_key.
292 */
293 struct GNUNET_HashCode pub_key_hash;
294
295 /**
296 * Join request sent to the origin / members.
297 */
298 struct MulticastJoinRequestMessage *join_req;
299
300 /**
301 * Join decision sent in reply to our request.
302 *
303 * Only a positive decision is stored here, in case of a negative decision the
304 * client is disconnected.
305 */
306 struct MulticastJoinDecisionMessageHeader *join_dcsn;
307
308 /**
309 * CADET channel to the origin.
310 */
311 struct Channel *origin_channel;
312
313 /**
314 * Peer identity of origin.
315 */
316 struct GNUNET_PeerIdentity origin;
317
318 /**
319 * Peer identity of relays (other members to connect).
320 */
321 struct GNUNET_PeerIdentity *relays;
322
323 /**
324 * Last request fragment ID sent to the origin.
325 */
326 uint64_t max_fragment_id;
327
328 /**
329 * Number of @a relays.
330 */
331 uint32_t relay_count;
332};
333
334
335/**
336 * Client context.
337 */
338struct Client {
339 struct GNUNET_SERVICE_Client *client;
340 struct Group *group;
341};
342
343
344struct ReplayRequestKey
345{
346 uint64_t fragment_id;
347 uint64_t message_id;
348 uint64_t fragment_offset;
349 uint64_t flags;
350};
351
352
353static struct Channel *
354cadet_channel_create (struct Group *grp, struct GNUNET_PeerIdentity *peer);
355
356static void
357cadet_channel_destroy (struct Channel *chn);
358
359static void
360client_send_join_decision (struct Member *mem,
361 const struct MulticastJoinDecisionMessageHeader *hdcsn);
362
363
364/**
365 * Task run during shutdown.
366 *
367 * @param cls unused
368 */
369static void
370shutdown_task (void *cls)
371{
372 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
373 "shutting down\n");
374 if (NULL != cadet)
375 {
376 GNUNET_CADET_disconnect (cadet);
377 cadet = NULL;
378 }
379 if (NULL != stats)
380 {
381 GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
382 stats = NULL;
383 }
384 /* FIXME: do more clean up here */
385}
386
387
388/**
389 * Clean up origin data structures after a client disconnected.
390 */
391static void
392cleanup_origin (struct Origin *orig)
393{
394 struct Group *grp = &orig->group;
395 GNUNET_CONTAINER_multihashmap_remove (origins, &grp->pub_key_hash, orig);
396 if (NULL != orig->cadet_port)
397 {
398 GNUNET_CADET_close_port (orig->cadet_port);
399 orig->cadet_port = NULL;
400 }
401 GNUNET_free (orig);
402}
403
404
405/**
406 * Clean up member data structures after a client disconnected.
407 */
408static void
409cleanup_member (struct Member *mem)
410{
411 struct Group *grp = &mem->group;
412 struct GNUNET_CONTAINER_MultiHashMap *
413 grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members,
414 &grp->pub_key_hash);
415 GNUNET_assert (NULL != grp_mem);
416 GNUNET_CONTAINER_multihashmap_remove (grp_mem, &mem->pub_key_hash, mem);
417
418 if (0 == GNUNET_CONTAINER_multihashmap_size (grp_mem))
419 {
420 GNUNET_CONTAINER_multihashmap_remove (group_members, &grp->pub_key_hash,
421 grp_mem);
422 GNUNET_CONTAINER_multihashmap_destroy (grp_mem);
423 }
424 if (NULL != mem->join_dcsn)
425 {
426 GNUNET_free (mem->join_dcsn);
427 mem->join_dcsn = NULL;
428 }
429 if (NULL != mem->origin_channel)
430 {
431 GNUNET_CADET_channel_destroy (mem->origin_channel->channel);
432 mem->origin_channel = NULL;
433 }
434 GNUNET_CONTAINER_multihashmap_remove (members, &grp->pub_key_hash, mem);
435 GNUNET_free (mem);
436}
437
438
439/**
440 * Clean up group data structures after a client disconnected.
441 */
442static void
443cleanup_group (struct Group *grp)
444{
445 (GNUNET_YES == grp->is_origin)
446 ? cleanup_origin (grp->origin)
447 : cleanup_member (grp->member);
448}
449
450
451void
452replay_key_hash (uint64_t fragment_id, uint64_t message_id,
453 uint64_t fragment_offset, uint64_t flags,
454 struct GNUNET_HashCode *key_hash)
455{
456 struct ReplayRequestKey key = {
457 .fragment_id = fragment_id,
458 .message_id = message_id,
459 .fragment_offset = fragment_offset,
460 .flags = flags,
461 };
462 GNUNET_CRYPTO_hash (&key, sizeof (key), key_hash);
463}
464
465
466/**
467 * Remove channel from replay request hashmap.
468 *
469 * @param chn
470 * Channel to remove.
471 *
472 * @return #GNUNET_YES if there are more entries to process,
473 * #GNUNET_NO when reached end of hashmap.
474 */
475static int
476replay_req_remove_cadet (struct Channel *chn)
477{
478 if (NULL == chn || NULL == chn->group)
479 return GNUNET_SYSERR;
480
481 struct GNUNET_CONTAINER_MultiHashMap *
482 grp_replay_req = GNUNET_CONTAINER_multihashmap_get (replay_req_cadet,
483 &chn->group->pub_key_hash);
484 if (NULL == grp_replay_req)
485 return GNUNET_NO;
486
487 struct GNUNET_CONTAINER_MultiHashMapIterator *
488 it = GNUNET_CONTAINER_multihashmap_iterator_create (grp_replay_req);
489 struct GNUNET_HashCode key;
490 const struct Channel *c;
491 while (GNUNET_YES
492 == GNUNET_CONTAINER_multihashmap_iterator_next (it, &key,
493 (const void **) &c))
494 {
495 if (c == chn)
496 {
497 GNUNET_CONTAINER_multihashmap_remove (grp_replay_req, &key, chn);
498 GNUNET_CONTAINER_multihashmap_iterator_destroy (it);
499 return GNUNET_YES;
500 }
501 }
502 GNUNET_CONTAINER_multihashmap_iterator_destroy (it);
503 return GNUNET_NO;
504}
505
506
507/**
508 * Remove client from replay request hashmap.
509 *
510 * @param client
511 * Client to remove.
512 *
513 * @return #GNUNET_YES if there are more entries to process,
514 * #GNUNET_NO when reached end of hashmap.
515 */
516static int
517replay_req_remove_client (struct Group *grp, struct GNUNET_SERVICE_Client *client)
518{
519 struct GNUNET_CONTAINER_MultiHashMap *
520 grp_replay_req = GNUNET_CONTAINER_multihashmap_get (replay_req_client,
521 &grp->pub_key_hash);
522 if (NULL == grp_replay_req)
523 return GNUNET_NO;
524
525 struct GNUNET_CONTAINER_MultiHashMapIterator *
526 it = GNUNET_CONTAINER_multihashmap_iterator_create (grp_replay_req);
527 struct GNUNET_HashCode key;
528 const struct GNUNET_SERVICE_Client *c;
529 while (GNUNET_YES
530 == GNUNET_CONTAINER_multihashmap_iterator_next (it, &key,
531 (const void **) &c))
532 {
533 if (c == client)
534 {
535 GNUNET_CONTAINER_multihashmap_remove (grp_replay_req, &key, client);
536 GNUNET_CONTAINER_multihashmap_iterator_destroy (it);
537 return GNUNET_YES;
538 }
539 }
540 GNUNET_CONTAINER_multihashmap_iterator_destroy (it);
541 return GNUNET_NO;
542}
543
544
545/**
546 * Send message to a client.
547 */
548static void
549client_send (struct GNUNET_SERVICE_Client *client,
550 const struct GNUNET_MessageHeader *msg)
551{
552 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
553 "%p Sending message to client.\n", client);
554
555 struct GNUNET_MQ_Envelope *
556 env = GNUNET_MQ_msg_copy (msg);
557
558 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
559 env);
560}
561
562
563/**
564 * Send message to all clients connected to the group.
565 */
566static void
567client_send_group_keep_envelope (const struct Group *grp,
568 struct GNUNET_MQ_Envelope *env)
569{
570 struct ClientList *cli = grp->clients_head;
571
572 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
573 "%p Sending message to all clients of the group.\n",
574 grp);
575 while (NULL != cli)
576 {
577 GNUNET_MQ_send_copy (GNUNET_SERVICE_client_get_mq (cli->client),
578 env);
579 cli = cli->next;
580 }
581}
582
583
584/**
585 * Send message to all clients connected to the group and
586 * takes care of freeing @env.
587 */
588static void
589client_send_group (const struct Group *grp,
590 struct GNUNET_MQ_Envelope *env)
591{
592 client_send_group_keep_envelope (grp, env);
593 GNUNET_MQ_discard (env);
594}
595
596
597/**
598 * Iterator callback for sending a message to origin clients.
599 */
600static int
601client_send_origin_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
602 void *origin)
603{
604 struct GNUNET_MQ_Envelope *env = cls;
605 struct Member *orig = origin;
606
607 client_send_group_keep_envelope (&orig->group, env);
608 return GNUNET_YES;
609}
610
611
612/**
613 * Iterator callback for sending a message to member clients.
614 */
615static int
616client_send_member_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
617 void *member)
618{
619 struct GNUNET_MQ_Envelope *env = cls;
620 struct Member *mem = member;
621
622 if (NULL != mem->join_dcsn)
623 { /* Only send message to admitted members */
624 client_send_group_keep_envelope (&mem->group, env);
625 }
626 return GNUNET_YES;
627}
628
629
630/**
631 * Send message to all origin and member clients connected to the group.
632 *
633 * @param pub_key_hash
634 * H(key_pub) of the group.
635 * @param msg
636 * Message to send.
637 */
638static int
639client_send_all (struct GNUNET_HashCode *pub_key_hash,
640 struct GNUNET_MQ_Envelope *env)
641{
642 int n = 0;
643 n += GNUNET_CONTAINER_multihashmap_get_multiple (origins, pub_key_hash,
644 client_send_origin_cb,
645 (void *) env);
646 n += GNUNET_CONTAINER_multihashmap_get_multiple (members, pub_key_hash,
647 client_send_member_cb,
648 (void *) env);
649 GNUNET_MQ_discard (env);
650 return n;
651}
652
653
654/**
655 * Send message to a random origin client or a random member client.
656 *
657 * @param grp The group to send @a msg to.
658 * @param msg Message to send.
659 */
660static int
661client_send_random (struct GNUNET_HashCode *pub_key_hash,
662 struct GNUNET_MQ_Envelope *env)
663{
664 int n = 0;
665 n = GNUNET_CONTAINER_multihashmap_get_random (origins, client_send_origin_cb,
666 (void *) env);
667 if (n <= 0)
668 n = GNUNET_CONTAINER_multihashmap_get_random (members, client_send_member_cb,
669 (void *) env);
670 GNUNET_MQ_discard (env);
671 return n;
672}
673
674
675/**
676 * Send message to all origin clients connected to the group.
677 *
678 * @param pub_key_hash
679 * H(key_pub) of the group.
680 * @param msg
681 * Message to send.
682 */
683static int
684client_send_origin (struct GNUNET_HashCode *pub_key_hash,
685 struct GNUNET_MQ_Envelope *env)
686{
687 int n = 0;
688 n += GNUNET_CONTAINER_multihashmap_get_multiple (origins, pub_key_hash,
689 client_send_origin_cb,
690 (void *) env);
691 return n;
692}
693
694
695/**
696 * Send fragment acknowledgement to all clients of the channel.
697 *
698 * @param pub_key_hash
699 * H(key_pub) of the group.
700 */
701static void
702client_send_ack (struct GNUNET_HashCode *pub_key_hash)
703{
704 struct GNUNET_MQ_Envelope *env;
705
706 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
707 "Sending message ACK to client.\n");
708 env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_MULTICAST_FRAGMENT_ACK);
709 client_send_all (pub_key_hash, env);
710}
711
712
713struct CadetTransmitClosure
714{
715 struct Channel *chn;
716 const struct GNUNET_MessageHeader *msg;
717};
718
719
720/**
721 * Send a message to a CADET channel.
722 *
723 * @param chn Channel.
724 * @param msg Message.
725 */
726static void
727cadet_send_channel (struct Channel *chn, const struct GNUNET_MessageHeader *msg)
728{
729 struct GNUNET_MQ_Envelope *
730 env = GNUNET_MQ_msg_copy (msg);
731
732 GNUNET_MQ_send (GNUNET_CADET_get_mq (chn->channel), env);
733
734 if (0 < chn->window_size)
735 {
736 client_send_ack (&chn->group_pub_hash);
737 }
738 else
739 {
740 chn->msgs_pending++;
741 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
742 "%p Queuing message. Pending messages: %u\n",
743 chn, chn->msgs_pending);
744 }
745}
746
747
748/**
749 * Create CADET channel and send a join request.
750 */
751static void
752cadet_send_join_request (struct Member *mem)
753{
754 mem->origin_channel = cadet_channel_create (&mem->group, &mem->origin);
755 cadet_send_channel (mem->origin_channel, &mem->join_req->header);
756
757 uint32_t i;
758 for (i = 0; i < mem->relay_count; i++)
759 {
760 struct Channel *
761 chn = cadet_channel_create (&mem->group, &mem->relays[i]);
762 cadet_send_channel (chn, &mem->join_req->header);
763 }
764}
765
766
767static int
768cadet_send_join_decision_cb (void *cls,
769 const struct GNUNET_HashCode *group_pub_hash,
770 void *channel)
771{
772 const struct MulticastJoinDecisionMessageHeader *hdcsn = cls;
773 struct Channel *chn = channel;
774
775 const struct MulticastJoinDecisionMessage *dcsn =
776 (struct MulticastJoinDecisionMessage *) &hdcsn[1];
777
778 if (0 == memcmp (&hdcsn->member_pub_key, &chn->member_pub_key, sizeof (chn->member_pub_key))
779 && 0 == memcmp (&hdcsn->peer, &chn->peer, sizeof (chn->peer)))
780 {
781 if (GNUNET_YES == ntohl (dcsn->is_admitted))
782 {
783 chn->join_status = JOIN_ADMITTED;
784 }
785 else
786 {
787 chn->join_status = JOIN_REFUSED;
788 }
789 cadet_send_channel (chn, &hdcsn->header);
790 return GNUNET_YES;
791 }
792
793 // return GNUNET_YES to continue the multihashmap_get iteration
794 return GNUNET_YES;
795}
796
797
798/**
799 * Send join decision to a remote peer.
800 */
801static void
802cadet_send_join_decision (struct Group *grp,
803 const struct MulticastJoinDecisionMessageHeader *hdcsn)
804{
805 GNUNET_CONTAINER_multihashmap_get_multiple (channels_in, &grp->pub_key_hash,
806 &cadet_send_join_decision_cb,
807 (void *) hdcsn);
808}
809
810
811/**
812 * Iterator callback for sending a message to origin clients.
813 */
814static int
815cadet_send_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
816 void *channel)
817{
818 const struct GNUNET_MessageHeader *msg = cls;
819 struct Channel *chn = channel;
820 if (JOIN_ADMITTED == chn->join_status)
821 cadet_send_channel (chn, msg);
822 return GNUNET_YES;
823}
824
825
826/**
827 * Send message to all connected children.
828 */
829static int
830cadet_send_children (struct GNUNET_HashCode *pub_key_hash,
831 const struct GNUNET_MessageHeader *msg)
832{
833 int n = 0;
834 if (channels_in != NULL)
835 n += GNUNET_CONTAINER_multihashmap_get_multiple (channels_in, pub_key_hash,
836 cadet_send_cb, (void *) msg);
837 return n;
838}
839
840
841#if 0 // unused as yet
842/**
843 * Send message to all connected parents.
844 */
845static int
846cadet_send_parents (struct GNUNET_HashCode *pub_key_hash,
847 const struct GNUNET_MessageHeader *msg)
848{
849 int n = 0;
850 if (channels_in != NULL)
851 n += GNUNET_CONTAINER_multihashmap_get_multiple (channels_out, pub_key_hash,
852 cadet_send_cb, (void *) msg);
853 return n;
854}
855#endif
856
857
858/**
859 * CADET channel connect handler.
860 *
861 * @see GNUNET_CADET_ConnectEventHandler()
862 */
863static void *
864cadet_notify_connect (void *cls,
865 struct GNUNET_CADET_Channel *channel,
866 const struct GNUNET_PeerIdentity *source)
867{
868 struct Channel *chn = GNUNET_malloc (sizeof (struct Channel));
869 chn->group = cls;
870 chn->channel = channel;
871 chn->direction = DIR_INCOMING;
872 chn->join_status = JOIN_NOT_ASKED;
873
874 GNUNET_CONTAINER_multihashmap_put (channels_in, &chn->group->pub_key_hash, chn,
875 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
876 return chn;
877}
878
879
880/**
881 * CADET window size change handler.
882 *
883 * @see GNUNET_CADET_WindowSizeEventHandler()
884 */
885static void
886cadet_notify_window_change (void *cls,
887 const struct GNUNET_CADET_Channel *channel,
888 int window_size)
889{
890 struct Channel *chn = cls;
891
892 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
893 "%p Window size changed to %d. Pending messages: %u\n",
894 chn, window_size, chn->msgs_pending);
895
896 chn->is_connected = GNUNET_YES;
897 chn->window_size = (int32_t) window_size;
898
899 for (int i = 0; i < window_size; i++)
900 {
901 if (0 < chn->msgs_pending)
902 {
903 client_send_ack (&chn->group_pub_hash);
904 chn->msgs_pending--;
905 }
906 else
907 {
908 break;
909 }
910 }
911}
912
913
914/**
915 * CADET channel disconnect handler.
916 *
917 * @see GNUNET_CADET_DisconnectEventHandler()
918 */
919static void
920cadet_notify_disconnect (void *cls,
921 const struct GNUNET_CADET_Channel *channel)
922{
923 if (NULL == cls)
924 return;
925
926 struct Channel *chn = cls;
927 if (NULL != chn->group)
928 {
929 if (GNUNET_NO == chn->group->is_origin)
930 {
931 struct Member *mem = (struct Member *) chn->group;
932 if (chn == mem->origin_channel)
933 mem->origin_channel = NULL;
934 }
935 }
936
937 int ret;
938 do
939 {
940 ret = replay_req_remove_cadet (chn);
941 }
942 while (GNUNET_YES == ret);
943
944 GNUNET_free (chn);
945}
946
947
948static int
949check_cadet_join_request (void *cls,
950 const struct MulticastJoinRequestMessage *req)
951{
952 struct Channel *chn = cls;
953
954 if (NULL == chn
955 || JOIN_NOT_ASKED != chn->join_status)
956 {
957 return GNUNET_SYSERR;
958 }
959
960 uint16_t size = ntohs (req->header.size);
961 if (size < sizeof (*req))
962 {
963 GNUNET_break_op (0);
964 return GNUNET_SYSERR;
965 }
966 if (ntohl (req->purpose.size) != (size
967 - sizeof (req->header)
968 - sizeof (req->reserved)
969 - sizeof (req->signature)))
970 {
971 GNUNET_break_op (0);
972 return GNUNET_SYSERR;
973 }
974 if (GNUNET_OK !=
975 GNUNET_CRYPTO_ecdsa_verify_ (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST,
976 &req->purpose, &req->signature,
977 &req->member_pub_key))
978 {
979 GNUNET_break_op (0);
980 return GNUNET_SYSERR;
981 }
982
983 return GNUNET_OK;
984}
985
986
987/**
988 * Incoming join request message from CADET.
989 */
990static void
991handle_cadet_join_request (void *cls,
992 const struct MulticastJoinRequestMessage *req)
993{
994 struct Channel *chn = cls;
995 GNUNET_CADET_receive_done (chn->channel);
996
997 struct GNUNET_HashCode group_pub_hash;
998 GNUNET_CRYPTO_hash (&req->group_pub_key, sizeof (req->group_pub_key), &group_pub_hash);
999 chn->group_pub_key = req->group_pub_key;
1000 chn->group_pub_hash = group_pub_hash;
1001 chn->member_pub_key = req->member_pub_key;
1002 chn->peer = req->peer;
1003 chn->join_status = JOIN_WAITING;
1004
1005 client_send_all (&group_pub_hash,
1006 GNUNET_MQ_msg_copy (&req->header));
1007}
1008
1009
1010static int
1011check_cadet_join_decision (void *cls,
1012 const struct MulticastJoinDecisionMessageHeader *hdcsn)
1013{
1014 uint16_t size = ntohs (hdcsn->header.size);
1015 if (size < sizeof (struct MulticastJoinDecisionMessageHeader) +
1016 sizeof (struct MulticastJoinDecisionMessage))
1017 {
1018 GNUNET_break_op (0);
1019 return GNUNET_SYSERR;
1020 }
1021
1022 struct Channel *chn = cls;
1023 if (NULL == chn)
1024 {
1025 GNUNET_break (0);
1026 return GNUNET_SYSERR;
1027 }
1028 if (NULL == chn->group || GNUNET_NO != chn->group->is_origin)
1029 {
1030 GNUNET_break (0);
1031 return GNUNET_SYSERR;
1032 }
1033 switch (chn->join_status)
1034 {
1035 case JOIN_REFUSED:
1036 return GNUNET_SYSERR;
1037
1038 case JOIN_ADMITTED:
1039 return GNUNET_OK;
1040
1041 case JOIN_NOT_ASKED:
1042 case JOIN_WAITING:
1043 break;
1044 }
1045
1046 return GNUNET_OK;
1047}
1048
1049
1050/**
1051 * Incoming join decision message from CADET.
1052 */
1053static void
1054handle_cadet_join_decision (void *cls,
1055 const struct MulticastJoinDecisionMessageHeader *hdcsn)
1056{
1057 const struct MulticastJoinDecisionMessage *
1058 dcsn = (const struct MulticastJoinDecisionMessage *) &hdcsn[1];
1059
1060 struct Channel *chn = cls;
1061 GNUNET_CADET_receive_done (chn->channel);
1062
1063 // FIXME: do we need to copy chn->peer or compare it with hdcsn->peer?
1064 struct Member *mem = (struct Member *) chn->group;
1065 client_send_join_decision (mem, hdcsn);
1066 if (GNUNET_YES == ntohl (dcsn->is_admitted))
1067 {
1068 chn->join_status = JOIN_ADMITTED;
1069 }
1070 else
1071 {
1072 chn->join_status = JOIN_REFUSED;
1073 cadet_channel_destroy (chn);
1074 }
1075}
1076
1077
1078static int
1079check_cadet_message (void *cls,
1080 const struct GNUNET_MULTICAST_MessageHeader *msg)
1081{
1082 uint16_t size = ntohs (msg->header.size);
1083 if (size < sizeof (*msg))
1084 {
1085 GNUNET_break_op (0);
1086 return GNUNET_SYSERR;
1087 }
1088
1089 struct Channel *chn = cls;
1090 if (NULL == chn)
1091 {
1092 GNUNET_break (0);
1093 return GNUNET_SYSERR;
1094 }
1095 if (ntohl (msg->purpose.size) != (size
1096 - sizeof (msg->header)
1097 - sizeof (msg->hop_counter)
1098 - sizeof (msg->signature)))
1099 {
1100 GNUNET_break_op (0);
1101 return GNUNET_SYSERR;
1102 }
1103 if (GNUNET_OK !=
1104 GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_MULTICAST_MESSAGE,
1105 &msg->purpose, &msg->signature,
1106 &chn->group_pub_key))
1107 {
1108 GNUNET_break_op (0);
1109 return GNUNET_SYSERR;
1110 }
1111
1112 return GNUNET_OK;
1113}
1114
1115
1116/**
1117 * Incoming multicast message from CADET.
1118 */
1119static void
1120handle_cadet_message (void *cls,
1121 const struct GNUNET_MULTICAST_MessageHeader *msg)
1122{
1123 struct Channel *chn = cls;
1124 GNUNET_CADET_receive_done (chn->channel);
1125 client_send_all (&chn->group_pub_hash,
1126 GNUNET_MQ_msg_copy (&msg->header));
1127}
1128
1129
1130static int
1131check_cadet_request (void *cls,
1132 const struct GNUNET_MULTICAST_RequestHeader *req)
1133{
1134 uint16_t size = ntohs (req->header.size);
1135 if (size < sizeof (*req))
1136 {
1137 GNUNET_break_op (0);
1138 return GNUNET_SYSERR;
1139 }
1140
1141 struct Channel *chn = cls;
1142 if (NULL == chn)
1143 {
1144 GNUNET_break (0);
1145 return GNUNET_SYSERR;
1146 }
1147 if (ntohl (req->purpose.size) != (size
1148 - sizeof (req->header)
1149 - sizeof (req->member_pub_key)
1150 - sizeof (req->signature)))
1151 {
1152 GNUNET_break_op (0);
1153 return GNUNET_SYSERR;
1154 }
1155 if (GNUNET_OK !=
1156 GNUNET_CRYPTO_ecdsa_verify_ (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST,
1157 &req->purpose, &req->signature,
1158 &req->member_pub_key))
1159 {
1160 GNUNET_break_op (0);
1161 return GNUNET_SYSERR;
1162 }
1163
1164 return GNUNET_OK;
1165}
1166
1167
1168/**
1169 * Incoming multicast request message from CADET.
1170 */
1171static void
1172handle_cadet_request (void *cls,
1173 const struct GNUNET_MULTICAST_RequestHeader *req)
1174{
1175 struct Channel *chn = cls;
1176 GNUNET_CADET_receive_done (chn->channel);
1177 client_send_origin (&chn->group_pub_hash,
1178 GNUNET_MQ_msg_copy (&req->header));
1179}
1180
1181
1182// FIXME: do checks in handle_cadet_replay_request
1183//static int
1184//check_cadet_replay_request (void *cls,
1185// const struct MulticastReplayRequestMessage *req)
1186//{
1187// uint16_t size = ntohs (req->header.size);
1188// if (size < sizeof (*req))
1189// {
1190// GNUNET_break_op (0);
1191// return GNUNET_SYSERR;
1192// }
1193//
1194// struct Channel *chn = cls;
1195// if (NULL == chn)
1196// {
1197// GNUNET_break_op (0);
1198// return GNUNET_SYSERR;
1199// }
1200//
1201// return GNUNET_OK;
1202//}
1203
1204
1205/**
1206 * Incoming multicast replay request from CADET.
1207 */
1208static void
1209handle_cadet_replay_request (void *cls,
1210 const struct MulticastReplayRequestMessage *req)
1211{
1212 struct Channel *chn = cls;
1213
1214 GNUNET_CADET_receive_done (chn->channel);
1215
1216 struct MulticastReplayRequestMessage rep = *req;
1217 GNUNET_memcpy (&rep.member_pub_key, &chn->member_pub_key, sizeof (chn->member_pub_key));
1218
1219 struct GNUNET_CONTAINER_MultiHashMap *
1220 grp_replay_req = GNUNET_CONTAINER_multihashmap_get (replay_req_cadet,
1221 &chn->group->pub_key_hash);
1222 if (NULL == grp_replay_req)
1223 {
1224 grp_replay_req = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
1225 GNUNET_CONTAINER_multihashmap_put (replay_req_cadet,
1226 &chn->group->pub_key_hash, grp_replay_req,
1227 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1228 }
1229 struct GNUNET_HashCode key_hash;
1230 replay_key_hash (rep.fragment_id,
1231 rep.message_id,
1232 rep.fragment_offset,
1233 rep.flags,
1234 &key_hash);
1235 GNUNET_CONTAINER_multihashmap_put (grp_replay_req, &key_hash, chn,
1236 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1237
1238 client_send_random (&chn->group_pub_hash,
1239 GNUNET_MQ_msg_copy (&rep.header));
1240}
1241
1242
1243static int
1244check_cadet_replay_response (void *cls,
1245 const struct MulticastReplayResponseMessage *res)
1246{
1247 struct Channel *chn = cls;
1248 if (NULL == chn)
1249 {
1250 GNUNET_break (0);
1251 return GNUNET_SYSERR;
1252 }
1253 return GNUNET_OK;
1254}
1255
1256
1257/**
1258 * Incoming multicast replay response from CADET.
1259 */
1260static void
1261handle_cadet_replay_response (void *cls,
1262 const struct MulticastReplayResponseMessage *res)
1263{
1264 struct Channel *chn = cls;
1265 GNUNET_CADET_receive_done (chn->channel);
1266
1267 /* @todo FIXME: got replay error response, send request to other members */
1268}
1269
1270
1271static void
1272group_set_cadet_port_hash (struct Group *grp)
1273{
1274 struct CadetPort {
1275 struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
1276 uint32_t app_type;
1277 } port = {
1278 grp->pub_key,
1279 GNUNET_APPLICATION_TYPE_MULTICAST,
1280 };
1281 GNUNET_CRYPTO_hash (&port, sizeof (port), &grp->cadet_port_hash);
1282}
1283
1284
1285
1286/**
1287 * Create new outgoing CADET channel.
1288 *
1289 * @param peer
1290 * Peer to connect to.
1291 * @param group_pub_key
1292 * Public key of group the channel belongs to.
1293 * @param group_pub_hash
1294 * Hash of @a group_pub_key.
1295 *
1296 * @return Channel.
1297 */
1298static struct Channel *
1299cadet_channel_create (struct Group *grp, struct GNUNET_PeerIdentity *peer)
1300{
1301 struct Channel *chn = GNUNET_malloc (sizeof (*chn));
1302 chn->group = grp;
1303 chn->group_pub_key = grp->pub_key;
1304 chn->group_pub_hash = grp->pub_key_hash;
1305 chn->peer = *peer;
1306 chn->direction = DIR_OUTGOING;
1307 chn->is_connected = GNUNET_NO;
1308 chn->join_status = JOIN_WAITING;
1309
1310 struct GNUNET_MQ_MessageHandler cadet_handlers[] = {
1311 GNUNET_MQ_hd_var_size (cadet_message,
1312 GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE,
1313 struct GNUNET_MULTICAST_MessageHeader,
1314 chn),
1315
1316 GNUNET_MQ_hd_var_size (cadet_join_decision,
1317 GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION,
1318 struct MulticastJoinDecisionMessageHeader,
1319 chn),
1320
1321 GNUNET_MQ_hd_fixed_size (cadet_replay_request,
1322 GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST,
1323 struct MulticastReplayRequestMessage,
1324 chn),
1325
1326 GNUNET_MQ_hd_var_size (cadet_replay_response,
1327 GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE,
1328 struct MulticastReplayResponseMessage,
1329 chn),
1330
1331 GNUNET_MQ_handler_end ()
1332 };
1333
1334 chn->channel = GNUNET_CADET_channel_create (cadet, chn, &chn->peer,
1335 &grp->cadet_port_hash,
1336 cadet_notify_window_change,
1337 cadet_notify_disconnect,
1338 cadet_handlers);
1339 GNUNET_CONTAINER_multihashmap_put (channels_out, &chn->group_pub_hash, chn,
1340 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1341 return chn;
1342}
1343
1344
1345/**
1346 * Destroy outgoing CADET channel.
1347 */
1348static void
1349cadet_channel_destroy (struct Channel *chn)
1350{
1351 GNUNET_CADET_channel_destroy (chn->channel);
1352 GNUNET_CONTAINER_multihashmap_remove_all (channels_out, &chn->group_pub_hash);
1353 GNUNET_free (chn);
1354}
1355
1356/**
1357 * Handle a connecting client starting an origin.
1358 */
1359static void
1360handle_client_origin_start (void *cls,
1361 const struct MulticastOriginStartMessage *msg)
1362{
1363 struct Client *c = cls;
1364 struct GNUNET_SERVICE_Client *client = c->client;
1365
1366 struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
1367 struct GNUNET_HashCode pub_key_hash;
1368
1369 GNUNET_CRYPTO_eddsa_key_get_public (&msg->group_key, &pub_key);
1370 GNUNET_CRYPTO_hash (&pub_key, sizeof (pub_key), &pub_key_hash);
1371
1372 struct Origin *
1373 orig = GNUNET_CONTAINER_multihashmap_get (origins, &pub_key_hash);
1374 struct Group *grp;
1375
1376 if (NULL == orig)
1377 {
1378 orig = GNUNET_new (struct Origin);
1379 orig->priv_key = msg->group_key;
1380 orig->max_fragment_id = GNUNET_ntohll (msg->max_fragment_id);
1381
1382 grp = c->group = &orig->group;
1383 grp->origin = orig;
1384 grp->is_origin = GNUNET_YES;
1385 grp->pub_key = pub_key;
1386 grp->pub_key_hash = pub_key_hash;
1387 grp->is_disconnected = GNUNET_NO;
1388
1389 GNUNET_CONTAINER_multihashmap_put (origins, &grp->pub_key_hash, orig,
1390 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1391
1392 group_set_cadet_port_hash (grp);
1393
1394 struct GNUNET_MQ_MessageHandler cadet_handlers[] = {
1395 GNUNET_MQ_hd_var_size (cadet_message,
1396 GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE,
1397 struct GNUNET_MULTICAST_MessageHeader,
1398 grp),
1399
1400 GNUNET_MQ_hd_var_size (cadet_request,
1401 GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST,
1402 struct GNUNET_MULTICAST_RequestHeader,
1403 grp),
1404
1405 GNUNET_MQ_hd_var_size (cadet_join_request,
1406 GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST,
1407 struct MulticastJoinRequestMessage,
1408 grp),
1409
1410 GNUNET_MQ_hd_fixed_size (cadet_replay_request,
1411 GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST,
1412 struct MulticastReplayRequestMessage,
1413 grp),
1414
1415 GNUNET_MQ_hd_var_size (cadet_replay_response,
1416 GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE,
1417 struct MulticastReplayResponseMessage,
1418 grp),
1419
1420 GNUNET_MQ_handler_end ()
1421 };
1422
1423
1424 orig->cadet_port = GNUNET_CADET_open_port (cadet,
1425 &grp->cadet_port_hash,
1426 cadet_notify_connect,
1427 grp,
1428 cadet_notify_window_change,
1429 cadet_notify_disconnect,
1430 cadet_handlers);
1431 }
1432 else
1433 {
1434 grp = &orig->group;
1435 }
1436
1437 struct ClientList *cl = GNUNET_new (struct ClientList);
1438 cl->client = client;
1439 GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
1440
1441 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1442 "%p Client connected as origin to group %s.\n",
1443 orig, GNUNET_h2s (&grp->pub_key_hash));
1444 GNUNET_SERVICE_client_continue (client);
1445}
1446
1447
1448static int
1449check_client_member_join (void *cls,
1450 const struct MulticastMemberJoinMessage *msg)
1451{
1452 uint16_t msg_size = ntohs (msg->header.size);
1453 struct GNUNET_PeerIdentity *relays = (struct GNUNET_PeerIdentity *) &msg[1];
1454 uint32_t relay_count = ntohl (msg->relay_count);
1455
1456 if (0 != relay_count)
1457 {
1458 if (UINT32_MAX / relay_count < sizeof (*relays)){
1459 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1460 "relay_count (%lu) * sizeof (*relays) (%lu) exceeds UINT32_MAX!\n",
1461 (unsigned long)relay_count,
1462 sizeof (*relays));
1463 return GNUNET_SYSERR;
1464 }
1465 }
1466 uint32_t relay_size = relay_count * sizeof (*relays);
1467 struct GNUNET_MessageHeader *join_msg = NULL;
1468 uint16_t join_msg_size = 0;
1469 if (sizeof (*msg) + relay_size + sizeof (struct GNUNET_MessageHeader)
1470 <= msg_size)
1471 {
1472 join_msg = (struct GNUNET_MessageHeader *)
1473 (((char *) &msg[1]) + relay_size);
1474 join_msg_size = ntohs (join_msg->size);
1475 if (UINT16_MAX - join_msg_size < sizeof (struct MulticastJoinRequestMessage)){
1476 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1477 "join_msg_size (%u) + sizeof (struct MulticastJoinRequestMessage) (%lu) exceeds UINT16_MAX!\n",
1478 (unsigned)join_msg_size,
1479 (unsigned long)sizeof (struct MulticastJoinRequestMessage));
1480 return GNUNET_SYSERR;
1481 }
1482 }
1483 if (msg_size != (sizeof (*msg) + relay_size + join_msg_size)){
1484 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1485 "msg_size does not match real size of message!\n");
1486 return GNUNET_SYSERR;
1487 }else{
1488 return GNUNET_OK;
1489 }
1490}
1491
1492
1493/**
1494 * Handle a connecting client joining a group.
1495 */
1496static void
1497handle_client_member_join (void *cls,
1498 const struct MulticastMemberJoinMessage *msg)
1499{
1500 struct Client *c = cls;
1501 struct GNUNET_SERVICE_Client *client = c->client;
1502
1503 uint16_t msg_size = ntohs (msg->header.size);
1504
1505 struct GNUNET_CRYPTO_EcdsaPublicKey mem_pub_key;
1506 struct GNUNET_HashCode pub_key_hash, mem_pub_key_hash;
1507
1508 GNUNET_CRYPTO_ecdsa_key_get_public (&msg->member_key, &mem_pub_key);
1509 GNUNET_CRYPTO_hash (&mem_pub_key, sizeof (mem_pub_key), &mem_pub_key_hash);
1510 GNUNET_CRYPTO_hash (&msg->group_pub_key, sizeof (msg->group_pub_key), &pub_key_hash);
1511
1512 struct GNUNET_CONTAINER_MultiHashMap *
1513 grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members, &pub_key_hash);
1514 struct Member *mem = NULL;
1515 struct Group *grp;
1516
1517 if (NULL != grp_mem)
1518 {
1519 mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &mem_pub_key_hash);
1520 }
1521
1522 if (NULL == mem)
1523 {
1524 mem = GNUNET_new (struct Member);
1525 mem->origin = msg->origin;
1526 mem->priv_key = msg->member_key;
1527 mem->pub_key = mem_pub_key;
1528 mem->pub_key_hash = mem_pub_key_hash;
1529 mem->max_fragment_id = 0; // FIXME
1530
1531 grp = c->group = &mem->group;
1532 grp->member = mem;
1533 grp->is_origin = GNUNET_NO;
1534 grp->pub_key = msg->group_pub_key;
1535 grp->pub_key_hash = pub_key_hash;
1536 grp->is_disconnected = GNUNET_NO;
1537 group_set_cadet_port_hash (grp);
1538
1539 if (NULL == grp_mem)
1540 {
1541 grp_mem = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
1542 GNUNET_CONTAINER_multihashmap_put (group_members, &grp->pub_key_hash, grp_mem,
1543 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1544 }
1545 GNUNET_CONTAINER_multihashmap_put (grp_mem, &mem->pub_key_hash, mem,
1546 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1547
1548 // FIXME: should the members hash map have option UNIQUE_FAST?
1549 GNUNET_CONTAINER_multihashmap_put (members, &grp->pub_key_hash, mem,
1550 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1551 }
1552 else
1553 {
1554 grp = &mem->group;
1555 }
1556
1557 struct ClientList *cl = GNUNET_new (struct ClientList);
1558 cl->client = client;
1559 GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
1560
1561 char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&mem->pub_key);
1562 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1563 "Client connected to group %s as member %s (%s). size = %d\n",
1564 GNUNET_h2s (&grp->pub_key_hash),
1565 GNUNET_h2s2 (&mem->pub_key_hash),
1566 str,
1567 GNUNET_CONTAINER_multihashmap_size (members));
1568 GNUNET_free (str);
1569
1570 if (NULL != mem->join_dcsn)
1571 { /* Already got a join decision, send it to client. */
1572 struct GNUNET_MQ_Envelope *
1573 env = GNUNET_MQ_msg_copy (&mem->join_dcsn->header);
1574
1575 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1576 env);
1577 }
1578 else
1579 { /* First client of the group, send join request. */
1580 struct GNUNET_PeerIdentity *relays = (struct GNUNET_PeerIdentity *) &msg[1];
1581 uint32_t relay_count = ntohl (msg->relay_count);
1582 uint16_t relay_size = relay_count * sizeof (*relays);
1583 struct GNUNET_MessageHeader *join_msg = NULL;
1584 uint16_t join_msg_size = 0;
1585 if (sizeof (*msg) + relay_size + sizeof (struct GNUNET_MessageHeader)
1586 <= msg_size)
1587 {
1588 join_msg = (struct GNUNET_MessageHeader *)
1589 (((char *) &msg[1]) + relay_size);
1590 join_msg_size = ntohs (join_msg->size);
1591 }
1592
1593 uint16_t req_msg_size = sizeof (struct MulticastJoinRequestMessage) + join_msg_size;
1594 struct MulticastJoinRequestMessage *
1595 req = GNUNET_malloc (req_msg_size);
1596 req->header.size = htons (req_msg_size);
1597 req->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST);
1598 req->group_pub_key = grp->pub_key;
1599 req->peer = this_peer;
1600 GNUNET_CRYPTO_ecdsa_key_get_public (&mem->priv_key, &req->member_pub_key);
1601 if (0 < join_msg_size)
1602 GNUNET_memcpy (&req[1], join_msg, join_msg_size);
1603
1604 req->member_pub_key = mem->pub_key;
1605 req->purpose.size = htonl (req_msg_size
1606 - sizeof (req->header)
1607 - sizeof (req->reserved)
1608 - sizeof (req->signature));
1609 req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
1610
1611 if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_sign_ (&mem->priv_key, &req->purpose,
1612 &req->signature))
1613 {
1614 /* FIXME: handle error */
1615 GNUNET_assert (0);
1616 }
1617
1618 if (NULL != mem->join_req)
1619 GNUNET_free (mem->join_req);
1620 mem->join_req = req;
1621
1622 if (0 ==
1623 client_send_origin (&grp->pub_key_hash,
1624 GNUNET_MQ_msg_copy (&mem->join_req->header)))
1625 { /* No local origins, send to remote origin */
1626 cadet_send_join_request (mem);
1627 }
1628 }
1629 GNUNET_SERVICE_client_continue (client);
1630}
1631
1632
1633static void
1634client_send_join_decision (struct Member *mem,
1635 const struct MulticastJoinDecisionMessageHeader *hdcsn)
1636{
1637 client_send_group (&mem->group, GNUNET_MQ_msg_copy (&hdcsn->header));
1638
1639 const struct MulticastJoinDecisionMessage *
1640 dcsn = (const struct MulticastJoinDecisionMessage *) &hdcsn[1];
1641 if (GNUNET_YES == ntohl (dcsn->is_admitted))
1642 { /* Member admitted, store join_decision. */
1643 uint16_t dcsn_size = ntohs (dcsn->header.size);
1644 mem->join_dcsn = GNUNET_malloc (dcsn_size);
1645 GNUNET_memcpy (mem->join_dcsn, dcsn, dcsn_size);
1646 }
1647 else
1648 { /* Refused entry, but replay would be still possible for past members. */
1649 }
1650}
1651
1652
1653static int
1654check_client_join_decision (void *cls,
1655 const struct MulticastJoinDecisionMessageHeader *hdcsn)
1656{
1657 return GNUNET_OK;
1658}
1659
1660
1661/**
1662 * Join decision from client.
1663 */
1664static void
1665handle_client_join_decision (void *cls,
1666 const struct MulticastJoinDecisionMessageHeader *hdcsn)
1667{
1668 struct Client *c = cls;
1669 struct GNUNET_SERVICE_Client *client = c->client;
1670 struct Group *grp = c->group;
1671
1672 if (NULL == grp)
1673 {
1674 GNUNET_break (0);
1675 GNUNET_SERVICE_client_drop (client);
1676 return;
1677 }
1678 GNUNET_assert (GNUNET_NO == grp->is_disconnected);
1679 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1680 "%p got join decision from client for group %s..\n",
1681 grp, GNUNET_h2s (&grp->pub_key_hash));
1682
1683 struct GNUNET_CONTAINER_MultiHashMap *
1684 grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members,
1685 &grp->pub_key_hash);
1686 struct Member *mem = NULL;
1687 if (NULL != grp_mem)
1688 {
1689 struct GNUNET_HashCode member_key_hash;
1690 GNUNET_CRYPTO_hash (&hdcsn->member_pub_key, sizeof (hdcsn->member_pub_key),
1691 &member_key_hash);
1692 mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &member_key_hash);
1693 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1694 "%p ..and member %s: %p\n",
1695 grp, GNUNET_h2s (&member_key_hash), mem);
1696 }
1697
1698 if (NULL != mem)
1699 { /* Found local member */
1700 client_send_join_decision (mem, hdcsn);
1701 }
1702 else
1703 { /* Look for remote member */
1704 cadet_send_join_decision (grp, hdcsn);
1705 }
1706 GNUNET_SERVICE_client_continue (client);
1707}
1708
1709
1710static void
1711handle_client_part_request (void *cls,
1712 const struct GNUNET_MessageHeader *msg)
1713{
1714 struct Client *c = cls;
1715 struct GNUNET_SERVICE_Client *client = c->client;
1716 struct Group *grp = c->group;
1717 struct GNUNET_MQ_Envelope *env;
1718
1719 if (NULL == grp)
1720 {
1721 GNUNET_break (0);
1722 GNUNET_SERVICE_client_drop (client);
1723 return;
1724 }
1725 GNUNET_assert (GNUNET_NO == grp->is_disconnected);
1726 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1727 "%p got part request from client for group %s.\n",
1728 grp, GNUNET_h2s (&grp->pub_key_hash));
1729 grp->is_disconnected = GNUNET_YES;
1730 env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_MULTICAST_PART_ACK);
1731 client_send_group (grp, env);
1732 GNUNET_SERVICE_client_continue (client);
1733}
1734
1735
1736static int
1737check_client_multicast_message (void *cls,
1738 const struct GNUNET_MULTICAST_MessageHeader *msg)
1739{
1740 return GNUNET_OK;
1741}
1742
1743
1744/**
1745 * Incoming message from a client.
1746 */
1747static void
1748handle_client_multicast_message (void *cls,
1749 const struct GNUNET_MULTICAST_MessageHeader *msg)
1750{
1751 // FIXME: what if GNUNET_YES == grp->is_disconnected? Do we allow sending messages?
1752 struct Client *c = cls;
1753 struct GNUNET_SERVICE_Client *client = c->client;
1754 struct Group *grp = c->group;
1755
1756 if (NULL == grp)
1757 {
1758 GNUNET_break (0);
1759 GNUNET_SERVICE_client_drop (client);
1760 return;
1761 }
1762 GNUNET_assert (GNUNET_YES == grp->is_origin);
1763 struct Origin *orig = grp->origin;
1764
1765 // FIXME: use GNUNET_MQ_msg_copy
1766 /* FIXME: yucky, should use separate message structs for P2P and CS! */
1767 struct GNUNET_MULTICAST_MessageHeader *
1768 out = (struct GNUNET_MULTICAST_MessageHeader *) GNUNET_copy_message (&msg->header);
1769 out->fragment_id = GNUNET_htonll (++orig->max_fragment_id);
1770 out->purpose.size = htonl (ntohs (out->header.size)
1771 - sizeof (out->header)
1772 - sizeof (out->hop_counter)
1773 - sizeof (out->signature));
1774 out->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_MESSAGE);
1775
1776 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign_ (&orig->priv_key, &out->purpose,
1777 &out->signature))
1778 {
1779 GNUNET_assert (0);
1780 }
1781
1782 client_send_all (&grp->pub_key_hash, GNUNET_MQ_msg_copy (&out->header));
1783 cadet_send_children (&grp->pub_key_hash, &out->header);
1784 client_send_ack (&grp->pub_key_hash);
1785 GNUNET_free (out);
1786
1787 GNUNET_SERVICE_client_continue (client);
1788}
1789
1790
1791static int
1792check_client_multicast_request (void *cls,
1793 const struct GNUNET_MULTICAST_RequestHeader *req)
1794{
1795 return GNUNET_OK;
1796}
1797
1798
1799/**
1800 * Incoming request from a client.
1801 */
1802static void
1803handle_client_multicast_request (void *cls,
1804 const struct GNUNET_MULTICAST_RequestHeader *req)
1805{
1806 struct Client *c = cls;
1807 struct GNUNET_SERVICE_Client *client = c->client;
1808 struct Group *grp = c->group;
1809
1810 if (NULL == grp)
1811 {
1812 GNUNET_break (0);
1813 GNUNET_SERVICE_client_drop (client);
1814 return;
1815 }
1816 GNUNET_assert (GNUNET_NO == grp->is_disconnected);
1817 GNUNET_assert (GNUNET_NO == grp->is_origin);
1818 struct Member *mem = grp->member;
1819
1820 /* FIXME: yucky, should use separate message structs for P2P and CS! */
1821 struct GNUNET_MULTICAST_RequestHeader *
1822 out = (struct GNUNET_MULTICAST_RequestHeader *) GNUNET_copy_message (&req->header);
1823 out->member_pub_key = mem->pub_key;
1824 out->fragment_id = GNUNET_ntohll (++mem->max_fragment_id);
1825 out->purpose.size = htonl (ntohs (out->header.size)
1826 - sizeof (out->header)
1827 - sizeof (out->member_pub_key)
1828 - sizeof (out->signature));
1829 out->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
1830
1831 if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_sign_ (&mem->priv_key, &out->purpose,
1832 &out->signature))
1833 {
1834 GNUNET_assert (0);
1835 }
1836
1837 uint8_t send_ack = GNUNET_YES;
1838 if (0 ==
1839 client_send_origin (&grp->pub_key_hash,
1840 GNUNET_MQ_msg_copy (&out->header)))
1841 { /* No local origins, send to remote origin */
1842 if (NULL != mem->origin_channel)
1843 {
1844 cadet_send_channel (mem->origin_channel, &out->header);
1845 send_ack = GNUNET_NO;
1846 }
1847 else
1848 {
1849 /* FIXME: not yet connected to origin */
1850 GNUNET_SERVICE_client_drop (client);
1851 GNUNET_free (out);
1852 return;
1853 }
1854 }
1855 if (GNUNET_YES == send_ack)
1856 {
1857 client_send_ack (&grp->pub_key_hash);
1858 }
1859 GNUNET_free (out);
1860 GNUNET_SERVICE_client_continue (client);
1861}
1862
1863
1864/**
1865 * Incoming replay request from a client.
1866 */
1867static void
1868handle_client_replay_request (void *cls,
1869 const struct MulticastReplayRequestMessage *rep)
1870{
1871 struct Client *c = cls;
1872 struct GNUNET_SERVICE_Client *client = c->client;
1873 struct Group *grp = c->group;
1874
1875 if (NULL == grp)
1876 {
1877 GNUNET_break (0);
1878 GNUNET_SERVICE_client_drop (client);
1879 return;
1880 }
1881 GNUNET_assert (GNUNET_NO == grp->is_disconnected);
1882 GNUNET_assert (GNUNET_NO == grp->is_origin);
1883 struct Member *mem = grp->member;
1884
1885 struct GNUNET_CONTAINER_MultiHashMap *
1886 grp_replay_req = GNUNET_CONTAINER_multihashmap_get (replay_req_client,
1887 &grp->pub_key_hash);
1888 if (NULL == grp_replay_req)
1889 {
1890 grp_replay_req = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
1891 GNUNET_CONTAINER_multihashmap_put (replay_req_client,
1892 &grp->pub_key_hash, grp_replay_req,
1893 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1894 }
1895
1896 struct GNUNET_HashCode key_hash;
1897 replay_key_hash (rep->fragment_id, rep->message_id, rep->fragment_offset,
1898 rep->flags, &key_hash);
1899 GNUNET_CONTAINER_multihashmap_put (grp_replay_req, &key_hash, client,
1900 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1901
1902 if (0 ==
1903 client_send_origin (&grp->pub_key_hash,
1904 GNUNET_MQ_msg_copy (&rep->header)))
1905 { /* No local origin, replay from remote members / origin. */
1906 if (NULL != mem->origin_channel)
1907 {
1908 cadet_send_channel (mem->origin_channel, &rep->header);
1909 }
1910 else
1911 {
1912 /* FIXME: not yet connected to origin */
1913
1914 GNUNET_assert (0);
1915 GNUNET_SERVICE_client_drop (client);
1916 return;
1917 }
1918 }
1919 GNUNET_SERVICE_client_continue (client);
1920}
1921
1922
1923static int
1924cadet_send_replay_response_cb (void *cls,
1925 const struct GNUNET_HashCode *key_hash,
1926 void *value)
1927{
1928 struct Channel *chn = value;
1929 struct GNUNET_MessageHeader *msg = cls;
1930
1931 cadet_send_channel (chn, msg);
1932 return GNUNET_OK;
1933}
1934
1935
1936static int
1937client_send_replay_response_cb (void *cls,
1938 const struct GNUNET_HashCode *key_hash,
1939 void *value)
1940{
1941 struct GNUNET_SERVICE_Client *client = value;
1942 struct GNUNET_MessageHeader *msg = cls;
1943
1944 client_send (client, msg);
1945 return GNUNET_OK;
1946}
1947
1948
1949static int
1950check_client_replay_response_end (void *cls,
1951 const struct MulticastReplayResponseMessage *res)
1952{
1953 return GNUNET_OK;
1954}
1955
1956
1957/**
1958 * End of replay response from a client.
1959 */
1960static void
1961handle_client_replay_response_end (void *cls,
1962 const struct MulticastReplayResponseMessage *res)
1963{
1964 struct Client *c = cls;
1965 struct GNUNET_SERVICE_Client *client = c->client;
1966 struct Group *grp = c->group;
1967
1968 if (NULL == grp)
1969 {
1970 GNUNET_break (0);
1971 GNUNET_SERVICE_client_drop (client);
1972 return;
1973 }
1974 GNUNET_assert (GNUNET_NO == grp->is_disconnected);
1975
1976 struct GNUNET_HashCode key_hash;
1977 replay_key_hash (res->fragment_id, res->message_id, res->fragment_offset,
1978 res->flags, &key_hash);
1979
1980 struct GNUNET_CONTAINER_MultiHashMap *
1981 grp_replay_req_cadet = GNUNET_CONTAINER_multihashmap_get (replay_req_cadet,
1982 &grp->pub_key_hash);
1983 if (NULL != grp_replay_req_cadet)
1984 {
1985 GNUNET_CONTAINER_multihashmap_remove_all (grp_replay_req_cadet, &key_hash);
1986 }
1987 struct GNUNET_CONTAINER_MultiHashMap *
1988 grp_replay_req_client = GNUNET_CONTAINER_multihashmap_get (replay_req_client,
1989 &grp->pub_key_hash);
1990 if (NULL != grp_replay_req_client)
1991 {
1992 GNUNET_CONTAINER_multihashmap_remove_all (grp_replay_req_client, &key_hash);
1993 }
1994 GNUNET_SERVICE_client_continue (client);
1995}
1996
1997
1998static int
1999check_client_replay_response (void *cls,
2000 const struct MulticastReplayResponseMessage *res)
2001{
2002 const struct GNUNET_MessageHeader *msg;
2003 if (GNUNET_MULTICAST_REC_OK == res->error_code)
2004 {
2005 msg = GNUNET_MQ_extract_nested_mh (res);
2006 if (NULL == msg)
2007 {
2008 return GNUNET_SYSERR;
2009 }
2010 }
2011 return GNUNET_OK;
2012}
2013
2014
2015/**
2016 * Incoming replay response from a client.
2017 *
2018 * Respond with a multicast message on success, or otherwise with an error code.
2019 */
2020static void
2021handle_client_replay_response (void *cls,
2022 const struct MulticastReplayResponseMessage *res)
2023{
2024 struct Client *c = cls;
2025 struct GNUNET_SERVICE_Client *client = c->client;
2026 struct Group *grp = c->group;
2027
2028 if (NULL == grp)
2029 {
2030 GNUNET_break (0);
2031 GNUNET_SERVICE_client_drop (client);
2032 return;
2033 }
2034 GNUNET_assert (GNUNET_NO == grp->is_disconnected);
2035
2036 const struct GNUNET_MessageHeader *msg = &res->header;
2037 if (GNUNET_MULTICAST_REC_OK == res->error_code)
2038 {
2039 msg = GNUNET_MQ_extract_nested_mh (res);
2040 }
2041
2042 struct GNUNET_HashCode key_hash;
2043 replay_key_hash (res->fragment_id, res->message_id, res->fragment_offset,
2044 res->flags, &key_hash);
2045
2046 struct GNUNET_CONTAINER_MultiHashMap *
2047 grp_replay_req_cadet = GNUNET_CONTAINER_multihashmap_get (replay_req_cadet,
2048 &grp->pub_key_hash);
2049 if (NULL != grp_replay_req_cadet)
2050 {
2051 GNUNET_CONTAINER_multihashmap_get_multiple (grp_replay_req_cadet, &key_hash,
2052 cadet_send_replay_response_cb,
2053 (void *) msg);
2054 }
2055 if (GNUNET_MULTICAST_REC_OK == res->error_code)
2056 {
2057 struct GNUNET_CONTAINER_MultiHashMap *
2058 grp_replay_req_client = GNUNET_CONTAINER_multihashmap_get (replay_req_client,
2059 &grp->pub_key_hash);
2060 if (NULL != grp_replay_req_client)
2061 {
2062 GNUNET_CONTAINER_multihashmap_get_multiple (grp_replay_req_client, &key_hash,
2063 client_send_replay_response_cb,
2064 (void *) msg);
2065 }
2066 }
2067 else
2068 {
2069 handle_client_replay_response_end (c, res);
2070 return;
2071 }
2072 GNUNET_SERVICE_client_continue (client);
2073}
2074
2075
2076/**
2077 * A new client connected.
2078 *
2079 * @param cls NULL
2080 * @param client client to add
2081 * @param mq message queue for @a client
2082 * @return @a client
2083 */
2084static void *
2085client_notify_connect (void *cls,
2086 struct GNUNET_SERVICE_Client *client,
2087 struct GNUNET_MQ_Handle *mq)
2088{
2089 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client connected: %p\n", client);
2090 /* FIXME: send connect ACK */
2091
2092 struct Client *c = GNUNET_new (struct Client);
2093 c->client = client;
2094
2095 return c;
2096}
2097
2098
2099/**
2100 * Called whenever a client is disconnected.
2101 * Frees our resources associated with that client.
2102 *
2103 * @param cls closure
2104 * @param client identification of the client
2105 * @param app_ctx must match @a client
2106 */
2107static void
2108client_notify_disconnect (void *cls,
2109 struct GNUNET_SERVICE_Client *client,
2110 void *app_ctx)
2111{
2112 struct Client *c = app_ctx;
2113 struct Group *grp = c->group;
2114 GNUNET_free (c);
2115
2116 if (NULL == grp)
2117 {
2118 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2119 "%p User context is NULL in client_disconnect()\n", grp);
2120 GNUNET_break (0);
2121 return;
2122 }
2123
2124 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2125 "%p Client (%s) disconnected from group %s\n",
2126 grp, (GNUNET_YES == grp->is_origin) ? "origin" : "member",
2127 GNUNET_h2s (&grp->pub_key_hash));
2128
2129 // FIXME (due to protocol change): here we must not remove all clients,
2130 // only the one we were notified about!
2131 struct ClientList *cl = grp->clients_head;
2132 while (NULL != cl)
2133 {
2134 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2135 "iterating clients for group %p\n",
2136 grp);
2137 if (cl->client == client)
2138 {
2139 GNUNET_CONTAINER_DLL_remove (grp->clients_head, grp->clients_tail, cl);
2140 GNUNET_free (cl);
2141 break;
2142 }
2143 cl = cl->next;
2144 }
2145
2146 while (GNUNET_YES == replay_req_remove_client (grp, client));
2147
2148 if (NULL == grp->clients_head)
2149 { /* Last client disconnected. */
2150 cleanup_group (grp);
2151 }
2152}
2153
2154
2155/**
2156 * Service started.
2157 *
2158 * @param cls closure
2159 * @param server the initialized server
2160 * @param cfg configuration to use
2161 */
2162static void
2163run (void *cls,
2164 const struct GNUNET_CONFIGURATION_Handle *c,
2165 struct GNUNET_SERVICE_Handle *svc)
2166{
2167 cfg = c;
2168 service = svc;
2169 GNUNET_CRYPTO_get_peer_identity (cfg, &this_peer);
2170
2171 stats = GNUNET_STATISTICS_create ("multicast", cfg);
2172 origins = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
2173 members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
2174 group_members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
2175 channels_in = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
2176 channels_out = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
2177 replay_req_cadet = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
2178 replay_req_client = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
2179
2180 cadet = GNUNET_CADET_connect (cfg);
2181
2182 GNUNET_assert (NULL != cadet);
2183
2184 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
2185 NULL);
2186}
2187
2188
2189/**
2190 * Define "main" method using service macro.
2191 */
2192GNUNET_SERVICE_MAIN
2193("multicast",
2194 GNUNET_SERVICE_OPTION_NONE,
2195 &run,
2196 &client_notify_connect,
2197 &client_notify_disconnect,
2198 NULL,
2199 GNUNET_MQ_hd_fixed_size (client_origin_start,
2200 GNUNET_MESSAGE_TYPE_MULTICAST_ORIGIN_START,
2201 struct MulticastOriginStartMessage,
2202 NULL),
2203 GNUNET_MQ_hd_var_size (client_member_join,
2204 GNUNET_MESSAGE_TYPE_MULTICAST_MEMBER_JOIN,
2205 struct MulticastMemberJoinMessage,
2206 NULL),
2207 GNUNET_MQ_hd_var_size (client_join_decision,
2208 GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION,
2209 struct MulticastJoinDecisionMessageHeader,
2210 NULL),
2211 GNUNET_MQ_hd_fixed_size (client_part_request,
2212 GNUNET_MESSAGE_TYPE_MULTICAST_PART_REQUEST,
2213 struct GNUNET_MessageHeader,
2214 NULL),
2215 GNUNET_MQ_hd_var_size (client_multicast_message,
2216 GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE,
2217 struct GNUNET_MULTICAST_MessageHeader,
2218 NULL),
2219 GNUNET_MQ_hd_var_size (client_multicast_request,
2220 GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST,
2221 struct GNUNET_MULTICAST_RequestHeader,
2222 NULL),
2223 GNUNET_MQ_hd_fixed_size (client_replay_request,
2224 GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST,
2225 struct MulticastReplayRequestMessage,
2226 NULL),
2227 GNUNET_MQ_hd_var_size (client_replay_response,
2228 GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE,
2229 struct MulticastReplayResponseMessage,
2230 NULL),
2231 GNUNET_MQ_hd_var_size (client_replay_response_end,
2232 GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE_END,
2233 struct MulticastReplayResponseMessage,
2234 NULL));
2235
2236/* end of gnunet-service-multicast.c */
diff --git a/src/multicast/multicast.conf.in b/src/multicast/multicast.conf.in
new file mode 100644
index 000000000..df255e26d
--- /dev/null
+++ b/src/multicast/multicast.conf.in
@@ -0,0 +1,21 @@
1[multicast]
2START_ON_DEMAND = @START_ON_DEMAND@
3BINARY = gnunet-service-multicast
4
5UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-multicast.sock
6UNIX_MATCH_UID = YES
7UNIX_MATCH_GID = YES
8
9HOSTNAME = localhost
10ACCEPT_FROM = 127.0.0.1;
11ACCEPT_FROM6 = ::1;
12
13# DISABLE_SOCKET_FORWARDING = NO
14# USERNAME =
15# MAXBUF =
16# TIMEOUT =
17# DISABLEV6 =
18# BINDTO =
19# REJECT_FROM =
20# REJECT_FROM6 =
21# PREFIX =
diff --git a/src/multicast/multicast.h b/src/multicast/multicast.h
new file mode 100644
index 000000000..8a3ca14c8
--- /dev/null
+++ b/src/multicast/multicast.h
@@ -0,0 +1,303 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19*/
20
21/**
22 * @file multicast/multicast.h
23 * @brief multicast IPC messages
24 * @author Christian Grothoff
25 * @author Gabor X Toth
26 */
27#ifndef MULTICAST_H
28#define MULTICAST_H
29
30#include "platform.h"
31#include "gnunet_multicast_service.h"
32
33GNUNET_NETWORK_STRUCT_BEGIN
34
35
36/**
37 * Header of a join request sent to the origin or another member.
38 */
39struct MulticastJoinRequestMessage
40{
41 /**
42 * Type: GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST
43 */
44 struct GNUNET_MessageHeader header;
45
46 /**
47 * Always zero.
48 */
49 uint32_t reserved;
50
51 /**
52 * ECC signature of the rest of the fields of the join request.
53 *
54 * Signature must match the public key of the joining member.
55 */
56 struct GNUNET_CRYPTO_EcdsaSignature signature;
57
58 /**
59 * Purpose for the signature and size of the signed data.
60 */
61 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
62
63 /**
64 * Public key of the target group.
65 */
66 struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key;
67
68 /**
69 * Public key of the joining member.
70 */
71 struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
72
73 /**
74 * Peer identity of the joining member.
75 */
76 struct GNUNET_PeerIdentity peer;
77
78 /* Followed by struct GNUNET_MessageHeader join_message */
79};
80
81
82/**
83 * Header of a join decision message sent to a peer requesting join.
84 */
85struct MulticastJoinDecisionMessage
86{
87 /**
88 * Type: GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION
89 */
90 struct GNUNET_MessageHeader header;
91
92 /**
93 * #GNUNET_YES if the peer was admitted
94 * #GNUNET_NO if entry was refused,
95 * #GNUNET_SYSERR if the request could not be answered.
96 */
97 int32_t is_admitted;
98
99 /**
100 * Number of relays given.
101 */
102 uint32_t relay_count;
103
104 /* Followed by relay_count peer identities */
105
106 /* Followed by the join response message */
107};
108
109
110/**
111 * Header added to a struct MulticastJoinDecisionMessage
112 * when sent between the client and service.
113 */
114struct MulticastJoinDecisionMessageHeader
115{
116 /**
117 * Type: GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION
118 */
119 struct GNUNET_MessageHeader header;
120
121 /**
122 * C->S: Peer to send the join decision to.
123 * S->C: Peer we received the join decision from.
124 */
125 struct GNUNET_PeerIdentity peer;
126
127 /**
128 * C->S: Public key of the member requesting join.
129 * S->C: Unused.
130 */
131 struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
132
133 /* Followed by struct MulticastJoinDecisionMessage */
134};
135
136
137/**
138 * Message sent from the client to the service to notify the service
139 * about the result of a membership test.
140 */
141struct MulticastMembershipTestResultMessage
142{
143 /**
144 * Type: GNUNET_MESSAGE_TYPE_MULTICAST_MEMBERSHIP_TEST_RESULT
145 */
146 struct GNUNET_MessageHeader header;
147
148 /**
149 * Unique ID that identifies the associated membership test.
150 */
151 uint32_t uid;
152
153 /**
154 * #GNUNET_YES if the peer is a member
155 * #GNUNET_NO if peer is not a member,
156 * #GNUNET_SYSERR if the test could not be answered.
157 */
158 int32_t is_admitted;
159};
160
161
162/**
163 * Message sent from the client to the service OR the service to the
164 * client asking for a message fragment to be replayed.
165 */
166struct MulticastReplayRequestMessage
167{
168
169 /**
170 * The message type should be
171 * #GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST.
172 */
173 struct GNUNET_MessageHeader header;
174
175 /**
176 * S->C: Public key of the member requesting replay.
177 * C->S: Unused.
178 */
179 struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
180
181 /**
182 * ID of the message that is being requested.
183 */
184 uint64_t fragment_id;
185
186 /**
187 * ID of the message that is being requested.
188 */
189 uint64_t message_id;
190
191 /**
192 * Offset of the fragment that is being requested.
193 */
194 uint64_t fragment_offset;
195
196 /**
197 * Additional flags for the request.
198 */
199 uint64_t flags;
200
201 /**
202 * Replay request ID.
203 */
204 uint32_t uid;
205};
206
207
208/**
209 * Message sent from the client to the service to give the service
210 * a replayed message.
211 */
212struct MulticastReplayResponseMessage
213{
214
215 /**
216 * Type: GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE
217 * or GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE_END
218 */
219 struct GNUNET_MessageHeader header;
220
221 /**
222 * ID of the message that is being requested.
223 */
224 uint64_t fragment_id;
225
226 /**
227 * ID of the message that is being requested.
228 */
229 uint64_t message_id;
230
231 /**
232 * Offset of the fragment that is being requested.
233 */
234 uint64_t fragment_offset;
235
236 /**
237 * Additional flags for the request.
238 */
239 uint64_t flags;
240
241 /**
242 * An `enum GNUNET_MULTICAST_ReplayErrorCode` identifying issues (in NBO).
243 */
244 int32_t error_code;
245
246 /* followed by replayed message */
247};
248
249
250/**
251 * Message sent from the client to the service to notify the service
252 * about the starting of a multicast group with this peers as its origin.
253 */
254struct MulticastOriginStartMessage
255{
256 /**
257 * Type: GNUNET_MESSAGE_TYPE_MULTICAST_ORIGIN_START
258 */
259 struct GNUNET_MessageHeader header;
260
261 /**
262 * Always zero.
263 */
264 uint32_t reserved;
265
266 /**
267 * Private, non-ephemeral key for the multicast group.
268 */
269 struct GNUNET_CRYPTO_EddsaPrivateKey group_key;
270
271 /**
272 * Last fragment ID sent to the group, used to continue counting fragments if
273 * we resume operating * a group.
274 */
275 uint64_t max_fragment_id;
276};
277
278
279struct MulticastMemberJoinMessage
280{
281 /**
282 * Type: GNUNET_MESSAGE_TYPE_MULTICAST_MEMBER_JOIN
283 */
284 struct GNUNET_MessageHeader header;
285
286 uint32_t relay_count GNUNET_PACKED;
287
288 struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key;
289
290 struct GNUNET_CRYPTO_EcdsaPrivateKey member_key;
291
292 struct GNUNET_PeerIdentity origin;
293
294 /* Followed by struct GNUNET_PeerIdentity relays[relay_count] */
295
296 /* Followed by struct GNUNET_MessageHeader join_msg */
297};
298
299
300GNUNET_NETWORK_STRUCT_END
301
302#endif
303/* end of multicast.h */
diff --git a/src/multicast/multicast_api.c b/src/multicast/multicast_api.c
new file mode 100644
index 000000000..e5e830225
--- /dev/null
+++ b/src/multicast/multicast_api.c
@@ -0,0 +1,1399 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
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 */
40struct 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 */
55struct 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
66struct 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_MQ_Handle *mq;
77
78 /**
79 * Message to send on connect.
80 */
81 struct GNUNET_MQ_Envelope *connect_env;
82
83 /**
84 * Time to wait until we try to reconnect on failure.
85 */
86 struct GNUNET_TIME_Relative reconnect_delay;
87
88 /**
89 * Task for reconnecting when the listener fails.
90 */
91 struct GNUNET_SCHEDULER_Task *reconnect_task;
92
93 GNUNET_MULTICAST_JoinRequestCallback join_req_cb;
94 GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb;
95 GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb;
96 GNUNET_MULTICAST_MessageCallback message_cb;
97 void *cb_cls;
98
99 /**
100 * Function called after disconnected from the service.
101 */
102 GNUNET_ContinuationCallback disconnect_cb;
103
104 /**
105 * Closure for @a disconnect_cb.
106 */
107 void *disconnect_cls;
108
109 /**
110 * Are we currently transmitting a message?
111 */
112 uint8_t in_transmit;
113
114 /**
115 * Number of MULTICAST_FRAGMENT_ACK messages we are still waiting for.
116 */
117 uint8_t acks_pending;
118
119 /**
120 * Is this the origin or a member?
121 */
122 uint8_t is_origin;
123
124 /**
125 * Is this channel in the process of disconnecting from the service?
126 * #GNUNET_YES or #GNUNET_NO
127 */
128 uint8_t is_disconnecting;
129};
130
131
132/**
133 * Handle for the origin of a multicast group.
134 */
135struct GNUNET_MULTICAST_Origin
136{
137 struct GNUNET_MULTICAST_Group grp;
138 struct GNUNET_MULTICAST_OriginTransmitHandle tmit;
139
140 GNUNET_MULTICAST_RequestCallback request_cb;
141};
142
143
144/**
145 * Handle for a multicast group member.
146 */
147struct GNUNET_MULTICAST_Member
148{
149 struct GNUNET_MULTICAST_Group grp;
150 struct GNUNET_MULTICAST_MemberTransmitHandle tmit;
151
152 GNUNET_MULTICAST_JoinDecisionCallback join_dcsn_cb;
153
154 /**
155 * Replay fragment -> struct GNUNET_MULTICAST_MemberReplayHandle *
156 */
157 struct GNUNET_CONTAINER_MultiHashMap *replay_reqs;
158
159 uint64_t next_fragment_id;
160};
161
162
163/**
164 * Handle that identifies a join request.
165 *
166 * Used to match calls to #GNUNET_MULTICAST_JoinRequestCallback to the
167 * corresponding calls to #GNUNET_MULTICAST_join_decision().
168 */
169struct GNUNET_MULTICAST_JoinHandle
170{
171 struct GNUNET_MULTICAST_Group *group;
172
173 /**
174 * Public key of the member requesting join.
175 */
176 struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
177
178 /**
179 * Peer identity of the member requesting join.
180 */
181 struct GNUNET_PeerIdentity peer;
182};
183
184
185/**
186 * Opaque handle to a replay request from the multicast service.
187 */
188struct GNUNET_MULTICAST_ReplayHandle
189{
190 struct GNUNET_MULTICAST_Group *grp;
191 struct MulticastReplayRequestMessage req;
192};
193
194
195/**
196 * Handle for a replay request.
197 */
198struct GNUNET_MULTICAST_MemberReplayHandle
199{
200};
201
202
203static void
204origin_to_all (struct GNUNET_MULTICAST_Origin *orig);
205
206static void
207member_to_origin (struct GNUNET_MULTICAST_Member *mem);
208
209
210/**
211 * Check join request message.
212 */
213static int
214check_group_join_request (void *cls,
215 const struct MulticastJoinRequestMessage *jreq)
216{
217 uint16_t size = ntohs (jreq->header.size);
218
219 if (sizeof (*jreq) == size)
220 return GNUNET_OK;
221
222 if (sizeof (*jreq) + sizeof (struct GNUNET_MessageHeader) <= size)
223 return GNUNET_OK;
224
225 return GNUNET_SYSERR;
226}
227
228
229/**
230 * Receive join request from service.
231 */
232static void
233handle_group_join_request (void *cls,
234 const struct MulticastJoinRequestMessage *jreq)
235{
236 struct GNUNET_MULTICAST_Group *grp = cls;
237 struct GNUNET_MULTICAST_JoinHandle *jh;
238 const struct GNUNET_MessageHeader *jmsg = NULL;
239
240 if (NULL == grp)
241 {
242 GNUNET_break (0);
243 return;
244 }
245 if (NULL == grp->join_req_cb)
246 return;
247
248 if (sizeof (*jreq) + sizeof (*jmsg) <= ntohs (jreq->header.size))
249 jmsg = (const struct GNUNET_MessageHeader *) &jreq[1];
250
251 jh = GNUNET_malloc (sizeof (*jh));
252 jh->group = grp;
253 jh->member_pub_key = jreq->member_pub_key;
254 jh->peer = jreq->peer;
255 grp->join_req_cb (grp->cb_cls, &jreq->member_pub_key, jmsg, jh);
256
257 grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
258}
259
260
261/**
262 * Check multicast message.
263 */
264static int
265check_group_message (void *cls,
266 const struct GNUNET_MULTICAST_MessageHeader *mmsg)
267{
268 return GNUNET_OK;
269}
270
271
272/**
273 * Receive multicast message from service.
274 */
275static void
276handle_group_message (void *cls,
277 const struct GNUNET_MULTICAST_MessageHeader *mmsg)
278{
279 struct GNUNET_MULTICAST_Group *grp = cls;
280
281 if (GNUNET_YES == grp->is_disconnecting)
282 return;
283
284 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
285 "Calling message callback with a message of size %u.\n",
286 ntohs (mmsg->header.size));
287
288 if (NULL != grp->message_cb)
289 grp->message_cb (grp->cb_cls, mmsg);
290
291 grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
292}
293
294
295/**
296 * Receive message/request fragment acknowledgement from service.
297 */
298static void
299handle_group_fragment_ack (void *cls,
300 const struct GNUNET_MessageHeader *msg)
301{
302 struct GNUNET_MULTICAST_Group *grp = cls;
303
304 LOG (GNUNET_ERROR_TYPE_DEBUG,
305 "%p Got fragment ACK. in_transmit=%u, acks_pending=%u\n",
306 grp, grp->in_transmit, grp->acks_pending);
307
308 if (0 == grp->acks_pending)
309 {
310 LOG (GNUNET_ERROR_TYPE_DEBUG,
311 "%p Ignoring extraneous fragment ACK.\n", grp);
312 return;
313 }
314 grp->acks_pending--;
315
316 if (GNUNET_YES != grp->in_transmit)
317 return;
318
319 if (GNUNET_YES == grp->is_origin)
320 origin_to_all ((struct GNUNET_MULTICAST_Origin *) grp);
321 else
322 member_to_origin ((struct GNUNET_MULTICAST_Member *) grp);
323
324 grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
325}
326
327
328/**
329 * Check unicast request.
330 */
331static int
332check_origin_request (void *cls,
333 const struct GNUNET_MULTICAST_RequestHeader *req)
334{
335 return GNUNET_OK;
336}
337
338
339/**
340 * Origin receives unicast request from a member.
341 */
342static void
343handle_origin_request (void *cls,
344 const struct GNUNET_MULTICAST_RequestHeader *req)
345{
346 struct GNUNET_MULTICAST_Group *grp;
347 struct GNUNET_MULTICAST_Origin *orig = cls;
348 grp = &orig->grp;
349
350 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
351 "Calling request callback with a request of size %u.\n",
352 ntohs (req->header.size));
353
354 if (NULL != orig->request_cb)
355 orig->request_cb (grp->cb_cls, req);
356
357 grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
358}
359
360
361/**
362 * Receive multicast replay request from service.
363 */
364static void
365handle_group_replay_request (void *cls,
366 const struct MulticastReplayRequestMessage *rep)
367
368{
369 struct GNUNET_MULTICAST_Group *grp = cls;
370
371 if (GNUNET_YES == grp->is_disconnecting)
372 return;
373
374 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got replay request.\n");
375
376 if (0 != rep->fragment_id)
377 {
378 if (NULL != grp->replay_frag_cb)
379 {
380 struct GNUNET_MULTICAST_ReplayHandle * rh = GNUNET_malloc (sizeof (*rh));
381 rh->grp = grp;
382 rh->req = *rep;
383 grp->replay_frag_cb (grp->cb_cls, &rep->member_pub_key,
384 GNUNET_ntohll (rep->fragment_id),
385 GNUNET_ntohll (rep->flags), rh);
386 }
387 }
388 else if (0 != rep->message_id)
389 {
390 if (NULL != grp->replay_msg_cb)
391 {
392 struct GNUNET_MULTICAST_ReplayHandle * rh = GNUNET_malloc (sizeof (*rh));
393 rh->grp = grp;
394 rh->req = *rep;
395 grp->replay_msg_cb (grp->cb_cls, &rep->member_pub_key,
396 GNUNET_ntohll (rep->message_id),
397 GNUNET_ntohll (rep->fragment_offset),
398 GNUNET_ntohll (rep->flags), rh);
399 }
400 }
401
402 grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
403}
404
405
406/**
407 * Check replay response.
408 */
409static int
410check_member_replay_response (void *cls,
411 const struct MulticastReplayResponseMessage *res)
412{
413 uint16_t size = ntohs (res->header.size);
414
415 if (sizeof (*res) == size)
416 return GNUNET_OK;
417
418 if (sizeof (*res) + sizeof (struct GNUNET_MULTICAST_MessageHeader) <= size)
419 return GNUNET_OK;
420
421 return GNUNET_SYSERR;
422}
423
424
425/**
426 * Receive replay response from service.
427 */
428static void
429handle_member_replay_response (void *cls,
430 const struct MulticastReplayResponseMessage *res)
431{
432 struct GNUNET_MULTICAST_Group *grp;
433 struct GNUNET_MULTICAST_Member *mem = cls;
434 grp = &mem->grp;
435
436 if (GNUNET_YES == grp->is_disconnecting)
437 return;
438
439 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got replay response.\n");
440
441 // FIXME: return result
442}
443
444
445/**
446 * Check join decision.
447 */
448static int
449check_member_join_decision (void *cls,
450 const struct MulticastJoinDecisionMessageHeader *hdcsn)
451{
452 return GNUNET_OK; // checked in handle below
453}
454
455
456/**
457 * Member receives join decision.
458 */
459static void
460handle_member_join_decision (void *cls,
461 const struct MulticastJoinDecisionMessageHeader *hdcsn)
462{
463 struct GNUNET_MULTICAST_Group *grp;
464 struct GNUNET_MULTICAST_Member *mem = cls;
465 grp = &mem->grp;
466
467 const struct MulticastJoinDecisionMessage *
468 dcsn = (const struct MulticastJoinDecisionMessage *) &hdcsn[1];
469
470 uint16_t dcsn_size = ntohs (dcsn->header.size);
471 int is_admitted = ntohl (dcsn->is_admitted);
472
473 LOG (GNUNET_ERROR_TYPE_DEBUG,
474 "%p Member got join decision from multicast: %d\n",
475 mem, is_admitted);
476
477 const struct GNUNET_MessageHeader *join_resp = NULL;
478 uint16_t join_resp_size = 0;
479
480 uint16_t relay_count = ntohl (dcsn->relay_count);
481 const struct GNUNET_PeerIdentity *relays = NULL;
482 uint16_t relay_size = relay_count * sizeof (*relays);
483 if (0 < relay_count)
484 {
485 if (dcsn_size < sizeof (*dcsn) + relay_size)
486 {
487 GNUNET_break_op (0);
488 is_admitted = GNUNET_SYSERR;
489 }
490 else
491 {
492 relays = (struct GNUNET_PeerIdentity *) &dcsn[1];
493 }
494 }
495
496 if (sizeof (*dcsn) + relay_size + sizeof (*join_resp) <= dcsn_size)
497 {
498 join_resp = (const struct GNUNET_MessageHeader *) ((char *) &dcsn[1] + relay_size);
499 join_resp_size = ntohs (join_resp->size);
500 }
501 if (dcsn_size < sizeof (*dcsn) + relay_size + join_resp_size)
502 {
503 LOG (GNUNET_ERROR_TYPE_DEBUG,
504 "Received invalid join decision message from multicast: %u < %u + %u + %u\n",
505 dcsn_size , sizeof (*dcsn), relay_size, join_resp_size);
506 GNUNET_break_op (0);
507 is_admitted = GNUNET_SYSERR;
508 }
509
510 if (NULL != mem->join_dcsn_cb)
511 mem->join_dcsn_cb (grp->cb_cls, is_admitted, &hdcsn->peer,
512 relay_count, relays, join_resp);
513
514 // FIXME:
515 //if (GNUNET_YES != is_admitted)
516 // GNUNET_MULTICAST_member_part (mem);
517
518 grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
519}
520
521
522static void
523group_cleanup (struct GNUNET_MULTICAST_Group *grp)
524{
525 if (NULL != grp->connect_env)
526 {
527 GNUNET_MQ_discard (grp->connect_env);
528 grp->connect_env = NULL;
529 }
530 if (NULL != grp->mq)
531 {
532 GNUNET_MQ_destroy (grp->mq);
533 grp->mq = NULL;
534 }
535 if (NULL != grp->disconnect_cb)
536 {
537 grp->disconnect_cb (grp->disconnect_cls);
538 grp->disconnect_cb = NULL;
539 }
540 GNUNET_free (grp);
541}
542
543
544static void
545handle_group_part_ack (void *cls,
546 const struct GNUNET_MessageHeader *msg)
547{
548 struct GNUNET_MULTICAST_Group *grp = cls;
549
550 group_cleanup (grp);
551}
552
553
554/**
555 * Function to call with the decision made for a join request.
556 *
557 * Must be called once and only once in response to an invocation of the
558 * #GNUNET_MULTICAST_JoinRequestCallback.
559 *
560 * @param join
561 * Join request handle.
562 * @param is_admitted
563 * #GNUNET_YES if the join is approved,
564 * #GNUNET_NO if it is disapproved,
565 * #GNUNET_SYSERR if we cannot answer the request.
566 * @param relay_count
567 * Number of relays given.
568 * @param relays
569 * Array of suggested peers that might be useful relays to use
570 * when joining the multicast group (essentially a list of peers that
571 * are already part of the multicast group and might thus be willing
572 * to help with routing). If empty, only this local peer (which must
573 * be the multicast origin) is a good candidate for building the
574 * multicast tree. Note that it is unnecessary to specify our own
575 * peer identity in this array.
576 * @param join_resp
577 * Message to send in response to the joining peer;
578 * can also be used to redirect the peer to a different group at the
579 * application layer; this response is to be transmitted to the
580 * peer that issued the request even if admission is denied.
581 */
582struct GNUNET_MULTICAST_ReplayHandle *
583GNUNET_MULTICAST_join_decision (struct GNUNET_MULTICAST_JoinHandle *join,
584 int is_admitted,
585 uint16_t relay_count,
586 const struct GNUNET_PeerIdentity *relays,
587 const struct GNUNET_MessageHeader *join_resp)
588{
589 struct GNUNET_MULTICAST_Group *grp = join->group;
590 uint16_t join_resp_size = (NULL != join_resp) ? ntohs (join_resp->size) : 0;
591 uint16_t relay_size = relay_count * sizeof (*relays);
592
593 struct MulticastJoinDecisionMessageHeader *hdcsn;
594 struct MulticastJoinDecisionMessage *dcsn;
595 struct GNUNET_MQ_Envelope *
596 env = GNUNET_MQ_msg_extra (hdcsn, sizeof (*dcsn) + relay_size + join_resp_size,
597 GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION);
598 hdcsn->member_pub_key = join->member_pub_key;
599 hdcsn->peer = join->peer;
600
601 dcsn = (struct MulticastJoinDecisionMessage *) &hdcsn[1];
602 dcsn->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION);
603 dcsn->header.size = htons (sizeof (*dcsn) + relay_size + join_resp_size);
604 dcsn->is_admitted = htonl (is_admitted);
605 dcsn->relay_count = htonl (relay_count);
606 if (0 < relay_size)
607 GNUNET_memcpy (&dcsn[1], relays, relay_size);
608 if (0 < join_resp_size)
609 GNUNET_memcpy (((char *) &dcsn[1]) + relay_size, join_resp, join_resp_size);
610
611 GNUNET_MQ_send (grp->mq, env);
612 GNUNET_free (join);
613 return NULL;
614}
615
616
617/**
618 * Replay a message fragment for the multicast group.
619 *
620 * @param rh
621 * Replay handle identifying which replay operation was requested.
622 * @param msg
623 * Replayed message fragment, NULL if not found / an error occurred.
624 * @param ec
625 * Error code. See enum GNUNET_MULTICAST_ReplayErrorCode
626 * If not #GNUNET_MULTICAST_REC_OK, the replay handle is invalidated.
627 */
628void
629GNUNET_MULTICAST_replay_response (struct GNUNET_MULTICAST_ReplayHandle *rh,
630 const struct GNUNET_MessageHeader *msg,
631 enum GNUNET_MULTICAST_ReplayErrorCode ec)
632{
633 uint8_t msg_size = (NULL != msg) ? ntohs (msg->size) : 0;
634 struct MulticastReplayResponseMessage *res;
635 struct GNUNET_MQ_Envelope *
636 env = GNUNET_MQ_msg_extra (res, msg_size,
637 GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE);
638 res->fragment_id = rh->req.fragment_id;
639 res->message_id = rh->req.message_id;
640 res->fragment_offset = rh->req.fragment_offset;
641 res->flags = rh->req.flags;
642 res->error_code = htonl (ec);
643
644 if (GNUNET_MULTICAST_REC_OK == ec)
645 {
646 GNUNET_assert (NULL != msg);
647 GNUNET_memcpy (&res[1], msg, msg_size);
648 }
649
650 GNUNET_MQ_send (rh->grp->mq, env);
651
652 if (GNUNET_MULTICAST_REC_OK != ec)
653 GNUNET_free (rh);
654}
655
656
657/**
658 * Indicate the end of the replay session.
659 *
660 * Invalidates the replay handle.
661 *
662 * @param rh
663 * Replay session to end.
664 */
665void
666GNUNET_MULTICAST_replay_response_end (struct GNUNET_MULTICAST_ReplayHandle *rh)
667{
668 struct MulticastReplayResponseMessage *end;
669 struct GNUNET_MQ_Envelope *
670 env = GNUNET_MQ_msg (end, GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE_END);
671
672 end->fragment_id = rh->req.fragment_id;
673 end->message_id = rh->req.message_id;
674 end->fragment_offset = rh->req.fragment_offset;
675 end->flags = rh->req.flags;
676
677 GNUNET_MQ_send (rh->grp->mq, env);
678 GNUNET_free (rh);
679}
680
681
682/**
683 * Replay a message for the multicast group.
684 *
685 * @param rh
686 * Replay handle identifying which replay operation was requested.
687 * @param notify
688 * Function to call to get the message.
689 * @param notify_cls
690 * Closure for @a notify.
691 */
692void
693GNUNET_MULTICAST_replay_response2 (struct GNUNET_MULTICAST_ReplayHandle *rh,
694 GNUNET_MULTICAST_ReplayTransmitNotify notify,
695 void *notify_cls)
696{
697}
698
699
700static void
701origin_connect (struct GNUNET_MULTICAST_Origin *orig);
702
703
704static void
705origin_reconnect (void *cls)
706{
707 origin_connect (cls);
708}
709
710
711/**
712 * Origin client disconnected from service.
713 *
714 * Reconnect after backoff period.
715 */
716static void
717origin_disconnected (void *cls, enum GNUNET_MQ_Error error)
718{
719 struct GNUNET_MULTICAST_Origin *orig = cls;
720 struct GNUNET_MULTICAST_Group *grp = &orig->grp;
721
722 LOG (GNUNET_ERROR_TYPE_DEBUG,
723 "Origin client disconnected (%d), re-connecting\n",
724 (int) error);
725 if (NULL != grp->mq)
726 {
727 GNUNET_MQ_destroy (grp->mq);
728 grp->mq = NULL;
729 }
730
731 grp->reconnect_task = GNUNET_SCHEDULER_add_delayed (grp->reconnect_delay,
732 origin_reconnect,
733 orig);
734 grp->reconnect_delay = GNUNET_TIME_STD_BACKOFF (grp->reconnect_delay);
735}
736
737
738/**
739 * Connect to service as origin.
740 */
741static void
742origin_connect (struct GNUNET_MULTICAST_Origin *orig)
743{
744 struct GNUNET_MULTICAST_Group *grp = &orig->grp;
745
746 struct GNUNET_MQ_MessageHandler handlers[] = {
747 GNUNET_MQ_hd_var_size (group_message,
748 GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE,
749 struct GNUNET_MULTICAST_MessageHeader,
750 grp),
751 GNUNET_MQ_hd_var_size (origin_request,
752 GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST,
753 struct GNUNET_MULTICAST_RequestHeader,
754 orig),
755 GNUNET_MQ_hd_fixed_size (group_fragment_ack,
756 GNUNET_MESSAGE_TYPE_MULTICAST_FRAGMENT_ACK,
757 struct GNUNET_MessageHeader,
758 grp),
759 GNUNET_MQ_hd_var_size (group_join_request,
760 GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST,
761 struct MulticastJoinRequestMessage,
762 grp),
763 GNUNET_MQ_hd_fixed_size (group_part_ack,
764 GNUNET_MESSAGE_TYPE_MULTICAST_PART_ACK,
765 struct GNUNET_MessageHeader,
766 grp),
767 GNUNET_MQ_hd_fixed_size (group_replay_request,
768 GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST,
769 struct MulticastReplayRequestMessage,
770 grp),
771 GNUNET_MQ_handler_end ()
772 };
773
774 grp->mq = GNUNET_CLIENT_connect (grp->cfg, "multicast",
775 handlers, origin_disconnected, orig);
776 GNUNET_assert (NULL != grp->mq);
777 GNUNET_MQ_send_copy (grp->mq, grp->connect_env);
778}
779
780
781/**
782 * Start a multicast group.
783 *
784 * Will advertise the origin in the P2P overlay network under the respective
785 * public key so that other peer can find this peer to join it. Peers that
786 * issue GNUNET_MULTICAST_member_join() can then transmit a join request to
787 * either an existing group member or to the origin. If the joining is
788 * approved, the member is cleared for @e replay and will begin to receive
789 * messages transmitted to the group. If joining is disapproved, the failed
790 * candidate will be given a response. Members in the group can send messages
791 * to the origin (one at a time).
792 *
793 * @param cfg
794 * Configuration to use.
795 * @param priv_key
796 * ECC key that will be used to sign messages for this
797 * multicast session; public key is used to identify the multicast group;
798 * @param max_fragment_id
799 * Maximum fragment ID already sent to the group.
800 * 0 for a new group.
801 * @param join_request_cb
802 * Function called to approve / disapprove joining of a peer.
803 * @param replay_frag_cb
804 * Function that can be called to replay a message fragment.
805 * @param replay_msg_cb
806 * Function that can be called to replay a message.
807 * @param request_cb
808 * Function called with message fragments from group members.
809 * @param message_cb
810 * Function called with the message fragments sent to the
811 * network by GNUNET_MULTICAST_origin_to_all(). These message fragments
812 * should be stored for answering replay requests later.
813 * @param cls
814 * Closure for the various callbacks that follow.
815 *
816 * @return Handle for the origin, NULL on error.
817 */
818struct GNUNET_MULTICAST_Origin *
819GNUNET_MULTICAST_origin_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
820 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv_key,
821 uint64_t max_fragment_id,
822 GNUNET_MULTICAST_JoinRequestCallback join_request_cb,
823 GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb,
824 GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb,
825 GNUNET_MULTICAST_RequestCallback request_cb,
826 GNUNET_MULTICAST_MessageCallback message_cb,
827 void *cls)
828{
829 struct GNUNET_MULTICAST_Origin *orig = GNUNET_malloc (sizeof (*orig));
830 struct GNUNET_MULTICAST_Group *grp = &orig->grp;
831
832 struct MulticastOriginStartMessage *start;
833 grp->connect_env = GNUNET_MQ_msg (start,
834 GNUNET_MESSAGE_TYPE_MULTICAST_ORIGIN_START);
835 start->max_fragment_id = max_fragment_id;
836 start->group_key = *priv_key;
837
838 grp->cfg = cfg;
839 grp->is_origin = GNUNET_YES;
840 grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
841
842 grp->cb_cls = cls;
843 grp->join_req_cb = join_request_cb;
844 grp->replay_frag_cb = replay_frag_cb;
845 grp->replay_msg_cb = replay_msg_cb;
846 grp->message_cb = message_cb;
847
848 orig->request_cb = request_cb;
849
850 origin_connect (orig);
851 return orig;
852}
853
854
855/**
856 * Stop a multicast group.
857 *
858 * @param origin
859 * Multicast group to stop.
860 */
861void
862GNUNET_MULTICAST_origin_stop (struct GNUNET_MULTICAST_Origin *orig,
863 GNUNET_ContinuationCallback stop_cb,
864 void *stop_cls)
865{
866 struct GNUNET_MULTICAST_Group *grp = &orig->grp;
867 struct GNUNET_MQ_Envelope *env;
868
869 grp->is_disconnecting = GNUNET_YES;
870 grp->disconnect_cb = stop_cb;
871 grp->disconnect_cls = stop_cls;
872 env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_MULTICAST_PART_REQUEST);
873 GNUNET_MQ_send (grp->mq, env);
874}
875
876
877static void
878origin_to_all (struct GNUNET_MULTICAST_Origin *orig)
879{
880 LOG (GNUNET_ERROR_TYPE_DEBUG, "%p origin_to_all()\n", orig);
881 struct GNUNET_MULTICAST_Group *grp = &orig->grp;
882 struct GNUNET_MULTICAST_OriginTransmitHandle *tmit = &orig->tmit;
883 GNUNET_assert (GNUNET_YES == grp->in_transmit);
884
885 size_t buf_size = GNUNET_MULTICAST_FRAGMENT_MAX_SIZE;
886 struct GNUNET_MULTICAST_MessageHeader *msg;
887 struct GNUNET_MQ_Envelope *
888 env = GNUNET_MQ_msg_extra (msg, buf_size - sizeof(*msg),
889 GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
890
891 int ret = tmit->notify (tmit->notify_cls, &buf_size, &msg[1]);
892
893 if (! (GNUNET_YES == ret || GNUNET_NO == ret)
894 || GNUNET_MULTICAST_FRAGMENT_MAX_SIZE < buf_size)
895 {
896 LOG (GNUNET_ERROR_TYPE_ERROR,
897 "%p OriginTransmitNotify() returned error or invalid message size.\n",
898 orig);
899 /* FIXME: handle error */
900 GNUNET_MQ_discard (env);
901 return;
902 }
903
904 if (GNUNET_NO == ret && 0 == buf_size)
905 {
906 LOG (GNUNET_ERROR_TYPE_DEBUG,
907 "%p OriginTransmitNotify() - transmission paused.\n", orig);
908 GNUNET_MQ_discard (env);
909 return; /* Transmission paused. */
910 }
911
912 msg->header.size = htons (sizeof (*msg) + buf_size);
913 msg->message_id = GNUNET_htonll (tmit->message_id);
914 msg->group_generation = tmit->group_generation;
915 msg->fragment_offset = GNUNET_htonll (tmit->fragment_offset);
916 tmit->fragment_offset += sizeof (*msg) + buf_size;
917
918 grp->acks_pending++;
919 GNUNET_MQ_send (grp->mq, env);
920
921 if (GNUNET_YES == ret)
922 grp->in_transmit = GNUNET_NO;
923}
924
925
926/**
927 * Send a message to the multicast group.
928 *
929 * @param orig
930 * Handle to the multicast group.
931 * @param message_id
932 * Application layer ID for the message. Opaque to multicast.
933 * @param group_generation
934 * Group generation of the message.
935 * Documented in struct GNUNET_MULTICAST_MessageHeader.
936 * @param notify
937 * Function to call to get the message.
938 * @param notify_cls
939 * Closure for @a notify.
940 *
941 * @return Message handle on success,
942 * NULL on error (i.e. another request is already pending).
943 */
944struct GNUNET_MULTICAST_OriginTransmitHandle *
945GNUNET_MULTICAST_origin_to_all (struct GNUNET_MULTICAST_Origin *orig,
946 uint64_t message_id,
947 uint64_t group_generation,
948 GNUNET_MULTICAST_OriginTransmitNotify notify,
949 void *notify_cls)
950{
951 struct GNUNET_MULTICAST_Group *grp = &orig->grp;
952 if (GNUNET_YES == grp->in_transmit)
953 return NULL;
954 grp->in_transmit = GNUNET_YES;
955
956 struct GNUNET_MULTICAST_OriginTransmitHandle *tmit = &orig->tmit;
957 tmit->origin = orig;
958 tmit->message_id = message_id;
959 tmit->fragment_offset = 0;
960 tmit->group_generation = group_generation;
961 tmit->notify = notify;
962 tmit->notify_cls = notify_cls;
963
964 origin_to_all (orig);
965 return tmit;
966}
967
968
969/**
970 * Resume message transmission to multicast group.
971 *
972 * @param th
973 * Transmission to cancel.
974 */
975void
976GNUNET_MULTICAST_origin_to_all_resume (struct GNUNET_MULTICAST_OriginTransmitHandle *th)
977{
978 struct GNUNET_MULTICAST_Group *grp = &th->origin->grp;
979 if (0 != grp->acks_pending || GNUNET_YES != grp->in_transmit)
980 return;
981 origin_to_all (th->origin);
982}
983
984
985/**
986 * Cancel request for message transmission to multicast group.
987 *
988 * @param th
989 * Transmission to cancel.
990 */
991void
992GNUNET_MULTICAST_origin_to_all_cancel (struct GNUNET_MULTICAST_OriginTransmitHandle *th)
993{
994 th->origin->grp.in_transmit = GNUNET_NO;
995}
996
997
998static void
999member_connect (struct GNUNET_MULTICAST_Member *mem);
1000
1001
1002static void
1003member_reconnect (void *cls)
1004{
1005 member_connect (cls);
1006}
1007
1008
1009/**
1010 * Member client disconnected from service.
1011 *
1012 * Reconnect after backoff period.
1013 */
1014static void
1015member_disconnected (void *cls, enum GNUNET_MQ_Error error)
1016{
1017 struct GNUNET_MULTICAST_Member *mem = cls;
1018 struct GNUNET_MULTICAST_Group *grp = &mem->grp;
1019
1020 LOG (GNUNET_ERROR_TYPE_DEBUG,
1021 "Member client disconnected (%d), re-connecting\n",
1022 (int) error);
1023 GNUNET_MQ_destroy (grp->mq);
1024 grp->mq = NULL;
1025
1026 grp->reconnect_task = GNUNET_SCHEDULER_add_delayed (grp->reconnect_delay,
1027 member_reconnect,
1028 mem);
1029 grp->reconnect_delay = GNUNET_TIME_STD_BACKOFF (grp->reconnect_delay);
1030}
1031
1032
1033/**
1034 * Connect to service as member.
1035 */
1036static void
1037member_connect (struct GNUNET_MULTICAST_Member *mem)
1038{
1039 struct GNUNET_MULTICAST_Group *grp = &mem->grp;
1040
1041 struct GNUNET_MQ_MessageHandler handlers[] = {
1042 GNUNET_MQ_hd_var_size (group_message,
1043 GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE,
1044 struct GNUNET_MULTICAST_MessageHeader,
1045 grp),
1046 GNUNET_MQ_hd_fixed_size (group_fragment_ack,
1047 GNUNET_MESSAGE_TYPE_MULTICAST_FRAGMENT_ACK,
1048 struct GNUNET_MessageHeader,
1049 grp),
1050 GNUNET_MQ_hd_var_size (group_join_request,
1051 GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST,
1052 struct MulticastJoinRequestMessage,
1053 grp),
1054 GNUNET_MQ_hd_var_size (member_join_decision,
1055 GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION,
1056 struct MulticastJoinDecisionMessageHeader,
1057 mem),
1058 GNUNET_MQ_hd_fixed_size (group_part_ack,
1059 GNUNET_MESSAGE_TYPE_MULTICAST_PART_ACK,
1060 struct GNUNET_MessageHeader,
1061 grp),
1062 GNUNET_MQ_hd_fixed_size (group_replay_request,
1063 GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST,
1064 struct MulticastReplayRequestMessage,
1065 grp),
1066 GNUNET_MQ_hd_var_size (member_replay_response,
1067 GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE,
1068 struct MulticastReplayResponseMessage,
1069 mem),
1070 GNUNET_MQ_handler_end ()
1071 };
1072
1073 grp->mq = GNUNET_CLIENT_connect (grp->cfg, "multicast",
1074 handlers, member_disconnected, mem);
1075 GNUNET_assert (NULL != grp->mq);
1076 GNUNET_MQ_send_copy (grp->mq, grp->connect_env);
1077}
1078
1079
1080/**
1081 * Join a multicast group.
1082 *
1083 * The entity joining is always the local peer. Further information about the
1084 * candidate can be provided in the @a join_request message. If the join fails, the
1085 * @a message_cb is invoked with a (failure) response and then with NULL. If
1086 * the join succeeds, outstanding (state) messages and ongoing multicast
1087 * messages will be given to the @a message_cb until the member decides to part
1088 * the group. The @a replay_cb function may be called at any time by the
1089 * multicast service to support relaying messages to other members of the group.
1090 *
1091 * @param cfg
1092 * Configuration to use.
1093 * @param group_key
1094 * ECC public key that identifies the group to join.
1095 * @param member_key
1096 * ECC key that identifies the member
1097 * and used to sign requests sent to the origin.
1098 * @param origin
1099 * Peer ID of the origin to send unicast requsets to. If NULL,
1100 * unicast requests are sent back via multiple hops on the reverse path
1101 * of multicast messages.
1102 * @param relay_count
1103 * Number of peers in the @a relays array.
1104 * @param relays
1105 * Peer identities of members of the group, which serve as relays
1106 * and can be used to join the group at. and send the @a join_request to.
1107 * If empty, the @a join_request is sent directly to the @a origin.
1108 * @param join_msg
1109 * Application-dependent join message to be passed to the peer @a origin.
1110 * @param join_request_cb
1111 * Function called to approve / disapprove joining of a peer.
1112 * @param join_decision_cb
1113 * Function called to inform about the join decision.
1114 * @param replay_frag_cb
1115 * Function that can be called to replay message fragments
1116 * this peer already knows from this group. NULL if this
1117 * client is unable to support replay.
1118 * @param replay_msg_cb
1119 * Function that can be called to replay message fragments
1120 * this peer already knows from this group. NULL if this
1121 * client is unable to support replay.
1122 * @param message_cb
1123 * Function to be called for all message fragments we
1124 * receive from the group, excluding those our @a replay_cb
1125 * already has.
1126 * @param cls
1127 * Closure for callbacks.
1128 *
1129 * @return Handle for the member, NULL on error.
1130 */
1131struct GNUNET_MULTICAST_Member *
1132GNUNET_MULTICAST_member_join (const struct GNUNET_CONFIGURATION_Handle *cfg,
1133 const struct GNUNET_CRYPTO_EddsaPublicKey *group_pub_key,
1134 const struct GNUNET_CRYPTO_EcdsaPrivateKey *member_key,
1135 const struct GNUNET_PeerIdentity *origin,
1136 uint16_t relay_count,
1137 const struct GNUNET_PeerIdentity *relays,
1138 const struct GNUNET_MessageHeader *join_msg,
1139 GNUNET_MULTICAST_JoinRequestCallback join_request_cb,
1140 GNUNET_MULTICAST_JoinDecisionCallback join_decision_cb,
1141 GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb,
1142 GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb,
1143 GNUNET_MULTICAST_MessageCallback message_cb,
1144 void *cls)
1145{
1146 struct GNUNET_MULTICAST_Member *mem = GNUNET_malloc (sizeof (*mem));
1147 struct GNUNET_MULTICAST_Group *grp = &mem->grp;
1148
1149 uint16_t relay_size = relay_count * sizeof (*relays);
1150 uint16_t join_msg_size = (NULL != join_msg) ? ntohs (join_msg->size) : 0;
1151 struct MulticastMemberJoinMessage *join;
1152 grp->connect_env = GNUNET_MQ_msg_extra (join, relay_size + join_msg_size,
1153 GNUNET_MESSAGE_TYPE_MULTICAST_MEMBER_JOIN);
1154 join->group_pub_key = *group_pub_key;
1155 join->member_key = *member_key;
1156 join->origin = *origin;
1157 join->relay_count = ntohl (relay_count);
1158 if (0 < relay_size)
1159 GNUNET_memcpy (&join[1], relays, relay_size);
1160 if (0 < join_msg_size)
1161 GNUNET_memcpy (((char *) &join[1]) + relay_size, join_msg, join_msg_size);
1162
1163 grp->cfg = cfg;
1164 grp->is_origin = GNUNET_NO;
1165 grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
1166
1167 mem->join_dcsn_cb = join_decision_cb;
1168 grp->join_req_cb = join_request_cb;
1169 grp->replay_frag_cb = replay_frag_cb;
1170 grp->replay_msg_cb = replay_msg_cb;
1171 grp->message_cb = message_cb;
1172 grp->cb_cls = cls;
1173
1174 member_connect (mem);
1175 return mem;
1176}
1177
1178
1179/**
1180 * Part a multicast group.
1181 *
1182 * Disconnects from all group members and invalidates the @a member handle.
1183 *
1184 * An application-dependent part message can be transmitted beforehand using
1185 * #GNUNET_MULTICAST_member_to_origin())
1186 *
1187 * @param member
1188 * Membership handle.
1189 */
1190void
1191GNUNET_MULTICAST_member_part (struct GNUNET_MULTICAST_Member *mem,
1192 GNUNET_ContinuationCallback part_cb,
1193 void *part_cls)
1194{
1195 struct GNUNET_MULTICAST_Group *grp = &mem->grp;
1196 struct GNUNET_MQ_Envelope *env;
1197
1198 mem->join_dcsn_cb = NULL;
1199 grp->join_req_cb = NULL;
1200 grp->message_cb = NULL;
1201 grp->replay_msg_cb = NULL;
1202 grp->replay_frag_cb = NULL;
1203 grp->is_disconnecting = GNUNET_YES;
1204 grp->disconnect_cb = part_cb;
1205 grp->disconnect_cls = part_cls;
1206 env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_MULTICAST_PART_REQUEST);
1207 GNUNET_MQ_send (grp->mq, env);
1208}
1209
1210
1211void
1212member_replay_request (struct GNUNET_MULTICAST_Member *mem,
1213 uint64_t fragment_id,
1214 uint64_t message_id,
1215 uint64_t fragment_offset,
1216 uint64_t flags)
1217{
1218 struct MulticastReplayRequestMessage *rep;
1219 struct GNUNET_MQ_Envelope *
1220 env = GNUNET_MQ_msg (rep, GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST);
1221
1222 rep->fragment_id = GNUNET_htonll (fragment_id);
1223 rep->message_id = GNUNET_htonll (message_id);
1224 rep->fragment_offset = GNUNET_htonll (fragment_offset);
1225 rep->flags = GNUNET_htonll (flags);
1226
1227 GNUNET_MQ_send (mem->grp.mq, env);
1228}
1229
1230
1231/**
1232 * Request a fragment to be replayed by fragment ID.
1233 *
1234 * Useful if messages below the @e max_known_fragment_id given when joining are
1235 * needed and not known to the client.
1236 *
1237 * @param member
1238 * Membership handle.
1239 * @param fragment_id
1240 * ID of a message fragment that this client would like to see replayed.
1241 * @param flags
1242 * Additional flags for the replay request.
1243 * It is used and defined by GNUNET_MULTICAST_ReplayFragmentCallback
1244 *
1245 * @return Replay request handle.
1246 */
1247struct GNUNET_MULTICAST_MemberReplayHandle *
1248GNUNET_MULTICAST_member_replay_fragment (struct GNUNET_MULTICAST_Member *mem,
1249 uint64_t fragment_id,
1250 uint64_t flags)
1251{
1252 member_replay_request (mem, fragment_id, 0, 0, flags);
1253 // FIXME: return something useful
1254 return NULL;
1255}
1256
1257
1258/**
1259 * Request a message fragment to be replayed.
1260 *
1261 * Useful if messages below the @e max_known_fragment_id given when joining are
1262 * needed and not known to the client.
1263 *
1264 * @param member
1265 * Membership handle.
1266 * @param message_id
1267 * ID of the message this client would like to see replayed.
1268 * @param fragment_offset
1269 * Offset of the fragment within the message to replay.
1270 * @param flags
1271 * Additional flags for the replay request.
1272 * It is used & defined by GNUNET_MULTICAST_ReplayMessageCallback
1273 *
1274 * @return Replay request handle, NULL on error.
1275 */
1276struct GNUNET_MULTICAST_MemberReplayHandle *
1277GNUNET_MULTICAST_member_replay_message (struct GNUNET_MULTICAST_Member *mem,
1278 uint64_t message_id,
1279 uint64_t fragment_offset,
1280 uint64_t flags)
1281{
1282 member_replay_request (mem, 0, message_id, fragment_offset, flags);
1283 // FIXME: return something useful
1284 return NULL;
1285}
1286
1287
1288static void
1289member_to_origin (struct GNUNET_MULTICAST_Member *mem)
1290{
1291 LOG (GNUNET_ERROR_TYPE_DEBUG, "member_to_origin()\n");
1292 struct GNUNET_MULTICAST_Group *grp = &mem->grp;
1293 struct GNUNET_MULTICAST_MemberTransmitHandle *tmit = &mem->tmit;
1294 GNUNET_assert (GNUNET_YES == grp->in_transmit);
1295
1296 size_t buf_size = GNUNET_MULTICAST_FRAGMENT_MAX_SIZE;
1297 struct GNUNET_MULTICAST_RequestHeader *req;
1298 struct GNUNET_MQ_Envelope *
1299 env = GNUNET_MQ_msg_extra (req, buf_size - sizeof(*req),
1300 GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST);
1301
1302 int ret = tmit->notify (tmit->notify_cls, &buf_size, &req[1]);
1303
1304 if (! (GNUNET_YES == ret || GNUNET_NO == ret)
1305 || GNUNET_MULTICAST_FRAGMENT_MAX_SIZE < buf_size)
1306 {
1307 LOG (GNUNET_ERROR_TYPE_ERROR,
1308 "MemberTransmitNotify() returned error or invalid message size. "
1309 "ret=%d, buf_size=%u\n", ret, buf_size);
1310 /* FIXME: handle error */
1311 GNUNET_MQ_discard (env);
1312 return;
1313 }
1314
1315 if (GNUNET_NO == ret && 0 == buf_size)
1316 {
1317 /* Transmission paused. */
1318 GNUNET_MQ_discard (env);
1319 return;
1320 }
1321
1322 req->header.size = htons (sizeof (*req) + buf_size);
1323 req->request_id = GNUNET_htonll (tmit->request_id);
1324 req->fragment_offset = GNUNET_ntohll (tmit->fragment_offset);
1325 tmit->fragment_offset += sizeof (*req) + buf_size;
1326
1327 GNUNET_MQ_send (grp->mq, env);
1328
1329 if (GNUNET_YES == ret)
1330 grp->in_transmit = GNUNET_NO;
1331}
1332
1333
1334/**
1335 * Send a message to the origin of the multicast group.
1336 *
1337 * @param mem
1338 * Membership handle.
1339 * @param request_id
1340 * Application layer ID for the request. Opaque to multicast.
1341 * @param notify
1342 * Callback to call to get the message.
1343 * @param notify_cls
1344 * Closure for @a notify.
1345 *
1346 * @return Handle to cancel request, NULL on error (i.e. request already pending).
1347 */
1348struct GNUNET_MULTICAST_MemberTransmitHandle *
1349GNUNET_MULTICAST_member_to_origin (struct GNUNET_MULTICAST_Member *mem,
1350 uint64_t request_id,
1351 GNUNET_MULTICAST_MemberTransmitNotify notify,
1352 void *notify_cls)
1353{
1354 if (GNUNET_YES == mem->grp.in_transmit)
1355 return NULL;
1356 mem->grp.in_transmit = GNUNET_YES;
1357
1358 struct GNUNET_MULTICAST_MemberTransmitHandle *tmit = &mem->tmit;
1359 tmit->member = mem;
1360 tmit->request_id = request_id;
1361 tmit->fragment_offset = 0;
1362 tmit->notify = notify;
1363 tmit->notify_cls = notify_cls;
1364
1365 member_to_origin (mem);
1366 return tmit;
1367}
1368
1369
1370/**
1371 * Resume message transmission to origin.
1372 *
1373 * @param th
1374 * Transmission to cancel.
1375 */
1376void
1377GNUNET_MULTICAST_member_to_origin_resume (struct GNUNET_MULTICAST_MemberTransmitHandle *th)
1378{
1379 struct GNUNET_MULTICAST_Group *grp = &th->member->grp;
1380 if (0 != grp->acks_pending || GNUNET_YES != grp->in_transmit)
1381 return;
1382 member_to_origin (th->member);
1383}
1384
1385
1386/**
1387 * Cancel request for message transmission to origin.
1388 *
1389 * @param th
1390 * Transmission to cancel.
1391 */
1392void
1393GNUNET_MULTICAST_member_to_origin_cancel (struct GNUNET_MULTICAST_MemberTransmitHandle *th)
1394{
1395 th->member->grp.in_transmit = GNUNET_NO;
1396}
1397
1398
1399/* end of multicast_api.c */
diff --git a/src/multicast/test_multicast.c b/src/multicast/test_multicast.c
new file mode 100644
index 000000000..b03c52980
--- /dev/null
+++ b/src/multicast/test_multicast.c
@@ -0,0 +1,758 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013 GNUnet e.V.
4 *
5 * GNUnet is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License,
8 * or (at your 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 * Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file multicast/test_multicast.c
23 * @brief Tests for the Multicast API.
24 * @author Gabor X Toth
25 */
26
27#include <inttypes.h>
28
29#include <gnunet/platform.h>
30#include <gnunet/gnunet_crypto_lib.h>
31#include <gnunet/gnunet_common.h>
32#include <gnunet/gnunet_util_lib.h>
33#include <gnunet/gnunet_testing_lib.h>
34#include "gnunet_multicast_service.h"
35
36#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
37
38/**
39 * Return value from 'main'.
40 */
41static int res;
42
43/**
44 * Handle for task for timeout termination.
45 */
46static struct GNUNET_SCHEDULER_Task * end_badly_task;
47
48static const struct GNUNET_CONFIGURATION_Handle *cfg;
49
50struct GNUNET_PeerIdentity this_peer;
51
52struct GNUNET_MULTICAST_Origin *origin;
53struct GNUNET_MULTICAST_Member *member;
54
55struct GNUNET_CRYPTO_EddsaPrivateKey *group_key;
56struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key;
57
58struct GNUNET_CRYPTO_EcdsaPrivateKey *member_key;
59struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
60
61struct TransmitClosure {
62 struct GNUNET_MULTICAST_OriginTransmitHandle *orig_tmit;
63 struct GNUNET_MULTICAST_MemberTransmitHandle *mem_tmit;
64 char * data[16];
65 uint8_t data_delay[16];
66 uint8_t data_count;
67 uint8_t paused;
68 uint8_t n;
69} tmit_cls;
70
71struct OriginClosure {
72 uint8_t msgs_expected;
73 uint8_t n;
74} origin_cls;
75
76struct MemberClosure {
77 uint8_t msgs_expected;
78 size_t n;
79} member_cls;
80
81struct GNUNET_MessageHeader *join_req, *join_resp;
82
83enum
84{
85 TEST_NONE = 0,
86 TEST_ORIGIN_START = 1,
87 TEST_MEMBER_JOIN_REFUSE = 2,
88 TEST_MEMBER_JOIN_ADMIT = 3,
89 TEST_ORIGIN_TO_ALL = 4,
90 TEST_ORIGIN_TO_ALL_RECV = 5,
91 TEST_MEMBER_TO_ORIGIN = 6,
92 TEST_MEMBER_REPLAY_ERROR = 7,
93 TEST_MEMBER_REPLAY_OK = 8,
94 TEST_MEMBER_PART = 9,
95 TEST_ORIGIN_STOP = 10,
96} test;
97
98uint64_t replay_fragment_id;
99uint64_t replay_flags;
100
101static void
102member_join (int t);
103
104
105/**
106 * Clean up all resources used.
107 */
108static void
109cleanup ()
110{
111 if (NULL != member)
112 {
113 GNUNET_MULTICAST_member_part (member, NULL, NULL);
114 member = NULL;
115 }
116 if (NULL != origin)
117 {
118 GNUNET_MULTICAST_origin_stop (origin, NULL, NULL);
119 origin = NULL;
120 }
121}
122
123
124/**
125 * Terminate the test case (failure).
126 *
127 * @param cls NULL
128 */
129static void
130end_badly (void *cls)
131{
132 res = 1;
133 cleanup ();
134 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Test FAILED.\n");
135}
136
137
138/**
139 * Terminate the test case (success).
140 *
141 * @param cls NULL
142 */
143static void
144end_normally (void *cls)
145{
146 res = 0;
147 cleanup ();
148 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Test PASSED.\n");
149}
150
151
152/**
153 * Finish the test case (successfully).
154 */
155static void
156end ()
157{
158 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending tests.\n");
159
160 if (end_badly_task != NULL)
161 {
162 GNUNET_SCHEDULER_cancel (end_badly_task);
163 end_badly_task = NULL;
164 }
165 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
166 &end_normally, NULL);
167}
168
169
170static void
171tmit_resume (void *cls)
172{
173 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmission resumed.\n");
174 struct TransmitClosure *tmit = cls;
175 if (NULL != tmit->orig_tmit)
176 GNUNET_MULTICAST_origin_to_all_resume (tmit->orig_tmit);
177 else if (NULL != tmit->mem_tmit)
178 GNUNET_MULTICAST_member_to_origin_resume (tmit->mem_tmit);
179}
180
181
182static int
183tmit_notify (void *cls, size_t *data_size, void *data)
184{
185 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
186 "Test #%u: origin_tmit_notify()\n", test);
187 struct TransmitClosure *tmit = cls;
188
189 if (0 == tmit->data_count)
190 {
191 *data_size = 0;
192 return GNUNET_YES;
193 }
194
195 uint16_t size = strlen (tmit->data[tmit->n]);
196 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
197 "Transmit notify data: %u bytes available, processing fragment %u/%u (size %u).\n",
198 (unsigned int) *data_size,
199 tmit->n + 1,
200 tmit->data_count,
201 size);
202 if (*data_size < size)
203 {
204 *data_size = 0;
205 GNUNET_assert (0);
206 return GNUNET_SYSERR;
207 }
208
209 if (GNUNET_YES != tmit->paused && 0 < tmit->data_delay[tmit->n])
210 {
211 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmission paused.\n");
212 tmit->paused = GNUNET_YES;
213 GNUNET_SCHEDULER_add_delayed (
214 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
215 tmit->data_delay[tmit->n]),
216 tmit_resume, tmit);
217 *data_size = 0;
218 return GNUNET_NO;
219 }
220 tmit->paused = GNUNET_NO;
221
222 *data_size = size;
223 GNUNET_memcpy (data, tmit->data[tmit->n], size);
224
225 return ++tmit->n < tmit->data_count ? GNUNET_NO : GNUNET_YES;
226}
227
228
229static void
230member_recv_join_request (void *cls,
231 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
232 const struct GNUNET_MessageHeader *join_msg,
233 struct GNUNET_MULTICAST_JoinHandle *jh)
234{
235 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
236 "Test #%u: member_recv_join_request()\n", test);
237}
238
239
240static void
241origin_stopped (void *cls)
242{
243 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
244 "Test #%u: origin_stopped()\n", test);
245 end ();
246}
247
248
249static void
250schedule_origin_stop (void *cls)
251{
252 test = TEST_ORIGIN_STOP;
253 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
254 "Test #%u: origin_stop()\n", test);
255 GNUNET_MULTICAST_origin_stop (origin, origin_stopped, NULL);
256 origin = NULL;
257}
258
259
260static void
261member_parted (void *cls)
262{
263 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
264 "Test #%u: member_parted()\n", test);
265 member = NULL;
266
267 switch (test)
268 {
269 case TEST_MEMBER_JOIN_REFUSE:
270 // Test 3 starts here
271 member_join (TEST_MEMBER_JOIN_ADMIT);
272 break;
273
274 case TEST_MEMBER_PART:
275 GNUNET_SCHEDULER_add_now (&schedule_origin_stop, NULL);
276 break;
277
278 default:
279 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
280 "Invalid test #%d in member_parted()\n", test);
281 GNUNET_assert (0);
282 }
283}
284
285
286static void
287schedule_member_part (void *cls)
288{
289 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
290 "Test #%u: schedule_member_part()\n", test);
291 GNUNET_MULTICAST_member_part (member, member_parted, NULL);
292}
293
294
295static void
296member_part ()
297{
298 test = TEST_MEMBER_PART;
299 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
300 "Test #%u: member_part()\n", test);
301 // Test 10 starts here
302 GNUNET_SCHEDULER_add_now (&schedule_member_part, NULL);
303}
304
305
306static void
307member_replay_ok ()
308{
309 // Execution of test 8 here
310 test = TEST_MEMBER_REPLAY_OK;
311 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
312 "Test #%u: member_replay_ok()\n", test);
313 replay_fragment_id = 1;
314 replay_flags = 1 | 1<<11;
315 GNUNET_MULTICAST_member_replay_fragment (member, replay_fragment_id,
316 replay_flags);
317}
318
319
320static void
321member_replay_error ()
322{
323 test = TEST_MEMBER_REPLAY_ERROR;
324 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
325 "Test #%u: member_replay_error()\n", test);
326 replay_fragment_id = 1234;
327 replay_flags = 11 | 1<<11;
328 GNUNET_MULTICAST_member_replay_fragment (member, replay_fragment_id,
329 replay_flags);
330}
331
332
333static void
334origin_recv_replay_msg (void *cls,
335 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
336 uint64_t message_id,
337 uint64_t fragment_offset,
338 uint64_t flags,
339 struct GNUNET_MULTICAST_ReplayHandle *rh)
340{
341 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
342 "Test #%u: origin_recv_replay_msg()\n", test);
343 GNUNET_assert (0);
344}
345
346
347static void
348member_recv_replay_msg (void *cls,
349 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
350 uint64_t message_id,
351 uint64_t fragment_offset,
352 uint64_t flags,
353 struct GNUNET_MULTICAST_ReplayHandle *rh)
354{
355 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
356 "Test #%u: member_recv_replay_msg()\n", test);
357 GNUNET_assert (0);
358}
359
360
361static void
362origin_recv_replay_frag (void *cls,
363 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
364 uint64_t fragment_id,
365 uint64_t flags,
366 struct GNUNET_MULTICAST_ReplayHandle *rh)
367{
368 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
369 "Test #%u: origin_recv_replay_frag()"
370 " - fragment_id=%" PRIu64 " flags=%" PRIu64 "\n",
371 test, fragment_id, flags);
372 GNUNET_assert (replay_fragment_id == fragment_id && replay_flags == flags);
373 switch (test)
374 {
375 case TEST_MEMBER_REPLAY_ERROR:
376 // Test 8 starts here
377 GNUNET_MULTICAST_replay_response (rh, NULL, GNUNET_SYSERR);
378 member_replay_ok ();
379 break;
380
381 case TEST_MEMBER_REPLAY_OK:
382 {
383 struct GNUNET_MULTICAST_MessageHeader mmsg = {
384 .header = {
385 .type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE),
386 .size = htons (sizeof (mmsg)),
387 },
388 .fragment_id = GNUNET_htonll (1),
389 .message_id = GNUNET_htonll (1),
390 .fragment_offset = 0,
391 .group_generation = GNUNET_htonll (1),
392 .flags = 0,
393 };
394 member_cls.n = 0;
395 member_cls.msgs_expected = 1;
396 GNUNET_MULTICAST_replay_response (rh, &mmsg.header, GNUNET_MULTICAST_REC_OK);
397 GNUNET_MULTICAST_replay_response_end (rh);
398 break;
399 }
400
401 default:
402 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
403 "Invalid test #%d in origin_recv_replay_frag()\n", test);
404 GNUNET_assert (0);
405 }
406}
407
408
409static void
410member_recv_replay_frag (void *cls,
411 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
412 uint64_t fragment_id,
413 uint64_t flags,
414 struct GNUNET_MULTICAST_ReplayHandle *rh)
415{
416 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
417 "Test #%u: member_recv_replay_frag()\n", test);
418 GNUNET_assert (0);
419}
420
421
422static void
423origin_recv_request (void *cls,
424 const struct GNUNET_MULTICAST_RequestHeader *req)
425{
426 struct OriginClosure *ocls = cls;
427 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
428 "Test #%u: origin_recv_request()\n", test);
429 if (++ocls->n != ocls->msgs_expected)
430 return;
431
432 GNUNET_assert (0 == memcmp (&req->member_pub_key,
433 &member_pub_key, sizeof (member_pub_key)));
434
435 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
436 "Test #%u: verify message content, take first 3 bytes: %.3s\n",
437 test, (char *)&req[1]);
438 GNUNET_assert (0 == memcmp (&req[1], "abc", 3));
439
440 // Test 7 starts here
441 member_replay_error ();
442}
443
444
445static void
446member_to_origin ()
447{
448 test = TEST_MEMBER_TO_ORIGIN;
449 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
450 "Test #%u: member_to_origin()\n", test);
451
452 struct TransmitClosure *tmit = &tmit_cls;
453 *tmit = (struct TransmitClosure) {};
454 tmit->data[0] = "abc def";
455 tmit->data[1] = "ghi jkl mno";
456 tmit->data_delay[1] = 2;
457 tmit->data[2] = "pqr stuw xyz";
458 tmit->data_count = 3;
459
460 origin_cls.n = 0;
461 origin_cls.msgs_expected = 1;
462
463 tmit->mem_tmit = GNUNET_MULTICAST_member_to_origin (member, 1,
464 tmit_notify, tmit);
465}
466
467
468static void
469member_recv_message (void *cls,
470 const struct GNUNET_MULTICAST_MessageHeader *msg)
471{
472 struct MemberClosure *mcls = cls;
473
474 // Test 5 starts here after message has been received from origin
475 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
476 "Test #%u: member_recv_message() %u/%u\n",
477 test,
478 (unsigned int) (mcls->n + 1),
479 mcls->msgs_expected);
480 if (++mcls->n != mcls->msgs_expected)
481 return;
482
483 // FIXME: check message content
484
485 switch (test)
486 {
487 case TEST_ORIGIN_TO_ALL:
488 test = TEST_ORIGIN_TO_ALL_RECV;
489 break;
490
491 case TEST_ORIGIN_TO_ALL_RECV:
492 // Test 6 starts here
493 member_to_origin ();
494 break;
495
496 case TEST_MEMBER_REPLAY_OK:
497 // Test 9 starts here
498 GNUNET_assert (replay_fragment_id == GNUNET_ntohll (msg->fragment_id));
499 member_part ();
500 break;
501
502 default:
503 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
504 "Invalid test #%d in origin_recv_message()\n", test);
505 GNUNET_assert (0);
506 }
507}
508
509
510static void
511origin_recv_message (void *cls,
512 const struct GNUNET_MULTICAST_MessageHeader *msg)
513{
514 struct OriginClosure *ocls = cls;
515 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
516 "Test #%u: origin_recv_message() %u/%u\n",
517 test, ocls->n + 1, ocls->msgs_expected);
518 if (++ocls->n != ocls->msgs_expected)
519 return;
520
521 // FIXME: check message content
522
523 switch (test)
524 {
525 case TEST_ORIGIN_TO_ALL:
526 // Prepare to execute test 5
527 test = TEST_ORIGIN_TO_ALL_RECV;
528 break;
529
530 case TEST_ORIGIN_TO_ALL_RECV:
531 // Test 6 starts here
532 member_to_origin ();
533 break;
534
535 default:
536 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
537 "Invalid test #%d in origin_recv_message()\n", test);
538 GNUNET_assert (0);
539 }
540}
541
542
543static void
544origin_to_all ()
545{
546 test = TEST_ORIGIN_TO_ALL;
547 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
548 "Test #%u: origin_to_all()\n", test);
549
550 struct TransmitClosure *tmit = &tmit_cls;
551 *tmit = (struct TransmitClosure) {};
552 tmit->data[0] = "ABC DEF";
553 tmit->data[1] = GNUNET_malloc (GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD + 1);
554 uint16_t i;
555 for (i = 0; i < GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD; i++)
556 tmit->data[1][i] = (0 == i % 10000) ? '0' + i / 10000 : '_';
557 tmit->data[2] = "GHI JKL MNO";
558 tmit->data_delay[2] = 2;
559 tmit->data[3] = "PQR STUW XYZ";
560 tmit->data_count = 4;
561
562 origin_cls.n = member_cls.n = 0;
563 origin_cls.msgs_expected = member_cls.msgs_expected = tmit->data_count;
564
565 tmit->orig_tmit = GNUNET_MULTICAST_origin_to_all (origin, 1, 1,
566 tmit_notify, tmit);
567}
568
569
570static void
571member_recv_join_decision (void *cls,
572 int is_admitted,
573 const struct GNUNET_PeerIdentity *peer,
574 uint16_t relay_count,
575 const struct GNUNET_PeerIdentity *relays,
576 const struct GNUNET_MessageHeader *join_msg)
577{
578 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
579 "Test #%u: member_recv_join_decision() - is_admitted: %d\n",
580 test, is_admitted);
581
582 GNUNET_assert (join_msg->size == join_resp->size);
583 GNUNET_assert (join_msg->type == join_resp->type);
584 GNUNET_assert (0 == memcmp (join_msg, join_resp, ntohs (join_resp->size)));
585
586 switch (test)
587 {
588 case TEST_MEMBER_JOIN_REFUSE:
589 GNUNET_assert (0 == relay_count);
590 // Test 3 starts here
591 GNUNET_SCHEDULER_add_now (&schedule_member_part, NULL);
592 break;
593
594 case TEST_MEMBER_JOIN_ADMIT:
595 GNUNET_assert (1 == relay_count);
596 GNUNET_assert (0 == memcmp (relays, &this_peer, sizeof (this_peer)));
597 // Test 4 starts here
598 origin_to_all ();
599 break;
600
601 default:
602 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
603 "Invalid test #%d in member_recv_join_decision()\n", test);
604 GNUNET_assert (0);
605 }
606}
607
608/**
609 * Test: origin receives join request
610 */
611static void
612origin_recv_join_request (void *cls,
613 const struct GNUNET_CRYPTO_EcdsaPublicKey *mem_key,
614 const struct GNUNET_MessageHeader *join_msg,
615 struct GNUNET_MULTICAST_JoinHandle *jh)
616{
617 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
618 "Test #%u: origin_recv_join_request()\n", test);
619
620 GNUNET_assert (0 == memcmp (mem_key, &member_pub_key, sizeof (member_pub_key)));
621 GNUNET_assert (join_msg->size == join_req->size);
622 GNUNET_assert (join_msg->type == join_req->type);
623 GNUNET_assert (0 == memcmp (join_msg, join_req, ntohs (join_req->size)));
624
625 char data[] = "here's the decision";
626 uint8_t data_size = strlen (data) + 1;
627 join_resp = GNUNET_malloc (sizeof (join_resp) + data_size);
628 join_resp->size = htons (sizeof (join_resp) + data_size);
629 join_resp->type = htons (456);
630 GNUNET_memcpy (&join_resp[1], data, data_size);
631
632 switch (test)
633 {
634 case TEST_MEMBER_JOIN_REFUSE:
635 // Test 3 starts here
636 GNUNET_MULTICAST_join_decision (jh, GNUNET_NO, 0, NULL, join_resp);
637 break;
638
639 case TEST_MEMBER_JOIN_ADMIT:
640 // Test 3 is running
641 GNUNET_MULTICAST_join_decision (jh, GNUNET_YES, 1, &this_peer, join_resp);
642 break;
643
644 default:
645 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
646 "Invalid test #%d in origin_recv_join_request()\n", test);
647 GNUNET_assert (0);
648 break;
649 }
650}
651
652/**
653 * Test: member joins multicast group
654 */
655static void
656member_join (int t)
657{
658 test = t;
659 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
660 "Test #%u: member_join()\n", test);
661
662 member_key = GNUNET_CRYPTO_ecdsa_key_create ();
663 GNUNET_CRYPTO_ecdsa_key_get_public (member_key, &member_pub_key);
664
665 if (NULL != join_req)
666 GNUNET_free (join_req);
667
668 char data[] = "let me in!";
669 uint8_t data_size = strlen (data) + 1;
670 join_req = GNUNET_malloc (sizeof (join_req) + data_size);
671 join_req->size = htons (sizeof (join_req) + data_size);
672 join_req->type = htons (123);
673 GNUNET_memcpy (&join_req[1], data, data_size);
674
675 member = GNUNET_MULTICAST_member_join (cfg, &group_pub_key, member_key,
676 &this_peer, 1, &this_peer, join_req,
677 member_recv_join_request,
678 member_recv_join_decision,
679 member_recv_replay_frag,
680 member_recv_replay_msg,
681 member_recv_message,
682 &member_cls);
683}
684
685/**
686 * Test: Start a multicast group as origin
687 */
688static void
689origin_start ()
690{
691 test = TEST_ORIGIN_START;
692 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
693 "Test #%u: origin_start()\n", test);
694
695 group_key = GNUNET_CRYPTO_eddsa_key_create ();
696 GNUNET_CRYPTO_eddsa_key_get_public (group_key, &group_pub_key);
697
698 origin = GNUNET_MULTICAST_origin_start (cfg, group_key, 0,
699 origin_recv_join_request,
700 origin_recv_replay_frag,
701 origin_recv_replay_msg,
702 origin_recv_request,
703 origin_recv_message,
704 &origin_cls);
705 // Test 2 starts here
706 member_join (TEST_MEMBER_JOIN_REFUSE);
707}
708
709
710/**
711 * Main function of the test, run from scheduler.
712 *
713 * @param cls NULL
714 * @param cfg configuration we use (also to connect to Multicast service)
715 * @param peer handle to access more of the peer (not used)
716 */
717static void
718#if DEBUG_TEST_MULTICAST
719run (void *cls,
720 char *const *args,
721 const char *cfgfile,
722 const struct GNUNET_CONFIGURATION_Handle *c)
723#else
724run (void *cls,
725 const struct GNUNET_CONFIGURATION_Handle *c,
726 struct GNUNET_TESTING_Peer *peer)
727#endif
728{
729 cfg = c;
730 end_badly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
731 &end_badly, NULL);
732 GNUNET_CRYPTO_get_peer_identity (cfg, &this_peer);
733
734 // Test 1 starts here
735 origin_start ();
736}
737
738
739int
740main (int argc, char *argv[])
741{
742 res = 1;
743#if DEBUG_TEST_MULTICAST
744 const struct GNUNET_GETOPT_CommandLineOption opts[] = {
745 GNUNET_GETOPT_OPTION_END
746 };
747 if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv, "test-multicast",
748 "test-multicast [options]",
749 opts, &run, NULL))
750 return 1;
751#else
752 if (0 != GNUNET_TESTING_peer_run ("test-multicast", "test_multicast.conf", &run, NULL))
753 return 1;
754#endif
755 return res;
756}
757
758/* end of test_multicast.c */
diff --git a/src/multicast/test_multicast_2peers.c b/src/multicast/test_multicast_2peers.c
new file mode 100644
index 000000000..009a0a11a
--- /dev/null
+++ b/src/multicast/test_multicast_2peers.c
@@ -0,0 +1,520 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013 GNUnet e.V.
4 *
5 * GNUnet is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License,
8 * or (at your 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 * Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file multicast/test_multicast_2peers.c
23 * @brief Tests for the Multicast API with two peers doing the ping
24 * pong test.
25 * @author xrs
26 */
27
28#include <inttypes.h>
29
30#include <gnunet/platform.h>
31#include <gnunet/gnunet_crypto_lib.h>
32#include <gnunet/gnunet_common.h>
33#include <gnunet/gnunet_util_lib.h>
34#include <gnunet/gnunet_testbed_service.h>
35#include "gnunet_multicast_service.h"
36
37#define NUM_PEERS 2
38
39static struct GNUNET_TESTBED_Operation *op0;
40static struct GNUNET_TESTBED_Operation *op1;
41static struct GNUNET_TESTBED_Operation *pi_op0;
42static struct GNUNET_TESTBED_Operation *pi_op1;
43
44static struct GNUNET_TESTBED_Peer **peers;
45const struct GNUNET_PeerIdentity *peer_id[2];
46
47static struct GNUNET_SCHEDULER_Task *timeout_tid;
48
49static struct GNUNET_MULTICAST_Origin *origin;
50static struct GNUNET_MULTICAST_Member *member;
51
52struct GNUNET_CRYPTO_EddsaPrivateKey *group_key;
53struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key;
54
55struct GNUNET_CRYPTO_EcdsaPrivateKey *member_key;
56struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
57
58/**
59 * Global result for testcase.
60 */
61static int result;
62
63
64/**
65 * Function run on CTRL-C or shutdown (i.e. success/timeout/etc.).
66 * Cleans up.
67 */
68static void
69shutdown_task (void *cls)
70{
71 if (NULL != op0)
72 {
73 GNUNET_TESTBED_operation_done (op0);
74 op0 = NULL;
75 }
76 if (NULL != op1)
77 {
78 GNUNET_TESTBED_operation_done (op1);
79 op1 = NULL;
80 }
81 if (NULL != pi_op0)
82 {
83 GNUNET_TESTBED_operation_done (pi_op0);
84 pi_op0 = NULL;
85 }
86 if (NULL != pi_op1)
87 {
88 GNUNET_TESTBED_operation_done (pi_op1);
89 pi_op1 = NULL;
90 }
91 if (NULL != timeout_tid)
92 {
93 GNUNET_SCHEDULER_cancel (timeout_tid);
94 timeout_tid = NULL;
95 }
96}
97
98
99static void
100timeout_task (void *cls)
101{
102 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
103 "Timeout!\n");
104 result = GNUNET_SYSERR;
105 GNUNET_SCHEDULER_shutdown ();
106}
107
108
109static void
110member_join_request (void *cls,
111 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
112 const struct GNUNET_MessageHeader *join_msg,
113 struct GNUNET_MULTICAST_JoinHandle *jh)
114{
115 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
116 "Member sent a join request.\n");
117
118}
119
120
121static int
122notify (void *cls,
123 size_t *data_size,
124 void *data)
125{
126
127 char text[] = "ping";
128 *data_size = strlen(text)+1;
129 GNUNET_memcpy(data, text, *data_size);
130
131 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
132 "Member sents message to origin: %s\n", text);
133
134 return GNUNET_YES;
135}
136
137
138static void
139member_join_decision (void *cls,
140 int is_admitted,
141 const struct GNUNET_PeerIdentity *peer,
142 uint16_t relay_count,
143 const struct GNUNET_PeerIdentity *relays,
144 const struct GNUNET_MessageHeader *join_msg)
145{
146 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
147 "Member received a decision from origin: %s\n",
148 (GNUNET_YES == is_admitted)
149 ? "accepted"
150 : "rejected");
151
152 if (GNUNET_YES == is_admitted)
153 {
154 struct GNUNET_MULTICAST_MemberTransmitHandle *req;
155
156 // FIXME: move to MQ-style API!
157 req = GNUNET_MULTICAST_member_to_origin (member,
158 0,
159 &notify,
160 NULL);
161 }
162}
163
164
165static void
166member_message (void *cls,
167 const struct GNUNET_MULTICAST_MessageHeader *msg)
168{
169 if (0 != strncmp ("pong", (char *)&msg[1], 4))
170 {
171 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "member did not receive pong\n");
172 result = GNUNET_SYSERR;
173 GNUNET_SCHEDULER_shutdown ();
174 }
175
176 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
177 "member receives: %s\n", (char *)&msg[1]);
178
179 // Testcase ends here.
180 result = GNUNET_YES;
181 GNUNET_SCHEDULER_shutdown ();
182}
183
184
185static void
186origin_join_request (void *cls,
187 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
188 const struct GNUNET_MessageHeader *join_msg,
189 struct GNUNET_MULTICAST_JoinHandle *jh)
190{
191 struct GNUNET_MessageHeader *join_resp;
192
193 uint8_t data_size = ntohs (join_msg->size);
194
195 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
196 "origin got a join request...\n");
197 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
198 "origin receives: '%s'\n", (char *)&join_msg[1]);
199
200 const char data[] = "Come in!";
201 data_size = strlen (data) + 1;
202 join_resp = GNUNET_malloc (sizeof (join_resp) + data_size);
203 join_resp->size = htons (sizeof (join_resp) + data_size);
204 join_resp->type = htons (123);
205 GNUNET_memcpy (&join_resp[1], data, data_size);
206
207 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
208 "origin sends: '%s'\n", data);
209
210 GNUNET_MULTICAST_join_decision (jh,
211 GNUNET_YES,
212 0,
213 NULL,
214 join_resp);
215 GNUNET_free (join_resp);
216 result = GNUNET_OK;
217}
218
219
220int
221origin_notify (void *cls,
222 size_t *data_size,
223 void *data)
224{
225 char text[] = "pong";
226
227 *data_size = strlen(text)+1;
228 GNUNET_memcpy (data,
229 text,
230 *data_size);
231
232 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin sends (to all): %s\n", text);
233
234 return GNUNET_YES;
235}
236
237
238static void
239origin_request (void *cls,
240 const struct GNUNET_MULTICAST_RequestHeader *req)
241{
242 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin receives: %s\n", (char *)&req[1]);
243
244 if (0 != strncmp ("ping", (char *)&req[1], 4))
245 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "origin didn't reveice a correct request");
246
247 GNUNET_MULTICAST_origin_to_all (origin,
248 0,
249 0,
250 origin_notify,
251 NULL);
252}
253
254
255static void
256origin_message (void *cls,
257 const struct GNUNET_MULTICAST_MessageHeader *msg)
258{
259 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin message msg\n");
260}
261
262
263static void
264service_connect1 (void *cls,
265 struct GNUNET_TESTBED_Operation *op,
266 void *ca_result,
267 const char *emsg)
268{
269 member = ca_result;
270
271 if (NULL == member)
272 {
273 result = GNUNET_SYSERR;
274 GNUNET_SCHEDULER_shutdown ();
275 }
276 else
277 {
278 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to multicast service of member\n");
279 }
280}
281
282
283static void
284multicast_da1 (void *cls,
285 void * op_result)
286{
287 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
288 "Member parting from multicast group\n");
289
290 GNUNET_MULTICAST_member_part (member, NULL, NULL);
291}
292
293
294static void *
295multicast_ca1 (void *cls,
296 const struct GNUNET_CONFIGURATION_Handle *cfg)
297{
298 struct GNUNET_MessageHeader *join_msg;
299 void *ret;
300
301 // Get members keys
302 member_key = GNUNET_CRYPTO_ecdsa_key_create ();
303 GNUNET_CRYPTO_ecdsa_key_get_public (member_key, &member_pub_key);
304
305 char data[] = "Hi, can I enter?";
306 uint8_t data_size = strlen (data) + 1;
307 join_msg = GNUNET_malloc (sizeof (join_msg) + data_size);
308 join_msg->size = htons (sizeof (join_msg) + data_size);
309 join_msg->type = htons (123);
310 GNUNET_memcpy (&join_msg[1], data, data_size);
311
312 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
313 "Members tries to join multicast group\n");
314
315 ret = GNUNET_MULTICAST_member_join (cfg,
316 &group_pub_key,
317 member_key,
318 peer_id[0],
319 0,
320 NULL,
321 join_msg, /* join message */
322 member_join_request,
323 member_join_decision,
324 NULL, /* no test for member_replay_frag */
325 NULL, /* no test for member_replay_msg */
326 member_message,
327 NULL);
328 GNUNET_free (join_msg);
329 return ret;
330}
331
332
333static void
334peer_information_cb (void *cls,
335 struct GNUNET_TESTBED_Operation *op,
336 const struct GNUNET_TESTBED_PeerInformation *pinfo,
337 const char *emsg)
338{
339 int i = (int) (long) cls;
340
341 if (NULL == pinfo)
342 {
343 result = GNUNET_SYSERR;
344 GNUNET_SCHEDULER_shutdown ();
345 }
346
347 peer_id[i] = pinfo->result.id;
348
349 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
350 "Got peer information of %s (%s)\n", (0==i)?"origin":"member" ,GNUNET_i2s(pinfo->result.id));
351
352 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
353 "Create member peer\n");
354
355 if (0 == i)
356 {
357 /* connect to multicast service of member */
358 op1 = GNUNET_TESTBED_service_connect (NULL, /* Closure for operation */
359 peers[1], /* The peer whose service to connect to */
360 "multicast", /* The name of the service */
361 service_connect1, /* callback to call after a handle to service
362 is opened */
363 NULL, /* closure for the above callback */
364 multicast_ca1, /* callback to call with peer's configuration;
365 this should open the needed service connection */
366 multicast_da1, /* callback to be called when closing the
367 opened service connection */
368 NULL); /* closure for the above two callbacks */
369 }
370}
371
372
373/**
374 * Test logic of peer "0" being origin starts here.
375 *
376 * @param cls closure, for the example: NULL
377 * @param op should be equal to "dht_op"
378 * @param ca_result result of the connect operation, the
379 * connection to the DHT service
380 * @param emsg error message, if testbed somehow failed to
381 * connect to the DHT.
382 */
383static void
384service_connect0 (void *cls,
385 struct GNUNET_TESTBED_Operation *op,
386 void *ca_result,
387 const char *emsg)
388{
389 origin = ca_result;
390
391 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
392 "Connected to multicast service of origin\n");
393
394 // Get GNUnet identity of origin
395 pi_op0 = GNUNET_TESTBED_peer_get_information (peers[0],
396 GNUNET_TESTBED_PIT_IDENTITY,
397 peer_information_cb,
398 (void *) 0);
399 // Get GNUnet identity of member
400 pi_op1 = GNUNET_TESTBED_peer_get_information (peers[1],
401 GNUNET_TESTBED_PIT_IDENTITY,
402 peer_information_cb,
403 (void *) 1);
404
405 /* Connection to service successful. Here we'd usually do something with
406 * the service. */
407 result = GNUNET_OK;
408 //GNUNET_SCHEDULER_shutdown (); /* Also kills the testbed */
409}
410
411
412
413/**
414 * Function run when service multicast has started and is providing us
415 * with a configuration file.
416 */
417static void *
418multicast_ca0 (void *cls,
419 const struct GNUNET_CONFIGURATION_Handle *cfg)
420{
421 group_key = GNUNET_CRYPTO_eddsa_key_create ();
422 GNUNET_CRYPTO_eddsa_key_get_public (group_key, &group_pub_key);
423
424 return GNUNET_MULTICAST_origin_start (cfg,
425 group_key,
426 0,
427 origin_join_request,
428 NULL, /* no test for origin_replay_frag */
429 NULL, /* no test for origin_replay_msg */
430 origin_request,
431 origin_message,
432 NULL);
433}
434
435static void
436multicast_da0 (void *cls,
437 void *op_result)
438{
439 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
440 "Origin closes multicast group\n");
441
442 GNUNET_MULTICAST_origin_stop (origin, NULL, NULL);
443}
444
445
446/**
447 * Main function inovked from TESTBED once all of the
448 * peers are up and running. This one then connects
449 * just to the multicast service of peer 0 and 1.
450 * Peer 0 is going to be origin.
451 * Peer 1 is going to be one member.
452 * Origin will start a multicast group and the member will try to join it.
453 * After that we execute some multicast test.
454 *
455 * @param cls closure
456 * @param h the run handle
457 * @param peers started peers for the test
458 * @param num_peers size of the 'peers' array
459 * @param links_succeeded number of links between peers that were created
460 * @param links_failed number of links testbed was unable to establish
461 */
462static void
463testbed_master (void *cls,
464 struct GNUNET_TESTBED_RunHandle *h,
465 unsigned int num_peers,
466 struct GNUNET_TESTBED_Peer **p,
467 unsigned int links_succeeded,
468 unsigned int links_failed)
469{
470 /* Testbed is ready with peers running and connected in a pre-defined overlay
471 topology (FIXME) */
472 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
473 "Connected to testbed_master()\n");
474
475 peers = p;
476
477 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
478 "Create origin peer\n");
479 op0 = GNUNET_TESTBED_service_connect (NULL, /* Closure for operation */
480 peers[0], /* The peer whose service to connect to */
481 "multicast", /* The name of the service */
482 service_connect0, /* callback to call after a handle to service
483 is opened */
484 NULL, /* closure for the above callback */
485 multicast_ca0, /* callback to call with peer's configuration;
486 this should open the needed service connection */
487 multicast_da0, /* callback to be called when closing the
488 opened service connection */
489 NULL); /* closure for the above two callbacks */
490
491 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); /* Schedule a new task on shutdown */
492
493 /* Schedule the shutdown task with a delay of a few Seconds */
494 timeout_tid = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 50),
495 &timeout_task, NULL);
496}
497
498
499int
500main (int argc, char *argv[])
501{
502 int ret;
503
504 result = GNUNET_SYSERR;
505 ret = GNUNET_TESTBED_test_run
506 ("test-multicast-2peers", /* test case name */
507 "test_multicast.conf", /* template configuration */
508 NUM_PEERS, /* number of peers to start */
509 0LL, /* Event mask - set to 0 for no event notifications */
510 NULL, /* Controller event callback */
511 NULL, /* Closure for controller event callback */
512 testbed_master, /* continuation callback to be called when testbed setup is complete */
513 NULL); /* Closure for the test_master callback */
514 if ( (GNUNET_OK != ret) || (GNUNET_OK != result) )
515 return 1;
516 return 0;
517}
518
519
520/* end of test_multicast_2peers.c */
diff --git a/src/multicast/test_multicast_multipeer.c b/src/multicast/test_multicast_multipeer.c
new file mode 100644
index 000000000..95f42f4dd
--- /dev/null
+++ b/src/multicast/test_multicast_multipeer.c
@@ -0,0 +1,643 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013 GNUnet e.V.
4 *
5 * GNUnet is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License,
8 * or (at your 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 * Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file multicast/test_multicast_multipeers.c
23 * @brief Tests for the Multicast API with multiple peers.
24 * @author xrs
25 */
26
27#include <inttypes.h>
28
29#include <gnunet/platform.h>
30#include <gnunet/gnunet_crypto_lib.h>
31#include <gnunet/gnunet_common.h>
32#include <gnunet/gnunet_util_lib.h>
33#include <gnunet/gnunet_testbed_service.h>
34#include "gnunet_multicast_service.h"
35
36#define PEERS_REQUESTED 12
37
38struct MulticastPeerContext
39{
40 int peer; /* peer number */
41 struct GNUNET_CRYPTO_EcdsaPrivateKey *key;
42 const struct GNUNET_PeerIdentity *id;
43 struct GNUNET_TESTBED_Operation *op; /* not yet in use */
44 struct GNUNET_TESTBED_Operation *pi_op; /* not yet in use */
45 int test_ok;
46};
47
48enum pingpong
49{
50 PING = 1,
51 PONG = 2
52};
53
54struct pingpong_msg
55{
56 int peer;
57 enum pingpong msg;
58};
59
60static void service_connect (void *cls,
61 struct GNUNET_TESTBED_Operation *op,
62 void *ca_result,
63 const char *emsg);
64
65static struct MulticastPeerContext **multicast_peers;
66static struct GNUNET_TESTBED_Peer **peers;
67
68static struct GNUNET_TESTBED_Operation *op[PEERS_REQUESTED];
69static struct GNUNET_TESTBED_Operation *pi_op[PEERS_REQUESTED];
70
71static struct GNUNET_MULTICAST_Origin *origin;
72static struct GNUNET_MULTICAST_Member *members[PEERS_REQUESTED]; /* first element always empty */
73
74static struct GNUNET_SCHEDULER_Task *timeout_tid;
75
76static struct GNUNET_CRYPTO_EddsaPrivateKey *group_key;
77static struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key;
78static struct GNUNET_HashCode group_pub_key_hash;
79
80/**
81 * Global result for testcase.
82 */
83static int result;
84
85/**
86 * Function run on CTRL-C or shutdown (i.e. success/timeout/etc.).
87 * Cleans up.
88 */
89static void
90shutdown_task (void *cls)
91{
92 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
93 "shutdown_task!\n");
94 for (int i=0;i<PEERS_REQUESTED;i++)
95 {
96 if (NULL != op[i])
97 {
98 GNUNET_TESTBED_operation_done(op[i]);
99 op[i] = NULL;
100 }
101 if (NULL != pi_op[i])
102 {
103 GNUNET_TESTBED_operation_done (pi_op[i]);
104 pi_op[i] = NULL;
105 }
106 }
107
108 if (NULL != multicast_peers)
109 {
110 for (int i=0; i < PEERS_REQUESTED; i++)
111 {
112 GNUNET_free_non_null (multicast_peers[i]->key);
113 GNUNET_free (multicast_peers[i]);
114 multicast_peers[i] = NULL;
115 }
116 GNUNET_free (multicast_peers);
117 multicast_peers = NULL;
118 }
119
120 if (NULL != timeout_tid)
121 {
122 GNUNET_SCHEDULER_cancel (timeout_tid);
123 timeout_tid = NULL;
124 }
125}
126
127
128static void
129timeout_task (void *cls)
130{
131 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
132 "Timeout!\n");
133 result = GNUNET_SYSERR;
134 GNUNET_SCHEDULER_shutdown ();
135}
136
137
138static void
139member_join_request (void *cls,
140 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
141 const struct GNUNET_MessageHeader *join_msg,
142 struct GNUNET_MULTICAST_JoinHandle *jh)
143{
144 struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
145 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
146 "Peer #%u (%s) sent a join request.\n",
147 mc_peer->peer,
148 GNUNET_i2s (multicast_peers[mc_peer->peer]->id));
149}
150
151
152static int
153notify (void *cls,
154 size_t *data_size,
155 void *data)
156{
157 struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
158
159 struct pingpong_msg *pp_msg = GNUNET_new (struct pingpong_msg);
160 pp_msg->peer = mc_peer->peer;
161 pp_msg->msg = PING;
162
163 *data_size = sizeof (struct pingpong_msg);
164 GNUNET_memcpy(data, pp_msg, *data_size);
165 GNUNET_free (pp_msg);
166
167 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
168 "Peer #%u sents ping to origin\n", mc_peer->peer);
169
170 return GNUNET_YES;
171}
172
173
174static void
175member_join_decision (void *cls,
176 int is_admitted,
177 const struct GNUNET_PeerIdentity *peer,
178 uint16_t relay_count,
179 const struct GNUNET_PeerIdentity *relays,
180 const struct GNUNET_MessageHeader *join_msg)
181{
182 struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
183
184 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
185 "Peer #%u (%s) received a decision from origin: %s\n",
186 mc_peer->peer,
187 GNUNET_i2s (multicast_peers[mc_peer->peer]->id),
188 (GNUNET_YES == is_admitted)?"accepted":"rejected");
189
190 if (GNUNET_YES == is_admitted)
191 {
192 GNUNET_MULTICAST_member_to_origin (members[mc_peer->peer],
193 0,
194 notify,
195 cls);
196
197 }
198}
199
200
201static void
202member_replay_frag ()
203{
204 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
205 "member replay frag...\n");
206}
207
208
209static void
210member_replay_msg ()
211{
212 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
213 "member replay msg...\n");
214}
215
216
217static void
218origin_disconnected_cb (void *cls)
219{
220 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
221 "Origin disconnected. Shutting down.\n");
222 result = GNUNET_YES;
223 GNUNET_SCHEDULER_shutdown ();
224}
225
226
227static void
228member_disconnected_cb (void *cls)
229{
230 for (int i = 1; i < PEERS_REQUESTED; ++i)
231 if (GNUNET_NO == multicast_peers[i]->test_ok)
232 return;
233 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
234 "All member disconnected. Stopping origin.\n");
235 GNUNET_MULTICAST_origin_stop (origin, origin_disconnected_cb, cls);
236}
237
238
239static void
240member_message (void *cls,
241 const struct GNUNET_MULTICAST_MessageHeader *msg)
242{
243 struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
244 struct pingpong_msg *pp_msg = (struct pingpong_msg*) &(msg[1]);
245
246 if (PONG == pp_msg->msg && mc_peer->peer == pp_msg->peer)
247 {
248 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
249 "peer #%i (%s) receives a pong\n",
250 mc_peer->peer,
251 GNUNET_i2s (multicast_peers[mc_peer->peer]->id));
252 mc_peer->test_ok = GNUNET_OK;
253 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
254 "peer #%u (%s) parting from multicast group\n",
255 mc_peer->peer,
256 GNUNET_i2s (multicast_peers[mc_peer->peer]->id));
257
258 GNUNET_MULTICAST_member_part (members[mc_peer->peer], member_disconnected_cb, cls);
259 }
260}
261
262
263static void
264origin_join_request (void *cls,
265 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
266 const struct GNUNET_MessageHeader *join_msg,
267 struct GNUNET_MULTICAST_JoinHandle *jh)
268{
269 struct GNUNET_MessageHeader *join_resp;
270
271 uint8_t data_size = ntohs (join_msg->size);
272
273 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
274 "origin got a join request...\n");
275 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
276 "origin receives: '%s'\n", (char *)&join_msg[1]);
277
278 char data[] = "Come in!";
279 data_size = strlen (data) + 1;
280 join_resp = GNUNET_malloc (sizeof (join_resp) + data_size);
281 join_resp->size = htons (sizeof (join_resp) + data_size);
282 join_resp->type = htons (123);
283 GNUNET_memcpy (&join_resp[1], data, data_size);
284
285 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
286 "origin sends: '%s'\n", data);
287
288 GNUNET_MULTICAST_join_decision (jh,
289 GNUNET_YES,
290 0,
291 NULL,
292 join_resp);
293
294 result = GNUNET_OK;
295}
296
297
298static void
299origin_replay_frag (void *cls,
300 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
301 uint64_t fragment_id,
302 uint64_t flags,
303 struct GNUNET_MULTICAST_ReplayHandle *rh)
304{
305 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin replay fraq msg\n");
306}
307
308
309static void
310origin_replay_msg (void *cls,
311 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
312 uint64_t message_id,
313 uint64_t fragment_offset,
314 uint64_t flags,
315 struct GNUNET_MULTICAST_ReplayHandle *rh)
316{
317
318 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin replay msg\n");
319}
320
321
322static int
323origin_notify (void *cls,
324 size_t *data_size,
325 void *data)
326{
327 struct pingpong_msg *rcv_pp_msg = (struct pingpong_msg*)cls;
328 struct pingpong_msg *pp_msg = GNUNET_new (struct pingpong_msg);
329
330 pp_msg->peer = rcv_pp_msg->peer;
331 pp_msg->msg = PONG;
332 *data_size = sizeof (struct pingpong_msg);
333 GNUNET_memcpy(data, pp_msg, *data_size);
334 GNUNET_free (pp_msg);
335
336 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin sends pong\n");
337
338 return GNUNET_YES;
339}
340
341
342static void
343origin_request (void *cls,
344 const struct GNUNET_MULTICAST_RequestHeader *req)
345{
346 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin receives a msg\n");
347
348 req++;
349 struct pingpong_msg *pp_msg = (struct pingpong_msg *) req;
350
351 if (1 != pp_msg->msg) {
352 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "origin didn't reveice a correct request");
353 }
354
355 GNUNET_MULTICAST_origin_to_all (origin,
356 0,
357 0,
358 origin_notify,
359 pp_msg);
360}
361
362
363static void
364origin_message (void *cls,
365 const struct GNUNET_MULTICAST_MessageHeader *msg)
366{
367 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin message msg\n");
368}
369
370
371static void
372multicast_disconnect (void *cls,
373 void *op_result)
374{
375
376}
377
378
379static void *
380multicast_connect (void *cls,
381 const struct GNUNET_CONFIGURATION_Handle *cfg)
382{
383 struct MulticastPeerContext *multicast_peer = cls;
384 struct GNUNET_MessageHeader *join_msg;
385 char data[64];
386
387 if (0 == multicast_peer->peer)
388 {
389 group_key = GNUNET_CRYPTO_eddsa_key_create ();
390 GNUNET_CRYPTO_eddsa_key_get_public (group_key, &group_pub_key);
391
392 GNUNET_CRYPTO_hash (&group_pub_key, sizeof (group_pub_key), &group_pub_key_hash);
393 origin = GNUNET_MULTICAST_origin_start (cfg,
394 group_key,
395 0,
396 origin_join_request,
397 origin_replay_frag,
398 origin_replay_msg,
399 origin_request,
400 origin_message,
401 cls);
402 if (NULL == origin)
403 {
404 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
405 "Peer #%u could not create a multicast group",
406 multicast_peer->peer);
407 return NULL;
408 }
409 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
410 "Peer #%u connected as origin to group %s\n",
411 multicast_peer->peer,
412 GNUNET_h2s (&group_pub_key_hash));
413 return origin;
414 }
415 else
416 {
417 multicast_peer->key = GNUNET_CRYPTO_ecdsa_key_create ();
418
419 sprintf(data, "Hi, I am peer #%u (%s). Can I enter?",
420 multicast_peer->peer,
421 GNUNET_i2s (multicast_peers[multicast_peer->peer]->id));
422 uint8_t data_size = strlen (data) + 1;
423 join_msg = GNUNET_malloc (sizeof (join_msg) + data_size);
424 join_msg->size = htons (sizeof (join_msg) + data_size);
425 join_msg->type = htons (123);
426 GNUNET_memcpy (&join_msg[1], data, data_size);
427
428 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
429 "Peer #%u (%s) tries to join multicast group %s\n",
430 multicast_peer->peer,
431 GNUNET_i2s (multicast_peers[multicast_peer->peer]->id),
432 GNUNET_h2s (&group_pub_key_hash));
433
434 members[multicast_peer->peer] =
435 GNUNET_MULTICAST_member_join (cfg,
436 &group_pub_key,
437 multicast_peer->key,
438 multicast_peers[0]->id,
439 0,
440 NULL,
441 join_msg, /* join message */
442 member_join_request,
443 member_join_decision,
444 member_replay_frag,
445 member_replay_msg,
446 member_message,
447 cls);
448 return members[multicast_peer->peer];
449 }
450}
451
452
453static void
454peer_information_cb (void *cls,
455 struct GNUNET_TESTBED_Operation *operation,
456 const struct GNUNET_TESTBED_PeerInformation *pinfo,
457 const char *emsg)
458{
459 struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
460
461 if (NULL == pinfo) {
462 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "got no peer information\n");
463 result = GNUNET_SYSERR;
464 GNUNET_SCHEDULER_shutdown ();
465 }
466
467 multicast_peers[mc_peer->peer]->id = pinfo->result.id;
468
469 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
470 "Got peer information of %s (%s)\n",
471 (0 == mc_peer->peer)? "origin" : "member",
472 GNUNET_i2s (pinfo->result.id));
473
474 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
475 "Create peer #%u (%s)\n",
476 mc_peer->peer,
477 GNUNET_i2s (multicast_peers[mc_peer->peer]->id));
478
479 if (0 != mc_peer->peer)
480 {
481 /* connect to multicast service of members */
482 op[mc_peer->peer] =
483 GNUNET_TESTBED_service_connect (/* Closure for operation */
484 NULL,
485 /* The peer whose service to connect to */
486 peers[mc_peer->peer],
487 /* The name of the service */
488 "multicast",
489 /* called after a handle to service is opened */
490 service_connect,
491 /* closure for the above callback */
492 cls,
493 /* called when opening the service connection */
494 multicast_connect,
495 /* called when closing the service connection */
496 multicast_disconnect,
497 /* closure for the above two callbacks */
498 cls);
499 }
500}
501
502
503static void
504service_connect (void *cls,
505 struct GNUNET_TESTBED_Operation *op,
506 void *ca_result,
507 const char *emsg)
508{
509 struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
510
511 if (NULL == ca_result)
512 {
513 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
514 "Connection adapter not created for peer #%u (%s)\n",
515 mc_peer->peer,
516 GNUNET_i2s (multicast_peers[mc_peer->peer]->id));
517
518 result = GNUNET_SYSERR;
519 GNUNET_SCHEDULER_shutdown();
520 }
521
522 if (0 == mc_peer->peer)
523 {
524 // Get GNUnet identity of members
525 for (int i = 0; i<PEERS_REQUESTED; i++)
526 {
527 pi_op[i] = GNUNET_TESTBED_peer_get_information (peers[i],
528 GNUNET_TESTBED_PIT_IDENTITY,
529 peer_information_cb,
530 multicast_peers[i]);
531 }
532 }
533}
534
535
536
537/**
538 * Main function inovked from TESTBED once all of the
539 * peers are up and running. This one then connects
540 * just to the multicast service of peer 0 and 1.
541 * Peer 0 is going to be origin.
542 * Peer 1 is going to be one member.
543 * Origin will start a multicast group and the member will try to join it.
544 * After that we execute some multicast test.
545 *
546 * @param cls closure
547 * @param h the run handle
548 * @param peers started peers for the test
549 * @param PEERS_REQUESTED size of the 'peers' array
550 * @param links_succeeded number of links between peers that were created
551 * @param links_failed number of links testbed was unable to establish
552 */
553static void
554testbed_master (void *cls,
555 struct GNUNET_TESTBED_RunHandle *h,
556 unsigned int num_peers,
557 struct GNUNET_TESTBED_Peer **p,
558 unsigned int links_succeeded,
559 unsigned int links_failed)
560{
561 /* Testbed is ready with peers running and connected in a pre-defined overlay
562 topology (FIXME) */
563 peers = p;
564 multicast_peers = GNUNET_new_array (PEERS_REQUESTED, struct MulticastPeerContext*);
565
566 // Create test contexts for members
567 for (int i = 0; i<PEERS_REQUESTED; i++)
568 {
569 multicast_peers[i] = GNUNET_new (struct MulticastPeerContext);
570 multicast_peers[i]->peer = i;
571 multicast_peers[i]->test_ok = GNUNET_NO;
572 }
573 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
574 "Create origin peer\n");
575 op[0] =
576 GNUNET_TESTBED_service_connect (/* Closure for operation */
577 NULL,
578 /* The peer whose service to connect to */
579 peers[0],
580 /* The name of the service */
581 "multicast",
582 /* called after a handle to service is opened */
583 service_connect,
584 /* closure for the above callback */
585 multicast_peers[0],
586 /* called when opening the service connection */
587 multicast_connect,
588 /* called when closing the service connection */
589 multicast_disconnect,
590 /* closure for the above two callbacks */
591 multicast_peers[0]);
592 /* Schedule a new task on shutdown */
593 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
594 /* Schedule the shutdown task with a delay of a few Seconds */
595 timeout_tid =
596 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
597 (GNUNET_TIME_UNIT_SECONDS, 400),
598 &timeout_task,
599 NULL);
600}
601
602
603int
604main (int argc, char *argv[])
605{
606 int ret;
607 char const *config_file;
608
609 if (strstr (argv[0], "_line") != NULL)
610 {
611 config_file = "test_multicast_line.conf";
612 }
613 else if (strstr(argv[0], "_star") != NULL)
614 {
615 config_file = "test_multicast_star.conf";
616 }
617 else
618 {
619 config_file = "test_multicast_star.conf";
620 }
621
622 result = GNUNET_SYSERR;
623 ret =
624 GNUNET_TESTBED_test_run ("test-multicast-multipeer",
625 config_file,
626 /* number of peers to start */
627 PEERS_REQUESTED,
628 /* Event mask - set to 0 for no event notifications */
629 0LL,
630 /* Controller event callback */
631 NULL,
632 /* Closure for controller event callback */
633 NULL,
634 /* called when testbed setup is complete */
635 testbed_master,
636 /* Closure for the test_master callback */
637 NULL);
638 if ( (GNUNET_OK != ret) || (GNUNET_OK != result) )
639 return 1;
640 return 0;
641}
642
643/* end of test_multicast_multipeer.c */