aboutsummaryrefslogtreecommitdiff
path: root/src/fs/gnunet-service-fs_cadet_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs/gnunet-service-fs_cadet_client.c')
-rw-r--r--src/fs/gnunet-service-fs_cadet_client.c766
1 files changed, 766 insertions, 0 deletions
diff --git a/src/fs/gnunet-service-fs_cadet_client.c b/src/fs/gnunet-service-fs_cadet_client.c
new file mode 100644
index 000000000..dde7aba48
--- /dev/null
+++ b/src/fs/gnunet-service-fs_cadet_client.c
@@ -0,0 +1,766 @@
1/*
2 This file is part of GNUnet.
3 (C) 2012, 2013 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file fs/gnunet-service-fs_cadet_client.c
23 * @brief non-anonymous file-transfer
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - PORT is set to old application type, unsure if we should keep
28 * it that way (fine for now)
29 */
30#include "platform.h"
31#include "gnunet_constants.h"
32#include "gnunet_util_lib.h"
33#include "gnunet_cadet_service.h"
34#include "gnunet_protocols.h"
35#include "gnunet_applications.h"
36#include "gnunet-service-fs.h"
37#include "gnunet-service-fs_indexing.h"
38#include "gnunet-service-fs_cadet.h"
39
40
41/**
42 * After how long do we reset connections without replies?
43 */
44#define CLIENT_RETRY_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
45
46
47/**
48 * Handle for a cadet to another peer.
49 */
50struct CadetHandle;
51
52
53/**
54 * Handle for a request that is going out via cadet API.
55 */
56struct GSF_CadetRequest
57{
58
59 /**
60 * DLL.
61 */
62 struct GSF_CadetRequest *next;
63
64 /**
65 * DLL.
66 */
67 struct GSF_CadetRequest *prev;
68
69 /**
70 * Which cadet is this request associated with?
71 */
72 struct CadetHandle *mh;
73
74 /**
75 * Function to call with the result.
76 */
77 GSF_CadetReplyProcessor proc;
78
79 /**
80 * Closure for 'proc'
81 */
82 void *proc_cls;
83
84 /**
85 * Query to transmit to the other peer.
86 */
87 struct GNUNET_HashCode query;
88
89 /**
90 * Desired type for the reply.
91 */
92 enum GNUNET_BLOCK_Type type;
93
94 /**
95 * Did we transmit this request already? #GNUNET_YES if we are
96 * in the 'waiting_map', #GNUNET_NO if we are in the 'pending' DLL.
97 */
98 int was_transmitted;
99};
100
101
102/**
103 * Handle for a cadet to another peer.
104 */
105struct CadetHandle
106{
107 /**
108 * Head of DLL of pending requests on this cadet.
109 */
110 struct GSF_CadetRequest *pending_head;
111
112 /**
113 * Tail of DLL of pending requests on this cadet.
114 */
115 struct GSF_CadetRequest *pending_tail;
116
117 /**
118 * Map from query to `struct GSF_CadetRequest`s waiting for
119 * a reply.
120 */
121 struct GNUNET_CONTAINER_MultiHashMap *waiting_map;
122
123 /**
124 * Channel to the other peer.
125 */
126 struct GNUNET_CADET_Channel *channel;
127
128 /**
129 * Handle for active write operation, or NULL.
130 */
131 struct GNUNET_CADET_TransmitHandle *wh;
132
133 /**
134 * Which peer does this cadet go to?
135 */
136 struct GNUNET_PeerIdentity target;
137
138 /**
139 * Task to kill inactive cadets (we keep them around for
140 * a few seconds to give the application a chance to give
141 * us another query).
142 */
143 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
144
145 /**
146 * Task to reset cadets that had errors (asynchronously,
147 * as we may not be able to do it immediately during a
148 * callback from the cadet API).
149 */
150 GNUNET_SCHEDULER_TaskIdentifier reset_task;
151
152};
153
154
155/**
156 * Cadet channel for creating outbound channels.
157 */
158static struct GNUNET_CADET_Handle *cadet_handle;
159
160/**
161 * Map from peer identities to 'struct CadetHandles' with cadet
162 * channels to those peers.
163 */
164static struct GNUNET_CONTAINER_MultiPeerMap *cadet_map;
165
166
167/* ********************* client-side code ************************* */
168
169
170/**
171 * Transmit pending requests via the cadet.
172 *
173 * @param mh cadet to process
174 */
175static void
176transmit_pending (struct CadetHandle *mh);
177
178
179/**
180 * Iterator called on each entry in a waiting map to
181 * move it back to the pending list.
182 *
183 * @param cls the `struct CadetHandle`
184 * @param key the key of the entry in the map (the query)
185 * @param value the `struct GSF_CadetRequest` to move to pending
186 * @return #GNUNET_YES (continue to iterate)
187 */
188static int
189move_to_pending (void *cls,
190 const struct GNUNET_HashCode *key,
191 void *value)
192{
193 struct CadetHandle *mh = cls;
194 struct GSF_CadetRequest *sr = value;
195
196 GNUNET_assert (GNUNET_YES ==
197 GNUNET_CONTAINER_multihashmap_remove (mh->waiting_map,
198 key,
199 value));
200 GNUNET_CONTAINER_DLL_insert (mh->pending_head,
201 mh->pending_tail,
202 sr);
203 sr->was_transmitted = GNUNET_NO;
204 return GNUNET_YES;
205}
206
207
208/**
209 * We had a serious error, tear down and re-create cadet from scratch.
210 *
211 * @param mh cadet to reset
212 */
213static void
214reset_cadet (struct CadetHandle *mh)
215{
216 struct GNUNET_CADET_Channel *channel = mh->channel;
217
218 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
219 "Resetting cadet channel to %s\n",
220 GNUNET_i2s (&mh->target));
221 mh->channel = NULL;
222 if (NULL != channel)
223 GNUNET_CADET_channel_destroy (channel);
224 GNUNET_CONTAINER_multihashmap_iterate (mh->waiting_map,
225 &move_to_pending,
226 mh);
227 mh->channel = GNUNET_CADET_channel_create (cadet_handle,
228 mh,
229 &mh->target,
230 GNUNET_APPLICATION_TYPE_FS_BLOCK_TRANSFER,
231 GNUNET_CADET_OPTION_RELIABLE);
232 transmit_pending (mh);
233}
234
235
236/**
237 * Task called when it is time to destroy an inactive cadet channel.
238 *
239 * @param cls the `struct CadetHandle` to tear down
240 * @param tc scheduler context, unused
241 */
242static void
243cadet_timeout (void *cls,
244 const struct GNUNET_SCHEDULER_TaskContext *tc)
245{
246 struct CadetHandle *mh = cls;
247 struct GNUNET_CADET_Channel *tun;
248
249 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
250 "Timeout on cadet channel to %s\n",
251 GNUNET_i2s (&mh->target));
252 mh->timeout_task = GNUNET_SCHEDULER_NO_TASK;
253 tun = mh->channel;
254 mh->channel = NULL;
255 GNUNET_CADET_channel_destroy (tun);
256}
257
258
259/**
260 * Task called when it is time to reset an cadet.
261 *
262 * @param cls the `struct CadetHandle` to tear down
263 * @param tc scheduler context, unused
264 */
265static void
266reset_cadet_task (void *cls,
267 const struct GNUNET_SCHEDULER_TaskContext *tc)
268{
269 struct CadetHandle *mh = cls;
270
271 mh->reset_task = GNUNET_SCHEDULER_NO_TASK;
272 reset_cadet (mh);
273}
274
275
276/**
277 * We had a serious error, tear down and re-create cadet from scratch,
278 * but do so asynchronously.
279 *
280 * @param mh cadet to reset
281 */
282static void
283reset_cadet_async (struct CadetHandle *mh)
284{
285 if (GNUNET_SCHEDULER_NO_TASK != mh->reset_task)
286 GNUNET_SCHEDULER_cancel (mh->reset_task);
287 mh->reset_task = GNUNET_SCHEDULER_add_now (&reset_cadet_task,
288 mh);
289}
290
291
292/**
293 * Functions of this signature are called whenever we are ready to transmit
294 * query via a cadet.
295 *
296 * @param cls the struct CadetHandle for which we did the write call
297 * @param size the number of bytes that can be written to @a buf
298 * @param buf where to write the message
299 * @return number of bytes written to @a buf
300 */
301static size_t
302transmit_sqm (void *cls,
303 size_t size,
304 void *buf)
305{
306 struct CadetHandle *mh = cls;
307 struct CadetQueryMessage sqm;
308 struct GSF_CadetRequest *sr;
309
310 mh->wh = NULL;
311 if (NULL == buf)
312 {
313 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
314 "Cadet channel to %s failed during transmission attempt, rebuilding\n",
315 GNUNET_i2s (&mh->target));
316 reset_cadet_async (mh);
317 return 0;
318 }
319 sr = mh->pending_head;
320 if (NULL == sr)
321 return 0;
322 GNUNET_assert (size >= sizeof (struct CadetQueryMessage));
323 GNUNET_CONTAINER_DLL_remove (mh->pending_head,
324 mh->pending_tail,
325 sr);
326 GNUNET_assert (GNUNET_OK ==
327 GNUNET_CONTAINER_multihashmap_put (mh->waiting_map,
328 &sr->query,
329 sr,
330 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
331 sr->was_transmitted = GNUNET_YES;
332 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
333 "Sending query for %s via cadet to %s\n",
334 GNUNET_h2s (&sr->query),
335 GNUNET_i2s (&mh->target));
336 sqm.header.size = htons (sizeof (sqm));
337 sqm.header.type = htons (GNUNET_MESSAGE_TYPE_FS_CADET_QUERY);
338 sqm.type = htonl (sr->type);
339 sqm.query = sr->query;
340 memcpy (buf, &sqm, sizeof (sqm));
341 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
342 "Successfully transmitted %u bytes via cadet to %s\n",
343 (unsigned int) size,
344 GNUNET_i2s (&mh->target));
345 transmit_pending (mh);
346 return sizeof (sqm);
347}
348
349
350/**
351 * Transmit pending requests via the cadet.
352 *
353 * @param mh cadet to process
354 */
355static void
356transmit_pending (struct CadetHandle *mh)
357{
358 if (NULL == mh->channel)
359 return;
360 if (NULL != mh->wh)
361 return;
362 mh->wh = GNUNET_CADET_notify_transmit_ready (mh->channel, GNUNET_YES /* allow cork */,
363 GNUNET_TIME_UNIT_FOREVER_REL,
364 sizeof (struct CadetQueryMessage),
365 &transmit_sqm, mh);
366}
367
368
369/**
370 * Closure for handle_reply().
371 */
372struct HandleReplyClosure
373{
374
375 /**
376 * Reply payload.
377 */
378 const void *data;
379
380 /**
381 * Expiration time for the block.
382 */
383 struct GNUNET_TIME_Absolute expiration;
384
385 /**
386 * Number of bytes in 'data'.
387 */
388 size_t data_size;
389
390 /**
391 * Type of the block.
392 */
393 enum GNUNET_BLOCK_Type type;
394
395 /**
396 * Did we have a matching query?
397 */
398 int found;
399};
400
401
402/**
403 * Iterator called on each entry in a waiting map to
404 * process a result.
405 *
406 * @param cls the `struct HandleReplyClosure`
407 * @param key the key of the entry in the map (the query)
408 * @param value the `struct GSF_CadetRequest` to handle result for
409 * @return #GNUNET_YES (continue to iterate)
410 */
411static int
412handle_reply (void *cls,
413 const struct GNUNET_HashCode *key,
414 void *value)
415{
416 struct HandleReplyClosure *hrc = cls;
417 struct GSF_CadetRequest *sr = value;
418
419 sr->proc (sr->proc_cls,
420 hrc->type,
421 hrc->expiration,
422 hrc->data_size,
423 hrc->data);
424 sr->proc = NULL;
425 GSF_cadet_query_cancel (sr);
426 hrc->found = GNUNET_YES;
427 return GNUNET_YES;
428}
429
430
431/**
432 * Functions with this signature are called whenever a complete reply
433 * is received.
434 *
435 * @param cls closure with the `struct CadetHandle`
436 * @param channel channel handle
437 * @param channel_ctx channel context
438 * @param message the actual message
439 * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing
440 */
441static int
442reply_cb (void *cls,
443 struct GNUNET_CADET_Channel *channel,
444 void **channel_ctx,
445 const struct GNUNET_MessageHeader *message)
446{
447 struct CadetHandle *mh = *channel_ctx;
448 const struct CadetReplyMessage *srm;
449 struct HandleReplyClosure hrc;
450 uint16_t msize;
451 enum GNUNET_BLOCK_Type type;
452 struct GNUNET_HashCode query;
453
454 msize = ntohs (message->size);
455 if (sizeof (struct CadetReplyMessage) > msize)
456 {
457 GNUNET_break_op (0);
458 reset_cadet_async (mh);
459 return GNUNET_SYSERR;
460 }
461 srm = (const struct CadetReplyMessage *) message;
462 msize -= sizeof (struct CadetReplyMessage);
463 type = (enum GNUNET_BLOCK_Type) ntohl (srm->type);
464 if (GNUNET_YES !=
465 GNUNET_BLOCK_get_key (GSF_block_ctx,
466 type,
467 &srm[1], msize, &query))
468 {
469 GNUNET_break_op (0);
470 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
471 "Received bogus reply of type %u with %u bytes via cadet from peer %s\n",
472 type,
473 msize,
474 GNUNET_i2s (&mh->target));
475 reset_cadet_async (mh);
476 return GNUNET_SYSERR;
477 }
478 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
479 "Received reply `%s' via cadet from peer %s\n",
480 GNUNET_h2s (&query),
481 GNUNET_i2s (&mh->target));
482 GNUNET_CADET_receive_done (channel);
483 GNUNET_STATISTICS_update (GSF_stats,
484 gettext_noop ("# replies received via cadet"), 1,
485 GNUNET_NO);
486 hrc.data = &srm[1];
487 hrc.data_size = msize;
488 hrc.expiration = GNUNET_TIME_absolute_ntoh (srm->expiration);
489 hrc.type = type;
490 hrc.found = GNUNET_NO;
491 GNUNET_CONTAINER_multihashmap_get_multiple (mh->waiting_map,
492 &query,
493 &handle_reply,
494 &hrc);
495 if (GNUNET_NO == hrc.found)
496 {
497 GNUNET_STATISTICS_update (GSF_stats,
498 gettext_noop ("# replies received via cadet dropped"), 1,
499 GNUNET_NO);
500 return GNUNET_OK;
501 }
502 return GNUNET_OK;
503}
504
505
506/**
507 * Get (or create) a cadet to talk to the given peer.
508 *
509 * @param target peer we want to communicate with
510 */
511static struct CadetHandle *
512get_cadet (const struct GNUNET_PeerIdentity *target)
513{
514 struct CadetHandle *mh;
515
516 mh = GNUNET_CONTAINER_multipeermap_get (cadet_map,
517 target);
518 if (NULL != mh)
519 {
520 if (GNUNET_SCHEDULER_NO_TASK != mh->timeout_task)
521 {
522 GNUNET_SCHEDULER_cancel (mh->timeout_task);
523 mh->timeout_task = GNUNET_SCHEDULER_NO_TASK;
524 }
525 return mh;
526 }
527 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
528 "Creating cadet channel to %s\n",
529 GNUNET_i2s (target));
530 mh = GNUNET_new (struct CadetHandle);
531 mh->reset_task = GNUNET_SCHEDULER_add_delayed (CLIENT_RETRY_TIMEOUT,
532 &reset_cadet_task,
533 mh);
534 mh->waiting_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_YES);
535 mh->target = *target;
536 GNUNET_assert (GNUNET_OK ==
537 GNUNET_CONTAINER_multipeermap_put (cadet_map,
538 &mh->target,
539 mh,
540 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
541 mh->channel = GNUNET_CADET_channel_create (cadet_handle,
542 mh,
543 &mh->target,
544 GNUNET_APPLICATION_TYPE_FS_BLOCK_TRANSFER,
545 GNUNET_CADET_OPTION_RELIABLE);
546 GNUNET_assert (mh ==
547 GNUNET_CONTAINER_multipeermap_get (cadet_map,
548 target));
549 return mh;
550}
551
552
553/**
554 * Look for a block by directly contacting a particular peer.
555 *
556 * @param target peer that should have the block
557 * @param query hash to query for the block
558 * @param type desired type for the block
559 * @param proc function to call with result
560 * @param proc_cls closure for @a proc
561 * @return handle to cancel the operation
562 */
563struct GSF_CadetRequest *
564GSF_cadet_query (const struct GNUNET_PeerIdentity *target,
565 const struct GNUNET_HashCode *query,
566 enum GNUNET_BLOCK_Type type,
567 GSF_CadetReplyProcessor proc, void *proc_cls)
568{
569 struct CadetHandle *mh;
570 struct GSF_CadetRequest *sr;
571
572 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
573 "Preparing to send query for %s via cadet to %s\n",
574 GNUNET_h2s (query),
575 GNUNET_i2s (target));
576 mh = get_cadet (target);
577 sr = GNUNET_new (struct GSF_CadetRequest);
578 sr->mh = mh;
579 sr->proc = proc;
580 sr->proc_cls = proc_cls;
581 sr->type = type;
582 sr->query = *query;
583 GNUNET_CONTAINER_DLL_insert (mh->pending_head,
584 mh->pending_tail,
585 sr);
586 transmit_pending (mh);
587 return sr;
588}
589
590
591/**
592 * Cancel an active request; must not be called after 'proc'
593 * was calld.
594 *
595 * @param sr request to cancel
596 */
597void
598GSF_cadet_query_cancel (struct GSF_CadetRequest *sr)
599{
600 struct CadetHandle *mh = sr->mh;
601 GSF_CadetReplyProcessor p;
602
603 p = sr->proc;
604 sr->proc = NULL;
605 if (NULL != p)
606 {
607 /* signal failure / cancellation to callback */
608 p (sr->proc_cls, GNUNET_BLOCK_TYPE_ANY,
609 GNUNET_TIME_UNIT_ZERO_ABS,
610 0, NULL);
611 }
612 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
613 "Cancelled query for %s via cadet to %s\n",
614 GNUNET_h2s (&sr->query),
615 GNUNET_i2s (&sr->mh->target));
616 if (GNUNET_YES == sr->was_transmitted)
617 GNUNET_assert (GNUNET_OK ==
618 GNUNET_CONTAINER_multihashmap_remove (mh->waiting_map,
619 &sr->query,
620 sr));
621 else
622 GNUNET_CONTAINER_DLL_remove (mh->pending_head,
623 mh->pending_tail,
624 sr);
625 GNUNET_free (sr);
626 if ( (0 == GNUNET_CONTAINER_multihashmap_size (mh->waiting_map)) &&
627 (NULL == mh->pending_head) )
628 mh->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
629 &cadet_timeout,
630 mh);
631}
632
633
634/**
635 * Iterator called on each entry in a waiting map to
636 * call the 'proc' continuation and release associated
637 * resources.
638 *
639 * @param cls the `struct CadetHandle`
640 * @param key the key of the entry in the map (the query)
641 * @param value the `struct GSF_CadetRequest` to clean up
642 * @return #GNUNET_YES (continue to iterate)
643 */
644static int
645free_waiting_entry (void *cls,
646 const struct GNUNET_HashCode *key,
647 void *value)
648{
649 struct GSF_CadetRequest *sr = value;
650
651 GSF_cadet_query_cancel (sr);
652 return GNUNET_YES;
653}
654
655
656/**
657 * Function called by cadet when a client disconnects.
658 * Cleans up our `struct CadetClient` of that channel.
659 *
660 * @param cls NULL
661 * @param channel channel of the disconnecting client
662 * @param channel_ctx our `struct CadetClient`
663 */
664static void
665cleaner_cb (void *cls,
666 const struct GNUNET_CADET_Channel *channel,
667 void *channel_ctx)
668{
669 struct CadetHandle *mh = channel_ctx;
670 struct GSF_CadetRequest *sr;
671
672 if (NULL == mh->channel)
673 return; /* being destroyed elsewhere */
674 GNUNET_assert (channel == mh->channel);
675 mh->channel = NULL;
676 while (NULL != (sr = mh->pending_head))
677 GSF_cadet_query_cancel (sr);
678 /* first remove `mh` from the `cadet_map`, so that if the
679 callback from `free_waiting_entry()` happens to re-issue
680 the request, we don't immediately have it back in the
681 `waiting_map`. */
682 GNUNET_assert (GNUNET_OK ==
683 GNUNET_CONTAINER_multipeermap_remove (cadet_map,
684 &mh->target,
685 mh));
686 GNUNET_CONTAINER_multihashmap_iterate (mh->waiting_map,
687 &free_waiting_entry,
688 mh);
689 if (NULL != mh->wh)
690 GNUNET_CADET_notify_transmit_ready_cancel (mh->wh);
691 if (GNUNET_SCHEDULER_NO_TASK != mh->timeout_task)
692 GNUNET_SCHEDULER_cancel (mh->timeout_task);
693 if (GNUNET_SCHEDULER_NO_TASK != mh->reset_task)
694 GNUNET_SCHEDULER_cancel (mh->reset_task);
695 GNUNET_assert (0 ==
696 GNUNET_CONTAINER_multihashmap_size (mh->waiting_map));
697 GNUNET_CONTAINER_multihashmap_destroy (mh->waiting_map);
698 GNUNET_free (mh);
699}
700
701
702/**
703 * Initialize subsystem for non-anonymous file-sharing.
704 */
705void
706GSF_cadet_start_client ()
707{
708 static const struct GNUNET_CADET_MessageHandler handlers[] = {
709 { &reply_cb, GNUNET_MESSAGE_TYPE_FS_CADET_REPLY, 0 },
710 { NULL, 0, 0 }
711 };
712
713 cadet_map = GNUNET_CONTAINER_multipeermap_create (16, GNUNET_YES);
714 cadet_handle = GNUNET_CADET_connect (GSF_cfg,
715 NULL,
716 NULL,
717 &cleaner_cb,
718 handlers,
719 NULL);
720}
721
722
723/**
724 * Function called on each active cadets to shut them down.
725 *
726 * @param cls NULL
727 * @param key target peer, unused
728 * @param value the `struct CadetHandle` to destroy
729 * @return #GNUNET_YES (continue to iterate)
730 */
731static int
732release_cadets (void *cls,
733 const struct GNUNET_PeerIdentity *key,
734 void *value)
735{
736 struct CadetHandle *mh = value;
737
738 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
739 "Timeout on cadet channel to %s\n",
740 GNUNET_i2s (&mh->target));
741 if (NULL != mh->channel)
742 GNUNET_CADET_channel_destroy (mh->channel);
743 return GNUNET_YES;
744}
745
746
747/**
748 * Shutdown subsystem for non-anonymous file-sharing.
749 */
750void
751GSF_cadet_stop_client ()
752{
753 GNUNET_CONTAINER_multipeermap_iterate (cadet_map,
754 &release_cadets,
755 NULL);
756 GNUNET_CONTAINER_multipeermap_destroy (cadet_map);
757 cadet_map = NULL;
758 if (NULL != cadet_handle)
759 {
760 GNUNET_CADET_disconnect (cadet_handle);
761 cadet_handle = NULL;
762 }
763}
764
765
766/* end of gnunet-service-fs_cadet_client.c */