2 This file is part of GNUnet.
3 Copyright (C) 2009-2016 GNUnet e.V.
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.
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.
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/>.
19 * @file core/core_api.c
20 * @brief core service; this is the main API for encrypted P2P
22 * @author Christian Grothoff
25 #include "gnunet_util_lib.h"
26 #include "gnunet_constants.h"
27 #include "gnunet_core_service.h"
30 #define LOG(kind,...) GNUNET_log_from (kind, "core-api",__VA_ARGS__)
34 * Information we track for each peer.
40 * Corresponding CORE handle.
42 struct GNUNET_CORE_Handle *h;
45 * Message queue for the peer.
47 struct GNUNET_MQ_Handle *mq;
50 * Message we are currently trying to pass to the CORE service
51 * for this peer (from @e mq).
53 struct GNUNET_MQ_Envelope *env;
56 * Value the client returned when we connected, used
57 * as the closure in various places.
62 * Peer the record is about.
64 struct GNUNET_PeerIdentity peer;
67 * SendMessageRequest ID generator for this peer.
75 * Context for the core service connection.
77 struct GNUNET_CORE_Handle
81 * Configuration we're using.
83 const struct GNUNET_CONFIGURATION_Handle *cfg;
86 * Closure for the various callbacks.
91 * Function to call once we've handshaked with the core service.
93 GNUNET_CORE_StartupCallback init;
96 * Function to call whenever we're notified about a peer connecting.
98 GNUNET_CORE_ConnectEventHandler connects;
101 * Function to call whenever we're notified about a peer disconnecting.
103 GNUNET_CORE_DisconnectEventHandler disconnects;
106 * Function handlers for messages of particular type.
108 struct GNUNET_MQ_MessageHandler *handlers;
111 * Our message queue for transmissions to the service.
113 struct GNUNET_MQ_Handle *mq;
116 * Hash map listing all of the peers that we are currently
119 struct GNUNET_CONTAINER_MultiPeerMap *peers;
122 * Identity of this peer.
124 struct GNUNET_PeerIdentity me;
127 * ID of reconnect task (if any).
129 struct GNUNET_SCHEDULER_Task *reconnect_task;
132 * Current delay we use for re-trying to connect to core.
134 struct GNUNET_TIME_Relative retry_backoff;
137 * Number of entries in the handlers array.
142 * Did we ever get INIT?
150 * Our current client connection went down. Clean it up
151 * and try to reconnect!
153 * @param h our handle to the core service
156 reconnect (struct GNUNET_CORE_Handle *h);
160 * Task schedule to try to re-connect to core.
162 * @param cls the `struct GNUNET_CORE_Handle`
163 * @param tc task context
166 reconnect_task (void *cls)
168 struct GNUNET_CORE_Handle *h = cls;
170 h->reconnect_task = NULL;
171 LOG (GNUNET_ERROR_TYPE_DEBUG,
172 "Connecting to CORE service after delay\n");
178 * Notify clients about disconnect and free the entry for connected
181 * @param cls the `struct GNUNET_CORE_Handle *`
182 * @param key the peer identity (not used)
183 * @param value the `struct PeerRecord` to free.
184 * @return #GNUNET_YES (continue)
187 disconnect_and_free_peer_entry (void *cls,
188 const struct GNUNET_PeerIdentity *key,
191 struct GNUNET_CORE_Handle *h = cls;
192 struct PeerRecord *pr = value;
194 GNUNET_assert (pr->h == h);
195 if (NULL != h->disconnects)
196 h->disconnects (h->cls,
199 GNUNET_assert (GNUNET_YES ==
200 GNUNET_CONTAINER_multipeermap_remove (h->peers,
203 GNUNET_MQ_destroy (pr->mq);
204 GNUNET_assert (NULL == pr->mq);
207 GNUNET_MQ_discard (pr->env);
216 * Close down any existing connection to the CORE service and
217 * try re-establishing it later.
219 * @param h our handle
222 reconnect_later (struct GNUNET_CORE_Handle *h)
224 GNUNET_assert (NULL == h->reconnect_task);
227 GNUNET_MQ_destroy (h->mq);
230 GNUNET_assert (NULL == h->reconnect_task);
232 GNUNET_SCHEDULER_add_delayed (h->retry_backoff,
235 GNUNET_CONTAINER_multipeermap_iterate (h->peers,
236 &disconnect_and_free_peer_entry,
238 h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
243 * Error handler for the message queue to the CORE service.
244 * On errors, we reconnect.
246 * @param cls closure, a `struct GNUNET_CORE_Handle *`
247 * @param error error code
250 handle_mq_error (void *cls,
251 enum GNUNET_MQ_Error error)
253 struct GNUNET_CORE_Handle *h = cls;
255 LOG (GNUNET_ERROR_TYPE_DEBUG,
263 * Inquire with CORE what options should be set for a message
264 * so that it is transmitted with the given @a priority and
265 * the given @a cork value.
267 * @param cork desired corking
268 * @param priority desired message priority
269 * @param[out] flags set to `flags` value for #GNUNET_MQ_set_options()
270 * @return `extra` argument to give to #GNUNET_MQ_set_options()
273 GNUNET_CORE_get_mq_options (int cork,
274 enum GNUNET_CORE_Priority priority,
277 *flags = ((uint64_t) priority) + (((uint64_t) cork) << 32);
283 * Implement sending functionality of a message queue for
284 * us sending messages to a peer.
286 * @param mq the message queue
287 * @param msg the message to send
288 * @param impl_state state of the implementation
291 core_mq_send_impl (struct GNUNET_MQ_Handle *mq,
292 const struct GNUNET_MessageHeader *msg,
295 struct PeerRecord *pr = impl_state;
296 struct GNUNET_CORE_Handle *h = pr->h;
297 struct SendMessageRequest *smr;
298 struct SendMessage *sm;
299 struct GNUNET_MQ_Envelope *env;
303 enum GNUNET_CORE_Priority priority;
307 /* We're currently reconnecting, pretend this worked */
308 GNUNET_MQ_impl_send_continue (mq);
311 GNUNET_assert (NULL == pr->env);
312 /* extract options from envelope */
313 env = GNUNET_MQ_get_current_envelope (mq);
314 GNUNET_break (NULL ==
315 GNUNET_MQ_env_get_options (env,
317 cork = (int) (flags >> 32);
318 priority = (uint32_t) flags;
320 /* check message size for sanity */
321 msize = ntohs (msg->size);
322 if (msize >= GNUNET_MAX_MESSAGE_SIZE - sizeof (struct SendMessage))
325 GNUNET_MQ_impl_send_continue (mq);
329 /* ask core for transmission */
330 LOG (GNUNET_ERROR_TYPE_DEBUG,
331 "Asking core for transmission of %u bytes to `%s'\n",
332 (unsigned int) msize,
333 GNUNET_i2s (&pr->peer));
334 env = GNUNET_MQ_msg (smr,
335 GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST);
336 smr->priority = htonl ((uint32_t) priority);
337 smr->peer = pr->peer;
338 smr->reserved = htonl (0);
339 smr->size = htons (msize);
340 smr->smr_id = htons (++pr->smr_id_gen);
341 GNUNET_MQ_send (h->mq,
344 /* prepare message with actual transmission data */
345 pr->env = GNUNET_MQ_msg_nested_mh (sm,
346 GNUNET_MESSAGE_TYPE_CORE_SEND,
348 sm->priority = htonl ((uint32_t) priority);
350 sm->cork = htonl ((uint32_t) cork);
351 sm->reserved = htonl (0);
352 LOG (GNUNET_ERROR_TYPE_DEBUG,
353 "Calling get_message with buffer of %u bytes (%s)\n",
354 (unsigned int) msize,
355 cork ? "corked" : "uncorked");
360 * Handle destruction of a message queue. Implementations must not
361 * free @a mq, but should take care of @a impl_state.
363 * @param mq the message queue to destroy
364 * @param impl_state state of the implementation
367 core_mq_destroy_impl (struct GNUNET_MQ_Handle *mq,
370 struct PeerRecord *pr = impl_state;
372 GNUNET_assert (mq == pr->mq);
378 * Implementation function that cancels the currently sent message.
379 * Should basically undo whatever #mq_send_impl() did.
381 * @param mq message queue
382 * @param impl_state state specific to the implementation
385 core_mq_cancel_impl (struct GNUNET_MQ_Handle *mq,
388 struct PeerRecord *pr = impl_state;
390 GNUNET_assert (NULL != pr->env);
391 GNUNET_MQ_discard (pr->env);
397 * We had an error processing a message we forwarded from a peer to
398 * the CORE service. We should just complain about it but otherwise
399 * continue processing.
402 * @param error error code
405 core_mq_error_handler (void *cls,
406 enum GNUNET_MQ_Error error)
408 /* struct PeerRecord *pr = cls; */
415 * Add the given peer to the list of our connected peers
416 * and create the respective data structures and notify
419 * @param h the core handle
420 * @param peer the peer that is connecting to us
423 connect_peer (struct GNUNET_CORE_Handle *h,
424 const struct GNUNET_PeerIdentity *peer)
426 struct PeerRecord *pr;
430 pr = GNUNET_new (struct PeerRecord);
433 GNUNET_assert (GNUNET_YES ==
434 GNUNET_CONTAINER_multipeermap_put (h->peers,
437 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
438 pr->mq = GNUNET_MQ_queue_for_callbacks (&core_mq_send_impl,
439 &core_mq_destroy_impl,
440 &core_mq_cancel_impl,
443 &core_mq_error_handler,
445 /* get our default options */
446 extra = GNUNET_CORE_get_mq_options (GNUNET_NO,
447 GNUNET_CORE_PRIO_BEST_EFFORT,
449 GNUNET_MQ_set_options (pr->mq,
452 if (NULL != h->connects)
454 pr->client_cls = h->connects (h->cls,
457 GNUNET_MQ_set_handlers_closure (pr->mq,
464 * Handle init reply message received from CORE service. Notify
465 * application that we are now connected to the CORE. Also fake
466 * loopback connection.
468 * @param cls the `struct GNUNET_CORE_Handle`
469 * @param m the init reply
472 handle_init_reply (void *cls,
473 const struct InitReplyMessage *m)
475 struct GNUNET_CORE_Handle *h = cls;
476 GNUNET_CORE_StartupCallback init;
478 GNUNET_break (0 == ntohl (m->reserved));
479 h->retry_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
480 if (NULL != (init = h->init))
482 /* mark so we don't call init on reconnect */
484 h->me = m->my_identity;
485 LOG (GNUNET_ERROR_TYPE_DEBUG,
486 "Connected to core service of peer `%s'.\n",
487 GNUNET_i2s (&h->me));
488 h->have_init = GNUNET_YES;
494 LOG (GNUNET_ERROR_TYPE_DEBUG,
495 "Successfully reconnected to core service.\n");
496 if (GNUNET_NO == h->have_init)
498 h->me = m->my_identity;
499 h->have_init = GNUNET_YES;
503 GNUNET_break (0 == memcmp (&h->me,
505 sizeof (struct GNUNET_PeerIdentity)));
508 /* fake 'connect to self' */
515 * Handle connect message received from CORE service.
516 * Notify the application about the new connection.
518 * @param cls the `struct GNUNET_CORE_Handle`
519 * @param cnm the connect message
522 handle_connect_notify (void *cls,
523 const struct ConnectNotifyMessage *cnm)
525 struct GNUNET_CORE_Handle *h = cls;
526 struct PeerRecord *pr;
528 LOG (GNUNET_ERROR_TYPE_DEBUG,
529 "Received notification about connection from `%s'.\n",
530 GNUNET_i2s (&cnm->peer));
531 if (0 == memcmp (&h->me,
533 sizeof (struct GNUNET_PeerIdentity)))
535 /* connect to self!? */
539 pr = GNUNET_CONTAINER_multipeermap_get (h->peers,
553 * Handle disconnect message received from CORE service.
554 * Notify the application about the lost connection.
556 * @param cls the `struct GNUNET_CORE_Handle`
557 * @param dnm message about the disconnect event
560 handle_disconnect_notify (void *cls,
561 const struct DisconnectNotifyMessage *dnm)
563 struct GNUNET_CORE_Handle *h = cls;
564 struct PeerRecord *pr;
566 if (0 == memcmp (&h->me,
568 sizeof (struct GNUNET_PeerIdentity)))
570 /* disconnect from self!? */
574 GNUNET_break (0 == ntohl (dnm->reserved));
575 LOG (GNUNET_ERROR_TYPE_DEBUG,
576 "Received notification about disconnect from `%s'.\n",
577 GNUNET_i2s (&dnm->peer));
578 pr = GNUNET_CONTAINER_multipeermap_get (h->peers,
586 disconnect_and_free_peer_entry (h,
593 * Check that message received from CORE service is well-formed.
595 * @param cls the `struct GNUNET_CORE_Handle`
596 * @param ntm the message we got
597 * @return #GNUNET_OK if the message is well-formed
600 check_notify_inbound (void *cls,
601 const struct NotifyTrafficMessage *ntm)
604 const struct GNUNET_MessageHeader *em;
606 msize = ntohs (ntm->header.size) - sizeof (struct NotifyTrafficMessage);
607 if (msize < sizeof (struct GNUNET_MessageHeader))
610 return GNUNET_SYSERR;
612 em = (const struct GNUNET_MessageHeader *) &ntm[1];
613 if (msize != ntohs (em->size))
616 return GNUNET_SYSERR;
623 * Handle inbound message received from CORE service. If applicable,
624 * notify the application.
626 * @param cls the `struct GNUNET_CORE_Handle`
627 * @param ntm the message we got from CORE.
630 handle_notify_inbound (void *cls,
631 const struct NotifyTrafficMessage *ntm)
633 struct GNUNET_CORE_Handle *h = cls;
634 const struct GNUNET_MessageHeader *em;
635 struct PeerRecord *pr;
637 LOG (GNUNET_ERROR_TYPE_DEBUG,
638 "Received inbound message from `%s'.\n",
639 GNUNET_i2s (&ntm->peer));
640 em = (const struct GNUNET_MessageHeader *) &ntm[1];
641 pr = GNUNET_CONTAINER_multipeermap_get (h->peers,
649 GNUNET_MQ_inject_message (pr->mq,
655 * Handle message received from CORE service notifying us that we are
656 * now allowed to send a message to a peer. If that message is still
657 * pending, put it into the queue to be transmitted.
659 * @param cls the `struct GNUNET_CORE_Handle`
660 * @param smr the message we got
663 handle_send_ready (void *cls,
664 const struct SendMessageReady *smr)
666 struct GNUNET_CORE_Handle *h = cls;
667 struct PeerRecord *pr;
669 pr = GNUNET_CONTAINER_multipeermap_get (h->peers,
677 LOG (GNUNET_ERROR_TYPE_DEBUG,
678 "Received notification about transmission readiness to `%s'.\n",
679 GNUNET_i2s (&smr->peer));
682 /* request must have been cancelled between the original request
683 * and the response from CORE, ignore CORE's readiness */
686 if (ntohs (smr->smr_id) != pr->smr_id_gen)
688 /* READY message is for expired or cancelled message,
689 * ignore! (we should have already sent another request) */
693 /* ok, all good, send message out! */
694 GNUNET_MQ_send (h->mq,
697 GNUNET_MQ_impl_send_continue (pr->mq);
702 * Our current client connection went down. Clean it up and try to
705 * @param h our handle to the core service
708 reconnect (struct GNUNET_CORE_Handle *h)
710 struct GNUNET_MQ_MessageHandler handlers[] = {
711 GNUNET_MQ_hd_fixed_size (init_reply,
712 GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY,
713 struct InitReplyMessage,
715 GNUNET_MQ_hd_fixed_size (connect_notify,
716 GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT,
717 struct ConnectNotifyMessage,
719 GNUNET_MQ_hd_fixed_size (disconnect_notify,
720 GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT,
721 struct DisconnectNotifyMessage,
723 GNUNET_MQ_hd_var_size (notify_inbound,
724 GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND,
725 struct NotifyTrafficMessage,
727 GNUNET_MQ_hd_fixed_size (send_ready,
728 GNUNET_MESSAGE_TYPE_CORE_SEND_READY,
729 struct SendMessageReady,
731 GNUNET_MQ_handler_end ()
733 struct InitMessage *init;
734 struct GNUNET_MQ_Envelope *env;
737 GNUNET_assert (NULL == h->mq);
738 h->mq = GNUNET_CLIENT_connect (h->cfg,
748 env = GNUNET_MQ_msg_extra (init,
749 sizeof (uint16_t) * h->hcnt,
750 GNUNET_MESSAGE_TYPE_CORE_INIT);
751 LOG (GNUNET_ERROR_TYPE_INFO,
752 "(Re)connecting to CORE service\n");
753 init->options = htonl (0);
754 ts = (uint16_t *) &init[1];
755 for (unsigned int hpos = 0; hpos < h->hcnt; hpos++)
756 ts[hpos] = htons (h->handlers[hpos].type);
757 GNUNET_MQ_send (h->mq,
763 * Connect to the core service. Note that the connection may complete
764 * (or fail) asynchronously.
766 * @param cfg configuration to use
767 * @param cls closure for the various callbacks that follow (including handlers in the handlers array)
768 * @param init callback to call once we have successfully
769 * connected to the core service
770 * @param connects function to call on peer connect, can be NULL
771 * @param disconnects function to call on peer disconnect / timeout, can be NULL
772 * @param handlers callbacks for messages we care about, NULL-terminated
773 * @return handle to the core service (only useful for disconnect until @a init is called);
774 * NULL on error (in this case, init is never called)
776 struct GNUNET_CORE_Handle *
777 GNUNET_CORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
779 GNUNET_CORE_StartupCallback init,
780 GNUNET_CORE_ConnectEventHandler connects,
781 GNUNET_CORE_DisconnectEventHandler disconnects,
782 const struct GNUNET_MQ_MessageHandler *handlers)
784 struct GNUNET_CORE_Handle *h;
786 h = GNUNET_new (struct GNUNET_CORE_Handle);
790 h->connects = connects;
791 h->disconnects = disconnects;
792 h->peers = GNUNET_CONTAINER_multipeermap_create (128,
794 h->handlers = GNUNET_MQ_copy_handlers (handlers);
795 h->hcnt = GNUNET_MQ_count_handlers (handlers);
796 GNUNET_assert (h->hcnt <
797 (GNUNET_MAX_MESSAGE_SIZE -
798 sizeof (struct InitMessage)) / sizeof (uint16_t));
799 LOG (GNUNET_ERROR_TYPE_DEBUG,
800 "Connecting to CORE service\n");
804 GNUNET_CORE_disconnect (h);
812 * Disconnect from the core service.
814 * @param handle connection to core to disconnect
817 GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle)
819 LOG (GNUNET_ERROR_TYPE_DEBUG,
820 "Disconnecting from CORE service\n");
821 GNUNET_CONTAINER_multipeermap_iterate (handle->peers,
822 &disconnect_and_free_peer_entry,
824 GNUNET_CONTAINER_multipeermap_destroy (handle->peers);
825 handle->peers = NULL;
826 if (NULL != handle->reconnect_task)
828 GNUNET_SCHEDULER_cancel (handle->reconnect_task);
829 handle->reconnect_task = NULL;
831 if (NULL != handle->mq)
833 GNUNET_MQ_destroy (handle->mq);
836 GNUNET_free_non_null (handle->handlers);
837 GNUNET_free (handle);
842 * Obtain the message queue for a connected peer.
844 * @param h the core handle
845 * @param pid the identity of the peer to check if it has been connected to us
846 * @return NULL if peer is not connected
848 struct GNUNET_MQ_Handle *
849 GNUNET_CORE_get_mq (const struct GNUNET_CORE_Handle *h,
850 const struct GNUNET_PeerIdentity *pid)
852 struct PeerRecord *pr;
854 pr = GNUNET_CONTAINER_multipeermap_get (h->peers,
862 /* end of core_api.c */