- update fs
[oweals/gnunet.git] / src / fs / gnunet-service-fs_cadet_client.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012, 2013 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file 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  */
50 struct CadetHandle;
51
52
53 /**
54  * Handle for a request that is going out via cadet API.
55  */
56 struct 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  */
105 struct 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  */
158 static struct GNUNET_CADET_Handle *cadet_handle;
159
160 /**
161  * Map from peer identities to 'struct CadetHandles' with cadet
162  * channels to those peers.
163  */
164 static 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  */
175 static void
176 transmit_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  */
188 static int
189 move_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  */
213 static void
214 reset_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  */
242 static void
243 cadet_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  */
265 static void
266 reset_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  */
282 static void
283 reset_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  */
301 static size_t
302 transmit_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  */
355 static void
356 transmit_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  */
372 struct 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  */
411 static int
412 handle_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  */
441 static int
442 reply_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  */
511 static struct CadetHandle *
512 get_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  */
563 struct GSF_CadetRequest *
564 GSF_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  */
597 void
598 GSF_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  */
644 static int
645 free_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  */
664 static void
665 cleaner_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  */
705 void
706 GSF_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  */
731 static int
732 release_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  */
750 void
751 GSF_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 */