error handling
[oweals/gnunet.git] / src / core / core_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009-2016 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  * @file core/core_api.c
22  * @brief core service; this is the main API for encrypted P2P
23  *        communications
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_core_service.h"
30 #include "core.h"
31
32 #define LOG(kind, ...) GNUNET_log_from (kind, "core-api", __VA_ARGS__)
33
34
35 /**
36  * Information we track for each peer.
37  */
38 struct PeerRecord
39 {
40   /**
41    * Corresponding CORE handle.
42    */
43   struct GNUNET_CORE_Handle *h;
44
45   /**
46    * Message queue for the peer.
47    */
48   struct GNUNET_MQ_Handle *mq;
49
50   /**
51    * Message we are currently trying to pass to the CORE service
52    * for this peer (from @e mq).
53    */
54   struct GNUNET_MQ_Envelope *env;
55
56   /**
57    * Value the client returned when we connected, used
58    * as the closure in various places.
59    */
60   void *client_cls;
61
62   /**
63    * Peer the record is about.
64    */
65   struct GNUNET_PeerIdentity peer;
66
67   /**
68    * SendMessageRequest ID generator for this peer.
69    */
70   uint16_t smr_id_gen;
71 };
72
73
74 /**
75  * Context for the core service connection.
76  */
77 struct GNUNET_CORE_Handle
78 {
79   /**
80    * Configuration we're using.
81    */
82   const struct GNUNET_CONFIGURATION_Handle *cfg;
83
84   /**
85    * Closure for the various callbacks.
86    */
87   void *cls;
88
89   /**
90    * Function to call once we've handshaked with the core service.
91    */
92   GNUNET_CORE_StartupCallback init;
93
94   /**
95    * Function to call whenever we're notified about a peer connecting.
96    */
97   GNUNET_CORE_ConnectEventHandler connects;
98
99   /**
100    * Function to call whenever we're notified about a peer disconnecting.
101    */
102   GNUNET_CORE_DisconnectEventHandler disconnects;
103
104   /**
105    * Function handlers for messages of particular type.
106    */
107   struct GNUNET_MQ_MessageHandler *handlers;
108
109   /**
110    * Our message queue for transmissions to the service.
111    */
112   struct GNUNET_MQ_Handle *mq;
113
114   /**
115    * Hash map listing all of the peers that we are currently
116    * connected to.
117    */
118   struct GNUNET_CONTAINER_MultiPeerMap *peers;
119
120   /**
121    * Identity of this peer.
122    */
123   struct GNUNET_PeerIdentity me;
124
125   /**
126    * ID of reconnect task (if any).
127    */
128   struct GNUNET_SCHEDULER_Task *reconnect_task;
129
130   /**
131    * Current delay we use for re-trying to connect to core.
132    */
133   struct GNUNET_TIME_Relative retry_backoff;
134
135   /**
136    * Number of entries in the handlers array.
137    */
138   unsigned int hcnt;
139
140   /**
141    * Did we ever get INIT?
142    */
143   int have_init;
144 };
145
146
147 /**
148  * Our current client connection went down.  Clean it up
149  * and try to reconnect!
150  *
151  * @param h our handle to the core service
152  */
153 static void
154 reconnect (struct GNUNET_CORE_Handle *h);
155
156
157 /**
158  * Task schedule to try to re-connect to core.
159  *
160  * @param cls the `struct GNUNET_CORE_Handle`
161  * @param tc task context
162  */
163 static void
164 reconnect_task (void *cls)
165 {
166   struct GNUNET_CORE_Handle *h = cls;
167
168   h->reconnect_task = NULL;
169   LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to CORE service after delay\n");
170   reconnect (h);
171 }
172
173
174 /**
175  * Notify clients about disconnect and free the entry for connected
176  * peer.
177  *
178  * @param cls the `struct GNUNET_CORE_Handle *`
179  * @param key the peer identity (not used)
180  * @param value the `struct PeerRecord` to free.
181  * @return #GNUNET_YES (continue)
182  */
183 static int
184 disconnect_and_free_peer_entry (void *cls,
185                                 const struct GNUNET_PeerIdentity *key,
186                                 void *value)
187 {
188   struct GNUNET_CORE_Handle *h = cls;
189   struct PeerRecord *pr = value;
190
191   GNUNET_assert (pr->h == h);
192   if (NULL != h->disconnects)
193     h->disconnects (h->cls, &pr->peer, pr->client_cls);
194   GNUNET_assert (GNUNET_YES ==
195                  GNUNET_CONTAINER_multipeermap_remove (h->peers, key, pr));
196   GNUNET_MQ_destroy (pr->mq);
197   GNUNET_assert (NULL == pr->mq);
198   if (NULL != pr->env)
199   {
200     GNUNET_MQ_discard (pr->env);
201     pr->env = NULL;
202   }
203   GNUNET_free (pr);
204   return GNUNET_YES;
205 }
206
207
208 /**
209  * Close down any existing connection to the CORE service and
210  * try re-establishing it later.
211  *
212  * @param h our handle
213  */
214 static void
215 reconnect_later (struct GNUNET_CORE_Handle *h)
216 {
217   GNUNET_assert (NULL == h->reconnect_task);
218   if (NULL != h->mq)
219   {
220     GNUNET_MQ_destroy (h->mq);
221     h->mq = NULL;
222   }
223   GNUNET_assert (NULL == h->reconnect_task);
224   h->reconnect_task =
225     GNUNET_SCHEDULER_add_delayed (h->retry_backoff, &reconnect_task, h);
226   GNUNET_CONTAINER_multipeermap_iterate (h->peers,
227                                          &disconnect_and_free_peer_entry,
228                                          h);
229   h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
230 }
231
232
233 /**
234  * Error handler for the message queue to the CORE service.
235  * On errors, we reconnect.
236  *
237  * @param cls closure, a `struct GNUNET_CORE_Handle *`
238  * @param error error code
239  */
240 static void
241 handle_mq_error (void *cls, enum GNUNET_MQ_Error error)
242 {
243   struct GNUNET_CORE_Handle *h = cls;
244
245   LOG (GNUNET_ERROR_TYPE_DEBUG, "MQ ERROR: %d\n", error);
246   reconnect_later (h);
247 }
248
249
250 /**
251  * Implement sending functionality of a message queue for
252  * us sending messages to a peer.
253  *
254  * @param mq the message queue
255  * @param msg the message to send
256  * @param impl_state state of the implementation
257  */
258 static void
259 core_mq_send_impl (struct GNUNET_MQ_Handle *mq,
260                    const struct GNUNET_MessageHeader *msg,
261                    void *impl_state)
262 {
263   struct PeerRecord *pr = impl_state;
264   struct GNUNET_CORE_Handle *h = pr->h;
265   struct SendMessageRequest *smr;
266   struct SendMessage *sm;
267   struct GNUNET_MQ_Envelope *env;
268   uint16_t msize;
269   enum GNUNET_MQ_PriorityPreferences flags;
270
271   if (NULL == h->mq)
272   {
273     /* We're currently reconnecting, pretend this worked */
274     GNUNET_MQ_impl_send_continue (mq);
275     return;
276   }
277   GNUNET_assert (NULL == pr->env);
278   /* extract options from envelope */
279   env = GNUNET_MQ_get_current_envelope (mq);
280   flags = GNUNET_MQ_env_get_options (env);
281
282   /* check message size for sanity */
283   msize = ntohs (msg->size);
284   if (msize >= GNUNET_MAX_MESSAGE_SIZE - sizeof(struct SendMessage))
285   {
286     GNUNET_break (0);
287     GNUNET_MQ_impl_send_continue (mq);
288     return;
289   }
290
291   /* ask core for transmission */
292   LOG (GNUNET_ERROR_TYPE_DEBUG,
293        "Asking core for transmission of %u bytes to `%s'\n",
294        (unsigned int) msize,
295        GNUNET_i2s (&pr->peer));
296   env = GNUNET_MQ_msg (smr, GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST);
297   smr->priority = htonl ((uint32_t) flags);
298   smr->peer = pr->peer;
299   smr->size = htons (msize);
300   smr->smr_id = htons (++pr->smr_id_gen);
301   GNUNET_MQ_send (h->mq, env);
302
303   /* prepare message with actual transmission data */
304   pr->env = GNUNET_MQ_msg_nested_mh (sm, GNUNET_MESSAGE_TYPE_CORE_SEND, msg);
305   sm->priority = htonl ((uint32_t) flags);
306   sm->peer = pr->peer;
307   LOG (GNUNET_ERROR_TYPE_DEBUG,
308        "Calling get_message with buffer of %u bytes\n",
309        (unsigned int) msize);
310 }
311
312
313 /**
314  * Handle destruction of a message queue.  Implementations must not
315  * free @a mq, but should take care of @a impl_state.
316  *
317  * @param mq the message queue to destroy
318  * @param impl_state state of the implementation
319  */
320 static void
321 core_mq_destroy_impl (struct GNUNET_MQ_Handle *mq, void *impl_state)
322 {
323   struct PeerRecord *pr = impl_state;
324
325   GNUNET_assert (mq == pr->mq);
326   pr->mq = NULL;
327 }
328
329
330 /**
331  * Implementation function that cancels the currently sent message.
332  * Should basically undo whatever #mq_send_impl() did.
333  *
334  * @param mq message queue
335  * @param impl_state state specific to the implementation
336  */
337 static void
338 core_mq_cancel_impl (struct GNUNET_MQ_Handle *mq, void *impl_state)
339 {
340   struct PeerRecord *pr = impl_state;
341
342   (void) mq;
343   GNUNET_assert (NULL != pr->env);
344   GNUNET_MQ_discard (pr->env);
345   pr->env = NULL;
346 }
347
348
349 /**
350  * We had an error processing a message we forwarded from a peer to
351  * the CORE service.  We should just complain about it but otherwise
352  * continue processing.
353  *
354  * @param cls closure
355  * @param error error code
356  */
357 static void
358 core_mq_error_handler (void *cls, enum GNUNET_MQ_Error error)
359 {
360   /* struct PeerRecord *pr = cls; */
361   (void) cls;
362   (void) error;
363   GNUNET_break_op (0);
364 }
365
366
367 /**
368  * Add the given peer to the list of our connected peers
369  * and create the respective data structures and notify
370  * the application.
371  *
372  * @param h the core handle
373  * @param peer the peer that is connecting to us
374  */
375 static void
376 connect_peer (struct GNUNET_CORE_Handle *h,
377               const struct GNUNET_PeerIdentity *peer)
378 {
379   struct PeerRecord *pr;
380
381   pr = GNUNET_new (struct PeerRecord);
382   pr->peer = *peer;
383   pr->h = h;
384   GNUNET_assert (GNUNET_YES ==
385                  GNUNET_CONTAINER_multipeermap_put (
386                    h->peers,
387                    &pr->peer,
388                    pr,
389                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
390   pr->mq = GNUNET_MQ_queue_for_callbacks (&core_mq_send_impl,
391                                           &core_mq_destroy_impl,
392                                           &core_mq_cancel_impl,
393                                           pr,
394                                           h->handlers,
395                                           &core_mq_error_handler,
396                                           pr);
397   if (NULL != h->connects)
398   {
399     pr->client_cls = h->connects (h->cls, &pr->peer, pr->mq);
400     GNUNET_MQ_set_handlers_closure (pr->mq, pr->client_cls);
401   }
402 }
403
404
405 /**
406  * Handle  init  reply message  received  from  CORE service.   Notify
407  * application  that we  are now  connected  to the  CORE.  Also  fake
408  * loopback connection.
409  *
410  * @param cls the `struct GNUNET_CORE_Handle`
411  * @param m the init reply
412  */
413 static void
414 handle_init_reply (void *cls, const struct InitReplyMessage *m)
415 {
416   struct GNUNET_CORE_Handle *h = cls;
417   GNUNET_CORE_StartupCallback init;
418
419   GNUNET_break (0 == ntohl (m->reserved));
420   h->retry_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
421   if (NULL != (init = h->init))
422   {
423     /* mark so we don't call init on reconnect */
424     h->init = NULL;
425     h->me = m->my_identity;
426     LOG (GNUNET_ERROR_TYPE_DEBUG,
427          "Connected to core service of peer `%s'.\n",
428          GNUNET_i2s (&h->me));
429     h->have_init = GNUNET_YES;
430     init (h->cls, &h->me);
431   }
432   else
433   {
434     LOG (GNUNET_ERROR_TYPE_DEBUG,
435          "Successfully reconnected to core service.\n");
436     if (GNUNET_NO == h->have_init)
437     {
438       h->me = m->my_identity;
439       h->have_init = GNUNET_YES;
440     }
441     else
442     {
443       GNUNET_break (0 == memcmp (&h->me,
444                                  &m->my_identity,
445                                  sizeof(struct GNUNET_PeerIdentity)));
446     }
447   }
448   /* fake 'connect to self' */
449   connect_peer (h, &h->me);
450 }
451
452
453 /**
454  * Handle connect message received from CORE service.
455  * Notify the application about the new connection.
456  *
457  * @param cls the `struct GNUNET_CORE_Handle`
458  * @param cnm the connect message
459  */
460 static void
461 handle_connect_notify (void *cls, const struct ConnectNotifyMessage *cnm)
462 {
463   struct GNUNET_CORE_Handle *h = cls;
464   struct PeerRecord *pr;
465
466   LOG (GNUNET_ERROR_TYPE_DEBUG,
467        "Received notification about connection from `%s'.\n",
468        GNUNET_i2s (&cnm->peer));
469   if (0 == memcmp (&h->me, &cnm->peer, sizeof(struct GNUNET_PeerIdentity)))
470   {
471     /* connect to self!? */
472     GNUNET_break (0);
473     return;
474   }
475   pr = GNUNET_CONTAINER_multipeermap_get (h->peers, &cnm->peer);
476   if (NULL != pr)
477   {
478     GNUNET_break (0);
479     reconnect_later (h);
480     return;
481   }
482   connect_peer (h, &cnm->peer);
483 }
484
485
486 /**
487  * Handle disconnect message received from CORE service.
488  * Notify the application about the lost connection.
489  *
490  * @param cls the `struct GNUNET_CORE_Handle`
491  * @param dnm message about the disconnect event
492  */
493 static void
494 handle_disconnect_notify (void *cls, const struct DisconnectNotifyMessage *dnm)
495 {
496   struct GNUNET_CORE_Handle *h = cls;
497   struct PeerRecord *pr;
498
499   if (0 == memcmp (&h->me, &dnm->peer, sizeof(struct GNUNET_PeerIdentity)))
500   {
501     /* disconnect from self!? */
502     GNUNET_break (0);
503     return;
504   }
505   GNUNET_break (0 == ntohl (dnm->reserved));
506   LOG (GNUNET_ERROR_TYPE_DEBUG,
507        "Received notification about disconnect from `%s'.\n",
508        GNUNET_i2s (&dnm->peer));
509   pr = GNUNET_CONTAINER_multipeermap_get (h->peers, &dnm->peer);
510   if (NULL == pr)
511   {
512     GNUNET_break (0);
513     reconnect_later (h);
514     return;
515   }
516   disconnect_and_free_peer_entry (h, &pr->peer, pr);
517 }
518
519
520 /**
521  * Check that message received from CORE service is well-formed.
522  *
523  * @param cls the `struct GNUNET_CORE_Handle`
524  * @param ntm the message we got
525  * @return #GNUNET_OK if the message is well-formed
526  */
527 static int
528 check_notify_inbound (void *cls, const struct NotifyTrafficMessage *ntm)
529 {
530   uint16_t msize;
531   const struct GNUNET_MessageHeader *em;
532
533   (void) cls;
534   msize = ntohs (ntm->header.size) - sizeof(struct NotifyTrafficMessage);
535   if (msize < sizeof(struct GNUNET_MessageHeader))
536   {
537     GNUNET_break (0);
538     return GNUNET_SYSERR;
539   }
540   em = (const struct GNUNET_MessageHeader *) &ntm[1];
541   if (msize != ntohs (em->size))
542   {
543     GNUNET_break (0);
544     return GNUNET_SYSERR;
545   }
546   return GNUNET_OK;
547 }
548
549
550 /**
551  * Handle inbound message received from CORE service.  If applicable,
552  * notify the application.
553  *
554  * @param cls the `struct GNUNET_CORE_Handle`
555  * @param ntm the message we got from CORE.
556  */
557 static void
558 handle_notify_inbound (void *cls, const struct NotifyTrafficMessage *ntm)
559 {
560   struct GNUNET_CORE_Handle *h = cls;
561   const struct GNUNET_MessageHeader *em;
562   struct PeerRecord *pr;
563
564   LOG (GNUNET_ERROR_TYPE_DEBUG,
565        "Received inbound message from `%s'.\n",
566        GNUNET_i2s (&ntm->peer));
567   em = (const struct GNUNET_MessageHeader *) &ntm[1];
568   pr = GNUNET_CONTAINER_multipeermap_get (h->peers, &ntm->peer);
569   if (NULL == pr)
570   {
571     GNUNET_break (0);
572     reconnect_later (h);
573     return;
574   }
575   GNUNET_MQ_inject_message (pr->mq, em);
576 }
577
578
579 /**
580  * Handle message received from CORE service notifying us that we are
581  * now allowed to send a message to a peer.  If that message is still
582  * pending, put it into the queue to be transmitted.
583  *
584  * @param cls the `struct GNUNET_CORE_Handle`
585  * @param smr the message we got
586  */
587 static void
588 handle_send_ready (void *cls, const struct SendMessageReady *smr)
589 {
590   struct GNUNET_CORE_Handle *h = cls;
591   struct PeerRecord *pr;
592
593   pr = GNUNET_CONTAINER_multipeermap_get (h->peers, &smr->peer);
594   if (NULL == pr)
595   {
596     GNUNET_break (0);
597     reconnect_later (h);
598     return;
599   }
600   LOG (GNUNET_ERROR_TYPE_DEBUG,
601        "Received notification about transmission readiness to `%s'.\n",
602        GNUNET_i2s (&smr->peer));
603   if (NULL == pr->env)
604   {
605     /* request must have been cancelled between the original request
606      * and the response from CORE, ignore CORE's readiness */
607     return;
608   }
609   if (ntohs (smr->smr_id) != pr->smr_id_gen)
610   {
611     /* READY message is for expired or cancelled message,
612      * ignore! (we should have already sent another request) */
613     return;
614   }
615
616   /* ok, all good, send message out! */
617   GNUNET_MQ_send (h->mq, pr->env);
618   pr->env = NULL;
619   GNUNET_MQ_impl_send_continue (pr->mq);
620 }
621
622
623 /**
624  * Our current client connection went down.  Clean it up and try to
625  * reconnect!
626  *
627  * @param h our handle to the core service
628  */
629 static void
630 reconnect (struct GNUNET_CORE_Handle *h)
631 {
632   struct GNUNET_MQ_MessageHandler handlers[] =
633   { GNUNET_MQ_hd_fixed_size (init_reply,
634                              GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY,
635                              struct InitReplyMessage,
636                              h),
637     GNUNET_MQ_hd_fixed_size (connect_notify,
638                              GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT,
639                              struct ConnectNotifyMessage,
640                              h),
641     GNUNET_MQ_hd_fixed_size (disconnect_notify,
642                              GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT,
643                              struct DisconnectNotifyMessage,
644                              h),
645     GNUNET_MQ_hd_var_size (notify_inbound,
646                            GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND,
647                            struct NotifyTrafficMessage,
648                            h),
649     GNUNET_MQ_hd_fixed_size (send_ready,
650                              GNUNET_MESSAGE_TYPE_CORE_SEND_READY,
651                              struct SendMessageReady,
652                              h),
653     GNUNET_MQ_handler_end () };
654   struct InitMessage *init;
655   struct GNUNET_MQ_Envelope *env;
656   uint16_t *ts;
657
658   GNUNET_assert (NULL == h->mq);
659   h->mq = GNUNET_CLIENT_connect (h->cfg, "core", handlers, &handle_mq_error, h);
660   if (NULL == h->mq)
661   {
662     reconnect_later (h);
663     return;
664   }
665   env = GNUNET_MQ_msg_extra (init,
666                              sizeof(uint16_t) * h->hcnt,
667                              GNUNET_MESSAGE_TYPE_CORE_INIT);
668   LOG (GNUNET_ERROR_TYPE_INFO, "(Re)connecting to CORE service\n");
669   init->options = htonl (0);
670   ts = (uint16_t *) &init[1];
671   for (unsigned int hpos = 0; hpos < h->hcnt; hpos++)
672     ts[hpos] = htons (h->handlers[hpos].type);
673   GNUNET_MQ_send (h->mq, env);
674 }
675
676
677 /**
678  * Connect to the core service.  Note that the connection may complete
679  * (or fail) asynchronously.
680  *
681  * @param cfg configuration to use
682  * @param cls closure for the various callbacks that follow (including handlers in the handlers array)
683  * @param init callback to call once we have successfully
684  *        connected to the core service
685  * @param connects function to call on peer connect, can be NULL
686  * @param disconnects function to call on peer disconnect / timeout, can be NULL
687  * @param handlers callbacks for messages we care about, NULL-terminated
688  * @return handle to the core service (only useful for disconnect until @a init is called);
689  *                NULL on error (in this case, init is never called)
690  */
691 struct GNUNET_CORE_Handle *
692 GNUNET_CORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
693                      void *cls,
694                      GNUNET_CORE_StartupCallback init,
695                      GNUNET_CORE_ConnectEventHandler connects,
696                      GNUNET_CORE_DisconnectEventHandler disconnects,
697                      const struct GNUNET_MQ_MessageHandler *handlers)
698 {
699   struct GNUNET_CORE_Handle *h;
700
701   h = GNUNET_new (struct GNUNET_CORE_Handle);
702   h->cfg = cfg;
703   h->cls = cls;
704   h->init = init;
705   h->connects = connects;
706   h->disconnects = disconnects;
707   h->peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO);
708   h->handlers = GNUNET_MQ_copy_handlers (handlers);
709   h->hcnt = GNUNET_MQ_count_handlers (handlers);
710   GNUNET_assert (h->hcnt <
711                  (GNUNET_MAX_MESSAGE_SIZE - sizeof(struct InitMessage))
712                  / sizeof(uint16_t));
713   LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to CORE service\n");
714   reconnect (h);
715   if (NULL == h->mq)
716   {
717     GNUNET_CORE_disconnect (h);
718     return NULL;
719   }
720   return h;
721 }
722
723
724 /**
725  * Disconnect from the core service.
726  *
727  * @param handle connection to core to disconnect
728  */
729 void
730 GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle)
731 {
732   LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from CORE service\n");
733   GNUNET_CONTAINER_multipeermap_iterate (handle->peers,
734                                          &disconnect_and_free_peer_entry,
735                                          handle);
736   GNUNET_CONTAINER_multipeermap_destroy (handle->peers);
737   handle->peers = NULL;
738   if (NULL != handle->reconnect_task)
739   {
740     GNUNET_SCHEDULER_cancel (handle->reconnect_task);
741     handle->reconnect_task = NULL;
742   }
743   if (NULL != handle->mq)
744   {
745     GNUNET_MQ_destroy (handle->mq);
746     handle->mq = NULL;
747   }
748   GNUNET_free_non_null (handle->handlers);
749   GNUNET_free (handle);
750 }
751
752
753 /**
754  * Obtain the message queue for a connected peer.
755  *
756  * @param h the core handle
757  * @param pid the identity of the peer to check if it has been connected to us
758  * @return NULL if peer is not connected
759  */
760 struct GNUNET_MQ_Handle *
761 GNUNET_CORE_get_mq (const struct GNUNET_CORE_Handle *h,
762                     const struct GNUNET_PeerIdentity *pid)
763 {
764   struct PeerRecord *pr;
765
766   pr = GNUNET_CONTAINER_multipeermap_get (h->peers, pid);
767   if (NULL == pr)
768     return NULL;
769   return pr->mq;
770 }
771
772
773 /* end of core_api.c */