From ca912f85dae6b61dd80ab02d0e3f0b20a556da7c Mon Sep 17 00:00:00 2001 From: TheJackiMonster Date: Thu, 12 Nov 2020 20:58:07 +0100 Subject: -remerge branch 'jacki/messenger' This reverts commit e11d1e59e4ae5f7d89c33df3ae9ca8f1ece990cf. --- src/messenger/.gitignore | 4 + src/messenger/Makefile.am | 131 +++ src/messenger/gnunet-messenger.c | 306 ++++++ src/messenger/gnunet-service-messenger.c | 306 ++++++ src/messenger/gnunet-service-messenger.h | 121 +++ src/messenger/gnunet-service-messenger_basement.c | 58 ++ src/messenger/gnunet-service-messenger_basement.h | 66 ++ src/messenger/gnunet-service-messenger_contact.c | 96 ++ src/messenger/gnunet-service-messenger_contact.h | 112 +++ src/messenger/gnunet-service-messenger_handle.c | 503 ++++++++++ src/messenger/gnunet-service-messenger_handle.h | 216 ++++ .../gnunet-service-messenger_list_handles.c | 95 ++ .../gnunet-service-messenger_list_handles.h | 96 ++ .../gnunet-service-messenger_list_messages.c | 76 ++ .../gnunet-service-messenger_list_messages.h | 81 ++ .../gnunet-service-messenger_message_handle.c | 130 +++ .../gnunet-service-messenger_message_handle.h | 128 +++ .../gnunet-service-messenger_message_kind.c | 192 ++++ .../gnunet-service-messenger_message_kind.h | 160 +++ .../gnunet-service-messenger_message_recv.c | 204 ++++ .../gnunet-service-messenger_message_recv.h | 159 +++ .../gnunet-service-messenger_message_send.c | 118 +++ .../gnunet-service-messenger_message_send.h | 155 +++ .../gnunet-service-messenger_message_store.c | 282 ++++++ .../gnunet-service-messenger_message_store.h | 120 +++ src/messenger/gnunet-service-messenger_room.c | 1051 ++++++++++++++++++++ src/messenger/gnunet-service-messenger_room.h | 378 +++++++ src/messenger/gnunet-service-messenger_service.c | 516 ++++++++++ src/messenger/gnunet-service-messenger_service.h | 259 +++++ src/messenger/gnunet-service-messenger_tunnel.c | 300 ++++++ src/messenger/gnunet-service-messenger_tunnel.h | 155 +++ src/messenger/gnunet-service-messenger_util.c | 64 ++ src/messenger/gnunet-service-messenger_util.h | 53 + src/messenger/messenger.conf.in | 13 + src/messenger/messenger_api.c | 568 +++++++++++ src/messenger/messenger_api_contact.c | 78 ++ src/messenger/messenger_api_contact.h | 93 ++ src/messenger/messenger_api_ego.h | 38 + src/messenger/messenger_api_handle.c | 213 ++++ src/messenger/messenger_api_handle.h | 174 ++++ src/messenger/messenger_api_list_tunnels.c | 112 +++ src/messenger/messenger_api_list_tunnels.h | 112 +++ src/messenger/messenger_api_message.c | 602 +++++++++++ src/messenger/messenger_api_message.h | 190 ++++ src/messenger/messenger_api_room.c | 189 ++++ src/messenger/messenger_api_room.h | 95 ++ src/messenger/test_messenger.c | 187 ++++ src/messenger/test_messenger_anonymous.c | 179 ++++ src/messenger/test_messenger_comm0.c | 252 +++++ 49 files changed, 9786 insertions(+) create mode 100644 src/messenger/.gitignore create mode 100644 src/messenger/Makefile.am create mode 100644 src/messenger/gnunet-messenger.c create mode 100644 src/messenger/gnunet-service-messenger.c create mode 100644 src/messenger/gnunet-service-messenger.h create mode 100644 src/messenger/gnunet-service-messenger_basement.c create mode 100644 src/messenger/gnunet-service-messenger_basement.h create mode 100644 src/messenger/gnunet-service-messenger_contact.c create mode 100644 src/messenger/gnunet-service-messenger_contact.h create mode 100644 src/messenger/gnunet-service-messenger_handle.c create mode 100644 src/messenger/gnunet-service-messenger_handle.h create mode 100644 src/messenger/gnunet-service-messenger_list_handles.c create mode 100644 src/messenger/gnunet-service-messenger_list_handles.h create mode 100644 src/messenger/gnunet-service-messenger_list_messages.c create mode 100644 src/messenger/gnunet-service-messenger_list_messages.h create mode 100644 src/messenger/gnunet-service-messenger_message_handle.c create mode 100644 src/messenger/gnunet-service-messenger_message_handle.h create mode 100644 src/messenger/gnunet-service-messenger_message_kind.c create mode 100644 src/messenger/gnunet-service-messenger_message_kind.h create mode 100644 src/messenger/gnunet-service-messenger_message_recv.c create mode 100644 src/messenger/gnunet-service-messenger_message_recv.h create mode 100644 src/messenger/gnunet-service-messenger_message_send.c create mode 100644 src/messenger/gnunet-service-messenger_message_send.h create mode 100644 src/messenger/gnunet-service-messenger_message_store.c create mode 100644 src/messenger/gnunet-service-messenger_message_store.h create mode 100644 src/messenger/gnunet-service-messenger_room.c create mode 100644 src/messenger/gnunet-service-messenger_room.h create mode 100644 src/messenger/gnunet-service-messenger_service.c create mode 100644 src/messenger/gnunet-service-messenger_service.h create mode 100644 src/messenger/gnunet-service-messenger_tunnel.c create mode 100644 src/messenger/gnunet-service-messenger_tunnel.h create mode 100644 src/messenger/gnunet-service-messenger_util.c create mode 100644 src/messenger/gnunet-service-messenger_util.h create mode 100644 src/messenger/messenger.conf.in create mode 100644 src/messenger/messenger_api.c create mode 100644 src/messenger/messenger_api_contact.c create mode 100644 src/messenger/messenger_api_contact.h create mode 100644 src/messenger/messenger_api_ego.h create mode 100644 src/messenger/messenger_api_handle.c create mode 100644 src/messenger/messenger_api_handle.h create mode 100644 src/messenger/messenger_api_list_tunnels.c create mode 100644 src/messenger/messenger_api_list_tunnels.h create mode 100644 src/messenger/messenger_api_message.c create mode 100644 src/messenger/messenger_api_message.h create mode 100644 src/messenger/messenger_api_room.c create mode 100644 src/messenger/messenger_api_room.h create mode 100644 src/messenger/test_messenger.c create mode 100644 src/messenger/test_messenger_anonymous.c create mode 100644 src/messenger/test_messenger_comm0.c (limited to 'src/messenger') diff --git a/src/messenger/.gitignore b/src/messenger/.gitignore new file mode 100644 index 000000000..9de3fb304 --- /dev/null +++ b/src/messenger/.gitignore @@ -0,0 +1,4 @@ +gnunet-service-messenger +gnunet-messenger +test_messenger_api +test_messenger_anonymous diff --git a/src/messenger/Makefile.am b/src/messenger/Makefile.am new file mode 100644 index 000000000..ebe08290e --- /dev/null +++ b/src/messenger/Makefile.am @@ -0,0 +1,131 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + +pkgcfgdir= $(pkgdatadir)/config.d/ + +libexecdir= $(pkglibdir)/libexec/ + +pkgcfg_DATA = \ + messenger.conf + +plugindir = $(libdir)/gnunet + +AM_CLFAGS = -g + +libexec_PROGRAMS = \ + gnunet-service-messenger \ + $(EXP_LIBEXEC) + +bin_PROGRAMS = \ + gnunet-messenger + +lib_LTLIBRARIES = \ + libgnunetmessenger_common.la \ + libgnunetmessenger.la \ + $(EXP_LIB) + +libgnunetmessenger_common_la_SOURCES = \ + messenger_api_ego.h \ + messenger_api_message.c messenger_api_message.h \ + messenger_api_list_tunnels.c messenger_api_list_tunnels.h +libgnunetmessenger_common_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/identity/libgnunetidentity.la \ + $(XLIB) \ + $(LTLIBINTL) +libgnunetmessenger_common_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) \ + -version-info 0:0:0 + +libgnunetmessenger_la_SOURCES = \ + messenger_api.c \ + messenger_api_contact.c messenger_api_contact.h \ + messenger_api_handle.c messenger_api_handle.h \ + messenger_api_room.c messenger_api_room.h +libgnunetmessenger_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/identity/libgnunetidentity.la \ + libgnunetmessenger_common.la \ + $(XLIB) \ + $(LTLIBINTL) +libgnunetmessenger_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) \ + -version-info 0:0:0 + +gnunet_messenger_SOURCES = \ + gnunet-messenger.c +gnunet_messenger_LDADD = \ + libgnunetmessenger_common.la \ + libgnunetmessenger.la \ + $(top_builddir)/src/util/libgnunetutil.la +gnunet_messenger_LDFLAGS = \ + $(GN_LIBINTL) + +gnunet_service_messenger_SOURCES = \ + gnunet-service-messenger.c gnunet-service-messenger.h \ + gnunet-service-messenger_service.c gnunet-service-messenger_service.h \ + gnunet-service-messenger_list_handles.c gnunet-service-messenger_list_handles.h \ + gnunet-service-messenger_list_messages.c gnunet-service-messenger_list_messages.h \ + gnunet-service-messenger_message_handle.c gnunet-service-messenger_message_handle.h \ + gnunet-service-messenger_message_kind.c gnunet-service-messenger_message_kind.h \ + gnunet-service-messenger_message_recv.c gnunet-service-messenger_message_recv.h \ + gnunet-service-messenger_message_send.c gnunet-service-messenger_message_send.h \ + gnunet-service-messenger_message_store.c gnunet-service-messenger_message_store.h \ + gnunet-service-messenger_basement.c gnunet-service-messenger_basement.h \ + gnunet-service-messenger_contact.c gnunet-service-messenger_contact.h \ + gnunet-service-messenger_handle.c gnunet-service-messenger_handle.h \ + gnunet-service-messenger_room.c gnunet-service-messenger_room.h \ + gnunet-service-messenger_tunnel.c gnunet-service-messenger_tunnel.h \ + gnunet-service-messenger_util.c gnunet-service-messenger_util.h +gnunet_service_messenger_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/cadet/libgnunetcadet.la \ + $(top_builddir)/src/identity/libgnunetidentity.la \ + libgnunetmessenger_common.la \ + libgnunetmessenger.la \ + $(GN_LIBINTL) + +check_PROGRAMS = \ + test_messenger_api \ + test_messenger_anonymous \ + test_messenger_comm0 + +if ENABLE_TEST_RUN +AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; +TESTS = \ + $(check_PROGRAMS) +endif + +test_messenger_api_SOURCES = \ + test_messenger.c +test_messenger_api_LDADD = \ + libgnunetmessenger_common.la \ + libgnunetmessenger.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_messenger_anonymous_SOURCES = \ + test_messenger_anonymous.c +test_messenger_anonymous_LDADD = \ + libgnunetmessenger_common.la \ + libgnunetmessenger.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_messenger_comm0_SOURCES = \ + test_messenger_comm0.c +test_messenger_comm0_LDADD = \ + libgnunetmessenger_common.la \ + libgnunetmessenger.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/testbed-logger/libgnunettestbedlogger.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/util/libgnunetutil.la + +EXTRA_DIST = \ + test_messenger_api.conf diff --git a/src/messenger/gnunet-messenger.c b/src/messenger/gnunet-messenger.c new file mode 100644 index 000000000..579e5c3ad --- /dev/null +++ b/src/messenger/gnunet-messenger.c @@ -0,0 +1,306 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-messenger.c + * @brief Print information about messenger groups. + */ + +#include + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_messenger_service.h" + +struct GNUNET_MESSENGER_Handle *messenger; + +/** + * Function called whenever a message is received or sent. + * + * @param cls Closure + * @param room Room + * @param message Message + * @param hash Hash of message + */ +void +on_message (void *cls, const struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_Contact *sender = GNUNET_MESSENGER_get_member (room, &(message->header.sender_id)); + + const char *sender_name = GNUNET_MESSENGER_contact_get_name (sender); + + if (!sender_name) + sender_name = "anonymous"; + + switch (message->header.kind) + { + case GNUNET_MESSENGER_KIND_JOIN: + { + printf ("* '%s' joined the room! [ %u %u %u %u ]\n", sender_name, message->body.join.key.ecdsa_key.q_y[0], + message->body.join.key.ecdsa_key.q_y[1], message->body.join.key.ecdsa_key.q_y[2], + message->body.join.key.ecdsa_key.q_y[3]); + break; + } + case GNUNET_MESSENGER_KIND_LEAVE: + { + printf ("* '%s' leaves the room!\n", sender_name); + break; + } + case GNUNET_MESSENGER_KIND_PEER: + { + printf ("* '%s' opened the room on: %s\n", sender_name, GNUNET_i2s_full (&(message->body.peer.peer))); + break; + } + case GNUNET_MESSENGER_KIND_TEXT: + { + printf ("* '%s' says: \"%s\"\n", sender_name, message->body.text.text); + break; + } + default: + { + break; + } + } +} + +struct GNUNET_SCHEDULER_Task *read_task; + +/** + * Task to shut down this application. + * + * @param cls Closure + */ +static void +shutdown_hook (void *cls) +{ + struct GNUNET_MESSENGER_Room *room = cls; + + if (read_task) + GNUNET_SCHEDULER_cancel (read_task); + + if (room) + GNUNET_MESSENGER_close_room (room); + + if (messenger) + GNUNET_MESSENGER_disconnect (messenger); +} + +static void +listen_stdio (void *cls); + +#define MAX_BUFFER_SIZE 60000 + +/** + * Task run in stdio mode, after some data is available at stdin. + * + * @param cls Closure + */ +static void +read_stdio (void *cls) +{ + read_task = NULL; + + char buffer[MAX_BUFFER_SIZE]; + ssize_t length; + + length = read (0, buffer, MAX_BUFFER_SIZE); + + if ((length <= 0) || (length >= MAX_BUFFER_SIZE)) + { + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (buffer[length - 1] == '\n') + buffer[length - 1] = '\0'; + else + buffer[length] = '\0'; + + struct GNUNET_MESSENGER_Room *room = cls; + + struct GNUNET_MESSENGER_Message message; + message.header.kind = GNUNET_MESSENGER_KIND_TEXT; + message.body.text.text = buffer; + + GNUNET_MESSENGER_send_message (room, &message); + + read_task = GNUNET_SCHEDULER_add_now (listen_stdio, cls); +} + +/** + * Wait for input on STDIO and send it out over the #ch. + * + * @param cls Closure + */ +static void +listen_stdio (void *cls) +{ + read_task = NULL; + + struct GNUNET_NETWORK_FDSet *rs = GNUNET_NETWORK_fdset_create (); + + GNUNET_NETWORK_fdset_set_native (rs, 0); + + read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, + GNUNET_TIME_UNIT_FOREVER_REL, + rs, + NULL, + &read_stdio, cls); + + GNUNET_NETWORK_fdset_destroy (rs); +} + +/** + * Initial task to startup application. + * + * @param cls Closure + */ +static void +idle (void *cls) +{ + struct GNUNET_MESSENGER_Room *room = cls; + + printf ("* You joined the room.\n"); + + read_task = GNUNET_SCHEDULER_add_now (listen_stdio, room); +} + +char *door_id; +char *ego_name; +char *room_key; + +struct GNUNET_SCHEDULER_Task *shutdown_task; + +/** + * Function called when an identity is retrieved. + * + * @param cls Closure + * @param handle Handle of messenger service + */ +static void +on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle) +{ + struct GNUNET_HashCode key; + memset (&key, 0, sizeof(key)); + + if (room_key) + GNUNET_CRYPTO_hash (room_key, strlen (room_key), &key); + + struct GNUNET_PeerIdentity *door = NULL; + + if (door_id) + { + door = GNUNET_new(struct GNUNET_PeerIdentity); + + if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (door_id, strlen (door_id), &(door->public_key))) + { + GNUNET_free(door); + door = NULL; + } + } + + const char *name = GNUNET_MESSENGER_get_name (handle); + + if (!name) + name = "anonymous"; + + printf ("* Welcome to the messenger, '%s'!\n", name); + + struct GNUNET_MESSENGER_Room *room; + + if (door) + { + printf ("* You try to entry a room...\n"); + + room = GNUNET_MESSENGER_entry_room (messenger, door, &key); + } + else + { + printf ("* You try to open a room...\n"); + + room = GNUNET_MESSENGER_open_room (messenger, &key); + } + + GNUNET_SCHEDULER_cancel (shutdown_task); + + shutdown_task = GNUNET_SCHEDULER_add_shutdown (shutdown_hook, room); + + if (!room) + GNUNET_SCHEDULER_shutdown (); + else + GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_get_zero_ (), GNUNET_SCHEDULER_PRIORITY_IDLE, idle, + room); +} + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param cfg configuration + */ +static void +run (void *cls, char *const*args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + messenger = GNUNET_MESSENGER_connect (cfg, ego_name, &on_identity, NULL, &on_message, NULL); + + shutdown_task = GNUNET_SCHEDULER_add_shutdown (shutdown_hook, NULL); +} + +/** + * The main function to obtain messenger information. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char **argv) +{ + const char *description = "Open and connect to rooms using the MESSENGER to chat."; + + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_string ('d', + "door", + "PEERIDENTITY", + "peer identity to entry into the room", + &door_id), + GNUNET_GETOPT_option_string ('e', + "ego", + "IDENTITY", + "identity to use for messaging", + &ego_name), + GNUNET_GETOPT_option_string ('r', + "room", + "ROOMKEY", + "key of the room to connect to", + &room_key), + GNUNET_GETOPT_OPTION_END }; + + return (GNUNET_OK == GNUNET_PROGRAM_run (argc, + argv, + "gnunet-messenger\0", + gettext_noop(description), + options, + &run, + NULL) ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/src/messenger/gnunet-service-messenger.c b/src/messenger/gnunet-service-messenger.c new file mode 100644 index 000000000..2c92305c4 --- /dev/null +++ b/src/messenger/gnunet-service-messenger.c @@ -0,0 +1,306 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger.h" + +#include "gnunet-service-messenger_service.h" +#include "messenger_api_message.h" + +struct GNUNET_MESSENGER_Client +{ + struct GNUNET_SERVICE_Client *client; + struct GNUNET_MESSENGER_SrvHandle *handle; +}; + +struct GNUNET_MESSENGER_Service *messenger; + +static int +check_create (void *cls, const struct GNUNET_MESSENGER_CreateMessage *msg) +{ + GNUNET_MQ_check_zero_termination(msg); + return GNUNET_OK; +} + +static void +handle_create (void *cls, const struct GNUNET_MESSENGER_CreateMessage *msg) +{ + struct GNUNET_MESSENGER_Client *msg_client = cls; + + const char *name = ((const char*) msg) + sizeof(*msg); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Handle created with name: %s\n", name); + + setup_handle_name (msg_client->handle, strlen (name) > 0? name : NULL); + + GNUNET_SERVICE_client_continue (msg_client->client); +} + +static void +handle_update (void *cls, const struct GNUNET_MESSENGER_UpdateMessage *msg) +{ + struct GNUNET_MESSENGER_Client *msg_client = cls; + + if (GNUNET_OK != update_handle (msg_client->handle)) + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Name is required to update key!\n"); + + GNUNET_SERVICE_client_continue (msg_client->client); +} + +static void +handle_destroy (void *cls, const struct GNUNET_MESSENGER_DestroyMessage *msg) +{ + struct GNUNET_MESSENGER_Client *msg_client = cls; + + GNUNET_SERVICE_client_drop (msg_client->client); +} + +static int +check_set_name (void *cls, const struct GNUNET_MESSENGER_NameMessage *msg) +{ + GNUNET_MQ_check_zero_termination(msg); + return GNUNET_OK; +} + +static void +handle_set_name (void *cls, const struct GNUNET_MESSENGER_NameMessage *msg) +{ + struct GNUNET_MESSENGER_Client *msg_client = cls; + + const char *name = ((const char*) msg) + sizeof(*msg); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Handles name is now: %s\n", name); + + if (GNUNET_YES != set_handle_name (msg_client->handle, name)) + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "No valid name: %s\n", name); + + GNUNET_SERVICE_client_continue (msg_client->client); +} + +static void +handle_room_open (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg) +{ + struct GNUNET_MESSENGER_Client *msg_client = cls; + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Opening room: %s\n", + GNUNET_h2s (&(msg->key))); + + if (GNUNET_YES == open_handle_room (msg_client->handle, &(msg->key))) + { + const struct GNUNET_ShortHashCode* member_id = get_handle_member_id(msg_client->handle, &(msg->key)); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Opening room with member id: %s\n", + GNUNET_sh2s (member_id)); + + struct GNUNET_MESSENGER_RoomMessage *response; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg(response, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_OPEN); + GNUNET_memcpy(&(response->key), &(msg->key), sizeof(msg->key)); + GNUNET_MQ_send (msg_client->handle->mq, env); + } + else + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Opening room failed: %s\n", + GNUNET_h2s (&(msg->key))); + + GNUNET_SERVICE_client_continue (msg_client->client); +} + +static void +handle_room_entry (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg) +{ + struct GNUNET_MESSENGER_Client *msg_client = cls; + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Entering room: %s, %s\n", + GNUNET_h2s (&(msg->key)), GNUNET_i2s (&(msg->door))); + + if (GNUNET_YES == entry_handle_room (msg_client->handle, &(msg->door), &(msg->key))) + { + const struct GNUNET_ShortHashCode* member_id = get_handle_member_id(msg_client->handle, &(msg->key)); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Entering room with member id: %s\n", + GNUNET_sh2s (member_id)); + + struct GNUNET_MESSENGER_RoomMessage *response; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg(response, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_ENTRY); + GNUNET_memcpy(&(response->door), &(msg->door), sizeof(msg->door)); + GNUNET_memcpy(&(response->key), &(msg->key), sizeof(msg->key)); + GNUNET_MQ_send (msg_client->handle->mq, env); + } + else + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Entrance into room failed: %s, %s\n", + GNUNET_h2s (&(msg->key)), GNUNET_i2s (&(msg->door))); + + GNUNET_SERVICE_client_continue (msg_client->client); +} + +static void +handle_room_close (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg) +{ + struct GNUNET_MESSENGER_Client *msg_client = cls; + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Closing room: %s\n", GNUNET_h2s (&(msg->key))); + + if (GNUNET_YES == close_handle_room (msg_client->handle, &(msg->key))) + { + const struct GNUNET_ShortHashCode* member_id = get_handle_member_id(msg_client->handle, &(msg->key)); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Closing room with member id: %s\n", + GNUNET_sh2s (member_id)); + + struct GNUNET_MESSENGER_RoomMessage *response; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg(response, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_CLOSE); + GNUNET_memcpy(&(response->key), &(msg->key), sizeof(msg->key)); + GNUNET_MQ_send (msg_client->handle->mq, env); + } + else + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Closing room failed: %s\n", GNUNET_h2s (&(msg->key))); + + GNUNET_SERVICE_client_continue (msg_client->client); +} + +static int +check_send_message (void *cls, const struct GNUNET_MESSENGER_SendMessage *msg) +{ + const uint16_t full_length = ntohs (msg->header.size) - sizeof(msg->header); + + if (full_length < sizeof(msg->key)) + return GNUNET_NO; + + const uint16_t length = full_length - sizeof(msg->key); + const char *buffer = ((const char*) msg) + sizeof(*msg); + + struct GNUNET_MESSENGER_Message message; + + if (GNUNET_YES != decode_message (&message, length, buffer)) + return GNUNET_NO; + + return GNUNET_OK; +} + +static void +handle_send_message (void *cls, const struct GNUNET_MESSENGER_SendMessage *msg) +{ + struct GNUNET_MESSENGER_Client *msg_client = cls; + + const struct GNUNET_HashCode *key = &(msg->key); + const char *buffer = ((const char*) msg) + sizeof(*msg); + + const uint16_t length = ntohs (msg->header.size) - sizeof(*msg); + + struct GNUNET_MESSENGER_Message message; + decode_message (&message, length, buffer); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending message: %s to %s\n", + GNUNET_MESSENGER_name_of_kind (message.header.kind), + GNUNET_h2s (key)); + + if (GNUNET_YES != send_handle_message (msg_client->handle, key, &message)) + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Sending message failed: %s to %s\n", + GNUNET_MESSENGER_name_of_kind (message.header.kind), + GNUNET_h2s (key)); + + GNUNET_SERVICE_client_continue (msg_client->client); +} + +static void +handle_get_message (void *cls, const struct GNUNET_MESSENGER_RecvMessage *msg) +{ + struct GNUNET_MESSENGER_Client *msg_client = cls; + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Requesting message from room: %s\n", + GNUNET_h2s (&(msg->key))); + + struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (messenger, &(msg->key)); + + if (room) + get_room_message (room, msg_client->handle, &(msg->hash), GNUNET_YES); + else + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Room not found: %s\n", + GNUNET_h2s (&(msg->key))); + + GNUNET_SERVICE_client_continue (msg_client->client); +} + +static void* +callback_client_connect (void *cls, struct GNUNET_SERVICE_Client *client, struct GNUNET_MQ_Handle *mq) +{ + struct GNUNET_MESSENGER_Client *msg_client = GNUNET_new(struct GNUNET_MESSENGER_Client); + + msg_client->client = client; + msg_client->handle = add_service_handle (messenger, mq); + + return msg_client; +} + +static void +callback_client_disconnect (void *cls, struct GNUNET_SERVICE_Client *client, void *internal_cls) +{ + struct GNUNET_MESSENGER_Client *msg_client = internal_cls; + + remove_service_handle (messenger, msg_client->handle); + + GNUNET_free(msg_client); +} + +/** + * Setup MESSENGER internals. + * + * @param cls closure + * @param config configuration to use + * @param service the initialized service + */ +static void +run (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, struct GNUNET_SERVICE_Handle *service) +{ + messenger = create_service (config, service); + + if ((!messenger) || (!messenger->cadet) || (!messenger->identity)) + GNUNET_SCHEDULER_shutdown (); +} + +/** + * Define "main" method using service macro. + */ +GNUNET_SERVICE_MAIN( + GNUNET_MESSENGER_SERVICE_NAME, + GNUNET_SERVICE_OPTION_NONE, + &run, + &callback_client_connect, + &callback_client_disconnect, + NULL, + GNUNET_MQ_hd_var_size( create, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_CREATE, struct GNUNET_MESSENGER_CreateMessage, NULL ), + GNUNET_MQ_hd_fixed_size( update, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_UPDATE, struct GNUNET_MESSENGER_UpdateMessage, NULL ), + GNUNET_MQ_hd_fixed_size( destroy, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_DESTROY, struct GNUNET_MESSENGER_DestroyMessage, NULL ), + GNUNET_MQ_hd_var_size( set_name, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_SET_NAME, struct GNUNET_MESSENGER_NameMessage, NULL ), + GNUNET_MQ_hd_fixed_size( room_open, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_OPEN, struct GNUNET_MESSENGER_RoomMessage, NULL ), + GNUNET_MQ_hd_fixed_size( room_entry, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_ENTRY, struct GNUNET_MESSENGER_RoomMessage, NULL ), + GNUNET_MQ_hd_fixed_size( room_close, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_CLOSE, struct GNUNET_MESSENGER_RoomMessage, NULL ), + GNUNET_MQ_hd_var_size( send_message, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_SEND_MESSAGE, struct GNUNET_MESSENGER_SendMessage, NULL ), + GNUNET_MQ_hd_fixed_size( get_message, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_GET_MESSAGE, struct GNUNET_MESSENGER_RecvMessage, NULL ), + GNUNET_MQ_handler_end()); diff --git a/src/messenger/gnunet-service-messenger.h b/src/messenger/gnunet-service-messenger.h new file mode 100644 index 000000000..85a1d2549 --- /dev/null +++ b/src/messenger/gnunet-service-messenger.h @@ -0,0 +1,121 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_H +#define GNUNET_SERVICE_MESSENGER_H + +#include "platform.h" +#include "gnunet_cadet_service.h" +#include "gnunet_container_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_identity_service.h" +#include "gnunet_mq_lib.h" +#include "gnunet_peer_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_util_lib.h" + +/** + * Message to create a handle for a client + */ +struct GNUNET_MESSENGER_CreateMessage +{ + struct GNUNET_MessageHeader header; +}; + +/** + * Message to update the handle (its EGO key) for a client + */ +struct GNUNET_MESSENGER_UpdateMessage +{ + struct GNUNET_MessageHeader header; +}; + +/** + * Message to destroy the handle for a client + */ +struct GNUNET_MESSENGER_DestroyMessage +{ + struct GNUNET_MessageHeader header; +}; + +/** + * Message to receive the current name of a handle + */ +struct GNUNET_MESSENGER_NameMessage +{ + struct GNUNET_MessageHeader header; +}; + +/** + * Message to receive the current public key of a handle + */ +struct GNUNET_MESSENGER_KeyMessage +{ + struct GNUNET_MessageHeader header; + struct GNUNET_IDENTITY_PublicKey pubkey; +}; + +/** + * General message to confirm interaction with a room + */ +struct GNUNET_MESSENGER_RoomMessage +{ + struct GNUNET_MessageHeader header; + + struct GNUNET_PeerIdentity door; + struct GNUNET_HashCode key; +}; + +/** + * Message to receive the current member id of a handle in room + */ +struct GNUNET_MESSENGER_MemberMessage +{ + struct GNUNET_MessageHeader header; + + struct GNUNET_HashCode key; + struct GNUNET_ShortHashCode id; +}; + +/** + * Message to send something into a room + */ +struct GNUNET_MESSENGER_SendMessage +{ + struct GNUNET_MessageHeader header; + struct GNUNET_HashCode key; +}; + +/** + * Message to receive something from a room + */ +struct GNUNET_MESSENGER_RecvMessage +{ + struct GNUNET_MessageHeader header; + struct GNUNET_HashCode key; + struct GNUNET_HashCode hash; +}; + +#endif //GNUNET_SERVICE_MESSENGER_H diff --git a/src/messenger/gnunet-service-messenger_basement.c b/src/messenger/gnunet-service-messenger_basement.c new file mode 100644 index 000000000..190cf2de5 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_basement.c @@ -0,0 +1,58 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_basement.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_basement.h" + +size_t +count_of_tunnels (const struct GNUNET_MESSENGER_ListTunnels *tunnels) +{ + const struct GNUNET_MESSENGER_ListTunnel *element; + size_t count = 0; + + for (element = tunnels->head; element; element = element->next) + count++; + + return count; +} + +int +should_connect_tunnel_to (size_t count, size_t src, size_t dst) +{ + if ((src + 1) % count == dst % count) + return GNUNET_YES; + + return GNUNET_NO; +} + +int +required_connection_between (size_t count, size_t src, size_t dst) +{ + if (GNUNET_YES == should_connect_tunnel_to (count, src, dst)) + return GNUNET_YES; + if (GNUNET_YES == should_connect_tunnel_to (count, dst, src)) + return GNUNET_YES; + + return GNUNET_NO; +} diff --git a/src/messenger/gnunet-service-messenger_basement.h b/src/messenger/gnunet-service-messenger_basement.h new file mode 100644 index 000000000..0a1a9b126 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_basement.h @@ -0,0 +1,66 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_basement.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_BASEMENT_H +#define GNUNET_SERVICE_MESSENGER_BASEMENT_H + +#include "messenger_api_list_tunnels.h" + +/** + * Returns the count of peers in a list (typically from the basement of a room). + * + * @param tunnels List of peer identities + * @return Count of the entries in the list + */ +size_t +count_of_tunnels (const struct GNUNET_MESSENGER_ListTunnels *tunnels); + +/** + * Returns GNUNET_YES or GNUNET_NO to determine if the peer at index src should + * or should not connect outgoing to the peer at index dst to construct a complete + * basement with a given count of peers. + * + * @param count Count of peers + * @param src Source index + * @param dst Destination index + * @return GNUNET_YES or GNUNET_NO based on topologic requirement + */ +int +should_connect_tunnel_to (size_t count, size_t src, size_t dst); + +/** + * Returns GNUNET_YES or GNUNET_NO to determine if the peers of index src and + * index dst should be connected in any direction to construct a complete + * basement with a given count of peers. + * + * @param count Count of peers + * @param src Source index + * @param dst Destination index + * @return GNUNET_YES or GNUNET_NO based on topologic requirement + */ +int +required_connection_between (size_t count, size_t src, size_t dst); + +#endif //GNUNET_SERVICE_MESSENGER_BASEMENT_H diff --git a/src/messenger/gnunet-service-messenger_contact.c b/src/messenger/gnunet-service-messenger_contact.c new file mode 100644 index 000000000..1ec125402 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_contact.c @@ -0,0 +1,96 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_contact.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_contact.h" + +struct GNUNET_MESSENGER_SrvContact* +create_contact (const struct GNUNET_IDENTITY_PublicKey *key) +{ + struct GNUNET_MESSENGER_SrvContact *contact = GNUNET_new(struct GNUNET_MESSENGER_SrvContact); + + contact->name = NULL; + contact->rc = 0; + + GNUNET_memcpy(&(contact->public_key), key, sizeof(contact->public_key)); + + return contact; +} + +void +destroy_contact (struct GNUNET_MESSENGER_SrvContact *contact) +{ + if (contact->name) + GNUNET_free(contact->name); + + GNUNET_free(contact); +} + +const char* +get_contact_name (const struct GNUNET_MESSENGER_SrvContact *contact) +{ + return contact->name; +} + +void +set_contact_name (struct GNUNET_MESSENGER_SrvContact *contact, const char *name) +{ + GNUNET_assert(name); + + if (contact->name) + GNUNET_free(contact->name); + + contact->name = GNUNET_strdup(name); +} + +const struct GNUNET_IDENTITY_PublicKey* +get_contact_key (const struct GNUNET_MESSENGER_SrvContact *contact) +{ + return &(contact->public_key); +} + +void +increase_contact_rc (struct GNUNET_MESSENGER_SrvContact *contact) +{ + contact->rc++; +} + +int +decrease_contact_rc (struct GNUNET_MESSENGER_SrvContact *contact) +{ + if (contact->rc > 0) + contact->rc--; + + return contact->rc ? GNUNET_NO : GNUNET_YES; +} + +const struct GNUNET_HashCode* +get_contact_id_from_key (const struct GNUNET_MESSENGER_SrvContact *contact) +{ + static struct GNUNET_HashCode id; + + GNUNET_CRYPTO_hash (&(contact->public_key), sizeof(contact->public_key), &id); + + return &id; +} diff --git a/src/messenger/gnunet-service-messenger_contact.h b/src/messenger/gnunet-service-messenger_contact.h new file mode 100644 index 000000000..4a4f8bf0f --- /dev/null +++ b/src/messenger/gnunet-service-messenger_contact.h @@ -0,0 +1,112 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_contact.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_CONTACT_H +#define GNUNET_SERVICE_MESSENGER_CONTACT_H + +#include "platform.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_identity_service.h" + +struct GNUNET_MESSENGER_SrvContact +{ + char *name; + size_t rc; + + struct GNUNET_IDENTITY_PublicKey public_key; +}; + +/** + * Creates and allocates a new contact with a given public key from an EGO. + * + * @param key Public key + * @return New contact + */ +struct GNUNET_MESSENGER_SrvContact* +create_contact (const struct GNUNET_IDENTITY_PublicKey *key); + +/** + * Destroys a contact and frees its memory fully. + * + * @param contact Contact + */ +void +destroy_contact (struct GNUNET_MESSENGER_SrvContact *contact); + +/** + * Returns the current name of a given contact or NULL if no valid name was assigned yet. + * + * @param contact Contact + * @return Name of the contact or NULL + */ +const char* +get_contact_name (const struct GNUNET_MESSENGER_SrvContact *contact); + +/** + * Changes the current name of a given contact by copying it from the parameter name. + * + * @param contact Contact + * @param name Valid name (may not be NULL!) + */ +void +set_contact_name (struct GNUNET_MESSENGER_SrvContact *contact, const char *name); + +/** + * Returns the public key of a given contact. + * + * @param contact Contact + * @return Public key of the contact + */ +const struct GNUNET_IDENTITY_PublicKey* +get_contact_key (const struct GNUNET_MESSENGER_SrvContact *contact); + +/** + * Increases the reference counter of a given contact which is zero as default. + * + * @param contact Contact + */ +void +increase_contact_rc (struct GNUNET_MESSENGER_SrvContact *contact); + +/** + * Decreases the reference counter if possible (can not underflow!) of a given contact + * and returns GNUNET_YES if the counter is equal to zero, otherwise GNUNET_NO. + * + * @param contact Contact + * @return GNUNET_YES or GNUNET_NO depending on the reference counter + */ +int +decrease_contact_rc (struct GNUNET_MESSENGER_SrvContact *contact); + +/** + * Returns the resulting hashcode of the public key from a given contact. + * + * @param contact Contact + * @return Hash of the contacts public key + */ +const struct GNUNET_HashCode* +get_contact_id_from_key (const struct GNUNET_MESSENGER_SrvContact *contact); + +#endif //GNUNET_SERVICE_MESSENGER_CONTACT_H diff --git a/src/messenger/gnunet-service-messenger_handle.c b/src/messenger/gnunet-service-messenger_handle.c new file mode 100644 index 000000000..38ad6fbb4 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_handle.c @@ -0,0 +1,503 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_handle.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_handle.h" + +#include "gnunet-service-messenger.h" +#include "gnunet-service-messenger_message_kind.h" + +struct GNUNET_MESSENGER_SrvHandle* +create_handle (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MQ_Handle *mq) +{ + struct GNUNET_MESSENGER_SrvHandle *handle = GNUNET_new(struct GNUNET_MESSENGER_SrvHandle); + + handle->service = service; + handle->mq = mq; + + handle->name = NULL; + + handle->operation = NULL; + + handle->ego = NULL; + + handle->member_ids = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + + return handle; +} + +int +iterate_free_member_ids (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + GNUNET_free(value); + + return GNUNET_YES; +} + +void +destroy_handle (struct GNUNET_MESSENGER_SrvHandle *handle) +{ + if (handle->service->dir) + save_handle_configuration(handle); + + if (handle->operation) + GNUNET_IDENTITY_cancel (handle->operation); + + if (handle->name) + GNUNET_free(handle->name); + + GNUNET_CONTAINER_multihashmap_iterate (handle->member_ids, iterate_free_member_ids, NULL); + GNUNET_CONTAINER_multihashmap_destroy (handle->member_ids); + + GNUNET_free(handle); +} + +void +get_handle_data_subdir (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name, char **dir) +{ + if (name) + GNUNET_asprintf (dir, "%s%s%c%s%c", handle->service->dir, "identities", + DIR_SEPARATOR, name, DIR_SEPARATOR); + else + GNUNET_asprintf (dir, "%s%s%c", handle->service->dir, "anonymous", + DIR_SEPARATOR); +} + +static int +create_handle_member_id (const struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key) +{ + struct GNUNET_ShortHashCode *random_id = generate_service_new_member_id (handle->service, key); + + if (!random_id) + return GNUNET_NO; + + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (handle->member_ids, key, random_id, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + { + GNUNET_free(random_id); + return GNUNET_NO; + } + + GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Created a new member id (%s) for room: %s\n", + GNUNET_sh2s(random_id), GNUNET_h2s(key)); + + return GNUNET_YES; +} + +const struct GNUNET_ShortHashCode* +get_handle_member_id (const struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key) +{ + return GNUNET_CONTAINER_multihashmap_get (handle->member_ids, key); +} + +void +change_handle_member_id (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key, + const struct GNUNET_ShortHashCode *unique_id) +{ + struct GNUNET_ShortHashCode *member_id = GNUNET_CONTAINER_multihashmap_get (handle->member_ids, key); + + if (member_id) + { + GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Changed a member id (%s) for room (%s) ", + GNUNET_sh2s(member_id), GNUNET_h2s(key)); + GNUNET_log(GNUNET_ERROR_TYPE_INFO, "into (%s).\n", + GNUNET_sh2s(unique_id)); + + GNUNET_memcpy(member_id, unique_id, sizeof(*unique_id)); + + struct GNUNET_MESSENGER_MemberMessage *msg; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_MEMBER_ID); + + GNUNET_memcpy(&(msg->key), key, sizeof(*key)); + GNUNET_memcpy(&(msg->id), member_id, sizeof(*member_id)); + + GNUNET_MQ_send (handle->mq, env); + } + else + { + member_id = GNUNET_new(struct GNUNET_ShortHashCode); + GNUNET_memcpy(member_id, unique_id, sizeof(*member_id)); + + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (handle->member_ids, key, member_id, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + GNUNET_free(member_id); + } +} + +static void +change_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name) +{ + if (handle->name) + GNUNET_free(handle->name); + + handle->name = name ? GNUNET_strdup(name) : NULL; + + const uint16_t name_len = handle->name ? strlen (handle->name) : 0; + + struct GNUNET_MESSENGER_NameMessage *msg; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg_extra(msg, name_len + 1, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_NAME); + + char *extra = ((char*) msg) + sizeof(*msg); + + if (name_len) + GNUNET_memcpy(extra, handle->name, name_len); + + extra[name_len] = '\0'; + + GNUNET_MQ_send (handle->mq, env); +} + +static void +change_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle, struct GNUNET_MESSENGER_Ego *ego) +{ + handle->ego = ego; + + ego = get_handle_ego(handle); + + struct GNUNET_MESSENGER_KeyMessage *msg; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_KEY); + + GNUNET_memcpy(&(msg->pubkey), &(ego->pub), sizeof(ego->pub)); + + GNUNET_MQ_send (handle->mq, env); +} + +struct GNUNET_MESSENGER_Ego* +get_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle) +{ + static struct GNUNET_MESSENGER_Ego anonymous; + static int read_keys = 0; + + if (handle->ego) + return handle->ego; + + if (!read_keys) + { + struct GNUNET_IDENTITY_Ego* ego = GNUNET_IDENTITY_ego_get_anonymous (); + GNUNET_memcpy(&(anonymous.priv), GNUNET_IDENTITY_ego_get_private_key(ego), sizeof(anonymous.priv)); + GNUNET_IDENTITY_ego_get_public_key(ego, &(anonymous.pub)); + read_keys = 1; + } + + return &anonymous; +} + +void +setup_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name) +{ + change_handle_name (handle, name); + change_handle_ego (handle, handle->name? lookup_service_ego(handle->service, handle->name) : NULL); + + if (handle->service->dir) + load_handle_configuration(handle); +} + +struct GNUNET_MESSENGER_MessageHandle +{ + struct GNUNET_MESSENGER_SrvHandle *handle; + struct GNUNET_MESSENGER_Message *message; +}; + +static int +iterate_send_message (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_MessageHandle *msg_handle = cls; + + send_handle_message (msg_handle->handle, key, msg_handle->message); + + return GNUNET_YES; +} + +static void +callback_ego_create (void *cls, const struct GNUNET_IDENTITY_PrivateKey *key, const char *emsg) +{ + struct GNUNET_MESSENGER_SrvHandle *handle = cls; + + handle->operation = NULL; + + if (emsg) + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s\n", emsg); + + if (key) + { + struct GNUNET_MESSENGER_MessageHandle msg_handle; + + msg_handle.handle = handle; + msg_handle.message = create_message_key (key); + + GNUNET_CONTAINER_multihashmap_iterate (handle->member_ids, iterate_send_message, &msg_handle); + + destroy_message (msg_handle.message); + + update_service_ego(handle->service, handle->name, key); + + change_handle_ego (handle, lookup_service_ego(handle->service, handle->name)); + } +} + +int +update_handle (struct GNUNET_MESSENGER_SrvHandle *handle) +{ + GNUNET_assert(handle); + + if (!handle->name) + return GNUNET_SYSERR; + + struct GNUNET_MESSENGER_Ego *ego = lookup_service_ego(handle->service, handle->name); + + if (!ego) + handle->operation = GNUNET_IDENTITY_create (handle->service->identity, handle->name, NULL, + GNUNET_IDENTITY_TYPE_ECDSA, callback_ego_create, handle); + else + change_handle_ego (handle, ego); + + return GNUNET_OK; +} + +int +set_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name) +{ + GNUNET_assert(handle); + + if ((name) && (lookup_service_ego(handle->service, name))) + return GNUNET_NO; + + struct GNUNET_IDENTITY_Operation *operation = handle->operation; + + if (handle->name) + handle->operation = GNUNET_IDENTITY_rename (handle->service->identity, handle->name, name, NULL, NULL); + + char *old_dir; + get_handle_data_subdir (handle, handle->name, &old_dir); + + char *new_dir; + get_handle_data_subdir (handle, name, &new_dir); + + int result = 0; + + if (GNUNET_YES == GNUNET_DISK_directory_test (old_dir, GNUNET_YES)) + { + GNUNET_DISK_directory_create_for_file (new_dir); + + result = rename (old_dir, new_dir); + } + else if (GNUNET_YES == GNUNET_DISK_directory_test (new_dir, GNUNET_NO)) + result = -1; + + if (0 == result) + { + struct GNUNET_MESSENGER_MessageHandle msg_handle; + + msg_handle.handle = handle; + msg_handle.message = create_message_name (name); + + GNUNET_CONTAINER_multihashmap_iterate (handle->member_ids, iterate_send_message, &msg_handle); + + destroy_message (msg_handle.message); + + change_handle_name (handle, name); + + if (operation) + GNUNET_IDENTITY_cancel (operation); + } + else + { + if (handle->operation) + { + GNUNET_IDENTITY_cancel (handle->operation); + + handle->operation = operation; + } + } + + GNUNET_free(old_dir); + GNUNET_free(new_dir); + + return (result == 0 ? GNUNET_OK : GNUNET_NO); +} + +int +open_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key) +{ + if ((!get_handle_member_id (handle, key)) && (GNUNET_YES != create_handle_member_id (handle, key))) + return GNUNET_NO; + + return open_service_room (handle->service, handle, key); +} + +int +entry_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_PeerIdentity *door, + const struct GNUNET_HashCode *key) +{ + if ((!get_handle_member_id (handle, key)) && (GNUNET_YES != create_handle_member_id (handle, key))) + return GNUNET_NO; + + return entry_service_room (handle->service, handle, door, key); +} + +int +close_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key) +{ + if (!get_handle_member_id (handle, key)) + return GNUNET_NO; + + return close_service_room (handle->service, handle, key); +} + +int +send_handle_message (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key, + struct GNUNET_MESSENGER_Message *message) +{ + const struct GNUNET_ShortHashCode *id = get_handle_member_id (handle, key); + + if (!id) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "It is required to be a member of a room to send messages!\n"); + return GNUNET_NO; + } + + struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (handle->service, key); + + if (!room) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "The room (%s) is unknown!\n", GNUNET_h2s (key)); + return GNUNET_NO; + } + + struct GNUNET_HashCode hash; + + GNUNET_memcpy(&(message->header.sender_id), id, sizeof(*id)); + + send_room_message (room, handle, message, &hash); + return GNUNET_YES; +} + +static int callback_scan_for_rooms(void* cls, const char *filename) { + struct GNUNET_MESSENGER_SrvHandle* handle = cls; + + struct GNUNET_CONFIGURATION_Handle* cfg = GNUNET_CONFIGURATION_create(); + + if ((GNUNET_YES == GNUNET_DISK_file_test(filename)) && + (GNUNET_OK == GNUNET_CONFIGURATION_parse(cfg, filename))) + { + struct GNUNET_HashCode key; + struct GNUNET_ShortHashCode member_id; + + if ((GNUNET_OK == GNUNET_CONFIGURATION_get_data(cfg, "room", "key", &key, sizeof(key))) && + (GNUNET_OK == GNUNET_CONFIGURATION_get_data(cfg, "room", "member_id", &member_id, sizeof(member_id)))) + change_handle_member_id(handle, &key, &member_id); + } + + GNUNET_CONFIGURATION_destroy(cfg); + return GNUNET_OK; +} + +void load_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle) { + char* id_dir; + get_handle_data_subdir(handle, handle->name, &id_dir); + + if (GNUNET_YES == GNUNET_DISK_directory_test(id_dir, GNUNET_YES)) + { + char* scan_dir; + GNUNET_asprintf(&scan_dir, "%s%s%c", id_dir, "rooms", DIR_SEPARATOR); + + if (GNUNET_OK == GNUNET_DISK_directory_test(scan_dir, GNUNET_YES)) + GNUNET_DISK_directory_scan(scan_dir, callback_scan_for_rooms, handle); + + GNUNET_free(scan_dir); + } + + GNUNET_free(id_dir); +} + +static int +iterate_save_rooms(void* cls, const struct GNUNET_HashCode* key, void* value) +{ + struct GNUNET_MESSENGER_SrvHandle* handle = cls; + struct GNUNET_ShortHashCode* member_id = value; + + char* id_dir; + get_handle_data_subdir(handle, handle->name, &id_dir); + + char* filename; + GNUNET_asprintf(&filename, "%s%s%c%s.cfg", + id_dir, "rooms", DIR_SEPARATOR, + GNUNET_h2s(key)); + + GNUNET_free(id_dir); + + struct GNUNET_CONFIGURATION_Handle* cfg = GNUNET_CONFIGURATION_create(); + + char* key_data = GNUNET_STRINGS_data_to_string_alloc(key, sizeof(*key)); + + if (key_data) + { + GNUNET_CONFIGURATION_set_value_string(cfg, "room", "key", key_data); + + GNUNET_free(key_data); + } + + char* member_id_data = GNUNET_STRINGS_data_to_string_alloc(member_id, sizeof(*member_id)); + + if (member_id_data) + { + GNUNET_CONFIGURATION_set_value_string(cfg, "room", "member_id", member_id_data); + + GNUNET_free(member_id_data); + } + + GNUNET_CONFIGURATION_write(cfg, filename); + GNUNET_CONFIGURATION_destroy(cfg); + + GNUNET_free(filename); + + return GNUNET_YES; +} + +void save_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle) +{ + char* id_dir; + get_handle_data_subdir(handle, handle->name, &id_dir); + + if ((GNUNET_YES == GNUNET_DISK_directory_test(id_dir, GNUNET_NO)) || + (GNUNET_OK == GNUNET_DISK_directory_create(id_dir))) + { + char* save_dir; + GNUNET_asprintf(&save_dir, "%s%s%c", id_dir, "rooms", DIR_SEPARATOR); + + if ((GNUNET_YES == GNUNET_DISK_directory_test(save_dir, GNUNET_NO)) || + (GNUNET_OK == GNUNET_DISK_directory_create(save_dir))) + GNUNET_CONTAINER_multihashmap_iterate(handle->member_ids, iterate_save_rooms, handle); + + GNUNET_free(save_dir); + } + + GNUNET_free(id_dir); +} diff --git a/src/messenger/gnunet-service-messenger_handle.h b/src/messenger/gnunet-service-messenger_handle.h new file mode 100644 index 000000000..81cf377a8 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_handle.h @@ -0,0 +1,216 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_handle.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_HANDLE_H +#define GNUNET_SERVICE_MESSENGER_HANDLE_H + +#include "platform.h" +#include "gnunet_cadet_service.h" +#include "gnunet_container_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_identity_service.h" +#include "gnunet_peer_lib.h" +#include "gnunet_mq_lib.h" + +#include "gnunet-service-messenger_service.h" + +#include "messenger_api_ego.h" +#include "messenger_api_message.h" + +struct GNUNET_MESSENGER_SrvHandle +{ + struct GNUNET_MESSENGER_Service *service; + struct GNUNET_MQ_Handle *mq; + + char *name; + + struct GNUNET_IDENTITY_Operation *operation; + + struct GNUNET_MESSENGER_Ego *ego; + + struct GNUNET_CONTAINER_MultiHashMap *member_ids; +}; + +/** + * Creates and allocates a new handle related to a service and using a given mq (message queue). + * + * @param service MESSENGER Service + * @param mq Message queue + * @return New handle + */ +struct GNUNET_MESSENGER_SrvHandle* +create_handle (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MQ_Handle *mq); + +/** + * Destroys a handle and frees its memory fully. + * + * @param handle Handle + */ +void +destroy_handle (struct GNUNET_MESSENGER_SrvHandle *handle); + +/** + * Writes the path of the directory for a given handle using a specific name to the parameter + * dir. This directory will be used to store data regarding the handle and its messages. + * + * @param handle Handle + * @param name Potential name of the handle + * @param dir[out] Path to store data + */ +void +get_handle_data_subdir (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name, char **dir); + +/** + * Returns the member id of a given handle in a specific room. + * + * If the handle is not a member of the specific room, NULL gets returned. + * + * @param handle Handle + * @param key Key of a room + * @return Member id or NULL + */ +const struct GNUNET_ShortHashCode* +get_handle_member_id (const struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key); + +/** + * Changes the member id of a given handle in a specific room to match a unique_id. + * + * The client connected to the handle will be informed afterwards automatically. + * + * @param handle Handle + * @param key Key of a room + * @param unique_id Unique member id + */ +void +change_handle_member_id (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key, + const struct GNUNET_ShortHashCode *unique_id); + +/** + * Returns the EGO used by a given handle. + * + * @param handle Handle + * @return EGO keypair + */ +struct GNUNET_MESSENGER_Ego* +get_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle); + +/** + * Tries to set the name and EGO key of a handle initially by looking up a specific name. + * + * @param handle Handle + * @param name Name (optionally: valid EGO name) + */ +void +setup_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name); + +/** + * Tries to change the keypair of an EGO of a handle under the same name and informs all rooms + * about the change automatically. + * + * @param handle Handle + * @return GNUNET_OK on success, otherwise GNUNET_SYSERR + */ +int +update_handle (struct GNUNET_MESSENGER_SrvHandle *handle); + +/** + * Tries to rename the handle which implies renaming the EGO its using and moving all related data into + * the directory fitting to the changed name. + * + * The client connected to the handle will be informed afterwards automatically. + * + * @param handle Handle + * @param name New name + * @return GNUNET_OK on success, otherwise GNUNET_NO + */ +int +set_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name); + +/** + * Makes a given handle a member of the room using a specific key and opens the + * room from the handles service. + * + * @param handle Handle + * @param key Key of a room + * @return GNUNET_YES on success, otherwise GNUNET_NO + */ +int +open_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key); + +/** + * Makes a given handle a member of the room using a specific key and enters the room + * through a tunnel to a peer identified by a given door (peer identity). + * + * @param handle Handle + * @param door Peer identity + * @param key Key of a room + * @return GNUNET_YES on success, otherwise GNUNET_NO + */ +int +entry_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_PeerIdentity *door, + const struct GNUNET_HashCode *key); + +/** + * Removes the membership of the room using a specific key and closes it if no other handle + * from this service is still a member of it. + * + * @param handle Handle + * @param key Key of a room + * @return GNUNET_YES on success, otherwise GNUNET_NO + */ +int +close_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key); + +/** + * Sends a message from a given handle to the room using a specific key. + * + * @param handle Handle + * @param key Key of a room + * @param message Message + * @return GNUNET_YES on success, otherwise GNUNET_NO + */ +int +send_handle_message (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key, + struct GNUNET_MESSENGER_Message *message); + +/** + * Loads member ids and other potential configuration from a given handle which + * depends on the given name the handle uses. + * + * @param handle Handle + */ +void +load_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle); + +/** + * Saves member ids and other potential configuration from a given handle which + * depends on the given name the handle uses. + * + * @param handle Handle + */ +void +save_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle); + +#endif //GNUNET_SERVICE_MESSENGER_HANDLE_H diff --git a/src/messenger/gnunet-service-messenger_list_handles.c b/src/messenger/gnunet-service-messenger_list_handles.c new file mode 100644 index 000000000..16a160dea --- /dev/null +++ b/src/messenger/gnunet-service-messenger_list_handles.c @@ -0,0 +1,95 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_list_handles.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_list_handles.h" + +#include "gnunet-service-messenger_handle.h" + +void +init_list_handles (struct GNUNET_MESSENGER_ListHandles *handles) +{ + GNUNET_assert(handles); + + handles->head = NULL; + handles->tail = NULL; +} + +void +clear_list_handles (struct GNUNET_MESSENGER_ListHandles *handles) +{ + GNUNET_assert(handles); + + while (handles->head) + { + struct GNUNET_MESSENGER_ListHandle *element = handles->head; + + GNUNET_CONTAINER_DLL_remove(handles->head, handles->tail, element); + destroy_handle (element->handle); + GNUNET_free(element); + } + + handles->head = NULL; + handles->tail = NULL; +} + +void +add_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle) +{ + struct GNUNET_MESSENGER_ListHandle *element = GNUNET_new(struct GNUNET_MESSENGER_ListHandle); + + element->handle = handle; + + GNUNET_CONTAINER_DLL_insert_tail(handles->head, handles->tail, element); +} + +int +remove_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle) +{ + struct GNUNET_MESSENGER_ListHandle *element; + + for (element = handles->head; element; element = element->next) + if (element->handle == handle) + break; + + if (!element) + return GNUNET_NO; + + GNUNET_CONTAINER_DLL_remove(handles->head, handles->tail, element); + GNUNET_free(element); + + return GNUNET_YES; +} + +void* +find_list_handle_by_member (struct GNUNET_MESSENGER_ListHandles *handles, const struct GNUNET_HashCode *key) +{ + struct GNUNET_MESSENGER_ListHandle *element; + + for (element = handles->head; element; element = element->next) + if (get_handle_member_id ((struct GNUNET_MESSENGER_SrvHandle*) element->handle, key)) + return element->handle; + + return NULL; +} diff --git a/src/messenger/gnunet-service-messenger_list_handles.h b/src/messenger/gnunet-service-messenger_list_handles.h new file mode 100644 index 000000000..fe92cc58a --- /dev/null +++ b/src/messenger/gnunet-service-messenger_list_handles.h @@ -0,0 +1,96 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_list_handles.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_LIST_HANDLES_H +#define GNUNET_SERVICE_MESSENGER_LIST_HANDLES_H + +#include "platform.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_container_lib.h" + +struct GNUNET_MESSENGER_ListHandle +{ + struct GNUNET_MESSENGER_ListHandle *prev; + struct GNUNET_MESSENGER_ListHandle *next; + + void *handle; +}; + +struct GNUNET_MESSENGER_ListHandles +{ + struct GNUNET_MESSENGER_ListHandle *head; + struct GNUNET_MESSENGER_ListHandle *tail; +}; + +/** + * Initializes list of handles as empty list. + * + * @param handles List of handles + */ +void +init_list_handles (struct GNUNET_MESSENGER_ListHandles *handles); + +/** + * Destroys remaining handles and clears the list. + * + * @param handles List of handles + */ +void +clear_list_handles (struct GNUNET_MESSENGER_ListHandles *handles); + +/** + * Adds a specific handle to the end of the list. + * + * @param handles List of handles + * @param handle Handle + */ +void +add_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle); + +/** + * Removes the first entry matching with a specific handle from the list and + * returns GNUNET_YES on success or GNUNET_NO on failure. + * + * @param handles List of handles + * @param handle Handle + * @return GNUNET_YES on success, otherwise GNUNET_NO + */ +int +remove_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle); + +/** + * Searches linearly through the list of handles for members of a specific room + * which is identified by a given key. + * + * If no handle is found which is a current member, NULL gets returned. + * + * @param handles List of handles + * @param key Common key of a room + * @return First handle which is a current member + */ +void* +find_list_handle_by_member (struct GNUNET_MESSENGER_ListHandles *handles, const struct GNUNET_HashCode *key); + +#endif //GNUNET_SERVICE_MESSENGER_LIST_HANDLES_H diff --git a/src/messenger/gnunet-service-messenger_list_messages.c b/src/messenger/gnunet-service-messenger_list_messages.c new file mode 100644 index 000000000..c4f1f7043 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_list_messages.c @@ -0,0 +1,76 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_list_messages.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_list_messages.h" + +void +init_list_messages (struct GNUNET_MESSENGER_ListMessages *messages) +{ + GNUNET_assert(messages); + + messages->head = NULL; + messages->tail = NULL; +} + +void +clear_list_messages (struct GNUNET_MESSENGER_ListMessages *messages) +{ + GNUNET_assert(messages); + + while (messages->head) + { + struct GNUNET_MESSENGER_ListMessage *element = messages->head; + + GNUNET_CONTAINER_DLL_remove(messages->head, messages->tail, element); + GNUNET_free(element); + } + + messages->head = NULL; + messages->tail = NULL; +} + +void +add_to_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_ListMessage *element = GNUNET_new(struct GNUNET_MESSENGER_ListMessage); + + GNUNET_memcpy(&(element->hash), hash, sizeof(struct GNUNET_HashCode)); + + GNUNET_CONTAINER_DLL_insert_tail(messages->head, messages->tail, element); +} + +void +remove_from_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_ListMessage *element; + + for (element = messages->head; element; element = element->next) + if (0 == GNUNET_CRYPTO_hash_cmp (&(element->hash), hash)) + { + GNUNET_CONTAINER_DLL_remove(messages->head, messages->tail, element); + GNUNET_free(element); + break; + } +} diff --git a/src/messenger/gnunet-service-messenger_list_messages.h b/src/messenger/gnunet-service-messenger_list_messages.h new file mode 100644 index 000000000..266c30ec6 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_list_messages.h @@ -0,0 +1,81 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_list_messages.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_LIST_MESSAGES_H +#define GNUNET_SERVICE_MESSENGER_LIST_MESSAGES_H + +#include "platform.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_container_lib.h" + +struct GNUNET_MESSENGER_ListMessage +{ + struct GNUNET_MESSENGER_ListMessage *prev; + struct GNUNET_MESSENGER_ListMessage *next; + + struct GNUNET_HashCode hash; +}; + +struct GNUNET_MESSENGER_ListMessages +{ + struct GNUNET_MESSENGER_ListMessage *head; + struct GNUNET_MESSENGER_ListMessage *tail; +}; + +/** + * Initializes list of message hashes as empty list. + * + * @param messages List of hashes + */ +void +init_list_messages (struct GNUNET_MESSENGER_ListMessages *messages); + +/** + * Clears the list of message hashes. + * + * @param messages List of hashes + */ +void +clear_list_messages (struct GNUNET_MESSENGER_ListMessages *messages); + +/** + * Adds a specific hash from a message to the end of the list. + * + * @param messages List of hashes + * @param hash Hash of message + */ +void +add_to_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const struct GNUNET_HashCode *hash); + +/** + * Removes the first entry with a matching hash from the list. + * + * @param messages List of hashes + * @param hash Hash of message + */ +void +remove_from_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const struct GNUNET_HashCode *hash); + +#endif //GNUNET_SERVICE_MESSENGER_LIST_MESSAGES_H diff --git a/src/messenger/gnunet-service-messenger_message_handle.c b/src/messenger/gnunet-service-messenger_message_handle.c new file mode 100644 index 000000000..1652435c8 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_message_handle.c @@ -0,0 +1,130 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_message_handle.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_message_handle.h" + +void +handle_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_SrvContact *contact = get_room_contact (room, &(message->header.sender_id)); + + if (!contact) + add_room_contact (room, &(message->header.sender_id), &(message->body.join.key)); + + struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, &(message->header.sender_id)); + + if (!info) + { + info = GNUNET_new(struct GNUNET_MESSENGER_MemberInfo); + + info->access = GNUNET_MESSENGER_MEMBER_UNKNOWN; + init_list_messages (&(info->session_messages)); + } + else + clear_list_messages (&(info->session_messages)); + + if (GNUNET_YES == GNUNET_CONTAINER_multishortmap_put (room->member_infos, &(message->header.sender_id), info, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + add_to_list_messages (&(info->session_messages), hash); +} + +void +handle_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, &(message->header.sender_id)); + + if (info) + clear_list_messages (&(info->session_messages)); +} + +void +handle_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_SrvContact *contact = get_room_contact (room, &(message->header.sender_id)); + + if (contact) + set_contact_name (contact, message->body.name.name); + + struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, &(message->header.sender_id)); + + if (info) + add_to_list_messages (&(info->session_messages), hash); +} + +void +handle_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_SrvContact *contact = get_room_contact (room, &(message->header.sender_id)); + + if (contact) + swap_service_contact_by_pubkey (room->service, contact, &(message->body.key.key)); + + struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, &(message->header.sender_id)); + + if (info) + add_to_list_messages (&(info->session_messages), hash); +} + +void +handle_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + if (GNUNET_NO == contains_list_tunnels (&(room->basement), &(message->body.peer.peer))) + add_to_list_tunnels (&(room->basement), &(message->body.peer.peer)); + + if (room->peer_message) + rebuild_room_basement_structure (room); +} + +void +handle_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, &(message->header.sender_id)); + + if (info) + add_to_list_messages (&(info->session_messages), hash); + + switch_room_member_id (room, &(message->header.sender_id), &(message->body.id.id), hash); +} + +void +handle_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_ListTunnel *element = find_list_tunnels (&(room->basement), &(message->body.peer.peer), NULL); + + if (!element) + return; + + remove_from_list_tunnels (&(room->basement), element); + + if (room->peer_message) + rebuild_room_basement_structure (room); +} diff --git a/src/messenger/gnunet-service-messenger_message_handle.h b/src/messenger/gnunet-service-messenger_message_handle.h new file mode 100644 index 000000000..d091e1d11 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_message_handle.h @@ -0,0 +1,128 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_message_handle.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_MESSAGE_HANDLE_H +#define GNUNET_SERVICE_MESSENGER_MESSAGE_HANDLE_H + +#include "platform.h" +#include "gnunet_crypto_lib.h" + +#include "gnunet-service-messenger_message_kind.h" + +#include "gnunet-service-messenger_tunnel.h" +#include "messenger_api_message.h" + +/** + * Handles a received or sent join message to make changes of current member information. + * (add matching member and clear member info) + * + * @param room Room of the message + * @param tunnel Receiving/sending connection (may be NULL) + * @param message JOIN-Message + * @param hash Hash of the message + */ +void +handle_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Handles a received or sent leave message to make changes of current member information. + * (remove matching member and clear member info) + * + * @param room Room of the message + * @param tunnel Receiving/sending connection (may be NULL) + * @param message LEAVE-Message + * @param hash Hash of the message + */ +void +handle_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Handles a received or sent name message to rename a current member. + * (change name of matching member) + * + * @param room Room of the message + * @param tunnel Receiving/sending connection (may be NULL) + * @param message NAME-Message + * @param hash Hash of the message + */ +void +handle_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Handles a received or sent key message to change the key of a member and rearrange the contacts accordingly. + * (move the member in the contacts and change its key) + * + * @param room Room of the message + * @param tunnel Receiving/sending connection (may be NULL) + * @param message KEY-Message + * @param hash Hash of the message + */ +void +handle_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Handles a received or sent peer message to make changes of the basement in the room. + * (add a new peer to the basement and restructure connections based on updated list of peers) + * + * @param room Room of the message + * @param tunnel Receiving/sending connection (may be NULL) + * @param message PEER-Message + * @param hash Hash of the message + */ +void +handle_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Handles a received or sent id message to change a members id. + * (change id of matching member) + * + * @param room Room of the message + * @param tunnel Receiving/sending connection (may be NULL) + * @param message ID-Message + * @param hash Hash of the message + */ +void +handle_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Handles a received or sent miss message to drop a peer from the basement in the room. + * (remove a peer from the basement and restructure connections based on updated list of peers) + * + * @param room Room of the message + * @param tunnel Receiving/sending connection (may be NULL) + * @param message MISS-Message + * @param hash Hash of the message + */ +void +handle_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +#endif //GNUNET_SERVICE_MESSENGER_MESSAGE_HANDLE_H diff --git a/src/messenger/gnunet-service-messenger_message_kind.c b/src/messenger/gnunet-service-messenger_message_kind.c new file mode 100644 index 000000000..9c829fe09 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_message_kind.c @@ -0,0 +1,192 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_message_kind.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_message_kind.h" +#include "gnunet-service-messenger_util.h" + +struct GNUNET_MESSENGER_Message* +create_message_info (struct GNUNET_MESSENGER_Ego *ego, struct GNUNET_CONTAINER_MultiShortmap *members) +{ + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_INFO); + + if (!message) + return NULL; + + GNUNET_memcpy(&(message->body.info.host_key), &(ego->pub), sizeof(ego->pub)); + + if (GNUNET_YES == generate_free_member_id (&(message->body.info.unique_id), members)) + return message; + else + { + destroy_message (message); + return NULL; + } +} + +struct GNUNET_MESSENGER_Message* +create_message_join (struct GNUNET_MESSENGER_Ego *ego) +{ + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_JOIN); + + if (!message) + return NULL; + + GNUNET_memcpy(&(message->body.join.key), &(ego->pub), sizeof(ego->pub)); + + return message; +} + +struct GNUNET_MESSENGER_Message* +create_message_leave () +{ + return create_message (GNUNET_MESSENGER_KIND_LEAVE); +} + +struct GNUNET_MESSENGER_Message* +create_message_name (const char *name) +{ + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_NAME); + + if (!message) + return NULL; + + message->body.name.name = GNUNET_strdup(name); + return message; +} + +struct GNUNET_MESSENGER_Message* +create_message_key (const struct GNUNET_IDENTITY_PrivateKey *key) +{ + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_KEY); + + if (!message) + return NULL; + + GNUNET_IDENTITY_key_get_public (key, &(message->body.key.key)); + return message; +} + +struct GNUNET_MESSENGER_Message* +create_message_peer (const struct GNUNET_MESSENGER_Service *service) +{ + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_PEER); + + if (!message) + return NULL; + + if (GNUNET_OK == get_service_peer_identity (service, &(message->body.peer.peer))) + return message; + else + { + destroy_message (message); + return NULL; + } +} + +struct GNUNET_MESSENGER_Message* +create_message_id (const struct GNUNET_ShortHashCode *unique_id) +{ + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_ID); + + if (!message) + return NULL; + + GNUNET_memcpy(&(message->body.id.id), unique_id, sizeof(struct GNUNET_ShortHashCode)); + + return message; +} + +struct GNUNET_MESSENGER_Message* +create_message_miss (const struct GNUNET_PeerIdentity *peer) +{ + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_MISS); + + if (!message) + { + return NULL; + } + + GNUNET_memcpy(&(message->body.miss.peer), peer, sizeof(struct GNUNET_PeerIdentity)); + + return message; +} + +struct GNUNET_MESSENGER_Message* +create_message_merge (const struct GNUNET_HashCode *previous) +{ + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_MERGE); + + if (!message) + return NULL; + + GNUNET_memcpy(&(message->body.merge.previous), previous, sizeof(struct GNUNET_HashCode)); + + return message; +} + +struct GNUNET_MESSENGER_Message* +create_message_request (const struct GNUNET_HashCode *hash) +{ + struct GNUNET_HashCode zero; + memset (&zero, 0, sizeof(zero)); + + if (0 == GNUNET_CRYPTO_hash_cmp (hash, &zero)) + return NULL; + + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_REQUEST); + + if (!message) + return NULL; + + GNUNET_memcpy(&(message->body.request.hash), hash, sizeof(struct GNUNET_HashCode)); + + return message; +} + +struct GNUNET_MESSENGER_Message* +create_message_invite (const struct GNUNET_PeerIdentity *door, const struct GNUNET_HashCode *key) +{ + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_INVITE); + + if (!message) + return NULL; + + GNUNET_memcpy(&(message->body.invite.door), door, sizeof(struct GNUNET_PeerIdentity)); + GNUNET_memcpy(&(message->body.invite.key), key, sizeof(struct GNUNET_HashCode)); + + return message; +} + +struct GNUNET_MESSENGER_Message* +create_message_text (const char *text) +{ + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_TEXT); + + if (!message) + return NULL; + + message->body.text.text = GNUNET_strdup(text); + return message; +} diff --git a/src/messenger/gnunet-service-messenger_message_kind.h b/src/messenger/gnunet-service-messenger_message_kind.h new file mode 100644 index 000000000..dd89d0b2f --- /dev/null +++ b/src/messenger/gnunet-service-messenger_message_kind.h @@ -0,0 +1,160 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_message_kind.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_MESSAGE_KIND_H +#define GNUNET_SERVICE_MESSENGER_MESSAGE_KIND_H + +#include "platform.h" +#include "gnunet_container_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_identity_service.h" + +#include "messenger_api_message.h" +#include "gnunet-service-messenger_service.h" +#include "messenger_api_ego.h" + +/** + * Creates and allocates a new info message containing the hosts public key and a newly generated unique member id. + * (all values are stored as copy) + * + * @param ego EGO of the host + * @param members Map of all assigned member ids + * @return New message + */ +struct GNUNET_MESSENGER_Message* +create_message_info (struct GNUNET_MESSENGER_Ego *ego, struct GNUNET_CONTAINER_MultiShortmap *members); + +/** + * Creates and allocates a new join message containing the clients public key. + * (all values are stored as copy) + * + * @param ego EGO of the client + * @return New message + */ +struct GNUNET_MESSENGER_Message* +create_message_join (struct GNUNET_MESSENGER_Ego *ego); + +/** + * Creates and allocates a new leave message. + * + * @return New message + */ +struct GNUNET_MESSENGER_Message* +create_message_leave (); + +/** + * Creates and allocates a new name message containing the name to change to. + * (all values are stored as copy) + * + * @param name New name + * @return New message + */ +struct GNUNET_MESSENGER_Message* +create_message_name (const char *name); + +/** + * Creates and allocates a new key message containing the public key to change to derived + * from its private counterpart. (all values are stored as copy) + * + * @param key Private key of EGO + * @return New message + */ +struct GNUNET_MESSENGER_Message* +create_message_key (const struct GNUNET_IDENTITY_PrivateKey *key); + +/** + * Creates and allocates a new peer message containing a services peer identity. + * (all values are stored as copy) + * + * @param service Service + * @return New message + */ +struct GNUNET_MESSENGER_Message* +create_message_peer (const struct GNUNET_MESSENGER_Service *service); + +/** + * Creates and allocates a new id message containing the unique member id to change to. + * (all values are stored as copy) + * + * @param unique_id Unique member id + * @return New message + */ +struct GNUNET_MESSENGER_Message* +create_message_id (const struct GNUNET_ShortHashCode *unique_id); + +/** + * Creates and allocates a new miss message containing the missing peer identity. + * (all values are stored as copy) + * + * @param peer Missing peer identity + * @return New message + */ +struct GNUNET_MESSENGER_Message* +create_message_miss (const struct GNUNET_PeerIdentity *peer); + +/** + * Creates and allocates a new merge message containing the hash of a second previous message + * besides the regular previous message mentioned in a messages header. + * (all values are stored as copy) + * + * @param previous Hash of message + * @return New message + */ +struct GNUNET_MESSENGER_Message* +create_message_merge (const struct GNUNET_HashCode *previous); + +/** + * Creates and allocates a new request message containing the hash of a missing message. + * (all values are stored as copy) + * + * @param hash Hash of message + * @return New message + */ +struct GNUNET_MESSENGER_Message* +create_message_request (const struct GNUNET_HashCode *hash); + +/** + * Creates and allocates a new invite message containing the peer identity of an entrance peer + * to a room using a given key as shared secret for communication. + * (all values are stored as copy) + * + * @param door Peer identity + * @param key Shared secret of a room + * @return New message + */ +struct GNUNET_MESSENGER_Message* +create_message_invite (const struct GNUNET_PeerIdentity *door, const struct GNUNET_HashCode *key); + +/** + * Creates and allocates a new text message containing a string representing text. + * (all values are stored as copy) + * + * @param text Text + * @return New message + */ +struct GNUNET_MESSENGER_Message* +create_message_text (const char *text); + +#endif //GNUNET_SERVICE_MESSENGER_MESSAGE_KIND_H diff --git a/src/messenger/gnunet-service-messenger_message_recv.c b/src/messenger/gnunet-service-messenger_message_recv.c new file mode 100644 index 000000000..aa28a36ea --- /dev/null +++ b/src/messenger/gnunet-service-messenger_message_recv.c @@ -0,0 +1,204 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_message_recv.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_message_recv.h" +#include "gnunet-service-messenger_message_handle.h" + +void +recv_message_info (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + int conflict = GNUNET_CONTAINER_multishortmap_contains (room->members, &(message->body.info.unique_id)); + + if (GNUNET_NO == conflict) + { + struct GNUNET_MESSENGER_Message *sync_message = create_message_id (&(message->body.info.unique_id)); + struct GNUNET_HashCode sync_hash; + + send_room_message_ext (room, room->host, sync_message, &sync_hash, tunnel); + destroy_message (sync_message); + + switch_room_member_id (room, get_room_host_id (room), &(message->body.info.unique_id), NULL); + + change_room_host_id (room, &(message->body.info.unique_id)); + } + + if (!tunnel->contact_id) + tunnel->contact_id = GNUNET_new(struct GNUNET_ShortHashCode); + + GNUNET_memcpy(tunnel->contact_id, &(message->header.sender_id), sizeof(struct GNUNET_ShortHashCode)); + + struct GNUNET_ShortHashCode original_id; + + if (GNUNET_YES == conflict) + { + GNUNET_memcpy(&original_id, get_room_host_id (room), sizeof(struct GNUNET_ShortHashCode)); + + change_room_host_id (room, &(message->body.info.unique_id)); + } + + { + struct GNUNET_MESSENGER_Message *join_message = create_message_join (room->host->ego); + struct GNUNET_HashCode join_hash; + + send_tunnel_message (tunnel, room->host, join_message, &join_hash); + destroy_message (join_message); + } + + if ((GNUNET_YES == conflict) && (0 != GNUNET_memcmp(&original_id, get_room_host_id (room)))) + { + struct GNUNET_MESSENGER_Message *sync_message = create_message_id (&original_id); + struct GNUNET_HashCode sync_hash; + + send_tunnel_message (tunnel, room->host, sync_message, &sync_hash); + destroy_message (sync_message); + } +} + +struct GNUNET_MESSENGER_MemberInfoSpread +{ + struct GNUNET_MESSENGER_SrvRoom *room; + struct GNUNET_MESSENGER_SrvTunnel *tunnel; +}; + +static int +iterate_send_member_infos (void *cls, const struct GNUNET_ShortHashCode *key, void *value) +{ + struct GNUNET_MESSENGER_MemberInfo *info = value; + struct GNUNET_MESSENGER_MemberInfoSpread *spread = cls; + + struct GNUNET_MESSENGER_ListMessage *element = info->session_messages.head; + + while (element) + { + const struct GNUNET_MESSENGER_Message *message = get_room_message (spread->room, spread->room->host, + &(element->hash), GNUNET_NO); + + if (message) + forward_tunnel_message (spread->tunnel, message, &(element->hash)); + + element = element->next; + } + + return GNUNET_YES; +} + +void +recv_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + const struct GNUNET_MESSENGER_Message *info_msg = get_room_message (room, room->host, &(message->header.previous), + GNUNET_NO); + + if ((info_msg) && (0 == GNUNET_memcmp(&(info_msg->header.sender_id), get_room_host_id (room))) + && (GNUNET_MESSENGER_KIND_INFO == info_msg->header.kind)) + { + struct GNUNET_MESSENGER_MemberInfoSpread spread; + + spread.room = room; + + if ((tunnel) && (tunnel->contact_id) && (0 == GNUNET_memcmp(tunnel->contact_id, &(message->header.sender_id)))) + spread.tunnel = tunnel; + else + spread.tunnel = find_room_tunnel_to (room, &(message->header.sender_id)); + + if (spread.tunnel) + GNUNET_CONTAINER_multishortmap_iterate (room->member_infos, iterate_send_member_infos, &spread); + } + + handle_message_join (room, tunnel, message, hash); +} + +void +recv_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + handle_message_leave (room, tunnel, message, hash); +} + +void +recv_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + handle_message_name (room, tunnel, message, hash); +} + +void +recv_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + handle_message_key (room, tunnel, message, hash); +} + +void +recv_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_PeerIdentity peer; + GNUNET_PEER_resolve (tunnel->peer, &peer); + + if (0 == GNUNET_memcmp(&peer, &(message->body.peer.peer))) + { + if (!tunnel->peer_message) + tunnel->peer_message = GNUNET_new(struct GNUNET_HashCode); + + GNUNET_memcpy(tunnel->peer_message, hash, sizeof(struct GNUNET_HashCode)); + + if (!tunnel->contact_id) + tunnel->contact_id = GNUNET_new(struct GNUNET_ShortHashCode); + + GNUNET_memcpy(tunnel->contact_id, &(message->header.sender_id), sizeof(struct GNUNET_ShortHashCode)); + } + + handle_message_peer (room, tunnel, message, hash); +} + +void +recv_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + if ((tunnel->contact_id) && (0 == GNUNET_memcmp(tunnel->contact_id, &(message->header.sender_id)))) + GNUNET_memcpy(tunnel->contact_id, &(message->body.id.id), sizeof(struct GNUNET_ShortHashCode)); + + handle_message_id (room, tunnel, message, hash); +} + +void +recv_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + handle_message_miss (room, tunnel, message, hash); +} + +void +recv_message_request (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + const struct GNUNET_MESSENGER_Message *msg = get_room_message (room, room->host, &(message->body.request.hash), + GNUNET_NO); + + if (msg) + forward_tunnel_message (tunnel, msg, &(message->body.request.hash)); +} diff --git a/src/messenger/gnunet-service-messenger_message_recv.h b/src/messenger/gnunet-service-messenger_message_recv.h new file mode 100644 index 000000000..245612cb0 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_message_recv.h @@ -0,0 +1,159 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_message_recv.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_MESSAGE_RECV_H +#define GNUNET_SERVICE_MESSENGER_MESSAGE_RECV_H + +#include "platform.h" +#include "gnunet_crypto_lib.h" + +#include "gnunet-service-messenger_tunnel.h" +#include "messenger_api_message.h" + +/** + * Handles a received info message to change the current member id to the one generated by + * the host connected to. (all current tunnels will be informed about the id change) + * + * @param room Room of the message + * @param tunnel Receiving connection + * @param message INFO-Message + * @param hash Hash of the message + */ +void +recv_message_info (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Handles a received join message to forward all member information to the new member if the message was + * the direct reaction to a previous info message from this peer. + * + * @param room Room of the message + * @param tunnel Receiving connection + * @param message JOIN-Message + * @param hash Hash of the message + */ +void +recv_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Handles a received leave message. + * @see handle_message_leave() + * + * @param room Room of the message + * @param tunnel Receiving connection + * @param message LEAVE-Message + * @param hash Hash of the message + */ +void +recv_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Handles a received name message. + * @see handle_message_name() + * + * @param room Room of the message + * @param tunnel Receiving connection + * @param message NAME-Message + * @param hash Hash of the message + */ +void +recv_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Handles a received key message. + * @see handle_message_key() + * + * @param room Room of the message + * @param tunnel Receiving connection + * @param message KEY-Message + * @param hash Hash of the message + */ +void +recv_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Handles a received peer message to link it to its origin tunnel if the peer identity matches. + * (the peer message and the member id can potentially be linked to the tunnel) + * + * TODO: This handling will only check the one given tunnel! + * + * @param room Room of the message + * @param tunnel Receiving connection + * @param message PEER-Message + * @param hash Hash of the message + */ +void +recv_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Handles a received id message to change the tunnels linked member id if necessary. + * (the tunnels linked member id will be changed if the sender id is matching) + * + * TODO: This handling will only check the one given tunnel! + * + * @param room Room of the message + * @param tunnel Receiving connection + * @param message ID-Message + * @param hash Hash of the message + */ +void +recv_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Handles a received miss message. + * @see handle_message_miss() + * + * @param room Room of the message + * @param tunnel Receiving connection + * @param message MISS-Message + * @param hash Hash of the message + */ +void +recv_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Handles a received request message by checking for the requested message and forwarding it back + * if the message was found. + * (this can also cause this peer to send a new request instead of only forwarding the received one) + * + * TODO: Requests can cause exponentially more requests! + * + * @param room Room of the message + * @param tunnel Receiving connection + * @param message REQUEST-Message + * @param hash Hash of the message + */ +void +recv_message_request (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +#endif //GNUNET_SERVICE_MESSENGER_MESSAGE_RECV_H diff --git a/src/messenger/gnunet-service-messenger_message_send.c b/src/messenger/gnunet-service-messenger_message_send.c new file mode 100644 index 000000000..86cf9b888 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_message_send.c @@ -0,0 +1,118 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_message_send.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_message_send.h" +#include "gnunet-service-messenger_message_handle.h" + +void +send_message_info (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + if (!tunnel->contact_id) + { + tunnel->contact_id = GNUNET_new(struct GNUNET_ShortHashCode); + + GNUNET_memcpy(tunnel->contact_id, &(message->body.info.unique_id), sizeof(struct GNUNET_ShortHashCode)); + } + else + { + disconnect_tunnel (tunnel); + } +} + +void +send_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + handle_message_join (room, tunnel, message, hash); + + if (room->peer_message) + { + const struct GNUNET_MESSENGER_Message *peer_message = get_room_message (room, handle, room->peer_message, + GNUNET_NO); + + if ((peer_message) && (tunnel)) + { + forward_tunnel_message (tunnel, peer_message, room->peer_message); + } + } +} + +void +send_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + handle_message_leave (room, tunnel, message, hash); +} + +void +send_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + handle_message_name (room, tunnel, message, hash); +} + +void +send_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + handle_message_key (room, tunnel, message, hash); +} + +void +send_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + if (!room->peer_message) + { + room->peer_message = GNUNET_new(struct GNUNET_HashCode); + } + + GNUNET_memcpy(room->peer_message, hash, sizeof(struct GNUNET_HashCode)); + + handle_message_peer (room, tunnel, message, hash); +} + +void +send_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + handle_message_id (room, tunnel, message, hash); +} + +void +send_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + handle_message_miss (room, tunnel, message, hash); +} diff --git a/src/messenger/gnunet-service-messenger_message_send.h b/src/messenger/gnunet-service-messenger_message_send.h new file mode 100644 index 000000000..c1096205a --- /dev/null +++ b/src/messenger/gnunet-service-messenger_message_send.h @@ -0,0 +1,155 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_message_send.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_MESSAGE_SEND_H +#define GNUNET_SERVICE_MESSENGER_MESSAGE_SEND_H + +#include "platform.h" +#include "gnunet_crypto_lib.h" + +#include "gnunet-service-messenger_tunnel.h" +#include "messenger_api_message.h" + +/** + * Handles a sent info message to setup a tunnels linked member id. + * (if a tunnel has already got a member id linked to it, the connection will be closed) + * + * @param room Room of the message + * @param handle Sending handle + * @param tunnel Sending connection (may be NULL) + * @param message INFO-Message + * @param hash Hash of the message + */ +void +send_message_info (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +/** + * Handles a sent join message to ensure growth of the decentralized room structure. + * (if the service provides a peer message for this room currently, it will be forwarded) + * + * @param room Room of the message + * @param handle Sending handle + * @param tunnel Sending connection (may be NULL) + * @param message JOIN-Message + * @param hash Hash of the message + */ +void +send_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +/** + * Handles a sent leave message. + * @see handle_message_leave() + * + * @param room Room of the message + * @param handle Sending handle + * @param tunnel Sending connection (may be NULL) + * @param message LEAVE-Message + * @param hash Hash of the message + */ +void +send_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +/** + * Handles a sent name message. + * @see handle_message_name() + * + * @param room Room of the message + * @param handle Sending handle + * @param tunnel Sending connection (may be NULL) + * @param message NAME-Message + * @param hash Hash of the message + */ +void +send_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +/** + * Handles a sent key message. + * @see handle_message_key() + * + * @param room Room of the message + * @param handle Sending handle + * @param tunnel Sending connection (may be NULL) + * @param message KEY-Message + * @param hash Hash of the message + */ +void +send_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +/** + * Handles a sent peer message to update the rooms peer message of this service. + * (a set peer message indicates this service being a part of the decentralized room structure) + * + * @param room Room of the message + * @param handle Sending handle + * @param tunnel Sending connection (may be NULL) + * @param message PEER-Message + * @param hash Hash of the message + */ +void +send_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +/** + * Handles a sent id message. + * @see handle_message_id() + * + * @param room Room of the message + * @param handle Sending handle + * @param tunnel Sending connection (may be NULL) + * @param message ID-Message + * @param hash Hash of the message + */ +void +send_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +/** + * Handles a sent miss message. + * @see handle_message_miss() + * + * @param room Room of the message + * @param handle Sending handle + * @param tunnel Sending connection (may be NULL) + * @param message MISS-Message + * @param hash Hash of the message + */ +void +send_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +#endif //GNUNET_SERVICE_MESSENGER_MESSAGE_SEND_H diff --git a/src/messenger/gnunet-service-messenger_message_store.c b/src/messenger/gnunet-service-messenger_message_store.c new file mode 100644 index 000000000..5933d6390 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_message_store.c @@ -0,0 +1,282 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_message_store.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_message_store.h" +#include "messenger_api_message.h" + +void +init_message_store (struct GNUNET_MESSENGER_MessageStore *store) +{ + store->storage_messages = NULL; + + store->entries = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + store->messages = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); +} + +static int +iterate_destroy_entries (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_MessageEntry *entry = value; + + GNUNET_free(entry); + + return GNUNET_YES; +} + +static int +iterate_destroy_messages (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_Message *message = value; + + destroy_message (message); + + return GNUNET_YES; +} + +void +clear_message_store (struct GNUNET_MESSENGER_MessageStore *store) +{ + if (store->storage_messages) + { + GNUNET_DISK_file_close (store->storage_messages); + + store->storage_messages = NULL; + } + + GNUNET_CONTAINER_multihashmap_iterate (store->entries, iterate_destroy_entries, NULL); + GNUNET_CONTAINER_multihashmap_iterate (store->messages, iterate_destroy_messages, NULL); + + GNUNET_CONTAINER_multihashmap_destroy (store->entries); + GNUNET_CONTAINER_multihashmap_destroy (store->messages); +} + +struct GNUNET_MESSENGER_MessageEntryStorage +{ + struct GNUNET_HashCode hash; + struct GNUNET_MESSENGER_MessageEntry entry; +}; + +void +load_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char *directory) +{ + enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE); + + if (store->storage_messages) + GNUNET_DISK_file_close (store->storage_messages); + + char *filename; + GNUNET_asprintf (&filename, "%s%s", directory, "messages.store"); + + if (GNUNET_YES == GNUNET_DISK_file_test (filename)) + store->storage_messages = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, permission); + else + store->storage_messages = NULL; + + GNUNET_free(filename); + + if (!store->storage_messages) + return; + + GNUNET_asprintf (&filename, "%s%s", directory, "entries.store"); + + if (GNUNET_YES != GNUNET_DISK_file_test (filename)) + goto free_filename; + + struct GNUNET_DISK_FileHandle *entries = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, permission); + + if (!entries) + goto free_filename; + + struct GNUNET_MESSENGER_MessageEntryStorage storage; + struct GNUNET_MESSENGER_MessageEntry *entry; + + do + { + entry = GNUNET_new(struct GNUNET_MESSENGER_MessageEntry); + + if (GNUNET_DISK_file_read (entries, &storage, sizeof(storage)) == sizeof(storage)) + { + GNUNET_memcpy(entry, &(storage.entry), sizeof(*entry)); + + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (store->entries, &(storage.hash), entry, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + GNUNET_free(entry); + } + else + { + GNUNET_free(entry); + + entry = NULL; + } + } + while (entry); + + GNUNET_DISK_file_close (entries); + +free_filename: + GNUNET_free(filename); +} + +struct GNUNET_MESSENGER_MessageSave +{ + struct GNUNET_MESSENGER_MessageStore *store; + + struct GNUNET_DISK_FileHandle *storage_entries; +}; + +static int +iterate_save_messages (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_MessageSave *save = cls; + + if (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains (save->store->entries, key)) + return GNUNET_YES; + + struct GNUNET_MESSENGER_Message *message = value; + struct GNUNET_MESSENGER_MessageEntryStorage storage; + + GNUNET_memcpy(&(storage.hash), key, sizeof(storage.hash)); + + storage.entry.length = get_message_size (message); + storage.entry.offset = GNUNET_DISK_file_seek (save->store->storage_messages, 0, GNUNET_DISK_SEEK_END); + + if ((GNUNET_SYSERR == storage.entry.offset) || + (sizeof(storage) != GNUNET_DISK_file_write (save->storage_entries, &storage, sizeof(storage)))) + return GNUNET_YES; + + char *buffer = GNUNET_malloc(storage.entry.length); + + encode_message (message, storage.entry.length, buffer); + + GNUNET_DISK_file_write (save->store->storage_messages, buffer, storage.entry.length); + + GNUNET_free(buffer); + + return GNUNET_YES; +} + +void +save_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char *directory) +{ + struct GNUNET_MESSENGER_MessageSave save; + + enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE); + + char *filename; + GNUNET_asprintf (&filename, "%s%s", directory, "entries.store"); + + save.store = store; + save.storage_entries = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE, permission); + + GNUNET_free(filename); + + if (!save.storage_entries) + return; + + if (GNUNET_SYSERR == GNUNET_DISK_file_seek (save.storage_entries, 0, GNUNET_DISK_SEEK_END)) + goto close_entries; + + if (store->storage_messages) + GNUNET_DISK_file_close (store->storage_messages); + + GNUNET_asprintf (&filename, "%s%s", directory, "messages.store"); + + store->storage_messages = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READWRITE | GNUNET_DISK_OPEN_CREATE, + permission); + + GNUNET_free(filename); + + if (store->storage_messages) + { + GNUNET_CONTAINER_multihashmap_iterate (store->messages, iterate_save_messages, &save); + + GNUNET_DISK_file_sync (store->storage_messages); + GNUNET_DISK_file_sync (save.storage_entries); + } + +close_entries: + GNUNET_DISK_file_close (save.storage_entries); +} + +int +contains_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash) +{ + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (store->messages, hash)) + return GNUNET_YES; + + return GNUNET_CONTAINER_multihashmap_contains (store->entries, hash); +} + +const struct GNUNET_MESSENGER_Message* +get_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_Message *message = GNUNET_CONTAINER_multihashmap_get (store->messages, hash); + + if (message) + return message; + + if (!store->storage_messages) + return NULL; + + const struct GNUNET_MESSENGER_MessageEntry *entry = GNUNET_CONTAINER_multihashmap_get (store->entries, hash); + + if (!entry) + return NULL; + + if (entry->offset != GNUNET_DISK_file_seek (store->storage_messages, entry->offset, GNUNET_DISK_SEEK_SET)) + return message; + + char *buffer = GNUNET_malloc(entry->length); + + if (GNUNET_DISK_file_read (store->storage_messages, buffer, entry->length) != entry->length) + goto free_buffer; + + + message = create_message (GNUNET_MESSENGER_KIND_UNKNOWN); + + if ((GNUNET_YES != decode_message (message, entry->length, buffer)) || (GNUNET_OK + != GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))) + { + destroy_message (message); + + message = NULL; + + GNUNET_CONTAINER_multihashmap_remove (store->entries, hash, entry); + } + +free_buffer: + GNUNET_free(buffer); + + return message; +} + +int +put_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash, + struct GNUNET_MESSENGER_Message *message) +{ + return GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); +} diff --git a/src/messenger/gnunet-service-messenger_message_store.h b/src/messenger/gnunet-service-messenger_message_store.h new file mode 100644 index 000000000..e58459b21 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_message_store.h @@ -0,0 +1,120 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_message_store.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_MESSAGE_STORE_H +#define GNUNET_SERVICE_MESSENGER_MESSAGE_STORE_H + +#include "platform.h" +#include "gnunet_container_lib.h" +#include "gnunet_disk_lib.h" + +struct GNUNET_MESSENGER_MessageEntry +{ + off_t offset; + uint16_t length; +}; + +struct GNUNET_MESSENGER_MessageStore +{ + struct GNUNET_DISK_FileHandle *storage_messages; + + struct GNUNET_CONTAINER_MultiHashMap *entries; + struct GNUNET_CONTAINER_MultiHashMap *messages; +}; + +/** + * Initializes a message store as fully empty. + * + * @param store Message store + */ +void +init_message_store (struct GNUNET_MESSENGER_MessageStore *store); + +/** + * Clears a message store, wipes its content and deallocates its memory. + * + * @param store Message store + */ +void +clear_message_store (struct GNUNET_MESSENGER_MessageStore *store); + +/** + * Loads messages from a directory into a message store. + * + * @param store Message store + * @param directory Path to a directory + */ +void +load_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char *directory); + +/** + * Saves messages from a message store into a directory. + * + * @param store Message store + * @param directory Path to a directory + */ +void +save_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char *directory); + +/** + * Checks if a message matching a given hash is stored in a message store. The function returns + * GNUNET_YES if a match is found, GNUNET_NO otherwise. + * + * The message has not to be loaded from disk into memory for this check! + * + * @param store Message store + * @param hash Hash of message + * @return GNUNET_YES on match, otherwise GNUNET_NO + */ +int +contains_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash); + +/** + * Returns the message from a message store matching a given hash. If no matching message is found, + * NULL gets returned. + * + * This function requires the message to be loaded into memory! + * @see contains_store_message() + * + * @param store Message store + * @param hash Hash of message + * @return Message or NULL + */ +const struct GNUNET_MESSENGER_Message* +get_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash); + +/** + * Stores a message into the message store. The result indicates if the operation was successful. + * + * @param store Message store + * @param hash Hash of message + * @param message Message + * @return GNUNET_OK on success, otherwise GNUNET_NO + */ +int +put_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash, + struct GNUNET_MESSENGER_Message *message); + +#endif //GNUNET_SERVICE_MESSENGER_MESSAGE_STORE_H diff --git a/src/messenger/gnunet-service-messenger_room.c b/src/messenger/gnunet-service-messenger_room.c new file mode 100644 index 000000000..7383e1d20 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_room.c @@ -0,0 +1,1051 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_room.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_room.h" + +#include "gnunet-service-messenger_message_kind.h" + +#include "gnunet-service-messenger_service.h" +#include "gnunet-service-messenger_util.h" + +static void +idle_request_room_messages (void *cls); + +struct GNUNET_MESSENGER_SrvRoom* +create_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key) +{ + GNUNET_assert((handle) && (key)); + + struct GNUNET_MESSENGER_SrvRoom *room = GNUNET_new(struct GNUNET_MESSENGER_SrvRoom); + + room->service = handle->service; + room->host = handle; + room->port = NULL; + + GNUNET_memcpy(&(room->key), key, sizeof(struct GNUNET_HashCode)); + + room->tunnels = GNUNET_CONTAINER_multipeermap_create (8, GNUNET_NO); + room->members = GNUNET_CONTAINER_multishortmap_create (8, GNUNET_NO); + room->member_infos = GNUNET_CONTAINER_multishortmap_create (8, GNUNET_NO); + + init_message_store (&(room->store)); + room->requested = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + + init_list_tunnels (&(room->basement)); + init_list_messages (&(room->last_messages)); + + room->peer_message = NULL; + + init_list_messages (&(room->handling)); + room->idle = NULL; + + room->strict_access = GNUNET_NO; + + if (room->service->dir) + load_service_room_and_messages (room->service, room); + + room->idle = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, idle_request_room_messages, room); + + return room; +} + +static int +iterate_destroy_tunnels (void *cls, const struct GNUNET_PeerIdentity *key, void *value) +{ + struct GNUNET_MESSENGER_SrvTunnel *tunnel = value; + destroy_tunnel (tunnel); + return GNUNET_YES; +} + +static int +iterate_clear_members (void *cls, const struct GNUNET_ShortHashCode *key, void *value) +{ + struct GNUNET_MESSENGER_SrvContact *contact = value; + + if (GNUNET_YES == decrease_contact_rc (contact)) + { + struct GNUNET_MESSENGER_SrvRoom *room = cls; + + const struct GNUNET_HashCode *id = get_contact_id_from_key (contact); + + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (room->service->contacts, id, contact)) + destroy_contact (contact); + } + + return GNUNET_YES; +} + +static int +iterate_destroy_member_infos (void *cls, const struct GNUNET_ShortHashCode *key, void *value) +{ + struct GNUNET_MESSENGER_MemberInfo *info = value; + + clear_list_messages (&(info->session_messages)); + + GNUNET_free(info); + return GNUNET_YES; +} + +void +destroy_room (struct GNUNET_MESSENGER_SrvRoom *room) +{ + GNUNET_assert(room); + + if (room->idle) + { + GNUNET_SCHEDULER_cancel (room->idle); + + room->idle = NULL; + } + + if (room->port) + GNUNET_CADET_close_port (room->port); + + merge_room_last_messages (room, room->host); + + GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, iterate_destroy_tunnels, + NULL); + + handle_room_messages (room); + + if (room->service->dir) + save_service_room_and_messages (room->service, room); + + GNUNET_CONTAINER_multishortmap_iterate (room->members, iterate_clear_members, room); + GNUNET_CONTAINER_multishortmap_iterate (room->member_infos, iterate_destroy_member_infos, NULL); + + clear_message_store (&(room->store)); + + GNUNET_CONTAINER_multihashmap_destroy (room->requested); + + GNUNET_CONTAINER_multipeermap_destroy (room->tunnels); + GNUNET_CONTAINER_multishortmap_destroy (room->members); + GNUNET_CONTAINER_multishortmap_destroy (room->member_infos); + + clear_list_tunnels (&(room->basement)); + clear_list_messages (&(room->last_messages)); + + if (room->peer_message) + GNUNET_free(room->peer_message); + + GNUNET_free(room); +} + +struct GNUNET_MESSENGER_SrvContact* +get_room_contact (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *id) +{ + GNUNET_assert((room) && (room->members)); + + return GNUNET_CONTAINER_multishortmap_get (room->members, id); +} + +void +add_room_contact (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *id, + const struct GNUNET_IDENTITY_PublicKey *pubkey) +{ + struct GNUNET_MESSENGER_SrvContact *contact = get_service_contact_by_pubkey (room->service, pubkey); + + if (GNUNET_OK == GNUNET_CONTAINER_multishortmap_put (room->members, id, contact, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + increase_contact_rc (contact); +} + +struct GNUNET_MESSENGER_MemberInfo* +get_room_member_info (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *id) +{ + GNUNET_assert((room) && (room->member_infos)); + + return GNUNET_CONTAINER_multishortmap_get (room->member_infos, id); +} + +struct GNUNET_ShortHashCode* +generate_room_member_id (const struct GNUNET_MESSENGER_SrvRoom *room) +{ + struct GNUNET_ShortHashCode *unique_id = GNUNET_new(struct GNUNET_ShortHashCode); + + GNUNET_assert(room); + + if (GNUNET_YES == generate_free_member_id (unique_id, room->members)) + return unique_id; + else + { + GNUNET_free(unique_id); + return NULL; + } +} + +const struct GNUNET_ShortHashCode* +get_room_host_id (const struct GNUNET_MESSENGER_SrvRoom *room) +{ + GNUNET_assert(room); + + return get_handle_member_id (room->host, &(room->key)); +} + +void +change_room_host_id (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *unique_id) +{ + GNUNET_assert(room); + + change_handle_member_id (room->host, &(room->key), unique_id); +} + +static int +send_room_info (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_SrvTunnel *tunnel) +{ + if (!handle) + return GNUNET_NO; + + merge_room_last_messages (room, handle); + + if (!is_tunnel_connected (tunnel)) + return GNUNET_NO; + + struct GNUNET_MESSENGER_Message *message = create_message_info (get_handle_ego(handle), room->members); + + if (!message) + return GNUNET_NO; + + if ((tunnel->peer_message) && (tunnel->contact_id)) + { + GNUNET_memcpy(&(message->body.info.unique_id), &(tunnel->contact_id), sizeof(struct GNUNET_ShortHashCode)); + GNUNET_free(tunnel->contact_id); + + tunnel->contact_id = NULL; + } + + struct GNUNET_HashCode hash; + + send_tunnel_message (tunnel, handle, message, &hash); + destroy_message (message); + + if (tunnel->contact_id) + { + GNUNET_free(tunnel->contact_id); + + tunnel->contact_id = NULL; + } + + return GNUNET_YES; +} + +static void* +callback_room_connect (void *cls, struct GNUNET_CADET_Channel *channel, const struct GNUNET_PeerIdentity *source) +{ + struct GNUNET_MESSENGER_SrvRoom *room = cls; + + struct GNUNET_MESSENGER_SrvTunnel *tunnel = GNUNET_CONTAINER_multipeermap_get (room->tunnels, source); + + if (tunnel) + { + if (GNUNET_YES == bind_tunnel (tunnel, channel)) + { + if (GNUNET_YES == send_room_info (room, room->host, tunnel)) + return tunnel; + else + { + disconnect_tunnel (tunnel); + return NULL; + } + } + else + { + delayed_disconnect_channel (channel); + return NULL; + } + } + else + { + tunnel = create_tunnel (room, source); + + if ((GNUNET_YES == bind_tunnel (tunnel, channel)) && (GNUNET_OK + == GNUNET_CONTAINER_multipeermap_put (room->tunnels, source, tunnel, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))) + { + if (GNUNET_YES == send_room_info (room, room->host, tunnel)) + return tunnel; + else + { + GNUNET_CONTAINER_multipeermap_remove (room->tunnels, source, tunnel); + + disconnect_tunnel (tunnel); + destroy_tunnel (tunnel); + return NULL; + } + } + else + { + tunnel->channel = NULL; + destroy_tunnel (tunnel); + + delayed_disconnect_channel (channel); + return NULL; + } + } +} + +static int +join_room (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_ShortHashCode *member_id) +{ + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Joining room: %s (%s)\n", GNUNET_h2s(get_room_key(room)), GNUNET_sh2s(member_id)); + + struct GNUNET_MESSENGER_Message *message = create_message_join (get_handle_ego(handle)); + + if (!message) + { + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Your join message could not be created!\n"); + + return GNUNET_NO; + } + + struct GNUNET_HashCode hash; + + send_room_message (room, handle, message, &hash); + destroy_message (message); + + struct GNUNET_MESSENGER_MemberInfo *info = GNUNET_new(struct GNUNET_MESSENGER_MemberInfo); + + info->access = GNUNET_MESSENGER_MEMBER_ALLOWED; + init_list_messages (&(info->session_messages)); + + if (GNUNET_YES == GNUNET_CONTAINER_multishortmap_put (room->member_infos, member_id, info, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + { + change_handle_member_id (handle, &(room->key), member_id); + + add_to_list_messages (&(info->session_messages), &hash); + return GNUNET_YES; + } + else + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Your member information could not be registered!\n"); + + GNUNET_free(info); + return GNUNET_NO; + } +} + +static int +join_room_locally (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle) +{ + const struct GNUNET_ShortHashCode *member_id = get_handle_member_id (handle, &(room->key)); + + struct GNUNET_MESSENGER_MemberInfo *info = GNUNET_CONTAINER_multishortmap_get (room->member_infos, member_id); + + if ((!info) && (GNUNET_NO == join_room (room, handle, member_id))) + return GNUNET_NO; + + return GNUNET_YES; +} + +extern int +check_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header); +extern void +handle_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header); + +extern void +callback_tunnel_disconnect (void *cls, const struct GNUNET_CADET_Channel *channel); + +int +open_room (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle) +{ + if (room->port) + return join_room_locally (room, handle); + + struct GNUNET_CADET_Handle *cadet = get_room_cadet (room); + struct GNUNET_HashCode *key = get_room_key (room); + + struct GNUNET_MQ_MessageHandler handlers[] = { GNUNET_MQ_hd_var_size(tunnel_message, GNUNET_MESSAGE_TYPE_CADET_CLI, + struct GNUNET_MessageHeader, NULL), + GNUNET_MQ_handler_end() }; + + room->port = GNUNET_CADET_open_port (cadet, key, callback_room_connect, room, NULL, + callback_tunnel_disconnect, handlers); + + const struct GNUNET_ShortHashCode *member_id = get_handle_member_id (handle, &(room->key)); + + struct GNUNET_MESSENGER_MemberInfo *info = GNUNET_CONTAINER_multishortmap_get (room->member_infos, member_id); + + if ((!info) && (GNUNET_NO == join_room (room, handle, member_id)) && (room->port)) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "You could not join the room, therefore it keeps closed!\n"); + + GNUNET_CADET_close_port (room->port); + room->port = NULL; + + return GNUNET_NO; + } + + struct GNUNET_MESSENGER_Message *message = create_message_peer (room->service); + + if (message) + { + struct GNUNET_HashCode hash; + + send_room_message (room, handle, message, &hash); + destroy_message (message); + } + + return (room->port ? GNUNET_YES : GNUNET_NO); +} + +int +entry_room_at (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_PeerIdentity *door) +{ + if (room->peer_message) + { + const struct GNUNET_MESSENGER_Message *msg = get_room_message (room, handle, room->peer_message, GNUNET_NO); + + if (0 == GNUNET_memcmp(&(msg->body.peer.peer), door)) + return join_room_locally (room, handle); + } + + struct GNUNET_MESSENGER_SrvTunnel *tunnel = GNUNET_CONTAINER_multipeermap_get (room->tunnels, door); + + if (tunnel) + { + switch (connect_tunnel (tunnel)) + { + case GNUNET_YES: + return GNUNET_YES; + case GNUNET_NO: + return join_room_locally (room, handle); + default: + return GNUNET_NO; + } + } + + tunnel = create_tunnel (room, door); + + if ((GNUNET_YES == connect_tunnel (tunnel)) && + (GNUNET_OK == GNUNET_CONTAINER_multipeermap_put (room->tunnels, door, tunnel, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))) + return GNUNET_YES; + else + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "You could not connect to that door!\n"); + + destroy_tunnel (tunnel); + return GNUNET_NO; + } +} + +struct GNUNET_MESSENGER_SrvTunnelFinder +{ + const struct GNUNET_ShortHashCode *needle; + struct GNUNET_MESSENGER_SrvTunnel *tunnel; +}; + +static int +iterate_find_tunnel (void *cls, const struct GNUNET_PeerIdentity *peer, void *value) +{ + struct GNUNET_MESSENGER_SrvTunnel *tunnel = value; + struct GNUNET_MESSENGER_SrvTunnelFinder *finder = cls; + + if ((tunnel->contact_id) && (0 == GNUNET_memcmp(tunnel->contact_id, finder->needle))) + { + finder->tunnel = tunnel; + return GNUNET_NO; + } + + return GNUNET_YES; +} + +struct GNUNET_MESSENGER_SrvTunnel* +find_room_tunnel_to (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *contact_id) +{ + struct GNUNET_MESSENGER_SrvTunnelFinder finder; + + finder.needle = contact_id; + finder.tunnel = NULL; + + GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, iterate_find_tunnel, &finder); + + return finder.tunnel; +} + +struct GNUNET_MQ_Envelope* +pack_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash, int mode) +{ + message->header.timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); + + const struct GNUNET_ShortHashCode *id = get_handle_member_id (handle, &(room->key)); + + GNUNET_assert(id); + + GNUNET_memcpy(&(message->header.sender_id), id, sizeof(struct GNUNET_ShortHashCode)); + + if (room->last_messages.head) + GNUNET_memcpy(&(message->header.previous), &(room->last_messages.head->hash), sizeof(struct GNUNET_HashCode)); + else + memset (&(message->header.previous), 0, sizeof(struct GNUNET_HashCode)); + + return pack_message (message, hash, get_handle_ego (handle), mode); +} + +struct GNUNET_MESSENGER_ClosureSendRoom +{ + struct GNUNET_MESSENGER_SrvRoom *room; + struct GNUNET_MESSENGER_SrvHandle *handle; + struct GNUNET_MESSENGER_SrvTunnel *exclude; + struct GNUNET_MESSENGER_Message *message; + struct GNUNET_HashCode *hash; + int packed; +}; + +static int +iterate_send_room_message (void *cls, const struct GNUNET_PeerIdentity *key, void *value) +{ + struct GNUNET_MESSENGER_SrvTunnel *tunnel = value; + + if ((!is_tunnel_connected (tunnel)) || (!tunnel->contact_id)) + return GNUNET_YES; + + struct GNUNET_MESSENGER_ClosureSendRoom *closure = cls; + + if (tunnel == closure->exclude) + return GNUNET_YES; + + struct GNUNET_MQ_Envelope *env = NULL; + + if (closure->packed == GNUNET_NO) + { + env = pack_room_message (closure->room, closure->handle, closure->message, closure->hash, + GNUNET_MESSENGER_PACK_MODE_ENVELOPE); + + if (env) + { + closure->message = copy_message (closure->message); + closure->packed = GNUNET_YES; + } + } + else + { + env = pack_message (closure->message, NULL, NULL, + GNUNET_MESSENGER_PACK_MODE_ENVELOPE); + } + + if (env) + send_tunnel_envelope (tunnel, closure->handle, env, closure->message, closure->hash); + + return GNUNET_YES; +} + +void +callback_room_sent (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, void *cls, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +void +send_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_ClosureSendRoom closure; + + closure.room = room; + closure.handle = handle; + closure.exclude = NULL; + closure.message = message; + closure.hash = hash; + closure.packed = GNUNET_NO; + + GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, iterate_send_room_message, &closure); + + if ((GNUNET_NO == closure.packed) && (closure.message == message)) + { + pack_room_message (room, handle, message, hash, + GNUNET_MESSENGER_PACK_MODE_UNKNOWN); + + callback_room_sent (room, handle, NULL, copy_message (message), hash); + } +} + +void +send_room_message_ext (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash, + struct GNUNET_MESSENGER_SrvTunnel *tunnel) +{ + struct GNUNET_MESSENGER_ClosureSendRoom closure; + + closure.room = room; + closure.handle = handle; + closure.exclude = tunnel; + closure.message = message; + closure.hash = hash; + closure.packed = GNUNET_NO; + + GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, iterate_send_room_message, &closure); + + if ((GNUNET_NO == closure.packed) && (closure.message == message)) + { + pack_room_message (room, handle, message, hash, + GNUNET_MESSENGER_PACK_MODE_UNKNOWN); + + callback_room_sent (room, handle, NULL, copy_message (message), hash); + } +} + +void +forward_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_ClosureSendRoom closure; + struct GNUNET_HashCode message_hash; + + GNUNET_memcpy(&message_hash, hash, sizeof(struct GNUNET_HashCode)); + + closure.room = room; + closure.handle = NULL; + closure.exclude = tunnel; + closure.message = copy_message (message); + closure.hash = &message_hash; + closure.packed = GNUNET_YES; + + GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, iterate_send_room_message, &closure); +} + +void +merge_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle) +{ + if (!handle) + return; + + if (!room->last_messages.head) + return; + + while (room->last_messages.head != room->last_messages.tail) + { + struct GNUNET_MESSENGER_ListMessage *element = room->last_messages.tail; + + struct GNUNET_MESSENGER_Message *message = create_message_merge (&(element->hash)); + + if (message) + { + struct GNUNET_HashCode hash; + + send_room_message (room, handle, message, &hash); + destroy_message (message); + } + + if (element->prev) + GNUNET_CONTAINER_DLL_remove(room->last_messages.head, room->last_messages.tail, element); + } +} + +struct GNUNET_CADET_Handle* +get_room_cadet (struct GNUNET_MESSENGER_SrvRoom *room) +{ + return room->service->cadet; +} + +struct GNUNET_HashCode* +get_room_key (struct GNUNET_MESSENGER_SrvRoom *room) +{ + return &(room->key); +} + +const struct GNUNET_MESSENGER_SrvTunnel* +get_room_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_PeerIdentity *peer) +{ + return GNUNET_CONTAINER_multipeermap_get (room->tunnels, peer); +} + +const struct GNUNET_MESSENGER_Message* +get_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_HashCode *hash, int request) +{ + const struct GNUNET_MESSENGER_Message *message = get_store_message (&(room->store), hash); + + if ((message) || (!handle) || (GNUNET_YES != request) + || (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains (room->requested, hash))) + return message; + + struct GNUNET_MESSENGER_Message *request_msg = create_message_request (hash); + + if (request_msg) + { + if (GNUNET_CONTAINER_multihashmap_put (room->requested, hash, NULL, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST) == GNUNET_OK) + { + struct GNUNET_HashCode request_hash; + + send_room_message (room, handle, request_msg, &request_hash); + } + + destroy_message (request_msg); + } + + return message; +} + +void +callback_room_disconnect (struct GNUNET_MESSENGER_SrvRoom *room, void *cls) +{ + struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls; + + if (!room->host) + return; + + struct GNUNET_PeerIdentity identity; + + GNUNET_PEER_resolve (tunnel->peer, &identity); + + if (GNUNET_YES == contains_list_tunnels (&(room->basement), &identity)) + { + struct GNUNET_MESSENGER_Message *message = create_message_miss (&identity); + + if (message) + { + struct GNUNET_HashCode hash; + + send_room_message (room, room->host, message, &hash); + destroy_message (message); + } + } +} + +int +callback_verify_room_message (struct GNUNET_MESSENGER_SrvRoom *room, void *cls, + struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash) +{ + if (GNUNET_MESSENGER_KIND_UNKNOWN == message->header.kind) + return GNUNET_SYSERR; + + struct GNUNET_MESSENGER_SrvContact *contact = GNUNET_CONTAINER_multishortmap_get (room->members, + &(message->header.sender_id)); + + if (!contact) + { + if (GNUNET_MESSENGER_KIND_INFO == message->header.kind) + contact = get_service_contact_by_pubkey (room->service, &(message->body.info.host_key)); + else if (GNUNET_MESSENGER_KIND_JOIN == message->header.kind) + contact = get_service_contact_by_pubkey (room->service, &(message->body.join.key)); + } + + if ((!contact) || (GNUNET_SYSERR == verify_message (message, hash, get_contact_key (contact)))) + return GNUNET_SYSERR; + + if (GNUNET_YES == room->strict_access) + { + struct GNUNET_MESSENGER_MemberInfo *info = GNUNET_CONTAINER_multishortmap_get (room->member_infos, + &(message->header.sender_id)); + + if ((info) && (GNUNET_MESSENGER_MEMBER_BLOCKED == info->access)) + return GNUNET_SYSERR; + } + + if (GNUNET_YES == contains_store_message (&(room->store), hash)) + return GNUNET_NO; + + return GNUNET_YES; +} + +static void +search_room_for_message (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_HashCode *hash) +{ + const struct GNUNET_MESSENGER_Message *message = get_room_message (room, room->host, hash, GNUNET_YES); + + if (!message) + return; + + if (GNUNET_MESSENGER_KIND_MERGE == message->header.kind) + search_room_for_message (room, &(message->body.merge.previous)); + + search_room_for_message (room, &(message->header.previous)); +} + +static void +idle_request_room_messages (void *cls) +{ + struct GNUNET_MESSENGER_SrvRoom *room = cls; + + room->idle = NULL; + + struct GNUNET_MESSENGER_ListMessage *element = room->last_messages.head; + + while (element) + { + search_room_for_message (room, &(element->hash)); + + element = element->next; + } + + merge_room_last_messages (room, room->host); + + room->idle = GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_get_second_ (), + GNUNET_SCHEDULER_PRIORITY_IDLE, idle_request_room_messages, + cls); +} + +void +update_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_ListMessage *element = room->last_messages.head; + struct GNUNET_MESSENGER_ListMessage *merging = NULL; + + if (GNUNET_MESSENGER_KIND_MERGE == message->header.kind) + { + merging = room->last_messages.head; + + while (merging) + { + if (0 == GNUNET_CRYPTO_hash_cmp (&(merging->hash), &(message->body.merge.previous))) + break; + + merging = merging->next; + } + + if (merging) + element = merging->next; + } + + while (element) + { + if (0 == GNUNET_CRYPTO_hash_cmp (&(element->hash), &(message->header.previous))) + break; + + element = element->next; + } + + if ((merging) && (!element)) + { + element = merging; + merging = NULL; + } + + if (element) + { + GNUNET_memcpy(&(element->hash), hash, sizeof(struct GNUNET_HashCode)); + + if (merging) + GNUNET_CONTAINER_DLL_remove(room->last_messages.head, room->last_messages.tail, merging); + } + else + add_to_list_messages (&(room->last_messages), hash); + + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (room->requested, hash)) + GNUNET_CONTAINER_multihashmap_remove_all (room->requested, hash); +} + +void +switch_room_member_id (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *old_id, + const struct GNUNET_ShortHashCode *new_id, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_SrvContact *contact = GNUNET_CONTAINER_multishortmap_get (room->members, old_id); + + if ((contact) && (GNUNET_YES == GNUNET_CONTAINER_multishortmap_remove (room->members, old_id, contact))) + GNUNET_CONTAINER_multishortmap_put (room->members, new_id, contact, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + + struct GNUNET_MESSENGER_MemberInfo *info = GNUNET_CONTAINER_multishortmap_get (room->member_infos, old_id); + + if ((!info) || (GNUNET_YES != GNUNET_CONTAINER_multishortmap_remove (room->member_infos, old_id, contact)) + || (GNUNET_YES != GNUNET_CONTAINER_multishortmap_put (room->member_infos, new_id, contact, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))) + return; + + if (hash) + add_to_list_messages (&(info->session_messages), hash); +} + +void +rebuild_room_basement_structure (struct GNUNET_MESSENGER_SrvRoom *room) +{ + struct GNUNET_PeerIdentity peer; + size_t src; + + if ((GNUNET_OK != get_service_peer_identity (room->service, &peer)) || (!find_list_tunnels (&(room->basement), &peer, + &src))) + return; + + size_t count = count_of_tunnels (&(room->basement)); + + struct GNUNET_MESSENGER_ListTunnel *element = room->basement.head; + struct GNUNET_MESSENGER_SrvTunnel *tunnel; + + size_t dst = 0; + + while (element) + { + GNUNET_PEER_resolve (element->peer, &peer); + + tunnel = GNUNET_CONTAINER_multipeermap_get (room->tunnels, &peer); + + if (!tunnel) + { + element = remove_from_list_tunnels (&(room->basement), element); + continue; + } + + if (GNUNET_YES == required_connection_between (count, src, dst)) + { + if (GNUNET_SYSERR == connect_tunnel (tunnel)) + { + element = remove_from_list_tunnels (&(room->basement), element); + continue; + } + } + else + disconnect_tunnel (tunnel); + + element = element->next; + dst++; + } +} + +void +handle_room_messages (struct GNUNET_MESSENGER_SrvRoom *room) +{ + while (room->handling.head) + { + struct GNUNET_MESSENGER_ListMessage *element = room->handling.head; + + const struct GNUNET_MESSENGER_Message *msg = get_room_message (room, room->host, &(element->hash), GNUNET_NO); + + if (msg) + handle_service_message (room->service, room, msg, &(element->hash)); + + GNUNET_CONTAINER_DLL_remove(room->handling.head, room->handling.tail, element); + GNUNET_free(element); + } +} + +#include "gnunet-service-messenger_message_recv.h" + +void +callback_room_recv (struct GNUNET_MESSENGER_SrvRoom *room, void *cls, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls; + + if (GNUNET_OK != put_store_message (&(room->store), hash, message)) + return; + + update_room_last_messages (room, message, hash); + + if (GNUNET_MESSENGER_KIND_INFO != message->header.kind) + forward_room_message (room, tunnel, message, hash); + + const int start_handle = room->handling.head ? GNUNET_NO : GNUNET_YES; + + add_to_list_messages (&(room->handling), hash); + + switch (message->header.kind) + { + case GNUNET_MESSENGER_KIND_INFO: + recv_message_info (room, tunnel, message, hash); + break; + case GNUNET_MESSENGER_KIND_JOIN: + recv_message_join (room, tunnel, message, hash); + break; + case GNUNET_MESSENGER_KIND_LEAVE: + recv_message_leave (room, tunnel, message, hash); + break; + case GNUNET_MESSENGER_KIND_NAME: + recv_message_name (room, tunnel, message, hash); + break; + case GNUNET_MESSENGER_KIND_KEY: + recv_message_key (room, tunnel, message, hash); + break; + case GNUNET_MESSENGER_KIND_PEER: + recv_message_peer (room, tunnel, message, hash); + break; + case GNUNET_MESSENGER_KIND_ID: + recv_message_id (room, tunnel, message, hash); + break; + case GNUNET_MESSENGER_KIND_MISS: + recv_message_miss (room, tunnel, message, hash); + break; + case GNUNET_MESSENGER_KIND_REQUEST: + recv_message_request (room, tunnel, message, hash); + break; + default: + break; + } + + if (GNUNET_YES == start_handle) + handle_room_messages (room); +} + +#include "gnunet-service-messenger_message_send.h" + +void +callback_room_sent (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, void *cls, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + const struct GNUNET_MESSENGER_Message *old_message = get_room_message (room, handle, hash, GNUNET_NO); + + if ((old_message) || (GNUNET_OK != put_store_message (&(room->store), hash, message))) + { + if (old_message != message) + GNUNET_free(message); + } + else + { + struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls; // may be NULL + + update_room_last_messages (room, message, hash); + + const int start_handle = room->handling.head ? GNUNET_NO : GNUNET_YES; + + add_to_list_messages (&(room->handling), hash); + + switch (message->header.kind) + { + case GNUNET_MESSENGER_KIND_INFO: + send_message_info (room, handle, tunnel, message, hash); + break; + case GNUNET_MESSENGER_KIND_JOIN: + send_message_join (room, handle, tunnel, message, hash); + break; + case GNUNET_MESSENGER_KIND_LEAVE: + send_message_leave (room, handle, tunnel, message, hash); + break; + case GNUNET_MESSENGER_KIND_NAME: + send_message_name (room, handle, tunnel, message, hash); + break; + case GNUNET_MESSENGER_KIND_KEY: + send_message_key (room, handle, tunnel, message, hash); + break; + case GNUNET_MESSENGER_KIND_PEER: + send_message_peer (room, handle, tunnel, message, hash); + break; + case GNUNET_MESSENGER_KIND_ID: + send_message_id (room, handle, tunnel, message, hash); + break; + case GNUNET_MESSENGER_KIND_MISS: + send_message_miss (room, handle, tunnel, message, hash); + break; + default: + break; + } + + if (GNUNET_YES == start_handle) + handle_room_messages (room); + } +} diff --git a/src/messenger/gnunet-service-messenger_room.h b/src/messenger/gnunet-service-messenger_room.h new file mode 100644 index 000000000..36c9e8cf5 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_room.h @@ -0,0 +1,378 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_room.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_ROOM_H +#define GNUNET_SERVICE_MESSENGER_ROOM_H + +#include "platform.h" +#include "gnunet_cadet_service.h" +#include "gnunet_container_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_identity_service.h" +#include "gnunet_mq_lib.h" + +#include "gnunet-service-messenger_contact.h" + +#include "gnunet_messenger_service.h" +#include "gnunet-service-messenger_basement.h" +#include "gnunet-service-messenger_handle.h" +#include "gnunet-service-messenger_tunnel.h" + +#include "gnunet-service-messenger_list_messages.h" +#include "messenger_api_list_tunnels.h" + +#include "gnunet-service-messenger_message_store.h" +#include "messenger_api_ego.h" + +enum GNUNET_MESSENGER_MemberAccess +{ + GNUNET_MESSENGER_MEMBER_ALLOWED = 1, + GNUNET_MESSENGER_MEMBER_BLOCKED = 1, + + GNUNET_MESSENGER_MEMBER_UNKNOWN = 0 +}; + +struct GNUNET_MESSENGER_MemberInfo +{ + enum GNUNET_MESSENGER_MemberAccess access; + + struct GNUNET_MESSENGER_ListMessages session_messages; +}; + +struct GNUNET_MESSENGER_SrvRoom +{ + struct GNUNET_MESSENGER_Service *service; + struct GNUNET_MESSENGER_SrvHandle *host; + struct GNUNET_CADET_Port *port; + + struct GNUNET_HashCode key; + + struct GNUNET_CONTAINER_MultiPeerMap *tunnels; + struct GNUNET_CONTAINER_MultiShortmap *members; + struct GNUNET_CONTAINER_MultiShortmap *member_infos; + + struct GNUNET_MESSENGER_MessageStore store; + struct GNUNET_CONTAINER_MultiHashMap *requested; + + struct GNUNET_MESSENGER_ListTunnels basement; + struct GNUNET_MESSENGER_ListMessages last_messages; + + struct GNUNET_HashCode *peer_message; + + struct GNUNET_MESSENGER_ListMessages handling; + struct GNUNET_SCHEDULER_Task *idle; + + int strict_access; +}; + +/** + * Creates and allocates a new room for a handle with a given key. + * + * @param handle Handle + * @param key Key of room + * @return New room + */ +struct GNUNET_MESSENGER_SrvRoom* +create_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key); + +/** + * Destroys a room and frees its memory fully. + * + * @param room Room + */ +void +destroy_room (struct GNUNET_MESSENGER_SrvRoom *room); + +/** + * Returns the contact of a member in a room identified by a given id. If the room + * does not contain a member with the given id, NULL gets returned. + * + * @param room Room + * @param id Member id + * @return Contact or NULL + */ +struct GNUNET_MESSENGER_SrvContact* +get_room_contact (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *id); + +/** + * Adds a contact from the service to a room under a specific id with a given public key. + * + * @param room Room + * @param id Member id + * @param pubkey Public key of EGO + */ +void +add_room_contact (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *id, + const struct GNUNET_IDENTITY_PublicKey *pubkey); + +/** + * Returns the member information of a member in a room identified by a given id. If the room + * does not contain a member with the given id, NULL gets returned. + * + * @param room Room + * @param id Member id + * @return Member information or NULL + */ +struct GNUNET_MESSENGER_MemberInfo* +get_room_member_info (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *id); + +/** + * Tries to generate and allocate a new unique member id checking all current members for possible + * duplicates. If the function fails, NULL gets returned. + * + * @param room Room + * @return New member id or NULL + */ +struct GNUNET_ShortHashCode* +generate_room_member_id (const struct GNUNET_MESSENGER_SrvRoom *room); + +/** + * Returns the member id of the member representing the handle currently hosting this room. + * + * @param room Room + * @return Host member id or NULL + */ +const struct GNUNET_ShortHashCode* +get_room_host_id (const struct GNUNET_MESSENGER_SrvRoom *room); + +/** + * Changes the member id of the member representing the handle currently hosting this room. + * + * @param room Room + * @param unique_id Unique member id + */ +void +change_room_host_id (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *unique_id); + +/** + * Tries to open a room for a given handle. If the room has already been opened, the handle + * will locally join the room. + * + * Calling this method should result in joining a room and sending a peer message as well for this peer. + * + * If the function returns GNUNET_YES the port for this room is guranteed to be open for incoming connections. + * + * @param room Room + * @param handle Handle + * @return GNUNET_YES on success, GNUNET_NO on failure. + */ +int +open_room (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle); + +/** + * Connects a tunnel to a hosting peer of a room through a so called door which is represented by + * a peer identity of a hosting peer. During the connection the handle will join the room as a member, waiting for + * an info message from the selected host. + * + * @param room Room + * @param handle Handle + * @param door Peer identity + * @return GNUNET_YES on success, GNUNET_NO on failure. + */ +int +entry_room_at (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_PeerIdentity *door); + +/** + * Returns a tunnel granting a direct connection to a specific member in a room. The member gets identified + * by an id. If no tunnel has been linked to the selected id, NULL gets returned. + * + * @param room Room + * @param contact_id Member id + * @return Tunnel to the member or NULL + */ +struct GNUNET_MESSENGER_SrvTunnel* +find_room_tunnel_to (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *contact_id); + +/** + * Packs a message depending on the selected mode into a newly allocated envelope. It will set the + * timestamp of the message, the sender id and the previous messages hash automatically before packing. The message + * will be signed by the handles EGO. + * + * If the optional hash parameter is a valid pointer, its value will be overriden by the signed messages hash. + * + * If mode is set to GNUNET_MESSENGER_PACK_MODE_ENVELOPE, the function returns a valid envelope to send + * through a message queue, otherwise NULL. + * + * @param room Room + * @param handle Handle + * @param message Message + * @param[out] hash Hash of message + * @param mode Packing mode + * @return New envelope or NULL + */ +struct GNUNET_MQ_Envelope* +pack_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash, int mode); + +/** + * Sends a message from a given handle into a room. The hash parameter will be + * updated with the hash-value resulting from the sent message. + * + * The function handles packing the message automatically and will call linked message-events locally even if + * the message won't be sent to another peer. + * + * @param room Room + * @param handle Handle + * @param message Message + * @param[out] hash Hash of message + */ +void +send_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash); + +/** + * Sends a message from a given handle into a room excluding one specific tunnel. + * The hash parameter will be updated with the hash-value resulting from the sent message. + * + * The function handles packing the message automatically and will call linked message-events locally even if + * the message won't be sent to another peer. + * + * @param room Room + * @param handle Handle + * @param message Message + * @param[out] hash Hash of message + * @param tunnel Tunnel + */ +void +send_room_message_ext (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash, + struct GNUNET_MESSENGER_SrvTunnel *tunnel); + +/** + * Forwards a message with a given hash to a specific tunnel inside of a room. + * + * @param room Room + * @param tunnel Tunnel + * @param message Message + * @param hash Hash of message + */ +void +forward_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Reduces all current forks inside of the message history of a room to one remaining last message + * by merging them down. All merge messages will be sent from a given handle. + * + * @param room Room + * @param handle Handle + */ +void +merge_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle); + +/** + * Returns the CADET handle from a rooms service. + * + * @param room Room + * @return CADET handle + */ +struct GNUNET_CADET_Handle* +get_room_cadet (struct GNUNET_MESSENGER_SrvRoom *room); + +/** + * Returns the shared secret you need to access a room. + * + * @param room Room + * @return Shared secret + */ +struct GNUNET_HashCode* +get_room_key (struct GNUNET_MESSENGER_SrvRoom *room); + +/** + * Returns a tunnel inside of a room leading towards a given peer if such a tunnel exists, + * otherwise NULL. + * + * @param room Room + * @param peer Peer identity + * @return Tunnel or NULL + */ +const struct GNUNET_MESSENGER_SrvTunnel* +get_room_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_PeerIdentity *peer); + +/** + * Returns a message from a room identified by a given hash. If no matching message is + * found and request is set to GNUNET_YES, the handle will request the missing message + * automatically. + * + * The function uses the optimized check for a message via its hash from the message store. + * @see contains_store_message() + * + * If a message is missing independent of the following request, NULL gets returned instead of the + * matching message. + * + * @param room Room + * @param handle Handle + * @param hash Hash of message + * @param request Flag to request a message + * @return Message or NULL + */ +const struct GNUNET_MESSENGER_Message* +get_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_HashCode *hash, int request); + +/** + * Updates the last messages of a room by replacing them if the previous hash of a given message + * matches with one of the latest messages. + * + * @param room Room + * @param message Message + * @param hash Hash of message + */ +void +update_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +/** + * Changes an id of a current member from an old id to a new one and adds optionally the hash of an + * id message to the members information. + * + * @param room Room + * @param old_id Old member id + * @param new_id New member id + * @param hash Hash of id message + */ +void +switch_room_member_id (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *old_id, + const struct GNUNET_ShortHashCode *new_id, const struct GNUNET_HashCode *hash); + +/** + * Rebuilds the decentralized structure for a room by ensuring all required connections are made + * depending on the amount of peers and this peers index in the list of them. + * + * @param room Room + */ +void +rebuild_room_basement_structure (struct GNUNET_MESSENGER_SrvRoom *room); + +/** + * Handles all queued up messages of a room to handle in correct order. + * + * @param room Room + */ +void +handle_room_messages (struct GNUNET_MESSENGER_SrvRoom *room); + +#endif //GNUNET_SERVICE_MESSENGER_ROOM_H diff --git a/src/messenger/gnunet-service-messenger_service.c b/src/messenger/gnunet-service-messenger_service.c new file mode 100644 index 000000000..963314fd8 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_service.c @@ -0,0 +1,516 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_service.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_service.h" + +#include "gnunet-service-messenger_message_kind.h" + +#include "gnunet-service-messenger.h" +#include "gnunet-service-messenger_util.h" + +static void +callback_shutdown_service (void *cls) +{ + struct GNUNET_MESSENGER_Service *service = cls; + + if (service) + { + service->shutdown = NULL; + + destroy_service (service); + } +} + +static void +callback_update_ego (void *cls, + struct GNUNET_IDENTITY_Ego *ego, + void **ctx, + const char *identifier) +{ + if ((!ego) || (!identifier)) + return; + + struct GNUNET_MESSENGER_Service *service = cls; + + update_service_ego(service, identifier, GNUNET_IDENTITY_ego_get_private_key(ego)); +} + +struct GNUNET_MESSENGER_Service* +create_service (const struct GNUNET_CONFIGURATION_Handle *config, struct GNUNET_SERVICE_Handle *service_handle) +{ + struct GNUNET_MESSENGER_Service *service = GNUNET_new(struct GNUNET_MESSENGER_Service); + + service->config = config; + service->service = service_handle; + + service->shutdown = GNUNET_SCHEDULER_add_shutdown (&callback_shutdown_service, service); + + service->dir = NULL; + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (service->config, + GNUNET_MESSENGER_SERVICE_NAME, + "MESSENGER_DIR", &(service->dir))) + { + if (service->dir) + GNUNET_free(service->dir); + + service->dir = NULL; + } + else + { + if ((GNUNET_YES != GNUNET_DISK_directory_test (service->dir, GNUNET_YES)) && (GNUNET_OK + != GNUNET_DISK_directory_create (service->dir))) + { + GNUNET_free(service->dir); + + service->dir = NULL; + } + } + + service->cadet = GNUNET_CADET_connect (service->config); + service->identity = GNUNET_IDENTITY_connect (service->config, &callback_update_ego, service); + + service->egos = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + + init_list_handles (&(service->handles)); + + service->contacts = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + service->rooms = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + + return service; +} + +static int +iterate_destroy_egos (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_Ego *ego = value; + GNUNET_free(ego); + return GNUNET_YES; +} + +static int +iterate_destroy_rooms (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_SrvRoom *room = value; + destroy_room (room); + return GNUNET_YES; +} + +static int +iterate_destroy_contacts (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_SrvContact *contact = value; + destroy_contact (contact); + return GNUNET_YES; +} + +void +destroy_service (struct GNUNET_MESSENGER_Service *service) +{ + if (service->shutdown) + { + GNUNET_SCHEDULER_cancel (service->shutdown); + + service->shutdown = NULL; + } + + GNUNET_CONTAINER_multihashmap_iterate (service->egos, iterate_destroy_egos, NULL); + + clear_list_handles (&(service->handles)); + + GNUNET_CONTAINER_multihashmap_iterate (service->rooms, iterate_destroy_rooms, NULL); + GNUNET_CONTAINER_multihashmap_iterate (service->contacts, iterate_destroy_contacts, NULL); + + GNUNET_CONTAINER_multihashmap_destroy (service->egos); + GNUNET_CONTAINER_multihashmap_destroy (service->rooms); + GNUNET_CONTAINER_multihashmap_destroy (service->contacts); + + if (service->cadet) + { + GNUNET_CADET_disconnect (service->cadet); + + service->cadet = NULL; + } + + if (service->identity) + { + GNUNET_IDENTITY_disconnect (service->identity); + + service->identity = NULL; + } + + if (service->dir) + { + GNUNET_free(service->dir); + + service->dir = NULL; + } + + GNUNET_SERVICE_shutdown (service->service); + + GNUNET_free(service); +} + +struct GNUNET_MESSENGER_Ego* +lookup_service_ego (struct GNUNET_MESSENGER_Service *service, const char *identifier) +{ + GNUNET_assert(identifier); + + struct GNUNET_HashCode hash; + + GNUNET_CRYPTO_hash(identifier, strlen(identifier), &hash); + return GNUNET_CONTAINER_multihashmap_get(service->egos, &hash); +} + +void +update_service_ego (struct GNUNET_MESSENGER_Service *service, const char *identifier, + const struct GNUNET_IDENTITY_PrivateKey* key) +{ + GNUNET_assert((identifier) && (key)); + + struct GNUNET_HashCode hash; + + GNUNET_CRYPTO_hash(identifier, strlen(identifier), &hash); + + struct GNUNET_MESSENGER_Ego* ego = GNUNET_CONTAINER_multihashmap_get(service->egos, &hash); + + if (!ego) + { + ego = GNUNET_new(struct GNUNET_MESSENGER_Ego); + GNUNET_CONTAINER_multihashmap_put(service->egos, &hash, ego, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + } + + GNUNET_memcpy(&(ego->priv), key, sizeof(*key)); + + if (GNUNET_OK != GNUNET_IDENTITY_key_get_public(key, &(ego->pub))) + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Updating invalid ego key failed!\n"); +} + +struct GNUNET_MESSENGER_SrvHandle* +add_service_handle (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MQ_Handle *mq) +{ + struct GNUNET_MESSENGER_SrvHandle *handle = create_handle (service, mq); + + if (handle) + { + add_list_handle (&(service->handles), handle); + } + + return handle; +} + +void +remove_service_handle (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle) +{ + if (!handle) + return; + + if (GNUNET_YES == remove_list_handle (&(service->handles), handle)) + destroy_handle (handle); +} + +int +get_service_peer_identity (const struct GNUNET_MESSENGER_Service *service, struct GNUNET_PeerIdentity *peer) +{ + return GNUNET_CRYPTO_get_peer_identity (service->config, peer); +} + +struct GNUNET_MESSENGER_SrvContact* +get_service_contact_by_pubkey (struct GNUNET_MESSENGER_Service *service, const struct GNUNET_IDENTITY_PublicKey *pubkey) +{ + struct GNUNET_HashCode hash; + + GNUNET_CRYPTO_hash (pubkey, sizeof(*pubkey), &hash); + + struct GNUNET_MESSENGER_SrvContact *contact = GNUNET_CONTAINER_multihashmap_get (service->contacts, &hash); + + if (contact) + return contact; + + contact = create_contact (pubkey); + + if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (service->contacts, &hash, contact, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + return contact; + + destroy_contact (contact); + return NULL; +} + +void +swap_service_contact_by_pubkey (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvContact *contact, + const struct GNUNET_IDENTITY_PublicKey *pubkey) +{ + const struct GNUNET_HashCode *hash = get_contact_id_from_key (contact); + + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (service->contacts, hash, contact)) + { + GNUNET_memcpy(&(contact->public_key), pubkey, sizeof(*pubkey)); + + hash = get_contact_id_from_key (contact); + + GNUNET_CONTAINER_multihashmap_put (service->contacts, hash, contact, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + } +} + +struct GNUNET_ShortHashCode* +generate_service_new_member_id (struct GNUNET_MESSENGER_Service *service, const struct GNUNET_HashCode *key) +{ + struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key); + + if (room) + { + return generate_room_member_id (room); + } + else + { + struct GNUNET_ShortHashCode *random_id = GNUNET_new(struct GNUNET_ShortHashCode); + generate_free_member_id (random_id, NULL); + return random_id; + } +} + +struct GNUNET_MESSENGER_SrvRoom* +get_service_room (struct GNUNET_MESSENGER_Service *service, const struct GNUNET_HashCode *key) +{ + return GNUNET_CONTAINER_multihashmap_get (service->rooms, key); +} + +int +open_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_HashCode *key) +{ + struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key); + + if (room) + return open_room (room, handle); + + room = create_room (handle, key); + + if ((GNUNET_YES == open_room (room, handle)) && (GNUNET_OK + == GNUNET_CONTAINER_multihashmap_put (service->rooms, key, room, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))) + return GNUNET_YES; + + destroy_room (room); + return GNUNET_NO; +} + +int +entry_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_PeerIdentity *door, const struct GNUNET_HashCode *key) +{ + struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key); + + if (room) + { + if (GNUNET_YES == entry_room_at (room, handle, door)) + return GNUNET_YES; + else + return GNUNET_NO; + } + + room = create_room (handle, key); + + if ((GNUNET_YES == entry_room_at (room, handle, door)) && (GNUNET_OK + == GNUNET_CONTAINER_multihashmap_put (service->rooms, key, room, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))) + { + return GNUNET_YES; + } + else + { + destroy_room (room); + return GNUNET_NO; + } + +} + +int +close_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_HashCode *key) +{ + struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key); + + if (!room) + return GNUNET_NO; + + struct GNUNET_MESSENGER_Message *message = create_message_leave (); + + if (message) + { + struct GNUNET_HashCode hash; + + send_room_message (room, handle, message, &hash); + destroy_message (message); + } + + const struct GNUNET_ShortHashCode *id = get_handle_member_id (handle, key); + + GNUNET_assert(id); + + if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove (handle->member_ids, key, id)) + return GNUNET_NO; + + struct GNUNET_MESSENGER_SrvHandle *member_handle = (struct GNUNET_MESSENGER_SrvHandle*) find_list_handle_by_member ( + &(service->handles), key); + + if (!member_handle) + { + if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_remove (service->rooms, key, room)) + { + destroy_room (room); + return GNUNET_YES; + } + else + return GNUNET_NO; + } + + if (room->host == handle) + room->host = member_handle; + + return GNUNET_YES; +} + +static void +get_room_data_subdir (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvRoom *room, char **dir) +{ + GNUNET_asprintf (dir, "%s%s%c%s%c", service->dir, "rooms", DIR_SEPARATOR, GNUNET_h2s (&(room->key)), DIR_SEPARATOR); +} + +void +load_service_room_and_messages (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvRoom *room) +{ + char *room_dir; + get_room_data_subdir (service, room, &room_dir); + + if (GNUNET_YES == GNUNET_DISK_directory_test (room_dir, GNUNET_YES)) + { + load_message_store (&room->store, room_dir); + + char *config_file; + GNUNET_asprintf (&config_file, "%s%s", room_dir, "room.cfg"); + + struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create (); + + if ((GNUNET_YES == GNUNET_DISK_file_test (config_file)) && (GNUNET_OK + == GNUNET_CONFIGURATION_parse (cfg, config_file))) + { + unsigned long long access; + + if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_number (cfg, "room", "access-rule", &access)) + room->strict_access = (int) (access); + + char *message_string; + + if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, "room", "last-message", &message_string)) && (message_string)) + { + struct GNUNET_HashCode hash; + + GNUNET_CRYPTO_hash_from_string(message_string, &hash); + + const struct GNUNET_MESSENGER_Message *message = get_room_message (room, room->host, &hash, GNUNET_NO); + + if (message) + update_room_last_messages (room, message, &hash); + + GNUNET_free(message_string); + } + } + + GNUNET_CONFIGURATION_destroy (cfg); + + GNUNET_free(config_file); + } + + GNUNET_free(room_dir); +} + +void +save_service_room_and_messages (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvRoom *room) +{ + if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (service->rooms, &(room->key))) + { + return; + } + + char *room_dir; + get_room_data_subdir (service, room, &room_dir); + + if ((GNUNET_YES == GNUNET_DISK_directory_test (room_dir, GNUNET_NO)) || (GNUNET_OK + == GNUNET_DISK_directory_create (room_dir))) + { + save_message_store (&room->store, room_dir); + + char *config_file; + GNUNET_asprintf (&config_file, "%s%s", room_dir, "room.cfg"); + + struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create (); + + GNUNET_CONFIGURATION_set_value_number (cfg, "room", "access-rule", room->strict_access); + + if (room->last_messages.head) + GNUNET_CONFIGURATION_set_value_string (cfg, "room", "last-message", + GNUNET_h2s_full (&(room->last_messages.head->hash))); + + GNUNET_CONFIGURATION_write (cfg, config_file); + GNUNET_CONFIGURATION_destroy (cfg); + + GNUNET_free(config_file); + } + + GNUNET_free(room_dir); +} + +void +handle_service_message (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvRoom *room, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_ListHandle *element = service->handles.head; + + const uint16_t length = get_message_size (message); + + while (element) + { + struct GNUNET_MESSENGER_SrvHandle *handle = (struct GNUNET_MESSENGER_SrvHandle*) element->handle; + + if ((handle->mq) && (get_handle_member_id (handle, &(room->key)))) + { + struct GNUNET_MESSENGER_RecvMessage *msg; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg_extra(msg, length, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_RECV_MESSAGE); + + GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key)); + GNUNET_memcpy(&(msg->hash), hash, sizeof(*hash)); + + char *buffer = ((char*) msg) + sizeof(*msg); + encode_message (message, length, buffer); + + GNUNET_MQ_send (handle->mq, env); + } + + element = element->next; + } +} diff --git a/src/messenger/gnunet-service-messenger_service.h b/src/messenger/gnunet-service-messenger_service.h new file mode 100644 index 000000000..246c74771 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_service.h @@ -0,0 +1,259 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_service.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_SERVICE_H +#define GNUNET_SERVICE_MESSENGER_SERVICE_H + +#include "platform.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_container_lib.h" +#include "gnunet_disk_lib.h" +#include "gnunet_identity_service.h" + +#include "messenger_api_ego.h" + +#include "gnunet-service-messenger_list_handles.h" + +#include "gnunet-service-messenger_contact.h" +#include "gnunet-service-messenger_room.h" + +struct GNUNET_MESSENGER_Service +{ + const struct GNUNET_CONFIGURATION_Handle *config; + struct GNUNET_SERVICE_Handle *service; + + struct GNUNET_SCHEDULER_Task *shutdown; + + char *dir; + + struct GNUNET_CADET_Handle *cadet; + struct GNUNET_IDENTITY_Handle *identity; + + struct GNUNET_CONTAINER_MultiHashMap *egos; + + struct GNUNET_MESSENGER_ListHandles handles; + + struct GNUNET_CONTAINER_MultiHashMap *contacts; + struct GNUNET_CONTAINER_MultiHashMap *rooms; +}; + +/** + * Creates and allocates a new service using a given config and a GNUnet service handle. + * + * @param config Configuration + * @param service_handle GNUnet service handle + * @return New service + */ +struct GNUNET_MESSENGER_Service* +create_service (const struct GNUNET_CONFIGURATION_Handle *config, struct GNUNET_SERVICE_Handle *service_handle); + +/** + * Destroys a service and frees its memory fully. + * + * @param service Service + */ +void +destroy_service (struct GNUNET_MESSENGER_Service *service); + +/** + * Lookups an EGO which was registered to a service under + * a specific identifier. + * + * @param service Service + * @param identifier Identifier string + * @return EGO or NULL + */ +struct GNUNET_MESSENGER_Ego* +lookup_service_ego (struct GNUNET_MESSENGER_Service *service, const char *identifier); + +/** + * Updates the registration of an EGO to a service under + * a specific identifier with a new key. + * + * @param service Service + * @param identifier Identifier string + * @param key Private EGO key + */ +void +update_service_ego (struct GNUNET_MESSENGER_Service *service, const char *identifier, + const struct GNUNET_IDENTITY_PrivateKey* key); + +/** + * Creates and adds a new handle to a service using a given message queue. + * + * @param service Service + * @param mq Message queue + * @return New handle + */ +struct GNUNET_MESSENGER_SrvHandle* +add_service_handle (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MQ_Handle *mq); + +/** + * Removes a handle from a service and destroys it. + * + * @param service Service + * @param handle Handle + */ +void +remove_service_handle (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle); + +/** + * Tries to write the peer identity of the peer running a service on to the peer + * parameter. The functions returns GNUNET_OK on success, otherwise GNUNET_SYSERR. + * + * @param service Service + * @param[out] peer Peer identity + * @return GNUNET_OK on success, otherwise GNUNET_SYSERR + */ +int +get_service_peer_identity (const struct GNUNET_MESSENGER_Service *service, struct GNUNET_PeerIdentity *peer); + +/** + * Returns a contact of a service identified by a given public key. If no matching contact exists, + * it will tried to create one with the specific public key. If the function still fails to do so, + * NULL gets returned. + * + * @param service Service + * @param pubkey Public key of EGO + * @return Contact + */ +struct GNUNET_MESSENGER_SrvContact* +get_service_contact_by_pubkey (struct GNUNET_MESSENGER_Service *service, const struct GNUNET_IDENTITY_PublicKey *pubkey); + +/** + * Changes the public key for a contact known to a service to a specific public key and + * updates local map entries to access the contact by its updated key. + * + * @param service Service + * @param contact Contact + * @param pubkey Public key of EGO + */ +void +swap_service_contact_by_pubkey (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvContact *contact, + const struct GNUNET_IDENTITY_PublicKey *pubkey); + +/** + * Tries to generate and allocate a new unique member id for a given room of a service identified by its key. + * If the generation fails caused by too many tries of duplicates, it returns NULL. + * + * @param service Service + * @param key Key of room + * @return Newly generated member id or NULL + */ +struct GNUNET_ShortHashCode* +generate_service_new_member_id (struct GNUNET_MESSENGER_Service *service, const struct GNUNET_HashCode *key); + +/** + * Returns the room identified by a given key for a service. If the service doesn't know any room + * using the given key, NULL gets returned. + * + * @param service Service + * @param key Key of room + * @return Room or NULL + */ +struct GNUNET_MESSENGER_SrvRoom* +get_service_room (struct GNUNET_MESSENGER_Service *service, const struct GNUNET_HashCode *key); + +/** + * Tries to open a room using a given key for a service by a specific handle. The room will be + * created if necessary. If the function is successful, it returns GNUNET_YES, otherwise GNUNET_NO. + * + * @param service Service + * @param handle Handle + * @param key Key of room + * @return GNUNET_YES on success, otherwise GNUNET_NO + */ +int +open_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_HashCode *key); + +/** + * Tries to enter a room using a given key for a service by a specific handle. The room will + * be created if necessary. If the function is successful, it returns GNUNET_YES, otherwise GNUNET_NO. + * + * The room will be entered through the peer identitied by the peer identity provided as door parameter and + * a new connection will be made. + * + * @param service Service + * @param handle Handle + * @param door Peer identity + * @param key Key of room + * @return GNUNET_YES on success, otherwise GNUNET_NO + */ +int +entry_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_PeerIdentity *door, const struct GNUNET_HashCode *key); + +/** + * Tries to close a room using a given key for a service by a specific handle. The room will + * be created if necessary. If the function is successful, it returns GNUNET_YES, otherwise GNUNET_NO. + * + * If the specific handle is currently the host of the room for this service, a new handle which is a member will + * take its place. Otherwise the room will be destroyed for this service. + * + * @param service Service + * @param handle Handle + * @param key Key of room + * @return GNUNET_YES on success, otherwise GNUNET_NO + */ +int +close_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_HashCode *key); + +/** + * Loads the local configuration for a given room of a service which contains the last messages hash + * and the ruleset for general access of new members. + * + * @param service Service + * @param room Room + */ +void +load_service_room_and_messages (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvRoom *room); + +/** + * Saves the configuration for a given room of a service which contains the last messages hash + * and the ruleset for general access of new members locally. + * + * @param service Service + * @param room Room + */ +void +save_service_room_and_messages (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvRoom *room); + +/** + * Sends a received or sent message with a given hash to each handle of a service which + * is currently member of a specific room for handling it in the client API. + * + * @param service Service + * @param room Room + * @param message Message + * @param hash Hash of message + */ +void +handle_service_message (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvRoom *room, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +#endif //GNUNET_SERVICE_MESSENGER_SERVICE_H diff --git a/src/messenger/gnunet-service-messenger_tunnel.c b/src/messenger/gnunet-service-messenger_tunnel.c new file mode 100644 index 000000000..df9e5c4c7 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_tunnel.c @@ -0,0 +1,300 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_tunnel.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_tunnel.h" + +#include "gnunet-service-messenger_handle.h" +#include "gnunet-service-messenger_util.h" + +struct GNUNET_MESSENGER_SrvTunnel* +create_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_PeerIdentity *door) +{ + GNUNET_assert((room) && (door)); + + struct GNUNET_MESSENGER_SrvTunnel *tunnel = GNUNET_new(struct GNUNET_MESSENGER_SrvTunnel); + + tunnel->room = room; + tunnel->channel = NULL; + + tunnel->peer = GNUNET_PEER_intern (door); + tunnel->contact_id = NULL; + + tunnel->peer_message = NULL; + tunnel->last_message = NULL; + + return tunnel; +} + +void +destroy_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel) +{ + GNUNET_assert(tunnel); + + if (tunnel->channel) + GNUNET_CADET_channel_destroy (tunnel->channel); + + GNUNET_PEER_change_rc (tunnel->peer, -1); + + if (tunnel->contact_id) + GNUNET_free(tunnel->contact_id); + + if (tunnel->peer_message) + GNUNET_free(tunnel->peer_message); + + if (tunnel->last_message) + GNUNET_free(tunnel->last_message); + + GNUNET_free(tunnel); +} + +int +bind_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_CADET_Channel *channel) +{ + GNUNET_assert(tunnel); + + if (tunnel->channel) + { + if (tunnel->contact_id) + return GNUNET_NO; + + delayed_disconnect_channel (tunnel->channel); + } + + tunnel->channel = channel; + + return GNUNET_YES; +} + +extern void +callback_room_disconnect (struct GNUNET_MESSENGER_SrvRoom *room, void *cls); + +void +callback_tunnel_disconnect (void *cls, const struct GNUNET_CADET_Channel *channel) +{ + struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls; + + if (tunnel) + { + tunnel->channel = NULL; + + callback_room_disconnect (tunnel->room, cls); + } +} + +extern int +callback_verify_room_message (struct GNUNET_MESSENGER_SrvRoom *room, void *cls, + struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash); + +int +check_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header) +{ + struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls; + + if (!tunnel) + return GNUNET_NO; + + const uint16_t length = ntohs (header->size) - sizeof(*header); + const char *buffer = (const char*) &header[1]; + + struct GNUNET_MESSENGER_Message message; + + if (length < sizeof(message.header)) + return GNUNET_NO; + + if (GNUNET_YES != decode_message (&message, length, buffer)) + return GNUNET_NO; + + struct GNUNET_HashCode hash; + hash_message (length, buffer, &hash); + + int result = callback_verify_room_message (tunnel->room, cls, &message, &hash); + + if (GNUNET_MESSENGER_KIND_PEER == message.header.kind) + { + struct GNUNET_PeerIdentity identity; + + GNUNET_PEER_resolve (tunnel->peer, &identity); + + if (0 == GNUNET_memcmp(&(message.body.peer.peer), &(identity))) + { + if (tunnel->contact_id) + { + if (0 != GNUNET_memcmp(tunnel->contact_id, &(message.header.sender_id))) + result = GNUNET_SYSERR; + } + else + { + tunnel->contact_id = GNUNET_new(struct GNUNET_ShortHashCode); + + GNUNET_memcpy(tunnel->contact_id, &(message.header.sender_id), sizeof(struct GNUNET_ShortHashCode)); + } + } + } + + return (result == GNUNET_YES ? GNUNET_OK : GNUNET_NO); +} + +extern void +callback_room_recv (struct GNUNET_MESSENGER_SrvRoom *room, void *cls, struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +void +handle_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header) +{ + struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls; + + const uint16_t length = ntohs (header->size) - sizeof(*header); + const char *buffer = (const char*) &header[1]; + + struct GNUNET_MESSENGER_Message message; + struct GNUNET_HashCode hash; + + decode_message (&message, length, buffer); + hash_message (length, buffer, &hash); + + if (tunnel) + { + if (!tunnel->last_message) + tunnel->last_message = GNUNET_new(struct GNUNET_HashCode); + + GNUNET_memcpy(tunnel->last_message, &hash, sizeof(struct GNUNET_HashCode)); + + callback_room_recv (tunnel->room, cls, copy_message (&message), &hash); + } + + GNUNET_CADET_receive_done (tunnel->channel); +} + +int +connect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel) +{ + GNUNET_assert(tunnel); + + if (tunnel->channel) + return GNUNET_NO; + + const struct GNUNET_PeerIdentity *door = GNUNET_PEER_resolve2 (tunnel->peer); + + struct GNUNET_CADET_Handle *cadet = get_room_cadet (tunnel->room); + struct GNUNET_HashCode *key = get_room_key (tunnel->room); + + struct GNUNET_MQ_MessageHandler handlers[] = { GNUNET_MQ_hd_var_size(tunnel_message, GNUNET_MESSAGE_TYPE_CADET_CLI, + struct GNUNET_MessageHeader, NULL), + GNUNET_MQ_handler_end() }; + + tunnel->channel = GNUNET_CADET_channel_create (cadet, tunnel, door, key, NULL, callback_tunnel_disconnect, handlers); + + return GNUNET_YES; +} + +void +disconnect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel) +{ + if (tunnel->channel) + { + delayed_disconnect_channel (tunnel->channel); + + tunnel->channel = NULL; + } +} + +int +is_tunnel_connected (const struct GNUNET_MESSENGER_SrvTunnel *tunnel) +{ + return (tunnel->channel ? GNUNET_YES : GNUNET_NO); +} + +struct GNUNET_MESSENGER_MessageSent +{ + struct GNUNET_MESSENGER_SrvTunnel *tunnel; + struct GNUNET_HashCode hash; +}; + +extern void +callback_room_sent (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, void *cls, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +static void +callback_tunnel_sent (void *cls) +{ + struct GNUNET_MESSENGER_MessageSent *sent = cls; + + if (sent->tunnel) + { + if (!sent->tunnel->last_message) + sent->tunnel->last_message = GNUNET_new(struct GNUNET_HashCode); + + GNUNET_memcpy(sent->tunnel->last_message, &(sent->hash), sizeof(struct GNUNET_HashCode)); + } + + GNUNET_free(sent); +} + +void +send_tunnel_envelope (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, struct GNUNET_MQ_Envelope *env, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MQ_Handle *mq = GNUNET_CADET_get_mq (tunnel->channel); + + struct GNUNET_MESSENGER_MessageSent *sent = GNUNET_new(struct GNUNET_MESSENGER_MessageSent); + + GNUNET_memcpy(&(sent->hash), hash, sizeof(struct GNUNET_HashCode)); + + sent->tunnel = tunnel; + + GNUNET_MQ_notify_sent (env, callback_tunnel_sent, sent); + GNUNET_MQ_send (mq, env); + + callback_room_sent (tunnel->room, (struct GNUNET_MESSENGER_SrvHandle*) handle, tunnel, message, hash); +} + +void +send_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, struct GNUNET_MESSENGER_Message *message, + struct GNUNET_HashCode *hash) +{ + struct GNUNET_MQ_Envelope *env = pack_room_message (tunnel->room, (struct GNUNET_MESSENGER_SrvHandle*) handle, + message, hash, + GNUNET_MESSENGER_PACK_MODE_ENVELOPE); + + if (env) + send_tunnel_envelope (tunnel, handle, env, copy_message (message), hash); +} + +void +forward_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_Message *clone = copy_message (message); + struct GNUNET_MQ_Envelope *env = pack_message (clone, NULL, NULL, GNUNET_MESSENGER_PACK_MODE_ENVELOPE); + + if (env) + send_tunnel_envelope (tunnel, NULL, env, clone, hash); +} + +const struct GNUNET_HashCode* +get_tunnel_peer_message (const struct GNUNET_MESSENGER_SrvTunnel *tunnel) +{ + return tunnel->peer_message; +} diff --git a/src/messenger/gnunet-service-messenger_tunnel.h b/src/messenger/gnunet-service-messenger_tunnel.h new file mode 100644 index 000000000..e6efb226d --- /dev/null +++ b/src/messenger/gnunet-service-messenger_tunnel.h @@ -0,0 +1,155 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_tunnel.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_TUNNEL_H +#define GNUNET_SERVICE_MESSENGER_TUNNEL_H + +#include "platform.h" +#include "gnunet_cadet_service.h" +#include "gnunet_peer_lib.h" +#include "gnunet_crypto_lib.h" + +#include "gnunet-service-messenger_room.h" + +struct GNUNET_MESSENGER_SrvTunnel +{ + struct GNUNET_MESSENGER_SrvRoom *room; + struct GNUNET_CADET_Channel *channel; + + GNUNET_PEER_Id peer; + struct GNUNET_ShortHashCode *contact_id; + + struct GNUNET_HashCode *peer_message; + struct GNUNET_HashCode *last_message; +}; + +/** + * Creates and allocates a tunnel of a room to a specific peer identity. + * + * @param room Room + * @param door Peer identity + * @return New tunnel + */ +struct GNUNET_MESSENGER_SrvTunnel* +create_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_PeerIdentity *door); + +/** + * Destroys a tunnel and frees its memory fully. + * + * @param tunnel + */ +void +destroy_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel); + +/** + * Binds a CADET channel to a tunnel on returns GNUNET_YES only if + * the bounds channel was replaced successfully, otherwise GNUNET_NO gets returned. + * + * @param tunnel Tunnel + * @param channel CADET channel + * @return GNUNET_YES on success, otherwise GNUNET_NO + */ +int +bind_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_CADET_Channel *channel); + +/** + * Tries to connect a tunnel by creating a new CADET channel and binding it. + * The function returns GNUNET_YES on success, otherwise GNUNET_NO. + * + * @param tunnel Tunnel + * @return GNUNET_YES on success, otherwise GNUNET_NO + */ +int +connect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel); + +/** + * Disconnects and unbinds a channel from a tunnel. The actual disconnection + * will be asynchronous. + * + * @param tunnel Tunnel + */ +void +disconnect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel); + +/** + * Returns the status of a currently bound channel of a tunnel. + * + * @param tunnel Tunnel + * @return GNUNET_YES or GNUNET_NO + */ +int +is_tunnel_connected (const struct GNUNET_MESSENGER_SrvTunnel *tunnel); + +/** + * Sends an envelope containing a message with a given hash through + * a tunnel by a given handle. + * + * @param tunnel Tunnel + * @param handle Handle + * @param env Envelope + * @param message Message + * @param hash Hash of message + */ +void +send_tunnel_envelope (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, struct GNUNET_MQ_Envelope *env, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Sends a message by packing it automatically into an envelope and passing it + * through the tunnel. The used handle will sign the message and + * the hash will be calculated and stored. + * + * @param tunnel Tunnel + * @param handle Handle + * @param[out] message Message + * @param[out] hash Hash of message + */ +void +send_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, struct GNUNET_MESSENGER_Message *message, + struct GNUNET_HashCode *hash); + +/** + * Forwards a given message with a known hash through a tunnel. + * + * @param tunnel Tunnel + * @param message Message + * @param hash Hash of message + */ +void +forward_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +/** + * Returns the hash of the latest peer message published through a given tunnel + * and matching the tunnels peer identity. If no peer message has been linked to the tunnel + * yet, NULL gets returned. + * + * @param tunnel Tunnel + * @return Hash of peer message or NULL + */ +const struct GNUNET_HashCode* +get_tunnel_peer_message (const struct GNUNET_MESSENGER_SrvTunnel *tunnel); + +#endif //GNUNET_SERVICE_MESSENGER_TUNNEL_H diff --git a/src/messenger/gnunet-service-messenger_util.c b/src/messenger/gnunet-service-messenger_util.c new file mode 100644 index 000000000..94fc9469d --- /dev/null +++ b/src/messenger/gnunet-service-messenger_util.c @@ -0,0 +1,64 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_util.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_util.h" + +static void +callback_close_channel (void *cls) +{ + struct GNUNET_CADET_Channel *channel = cls; + + if (channel) + GNUNET_CADET_channel_destroy (channel); +} + +void +delayed_disconnect_channel (struct GNUNET_CADET_Channel *channel) +{ + GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_get_zero_ (), GNUNET_SCHEDULER_PRIORITY_URGENT, + callback_close_channel, channel); +} + +int +generate_free_member_id (struct GNUNET_ShortHashCode *id, const struct GNUNET_CONTAINER_MultiShortmap *members) +{ + size_t counter = 1 + (members ? GNUNET_CONTAINER_multishortmap_size (members) : 0); + + do + { + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, id, sizeof(struct GNUNET_ShortHashCode)); + + if ((members) && (GNUNET_YES == GNUNET_CONTAINER_multishortmap_contains (members, id))) + counter--; + else + break; + } + while (counter > 0); + + if (counter) + return GNUNET_YES; + + return GNUNET_NO; +} diff --git a/src/messenger/gnunet-service-messenger_util.h b/src/messenger/gnunet-service-messenger_util.h new file mode 100644 index 000000000..20f8f0afe --- /dev/null +++ b/src/messenger/gnunet-service-messenger_util.h @@ -0,0 +1,53 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/gnunet-service-messenger_util.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_UTIL_H +#define GNUNET_SERVICE_MESSENGER_UTIL_H + +#include "platform.h" +#include "gnunet_cadet_service.h" +#include "gnunet_container_lib.h" +#include "gnunet_crypto_lib.h" + +/** + * Starts an urgent task to close a CADET channel asynchronously. + * + * @param channel Channel + */ +void +delayed_disconnect_channel (struct GNUNET_CADET_Channel *channel); + +/** + * Tries to generate an unused member id and store it into the id parameter. A map containing all currently + * used member ids is used to check against. + * + * @param[out] id New member id + * @param members Map of member ids + * @return GNUNET_YES on success, GNUNET_NO on failure + */ +int +generate_free_member_id (struct GNUNET_ShortHashCode *id, const struct GNUNET_CONTAINER_MultiShortmap *members); + +#endif //GNUNET_SERVICE_MESSENGER_UTIL_H diff --git a/src/messenger/messenger.conf.in b/src/messenger/messenger.conf.in new file mode 100644 index 000000000..59e11b166 --- /dev/null +++ b/src/messenger/messenger.conf.in @@ -0,0 +1,13 @@ +[messenger] +START_ON_DEMAND = YES +PORT = 2097 +HOSTNAME = localhost +BINARY = gnunet-service-messenger +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-messenger.sock +UNIX_MATCH_UID = NO +UNIX_MATCH_GID = YES + +# Directory to store messages and contacts +MESSENGER_DIR = $GNUNET_DATA_HOME/messenger/ \ No newline at end of file diff --git a/src/messenger/messenger_api.c b/src/messenger/messenger_api.c new file mode 100644 index 000000000..6401b18d7 --- /dev/null +++ b/src/messenger/messenger_api.c @@ -0,0 +1,568 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/messenger_api.c + * @brief messenger api: client implementation of GNUnet MESSENGER service + */ + +#include "gnunet_messenger_service.h" + +#include "gnunet-service-messenger.h" + +#include "messenger_api_handle.h" +#include "messenger_api_message.h" + +const char* +GNUNET_MESSENGER_name_of_kind (enum GNUNET_MESSENGER_MessageKind kind) +{ + switch (kind) + { + case GNUNET_MESSENGER_KIND_INFO: + return "INFO"; + case GNUNET_MESSENGER_KIND_JOIN: + return "JOIN"; + case GNUNET_MESSENGER_KIND_LEAVE: + return "LEAVE"; + case GNUNET_MESSENGER_KIND_NAME: + return "NAME"; + case GNUNET_MESSENGER_KIND_KEY: + return "KEY"; + case GNUNET_MESSENGER_KIND_PEER: + return "PEER"; + case GNUNET_MESSENGER_KIND_ID: + return "ID"; + case GNUNET_MESSENGER_KIND_MISS: + return "MISS"; + case GNUNET_MESSENGER_KIND_MERGE: + return "MERGE"; + case GNUNET_MESSENGER_KIND_REQUEST: + return "REQUEST"; + case GNUNET_MESSENGER_KIND_INVITE: + return "INVITE"; + case GNUNET_MESSENGER_KIND_TEXT: + return "TEXT"; + case GNUNET_MESSENGER_KIND_FILE: + return "FILE"; + default: + return "UNKNOWN"; + } +} + +static int +check_get_name (void *cls, const struct GNUNET_MESSENGER_NameMessage *msg) +{ + GNUNET_MQ_check_zero_termination(msg); + return GNUNET_OK; +} + +static void +handle_get_name (void *cls, const struct GNUNET_MESSENGER_NameMessage *msg) +{ + struct GNUNET_MESSENGER_Handle *handle = cls; + + const char *name = ((const char*) msg) + sizeof(*msg); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Set name of handle: %s\n", name); + + set_handle_name (handle, strlen(name) > 0? name : NULL); +} + +static void +handle_get_key (void *cls, const struct GNUNET_MESSENGER_KeyMessage *msg) +{ + struct GNUNET_MESSENGER_Handle *handle = cls; + + const struct GNUNET_IDENTITY_PublicKey *pubkey = &(msg->pubkey); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Set key of handle: %s\n", GNUNET_IDENTITY_public_key_to_string (pubkey)); + + set_handle_key (handle, pubkey); + + if (handle->identity_callback) + handle->identity_callback (handle->identity_cls, handle); +} + +static void +handle_member_id (void *cls, const struct GNUNET_MESSENGER_MemberMessage *msg) +{ + struct GNUNET_MESSENGER_Handle *handle = cls; + + const struct GNUNET_HashCode *key = &(msg->key); + const struct GNUNET_ShortHashCode *id = &(msg->id); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Set id of handle in room: %s\n", GNUNET_h2s (key)); + + struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get (handle->rooms, key); + + if (room) + { + if (!room->contact_id) + room->contact_id = GNUNET_new(struct GNUNET_ShortHashCode); + + GNUNET_memcpy(room->contact_id, id, sizeof(*id)); + } +} + +static void +handle_room_open (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg) +{ + struct GNUNET_MESSENGER_Handle *handle = cls; + + const struct GNUNET_HashCode *key = &(msg->key); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Opened room: %s\n", GNUNET_h2s (key)); + + open_handle_room (handle, key); +} + +static void +handle_room_entry (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg) +{ + struct GNUNET_MESSENGER_Handle *handle = cls; + + const struct GNUNET_PeerIdentity *door = &(msg->door); + const struct GNUNET_HashCode *key = &(msg->key); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Entered room: %s\n", GNUNET_h2s (key)); + + entry_handle_room_at (handle, door, key); +} + +static void +handle_room_close (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg) +{ + struct GNUNET_MESSENGER_Handle *handle = cls; + + const struct GNUNET_HashCode *key = &(msg->key); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Closed room: %s\n", GNUNET_h2s (key)); + + close_handle_room (handle, key); +} + +static int +check_recv_message (void *cls, const struct GNUNET_MESSENGER_RecvMessage *msg) +{ + const uint16_t full_length = ntohs (msg->header.size) - sizeof(msg->header); + + if (full_length < sizeof(msg->hash)) + return GNUNET_NO; + + const uint16_t length = full_length - sizeof(msg->hash); + const char *buffer = ((const char*) msg) + sizeof(*msg); + + struct GNUNET_MESSENGER_Message message; + + if (length < sizeof(message.header)) + return GNUNET_NO; + + if (GNUNET_YES != decode_message (&message, length, buffer)) + return GNUNET_NO; + + return GNUNET_OK; +} + +static void +handle_recv_message (void *cls, const struct GNUNET_MESSENGER_RecvMessage *msg) +{ + struct GNUNET_MESSENGER_Handle *handle = cls; + + const struct GNUNET_HashCode *key = &(msg->key); + const struct GNUNET_HashCode *hash = &(msg->hash); + + const char *buffer = ((const char*) msg) + sizeof(*msg); + + const uint16_t length = ntohs (msg->header.size) - sizeof(*msg); + + struct GNUNET_MESSENGER_Message message; + decode_message (&message, length, buffer); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Receiving message: %s\n", GNUNET_MESSENGER_name_of_kind (message.header.kind)); + + struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get (handle->rooms, key); + + if (room) + { + handle_room_message (room, &message, hash); + + if (handle->msg_callback) + handle->msg_callback (handle->msg_cls, room, &message, hash); + } + else + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "MESSENGER ERROR: Room not found\n"); +} + +static void +reconnect (struct GNUNET_MESSENGER_Handle *handle); + +static void +send_open_room (struct GNUNET_MESSENGER_Handle *handle, struct GNUNET_MESSENGER_Room *room) +{ + struct GNUNET_MESSENGER_RoomMessage *msg; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_OPEN); + GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key)); + GNUNET_MQ_send (handle->mq, env); +} + +static void +send_entry_room (struct GNUNET_MESSENGER_Handle *handle, struct GNUNET_MESSENGER_Room *room, + const struct GNUNET_PeerIdentity *door) +{ + struct GNUNET_MESSENGER_RoomMessage *msg; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_ENTRY); + GNUNET_memcpy(&(msg->door), door, sizeof(*door)); + GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key)); + GNUNET_MQ_send (handle->mq, env); +} + +static void +send_close_room (struct GNUNET_MESSENGER_Handle *handle, struct GNUNET_MESSENGER_Room *room) +{ + struct GNUNET_MESSENGER_RoomMessage *msg; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_CLOSE); + GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key)); + GNUNET_MQ_send (handle->mq, env); +} + +static int +iterate_reset_room (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_Handle *handle = cls; + struct GNUNET_MESSENGER_Room *room = value; + + if (GNUNET_YES == room->opened) + send_open_room (handle, room); + + struct GNUNET_MESSENGER_ListTunnel *entry = room->entries.head; + + struct GNUNET_PeerIdentity door; + + while (entry) + { + GNUNET_PEER_resolve (entry->peer, &door); + + send_entry_room (handle, room, &door); + + entry = entry->next; + } + + return GNUNET_YES; +} + +static void +callback_reconnect (void *cls) +{ + struct GNUNET_MESSENGER_Handle *handle = cls; + + handle->reconnect_task = NULL; + handle->reconnect_time = GNUNET_TIME_STD_BACKOFF(handle->reconnect_time) + ; + + reconnect (handle); + + GNUNET_CONTAINER_multihashmap_iterate (handle->rooms, iterate_reset_room, handle); +} + +static int +iterate_close_room (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_Handle *handle = cls; + struct GNUNET_MESSENGER_Room *room = value; + + send_close_room (handle, room); + + return GNUNET_YES; +} + +static void +callback_mq_error (void *cls, enum GNUNET_MQ_Error error) +{ + struct GNUNET_MESSENGER_Handle *handle = cls; + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "MQ ERROR: %u\n", error); + + GNUNET_CONTAINER_multihashmap_iterate (handle->rooms, iterate_close_room, handle); + + if (handle->mq) + { + GNUNET_MQ_destroy (handle->mq); + handle->mq = NULL; + } + + handle->reconnect_task = GNUNET_SCHEDULER_add_delayed (handle->reconnect_time, &callback_reconnect, handle); +} + +static void +reconnect (struct GNUNET_MESSENGER_Handle *handle) +{ + const struct GNUNET_MQ_MessageHandler handlers[] = { GNUNET_MQ_hd_var_size( + get_name, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_NAME, struct GNUNET_MESSENGER_NameMessage, handle), + GNUNET_MQ_hd_fixed_size( + get_key, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_KEY, + struct GNUNET_MESSENGER_KeyMessage, handle), + GNUNET_MQ_hd_fixed_size( + member_id, + GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_MEMBER_ID, + struct GNUNET_MESSENGER_MemberMessage, handle), + GNUNET_MQ_hd_fixed_size(room_open, + GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_OPEN, + struct GNUNET_MESSENGER_RoomMessage, + handle), + GNUNET_MQ_hd_fixed_size(room_entry, + GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_ENTRY, + struct GNUNET_MESSENGER_RoomMessage, + handle), + GNUNET_MQ_hd_fixed_size(room_close, + GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_CLOSE, + struct GNUNET_MESSENGER_RoomMessage, + handle), + GNUNET_MQ_hd_var_size( + recv_message, + GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_RECV_MESSAGE, + struct GNUNET_MESSENGER_RecvMessage, handle), + GNUNET_MQ_handler_end() }; + + handle->mq = GNUNET_CLIENT_connect (handle->cfg, + GNUNET_MESSENGER_SERVICE_NAME, + handlers, &callback_mq_error, handle); +} + +struct GNUNET_MESSENGER_Handle* +GNUNET_MESSENGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, const char *name, + GNUNET_MESSENGER_IdentityCallback identity_callback, void *identity_cls, + GNUNET_MESSENGER_MessageCallback msg_callback, void *msg_cls) +{ + struct GNUNET_MESSENGER_Handle *handle = create_handle (cfg, identity_callback, identity_cls, msg_callback, msg_cls); + + reconnect (handle); + + if (handle->mq) + { + const uint16_t name_len = name ? strlen (name) : 0; + + struct GNUNET_MESSENGER_CreateMessage *msg; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg_extra(msg, name_len + 1, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_CREATE); + + char *extra = ((char*) msg) + sizeof(*msg); + + if (name_len) + GNUNET_memcpy(extra, name, name_len); + + extra[name_len] = '\0'; + + GNUNET_MQ_send (handle->mq, env); + return handle; + } + else + { + destroy_handle (handle); + return NULL; + } +} + +int +GNUNET_MESSENGER_update (struct GNUNET_MESSENGER_Handle *handle) +{ + if ((!handle) || (!get_handle_name(handle))) + return GNUNET_SYSERR; + + struct GNUNET_MESSENGER_UpdateMessage *msg; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_UPDATE); + GNUNET_MQ_send (handle->mq, env); + return GNUNET_OK; +} + +void +GNUNET_MESSENGER_disconnect (struct GNUNET_MESSENGER_Handle *handle) +{ + if (!handle) + return; + + struct GNUNET_MESSENGER_DestroyMessage *msg; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_DESTROY); + GNUNET_MQ_send (handle->mq, env); + + destroy_handle (handle); +} + +const char* +GNUNET_MESSENGER_get_name (const struct GNUNET_MESSENGER_Handle *handle) +{ + if (!handle) + return NULL; + + return get_handle_name (handle); +} + +int +GNUNET_MESSENGER_set_name (struct GNUNET_MESSENGER_Handle *handle, const char *name) +{ + if (!handle) + return GNUNET_SYSERR; + + const uint16_t name_len = name ? strlen (name) : 0; + + struct GNUNET_MESSENGER_NameMessage *msg; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg_extra(msg, name_len + 1, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_SET_NAME); + + char *extra = ((char*) msg) + sizeof(*msg); + + if (name_len) + GNUNET_memcpy(extra, name, name_len); + + extra[name_len] = '\0'; + + GNUNET_MQ_send (handle->mq, env); + return GNUNET_YES; +} + +const struct GNUNET_IDENTITY_PublicKey* +GNUNET_MESSENGER_get_key (const struct GNUNET_MESSENGER_Handle *handle) +{ + if (!handle) + return NULL; + + return get_handle_key (handle); +} + +struct GNUNET_MESSENGER_Room* +GNUNET_MESSENGER_open_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key) +{ + struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get (handle->rooms, key); + + if (!room) + { + room = create_room (handle, key); + + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (handle->rooms, key, room, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + { + destroy_room (room); + return NULL; + } + } + + send_open_room (handle, room); + return room; +} + +struct GNUNET_MESSENGER_Room* +GNUNET_MESSENGER_entry_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_PeerIdentity *door, + const struct GNUNET_HashCode *key) +{ + struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get (handle->rooms, key); + + if (!room) + { + room = create_room (handle, key); + + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (handle->rooms, key, room, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + { + destroy_room (room); + return NULL; + } + } + + send_entry_room (handle, room, door); + return room; +} + +void +GNUNET_MESSENGER_close_room (struct GNUNET_MESSENGER_Room *room) +{ + send_close_room (room->handle, room); +} + +struct GNUNET_MESSENGER_Contact* +GNUNET_MESSENGER_get_member (const struct GNUNET_MESSENGER_Room *room, const struct GNUNET_ShortHashCode *id) +{ + return GNUNET_CONTAINER_multishortmap_get (room->members, id); +} + +const char* +GNUNET_MESSENGER_contact_get_name (const struct GNUNET_MESSENGER_Contact *contact) +{ + if (!contact) + return NULL; + + return get_contact_name (contact); +} + +const struct GNUNET_IDENTITY_PublicKey* +GNUNET_MESSENGER_contact_get_key (const struct GNUNET_MESSENGER_Contact *contact) +{ + if (!contact) + return NULL; + + return get_contact_key (contact); +} + +void +GNUNET_MESSENGER_send_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message) +{ + const uint16_t length = get_message_size (message); + + struct GNUNET_MESSENGER_SendMessage *msg; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg_extra(msg, length, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_SEND_MESSAGE); + + GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key)); + + char *buffer = ((char*) msg) + sizeof(*msg); + encode_message (message, length, buffer); + + GNUNET_MQ_send (room->handle->mq, env); +} + +const struct GNUNET_MESSENGER_Message* +GNUNET_MESSENGER_get_message (const struct GNUNET_MESSENGER_Room *room, const struct GNUNET_HashCode *hash) +{ + const struct GNUNET_MESSENGER_Message *message = get_room_message (room, hash); + + if (!message) + { + struct GNUNET_MESSENGER_RecvMessage *msg; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_GET_MESSAGE); + GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key)); + GNUNET_memcpy(&(msg->hash), hash, sizeof(*hash)); + GNUNET_MQ_send (room->handle->mq, env); + } + + return message; +} diff --git a/src/messenger/messenger_api_contact.c b/src/messenger/messenger_api_contact.c new file mode 100644 index 000000000..9a242aa00 --- /dev/null +++ b/src/messenger/messenger_api_contact.c @@ -0,0 +1,78 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/messenger_api_contact.c + * @brief messenger api: client implementation of GNUnet MESSENGER service + */ + +#include "messenger_api_contact.h" + +struct GNUNET_MESSENGER_Contact* +create_contact (const struct GNUNET_IDENTITY_PublicKey *key) +{ + struct GNUNET_MESSENGER_Contact *contact = GNUNET_new(struct GNUNET_MESSENGER_Contact); + + contact->name = NULL; + + GNUNET_memcpy(&(contact->public_key), key, sizeof(contact->public_key)); + + return contact; +} + +void +destroy_contact (struct GNUNET_MESSENGER_Contact *contact) +{ + if (contact->name) + GNUNET_free(contact->name); + + GNUNET_free(contact); +} + +const char* +get_contact_name (const struct GNUNET_MESSENGER_Contact *contact) +{ + return contact->name; +} + +void +set_contact_name (struct GNUNET_MESSENGER_Contact *contact, const char *name) +{ + if (contact->name) + GNUNET_free(contact->name); + + contact->name = name? GNUNET_strdup(name) : NULL; +} + +const struct GNUNET_IDENTITY_PublicKey* +get_contact_key (const struct GNUNET_MESSENGER_Contact *contact) +{ + return &(contact->public_key); +} + +const struct GNUNET_HashCode* +get_contact_id_from_key (const struct GNUNET_MESSENGER_Contact *contact) +{ + static struct GNUNET_HashCode id; + + GNUNET_CRYPTO_hash (&(contact->public_key), sizeof(contact->public_key), &id); + + return &id; +} diff --git a/src/messenger/messenger_api_contact.h b/src/messenger/messenger_api_contact.h new file mode 100644 index 000000000..0673b9b85 --- /dev/null +++ b/src/messenger/messenger_api_contact.h @@ -0,0 +1,93 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/messenger_api_contact.h + * @brief messenger api: client implementation of GNUnet MESSENGER service + */ + +#ifndef GNUNET_MESSENGER_API_CONTACT_H +#define GNUNET_MESSENGER_API_CONTACT_H + +#include "platform.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_identity_service.h" + +struct GNUNET_MESSENGER_Contact +{ + char *name; + + struct GNUNET_IDENTITY_PublicKey public_key; +}; + +/** + * Creates and allocates a new contact with a given public key from an EGO. + * + * @param key Public key + * @return New contact + */ +struct GNUNET_MESSENGER_Contact* +create_contact (const struct GNUNET_IDENTITY_PublicKey *key); + +/** + * Destroys a contact and frees its memory fully. + * + * @param contact Contact + */ +void +destroy_contact (struct GNUNET_MESSENGER_Contact *contact); + +/** + * Returns the current name of a given contact or NULL if no valid name was assigned yet. + * + * @param contact Contact + * @return Name of the contact or NULL + */ +const char* +get_contact_name (const struct GNUNET_MESSENGER_Contact *contact); + +/** + * Changes the current name of a given contact by copying it from the parameter name. + * + * @param contact Contact + * @param name Valid name (may not be NULL!) + */ +void +set_contact_name (struct GNUNET_MESSENGER_Contact *contact, const char *name); + +/** + * Returns the public key of a given contact. + * + * @param contact Contact + * @return Public key of the contact + */ +const struct GNUNET_IDENTITY_PublicKey* +get_contact_key (const struct GNUNET_MESSENGER_Contact *contact); + +/** + * Returns the resulting hashcode of the public key from a given contact. + * + * @param contact Contact + * @return Hash of the contacts public key + */ +const struct GNUNET_HashCode* +get_contact_id_from_key (const struct GNUNET_MESSENGER_Contact *contact); + +#endif //GNUNET_MESSENGER_API_CONTACT_H diff --git a/src/messenger/messenger_api_ego.h b/src/messenger/messenger_api_ego.h new file mode 100644 index 000000000..c60eeac50 --- /dev/null +++ b/src/messenger/messenger_api_ego.h @@ -0,0 +1,38 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/messenger_api_ego.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_MESSENGER_API_EGO_H +#define GNUNET_MESSENGER_API_EGO_H + +#include "platform.h" +#include "gnunet_identity_service.h" + +struct GNUNET_MESSENGER_Ego +{ + struct GNUNET_IDENTITY_PrivateKey priv; + struct GNUNET_IDENTITY_PublicKey pub; +}; + +#endif //GNUNET_MESSENGER_API_EGO_H diff --git a/src/messenger/messenger_api_handle.c b/src/messenger/messenger_api_handle.c new file mode 100644 index 000000000..20ef77254 --- /dev/null +++ b/src/messenger/messenger_api_handle.c @@ -0,0 +1,213 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/messenger_api_handle.c + * @brief messenger api: client implementation of GNUnet MESSENGER service + */ + +#include "messenger_api_handle.h" + +struct GNUNET_MESSENGER_Handle* +create_handle (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_MESSENGER_IdentityCallback identity_callback, + void *identity_cls, GNUNET_MESSENGER_MessageCallback msg_callback, void *msg_cls) +{ + struct GNUNET_MESSENGER_Handle *handle = GNUNET_new(struct GNUNET_MESSENGER_Handle); + + handle->cfg = cfg; + handle->mq = NULL; + + handle->identity_callback = identity_callback; + handle->identity_cls = identity_cls; + + handle->msg_callback = msg_callback; + handle->msg_cls = msg_cls; + + handle->name = NULL; + handle->pubkey = NULL; + + handle->reconnect_time = GNUNET_TIME_relative_get_zero_ (); + handle->reconnect_task = NULL; + + handle->rooms = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + handle->contacts = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + + return handle; +} + +static int +iterate_destroy_room (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_Room *room = value; + + destroy_room (room); + + return GNUNET_YES; +} + +static int +iterate_destroy_contact (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_Contact *contact = value; + + destroy_contact (contact); + + return GNUNET_YES; +} + +void +destroy_handle (struct GNUNET_MESSENGER_Handle *handle) +{ + if (handle->reconnect_task) + GNUNET_SCHEDULER_cancel (handle->reconnect_task); + + if (handle->mq) + GNUNET_MQ_destroy (handle->mq); + + if (handle->name) + GNUNET_free(handle->name); + + if (handle->pubkey) + GNUNET_free(handle->pubkey); + + if (handle->rooms) + { + GNUNET_CONTAINER_multihashmap_iterate (handle->rooms, iterate_destroy_room, NULL); + + GNUNET_CONTAINER_multihashmap_destroy (handle->rooms); + } + + if (handle->contacts) + { + GNUNET_CONTAINER_multihashmap_iterate (handle->contacts, iterate_destroy_contact, NULL); + + GNUNET_CONTAINER_multihashmap_destroy (handle->contacts); + } + + GNUNET_free(handle->name); +} + +void +set_handle_name (struct GNUNET_MESSENGER_Handle *handle, const char *name) +{ + if (handle->name) + GNUNET_free(handle->name); + + handle->name = name? GNUNET_strdup(name) : NULL; +} + +const char* +get_handle_name (const struct GNUNET_MESSENGER_Handle *handle) +{ + return handle->name; +} + +void +set_handle_key (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_IDENTITY_PublicKey *pubkey) +{ + if (!handle->pubkey) + handle->pubkey = GNUNET_new(struct GNUNET_IDENTITY_PublicKey); + + GNUNET_memcpy(handle->pubkey, pubkey, sizeof(*pubkey)); +} + +const struct GNUNET_IDENTITY_PublicKey* +get_handle_key (const struct GNUNET_MESSENGER_Handle *handle) +{ + if (!handle->pubkey) + { + struct GNUNET_IDENTITY_Ego *anonymous = GNUNET_IDENTITY_ego_get_anonymous (); + static struct GNUNET_IDENTITY_PublicKey pubkey; + + GNUNET_IDENTITY_ego_get_public_key (anonymous, &pubkey); + + return &pubkey; + } + + return handle->pubkey; +} + +struct GNUNET_MESSENGER_Contact* +get_handle_contact_by_pubkey (const struct GNUNET_MESSENGER_Handle *handle, + const struct GNUNET_IDENTITY_PublicKey *pubkey) +{ + struct GNUNET_HashCode hash; + + GNUNET_CRYPTO_hash (pubkey, sizeof(*pubkey), &hash); + + struct GNUNET_MESSENGER_Contact *contact = GNUNET_CONTAINER_multihashmap_get (handle->contacts, &hash); + + if (contact) + return contact; + + contact = create_contact (pubkey); + + if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (handle->contacts, &hash, contact, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + return contact; + + destroy_contact (contact); + return NULL; +} + +void +swap_handle_contact_by_pubkey (struct GNUNET_MESSENGER_Handle *handle, struct GNUNET_MESSENGER_Contact *contact, + const struct GNUNET_IDENTITY_PublicKey *pubkey) +{ + const struct GNUNET_HashCode *hash = get_contact_id_from_key (contact); + + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (handle->contacts, hash, contact)) + { + GNUNET_memcpy(&(contact->public_key), pubkey, sizeof(*pubkey)); + + hash = get_contact_id_from_key (contact); + + GNUNET_CONTAINER_multihashmap_put (handle->contacts, hash, contact, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + } +} + +void +open_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key) +{ + struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get (handle->rooms, key); + + if (room) + room->opened = GNUNET_YES; +} + +void +entry_handle_room_at (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_PeerIdentity *door, + const struct GNUNET_HashCode *key) +{ + struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get (handle->rooms, key); + + if (room) + add_to_list_tunnels (&(room->entries), door); +} + +void +close_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key) +{ + struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get (handle->rooms, key); + + if ((room) && (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (handle->rooms, key, room))) + destroy_room (room); +} diff --git a/src/messenger/messenger_api_handle.h b/src/messenger/messenger_api_handle.h new file mode 100644 index 000000000..d6cde0106 --- /dev/null +++ b/src/messenger/messenger_api_handle.h @@ -0,0 +1,174 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/messenger_api_handle.h + * @brief messenger api: client implementation of GNUnet MESSENGER service + */ + +#ifndef GNUNET_MESSENGER_API_HANDLE_H +#define GNUNET_MESSENGER_API_HANDLE_H + +#include "platform.h" +#include "gnunet_cadet_service.h" +#include "gnunet_container_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_identity_service.h" +#include "gnunet_peer_lib.h" + +#include "gnunet_messenger_service.h" + +#include "messenger_api_contact.h" +#include "messenger_api_room.h" + +struct GNUNET_MESSENGER_Handle +{ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + struct GNUNET_MQ_Handle *mq; + + GNUNET_MESSENGER_IdentityCallback identity_callback; + void *identity_cls; + + GNUNET_MESSENGER_MessageCallback msg_callback; + void *msg_cls; + + char *name; + struct GNUNET_IDENTITY_PublicKey *pubkey; + + struct GNUNET_TIME_Relative reconnect_time; + struct GNUNET_SCHEDULER_Task *reconnect_task; + + struct GNUNET_CONTAINER_MultiHashMap *rooms; + struct GNUNET_CONTAINER_MultiHashMap *contacts; +}; + +/** + * Creates and allocates a new handle using a given configuration and a custom message callback + * with a given closure for the client API. + * + * @param cfg Configuration + * @param msg_callback Message callback + * @param msg_cls Closure + * @return New handle + */ +struct GNUNET_MESSENGER_Handle* +create_handle (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_MESSENGER_IdentityCallback identity_callback, + void *identity_cls, GNUNET_MESSENGER_MessageCallback msg_callback, void *msg_cls); + +/** + * Destroys a handle and frees its memory fully from the client API. + * + * @param handle Handle + */ +void +destroy_handle (struct GNUNET_MESSENGER_Handle *handle); + +/** + * Sets the name of a handle to a specific name. + * + * @param handle Handle + * @param name New name + */ +void +set_handle_name (struct GNUNET_MESSENGER_Handle *handle, const char *name); + +/** + * Returns the current name of a given handle or NULL if no valid name was assigned yet. + * + * @param handle Handle + * @return Name of the handle or NULL + */ +const char* +get_handle_name (const struct GNUNET_MESSENGER_Handle *handle); + +/** + * Sets the public key of a given handle to a specific public key. + * + * @param handle Handle + * @param pubkey Public key + */ +void +set_handle_key (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_IDENTITY_PublicKey *pubkey); + +/** + * Returns the public key of a given handle. + * + * @param handle Handle + * @return Public key of the handle + */ +const struct GNUNET_IDENTITY_PublicKey* +get_handle_key (const struct GNUNET_MESSENGER_Handle *handle); + +/** + * Returns a contact known to a handle identified by a given public key. If not matching + * contact is found, NULL gets returned. + * + * @param handle Handle + * @param pubkey Public key of EGO + * @return Contact or NULL + */ +struct GNUNET_MESSENGER_Contact* +get_handle_contact_by_pubkey (const struct GNUNET_MESSENGER_Handle *handle, + const struct GNUNET_IDENTITY_PublicKey *pubkey); + +/** + * Changes the public key for a contact known to a handle to a specific public key and + * updates local map entries to access the contact by its updated key. + * + * @param handle Handle + * @param contact Contact + * @param pubkey Public key of EGO + */ +void +swap_handle_contact_by_pubkey (struct GNUNET_MESSENGER_Handle *handle, struct GNUNET_MESSENGER_Contact *contact, + const struct GNUNET_IDENTITY_PublicKey *pubkey); + +/** + * Marks a room known to a handle identified by a given key as open. + * + * @param handle Handle + * @param key Key of room + */ +void +open_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key); + +/** + * Adds a tunnel for a room known to a handle identified by a given key to a + * list of opened connections. + * + * @param handle Handle + * @param door Peer identity + * @param key Key of room + */ +void +entry_handle_room_at (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_PeerIdentity *door, + const struct GNUNET_HashCode *key); + +/** + * Destroys and so implicitly closes a room known to a handle identified by a given key. + * + * @param handle Handle + * @param key Key of room + */ +void +close_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key); + +#endif //GNUNET_MESSENGER_API_HANDLE_H diff --git a/src/messenger/messenger_api_list_tunnels.c b/src/messenger/messenger_api_list_tunnels.c new file mode 100644 index 000000000..13d8c1906 --- /dev/null +++ b/src/messenger/messenger_api_list_tunnels.c @@ -0,0 +1,112 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/messenger_api_list_tunnels.c + * @brief messenger api: client and service implementation of GNUnet MESSENGER service + */ + +#include "messenger_api_list_tunnels.h" + +void +init_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels) +{ + GNUNET_assert(tunnels); + + tunnels->head = NULL; + tunnels->tail = NULL; +} + +void +clear_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels) +{ + GNUNET_assert(tunnels); + + struct GNUNET_MESSENGER_ListTunnel *element; + + for (element = tunnels->head; element; element = tunnels->head) + { + GNUNET_CONTAINER_DLL_remove(tunnels->head, tunnels->tail, element); + GNUNET_PEER_change_rc (element->peer, -1); + GNUNET_free(element); + } + + tunnels->head = NULL; + tunnels->tail = NULL; +} + +static int +compare_list_tunnels (void *cls, struct GNUNET_MESSENGER_ListTunnel *element0, + struct GNUNET_MESSENGER_ListTunnel *element1) +{ + return ((int) element0->peer) - ((int) element1->peer); +} + +void +add_to_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct GNUNET_PeerIdentity *peer) +{ + struct GNUNET_MESSENGER_ListTunnel *element = GNUNET_new(struct GNUNET_MESSENGER_ListTunnel); + + element->peer = GNUNET_PEER_intern (peer); + + GNUNET_CONTAINER_DLL_insert_sorted(struct GNUNET_MESSENGER_ListTunnel, compare_list_tunnels, NULL, tunnels->head, + tunnels->tail, element); +} + +struct GNUNET_MESSENGER_ListTunnel* +find_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct GNUNET_PeerIdentity *peer, size_t *index) +{ + struct GNUNET_MESSENGER_ListTunnel *element; + struct GNUNET_PeerIdentity pid; + + if (index) + *index = 0; + + for (element = tunnels->head; element; element = element->next) + { + GNUNET_PEER_resolve (element->peer, &pid); + + if (0 == GNUNET_memcmp(&pid, peer)) + return element; + + if (index) + (*index) = (*index) + 1; + } + + return NULL; +} + +int +contains_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct GNUNET_PeerIdentity *peer) +{ + return find_list_tunnels (tunnels, peer, NULL) != NULL ? GNUNET_YES : GNUNET_NO; +} + +struct GNUNET_MESSENGER_ListTunnel* +remove_from_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, struct GNUNET_MESSENGER_ListTunnel *element) +{ + struct GNUNET_MESSENGER_ListTunnel *next = element->next; + + GNUNET_CONTAINER_DLL_remove(tunnels->head, tunnels->tail, element); + GNUNET_PEER_change_rc (element->peer, -1); + GNUNET_free(element); + + return next; +} diff --git a/src/messenger/messenger_api_list_tunnels.h b/src/messenger/messenger_api_list_tunnels.h new file mode 100644 index 000000000..0240fceb8 --- /dev/null +++ b/src/messenger/messenger_api_list_tunnels.h @@ -0,0 +1,112 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/messenger_api_list_tunnels.h + * @brief messenger api: client and service implementation of GNUnet MESSENGER service + */ + +#ifndef GNUNET_MESSENGER_API_LIST_TUNNELS_H +#define GNUNET_MESSENGER_API_LIST_TUNNELS_H + +#include "platform.h" +#include "gnunet_peer_lib.h" +#include "gnunet_container_lib.h" + +struct GNUNET_MESSENGER_ListTunnel +{ + struct GNUNET_MESSENGER_ListTunnel *prev; + struct GNUNET_MESSENGER_ListTunnel *next; + + GNUNET_PEER_Id peer; +}; + +struct GNUNET_MESSENGER_ListTunnels +{ + struct GNUNET_MESSENGER_ListTunnel *head; + struct GNUNET_MESSENGER_ListTunnel *tail; +}; + +/** + * Initializes list of tunnels peer identities as empty list. + * + * @param tunnels List of peer identities + */ +void +init_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels); + +/** + * Clears the list of tunnels peer identities. + * + * @param tunnels List of peer identities + */ +void +clear_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels); + +/** + * Adds a specific peer from a tunnel to the end of the list. + * + * @param tunnels List of peer identities + * @param peer Peer identity of tunnel + */ +void +add_to_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct GNUNET_PeerIdentity *peer); + +/** + * Searches linearly through the list of tunnels peer identities for matching a + * specific peer identity and returns the matching element of the list. + * + * If no matching element is found, NULL gets returned. + * + * If index is not NULL, index will be overriden with the numeric index of + * the found element in the list. If no matching element is found, index will + * contain the total amount of elements in the list. + * + * @param tunnels List of peer identities + * @param peer Peer identity of tunnel + * @param[out] index Index of found element (optional) + * @return Element in the list with matching peer identity + */ +struct GNUNET_MESSENGER_ListTunnel* +find_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct GNUNET_PeerIdentity *peer, size_t *index); + +/** + * Tests linearly if the list of tunnels peer identities contains a specific + * peer identity and returns GNUNET_YES on success, otherwise GNUNET_NO. + * + * @param tunnels List of peer identities + * @param peer Peer identity of tunnel + * @return GNUNET_YES on success, otherwise GNUNET_NO + */ +int +contains_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct GNUNET_PeerIdentity *peer); + +/** + * Removes a specific element from the list of tunnels peer identities and returns + * the next element in the list. + * + * @param tunnels List of peer identities + * @param element Element of the list + * @return Next element in the list + */ +struct GNUNET_MESSENGER_ListTunnel* +remove_from_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, struct GNUNET_MESSENGER_ListTunnel *element); + +#endif //GNUNET_MESSENGER_API_LIST_TUNNELS_H diff --git a/src/messenger/messenger_api_message.c b/src/messenger/messenger_api_message.c new file mode 100644 index 000000000..fdab60eef --- /dev/null +++ b/src/messenger/messenger_api_message.c @@ -0,0 +1,602 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/messenger_api_message.c + * @brief messenger api: client and service implementation of GNUnet MESSENGER service + */ + +#include "messenger_api_message.h" + +struct GNUNET_MESSENGER_MessageSignature +{ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + struct GNUNET_HashCode hash; +}; + +struct GNUNET_MESSENGER_ShortMessage +{ + enum GNUNET_MESSENGER_MessageKind kind; + struct GNUNET_MESSENGER_MessageBody body; +}; + +struct GNUNET_MESSENGER_Message* +create_message (enum GNUNET_MESSENGER_MessageKind kind) +{ + struct GNUNET_MESSENGER_Message *message = GNUNET_new(struct GNUNET_MESSENGER_Message); + + message->header.kind = kind; + + switch (message->header.kind) + { + case GNUNET_MESSENGER_KIND_NAME: + message->body.name.name = NULL; + break; + case GNUNET_MESSENGER_KIND_TEXT: + message->body.text.text = NULL; + break; + case GNUNET_MESSENGER_KIND_FILE: + message->body.file.uri = NULL; + break; + case GNUNET_MESSENGER_KIND_PRIVATE: + message->body.private.length = 0; + message->body.private.data = NULL; + break; + default: + break; + } + + return message; +} + +struct GNUNET_MESSENGER_Message* +copy_message (const struct GNUNET_MESSENGER_Message *message) +{ + struct GNUNET_MESSENGER_Message *copy = GNUNET_new(struct GNUNET_MESSENGER_Message); + + GNUNET_memcpy(copy, message, sizeof(struct GNUNET_MESSENGER_Message)); + + switch (message->header.kind) + { + case GNUNET_MESSENGER_KIND_NAME: + copy->body.name.name = GNUNET_strdup(message->body.name.name); + break; + case GNUNET_MESSENGER_KIND_TEXT: + copy->body.text.text = GNUNET_strdup(message->body.text.text); + break; + case GNUNET_MESSENGER_KIND_FILE: + copy->body.file.uri = GNUNET_strdup(message->body.file.uri); + break; + case GNUNET_MESSENGER_KIND_PRIVATE: + copy->body.private.data = copy->body.private.length ? GNUNET_malloc(copy->body.private.length) : NULL; + + if (copy->body.private.data) + { + GNUNET_memcpy(copy->body.private.data, message->body.private.data, copy->body.private.length); + } + + break; + default: + break; + } + + return copy; +} + +static void +destroy_message_body (enum GNUNET_MESSENGER_MessageKind kind, struct GNUNET_MESSENGER_MessageBody *body) +{ + switch (kind) + { + case GNUNET_MESSENGER_KIND_NAME: + GNUNET_free(body->name.name); + break; + case GNUNET_MESSENGER_KIND_TEXT: + GNUNET_free(body->text.text); + break; + case GNUNET_MESSENGER_KIND_FILE: + GNUNET_free(body->file.uri); + break; + case GNUNET_MESSENGER_KIND_PRIVATE: + GNUNET_free(body->private.data); + break; + default: + break; + } +} + +void +destroy_message (struct GNUNET_MESSENGER_Message *message) +{ + destroy_message_body (message->header.kind, &(message->body)); + + GNUNET_free(message); +} + +static void +fold_short_message (const struct GNUNET_MESSENGER_Message *message, struct GNUNET_MESSENGER_ShortMessage *shortened) +{ + shortened->kind = message->header.kind; + + GNUNET_memcpy(&(shortened->body), &(message->body), sizeof(struct GNUNET_MESSENGER_MessageBody)); +} + +static void +unfold_short_message (struct GNUNET_MESSENGER_ShortMessage *shortened, struct GNUNET_MESSENGER_Message *message) +{ + destroy_message_body (message->header.kind, &(message->body)); + + message->header.kind = shortened->kind; + + GNUNET_memcpy(&(message->body), &(shortened->body), sizeof(struct GNUNET_MESSENGER_MessageBody)); +} + +#define member_size(type, member) sizeof(((type*) NULL)->member) + +static uint16_t +get_message_body_kind_size (enum GNUNET_MESSENGER_MessageKind kind) +{ + uint16_t length = 0; + + switch (kind) + { + case GNUNET_MESSENGER_KIND_INFO: + length += member_size(struct GNUNET_MESSENGER_Message, body.info.host_key); + length += member_size(struct GNUNET_MESSENGER_Message, body.info.unique_id); + break; + case GNUNET_MESSENGER_KIND_JOIN: + length += member_size(struct GNUNET_MESSENGER_Message, body.join.key); + break; + case GNUNET_MESSENGER_KIND_LEAVE: + break; + case GNUNET_MESSENGER_KIND_NAME: + break; + case GNUNET_MESSENGER_KIND_KEY: + length += member_size(struct GNUNET_MESSENGER_Message, body.key.key); + break; + case GNUNET_MESSENGER_KIND_PEER: + length += member_size(struct GNUNET_MESSENGER_Message, body.peer.peer); + break; + case GNUNET_MESSENGER_KIND_ID: + length += member_size(struct GNUNET_MESSENGER_Message, body.id.id); + break; + case GNUNET_MESSENGER_KIND_MISS: + length += member_size(struct GNUNET_MESSENGER_Message, body.miss.peer); + break; + case GNUNET_MESSENGER_KIND_MERGE: + length += member_size(struct GNUNET_MESSENGER_Message, body.merge.previous); + break; + case GNUNET_MESSENGER_KIND_REQUEST: + length += member_size(struct GNUNET_MESSENGER_Message, body.request.hash); + break; + case GNUNET_MESSENGER_KIND_INVITE: + length += member_size(struct GNUNET_MESSENGER_Message, body.invite.door); + length += member_size(struct GNUNET_MESSENGER_Message, body.invite.key); + break; + case GNUNET_MESSENGER_KIND_TEXT: + break; + case GNUNET_MESSENGER_KIND_FILE: + length += member_size(struct GNUNET_MESSENGER_Message, body.file.key); + length += member_size(struct GNUNET_MESSENGER_Message, body.file.hash); + length += NAME_MAX; + break; + case GNUNET_MESSENGER_KIND_PRIVATE: + length += member_size(struct GNUNET_MESSENGER_Message, body.private.key); + break; + default: + break; + } + + return length; +} + +uint16_t +get_message_kind_size (enum GNUNET_MESSENGER_MessageKind kind) +{ + uint16_t length = 0; + + length += member_size(struct GNUNET_MESSENGER_Message, header.signature); + length += member_size(struct GNUNET_MESSENGER_Message, header.timestamp); + length += member_size(struct GNUNET_MESSENGER_Message, header.sender_id); + length += member_size(struct GNUNET_MESSENGER_Message, header.previous); + length += member_size(struct GNUNET_MESSENGER_Message, header.kind); + + return length + get_message_body_kind_size (kind); +} + +static uint16_t +get_message_body_size (enum GNUNET_MESSENGER_MessageKind kind, const struct GNUNET_MESSENGER_MessageBody *body) +{ + uint16_t length = 0; + + switch (kind) + { + case GNUNET_MESSENGER_KIND_NAME: + length += (body->name.name? strlen (body->name.name) : 0); + break; + case GNUNET_MESSENGER_KIND_TEXT: + length += strlen (body->text.text); + break; + case GNUNET_MESSENGER_KIND_FILE: + length += strlen (body->file.uri); + break; + case GNUNET_MESSENGER_KIND_PRIVATE: + length += body->private.length; + break; + default: + break; + } + + return length; +} + +uint16_t +get_message_size (const struct GNUNET_MESSENGER_Message *message) +{ + return get_message_kind_size (message->header.kind) + get_message_body_size (message->header.kind, &(message->body)); +} + +static uint16_t +get_short_message_size (const struct GNUNET_MESSENGER_ShortMessage *message) +{ + if (message) + return sizeof(message->kind) + get_message_body_kind_size (message->kind) + + get_message_body_size (message->kind, &(message->body)); + else + return sizeof(message->kind); +} + +#define min(x, y) (x < y? x : y) + +#define encode_step_ext(dst, offset, src, size) do { \ + GNUNET_memcpy(dst + offset, src, size); \ + offset += size; \ +} while (0) + +#define encode_step(dst, offset, src) do { \ + encode_step_ext(dst, offset, src, sizeof(*src)); \ +} while(0) + +static void +encode_message_body (enum GNUNET_MESSENGER_MessageKind kind, const struct GNUNET_MESSENGER_MessageBody *body, + uint16_t length, char *buffer, uint16_t offset) +{ + switch (kind) + { + case GNUNET_MESSENGER_KIND_INFO: + encode_step(buffer, offset, &(body->info.host_key)); + encode_step(buffer, offset, &(body->info.unique_id)); + break; + case GNUNET_MESSENGER_KIND_JOIN: + encode_step(buffer, offset, &(body->join.key)); + break; + case GNUNET_MESSENGER_KIND_LEAVE: + break; + case GNUNET_MESSENGER_KIND_NAME: + if (body->name.name) + encode_step_ext(buffer, offset, body->name.name, min(length - offset, strlen(body->name.name))); + break; + case GNUNET_MESSENGER_KIND_KEY: + encode_step(buffer, offset, &(body->key.key)); + break; + case GNUNET_MESSENGER_KIND_PEER: + encode_step(buffer, offset, &(body->peer.peer)); + break; + case GNUNET_MESSENGER_KIND_ID: + encode_step(buffer, offset, &(body->id.id)); + break; + case GNUNET_MESSENGER_KIND_MISS: + encode_step(buffer, offset, &(body->miss.peer)); + break; + case GNUNET_MESSENGER_KIND_MERGE: + encode_step(buffer, offset, &(body->merge.previous)); + break; + case GNUNET_MESSENGER_KIND_REQUEST: + encode_step(buffer, offset, &(body->request.hash)); + break; + case GNUNET_MESSENGER_KIND_INVITE: + encode_step(buffer, offset, &(body->invite.door)); + encode_step(buffer, offset, &(body->invite.key)); + break; + case GNUNET_MESSENGER_KIND_TEXT: + encode_step_ext(buffer, offset, body->text.text, min(length - offset, strlen(body->text.text))); + break; + case GNUNET_MESSENGER_KIND_FILE: + encode_step(buffer, offset, &(body->file.key)); + encode_step(buffer, offset, &(body->file.hash)); + encode_step_ext(buffer, offset, body->file.name, NAME_MAX); + encode_step_ext(buffer, offset, body->file.uri, min(length - offset, strlen(body->file.uri))); + break; + case GNUNET_MESSENGER_KIND_PRIVATE: + encode_step(buffer, offset, &(body->private.key)); + encode_step_ext(buffer, offset, body->private.data, min(length - offset, body->private.length)); + break; + default: + break; + } +} + +void +encode_message (const struct GNUNET_MESSENGER_Message *message, uint16_t length, char *buffer) +{ + uint16_t offset = 0; + + encode_step(buffer, offset, &(message->header.signature)); + encode_step(buffer, offset, &(message->header.timestamp)); + encode_step(buffer, offset, &(message->header.sender_id)); + encode_step(buffer, offset, &(message->header.previous)); + encode_step(buffer, offset, &(message->header.kind)); + + encode_message_body (message->header.kind, &(message->body), length, buffer, offset); +} + +static void +encode_short_message (const struct GNUNET_MESSENGER_ShortMessage *message, uint16_t length, char *buffer) +{ + uint16_t offset = 0; + + encode_step(buffer, offset, &(message->kind)); + + encode_message_body (message->kind, &(message->body), length, buffer, offset); +} + +#define decode_step_ext(src, offset, dst, size) do { \ + GNUNET_memcpy(dst, src + offset, size); \ + offset += size; \ +} while (0) + +#define decode_step(src, offset, dst) do { \ + decode_step_ext(src, offset, dst, sizeof(*dst)); \ +} while (0) + +#define decode_step_malloc(src, offset, dst, size, zero) do { \ + dst = GNUNET_malloc(size + zero); \ + if (zero) dst[size] = 0; \ + decode_step_ext(src, offset, dst, size); \ +} while (0) + +static void +decode_message_body (enum GNUNET_MESSENGER_MessageKind *kind, struct GNUNET_MESSENGER_MessageBody *body, + uint16_t length, const char *buffer, uint16_t offset) +{ + switch (*kind) + { + case GNUNET_MESSENGER_KIND_INFO: + decode_step(buffer, offset, &(body->info.host_key)); + decode_step(buffer, offset, &(body->info.unique_id)); + break; + case GNUNET_MESSENGER_KIND_JOIN: + decode_step(buffer, offset, &(body->join.key)); + break; + case GNUNET_MESSENGER_KIND_LEAVE: + break; + case GNUNET_MESSENGER_KIND_NAME: + if (length - offset > 0) + decode_step_malloc(buffer, offset, body->name.name, length - offset, 1); + else + body->name.name = NULL; + break; + case GNUNET_MESSENGER_KIND_KEY: + decode_step(buffer, offset, &(body->key.key)); + break; + case GNUNET_MESSENGER_KIND_PEER: + decode_step(buffer, offset, &(body->peer.peer)); + break; + case GNUNET_MESSENGER_KIND_ID: + decode_step(buffer, offset, &(body->id.id)); + break; + case GNUNET_MESSENGER_KIND_MISS: + decode_step(buffer, offset, &(body->miss.peer)); + break; + case GNUNET_MESSENGER_KIND_MERGE: + decode_step(buffer, offset, &(body->merge.previous)); + break; + case GNUNET_MESSENGER_KIND_REQUEST: + decode_step(buffer, offset, &(body->request.hash)); + break; + case GNUNET_MESSENGER_KIND_INVITE: + decode_step(buffer, offset, &(body->invite.door)); + decode_step(buffer, offset, &(body->invite.key)); + break; + case GNUNET_MESSENGER_KIND_TEXT: + decode_step_malloc(buffer, offset, body->text.text, length - offset, 1); + break; + case GNUNET_MESSENGER_KIND_FILE: + decode_step(buffer, offset, &(body->file.key)); + decode_step(buffer, offset, &(body->file.hash)); + decode_step_ext(buffer, offset, body->file.name, NAME_MAX); + decode_step_malloc(buffer, offset, body->file.uri, length - offset, 1); + break; + case GNUNET_MESSENGER_KIND_PRIVATE: + decode_step(buffer, offset, &(body->private.key)); + + body->private.length = (length - offset); + decode_step_malloc(buffer, offset, body->private.data, length - offset, 0); + break; + default: + *kind = GNUNET_MESSENGER_KIND_UNKNOWN; + break; + } +} + +int +decode_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, const char *buffer) +{ + uint16_t offset = 0; + + if (length < get_message_kind_size (GNUNET_MESSENGER_KIND_UNKNOWN)) + return GNUNET_NO; + + decode_step(buffer, offset, &(message->header.signature)); + decode_step(buffer, offset, &(message->header.timestamp)); + decode_step(buffer, offset, &(message->header.sender_id)); + decode_step(buffer, offset, &(message->header.previous)); + decode_step(buffer, offset, &(message->header.kind)); + + if (length < get_message_kind_size (message->header.kind)) + return GNUNET_NO; + + decode_message_body (&(message->header.kind), &(message->body), length, buffer, offset); + + return GNUNET_YES; +} + +static int +decode_short_message (struct GNUNET_MESSENGER_ShortMessage *message, uint16_t length, const char *buffer) +{ + uint16_t offset = 0; + + if (length < get_short_message_size (NULL)) + return GNUNET_NO; + + decode_step(buffer, offset, &(message->kind)); + + if (length < get_short_message_size (message)) + return GNUNET_NO; + + decode_message_body (&(message->kind), &(message->body), length, buffer, offset); + + return GNUNET_YES; +} + +void +hash_message (uint16_t length, const char *buffer, struct GNUNET_HashCode *hash) +{ + GNUNET_CRYPTO_hash (buffer + sizeof(struct GNUNET_CRYPTO_EcdsaSignature), + length - sizeof(struct GNUNET_CRYPTO_EcdsaSignature), hash); +} + +void +sign_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, char *buffer, + const struct GNUNET_HashCode *hash, const struct GNUNET_MESSENGER_Ego *ego) +{ + struct GNUNET_MESSENGER_MessageSignature signature; + + signature.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE); + signature.purpose.size = htonl (sizeof(signature)); + + GNUNET_memcpy(&(signature.hash), hash, sizeof(struct GNUNET_HashCode)); + + GNUNET_IDENTITY_sign(&(ego->priv), &signature, &(message->header.signature)); + GNUNET_memcpy(buffer, &(message->header.signature), sizeof(struct GNUNET_CRYPTO_EcdsaSignature)); +} + +int +verify_message (const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash, + const struct GNUNET_IDENTITY_PublicKey *key) +{ + struct GNUNET_MESSENGER_MessageSignature signature; + + signature.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE); + signature.purpose.size = htonl (sizeof(signature)); + + GNUNET_memcpy(&(signature.hash), hash, sizeof(struct GNUNET_HashCode)); + + return GNUNET_IDENTITY_signature_verify(GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE, &signature, + &(message->header.signature), key); +} + +int +encrypt_message (struct GNUNET_MESSENGER_Message *message, const struct GNUNET_IDENTITY_PublicKey *key) +{ + struct GNUNET_MESSENGER_ShortMessage shortened; + + fold_short_message (message, &shortened); + + const uint16_t length = get_short_message_size (&shortened); + + message->header.kind = GNUNET_MESSENGER_KIND_PRIVATE; + message->body.private.data = GNUNET_malloc(length); + + encode_short_message (&shortened, length, message->body.private.data); + + if (GNUNET_IDENTITY_encrypt (message->body.private.data, length, key, &(message->body.private.key), + message->body.private.data) + == length) + { + destroy_message_body (shortened.kind, &(shortened.body)); + return GNUNET_YES; + } + else + { + unfold_short_message (&shortened, message); + return GNUNET_NO; + } +} + +int +decrypt_message (struct GNUNET_MESSENGER_Message *message, const struct GNUNET_IDENTITY_PrivateKey *key) +{ + if (message->body.private.length != GNUNET_IDENTITY_decrypt (message->body.private.data, + message->body.private.length, key, + &(message->body.private.key), + message->body.private.data)) + return GNUNET_NO; + + struct GNUNET_MESSENGER_ShortMessage shortened; + + if (GNUNET_YES != decode_short_message (&shortened, message->body.private.length, message->body.private.data)) + return GNUNET_NO; + + unfold_short_message (&shortened, message); + return GNUNET_YES; +} + +struct GNUNET_MQ_Envelope* +pack_message (struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash, + const struct GNUNET_MESSENGER_Ego *ego, int mode) +{ + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Packing message: %u\n", message->header.kind); + + struct GNUNET_MessageHeader *header; + + uint16_t length = get_message_size (message); + + struct GNUNET_MQ_Envelope *env; + char *buffer; + + if (GNUNET_MESSENGER_PACK_MODE_ENVELOPE == mode) + { + env = GNUNET_MQ_msg_extra(header, length, GNUNET_MESSAGE_TYPE_CADET_CLI); + + buffer = (char*) &(header[1]); + } + else + { + env = NULL; + + buffer = GNUNET_malloc(length); + } + + encode_message (message, length, buffer); + + if (hash) + { + hash_message (length, buffer, hash); + + if (ego) + sign_message (message, length, buffer, hash, ego); + } + + if (GNUNET_MESSENGER_PACK_MODE_ENVELOPE != mode) + GNUNET_free(buffer); + + return env; +} diff --git a/src/messenger/messenger_api_message.h b/src/messenger/messenger_api_message.h new file mode 100644 index 000000000..0f0a97e9c --- /dev/null +++ b/src/messenger/messenger_api_message.h @@ -0,0 +1,190 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/messenger_api_message.h + * @brief messenger api: client and service implementation of GNUnet MESSENGER service + */ + +#ifndef GNUNET_MESSENGER_API_MESSAGE_H +#define GNUNET_MESSENGER_API_MESSAGE_H + +#include "platform.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_identity_service.h" +#include "gnunet_mq_lib.h" +#include "gnunet_signatures.h" + +#include "gnunet_messenger_service.h" + +#include "messenger_api_ego.h" + +/** + * Creates and allocates a new message with a specific kind. + * + * @param kind Kind of message + * @return New message + */ +struct GNUNET_MESSENGER_Message* +create_message (enum GNUNET_MESSENGER_MessageKind kind); + +/** + * Creates and allocates a copy of a given message. + * + * @param message Message + * @return New message + */ +struct GNUNET_MESSENGER_Message* +copy_message (const struct GNUNET_MESSENGER_Message *message); + +/** + * Destroys a message and frees its memory fully. + * + * @param message Message + */ +void +destroy_message (struct GNUNET_MESSENGER_Message *message); + +/** + * Returns the minimal size in bytes to encode a message of a specific kind. + * + * @param kind Kind of message + * @return Minimal size to encode + */ +uint16_t +get_message_kind_size (enum GNUNET_MESSENGER_MessageKind kind); + +/** + * Returns the exact size in bytes to encode a given message. + * + * @param message Message + * @return Size to encode + */ +uint16_t +get_message_size (const struct GNUNET_MESSENGER_Message *message); + +/** + * Encodes a given message into a buffer of a maximal length in bytes. + * + * @param message Message + * @param length Maximal length to encode + * @param[out] buffer Buffer + */ +void +encode_message (const struct GNUNET_MESSENGER_Message *message, uint16_t length, char *buffer); + +/** + * Decodes a message from a given buffer of a maximal length in bytes. + * + * If the buffer is too small for a message of its decoded kind the function fails with + * resulting GNUNET_NO after decoding only the messages header. + * + * On success the function returns GNUNET_YES. + * + * @param[out] message Message + * @param length Maximal length to decode + * @param buffer Buffer + * @return GNUNET_YES on success, otherwise GNUNET_NO + */ +int +decode_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, const char *buffer); + +/** + * Calculates a hash of a given buffer of a length in bytes. + * + * @param length Length of buffer + * @param buffer Buffer + * @param[out] hash Hash + */ +void +hash_message (uint16_t length, const char *buffer, struct GNUNET_HashCode *hash); + +/** + * Signs the hash of a message with a given ego and writes the signature + * into the buffer as well. + * + * @param[out] message Message + * @param length Length of buffer + * @param[out] buffer Buffer + * @param hash Hash of message + * @param ego EGO + */ +void +sign_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, char *buffer, + const struct GNUNET_HashCode *hash, const struct GNUNET_MESSENGER_Ego *ego); + +/** + * Verifies the signature of a given message and its hash with a specific + * public key. The function returns GNUNET_OK if the signature was valid, otherwise + * GNUNET_SYSERR. + * + * @param message Message + * @param hash Hash of message + * @param key Public key of EGO + * @return GNUNET_OK on success, otherwise GNUNET_SYSERR + */ +int +verify_message (const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash, + const struct GNUNET_IDENTITY_PublicKey *key); + +/** + * Encrypts a message using a given public key and replaces its body + * and kind with the now private encrypted message. The function returns + * GNUNET_YES if the operation succeeded, otherwise GNUNET_NO. + * + * @param message Message + * @param key Public key of EGO + * @return GNUNET_YES on success, otherwise GNUNET_NO + */ +int +encrypt_message (struct GNUNET_MESSENGER_Message *message, const struct GNUNET_IDENTITY_PublicKey *key); + +/** + * Decrypts a private message using a given private key and replaces its body + * and kind with the inner encrypted message. The function returns GNUNET_YES if the + * operation succeeded, otherwise GNUNET_NO. + * + * @param message Message + * @param key Private key of EGO + * @return GNUNET_YES on success, otherwise GNUNET_NO + */ +int +decrypt_message (struct GNUNET_MESSENGER_Message *message, const struct GNUNET_IDENTITY_PrivateKey *key); + +#define GNUNET_MESSENGER_PACK_MODE_ENVELOPE 0x1 +#define GNUNET_MESSENGER_PACK_MODE_UNKNOWN 0x0 + +/** + * Encodes the message to pack it into a newly allocated envelope if mode + * is equal to GNUNET_MESSENGER_PACK_MODE_ENVELOPE. Independent of the mode the message + * will be hashed if hash is not NULL and it will be signed if the ego is + * not NULL. + * + * @param[out] message Message + * @param[out] hash Hash of message + * @param ego EGO to sign + * @param mode Mode of packing + * @return Envelope or NULL + */ +struct GNUNET_MQ_Envelope* +pack_message (struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash, + const struct GNUNET_MESSENGER_Ego *ego, int mode); + +#endif //GNUNET_MESSENGER_API_MESSAGE_H diff --git a/src/messenger/messenger_api_room.c b/src/messenger/messenger_api_room.c new file mode 100644 index 000000000..5fedf1a78 --- /dev/null +++ b/src/messenger/messenger_api_room.c @@ -0,0 +1,189 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/messenger_api_room.c + * @brief messenger api: client implementation of GNUnet MESSENGER service + */ + +#include "messenger_api_room.h" + +#include "messenger_api_handle.h" + +struct GNUNET_MESSENGER_Room* +create_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key) +{ + struct GNUNET_MESSENGER_Room *room = GNUNET_new(struct GNUNET_MESSENGER_Room); + + room->handle = handle; + GNUNET_memcpy(&(room->key), key, sizeof(*key)); + + room->opened = GNUNET_NO; + room->contact_id = NULL; + + room->members = GNUNET_CONTAINER_multishortmap_create (8, GNUNET_NO); + + init_list_tunnels (&(room->entries)); + + room->messages = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + + return room; +} + +static int +iterate_destroy_message (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_Message *message = value; + + destroy_message (message); + + return GNUNET_YES; +} + +void +destroy_room (struct GNUNET_MESSENGER_Room *room) +{ + if (room->members) + GNUNET_CONTAINER_multishortmap_destroy (room->members); + + clear_list_tunnels (&(room->entries)); + + if (room->messages) + { + GNUNET_CONTAINER_multihashmap_iterate (room->messages, iterate_destroy_message, NULL); + + GNUNET_CONTAINER_multihashmap_destroy (room->messages); + } + + if (room->contact_id) + GNUNET_free(room->contact_id); + + GNUNET_free(room); +} + +const struct GNUNET_MESSENGER_Message* +get_room_message (const struct GNUNET_MESSENGER_Room *room, const struct GNUNET_HashCode *hash) +{ + return GNUNET_CONTAINER_multihashmap_get (room->messages, hash); +} + +static void +handle_join_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_Contact *contact = get_handle_contact_by_pubkey (room->handle, &(message->body.join.key)); + + if (contact) + GNUNET_CONTAINER_multishortmap_put (room->members, &(message->header.sender_id), contact, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); +} + +static void +handle_leave_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + GNUNET_CONTAINER_multishortmap_remove_all (room->members, &(message->header.sender_id)); +} + +static void +handle_name_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_Contact *contact = GNUNET_CONTAINER_multishortmap_get (room->members, + &(message->header.sender_id)); + + if (contact) + set_contact_name (contact, message->body.name.name); +} + +static void +handle_key_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_Contact *contact = GNUNET_CONTAINER_multishortmap_get (room->members, + &(message->header.sender_id)); + + if (contact) + swap_handle_contact_by_pubkey (room->handle, contact, &(message->body.key.key)); +} + +static void +handle_id_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_Contact *contact = GNUNET_CONTAINER_multishortmap_get (room->members, + &(message->header.sender_id)); + + if ((contact) && (GNUNET_OK + == GNUNET_CONTAINER_multishortmap_put (room->members, &(message->body.id.id), contact, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))) + GNUNET_CONTAINER_multishortmap_remove (room->members, &(message->header.sender_id), contact); +} + +static void +handle_miss_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + if ((room->contact_id) && (0 == GNUNET_memcmp(&(message->header.sender_id), room->contact_id))) + { + struct GNUNET_MESSENGER_ListTunnel *match = find_list_tunnels (&(room->entries), &(message->body.miss.peer), NULL); + + if (match) + remove_from_list_tunnels (&(room->entries), match); + } +} + +void +handle_room_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + if (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains (room->messages, hash)) + return; + + switch (message->header.kind) + { + case GNUNET_MESSENGER_KIND_JOIN: + handle_join_message (room, message, hash); + break; + case GNUNET_MESSENGER_KIND_LEAVE: + handle_leave_message (room, message, hash); + break; + case GNUNET_MESSENGER_KIND_NAME: + handle_name_message (room, message, hash); + break; + case GNUNET_MESSENGER_KIND_KEY: + handle_key_message (room, message, hash); + break; + case GNUNET_MESSENGER_KIND_ID: + handle_id_message (room, message, hash); + break; + case GNUNET_MESSENGER_KIND_MISS: + handle_miss_message (room, message, hash); + break; + default: + break; + } + + struct GNUNET_MESSENGER_Message *clone = copy_message (message); + + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (room->messages, hash, clone, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + destroy_message (clone); +} diff --git a/src/messenger/messenger_api_room.h b/src/messenger/messenger_api_room.h new file mode 100644 index 000000000..0038128d8 --- /dev/null +++ b/src/messenger/messenger_api_room.h @@ -0,0 +1,95 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Tobias Frisch + * @file src/messenger/messenger_api_room.h + * @brief messenger api: client implementation of GNUnet MESSENGER service + */ + +#ifndef GNUNET_MESSENGER_API_ROOM_H +#define GNUNET_MESSENGER_API_ROOM_H + +#include "platform.h" +#include "gnunet_container_lib.h" +#include "gnunet_crypto_lib.h" + +#include "gnunet_messenger_service.h" + +#include "messenger_api_list_tunnels.h" +#include "messenger_api_contact.h" +#include "messenger_api_message.h" + +struct GNUNET_MESSENGER_Room +{ + struct GNUNET_MESSENGER_Handle *handle; + struct GNUNET_HashCode key; + + int opened; + + struct GNUNET_ShortHashCode *contact_id; + + struct GNUNET_CONTAINER_MultiShortmap *members; + struct GNUNET_MESSENGER_ListTunnels entries; + + struct GNUNET_CONTAINER_MultiHashMap *messages; +}; + +/** + * Creates and allocates a new room for a handle with a given key for the client API. + * + * @param handle Handle + * @param key Key of room + * @return New room + */ +struct GNUNET_MESSENGER_Room* +create_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key); + +/** + * Destroys a room and frees its memory fully from the client API. + * + * @param room Room + */ +void +destroy_room (struct GNUNET_MESSENGER_Room *room); + +/** + * Returns a message locally stored from a map for a given hash in a room. If no matching + * message is found, NULL gets returned. + * + * @param room Room + * @param hash Hash of message + * @return Message or NULL + */ +const struct GNUNET_MESSENGER_Message* +get_room_message (const struct GNUNET_MESSENGER_Room *room, const struct GNUNET_HashCode *hash); + +/** + * Handles a message with a given hash in a room for the client API to update + * members and its information. The function also stores the message in map locally for access afterwards. + * + * @param room Room + * @param message Message + * @param hash Hash of message + */ +void +handle_room_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +#endif //GNUNET_MESSENGER_API_ROOM_H diff --git a/src/messenger/test_messenger.c b/src/messenger/test_messenger.c new file mode 100644 index 000000000..b42dfe6d9 --- /dev/null +++ b/src/messenger/test_messenger.c @@ -0,0 +1,187 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file messenger/test_messenger.c + * @author Tobias Frisch + * @brief Test for the messenger service using cadet API. + */ +#include +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_lib.h" +#include "gnunet_messenger_service.h" + +/** + * How long until we really give up on a particular testcase portion? + */ +#define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, \ + 60) + +/** + * How long until we give up on any particular operation (and retry)? + */ +#define BASE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +#define TESTER_NAME "tester" + +static int status = 1; + +static struct GNUNET_SCHEDULER_Task *die_task = NULL; +static struct GNUNET_SCHEDULER_Task *op_task = NULL; + +struct GNUNET_MESSENGER_Handle *messenger = NULL; + +static void +end (void *cls) +{ + die_task = NULL; + + if (op_task) + { + GNUNET_SCHEDULER_cancel (op_task); + op_task = NULL; + } + + if (messenger) + { + GNUNET_MESSENGER_disconnect(messenger); + messenger = NULL; + } + + status = 0; +} + + +static void +end_badly (void *cls) +{ + fprintf (stderr, "Testcase failed (timeout).\n"); + + end (NULL); + status = 1; +} + +static void +end_operation (void *cls) +{ + op_task = NULL; + + fprintf (stderr, "Testcase failed (operation: '%s').\n", cls? (const char*) cls : "unknown"); + + if (die_task) + GNUNET_SCHEDULER_cancel (die_task); + + end (NULL); + status = 1; +} + +static int identity_counter = 0; + +/** + * Function called when an identity is retrieved. + * + * @param cls Closure + * @param handle Handle of messenger service + */ +static void +on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle) +{ + if (op_task) + { + GNUNET_SCHEDULER_cancel (op_task); + op_task = NULL; + } + + const char* name = GNUNET_MESSENGER_get_name(handle); + + if (0 != strcmp(name, TESTER_NAME)) + { + op_task = GNUNET_SCHEDULER_add_now (&end_operation, "name"); + return; + } + + struct GNUNET_IDENTITY_Ego* ego = GNUNET_IDENTITY_ego_get_anonymous(); + struct GNUNET_IDENTITY_PublicKey anonymous_key; + + GNUNET_IDENTITY_ego_get_public_key(ego, &anonymous_key); + + const struct GNUNET_IDENTITY_PublicKey* key = GNUNET_MESSENGER_get_key(handle); + + if (((!identity_counter) && (0 != GNUNET_memcmp(key, (&anonymous_key)))) || + ((identity_counter) && (0 == GNUNET_memcmp(key, (&anonymous_key))))) + { + op_task = GNUNET_SCHEDULER_add_now (&end_operation, "key"); + return; + } + + if (identity_counter) { + GNUNET_MESSENGER_disconnect(handle); + + op_task = NULL; + messenger = NULL; + + if (die_task) + GNUNET_SCHEDULER_cancel (die_task); + + die_task = GNUNET_SCHEDULER_add_now (&end, NULL); + return; + } + + GNUNET_MESSENGER_update(messenger); + identity_counter++; +} + +/** + * Main function for testcase. + * + * @param cls Closure + * @param cfg Configuration + * @param peer Peer for testing + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + die_task = GNUNET_SCHEDULER_add_delayed (TOTAL_TIMEOUT, &end_badly, NULL); + + identity_counter = 0; + + op_task = GNUNET_SCHEDULER_add_delayed (BASE_TIMEOUT, &end_operation, "connect"); + messenger = GNUNET_MESSENGER_connect(cfg, TESTER_NAME, &on_identity, NULL, NULL, NULL); +} + +/** + * The main function. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main(int argc, char **argv) +{ + if (0 != GNUNET_TESTING_peer_run("test-messenger", + "test_messenger_api.conf", + &run, NULL)) + return 1; + + return status; +} diff --git a/src/messenger/test_messenger_anonymous.c b/src/messenger/test_messenger_anonymous.c new file mode 100644 index 000000000..e2057acc4 --- /dev/null +++ b/src/messenger/test_messenger_anonymous.c @@ -0,0 +1,179 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file messenger/test_messenger_anonymous.c + * @author Tobias Frisch + * @brief Test for the messenger service using cadet API. + */ +#include +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_lib.h" +#include "gnunet_messenger_service.h" + +/** + * How long until we really give up on a particular testcase portion? + */ +#define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, \ + 60) + +/** + * How long until we give up on any particular operation (and retry)? + */ +#define BASE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +static int status = 1; + +static struct GNUNET_SCHEDULER_Task *die_task = NULL; +static struct GNUNET_SCHEDULER_Task *op_task = NULL; + +struct GNUNET_MESSENGER_Handle *messenger = NULL; + +static void +end (void *cls) +{ + die_task = NULL; + + if (op_task) + { + GNUNET_SCHEDULER_cancel (op_task); + op_task = NULL; + } + + if (messenger) + { + GNUNET_MESSENGER_disconnect(messenger); + messenger = NULL; + } + + status = 0; +} + + +static void +end_badly (void *cls) +{ + fprintf (stderr, "Testcase failed (timeout).\n"); + + end (NULL); + status = 1; +} + +static void +end_operation (void *cls) +{ + op_task = NULL; + + fprintf (stderr, "Testcase failed (operation: '%s').\n", cls? (const char*) cls : "unknown"); + + if (die_task) + GNUNET_SCHEDULER_cancel (die_task); + + end (NULL); + status = 1; +} + +/** + * Function called when an identity is retrieved. + * + * @param cls Closure + * @param handle Handle of messenger service + */ +static void +on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle) +{ + if (op_task) + { + GNUNET_SCHEDULER_cancel (op_task); + op_task = NULL; + } + + const char* name = GNUNET_MESSENGER_get_name(handle); + + if (NULL != name) + { + op_task = GNUNET_SCHEDULER_add_now (&end_operation, "name-anonymous"); + return; + } + + if (GNUNET_SYSERR != GNUNET_MESSENGER_update(handle)) + { + op_task = GNUNET_SCHEDULER_add_now (&end_operation, "update-fail"); + return; + } + + struct GNUNET_IDENTITY_Ego* ego = GNUNET_IDENTITY_ego_get_anonymous(); + struct GNUNET_IDENTITY_PublicKey anonymous_key; + + GNUNET_IDENTITY_ego_get_public_key(ego, &anonymous_key); + + const struct GNUNET_IDENTITY_PublicKey* key = GNUNET_MESSENGER_get_key(handle); + + if (0 != GNUNET_memcmp(key, (&anonymous_key))) + { + op_task = GNUNET_SCHEDULER_add_now (&end_operation, "key-anonymous"); + return; + } + + GNUNET_MESSENGER_disconnect(handle); + + messenger = NULL; + + if (die_task) + GNUNET_SCHEDULER_cancel (die_task); + + die_task = GNUNET_SCHEDULER_add_now (&end, NULL); +} + +/** + * Main function for testcase. + * + * @param cls Closure + * @param cfg Configuration + * @param peer Peer for testing + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + die_task = GNUNET_SCHEDULER_add_delayed (TOTAL_TIMEOUT, &end_badly, NULL); + + op_task = GNUNET_SCHEDULER_add_delayed (BASE_TIMEOUT, &end_operation, "connect"); + messenger = GNUNET_MESSENGER_connect(cfg, NULL, &on_identity, NULL, NULL, NULL); +} + +/** + * The main function. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main(int argc, char **argv) +{ + if (0 != GNUNET_TESTING_peer_run("test-messenger", + "test_messenger_api.conf", + &run, NULL)) + return 1; + + return status; +} diff --git a/src/messenger/test_messenger_comm0.c b/src/messenger/test_messenger_comm0.c new file mode 100644 index 000000000..631b5b2c9 --- /dev/null +++ b/src/messenger/test_messenger_comm0.c @@ -0,0 +1,252 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file messenger/test_messenger_comm0.c + * @author Tobias Frisch + * @brief Test for the messenger service using cadet API. + */ +#include +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testbed_logger_service.h" +#include "gnunet_testbed_service.h" +#include "gnunet_testing_lib.h" +#include "gnunet_messenger_service.h" + +/** + * How long until we really give up on a particular testcase portion? + */ +#define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, \ + 60) + +/** + * How long until we give up on any particular operation (and retry)? + */ +#define BASE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +static int status = 1; + +static struct GNUNET_SCHEDULER_Task *die_task = NULL; +static struct GNUNET_SCHEDULER_Task *op_task = NULL; + +static void +end (void *cls) +{ + die_task = NULL; + + if (op_task) + { + GNUNET_SCHEDULER_cancel (op_task); + op_task = NULL; + } + + GNUNET_SCHEDULER_shutdown (); + status = 0; +} + + +static void +end_badly (void *cls) +{ + fprintf (stderr, "Testcase failed (timeout).\n"); + + end (NULL); + status = 1; +} + +static void +end_operation (void *cls) +{ + op_task = NULL; + + fprintf (stderr, "Testcase failed (operation: '%s').\n", cls? (const char*) cls : "unknown"); + + if (die_task) + GNUNET_SCHEDULER_cancel (die_task); + + end (NULL); + status = 1; +} + +static void +end_error (void *cls) +{ + op_task = NULL; + + fprintf (stderr, "Testcase failed (error: '%s').\n", cls? (const char*) cls : "unknown"); + GNUNET_free(cls); + + if (die_task) + GNUNET_SCHEDULER_cancel (die_task); + + end (NULL); + status = 1; +} + +/** + * Function called whenever a message is received or sent. + * + * @param cls Closure + * @param room Room + * @param message Message + * @param hash Hash of message + */ +static void +on_message (void *cls, const struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + // TODO +} + +/** + * Function called when an identity is retrieved. + * + * @param cls Closure + * @param handle Handle of messenger service + */ +static void +on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle) +{ + // TODO +} + +static void +on_peer (void *cb_cls, struct GNUNET_TESTBED_Operation *op, + const struct GNUNET_TESTBED_PeerInformation *pinfo, + const char *emsg) +{ + if (emsg) + { + op_task = GNUNET_SCHEDULER_add_now (&end_error, GNUNET_strdup(emsg)); + return; + } + + if (pinfo->pit != GNUNET_TESTBED_PIT_CONFIGURATION) + { + op_task = GNUNET_SCHEDULER_add_now (&end_operation, "config"); + return; + } + + struct GNUNET_MESSENGER_Handle *handle; + struct GNUNET_MESSENGER_Room *room; + + fprintf (stderr, "MSG: connect\n"); + + handle = GNUNET_MESSENGER_connect(pinfo->result.cfg, "tester", &on_identity, NULL, &on_message, NULL); + + struct GNUNET_HashCode hash; + GNUNET_CRYPTO_hash("test", 4, &hash); + + fprintf (stderr, "MSG: open\n"); + + room = GNUNET_MESSENGER_open_room(handle, &hash); + + fprintf (stderr, "MSG: close\n"); + + GNUNET_MESSENGER_close_room(room); + + fprintf (stderr, "MSG: disconnect\n"); + + GNUNET_MESSENGER_disconnect(handle); + + GNUNET_TESTBED_operation_done(op); + +} + +/** + * Main function for a peer of the testcase. + * + * @param cls Closure + * @param event Information about the event + */ +static void +run (void *cls, const struct GNUNET_TESTBED_EventInformation *event) +{ + if (GNUNET_TESTBED_ET_PEER_START != event->type) + { + op_task = GNUNET_SCHEDULER_add_now (&end_operation, "start"); + return; + } + + GNUNET_TESTBED_peer_get_information(event->details.peer_start.peer, + GNUNET_TESTBED_PIT_CONFIGURATION, + on_peer, event->details.peer_start.peer); + + fprintf (stderr, "MSG: barrier\n"); + + GNUNET_TESTBED_barrier_wait("exit", NULL, NULL); + + fprintf (stderr, "MSG: exit\n"); +} + +static void +exit_status (void *cls, const char *name, + struct GNUNET_TESTBED_Barrier *barrier, + enum GNUNET_TESTBED_BarrierStatus status, + const char *emsg) +{ + if (emsg) + { + op_task = GNUNET_SCHEDULER_add_now (&end_error, GNUNET_strdup(emsg)); + return; + } + + if (GNUNET_TESTBED_BARRIERSTATUS_ERROR == status) + { + op_task = GNUNET_SCHEDULER_add_now (&end_operation, "exit"); + return; + } + else if (GNUNET_TESTBED_BARRIERSTATUS_CROSSED == status) + GNUNET_SCHEDULER_add_now(&end, NULL); +} + +static void +init (void *cls, struct GNUNET_TESTBED_RunHandle *h, unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers, unsigned int links_succeeded, + unsigned int links_failed) +{ + die_task = GNUNET_SCHEDULER_add_delayed (TOTAL_TIMEOUT, &end_badly, NULL); + + struct GNUNET_TESTBED_Controller *controller; + + controller = GNUNET_TESTBED_run_get_controller_handle(h); + + GNUNET_TESTBED_barrier_init(controller, "exit", num_peers, exit_status, NULL); +} + +/** + * The main function. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main(int argc, char **argv) +{ + if (GNUNET_OK != GNUNET_TESTBED_test_run("test-messenger-comm0", + "test_messenger_api.conf", + 2, 0, + &run, NULL, + &init, NULL)) + return 1; + + return status; +} -- cgit v1.2.3