diff options
Diffstat (limited to 'src/multicast/gnunet-service-multicast.c')
-rw-r--r-- | src/multicast/gnunet-service-multicast.c | 2234 |
1 files changed, 0 insertions, 2234 deletions
diff --git a/src/multicast/gnunet-service-multicast.c b/src/multicast/gnunet-service-multicast.c deleted file mode 100644 index 18c366118..000000000 --- a/src/multicast/gnunet-service-multicast.c +++ /dev/null | |||
@@ -1,2234 +0,0 @@ | |||
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 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_signatures.h" | ||
29 | #include "gnunet_applications.h" | ||
30 | #include "gnunet_statistics_service.h" | ||
31 | #include "gnunet_cadet_service.h" | ||
32 | #include "gnunet_multicast_service.h" | ||
33 | #include "multicast.h" | ||
34 | |||
35 | /** | ||
36 | * Handle to our current configuration. | ||
37 | */ | ||
38 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
39 | |||
40 | /** | ||
41 | * Service handle. | ||
42 | */ | ||
43 | static struct GNUNET_SERVICE_Handle *service; | ||
44 | |||
45 | /** | ||
46 | * CADET handle. | ||
47 | */ | ||
48 | static struct GNUNET_CADET_Handle *cadet; | ||
49 | |||
50 | /** | ||
51 | * Identity of this peer. | ||
52 | */ | ||
53 | static struct GNUNET_PeerIdentity this_peer; | ||
54 | |||
55 | /** | ||
56 | * Handle to the statistics service. | ||
57 | */ | ||
58 | static struct GNUNET_STATISTICS_Handle *stats; | ||
59 | |||
60 | /** | ||
61 | * All connected origin clients. | ||
62 | * Group's pub_key_hash -> struct Origin * (uniq) | ||
63 | */ | ||
64 | static struct GNUNET_CONTAINER_MultiHashMap *origins; | ||
65 | |||
66 | /** | ||
67 | * All connected member clients. | ||
68 | * Group's pub_key_hash -> struct Member * (multi) | ||
69 | */ | ||
70 | static struct GNUNET_CONTAINER_MultiHashMap *members; | ||
71 | |||
72 | /** | ||
73 | * Connected member clients per group. | ||
74 | * Group's pub_key_hash -> Member's pub_key_hash (uniq) -> struct Member * (uniq) | ||
75 | */ | ||
76 | static struct GNUNET_CONTAINER_MultiHashMap *group_members; | ||
77 | |||
78 | /** | ||
79 | * Incoming CADET channels with connected children in the tree. | ||
80 | * Group's pub_key_hash -> struct Channel * (multi) | ||
81 | */ | ||
82 | static struct GNUNET_CONTAINER_MultiHashMap *channels_in; | ||
83 | |||
84 | /** | ||
85 | * Outgoing CADET channels connecting to parents in the tree. | ||
86 | * Group's pub_key_hash -> struct Channel * (multi) | ||
87 | */ | ||
88 | static struct GNUNET_CONTAINER_MultiHashMap *channels_out; | ||
89 | |||
90 | /** | ||
91 | * Incoming replay requests from CADET. | ||
92 | * Group's pub_key_hash -> | ||
93 | * H(fragment_id, message_id, fragment_offset, flags) -> struct Channel * | ||
94 | */ | ||
95 | static struct GNUNET_CONTAINER_MultiHashMap *replay_req_cadet; | ||
96 | |||
97 | /** | ||
98 | * Incoming replay requests from clients. | ||
99 | * Group's pub_key_hash -> | ||
100 | * H(fragment_id, message_id, fragment_offset, flags) -> struct GNUNET_SERVICE_Client * | ||
101 | */ | ||
102 | static struct GNUNET_CONTAINER_MultiHashMap *replay_req_client; | ||
103 | |||
104 | |||
105 | /** | ||
106 | * Join status of a remote peer. | ||
107 | */ | ||
108 | enum JoinStatus | ||
109 | { | ||
110 | JOIN_REFUSED = -1, | ||
111 | JOIN_NOT_ASKED = 0, | ||
112 | JOIN_WAITING = 1, | ||
113 | JOIN_ADMITTED = 2, | ||
114 | }; | ||
115 | |||
116 | enum ChannelDirection | ||
117 | { | ||
118 | DIR_INCOMING = 0, | ||
119 | DIR_OUTGOING = 1, | ||
120 | }; | ||
121 | |||
122 | |||
123 | /** | ||
124 | * Context for a CADET channel. | ||
125 | */ | ||
126 | struct Channel | ||
127 | { | ||
128 | /** | ||
129 | * Group the channel belongs to. | ||
130 | * | ||
131 | * Only set for outgoing channels. | ||
132 | */ | ||
133 | struct Group *group; | ||
134 | |||
135 | /** | ||
136 | * CADET channel. | ||
137 | */ | ||
138 | struct GNUNET_CADET_Channel *channel; | ||
139 | |||
140 | // FIXME: not used | ||
141 | /** | ||
142 | * CADET transmission handle. | ||
143 | */ | ||
144 | struct GNUNET_CADET_TransmitHandle *tmit_handle; | ||
145 | |||
146 | /** | ||
147 | * Public key of the target group. | ||
148 | */ | ||
149 | struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key; | ||
150 | |||
151 | /** | ||
152 | * Hash of @a group_pub_key. | ||
153 | */ | ||
154 | struct GNUNET_HashCode group_pub_hash; | ||
155 | |||
156 | /** | ||
157 | * Public key of the joining member. | ||
158 | */ | ||
159 | struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key; | ||
160 | |||
161 | /** | ||
162 | * Remote peer identity. | ||
163 | */ | ||
164 | struct GNUNET_PeerIdentity peer; | ||
165 | |||
166 | /** | ||
167 | * Current window size, set by cadet_notify_window_change() | ||
168 | */ | ||
169 | int32_t window_size; | ||
170 | |||
171 | /** | ||
172 | * Is the connection established? | ||
173 | */ | ||
174 | int8_t is_connected; | ||
175 | |||
176 | /** | ||
177 | * Is the remote peer admitted to the group? | ||
178 | * @see enum JoinStatus | ||
179 | */ | ||
180 | int8_t join_status; | ||
181 | |||
182 | /** | ||
183 | * Number of messages waiting to be sent to CADET. | ||
184 | */ | ||
185 | uint8_t msgs_pending; | ||
186 | |||
187 | /** | ||
188 | * Channel direction. | ||
189 | * @see enum ChannelDirection | ||
190 | */ | ||
191 | uint8_t direction; | ||
192 | }; | ||
193 | |||
194 | |||
195 | /** | ||
196 | * List of connected clients. | ||
197 | */ | ||
198 | struct ClientList | ||
199 | { | ||
200 | struct ClientList *prev; | ||
201 | struct ClientList *next; | ||
202 | struct GNUNET_SERVICE_Client *client; | ||
203 | }; | ||
204 | |||
205 | |||
206 | /** | ||
207 | * Client context for an origin or member. | ||
208 | */ | ||
209 | struct Group | ||
210 | { | ||
211 | struct ClientList *clients_head; | ||
212 | struct ClientList *clients_tail; | ||
213 | |||
214 | /** | ||
215 | * Public key of the group. | ||
216 | */ | ||
217 | struct GNUNET_CRYPTO_EddsaPublicKey pub_key; | ||
218 | |||
219 | /** | ||
220 | * Hash of @a pub_key. | ||
221 | */ | ||
222 | struct GNUNET_HashCode pub_key_hash; | ||
223 | |||
224 | /** | ||
225 | * CADET port hash. | ||
226 | */ | ||
227 | struct GNUNET_HashCode cadet_port_hash; | ||
228 | |||
229 | /** | ||
230 | * Is the client disconnected? #GNUNET_YES or #GNUNET_NO | ||
231 | */ | ||
232 | uint8_t is_disconnected; | ||
233 | |||
234 | /** | ||
235 | * Is this an origin (#GNUNET_YES), or member (#GNUNET_NO)? | ||
236 | */ | ||
237 | uint8_t is_origin; | ||
238 | |||
239 | union { | ||
240 | struct Origin *origin; | ||
241 | struct Member *member; | ||
242 | }; | ||
243 | }; | ||
244 | |||
245 | |||
246 | /** | ||
247 | * Client context for a group's origin. | ||
248 | */ | ||
249 | struct Origin | ||
250 | { | ||
251 | struct Group group; | ||
252 | |||
253 | /** | ||
254 | * Private key of the group. | ||
255 | */ | ||
256 | struct GNUNET_CRYPTO_EddsaPrivateKey priv_key; | ||
257 | |||
258 | /** | ||
259 | * CADET port. | ||
260 | */ | ||
261 | struct GNUNET_CADET_Port *cadet_port; | ||
262 | |||
263 | /** | ||
264 | * Last message fragment ID sent to the group. | ||
265 | */ | ||
266 | uint64_t max_fragment_id; | ||
267 | }; | ||
268 | |||
269 | |||
270 | /** | ||
271 | * Client context for a group member. | ||
272 | */ | ||
273 | struct Member | ||
274 | { | ||
275 | struct Group group; | ||
276 | |||
277 | /** | ||
278 | * Private key of the member. | ||
279 | */ | ||
280 | struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key; | ||
281 | |||
282 | /** | ||
283 | * Public key of the member. | ||
284 | */ | ||
285 | struct GNUNET_CRYPTO_EcdsaPublicKey pub_key; | ||
286 | |||
287 | /** | ||
288 | * Hash of @a pub_key. | ||
289 | */ | ||
290 | struct GNUNET_HashCode pub_key_hash; | ||
291 | |||
292 | /** | ||
293 | * Join request sent to the origin / members. | ||
294 | */ | ||
295 | struct MulticastJoinRequestMessage *join_req; | ||
296 | |||
297 | /** | ||
298 | * Join decision sent in reply to our request. | ||
299 | * | ||
300 | * Only a positive decision is stored here, in case of a negative decision the | ||
301 | * client is disconnected. | ||
302 | */ | ||
303 | struct MulticastJoinDecisionMessageHeader *join_dcsn; | ||
304 | |||
305 | /** | ||
306 | * CADET channel to the origin. | ||
307 | */ | ||
308 | struct Channel *origin_channel; | ||
309 | |||
310 | /** | ||
311 | * Peer identity of origin. | ||
312 | */ | ||
313 | struct GNUNET_PeerIdentity origin; | ||
314 | |||
315 | /** | ||
316 | * Peer identity of relays (other members to connect). | ||
317 | */ | ||
318 | struct GNUNET_PeerIdentity *relays; | ||
319 | |||
320 | /** | ||
321 | * Last request fragment ID sent to the origin. | ||
322 | */ | ||
323 | uint64_t max_fragment_id; | ||
324 | |||
325 | /** | ||
326 | * Number of @a relays. | ||
327 | */ | ||
328 | uint32_t relay_count; | ||
329 | }; | ||
330 | |||
331 | |||
332 | /** | ||
333 | * Client context. | ||
334 | */ | ||
335 | struct Client { | ||
336 | struct GNUNET_SERVICE_Client *client; | ||
337 | struct Group *group; | ||
338 | }; | ||
339 | |||
340 | |||
341 | struct ReplayRequestKey | ||
342 | { | ||
343 | uint64_t fragment_id; | ||
344 | uint64_t message_id; | ||
345 | uint64_t fragment_offset; | ||
346 | uint64_t flags; | ||
347 | }; | ||
348 | |||
349 | |||
350 | static struct Channel * | ||
351 | cadet_channel_create (struct Group *grp, struct GNUNET_PeerIdentity *peer); | ||
352 | |||
353 | static void | ||
354 | cadet_channel_destroy (struct Channel *chn); | ||
355 | |||
356 | static void | ||
357 | client_send_join_decision (struct Member *mem, | ||
358 | const struct MulticastJoinDecisionMessageHeader *hdcsn); | ||
359 | |||
360 | |||
361 | /** | ||
362 | * Task run during shutdown. | ||
363 | * | ||
364 | * @param cls unused | ||
365 | */ | ||
366 | static void | ||
367 | shutdown_task (void *cls) | ||
368 | { | ||
369 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
370 | "shutting down\n"); | ||
371 | if (NULL != cadet) | ||
372 | { | ||
373 | GNUNET_CADET_disconnect (cadet); | ||
374 | cadet = NULL; | ||
375 | } | ||
376 | if (NULL != stats) | ||
377 | { | ||
378 | GNUNET_STATISTICS_destroy (stats, GNUNET_YES); | ||
379 | stats = NULL; | ||
380 | } | ||
381 | /* FIXME: do more clean up here */ | ||
382 | } | ||
383 | |||
384 | |||
385 | /** | ||
386 | * Clean up origin data structures after a client disconnected. | ||
387 | */ | ||
388 | static void | ||
389 | cleanup_origin (struct Origin *orig) | ||
390 | { | ||
391 | struct Group *grp = &orig->group; | ||
392 | GNUNET_CONTAINER_multihashmap_remove (origins, &grp->pub_key_hash, orig); | ||
393 | if (NULL != orig->cadet_port) | ||
394 | { | ||
395 | GNUNET_CADET_close_port (orig->cadet_port); | ||
396 | orig->cadet_port = NULL; | ||
397 | } | ||
398 | GNUNET_free (orig); | ||
399 | } | ||
400 | |||
401 | |||
402 | /** | ||
403 | * Clean up member data structures after a client disconnected. | ||
404 | */ | ||
405 | static void | ||
406 | cleanup_member (struct Member *mem) | ||
407 | { | ||
408 | struct Group *grp = &mem->group; | ||
409 | struct GNUNET_CONTAINER_MultiHashMap * | ||
410 | grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members, | ||
411 | &grp->pub_key_hash); | ||
412 | GNUNET_assert (NULL != grp_mem); | ||
413 | GNUNET_CONTAINER_multihashmap_remove (grp_mem, &mem->pub_key_hash, mem); | ||
414 | |||
415 | if (0 == GNUNET_CONTAINER_multihashmap_size (grp_mem)) | ||
416 | { | ||
417 | GNUNET_CONTAINER_multihashmap_remove (group_members, &grp->pub_key_hash, | ||
418 | grp_mem); | ||
419 | GNUNET_CONTAINER_multihashmap_destroy (grp_mem); | ||
420 | } | ||
421 | if (NULL != mem->join_dcsn) | ||
422 | { | ||
423 | GNUNET_free (mem->join_dcsn); | ||
424 | mem->join_dcsn = NULL; | ||
425 | } | ||
426 | if (NULL != mem->origin_channel) | ||
427 | { | ||
428 | GNUNET_CADET_channel_destroy (mem->origin_channel->channel); | ||
429 | mem->origin_channel = NULL; | ||
430 | } | ||
431 | GNUNET_CONTAINER_multihashmap_remove (members, &grp->pub_key_hash, mem); | ||
432 | GNUNET_free (mem); | ||
433 | } | ||
434 | |||
435 | |||
436 | /** | ||
437 | * Clean up group data structures after a client disconnected. | ||
438 | */ | ||
439 | static void | ||
440 | cleanup_group (struct Group *grp) | ||
441 | { | ||
442 | (GNUNET_YES == grp->is_origin) | ||
443 | ? cleanup_origin (grp->origin) | ||
444 | : cleanup_member (grp->member); | ||
445 | } | ||
446 | |||
447 | |||
448 | void | ||
449 | replay_key_hash (uint64_t fragment_id, uint64_t message_id, | ||
450 | uint64_t fragment_offset, uint64_t flags, | ||
451 | struct GNUNET_HashCode *key_hash) | ||
452 | { | ||
453 | struct ReplayRequestKey key = { | ||
454 | .fragment_id = fragment_id, | ||
455 | .message_id = message_id, | ||
456 | .fragment_offset = fragment_offset, | ||
457 | .flags = flags, | ||
458 | }; | ||
459 | GNUNET_CRYPTO_hash (&key, sizeof (key), key_hash); | ||
460 | } | ||
461 | |||
462 | |||
463 | /** | ||
464 | * Remove channel from replay request hashmap. | ||
465 | * | ||
466 | * @param chn | ||
467 | * Channel to remove. | ||
468 | * | ||
469 | * @return #GNUNET_YES if there are more entries to process, | ||
470 | * #GNUNET_NO when reached end of hashmap. | ||
471 | */ | ||
472 | static int | ||
473 | replay_req_remove_cadet (struct Channel *chn) | ||
474 | { | ||
475 | if (NULL == chn || NULL == chn->group) | ||
476 | return GNUNET_SYSERR; | ||
477 | |||
478 | struct GNUNET_CONTAINER_MultiHashMap * | ||
479 | grp_replay_req = GNUNET_CONTAINER_multihashmap_get (replay_req_cadet, | ||
480 | &chn->group->pub_key_hash); | ||
481 | if (NULL == grp_replay_req) | ||
482 | return GNUNET_NO; | ||
483 | |||
484 | struct GNUNET_CONTAINER_MultiHashMapIterator * | ||
485 | it = GNUNET_CONTAINER_multihashmap_iterator_create (grp_replay_req); | ||
486 | struct GNUNET_HashCode key; | ||
487 | const struct Channel *c; | ||
488 | while (GNUNET_YES | ||
489 | == GNUNET_CONTAINER_multihashmap_iterator_next (it, &key, | ||
490 | (const void **) &c)) | ||
491 | { | ||
492 | if (c == chn) | ||
493 | { | ||
494 | GNUNET_CONTAINER_multihashmap_remove (grp_replay_req, &key, chn); | ||
495 | GNUNET_CONTAINER_multihashmap_iterator_destroy (it); | ||
496 | return GNUNET_YES; | ||
497 | } | ||
498 | } | ||
499 | GNUNET_CONTAINER_multihashmap_iterator_destroy (it); | ||
500 | return GNUNET_NO; | ||
501 | } | ||
502 | |||
503 | |||
504 | /** | ||
505 | * Remove client from replay request hashmap. | ||
506 | * | ||
507 | * @param client | ||
508 | * Client to remove. | ||
509 | * | ||
510 | * @return #GNUNET_YES if there are more entries to process, | ||
511 | * #GNUNET_NO when reached end of hashmap. | ||
512 | */ | ||
513 | static int | ||
514 | replay_req_remove_client (struct Group *grp, struct GNUNET_SERVICE_Client *client) | ||
515 | { | ||
516 | struct GNUNET_CONTAINER_MultiHashMap * | ||
517 | grp_replay_req = GNUNET_CONTAINER_multihashmap_get (replay_req_client, | ||
518 | &grp->pub_key_hash); | ||
519 | if (NULL == grp_replay_req) | ||
520 | return GNUNET_NO; | ||
521 | |||
522 | struct GNUNET_CONTAINER_MultiHashMapIterator * | ||
523 | it = GNUNET_CONTAINER_multihashmap_iterator_create (grp_replay_req); | ||
524 | struct GNUNET_HashCode key; | ||
525 | const struct GNUNET_SERVICE_Client *c; | ||
526 | while (GNUNET_YES | ||
527 | == GNUNET_CONTAINER_multihashmap_iterator_next (it, &key, | ||
528 | (const void **) &c)) | ||
529 | { | ||
530 | if (c == client) | ||
531 | { | ||
532 | GNUNET_CONTAINER_multihashmap_remove (grp_replay_req, &key, client); | ||
533 | GNUNET_CONTAINER_multihashmap_iterator_destroy (it); | ||
534 | return GNUNET_YES; | ||
535 | } | ||
536 | } | ||
537 | GNUNET_CONTAINER_multihashmap_iterator_destroy (it); | ||
538 | return GNUNET_NO; | ||
539 | } | ||
540 | |||
541 | |||
542 | /** | ||
543 | * Send message to a client. | ||
544 | */ | ||
545 | static void | ||
546 | client_send (struct GNUNET_SERVICE_Client *client, | ||
547 | const struct GNUNET_MessageHeader *msg) | ||
548 | { | ||
549 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
550 | "%p Sending message to client.\n", client); | ||
551 | |||
552 | struct GNUNET_MQ_Envelope * | ||
553 | env = GNUNET_MQ_msg_copy (msg); | ||
554 | |||
555 | GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), | ||
556 | env); | ||
557 | } | ||
558 | |||
559 | |||
560 | /** | ||
561 | * Send message to all clients connected to the group. | ||
562 | */ | ||
563 | static void | ||
564 | client_send_group_keep_envelope (const struct Group *grp, | ||
565 | struct GNUNET_MQ_Envelope *env) | ||
566 | { | ||
567 | struct ClientList *cli = grp->clients_head; | ||
568 | |||
569 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
570 | "%p Sending message to all clients of the group.\n", | ||
571 | grp); | ||
572 | while (NULL != cli) | ||
573 | { | ||
574 | GNUNET_MQ_send_copy (GNUNET_SERVICE_client_get_mq (cli->client), | ||
575 | env); | ||
576 | cli = cli->next; | ||
577 | } | ||
578 | } | ||
579 | |||
580 | |||
581 | /** | ||
582 | * Send message to all clients connected to the group and | ||
583 | * takes care of freeing @env. | ||
584 | */ | ||
585 | static void | ||
586 | client_send_group (const struct Group *grp, | ||
587 | struct GNUNET_MQ_Envelope *env) | ||
588 | { | ||
589 | client_send_group_keep_envelope (grp, env); | ||
590 | GNUNET_MQ_discard (env); | ||
591 | } | ||
592 | |||
593 | |||
594 | /** | ||
595 | * Iterator callback for sending a message to origin clients. | ||
596 | */ | ||
597 | static int | ||
598 | client_send_origin_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash, | ||
599 | void *origin) | ||
600 | { | ||
601 | struct GNUNET_MQ_Envelope *env = cls; | ||
602 | struct Member *orig = origin; | ||
603 | |||
604 | client_send_group_keep_envelope (&orig->group, env); | ||
605 | return GNUNET_YES; | ||
606 | } | ||
607 | |||
608 | |||
609 | /** | ||
610 | * Iterator callback for sending a message to member clients. | ||
611 | */ | ||
612 | static int | ||
613 | client_send_member_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash, | ||
614 | void *member) | ||
615 | { | ||
616 | struct GNUNET_MQ_Envelope *env = cls; | ||
617 | struct Member *mem = member; | ||
618 | |||
619 | if (NULL != mem->join_dcsn) | ||
620 | { /* Only send message to admitted members */ | ||
621 | client_send_group_keep_envelope (&mem->group, env); | ||
622 | } | ||
623 | return GNUNET_YES; | ||
624 | } | ||
625 | |||
626 | |||
627 | /** | ||
628 | * Send message to all origin and member clients connected to the group. | ||
629 | * | ||
630 | * @param pub_key_hash | ||
631 | * H(key_pub) of the group. | ||
632 | * @param msg | ||
633 | * Message to send. | ||
634 | */ | ||
635 | static int | ||
636 | client_send_all (struct GNUNET_HashCode *pub_key_hash, | ||
637 | struct GNUNET_MQ_Envelope *env) | ||
638 | { | ||
639 | int n = 0; | ||
640 | n += GNUNET_CONTAINER_multihashmap_get_multiple (origins, pub_key_hash, | ||
641 | client_send_origin_cb, | ||
642 | (void *) env); | ||
643 | n += GNUNET_CONTAINER_multihashmap_get_multiple (members, pub_key_hash, | ||
644 | client_send_member_cb, | ||
645 | (void *) env); | ||
646 | GNUNET_MQ_discard (env); | ||
647 | return n; | ||
648 | } | ||
649 | |||
650 | |||
651 | /** | ||
652 | * Send message to a random origin client or a random member client. | ||
653 | * | ||
654 | * @param grp The group to send @a msg to. | ||
655 | * @param msg Message to send. | ||
656 | */ | ||
657 | static int | ||
658 | client_send_random (struct GNUNET_HashCode *pub_key_hash, | ||
659 | struct GNUNET_MQ_Envelope *env) | ||
660 | { | ||
661 | int n = 0; | ||
662 | n = GNUNET_CONTAINER_multihashmap_get_random (origins, client_send_origin_cb, | ||
663 | (void *) env); | ||
664 | if (n <= 0) | ||
665 | n = GNUNET_CONTAINER_multihashmap_get_random (members, client_send_member_cb, | ||
666 | (void *) env); | ||
667 | GNUNET_MQ_discard (env); | ||
668 | return n; | ||
669 | } | ||
670 | |||
671 | |||
672 | /** | ||
673 | * Send message to all origin clients connected to the group. | ||
674 | * | ||
675 | * @param pub_key_hash | ||
676 | * H(key_pub) of the group. | ||
677 | * @param msg | ||
678 | * Message to send. | ||
679 | */ | ||
680 | static int | ||
681 | client_send_origin (struct GNUNET_HashCode *pub_key_hash, | ||
682 | struct GNUNET_MQ_Envelope *env) | ||
683 | { | ||
684 | int n = 0; | ||
685 | n += GNUNET_CONTAINER_multihashmap_get_multiple (origins, pub_key_hash, | ||
686 | client_send_origin_cb, | ||
687 | (void *) env); | ||
688 | return n; | ||
689 | } | ||
690 | |||
691 | |||
692 | /** | ||
693 | * Send fragment acknowledgement to all clients of the channel. | ||
694 | * | ||
695 | * @param pub_key_hash | ||
696 | * H(key_pub) of the group. | ||
697 | */ | ||
698 | static void | ||
699 | client_send_ack (struct GNUNET_HashCode *pub_key_hash) | ||
700 | { | ||
701 | struct GNUNET_MQ_Envelope *env; | ||
702 | |||
703 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
704 | "Sending message ACK to client.\n"); | ||
705 | env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_MULTICAST_FRAGMENT_ACK); | ||
706 | client_send_all (pub_key_hash, env); | ||
707 | } | ||
708 | |||
709 | |||
710 | struct CadetTransmitClosure | ||
711 | { | ||
712 | struct Channel *chn; | ||
713 | const struct GNUNET_MessageHeader *msg; | ||
714 | }; | ||
715 | |||
716 | |||
717 | /** | ||
718 | * Send a message to a CADET channel. | ||
719 | * | ||
720 | * @param chn Channel. | ||
721 | * @param msg Message. | ||
722 | */ | ||
723 | static void | ||
724 | cadet_send_channel (struct Channel *chn, const struct GNUNET_MessageHeader *msg) | ||
725 | { | ||
726 | struct GNUNET_MQ_Envelope * | ||
727 | env = GNUNET_MQ_msg_copy (msg); | ||
728 | |||
729 | GNUNET_MQ_send (GNUNET_CADET_get_mq (chn->channel), env); | ||
730 | |||
731 | if (0 < chn->window_size) | ||
732 | { | ||
733 | client_send_ack (&chn->group_pub_hash); | ||
734 | } | ||
735 | else | ||
736 | { | ||
737 | chn->msgs_pending++; | ||
738 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
739 | "%p Queuing message. Pending messages: %u\n", | ||
740 | chn, chn->msgs_pending); | ||
741 | } | ||
742 | } | ||
743 | |||
744 | |||
745 | /** | ||
746 | * Create CADET channel and send a join request. | ||
747 | */ | ||
748 | static void | ||
749 | cadet_send_join_request (struct Member *mem) | ||
750 | { | ||
751 | mem->origin_channel = cadet_channel_create (&mem->group, &mem->origin); | ||
752 | cadet_send_channel (mem->origin_channel, &mem->join_req->header); | ||
753 | |||
754 | uint32_t i; | ||
755 | for (i = 0; i < mem->relay_count; i++) | ||
756 | { | ||
757 | struct Channel * | ||
758 | chn = cadet_channel_create (&mem->group, &mem->relays[i]); | ||
759 | cadet_send_channel (chn, &mem->join_req->header); | ||
760 | } | ||
761 | } | ||
762 | |||
763 | |||
764 | static int | ||
765 | cadet_send_join_decision_cb (void *cls, | ||
766 | const struct GNUNET_HashCode *group_pub_hash, | ||
767 | void *channel) | ||
768 | { | ||
769 | const struct MulticastJoinDecisionMessageHeader *hdcsn = cls; | ||
770 | struct Channel *chn = channel; | ||
771 | |||
772 | const struct MulticastJoinDecisionMessage *dcsn = | ||
773 | (struct MulticastJoinDecisionMessage *) &hdcsn[1]; | ||
774 | |||
775 | if (0 == memcmp (&hdcsn->member_pub_key, &chn->member_pub_key, sizeof (chn->member_pub_key)) | ||
776 | && 0 == memcmp (&hdcsn->peer, &chn->peer, sizeof (chn->peer))) | ||
777 | { | ||
778 | if (GNUNET_YES == ntohl (dcsn->is_admitted)) | ||
779 | { | ||
780 | chn->join_status = JOIN_ADMITTED; | ||
781 | } | ||
782 | else | ||
783 | { | ||
784 | chn->join_status = JOIN_REFUSED; | ||
785 | } | ||
786 | cadet_send_channel (chn, &hdcsn->header); | ||
787 | return GNUNET_YES; | ||
788 | } | ||
789 | |||
790 | // return GNUNET_YES to continue the multihashmap_get iteration | ||
791 | return GNUNET_YES; | ||
792 | } | ||
793 | |||
794 | |||
795 | /** | ||
796 | * Send join decision to a remote peer. | ||
797 | */ | ||
798 | static void | ||
799 | cadet_send_join_decision (struct Group *grp, | ||
800 | const struct MulticastJoinDecisionMessageHeader *hdcsn) | ||
801 | { | ||
802 | GNUNET_CONTAINER_multihashmap_get_multiple (channels_in, &grp->pub_key_hash, | ||
803 | &cadet_send_join_decision_cb, | ||
804 | (void *) hdcsn); | ||
805 | } | ||
806 | |||
807 | |||
808 | /** | ||
809 | * Iterator callback for sending a message to origin clients. | ||
810 | */ | ||
811 | static int | ||
812 | cadet_send_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash, | ||
813 | void *channel) | ||
814 | { | ||
815 | const struct GNUNET_MessageHeader *msg = cls; | ||
816 | struct Channel *chn = channel; | ||
817 | if (JOIN_ADMITTED == chn->join_status) | ||
818 | cadet_send_channel (chn, msg); | ||
819 | return GNUNET_YES; | ||
820 | } | ||
821 | |||
822 | |||
823 | /** | ||
824 | * Send message to all connected children. | ||
825 | */ | ||
826 | static int | ||
827 | cadet_send_children (struct GNUNET_HashCode *pub_key_hash, | ||
828 | const struct GNUNET_MessageHeader *msg) | ||
829 | { | ||
830 | int n = 0; | ||
831 | if (channels_in != NULL) | ||
832 | n += GNUNET_CONTAINER_multihashmap_get_multiple (channels_in, pub_key_hash, | ||
833 | cadet_send_cb, (void *) msg); | ||
834 | return n; | ||
835 | } | ||
836 | |||
837 | |||
838 | #if 0 // unused as yet | ||
839 | /** | ||
840 | * Send message to all connected parents. | ||
841 | */ | ||
842 | static int | ||
843 | cadet_send_parents (struct GNUNET_HashCode *pub_key_hash, | ||
844 | const struct GNUNET_MessageHeader *msg) | ||
845 | { | ||
846 | int n = 0; | ||
847 | if (channels_in != NULL) | ||
848 | n += GNUNET_CONTAINER_multihashmap_get_multiple (channels_out, pub_key_hash, | ||
849 | cadet_send_cb, (void *) msg); | ||
850 | return n; | ||
851 | } | ||
852 | #endif | ||
853 | |||
854 | |||
855 | /** | ||
856 | * CADET channel connect handler. | ||
857 | * | ||
858 | * @see GNUNET_CADET_ConnectEventHandler() | ||
859 | */ | ||
860 | static void * | ||
861 | cadet_notify_connect (void *cls, | ||
862 | struct GNUNET_CADET_Channel *channel, | ||
863 | const struct GNUNET_PeerIdentity *source) | ||
864 | { | ||
865 | struct Channel *chn = GNUNET_malloc (sizeof (struct Channel)); | ||
866 | chn->group = cls; | ||
867 | chn->channel = channel; | ||
868 | chn->direction = DIR_INCOMING; | ||
869 | chn->join_status = JOIN_NOT_ASKED; | ||
870 | |||
871 | GNUNET_CONTAINER_multihashmap_put (channels_in, &chn->group->pub_key_hash, chn, | ||
872 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
873 | return chn; | ||
874 | } | ||
875 | |||
876 | |||
877 | /** | ||
878 | * CADET window size change handler. | ||
879 | * | ||
880 | * @see GNUNET_CADET_WindowSizeEventHandler() | ||
881 | */ | ||
882 | static void | ||
883 | cadet_notify_window_change (void *cls, | ||
884 | const struct GNUNET_CADET_Channel *channel, | ||
885 | int window_size) | ||
886 | { | ||
887 | struct Channel *chn = cls; | ||
888 | |||
889 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
890 | "%p Window size changed to %d. Pending messages: %u\n", | ||
891 | chn, window_size, chn->msgs_pending); | ||
892 | |||
893 | chn->is_connected = GNUNET_YES; | ||
894 | chn->window_size = (int32_t) window_size; | ||
895 | |||
896 | for (int i = 0; i < window_size; i++) | ||
897 | { | ||
898 | if (0 < chn->msgs_pending) | ||
899 | { | ||
900 | client_send_ack (&chn->group_pub_hash); | ||
901 | chn->msgs_pending--; | ||
902 | } | ||
903 | else | ||
904 | { | ||
905 | break; | ||
906 | } | ||
907 | } | ||
908 | } | ||
909 | |||
910 | |||
911 | /** | ||
912 | * CADET channel disconnect handler. | ||
913 | * | ||
914 | * @see GNUNET_CADET_DisconnectEventHandler() | ||
915 | */ | ||
916 | static void | ||
917 | cadet_notify_disconnect (void *cls, | ||
918 | const struct GNUNET_CADET_Channel *channel) | ||
919 | { | ||
920 | if (NULL == cls) | ||
921 | return; | ||
922 | |||
923 | struct Channel *chn = cls; | ||
924 | if (NULL != chn->group) | ||
925 | { | ||
926 | if (GNUNET_NO == chn->group->is_origin) | ||
927 | { | ||
928 | struct Member *mem = (struct Member *) chn->group; | ||
929 | if (chn == mem->origin_channel) | ||
930 | mem->origin_channel = NULL; | ||
931 | } | ||
932 | } | ||
933 | |||
934 | int ret; | ||
935 | do | ||
936 | { | ||
937 | ret = replay_req_remove_cadet (chn); | ||
938 | } | ||
939 | while (GNUNET_YES == ret); | ||
940 | |||
941 | GNUNET_free (chn); | ||
942 | } | ||
943 | |||
944 | |||
945 | static int | ||
946 | check_cadet_join_request (void *cls, | ||
947 | const struct MulticastJoinRequestMessage *req) | ||
948 | { | ||
949 | struct Channel *chn = cls; | ||
950 | |||
951 | if (NULL == chn | ||
952 | || JOIN_NOT_ASKED != chn->join_status) | ||
953 | { | ||
954 | return GNUNET_SYSERR; | ||
955 | } | ||
956 | |||
957 | uint16_t size = ntohs (req->header.size); | ||
958 | if (size < sizeof (*req)) | ||
959 | { | ||
960 | GNUNET_break_op (0); | ||
961 | return GNUNET_SYSERR; | ||
962 | } | ||
963 | if (ntohl (req->purpose.size) != (size | ||
964 | - sizeof (req->header) | ||
965 | - sizeof (req->reserved) | ||
966 | - sizeof (req->signature))) | ||
967 | { | ||
968 | GNUNET_break_op (0); | ||
969 | return GNUNET_SYSERR; | ||
970 | } | ||
971 | if (GNUNET_OK != | ||
972 | GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST, | ||
973 | &req->purpose, &req->signature, | ||
974 | &req->member_pub_key)) | ||
975 | { | ||
976 | GNUNET_break_op (0); | ||
977 | return GNUNET_SYSERR; | ||
978 | } | ||
979 | |||
980 | return GNUNET_OK; | ||
981 | } | ||
982 | |||
983 | |||
984 | /** | ||
985 | * Incoming join request message from CADET. | ||
986 | */ | ||
987 | static void | ||
988 | handle_cadet_join_request (void *cls, | ||
989 | const struct MulticastJoinRequestMessage *req) | ||
990 | { | ||
991 | struct Channel *chn = cls; | ||
992 | GNUNET_CADET_receive_done (chn->channel); | ||
993 | |||
994 | struct GNUNET_HashCode group_pub_hash; | ||
995 | GNUNET_CRYPTO_hash (&req->group_pub_key, sizeof (req->group_pub_key), &group_pub_hash); | ||
996 | chn->group_pub_key = req->group_pub_key; | ||
997 | chn->group_pub_hash = group_pub_hash; | ||
998 | chn->member_pub_key = req->member_pub_key; | ||
999 | chn->peer = req->peer; | ||
1000 | chn->join_status = JOIN_WAITING; | ||
1001 | |||
1002 | client_send_all (&group_pub_hash, | ||
1003 | GNUNET_MQ_msg_copy (&req->header)); | ||
1004 | } | ||
1005 | |||
1006 | |||
1007 | static int | ||
1008 | check_cadet_join_decision (void *cls, | ||
1009 | const struct MulticastJoinDecisionMessageHeader *hdcsn) | ||
1010 | { | ||
1011 | uint16_t size = ntohs (hdcsn->header.size); | ||
1012 | if (size < sizeof (struct MulticastJoinDecisionMessageHeader) + | ||
1013 | sizeof (struct MulticastJoinDecisionMessage)) | ||
1014 | { | ||
1015 | GNUNET_break_op (0); | ||
1016 | return GNUNET_SYSERR; | ||
1017 | } | ||
1018 | |||
1019 | struct Channel *chn = cls; | ||
1020 | if (NULL == chn) | ||
1021 | { | ||
1022 | GNUNET_break (0); | ||
1023 | return GNUNET_SYSERR; | ||
1024 | } | ||
1025 | if (NULL == chn->group || GNUNET_NO != chn->group->is_origin) | ||
1026 | { | ||
1027 | GNUNET_break (0); | ||
1028 | return GNUNET_SYSERR; | ||
1029 | } | ||
1030 | switch (chn->join_status) | ||
1031 | { | ||
1032 | case JOIN_REFUSED: | ||
1033 | return GNUNET_SYSERR; | ||
1034 | |||
1035 | case JOIN_ADMITTED: | ||
1036 | return GNUNET_OK; | ||
1037 | |||
1038 | case JOIN_NOT_ASKED: | ||
1039 | case JOIN_WAITING: | ||
1040 | break; | ||
1041 | } | ||
1042 | |||
1043 | return GNUNET_OK; | ||
1044 | } | ||
1045 | |||
1046 | |||
1047 | /** | ||
1048 | * Incoming join decision message from CADET. | ||
1049 | */ | ||
1050 | static void | ||
1051 | handle_cadet_join_decision (void *cls, | ||
1052 | const struct MulticastJoinDecisionMessageHeader *hdcsn) | ||
1053 | { | ||
1054 | const struct MulticastJoinDecisionMessage * | ||
1055 | dcsn = (const struct MulticastJoinDecisionMessage *) &hdcsn[1]; | ||
1056 | |||
1057 | struct Channel *chn = cls; | ||
1058 | GNUNET_CADET_receive_done (chn->channel); | ||
1059 | |||
1060 | // FIXME: do we need to copy chn->peer or compare it with hdcsn->peer? | ||
1061 | struct Member *mem = (struct Member *) chn->group; | ||
1062 | client_send_join_decision (mem, hdcsn); | ||
1063 | if (GNUNET_YES == ntohl (dcsn->is_admitted)) | ||
1064 | { | ||
1065 | chn->join_status = JOIN_ADMITTED; | ||
1066 | } | ||
1067 | else | ||
1068 | { | ||
1069 | chn->join_status = JOIN_REFUSED; | ||
1070 | cadet_channel_destroy (chn); | ||
1071 | } | ||
1072 | } | ||
1073 | |||
1074 | |||
1075 | static int | ||
1076 | check_cadet_message (void *cls, | ||
1077 | const struct GNUNET_MULTICAST_MessageHeader *msg) | ||
1078 | { | ||
1079 | uint16_t size = ntohs (msg->header.size); | ||
1080 | if (size < sizeof (*msg)) | ||
1081 | { | ||
1082 | GNUNET_break_op (0); | ||
1083 | return GNUNET_SYSERR; | ||
1084 | } | ||
1085 | |||
1086 | struct Channel *chn = cls; | ||
1087 | if (NULL == chn) | ||
1088 | { | ||
1089 | GNUNET_break (0); | ||
1090 | return GNUNET_SYSERR; | ||
1091 | } | ||
1092 | if (ntohl (msg->purpose.size) != (size | ||
1093 | - sizeof (msg->header) | ||
1094 | - sizeof (msg->hop_counter) | ||
1095 | - sizeof (msg->signature))) | ||
1096 | { | ||
1097 | GNUNET_break_op (0); | ||
1098 | return GNUNET_SYSERR; | ||
1099 | } | ||
1100 | if (GNUNET_OK != | ||
1101 | GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_MULTICAST_MESSAGE, | ||
1102 | &msg->purpose, &msg->signature, | ||
1103 | &chn->group_pub_key)) | ||
1104 | { | ||
1105 | GNUNET_break_op (0); | ||
1106 | return GNUNET_SYSERR; | ||
1107 | } | ||
1108 | |||
1109 | return GNUNET_OK; | ||
1110 | } | ||
1111 | |||
1112 | |||
1113 | /** | ||
1114 | * Incoming multicast message from CADET. | ||
1115 | */ | ||
1116 | static void | ||
1117 | handle_cadet_message (void *cls, | ||
1118 | const struct GNUNET_MULTICAST_MessageHeader *msg) | ||
1119 | { | ||
1120 | struct Channel *chn = cls; | ||
1121 | GNUNET_CADET_receive_done (chn->channel); | ||
1122 | client_send_all (&chn->group_pub_hash, | ||
1123 | GNUNET_MQ_msg_copy (&msg->header)); | ||
1124 | } | ||
1125 | |||
1126 | |||
1127 | static int | ||
1128 | check_cadet_request (void *cls, | ||
1129 | const struct GNUNET_MULTICAST_RequestHeader *req) | ||
1130 | { | ||
1131 | uint16_t size = ntohs (req->header.size); | ||
1132 | if (size < sizeof (*req)) | ||
1133 | { | ||
1134 | GNUNET_break_op (0); | ||
1135 | return GNUNET_SYSERR; | ||
1136 | } | ||
1137 | |||
1138 | struct Channel *chn = cls; | ||
1139 | if (NULL == chn) | ||
1140 | { | ||
1141 | GNUNET_break (0); | ||
1142 | return GNUNET_SYSERR; | ||
1143 | } | ||
1144 | if (ntohl (req->purpose.size) != (size | ||
1145 | - sizeof (req->header) | ||
1146 | - sizeof (req->member_pub_key) | ||
1147 | - sizeof (req->signature))) | ||
1148 | { | ||
1149 | GNUNET_break_op (0); | ||
1150 | return GNUNET_SYSERR; | ||
1151 | } | ||
1152 | if (GNUNET_OK != | ||
1153 | GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST, | ||
1154 | &req->purpose, &req->signature, | ||
1155 | &req->member_pub_key)) | ||
1156 | { | ||
1157 | GNUNET_break_op (0); | ||
1158 | return GNUNET_SYSERR; | ||
1159 | } | ||
1160 | |||
1161 | return GNUNET_OK; | ||
1162 | } | ||
1163 | |||
1164 | |||
1165 | /** | ||
1166 | * Incoming multicast request message from CADET. | ||
1167 | */ | ||
1168 | static void | ||
1169 | handle_cadet_request (void *cls, | ||
1170 | const struct GNUNET_MULTICAST_RequestHeader *req) | ||
1171 | { | ||
1172 | struct Channel *chn = cls; | ||
1173 | GNUNET_CADET_receive_done (chn->channel); | ||
1174 | client_send_origin (&chn->group_pub_hash, | ||
1175 | GNUNET_MQ_msg_copy (&req->header)); | ||
1176 | } | ||
1177 | |||
1178 | |||
1179 | // FIXME: do checks in handle_cadet_replay_request | ||
1180 | //static int | ||
1181 | //check_cadet_replay_request (void *cls, | ||
1182 | // const struct MulticastReplayRequestMessage *req) | ||
1183 | //{ | ||
1184 | // uint16_t size = ntohs (req->header.size); | ||
1185 | // if (size < sizeof (*req)) | ||
1186 | // { | ||
1187 | // GNUNET_break_op (0); | ||
1188 | // return GNUNET_SYSERR; | ||
1189 | // } | ||
1190 | // | ||
1191 | // struct Channel *chn = cls; | ||
1192 | // if (NULL == chn) | ||
1193 | // { | ||
1194 | // GNUNET_break_op (0); | ||
1195 | // return GNUNET_SYSERR; | ||
1196 | // } | ||
1197 | // | ||
1198 | // return GNUNET_OK; | ||
1199 | //} | ||
1200 | |||
1201 | |||
1202 | /** | ||
1203 | * Incoming multicast replay request from CADET. | ||
1204 | */ | ||
1205 | static void | ||
1206 | handle_cadet_replay_request (void *cls, | ||
1207 | const struct MulticastReplayRequestMessage *req) | ||
1208 | { | ||
1209 | struct Channel *chn = cls; | ||
1210 | |||
1211 | GNUNET_CADET_receive_done (chn->channel); | ||
1212 | |||
1213 | struct MulticastReplayRequestMessage rep = *req; | ||
1214 | GNUNET_memcpy (&rep.member_pub_key, &chn->member_pub_key, sizeof (chn->member_pub_key)); | ||
1215 | |||
1216 | struct GNUNET_CONTAINER_MultiHashMap * | ||
1217 | grp_replay_req = GNUNET_CONTAINER_multihashmap_get (replay_req_cadet, | ||
1218 | &chn->group->pub_key_hash); | ||
1219 | if (NULL == grp_replay_req) | ||
1220 | { | ||
1221 | grp_replay_req = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO); | ||
1222 | GNUNET_CONTAINER_multihashmap_put (replay_req_cadet, | ||
1223 | &chn->group->pub_key_hash, grp_replay_req, | ||
1224 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); | ||
1225 | } | ||
1226 | struct GNUNET_HashCode key_hash; | ||
1227 | replay_key_hash (rep.fragment_id, | ||
1228 | rep.message_id, | ||
1229 | rep.fragment_offset, | ||
1230 | rep.flags, | ||
1231 | &key_hash); | ||
1232 | GNUNET_CONTAINER_multihashmap_put (grp_replay_req, &key_hash, chn, | ||
1233 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
1234 | |||
1235 | client_send_random (&chn->group_pub_hash, | ||
1236 | GNUNET_MQ_msg_copy (&rep.header)); | ||
1237 | } | ||
1238 | |||
1239 | |||
1240 | static int | ||
1241 | check_cadet_replay_response (void *cls, | ||
1242 | const struct MulticastReplayResponseMessage *res) | ||
1243 | { | ||
1244 | struct Channel *chn = cls; | ||
1245 | if (NULL == chn) | ||
1246 | { | ||
1247 | GNUNET_break (0); | ||
1248 | return GNUNET_SYSERR; | ||
1249 | } | ||
1250 | return GNUNET_OK; | ||
1251 | } | ||
1252 | |||
1253 | |||
1254 | /** | ||
1255 | * Incoming multicast replay response from CADET. | ||
1256 | */ | ||
1257 | static void | ||
1258 | handle_cadet_replay_response (void *cls, | ||
1259 | const struct MulticastReplayResponseMessage *res) | ||
1260 | { | ||
1261 | struct Channel *chn = cls; | ||
1262 | GNUNET_CADET_receive_done (chn->channel); | ||
1263 | |||
1264 | /* @todo FIXME: got replay error response, send request to other members */ | ||
1265 | } | ||
1266 | |||
1267 | |||
1268 | static void | ||
1269 | group_set_cadet_port_hash (struct Group *grp) | ||
1270 | { | ||
1271 | struct CadetPort { | ||
1272 | struct GNUNET_CRYPTO_EddsaPublicKey pub_key; | ||
1273 | uint32_t app_type; | ||
1274 | } port = { | ||
1275 | grp->pub_key, | ||
1276 | GNUNET_APPLICATION_TYPE_MULTICAST, | ||
1277 | }; | ||
1278 | GNUNET_CRYPTO_hash (&port, sizeof (port), &grp->cadet_port_hash); | ||
1279 | } | ||
1280 | |||
1281 | |||
1282 | |||
1283 | /** | ||
1284 | * Create new outgoing CADET channel. | ||
1285 | * | ||
1286 | * @param peer | ||
1287 | * Peer to connect to. | ||
1288 | * @param group_pub_key | ||
1289 | * Public key of group the channel belongs to. | ||
1290 | * @param group_pub_hash | ||
1291 | * Hash of @a group_pub_key. | ||
1292 | * | ||
1293 | * @return Channel. | ||
1294 | */ | ||
1295 | static struct Channel * | ||
1296 | cadet_channel_create (struct Group *grp, struct GNUNET_PeerIdentity *peer) | ||
1297 | { | ||
1298 | struct Channel *chn = GNUNET_malloc (sizeof (*chn)); | ||
1299 | chn->group = grp; | ||
1300 | chn->group_pub_key = grp->pub_key; | ||
1301 | chn->group_pub_hash = grp->pub_key_hash; | ||
1302 | chn->peer = *peer; | ||
1303 | chn->direction = DIR_OUTGOING; | ||
1304 | chn->is_connected = GNUNET_NO; | ||
1305 | chn->join_status = JOIN_WAITING; | ||
1306 | |||
1307 | struct GNUNET_MQ_MessageHandler cadet_handlers[] = { | ||
1308 | GNUNET_MQ_hd_var_size (cadet_message, | ||
1309 | GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE, | ||
1310 | struct GNUNET_MULTICAST_MessageHeader, | ||
1311 | chn), | ||
1312 | |||
1313 | GNUNET_MQ_hd_var_size (cadet_join_decision, | ||
1314 | GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION, | ||
1315 | struct MulticastJoinDecisionMessageHeader, | ||
1316 | chn), | ||
1317 | |||
1318 | GNUNET_MQ_hd_fixed_size (cadet_replay_request, | ||
1319 | GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST, | ||
1320 | struct MulticastReplayRequestMessage, | ||
1321 | chn), | ||
1322 | |||
1323 | GNUNET_MQ_hd_var_size (cadet_replay_response, | ||
1324 | GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE, | ||
1325 | struct MulticastReplayResponseMessage, | ||
1326 | chn), | ||
1327 | |||
1328 | GNUNET_MQ_handler_end () | ||
1329 | }; | ||
1330 | |||
1331 | chn->channel = GNUNET_CADET_channel_create (cadet, chn, &chn->peer, | ||
1332 | &grp->cadet_port_hash, | ||
1333 | GNUNET_CADET_OPTION_RELIABLE, | ||
1334 | cadet_notify_window_change, | ||
1335 | cadet_notify_disconnect, | ||
1336 | cadet_handlers); | ||
1337 | GNUNET_CONTAINER_multihashmap_put (channels_out, &chn->group_pub_hash, chn, | ||
1338 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
1339 | return chn; | ||
1340 | } | ||
1341 | |||
1342 | |||
1343 | /** | ||
1344 | * Destroy outgoing CADET channel. | ||
1345 | */ | ||
1346 | static void | ||
1347 | cadet_channel_destroy (struct Channel *chn) | ||
1348 | { | ||
1349 | GNUNET_CADET_channel_destroy (chn->channel); | ||
1350 | GNUNET_CONTAINER_multihashmap_remove_all (channels_out, &chn->group_pub_hash); | ||
1351 | GNUNET_free (chn); | ||
1352 | } | ||
1353 | |||
1354 | /** | ||
1355 | * Handle a connecting client starting an origin. | ||
1356 | */ | ||
1357 | static void | ||
1358 | handle_client_origin_start (void *cls, | ||
1359 | const struct MulticastOriginStartMessage *msg) | ||
1360 | { | ||
1361 | struct Client *c = cls; | ||
1362 | struct GNUNET_SERVICE_Client *client = c->client; | ||
1363 | |||
1364 | struct GNUNET_CRYPTO_EddsaPublicKey pub_key; | ||
1365 | struct GNUNET_HashCode pub_key_hash; | ||
1366 | |||
1367 | GNUNET_CRYPTO_eddsa_key_get_public (&msg->group_key, &pub_key); | ||
1368 | GNUNET_CRYPTO_hash (&pub_key, sizeof (pub_key), &pub_key_hash); | ||
1369 | |||
1370 | struct Origin * | ||
1371 | orig = GNUNET_CONTAINER_multihashmap_get (origins, &pub_key_hash); | ||
1372 | struct Group *grp; | ||
1373 | |||
1374 | if (NULL == orig) | ||
1375 | { | ||
1376 | orig = GNUNET_new (struct Origin); | ||
1377 | orig->priv_key = msg->group_key; | ||
1378 | orig->max_fragment_id = GNUNET_ntohll (msg->max_fragment_id); | ||
1379 | |||
1380 | grp = c->group = &orig->group; | ||
1381 | grp->origin = orig; | ||
1382 | grp->is_origin = GNUNET_YES; | ||
1383 | grp->pub_key = pub_key; | ||
1384 | grp->pub_key_hash = pub_key_hash; | ||
1385 | grp->is_disconnected = GNUNET_NO; | ||
1386 | |||
1387 | GNUNET_CONTAINER_multihashmap_put (origins, &grp->pub_key_hash, orig, | ||
1388 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); | ||
1389 | |||
1390 | group_set_cadet_port_hash (grp); | ||
1391 | |||
1392 | struct GNUNET_MQ_MessageHandler cadet_handlers[] = { | ||
1393 | GNUNET_MQ_hd_var_size (cadet_message, | ||
1394 | GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE, | ||
1395 | struct GNUNET_MULTICAST_MessageHeader, | ||
1396 | grp), | ||
1397 | |||
1398 | GNUNET_MQ_hd_var_size (cadet_request, | ||
1399 | GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST, | ||
1400 | struct GNUNET_MULTICAST_RequestHeader, | ||
1401 | grp), | ||
1402 | |||
1403 | GNUNET_MQ_hd_var_size (cadet_join_request, | ||
1404 | GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST, | ||
1405 | struct MulticastJoinRequestMessage, | ||
1406 | grp), | ||
1407 | |||
1408 | GNUNET_MQ_hd_fixed_size (cadet_replay_request, | ||
1409 | GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST, | ||
1410 | struct MulticastReplayRequestMessage, | ||
1411 | grp), | ||
1412 | |||
1413 | GNUNET_MQ_hd_var_size (cadet_replay_response, | ||
1414 | GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE, | ||
1415 | struct MulticastReplayResponseMessage, | ||
1416 | grp), | ||
1417 | |||
1418 | GNUNET_MQ_handler_end () | ||
1419 | }; | ||
1420 | |||
1421 | |||
1422 | orig->cadet_port = GNUNET_CADET_open_port (cadet, | ||
1423 | &grp->cadet_port_hash, | ||
1424 | cadet_notify_connect, | ||
1425 | grp, | ||
1426 | cadet_notify_window_change, | ||
1427 | cadet_notify_disconnect, | ||
1428 | cadet_handlers); | ||
1429 | } | ||
1430 | else | ||
1431 | { | ||
1432 | grp = &orig->group; | ||
1433 | } | ||
1434 | |||
1435 | struct ClientList *cl = GNUNET_new (struct ClientList); | ||
1436 | cl->client = client; | ||
1437 | GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl); | ||
1438 | |||
1439 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1440 | "%p Client connected as origin to group %s.\n", | ||
1441 | orig, GNUNET_h2s (&grp->pub_key_hash)); | ||
1442 | GNUNET_SERVICE_client_continue (client); | ||
1443 | } | ||
1444 | |||
1445 | |||
1446 | static int | ||
1447 | check_client_member_join (void *cls, | ||
1448 | const struct MulticastMemberJoinMessage *msg) | ||
1449 | { | ||
1450 | uint16_t msg_size = ntohs (msg->header.size); | ||
1451 | struct GNUNET_PeerIdentity *relays = (struct GNUNET_PeerIdentity *) &msg[1]; | ||
1452 | uint32_t relay_count = ntohl (msg->relay_count); | ||
1453 | |||
1454 | if (0 != relay_count) | ||
1455 | { | ||
1456 | if (UINT32_MAX / relay_count < sizeof (*relays)){ | ||
1457 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1458 | "relay_count (%lu) * sizeof (*relays) (%lu) exceeds UINT32_MAX!\n", | ||
1459 | (unsigned long)relay_count, | ||
1460 | sizeof (*relays)); | ||
1461 | return GNUNET_SYSERR; | ||
1462 | } | ||
1463 | } | ||
1464 | uint32_t relay_size = relay_count * sizeof (*relays); | ||
1465 | struct GNUNET_MessageHeader *join_msg = NULL; | ||
1466 | uint16_t join_msg_size = 0; | ||
1467 | if (sizeof (*msg) + relay_size + sizeof (struct GNUNET_MessageHeader) | ||
1468 | <= msg_size) | ||
1469 | { | ||
1470 | join_msg = (struct GNUNET_MessageHeader *) | ||
1471 | (((char *) &msg[1]) + relay_size); | ||
1472 | join_msg_size = ntohs (join_msg->size); | ||
1473 | if (UINT16_MAX - join_msg_size < sizeof (struct MulticastJoinRequestMessage)){ | ||
1474 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1475 | "join_msg_size (%u) + sizeof (struct MulticastJoinRequestMessage) (%lu) exceeds UINT16_MAX!\n", | ||
1476 | (unsigned)join_msg_size, | ||
1477 | (unsigned long)sizeof (struct MulticastJoinRequestMessage)); | ||
1478 | return GNUNET_SYSERR; | ||
1479 | } | ||
1480 | } | ||
1481 | if (msg_size != (sizeof (*msg) + relay_size + join_msg_size)){ | ||
1482 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1483 | "msg_size does not match real size of message!\n"); | ||
1484 | return GNUNET_SYSERR; | ||
1485 | }else{ | ||
1486 | return GNUNET_OK; | ||
1487 | } | ||
1488 | } | ||
1489 | |||
1490 | |||
1491 | /** | ||
1492 | * Handle a connecting client joining a group. | ||
1493 | */ | ||
1494 | static void | ||
1495 | handle_client_member_join (void *cls, | ||
1496 | const struct MulticastMemberJoinMessage *msg) | ||
1497 | { | ||
1498 | struct Client *c = cls; | ||
1499 | struct GNUNET_SERVICE_Client *client = c->client; | ||
1500 | |||
1501 | uint16_t msg_size = ntohs (msg->header.size); | ||
1502 | |||
1503 | struct GNUNET_CRYPTO_EcdsaPublicKey mem_pub_key; | ||
1504 | struct GNUNET_HashCode pub_key_hash, mem_pub_key_hash; | ||
1505 | |||
1506 | GNUNET_CRYPTO_ecdsa_key_get_public (&msg->member_key, &mem_pub_key); | ||
1507 | GNUNET_CRYPTO_hash (&mem_pub_key, sizeof (mem_pub_key), &mem_pub_key_hash); | ||
1508 | GNUNET_CRYPTO_hash (&msg->group_pub_key, sizeof (msg->group_pub_key), &pub_key_hash); | ||
1509 | |||
1510 | struct GNUNET_CONTAINER_MultiHashMap * | ||
1511 | grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members, &pub_key_hash); | ||
1512 | struct Member *mem = NULL; | ||
1513 | struct Group *grp; | ||
1514 | |||
1515 | if (NULL != grp_mem) | ||
1516 | { | ||
1517 | mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &mem_pub_key_hash); | ||
1518 | } | ||
1519 | |||
1520 | if (NULL == mem) | ||
1521 | { | ||
1522 | mem = GNUNET_new (struct Member); | ||
1523 | mem->origin = msg->origin; | ||
1524 | mem->priv_key = msg->member_key; | ||
1525 | mem->pub_key = mem_pub_key; | ||
1526 | mem->pub_key_hash = mem_pub_key_hash; | ||
1527 | mem->max_fragment_id = 0; // FIXME | ||
1528 | |||
1529 | grp = c->group = &mem->group; | ||
1530 | grp->member = mem; | ||
1531 | grp->is_origin = GNUNET_NO; | ||
1532 | grp->pub_key = msg->group_pub_key; | ||
1533 | grp->pub_key_hash = pub_key_hash; | ||
1534 | grp->is_disconnected = GNUNET_NO; | ||
1535 | group_set_cadet_port_hash (grp); | ||
1536 | |||
1537 | if (NULL == grp_mem) | ||
1538 | { | ||
1539 | grp_mem = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES); | ||
1540 | GNUNET_CONTAINER_multihashmap_put (group_members, &grp->pub_key_hash, grp_mem, | ||
1541 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); | ||
1542 | } | ||
1543 | GNUNET_CONTAINER_multihashmap_put (grp_mem, &mem->pub_key_hash, mem, | ||
1544 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); | ||
1545 | |||
1546 | // FIXME: should the members hash map have option UNIQUE_FAST? | ||
1547 | GNUNET_CONTAINER_multihashmap_put (members, &grp->pub_key_hash, mem, | ||
1548 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
1549 | } | ||
1550 | else | ||
1551 | { | ||
1552 | grp = &mem->group; | ||
1553 | } | ||
1554 | |||
1555 | struct ClientList *cl = GNUNET_new (struct ClientList); | ||
1556 | cl->client = client; | ||
1557 | GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl); | ||
1558 | |||
1559 | char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&mem->pub_key); | ||
1560 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1561 | "Client connected to group %s as member %s (%s). size = %d\n", | ||
1562 | GNUNET_h2s (&grp->pub_key_hash), | ||
1563 | GNUNET_h2s2 (&mem->pub_key_hash), | ||
1564 | str, | ||
1565 | GNUNET_CONTAINER_multihashmap_size (members)); | ||
1566 | GNUNET_free (str); | ||
1567 | |||
1568 | if (NULL != mem->join_dcsn) | ||
1569 | { /* Already got a join decision, send it to client. */ | ||
1570 | struct GNUNET_MQ_Envelope * | ||
1571 | env = GNUNET_MQ_msg_copy (&mem->join_dcsn->header); | ||
1572 | |||
1573 | GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), | ||
1574 | env); | ||
1575 | } | ||
1576 | else | ||
1577 | { /* First client of the group, send join request. */ | ||
1578 | struct GNUNET_PeerIdentity *relays = (struct GNUNET_PeerIdentity *) &msg[1]; | ||
1579 | uint32_t relay_count = ntohl (msg->relay_count); | ||
1580 | uint16_t relay_size = relay_count * sizeof (*relays); | ||
1581 | struct GNUNET_MessageHeader *join_msg = NULL; | ||
1582 | uint16_t join_msg_size = 0; | ||
1583 | if (sizeof (*msg) + relay_size + sizeof (struct GNUNET_MessageHeader) | ||
1584 | <= msg_size) | ||
1585 | { | ||
1586 | join_msg = (struct GNUNET_MessageHeader *) | ||
1587 | (((char *) &msg[1]) + relay_size); | ||
1588 | join_msg_size = ntohs (join_msg->size); | ||
1589 | } | ||
1590 | |||
1591 | uint16_t req_msg_size = sizeof (struct MulticastJoinRequestMessage) + join_msg_size; | ||
1592 | struct MulticastJoinRequestMessage * | ||
1593 | req = GNUNET_malloc (req_msg_size); | ||
1594 | req->header.size = htons (req_msg_size); | ||
1595 | req->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST); | ||
1596 | req->group_pub_key = grp->pub_key; | ||
1597 | req->peer = this_peer; | ||
1598 | GNUNET_CRYPTO_ecdsa_key_get_public (&mem->priv_key, &req->member_pub_key); | ||
1599 | if (0 < join_msg_size) | ||
1600 | GNUNET_memcpy (&req[1], join_msg, join_msg_size); | ||
1601 | |||
1602 | req->member_pub_key = mem->pub_key; | ||
1603 | req->purpose.size = htonl (req_msg_size | ||
1604 | - sizeof (req->header) | ||
1605 | - sizeof (req->reserved) | ||
1606 | - sizeof (req->signature)); | ||
1607 | req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST); | ||
1608 | |||
1609 | if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_sign (&mem->priv_key, &req->purpose, | ||
1610 | &req->signature)) | ||
1611 | { | ||
1612 | /* FIXME: handle error */ | ||
1613 | GNUNET_assert (0); | ||
1614 | } | ||
1615 | |||
1616 | if (NULL != mem->join_req) | ||
1617 | GNUNET_free (mem->join_req); | ||
1618 | mem->join_req = req; | ||
1619 | |||
1620 | if (0 == | ||
1621 | client_send_origin (&grp->pub_key_hash, | ||
1622 | GNUNET_MQ_msg_copy (&mem->join_req->header))) | ||
1623 | { /* No local origins, send to remote origin */ | ||
1624 | cadet_send_join_request (mem); | ||
1625 | } | ||
1626 | } | ||
1627 | GNUNET_SERVICE_client_continue (client); | ||
1628 | } | ||
1629 | |||
1630 | |||
1631 | static void | ||
1632 | client_send_join_decision (struct Member *mem, | ||
1633 | const struct MulticastJoinDecisionMessageHeader *hdcsn) | ||
1634 | { | ||
1635 | client_send_group (&mem->group, GNUNET_MQ_msg_copy (&hdcsn->header)); | ||
1636 | |||
1637 | const struct MulticastJoinDecisionMessage * | ||
1638 | dcsn = (const struct MulticastJoinDecisionMessage *) &hdcsn[1]; | ||
1639 | if (GNUNET_YES == ntohl (dcsn->is_admitted)) | ||
1640 | { /* Member admitted, store join_decision. */ | ||
1641 | uint16_t dcsn_size = ntohs (dcsn->header.size); | ||
1642 | mem->join_dcsn = GNUNET_malloc (dcsn_size); | ||
1643 | GNUNET_memcpy (mem->join_dcsn, dcsn, dcsn_size); | ||
1644 | } | ||
1645 | else | ||
1646 | { /* Refused entry, but replay would be still possible for past members. */ | ||
1647 | } | ||
1648 | } | ||
1649 | |||
1650 | |||
1651 | static int | ||
1652 | check_client_join_decision (void *cls, | ||
1653 | const struct MulticastJoinDecisionMessageHeader *hdcsn) | ||
1654 | { | ||
1655 | return GNUNET_OK; | ||
1656 | } | ||
1657 | |||
1658 | |||
1659 | /** | ||
1660 | * Join decision from client. | ||
1661 | */ | ||
1662 | static void | ||
1663 | handle_client_join_decision (void *cls, | ||
1664 | const struct MulticastJoinDecisionMessageHeader *hdcsn) | ||
1665 | { | ||
1666 | struct Client *c = cls; | ||
1667 | struct GNUNET_SERVICE_Client *client = c->client; | ||
1668 | struct Group *grp = c->group; | ||
1669 | |||
1670 | if (NULL == grp) | ||
1671 | { | ||
1672 | GNUNET_break (0); | ||
1673 | GNUNET_SERVICE_client_drop (client); | ||
1674 | return; | ||
1675 | } | ||
1676 | GNUNET_assert (GNUNET_NO == grp->is_disconnected); | ||
1677 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1678 | "%p got join decision from client for group %s..\n", | ||
1679 | grp, GNUNET_h2s (&grp->pub_key_hash)); | ||
1680 | |||
1681 | struct GNUNET_CONTAINER_MultiHashMap * | ||
1682 | grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members, | ||
1683 | &grp->pub_key_hash); | ||
1684 | struct Member *mem = NULL; | ||
1685 | if (NULL != grp_mem) | ||
1686 | { | ||
1687 | struct GNUNET_HashCode member_key_hash; | ||
1688 | GNUNET_CRYPTO_hash (&hdcsn->member_pub_key, sizeof (hdcsn->member_pub_key), | ||
1689 | &member_key_hash); | ||
1690 | mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &member_key_hash); | ||
1691 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1692 | "%p ..and member %s: %p\n", | ||
1693 | grp, GNUNET_h2s (&member_key_hash), mem); | ||
1694 | } | ||
1695 | |||
1696 | if (NULL != mem) | ||
1697 | { /* Found local member */ | ||
1698 | client_send_join_decision (mem, hdcsn); | ||
1699 | } | ||
1700 | else | ||
1701 | { /* Look for remote member */ | ||
1702 | cadet_send_join_decision (grp, hdcsn); | ||
1703 | } | ||
1704 | GNUNET_SERVICE_client_continue (client); | ||
1705 | } | ||
1706 | |||
1707 | |||
1708 | static void | ||
1709 | handle_client_part_request (void *cls, | ||
1710 | const struct GNUNET_MessageHeader *msg) | ||
1711 | { | ||
1712 | struct Client *c = cls; | ||
1713 | struct GNUNET_SERVICE_Client *client = c->client; | ||
1714 | struct Group *grp = c->group; | ||
1715 | struct GNUNET_MQ_Envelope *env; | ||
1716 | |||
1717 | if (NULL == grp) | ||
1718 | { | ||
1719 | GNUNET_break (0); | ||
1720 | GNUNET_SERVICE_client_drop (client); | ||
1721 | return; | ||
1722 | } | ||
1723 | GNUNET_assert (GNUNET_NO == grp->is_disconnected); | ||
1724 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1725 | "%p got part request from client for group %s.\n", | ||
1726 | grp, GNUNET_h2s (&grp->pub_key_hash)); | ||
1727 | grp->is_disconnected = GNUNET_YES; | ||
1728 | env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_MULTICAST_PART_ACK); | ||
1729 | client_send_group (grp, env); | ||
1730 | GNUNET_SERVICE_client_continue (client); | ||
1731 | } | ||
1732 | |||
1733 | |||
1734 | static int | ||
1735 | check_client_multicast_message (void *cls, | ||
1736 | const struct GNUNET_MULTICAST_MessageHeader *msg) | ||
1737 | { | ||
1738 | return GNUNET_OK; | ||
1739 | } | ||
1740 | |||
1741 | |||
1742 | /** | ||
1743 | * Incoming message from a client. | ||
1744 | */ | ||
1745 | static void | ||
1746 | handle_client_multicast_message (void *cls, | ||
1747 | const struct GNUNET_MULTICAST_MessageHeader *msg) | ||
1748 | { | ||
1749 | // FIXME: what if GNUNET_YES == grp->is_disconnected? Do we allow sending messages? | ||
1750 | struct Client *c = cls; | ||
1751 | struct GNUNET_SERVICE_Client *client = c->client; | ||
1752 | struct Group *grp = c->group; | ||
1753 | |||
1754 | if (NULL == grp) | ||
1755 | { | ||
1756 | GNUNET_break (0); | ||
1757 | GNUNET_SERVICE_client_drop (client); | ||
1758 | return; | ||
1759 | } | ||
1760 | GNUNET_assert (GNUNET_YES == grp->is_origin); | ||
1761 | struct Origin *orig = grp->origin; | ||
1762 | |||
1763 | // FIXME: use GNUNET_MQ_msg_copy | ||
1764 | /* FIXME: yucky, should use separate message structs for P2P and CS! */ | ||
1765 | struct GNUNET_MULTICAST_MessageHeader * | ||
1766 | out = (struct GNUNET_MULTICAST_MessageHeader *) GNUNET_copy_message (&msg->header); | ||
1767 | out->fragment_id = GNUNET_htonll (++orig->max_fragment_id); | ||
1768 | out->purpose.size = htonl (ntohs (out->header.size) | ||
1769 | - sizeof (out->header) | ||
1770 | - sizeof (out->hop_counter) | ||
1771 | - sizeof (out->signature)); | ||
1772 | out->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_MESSAGE); | ||
1773 | |||
1774 | if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&orig->priv_key, &out->purpose, | ||
1775 | &out->signature)) | ||
1776 | { | ||
1777 | GNUNET_assert (0); | ||
1778 | } | ||
1779 | |||
1780 | client_send_all (&grp->pub_key_hash, GNUNET_MQ_msg_copy (&out->header)); | ||
1781 | cadet_send_children (&grp->pub_key_hash, &out->header); | ||
1782 | client_send_ack (&grp->pub_key_hash); | ||
1783 | GNUNET_free (out); | ||
1784 | |||
1785 | GNUNET_SERVICE_client_continue (client); | ||
1786 | } | ||
1787 | |||
1788 | |||
1789 | static int | ||
1790 | check_client_multicast_request (void *cls, | ||
1791 | const struct GNUNET_MULTICAST_RequestHeader *req) | ||
1792 | { | ||
1793 | return GNUNET_OK; | ||
1794 | } | ||
1795 | |||
1796 | |||
1797 | /** | ||
1798 | * Incoming request from a client. | ||
1799 | */ | ||
1800 | static void | ||
1801 | handle_client_multicast_request (void *cls, | ||
1802 | const struct GNUNET_MULTICAST_RequestHeader *req) | ||
1803 | { | ||
1804 | struct Client *c = cls; | ||
1805 | struct GNUNET_SERVICE_Client *client = c->client; | ||
1806 | struct Group *grp = c->group; | ||
1807 | |||
1808 | if (NULL == grp) | ||
1809 | { | ||
1810 | GNUNET_break (0); | ||
1811 | GNUNET_SERVICE_client_drop (client); | ||
1812 | return; | ||
1813 | } | ||
1814 | GNUNET_assert (GNUNET_NO == grp->is_disconnected); | ||
1815 | GNUNET_assert (GNUNET_NO == grp->is_origin); | ||
1816 | struct Member *mem = grp->member; | ||
1817 | |||
1818 | /* FIXME: yucky, should use separate message structs for P2P and CS! */ | ||
1819 | struct GNUNET_MULTICAST_RequestHeader * | ||
1820 | out = (struct GNUNET_MULTICAST_RequestHeader *) GNUNET_copy_message (&req->header); | ||
1821 | out->member_pub_key = mem->pub_key; | ||
1822 | out->fragment_id = GNUNET_ntohll (++mem->max_fragment_id); | ||
1823 | out->purpose.size = htonl (ntohs (out->header.size) | ||
1824 | - sizeof (out->header) | ||
1825 | - sizeof (out->member_pub_key) | ||
1826 | - sizeof (out->signature)); | ||
1827 | out->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST); | ||
1828 | |||
1829 | if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_sign (&mem->priv_key, &out->purpose, | ||
1830 | &out->signature)) | ||
1831 | { | ||
1832 | GNUNET_assert (0); | ||
1833 | } | ||
1834 | |||
1835 | uint8_t send_ack = GNUNET_YES; | ||
1836 | if (0 == | ||
1837 | client_send_origin (&grp->pub_key_hash, | ||
1838 | GNUNET_MQ_msg_copy (&out->header))) | ||
1839 | { /* No local origins, send to remote origin */ | ||
1840 | if (NULL != mem->origin_channel) | ||
1841 | { | ||
1842 | cadet_send_channel (mem->origin_channel, &out->header); | ||
1843 | send_ack = GNUNET_NO; | ||
1844 | } | ||
1845 | else | ||
1846 | { | ||
1847 | /* FIXME: not yet connected to origin */ | ||
1848 | GNUNET_SERVICE_client_drop (client); | ||
1849 | GNUNET_free (out); | ||
1850 | return; | ||
1851 | } | ||
1852 | } | ||
1853 | if (GNUNET_YES == send_ack) | ||
1854 | { | ||
1855 | client_send_ack (&grp->pub_key_hash); | ||
1856 | } | ||
1857 | GNUNET_free (out); | ||
1858 | GNUNET_SERVICE_client_continue (client); | ||
1859 | } | ||
1860 | |||
1861 | |||
1862 | /** | ||
1863 | * Incoming replay request from a client. | ||
1864 | */ | ||
1865 | static void | ||
1866 | handle_client_replay_request (void *cls, | ||
1867 | const struct MulticastReplayRequestMessage *rep) | ||
1868 | { | ||
1869 | struct Client *c = cls; | ||
1870 | struct GNUNET_SERVICE_Client *client = c->client; | ||
1871 | struct Group *grp = c->group; | ||
1872 | |||
1873 | if (NULL == grp) | ||
1874 | { | ||
1875 | GNUNET_break (0); | ||
1876 | GNUNET_SERVICE_client_drop (client); | ||
1877 | return; | ||
1878 | } | ||
1879 | GNUNET_assert (GNUNET_NO == grp->is_disconnected); | ||
1880 | GNUNET_assert (GNUNET_NO == grp->is_origin); | ||
1881 | struct Member *mem = grp->member; | ||
1882 | |||
1883 | struct GNUNET_CONTAINER_MultiHashMap * | ||
1884 | grp_replay_req = GNUNET_CONTAINER_multihashmap_get (replay_req_client, | ||
1885 | &grp->pub_key_hash); | ||
1886 | if (NULL == grp_replay_req) | ||
1887 | { | ||
1888 | grp_replay_req = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO); | ||
1889 | GNUNET_CONTAINER_multihashmap_put (replay_req_client, | ||
1890 | &grp->pub_key_hash, grp_replay_req, | ||
1891 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); | ||
1892 | } | ||
1893 | |||
1894 | struct GNUNET_HashCode key_hash; | ||
1895 | replay_key_hash (rep->fragment_id, rep->message_id, rep->fragment_offset, | ||
1896 | rep->flags, &key_hash); | ||
1897 | GNUNET_CONTAINER_multihashmap_put (grp_replay_req, &key_hash, client, | ||
1898 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); | ||
1899 | |||
1900 | if (0 == | ||
1901 | client_send_origin (&grp->pub_key_hash, | ||
1902 | GNUNET_MQ_msg_copy (&rep->header))) | ||
1903 | { /* No local origin, replay from remote members / origin. */ | ||
1904 | if (NULL != mem->origin_channel) | ||
1905 | { | ||
1906 | cadet_send_channel (mem->origin_channel, &rep->header); | ||
1907 | } | ||
1908 | else | ||
1909 | { | ||
1910 | /* FIXME: not yet connected to origin */ | ||
1911 | |||
1912 | GNUNET_assert (0); | ||
1913 | GNUNET_SERVICE_client_drop (client); | ||
1914 | return; | ||
1915 | } | ||
1916 | } | ||
1917 | GNUNET_SERVICE_client_continue (client); | ||
1918 | } | ||
1919 | |||
1920 | |||
1921 | static int | ||
1922 | cadet_send_replay_response_cb (void *cls, | ||
1923 | const struct GNUNET_HashCode *key_hash, | ||
1924 | void *value) | ||
1925 | { | ||
1926 | struct Channel *chn = value; | ||
1927 | struct GNUNET_MessageHeader *msg = cls; | ||
1928 | |||
1929 | cadet_send_channel (chn, msg); | ||
1930 | return GNUNET_OK; | ||
1931 | } | ||
1932 | |||
1933 | |||
1934 | static int | ||
1935 | client_send_replay_response_cb (void *cls, | ||
1936 | const struct GNUNET_HashCode *key_hash, | ||
1937 | void *value) | ||
1938 | { | ||
1939 | struct GNUNET_SERVICE_Client *client = value; | ||
1940 | struct GNUNET_MessageHeader *msg = cls; | ||
1941 | |||
1942 | client_send (client, msg); | ||
1943 | return GNUNET_OK; | ||
1944 | } | ||
1945 | |||
1946 | |||
1947 | static int | ||
1948 | check_client_replay_response_end (void *cls, | ||
1949 | const struct MulticastReplayResponseMessage *res) | ||
1950 | { | ||
1951 | return GNUNET_OK; | ||
1952 | } | ||
1953 | |||
1954 | |||
1955 | /** | ||
1956 | * End of replay response from a client. | ||
1957 | */ | ||
1958 | static void | ||
1959 | handle_client_replay_response_end (void *cls, | ||
1960 | const struct MulticastReplayResponseMessage *res) | ||
1961 | { | ||
1962 | struct Client *c = cls; | ||
1963 | struct GNUNET_SERVICE_Client *client = c->client; | ||
1964 | struct Group *grp = c->group; | ||
1965 | |||
1966 | if (NULL == grp) | ||
1967 | { | ||
1968 | GNUNET_break (0); | ||
1969 | GNUNET_SERVICE_client_drop (client); | ||
1970 | return; | ||
1971 | } | ||
1972 | GNUNET_assert (GNUNET_NO == grp->is_disconnected); | ||
1973 | |||
1974 | struct GNUNET_HashCode key_hash; | ||
1975 | replay_key_hash (res->fragment_id, res->message_id, res->fragment_offset, | ||
1976 | res->flags, &key_hash); | ||
1977 | |||
1978 | struct GNUNET_CONTAINER_MultiHashMap * | ||
1979 | grp_replay_req_cadet = GNUNET_CONTAINER_multihashmap_get (replay_req_cadet, | ||
1980 | &grp->pub_key_hash); | ||
1981 | if (NULL != grp_replay_req_cadet) | ||
1982 | { | ||
1983 | GNUNET_CONTAINER_multihashmap_remove_all (grp_replay_req_cadet, &key_hash); | ||
1984 | } | ||
1985 | struct GNUNET_CONTAINER_MultiHashMap * | ||
1986 | grp_replay_req_client = GNUNET_CONTAINER_multihashmap_get (replay_req_client, | ||
1987 | &grp->pub_key_hash); | ||
1988 | if (NULL != grp_replay_req_client) | ||
1989 | { | ||
1990 | GNUNET_CONTAINER_multihashmap_remove_all (grp_replay_req_client, &key_hash); | ||
1991 | } | ||
1992 | GNUNET_SERVICE_client_continue (client); | ||
1993 | } | ||
1994 | |||
1995 | |||
1996 | static int | ||
1997 | check_client_replay_response (void *cls, | ||
1998 | const struct MulticastReplayResponseMessage *res) | ||
1999 | { | ||
2000 | const struct GNUNET_MessageHeader *msg; | ||
2001 | if (GNUNET_MULTICAST_REC_OK == res->error_code) | ||
2002 | { | ||
2003 | msg = GNUNET_MQ_extract_nested_mh (res); | ||
2004 | if (NULL == msg) | ||
2005 | { | ||
2006 | return GNUNET_SYSERR; | ||
2007 | } | ||
2008 | } | ||
2009 | return GNUNET_OK; | ||
2010 | } | ||
2011 | |||
2012 | |||
2013 | /** | ||
2014 | * Incoming replay response from a client. | ||
2015 | * | ||
2016 | * Respond with a multicast message on success, or otherwise with an error code. | ||
2017 | */ | ||
2018 | static void | ||
2019 | handle_client_replay_response (void *cls, | ||
2020 | const struct MulticastReplayResponseMessage *res) | ||
2021 | { | ||
2022 | struct Client *c = cls; | ||
2023 | struct GNUNET_SERVICE_Client *client = c->client; | ||
2024 | struct Group *grp = c->group; | ||
2025 | |||
2026 | if (NULL == grp) | ||
2027 | { | ||
2028 | GNUNET_break (0); | ||
2029 | GNUNET_SERVICE_client_drop (client); | ||
2030 | return; | ||
2031 | } | ||
2032 | GNUNET_assert (GNUNET_NO == grp->is_disconnected); | ||
2033 | |||
2034 | const struct GNUNET_MessageHeader *msg = &res->header; | ||
2035 | if (GNUNET_MULTICAST_REC_OK == res->error_code) | ||
2036 | { | ||
2037 | msg = GNUNET_MQ_extract_nested_mh (res); | ||
2038 | } | ||
2039 | |||
2040 | struct GNUNET_HashCode key_hash; | ||
2041 | replay_key_hash (res->fragment_id, res->message_id, res->fragment_offset, | ||
2042 | res->flags, &key_hash); | ||
2043 | |||
2044 | struct GNUNET_CONTAINER_MultiHashMap * | ||
2045 | grp_replay_req_cadet = GNUNET_CONTAINER_multihashmap_get (replay_req_cadet, | ||
2046 | &grp->pub_key_hash); | ||
2047 | if (NULL != grp_replay_req_cadet) | ||
2048 | { | ||
2049 | GNUNET_CONTAINER_multihashmap_get_multiple (grp_replay_req_cadet, &key_hash, | ||
2050 | cadet_send_replay_response_cb, | ||
2051 | (void *) msg); | ||
2052 | } | ||
2053 | if (GNUNET_MULTICAST_REC_OK == res->error_code) | ||
2054 | { | ||
2055 | struct GNUNET_CONTAINER_MultiHashMap * | ||
2056 | grp_replay_req_client = GNUNET_CONTAINER_multihashmap_get (replay_req_client, | ||
2057 | &grp->pub_key_hash); | ||
2058 | if (NULL != grp_replay_req_client) | ||
2059 | { | ||
2060 | GNUNET_CONTAINER_multihashmap_get_multiple (grp_replay_req_client, &key_hash, | ||
2061 | client_send_replay_response_cb, | ||
2062 | (void *) msg); | ||
2063 | } | ||
2064 | } | ||
2065 | else | ||
2066 | { | ||
2067 | handle_client_replay_response_end (c, res); | ||
2068 | return; | ||
2069 | } | ||
2070 | GNUNET_SERVICE_client_continue (client); | ||
2071 | } | ||
2072 | |||
2073 | |||
2074 | /** | ||
2075 | * A new client connected. | ||
2076 | * | ||
2077 | * @param cls NULL | ||
2078 | * @param client client to add | ||
2079 | * @param mq message queue for @a client | ||
2080 | * @return @a client | ||
2081 | */ | ||
2082 | static void * | ||
2083 | client_notify_connect (void *cls, | ||
2084 | struct GNUNET_SERVICE_Client *client, | ||
2085 | struct GNUNET_MQ_Handle *mq) | ||
2086 | { | ||
2087 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client connected: %p\n", client); | ||
2088 | /* FIXME: send connect ACK */ | ||
2089 | |||
2090 | struct Client *c = GNUNET_new (struct Client); | ||
2091 | c->client = client; | ||
2092 | |||
2093 | return c; | ||
2094 | } | ||
2095 | |||
2096 | |||
2097 | /** | ||
2098 | * Called whenever a client is disconnected. | ||
2099 | * Frees our resources associated with that client. | ||
2100 | * | ||
2101 | * @param cls closure | ||
2102 | * @param client identification of the client | ||
2103 | * @param app_ctx must match @a client | ||
2104 | */ | ||
2105 | static void | ||
2106 | client_notify_disconnect (void *cls, | ||
2107 | struct GNUNET_SERVICE_Client *client, | ||
2108 | void *app_ctx) | ||
2109 | { | ||
2110 | struct Client *c = app_ctx; | ||
2111 | struct Group *grp = c->group; | ||
2112 | GNUNET_free (c); | ||
2113 | |||
2114 | if (NULL == grp) | ||
2115 | { | ||
2116 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
2117 | "%p User context is NULL in client_disconnect()\n", grp); | ||
2118 | GNUNET_break (0); | ||
2119 | return; | ||
2120 | } | ||
2121 | |||
2122 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2123 | "%p Client (%s) disconnected from group %s\n", | ||
2124 | grp, (GNUNET_YES == grp->is_origin) ? "origin" : "member", | ||
2125 | GNUNET_h2s (&grp->pub_key_hash)); | ||
2126 | |||
2127 | // FIXME (due to protocol change): here we must not remove all clients, | ||
2128 | // only the one we were notified about! | ||
2129 | struct ClientList *cl = grp->clients_head; | ||
2130 | while (NULL != cl) | ||
2131 | { | ||
2132 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2133 | "iterating clients for group %p\n", | ||
2134 | grp); | ||
2135 | if (cl->client == client) | ||
2136 | { | ||
2137 | GNUNET_CONTAINER_DLL_remove (grp->clients_head, grp->clients_tail, cl); | ||
2138 | GNUNET_free (cl); | ||
2139 | break; | ||
2140 | } | ||
2141 | cl = cl->next; | ||
2142 | } | ||
2143 | |||
2144 | while (GNUNET_YES == replay_req_remove_client (grp, client)); | ||
2145 | |||
2146 | if (NULL == grp->clients_head) | ||
2147 | { /* Last client disconnected. */ | ||
2148 | cleanup_group (grp); | ||
2149 | } | ||
2150 | } | ||
2151 | |||
2152 | |||
2153 | /** | ||
2154 | * Service started. | ||
2155 | * | ||
2156 | * @param cls closure | ||
2157 | * @param server the initialized server | ||
2158 | * @param cfg configuration to use | ||
2159 | */ | ||
2160 | static void | ||
2161 | run (void *cls, | ||
2162 | const struct GNUNET_CONFIGURATION_Handle *c, | ||
2163 | struct GNUNET_SERVICE_Handle *svc) | ||
2164 | { | ||
2165 | cfg = c; | ||
2166 | service = svc; | ||
2167 | GNUNET_CRYPTO_get_peer_identity (cfg, &this_peer); | ||
2168 | |||
2169 | stats = GNUNET_STATISTICS_create ("multicast", cfg); | ||
2170 | origins = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES); | ||
2171 | members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES); | ||
2172 | group_members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO); | ||
2173 | channels_in = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES); | ||
2174 | channels_out = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES); | ||
2175 | replay_req_cadet = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO); | ||
2176 | replay_req_client = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO); | ||
2177 | |||
2178 | cadet = GNUNET_CADET_connect (cfg); | ||
2179 | |||
2180 | GNUNET_assert (NULL != cadet); | ||
2181 | |||
2182 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
2183 | NULL); | ||
2184 | } | ||
2185 | |||
2186 | |||
2187 | /** | ||
2188 | * Define "main" method using service macro. | ||
2189 | */ | ||
2190 | GNUNET_SERVICE_MAIN | ||
2191 | ("multicast", | ||
2192 | GNUNET_SERVICE_OPTION_NONE, | ||
2193 | &run, | ||
2194 | &client_notify_connect, | ||
2195 | &client_notify_disconnect, | ||
2196 | NULL, | ||
2197 | GNUNET_MQ_hd_fixed_size (client_origin_start, | ||
2198 | GNUNET_MESSAGE_TYPE_MULTICAST_ORIGIN_START, | ||
2199 | struct MulticastOriginStartMessage, | ||
2200 | NULL), | ||
2201 | GNUNET_MQ_hd_var_size (client_member_join, | ||
2202 | GNUNET_MESSAGE_TYPE_MULTICAST_MEMBER_JOIN, | ||
2203 | struct MulticastMemberJoinMessage, | ||
2204 | NULL), | ||
2205 | GNUNET_MQ_hd_var_size (client_join_decision, | ||
2206 | GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION, | ||
2207 | struct MulticastJoinDecisionMessageHeader, | ||
2208 | NULL), | ||
2209 | GNUNET_MQ_hd_fixed_size (client_part_request, | ||
2210 | GNUNET_MESSAGE_TYPE_MULTICAST_PART_REQUEST, | ||
2211 | struct GNUNET_MessageHeader, | ||
2212 | NULL), | ||
2213 | GNUNET_MQ_hd_var_size (client_multicast_message, | ||
2214 | GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE, | ||
2215 | struct GNUNET_MULTICAST_MessageHeader, | ||
2216 | NULL), | ||
2217 | GNUNET_MQ_hd_var_size (client_multicast_request, | ||
2218 | GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST, | ||
2219 | struct GNUNET_MULTICAST_RequestHeader, | ||
2220 | NULL), | ||
2221 | GNUNET_MQ_hd_fixed_size (client_replay_request, | ||
2222 | GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST, | ||
2223 | struct MulticastReplayRequestMessage, | ||
2224 | NULL), | ||
2225 | GNUNET_MQ_hd_var_size (client_replay_response, | ||
2226 | GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE, | ||
2227 | struct MulticastReplayResponseMessage, | ||
2228 | NULL), | ||
2229 | GNUNET_MQ_hd_var_size (client_replay_response_end, | ||
2230 | GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE_END, | ||
2231 | struct MulticastReplayResponseMessage, | ||
2232 | NULL)); | ||
2233 | |||
2234 | /* end of gnunet-service-multicast.c */ | ||