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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
21 * @file core/core_api.c
22 * @brief core service; this is the main API for encrypted P2P
24 * @author Christian Grothoff
27 #include "gnunet_util_lib.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_core_service.h"
32 #define LOG(kind, ...) GNUNET_log_from(kind, "core-api", __VA_ARGS__)
36 * 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.
74 * Context for the core service connection.
76 struct GNUNET_CORE_Handle {
78 * Configuration we're using.
80 const struct GNUNET_CONFIGURATION_Handle *cfg;
83 * Closure for the various callbacks.
88 * Function to call once we've handshaked with the core service.
90 GNUNET_CORE_StartupCallback init;
93 * Function to call whenever we're notified about a peer connecting.
95 GNUNET_CORE_ConnectEventHandler connects;
98 * Function to call whenever we're notified about a peer disconnecting.
100 GNUNET_CORE_DisconnectEventHandler disconnects;
103 * Function handlers for messages of particular type.
105 struct GNUNET_MQ_MessageHandler *handlers;
108 * Our message queue for transmissions to the service.
110 struct GNUNET_MQ_Handle *mq;
113 * Hash map listing all of the peers that we are currently
116 struct GNUNET_CONTAINER_MultiPeerMap *peers;
119 * Identity of this peer.
121 struct GNUNET_PeerIdentity me;
124 * ID of reconnect task (if any).
126 struct GNUNET_SCHEDULER_Task *reconnect_task;
129 * Current delay we use for re-trying to connect to core.
131 struct GNUNET_TIME_Relative retry_backoff;
134 * Number of entries in the handlers array.
139 * Did we ever get INIT?
146 * Our current client connection went down. Clean it up
147 * and try to reconnect!
149 * @param h our handle to the core service
152 reconnect(struct GNUNET_CORE_Handle *h);
156 * Task schedule to try to re-connect to core.
158 * @param cls the `struct GNUNET_CORE_Handle`
159 * @param tc task context
162 reconnect_task(void *cls)
164 struct GNUNET_CORE_Handle *h = cls;
166 h->reconnect_task = NULL;
167 LOG(GNUNET_ERROR_TYPE_DEBUG, "Connecting to CORE service after delay\n");
173 * Notify clients about disconnect and free the entry for connected
176 * @param cls the `struct GNUNET_CORE_Handle *`
177 * @param key the peer identity (not used)
178 * @param value the `struct PeerRecord` to free.
179 * @return #GNUNET_YES (continue)
182 disconnect_and_free_peer_entry(void *cls,
183 const struct GNUNET_PeerIdentity *key,
186 struct GNUNET_CORE_Handle *h = cls;
187 struct PeerRecord *pr = value;
189 GNUNET_assert(pr->h == h);
190 if (NULL != h->disconnects)
191 h->disconnects(h->cls, &pr->peer, pr->client_cls);
192 GNUNET_assert(GNUNET_YES ==
193 GNUNET_CONTAINER_multipeermap_remove(h->peers, key, pr));
194 GNUNET_MQ_destroy(pr->mq);
195 GNUNET_assert(NULL == pr->mq);
198 GNUNET_MQ_discard(pr->env);
207 * Close down any existing connection to the CORE service and
208 * try re-establishing it later.
210 * @param h our handle
213 reconnect_later(struct GNUNET_CORE_Handle *h)
215 GNUNET_assert(NULL == h->reconnect_task);
218 GNUNET_MQ_destroy(h->mq);
221 GNUNET_assert(NULL == h->reconnect_task);
223 GNUNET_SCHEDULER_add_delayed(h->retry_backoff, &reconnect_task, h);
224 GNUNET_CONTAINER_multipeermap_iterate(h->peers,
225 &disconnect_and_free_peer_entry,
227 h->retry_backoff = GNUNET_TIME_STD_BACKOFF(h->retry_backoff);
232 * Error handler for the message queue to the CORE service.
233 * On errors, we reconnect.
235 * @param cls closure, a `struct GNUNET_CORE_Handle *`
236 * @param error error code
239 handle_mq_error(void *cls, enum GNUNET_MQ_Error error)
241 struct GNUNET_CORE_Handle *h = cls;
243 LOG(GNUNET_ERROR_TYPE_DEBUG, "MQ ERROR: %d\n", error);
249 * Implement sending functionality of a message queue for
250 * us sending messages to a peer.
252 * @param mq the message queue
253 * @param msg the message to send
254 * @param impl_state state of the implementation
257 core_mq_send_impl(struct GNUNET_MQ_Handle *mq,
258 const struct GNUNET_MessageHeader *msg,
261 struct PeerRecord *pr = impl_state;
262 struct GNUNET_CORE_Handle *h = pr->h;
263 struct SendMessageRequest *smr;
264 struct SendMessage *sm;
265 struct GNUNET_MQ_Envelope *env;
267 enum GNUNET_MQ_PriorityPreferences flags;
271 /* We're currently reconnecting, pretend this worked */
272 GNUNET_MQ_impl_send_continue(mq);
275 GNUNET_assert(NULL == pr->env);
276 /* extract options from envelope */
277 env = GNUNET_MQ_get_current_envelope(mq);
278 flags = GNUNET_MQ_env_get_options(env);
280 /* check message size for sanity */
281 msize = ntohs(msg->size);
282 if (msize >= GNUNET_MAX_MESSAGE_SIZE - sizeof(struct SendMessage))
285 GNUNET_MQ_impl_send_continue(mq);
289 /* ask core for transmission */
290 LOG(GNUNET_ERROR_TYPE_DEBUG,
291 "Asking core for transmission of %u bytes to `%s'\n",
293 GNUNET_i2s(&pr->peer));
294 env = GNUNET_MQ_msg(smr, GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST);
295 smr->priority = htonl((uint32_t)flags);
296 smr->peer = pr->peer;
297 smr->size = htons(msize);
298 smr->smr_id = htons(++pr->smr_id_gen);
299 GNUNET_MQ_send(h->mq, env);
301 /* prepare message with actual transmission data */
302 pr->env = GNUNET_MQ_msg_nested_mh(sm, GNUNET_MESSAGE_TYPE_CORE_SEND, msg);
303 sm->priority = htonl((uint32_t)flags);
305 LOG(GNUNET_ERROR_TYPE_DEBUG,
306 "Calling get_message with buffer of %u bytes\n",
307 (unsigned int)msize);
312 * Handle destruction of a message queue. Implementations must not
313 * free @a mq, but should take care of @a impl_state.
315 * @param mq the message queue to destroy
316 * @param impl_state state of the implementation
319 core_mq_destroy_impl(struct GNUNET_MQ_Handle *mq, void *impl_state)
321 struct PeerRecord *pr = impl_state;
323 GNUNET_assert(mq == pr->mq);
329 * Implementation function that cancels the currently sent message.
330 * Should basically undo whatever #mq_send_impl() did.
332 * @param mq message queue
333 * @param impl_state state specific to the implementation
336 core_mq_cancel_impl(struct GNUNET_MQ_Handle *mq, void *impl_state)
338 struct PeerRecord *pr = impl_state;
341 GNUNET_assert(NULL != pr->env);
342 GNUNET_MQ_discard(pr->env);
348 * We had an error processing a message we forwarded from a peer to
349 * the CORE service. We should just complain about it but otherwise
350 * continue processing.
353 * @param error error code
356 core_mq_error_handler(void *cls, enum GNUNET_MQ_Error error)
358 /* struct PeerRecord *pr = cls; */
366 * Add the given peer to the list of our connected peers
367 * and create the respective data structures and notify
370 * @param h the core handle
371 * @param peer the peer that is connecting to us
374 connect_peer(struct GNUNET_CORE_Handle *h,
375 const struct GNUNET_PeerIdentity *peer)
377 struct PeerRecord *pr;
379 pr = GNUNET_new(struct PeerRecord);
382 GNUNET_assert(GNUNET_YES ==
383 GNUNET_CONTAINER_multipeermap_put(
387 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
388 pr->mq = GNUNET_MQ_queue_for_callbacks(&core_mq_send_impl,
389 &core_mq_destroy_impl,
390 &core_mq_cancel_impl,
393 &core_mq_error_handler,
395 if (NULL != h->connects)
397 pr->client_cls = h->connects(h->cls, &pr->peer, pr->mq);
398 GNUNET_MQ_set_handlers_closure(pr->mq, pr->client_cls);
404 * Handle init reply message received from CORE service. Notify
405 * application that we are now connected to the CORE. Also fake
406 * loopback connection.
408 * @param cls the `struct GNUNET_CORE_Handle`
409 * @param m the init reply
412 handle_init_reply(void *cls, const struct InitReplyMessage *m)
414 struct GNUNET_CORE_Handle *h = cls;
415 GNUNET_CORE_StartupCallback init;
417 GNUNET_break(0 == ntohl(m->reserved));
418 h->retry_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
419 if (NULL != (init = h->init))
421 /* mark so we don't call init on reconnect */
423 h->me = m->my_identity;
424 LOG(GNUNET_ERROR_TYPE_DEBUG,
425 "Connected to core service of peer `%s'.\n",
427 h->have_init = GNUNET_YES;
428 init(h->cls, &h->me);
432 LOG(GNUNET_ERROR_TYPE_DEBUG,
433 "Successfully reconnected to core service.\n");
434 if (GNUNET_NO == h->have_init)
436 h->me = m->my_identity;
437 h->have_init = GNUNET_YES;
441 GNUNET_break(0 == memcmp(&h->me,
443 sizeof(struct GNUNET_PeerIdentity)));
446 /* fake 'connect to self' */
447 connect_peer(h, &h->me);
452 * Handle connect message received from CORE service.
453 * Notify the application about the new connection.
455 * @param cls the `struct GNUNET_CORE_Handle`
456 * @param cnm the connect message
459 handle_connect_notify(void *cls, const struct ConnectNotifyMessage *cnm)
461 struct GNUNET_CORE_Handle *h = cls;
462 struct PeerRecord *pr;
464 LOG(GNUNET_ERROR_TYPE_DEBUG,
465 "Received notification about connection from `%s'.\n",
466 GNUNET_i2s(&cnm->peer));
467 if (0 == memcmp(&h->me, &cnm->peer, sizeof(struct GNUNET_PeerIdentity)))
469 /* connect to self!? */
473 pr = GNUNET_CONTAINER_multipeermap_get(h->peers, &cnm->peer);
480 connect_peer(h, &cnm->peer);
485 * Handle disconnect message received from CORE service.
486 * Notify the application about the lost connection.
488 * @param cls the `struct GNUNET_CORE_Handle`
489 * @param dnm message about the disconnect event
492 handle_disconnect_notify(void *cls, const struct DisconnectNotifyMessage *dnm)
494 struct GNUNET_CORE_Handle *h = cls;
495 struct PeerRecord *pr;
497 if (0 == memcmp(&h->me, &dnm->peer, sizeof(struct GNUNET_PeerIdentity)))
499 /* disconnect from self!? */
503 GNUNET_break(0 == ntohl(dnm->reserved));
504 LOG(GNUNET_ERROR_TYPE_DEBUG,
505 "Received notification about disconnect from `%s'.\n",
506 GNUNET_i2s(&dnm->peer));
507 pr = GNUNET_CONTAINER_multipeermap_get(h->peers, &dnm->peer);
514 disconnect_and_free_peer_entry(h, &pr->peer, pr);
519 * Check that message received from CORE service is well-formed.
521 * @param cls the `struct GNUNET_CORE_Handle`
522 * @param ntm the message we got
523 * @return #GNUNET_OK if the message is well-formed
526 check_notify_inbound(void *cls, const struct NotifyTrafficMessage *ntm)
529 const struct GNUNET_MessageHeader *em;
532 msize = ntohs(ntm->header.size) - sizeof(struct NotifyTrafficMessage);
533 if (msize < sizeof(struct GNUNET_MessageHeader))
536 return GNUNET_SYSERR;
538 em = (const struct GNUNET_MessageHeader *)&ntm[1];
539 if (msize != ntohs(em->size))
542 return GNUNET_SYSERR;
549 * Handle inbound message received from CORE service. If applicable,
550 * notify the application.
552 * @param cls the `struct GNUNET_CORE_Handle`
553 * @param ntm the message we got from CORE.
556 handle_notify_inbound(void *cls, const struct NotifyTrafficMessage *ntm)
558 struct GNUNET_CORE_Handle *h = cls;
559 const struct GNUNET_MessageHeader *em;
560 struct PeerRecord *pr;
562 LOG(GNUNET_ERROR_TYPE_DEBUG,
563 "Received inbound message from `%s'.\n",
564 GNUNET_i2s(&ntm->peer));
565 em = (const struct GNUNET_MessageHeader *)&ntm[1];
566 pr = GNUNET_CONTAINER_multipeermap_get(h->peers, &ntm->peer);
573 GNUNET_MQ_inject_message(pr->mq, em);
578 * Handle message received from CORE service notifying us that we are
579 * now allowed to send a message to a peer. If that message is still
580 * pending, put it into the queue to be transmitted.
582 * @param cls the `struct GNUNET_CORE_Handle`
583 * @param smr the message we got
586 handle_send_ready(void *cls, const struct SendMessageReady *smr)
588 struct GNUNET_CORE_Handle *h = cls;
589 struct PeerRecord *pr;
591 pr = GNUNET_CONTAINER_multipeermap_get(h->peers, &smr->peer);
598 LOG(GNUNET_ERROR_TYPE_DEBUG,
599 "Received notification about transmission readiness to `%s'.\n",
600 GNUNET_i2s(&smr->peer));
603 /* request must have been cancelled between the original request
604 * and the response from CORE, ignore CORE's readiness */
607 if (ntohs(smr->smr_id) != pr->smr_id_gen)
609 /* READY message is for expired or cancelled message,
610 * ignore! (we should have already sent another request) */
614 /* ok, all good, send message out! */
615 GNUNET_MQ_send(h->mq, pr->env);
617 GNUNET_MQ_impl_send_continue(pr->mq);
622 * Our current client connection went down. Clean it up and try to
625 * @param h our handle to the core service
628 reconnect(struct GNUNET_CORE_Handle *h)
630 struct GNUNET_MQ_MessageHandler handlers[] =
631 { GNUNET_MQ_hd_fixed_size(init_reply,
632 GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY,
633 struct InitReplyMessage,
635 GNUNET_MQ_hd_fixed_size(connect_notify,
636 GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT,
637 struct ConnectNotifyMessage,
639 GNUNET_MQ_hd_fixed_size(disconnect_notify,
640 GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT,
641 struct DisconnectNotifyMessage,
643 GNUNET_MQ_hd_var_size(notify_inbound,
644 GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND,
645 struct NotifyTrafficMessage,
647 GNUNET_MQ_hd_fixed_size(send_ready,
648 GNUNET_MESSAGE_TYPE_CORE_SEND_READY,
649 struct SendMessageReady,
651 GNUNET_MQ_handler_end() };
652 struct InitMessage *init;
653 struct GNUNET_MQ_Envelope *env;
656 GNUNET_assert(NULL == h->mq);
657 h->mq = GNUNET_CLIENT_connect(h->cfg, "core", handlers, &handle_mq_error, h);
663 env = GNUNET_MQ_msg_extra(init,
664 sizeof(uint16_t) * h->hcnt,
665 GNUNET_MESSAGE_TYPE_CORE_INIT);
666 LOG(GNUNET_ERROR_TYPE_INFO, "(Re)connecting to CORE service\n");
667 init->options = htonl(0);
668 ts = (uint16_t *)&init[1];
669 for (unsigned int hpos = 0; hpos < h->hcnt; hpos++)
670 ts[hpos] = htons(h->handlers[hpos].type);
671 GNUNET_MQ_send(h->mq, env);
676 * Connect to the core service. Note that the connection may complete
677 * (or fail) asynchronously.
679 * @param cfg configuration to use
680 * @param cls closure for the various callbacks that follow (including handlers in the handlers array)
681 * @param init callback to call once we have successfully
682 * connected to the core service
683 * @param connects function to call on peer connect, can be NULL
684 * @param disconnects function to call on peer disconnect / timeout, can be NULL
685 * @param handlers callbacks for messages we care about, NULL-terminated
686 * @return handle to the core service (only useful for disconnect until @a init is called);
687 * NULL on error (in this case, init is never called)
689 struct GNUNET_CORE_Handle *
690 GNUNET_CORE_connect(const struct GNUNET_CONFIGURATION_Handle *cfg,
692 GNUNET_CORE_StartupCallback init,
693 GNUNET_CORE_ConnectEventHandler connects,
694 GNUNET_CORE_DisconnectEventHandler disconnects,
695 const struct GNUNET_MQ_MessageHandler *handlers)
697 struct GNUNET_CORE_Handle *h;
699 h = GNUNET_new(struct GNUNET_CORE_Handle);
703 h->connects = connects;
704 h->disconnects = disconnects;
705 h->peers = GNUNET_CONTAINER_multipeermap_create(128, GNUNET_NO);
706 h->handlers = GNUNET_MQ_copy_handlers(handlers);
707 h->hcnt = GNUNET_MQ_count_handlers(handlers);
708 GNUNET_assert(h->hcnt <
709 (GNUNET_MAX_MESSAGE_SIZE - sizeof(struct InitMessage)) /
711 LOG(GNUNET_ERROR_TYPE_DEBUG, "Connecting to CORE service\n");
715 GNUNET_CORE_disconnect(h);
723 * Disconnect from the core service.
725 * @param handle connection to core to disconnect
728 GNUNET_CORE_disconnect(struct GNUNET_CORE_Handle *handle)
730 LOG(GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from CORE service\n");
731 GNUNET_CONTAINER_multipeermap_iterate(handle->peers,
732 &disconnect_and_free_peer_entry,
734 GNUNET_CONTAINER_multipeermap_destroy(handle->peers);
735 handle->peers = NULL;
736 if (NULL != handle->reconnect_task)
738 GNUNET_SCHEDULER_cancel(handle->reconnect_task);
739 handle->reconnect_task = NULL;
741 if (NULL != handle->mq)
743 GNUNET_MQ_destroy(handle->mq);
746 GNUNET_free_non_null(handle->handlers);
752 * Obtain the message queue for a connected peer.
754 * @param h the core handle
755 * @param pid the identity of the peer to check if it has been connected to us
756 * @return NULL if peer is not connected
758 struct GNUNET_MQ_Handle *
759 GNUNET_CORE_get_mq(const struct GNUNET_CORE_Handle *h,
760 const struct GNUNET_PeerIdentity *pid)
762 struct PeerRecord *pr;
764 pr = GNUNET_CONTAINER_multipeermap_get(h->peers, pid);
771 /* end of core_api.c */