From 2925310d67483aca6e055e1ce0593c6463cd6c72 Mon Sep 17 00:00:00 2001 From: TheJackiMonster Date: Sun, 1 Nov 2020 22:57:28 +0100 Subject: -added core functionality of the messenger service Signed-off-by: TheJackiMonster formatting messenger code Signed-off-by: TheJackiMonster -completed core functionality of messenger service Signed-off-by: TheJackiMonster -code cleanup and reuse Signed-off-by: TheJackiMonster +added structure to memberships of rooms Signed-off-by: TheJackiMonster -implemented member permission checks and deletion of messages Signed-off-by: TheJackiMonster -moved solving requests out of updating last messages (also forward before update) Signed-off-by: TheJackiMonster -reduced complexity of permisson check and changed load/save of rooms Signed-off-by: TheJackiMonster -added save/load for accessed keys and basement peers of a room Signed-off-by: TheJackiMonster -implemented save/load for members with their history and session Signed-off-by: TheJackiMonster -abstracted management of egos and contacts Signed-off-by: TheJackiMonster -fix warning Signed-off-by: TheJackiMonster -abstracted management of members Signed-off-by: TheJackiMonster -fixed and adjusted test case Signed-off-by: TheJackiMonster -separated handling of direct and anonymous contacts Signed-off-by: TheJackiMonster -messenger added member-sessions which fix multiple edge cases, also additional cleanup Signed-off-by: TheJackiMonster -updated docs and fixed memory leak Signed-off-by: TheJackiMonster -changed info messages and added protocol version exchange Signed-off-by: TheJackiMonster -adjusted client api to use contacts from sessions Signed-off-by: TheJackiMonster -added more logging and fixed wrong session usage Signed-off-by: TheJackiMonster -adjusted comm0 test case and removed adding members from checking messages Signed-off-by: TheJackiMonster -fixed test-case for peer exchange Signed-off-by: TheJackiMonster -getting multiple peers connected in test-case with cadet Signed-off-by: TheJackiMonster -fixed wrong assert and added tunnel version check -simplified handling and forwarding Signed-off-by: TheJackiMonster -fixed merging last messages and cycling info messages Signed-off-by: TheJackiMonster -automated adding sessions and members Signed-off-by: TheJackiMonster -corrected use of identity keys and signatures Signed-off-by: TheJackiMonster -adding local joining on entering external room Signed-off-by: TheJackiMonster -fixed test-case comm0 Signed-off-by: TheJackiMonster -added more test-cases with generic setup Signed-off-by: TheJackiMonster -fixed multiple simultaneous channels blocking each other Signed-off-by: TheJackiMonster -making test-cases more precise Signed-off-by: TheJackiMonster -added check of members in test-cases, reduced merge messages Signed-off-by: TheJackiMonster -unified delayed operations: requests, deletions and merges Signed-off-by: TheJackiMonster -finished handling of operations Signed-off-by: TheJackiMonster -fixed member session historystorage, added request permission check and padding for transmission Signed-off-by: TheJackiMonster -improved padding and removed automatic recursive requests Signed-off-by: TheJackiMonster -implemented filter for sending messages and added private messages to API level Signed-off-by: TheJackiMonster -wrong setups fixed with proper ego lookups Signed-off-by: TheJackiMonster -fixed problem with anonymous ego and changed to discrete-level padding Signed-off-by: TheJackiMonster -added links to replace deleted messages, added local deletion and fixed anonymous id changing Signed-off-by: TheJackiMonster -added session completion and removal through completion process Signed-off-by: TheJackiMonster --- po/POTFILES.in | 2 + src/include/gnunet_messenger_service.h | 240 ++++- src/messenger/.gitignore | 10 + src/messenger/Makefile.am | 136 ++- src/messenger/gnunet-messenger.c | 132 ++- src/messenger/gnunet-service-messenger.c | 148 ++- src/messenger/gnunet-service-messenger.h | 20 +- src/messenger/gnunet-service-messenger_basement.c | 4 +- src/messenger/gnunet-service-messenger_basement.h | 24 +- src/messenger/gnunet-service-messenger_contact.c | 96 -- src/messenger/gnunet-service-messenger_contact.h | 112 -- src/messenger/gnunet-service-messenger_ego_store.c | 295 ++++++ src/messenger/gnunet-service-messenger_ego_store.h | 152 +++ src/messenger/gnunet-service-messenger_handle.c | 449 +++++--- src/messenger/gnunet-service-messenger_handle.h | 120 ++- .../gnunet-service-messenger_list_handles.c | 16 +- .../gnunet-service-messenger_list_handles.h | 42 +- .../gnunet-service-messenger_list_messages.c | 76 +- .../gnunet-service-messenger_list_messages.h | 42 +- src/messenger/gnunet-service-messenger_member.c | 379 +++++++ src/messenger/gnunet-service-messenger_member.h | 170 +++ .../gnunet-service-messenger_member_session.c | 707 +++++++++++++ .../gnunet-service-messenger_member_session.h | 275 +++++ .../gnunet-service-messenger_member_store.c | 250 +++++ .../gnunet-service-messenger_member_store.h | 151 +++ .../gnunet-service-messenger_message_handle.c | 108 +- .../gnunet-service-messenger_message_handle.h | 100 +- .../gnunet-service-messenger_message_kind.c | 69 +- .../gnunet-service-messenger_message_kind.h | 58 +- .../gnunet-service-messenger_message_recv.c | 206 ++-- .../gnunet-service-messenger_message_recv.h | 128 +-- .../gnunet-service-messenger_message_send.c | 89 +- .../gnunet-service-messenger_message_send.h | 121 +-- .../gnunet-service-messenger_message_store.c | 360 ++++++- .../gnunet-service-messenger_message_store.h | 92 +- src/messenger/gnunet-service-messenger_operation.c | 214 ++++ src/messenger/gnunet-service-messenger_operation.h | 129 +++ .../gnunet-service-messenger_operation_store.c | 224 ++++ .../gnunet-service-messenger_operation_store.h | 131 +++ src/messenger/gnunet-service-messenger_room.c | 1092 ++++++++++---------- src/messenger/gnunet-service-messenger_room.h | 265 ++--- src/messenger/gnunet-service-messenger_service.c | 306 +----- src/messenger/gnunet-service-messenger_service.h | 162 +-- src/messenger/gnunet-service-messenger_tunnel.c | 233 +++-- src/messenger/gnunet-service-messenger_tunnel.h | 104 +- src/messenger/gnunet-service-messenger_util.c | 64 -- src/messenger/gnunet-service-messenger_util.h | 53 - src/messenger/messenger_api.c | 264 +++-- src/messenger/messenger_api_contact.c | 44 +- src/messenger/messenger_api_contact.h | 45 +- src/messenger/messenger_api_contact_store.c | 182 ++++ src/messenger/messenger_api_contact_store.h | 122 +++ src/messenger/messenger_api_ego.h | 2 +- src/messenger/messenger_api_handle.c | 99 +- src/messenger/messenger_api_handle.h | 69 +- src/messenger/messenger_api_list_tunnels.c | 74 +- src/messenger/messenger_api_list_tunnels.h | 44 +- src/messenger/messenger_api_message.c | 423 ++++++-- src/messenger/messenger_api_message.h | 119 ++- src/messenger/messenger_api_room.c | 214 +++- src/messenger/messenger_api_room.h | 54 +- src/messenger/messenger_api_util.c | 84 ++ src/messenger/messenger_api_util.h | 64 ++ src/messenger/test_messenger.c | 40 +- src/messenger/test_messenger_adapt.c | 47 + src/messenger/test_messenger_anonymous.c | 34 +- src/messenger/test_messenger_api.conf | 32 +- src/messenger/test_messenger_async_client.c | 47 + src/messenger/test_messenger_async_p2p.c | 47 + src/messenger/test_messenger_comm0.c | 252 ----- src/messenger/test_messenger_growth.c | 47 + src/messenger/test_messenger_ring.c | 47 + src/messenger/test_messenger_server.c | 47 + src/messenger/test_messenger_sync_client.c | 47 + src/messenger/test_messenger_sync_p2p.c | 47 + src/messenger/test_messenger_worst_client.c | 47 + src/messenger/test_messenger_worst_p2p.c | 47 + src/messenger/testing_messenger_barrier.c | 170 +++ src/messenger/testing_messenger_barrier.h | 131 +++ src/messenger/testing_messenger_setup.c | 528 ++++++++++ src/messenger/testing_messenger_setup.h | 39 + 81 files changed, 8757 insertions(+), 3198 deletions(-) delete mode 100644 src/messenger/gnunet-service-messenger_contact.c delete mode 100644 src/messenger/gnunet-service-messenger_contact.h create mode 100644 src/messenger/gnunet-service-messenger_ego_store.c create mode 100644 src/messenger/gnunet-service-messenger_ego_store.h create mode 100644 src/messenger/gnunet-service-messenger_member.c create mode 100644 src/messenger/gnunet-service-messenger_member.h create mode 100644 src/messenger/gnunet-service-messenger_member_session.c create mode 100644 src/messenger/gnunet-service-messenger_member_session.h create mode 100644 src/messenger/gnunet-service-messenger_member_store.c create mode 100644 src/messenger/gnunet-service-messenger_member_store.h create mode 100644 src/messenger/gnunet-service-messenger_operation.c create mode 100644 src/messenger/gnunet-service-messenger_operation.h create mode 100644 src/messenger/gnunet-service-messenger_operation_store.c create mode 100644 src/messenger/gnunet-service-messenger_operation_store.h delete mode 100644 src/messenger/gnunet-service-messenger_util.c delete mode 100644 src/messenger/gnunet-service-messenger_util.h create mode 100644 src/messenger/messenger_api_contact_store.c create mode 100644 src/messenger/messenger_api_contact_store.h create mode 100644 src/messenger/messenger_api_util.c create mode 100644 src/messenger/messenger_api_util.h create mode 100644 src/messenger/test_messenger_adapt.c create mode 100644 src/messenger/test_messenger_async_client.c create mode 100644 src/messenger/test_messenger_async_p2p.c delete mode 100644 src/messenger/test_messenger_comm0.c create mode 100644 src/messenger/test_messenger_growth.c create mode 100644 src/messenger/test_messenger_ring.c create mode 100644 src/messenger/test_messenger_server.c create mode 100644 src/messenger/test_messenger_sync_client.c create mode 100644 src/messenger/test_messenger_sync_p2p.c create mode 100644 src/messenger/test_messenger_worst_client.c create mode 100644 src/messenger/test_messenger_worst_p2p.c create mode 100644 src/messenger/testing_messenger_barrier.c create mode 100644 src/messenger/testing_messenger_barrier.h create mode 100644 src/messenger/testing_messenger_setup.c create mode 100644 src/messenger/testing_messenger_setup.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 51ada43b3..b2fa88d73 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -550,5 +550,7 @@ src/vpn/vpn_api.c src/zonemaster/gnunet-service-zonemaster-monitor.c src/zonemaster/gnunet-service-zonemaster.c src/fs/fs_api.h +src/include/gnunet_identity_service.h +src/include/gnunet_messenger_service.h src/testbed/testbed_api.h src/testbed/testbed_api_operations.h diff --git a/src/include/gnunet_messenger_service.h b/src/include/gnunet_messenger_service.h index 389b6b8fd..99d4cf267 100644 --- a/src/include/gnunet_messenger_service.h +++ b/src/include/gnunet_messenger_service.h @@ -48,6 +48,14 @@ extern "C" { #include "gnunet_scheduler_lib.h" #include "gnunet_time_lib.h" +/** + * Version number of GNUnet Messenger API. + */ +#define GNUNET_MESSENGER_VERSION 0x00000001 + +/** + * Identifier of GNUnet MESSENGER Service. + */ #define GNUNET_MESSENGER_SERVICE_NAME "messenger" /** @@ -140,12 +148,19 @@ enum GNUNET_MESSENGER_MessageKind */ GNUNET_MESSENGER_KIND_PRIVATE = 14, + /** + * The delete kind. The message contains a #GNUNET_MESSENGER_MessageDelete body. + */ + GNUNET_MESSENGER_KIND_DELETE = 15, + /** * The unknown kind. The message contains an unknown body. */ GNUNET_MESSENGER_KIND_UNKNOWN = 0 }; +#define GNUNET_MESSENGER_KIND_MAX (GNUNET_MESSENGER_KIND_DELETE) + /** * Get the name of a message kind. * @@ -157,6 +172,9 @@ GNUNET_MESSENGER_name_of_kind (enum GNUNET_MESSENGER_MessageKind kind); /** * The header of a #GNUNET_MESSENGER_Message. + * This allows authentification of the sender, temporal ordering and finding potentially missed messages. + * + * Message-header-size: 40+ bytes */ struct GNUNET_MESSENGER_MessageHeader { @@ -188,6 +206,9 @@ struct GNUNET_MESSENGER_MessageHeader /** * An info message body. + * This allows ensuring member ids are unique and this first message can be verified. + * + * Message-body-size: 8+ bytes */ struct GNUNET_MESSENGER_MessageInfo { @@ -197,13 +218,16 @@ struct GNUNET_MESSENGER_MessageInfo struct GNUNET_IDENTITY_PublicKey host_key; /** - * The new unique id for the receiver in a room. + * The version of GNUnet Messenger API. */ - struct GNUNET_ShortHashCode unique_id; + uint32_t messenger_version; }; /** * A join message body. + * This allows informing others about joining the room with a given key pair. + * + * Message-body-size: 4+ bytes */ struct GNUNET_MESSENGER_MessageJoin { @@ -215,6 +239,9 @@ struct GNUNET_MESSENGER_MessageJoin /** * A leave message body. + * This allows informing others about leaving the room. + * + * Message-body-size: 0 bytes */ struct GNUNET_MESSENGER_MessageLeave { @@ -222,6 +249,9 @@ struct GNUNET_MESSENGER_MessageLeave /** * A name message body. + * This allows replacing the current name with another one. + * + * Message-body-size: 0+ bytes */ struct GNUNET_MESSENGER_MessageName { @@ -233,6 +263,9 @@ struct GNUNET_MESSENGER_MessageName /** * A key message body. + * This allows replacing the current key pair with another one. + * + * Message-body-size: 4+ bytes */ struct GNUNET_MESSENGER_MessageKey { @@ -244,6 +277,9 @@ struct GNUNET_MESSENGER_MessageKey /** * A peer message body. + * This allows informing others to open a peer as a door to the current room. + * + * Message-body-size: 32 bytes */ struct GNUNET_MESSENGER_MessagePeer { @@ -255,6 +291,9 @@ struct GNUNET_MESSENGER_MessagePeer /** * An id message body. + * This allows replacing the member id with a newly unique generated one. + * + * Message-body-size: 8 bytes */ struct GNUNET_MESSENGER_MessageId { @@ -266,6 +305,9 @@ struct GNUNET_MESSENGER_MessageId /** * A miss message body. + * This allows informing others about a disconnection of any door. + * + * Message-body-size: 32 bytes */ struct GNUNET_MESSENGER_MessageMiss { @@ -277,6 +319,9 @@ struct GNUNET_MESSENGER_MessageMiss /** * A merge message body. + * This allows merging message history branches together. + * + * Message-body-size: 16 bytes */ struct GNUNET_MESSENGER_MessageMerge { @@ -288,6 +333,9 @@ struct GNUNET_MESSENGER_MessageMerge /** * A request message body. + * This allows requesting the content of a specific message which is currently missing. + * + * Message-body-size: 16 bytes */ struct GNUNET_MESSENGER_MessageRequest { @@ -299,6 +347,9 @@ struct GNUNET_MESSENGER_MessageRequest /** * An invite message body. + * This allows sharing information about other rooms in form of an invitation. + * + * Message-body-size: 48 bytes */ struct GNUNET_MESSENGER_MessageInvite { @@ -315,6 +366,9 @@ struct GNUNET_MESSENGER_MessageInvite /** * A text message body. + * This allows general communication in text form. + * + * Message-body-size: 0+ bytes */ struct GNUNET_MESSENGER_MessageText { @@ -326,6 +380,9 @@ struct GNUNET_MESSENGER_MessageText /** * A file message body. + * This allows sending necessary details about an uploaded encrypted file to allow access to it. + * + * Message-body-size: 335+ bytes */ struct GNUNET_MESSENGER_MessageFile { @@ -352,6 +409,9 @@ struct GNUNET_MESSENGER_MessageFile /** * A private message body. + * This allows to encapsulate any message to be encrypted for only one specific member to receive in a room. + * + * Message-body-size: 32+ bytes */ struct GNUNET_MESSENGER_MessagePrivate { @@ -371,6 +431,25 @@ struct GNUNET_MESSENGER_MessagePrivate char *data; }; +/** + * A delete message body + * This allows deletion of an own previous message with any custom automatic delay. + * + * Message-body-size: 24 bytes + */ +struct GNUNET_MESSENGER_MessageDelete +{ + /** + * The hash of the message to delete. + */ + struct GNUNET_HashCode hash; + + /** + * The delay of the delete operation to get processed. + */ + struct GNUNET_TIME_RelativeNBO delay; +}; + /** * The unified body of a #GNUNET_MESSENGER_Message. */ @@ -392,6 +471,7 @@ struct GNUNET_MESSENGER_MessageBody struct GNUNET_MESSENGER_MessageText text; struct GNUNET_MESSENGER_MessageFile file; struct GNUNET_MESSENGER_MessagePrivate private; + struct GNUNET_MESSENGER_MessageDelete delete; }; }; @@ -411,12 +491,28 @@ struct GNUNET_MESSENGER_Message struct GNUNET_MESSENGER_MessageBody body; }; +/** + * Enum for the different supported flags used by message handling + */ +enum GNUNET_MESSENGER_MessageFlags +{ + /** + * The none flag. The flag indicates that the message is not affected by any special context. + */ + GNUNET_MESSENGER_FLAG_NONE = 0, + + /** + * The private flag. The flag indicates that the message was privately encrypted. + */ + GNUNET_MESSENGER_FLAG_PRIVATE = 1, +}; + /** * Method called whenever the EGO of a handle changes or if the first connection fails * to load a valid EGO and the anonymous key pair will be used instead. * - * @param cls Closure from GNUNET_MESSENGER_connect - * @param handle Messenger handle + * @param[in/out] cls Closure from #GNUNET_MESSENGER_connect + * @param[in/out] handle Messenger handle */ typedef void (*GNUNET_MESSENGER_IdentityCallback) (void *cls, struct GNUNET_MESSENGER_Handle *handle); @@ -424,25 +520,45 @@ typedef void /** * Method called whenever a message is sent or received from a room. * - * @param cls Closure from GNUNET_MESSENGER_connect - * @param room Room handle - * @param message Newly received or sent message - * @param hash Hash identifying the message + * The flag private_message will be #GNUNET_YES if a message was + * received privately, otherwise #GNUNET_NO. + * + * @param[in/out] cls Closure from #GNUNET_MESSENGER_connect + * @param[in] room Room handle + * @param[in] sender Sender of message + * @param[in] message Newly received or sent message + * @param[in] hash Hash identifying the message + * @param[in] flags Flags of the message */ typedef void -(*GNUNET_MESSENGER_MessageCallback) (void *cls, const struct GNUNET_MESSENGER_Room *room, - const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); +(*GNUNET_MESSENGER_MessageCallback) (void *cls, struct GNUNET_MESSENGER_Room *room, + const struct GNUNET_MESSENGER_Contact *sender, + const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash, + enum GNUNET_MESSENGER_MessageFlags flags); + +/** + * Method called for each member in a room during iteration. If the method returns + * #GNUNET_YES the iteration continues, otherwise it will quit the iteration. + * + * @param[in/out] cls Closure from #GNUNET_MESSENGER_iterate_members + * @param[in] room Room handle + * @param[in] contact Contact handle + */ +typedef int +(*GNUNET_MESSENGER_MemberCallback) (void* cls, struct GNUNET_MESSENGER_Room *room, + const struct GNUNET_MESSENGER_Contact *contact); /** * Set up a handle for the messenger related functions and connects to all necessary services. It will look up the ego * key identified by its name and use it for signing all messages from the handle. * - * @param cfg Configuration to use - * @param name Name to look up an ego or NULL to stay anonymous - * @param identity_callback Function called when the EGO of the handle changes - * @param identity_cls Closure for the identity_callback handler - * @param msg_callback Function called when a new message is sent or received - * @param msg_cls Closure for the msg_callback handler + * @param[in] cfg Configuration to use + * @param[in] name Name to look up an ego or NULL to stay anonymous + * @param[in] identity_callback Function called when the EGO of the handle changes + * @param[in/out] identity_cls Closure for the identity_callback handler + * @param[in] msg_callback Function called when a new message is sent or received + * @param[in/out] msg_cls Closure for the msg_callback handler * @return Messenger handle to use, NULL on error */ struct GNUNET_MESSENGER_Handle* @@ -458,8 +574,8 @@ GNUNET_MESSENGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, const c * Keep in mind that this will fully delete the old ego key (if any is used) even if any other service wants to use it * as default. * - * @param handle Messenger handle to use - * @return GNUNET_OK on success, GNUNET_SYSERR on failure + * @param[in/out] handle Messenger handle to use + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ int GNUNET_MESSENGER_update (struct GNUNET_MESSENGER_Handle *handle); @@ -467,7 +583,7 @@ GNUNET_MESSENGER_update (struct GNUNET_MESSENGER_Handle *handle); /** * Disconnect all of the messengers used services and clears up its used memory. * - * @param handle Messenger handle to use + * @param[in/out] handle Messenger handle to use */ void GNUNET_MESSENGER_disconnect (struct GNUNET_MESSENGER_Handle *handle); @@ -475,7 +591,7 @@ GNUNET_MESSENGER_disconnect (struct GNUNET_MESSENGER_Handle *handle); /** * Get the name (if specified, otherwise NULL) used by the messenger. * - * @param handle Messenger handle to use + * @param[in] handle Messenger handle to use * @return Name used by the messenger or NULL */ const char* @@ -483,21 +599,21 @@ GNUNET_MESSENGER_get_name (const struct GNUNET_MESSENGER_Handle *handle); /** * Set the name for the messenger. This will rename the currently used ego and move all stored files related to the current - * name to its new directory. If anything fails during this process the function returns GNUNET_NO and the name for + * name to its new directory. If anything fails during this process the function returns #GNUNET_NO and the name for * the messenger won't change as specified. * - * @param handle Messenger handle to use - * @param name Name for the messenger to change to - * @return GNUNET_YES on success, GNUNET_NO on failure and GNUNET_SYSERR if handle is NULL + * @param[in/out] handle Messenger handle to use + * @param[in] name Name for the messenger to change to + * @return #GNUNET_YES on success, #GNUNET_NO on failure and #GNUNET_SYSERR if handle is NULL */ int GNUNET_MESSENGER_set_name (struct GNUNET_MESSENGER_Handle *handle, const char *name); /** - * Get the public key used by the messenger. + * Get the public key used by the messenger or NULL if the anonymous key was used. * - * @param handle Messenger handle to use - * @return Used ego's public key + * @param[in] handle Messenger handle to use + * @return Used ego's public key or NULL */ const struct GNUNET_IDENTITY_PublicKey* GNUNET_MESSENGER_get_key (const struct GNUNET_MESSENGER_Handle *handle); @@ -509,38 +625,38 @@ GNUNET_MESSENGER_get_key (const struct GNUNET_MESSENGER_Handle *handle); * Notice that there can only be one room related to a specific key. So trying to open two rooms with the same * key will result in opening the room once but returning the handle both times because the room stays open. * - * You can also open a room after entering it through a door using GNUNET_MESSENGER_entry_room(...). This + * You can also open a room after entering it through a door using #GNUNET_MESSENGER_enter_room. This * will notify all entered doors to list you as new door. * * ( All doors form a ring structured network to shorten the latency sending and receiving messages. ) * - * @param handle Messenger handle to use - * @param key Hash identifying the port + * @param[in/out] handle Messenger handle to use + * @param[in] key Hash identifying the port * @return Room handle, NULL on error */ struct GNUNET_MESSENGER_Room* GNUNET_MESSENGER_open_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key); /** - * Enter a room to send and receive messages through a door opened using GNUNET_MESSENGER_open_room(...). + * Enter a room to send and receive messages through a door opened using #GNUNET_MESSENGER_open_room. * * Notice that there can only be one room related to a specific key. So trying to enter two rooms with the same * key will result in entering the room once but returning the handle both times because the room stays entered. * You can however enter a room through multiple doors in parallel which results in connecting both ends. But * entering the room through the same door won't have any effect after the first time. * - * You can also enter a room through a door after opening it using GNUNET_MESSENGER_open_room(...). But the + * You can also enter a room through a door after opening it using #GNUNET_MESSENGER_open_room. But the * door may not be your own peer identity. * * ( All doors form a ring structured network to shorten the latency sending and receiving messages. ) * - * @param handle Messenger handle to use - * @param door Peer identity of an open door - * @param key Hash identifying the port + * @param[in/out] handle Messenger handle to use + * @param[in] door Peer identity of an open door + * @param[in] key Hash identifying the port * @return Room handle, NULL on error */ struct GNUNET_MESSENGER_Room* -GNUNET_MESSENGER_entry_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_PeerIdentity *door, +GNUNET_MESSENGER_enter_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_PeerIdentity *door, const struct GNUNET_HashCode *key); /** @@ -550,37 +666,37 @@ GNUNET_MESSENGER_entry_room (struct GNUNET_MESSENGER_Handle *handle, const struc * ( After a member closes a door, all members entered through that specific door have to use another one * or open the room on their own. ) * - * @param room Room handle + * @param[in/out] room Room handle */ void GNUNET_MESSENGER_close_room (struct GNUNET_MESSENGER_Room *room); /** - * Get the contact of a member in a room identified by their id. + * Get the contact of a member in a room which sent a specific message identified with a given hash. * * Notice that contacts are independent of rooms but will be removed if all rooms containing these contacts get closed. * - * @param room Room handle - * @param id Hash identifying a member - * @return Contact handle, NULL if id is not in use + * @param[in] room Room handle + * @param[in] hash Hash identifying a message + * @return Contact handle, NULL otherwise */ struct GNUNET_MESSENGER_Contact* -GNUNET_MESSENGER_get_member (const struct GNUNET_MESSENGER_Room *room, const struct GNUNET_ShortHashCode *id); +GNUNET_MESSENGER_get_sender (const struct GNUNET_MESSENGER_Room *room, const struct GNUNET_HashCode *hash); /** * Get the name used by the contact. * - * @param contact Contact handle + * @param[in] contact Contact handle * @return Name of contact or NULL */ const char* GNUNET_MESSENGER_contact_get_name (const struct GNUNET_MESSENGER_Contact *contact); /** - * Get the public key used by the contact. + * Get the public key used by the contact or NULL if the anonymous key was used. * - * @param contact Contact handle - * @return Public key of the ego used by contact + * @param[in] contact Contact handle + * @return Public key of the ego used by contact or NULL */ const struct GNUNET_IDENTITY_PublicKey* GNUNET_MESSENGER_contact_get_key (const struct GNUNET_MESSENGER_Contact *contact); @@ -593,22 +709,44 @@ GNUNET_MESSENGER_contact_get_key (const struct GNUNET_MESSENGER_Contact *contact * * Notice that all messages sent and received are also stored and can be propagated to new members entering the room. * - * @param room Room handle - * @param message New message to send + * If you provide a specific contact as receiver of the given message, the message will automatically be + * encrypted and sent as a private message (see #GNUNET_MESSENGER_MessagePrivate). Therefore the selected contact + * will be the only member receiving the actual message. + * + * Sending a message to all members in a given room can be done by providing NULL as contact. + * + * @param[in/out] room Room handle + * @param[in] message New message to send + * @param[in] contact Contact or NULL */ void -GNUNET_MESSENGER_send_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message); +GNUNET_MESSENGER_send_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_MESSENGER_Contact* contact); /** * Get the message in a room identified by its hash. * - * @param room Room handle - * @param hash Hash identifying a message + * @param[in] room Room handle + * @param[in] hash Hash identifying a message * @return Message struct or NULL if no message with that hash is known */ const struct GNUNET_MESSENGER_Message* GNUNET_MESSENGER_get_message (const struct GNUNET_MESSENGER_Room *room, const struct GNUNET_HashCode *hash); +/** + * Iterates through all members of a given room and calls a selected callback + * for each of them with a provided closure. The callback will receive the contact of each + * member. The function returns the amount of members iterated with the given callback. + * + * @param[in] room Room handle + * @param[in] callback Function called for each member + * @param[in] cls Closure for the callback handler + * @return Amount of members iterated + */ +int +GNUNET_MESSENGER_iterate_members (struct GNUNET_MESSENGER_Room *room, GNUNET_MESSENGER_MemberCallback callback, + void* cls); + #if 0 /* keep Emacsens' auto-indent happy */ { #endif diff --git a/src/messenger/.gitignore b/src/messenger/.gitignore index 9de3fb304..ed78c5562 100644 --- a/src/messenger/.gitignore +++ b/src/messenger/.gitignore @@ -2,3 +2,13 @@ gnunet-service-messenger gnunet-messenger test_messenger_api test_messenger_anonymous +test_messenger_sync_client +test_messenger_async_client +test_messenger_worst_client +test_messenger_sync_p2p +test_messenger_async_p2p +test_messenger_worst_p2p +test_messenger_server +test_messenger_growth +test_messenger_ring +test_messenger_adapt diff --git a/src/messenger/Makefile.am b/src/messenger/Makefile.am index d9694420b..1ebfbe5ed 100644 --- a/src/messenger/Makefile.am +++ b/src/messenger/Makefile.am @@ -31,10 +31,14 @@ lib_LTLIBRARIES = \ libgnunetmessenger_common_la_SOURCES = \ messenger_api_ego.h \ + messenger_api_contact.c messenger_api_contact.h \ + messenger_api_contact_store.c messenger_api_contact_store.h \ messenger_api_message.c messenger_api_message.h \ - messenger_api_list_tunnels.c messenger_api_list_tunnels.h + messenger_api_list_tunnels.c messenger_api_list_tunnels.h \ + messenger_api_util.c messenger_api_util.h libgnunetmessenger_common_la_LIBADD = \ $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/cadet/libgnunetcadet.la \ $(top_builddir)/src/identity/libgnunetidentity.la \ $(XLIB) \ $(LTLIBINTL) @@ -44,7 +48,6 @@ libgnunetmessenger_common_la_LDFLAGS = \ 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 = \ @@ -72,17 +75,21 @@ gnunet_service_messenger_SOURCES = \ 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_member_session.c gnunet-service-messenger_member_session.h \ + gnunet-service-messenger_member.c gnunet-service-messenger_member.h \ + gnunet-service-messenger_member_store.c gnunet-service-messenger_member_store.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_operation_store.c gnunet-service-messenger_operation_store.h \ + gnunet-service-messenger_operation.c gnunet-service-messenger_operation.h \ gnunet-service-messenger_basement.c gnunet-service-messenger_basement.h \ - gnunet-service-messenger_contact.c gnunet-service-messenger_contact.h \ + gnunet-service-messenger_ego_store.c gnunet-service-messenger_ego_store.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_tunnel.c gnunet-service-messenger_tunnel.h gnunet_service_messenger_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(top_builddir)/src/cadet/libgnunetcadet.la \ @@ -94,7 +101,16 @@ gnunet_service_messenger_LDADD = \ check_PROGRAMS = \ test_messenger_api \ test_messenger_anonymous \ - test_messenger_comm0 + test_messenger_sync_client \ + test_messenger_async_client \ + test_messenger_worst_client \ + test_messenger_sync_p2p \ + test_messenger_async_p2p \ + test_messenger_worst_p2p \ + test_messenger_server \ + test_messenger_growth \ + test_messenger_ring \ + test_messenger_adapt 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; @@ -118,13 +134,113 @@ test_messenger_anonymous_LDADD = \ $(top_builddir)/src/testing/libgnunettesting.la \ $(top_builddir)/src/util/libgnunetutil.la -test_messenger_comm0_SOURCES = \ - test_messenger_comm0.c -test_messenger_comm0_LDADD = \ +test_messenger_sync_client_SOURCES = \ + test_messenger_sync_client.c \ + testing_messenger_barrier.c testing_messenger_barrier.h \ + testing_messenger_setup.c testing_messenger_setup.h +test_messenger_sync_client_LDADD = \ + libgnunetmessenger_common.la \ + libgnunetmessenger.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_messenger_async_client_SOURCES = \ + test_messenger_async_client.c \ + testing_messenger_barrier.c testing_messenger_barrier.h \ + testing_messenger_setup.c testing_messenger_setup.h +test_messenger_async_client_LDADD = \ + libgnunetmessenger_common.la \ + libgnunetmessenger.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_messenger_worst_client_SOURCES = \ + test_messenger_worst_client.c \ + testing_messenger_barrier.c testing_messenger_barrier.h \ + testing_messenger_setup.c testing_messenger_setup.h +test_messenger_worst_client_LDADD = \ + libgnunetmessenger_common.la \ + libgnunetmessenger.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_messenger_sync_p2p_SOURCES = \ + test_messenger_sync_p2p.c \ + testing_messenger_barrier.c testing_messenger_barrier.h \ + testing_messenger_setup.c testing_messenger_setup.h +test_messenger_sync_p2p_LDADD = \ + libgnunetmessenger_common.la \ + libgnunetmessenger.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_messenger_async_p2p_SOURCES = \ + test_messenger_async_p2p.c \ + testing_messenger_barrier.c testing_messenger_barrier.h \ + testing_messenger_setup.c testing_messenger_setup.h +test_messenger_async_p2p_LDADD = \ + libgnunetmessenger_common.la \ + libgnunetmessenger.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_messenger_worst_p2p_SOURCES = \ + test_messenger_worst_p2p.c \ + testing_messenger_barrier.c testing_messenger_barrier.h \ + testing_messenger_setup.c testing_messenger_setup.h +test_messenger_worst_p2p_LDADD = \ + libgnunetmessenger_common.la \ + libgnunetmessenger.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_messenger_server_SOURCES = \ + test_messenger_server.c \ + testing_messenger_barrier.c testing_messenger_barrier.h \ + testing_messenger_setup.c testing_messenger_setup.h +test_messenger_server_LDADD = \ + libgnunetmessenger_common.la \ + libgnunetmessenger.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_messenger_growth_SOURCES = \ + test_messenger_growth.c \ + testing_messenger_barrier.c testing_messenger_barrier.h \ + testing_messenger_setup.c testing_messenger_setup.h +test_messenger_growth_LDADD = \ + libgnunetmessenger_common.la \ + libgnunetmessenger.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_messenger_ring_SOURCES = \ + test_messenger_ring.c \ + testing_messenger_barrier.c testing_messenger_barrier.h \ + testing_messenger_setup.c testing_messenger_setup.h +test_messenger_ring_LDADD = \ + libgnunetmessenger_common.la \ + libgnunetmessenger.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_messenger_adapt_SOURCES = \ + test_messenger_adapt.c \ + testing_messenger_barrier.c testing_messenger_barrier.h \ + testing_messenger_setup.c testing_messenger_setup.h +test_messenger_adapt_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 diff --git a/src/messenger/gnunet-messenger.c b/src/messenger/gnunet-messenger.c index 579e5c3ad..737bb83c8 100644 --- a/src/messenger/gnunet-messenger.c +++ b/src/messenger/gnunet-messenger.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -34,29 +34,38 @@ 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 + * @param[in/out] cls Closure + * @param[in] room Room + * @param[in] sender Sender of message + * @param[in] message Message + * @param[in] hash Hash of message + * @param[in] flags Flags of message */ void -on_message (void *cls, const struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, - const struct GNUNET_HashCode *hash) +on_message (void *cls, struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Contact *sender, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash, + enum GNUNET_MESSENGER_MessageFlags flags) { - 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"; + printf ("[%s] ", GNUNET_sh2s(&(message->header.sender_id))); + + if (flags & GNUNET_MESSENGER_FLAG_PRIVATE) + printf ("*"); + 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]); + printf ("* '%s' joined the room!\n", sender_name); + break; + } + case GNUNET_MESSENGER_KIND_NAME: + { + printf ("* '%s' gets renamed to '%s'\n", sender_name, message->body.name.name); break; } case GNUNET_MESSENGER_KIND_LEAVE: @@ -76,6 +85,7 @@ on_message (void *cls, const struct GNUNET_MESSENGER_Room *room, const struct GN } default: { + printf ("~ message: %s\n", GNUNET_MESSENGER_name_of_kind(message->header.kind)); break; } } @@ -86,7 +96,7 @@ struct GNUNET_SCHEDULER_Task *read_task; /** * Task to shut down this application. * - * @param cls Closure + * @param[in/out] cls Closure */ static void shutdown_hook (void *cls) @@ -108,10 +118,24 @@ listen_stdio (void *cls); #define MAX_BUFFER_SIZE 60000 +static int +iterate_send_private_message (void *cls, struct GNUNET_MESSENGER_Room *room, + const struct GNUNET_MESSENGER_Contact *contact) +{ + struct GNUNET_MESSENGER_Message *message = cls; + + if (GNUNET_MESSENGER_contact_get_key(contact)) + GNUNET_MESSENGER_send_message (room, message, contact); + + return GNUNET_YES; +} + +int private_mode; + /** * Task run in stdio mode, after some data is available at stdin. * - * @param cls Closure + * @param[in/out] cls Closure */ static void read_stdio (void *cls) @@ -140,7 +164,10 @@ read_stdio (void *cls) message.header.kind = GNUNET_MESSENGER_KIND_TEXT; message.body.text.text = buffer; - GNUNET_MESSENGER_send_message (room, &message); + if (GNUNET_YES == private_mode) + GNUNET_MESSENGER_iterate_members(room, iterate_send_private_message, &message); + else + GNUNET_MESSENGER_send_message (room, &message, NULL); read_task = GNUNET_SCHEDULER_add_now (listen_stdio, cls); } @@ -148,7 +175,7 @@ read_stdio (void *cls) /** * Wait for input on STDIO and send it out over the #ch. * - * @param cls Closure + * @param[in/out] cls Closure */ static void listen_stdio (void *cls) @@ -160,10 +187,8 @@ listen_stdio (void *cls) 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_TIME_UNIT_FOREVER_REL, rs, + NULL, &read_stdio, cls); GNUNET_NETWORK_fdset_destroy (rs); } @@ -171,7 +196,7 @@ listen_stdio (void *cls) /** * Initial task to startup application. * - * @param cls Closure + * @param[in/out] cls Closure */ static void idle (void *cls) @@ -192,8 +217,8 @@ struct GNUNET_SCHEDULER_Task *shutdown_task; /** * Function called when an identity is retrieved. * - * @param cls Closure - * @param handle Handle of messenger service + * @param[in/out] cls Closure + * @param[in/out] handle Handle of messenger service */ static void on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle) @@ -230,7 +255,7 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle) { printf ("* You try to entry a room...\n"); - room = GNUNET_MESSENGER_entry_room (messenger, door, &key); + room = GNUNET_MESSENGER_enter_room (messenger, door, &key); } else { @@ -246,17 +271,26 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle) if (!room) GNUNET_SCHEDULER_shutdown (); else + { + struct GNUNET_MESSENGER_Message message; + message.header.kind = GNUNET_MESSENGER_KIND_NAME; + message.body.name.name = GNUNET_strdup(name); + + GNUNET_MESSENGER_send_message (room, &message, NULL); + GNUNET_free(message.body.name.name); + 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 + * @param[in/out] cls closure + * @param[in] args remaining command-line arguments + * @param[in] cfgfile name of the configuration file used (for saving, can be NULL!) + * @param[in] cfg configuration */ static void run (void *cls, char *const*args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) @@ -269,38 +303,24 @@ run (void *cls, char *const*args, const char *cfgfile, const struct GNUNET_CONFI /** * 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 + * @param[in] argc number of arguments from the command line + * @param[in] argv command line arguments + * @return #EXIT_SUCCESS ok, #EXIT_FAILURE 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); + 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_flag ('p', "private", "flag to enable private mode", &private_mode), + 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 index 2c92305c4..187b65ed5 100644 --- a/src/messenger/gnunet-service-messenger.c +++ b/src/messenger/gnunet-service-messenger.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -52,7 +52,7 @@ handle_create (void *cls, const struct GNUNET_MESSENGER_CreateMessage *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); + setup_handle_name (msg_client->handle, strlen (name) > 0 ? name : NULL); GNUNET_SERVICE_client_continue (msg_client->client); } @@ -62,8 +62,7 @@ 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"); + update_handle (msg_client->handle); GNUNET_SERVICE_client_continue (msg_client->client); } @@ -92,8 +91,7 @@ handle_set_name (void *cls, const struct GNUNET_MESSENGER_NameMessage *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); + set_handle_name (msg_client->handle, name); GNUNET_SERVICE_client_continue (msg_client->client); } @@ -103,15 +101,13 @@ 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))); + 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)); + 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)); + 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; @@ -121,8 +117,7 @@ handle_room_open (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg) 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_log(GNUNET_ERROR_TYPE_ERROR, "Opening room failed: %s\n", GNUNET_h2s (&(msg->key))); GNUNET_SERVICE_client_continue (msg_client->client); } @@ -132,15 +127,13 @@ 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))); + 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)); + 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)); + 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; @@ -151,8 +144,8 @@ handle_room_entry (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg) 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_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); } @@ -166,10 +159,9 @@ handle_room_close (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg) 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)); + 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)); + 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; @@ -187,17 +179,40 @@ handle_room_close (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg) 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); + const uint16_t full_length = ntohs (msg->header.size); - if (full_length < sizeof(msg->key)) + if (full_length < sizeof(*msg)) return GNUNET_NO; - const uint16_t length = full_length - sizeof(msg->key); + const enum GNUNET_MESSENGER_MessageFlags flags = ( + (enum GNUNET_MESSENGER_MessageFlags) (msg->flags) + ); + + const uint16_t length = full_length - sizeof(*msg); const char *buffer = ((const char*) msg) + sizeof(*msg); + uint16_t key_length = 0; + + if (!(flags & GNUNET_MESSENGER_FLAG_PRIVATE)) + goto check_for_message; + + struct GNUNET_IDENTITY_PublicKey public_key; + + key_length = GNUNET_IDENTITY_read_key_from_buffer(&public_key, buffer, length); + +check_for_message: + if (key_length < 0) + return GNUNET_NO; + + const uint16_t msg_length = length - key_length; + const char* msg_buffer = buffer + key_length; + struct GNUNET_MESSENGER_Message message; - if (GNUNET_YES != decode_message (&message, length, buffer)) + if (GNUNET_YES != decode_message (&message, msg_length, msg_buffer, GNUNET_NO, NULL)) + return GNUNET_NO; + + if (GNUNET_YES != filter_message_sending(&message)) return GNUNET_NO; return GNUNET_OK; @@ -208,42 +223,85 @@ handle_send_message (void *cls, const struct GNUNET_MESSENGER_SendMessage *msg) { struct GNUNET_MESSENGER_Client *msg_client = cls; + const enum GNUNET_MESSENGER_MessageFlags flags = ( + (enum GNUNET_MESSENGER_MessageFlags) (msg->flags) + ); + 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); + uint16_t key_length = 0; + + struct GNUNET_IDENTITY_PublicKey public_key; + + if (flags & GNUNET_MESSENGER_FLAG_PRIVATE) + key_length = GNUNET_IDENTITY_read_key_from_buffer( + &public_key, buffer, length + ); + + const uint16_t msg_length = length - key_length; + const char* msg_buffer = buffer + key_length; struct GNUNET_MESSENGER_Message message; - decode_message (&message, length, buffer); + decode_message (&message, msg_length, msg_buffer, GNUNET_NO, NULL); + + if ((flags & GNUNET_MESSENGER_FLAG_PRIVATE) && + (GNUNET_YES != encrypt_message(&message, &public_key))) + { + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Encrypting message failed: Message got dropped!\n"); + + goto end_handling; + } GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending message: %s to %s\n", - GNUNET_MESSENGER_name_of_kind (message.header.kind), - GNUNET_h2s (key)); + 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_MESSENGER_name_of_kind (message.header.kind), GNUNET_h2s (key)); +end_handling: GNUNET_SERVICE_client_continue (msg_client->client); } static void -handle_get_message (void *cls, const struct GNUNET_MESSENGER_RecvMessage *msg) +handle_get_message (void *cls, const struct GNUNET_MESSENGER_GetMessage *msg) { struct GNUNET_MESSENGER_Client *msg_client = cls; - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Requesting message from room: %s\n", - GNUNET_h2s (&(msg->key))); + 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))); + if (!room) + { + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Room not found: %s\n", GNUNET_h2s (&(msg->key))); + goto end_handling; + } + + const struct GNUNET_MESSENGER_Message *message = get_room_message (room, msg_client->handle, &(msg->hash), + GNUNET_YES); + + if (!message) + goto end_handling; + + struct GNUNET_MESSENGER_MemberStore *store = get_room_member_store(room); + + struct GNUNET_MESSENGER_Member *member = get_store_member_of(store, message); + + if (!member) + { + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Sender of message (%s) unknown!\n", GNUNET_h2s (&(msg->hash))); + goto end_handling; + } + + struct GNUNET_MESSENGER_MemberSession *session = get_member_session_of(member, message, &(msg->hash)); + + if (session) + notify_handle_message (msg_client->handle, get_room_key(room), session, message, &(msg->hash)); +end_handling: GNUNET_SERVICE_client_continue (msg_client->client); } @@ -271,16 +329,16 @@ callback_client_disconnect (void *cls, struct GNUNET_SERVICE_Client *client, voi /** * Setup MESSENGER internals. * - * @param cls closure - * @param config configuration to use - * @param service the initialized service + * @param[in/out] cls closure + * @param[in] config configuration to use + * @param[in/out] 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)) + if (!messenger) GNUNET_SCHEDULER_shutdown (); } @@ -302,5 +360,5 @@ GNUNET_SERVICE_MAIN( 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_hd_fixed_size( get_message, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_GET_MESSAGE, struct GNUNET_MESSENGER_GetMessage, NULL ), GNUNET_MQ_handler_end()); diff --git a/src/messenger/gnunet-service-messenger.h b/src/messenger/gnunet-service-messenger.h index 85a1d2549..253fbaadb 100644 --- a/src/messenger/gnunet-service-messenger.h +++ b/src/messenger/gnunet-service-messenger.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -74,7 +74,6 @@ struct GNUNET_MESSENGER_NameMessage struct GNUNET_MESSENGER_KeyMessage { struct GNUNET_MessageHeader header; - struct GNUNET_IDENTITY_PublicKey pubkey; }; /** @@ -105,7 +104,20 @@ struct GNUNET_MESSENGER_MemberMessage struct GNUNET_MESSENGER_SendMessage { struct GNUNET_MessageHeader header; + struct GNUNET_HashCode key; + uint32_t flags; +}; + +/** + * Message to request something from a room + */ +struct GNUNET_MESSENGER_GetMessage +{ + struct GNUNET_MessageHeader header; + + struct GNUNET_HashCode key; + struct GNUNET_HashCode hash; }; /** @@ -114,8 +126,12 @@ struct GNUNET_MESSENGER_SendMessage struct GNUNET_MESSENGER_RecvMessage { struct GNUNET_MessageHeader header; + struct GNUNET_HashCode key; + struct GNUNET_HashCode sender; + struct GNUNET_HashCode context; struct GNUNET_HashCode hash; + uint32_t flags; }; #endif //GNUNET_SERVICE_MESSENGER_H diff --git a/src/messenger/gnunet-service-messenger_basement.c b/src/messenger/gnunet-service-messenger_basement.c index 190cf2de5..f302c8d66 100644 --- a/src/messenger/gnunet-service-messenger_basement.c +++ b/src/messenger/gnunet-service-messenger_basement.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -28,6 +28,8 @@ size_t count_of_tunnels (const struct GNUNET_MESSENGER_ListTunnels *tunnels) { + GNUNET_assert(tunnels); + const struct GNUNET_MESSENGER_ListTunnel *element; size_t count = 0; diff --git a/src/messenger/gnunet-service-messenger_basement.h b/src/messenger/gnunet-service-messenger_basement.h index 0a1a9b126..b19aec405 100644 --- a/src/messenger/gnunet-service-messenger_basement.h +++ b/src/messenger/gnunet-service-messenger_basement.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -31,34 +31,34 @@ /** * Returns the count of peers in a list (typically from the basement of a room). * - * @param tunnels List of peer identities + * @param[in] 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 + * 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 + * @param[in] count Count of peers + * @param[in] src Source index + * @param[in] 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 + * 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 + * @param[in] count Count of peers + * @param[in] src Source index + * @param[in] 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); diff --git a/src/messenger/gnunet-service-messenger_contact.c b/src/messenger/gnunet-service-messenger_contact.c deleted file mode 100644 index 1ec125402..000000000 --- a/src/messenger/gnunet-service-messenger_contact.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - 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 deleted file mode 100644 index 4a4f8bf0f..000000000 --- a/src/messenger/gnunet-service-messenger_contact.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - 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_ego_store.c b/src/messenger/gnunet-service-messenger_ego_store.c new file mode 100644 index 000000000..3b069fcf5 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_ego_store.c @@ -0,0 +1,295 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020--2021 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_ego_store.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_ego_store.h" + +#include "gnunet-service-messenger_handle.h" + +static void +callback_update_ego (void *cls, struct GNUNET_IDENTITY_Ego *ego, void **ctx, const char *identifier) +{ + if ((!ego) || (!identifier)) + return; + + struct GNUNET_MESSENGER_EgoStore *store = cls; + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "New ego in use: '%s'\n", identifier); + + update_store_ego (store, identifier, GNUNET_IDENTITY_ego_get_private_key (ego)); +} + +void +init_ego_store(struct GNUNET_MESSENGER_EgoStore *store, const struct GNUNET_CONFIGURATION_Handle *config) +{ + GNUNET_assert ((store) && (config)); + + store->cfg = config; + store->identity = GNUNET_IDENTITY_connect (config, &callback_update_ego, store); + store->egos = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + + store->lu_start = NULL; + store->lu_end = NULL; + + store->op_start = NULL; + store->op_end = NULL; +} + + +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; +} + +void +clear_ego_store(struct GNUNET_MESSENGER_EgoStore *store) +{ + GNUNET_assert (store); + + struct GNUNET_MESSENGER_EgoOperation *op; + + while (store->op_start) + { + op = store->op_start; + + GNUNET_IDENTITY_cancel (op->operation); + GNUNET_CONTAINER_DLL_remove (store->op_start, store->op_end, op); + + if (op->identifier) + GNUNET_free (op->identifier); + + GNUNET_free (op); + } + + struct GNUNET_MESSENGER_EgoLookup *lu; + + while (store->lu_start) + { + lu = store->lu_start; + + GNUNET_IDENTITY_ego_lookup_cancel(lu->lookup); + GNUNET_CONTAINER_DLL_remove (store->lu_start, store->lu_end, lu); + + if (lu->identifier) + GNUNET_free(lu->identifier); + + GNUNET_free (lu); + } + + GNUNET_CONTAINER_multihashmap_iterate (store->egos, iterate_destroy_egos, NULL); + GNUNET_CONTAINER_multihashmap_destroy (store->egos); + + if (store->identity) + { + GNUNET_IDENTITY_disconnect (store->identity); + + store->identity = NULL; + } +} + +static void +callback_ego_create (void *cls, const struct GNUNET_IDENTITY_PrivateKey *key, const char *emsg) +{ + struct GNUNET_MESSENGER_EgoOperation *element = cls; + struct GNUNET_MESSENGER_EgoStore *store = element->store; + + GNUNET_assert(element->identifier); + + if (emsg) + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s\n", emsg); + + if (key) + { + struct GNUNET_MESSENGER_SrvHandle *handle = element->handle; + + struct GNUNET_MESSENGER_Ego *msg_ego = update_store_ego (store, element->identifier, key); + + set_handle_ego (handle, msg_ego); + } + else + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Creating ego failed!\n"); + + GNUNET_CONTAINER_DLL_remove (store->op_start, store->op_end, element); + GNUNET_free (element->identifier); + GNUNET_free (element); +} + +void +create_store_ego (struct GNUNET_MESSENGER_EgoStore *store, const char *identifier, + void *handle) +{ + GNUNET_assert ((store) && (identifier)); + + struct GNUNET_MESSENGER_EgoOperation *element = GNUNET_new (struct GNUNET_MESSENGER_EgoOperation); + + element->store = store; + element->handle = handle; + + element->identifier = GNUNET_strdup (identifier); + + element->operation = GNUNET_IDENTITY_create (store->identity, identifier, NULL, + GNUNET_IDENTITY_TYPE_ECDSA, callback_ego_create, element); + + GNUNET_CONTAINER_DLL_insert (store->op_start, store->op_end, element); +} + +static void +callback_ego_lookup (void *cls, struct GNUNET_IDENTITY_Ego *ego) +{ + struct GNUNET_MESSENGER_EgoLookup *element = cls; + struct GNUNET_MESSENGER_EgoStore *store = element->store; + + GNUNET_assert(element->identifier); + + struct GNUNET_MESSENGER_Ego *msg_ego; + + if (ego) + msg_ego = update_store_ego ( + store, element->identifier, GNUNET_IDENTITY_ego_get_private_key(ego) + ); + else + msg_ego = NULL; + + if (element->cb) + element->cb(element->cls, element->identifier, msg_ego); + + GNUNET_CONTAINER_DLL_remove (store->lu_start, store->lu_end, element); + GNUNET_free (element->identifier); + GNUNET_free (element); +} + +void +lookup_store_ego(struct GNUNET_MESSENGER_EgoStore *store, const char *identifier, + GNUNET_MESSENGER_EgoLookupCallback lookup, void *cls) +{ + GNUNET_assert (store); + + if (!identifier) + { + lookup(cls, identifier, NULL); + return; + } + + struct GNUNET_HashCode hash; + GNUNET_CRYPTO_hash (identifier, strlen (identifier), &hash); + + struct GNUNET_MESSENGER_Ego *ego = GNUNET_CONTAINER_multihashmap_get (store->egos, &hash); + + if (ego) + lookup(cls, identifier, ego); + else + { + struct GNUNET_MESSENGER_EgoLookup *element = GNUNET_new (struct GNUNET_MESSENGER_EgoLookup); + + element->store = store; + + element->cb = lookup; + element->cls = cls; + + element->identifier = GNUNET_strdup (identifier); + + element->lookup = GNUNET_IDENTITY_ego_lookup(store->cfg, identifier, callback_ego_lookup, element); + + GNUNET_CONTAINER_DLL_insert (store->lu_start, store->lu_end, element); + } +} + +struct GNUNET_MESSENGER_Ego* +update_store_ego(struct GNUNET_MESSENGER_EgoStore *store, const char *identifier, + const struct GNUNET_IDENTITY_PrivateKey *key) +{ + GNUNET_assert ((store) && (identifier) && (key)); + + struct GNUNET_HashCode hash; + GNUNET_CRYPTO_hash (identifier, strlen (identifier), &hash); + + struct GNUNET_MESSENGER_Ego *ego = GNUNET_CONTAINER_multihashmap_get (store->egos, &hash); + + if (!ego) + { + ego = GNUNET_new(struct GNUNET_MESSENGER_Ego); + GNUNET_CONTAINER_multihashmap_put (store->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"); + + return ego; +} + +static void +callback_ego_rename (void *cls, const char *emsg) +{ + struct GNUNET_MESSENGER_EgoOperation *element = cls; + struct GNUNET_MESSENGER_EgoStore *store = element->store; + + GNUNET_assert(element->identifier); + + if (emsg) + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s\n", emsg); + + struct GNUNET_HashCode hash; + GNUNET_CRYPTO_hash (element->identifier, strlen (element->identifier), &hash); + + struct GNUNET_MESSENGER_Ego *ego = GNUNET_CONTAINER_multihashmap_get (store->egos, &hash); + + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (store->egos, &hash, ego)) + { + GNUNET_CRYPTO_hash ((char*) element->handle, strlen ((char*) element->handle), &hash); + + GNUNET_CONTAINER_multihashmap_put (store->egos, &hash, ego, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + } + else + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Renaming ego failed!\n"); + + GNUNET_free (element->handle); + + GNUNET_CONTAINER_DLL_remove (store->op_start, store->op_end, element); + GNUNET_free (element->identifier); + GNUNET_free (element); +} + +void +rename_store_ego (struct GNUNET_MESSENGER_EgoStore *store, const char *old_identifier, + const char *new_identifier) +{ + GNUNET_assert ((store) && (old_identifier) && (new_identifier)); + + struct GNUNET_MESSENGER_EgoOperation *element = GNUNET_new (struct GNUNET_MESSENGER_EgoOperation); + + element->store = store; + element->handle = GNUNET_strdup (new_identifier); + + element->identifier = GNUNET_strdup (old_identifier); + + element->operation = GNUNET_IDENTITY_rename (store->identity, old_identifier, new_identifier, callback_ego_rename, element); + + GNUNET_CONTAINER_DLL_insert (store->op_start, store->op_end, element); +} diff --git a/src/messenger/gnunet-service-messenger_ego_store.h b/src/messenger/gnunet-service-messenger_ego_store.h new file mode 100644 index 000000000..41f14fff2 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_ego_store.h @@ -0,0 +1,152 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020--2021 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_ego_store.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_EGO_STORE_H +#define GNUNET_SERVICE_MESSENGER_EGO_STORE_H + +#include "platform.h" +#include "gnunet_container_lib.h" + +#include "messenger_api_ego.h" + +struct GNUNET_MESSENGER_Ego; +struct GNUNET_MESSENGER_EgoStore; + +typedef void +(*GNUNET_MESSENGER_EgoLookupCallback) (void *cls, const char *identifier, + const struct GNUNET_MESSENGER_Ego *ego); + +struct GNUNET_MESSENGER_EgoLookup +{ + struct GNUNET_MESSENGER_EgoLookup *prev; + struct GNUNET_MESSENGER_EgoLookup *next; + + struct GNUNET_IDENTITY_EgoLookup *lookup; + + struct GNUNET_MESSENGER_EgoStore *store; + + GNUNET_MESSENGER_EgoLookupCallback cb; + void *cls; + + char *identifier; +}; + +struct GNUNET_MESSENGER_EgoOperation +{ + struct GNUNET_MESSENGER_EgoOperation *prev; + struct GNUNET_MESSENGER_EgoOperation *next; + + struct GNUNET_IDENTITY_Operation *operation; + + struct GNUNET_MESSENGER_EgoStore *store; + void *handle; + + char *identifier; +}; + +struct GNUNET_MESSENGER_EgoStore +{ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + struct GNUNET_IDENTITY_Handle *identity; + struct GNUNET_CONTAINER_MultiHashMap *egos; + + struct GNUNET_MESSENGER_EgoLookup *lu_start; + struct GNUNET_MESSENGER_EgoLookup *lu_end; + + struct GNUNET_MESSENGER_EgoOperation *op_start; + struct GNUNET_MESSENGER_EgoOperation *op_end; +}; + +/** + * Initializes an EGO-store as fully empty. + * + * @param[out] store EGO-store + * @param[in] config Configuration handle + */ +void +init_ego_store (struct GNUNET_MESSENGER_EgoStore *store, const struct GNUNET_CONFIGURATION_Handle *config); + +/** + * Clears an EGO-store, wipes its content and deallocates its memory. + * + * @param[in/out] store EGO-store + */ +void +clear_ego_store (struct GNUNET_MESSENGER_EgoStore *store); + +/** + * Creates a new EGO which will be registered to a store under + * a specific identifier. A given handle will be informed + * about the creation and changes its EGO accordingly. + * + * @param[in/out] store EGO-store + * @param[in] identifier Identifier string + * @param[in/out] handle Handle or NULL + */ +void +create_store_ego (struct GNUNET_MESSENGER_EgoStore *store, const char *identifier, + void *handle); + +/** + * Lookups an EGO which was registered to a store under + * a specific identifier. + * + * @param[in/out] store EGO-store + * @param[in] identifier Identifier string + * @param[in] lookup Lookup callback (non-NULL) + * @param[in] cls Closure + */ +void +lookup_store_ego (struct GNUNET_MESSENGER_EgoStore *store, const char *identifier, + GNUNET_MESSENGER_EgoLookupCallback lookup, void *cls); + +/** + * Updates the registration of an EGO to a store under + * a specific identifier with a new key. + * + * @param[in/out] store EGO-store + * @param[in] identifier Identifier string + * @param[in] key Private EGO key + * @return Updated EGO + */ +struct GNUNET_MESSENGER_Ego* +update_store_ego (struct GNUNET_MESSENGER_EgoStore *store, const char *identifier, + const struct GNUNET_IDENTITY_PrivateKey *key); + +/** + * Updates the location of a registered EGO in a store to + * a different one under a specific new_identifier replacing + * its old one. + * + * @param[in/out] store EGO-store + * @param[in] old_identifier Old identifier string + * @param[in] new_identifier New identifier string + */ +void +rename_store_ego (struct GNUNET_MESSENGER_EgoStore *store, const char *old_identifier, + const char *new_identifier); + +#endif //GNUNET_SERVICE_MESSENGER_EGO_STORE_H diff --git a/src/messenger/gnunet-service-messenger_handle.c b/src/messenger/gnunet-service-messenger_handle.c index 38ad6fbb4..4d2318d62 100644 --- a/src/messenger/gnunet-service-messenger_handle.c +++ b/src/messenger/gnunet-service-messenger_handle.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -28,18 +28,19 @@ #include "gnunet-service-messenger.h" #include "gnunet-service-messenger_message_kind.h" +#include "messenger_api_util.h" + struct GNUNET_MESSENGER_SrvHandle* create_handle (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MQ_Handle *mq) { + GNUNET_assert((service) && (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); @@ -58,11 +59,10 @@ iterate_free_member_ids (void *cls, const struct GNUNET_HashCode *key, void *val void destroy_handle (struct GNUNET_MESSENGER_SrvHandle *handle) { - if (handle->service->dir) - save_handle_configuration(handle); + GNUNET_assert(handle); - if (handle->operation) - GNUNET_IDENTITY_cancel (handle->operation); + if (handle->service->dir) + save_handle_configuration (handle); if (handle->name) GNUNET_free(handle->name); @@ -74,8 +74,10 @@ destroy_handle (struct GNUNET_MESSENGER_SrvHandle *handle) } void -get_handle_data_subdir (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name, char **dir) +get_handle_data_subdir (const struct GNUNET_MESSENGER_SrvHandle *handle, const char *name, char **dir) { + GNUNET_assert((handle) && (dir)); + if (name) GNUNET_asprintf (dir, "%s%s%c%s%c", handle->service->dir, "identities", DIR_SEPARATOR, name, DIR_SEPARATOR); @@ -87,11 +89,15 @@ get_handle_data_subdir (struct GNUNET_MESSENGER_SrvHandle *handle, const char *n 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); + GNUNET_assert((handle) && (key)); + + struct GNUNET_ShortHashCode *random_id = GNUNET_new(struct GNUNET_ShortHashCode); if (!random_id) return GNUNET_NO; + generate_free_member_id (random_id, NULL); + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (handle->member_ids, key, random_id, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) { @@ -99,8 +105,8 @@ create_handle_member_id (const struct GNUNET_MESSENGER_SrvHandle *handle, const 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)); + 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; } @@ -108,48 +114,61 @@ create_handle_member_id (const struct GNUNET_MESSENGER_SrvHandle *handle, const const struct GNUNET_ShortHashCode* get_handle_member_id (const struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key) { + GNUNET_assert((handle) && (key)); + return GNUNET_CONTAINER_multihashmap_get (handle->member_ids, key); } -void +int 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; + GNUNET_assert((handle) && (key) && (unique_id)); - 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)); + struct GNUNET_ShortHashCode *member_id = GNUNET_CONTAINER_multihashmap_get (handle->member_ids, key); - GNUNET_MQ_send (handle->mq, env); - } - else + if (!member_id) { 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); + return GNUNET_SYSERR; + } } + + if (0 == GNUNET_memcmp(unique_id, member_id)) + goto send_message_to_client; + + GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Change a member id (%s) for room (%s).\n", GNUNET_sh2s (member_id), + GNUNET_h2s (key)); + + GNUNET_memcpy(member_id, unique_id, sizeof(*unique_id)); + + GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Member id changed to (%s).\n", GNUNET_sh2s (unique_id)); + + struct GNUNET_MESSENGER_MemberMessage *msg; + struct GNUNET_MQ_Envelope *env; + +send_message_to_client: + + 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); + return GNUNET_OK; } static void change_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name) { + GNUNET_assert(handle); + if (handle->name) GNUNET_free(handle->name); @@ -173,25 +192,67 @@ change_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name) } static void -change_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle, struct GNUNET_MESSENGER_Ego *ego) +change_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_MESSENGER_Ego *ego) { + GNUNET_assert(handle); + handle->ego = ego; - ego = get_handle_ego(handle); + ego = get_handle_ego (handle); + + const uint16_t length = GNUNET_IDENTITY_key_get_length(&(ego->pub)); struct GNUNET_MESSENGER_KeyMessage *msg; struct GNUNET_MQ_Envelope *env; - env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_KEY); + env = GNUNET_MQ_msg_extra(msg, length, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_KEY); - GNUNET_memcpy(&(msg->pubkey), &(ego->pub), sizeof(ego->pub)); + char *extra = ((char*) msg) + sizeof(*msg); + + if (GNUNET_IDENTITY_write_key_to_buffer(&(ego->pub), extra, length) < 0) + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Could not write key to buffer.\n"); GNUNET_MQ_send (handle->mq, env); } -struct GNUNET_MESSENGER_Ego* -get_handle_ego (struct GNUNET_MESSENGER_SrvHandle *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; +} + +void +set_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_MESSENGER_Ego *ego) +{ + GNUNET_assert((handle) && (ego)); + + struct GNUNET_MESSENGER_MessageHandle msg_handle; + + msg_handle.handle = handle; + msg_handle.message = create_message_key (&(ego->priv)); + + GNUNET_CONTAINER_multihashmap_iterate (handle->member_ids, iterate_send_message, &msg_handle); + + destroy_message (msg_handle.message); + + change_handle_ego (handle, ego); +} + +const struct GNUNET_MESSENGER_Ego* +get_handle_ego (const struct GNUNET_MESSENGER_SrvHandle *handle) { + GNUNET_assert(handle); + static struct GNUNET_MESSENGER_Ego anonymous; static int read_keys = 0; @@ -200,99 +261,86 @@ get_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle) 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)); + 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) +static void +callback_setup_handle_name (void *cls, const char *name, const struct GNUNET_MESSENGER_Ego *ego) { + struct GNUNET_MESSENGER_SrvHandle *handle = cls; + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Setting up handle...\n"); + change_handle_name (handle, name); - change_handle_ego (handle, handle->name? lookup_service_ego(handle->service, handle->name) : NULL); + change_handle_ego (handle, ego); if (handle->service->dir) - load_handle_configuration(handle); + 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) +void +setup_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name) { - struct GNUNET_MESSENGER_MessageHandle *msg_handle = cls; + GNUNET_assert(handle); - send_handle_message (msg_handle->handle, key, msg_handle->message); + struct GNUNET_MESSENGER_EgoStore *store = get_service_ego_store(handle->service); - return GNUNET_YES; + lookup_store_ego (store, name, callback_setup_handle_name, handle); } static void -callback_ego_create (void *cls, const struct GNUNET_IDENTITY_PrivateKey *key, const char *emsg) +callback_update_handle (void *cls, const char *name, const struct GNUNET_MESSENGER_Ego *ego) { struct GNUNET_MESSENGER_SrvHandle *handle = cls; - handle->operation = NULL; + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Updating handle...\n"); - if (emsg) - GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s\n", emsg); + struct GNUNET_MESSENGER_EgoStore *store = get_service_ego_store(handle->service); - 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)); - } + if (!ego) + create_store_ego(store, handle->name, handle); + else + change_handle_ego (handle, ego); } -int +void 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); + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Updating handle failed: Name is required!\n"); + return; + } - 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); + struct GNUNET_MESSENGER_EgoStore *store = get_service_ego_store(handle->service); - return GNUNET_OK; + lookup_store_ego (store, handle->name, callback_update_handle, handle); } -int -set_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name) +static void +callback_set_handle_name (void *cls, const char *name, const struct GNUNET_MESSENGER_Ego *ego) { - GNUNET_assert(handle); + struct GNUNET_MESSENGER_SrvHandle *handle = cls; - if ((name) && (lookup_service_ego(handle->service, name))) - return GNUNET_NO; + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Renaming handle...\n"); + + if (ego) + { + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Renaming handle failed: Name is occupied! (%s)\n", name); + return; + } - struct GNUNET_IDENTITY_Operation *operation = handle->operation; + struct GNUNET_MESSENGER_EgoStore *store = get_service_ego_store(handle->service); - if (handle->name) - handle->operation = GNUNET_IDENTITY_rename (handle->service->identity, handle->name, name, NULL, NULL); + int rename_ego_in_store = handle->ego? GNUNET_YES : GNUNET_NO; char *old_dir; get_handle_data_subdir (handle, handle->name, &old_dir); @@ -323,29 +371,42 @@ set_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name) destroy_message (msg_handle.message); change_handle_name (handle, name); - - if (operation) - GNUNET_IDENTITY_cancel (operation); } else + rename_ego_in_store = GNUNET_NO; + + GNUNET_free(old_dir); + GNUNET_free(new_dir); + + if (GNUNET_YES == rename_ego_in_store) + rename_store_ego(store, handle->name, name); +} + +void +set_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name) +{ + GNUNET_assert(handle); + + if (!name) { - if (handle->operation) - { - GNUNET_IDENTITY_cancel (handle->operation); + if (handle->ego) + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Renaming handle failed: Name is required!\n"); + else + change_handle_name (handle, name); - handle->operation = operation; - } + return; } - GNUNET_free(old_dir); - GNUNET_free(new_dir); + struct GNUNET_MESSENGER_EgoStore *store = get_service_ego_store(handle->service); - return (result == 0 ? GNUNET_OK : GNUNET_NO); + lookup_store_ego (store, name, callback_set_handle_name, handle); } int open_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key) { + GNUNET_assert((handle) && (key)); + if ((!get_handle_member_id (handle, key)) && (GNUNET_YES != create_handle_member_id (handle, key))) return GNUNET_NO; @@ -356,6 +417,8 @@ int entry_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_PeerIdentity *door, const struct GNUNET_HashCode *key) { + GNUNET_assert((handle) && (door) && (key)); + if ((!get_handle_member_id (handle, key)) && (GNUNET_YES != create_handle_member_id (handle, key))) return GNUNET_NO; @@ -365,6 +428,8 @@ entry_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNE int close_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key) { + GNUNET_assert((handle) && (key)); + if (!get_handle_member_id (handle, key)) return GNUNET_NO; @@ -373,8 +438,10 @@ close_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNE int send_handle_message (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key, - struct GNUNET_MESSENGER_Message *message) + const struct GNUNET_MESSENGER_Message *message) { + GNUNET_assert((handle) && (key) && (message)); + const struct GNUNET_ShortHashCode *id = get_handle_member_id (handle, key); if (!id) @@ -391,45 +458,120 @@ send_handle_message (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNU return GNUNET_NO; } - struct GNUNET_HashCode hash; + struct GNUNET_MESSENGER_Message *msg = copy_message(message); - GNUNET_memcpy(&(message->header.sender_id), id, sizeof(*id)); + GNUNET_memcpy(&(msg->header.sender_id), id, sizeof(*id)); - send_room_message (room, handle, message, &hash); - return GNUNET_YES; + return send_room_message (room, handle, msg); +} + +static const struct GNUNET_HashCode* +get_next_member_session_contect(const struct GNUNET_MESSENGER_MemberSession *session) +{ + if (session->next) + return get_next_member_session_contect (session->next); + else + return get_member_session_context(session); +} + +void +notify_handle_message (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key, + const struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + GNUNET_assert((handle) && (key) && (session) && (message) && (hash)); + + if ((!handle->mq) || (!get_handle_member_id (handle, key))) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Notifying client about message requires membership!\n"); + return; + } + + const struct GNUNET_IDENTITY_PublicKey *pubkey = get_contact_key(session->contact); + + struct GNUNET_HashCode sender; + GNUNET_CRYPTO_hash(pubkey, sizeof(*pubkey), &sender); + + const struct GNUNET_HashCode *context = get_next_member_session_contect (session); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Notifying client about message: %s\n", GNUNET_h2s (hash)); + + struct GNUNET_MESSENGER_Message *private_message = NULL; + + if (GNUNET_MESSENGER_KIND_PRIVATE == message->header.kind) + { + private_message = copy_message(message); + + if (GNUNET_YES != decrypt_message(private_message, &(get_handle_ego(handle)->priv))) + { + destroy_message(private_message); + private_message = NULL; + } + else + message = private_message; + } + + struct GNUNET_MESSENGER_RecvMessage *msg; + struct GNUNET_MQ_Envelope *env; + + uint16_t length = get_message_size (message, GNUNET_YES); + + env = GNUNET_MQ_msg_extra(msg, length, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_RECV_MESSAGE); + + GNUNET_memcpy(&(msg->key), key, sizeof(msg->key)); + GNUNET_memcpy(&(msg->sender), &sender, sizeof(msg->sender)); + GNUNET_memcpy(&(msg->context), context, sizeof(msg->context)); + GNUNET_memcpy(&(msg->hash), hash, sizeof(msg->hash)); + + msg->flags = (uint32_t) ( + private_message? GNUNET_MESSENGER_FLAG_PRIVATE : GNUNET_MESSENGER_FLAG_NONE + ); + + char *buffer = ((char*) msg) + sizeof(*msg); + encode_message (message, length, buffer, GNUNET_YES); + + if (private_message) + destroy_message(private_message); + + GNUNET_MQ_send (handle->mq, env); } -static int callback_scan_for_rooms(void* cls, const char *filename) { - struct GNUNET_MESSENGER_SrvHandle* handle = cls; +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(); + struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create (); - if ((GNUNET_YES == GNUNET_DISK_file_test(filename)) && - (GNUNET_OK == GNUNET_CONFIGURATION_parse(cfg, filename))) + 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); + 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); + 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); +void +load_handle_configuration (struct GNUNET_MESSENGER_SrvHandle *handle) +{ + GNUNET_assert(handle); - if (GNUNET_YES == GNUNET_DISK_directory_test(id_dir, GNUNET_YES)) + 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); + 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); + 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); } @@ -438,63 +580,64 @@ void load_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle) { } static int -iterate_save_rooms(void* cls, const struct GNUNET_HashCode* key, void* value) +iterate_save_rooms (void *cls, const struct GNUNET_HashCode *key, void *value) { - struct GNUNET_MESSENGER_SrvHandle* handle = cls; - struct GNUNET_ShortHashCode* member_id = 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 *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)); + 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(); + struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create (); - char* key_data = GNUNET_STRINGS_data_to_string_alloc(key, sizeof(*key)); + 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_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)); + 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_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_CONFIGURATION_write (cfg, filename); + GNUNET_CONFIGURATION_destroy (cfg); GNUNET_free(filename); return GNUNET_YES; } -void save_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle) +void +save_handle_configuration (struct GNUNET_MESSENGER_SrvHandle *handle) { - char* id_dir; - get_handle_data_subdir(handle, handle->name, &id_dir); + GNUNET_assert(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))) + 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); + 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); + 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); } diff --git a/src/messenger/gnunet-service-messenger_handle.h b/src/messenger/gnunet-service-messenger_handle.h index 81cf377a8..70b2cac6d 100644 --- a/src/messenger/gnunet-service-messenger_handle.h +++ b/src/messenger/gnunet-service-messenger_handle.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -35,6 +35,7 @@ #include "gnunet_mq_lib.h" #include "gnunet-service-messenger_service.h" +#include "gnunet-service-messenger_member_session.h" #include "messenger_api_ego.h" #include "messenger_api_message.h" @@ -46,9 +47,7 @@ struct GNUNET_MESSENGER_SrvHandle char *name; - struct GNUNET_IDENTITY_Operation *operation; - - struct GNUNET_MESSENGER_Ego *ego; + const struct GNUNET_MESSENGER_Ego *ego; struct GNUNET_CONTAINER_MultiHashMap *member_ids; }; @@ -56,8 +55,8 @@ struct GNUNET_MESSENGER_SrvHandle /** * 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 + * @param[in/out] service MESSENGER Service + * @param[in/out] mq Message queue * @return New handle */ struct GNUNET_MESSENGER_SrvHandle* @@ -66,7 +65,7 @@ create_handle (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MQ_Handle /** * Destroys a handle and frees its memory fully. * - * @param handle Handle + * @param[in/out] handle Handle */ void destroy_handle (struct GNUNET_MESSENGER_SrvHandle *handle); @@ -75,52 +74,63 @@ 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 + * @param[in] handle Handle + * @param[in] name Potential name of the handle + * @param[out] dir Path to store data */ void -get_handle_data_subdir (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name, char **dir); +get_handle_data_subdir (const 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 + * @param[in] handle Handle + * @param[in] 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. + * Changes the member id of a given handle in a specific room to match a unique_id + * and returns GNUNET_OK on success. * * 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 + * @param[in/out] handle Handle + * @param[in] key Key of a room + * @param[in] unique_id Unique member id + * @return GNUNET_OK on success, otherwise GNUNET_SYSERR */ -void +int change_handle_member_id (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key, const struct GNUNET_ShortHashCode *unique_id); +/** + * Sets the EGO used by a given handle. + * + * @param[in/out] handle Handle + * @param[in] ego EGO keypair + */ +void +set_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_MESSENGER_Ego *ego); + /** * Returns the EGO used by a given handle. * - * @param handle Handle + * @param[in] handle Handle * @return EGO keypair */ -struct GNUNET_MESSENGER_Ego* -get_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle); +const struct GNUNET_MESSENGER_Ego* +get_handle_ego (const 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) + * @param[in/out] handle Handle + * @param[in] name Name (optionally: valid EGO name) */ void setup_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name); @@ -129,10 +139,9 @@ 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 + * @param[in/out] handle Handle */ -int +void update_handle (struct GNUNET_MESSENGER_SrvHandle *handle); /** @@ -141,20 +150,19 @@ update_handle (struct GNUNET_MESSENGER_SrvHandle *handle); * * 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 + * @param[in/out] handle Handle + * @param[in] name New name */ -int +void 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 + * @param[in/out] handle Handle + * @param[in] 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); @@ -163,10 +171,10 @@ open_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET * 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 + * @param[in/out] handle Handle + * @param[in] door Peer identity + * @param[in] 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, @@ -176,9 +184,9 @@ entry_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNE * 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 + * @param[in/out] handle Handle + * @param[in] 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); @@ -186,31 +194,45 @@ close_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNE /** * 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 + * @param[in/out] handle Handle + * @param[in] key Key of a room + * @param[in] message Message + * @return #GNUNET_YES on success, #GNUNET_NO or #GNUNET_SYSERR otherwise. */ int send_handle_message (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key, - struct GNUNET_MESSENGER_Message *message); + const struct GNUNET_MESSENGER_Message *message); + +/** + * Notifies the handle that a new message was received or sent. + * + * @param[in/out] handle Handle + * @param[in] key Key of room + * @param[in] session Member session + * @param[in] message Message + * @param[in] hash Hash of message + */ +void +notify_handle_message (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key, + const struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); /** * Loads member ids and other potential configuration from a given handle which * depends on the given name the handle uses. * - * @param handle Handle + * @param[out] handle Handle */ void -load_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle); +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 + * @param[in] handle Handle */ void -save_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle); +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 index 16a160dea..adcbf6a42 100644 --- a/src/messenger/gnunet-service-messenger_list_handles.c +++ b/src/messenger/gnunet-service-messenger_list_handles.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -55,8 +55,10 @@ clear_list_handles (struct GNUNET_MESSENGER_ListHandles *handles) } void -add_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle) +add_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, struct GNUNET_MESSENGER_SrvHandle *handle) { + GNUNET_assert((handles) && (handle)); + struct GNUNET_MESSENGER_ListHandle *element = GNUNET_new(struct GNUNET_MESSENGER_ListHandle); element->handle = handle; @@ -65,8 +67,10 @@ add_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle) } int -remove_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle) +remove_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, struct GNUNET_MESSENGER_SrvHandle *handle) { + GNUNET_assert((handles) && (handle)); + struct GNUNET_MESSENGER_ListHandle *element; for (element = handles->head; element; element = element->next) @@ -82,9 +86,11 @@ remove_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle) return GNUNET_YES; } -void* -find_list_handle_by_member (struct GNUNET_MESSENGER_ListHandles *handles, const struct GNUNET_HashCode *key) +struct GNUNET_MESSENGER_SrvHandle* +find_list_handle_by_member (const struct GNUNET_MESSENGER_ListHandles *handles, const struct GNUNET_HashCode *key) { + GNUNET_assert((handles) && (key)); + struct GNUNET_MESSENGER_ListHandle *element; for (element = handles->head; element; element = element->next) diff --git a/src/messenger/gnunet-service-messenger_list_handles.h b/src/messenger/gnunet-service-messenger_list_handles.h index fe92cc58a..9f7ca725f 100644 --- a/src/messenger/gnunet-service-messenger_list_handles.h +++ b/src/messenger/gnunet-service-messenger_list_handles.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -30,12 +30,14 @@ #include "gnunet_crypto_lib.h" #include "gnunet_container_lib.h" +struct GNUNET_MESSENGER_SrvHandle; + struct GNUNET_MESSENGER_ListHandle { struct GNUNET_MESSENGER_ListHandle *prev; struct GNUNET_MESSENGER_ListHandle *next; - void *handle; + struct GNUNET_MESSENGER_SrvHandle *handle; }; struct GNUNET_MESSENGER_ListHandles @@ -45,17 +47,17 @@ struct GNUNET_MESSENGER_ListHandles }; /** - * Initializes list of handles as empty list. + * Initializes list of handles as empty list. * - * @param handles List of handles + * @param[out] handles List of handles */ void init_list_handles (struct GNUNET_MESSENGER_ListHandles *handles); /** - * Destroys remaining handles and clears the list. + * Destroys remaining handles and clears the list. * - * @param handles List of handles + * @param[in/out] handles List of handles */ void clear_list_handles (struct GNUNET_MESSENGER_ListHandles *handles); @@ -63,34 +65,34 @@ 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 + * @param[in/out] handles List of handles + * @param[in/out] handle Handle */ void -add_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle); +add_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, struct GNUNET_MESSENGER_SrvHandle *handle); /** - * Removes the first entry matching with a specific handle from the list and - * returns GNUNET_YES on success or GNUNET_NO on failure. + * Removes the first entry matching with a specific handle from the list of + * handles 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 + * @param[in/out] handles List of handles + * @param[in/out] handle Handle + * @return #GNUNET_YES on success, otherwise #GNUNET_NO */ int -remove_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle); +remove_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, struct GNUNET_MESSENGER_SrvHandle *handle); /** - * Searches linearly through the list of handles for members of a specific room + * 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 + * @param[in] handles List of handles + * @param[in] 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); +struct GNUNET_MESSENGER_SrvHandle* +find_list_handle_by_member (const 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 index c4f1f7043..bb6086e41 100644 --- a/src/messenger/gnunet-service-messenger_list_messages.c +++ b/src/messenger/gnunet-service-messenger_list_messages.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -54,6 +54,8 @@ clear_list_messages (struct GNUNET_MESSENGER_ListMessages *messages) void add_to_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const struct GNUNET_HashCode *hash) { + GNUNET_assert((messages) && (hash)); + struct GNUNET_MESSENGER_ListMessage *element = GNUNET_new(struct GNUNET_MESSENGER_ListMessage); GNUNET_memcpy(&(element->hash), hash, sizeof(struct GNUNET_HashCode)); @@ -61,9 +63,22 @@ add_to_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const stru GNUNET_CONTAINER_DLL_insert_tail(messages->head, messages->tail, element); } +void +copy_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const struct GNUNET_MESSENGER_ListMessages *origin) +{ + GNUNET_assert((messages) && (origin)); + + struct GNUNET_MESSENGER_ListMessage *element; + + for (element = origin->head; element; element = element->next) + add_to_list_messages (messages, &(element->hash)); +} + void remove_from_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const struct GNUNET_HashCode *hash) { + GNUNET_assert((messages) && (hash)); + struct GNUNET_MESSENGER_ListMessage *element; for (element = messages->head; element; element = element->next) @@ -74,3 +89,62 @@ remove_from_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const break; } } + +void +load_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const char *path) +{ + GNUNET_assert((messages) && (path)); + + if (GNUNET_YES != GNUNET_DISK_file_test (path)) + return; + + enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE); + + struct GNUNET_DISK_FileHandle *handle = GNUNET_DISK_file_open( + path, GNUNET_DISK_OPEN_READ, permission + ); + + if (!handle) + return; + + GNUNET_DISK_file_seek(handle, 0, GNUNET_DISK_SEEK_SET); + + struct GNUNET_HashCode hash; + ssize_t len; + + do { + len = GNUNET_DISK_file_read(handle, &hash, sizeof(hash)); + + if (len != sizeof(hash)) + break; + + add_to_list_messages(messages, &hash); + } while (len == sizeof(hash)); + + GNUNET_DISK_file_close(handle); +} + +void +save_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const char *path) +{ + GNUNET_assert((messages) && (path)); + + enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE); + + struct GNUNET_DISK_FileHandle *handle = GNUNET_DISK_file_open( + path, GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE, permission + ); + + if (!handle) + return; + + GNUNET_DISK_file_seek(handle, 0, GNUNET_DISK_SEEK_SET); + + struct GNUNET_MESSENGER_ListMessage *element; + + for (element = messages->head; element; element = element->next) + GNUNET_DISK_file_write(handle, &(element->hash), sizeof(element->hash)); + + GNUNET_DISK_file_sync(handle); + GNUNET_DISK_file_close(handle); +} diff --git a/src/messenger/gnunet-service-messenger_list_messages.h b/src/messenger/gnunet-service-messenger_list_messages.h index 266c30ec6..9ace84cbf 100644 --- a/src/messenger/gnunet-service-messenger_list_messages.h +++ b/src/messenger/gnunet-service-messenger_list_messages.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -29,6 +29,7 @@ #include "platform.h" #include "gnunet_crypto_lib.h" #include "gnunet_container_lib.h" +#include "gnunet_disk_lib.h" struct GNUNET_MESSENGER_ListMessage { @@ -47,7 +48,7 @@ struct GNUNET_MESSENGER_ListMessages /** * Initializes list of message hashes as empty list. * - * @param messages List of hashes + * @param[out] messages List of hashes */ void init_list_messages (struct GNUNET_MESSENGER_ListMessages *messages); @@ -55,7 +56,7 @@ init_list_messages (struct GNUNET_MESSENGER_ListMessages *messages); /** * Clears the list of message hashes. * - * @param messages List of hashes + * @param[in/out] messages List of hashes */ void clear_list_messages (struct GNUNET_MESSENGER_ListMessages *messages); @@ -63,19 +64,46 @@ 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 + * @param[in/out] messages List of hashes + * @param[in] hash Hash of message */ void add_to_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const struct GNUNET_HashCode *hash); +/** + * Copies all message hashes from an origin to another list. + * + * @param[in/out] messages Destination list of hashes + * @param[in] origin Source list of hashes + */ +void +copy_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const struct GNUNET_MESSENGER_ListMessages *origin); + /** * Removes the first entry with a matching hash from the list. * - * @param messages List of hashes - * @param hash Hash of message + * @param[in/out] messages List of hashes + * @param[in] hash Hash of message */ void remove_from_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const struct GNUNET_HashCode *hash); +/** + * Loads the list of message hashes from a file under a given path. + * + * @param[out] messages List of hashes + * @param[in] path Path of file + */ +void +load_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const char *path); + +/** + * Saves the list of message hashes to a file under a given path. + * + * @param[in] messages List of hashes + * @param[in] path Path of file + */ +void +save_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const char *path); + #endif //GNUNET_SERVICE_MESSENGER_LIST_MESSAGES_H diff --git a/src/messenger/gnunet-service-messenger_member.c b/src/messenger/gnunet-service-messenger_member.c new file mode 100644 index 000000000..6e39cec13 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_member.c @@ -0,0 +1,379 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020--2021 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_member.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_member.h" + +#include "gnunet-service-messenger_member_session.h" + +struct GNUNET_MESSENGER_Member* +create_member (struct GNUNET_MESSENGER_MemberStore *store, const struct GNUNET_ShortHashCode *id) +{ + GNUNET_assert (store); + + struct GNUNET_MESSENGER_Member *member = GNUNET_new(struct GNUNET_MESSENGER_Member); + + member->store = store; + + if (id) + GNUNET_memcpy(&(member->id), id, sizeof(member->id)); + else if (GNUNET_YES != generate_free_member_id(&(member->id), store->members)) + { + GNUNET_free (member); + return NULL; + } + + member->sessions = GNUNET_CONTAINER_multihashmap_create(2, GNUNET_NO); + + return member; +} + +static int +iterate_destroy_session (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_MemberSession *session = value; + destroy_member_session(session); + return GNUNET_YES; +} + +void +destroy_member (struct GNUNET_MESSENGER_Member *member) +{ + GNUNET_assert((member) && (member->sessions)); + + GNUNET_CONTAINER_multihashmap_iterate (member->sessions, iterate_destroy_session, NULL); + GNUNET_CONTAINER_multihashmap_destroy (member->sessions); + + GNUNET_free (member); +} + +const struct GNUNET_ShortHashCode* +get_member_id (const struct GNUNET_MESSENGER_Member *member) +{ + GNUNET_assert (member); + + return &(member->id); +} + +static int +callback_scan_for_sessions (void *cls, const char *filename) +{ + struct GNUNET_MESSENGER_Member *member = cls; + + if (GNUNET_YES == GNUNET_DISK_directory_test (filename, GNUNET_YES)) + { + char *directory; + + GNUNET_asprintf (&directory, "%s%c", filename, DIR_SEPARATOR); + + load_member_session(member, directory); + } + + return GNUNET_OK; +} + +void +load_member (struct GNUNET_MESSENGER_MemberStore *store, const char *directory) +{ + GNUNET_assert ((store) && (directory)); + + char *config_file; + GNUNET_asprintf (&config_file, "%s%s", directory, "member.cfg"); + + struct GNUNET_MESSENGER_Member *member = NULL; + + if (GNUNET_YES != GNUNET_DISK_file_test (config_file)) + goto free_config; + + struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create (); + + if (GNUNET_OK == GNUNET_CONFIGURATION_parse (cfg, config_file)) + { + struct GNUNET_ShortHashCode id; + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_data (cfg, "member", "id", &id, sizeof(id))) + goto destroy_config; + + member = add_store_member(store, &id); + } + +destroy_config: + + GNUNET_CONFIGURATION_destroy (cfg); + +free_config: + GNUNET_free(config_file); + + if (!member) + return; + + char *scan_dir; + GNUNET_asprintf (&scan_dir, "%s%s%c", directory, "sessions", DIR_SEPARATOR); + + if (GNUNET_OK == GNUNET_DISK_directory_test (scan_dir, GNUNET_YES)) + GNUNET_DISK_directory_scan (scan_dir, callback_scan_for_sessions, member); + + GNUNET_free(scan_dir); +} + +static int +iterate_load_next_session (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + const char* sessions_directory = cls; + + char* load_dir; + GNUNET_asprintf (&load_dir, "%s%s%c", sessions_directory, GNUNET_h2s(key), DIR_SEPARATOR); + + struct GNUNET_MESSENGER_MemberSession *session = value; + + if (GNUNET_YES == GNUNET_DISK_directory_test (load_dir, GNUNET_YES)) + load_member_session_next (session, load_dir); + + GNUNET_free (load_dir); + return GNUNET_YES; +} + +void +load_member_next_sessions (const struct GNUNET_MESSENGER_Member *member, const char *directory) +{ + GNUNET_assert ((member) && (directory)); + + char* load_dir; + GNUNET_asprintf (&load_dir, "%s%s%c", directory, "sessions", DIR_SEPARATOR); + + GNUNET_CONTAINER_multihashmap_iterate (member->sessions, iterate_load_next_session, load_dir); + + GNUNET_free(load_dir); +} + +static int +iterate_save_session (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + const char* sessions_directory = cls; + + char* save_dir; + GNUNET_asprintf (&save_dir, "%s%s%c", sessions_directory, GNUNET_h2s(key), DIR_SEPARATOR); + + struct GNUNET_MESSENGER_MemberSession *session = value; + + if ((GNUNET_YES == GNUNET_DISK_directory_test (save_dir, GNUNET_NO)) || + (GNUNET_OK == GNUNET_DISK_directory_create (save_dir))) + save_member_session (session, save_dir); + + GNUNET_free (save_dir); + return GNUNET_YES; +} + +void +save_member (struct GNUNET_MESSENGER_Member *member, const char *directory) +{ + GNUNET_assert ((member) && (directory)); + + char *config_file; + GNUNET_asprintf (&config_file, "%s%s", directory, "member.cfg"); + + struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create (); + + char *id_data = GNUNET_STRINGS_data_to_string_alloc (&(member->id), sizeof(member->id)); + + if (id_data) + { + GNUNET_CONFIGURATION_set_value_string (cfg, "member", "id", id_data); + + GNUNET_free(id_data); + } + + GNUNET_CONFIGURATION_write (cfg, config_file); + GNUNET_CONFIGURATION_destroy (cfg); + + GNUNET_free(config_file); + + char* save_dir; + GNUNET_asprintf (&save_dir, "%s%s%c", directory, "sessions", 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 (member->sessions, iterate_save_session, save_dir); + + GNUNET_free(save_dir); +} + +static void +sync_session_contact_from_next (struct GNUNET_MESSENGER_MemberSession *session, struct GNUNET_MESSENGER_MemberSession *next) +{ + GNUNET_assert((session) && (next)); + + if (session == next) + return; + + if (next->next) + sync_session_contact_from_next (session, next->next); + else + session->contact = next->contact; +} + +static int +iterate_sync_session_contact (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_MemberSession *session = value; + + if (session->next) + sync_session_contact_from_next (session, session->next); + + return GNUNET_YES; +} + +void +sync_member_contacts (struct GNUNET_MESSENGER_Member *member) +{ + GNUNET_assert ((member) && (member->sessions)); + + GNUNET_CONTAINER_multihashmap_iterate (member->sessions, iterate_sync_session_contact, NULL); +} + +struct GNUNET_MESSENGER_MemberSession* +get_member_session (const struct GNUNET_MESSENGER_Member *member, const struct GNUNET_IDENTITY_PublicKey *public_key) +{ + GNUNET_assert ((member) && (public_key)); + + struct GNUNET_HashCode hash; + GNUNET_CRYPTO_hash(public_key, sizeof(*public_key), &hash); + + return GNUNET_CONTAINER_multihashmap_get(member->sessions, &hash); +} + +struct GNUNET_MESSENGER_ClosureSearchSession { + const struct GNUNET_MESSENGER_Message *message; + const struct GNUNET_HashCode *hash; + + struct GNUNET_MESSENGER_MemberSession *match; +}; + +static int +iterate_search_session (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_ClosureSearchSession* search = cls; + struct GNUNET_MESSENGER_MemberSession *session = value; + + if (GNUNET_OK != verify_member_session_as_sender(session, search->message, search->hash)) + return GNUNET_YES; + + search->match = session; + return GNUNET_NO; +} + +static struct GNUNET_MESSENGER_MemberSession* +try_member_session (struct GNUNET_MESSENGER_Member *member, const struct GNUNET_IDENTITY_PublicKey *public_key) +{ + struct GNUNET_MESSENGER_MemberSession* session = get_member_session(member, public_key); + + if (session) + return session; + + session = create_member_session(member, public_key); + + if (session) + add_member_session(member, session); + + return session; +} + +struct GNUNET_MESSENGER_MemberSession* +get_member_session_of (struct GNUNET_MESSENGER_Member *member, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + GNUNET_assert ((member) && (message) && (hash) && + (0 == GNUNET_memcmp(&(member->id), &(message->header.sender_id)))); + + if (GNUNET_MESSENGER_KIND_INFO == message->header.kind) + return try_member_session(member, &(message->body.info.host_key)); + else if (GNUNET_MESSENGER_KIND_JOIN == message->header.kind) + return try_member_session(member, &(message->body.join.key)); + + struct GNUNET_MESSENGER_ClosureSearchSession search; + + search.message = message; + search.hash = hash; + + search.match = NULL; + GNUNET_CONTAINER_multihashmap_iterate(member->sessions, iterate_search_session, &search); + + return search.match; +} + +void +add_member_session (struct GNUNET_MESSENGER_Member *member, struct GNUNET_MESSENGER_MemberSession *session) +{ + if (!session) + return; + + GNUNET_assert((member) && (session->member == member)); + + const struct GNUNET_IDENTITY_PublicKey *public_key = get_member_session_public_key(session); + + struct GNUNET_HashCode hash; + GNUNET_CRYPTO_hash(public_key, sizeof(*public_key), &hash); + + GNUNET_CONTAINER_multihashmap_put(member->sessions, &hash, session, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); +} + +void +remove_member_session (struct GNUNET_MESSENGER_Member *member, struct GNUNET_MESSENGER_MemberSession *session) +{ + GNUNET_assert ((member) && (session) && (session->member == member)); + + const struct GNUNET_IDENTITY_PublicKey *public_key = get_member_session_public_key(session); + + struct GNUNET_HashCode hash; + GNUNET_CRYPTO_hash(public_key, sizeof(*public_key), &hash); + + GNUNET_CONTAINER_multihashmap_remove(member->sessions, &hash, session); +} + +struct GNUNET_MESSENGER_ClosureIterateSessions { + GNUNET_MESSENGER_MemberIteratorCallback it; + void *cls; +}; + +static int +iterate_member_sessions_it (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_ClosureIterateSessions *iterate = cls; + struct GNUNET_MESSENGER_MemberSession *session = value; + + return iterate->it (iterate->cls, get_member_session_public_key(session), session); +} + +int +iterate_member_sessions (struct GNUNET_MESSENGER_Member *member, GNUNET_MESSENGER_MemberIteratorCallback it, void *cls) +{ + GNUNET_assert ((member) && (member->sessions) && (it)); + + struct GNUNET_MESSENGER_ClosureIterateSessions iterate; + + iterate.it = it; + iterate.cls = cls; + + return GNUNET_CONTAINER_multihashmap_iterate(member->sessions, iterate_member_sessions_it, &iterate); +} diff --git a/src/messenger/gnunet-service-messenger_member.h b/src/messenger/gnunet-service-messenger_member.h new file mode 100644 index 000000000..fb2e57cfb --- /dev/null +++ b/src/messenger/gnunet-service-messenger_member.h @@ -0,0 +1,170 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020--2021 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_member.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_MEMBER_H +#define GNUNET_SERVICE_MESSENGER_MEMBER_H + +#include "messenger_api_contact.h" + +#include "gnunet-service-messenger_list_messages.h" +#include "gnunet-service-messenger_member_store.h" +#include "messenger_api_message.h" +#include "messenger_api_util.h" + +struct GNUNET_MESSENGER_Member +{ + struct GNUNET_MESSENGER_MemberStore *store; + struct GNUNET_ShortHashCode id; + + struct GNUNET_CONTAINER_MultiHashMap *sessions; +}; + +/** + * Creates and allocates a new member of a room with an optionally defined or + * random id. + * + * If the creation fails, NULL gets returned. + * + * @param[in/out] store Member store + * @param[in] id Member id or NULL + * @return New member or NULL + */ +struct GNUNET_MESSENGER_Member* +create_member (struct GNUNET_MESSENGER_MemberStore *store, const struct GNUNET_ShortHashCode *id); + +/** + * Destroys a member and frees its memory fully. + * + * @param[in/out] member Member + */ +void +destroy_member (struct GNUNET_MESSENGER_Member *member); + +/** + * Returns the current id of a given member. + * + * @param[in] member Member + * @return Member id + */ +const struct GNUNET_ShortHashCode* +get_member_id (const struct GNUNET_MESSENGER_Member *member); + +/** + * Loads data from a directory into a new allocated and created member + * of a store if the required information can be read from the content + * of the given directory. + * + * @param[out] store Member store + * @param[in] directory Path to a directory + */ +void +load_member (struct GNUNET_MESSENGER_MemberStore *store, const char *directory); + +/** + * Loads data about next sessions from a directory into an empty loaded + * member which does not contain a fully built session graph yet. + * + * @param[in/out] member Member + * @param[in] directory Path to a directory + */ +void +load_member_next_sessions (const struct GNUNET_MESSENGER_Member *member, const char *directory); + +/** + * Saves data from a member into a directory which + * can be load to restore the member completely. + * + * @param[in] member Member + * @param[in] directory Path to a directory + */ +void +save_member (struct GNUNET_MESSENGER_Member *member, const char *directory); + +/** + * Synchronizes contacts between all sessions from a given member + * and other sessions which are linked to them. + * + * @param[in/out] member Member + */ +void +sync_member_contacts (struct GNUNET_MESSENGER_Member *member); + +/** + * Returns the member session of a member identified by a given public key. + * If the member does not provide a session with the given key, NULL gets returned. + * + * @param[in] member Member + * @param[in] public_key Public key of EGO + * @return Member session + */ +struct GNUNET_MESSENGER_MemberSession* +get_member_session (const struct GNUNET_MESSENGER_Member *member, const struct GNUNET_IDENTITY_PublicKey *public_key); + +/** + * Returns the member session of a member using a public key which can verify + * the signature of a given message and its hash. If the member does + * not provide a matching session, NULL gets returned. + * + * @param[in] member Member + * @param[in] message Message + * @param[in] hash Hash of message + * @return Member session + */ +struct GNUNET_MESSENGER_MemberSession* +get_member_session_of (struct GNUNET_MESSENGER_Member *member, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +/** + * Adds a given member session to its member. + * + * @param[in/out] member Member + * @param[in/out] session Member session + */ +void +add_member_session (struct GNUNET_MESSENGER_Member *member, struct GNUNET_MESSENGER_MemberSession *session); + +/** + * Removes a given member session from its member. + * + * @param[in/out] member Member + * @param[in/out] session Member session + */ +void +remove_member_session (struct GNUNET_MESSENGER_Member *member, struct GNUNET_MESSENGER_MemberSession *session); + +/** + * Iterate through all member sessions currently connected to a given member + * and call the provided iterator callback with a selected closure. The function + * will return the amount of member sessions it iterated through. + * + * @param[in/out] member Member + * @param[in] it Iterator callback + * @param[in/out] cls Closure + * @return Amount of sessions iterated through + */ +int +iterate_member_sessions (struct GNUNET_MESSENGER_Member *member, GNUNET_MESSENGER_MemberIteratorCallback it, void* cls); + +#endif //GNUNET_SERVICE_MESSENGER_MEMBER_H diff --git a/src/messenger/gnunet-service-messenger_member_session.c b/src/messenger/gnunet-service-messenger_member_session.c new file mode 100644 index 000000000..636285d93 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_member_session.c @@ -0,0 +1,707 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_member_session.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_member_session.h" + +#include "gnunet-service-messenger_room.h" +#include "gnunet-service-messenger_message_store.h" + +#include "messenger_api_contact_store.h" + +struct GNUNET_MESSENGER_MemberSession* +create_member_session (struct GNUNET_MESSENGER_Member *member, + const struct GNUNET_IDENTITY_PublicKey *pubkey) +{ + if ((!member) || (!pubkey) || (!(member->store))) + return NULL; + + struct GNUNET_MESSENGER_MemberSession *session = GNUNET_new(struct GNUNET_MESSENGER_MemberSession); + session->member = member; + + GNUNET_memcpy(&(session->public_key), pubkey, sizeof(session->public_key)); + + get_context_from_member ( + get_member_session_key (session), + get_member_session_id (session), + &(session->context) + ); + + struct GNUNET_MESSENGER_ContactStore *store = get_member_contact_store(session->member->store); + + session->contact = get_store_contact( + store, + get_member_session_context (session), + get_member_session_public_key (session) + ); + + if (!(session->contact)) + { + GNUNET_free(session); + return NULL; + } + + increase_contact_rc (session->contact); + + session->history = GNUNET_CONTAINER_multihashmap_create(8, GNUNET_NO); + + init_list_messages(&(session->messages)); + + session->next = NULL; + + session->closed = GNUNET_NO; + session->completed = GNUNET_NO; + + return session; +} + +static void +check_member_session_completion (struct GNUNET_MESSENGER_MemberSession *session) +{ + GNUNET_assert (session); + + if (!session->messages.tail) + { + session->completed = GNUNET_YES; + goto completion; + } + + const struct GNUNET_HashCode* start = &(session->messages.head->hash); + const struct GNUNET_HashCode* end = &(session->messages.tail->hash); + + struct GNUNET_MESSENGER_ListMessages level; + init_list_messages(&level); + + add_to_list_messages(&level, end); + + struct GNUNET_MESSENGER_MessageStore *store = get_room_message_store(session->member->store->room); + + struct GNUNET_MESSENGER_ListMessages list; + init_list_messages(&list); + + while (level.head) + { + struct GNUNET_MESSENGER_ListMessage *element; + + for (element = level.head; element; element = element->next) + { + const struct GNUNET_MESSENGER_MessageLink *link = get_store_message_link( + store, &(element->hash), GNUNET_NO + ); + + if (!link) + continue; + + add_to_list_messages(&list, &(link->first)); + + if (GNUNET_YES == link->multiple) + add_to_list_messages(&list, &(link->second)); + } + + clear_list_messages(&level); + + for (element = list.head; element; element = element->next) + if (GNUNET_YES == check_member_session_history(session, &(element->hash), GNUNET_YES)) + break; + + if (element) + if (0 != GNUNET_CRYPTO_hash_cmp(&(element->hash), start)) + add_to_list_messages(&level, &(element->hash)); + else + session->completed = GNUNET_YES; + else + copy_list_messages(&level, &list); + + clear_list_messages(&list); + } + +completion: + if (GNUNET_YES == is_member_session_completed(session)) + { + GNUNET_CONTAINER_multihashmap_destroy (session->history); + + struct GNUNET_MESSENGER_ContactStore *store = get_member_contact_store(session->member->store); + + if ((session->contact) && (GNUNET_YES == decrease_contact_rc (session->contact))) + remove_store_contact ( + store, + session->contact, + get_member_session_context(session) + ); + + session->contact = NULL; + } +} + +static int +iterate_copy_history (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_MemberSession *next = cls; + + GNUNET_CONTAINER_multihashmap_put(next->history, key, (value? next : NULL), + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + + return GNUNET_YES; +} + +struct GNUNET_MESSENGER_MemberSession* +switch_member_session (struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + if ((!session) || (!message) || (!hash)) + return NULL; + + GNUNET_assert((GNUNET_MESSENGER_KIND_ID == message->header.kind) || + (GNUNET_MESSENGER_KIND_KEY == message->header.kind)); + + struct GNUNET_MESSENGER_MemberSession *next = GNUNET_new(struct GNUNET_MESSENGER_MemberSession); + + if (GNUNET_MESSENGER_KIND_ID == message->header.kind) + next->member = add_store_member(session->member->store, &(message->body.id.id)); + else + next->member = session->member; + + if (GNUNET_MESSENGER_KIND_KEY == message->header.kind) + GNUNET_memcpy(&(next->public_key), &(message->body.key.key), sizeof(next->public_key)); + else + GNUNET_memcpy(&(next->public_key), get_member_session_public_key(session), sizeof(next->public_key)); + + get_context_from_member ( + get_member_session_key (next), + get_member_session_id (next), + &(next->context) + ); + + update_store_contact( + get_member_contact_store(next->member->store), + get_member_session_contact(session), + get_member_session_context(session), + get_member_session_context(next), + get_member_session_public_key(next) + ); + + next->contact = get_member_session_contact(session); + + if (!(next->contact)) + { + GNUNET_free(next); + return NULL; + } + + increase_contact_rc (next->contact); + + next->history = GNUNET_CONTAINER_multihashmap_create( + GNUNET_CONTAINER_multihashmap_size(session->history), GNUNET_NO + ); + + GNUNET_CONTAINER_multihashmap_iterate(session->history, iterate_copy_history, next); + + init_list_messages(&(next->messages)); + copy_list_messages(&(next->messages), &(session->messages)); + + session->next = next; + next->next = NULL; + + session->closed = GNUNET_YES; + next->closed = GNUNET_NO; + next->completed = GNUNET_NO; + + check_member_session_completion (session); + + return next; +} + +void +destroy_member_session(struct GNUNET_MESSENGER_MemberSession* session) +{ + GNUNET_assert (session); + + GNUNET_CONTAINER_multihashmap_destroy (session->history); + + clear_list_messages (&(session->messages)); + + struct GNUNET_MESSENGER_Contact *contact = get_member_session_contact (session); + + if ((contact) && (GNUNET_YES == decrease_contact_rc (contact))) + remove_store_contact ( + get_member_contact_store(session->member->store), + contact, + get_member_session_context(session) + ); + + GNUNET_free(session); +} + +int +reset_member_session (struct GNUNET_MESSENGER_MemberSession* session, + const struct GNUNET_HashCode *hash) +{ + GNUNET_assert ((session) && (hash)); + + struct GNUNET_MESSENGER_ContactStore *store = get_member_contact_store(session->member->store); + struct GNUNET_MESSENGER_Contact *contact = get_store_contact( + store, + get_member_session_context (session), + get_member_session_public_key (session) + ); + + if (!contact) + return GNUNET_SYSERR; + + if (contact == session->contact) + goto clear_messages; + + session->contact = contact; + increase_contact_rc (session->contact); + +clear_messages: + clear_list_messages(&(session->messages)); + add_to_list_messages(&(session->messages), hash); + + session->next = NULL; + session->closed = GNUNET_NO; + session->completed = GNUNET_NO; + + return GNUNET_OK; +} + +void +close_member_session (struct GNUNET_MESSENGER_MemberSession* session) +{ + GNUNET_assert (session); + + session->closed = GNUNET_YES; + check_member_session_completion (session); +} + +int +is_member_session_closed (const struct GNUNET_MESSENGER_MemberSession* session) +{ + GNUNET_assert(session); + + return session->closed; +} + +int +is_member_session_completed (const struct GNUNET_MESSENGER_MemberSession* session) +{ + GNUNET_assert(session); + + return session->completed; +} + +const struct GNUNET_HashCode* +get_member_session_key (const struct GNUNET_MESSENGER_MemberSession* session) +{ + GNUNET_assert((session) && (session->member)); + + return get_member_store_key(session->member->store); +} + +const struct GNUNET_ShortHashCode* +get_member_session_id (const struct GNUNET_MESSENGER_MemberSession* session) +{ + GNUNET_assert(session); + + return get_member_id(session->member); +} + +const struct GNUNET_IDENTITY_PublicKey* +get_member_session_public_key (const struct GNUNET_MESSENGER_MemberSession* session) +{ + GNUNET_assert(session); + + return &(session->public_key); +} + +const struct GNUNET_HashCode* +get_member_session_context (const struct GNUNET_MESSENGER_MemberSession* session) +{ + GNUNET_assert(session); + + return &(session->context); +} + +struct GNUNET_MESSENGER_Contact* +get_member_session_contact (struct GNUNET_MESSENGER_MemberSession* session) +{ + GNUNET_assert (session); + + return session->contact; +} + +int verify_member_session_as_sender (const struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + GNUNET_assert((session) && (message) && (hash)); + + if (GNUNET_YES == is_member_session_completed(session)) + return GNUNET_SYSERR; + + if (0 != GNUNET_memcmp(get_member_session_id(session), &(message->header.sender_id))) + return GNUNET_SYSERR; + + return verify_message(message, hash, get_member_session_public_key(session)); +} + +int +check_member_session_history (const struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_HashCode *hash, int ownership) +{ + GNUNET_assert((session) && (hash)); + + if (GNUNET_YES == ownership) + return (NULL != GNUNET_CONTAINER_multihashmap_get(session->history, hash)? GNUNET_YES : GNUNET_NO); + else + return GNUNET_CONTAINER_multihashmap_contains(session->history, hash); +} + +static void +update_member_chain_history (struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_HashCode *hash, int ownership) +{ + GNUNET_assert ((session) && (hash)); + + if ((GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(session->history, hash, (GNUNET_YES == ownership? session : NULL), + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) && (session->next)) + update_member_chain_history (session->next, hash, ownership); +} + +void +update_member_session_history (struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash) +{ + GNUNET_assert((session) && (message) && (hash)); + + if (GNUNET_YES == is_member_session_completed(session)) + return; + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Updating sessions history (%s) += (%s)\n", + GNUNET_sh2s(get_member_session_id(session)), GNUNET_h2s(hash)); + + if (GNUNET_OK == verify_member_session_as_sender (session, message, hash)) + { + if (GNUNET_YES == is_message_session_bound (message)) + add_to_list_messages(&(session->messages), hash); + + update_member_chain_history (session, hash, GNUNET_YES); + } + else + update_member_chain_history (session, hash, GNUNET_NO); + + if (GNUNET_YES == session->closed) + check_member_session_completion(session); +} + +static void +clear_member_chain_history (struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_HashCode *hash) +{ + GNUNET_assert ((session) && (hash)); + + if ((0 < GNUNET_CONTAINER_multihashmap_remove_all(session->history, hash)) && (session->next)) + clear_member_session_history(session->next, hash); +} + +void +clear_member_session_history (struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_HashCode *hash) +{ + GNUNET_assert((session) && (hash)); + + clear_member_chain_history (session, hash); +} + +struct GNUNET_MESSENGER_MemberSessionHistoryEntry +{ + struct GNUNET_HashCode hash; + unsigned char ownership; +}; + +static void +load_member_session_history (struct GNUNET_MESSENGER_MemberSession *session, const char *path) +{ + GNUNET_assert((session) && (path)); + + if (GNUNET_YES != GNUNET_DISK_file_test (path)) + return; + + enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE); + + struct GNUNET_DISK_FileHandle *handle = GNUNET_DISK_file_open( + path, GNUNET_DISK_OPEN_READ, permission + ); + + if (!handle) + return; + + GNUNET_DISK_file_seek(handle, 0, GNUNET_DISK_SEEK_SET); + + struct GNUNET_MESSENGER_MemberSessionHistoryEntry entry; + ssize_t len; + + int status; + + do { + len = GNUNET_DISK_file_read(handle, &(entry.hash), sizeof(entry.hash)); + + if (len != sizeof(entry.hash)) + break; + + len = GNUNET_DISK_file_read(handle, &(entry.ownership), sizeof(entry.ownership)); + + if (len != sizeof(entry.ownership)) + break; + + status = GNUNET_CONTAINER_multihashmap_put(session->history, &(entry.hash), (entry.ownership? session : NULL), + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + } while (status == GNUNET_OK); + + GNUNET_DISK_file_close(handle); +} + +void +load_member_session (struct GNUNET_MESSENGER_Member *member, const char *directory) +{ + GNUNET_assert ((member) && (directory)); + + char *config_file; + GNUNET_asprintf (&config_file, "%s%s", directory, "session.cfg"); + + struct GNUNET_MESSENGER_MemberSession *session = NULL; + + if (GNUNET_YES != GNUNET_DISK_file_test (config_file)) + goto free_config; + + struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create (); + + if (GNUNET_OK == GNUNET_CONFIGURATION_parse (cfg, config_file)) + { + char *key_data; + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, "session", "key", &key_data)) + goto destroy_config; + + struct GNUNET_IDENTITY_PublicKey key; + + enum GNUNET_GenericReturnValue key_return = GNUNET_IDENTITY_public_key_from_string(key_data, &key); + + GNUNET_free(key_data); + + if (GNUNET_OK != key_return) + goto destroy_config; + + session = create_member_session(member, &key); + } + +destroy_config: + GNUNET_CONFIGURATION_destroy (cfg); + +free_config: + GNUNET_free(config_file); + + if (!session) + return; + + char *history_file; + GNUNET_asprintf (&history_file, "%s%s", directory, "history.map"); + + load_member_session_history (session, history_file); + GNUNET_free(history_file); + + char *messages_file; + GNUNET_asprintf (&messages_file, "%s%s", directory, "messages.list"); + + load_list_messages(&(session->messages), messages_file); + GNUNET_free(messages_file); + + add_member_session(member, session); +} + +static struct GNUNET_MESSENGER_MemberSession* +get_cycle_safe_next_session (struct GNUNET_MESSENGER_MemberSession *session, struct GNUNET_MESSENGER_MemberSession *next) +{ + if (!next) + return NULL; + + struct GNUNET_MESSENGER_MemberSession *check = next; + + do { + if (check == session) + return NULL; + + check = check->next; + } while (check); + + return next; +} + +void +load_member_session_next (struct GNUNET_MESSENGER_MemberSession *session, const char *directory) +{ + GNUNET_assert ((session) && (directory)); + + char *config_file; + GNUNET_asprintf (&config_file, "%s%s", directory, "session.cfg"); + + if (GNUNET_YES != GNUNET_DISK_file_test (config_file)) + goto free_config; + + struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create (); + + if (GNUNET_OK == GNUNET_CONFIGURATION_parse (cfg, config_file)) + { + char *key_data; + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, "session", "next_key", &key_data)) + goto destroy_config; + + struct GNUNET_IDENTITY_PublicKey next_key; + + enum GNUNET_GenericReturnValue key_return = GNUNET_IDENTITY_public_key_from_string(key_data, &next_key); + + GNUNET_free(key_data); + + if (GNUNET_OK != key_return) + goto destroy_config; + + struct GNUNET_ShortHashCode next_id; + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_data (cfg, "session", "next_id", &next_id, sizeof(next_id))) + goto destroy_config; + + struct GNUNET_MESSENGER_Member *member = get_store_member(session->member->store, &next_id); + + session->next = get_cycle_safe_next_session( + session, member? get_member_session (member, &next_key) : NULL + ); + } + +destroy_config: + GNUNET_CONFIGURATION_destroy (cfg); + +free_config: + GNUNET_free(config_file); +} + +static int +iterate_save_member_session_history_hentries (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_DISK_FileHandle *handle = cls; + unsigned char ownership = value? GNUNET_YES : GNUNET_NO; + + GNUNET_DISK_file_write(handle, key, sizeof(*key)); + GNUNET_DISK_file_write(handle, &ownership, sizeof(ownership)); + + return GNUNET_YES; +} + +static void +save_member_session_history (struct GNUNET_MESSENGER_MemberSession *session, const char *path) +{ + GNUNET_assert((session) && (path)); + + enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE); + + struct GNUNET_DISK_FileHandle *handle = GNUNET_DISK_file_open( + path, GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE, permission + ); + + if (!handle) + return; + + GNUNET_DISK_file_seek(handle, 0, GNUNET_DISK_SEEK_SET); + + GNUNET_CONTAINER_multihashmap_iterate( + session->history, + iterate_save_member_session_history_hentries, + handle + ); + + GNUNET_DISK_file_sync(handle); + GNUNET_DISK_file_close(handle); +} + +void +save_member_session (struct GNUNET_MESSENGER_MemberSession *session, const char *directory) +{ + GNUNET_assert ((session) && (directory)); + + char *config_file; + GNUNET_asprintf (&config_file, "%s%s", directory, "session.cfg"); + + struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create (); + + char *key_data = GNUNET_IDENTITY_public_key_to_string(get_member_session_public_key(session)); + + if (key_data) + { + GNUNET_CONFIGURATION_set_value_string (cfg, "session", "key", key_data); + + GNUNET_free(key_data); + } + + if (session->next) + { + const struct GNUNET_ShortHashCode *next_id = get_member_session_id(session->next); + + char *next_id_data = GNUNET_STRINGS_data_to_string_alloc (next_id, sizeof(*next_id)); + + if (next_id_data) + { + GNUNET_CONFIGURATION_set_value_string (cfg, "session", "next_id", next_id_data); + + GNUNET_free(next_id_data); + } + + key_data = GNUNET_IDENTITY_public_key_to_string(get_member_session_public_key(session->next)); + + if (key_data) + { + GNUNET_CONFIGURATION_set_value_string (cfg, "session", "next_key", key_data); + + GNUNET_free(key_data); + } + } + + GNUNET_CONFIGURATION_write (cfg, config_file); + GNUNET_CONFIGURATION_destroy (cfg); + + GNUNET_free(config_file); + + char *history_file; + GNUNET_asprintf (&history_file, "%s%s", directory, "history.map"); + + save_member_session_history (session, history_file); + GNUNET_free(history_file); + + char *messages_file; + GNUNET_asprintf (&messages_file, "%s%s", directory, "messages.list"); + + save_list_messages(&(session->messages), messages_file); + GNUNET_free(messages_file); +} diff --git a/src/messenger/gnunet-service-messenger_member_session.h b/src/messenger/gnunet-service-messenger_member_session.h new file mode 100644 index 000000000..dd753be2a --- /dev/null +++ b/src/messenger/gnunet-service-messenger_member_session.h @@ -0,0 +1,275 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_member_session.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_MEMBER_SESSION_H +#define GNUNET_SERVICE_MESSENGER_MEMBER_SESSION_H + +#include "platform.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_container_lib.h" +#include "gnunet_identity_service.h" + +#include "gnunet-service-messenger_member.h" + +#include "messenger_api_contact.h" + +struct GNUNET_MESSENGER_MemberSession { + struct GNUNET_MESSENGER_Member *member; + + struct GNUNET_IDENTITY_PublicKey public_key; + struct GNUNET_HashCode context; + + struct GNUNET_MESSENGER_Contact *contact; + + struct GNUNET_CONTAINER_MultiHashMap *history; + struct GNUNET_MESSENGER_ListMessages messages; + + struct GNUNET_MESSENGER_MemberSession* next; + + int closed; + int completed; +}; + +/** + * Creates and allocates a new member session of a member with a given + * public key. + * + * If the creation fails, NULL gets returned. + * + * @param[in/out] member Member + * @param[in] pubkey Public key of EGO + * @return New member session + */ +struct GNUNET_MESSENGER_MemberSession* +create_member_session (struct GNUNET_MESSENGER_Member *member, + const struct GNUNET_IDENTITY_PublicKey *pubkey); + +/** + * Creates and allocates a new member session closing and replacing a given + * other session of the same member. The new session could have significant + * changes to the members public key or its member id depending on the used + * message to switch session. The new session will be linked to the old + * one. + * + * @param[in/out] session Old member session + * @param[in] message Message + * @param[in] hash Hash of message + * @return New member session + */ +struct GNUNET_MESSENGER_MemberSession* +switch_member_session (struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +/** + * Destroys a member session and frees its memory fully. + * + * @param[in/out] session Member session + */ +void +destroy_member_session(struct GNUNET_MESSENGER_MemberSession* session); + +/** + * Resets a given member session which re-opens a member + * session for new usage. Every connection to other sessions will be + * be dropped. The member sessions messages will be cleared but old + * history from uncompleted sessions however can be reused! + * + * @param[in/out] session Member session + * @param[in] hash Hash of initial message (JOIN message!) + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +int +reset_member_session (struct GNUNET_MESSENGER_MemberSession* session, + const struct GNUNET_HashCode *hash); + +/** + * Closes a given member session which opens the request + * for completion of the given member session. + * + * Closing a session may complete a session and can't be used without + * a reset! ( @see #reset_member_session() ) + * + * @param[in/out] session Member session + */ +void +close_member_session (struct GNUNET_MESSENGER_MemberSession* session); + +/** + * Returns if the given member session has been closed. + * + * @param[in] session Member session + * @return #GNUNET_YES or #GNUNET_NO + */ +int +is_member_session_closed (const struct GNUNET_MESSENGER_MemberSession* session); + +/** + * Returns if the given member session has been completed. + * + * A completed member session can't verify any message as its own and + * it won't add any message to its history. + * + * @param[in] session Member session + * @return #GNUNET_YES or #GNUNET_NO + */ +int +is_member_session_completed (const struct GNUNET_MESSENGER_MemberSession* session); + +/** + * Returns the key of the room a given member session belongs to. + * + * @param[in] session Member session + * @return Key of room + */ +const struct GNUNET_HashCode* +get_member_session_key (const struct GNUNET_MESSENGER_MemberSession* session); + +/** + * Returns the member id of a given member session. + * + * @param[in] session Member session + * @return Member id + */ +const struct GNUNET_ShortHashCode* +get_member_session_id (const struct GNUNET_MESSENGER_MemberSession* session); + +/** + * Returns the public key from an EGO of a given member session. + * + * @param[in] session Member session + * @return Public key of EGO + */ +const struct GNUNET_IDENTITY_PublicKey* +get_member_session_public_key (const struct GNUNET_MESSENGER_MemberSession* session); + +/** + * Returns the member context of a given member session. + * + * @param[in] session Member session + * @return Member context as hash + */ +const struct GNUNET_HashCode* +get_member_session_context (const struct GNUNET_MESSENGER_MemberSession* session); + +/** + * Retruns the contact which is connected to a given member session. + * + * @param[in] session Member session + * @return Contact + */ +struct GNUNET_MESSENGER_Contact* +get_member_session_contact (struct GNUNET_MESSENGER_MemberSession* session); + +/** + * Verifies a given member session as sender of a selected message and + * its hash. The function returns #GNUNET_OK if the message session is verified + * as sender, otherwise #GNUNET_SYSERR. + * + * @see #is_member_session_completed() for verification. + * + * @param[in] session Member session + * @param[in] message Message + * @param[in] hash Hash of message + * @return #GNUNET_OK on success, otherwise #GNUNET_SYSERR + */ +int +verify_member_session_as_sender (const struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +/** + * Checks the history of a session for a specific message which is identified + * by its hash and if the ownership flag is set, if the message is + * owned by the sessions contact. + * + * @param[in] session Member session + * @param[in] hash Hash of message + * @param[in] ownership Ownership flag + * @return #GNUNET_YES if found, otherwise #GNUNET_NO + */ +int +check_member_session_history (const struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_HashCode *hash, int ownership); + +/** + * Adds a given message to the history of a session using the messages + * hash. The ownership will be set automatically. + * + * @see #is_member_session_completed() for updating a history. + * + * @param[in/out] session Member session + * @param[in] message Message + * @param[in] hash Hash of message + */ +void +update_member_session_history (struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash); + +/** + * Removes a message from the history of a session using the messages + * hash. + * + * @param[in/out] session Member session + * @param[in] hash Hash of message + */ +void +clear_member_session_history (struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_HashCode *hash); + +/** + * Loads data from a directory into a new allocated and created member + * session of a member if the required information can be read from the + * content of the given directory. + * + * @param[out] member Member + * @param[in] directory Path to a directory + */ +void +load_member_session (struct GNUNET_MESSENGER_Member *member, const char *directory); + +/** + * Loads the connection from one session to another through the + * next attribute. Necessary information will be loaded from a configuration + * file inside of a given directory. + * + * @param[in/out] session Member session + * @param[in] directory Path to a directory + */ +void +load_member_session_next (struct GNUNET_MESSENGER_MemberSession *session, const char *directory); + +/** + * Saves data from a member session into a directory which can be + * load to restore the member session completely. + * + * @param[in] session Member session + * @param[in] directory Path to a directory + */ +void +save_member_session (struct GNUNET_MESSENGER_MemberSession *session, const char *directory); + +#endif //GNUNET_SERVICE_MESSENGER_MEMBER_SESSION_H diff --git a/src/messenger/gnunet-service-messenger_member_store.c b/src/messenger/gnunet-service-messenger_member_store.c new file mode 100644 index 000000000..2925965d4 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_member_store.c @@ -0,0 +1,250 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020--2021 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_member_store.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_member_store.h" + +#include "gnunet-service-messenger_member.h" +#include "gnunet-service-messenger_service.h" +#include "gnunet-service-messenger_room.h" + +void +init_member_store (struct GNUNET_MESSENGER_MemberStore *store, struct GNUNET_MESSENGER_SrvRoom *room) +{ + GNUNET_assert ((store) && (room)); + + store->room = room; + store->members = GNUNET_CONTAINER_multishortmap_create(8, GNUNET_NO); +} + +static int +iterate_destroy_members (void *cls, const struct GNUNET_ShortHashCode *key, void *value) +{ + struct GNUNET_MESSENGER_Member *member = value; + destroy_member(member); + return GNUNET_YES; +} + +void +clear_member_store (struct GNUNET_MESSENGER_MemberStore *store) +{ + GNUNET_assert ((store) && (store->members)); + + GNUNET_CONTAINER_multishortmap_iterate (store->members, iterate_destroy_members, NULL); + GNUNET_CONTAINER_multishortmap_destroy (store->members); +} + + +struct GNUNET_MESSENGER_ContactStore* +get_member_contact_store (struct GNUNET_MESSENGER_MemberStore *store) +{ + GNUNET_assert ((store) && (store->room)); + + struct GNUNET_MESSENGER_SrvRoom *room = store->room; + + return get_service_contact_store(room->service); +} + +const struct GNUNET_HashCode* +get_member_store_key (const struct GNUNET_MESSENGER_MemberStore *store) +{ + GNUNET_assert (store); + + return get_room_key((const struct GNUNET_MESSENGER_SrvRoom*) store->room); +} + +static int +callback_scan_for_members (void *cls, const char *filename) +{ + struct GNUNET_MESSENGER_MemberStore *store = cls; + + if (GNUNET_YES == GNUNET_DISK_directory_test (filename, GNUNET_YES)) + { + char *directory; + + GNUNET_asprintf (&directory, "%s%c", filename, DIR_SEPARATOR); + + load_member(store, directory); + + GNUNET_free(directory); + } + + return GNUNET_OK; +} + +static int +iterate_load_next_member_sessions (void *cls, const struct GNUNET_ShortHashCode *id, void *value) +{ + const char *sync_dir = cls; + + struct GNUNET_MESSENGER_Member *member = value; + + if (!member) + return GNUNET_YES; + + char *member_dir; + GNUNET_asprintf (&member_dir, "%s%s%c", sync_dir, GNUNET_sh2s(id), DIR_SEPARATOR); + + if (GNUNET_YES == GNUNET_DISK_directory_test (member_dir, GNUNET_YES)) + load_member_next_sessions (member, member_dir); + + GNUNET_free(member_dir); + return GNUNET_YES; +} + +static int +iterate_sync_member_contacts (void *cls, const struct GNUNET_ShortHashCode *id, void *value) +{ + struct GNUNET_MESSENGER_Member *member = value; + + if (!member) + return GNUNET_YES; + + sync_member_contacts (member); + return GNUNET_YES; +} + +void +load_member_store (struct GNUNET_MESSENGER_MemberStore *store, const char *directory) +{ + GNUNET_assert ((store) && (directory)); + + char *scan_dir; + GNUNET_asprintf (&scan_dir, "%s%s%c", directory, "members", DIR_SEPARATOR); + + if (GNUNET_OK == GNUNET_DISK_directory_test (scan_dir, GNUNET_YES)) + GNUNET_DISK_directory_scan (scan_dir, callback_scan_for_members, store); + + GNUNET_CONTAINER_multishortmap_iterate(store->members, iterate_load_next_member_sessions, scan_dir); + GNUNET_CONTAINER_multishortmap_iterate(store->members, iterate_sync_member_contacts, NULL); + + GNUNET_free(scan_dir); +} + +static int +iterate_save_members (void *cls, const struct GNUNET_ShortHashCode *id, void *value) +{ + const char *save_dir = cls; + + struct GNUNET_MESSENGER_Member *member = value; + + if (!member) + return GNUNET_YES; + + char *member_dir; + GNUNET_asprintf (&member_dir, "%s%s%c", save_dir, GNUNET_sh2s(id), DIR_SEPARATOR); + + if ((GNUNET_YES == GNUNET_DISK_directory_test (member_dir, GNUNET_NO)) || + (GNUNET_OK == GNUNET_DISK_directory_create (member_dir))) + save_member(member, member_dir); + + GNUNET_free(member_dir); + return GNUNET_YES; +} + +void +save_member_store (struct GNUNET_MESSENGER_MemberStore *store, const char *directory) +{ + GNUNET_assert ((store) && (directory)); + + char* save_dir; + GNUNET_asprintf (&save_dir, "%s%s%c", directory, "members", DIR_SEPARATOR); + + if ((GNUNET_YES == GNUNET_DISK_directory_test (save_dir, GNUNET_NO)) || + (GNUNET_OK == GNUNET_DISK_directory_create (save_dir))) + GNUNET_CONTAINER_multishortmap_iterate(store->members, iterate_save_members, save_dir); + + GNUNET_free(save_dir); +} + +struct GNUNET_MESSENGER_Member* +get_store_member (const struct GNUNET_MESSENGER_MemberStore *store, const struct GNUNET_ShortHashCode *id) +{ + GNUNET_assert ((store) && (store->members) && (id)); + + return GNUNET_CONTAINER_multishortmap_get (store->members, id); +} + +struct GNUNET_MESSENGER_Member* +get_store_member_of (struct GNUNET_MESSENGER_MemberStore *store, const struct GNUNET_MESSENGER_Message *message) +{ + if ((GNUNET_MESSENGER_KIND_INFO == message->header.kind) || + (GNUNET_MESSENGER_KIND_JOIN == message->header.kind)) + return add_store_member(store, &(message->header.sender_id)); + else + return get_store_member(store, &(message->header.sender_id)); +} + +struct GNUNET_MESSENGER_Member* +add_store_member (struct GNUNET_MESSENGER_MemberStore *store, const struct GNUNET_ShortHashCode *id) +{ + GNUNET_assert ((store) && (store->members)); + + struct GNUNET_MESSENGER_Member *member = id? get_store_member(store, id) : NULL; + + if (member) + return member; + + member = create_member(store, id); + + if (!member) + return NULL; + + if (GNUNET_OK != GNUNET_CONTAINER_multishortmap_put (store->members, get_member_id(member), member, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + { + destroy_member(member); + return NULL; + } + + return member; +} + +struct GNUNET_MESSENGER_ClosureIterateMembers { + GNUNET_MESSENGER_MemberIteratorCallback it; + void *cls; +}; + +static int +iterate_store_members_it (void *cls, const struct GNUNET_ShortHashCode *key, void *value) +{ + struct GNUNET_MESSENGER_ClosureIterateMembers *iterate = cls; + struct GNUNET_MESSENGER_Member *member = value; + + return iterate_member_sessions(member, iterate->it, iterate->cls); +} + +int +iterate_store_members (struct GNUNET_MESSENGER_MemberStore *store, GNUNET_MESSENGER_MemberIteratorCallback it, + void* cls) +{ + GNUNET_assert ((store) && (store->members) && (it)); + + struct GNUNET_MESSENGER_ClosureIterateMembers iterate; + + iterate.it = it; + iterate.cls = cls; + + return GNUNET_CONTAINER_multishortmap_iterate(store->members, iterate_store_members_it, &iterate); +} diff --git a/src/messenger/gnunet-service-messenger_member_store.h b/src/messenger/gnunet-service-messenger_member_store.h new file mode 100644 index 000000000..859e4683d --- /dev/null +++ b/src/messenger/gnunet-service-messenger_member_store.h @@ -0,0 +1,151 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020--2021 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_member_store.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_MEMBER_STORE_H +#define GNUNET_SERVICE_MESSENGER_MEMBER_STORE_H + +#include "platform.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_container_lib.h" +#include "gnunet_identity_service.h" +#include "messenger_api_message.h" + +struct GNUNET_MESSENGER_SrvRoom; + +struct GNUNET_MESSENGER_Member; +struct GNUNET_MESSENGER_MemberSession; + +struct GNUNET_MESSENGER_MemberStore +{ + struct GNUNET_MESSENGER_SrvRoom *room; + + struct GNUNET_CONTAINER_MultiShortmap *members; +}; + +typedef int (*GNUNET_MESSENGER_MemberIteratorCallback) ( + void *cls, + const struct GNUNET_IDENTITY_PublicKey *public_key, + struct GNUNET_MESSENGER_MemberSession *session); + +/** + * Initializes a member store as fully empty connected to a room. + * + * @param[out] store Member store + * @param room Room + */ +void +init_member_store (struct GNUNET_MESSENGER_MemberStore *store, struct GNUNET_MESSENGER_SrvRoom *room); + +/** + * Clears a member store, wipes its content and deallocates its memory. + * + * @param[in/out] store Member store + */ +void +clear_member_store (struct GNUNET_MESSENGER_MemberStore *store); + +/** + * Returns the used contact store of a given member store. + * + * @param[in/out] store Member store + * @return Contact store + */ +struct GNUNET_MESSENGER_ContactStore* +get_member_contact_store (struct GNUNET_MESSENGER_MemberStore *store); + +/** + * Returns the shared secret you need to access a room of the store. + * + * @param[in] store Member store + * @return Shared secret + */ +const struct GNUNET_HashCode* +get_member_store_key (const struct GNUNET_MESSENGER_MemberStore *store); + +/** + * Loads members from a directory into a member store. + * + * @param[out] store Member store + * @param[in] directory Path to a directory + */ +void +load_member_store (struct GNUNET_MESSENGER_MemberStore *store, const char *directory); + +/** + * Saves members from a member store into a directory. + * + * @param[in] store Member store + * @param[in] directory Path to a directory + */ +void +save_member_store (struct GNUNET_MESSENGER_MemberStore *store, const char *directory); + +/** + * Returns the member in a store identified by a given id. If the store + * does not contain a member with the given id, NULL gets returned. + * + * @param[in] store Member store + * @param[in] id Member id + * @return Member or NULL + */ +struct GNUNET_MESSENGER_Member* +get_store_member (const struct GNUNET_MESSENGER_MemberStore *store, const struct GNUNET_ShortHashCode *id); + +/** + * Returns the member of a store using a sender id of a given message. + * If the member does not provide a matching session, NULL gets returned. + * + * @param[in/out] store Member store + * @param[in] message Message + * @return Member or NULL + */ +struct GNUNET_MESSENGER_Member* +get_store_member_of (struct GNUNET_MESSENGER_MemberStore *store, const struct GNUNET_MESSENGER_Message *message); + +/** + * Adds a member to a store under a specific id and returns it on success. + * + * @param[in/out] store Member store + * @param[in] id Member id + * @return Member or NULL + */ +struct GNUNET_MESSENGER_Member* +add_store_member (struct GNUNET_MESSENGER_MemberStore *store, const struct GNUNET_ShortHashCode *id); + +/** + * Iterate through all member sessions currently connected to the members of the given + * member store and call the provided iterator callback with a selected closure. + * The function will return the amount of members it iterated through. + * + * @param[in/out] store Member store + * @param[in] it Iterator callback + * @param[in/out] cls Closure + * @return Amount of members iterated through + */ +int +iterate_store_members (struct GNUNET_MESSENGER_MemberStore *store, GNUNET_MESSENGER_MemberIteratorCallback it, + void* cls); + +#endif //GNUNET_SERVICE_MESSENGER_MEMBER_STORE_H diff --git a/src/messenger/gnunet-service-messenger_message_handle.c b/src/messenger/gnunet-service-messenger_message_handle.c index 1652435c8..c22e51fbf 100644 --- a/src/messenger/gnunet-service-messenger_message_handle.c +++ b/src/messenger/gnunet-service-messenger_message_handle.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -25,75 +25,59 @@ #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) +static void +handle_session_switch (struct GNUNET_MESSENGER_MemberSession *session, + const 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)); + struct GNUNET_MESSENGER_MemberSession *next = switch_member_session(session, message, hash); - 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); + if (next != session) + add_member_session(next->member, next); } void -handle_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, - struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +handle_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) { - struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, &(message->header.sender_id)); + GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Member (%s) joins room (%s).\n", + GNUNET_sh2s (&(message->header.sender_id)), GNUNET_h2s(get_room_key(room))); - if (info) - clear_list_messages (&(info->session_messages)); + if (GNUNET_OK != reset_member_session(session, hash)) + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Resetting member session failed!\n"); } void -handle_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, - struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +handle_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) { - struct GNUNET_MESSENGER_SrvContact *contact = get_room_contact (room, &(message->header.sender_id)); + GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Member (%s) leaves room (%s).\n", + GNUNET_sh2s (&(message->header.sender_id)), GNUNET_h2s(get_room_key(room))); - 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); + close_member_session(session); } void -handle_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, - struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +handle_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) { - struct GNUNET_MESSENGER_SrvContact *contact = get_room_contact (room, &(message->header.sender_id)); + struct GNUNET_MESSENGER_Contact *contact = get_member_session_contact(session); - if (contact) - swap_service_contact_by_pubkey (room->service, contact, &(message->body.key.key)); + if (!contact) + return; - struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, &(message->header.sender_id)); + set_contact_name (contact, message->body.name.name); +} - if (info) - add_to_list_messages (&(info->session_messages), hash); +void +handle_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + handle_session_switch (session, message, 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) +handle_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const 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)); @@ -103,20 +87,15 @@ handle_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSEN } void -handle_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, - struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +handle_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const 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); + handle_session_switch (session, message, 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) +handle_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const 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); @@ -128,3 +107,16 @@ handle_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSEN if (room->peer_message) rebuild_room_basement_structure (room); } + +void +handle_message_delete (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_TIME_Relative delay = GNUNET_TIME_relative_ntoh (message->body.delete.delay); + struct GNUNET_TIME_Absolute action = GNUNET_TIME_absolute_ntoh (message->header.timestamp); + + action = GNUNET_TIME_absolute_add (action, delay); + delay = GNUNET_TIME_absolute_get_difference (GNUNET_TIME_absolute_get (), action); + + delete_room_message (room, session, &(message->body.delete.hash), delay); +} diff --git a/src/messenger/gnunet-service-messenger_message_handle.h b/src/messenger/gnunet-service-messenger_message_handle.h index d091e1d11..844142b77 100644 --- a/src/messenger/gnunet-service-messenger_message_handle.h +++ b/src/messenger/gnunet-service-messenger_message_handle.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -31,6 +31,7 @@ #include "gnunet-service-messenger_message_kind.h" +#include "gnunet-service-messenger_member_session.h" #include "gnunet-service-messenger_tunnel.h" #include "messenger_api_message.h" @@ -38,91 +39,104 @@ * 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 + * @param[in/out] room Room of the message + * @param[in/out] session Member session + * @param[in] message JOIN-Message + * @param[in] 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); +handle_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const 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 + * @param[in/out] room Room of the message + * @param[in/out] session Member session + * @param[in] message LEAVE-Message + * @param[in] 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); +handle_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const 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 + * @param[in/out] room Room of the message + * @param[in/out] session Member session + * @param[in] message NAME-Message + * @param[in] 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); +handle_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const 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 + * @param[in/out] room Room of the message + * @param[in/out] session Member session + * @param[in] message KEY-Message + * @param[in] 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); +handle_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const 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 + * @param[in/out] room Room of the message + * @param[in/out] session Member session + * @param[in] message PEER-Message + * @param[in] 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); +handle_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const 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 + * @param[in/out] room Room of the message + * @param[in/out] session Member session + * @param[in] message ID-Message + * @param[in] 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); +handle_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const 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 + * @param[in/out] room Room of the message + * @param[in/out] session Member session + * @param[in] message MISS-Message + * @param[in] 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); +handle_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Handles a received or sent delete message to delete a specific message from the store. + * (remove a message from the store of a room under a given delay) + * + * @param[in/out] room Room of the message + * @param[in/out] session Member session + * @param[in] message DELETE-Message + * @param[in] hash Hash of the message + */ +void +handle_message_delete (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const 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 index 9c829fe09..ef9bbfd2e 100644 --- a/src/messenger/gnunet-service-messenger_message_kind.c +++ b/src/messenger/gnunet-service-messenger_message_kind.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -24,11 +24,15 @@ */ #include "gnunet-service-messenger_message_kind.h" -#include "gnunet-service-messenger_util.h" + +#include "messenger_api_util.h" struct GNUNET_MESSENGER_Message* -create_message_info (struct GNUNET_MESSENGER_Ego *ego, struct GNUNET_CONTAINER_MultiShortmap *members) +create_message_info (const struct GNUNET_MESSENGER_Ego *ego) { + if (!ego) + return NULL; + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_INFO); if (!message) @@ -36,18 +40,17 @@ create_message_info (struct GNUNET_MESSENGER_Ego *ego, struct GNUNET_CONTAINER_M 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; - } + message->body.info.messenger_version = GNUNET_MESSENGER_VERSION; + + return message; } struct GNUNET_MESSENGER_Message* -create_message_join (struct GNUNET_MESSENGER_Ego *ego) +create_message_join (const struct GNUNET_MESSENGER_Ego *ego) { + if (!ego) + return NULL; + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_JOIN); if (!message) @@ -67,6 +70,9 @@ create_message_leave () struct GNUNET_MESSENGER_Message* create_message_name (const char *name) { + if (!name) + return NULL; + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_NAME); if (!message) @@ -79,6 +85,9 @@ create_message_name (const char *name) struct GNUNET_MESSENGER_Message* create_message_key (const struct GNUNET_IDENTITY_PrivateKey *key) { + if (!key) + return NULL; + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_KEY); if (!message) @@ -91,6 +100,9 @@ create_message_key (const struct GNUNET_IDENTITY_PrivateKey *key) struct GNUNET_MESSENGER_Message* create_message_peer (const struct GNUNET_MESSENGER_Service *service) { + if (!service) + return NULL; + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_PEER); if (!message) @@ -108,6 +120,9 @@ create_message_peer (const struct GNUNET_MESSENGER_Service *service) struct GNUNET_MESSENGER_Message* create_message_id (const struct GNUNET_ShortHashCode *unique_id) { + if (!unique_id) + return NULL; + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_ID); if (!message) @@ -121,6 +136,9 @@ create_message_id (const struct GNUNET_ShortHashCode *unique_id) struct GNUNET_MESSENGER_Message* create_message_miss (const struct GNUNET_PeerIdentity *peer) { + if (!peer) + return NULL; + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_MISS); if (!message) @@ -136,6 +154,9 @@ create_message_miss (const struct GNUNET_PeerIdentity *peer) struct GNUNET_MESSENGER_Message* create_message_merge (const struct GNUNET_HashCode *previous) { + if (!previous) + return NULL; + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_MERGE); if (!message) @@ -149,6 +170,9 @@ create_message_merge (const struct GNUNET_HashCode *previous) struct GNUNET_MESSENGER_Message* create_message_request (const struct GNUNET_HashCode *hash) { + if (!hash) + return NULL; + struct GNUNET_HashCode zero; memset (&zero, 0, sizeof(zero)); @@ -168,6 +192,9 @@ create_message_request (const struct GNUNET_HashCode *hash) struct GNUNET_MESSENGER_Message* create_message_invite (const struct GNUNET_PeerIdentity *door, const struct GNUNET_HashCode *key) { + if ((!door) || (!key)) + return NULL; + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_INVITE); if (!message) @@ -182,6 +209,9 @@ create_message_invite (const struct GNUNET_PeerIdentity *door, const struct GNUN struct GNUNET_MESSENGER_Message* create_message_text (const char *text) { + if (!text) + return NULL; + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_TEXT); if (!message) @@ -190,3 +220,20 @@ create_message_text (const char *text) message->body.text.text = GNUNET_strdup(text); return message; } + +struct GNUNET_MESSENGER_Message* +create_message_delete (const struct GNUNET_HashCode *hash, const struct GNUNET_TIME_Relative delay) +{ + if (!hash) + return NULL; + + struct GNUNET_MESSENGER_Message *message = create_message (GNUNET_MESSENGER_KIND_DELETE); + + if (!message) + return NULL; + + GNUNET_memcpy(&(message->body.delete.hash), hash, sizeof(struct GNUNET_HashCode)); + message->body.delete.delay = GNUNET_TIME_relative_hton (delay); + + return message; +} diff --git a/src/messenger/gnunet-service-messenger_message_kind.h b/src/messenger/gnunet-service-messenger_message_kind.h index dd89d0b2f..c098868c0 100644 --- a/src/messenger/gnunet-service-messenger_message_kind.h +++ b/src/messenger/gnunet-service-messenger_message_kind.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -30,31 +30,32 @@ #include "gnunet_container_lib.h" #include "gnunet_crypto_lib.h" #include "gnunet_identity_service.h" +#include "gnunet_time_lib.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. + * Creates and allocates a new info message containing the hosts EGO 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 + * @param[in] ego EGO of the host + * @param[in] 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); +create_message_info (const struct GNUNET_MESSENGER_Ego *ego); /** - * Creates and allocates a new join message containing the clients public key. + * Creates and allocates a new join message containing the clients EGO public key. * (all values are stored as copy) * - * @param ego EGO of the client + * @param[in] ego EGO of the client * @return New message */ struct GNUNET_MESSENGER_Message* -create_message_join (struct GNUNET_MESSENGER_Ego *ego); +create_message_join (const struct GNUNET_MESSENGER_Ego *ego); /** * Creates and allocates a new leave message. @@ -68,17 +69,17 @@ 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 + * @param[in] 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 + * 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 + * @param[in] key Private key of EGO * @return New message */ struct GNUNET_MESSENGER_Message* @@ -88,7 +89,7 @@ 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 + * @param[in] service Service * @return New message */ struct GNUNET_MESSENGER_Message* @@ -98,38 +99,38 @@ 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 + * @param[in] 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. + * Creates and allocates a new miss message containing the missing peer identity. * (all values are stored as copy) * - * @param peer Missing peer identity + * @param[in] 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 + * 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 + * @param[in] 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. + * 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 + * @param[in] hash Hash of message * @return New message */ struct GNUNET_MESSENGER_Message* @@ -140,21 +141,32 @@ create_message_request (const struct GNUNET_HashCode *hash); * 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 + * @param[in] door Peer identity + * @param[in] 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. + * Creates and allocates a new text message containing a string representing text. * (all values are stored as copy) * - * @param text Text + * @param[in] text Text * @return New message */ struct GNUNET_MESSENGER_Message* create_message_text (const char *text); +/** + * Creates and allocates a new delete message containing the hash of a message to delete after a specific delay. + * (all values are stored as copy) + * + * @param[in] hash Hash of message + * @param[in] delay Delay of deletion + * @return New message + */ +struct GNUNET_MESSENGER_Message* +create_message_delete (const struct GNUNET_HashCode *hash, const struct GNUNET_TIME_Relative delay); + #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 index aa28a36ea..8aab805d2 100644 --- a/src/messenger/gnunet-service-messenger_message_recv.c +++ b/src/messenger/gnunet-service-messenger_message_recv.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -24,137 +24,64 @@ */ #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; -}; +#include "gnunet-service-messenger_operation.h" static int -iterate_send_member_infos (void *cls, const struct GNUNET_ShortHashCode *key, void *value) +iterate_forward_members (void *cls, const struct GNUNET_IDENTITY_PublicKey *public_key, + struct GNUNET_MESSENGER_MemberSession *session) { - struct GNUNET_MESSENGER_MemberInfo *info = value; - struct GNUNET_MESSENGER_MemberInfoSpread *spread = cls; + struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls; + struct GNUNET_MESSENGER_SrvRoom *room = tunnel->room; - struct GNUNET_MESSENGER_ListMessage *element = info->session_messages.head; + struct GNUNET_MESSENGER_ListMessage *element; - 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; - } + for (element = session->messages.head; element; element = element->next) + forward_tunnel_message(tunnel, get_room_message(room, NULL, &(element->hash), GNUNET_NO), &(element->hash)); 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) +int +recv_message_info (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + const 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); + const uint32_t version = get_tunnel_messenger_version(tunnel); - if ((info_msg) && (0 == GNUNET_memcmp(&(info_msg->header.sender_id), get_room_host_id (room))) - && (GNUNET_MESSENGER_KIND_INFO == info_msg->header.kind)) + if (GNUNET_OK != update_tunnel_messenger_version(tunnel, message->body.info.messenger_version)) { - struct GNUNET_MESSENGER_MemberInfoSpread spread; + disconnect_tunnel(tunnel); + return GNUNET_NO; + } - spread.room = room; + if (version == get_tunnel_messenger_version(tunnel)) + return GNUNET_NO; - 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 (room->host) + { + const struct GNUNET_MESSENGER_Ego *ego = get_handle_ego(room->host); - if (spread.tunnel) - GNUNET_CONTAINER_multishortmap_iterate (room->member_infos, iterate_send_member_infos, &spread); + send_tunnel_message (tunnel, room->host, create_message_info(ego)); } - handle_message_join (room, tunnel, message, hash); -} + struct GNUNET_PeerIdentity peer; + get_tunnel_peer_identity(tunnel, &peer); -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); -} + if (GNUNET_YES != contains_list_tunnels(&(room->basement), &peer)) + { + struct GNUNET_MESSENGER_MemberStore *member_store = get_room_member_store(room); -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); -} + iterate_store_members(member_store, iterate_forward_members, tunnel); + } -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); + check_room_peer_status(room, tunnel); + + return GNUNET_NO; } -void +int recv_message_peer (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 *message, const struct GNUNET_HashCode *hash) { struct GNUNET_PeerIdentity peer; GNUNET_PEER_resolve (tunnel->peer, &peer); @@ -164,41 +91,48 @@ recv_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGE 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)); + GNUNET_memcpy(tunnel->peer_message, &hash, sizeof(hash)); } - handle_message_peer (room, tunnel, message, hash); + return GNUNET_YES; } -void -recv_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, - struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +int +recv_message_request (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + const 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)); + const struct GNUNET_MESSENGER_Message *msg = get_room_message ( + room, NULL, &(message->body.request.hash), GNUNET_NO + ); - handle_message_id (room, tunnel, message, hash); -} + if (!msg) + { + struct GNUNET_MESSENGER_OperationStore *operation_store = get_room_operation_store(room); -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); -} + use_store_operation( + operation_store, + &(message->body.request.hash), + GNUNET_MESSENGER_OP_REQUEST, + GNUNET_MESSENGER_REQUEST_DELAY + ); -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); + return GNUNET_YES; + } + + struct GNUNET_MESSENGER_MemberStore *member_store = get_room_member_store(room); + struct GNUNET_MESSENGER_Member *member = get_store_member_of(member_store, message); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Callback for message (%s)\n", GNUNET_h2s (hash)); + + if (!member) + return GNUNET_NO; + + struct GNUNET_MESSENGER_MemberSession *session = get_member_session_of(member, message, hash); + + if ((!session) || (GNUNET_YES != check_member_session_history(session, hash, GNUNET_NO))) + return GNUNET_NO; + + forward_tunnel_message (tunnel, msg, &(message->body.request.hash)); - if (msg) - forward_tunnel_message (tunnel, msg, &(message->body.request.hash)); + return GNUNET_NO; } diff --git a/src/messenger/gnunet-service-messenger_message_recv.h b/src/messenger/gnunet-service-messenger_message_recv.h index 245612cb0..9cb36c466 100644 --- a/src/messenger/gnunet-service-messenger_message_recv.h +++ b/src/messenger/gnunet-service-messenger_message_recv.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -29,6 +29,9 @@ #include "platform.h" #include "gnunet_crypto_lib.h" +#include "gnunet-service-messenger_message_kind.h" + +#include "gnunet-service-messenger_member_session.h" #include "gnunet-service-messenger_tunnel.h" #include "messenger_api_message.h" @@ -36,124 +39,43 @@ * 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 + * @param[in/out] room Room of the message + * @param[in/out] tunnel Receiving connection + * @param[in] message INFO-Message + * @param[in] hash Hash of the message + * @return #GNUNET_NO to not forward the message */ -void +int 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); + const 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 + * @param[in/out] room Room of the message + * @param[in/out] tunnel Receiving connection + * @param[in] message PEER-Message + * @param[in] hash Hash of the message + * @return #GNUNET_YES to forward the message */ -void +int 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); + const 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 + * @param[in/out] room Room of the message + * @param[in/out] tunnel Receiving connection + * @param[in] message REQUEST-Message + * @param[in] hash Hash of the message + * @return #GNUNET_YES or #GNUNET_NO depending on required forwarding */ -void +int 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 *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 index 86cf9b888..59bbaea8d 100644 --- a/src/messenger/gnunet-service-messenger_message_send.c +++ b/src/messenger/gnunet-service-messenger_message_send.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -24,95 +24,38 @@ */ #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); - } -} +#include "gnunet-service-messenger_member.h" +#include "gnunet-service-messenger_member_session.h" +#include "gnunet-service-messenger_operation.h" 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) + const 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); + check_room_peer_status(room, NULL); } 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) + const 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) +send_message_request (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) { - handle_message_id (room, tunnel, message, hash); -} + struct GNUNET_MESSENGER_OperationStore *operation_store = get_room_operation_store(room); -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); + use_store_operation( + operation_store, + &(message->body.request.hash), + GNUNET_MESSENGER_OP_REQUEST, + GNUNET_MESSENGER_REQUEST_DELAY + ); } diff --git a/src/messenger/gnunet-service-messenger_message_send.h b/src/messenger/gnunet-service-messenger_message_send.h index c1096205a..8e3ff4495 100644 --- a/src/messenger/gnunet-service-messenger_message_send.h +++ b/src/messenger/gnunet-service-messenger_message_send.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -29,127 +29,48 @@ #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 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 + * @param[in/out] room Room of the message + * @param[in/out] handle Sending handle + * @param[in] message JOIN-Message + * @param[in] 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); + const 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 + * @param[in/out] room Room of the message + * @param[in/out] handle Sending handle + * @param[in] message PEER-Message + * @param[in] 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); + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); /** - * Handles a sent miss message. - * @see handle_message_miss() + * Handles a sent request message to trigger the request operation for this service. + * (the request operation will deactivate the possibility of spamming requests) * - * @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 + * @param[in/out] room Room of the message + * @param[in/out] handle Sending handle + * @param[in] message PEER-Message + * @param[in] 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); +send_message_request (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + const 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 index 5933d6390..1f3d262ac 100644 --- a/src/messenger/gnunet-service-messenger_message_store.c +++ b/src/messenger/gnunet-service-messenger_message_store.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -29,10 +29,16 @@ void init_message_store (struct GNUNET_MESSENGER_MessageStore *store) { + GNUNET_assert(store); + store->storage_messages = NULL; store->entries = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); store->messages = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + store->links = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + + store->rewrite_entries = GNUNET_NO; + store->write_links = GNUNET_NO; } static int @@ -55,9 +61,21 @@ iterate_destroy_messages (void *cls, const struct GNUNET_HashCode *key, void *va return GNUNET_YES; } +static int +iterate_destroy_links (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_HashCode *previous = value; + + GNUNET_free(previous); + + return GNUNET_YES; +} + void clear_message_store (struct GNUNET_MESSENGER_MessageStore *store) { + GNUNET_assert(store); + if (store->storage_messages) { GNUNET_DISK_file_close (store->storage_messages); @@ -67,9 +85,11 @@ clear_message_store (struct GNUNET_MESSENGER_MessageStore *store) GNUNET_CONTAINER_multihashmap_iterate (store->entries, iterate_destroy_entries, NULL); GNUNET_CONTAINER_multihashmap_iterate (store->messages, iterate_destroy_messages, NULL); + GNUNET_CONTAINER_multihashmap_iterate (store->links, iterate_destroy_links, NULL); GNUNET_CONTAINER_multihashmap_destroy (store->entries); GNUNET_CONTAINER_multihashmap_destroy (store->messages); + GNUNET_CONTAINER_multihashmap_destroy (store->links); } struct GNUNET_MESSENGER_MessageEntryStorage @@ -78,36 +98,15 @@ struct GNUNET_MESSENGER_MessageEntryStorage struct GNUNET_MESSENGER_MessageEntry entry; }; -void -load_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char *directory) +static void +load_message_store_entries (struct GNUNET_MESSENGER_MessageStore *store, const char *filename) { - 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; + enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ); struct GNUNET_DISK_FileHandle *entries = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, permission); if (!entries) - goto free_filename; + return; struct GNUNET_MESSENGER_MessageEntryStorage storage; struct GNUNET_MESSENGER_MessageEntry *entry; @@ -120,9 +119,13 @@ load_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char *dir { GNUNET_memcpy(entry, &(storage.entry), sizeof(*entry)); - if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (store->entries, &(storage.hash), entry, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + if ((GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (store->entries, &(storage.hash))) || + (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (store->entries, &(storage.hash), entry, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))) + { + store->rewrite_entries = GNUNET_YES; GNUNET_free(entry); + } } else { @@ -134,22 +137,118 @@ load_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char *dir while (entry); GNUNET_DISK_file_close (entries); +} + +struct GNUNET_MESSENGER_MessageLinkStorage +{ + struct GNUNET_HashCode hash; + struct GNUNET_MESSENGER_MessageLink link; +}; + +static void +load_message_store_links (struct GNUNET_MESSENGER_MessageStore *store, const char *filename) +{ + enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ); + + struct GNUNET_DISK_FileHandle *entries = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, permission); + + if (!entries) + return; + + struct GNUNET_MESSENGER_MessageLinkStorage storage; + struct GNUNET_MESSENGER_MessageLink *link = NULL; + + do + { + if ((sizeof(storage.hash) != GNUNET_DISK_file_read (entries, &(storage.hash), sizeof(storage.hash))) || + (sizeof(storage.link.multiple) != GNUNET_DISK_file_read (entries, &(storage.link.multiple), sizeof(storage.link.multiple))) || + (sizeof(storage.link.first) != GNUNET_DISK_file_read (entries, &(storage.link.first), sizeof(storage.link.first))) || + ((GNUNET_YES == storage.link.multiple) && + (sizeof(storage.link.second) != GNUNET_DISK_file_read (entries, &(storage.link.second), sizeof(storage.link.second))))) + break; + + link = GNUNET_new(struct GNUNET_MESSENGER_MessageLink); + + GNUNET_memcpy(link, &(storage.link), sizeof(*link)); + + if ((GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (store->links, &(storage.hash))) || + (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (store->links, &(storage.hash), link, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))) + break; + } + while (link); + + if (link) + GNUNET_free(link); + + GNUNET_DISK_file_close (entries); +} + +void +load_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char *directory) +{ + GNUNET_assert((store) && (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_READWRITE, 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)) + load_message_store_entries(store, filename); + + GNUNET_free(filename); + + GNUNET_asprintf (&filename, "%s%s", directory, "links.store"); + + if (GNUNET_YES == GNUNET_DISK_file_test (filename)) + load_message_store_links(store, filename); -free_filename: GNUNET_free(filename); } -struct GNUNET_MESSENGER_MessageSave +struct GNUNET_MESSENGER_ClosureMessageSave { struct GNUNET_MESSENGER_MessageStore *store; - struct GNUNET_DISK_FileHandle *storage_entries; + struct GNUNET_DISK_FileHandle *storage; }; +static int +iterate_save_entries (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_ClosureMessageSave *save = cls; + struct GNUNET_MESSENGER_MessageEntry *entry = value; + + struct GNUNET_MESSENGER_MessageEntryStorage storage; + + GNUNET_memcpy(&(storage.hash), key, sizeof(storage.hash)); + GNUNET_memcpy(&(storage.entry), entry, sizeof(*entry)); + + GNUNET_DISK_file_write (save->storage, &storage, sizeof(storage)); + + return GNUNET_YES; +} + static int iterate_save_messages (void *cls, const struct GNUNET_HashCode *key, void *value) { - struct GNUNET_MESSENGER_MessageSave *save = cls; + struct GNUNET_MESSENGER_ClosureMessageSave *save = cls; if (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains (save->store->entries, key)) return GNUNET_YES; @@ -159,16 +258,16 @@ iterate_save_messages (void *cls, const struct GNUNET_HashCode *key, void *value GNUNET_memcpy(&(storage.hash), key, sizeof(storage.hash)); - storage.entry.length = get_message_size (message); + storage.entry.length = get_message_size (message, GNUNET_YES); 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)))) + if ((GNUNET_SYSERR == storage.entry.offset) || (sizeof(storage) + != GNUNET_DISK_file_write (save->storage, &storage, sizeof(storage)))) return GNUNET_YES; char *buffer = GNUNET_malloc(storage.entry.length); - encode_message (message, storage.entry.length, buffer); + encode_message (message, storage.entry.length, buffer, GNUNET_YES); GNUNET_DISK_file_write (save->store->storage_messages, buffer, storage.entry.length); @@ -177,25 +276,74 @@ iterate_save_messages (void *cls, const struct GNUNET_HashCode *key, void *value return GNUNET_YES; } +static int +iterate_save_links (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_ClosureMessageSave *save = cls; + struct GNUNET_MESSENGER_MessageLink *link = value; + + GNUNET_DISK_file_write (save->storage, key, sizeof(*key)); + GNUNET_DISK_file_write (save->storage, &(link->multiple), sizeof(link->multiple)); + GNUNET_DISK_file_write (save->storage, &(link->first), sizeof(link->first)); + + if (GNUNET_YES == link->multiple) + GNUNET_DISK_file_write (save->storage, &(link->second), sizeof(link->second)); + + return GNUNET_YES; +} + void save_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char *directory) { - struct GNUNET_MESSENGER_MessageSave save; + GNUNET_assert((store) && (directory)); + + struct GNUNET_MESSENGER_ClosureMessageSave save; enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE); char *filename; + + if (GNUNET_YES != store->write_links) + goto save_entries; + + GNUNET_asprintf (&filename, "%s%s", directory, "links.store"); + + save.store = store; + save.storage = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE, permission); + + if (!save.storage) + goto save_entries; + + if (GNUNET_SYSERR == GNUNET_DISK_file_seek (save.storage, 0, GNUNET_DISK_SEEK_SET)) + goto close_links; + + GNUNET_CONTAINER_multihashmap_iterate (store->links, iterate_save_links, &save); + store->write_links = GNUNET_NO; + +close_links: + GNUNET_DISK_file_close (save.storage); + +save_entries: + GNUNET_free(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); + save.storage = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE, permission); GNUNET_free(filename); - if (!save.storage_entries) + if (!save.storage) return; - if (GNUNET_SYSERR == GNUNET_DISK_file_seek (save.storage_entries, 0, GNUNET_DISK_SEEK_END)) + if (GNUNET_YES == store->rewrite_entries) + { + if (GNUNET_SYSERR == GNUNET_DISK_file_seek (save.storage, 0, GNUNET_DISK_SEEK_SET)) + goto close_entries; + + GNUNET_CONTAINER_multihashmap_iterate (store->entries, iterate_save_entries, &save); + store->rewrite_entries = GNUNET_NO; + } + else if (GNUNET_SYSERR == GNUNET_DISK_file_seek (save.storage, 0, GNUNET_DISK_SEEK_END)) goto close_entries; if (store->storage_messages) @@ -213,16 +361,18 @@ save_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char *dir GNUNET_CONTAINER_multihashmap_iterate (store->messages, iterate_save_messages, &save); GNUNET_DISK_file_sync (store->storage_messages); - GNUNET_DISK_file_sync (save.storage_entries); + GNUNET_DISK_file_sync (save.storage); } close_entries: - GNUNET_DISK_file_close (save.storage_entries); + GNUNET_DISK_file_close (save.storage); } int -contains_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash) +contains_store_message (const struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash) { + GNUNET_assert((store) && (hash)); + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (store->messages, hash)) return GNUNET_YES; @@ -232,6 +382,8 @@ contains_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struc const struct GNUNET_MESSENGER_Message* get_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash) { + GNUNET_assert((store) && (hash)); + struct GNUNET_MESSENGER_Message *message = GNUNET_CONTAINER_multihashmap_get (store->messages, hash); if (message) @@ -250,33 +402,141 @@ get_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct GNU char *buffer = GNUNET_malloc(entry->length); + if (!buffer) + return NULL; + 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); + const int decoding = decode_message (message, entry->length, buffer, GNUNET_YES, NULL); - message = NULL; + struct GNUNET_HashCode check; + hash_message (message, entry->length, buffer, &check); + if ((GNUNET_YES != decoding) || (GNUNET_CRYPTO_hash_cmp (hash, &check) != 0)) + { GNUNET_CONTAINER_multihashmap_remove (store->entries, hash, entry); + store->rewrite_entries = GNUNET_YES; + + goto free_message; } + if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + goto free_buffer; + +free_message: destroy_message (message); + message = NULL; + free_buffer: GNUNET_free(buffer); return message; } +const struct GNUNET_MESSENGER_MessageLink* +get_store_message_link (struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash, + int deleted_only) +{ + if (deleted_only) + goto get_link; + + const struct GNUNET_MESSENGER_Message *message = get_store_message(store, hash); + + if (!message) + goto get_link; + + static struct GNUNET_MESSENGER_MessageLink link; + + GNUNET_memcpy(&(link.first), &(message->header.previous), sizeof(link.first)); + + link.multiple = GNUNET_MESSENGER_KIND_MERGE == message->header.kind? GNUNET_YES : GNUNET_NO; + + if (GNUNET_YES == link.multiple) + GNUNET_memcpy(&(link.second), &(message->body.merge.previous), sizeof(link.second)); + else + GNUNET_memcpy(&(link.second), &(message->header.previous), sizeof(link.second)); + + return &link; + +get_link: + return GNUNET_CONTAINER_multihashmap_get (store->links, hash); +} + int put_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash, struct GNUNET_MESSENGER_Message *message) { + GNUNET_assert((store) && (hash) && (message)); + return GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); } + +static void +add_link (struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash, + const struct GNUNET_MESSENGER_Message *message) +{ + struct GNUNET_MESSENGER_MessageLink *link = GNUNET_new(struct GNUNET_MESSENGER_MessageLink); + + GNUNET_memcpy(&(link->first), &(message->header.previous), sizeof(link->first)); + + link->multiple = GNUNET_MESSENGER_KIND_MERGE == message->header.kind? GNUNET_YES : GNUNET_NO; + + if (GNUNET_YES == link->multiple) + GNUNET_memcpy(&(link->second), &(message->body.merge.previous), sizeof(link->second)); + else + GNUNET_memcpy(&(link->second), &(message->header.previous), sizeof(link->second)); + + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(store->links, hash, link, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + GNUNET_free(link); +} + +int +delete_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash) +{ + GNUNET_assert((store) && (hash)); + + const struct GNUNET_MESSENGER_MessageEntry *entry = GNUNET_CONTAINER_multihashmap_get (store->entries, hash); + + if (!entry) + goto clear_memory; + + const struct GNUNET_MESSENGER_Message *message = get_store_message(store, hash); + + if (message) + add_link (store, hash, message); + + if (!store->storage_messages) + goto clear_entry; + + if (entry->offset != GNUNET_DISK_file_seek (store->storage_messages, entry->offset, GNUNET_DISK_SEEK_SET)) + return GNUNET_SYSERR; + + char *clear_buffer = GNUNET_malloc(entry->length); + + if (!clear_buffer) + return GNUNET_SYSERR; + + GNUNET_CRYPTO_zero_keys (clear_buffer, entry->length); + + if ((entry->length != GNUNET_DISK_file_write (store->storage_messages, clear_buffer, entry->length)) || (GNUNET_OK + != GNUNET_DISK_file_sync (store->storage_messages))) + { + GNUNET_free(clear_buffer); + return GNUNET_SYSERR; + } + + GNUNET_free(clear_buffer); + +clear_entry: + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (store->entries, hash, entry)) + store->rewrite_entries = GNUNET_YES; + +clear_memory: + GNUNET_CONTAINER_multihashmap_remove_all (store->messages, hash); + return GNUNET_OK; +} diff --git a/src/messenger/gnunet-service-messenger_message_store.h b/src/messenger/gnunet-service-messenger_message_store.h index e58459b21..87305826a 100644 --- a/src/messenger/gnunet-service-messenger_message_store.h +++ b/src/messenger/gnunet-service-messenger_message_store.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -36,85 +36,127 @@ struct GNUNET_MESSENGER_MessageEntry uint16_t length; }; +struct GNUNET_MESSENGER_Message; + +struct GNUNET_MESSENGER_MessageLink +{ + uint8_t multiple; + + struct GNUNET_HashCode first; + struct GNUNET_HashCode second; +}; + struct GNUNET_MESSENGER_MessageStore { struct GNUNET_DISK_FileHandle *storage_messages; struct GNUNET_CONTAINER_MultiHashMap *entries; struct GNUNET_CONTAINER_MultiHashMap *messages; + struct GNUNET_CONTAINER_MultiHashMap *links; + + int rewrite_entries; + int write_links; }; /** - * Initializes a message store as fully empty. + * Initializes a message store as fully empty. * - * @param store Message store + * @param[out] store Message store */ void init_message_store (struct GNUNET_MESSENGER_MessageStore *store); /** - * Clears a message store, wipes its content and deallocates its memory. + * Clears a message store, wipes its content and deallocates its memory. * - * @param store Message store + * @param[in/out] store Message store */ void clear_message_store (struct GNUNET_MESSENGER_MessageStore *store); /** - * Loads messages from a directory into a message store. + * Loads messages from a directory into a message store. * - * @param store Message store - * @param directory Path to a directory + * @param[out] store Message store + * @param[in] 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. + * Saves messages from a message store into a directory. * - * @param store Message store - * @param directory Path to a directory + * @param[in] store Message store + * @param[in] 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. + * 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 + * @param[in] store Message store + * @param[in] 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); +contains_store_message (const 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. + * 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 + * @param[in/out] store Message store + * @param[in] 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); +/** + * Returns the message link from a message store matching a given hash. If the + * flag is set to #GNUNET_YES, only links from deleted messages will be returned or NULL. + * + * Otherwise message links will also returned for messages found in the store under the given + * hash. The link which will be returned copies link information from the message for + * temporary usage. + * + * @param[in/out] store Message store + * @param[in] hash Hash of message + * @param[in] deleted_only Flag + * @return Message link or NULL + */ +const struct GNUNET_MESSENGER_MessageLink* +get_store_message_link (struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash, + int deleted_only); + /** * 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 + * @param[in/out] store Message store + * @param[in] hash Hash of message + * @param[in/out] 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); +/** + * Deletes a message in the message store. It will be removed from disk space and memory. The result + * indicates if the operation was successful. + * + * @param[in/out] store Message store + * @param[in] hash Hash of message + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure + */ +int +delete_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash); + #endif //GNUNET_SERVICE_MESSENGER_MESSAGE_STORE_H diff --git a/src/messenger/gnunet-service-messenger_operation.c b/src/messenger/gnunet-service-messenger_operation.c new file mode 100644 index 000000000..d0c378699 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_operation.c @@ -0,0 +1,214 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_operation.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_operation.h" + +#include "gnunet-service-messenger_operation_store.h" + +struct GNUNET_MESSENGER_Operation* +create_operation (const struct GNUNET_HashCode *hash) +{ + GNUNET_assert(hash); + + struct GNUNET_MESSENGER_Operation *op = GNUNET_new(struct GNUNET_MESSENGER_Operation); + + op->type = GNUNET_MESSENGER_OP_UNKNOWN; + GNUNET_memcpy(&(op->hash), hash, sizeof(*hash)); + op->timestamp = GNUNET_TIME_absolute_get_zero_(); + op->store = NULL; + op->task = NULL; + + return op; +} + +void +destroy_operation (struct GNUNET_MESSENGER_Operation *op) +{ + GNUNET_assert(op); + + if (op->task) + GNUNET_SCHEDULER_cancel(op->task); + + GNUNET_free(op); +} + +static void +callback_operation (void *cls); + +struct GNUNET_MESSENGER_Operation* +load_operation (struct GNUNET_MESSENGER_OperationStore *store, const char *path) +{ + GNUNET_assert((store) && (path)); + + struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create (); + struct GNUNET_MESSENGER_Operation* op = NULL; + + if (GNUNET_OK != GNUNET_CONFIGURATION_parse(cfg, path)) + goto destroy_config; + + struct GNUNET_HashCode hash; + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_data (cfg, "operation", "hash", &hash, sizeof(hash))) + goto destroy_config; + + op = create_operation(&hash); + + unsigned long long type_number; + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number(cfg, "operation", "type", &type_number)) + switch (type_number) + { + case GNUNET_MESSENGER_OP_REQUEST: + op->type = GNUNET_MESSENGER_OP_REQUEST; + break; + case GNUNET_MESSENGER_OP_DELETE: + op->type = GNUNET_MESSENGER_OP_DELETE; + break; + case GNUNET_MESSENGER_OP_MERGE: + op->type = GNUNET_MESSENGER_OP_MERGE; + break; + default: + break; + } + + if ((GNUNET_MESSENGER_OP_UNKNOWN == op->type) || + (GNUNET_OK != GNUNET_CONFIGURATION_get_data (cfg, "operation", "timestamp", &(op->timestamp), sizeof(op->timestamp)))) + { + destroy_operation(op); + op = NULL; + goto destroy_config; + } + + const struct GNUNET_TIME_Relative delay = GNUNET_TIME_absolute_get_remaining(op->timestamp); + + op->task = GNUNET_SCHEDULER_add_delayed_with_priority( + delay, + GNUNET_SCHEDULER_PRIORITY_BACKGROUND, + callback_operation, + op + ); + + op->store = store; + +destroy_config: + GNUNET_CONFIGURATION_destroy (cfg); + + return op; +} + +void +save_operation (const struct GNUNET_MESSENGER_Operation *op, const char *path) +{ + GNUNET_assert((path) && (op)); + + struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create (); + + char *hash_data; + hash_data = GNUNET_STRINGS_data_to_string_alloc (&(op->hash), sizeof(op->hash)); + + if (hash_data) + { + GNUNET_CONFIGURATION_set_value_string (cfg, "operation", "hash", hash_data); + + GNUNET_free(hash_data); + } + + GNUNET_CONFIGURATION_set_value_number(cfg, "operation", "type", op->type); + + char *timestamp_data; + timestamp_data = GNUNET_STRINGS_data_to_string_alloc (&(op->timestamp), sizeof(op->timestamp)); + + if (timestamp_data) + { + GNUNET_CONFIGURATION_set_value_string (cfg, "operation", "timestamp", timestamp_data); + + GNUNET_free(timestamp_data); + } + + GNUNET_CONFIGURATION_write (cfg, path); + GNUNET_CONFIGURATION_destroy (cfg); +} + +extern void +callback_store_operation (struct GNUNET_MESSENGER_OperationStore *store, + enum GNUNET_MESSENGER_OperationType type, + const struct GNUNET_HashCode *hash); + +static void +callback_operation (void *cls) +{ + struct GNUNET_MESSENGER_Operation *op = cls; + + op->task = NULL; + + callback_store_operation (op->store, op->type, &(op->hash)); +} + +int +start_operation (struct GNUNET_MESSENGER_Operation *op, + enum GNUNET_MESSENGER_OperationType type, + struct GNUNET_MESSENGER_OperationStore *store, + struct GNUNET_TIME_Relative delay) +{ + GNUNET_assert((op) && (store)); + + if (op->task) + return GNUNET_SYSERR; + + const struct GNUNET_TIME_Absolute timestamp = GNUNET_TIME_absolute_add( + GNUNET_TIME_absolute_get(), + delay + ); + + op->task = GNUNET_SCHEDULER_add_delayed_with_priority( + delay, + GNUNET_SCHEDULER_PRIORITY_BACKGROUND, + callback_operation, + op + ); + + op->type = type; + op->timestamp = timestamp; + op->store = store; + + return GNUNET_OK; +} + +int +stop_operation (struct GNUNET_MESSENGER_Operation *op) +{ + GNUNET_assert(op); + + if (!op->task) + return GNUNET_SYSERR; + + GNUNET_SCHEDULER_cancel(op->task); + op->task = NULL; + + op->type = GNUNET_MESSENGER_OP_UNKNOWN; + op->timestamp = GNUNET_TIME_absolute_get_zero_(); + op->store = NULL; + + return GNUNET_OK; +} diff --git a/src/messenger/gnunet-service-messenger_operation.h b/src/messenger/gnunet-service-messenger_operation.h new file mode 100644 index 000000000..7757b8e88 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_operation.h @@ -0,0 +1,129 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_operation.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_OPERATION_H +#define GNUNET_SERVICE_MESSENGER_OPERATION_H + +#include "platform.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_strings_lib.h" +#include "gnunet_time_lib.h" + +enum GNUNET_MESSENGER_OperationType +{ + GNUNET_MESSENGER_OP_REQUEST = 1, + GNUNET_MESSENGER_OP_DELETE = 2, + GNUNET_MESSENGER_OP_MERGE = 3, + + GNUNET_MESSENGER_OP_UNKNOWN = 0 +}; + +struct GNUNET_MESSENGER_OperationStore; + +struct GNUNET_MESSENGER_Operation +{ + enum GNUNET_MESSENGER_OperationType type; + + struct GNUNET_HashCode hash; + struct GNUNET_TIME_Absolute timestamp; + + struct GNUNET_MESSENGER_OperationStore *store; + struct GNUNET_SCHEDULER_Task* task; +}; + +/** + * Creates and allocates a new operation under a given hash. + * + * @param[in] hash Hash of message + */ +struct GNUNET_MESSENGER_Operation* +create_operation (const struct GNUNET_HashCode *hash); + +/** + * Destroys an operation and frees its memory fully. + * + * @param[in/out] op Operation + */ +void +destroy_operation (struct GNUNET_MESSENGER_Operation *op); + +/** + * Loads data from a configuration file at a selected path into + * a new allocated and created operation for a specific operation + * store if the required information could be read successfully. + * + * The method will return the new operation and it will be started + * automatically to match its timestamp of execution. + * + * If the method fails to restore any valid operation from the file, + * NULL gets returned instead. + * + * @param[in/out] store Operation store + * @param[in] path Path of a configuration file + */ +struct GNUNET_MESSENGER_Operation* +load_operation (struct GNUNET_MESSENGER_OperationStore *store, const char *path); + +/** + * Saves data from an operation into a configuration file at a + * selected path which can be load to restore the operation + * completely and continue its process. + * + * @param[in] op Operation + * @param[in] path Path of a configuration file + */ +void +save_operation (const struct GNUNET_MESSENGER_Operation *op, const char *path); + +/** + * Starts an inactive operation with a given delay in a + * specific operation store. The method will replace the + * operations type to process it correctly. An opeation can't be + * started twice, it has to be stopped or fully processed first. + * + * @param[in/out] op Operation + * @param[in] type Type of operation + * @param[in/out] store Operation store + * @param[in] delay Delay + * @return #GNUNET_OK on success, otherwise #GNUNET_SYSERR + */ +int +start_operation (struct GNUNET_MESSENGER_Operation *op, + enum GNUNET_MESSENGER_OperationType type, + struct GNUNET_MESSENGER_OperationStore *store, + struct GNUNET_TIME_Relative delay); + +/** + * Stops an active operation and resets its type to be + * #GNUNET_MESSENGER_OP_UNKNOWN. + * + * @return #GNUNET_OK on success, otherwise #GNUNET_SYSERR + */ +int +stop_operation (struct GNUNET_MESSENGER_Operation *op); + +#endif //GNUNET_SERVICE_MESSENGER_OPERATION_H diff --git a/src/messenger/gnunet-service-messenger_operation_store.c b/src/messenger/gnunet-service-messenger_operation_store.c new file mode 100644 index 000000000..05985ef84 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_operation_store.c @@ -0,0 +1,224 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_operation_store.c + * @brief GNUnet MESSENGER service + */ + +#include "gnunet-service-messenger_operation_store.h" + +#include "gnunet-service-messenger_operation.h" +#include "gnunet-service-messenger_room.h" + +void +init_operation_store (struct GNUNET_MESSENGER_OperationStore *store, struct GNUNET_MESSENGER_SrvRoom *room) +{ + GNUNET_assert((store) && (room)); + + store->room = room; + store->operations = GNUNET_CONTAINER_multihashmap_create(8, GNUNET_NO); +} + +static int +iterate_destroy_operations (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_Operation *op = value; + + destroy_operation(op); + + return GNUNET_YES; +} + +void +clear_operation_store (struct GNUNET_MESSENGER_OperationStore *store) +{ + GNUNET_assert(store); + + GNUNET_CONTAINER_multihashmap_iterate (store->operations, iterate_destroy_operations, NULL); + GNUNET_CONTAINER_multihashmap_destroy(store->operations); +} + +static int +callback_scan_for_operations (void *cls, const char *filename) +{ + struct GNUNET_MESSENGER_OperationStore *store = cls; + + if (GNUNET_YES == GNUNET_DISK_file_test (filename)) + { + char *path; + + GNUNET_asprintf (&path, "%s%c", filename, DIR_SEPARATOR); + + struct GNUNET_MESSENGER_Operation *op = load_operation(store, path); + + if ((op) && (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put( + store->operations, + &(op->hash), op, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))) + { + destroy_operation(op); + } + + GNUNET_free(path); + } + + return GNUNET_OK; +} + +void +load_operation_store (struct GNUNET_MESSENGER_OperationStore *store, + const char *directory) +{ + GNUNET_assert ((store) && (directory)); + + if (GNUNET_OK == GNUNET_DISK_directory_test (directory, GNUNET_YES)) + GNUNET_DISK_directory_scan (directory, callback_scan_for_operations, store); +} + +static int +iterate_save_operations (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + const char *save_dir = cls; + + struct GNUNET_MESSENGER_Operation *op = value; + + if (!op) + return GNUNET_YES; + + char *op_dir; + GNUNET_asprintf (&op_dir, "%s%s.cfg", save_dir, GNUNET_h2s(key)); + save_operation(op, op_dir); + + GNUNET_free(op_dir); + return GNUNET_YES; +} + +void +save_operation_store (const struct GNUNET_MESSENGER_OperationStore *store, + const char *directory) +{ + GNUNET_assert ((store) && (directory)); + + char* save_dir; + GNUNET_asprintf (&save_dir, "%s%s%c", directory, "operations", 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 (store->operations, iterate_save_operations, save_dir); + + GNUNET_free(save_dir); +} + +enum GNUNET_MESSENGER_OperationType +get_store_operation_type (const struct GNUNET_MESSENGER_OperationStore *store, + const struct GNUNET_HashCode *hash) +{ + GNUNET_assert((store) && (hash)); + + struct GNUNET_MESSENGER_Operation *op = GNUNET_CONTAINER_multihashmap_get(store->operations, hash); + + if (!op) + return GNUNET_MESSENGER_OP_UNKNOWN; + + return op->type; +} + +int +use_store_operation (struct GNUNET_MESSENGER_OperationStore *store, + const struct GNUNET_HashCode *hash, + enum GNUNET_MESSENGER_OperationType type, + struct GNUNET_TIME_Relative delay) +{ + GNUNET_assert((store) && (hash)); + + struct GNUNET_MESSENGER_Operation *op = GNUNET_CONTAINER_multihashmap_get(store->operations, hash); + + if (op) + goto use_op; + + op = create_operation(hash); + + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(store->operations, hash, op, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + { + destroy_operation(op); + + return GNUNET_SYSERR; + } + +use_op: + if ((op->type != GNUNET_MESSENGER_OP_UNKNOWN) && + (type == GNUNET_MESSENGER_OP_DELETE)) + stop_operation (op); + + return start_operation(op, type, store, delay); +} + +void +cancel_store_operation (struct GNUNET_MESSENGER_OperationStore *store, + const struct GNUNET_HashCode *hash) +{ + GNUNET_assert((store) && (hash)); + + struct GNUNET_MESSENGER_Operation *op = GNUNET_CONTAINER_multihashmap_get(store->operations, hash); + + if (!op) + return; + + stop_operation(op); + + GNUNET_CONTAINER_multihashmap_remove(store->operations, hash, op); + + destroy_operation(op); +} + +extern void +callback_room_deletion (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_HashCode *hash); + +extern void +callback_room_merge (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_HashCode *hash); + +void +callback_store_operation (struct GNUNET_MESSENGER_OperationStore *store, + enum GNUNET_MESSENGER_OperationType type, + const struct GNUNET_HashCode *hash) +{ + GNUNET_assert((store) && (hash)); + + struct GNUNET_HashCode op_hash; + GNUNET_memcpy(&op_hash, hash, sizeof(op_hash)); + cancel_store_operation (store, &op_hash); + + struct GNUNET_MESSENGER_SrvRoom *room = store->room; + + switch (type) + { + case GNUNET_MESSENGER_OP_REQUEST: + break; + case GNUNET_MESSENGER_OP_DELETE: + callback_room_deletion (room, &op_hash); + break; + case GNUNET_MESSENGER_OP_MERGE: + callback_room_merge (room, &op_hash); + break; + default: + break; + } +} diff --git a/src/messenger/gnunet-service-messenger_operation_store.h b/src/messenger/gnunet-service-messenger_operation_store.h new file mode 100644 index 000000000..2fd604340 --- /dev/null +++ b/src/messenger/gnunet-service-messenger_operation_store.h @@ -0,0 +1,131 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_operation_store.h + * @brief GNUnet MESSENGER service + */ + +#ifndef GNUNET_SERVICE_MESSENGER_OPERATION_STORE_H +#define GNUNET_SERVICE_MESSENGER_OPERATION_STORE_H + +#include "platform.h" +#include "gnunet_container_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + +struct GNUNET_MESSENGER_SrvRoom; + +struct GNUNET_MESSENGER_OperationStore +{ + struct GNUNET_MESSENGER_SrvRoom *room; + + struct GNUNET_CONTAINER_MultiHashMap *operations; +}; + +/** + * Initializes an operation store as fully empty with a given room. + * + * @param[out] store Operation store + * @param[in/out] room Room + */ +void +init_operation_store (struct GNUNET_MESSENGER_OperationStore *store, struct GNUNET_MESSENGER_SrvRoom *room); + +/** + * Clears an operation store, stops all operations and deallocates its memory. + * + * @param[in/out] store Operation store + */ +void +clear_operation_store (struct GNUNET_MESSENGER_OperationStore *store); + +/** + * Loads operations from a directory into an operation store. + * + * @param[out] store Operation store + * @param[in] directory Path to a directory + */ +void +load_operation_store (struct GNUNET_MESSENGER_OperationStore *store, + const char *directory); + +/** + * Saves operations from an operation store into a directory. + * + * @param[in] store Operation store + * @param[in] directory Path to a directory + */ +void +save_operation_store (const struct GNUNET_MESSENGER_OperationStore *store, + const char *directory); + +/** + * Retruns the type of the active operation under a given hash in + * a specific operation store. If there is no active operation under + * the given hash, #GNUNET_MESSENGER_OP_UNKNOWN gets returned instead. + * + * @param[in] store Operation store + * @param[in] hash Hash of message + * @return Type of operation or #GNUNET_MESSENGER_OP_UNKNOWN + */ +enum GNUNET_MESSENGER_OperationType +get_store_operation_type (const struct GNUNET_MESSENGER_OperationStore *store, + const struct GNUNET_HashCode *hash); + +/** + * Tries to use an operation under a given hash in a specific + * operation store. The operation will use the selected type + * if successful. The operation will be delayed by a given delay. + * + * If the selected type is #GNUNET_MESSENGER_OP_DELETE any active operation + * under the given hash will be stopped and replaced. + * + * If the new operation could be started successfully the method returns + * #GNUNET_OK, otherwise #GNUNET_SYSERR. + * + * @param[in/out] store Operation store + * @param[in] hash Hash of message + * @param[in] type Operation type + * @param[in] delay Delay + * @return #GNUNET_OK on success, otherwise #GNUNET_SYSERR + */ +int +use_store_operation (struct GNUNET_MESSENGER_OperationStore *store, + const struct GNUNET_HashCode *hash, + enum GNUNET_MESSENGER_OperationType type, + struct GNUNET_TIME_Relative delay); + +/** + * Stops any active operation under a given hash in a specific + * operation store. + * + * Beware that calling this method will also implicitly free the memory + * of any active operation under the given hash! + * + * @param[in/out] store Operation store + * @param[in] hash Hash of message + */ +void +cancel_store_operation (struct GNUNET_MESSENGER_OperationStore *store, + const struct GNUNET_HashCode *hash); + +#endif //GNUNET_SERVICE_MESSENGER_OPERATION_STORE_H diff --git a/src/messenger/gnunet-service-messenger_room.c b/src/messenger/gnunet-service-messenger_room.c index 7383e1d20..e0bec7991 100644 --- a/src/messenger/gnunet-service-messenger_room.c +++ b/src/messenger/gnunet-service-messenger_room.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -25,10 +25,19 @@ #include "gnunet-service-messenger_room.h" +#include "gnunet-service-messenger_member.h" +#include "gnunet-service-messenger_member_session.h" + #include "gnunet-service-messenger_message_kind.h" +#include "gnunet-service-messenger_message_handle.h" +#include "gnunet-service-messenger_message_send.h" +#include "gnunet-service-messenger_operation.h" + +#include "gnunet-service-messenger.h" #include "gnunet-service-messenger_service.h" -#include "gnunet-service-messenger_util.h" +#include "gnunet-service-messenger_tunnel.h" +#include "messenger_api_util.h" static void idle_request_room_messages (void *cls); @@ -47,11 +56,10 @@ create_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_Hash 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_member_store(get_room_member_store(room), room); + init_message_store (get_room_message_store(room)); + init_operation_store(get_room_operation_store(room), room); init_list_tunnels (&(room->basement)); init_list_messages (&(room->last_messages)); @@ -61,10 +69,8 @@ create_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_Hash 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); + load_room (room); room->idle = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, idle_request_room_messages, room); @@ -79,34 +85,8 @@ iterate_destroy_tunnels (void *cls, const struct GNUNET_PeerIdentity *key, void 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; -} +static void +handle_room_messages (struct GNUNET_MESSENGER_SrvRoom *room); void destroy_room (struct GNUNET_MESSENGER_SrvRoom *room) @@ -123,26 +103,18 @@ destroy_room (struct GNUNET_MESSENGER_SrvRoom *room) 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); + 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); + save_room (room); - clear_message_store (&(room->store)); - - GNUNET_CONTAINER_multihashmap_destroy (room->requested); + clear_member_store (get_room_member_store(room)); + clear_message_store (get_room_message_store(room)); + clear_operation_store(get_room_operation_store(room)); 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)); @@ -153,47 +125,28 @@ destroy_room (struct GNUNET_MESSENGER_SrvRoom *room) GNUNET_free(room); } -struct GNUNET_MESSENGER_SrvContact* -get_room_contact (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *id) +struct GNUNET_MESSENGER_MemberStore* +get_room_member_store (struct GNUNET_MESSENGER_SrvRoom *room) { - 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); + GNUNET_assert(room); - if (GNUNET_OK == GNUNET_CONTAINER_multishortmap_put (room->members, id, contact, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) - increase_contact_rc (contact); + return &(room->member_store); } -struct GNUNET_MESSENGER_MemberInfo* -get_room_member_info (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *id) +struct GNUNET_MESSENGER_MessageStore* +get_room_message_store (struct GNUNET_MESSENGER_SrvRoom *room) { - GNUNET_assert((room) && (room->member_infos)); + GNUNET_assert(room); - return GNUNET_CONTAINER_multishortmap_get (room->member_infos, id); + return &(room->message_store); } -struct GNUNET_ShortHashCode* -generate_room_member_id (const struct GNUNET_MESSENGER_SrvRoom *room) +struct GNUNET_MESSENGER_OperationStore* +get_room_operation_store (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; - } + return &(room->operation_store); } const struct GNUNET_ShortHashCode* @@ -201,7 +154,7 @@ get_room_host_id (const struct GNUNET_MESSENGER_SrvRoom *room) { GNUNET_assert(room); - return get_handle_member_id (room->host, &(room->key)); + return get_handle_member_id (room->host, get_room_key(room)); } void @@ -209,47 +162,17 @@ change_room_host_id (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ { GNUNET_assert(room); - change_handle_member_id (room->host, &(room->key), unique_id); + change_handle_member_id (room->host, get_room_key(room), 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) + if ((!handle) || (!is_tunnel_connected (tunnel))) 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; + return send_tunnel_message (tunnel, handle, create_message_info (get_handle_ego (handle))); } static void* @@ -257,63 +180,51 @@ callback_room_connect (void *cls, struct GNUNET_CADET_Channel *channel, const st { struct GNUNET_MESSENGER_SrvRoom *room = cls; - struct GNUNET_MESSENGER_SrvTunnel *tunnel = GNUNET_CONTAINER_multipeermap_get (room->tunnels, source); + struct GNUNET_MESSENGER_SrvTunnel *tunnel = create_tunnel (room, source); - if (tunnel) + if ((tunnel) && + (GNUNET_OK != GNUNET_CONTAINER_multipeermap_put (room->tunnels, source, tunnel, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE))) { - 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; - } + destroy_tunnel (tunnel); + tunnel = NULL; } - else + + if (!tunnel) { - tunnel = create_tunnel (room, source); + delayed_disconnect_channel (channel); + return NULL; + } - 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); + bind_tunnel(tunnel, channel); - disconnect_tunnel (tunnel); - destroy_tunnel (tunnel); - return NULL; - } - } - else - { - tunnel->channel = NULL; - destroy_tunnel (tunnel); + GNUNET_log(GNUNET_ERROR_TYPE_INFO, "New tunnel in room (%s) established to peer: %s\n", + GNUNET_h2s(get_room_key(room)), GNUNET_i2s (source)); - delayed_disconnect_channel (channel); - return NULL; - } - } + if (GNUNET_YES == send_room_info (room, room->host, tunnel)) + return tunnel; + + disconnect_tunnel (tunnel); + + if (GNUNET_YES == GNUNET_CONTAINER_multipeermap_remove (room->tunnels, source, tunnel)) + destroy_tunnel (tunnel); + + return NULL; } static int join_room (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, - const struct GNUNET_ShortHashCode *member_id) + struct GNUNET_MESSENGER_Member *member) { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Joining room: %s (%s)\n", GNUNET_h2s(get_room_key(room)), GNUNET_sh2s(member_id)); + GNUNET_assert((room) && (handle) && (member)); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Joining room: %s (%s)\n", GNUNET_h2s (get_room_key (room)), + GNUNET_sh2s (get_member_id(member))); + + if (GNUNET_OK != change_handle_member_id (handle, get_room_key(room), get_member_id(member))) + return GNUNET_NO; - struct GNUNET_MESSENGER_Message *message = create_message_join (get_handle_ego(handle)); + struct GNUNET_MESSENGER_Message *message = create_message_join (get_handle_ego (handle)); if (!message) { @@ -322,48 +233,74 @@ join_room (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHan return GNUNET_NO; } - struct GNUNET_HashCode hash; + return send_room_message (room, handle, message); +} - send_room_message (room, handle, message, &hash); - destroy_message (message); +struct GNUNET_MESSENGER_MemberNotify +{ + struct GNUNET_MESSENGER_SrvRoom *room; + struct GNUNET_MESSENGER_SrvHandle *handle; + struct GNUNET_MESSENGER_MemberSession *session; +}; - struct GNUNET_MESSENGER_MemberInfo *info = GNUNET_new(struct GNUNET_MESSENGER_MemberInfo); +static int +iterate_notify_about_members (void *cls, const struct GNUNET_IDENTITY_PublicKey *public_key, + struct GNUNET_MESSENGER_MemberSession *session) +{ + struct GNUNET_MESSENGER_MemberNotify *notify = cls; - info->access = GNUNET_MESSENGER_MEMBER_ALLOWED; - init_list_messages (&(info->session_messages)); + if (notify->session == session) + return GNUNET_YES; - 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); + struct GNUNET_MESSENGER_ListMessage *element; - add_to_list_messages (&(info->session_messages), &hash); - return GNUNET_YES; - } - else + for (element = session->messages.head; element; element = element->next) { - GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Your member information could not be registered!\n"); + const struct GNUNET_MESSENGER_Message *message = get_room_message ( + notify->room, notify->handle, &(element->hash), GNUNET_NO + ); - GNUNET_free(info); - return GNUNET_NO; + if (message) + notify_handle_message (notify->handle, get_room_key(notify->room), session, message, &(element->hash)); } + + return GNUNET_YES; } 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)); + const struct GNUNET_ShortHashCode *member_id = get_handle_member_id (handle, get_room_key(room)); + + struct GNUNET_MESSENGER_MemberStore *member_store = get_room_member_store(room); + struct GNUNET_MESSENGER_Member *member = add_store_member(member_store, member_id); + + if (GNUNET_NO == join_room (room, handle, member)) + return GNUNET_NO; - struct GNUNET_MESSENGER_MemberInfo *info = GNUNET_CONTAINER_multishortmap_get (room->member_infos, member_id); + const struct GNUNET_MESSENGER_Ego *ego = get_handle_ego(handle); + struct GNUNET_MESSENGER_MemberSession *session = get_member_session (member, &(ego->pub)); - if ((!info) && (GNUNET_NO == join_room (room, handle, member_id))) + if (!session) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "A valid session is required to join a room!\n"); return GNUNET_NO; + } + + struct GNUNET_MESSENGER_MemberNotify notify; + + notify.room = room; + notify.handle = handle; + notify.session = session; + + iterate_store_members(get_room_member_store(room), iterate_notify_about_members, ¬ify); 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); @@ -373,24 +310,39 @@ callback_tunnel_disconnect (void *cls, const struct GNUNET_CADET_Channel *channe int open_room (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle) { + GNUNET_assert((room) && (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); + const 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); + 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)); + if (room->port) + GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Port of room (%s) was opened!\n", + GNUNET_h2s(get_room_key(room))); + else + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Port of room (%s) could not be opened!\n", + GNUNET_h2s(get_room_key(room))); + + const struct GNUNET_ShortHashCode *member_id = get_handle_member_id (handle, get_room_key(room)); + + struct GNUNET_MESSENGER_MemberStore *member_store = get_room_member_store(room); + struct GNUNET_MESSENGER_Member *member = get_store_member(member_store, member_id); - struct GNUNET_MESSENGER_MemberInfo *info = GNUNET_CONTAINER_multishortmap_get (room->member_infos, member_id); + if (member) + goto exit_open_room; - if ((!info) && (GNUNET_NO == join_room (room, handle, member_id)) && (room->port)) + member = add_store_member(member_store, member_id); + + if ((GNUNET_NO == join_room (room, handle, member)) && (room->port)) { GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "You could not join the room, therefore it keeps closed!\n"); @@ -400,102 +352,57 @@ open_room (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHan 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); +exit_open_room: + return (room->port ? send_room_message (room, handle, create_message_peer (room->service)) : GNUNET_NO); } int -entry_room_at (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, +enter_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); + GNUNET_assert((room) && (handle) && (door)); - if (0 == GNUNET_memcmp(&(msg->body.peer.peer), door)) - return join_room_locally (room, handle); - } + struct GNUNET_PeerIdentity peer; + + if ((GNUNET_OK == get_service_peer_identity (room->service, &peer)) && + (0 == GNUNET_memcmp(&peer, door))) + return join_room_locally (room, handle); struct GNUNET_MESSENGER_SrvTunnel *tunnel = GNUNET_CONTAINER_multipeermap_get (room->tunnels, door); - if (tunnel) + if (!tunnel) { - switch (connect_tunnel (tunnel)) + tunnel = create_tunnel (room, door); + + if (GNUNET_OK != GNUNET_CONTAINER_multipeermap_put (room->tunnels, door, tunnel, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)) { - case GNUNET_YES: - return GNUNET_YES; - case GNUNET_NO: - return join_room_locally (room, handle); - default: + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "You could not connect to that door!\n"); + destroy_tunnel (tunnel); 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 + if (GNUNET_SYSERR == connect_tunnel (tunnel)) { - GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "You could not connect to that door!\n"); - + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Connection failure during entrance!\n"); + GNUNET_CONTAINER_multipeermap_remove (room->tunnels, door, tunnel); 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; + return join_room_locally (room, handle); } struct GNUNET_MQ_Envelope* -pack_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, +pack_room_message (const struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_MESSENGER_SrvHandle *handle, struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash, int mode) { + GNUNET_assert((room) && (handle) && (message) && (hash)); + message->header.timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); - const struct GNUNET_ShortHashCode *id = get_handle_member_id (handle, &(room->key)); + const struct GNUNET_ShortHashCode *id = get_handle_member_id (handle, get_room_key(room)); GNUNET_assert(id); @@ -524,7 +431,8 @@ iterate_send_room_message (void *cls, const struct GNUNET_PeerIdentity *key, voi { struct GNUNET_MESSENGER_SrvTunnel *tunnel = value; - if ((!is_tunnel_connected (tunnel)) || (!tunnel->contact_id)) + if ((!is_tunnel_connected (tunnel)) || + (get_tunnel_messenger_version(tunnel) < GNUNET_MESSENGER_VERSION)) return GNUNET_YES; struct GNUNET_MESSENGER_ClosureSendRoom *closure = cls; @@ -540,80 +448,88 @@ iterate_send_room_message (void *cls, const struct GNUNET_PeerIdentity *key, voi 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); - } + 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); + send_tunnel_envelope (tunnel, env, 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); +int +update_room_message (struct GNUNET_MESSENGER_SrvRoom *room, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); void +callback_room_handle_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +int 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_Message *message) { - 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_assert((room) && (handle)); - GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, iterate_send_room_message, &closure); + if (!message) + return GNUNET_NO; - if ((GNUNET_NO == closure.packed) && (closure.message == message)) - { - pack_room_message (room, handle, message, hash, - GNUNET_MESSENGER_PACK_MODE_UNKNOWN); + if (GNUNET_YES == is_message_session_bound(message)) + merge_room_last_messages(room, handle); - callback_room_sent (room, handle, NULL, copy_message (message), hash); - } -} + GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Sending message from handle with member id: %s\n", + GNUNET_sh2s(get_handle_member_id(handle, get_room_key(room)))); -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_HashCode hash; struct GNUNET_MESSENGER_ClosureSendRoom closure; closure.room = room; closure.handle = handle; - closure.exclude = tunnel; + closure.exclude = NULL; closure.message = message; - closure.hash = hash; + 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); + if (GNUNET_NO == closure.packed) + pack_room_message (room, handle, message, &hash, GNUNET_MESSENGER_PACK_MODE_UNKNOWN); + + const int new_message = update_room_message (room, message, &hash); - callback_room_sent (room, handle, NULL, copy_message (message), hash); + if (GNUNET_YES != new_message) + return GNUNET_SYSERR; + + switch (message->header.kind) + { + case GNUNET_MESSENGER_KIND_JOIN: + send_message_join (room, handle, message, &hash); + break; + case GNUNET_MESSENGER_KIND_PEER: + send_message_peer (room, handle, message, &hash); + break; + case GNUNET_MESSENGER_KIND_REQUEST: + send_message_request (room, handle, message, &hash); + break; + default: + break; } + + callback_room_handle_message (room, handle, message, &hash); + return GNUNET_YES; } 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_Message *message, const struct GNUNET_HashCode *hash) { + GNUNET_assert((room) && (tunnel)); + + if (!message) + return; + struct GNUNET_MESSENGER_ClosureSendRoom closure; struct GNUNET_HashCode message_hash; @@ -622,7 +538,7 @@ forward_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSE closure.room = room; closure.handle = NULL; closure.exclude = tunnel; - closure.message = copy_message (message); + closure.message = message; closure.hash = &message_hash; closure.packed = GNUNET_YES; @@ -630,257 +546,241 @@ forward_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSE } void -merge_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle) +check_room_peer_status (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel) { - if (!handle) + if (!room->peer_message) return; - if (!room->last_messages.head) - return; + const struct GNUNET_MESSENGER_Message *message = get_room_message(room, NULL, room->peer_message, GNUNET_NO); - while (room->last_messages.head != room->last_messages.tail) + if (!message) { - struct GNUNET_MESSENGER_ListMessage *element = room->last_messages.tail; + GNUNET_free(room->peer_message); + room->peer_message = NULL; + return; + } - struct GNUNET_MESSENGER_Message *message = create_message_merge (&(element->hash)); + struct GNUNET_MESSENGER_MemberStore *member_store = get_room_member_store(room); + struct GNUNET_MESSENGER_Member *member = get_store_member_of(member_store, message); - if (message) - { - struct GNUNET_HashCode hash; + if (!member) + goto resend_peer_message; - send_room_message (room, handle, message, &hash); - destroy_message (message); - } + struct GNUNET_MESSENGER_MemberSession *session = get_member_session_of(member, message, room->peer_message); - if (element->prev) - GNUNET_CONTAINER_DLL_remove(room->last_messages.head, room->last_messages.tail, element); - } -} + if (GNUNET_YES == is_member_session_closed(session)) + goto resend_peer_message; -struct GNUNET_CADET_Handle* -get_room_cadet (struct GNUNET_MESSENGER_SrvRoom *room) -{ - return room->service->cadet; -} + if (tunnel) + forward_tunnel_message(tunnel, message, room->peer_message); -struct GNUNET_HashCode* -get_room_key (struct GNUNET_MESSENGER_SrvRoom *room) -{ - return &(room->key); + return; + +resend_peer_message: + if (room->host) + send_room_message (room, room->host, create_message_peer (room->service)); } -const struct GNUNET_MESSENGER_SrvTunnel* -get_room_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_PeerIdentity *peer) +static void +merge_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_HashCode *hash) { - return GNUNET_CONTAINER_multipeermap_get (room->tunnels, peer); + GNUNET_assert((room) && (handle) && (hash)); + + send_room_message (room, handle, create_message_merge (hash)); } -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) +void +merge_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle) { - const struct GNUNET_MESSENGER_Message *message = get_store_message (&(room->store), hash); + GNUNET_assert(room); - if ((message) || (!handle) || (GNUNET_YES != request) - || (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains (room->requested, hash))) - return message; + if (!handle) + return; - struct GNUNET_MESSENGER_Message *request_msg = create_message_request (hash); + if (!room->last_messages.head) + return; - if (request_msg) - { - if (GNUNET_CONTAINER_multihashmap_put (room->requested, hash, NULL, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST) == GNUNET_OK) - { - struct GNUNET_HashCode request_hash; + while (room->last_messages.head != room->last_messages.tail) + merge_room_message (room, handle, &(room->last_messages.tail->hash)); +} - send_room_message (room, handle, request_msg, &request_hash); - } +void +callback_room_deletion (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_HashCode *hash) +{ + const struct GNUNET_MESSENGER_Message *message = get_room_message(room, NULL, hash, GNUNET_NO); - destroy_message (request_msg); - } + if (message) + add_to_list_messages(&(room->last_messages), &(message->header.previous)); - return message; + if (GNUNET_OK != delete_store_message (get_room_message_store(room), hash)) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Deletion of message failed! (%s)\n", GNUNET_h2s(hash)); + return; + } } void -callback_room_disconnect (struct GNUNET_MESSENGER_SrvRoom *room, void *cls) +callback_room_merge (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_HashCode *hash) { - 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); - } - } + merge_room_message (room, room->host, hash); } int -callback_verify_room_message (struct GNUNET_MESSENGER_SrvRoom *room, void *cls, - struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash) +delete_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_HashCode *hash, const struct GNUNET_TIME_Relative delay) { - if (GNUNET_MESSENGER_KIND_UNKNOWN == message->header.kind) - return GNUNET_SYSERR; + GNUNET_assert((room) && (session) && (hash)); - struct GNUNET_MESSENGER_SrvContact *contact = GNUNET_CONTAINER_multishortmap_get (room->members, - &(message->header.sender_id)); + const struct GNUNET_TIME_Relative forever = GNUNET_TIME_relative_get_forever_ (); - if (!contact) + if (0 == GNUNET_memcmp(&forever, &delay)) { - 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)); + GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Deletion is delayed forever: operation is impossible!\n"); + return GNUNET_SYSERR; } - if ((!contact) || (GNUNET_SYSERR == verify_message (message, hash, get_contact_key (contact)))) - return GNUNET_SYSERR; + const struct GNUNET_MESSENGER_Message *message = get_room_message(room, NULL, hash, GNUNET_NO); - if (GNUNET_YES == room->strict_access) + if (!message) + return GNUNET_YES; + + if (GNUNET_YES != check_member_session_history(session, hash, GNUNET_YES)) { - struct GNUNET_MESSENGER_MemberInfo *info = GNUNET_CONTAINER_multishortmap_get (room->member_infos, - &(message->header.sender_id)); + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Unpermitted request for deletion by member (%s) of message (%s)!\n", + GNUNET_sh2s(get_member_session_id(session)), GNUNET_h2s(hash)); - if ((info) && (GNUNET_MESSENGER_MEMBER_BLOCKED == info->access)) - return GNUNET_SYSERR; + return GNUNET_NO; } - if (GNUNET_YES == contains_store_message (&(room->store), hash)) - return GNUNET_NO; + struct GNUNET_MESSENGER_OperationStore *operation_store = get_room_operation_store(room); + + if (GNUNET_OK != use_store_operation(operation_store, hash, GNUNET_MESSENGER_OP_DELETE, delay)) + { + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Deletion has failed: operation denied!\n"); + return GNUNET_SYSERR; + } return GNUNET_YES; } -static void -search_room_for_message (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_HashCode *hash) +struct GNUNET_CADET_Handle* +get_room_cadet (struct GNUNET_MESSENGER_SrvRoom *room) { - const struct GNUNET_MESSENGER_Message *message = get_room_message (room, room->host, hash, GNUNET_YES); + GNUNET_assert(room); - if (!message) - return; + return room->service->cadet; +} - if (GNUNET_MESSENGER_KIND_MERGE == message->header.kind) - search_room_for_message (room, &(message->body.merge.previous)); +const struct GNUNET_HashCode* +get_room_key (const struct GNUNET_MESSENGER_SrvRoom *room) +{ + GNUNET_assert(room); - search_room_for_message (room, &(message->header.previous)); + return &(room->key); } -static void -idle_request_room_messages (void *cls) +const struct GNUNET_MESSENGER_SrvTunnel* +get_room_tunnel (const struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_PeerIdentity *peer) { - struct GNUNET_MESSENGER_SrvRoom *room = cls; + GNUNET_assert((room) && (peer)); - room->idle = NULL; + return GNUNET_CONTAINER_multipeermap_get (room->tunnels, peer); +} - struct GNUNET_MESSENGER_ListMessage *element = room->last_messages.head; +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) +{ + GNUNET_assert((room) && (hash)); - while (element) - { - search_room_for_message (room, &(element->hash)); + const struct GNUNET_MESSENGER_Message *message = get_store_message (get_room_message_store(room), hash); - element = element->next; - } + if ((message) || (!handle) || (GNUNET_YES != request)) + return message; - merge_room_last_messages (room, room->host); + struct GNUNET_MESSENGER_OperationStore* operation_store = get_room_operation_store(room); - room->idle = GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_get_second_ (), - GNUNET_SCHEDULER_PRIORITY_IDLE, idle_request_room_messages, - cls); + if (GNUNET_OK == use_store_operation(operation_store, hash, GNUNET_MESSENGER_OP_REQUEST, GNUNET_MESSENGER_REQUEST_DELAY)) + send_room_message (room, handle, create_message_request (hash)); + + return NULL; } void -update_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_MESSENGER_Message *message, - const struct GNUNET_HashCode *hash) +callback_room_disconnect (struct GNUNET_MESSENGER_SrvRoom *room, void *cls) { - 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; + struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls; - merging = merging->next; - } + if (!room->host) + return; - if (merging) - element = merging->next; - } + struct GNUNET_PeerIdentity identity; + get_tunnel_peer_identity(tunnel, &identity); - while (element) - { - if (0 == GNUNET_CRYPTO_hash_cmp (&(element->hash), &(message->header.previous))) - break; + if ((GNUNET_YES != GNUNET_CONTAINER_multipeermap_remove (room->tunnels, &identity, tunnel)) || + (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains(room->tunnels, &identity))) + return; - element = element->next; - } + if (GNUNET_YES == contains_list_tunnels (&(room->basement), &identity)) + send_room_message (room, room->host, create_message_miss (&identity)); +} - if ((merging) && (!element)) +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) { - element = merging; - merging = NULL; + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Message error: Kind is unknown! (%d)\n", message->header.kind); + return GNUNET_SYSERR; } - if (element) - { - GNUNET_memcpy(&(element->hash), hash, sizeof(struct GNUNET_HashCode)); + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Receiving message of kind: %s!\n", + GNUNET_MESSENGER_name_of_kind(message->header.kind)); - 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); + return GNUNET_OK; } -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) +static void +idle_request_room_messages (void *cls) { - 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); + struct GNUNET_MESSENGER_SrvRoom *room = cls; - 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; + room->idle = NULL; - if (hash) - add_to_list_messages (&(info->session_messages), hash); + struct GNUNET_MESSENGER_OperationStore *operation_store = get_room_operation_store(room); + + if ((room->last_messages.head != room->last_messages.tail) && + (GNUNET_MESSENGER_OP_UNKNOWN == get_store_operation_type(operation_store, &(room->last_messages.tail->hash)))) + use_store_operation( + operation_store, + &(room->last_messages.tail->hash), + GNUNET_MESSENGER_OP_MERGE, + GNUNET_MESSENGER_MERGE_DELAY + ); + + room->idle = GNUNET_SCHEDULER_add_delayed_with_priority ( + GNUNET_MESSENGER_IDLE_DELAY, + GNUNET_SCHEDULER_PRIORITY_IDLE, + idle_request_room_messages, + cls + ); } void rebuild_room_basement_structure (struct GNUNET_MESSENGER_SrvRoom *room) { + GNUNET_assert(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))) + 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)); @@ -918,7 +818,7 @@ rebuild_room_basement_structure (struct GNUNET_MESSENGER_SrvRoom *room) } } -void +static void handle_room_messages (struct GNUNET_MESSENGER_SrvRoom *room) { while (room->handling.head) @@ -927,29 +827,159 @@ handle_room_messages (struct GNUNET_MESSENGER_SrvRoom *room) 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)); + if (!msg) + goto finish_handling; + + struct GNUNET_MESSENGER_MemberStore *member_store = get_room_member_store(room); + struct GNUNET_MESSENGER_Member *member = get_store_member_of(member_store, msg); + + if (!member) + goto finish_handling; + + struct GNUNET_MESSENGER_MemberSession *session = get_member_session_of(member, msg, &(element->hash)); + if (session) + handle_service_message (room->service, room, session, msg, &(element->hash)); + +finish_handling: GNUNET_CONTAINER_DLL_remove(room->handling.head, room->handling.tail, element); GNUNET_free(element); } } -#include "gnunet-service-messenger_message_recv.h" +static void +remove_room_last_message (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_HashCode *hash) +{ + remove_from_list_messages(&(room->last_messages), hash); + + struct GNUNET_MESSENGER_OperationStore *operation_store = get_room_operation_store(room); + + if (GNUNET_MESSENGER_OP_MERGE == get_store_operation_type(operation_store, hash)) + cancel_store_operation(operation_store, hash); +} + +int +update_room_message (struct GNUNET_MESSENGER_SrvRoom *room, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + GNUNET_assert((room) && (message) && (hash)); + + struct GNUNET_MESSENGER_OperationStore *operation_store = get_room_operation_store(room); + + const int requested = (GNUNET_MESSENGER_OP_REQUEST == get_store_operation_type(operation_store, hash)? + GNUNET_YES : GNUNET_NO + ); + + if (GNUNET_YES == requested) + cancel_store_operation(operation_store, hash); + + const struct GNUNET_MESSENGER_Message *old_message = get_room_message (room, NULL, hash, GNUNET_NO); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Handle a message in room (%s).\n", GNUNET_h2s (get_room_key(room))); + + if ((old_message) || (GNUNET_OK != put_store_message (get_room_message_store(room), hash, message))) + { + if (old_message != message) + destroy_message(message); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Duplicate message got dropped!\n"); + return GNUNET_NO; + } + + if ((GNUNET_YES == requested) || + (GNUNET_MESSENGER_KIND_INFO == message->header.kind) || + (GNUNET_MESSENGER_KIND_REQUEST == message->header.kind)) + return GNUNET_YES; + + if (GNUNET_MESSENGER_KIND_MERGE == message->header.kind) + remove_room_last_message(room, &(message->body.merge.previous)); + remove_room_last_message(room, &(message->header.previous)); + + add_to_list_messages (&(room->last_messages), hash); + + return GNUNET_YES; +} + +struct GNUNET_MESSENGER_MemberSessionCompletion +{ + struct GNUNET_MESSENGER_MemberSessionCompletion *prev; + struct GNUNET_MESSENGER_MemberSessionCompletion *next; + + struct GNUNET_MESSENGER_MemberSession *session; +}; + +struct GNUNET_MESSENGER_MemberUpdate +{ + const struct GNUNET_MESSENGER_Message *message; + const struct GNUNET_HashCode *hash; + + struct GNUNET_MESSENGER_MemberSessionCompletion *head; + struct GNUNET_MESSENGER_MemberSessionCompletion *tail; +}; + +static int +iterate_update_member_sessions (void *cls, const struct GNUNET_IDENTITY_PublicKey *public_key, + struct GNUNET_MESSENGER_MemberSession *session) +{ + struct GNUNET_MESSENGER_MemberUpdate *update = cls; + + update_member_session_history(session, update->message, update->hash); + + if (GNUNET_YES == is_member_session_completed(session)) + { + struct GNUNET_MESSENGER_MemberSessionCompletion *element = GNUNET_new( + struct GNUNET_MESSENGER_MemberSessionCompletion + ); + + element->session = session; + + GNUNET_CONTAINER_DLL_insert_tail(update->head, update->tail, element); + } + + return GNUNET_YES; +} void -callback_room_recv (struct GNUNET_MESSENGER_SrvRoom *room, void *cls, struct GNUNET_MESSENGER_Message *message, - const struct GNUNET_HashCode *hash) +callback_room_handle_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) { - struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls; + struct GNUNET_MESSENGER_MemberStore *member_store = get_room_member_store(room); + struct GNUNET_MESSENGER_Member *member = get_store_member_of(member_store, message); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Callback for message (%s)\n", GNUNET_h2s (hash)); + + if (!member) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Message handling dropped: Member is missing!\n"); + return; + } + + struct GNUNET_MESSENGER_MemberSession *session = get_member_session_of(member, message, hash); - if (GNUNET_OK != put_store_message (&(room->store), hash, message)) + if (!session) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Message handling dropped: Session is missing!\n"); return; + } + + struct GNUNET_MESSENGER_MemberUpdate update; + update.message = message; + update.hash = hash; + + update.head = NULL; + update.tail = NULL; + + iterate_store_members(member_store, iterate_update_member_sessions, &update); + + while (update.head) + { + struct GNUNET_MESSENGER_MemberSessionCompletion *element = update.head; - update_room_last_messages (room, message, hash); + remove_member_session (element->session->member, element->session); - if (GNUNET_MESSENGER_KIND_INFO != message->header.kind) - forward_room_message (room, tunnel, message, hash); + GNUNET_CONTAINER_DLL_remove(update.head, update.tail, element); + GNUNET_free (element); + } const int start_handle = room->handling.head ? GNUNET_NO : GNUNET_YES; @@ -957,32 +987,29 @@ callback_room_recv (struct GNUNET_MESSENGER_SrvRoom *room, void *cls, struct GNU 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); + handle_message_join (room, session, message, hash); break; case GNUNET_MESSENGER_KIND_LEAVE: - recv_message_leave (room, tunnel, message, hash); + handle_message_leave (room, session, message, hash); break; case GNUNET_MESSENGER_KIND_NAME: - recv_message_name (room, tunnel, message, hash); + handle_message_name (room, session, message, hash); break; case GNUNET_MESSENGER_KIND_KEY: - recv_message_key (room, tunnel, message, hash); + handle_message_key (room, session, message, hash); break; case GNUNET_MESSENGER_KIND_PEER: - recv_message_peer (room, tunnel, message, hash); + handle_message_peer (room, session, message, hash); break; case GNUNET_MESSENGER_KIND_ID: - recv_message_id (room, tunnel, message, hash); + handle_message_id (room, session, message, hash); break; case GNUNET_MESSENGER_KIND_MISS: - recv_message_miss (room, tunnel, message, hash); + handle_message_miss (room, session, message, hash); break; - case GNUNET_MESSENGER_KIND_REQUEST: - recv_message_request (room, tunnel, message, hash); + case GNUNET_MESSENGER_KIND_DELETE: + handle_message_delete (room, session, message, hash); break; default: break; @@ -992,60 +1019,71 @@ callback_room_recv (struct GNUNET_MESSENGER_SrvRoom *room, void *cls, struct GNU handle_room_messages (room); } -#include "gnunet-service-messenger_message_send.h" +static void +get_room_data_subdir (struct GNUNET_MESSENGER_SrvRoom *room, char **dir) +{ + GNUNET_assert((room) && (dir)); + + GNUNET_asprintf (dir, "%s%s%c%s%c", room->service->dir, "rooms", DIR_SEPARATOR, GNUNET_h2s (get_room_key(room)), DIR_SEPARATOR); +} 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) +load_room (struct GNUNET_MESSENGER_SrvRoom *room) { - const struct GNUNET_MESSENGER_Message *old_message = get_room_message (room, handle, hash, GNUNET_NO); + GNUNET_assert(room); - if ((old_message) || (GNUNET_OK != put_store_message (&(room->store), hash, message))) + char *room_dir; + get_room_data_subdir (room, &room_dir); + + if (GNUNET_YES == GNUNET_DISK_directory_test (room_dir, GNUNET_YES)) { - if (old_message != message) - GNUNET_free(message); + load_member_store (get_room_member_store(room), room_dir); + load_message_store (get_room_message_store(room), room_dir); + load_operation_store(get_room_operation_store(room), room_dir); + + char *basement_file; + GNUNET_asprintf (&basement_file, "%s%s", room_dir, "basement.list"); + + load_list_tunnels(&(room->basement), basement_file); + GNUNET_free(basement_file); + + char *last_messages_file; + GNUNET_asprintf (&last_messages_file, "%s%s", room_dir, "last_messages.list"); + + load_list_messages(&(room->last_messages), last_messages_file); + GNUNET_free(last_messages_file); } - else - { - struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls; // may be NULL - update_room_last_messages (room, message, hash); + GNUNET_free(room_dir); +} + +void +save_room (struct GNUNET_MESSENGER_SrvRoom *room) +{ + GNUNET_assert(room); + + char *room_dir; + get_room_data_subdir (room, &room_dir); - const int start_handle = room->handling.head ? GNUNET_NO : GNUNET_YES; + if ((GNUNET_YES == GNUNET_DISK_directory_test (room_dir, GNUNET_NO)) || + (GNUNET_OK == GNUNET_DISK_directory_create (room_dir))) + { + save_member_store(get_room_member_store(room), room_dir); + save_message_store (get_room_message_store(room), room_dir); + save_operation_store(get_room_operation_store(room), room_dir); - add_to_list_messages (&(room->handling), hash); + char *basement_file; + GNUNET_asprintf (&basement_file, "%s%s", room_dir, "basement.list"); - 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; - } + save_list_tunnels(&(room->basement), basement_file); + GNUNET_free(basement_file); + + char *last_messages_file; + GNUNET_asprintf (&last_messages_file, "%s%s", room_dir, "last_messages.list"); - if (GNUNET_YES == start_handle) - handle_room_messages (room); + save_list_messages(&(room->last_messages), last_messages_file); + GNUNET_free(last_messages_file); } + + GNUNET_free(room_dir); } diff --git a/src/messenger/gnunet-service-messenger_room.h b/src/messenger/gnunet-service-messenger_room.h index 36c9e8cf5..a40961177 100644 --- a/src/messenger/gnunet-service-messenger_room.h +++ b/src/messenger/gnunet-service-messenger_room.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -33,33 +33,29 @@ #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_member_store.h" #include "gnunet-service-messenger_message_store.h" +#include "gnunet-service-messenger_operation_store.h" #include "messenger_api_ego.h" -enum GNUNET_MESSENGER_MemberAccess -{ - GNUNET_MESSENGER_MEMBER_ALLOWED = 1, - GNUNET_MESSENGER_MEMBER_BLOCKED = 1, +#define GNUNET_MESSENGER_IDLE_DELAY GNUNET_TIME_relative_multiply \ + (GNUNET_TIME_relative_get_second_ (), 5) - GNUNET_MESSENGER_MEMBER_UNKNOWN = 0 -}; +#define GNUNET_MESSENGER_REQUEST_DELAY GNUNET_TIME_relative_multiply \ + (GNUNET_TIME_relative_get_minute_ (), 5) -struct GNUNET_MESSENGER_MemberInfo -{ - enum GNUNET_MESSENGER_MemberAccess access; +#define GNUNET_MESSENGER_MERGE_DELAY GNUNET_TIME_relative_multiply \ + (GNUNET_TIME_relative_get_second_ (), 30) - struct GNUNET_MESSENGER_ListMessages session_messages; -}; +struct GNUNET_MESSENGER_SrvTunnel; +struct GNUNET_MESSENGER_MemberSession; struct GNUNET_MESSENGER_SrvRoom { @@ -70,11 +66,10 @@ struct GNUNET_MESSENGER_SrvRoom 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_MemberStore member_store; + struct GNUNET_MESSENGER_MessageStore message_store; + struct GNUNET_MESSENGER_OperationStore operation_store; struct GNUNET_MESSENGER_ListTunnels basement; struct GNUNET_MESSENGER_ListMessages last_messages; @@ -83,15 +78,13 @@ struct GNUNET_MESSENGER_SrvRoom 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 + * @param[in/out] handle Handle + * @param[in] key Key of room * @return New room */ struct GNUNET_MESSENGER_SrvRoom* @@ -100,58 +93,42 @@ create_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_Hash /** * Destroys a room and frees its memory fully. * - * @param room Room + * @param[in/out] 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. + * Returns the used member store of a given room. * - * @param room Room - * @param id Member id - * @param pubkey Public key of EGO + * @param[in/out] room Room + * @return Member store */ -void -add_room_contact (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *id, - const struct GNUNET_IDENTITY_PublicKey *pubkey); +struct GNUNET_MESSENGER_MemberStore* +get_room_member_store (struct GNUNET_MESSENGER_SrvRoom *room); /** - * 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. + * Returns the used message store of a given room. * - * @param room Room - * @param id Member id - * @return Member information or NULL + * @param[in/out] room Room + * @return Message store */ -struct GNUNET_MESSENGER_MemberInfo* -get_room_member_info (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *id); +struct GNUNET_MESSENGER_MessageStore* +get_room_message_store (struct GNUNET_MESSENGER_SrvRoom *room); /** - * Tries to generate and allocate a new unique member id checking all current members for possible - * duplicates. If the function fails, NULL gets returned. + * Returns the used operation store of a given room. * - * @param room Room - * @return New member id or NULL + * @param[in/out] room Room + * @return Operation store */ -struct GNUNET_ShortHashCode* -generate_room_member_id (const struct GNUNET_MESSENGER_SrvRoom *room); +struct GNUNET_MESSENGER_OperationStore* +get_room_operation_store (struct GNUNET_MESSENGER_SrvRoom *room); /** * Returns the member id of the member representing the handle currently hosting this room. * - * @param room Room + * @param[in] room Room * @return Host member id or NULL */ const struct GNUNET_ShortHashCode* @@ -160,8 +137,8 @@ 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 + * @param[in/out] room Room + * @param[in] unique_id Unique member id */ void change_room_host_id (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *unique_id); @@ -172,11 +149,11 @@ change_room_host_id (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ * * 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. + * 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. + * @param[in/out] room Room + * @param[in/out] handle Handle + * @return #GNUNET_YES on success, #GNUNET_NO on failure. */ int open_room (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle); @@ -186,26 +163,15 @@ open_room (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHan * 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. + * @param[in/out] room Room + * @param[in/out] handle Handle + * @param[in] 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, +enter_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 @@ -213,18 +179,18 @@ find_room_tunnel_to (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ * * 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 + * 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[in] room Room + * @param[in] handle Handle + * @param[in/out] message Message * @param[out] hash Hash of message - * @param mode Packing mode + * @param[in] 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, +pack_room_message (const struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_MESSENGER_SrvHandle *handle, struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash, int mode); /** @@ -234,59 +200,69 @@ pack_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGE * 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 + * The function returns #GNUNET_YES on success, #GNUNET_NO if message is null and + * #GNUNET_SYSERR if the message was known already. + * + * @param[in/out] room Room + * @param[in/out] handle Handle + * @param[in/out] message Message + * @return #GNUNET_YES on success, #GNUNET_NO or #GNUNET_SYSERR otherwise. */ -void +int 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_Message *message); /** - * 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. + * Forwards a message with a given hash to a specific tunnel inside of a room. * - * @param room Room - * @param handle Handle - * @param message Message - * @param[out] hash Hash of message - * @param tunnel Tunnel + * @param[in/out] room Room + * @param[in/out] tunnel Tunnel + * @param[in/out] message Message + * @param[in] hash Hash of message */ 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); +forward_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); /** - * Forwards a message with a given hash to a specific tunnel inside of a room. + * Checks the current state of opening a given room from this peer and re-publishes it + * if necessary to a selected tunnel or to all connected tunnels if necessary or if the + * selected tunnel is NULL. * - * @param room Room - * @param tunnel Tunnel - * @param message Message - * @param hash Hash of message + * @param[in/out] room Room + * @param[in/out] tunnel Tunnel */ 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); +check_room_peer_status (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel); /** * 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 + * @param[in/out] room Room + * @param[in/out] handle Handle */ void merge_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle); +/** + * Deletes a message from the room with a given hash in a specific delay if + * the provided member by its session is permitted to do so. + * + * @param[in/out] room Room + * @param[in/out] session Member session + * @param[in] hash Hash of message + * @param[in] delay Delay of deletion + * @return #GNUNET_YES on success, #GNUNET_NO if permission gets denied, #GNUNET_SYSERR on operation failure + */ +int +delete_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, + const struct GNUNET_HashCode *hash, const struct GNUNET_TIME_Relative delay); + /** * Returns the CADET handle from a rooms service. * - * @param room Room + * @param[in/out] room Room * @return CADET handle */ struct GNUNET_CADET_Handle* @@ -295,26 +271,26 @@ get_room_cadet (struct GNUNET_MESSENGER_SrvRoom *room); /** * Returns the shared secret you need to access a room. * - * @param room Room + * @param[in] room Room * @return Shared secret */ -struct GNUNET_HashCode* -get_room_key (struct GNUNET_MESSENGER_SrvRoom *room); +const struct GNUNET_HashCode* +get_room_key (const 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 + * @param[in] room Room + * @param[in] 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); +get_room_tunnel (const 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 + * 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. @@ -323,10 +299,10 @@ get_room_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_Peer * 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 + * @param[in/out] room Room + * @param[in/out] handle Handle + * @param[in] hash Hash of message + * @param[in] request Flag to request a message * @return Message or NULL */ const struct GNUNET_MESSENGER_Message* @@ -334,45 +310,30 @@ get_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER 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. + * 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 - * @param old_id Old member id - * @param new_id New member id - * @param hash Hash of id message + * @param[in/out] room Room */ 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); +rebuild_room_basement_structure (struct GNUNET_MESSENGER_SrvRoom *room); /** - * 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. + * 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 room Room + * @param[out] room Room */ void -rebuild_room_basement_structure (struct GNUNET_MESSENGER_SrvRoom *room); +load_room (struct GNUNET_MESSENGER_SrvRoom *room); /** - * Handles all queued up messages of a room to handle in correct order. + * 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 room Room + * @param[in] room Room */ void -handle_room_messages (struct GNUNET_MESSENGER_SrvRoom *room); +save_room (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 index 963314fd8..8c63e9bf4 100644 --- a/src/messenger/gnunet-service-messenger_service.c +++ b/src/messenger/gnunet-service-messenger_service.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -24,11 +24,8 @@ */ #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) @@ -43,23 +40,11 @@ callback_shutdown_service (void *cls) } } -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) { + GNUNET_assert((config) && (service_handle)); + struct GNUNET_MESSENGER_Service *service = GNUNET_new(struct GNUNET_MESSENGER_Service); service->config = config; @@ -90,24 +75,16 @@ create_service (const struct GNUNET_CONFIGURATION_Handle *config, struct GNUNET_ } 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_ego_store(get_service_ego_store(service), service->config); 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; -} + init_contact_store(get_service_contact_store(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; + return service; } static int @@ -118,17 +95,11 @@ iterate_destroy_rooms (void *cls, const struct GNUNET_HashCode *key, void *value 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) { + GNUNET_assert(service); + if (service->shutdown) { GNUNET_SCHEDULER_cancel (service->shutdown); @@ -136,16 +107,13 @@ destroy_service (struct GNUNET_MESSENGER_Service *service) service->shutdown = NULL; } - GNUNET_CONTAINER_multihashmap_iterate (service->egos, iterate_destroy_egos, NULL); - + clear_ego_store(get_service_ego_store(service)); 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); + + clear_contact_store(get_service_contact_store(service)); if (service->cadet) { @@ -154,13 +122,6 @@ destroy_service (struct GNUNET_MESSENGER_Service *service) service->cadet = NULL; } - if (service->identity) - { - GNUNET_IDENTITY_disconnect (service->identity); - - service->identity = NULL; - } - if (service->dir) { GNUNET_free(service->dir); @@ -173,44 +134,27 @@ destroy_service (struct GNUNET_MESSENGER_Service *service) GNUNET_free(service); } -struct GNUNET_MESSENGER_Ego* -lookup_service_ego (struct GNUNET_MESSENGER_Service *service, const char *identifier) +struct GNUNET_MESSENGER_EgoStore* +get_service_ego_store (struct GNUNET_MESSENGER_Service *service) { - GNUNET_assert(identifier); + GNUNET_assert(service); - struct GNUNET_HashCode hash; - - GNUNET_CRYPTO_hash(identifier, strlen(identifier), &hash); - return GNUNET_CONTAINER_multihashmap_get(service->egos, &hash); + return &(service->ego_store); } -void -update_service_ego (struct GNUNET_MESSENGER_Service *service, const char *identifier, - const struct GNUNET_IDENTITY_PrivateKey* key) +struct GNUNET_MESSENGER_ContactStore* +get_service_contact_store (struct GNUNET_MESSENGER_Service *service) { - GNUNET_assert((identifier) && (key)); - - struct GNUNET_HashCode hash; - - GNUNET_CRYPTO_hash(identifier, strlen(identifier), &hash); + GNUNET_assert(service); - 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"); + return &(service->contact_store); } struct GNUNET_MESSENGER_SrvHandle* add_service_handle (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MQ_Handle *mq) { + GNUNET_assert((service) && (mq)); + struct GNUNET_MESSENGER_SrvHandle *handle = create_handle (service, mq); if (handle) @@ -224,6 +168,8 @@ add_service_handle (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MQ_H void remove_service_handle (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle) { + GNUNET_assert((service) && (handle)); + if (!handle) return; @@ -234,68 +180,16 @@ remove_service_handle (struct GNUNET_MESSENGER_Service *service, struct GNUNET_M int get_service_peer_identity (const struct GNUNET_MESSENGER_Service *service, struct GNUNET_PeerIdentity *peer) { - return GNUNET_CRYPTO_get_peer_identity (service->config, peer); -} + GNUNET_assert((service) && (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; - } + return GNUNET_CRYPTO_get_peer_identity (service->config, peer); } struct GNUNET_MESSENGER_SrvRoom* -get_service_room (struct GNUNET_MESSENGER_Service *service, const struct GNUNET_HashCode *key) +get_service_room (const struct GNUNET_MESSENGER_Service *service, const struct GNUNET_HashCode *key) { + GNUNET_assert((service) && (key)); + return GNUNET_CONTAINER_multihashmap_get (service->rooms, key); } @@ -303,6 +197,8 @@ int open_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key) { + GNUNET_assert((service) && (handle) && (key)); + struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key); if (room) @@ -310,8 +206,10 @@ open_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSE 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))) + 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); @@ -322,11 +220,13 @@ int entry_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_PeerIdentity *door, const struct GNUNET_HashCode *key) { + GNUNET_assert((service) && (handle) && (door) && (key)); + struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key); if (room) { - if (GNUNET_YES == entry_room_at (room, handle, door)) + if (GNUNET_YES == enter_room_at (room, handle, door)) return GNUNET_YES; else return GNUNET_NO; @@ -334,8 +234,10 @@ entry_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESS 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))) + if ((GNUNET_YES == enter_room_at (room, handle, door)) && + (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (service->rooms, + key, room, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))) { return GNUNET_YES; } @@ -351,20 +253,14 @@ int close_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key) { + GNUNET_assert((service) && (handle) && (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); - } + send_room_message (room, handle, create_message_leave ()); const struct GNUNET_ShortHashCode *id = get_handle_member_id (handle, key); @@ -393,124 +289,18 @@ close_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESS 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_MemberSession *session, const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) { - struct GNUNET_MESSENGER_ListHandle *element = service->handles.head; + GNUNET_assert((service) && (room) && (session) && (message) && (hash)); - const uint16_t length = get_message_size (message); + struct GNUNET_MESSENGER_ListHandle *element = service->handles.head; 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); - } - + notify_handle_message (element->handle, get_room_key(room), session, message, hash); element = element->next; } } diff --git a/src/messenger/gnunet-service-messenger_service.h b/src/messenger/gnunet-service-messenger_service.h index 246c74771..aa43fa457 100644 --- a/src/messenger/gnunet-service-messenger_service.h +++ b/src/messenger/gnunet-service-messenger_service.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -33,13 +33,14 @@ #include "gnunet_disk_lib.h" #include "gnunet_identity_service.h" -#include "messenger_api_ego.h" - +#include "gnunet-service-messenger_ego_store.h" #include "gnunet-service-messenger_list_handles.h" -#include "gnunet-service-messenger_contact.h" +#include "messenger_api_contact_store.h" #include "gnunet-service-messenger_room.h" +#include "gnunet-service-messenger_member_session.h" + struct GNUNET_MESSENGER_Service { const struct GNUNET_CONFIGURATION_Handle *config; @@ -50,21 +51,20 @@ struct GNUNET_MESSENGER_Service char *dir; struct GNUNET_CADET_Handle *cadet; - struct GNUNET_IDENTITY_Handle *identity; - struct GNUNET_CONTAINER_MultiHashMap *egos; + struct GNUNET_MESSENGER_EgoStore ego_store; + struct GNUNET_MESSENGER_ContactStore contact_store; 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 + * @param[in] config Configuration + * @param[in/out] service_handle GNUnet service handle * @return New service */ struct GNUNET_MESSENGER_Service* @@ -73,39 +73,34 @@ create_service (const struct GNUNET_CONFIGURATION_Handle *config, struct GNUNET_ /** * Destroys a service and frees its memory fully. * - * @param service Service + * @param[in/out] service Service */ void destroy_service (struct GNUNET_MESSENGER_Service *service); /** - * Lookups an EGO which was registered to a service under - * a specific identifier. + * Returns the used EGO-store of a given service. * - * @param service Service - * @param identifier Identifier string - * @return EGO or NULL + * @param[in/out] service Service + * @return EGO-store */ -struct GNUNET_MESSENGER_Ego* -lookup_service_ego (struct GNUNET_MESSENGER_Service *service, const char *identifier); +struct GNUNET_MESSENGER_EgoStore* +get_service_ego_store (struct GNUNET_MESSENGER_Service *service); /** - * Updates the registration of an EGO to a service under - * a specific identifier with a new key. + * Returns the used contact store of a given service. * - * @param service Service - * @param identifier Identifier string - * @param key Private EGO key + * @param[in/out] service Service + * @return Contact store */ -void -update_service_ego (struct GNUNET_MESSENGER_Service *service, const char *identifier, - const struct GNUNET_IDENTITY_PrivateKey* key); +struct GNUNET_MESSENGER_ContactStore* +get_service_contact_store (struct GNUNET_MESSENGER_Service *service); /** * Creates and adds a new handle to a service using a given message queue. * - * @param service Service - * @param mq Message queue + * @param[in/out] service Service + * @param[in/out] mq Message queue * @return New handle */ struct GNUNET_MESSENGER_SrvHandle* @@ -114,77 +109,42 @@ add_service_handle (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MQ_H /** * Removes a handle from a service and destroys it. * - * @param service Service - * @param handle Handle + * @param[in/out] service Service + * @param[in/out] 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. + * parameter. The functions returns #GNUNET_OK on success, otherwise #GNUNET_SYSERR. * - * @param service Service + * @param[in] service Service * @param[out] peer Peer identity - * @return GNUNET_OK on success, otherwise GNUNET_SYSERR + * @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 + * @param[in] service Service + * @param[in] 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); +get_service_room (const 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. + * 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 + * @param[in/out] service Service + * @param[in/out] handle Handle + * @param[in] 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, @@ -192,16 +152,16 @@ open_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSE /** * 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. + * 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 + * @param[in/out] service Service + * @param[in/out] handle Handle + * @param[in] door Peer identity + * @param[in] 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, @@ -209,51 +169,33 @@ entry_service_room (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESS /** * 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. + * 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 + * @param[in/out] service Service + * @param[in/out] handle Handle + * @param[in] 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 + * @param[in/out] service Service + * @param[in/out] room Room + * @param[in] session Member session + * @param[in] message Message + * @param[in] hash Hash of message */ void handle_service_message (struct GNUNET_MESSENGER_Service *service, struct GNUNET_MESSENGER_SrvRoom *room, + const struct GNUNET_MESSENGER_MemberSession *session, 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 index df9e5c4c7..f7e8713c6 100644 --- a/src/messenger/gnunet-service-messenger_tunnel.c +++ b/src/messenger/gnunet-service-messenger_tunnel.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -26,7 +26,8 @@ #include "gnunet-service-messenger_tunnel.h" #include "gnunet-service-messenger_handle.h" -#include "gnunet-service-messenger_util.h" +#include "gnunet-service-messenger_message_recv.h" +#include "messenger_api_util.h" struct GNUNET_MESSENGER_SrvTunnel* create_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_PeerIdentity *door) @@ -39,7 +40,8 @@ create_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_PeerId tunnel->channel = NULL; tunnel->peer = GNUNET_PEER_intern (door); - tunnel->contact_id = NULL; + + tunnel->messenger_version = 0; tunnel->peer_message = NULL; tunnel->last_message = NULL; @@ -57,9 +59,6 @@ destroy_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel) 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); @@ -69,22 +68,15 @@ destroy_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel) GNUNET_free(tunnel); } -int +void 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 @@ -113,52 +105,49 @@ check_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header) struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls; if (!tunnel) - return GNUNET_NO; + return GNUNET_SYSERR; 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); + if (length < get_message_kind_size(GNUNET_MESSENGER_KIND_UNKNOWN)) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Tunnel error: Message too short! (%d)\n", length); + return GNUNET_SYSERR; + } - int result = callback_verify_room_message (tunnel->room, cls, &message, &hash); + uint16_t padding = 0; - if (GNUNET_MESSENGER_KIND_PEER == message.header.kind) + if (GNUNET_YES != decode_message (&message, length, buffer, GNUNET_YES, &padding)) { - 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)); - } - } + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Tunnel error: Decoding failed!\n"); + return GNUNET_SYSERR; } - return (result == GNUNET_YES ? GNUNET_OK : GNUNET_NO); + struct GNUNET_HashCode hash; + hash_message (&message, length - padding, buffer, &hash); + + return callback_verify_room_message (tunnel->room, cls, &message, &hash); } +extern int +update_room_message (struct GNUNET_MESSENGER_SrvRoom *room, + struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + extern void -callback_room_recv (struct GNUNET_MESSENGER_SrvRoom *room, void *cls, struct GNUNET_MESSENGER_Message *message, - const struct GNUNET_HashCode *hash); +callback_room_handle_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +static void +update_tunnel_last_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, const struct GNUNET_HashCode *hash) +{ + if (!tunnel->last_message) + tunnel->last_message = GNUNET_new(struct GNUNET_HashCode); + + GNUNET_memcpy(tunnel->last_message, hash, sizeof(*hash)); +} void handle_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header) @@ -171,19 +160,48 @@ handle_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header) struct GNUNET_MESSENGER_Message message; struct GNUNET_HashCode hash; - decode_message (&message, length, buffer); - hash_message (length, buffer, &hash); + uint16_t padding = 0; - if (tunnel) - { - if (!tunnel->last_message) - tunnel->last_message = GNUNET_new(struct GNUNET_HashCode); + decode_message (&message, length, buffer, GNUNET_YES, &padding); + hash_message (&message, length - padding, buffer, &hash); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Got message of kind: %s!\n", + GNUNET_MESSENGER_name_of_kind(message.header.kind)); + + if (!tunnel) + return; + + const int new_message = update_room_message (tunnel->room, copy_message (&message), &hash); + + if (GNUNET_YES != new_message) + goto receive_done; - GNUNET_memcpy(tunnel->last_message, &hash, sizeof(struct GNUNET_HashCode)); + update_tunnel_last_message (tunnel, &hash); - callback_room_recv (tunnel->room, cls, copy_message (&message), &hash); + int forward_message = GNUNET_YES; + + switch (message.header.kind) + { + case GNUNET_MESSENGER_KIND_INFO: + forward_message = recv_message_info (tunnel->room, tunnel, &message, &hash); + break; + case GNUNET_MESSENGER_KIND_PEER: + forward_message = recv_message_peer (tunnel->room, tunnel, &message, &hash); + break; + case GNUNET_MESSENGER_KIND_REQUEST: + forward_message = recv_message_request (tunnel->room, tunnel, &message, &hash); + break; + default: + break; + } + + if (GNUNET_YES == forward_message) + { + forward_room_message (tunnel->room, tunnel, &message, &hash); + callback_room_handle_message (tunnel->room, NULL, &message, &hash); } +receive_done: GNUNET_CADET_receive_done (tunnel->channel); } @@ -198,7 +216,7 @@ connect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel) 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); + const 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), @@ -212,6 +230,8 @@ connect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel) void disconnect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel) { + GNUNET_assert(tunnel); + if (tunnel->channel) { delayed_disconnect_channel (tunnel->channel); @@ -223,6 +243,8 @@ disconnect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel) int is_tunnel_connected (const struct GNUNET_MESSENGER_SrvTunnel *tunnel) { + GNUNET_assert(tunnel); + return (tunnel->channel ? GNUNET_YES : GNUNET_NO); } @@ -232,30 +254,23 @@ struct GNUNET_MESSENGER_MessageSent 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)); - } + update_tunnel_last_message (sent->tunnel, &(sent->hash)); 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) +send_tunnel_envelope (struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MQ_Envelope *env, + const struct GNUNET_HashCode *hash) { + GNUNET_assert((tunnel) && (env) && (hash)); + struct GNUNET_MQ_Handle *mq = GNUNET_CADET_get_mq (tunnel->channel); struct GNUNET_MESSENGER_MessageSent *sent = GNUNET_new(struct GNUNET_MESSENGER_MessageSent); @@ -266,35 +281,91 @@ send_tunnel_envelope (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, s 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) +int +send_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, struct GNUNET_MESSENGER_Message *message) { - struct GNUNET_MQ_Envelope *env = pack_room_message (tunnel->room, (struct GNUNET_MESSENGER_SrvHandle*) handle, - message, hash, - GNUNET_MESSENGER_PACK_MODE_ENVELOPE); + GNUNET_assert((tunnel) && (handle)); + + if (!message) + return GNUNET_NO; + + 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 + ); + + destroy_message(message); + + if (!env) + return GNUNET_NO; + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending tunnel message: %s\n", + GNUNET_h2s(&hash)); - if (env) - send_tunnel_envelope (tunnel, handle, env, copy_message (message), hash); + send_tunnel_envelope (tunnel, env, &hash); + return GNUNET_YES; } 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 (!message) + return; + + GNUNET_assert((tunnel) && (message) && (hash)); + + struct GNUNET_MESSENGER_Message *copy = copy_message(message); + struct GNUNET_MQ_Envelope *env = pack_message (copy, NULL, NULL, GNUNET_MESSENGER_PACK_MODE_ENVELOPE); + + destroy_message(copy); + + if (!env) + return; + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Forwarding tunnel message: %s\n", + GNUNET_h2s(hash)); - if (env) - send_tunnel_envelope (tunnel, NULL, env, clone, hash); + send_tunnel_envelope (tunnel, env, hash); } const struct GNUNET_HashCode* get_tunnel_peer_message (const struct GNUNET_MESSENGER_SrvTunnel *tunnel) { + GNUNET_assert(tunnel); + return tunnel->peer_message; } + +void +get_tunnel_peer_identity (const struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_PeerIdentity *peer) +{ + GNUNET_assert(tunnel); + + GNUNET_PEER_resolve(tunnel->peer, peer); +} + +uint32_t +get_tunnel_messenger_version (const struct GNUNET_MESSENGER_SrvTunnel *tunnel) +{ + GNUNET_assert(tunnel); + + return tunnel->messenger_version; +} + +int +update_tunnel_messenger_version (struct GNUNET_MESSENGER_SrvTunnel *tunnel, uint32_t version) +{ + GNUNET_assert(tunnel); + + if (version != GNUNET_MESSENGER_VERSION) + return GNUNET_SYSERR; + + if (version > tunnel->messenger_version) + tunnel->messenger_version = version; + + return GNUNET_OK; +} diff --git a/src/messenger/gnunet-service-messenger_tunnel.h b/src/messenger/gnunet-service-messenger_tunnel.h index e6efb226d..51c5d32c1 100644 --- a/src/messenger/gnunet-service-messenger_tunnel.h +++ b/src/messenger/gnunet-service-messenger_tunnel.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -39,17 +39,18 @@ struct GNUNET_MESSENGER_SrvTunnel struct GNUNET_CADET_Channel *channel; GNUNET_PEER_Id peer; - struct GNUNET_ShortHashCode *contact_id; + + uint32_t messenger_version; struct GNUNET_HashCode *peer_message; struct GNUNET_HashCode *last_message; }; /** - * Creates and allocates a tunnel of a room to a specific peer identity. + * Creates and allocates a tunnel of a room to a specific peer identity (called door). * - * @param room Room - * @param door Peer identity + * @param[in/out] room Room + * @param[in] door Peer identity * @return New tunnel */ struct GNUNET_MESSENGER_SrvTunnel* @@ -58,28 +59,27 @@ create_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_PeerId /** * Destroys a tunnel and frees its memory fully. * - * @param tunnel + * @param[in/out] 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. + * Binds a CADET channel to a tunnel and replaces its channel + * the tunnel is currently bound to if necessary. * - * @param tunnel Tunnel - * @param channel CADET channel - * @return GNUNET_YES on success, otherwise GNUNET_NO + * @param[in/out] tunnel Tunnel + * @param[in/out] channel CADET channel */ -int +void 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. + * The function returns #GNUNET_YES on success, otherwise #GNUNET_NO. * - * @param tunnel Tunnel - * @return GNUNET_YES on success, otherwise GNUNET_NO + * @param[in/out] tunnel Tunnel + * @return #GNUNET_YES on success, otherwise #GNUNET_NO */ int connect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel); @@ -88,7 +88,7 @@ connect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel); * Disconnects and unbinds a channel from a tunnel. The actual disconnection * will be asynchronous. * - * @param tunnel Tunnel + * @param[in/out] tunnel Tunnel */ void disconnect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel); @@ -96,46 +96,43 @@ 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 + * @param[in] 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. + * a tunnel. * - * @param tunnel Tunnel - * @param handle Handle - * @param env Envelope - * @param message Message - * @param hash Hash of message + * @param[in/out] tunnel Tunnel + * @param[in/out] env Envelope + * @param[in] 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); +send_tunnel_envelope (struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_MQ_Envelope *env, + 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 + * @param[in/out] tunnel Tunnel + * @param[in/out] handle Handle + * @param[in/out] message Message + * @return #GNUNET_YES on success, GNUNET_NO otherwise */ -void -send_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, struct GNUNET_MESSENGER_Message *message, - struct GNUNET_HashCode *hash); +int +send_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, struct GNUNET_MESSENGER_Message *message); /** * Forwards a given message with a known hash through a tunnel. * - * @param tunnel Tunnel - * @param message Message - * @param hash Hash of message + * @param[in/out] tunnel Tunnel + * @param[in] message Message + * @param[in] hash Hash of message */ void forward_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, const struct GNUNET_MESSENGER_Message *message, @@ -146,10 +143,43 @@ forward_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, const struct * and matching the tunnels peer identity. If no peer message has been linked to the tunnel * yet, NULL gets returned. * - * @param tunnel Tunnel + * @param[in] tunnel Tunnel * @return Hash of peer message or NULL */ const struct GNUNET_HashCode* get_tunnel_peer_message (const struct GNUNET_MESSENGER_SrvTunnel *tunnel); +/** + * Writes the peer identity of the peer connected via tunnel to this peer into + * the peer parameter. + * + * @param[in] tunnel Tunnel + * @param[out] peer Peer identity + */ +void +get_tunnel_peer_identity (const struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct GNUNET_PeerIdentity *peer); + +/** + * Returns the current messenger version the peer connected via a given tunnel + * has reported to be using if it was compatible during updating. + * + * @see update_tunnel_messenger_version + * + * @param[in] tunnel Tunnel + * @return Version of messenger + */ +uint32_t +get_tunnel_messenger_version (const struct GNUNET_MESSENGER_SrvTunnel *tunnel); + +/** + * Updates the messenger version of the tunnel to a given version if + * it is compatible to the running peer of the service. Depending on success it + * returns #GNUNET_OK or #GNUNET_SYSERR on failure. + * + * @param[in/out] tunnel Tunnel + * @param[in] version Version of messenger + */ +int +update_tunnel_messenger_version (struct GNUNET_MESSENGER_SrvTunnel *tunnel, uint32_t version); + #endif //GNUNET_SERVICE_MESSENGER_TUNNEL_H diff --git a/src/messenger/gnunet-service-messenger_util.c b/src/messenger/gnunet-service-messenger_util.c deleted file mode 100644 index 94fc9469d..000000000 --- a/src/messenger/gnunet-service-messenger_util.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - 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 deleted file mode 100644 index 20f8f0afe..000000000 --- a/src/messenger/gnunet-service-messenger_util.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - 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_api.c b/src/messenger/messenger_api.c index 6401b18d7..dc6d11aaf 100644 --- a/src/messenger/messenger_api.c +++ b/src/messenger/messenger_api.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -29,6 +29,7 @@ #include "messenger_api_handle.h" #include "messenger_api_message.h" +#include "messenger_api_util.h" const char* GNUNET_MESSENGER_name_of_kind (enum GNUNET_MESSENGER_MessageKind kind) @@ -61,6 +62,8 @@ GNUNET_MESSENGER_name_of_kind (enum GNUNET_MESSENGER_MessageKind kind) return "TEXT"; case GNUNET_MESSENGER_KIND_FILE: return "FILE"; + case GNUNET_MESSENGER_KIND_PRIVATE: + return "PRIVATE"; default: return "UNKNOWN"; } @@ -82,7 +85,25 @@ handle_get_name (void *cls, const struct GNUNET_MESSENGER_NameMessage *msg) GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Set name of handle: %s\n", name); - set_handle_name (handle, strlen(name) > 0? name : NULL); + set_handle_name (handle, strlen (name) > 0 ? name : NULL); +} + +static int +check_get_key (void *cls, const struct GNUNET_MESSENGER_KeyMessage *msg) +{ + const uint16_t full_length = ntohs (msg->header.size); + + if (full_length < sizeof(*msg)) + return GNUNET_NO; + + const uint16_t length = full_length - sizeof(*msg); + const char *buffer = ((const char*) msg) + sizeof(*msg); + + struct GNUNET_IDENTITY_PublicKey pubkey; + if (GNUNET_IDENTITY_read_key_from_buffer(&pubkey, buffer, length) < 0) + return GNUNET_NO; + + return GNUNET_OK; } static void @@ -90,11 +111,18 @@ 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); + const uint16_t length = ntohs (msg->header.size) - sizeof(*msg); + const char *buffer = ((const char*) msg) + sizeof(*msg); + + struct GNUNET_IDENTITY_PublicKey pubkey; + if (GNUNET_IDENTITY_read_key_from_buffer(&pubkey, buffer, length) < 0) + return; - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Set key of handle: %s\n", GNUNET_IDENTITY_public_key_to_string (pubkey)); + char* str = GNUNET_IDENTITY_public_key_to_string (&pubkey); + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Set key of handle: %s\n", str); + GNUNET_free(str); - set_handle_key (handle, pubkey); + set_handle_key (handle, &pubkey); if (handle->identity_callback) handle->identity_callback (handle->identity_cls, handle); @@ -161,12 +189,12 @@ handle_room_close (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg) 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); + const uint16_t full_length = ntohs (msg->header.size); - if (full_length < sizeof(msg->hash)) + if (full_length < sizeof(*msg)) return GNUNET_NO; - const uint16_t length = full_length - sizeof(msg->hash); + const uint16_t length = full_length - sizeof(*msg); const char *buffer = ((const char*) msg) + sizeof(*msg); struct GNUNET_MESSENGER_Message message; @@ -174,7 +202,7 @@ check_recv_message (void *cls, const struct GNUNET_MESSENGER_RecvMessage *msg) if (length < sizeof(message.header)) return GNUNET_NO; - if (GNUNET_YES != decode_message (&message, length, buffer)) + if (GNUNET_YES != decode_message (&message, length, buffer, GNUNET_YES, NULL)) return GNUNET_NO; return GNUNET_OK; @@ -186,14 +214,18 @@ 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 *sender = &(msg->sender); + const struct GNUNET_HashCode *context = &(msg->context); const struct GNUNET_HashCode *hash = &(msg->hash); - - const char *buffer = ((const char*) msg) + sizeof(*msg); + const enum GNUNET_MESSENGER_MessageFlags flags = ( + (enum GNUNET_MESSENGER_MessageFlags) (msg->flags) + ); const uint16_t length = ntohs (msg->header.size) - sizeof(*msg); + const char *buffer = ((const char*) msg) + sizeof(*msg); struct GNUNET_MESSENGER_Message message; - decode_message (&message, length, buffer); + decode_message (&message, length, buffer, GNUNET_YES, NULL); GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Receiving message: %s\n", GNUNET_MESSENGER_name_of_kind (message.header.kind)); @@ -201,13 +233,22 @@ handle_recv_message (void *cls, const struct GNUNET_MESSENGER_RecvMessage *msg) if (room) { - handle_room_message (room, &message, hash); + struct GNUNET_MESSENGER_ContactStore *store = get_handle_contact_store(handle); + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Raw contact from sender and context: (%s : %s)\n", + GNUNET_h2s(sender), GNUNET_h2s_full(context)); + + struct GNUNET_MESSENGER_Contact *contact = get_store_contact_raw( + store, context, sender + ); + + handle_room_message (room, contact, &message, hash); if (handle->msg_callback) - handle->msg_callback (handle->msg_cls, room, &message, hash); + handle->msg_callback (handle->msg_cls, room, contact, &message, hash, flags); } else - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "MESSENGER ERROR: Room not found\n"); + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Room not found\n"); } static void @@ -220,12 +261,12 @@ send_open_room (struct GNUNET_MESSENGER_Handle *handle, struct GNUNET_MESSENGER_ 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_memcpy(&(msg->key), &(room->key), sizeof(msg->key)); GNUNET_MQ_send (handle->mq, env); } static void -send_entry_room (struct GNUNET_MESSENGER_Handle *handle, struct GNUNET_MESSENGER_Room *room, +send_enter_room (struct GNUNET_MESSENGER_Handle *handle, struct GNUNET_MESSENGER_Room *room, const struct GNUNET_PeerIdentity *door) { struct GNUNET_MESSENGER_RoomMessage *msg; @@ -233,7 +274,7 @@ send_entry_room (struct GNUNET_MESSENGER_Handle *handle, struct GNUNET_MESSENGER 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_memcpy(&(msg->key), &(room->key), sizeof(msg->key)); GNUNET_MQ_send (handle->mq, env); } @@ -244,7 +285,7 @@ send_close_room (struct GNUNET_MESSENGER_Handle *handle, struct GNUNET_MESSENGER 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_memcpy(&(msg->key), &(room->key), sizeof(msg->key)); GNUNET_MQ_send (handle->mq, env); } @@ -265,7 +306,7 @@ iterate_reset_room (void *cls, const struct GNUNET_HashCode *key, void *value) { GNUNET_PEER_resolve (entry->peer, &door); - send_entry_room (handle, room, &door); + send_enter_room (handle, room, &door); entry = entry->next; } @@ -303,7 +344,7 @@ 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_log(GNUNET_ERROR_TYPE_ERROR, "MQ_Error: %u\n", error); GNUNET_CONTAINER_multihashmap_iterate (handle->rooms, iterate_close_room, handle); @@ -319,36 +360,45 @@ callback_mq_error (void *cls, enum GNUNET_MQ_Error error) 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); + 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_var_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* @@ -389,7 +439,7 @@ GNUNET_MESSENGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, const c int GNUNET_MESSENGER_update (struct GNUNET_MESSENGER_Handle *handle) { - if ((!handle) || (!get_handle_name(handle))) + if ((!handle) || (!get_handle_name (handle))) return GNUNET_SYSERR; struct GNUNET_MESSENGER_UpdateMessage *msg; @@ -448,18 +498,30 @@ GNUNET_MESSENGER_set_name (struct GNUNET_MESSENGER_Handle *handle, const char *n return GNUNET_YES; } +static const struct GNUNET_IDENTITY_PublicKey* +get_non_anonymous_key (const struct GNUNET_IDENTITY_PublicKey* public_key) +{ + if (0 == GNUNET_memcmp(public_key, get_anonymous_public_key())) + return NULL; + + return public_key; +} + const struct GNUNET_IDENTITY_PublicKey* GNUNET_MESSENGER_get_key (const struct GNUNET_MESSENGER_Handle *handle) { if (!handle) return NULL; - return get_handle_key (handle); + return get_non_anonymous_key (get_handle_key (handle)); } struct GNUNET_MESSENGER_Room* GNUNET_MESSENGER_open_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key) { + if ((!handle) || (!key)) + return NULL; + struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get (handle->rooms, key); if (!room) @@ -479,9 +541,12 @@ GNUNET_MESSENGER_open_room (struct GNUNET_MESSENGER_Handle *handle, const struct } struct GNUNET_MESSENGER_Room* -GNUNET_MESSENGER_entry_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_PeerIdentity *door, +GNUNET_MESSENGER_enter_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_PeerIdentity *door, const struct GNUNET_HashCode *key) { + if ((!handle) || (!door) || (!key)) + return NULL; + struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get (handle->rooms, key); if (!room) @@ -496,20 +561,26 @@ GNUNET_MESSENGER_entry_room (struct GNUNET_MESSENGER_Handle *handle, const struc } } - send_entry_room (handle, room, door); + send_enter_room (handle, room, door); return room; } void GNUNET_MESSENGER_close_room (struct GNUNET_MESSENGER_Room *room) { + if (!room) + return; + 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) +GNUNET_MESSENGER_get_sender (const struct GNUNET_MESSENGER_Room *room, const struct GNUNET_HashCode *hash) { - return GNUNET_CONTAINER_multishortmap_get (room->members, id); + if ((!room) || (!hash)) + return NULL; + + return get_room_sender(room, hash); } const char* @@ -527,23 +598,73 @@ GNUNET_MESSENGER_contact_get_key (const struct GNUNET_MESSENGER_Contact *contact if (!contact) return NULL; - return get_contact_key (contact); + return get_non_anonymous_key (get_contact_key (contact)); } void -GNUNET_MESSENGER_send_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message) +GNUNET_MESSENGER_send_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_MESSENGER_Contact *contact) { - const uint16_t length = get_message_size (message); + if ((!room) || (!message)) + return; + + switch (filter_message_sending (message)) + { + case GNUNET_SYSERR: + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Sending message aborted: This kind of message is reserved for the service!\n"); + return; + case GNUNET_NO: + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Sending message aborted: This kind of message could cause issues!\n"); + return; + default: + break; + } + + ssize_t key_length = 0; + + if (contact) + { + const struct GNUNET_IDENTITY_PublicKey *public_key = get_non_anonymous_key ( + get_contact_key(contact) + ); + + if (public_key) + key_length = GNUNET_IDENTITY_key_get_length(public_key); + else + key_length = -1; + } + + if (key_length < 0) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Sending message aborted: Invalid key!\n"); + return; + } + + const uint16_t msg_length = get_message_size (message, GNUNET_NO); struct GNUNET_MESSENGER_SendMessage *msg; struct GNUNET_MQ_Envelope *env; - env = GNUNET_MQ_msg_extra(msg, length, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_SEND_MESSAGE); + const uint16_t length = (uint16_t) key_length + msg_length; + + env = GNUNET_MQ_msg_extra( + msg, length, + GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_SEND_MESSAGE + ); - GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key)); + GNUNET_memcpy(&(msg->key), &(room->key), sizeof(msg->key)); + + msg->flags = (uint32_t) ( + contact? GNUNET_MESSENGER_FLAG_PRIVATE : GNUNET_MESSENGER_FLAG_NONE + ); char *buffer = ((char*) msg) + sizeof(*msg); - encode_message (message, length, buffer); + char *msg_buffer = buffer + key_length; + + if (key_length > 0) + GNUNET_IDENTITY_write_key_to_buffer(get_contact_key(contact), buffer, key_length); + + encode_message (message, msg_length, msg_buffer, GNUNET_NO); GNUNET_MQ_send (room->handle->mq, env); } @@ -551,18 +672,31 @@ GNUNET_MESSENGER_send_message (struct GNUNET_MESSENGER_Room *room, const struct const struct GNUNET_MESSENGER_Message* GNUNET_MESSENGER_get_message (const struct GNUNET_MESSENGER_Room *room, const struct GNUNET_HashCode *hash) { + if ((!room) || (!hash)) + return NULL; + const struct GNUNET_MESSENGER_Message *message = get_room_message (room, hash); if (!message) { - struct GNUNET_MESSENGER_RecvMessage *msg; + struct GNUNET_MESSENGER_GetMessage *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->key), &(room->key), sizeof(msg->key)); GNUNET_memcpy(&(msg->hash), hash, sizeof(*hash)); GNUNET_MQ_send (room->handle->mq, env); } return message; } + +int +GNUNET_MESSENGER_iterate_members (struct GNUNET_MESSENGER_Room *room, GNUNET_MESSENGER_MemberCallback callback, + void* cls) +{ + if (!room) + return GNUNET_SYSERR; + + return iterate_room_members(room, callback, cls); +} diff --git a/src/messenger/messenger_api_contact.c b/src/messenger/messenger_api_contact.c index 9a242aa00..04e1f60c1 100644 --- a/src/messenger/messenger_api_contact.c +++ b/src/messenger/messenger_api_contact.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -28,9 +28,12 @@ struct GNUNET_MESSENGER_Contact* create_contact (const struct GNUNET_IDENTITY_PublicKey *key) { + GNUNET_assert(key); + struct GNUNET_MESSENGER_Contact *contact = GNUNET_new(struct GNUNET_MESSENGER_Contact); contact->name = NULL; + contact->rc = 0; GNUNET_memcpy(&(contact->public_key), key, sizeof(contact->public_key)); @@ -40,6 +43,8 @@ create_contact (const struct GNUNET_IDENTITY_PublicKey *key) void destroy_contact (struct GNUNET_MESSENGER_Contact *contact) { + GNUNET_assert(contact); + if (contact->name) GNUNET_free(contact->name); @@ -49,30 +54,55 @@ destroy_contact (struct GNUNET_MESSENGER_Contact *contact) const char* get_contact_name (const struct GNUNET_MESSENGER_Contact *contact) { + GNUNET_assert(contact); + return contact->name; } void set_contact_name (struct GNUNET_MESSENGER_Contact *contact, const char *name) { + GNUNET_assert(contact); + if (contact->name) GNUNET_free(contact->name); - contact->name = name? GNUNET_strdup(name) : NULL; + contact->name = name ? GNUNET_strdup(name) : NULL; } const struct GNUNET_IDENTITY_PublicKey* get_contact_key (const struct GNUNET_MESSENGER_Contact *contact) { + GNUNET_assert(contact); + return &(contact->public_key); } -const struct GNUNET_HashCode* -get_contact_id_from_key (const struct GNUNET_MESSENGER_Contact *contact) +void +increase_contact_rc (struct GNUNET_MESSENGER_Contact *contact) { - static struct GNUNET_HashCode id; + GNUNET_assert(contact); + + contact->rc++; +} - GNUNET_CRYPTO_hash (&(contact->public_key), sizeof(contact->public_key), &id); +int +decrease_contact_rc (struct GNUNET_MESSENGER_Contact *contact) +{ + GNUNET_assert(contact); + + if (contact->rc > 0) + contact->rc--; + + return contact->rc ? GNUNET_NO : GNUNET_YES; +} + +void +get_context_from_member (const struct GNUNET_HashCode *key, const struct GNUNET_ShortHashCode *id, + struct GNUNET_HashCode *context) +{ + GNUNET_assert((key) && (id) && (context)); - return &id; + GNUNET_CRYPTO_hash (id, sizeof(*id), context); + GNUNET_CRYPTO_hash_xor (key, context, context); } diff --git a/src/messenger/messenger_api_contact.h b/src/messenger/messenger_api_contact.h index 0673b9b85..e94d1fcd0 100644 --- a/src/messenger/messenger_api_contact.h +++ b/src/messenger/messenger_api_contact.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -33,6 +33,7 @@ struct GNUNET_MESSENGER_Contact { char *name; + size_t rc; struct GNUNET_IDENTITY_PublicKey public_key; }; @@ -40,7 +41,7 @@ struct GNUNET_MESSENGER_Contact /** * Creates and allocates a new contact with a given public key from an EGO. * - * @param key Public key + * @param[in] key Public key * @return New contact */ struct GNUNET_MESSENGER_Contact* @@ -49,7 +50,7 @@ create_contact (const struct GNUNET_IDENTITY_PublicKey *key); /** * Destroys a contact and frees its memory fully. * - * @param contact Contact + * @param[in/out] contact Contact */ void destroy_contact (struct GNUNET_MESSENGER_Contact *contact); @@ -57,7 +58,7 @@ 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 + * @param[in] contact Contact * @return Name of the contact or NULL */ const char* @@ -66,8 +67,8 @@ 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!) + * @param[in/out] contact Contact + * @param[in] name Name */ void set_contact_name (struct GNUNET_MESSENGER_Contact *contact, const char *name); @@ -75,19 +76,39 @@ set_contact_name (struct GNUNET_MESSENGER_Contact *contact, const char *name); /** * Returns the public key of a given contact. * - * @param contact Contact + * @param[in] 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. + * Increases the reference counter of a given contact which is zero as default. * - * @param contact Contact - * @return Hash of the contacts public key + * @param[in/out] contact Contact */ -const struct GNUNET_HashCode* -get_contact_id_from_key (const struct GNUNET_MESSENGER_Contact *contact); +void +increase_contact_rc (struct GNUNET_MESSENGER_Contact *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[in/out] contact Contact + * @return #GNUNET_YES or #GNUNET_NO depending on the reference counter + */ +int +decrease_contact_rc (struct GNUNET_MESSENGER_Contact *contact); + +/** + * Calculates the context hash of a member in a room and returns it. + * + * @param[in] key Key of room + * @param[in] id Member id + * @param[out] hash Member context + */ +void +get_context_from_member (const struct GNUNET_HashCode *key, const struct GNUNET_ShortHashCode *id, + struct GNUNET_HashCode *context); #endif //GNUNET_MESSENGER_API_CONTACT_H diff --git a/src/messenger/messenger_api_contact_store.c b/src/messenger/messenger_api_contact_store.c new file mode 100644 index 000000000..5238b2c58 --- /dev/null +++ b/src/messenger/messenger_api_contact_store.c @@ -0,0 +1,182 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020--2021 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_store.c + * @brief messenger api: client implementation of GNUnet MESSENGER service + */ + +#include "messenger_api_contact_store.h" + +#include "messenger_api_contact.h" +#include "messenger_api_util.h" + +void +init_contact_store (struct GNUNET_MESSENGER_ContactStore *store) +{ + GNUNET_assert (store); + + store->anonymous = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + store->contacts = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); +} + +static int +iterate_destroy_contacts (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_MESSENGER_Contact *contact = value; + destroy_contact (contact); + return GNUNET_YES; +} + +void +clear_contact_store (struct GNUNET_MESSENGER_ContactStore *store) +{ + GNUNET_assert ((store) && (store->contacts)); + + GNUNET_CONTAINER_multihashmap_iterate (store->anonymous, iterate_destroy_contacts, NULL); + GNUNET_CONTAINER_multihashmap_iterate (store->contacts, iterate_destroy_contacts, NULL); + + GNUNET_CONTAINER_multihashmap_destroy (store->anonymous); + GNUNET_CONTAINER_multihashmap_destroy (store->contacts); +} + +static struct GNUNET_CONTAINER_MultiHashMap* +select_store_contact_map (struct GNUNET_MESSENGER_ContactStore *store, const struct GNUNET_HashCode *context, + struct GNUNET_HashCode *hash) +{ + const struct GNUNET_IDENTITY_PublicKey *anonymous = get_anonymous_public_key (); + + struct GNUNET_HashCode anonHash; + GNUNET_CRYPTO_hash (anonymous, sizeof(*anonymous), &anonHash); + + if ((context) && (0 == GNUNET_CRYPTO_hash_cmp(hash, &anonHash))) + { + GNUNET_memcpy(hash, context, sizeof(*context)); + return store->anonymous; + } + else + return store->contacts; +} + +struct GNUNET_MESSENGER_Contact* +get_store_contact_raw (struct GNUNET_MESSENGER_ContactStore *store, const struct GNUNET_HashCode *context, + const struct GNUNET_HashCode *key_hash) +{ + GNUNET_assert ((store) && (store->contacts) && (context) && (key_hash)); + + struct GNUNET_HashCode hash; + GNUNET_memcpy(&hash, key_hash, sizeof(*key_hash)); + + struct GNUNET_CONTAINER_MultiHashMap *map = select_store_contact_map ( + store, context, &hash + ); + + return GNUNET_CONTAINER_multihashmap_get (map, &hash); +} + +struct GNUNET_MESSENGER_Contact* +get_store_contact (struct GNUNET_MESSENGER_ContactStore *store, const struct GNUNET_HashCode *context, + const struct GNUNET_IDENTITY_PublicKey *pubkey) +{ + GNUNET_assert ((store) && (store->contacts) && (context) && (pubkey)); + + struct GNUNET_HashCode hash; + GNUNET_CRYPTO_hash (pubkey, sizeof(*pubkey), &hash); + + struct GNUNET_CONTAINER_MultiHashMap *map = select_store_contact_map ( + store, context, &hash + ); + + struct GNUNET_MESSENGER_Contact *contact = GNUNET_CONTAINER_multihashmap_get (map, &hash); + + if (contact) + { + if (0 != GNUNET_memcmp(pubkey, get_contact_key(contact))) + { + char* str = GNUNET_IDENTITY_public_key_to_string (get_contact_key(contact)); + GNUNET_log (GNUNET_ERROR_TYPE_INVALID, "Contact in store uses wrong key: %s\n", str); + GNUNET_free (str); + return NULL; + } + + return contact; + } + + contact = create_contact (pubkey); + + if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (map, &hash, contact, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + return contact; + + destroy_contact (contact); + return NULL; +} + +void +update_store_contact (struct GNUNET_MESSENGER_ContactStore *store, struct GNUNET_MESSENGER_Contact* contact, + const struct GNUNET_HashCode *context, const struct GNUNET_HashCode *next_context, + const struct GNUNET_IDENTITY_PublicKey *pubkey) +{ + GNUNET_assert ((store) && (store->contacts) && (contact) && (pubkey)); + + const struct GNUNET_IDENTITY_PublicKey* oldkey = get_contact_key (contact); + + struct GNUNET_HashCode hash; + GNUNET_CRYPTO_hash (oldkey, sizeof(*oldkey), &hash); + + struct GNUNET_CONTAINER_MultiHashMap *map = select_store_contact_map ( + store, context, &hash + ); + + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (map, &hash, contact)) + { + GNUNET_memcpy(&(contact->public_key), pubkey, sizeof(*pubkey)); + + GNUNET_CRYPTO_hash (pubkey, sizeof(*pubkey), &hash); + + map = select_store_contact_map ( + store, next_context, &hash + ); + + GNUNET_CONTAINER_multihashmap_put (map, &hash, contact, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + } +} + +void +remove_store_contact (struct GNUNET_MESSENGER_ContactStore *store, struct GNUNET_MESSENGER_Contact* contact, + const struct GNUNET_HashCode *context) +{ + GNUNET_assert ((store) && (store->contacts) && (contact)); + + const struct GNUNET_IDENTITY_PublicKey* pubkey = get_contact_key(contact); + + struct GNUNET_HashCode hash; + GNUNET_CRYPTO_hash (pubkey, sizeof(*pubkey), &hash); + + struct GNUNET_CONTAINER_MultiHashMap *map = select_store_contact_map ( + store, context, &hash + ); + + if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove (map, &hash, contact)) + return; + + destroy_contact (contact); +} diff --git a/src/messenger/messenger_api_contact_store.h b/src/messenger/messenger_api_contact_store.h new file mode 100644 index 000000000..966f6d962 --- /dev/null +++ b/src/messenger/messenger_api_contact_store.h @@ -0,0 +1,122 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020--2021 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_store.h + * @brief messenger api: client implementation of GNUnet MESSENGER service + */ + +#ifndef GNUNET_MESSENGER_API_CONTACT_STORE_H +#define GNUNET_MESSENGER_API_CONTACT_STORE_H + +#include "platform.h" +#include "gnunet_container_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_identity_service.h" + +struct GNUNET_MESSENGER_Contact; + +struct GNUNET_MESSENGER_ContactStore +{ + struct GNUNET_CONTAINER_MultiHashMap *anonymous; + struct GNUNET_CONTAINER_MultiHashMap *contacts; +}; + +/** + * Initializes a contact store as fully empty. + * + * @param[out] store Contact store + */ +void +init_contact_store (struct GNUNET_MESSENGER_ContactStore *store); + +/** + * Clears a contact store, wipes its content and deallocates its memory. + * + * @param[in/out] store Contact store + */ +void +clear_contact_store (struct GNUNET_MESSENGER_ContactStore *store); + +/** + * Returns a contact using the hash of a specific public key. In case the anonymous + * key gets used by the requested contact, it will use its provided member + * context to select the matching contact from the store. + * + * In case there is no contact stored which uses the given key or context, + * NULL gets returned. + * + * @param[in/out] store Contact store + * @param[in] context Member context + * @param[in] key_hash Hash of public key + */ +struct GNUNET_MESSENGER_Contact* +get_store_contact_raw (struct GNUNET_MESSENGER_ContactStore *store, const struct GNUNET_HashCode *context, + const struct GNUNET_HashCode *key_hash); + +/** + * Returns a contact using a specific public key. In case the anonymous + * key gets used by the requested contact, it will use its provided member + * context to select the matching contact from the store. + * + * In case there is no contact stored which uses the given key or context, + * a new contact will be created automatically. + * + * The function returns NULL if an error occures during allocation + * or validation of the contacts key. + * + * @param[in/out] store Contact store + * @param[in] context Member context + * @param[in] pubkey Public key of EGO + */ +struct GNUNET_MESSENGER_Contact* +get_store_contact (struct GNUNET_MESSENGER_ContactStore *store, const struct GNUNET_HashCode *context, + const struct GNUNET_IDENTITY_PublicKey *pubkey); + +/** + * Moves a contact from the store to another location + * matching a given public key and member context. + * + * This function allows changes of keys or changes of member contexts! + * + * @param[in/out] store Contact store + * @param[in/out] contact Contact + * @param[in] context Member context + * @param[in] next_context Member context + * @param[in] pubkey Public key of EGO + */ +void +update_store_contact (struct GNUNET_MESSENGER_ContactStore *store, struct GNUNET_MESSENGER_Contact* contact, + const struct GNUNET_HashCode *context, const struct GNUNET_HashCode *next_context, + const struct GNUNET_IDENTITY_PublicKey *pubkey); + +/** + * Removes a contact from the store which uses + * a given member context. + * + * @param[in/out] store Contact store + * @param[in/out] contact Contact + * @param[in] context Member context + */ +void +remove_store_contact (struct GNUNET_MESSENGER_ContactStore *store, struct GNUNET_MESSENGER_Contact* contact, + const struct GNUNET_HashCode *context); + +#endif //GNUNET_MESSENGER_API_CONTACT_STORE_H diff --git a/src/messenger/messenger_api_ego.h b/src/messenger/messenger_api_ego.h index c60eeac50..b52b895f2 100644 --- a/src/messenger/messenger_api_ego.h +++ b/src/messenger/messenger_api_ego.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 diff --git a/src/messenger/messenger_api_handle.c b/src/messenger/messenger_api_handle.c index 20ef77254..ab57f82cc 100644 --- a/src/messenger/messenger_api_handle.c +++ b/src/messenger/messenger_api_handle.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -25,10 +25,14 @@ #include "messenger_api_handle.h" +#include "messenger_api_util.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) { + GNUNET_assert(cfg); + struct GNUNET_MESSENGER_Handle *handle = GNUNET_new(struct GNUNET_MESSENGER_Handle); handle->cfg = cfg; @@ -47,7 +51,8 @@ create_handle (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_MESSENGER_I handle->reconnect_task = NULL; handle->rooms = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); - handle->contacts = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + + init_contact_store(get_handle_contact_store(handle)); return handle; } @@ -62,19 +67,11 @@ iterate_destroy_room (void *cls, const struct GNUNET_HashCode *key, void *value) 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) { + GNUNET_assert(handle); + if (handle->reconnect_task) GNUNET_SCHEDULER_cancel (handle->reconnect_task); @@ -94,12 +91,7 @@ destroy_handle (struct GNUNET_MESSENGER_Handle *handle) 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); - } + clear_contact_store(get_handle_contact_store(handle)); GNUNET_free(handle->name); } @@ -107,21 +99,27 @@ destroy_handle (struct GNUNET_MESSENGER_Handle *handle) void set_handle_name (struct GNUNET_MESSENGER_Handle *handle, const char *name) { + GNUNET_assert(handle); + if (handle->name) GNUNET_free(handle->name); - handle->name = name? GNUNET_strdup(name) : NULL; + handle->name = name ? GNUNET_strdup(name) : NULL; } const char* get_handle_name (const struct GNUNET_MESSENGER_Handle *handle) { + GNUNET_assert(handle); + return handle->name; } void set_handle_key (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_IDENTITY_PublicKey *pubkey) { + GNUNET_assert(handle); + if (!handle->pubkey) handle->pubkey = GNUNET_new(struct GNUNET_IDENTITY_PublicKey); @@ -131,62 +129,43 @@ set_handle_key (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_IDEN 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_assert(handle); - GNUNET_IDENTITY_ego_get_public_key (anonymous, &pubkey); - - return &pubkey; - } + if (handle->pubkey) + return handle->pubkey; - return handle->pubkey; + return get_anonymous_public_key (); } -struct GNUNET_MESSENGER_Contact* -get_handle_contact_by_pubkey (const struct GNUNET_MESSENGER_Handle *handle, - const struct GNUNET_IDENTITY_PublicKey *pubkey) +struct GNUNET_MESSENGER_ContactStore* +get_handle_contact_store (struct GNUNET_MESSENGER_Handle *handle) { - 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; + GNUNET_assert(handle); - destroy_contact (contact); - return NULL; + return &(handle->contact_store); } -void -swap_handle_contact_by_pubkey (struct GNUNET_MESSENGER_Handle *handle, struct GNUNET_MESSENGER_Contact *contact, - const struct GNUNET_IDENTITY_PublicKey *pubkey) +struct GNUNET_MESSENGER_Contact* +get_handle_contact (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key) { - const struct GNUNET_HashCode *hash = get_contact_id_from_key (contact); + GNUNET_assert((handle) && (key)); - if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (handle->contacts, hash, contact)) - { - GNUNET_memcpy(&(contact->public_key), pubkey, sizeof(*pubkey)); + struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get (handle->rooms, key); - hash = get_contact_id_from_key (contact); + if ((!room) || (!(room->contact_id))) + return NULL; - GNUNET_CONTAINER_multihashmap_put (handle->contacts, hash, contact, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); - } + struct GNUNET_HashCode context; + get_context_from_member (key, room->contact_id, &context); + + return get_store_contact(get_handle_contact_store(handle), &context, get_handle_key(handle)); } void open_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key) { + GNUNET_assert((handle) && (key)); + struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get (handle->rooms, key); if (room) @@ -197,6 +176,8 @@ void entry_handle_room_at (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_PeerIdentity *door, const struct GNUNET_HashCode *key) { + GNUNET_assert((handle) && (door) && (key)); + struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get (handle->rooms, key); if (room) @@ -206,6 +187,8 @@ entry_handle_room_at (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNE void close_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key) { + GNUNET_assert((handle) && (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))) diff --git a/src/messenger/messenger_api_handle.h b/src/messenger/messenger_api_handle.h index d6cde0106..e6ca474f2 100644 --- a/src/messenger/messenger_api_handle.h +++ b/src/messenger/messenger_api_handle.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -35,7 +35,7 @@ #include "gnunet_messenger_service.h" -#include "messenger_api_contact.h" +#include "messenger_api_contact_store.h" #include "messenger_api_room.h" struct GNUNET_MESSENGER_Handle @@ -56,17 +56,18 @@ struct GNUNET_MESSENGER_Handle struct GNUNET_TIME_Relative reconnect_time; struct GNUNET_SCHEDULER_Task *reconnect_task; + struct GNUNET_MESSENGER_ContactStore contact_store; + 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 + * @param[in] cfg Configuration + * @param[in] msg_callback Message callback + * @param[in/out] msg_cls Closure * @return New handle */ struct GNUNET_MESSENGER_Handle* @@ -76,7 +77,7 @@ create_handle (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_MESSENGER_I /** * Destroys a handle and frees its memory fully from the client API. * - * @param handle Handle + * @param[in/out] handle Handle */ void destroy_handle (struct GNUNET_MESSENGER_Handle *handle); @@ -84,8 +85,8 @@ destroy_handle (struct GNUNET_MESSENGER_Handle *handle); /** * Sets the name of a handle to a specific name. * - * @param handle Handle - * @param name New name + * @param[in/out] handle Handle + * @param[in] name New name */ void set_handle_name (struct GNUNET_MESSENGER_Handle *handle, const char *name); @@ -93,7 +94,7 @@ 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 + * @param[in] handle Handle * @return Name of the handle or NULL */ const char* @@ -102,8 +103,8 @@ 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 + * @param[in/out] handle Handle + * @param[in] pubkey Public key */ void set_handle_key (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_IDENTITY_PublicKey *pubkey); @@ -111,41 +112,37 @@ set_handle_key (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_IDEN /** * Returns the public key of a given handle. * - * @param handle Handle + * @param[in] 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. + * Returns the used contact store of a given handle. * - * @param handle Handle - * @param pubkey Public key of EGO - * @return Contact or NULL + * @param[in/out] handle Handle + * @return Contact store */ -struct GNUNET_MESSENGER_Contact* -get_handle_contact_by_pubkey (const struct GNUNET_MESSENGER_Handle *handle, - const struct GNUNET_IDENTITY_PublicKey *pubkey); +struct GNUNET_MESSENGER_ContactStore* +get_handle_contact_store (struct GNUNET_MESSENGER_Handle *handle); /** - * 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. + * Returns the contact of a given handle in a room identified by a + * given key. * - * @param handle Handle - * @param contact Contact - * @param pubkey Public key of EGO + * @param[in/out] handle Handle + * @param[in] key Key of room + * @return Contact */ -void -swap_handle_contact_by_pubkey (struct GNUNET_MESSENGER_Handle *handle, struct GNUNET_MESSENGER_Contact *contact, - const struct GNUNET_IDENTITY_PublicKey *pubkey); +struct GNUNET_MESSENGER_Contact* +get_handle_contact (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key); /** * Marks a room known to a handle identified by a given key as open. * - * @param handle Handle - * @param key Key of room + * @param[in/out] handle Handle + * @param[in] key Key of room */ void open_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key); @@ -154,9 +151,9 @@ open_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_Ha * 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 + * @param[in/out] handle Handle + * @param[in] door Peer identity + * @param[in] key Key of room */ void entry_handle_room_at (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_PeerIdentity *door, @@ -165,8 +162,8 @@ entry_handle_room_at (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNE /** * Destroys and so implicitly closes a room known to a handle identified by a given key. * - * @param handle Handle - * @param key Key of room + * @param[in/out] handle Handle + * @param[in] key Key of room */ void close_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key); diff --git a/src/messenger/messenger_api_list_tunnels.c b/src/messenger/messenger_api_list_tunnels.c index 13d8c1906..990e36878 100644 --- a/src/messenger/messenger_api_list_tunnels.c +++ b/src/messenger/messenger_api_list_tunnels.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -62,6 +62,8 @@ compare_list_tunnels (void *cls, struct GNUNET_MESSENGER_ListTunnel *element0, void add_to_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct GNUNET_PeerIdentity *peer) { + GNUNET_assert((tunnels) && (peer)); + struct GNUNET_MESSENGER_ListTunnel *element = GNUNET_new(struct GNUNET_MESSENGER_ListTunnel); element->peer = GNUNET_PEER_intern (peer); @@ -73,6 +75,8 @@ add_to_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct struct GNUNET_MESSENGER_ListTunnel* find_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct GNUNET_PeerIdentity *peer, size_t *index) { + GNUNET_assert((tunnels) && (peer)); + struct GNUNET_MESSENGER_ListTunnel *element; struct GNUNET_PeerIdentity pid; @@ -96,12 +100,16 @@ find_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct GN int contains_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct GNUNET_PeerIdentity *peer) { + GNUNET_assert((tunnels) && (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) { + GNUNET_assert((tunnels) && (element)); + struct GNUNET_MESSENGER_ListTunnel *next = element->next; GNUNET_CONTAINER_DLL_remove(tunnels->head, tunnels->tail, element); @@ -110,3 +118,67 @@ remove_from_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, struct G return next; } + +void +load_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const char *path) +{ + GNUNET_assert((tunnels) && (path)); + + if (GNUNET_YES != GNUNET_DISK_file_test (path)) + return; + + enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE); + + struct GNUNET_DISK_FileHandle *handle = GNUNET_DISK_file_open( + path, GNUNET_DISK_OPEN_READ, permission + ); + + if (!handle) + return; + + GNUNET_DISK_file_seek(handle, 0, GNUNET_DISK_SEEK_SET); + + struct GNUNET_PeerIdentity peer; + ssize_t len; + + do { + len = GNUNET_DISK_file_read(handle, &peer, sizeof(peer)); + + if (len != sizeof(peer)) + break; + + add_to_list_tunnels(tunnels, &peer); + } while (len == sizeof(peer)); + + GNUNET_DISK_file_close(handle); +} + +void +save_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const char *path) +{ + GNUNET_assert((tunnels) && (path)); + + enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE); + + struct GNUNET_DISK_FileHandle *handle = GNUNET_DISK_file_open( + path, GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE, permission + ); + + if (!handle) + return; + + GNUNET_DISK_file_seek(handle, 0, GNUNET_DISK_SEEK_SET); + + struct GNUNET_MESSENGER_ListTunnel *element; + struct GNUNET_PeerIdentity pid; + + for (element = tunnels->head; element; element = element->next) + { + GNUNET_PEER_resolve (element->peer, &pid); + + GNUNET_DISK_file_write(handle, &pid, sizeof(pid)); + } + + GNUNET_DISK_file_sync(handle); + GNUNET_DISK_file_close(handle); +} diff --git a/src/messenger/messenger_api_list_tunnels.h b/src/messenger/messenger_api_list_tunnels.h index 0240fceb8..bd0a42e41 100644 --- a/src/messenger/messenger_api_list_tunnels.h +++ b/src/messenger/messenger_api_list_tunnels.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -47,7 +47,7 @@ struct GNUNET_MESSENGER_ListTunnels /** * Initializes list of tunnels peer identities as empty list. * - * @param tunnels List of peer identities + * @param[out] tunnels List of peer identities */ void init_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels); @@ -55,7 +55,7 @@ init_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels); /** * Clears the list of tunnels peer identities. * - * @param tunnels List of peer identities + * @param[in/out] tunnels List of peer identities */ void clear_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels); @@ -63,8 +63,8 @@ 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 + * @param[in/out] tunnels List of peer identities + * @param[in] peer Peer identity of tunnel */ void add_to_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct GNUNET_PeerIdentity *peer); @@ -79,8 +79,8 @@ add_to_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct * 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[in/out] tunnels List of peer identities + * @param[in] peer Peer identity of tunnel * @param[out] index Index of found element (optional) * @return Element in the list with matching peer identity */ @@ -89,11 +89,11 @@ find_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct GN /** * Tests linearly if the list of tunnels peer identities contains a specific - * peer identity and returns GNUNET_YES on success, otherwise GNUNET_NO. + * 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 + * @param[in/out] tunnels List of peer identities + * @param[in] 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); @@ -102,11 +102,29 @@ contains_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struc * 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 + * @param[in/out] tunnels List of peer identities + * @param[in/out] 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); +/** + * Loads the list of tunnels peer identities from a file under a given path. + * + * @param[out] messages List of hashes + * @param[in] path Path of file + */ +void +load_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const char *path); + +/** + * Saves the list of tunnels peer identities to a file under a given path. + * + * @param[in] messages List of hashes + * @param[in] path Path of file + */ +void +save_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const char *path); + #endif //GNUNET_MESSENGER_API_LIST_TUNNELS_H diff --git a/src/messenger/messenger_api_message.c b/src/messenger/messenger_api_message.c index fdab60eef..d88859186 100644 --- a/src/messenger/messenger_api_message.c +++ b/src/messenger/messenger_api_message.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -69,6 +69,8 @@ create_message (enum GNUNET_MESSENGER_MessageKind kind) struct GNUNET_MESSENGER_Message* copy_message (const struct GNUNET_MESSENGER_Message *message) { + GNUNET_assert(message); + struct GNUNET_MESSENGER_Message *copy = GNUNET_new(struct GNUNET_MESSENGER_Message); GNUNET_memcpy(copy, message, sizeof(struct GNUNET_MESSENGER_Message)); @@ -125,11 +127,28 @@ destroy_message_body (enum GNUNET_MESSENGER_MessageKind kind, struct GNUNET_MESS void destroy_message (struct GNUNET_MESSENGER_Message *message) { + GNUNET_assert(message); + destroy_message_body (message->header.kind, &(message->body)); GNUNET_free(message); } +int +is_message_session_bound (const struct GNUNET_MESSENGER_Message *message) +{ + GNUNET_assert(message); + + if ((GNUNET_MESSENGER_KIND_JOIN == message->header.kind) || + (GNUNET_MESSENGER_KIND_LEAVE == message->header.kind) || + (GNUNET_MESSENGER_KIND_NAME == message->header.kind) || + (GNUNET_MESSENGER_KIND_KEY == message->header.kind) || + (GNUNET_MESSENGER_KIND_ID == message->header.kind)) + return GNUNET_YES; + else + return GNUNET_NO; +} + static void fold_short_message (const struct GNUNET_MESSENGER_Message *message, struct GNUNET_MESSENGER_ShortMessage *shortened) { @@ -158,18 +177,7 @@ get_message_body_kind_size (enum GNUNET_MESSENGER_MessageKind kind) 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); + length += member_size(struct GNUNET_MESSENGER_Message, body.info.messenger_version); break; case GNUNET_MESSENGER_KIND_PEER: length += member_size(struct GNUNET_MESSENGER_Message, body.peer.peer); @@ -195,7 +203,7 @@ get_message_body_kind_size (enum GNUNET_MESSENGER_MessageKind kind) 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; + length += member_size(struct GNUNET_MESSENGER_Message, body.file.name); break; case GNUNET_MESSENGER_KIND_PRIVATE: length += member_size(struct GNUNET_MESSENGER_Message, body.private.key); @@ -207,16 +215,17 @@ get_message_body_kind_size (enum GNUNET_MESSENGER_MessageKind kind) return length; } +typedef uint32_t kind_t; + 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); + length += sizeof(kind_t); return length + get_message_body_kind_size (kind); } @@ -228,8 +237,17 @@ get_message_body_size (enum GNUNET_MESSENGER_MessageKind kind, const struct GNUN switch (kind) { + case GNUNET_MESSENGER_KIND_INFO: + length += GNUNET_IDENTITY_key_get_length(&(body->info.host_key)); + break; + case GNUNET_MESSENGER_KIND_JOIN: + length += GNUNET_IDENTITY_key_get_length(&(body->join.key)); + break; case GNUNET_MESSENGER_KIND_NAME: - length += (body->name.name? strlen (body->name.name) : 0); + length += (body->name.name ? strlen (body->name.name) : 0); + break; + case GNUNET_MESSENGER_KIND_KEY: + length += GNUNET_IDENTITY_key_get_length(&(body->key.key)); break; case GNUNET_MESSENGER_KIND_TEXT: length += strlen (body->text.text); @@ -248,19 +266,76 @@ get_message_body_size (enum GNUNET_MESSENGER_MessageKind kind, const struct GNUN } uint16_t -get_message_size (const struct GNUNET_MESSENGER_Message *message) +get_message_size (const struct GNUNET_MESSENGER_Message *message, + int include_signature) { - return get_message_kind_size (message->header.kind) + get_message_body_size (message->header.kind, &(message->body)); + GNUNET_assert(message); + + uint16_t length = 0; + + if (GNUNET_YES == include_signature) + length += GNUNET_IDENTITY_signature_get_length(&(message->header.signature)); + + length += get_message_kind_size (message->header.kind); + length += get_message_body_size (message->header.kind, &(message->body)); + + return length; } static uint16_t -get_short_message_size (const struct GNUNET_MESSENGER_ShortMessage *message) +get_short_message_size (const struct GNUNET_MESSENGER_ShortMessage *message, int include_body) { + const uint16_t minimum_size = sizeof(struct GNUNET_HashCode) + sizeof(kind_t); + if (message) - return sizeof(message->kind) + get_message_body_kind_size (message->kind) - + get_message_body_size (message->kind, &(message->body)); + return minimum_size + get_message_body_kind_size (message->kind) + + (include_body == GNUNET_YES? get_message_body_size (message->kind, &(message->body)) : 0); else - return sizeof(message->kind); + return minimum_size; +} + +static uint16_t +calc_usual_padding () +{ + uint16_t padding = 0; + uint16_t kind_size; + + for (int i = 0; i <= GNUNET_MESSENGER_KIND_MAX; i++) { + kind_size = get_message_kind_size ((enum GNUNET_MESSENGER_MessageKind) i); + + if (kind_size > padding) + padding = kind_size; + } + + return padding + GNUNET_MESSENGER_PADDING_MIN; +} + +#define max(x, y) (x > y? x : y) + +static uint16_t +calc_padded_length (uint16_t length) +{ + static uint16_t usual_padding = 0; + + if (!usual_padding) + usual_padding = calc_usual_padding(); + + const uint16_t padded_length = max( + length + GNUNET_MESSENGER_PADDING_MIN, + usual_padding + ); + + if (padded_length <= GNUNET_MESSENGER_PADDING_LEVEL0) + return GNUNET_MESSENGER_PADDING_LEVEL0; + + if (padded_length <= GNUNET_MESSENGER_PADDING_LEVEL1) + return GNUNET_MESSENGER_PADDING_LEVEL1; + + if (padded_length <= GNUNET_MESSENGER_PADDING_LEVEL2) + return GNUNET_MESSENGER_PADDING_LEVEL2; + + return GNUNET_MESSENGER_MAX_MESSAGE_SIZE; + } #define min(x, y) (x < y? x : y) @@ -272,7 +347,27 @@ get_short_message_size (const struct GNUNET_MESSENGER_ShortMessage *message) #define encode_step(dst, offset, src) do { \ encode_step_ext(dst, offset, src, sizeof(*src)); \ -} while(0) +} while (0) + +#define encode_step_key(dst, offset, src, length) do { \ + ssize_t result = GNUNET_IDENTITY_write_key_to_buffer( \ + src, dst + offset, length - offset \ + ); \ + if (result < 0) \ + GNUNET_break (0); \ + else \ + offset += result; \ +} while (0) + +#define encode_step_signature(dst, offset, src, length) do { \ + ssize_t result = GNUNET_IDENTITY_write_signature_to_buffer( \ + src, dst + offset, length - offset \ + ); \ + if (result < 0) \ + GNUNET_break (0); \ + else \ + offset += result; \ +} while (0) static void encode_message_body (enum GNUNET_MESSENGER_MessageKind kind, const struct GNUNET_MESSENGER_MessageBody *body, @@ -281,20 +376,18 @@ encode_message_body (enum GNUNET_MESSENGER_MessageKind kind, const struct GNUNET switch (kind) { case GNUNET_MESSENGER_KIND_INFO: - encode_step(buffer, offset, &(body->info.host_key)); - encode_step(buffer, offset, &(body->info.unique_id)); + encode_step_key(buffer, offset, &(body->info.host_key), length); + encode_step(buffer, offset, &(body->info.messenger_version)); break; case GNUNET_MESSENGER_KIND_JOIN: - encode_step(buffer, offset, &(body->join.key)); - break; - case GNUNET_MESSENGER_KIND_LEAVE: + encode_step_key(buffer, offset, &(body->join.key), length); 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)); + encode_step_key(buffer, offset, &(body->key.key), length); break; case GNUNET_MESSENGER_KIND_PEER: encode_step(buffer, offset, &(body->peer.peer)); @@ -321,7 +414,7 @@ encode_message_body (enum GNUNET_MESSENGER_MessageKind kind, const struct GNUNET 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.name, sizeof(body->file.name)); encode_step_ext(buffer, offset, body->file.uri, min(length - offset, strlen(body->file.uri))); break; case GNUNET_MESSENGER_KIND_PRIVATE: @@ -331,18 +424,40 @@ encode_message_body (enum GNUNET_MESSENGER_MessageKind kind, const struct GNUNET default: break; } + + if (offset >= length) + return; + + const uint16_t padding = length - offset; + const uint16_t used_padding = sizeof(padding) + sizeof(char); + + GNUNET_assert(padding >= used_padding); + + buffer[offset++] = '\0'; + + if (padding > used_padding) + GNUNET_CRYPTO_random_block(GNUNET_CRYPTO_QUALITY_WEAK, buffer + offset, padding - used_padding); + + GNUNET_memcpy(buffer + length - sizeof(padding), &padding, sizeof(padding)); } void -encode_message (const struct GNUNET_MESSENGER_Message *message, uint16_t length, char *buffer) +encode_message (const struct GNUNET_MESSENGER_Message *message, uint16_t length, char *buffer, + int include_signature) { + GNUNET_assert((message) && (buffer)); + uint16_t offset = 0; - encode_step(buffer, offset, &(message->header.signature)); + if (GNUNET_YES == include_signature) + encode_step_signature(buffer, offset, &(message->header.signature), length); + + const kind_t kind = (kind_t) message->header.kind; + 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_step(buffer, offset, &kind); encode_message_body (message->header.kind, &(message->body), length, buffer, offset); } @@ -350,11 +465,22 @@ encode_message (const struct GNUNET_MESSENGER_Message *message, uint16_t length, static void encode_short_message (const struct GNUNET_MESSENGER_ShortMessage *message, uint16_t length, char *buffer) { - uint16_t offset = 0; + struct GNUNET_HashCode hash; + uint16_t offset = sizeof(hash); - encode_step(buffer, offset, &(message->kind)); + const kind_t kind = (kind_t) message->kind; + + encode_step(buffer, offset, &kind); encode_message_body (message->kind, &(message->body), length, buffer, offset); + + GNUNET_CRYPTO_hash( + buffer + sizeof(hash), + length - sizeof(hash), + &hash + ); + + GNUNET_memcpy(buffer, &hash, sizeof(hash)); } #define decode_step_ext(src, offset, dst, size) do { \ @@ -372,29 +498,51 @@ encode_short_message (const struct GNUNET_MESSENGER_ShortMessage *message, uint1 decode_step_ext(src, offset, dst, size); \ } while (0) -static void +#define decode_step_key(src, offset, dst, length) do { \ + ssize_t result = GNUNET_IDENTITY_read_key_from_buffer( \ + dst, src + offset, length - offset \ + ); \ + if (result < 0) \ + GNUNET_break(0); \ + else \ + offset += result; \ +} while (0) + +static uint16_t decode_message_body (enum GNUNET_MESSENGER_MessageKind *kind, struct GNUNET_MESSENGER_MessageBody *body, uint16_t length, const char *buffer, uint16_t offset) { + uint16_t padding = 0; + + GNUNET_memcpy(&padding, buffer + length - sizeof(padding), sizeof(padding)); + + if (padding > length - offset) + padding = 0; + + const uint16_t end_zero = length - padding; + + if ((padding) && (buffer[end_zero] != '\0')) + padding = 0; + + length -= padding; + switch (*kind) { - case GNUNET_MESSENGER_KIND_INFO: - decode_step(buffer, offset, &(body->info.host_key)); - decode_step(buffer, offset, &(body->info.unique_id)); + case GNUNET_MESSENGER_KIND_INFO: { + decode_step_key(buffer, offset, &(body->info.host_key), length); + decode_step(buffer, offset, &(body->info.messenger_version)); break; - case GNUNET_MESSENGER_KIND_JOIN: - decode_step(buffer, offset, &(body->join.key)); - break; - case GNUNET_MESSENGER_KIND_LEAVE: + } case GNUNET_MESSENGER_KIND_JOIN: { + decode_step_key(buffer, offset, &(body->join.key), length); break; - case GNUNET_MESSENGER_KIND_NAME: + } 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)); + decode_step_key(buffer, offset, &(body->key.key), length); break; case GNUNET_MESSENGER_KIND_PEER: decode_step(buffer, offset, &(body->peer.peer)); @@ -421,7 +569,7 @@ decode_message_body (enum GNUNET_MESSENGER_MessageKind *kind, struct GNUNET_MESS 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_ext(buffer, offset, body->file.name, sizeof(body->file.name)); decode_step_malloc(buffer, offset, body->file.uri, length - offset, 1); break; case GNUNET_MESSENGER_KIND_PRIVATE: @@ -434,26 +582,51 @@ decode_message_body (enum GNUNET_MESSENGER_MessageKind *kind, struct GNUNET_MESS *kind = GNUNET_MESSENGER_KIND_UNKNOWN; break; } + + return padding; } int -decode_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, const char *buffer) +decode_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, const char *buffer, + int include_signature, uint16_t *padding) { + GNUNET_assert((message) && (buffer)); + uint16_t offset = 0; - if (length < get_message_kind_size (GNUNET_MESSENGER_KIND_UNKNOWN)) + if (GNUNET_YES == include_signature) + { + ssize_t result = GNUNET_IDENTITY_read_signature_from_buffer( + &(message->header.signature), buffer, length - offset + ); + + if (result < 0) + return GNUNET_NO; + else + offset += result; + } + + const uint16_t count = length - offset; + + if (count < get_message_kind_size (GNUNET_MESSENGER_KIND_UNKNOWN)) return GNUNET_NO; - decode_step(buffer, offset, &(message->header.signature)); + kind_t kind; + 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)); + decode_step(buffer, offset, &kind); - if (length < get_message_kind_size (message->header.kind)) + message->header.kind = (enum GNUNET_MESSENGER_MessageKind) kind; + + if (count < get_message_kind_size (message->header.kind)) return GNUNET_NO; - decode_message_body (&(message->header.kind), &(message->body), length, buffer, offset); + const uint16_t result = decode_message_body (&(message->header.kind), &(message->body), length, buffer, offset); + + if (padding) + *padding = result; return GNUNET_YES; } @@ -461,47 +634,80 @@ decode_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, const static int decode_short_message (struct GNUNET_MESSENGER_ShortMessage *message, uint16_t length, const char *buffer) { - uint16_t offset = 0; + struct GNUNET_HashCode expected, hash; + uint16_t offset = sizeof(hash); + + if (length < get_short_message_size (NULL, GNUNET_NO)) + return GNUNET_NO; + + GNUNET_memcpy(&hash, buffer, sizeof(hash)); + + GNUNET_CRYPTO_hash( + buffer + sizeof(hash), + length - sizeof(hash), + &expected + ); - if (length < get_short_message_size (NULL)) + if (0 != GNUNET_CRYPTO_hash_cmp(&hash, &expected)) return GNUNET_NO; - decode_step(buffer, offset, &(message->kind)); + kind_t kind; - if (length < get_short_message_size (message)) + decode_step(buffer, offset, &kind); + + message->kind = (enum GNUNET_MESSENGER_MessageKind) kind; + + if (length < get_short_message_size (message, GNUNET_NO)) return GNUNET_NO; decode_message_body (&(message->kind), &(message->body), length, buffer, offset); + if (GNUNET_MESSENGER_KIND_UNKNOWN == message->kind) + return GNUNET_NO; + return GNUNET_YES; } void -hash_message (uint16_t length, const char *buffer, struct GNUNET_HashCode *hash) +hash_message (const struct GNUNET_MESSENGER_Message *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); + GNUNET_assert((message) && (buffer) && (hash)); + + const ssize_t offset = GNUNET_IDENTITY_signature_get_length( + &(message->header.signature) + ); + + GNUNET_CRYPTO_hash (buffer + offset, length - offset, 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) { + GNUNET_assert((message) && (buffer) && (hash) && (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)); + + uint16_t offset = 0; + encode_step_signature(buffer, offset, &(message->header.signature), length); } int verify_message (const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash, const struct GNUNET_IDENTITY_PublicKey *key) { + GNUNET_assert((message) && (hash) && (key)); + + if (ntohl (key->type) != ntohl (message->header.signature.type)) + return GNUNET_SYSERR; + struct GNUNET_MESSENGER_MessageSignature signature; signature.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE); @@ -516,26 +722,32 @@ verify_message (const struct GNUNET_MESSENGER_Message *message, const struct GNU int encrypt_message (struct GNUNET_MESSENGER_Message *message, const struct GNUNET_IDENTITY_PublicKey *key) { + GNUNET_assert((message) && (key)); + struct GNUNET_MESSENGER_ShortMessage shortened; fold_short_message (message, &shortened); - const uint16_t length = get_short_message_size (&shortened); + const uint16_t length = get_short_message_size (&shortened, GNUNET_YES); + const uint16_t padded_length = calc_padded_length(length); message->header.kind = GNUNET_MESSENGER_KIND_PRIVATE; - message->body.private.data = GNUNET_malloc(length); + message->body.private.data = GNUNET_malloc(padded_length); + message->body.private.length = padded_length; - encode_short_message (&shortened, length, message->body.private.data); + encode_short_message (&shortened, padded_length, message->body.private.data); - if (GNUNET_IDENTITY_encrypt (message->body.private.data, length, key, &(message->body.private.key), - message->body.private.data) - == length) + if (padded_length == GNUNET_IDENTITY_encrypt (message->body.private.data, padded_length, key, + &(message->body.private.key), + message->body.private.data)) { destroy_message_body (shortened.kind, &(shortened.body)); return GNUNET_YES; } else { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Encrypting message failed!\n"); + unfold_short_message (&shortened, message); return GNUNET_NO; } @@ -544,18 +756,28 @@ encrypt_message (struct GNUNET_MESSENGER_Message *message, const struct GNUNET_I 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), + GNUNET_assert((message) && (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)) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Decrypting message failed!\n"); + return GNUNET_NO; + } struct GNUNET_MESSENGER_ShortMessage shortened; if (GNUNET_YES != decode_short_message (&shortened, message->body.private.length, message->body.private.data)) + { + GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Decoding decrypted message failed!\n"); + return GNUNET_NO; + } unfold_short_message (&shortened, message); + return GNUNET_YES; } @@ -563,18 +785,25 @@ 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); + GNUNET_assert(message); + + if (ego) + message->header.signature.type = ego->priv.type; + + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Packing message kind=%u and sender: %s\n", + message->header.kind, GNUNET_sh2s(&(message->header.sender_id))); struct GNUNET_MessageHeader *header; - uint16_t length = get_message_size (message); + const uint16_t length = get_message_size (message, GNUNET_YES); + const uint16_t padded_length = calc_padded_length(length); 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); + env = GNUNET_MQ_msg_extra(header, padded_length, GNUNET_MESSAGE_TYPE_CADET_CLI); buffer = (char*) &(header[1]); } @@ -582,14 +811,14 @@ pack_message (struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode * { env = NULL; - buffer = GNUNET_malloc(length); + buffer = GNUNET_malloc(padded_length); } - encode_message (message, length, buffer); + encode_message (message, padded_length, buffer, GNUNET_YES); if (hash) { - hash_message (length, buffer, hash); + hash_message (message, length, buffer, hash); if (ego) sign_message (message, length, buffer, hash, ego); @@ -600,3 +829,43 @@ pack_message (struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode * return env; } + +int +filter_message_sending (const struct GNUNET_MESSENGER_Message *message) +{ + switch (message->header.kind) + { + case GNUNET_MESSENGER_KIND_INFO: + return GNUNET_SYSERR; // Reserved for connection handling only! + case GNUNET_MESSENGER_KIND_JOIN: + return GNUNET_NO; // Use #GNUNET_MESSENGER_enter_room(...) instead! + case GNUNET_MESSENGER_KIND_LEAVE: + return GNUNET_NO; // Use #GNUNET_MESSENGER_close_room(...) instead! + case GNUNET_MESSENGER_KIND_NAME: + return GNUNET_YES; + case GNUNET_MESSENGER_KIND_KEY: + return GNUNET_NO; // Use #GNUNET_MESSENGER_update(...) instead! + case GNUNET_MESSENGER_KIND_PEER: + return GNUNET_NO; // Use #GNUNET_MESSENGER_open_room(...) instead! + case GNUNET_MESSENGER_KIND_ID: + return GNUNET_SYSERR; // Reserved for member id handling only! + case GNUNET_MESSENGER_KIND_MISS: + return GNUNET_SYSERR; // Reserved for connection handling only! + case GNUNET_MESSENGER_KIND_MERGE: + return GNUNET_YES; + case GNUNET_MESSENGER_KIND_REQUEST: + return GNUNET_YES; + case GNUNET_MESSENGER_KIND_INVITE: + return GNUNET_YES; + case GNUNET_MESSENGER_KIND_TEXT: + return GNUNET_YES; + case GNUNET_MESSENGER_KIND_FILE: + return GNUNET_YES; + case GNUNET_MESSENGER_KIND_PRIVATE: + return GNUNET_NO; // Use #GNUNET_MESSENGER_send_message(...) with a contact instead! + case GNUNET_MESSENGER_KIND_DELETE: + return GNUNET_YES; + default: + return GNUNET_SYSERR; + } +} diff --git a/src/messenger/messenger_api_message.h b/src/messenger/messenger_api_message.h index 0f0a97e9c..7ce30dc92 100644 --- a/src/messenger/messenger_api_message.h +++ b/src/messenger/messenger_api_message.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -36,10 +36,17 @@ #include "messenger_api_ego.h" +#define GNUNET_MESSENGER_MAX_MESSAGE_SIZE (GNUNET_MAX_MESSAGE_SIZE - GNUNET_MIN_MESSAGE_SIZE) + +#define GNUNET_MESSENGER_PADDING_MIN (sizeof(uint16_t) + sizeof(char)) +#define GNUNET_MESSENGER_PADDING_LEVEL0 (512) +#define GNUNET_MESSENGER_PADDING_LEVEL1 (4096) +#define GNUNET_MESSENGER_PADDING_LEVEL2 (32768) + /** * Creates and allocates a new message with a specific kind. * - * @param kind Kind of message + * @param[in] kind Kind of message * @return New message */ struct GNUNET_MESSENGER_Message* @@ -48,7 +55,7 @@ create_message (enum GNUNET_MESSENGER_MessageKind kind); /** * Creates and allocates a copy of a given message. * - * @param message Message + * @param[in] message Message * @return New message */ struct GNUNET_MESSENGER_Message* @@ -57,15 +64,24 @@ copy_message (const struct GNUNET_MESSENGER_Message *message); /** * Destroys a message and frees its memory fully. * - * @param message Message + * @param[in/out] message Message */ void destroy_message (struct GNUNET_MESSENGER_Message *message); +/** + * Returns if the message should be bound to a member session. + * + * @param[in] message Message + * @return #GNUNET_YES or #GNUNET_NO + */ +int +is_message_session_bound (const struct GNUNET_MESSENGER_Message *message); + /** * Returns the minimal size in bytes to encode a message of a specific kind. * - * @param kind Kind of message + * @param[in] kind Kind of message * @return Minimal size to encode */ uint16_t @@ -74,57 +90,66 @@ get_message_kind_size (enum GNUNET_MESSENGER_MessageKind kind); /** * Returns the exact size in bytes to encode a given message. * - * @param message Message + * @param[in] message Message + * @param[in] encode_signature Flag to include signature * @return Size to encode */ uint16_t -get_message_size (const struct GNUNET_MESSENGER_Message *message); +get_message_size (const struct GNUNET_MESSENGER_Message *message, + int include_signature); /** * Encodes a given message into a buffer of a maximal length in bytes. * - * @param message Message - * @param length Maximal length to encode + * @param[in] message Message + * @param[in] length Maximal length to encode * @param[out] buffer Buffer + * @param[in] encode_signature Flag to include signature */ void -encode_message (const struct GNUNET_MESSENGER_Message *message, uint16_t length, char *buffer); +encode_message (const struct GNUNET_MESSENGER_Message *message, uint16_t length, char *buffer, + int include_signature); /** * 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. + * resulting #GNUNET_NO after decoding only the messages header. * - * On success the function returns GNUNET_YES. + * 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 + * @param[in] length Maximal length to decode + * @param[in] buffer Buffer + * @param[out] padding Padding + * @return #GNUNET_YES on success, otherwise #GNUNET_NO */ int -decode_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, const char *buffer); +decode_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, const char *buffer, + int include_signature, uint16_t *padding); /** - * Calculates a hash of a given buffer of a length in bytes. + * Calculates a hash of a given buffer with a length in bytes + * from a message. * - * @param length Length of buffer - * @param buffer Buffer + * @param[in] message Message + * @param[in] length Length of buffer + * @param[in] buffer Buffer * @param[out] hash Hash */ void -hash_message (uint16_t length, const char *buffer, struct GNUNET_HashCode *hash); +hash_message (const struct GNUNET_MESSENGER_Message *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[in/out] message Message + * @param[in] length Length of buffer * @param[out] buffer Buffer - * @param hash Hash of message - * @param ego EGO + * @param[in] hash Hash of message + * @param[in] ego EGO */ void sign_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, char *buffer, @@ -132,13 +157,13 @@ sign_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, char *b /** * 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. + * 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 + * @param[in] message Message + * @param[in] hash Hash of message + * @param[in] 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, @@ -147,23 +172,23 @@ verify_message (const struct GNUNET_MESSENGER_Message *message, const struct GNU /** * 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. + * #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 + * @param[in/out] message Message + * @param[in] 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. + * 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 + * @param[in/out] message Message + * @param[in] 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); @@ -173,18 +198,28 @@ decrypt_message (struct GNUNET_MESSENGER_Message *message, const struct GNUNET_I /** * 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 + * 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 + * @param[in] ego EGO to sign + * @param[in] 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); +/** + * Returns if a specific kind of message should be sent by a client. The function returns + * #GNUNET_YES or #GNUNET_NO for recommendations and #GNUNET_SYSERR for specific kinds + * of messages which should not be sent manually at all. + * + * @param[in] message Message + */ +int +filter_message_sending (const struct GNUNET_MESSENGER_Message *message); + #endif //GNUNET_MESSENGER_API_MESSAGE_H diff --git a/src/messenger/messenger_api_room.c b/src/messenger/messenger_api_room.c index 5fedf1a78..df141ca12 100644 --- a/src/messenger/messenger_api_room.c +++ b/src/messenger/messenger_api_room.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -30,6 +30,8 @@ struct GNUNET_MESSENGER_Room* create_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCode *key) { + GNUNET_assert((handle) && (key)); + struct GNUNET_MESSENGER_Room *room = GNUNET_new(struct GNUNET_MESSENGER_Room); room->handle = handle; @@ -38,11 +40,10 @@ create_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCod 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); + room->members = GNUNET_CONTAINER_multishortmap_create (8, GNUNET_NO); return room; } @@ -50,9 +51,10 @@ create_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCod static int iterate_destroy_message (void *cls, const struct GNUNET_HashCode *key, void *value) { - struct GNUNET_MESSENGER_Message *message = value; + struct GNUNET_MESSENGER_RoomMessageEntry *entry = value; - destroy_message (message); + destroy_message (entry->message); + GNUNET_free(entry); return GNUNET_YES; } @@ -60,8 +62,7 @@ iterate_destroy_message (void *cls, const struct GNUNET_HashCode *key, void *val void destroy_room (struct GNUNET_MESSENGER_Room *room) { - if (room->members) - GNUNET_CONTAINER_multishortmap_destroy (room->members); + GNUNET_assert(room); clear_list_tunnels (&(room->entries)); @@ -72,6 +73,9 @@ destroy_room (struct GNUNET_MESSENGER_Room *room) GNUNET_CONTAINER_multihashmap_destroy (room->messages); } + if (room->members) + GNUNET_CONTAINER_multishortmap_destroy (room->members); + if (room->contact_id) GNUNET_free(room->contact_id); @@ -81,65 +85,111 @@ destroy_room (struct GNUNET_MESSENGER_Room *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); + GNUNET_assert((room) && (hash)); + + struct GNUNET_MESSENGER_RoomMessageEntry *entry = GNUNET_CONTAINER_multihashmap_get ( + room->messages, hash + ); + + return (entry? entry->message : NULL); +} + +struct GNUNET_MESSENGER_Contact* +get_room_sender (const struct GNUNET_MESSENGER_Room *room, const struct GNUNET_HashCode *hash) +{ + GNUNET_assert((room) && (hash)); + + struct GNUNET_MESSENGER_RoomMessageEntry *entry = GNUNET_CONTAINER_multihashmap_get ( + room->messages, hash + ); + + return (entry? entry->sender : NULL); } static void -handle_join_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, - const struct GNUNET_HashCode *hash) +handle_join_message (struct GNUNET_MESSENGER_Room *room, struct GNUNET_MESSENGER_Contact *sender, + 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 (!sender) + { + struct GNUNET_MESSENGER_ContactStore *store = get_handle_contact_store(room->handle); + struct GNUNET_HashCode context; + + get_context_from_member(&(room->key), &(message->header.sender_id), &context); + + sender = get_store_contact(store, &context, &(message->body.join.key)); + } - if (contact) - GNUNET_CONTAINER_multishortmap_put (room->members, &(message->header.sender_id), contact, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + if ((GNUNET_YES != GNUNET_CONTAINER_multishortmap_contains_value(room->members, &(message->header.sender_id), sender)) && + (GNUNET_OK == GNUNET_CONTAINER_multishortmap_put(room->members, &(message->header.sender_id), sender, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE))) + increase_contact_rc(sender); } static void -handle_leave_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, - const struct GNUNET_HashCode *hash) +handle_leave_message (struct GNUNET_MESSENGER_Room *room, struct GNUNET_MESSENGER_Contact *sender, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) { - GNUNET_CONTAINER_multishortmap_remove_all (room->members, &(message->header.sender_id)); + if ((!sender) || + (GNUNET_YES != GNUNET_CONTAINER_multishortmap_remove(room->members, &(message->header.sender_id), sender))) + return; + + struct GNUNET_HashCode context; + get_context_from_member(&(room->key), &(message->header.sender_id), &context); + + struct GNUNET_MESSENGER_ContactStore *store = get_handle_contact_store(room->handle); + + if (GNUNET_YES == decrease_contact_rc(sender)) + remove_store_contact(store, sender, &context); } static void -handle_name_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, - const struct GNUNET_HashCode *hash) +handle_name_message (struct GNUNET_MESSENGER_Room *room, struct GNUNET_MESSENGER_Contact *sender, + 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 (!sender) + return; - if (contact) - set_contact_name (contact, message->body.name.name); + set_contact_name (sender, 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) +handle_key_message (struct GNUNET_MESSENGER_Room *room, struct GNUNET_MESSENGER_Contact *sender, + 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 (!sender) + return; + + struct GNUNET_HashCode context; + get_context_from_member(&(room->key), &(message->header.sender_id), &context); - if (contact) - swap_handle_contact_by_pubkey (room->handle, contact, &(message->body.key.key)); + struct GNUNET_MESSENGER_ContactStore *store = get_handle_contact_store(room->handle); + + update_store_contact(store, sender, &context, &context, &(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) +handle_id_message (struct GNUNET_MESSENGER_Room *room, struct GNUNET_MESSENGER_Contact *sender, + 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 ((!sender) || + (GNUNET_YES != GNUNET_CONTAINER_multishortmap_remove(room->members, &(message->header.sender_id), sender)) || + (GNUNET_OK != GNUNET_CONTAINER_multishortmap_put(room->members, &(message->body.id.id), sender, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE))) + return; + + struct GNUNET_HashCode context, next_context; + get_context_from_member(&(room->key), &(message->header.sender_id), &context); + get_context_from_member(&(room->key), &(message->body.id.id), &next_context); - 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); + struct GNUNET_MESSENGER_ContactStore *store = get_handle_contact_store(room->handle); + + update_store_contact(store, sender, &context, &next_context, get_contact_key(sender)); } static void -handle_miss_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, - const struct GNUNET_HashCode *hash) +handle_miss_message (struct GNUNET_MESSENGER_Room *room, struct GNUNET_MESSENGER_Contact *sender, + 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))) { @@ -150,9 +200,25 @@ handle_miss_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MES } } +static void +handle_delete_message (struct GNUNET_MESSENGER_Room *room, struct GNUNET_MESSENGER_Contact *sender, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) +{ + struct GNUNET_MESSENGER_RoomMessageEntry *entry = GNUNET_CONTAINER_multihashmap_get ( + room->messages, &(message->body.delete.hash) + ); + + if ((entry) && ((entry->sender == sender) || (get_handle_contact (room->handle, &(room->key)) == sender)) && + (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (room->messages, &(message->body.delete.hash), entry))) + { + destroy_message (entry->message); + GNUNET_free(entry); + } +} + void -handle_room_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, - const struct GNUNET_HashCode *hash) +handle_room_message (struct GNUNET_MESSENGER_Room *room, struct GNUNET_MESSENGER_Contact *sender, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) { if (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains (room->messages, hash)) return; @@ -160,30 +226,78 @@ handle_room_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MES switch (message->header.kind) { case GNUNET_MESSENGER_KIND_JOIN: - handle_join_message (room, message, hash); + handle_join_message (room, sender, message, hash); break; case GNUNET_MESSENGER_KIND_LEAVE: - handle_leave_message (room, message, hash); + handle_leave_message (room, sender, message, hash); break; case GNUNET_MESSENGER_KIND_NAME: - handle_name_message (room, message, hash); + handle_name_message (room, sender, message, hash); break; case GNUNET_MESSENGER_KIND_KEY: - handle_key_message (room, message, hash); + handle_key_message (room, sender, message, hash); break; case GNUNET_MESSENGER_KIND_ID: - handle_id_message (room, message, hash); + handle_id_message (room, sender, message, hash); break; case GNUNET_MESSENGER_KIND_MISS: - handle_miss_message (room, message, hash); + handle_miss_message (room, sender, message, hash); + break; + case GNUNET_MESSENGER_KIND_DELETE: + handle_delete_message (room, sender, message, hash); break; default: break; } - struct GNUNET_MESSENGER_Message *clone = copy_message (message); + struct GNUNET_MESSENGER_RoomMessageEntry *entry = GNUNET_new(struct GNUNET_MESSENGER_RoomMessageEntry); + + if (!entry) + return; + + entry->sender = sender; + entry->message = copy_message (message); - if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (room->messages, hash, clone, + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (room->messages, hash, entry, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) - destroy_message (clone); + { + destroy_message (entry->message); + GNUNET_free(entry); + } +} + +struct GNUNET_MESSENGER_MemberCall +{ + struct GNUNET_MESSENGER_Room *room; + GNUNET_MESSENGER_MemberCallback callback; + void *cls; +}; + +static int +iterate_local_members (void* cls, const struct GNUNET_ShortHashCode *key, void *value) +{ + struct GNUNET_MESSENGER_MemberCall *call = cls; + struct GNUNET_MESSENGER_Contact *contact = value; + + return call->callback(call->cls, call->room, contact); +} + +int +iterate_room_members (struct GNUNET_MESSENGER_Room *room, GNUNET_MESSENGER_MemberCallback callback, + void* cls) +{ + GNUNET_assert(room); + + if (!callback) + return GNUNET_CONTAINER_multishortmap_iterate(room->members, NULL, NULL); + + struct GNUNET_MESSENGER_MemberCall call; + + call.room = room; + call.callback = callback; + call.cls = cls; + + GNUNET_assert(callback); + + return GNUNET_CONTAINER_multishortmap_iterate(room->members, iterate_local_members, &call); } diff --git a/src/messenger/messenger_api_room.h b/src/messenger/messenger_api_room.h index 0038128d8..9455fd43b 100644 --- a/src/messenger/messenger_api_room.h +++ b/src/messenger/messenger_api_room.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -36,6 +36,11 @@ #include "messenger_api_contact.h" #include "messenger_api_message.h" +struct GNUNET_MESSENGER_RoomMessageEntry { + struct GNUNET_MESSENGER_Contact* sender; + struct GNUNET_MESSENGER_Message* message; +}; + struct GNUNET_MESSENGER_Room { struct GNUNET_MESSENGER_Handle *handle; @@ -45,17 +50,17 @@ struct GNUNET_MESSENGER_Room struct GNUNET_ShortHashCode *contact_id; - struct GNUNET_CONTAINER_MultiShortmap *members; struct GNUNET_MESSENGER_ListTunnels entries; struct GNUNET_CONTAINER_MultiHashMap *messages; + struct GNUNET_CONTAINER_MultiShortmap *members; }; /** * 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 + * @param[in/out] handle Handle + * @param[in] key Key of room * @return New room */ struct GNUNET_MESSENGER_Room* @@ -64,7 +69,7 @@ create_room (struct GNUNET_MESSENGER_Handle *handle, const struct GNUNET_HashCod /** * Destroys a room and frees its memory fully from the client API. * - * @param room Room + * @param[in/out] room Room */ void destroy_room (struct GNUNET_MESSENGER_Room *room); @@ -73,23 +78,48 @@ 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 + * @param[in] room Room + * @param[in] 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); +/** + * Returns a messages sender locally stored from a map for a given hash in a room. If no + * matching message is found, NULL gets returned. + * + * @param[in] room Room + * @param[in] hash Hash of message + * @return Contact of sender or NULL + */ +struct GNUNET_MESSENGER_Contact* +get_room_sender (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 + * @param[in/out] room Room + * @param[in/out] sender Contact of sender + * @param[in] message Message + * @param[in] hash Hash of message */ void -handle_room_message (struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Message *message, - const struct GNUNET_HashCode *hash); +handle_room_message (struct GNUNET_MESSENGER_Room *room, struct GNUNET_MESSENGER_Contact *sender, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); + +/** + * Iterates through all members of a given room to forward each of them to a selected + * callback with a custom closure. + * + * @param[in/out] room Room + * @param[in] callback Function called for each member + * @param[in/out] cls Closure + * @return Amount of members iterated + */ +int +iterate_room_members (struct GNUNET_MESSENGER_Room *room, GNUNET_MESSENGER_MemberCallback callback, + void* cls); #endif //GNUNET_MESSENGER_API_ROOM_H diff --git a/src/messenger/messenger_api_util.c b/src/messenger/messenger_api_util.c new file mode 100644 index 000000000..68e15d789 --- /dev/null +++ b/src/messenger/messenger_api_util.c @@ -0,0 +1,84 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020--2021 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_util.c + * @brief messenger api: client implementation of GNUnet MESSENGER service + */ + +#include "messenger_api_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_assert(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) +{ + GNUNET_assert(id); + + 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; +} + +const struct GNUNET_IDENTITY_PublicKey* +get_anonymous_public_key () +{ + static struct GNUNET_IDENTITY_PublicKey public_key; + static struct GNUNET_IDENTITY_Ego* ego = NULL; + + if (!ego) + { + ego = GNUNET_IDENTITY_ego_get_anonymous(); + GNUNET_IDENTITY_ego_get_public_key(ego, &public_key); + } + + return &public_key; +} diff --git a/src/messenger/messenger_api_util.h b/src/messenger/messenger_api_util.h new file mode 100644 index 000000000..c70a3601f --- /dev/null +++ b/src/messenger/messenger_api_util.h @@ -0,0 +1,64 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020--2021 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_util.h + * @brief messenger api: client implementation of 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" +#include "gnunet_disk_lib.h" +#include "gnunet_identity_service.h" + +/** + * Starts an urgent task to close a CADET channel asynchronously. + * + * @param[in/out] 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[in] 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); + +/** + * Returns the public identity key of #GNUNET_IDENTITY_ego_get_anonymous() without + * recalculating it every time. + * + * @return anonymous public key + */ +const struct GNUNET_IDENTITY_PublicKey* +get_anonymous_public_key (); + +#endif //GNUNET_SERVICE_MESSENGER_UTIL_H diff --git a/src/messenger/test_messenger.c b/src/messenger/test_messenger.c index b42dfe6d9..fb3e3e1bc 100644 --- a/src/messenger/test_messenger.c +++ b/src/messenger/test_messenger.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -61,14 +61,13 @@ end (void *cls) if (messenger) { - GNUNET_MESSENGER_disconnect(messenger); + GNUNET_MESSENGER_disconnect (messenger); messenger = NULL; } status = 0; } - static void end_badly (void *cls) { @@ -83,7 +82,7 @@ end_operation (void *cls) { op_task = NULL; - fprintf (stderr, "Testcase failed (operation: '%s').\n", cls? (const char*) cls : "unknown"); + fprintf (stderr, "Testcase failed (operation: '%s').\n", cls ? (const char*) cls : "unknown"); if (die_task) GNUNET_SCHEDULER_cancel (die_task); @@ -109,30 +108,25 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle) op_task = NULL; } - const char* name = GNUNET_MESSENGER_get_name(handle); + const char *name = GNUNET_MESSENGER_get_name (handle); - if (0 != strcmp(name, TESTER_NAME)) + 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); - 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))))) + if (((!identity_counter) && (key)) || ((identity_counter) && (!key))) { op_task = GNUNET_SCHEDULER_add_now (&end_operation, "key"); return; } - if (identity_counter) { - GNUNET_MESSENGER_disconnect(handle); + if (identity_counter) + { + GNUNET_MESSENGER_disconnect (handle); op_task = NULL; messenger = NULL; @@ -144,7 +138,7 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle) return; } - GNUNET_MESSENGER_update(messenger); + GNUNET_MESSENGER_update (messenger); identity_counter++; } @@ -156,16 +150,14 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle) * @param peer Peer for testing */ static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) +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); + messenger = GNUNET_MESSENGER_connect (cfg, TESTER_NAME, &on_identity, NULL, NULL, NULL); } /** @@ -176,11 +168,9 @@ run (void *cls, * @return 0 ok, 1 on error */ int -main(int argc, char **argv) +main (int argc, char **argv) { - if (0 != GNUNET_TESTING_peer_run("test-messenger", - "test_messenger_api.conf", - &run, NULL)) + 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_adapt.c b/src/messenger/test_messenger_adapt.c new file mode 100644 index 000000000..90e8ac28d --- /dev/null +++ b/src/messenger/test_messenger_adapt.c @@ -0,0 +1,47 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_adapt.c + * @author Tobias Frisch + * @brief Test for the messenger service using cadet API. + */ + +#include "testing_messenger_setup.h" + +/** + * 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) +{ + unsigned int doors [] = { 5, 1, 2, 3, 6, 7, 8, 4 }; + unsigned int stages [] = { 0x21, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x21 }; + + struct test_configuration cfg; + cfg.count = 8; + cfg.doors = doors; + cfg.stages = stages; + + return GNUNET_run_messenger_setup ("test_messenger_adapt", &cfg); +} diff --git a/src/messenger/test_messenger_anonymous.c b/src/messenger/test_messenger_anonymous.c index e2057acc4..a70121a30 100644 --- a/src/messenger/test_messenger_anonymous.c +++ b/src/messenger/test_messenger_anonymous.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2020 GNUnet e.V. + Copyright (C) 2020--2021 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 @@ -59,14 +59,13 @@ end (void *cls) if (messenger) { - GNUNET_MESSENGER_disconnect(messenger); + GNUNET_MESSENGER_disconnect (messenger); messenger = NULL; } status = 0; } - static void end_badly (void *cls) { @@ -81,7 +80,7 @@ end_operation (void *cls) { op_task = NULL; - fprintf (stderr, "Testcase failed (operation: '%s').\n", cls? (const char*) cls : "unknown"); + fprintf (stderr, "Testcase failed (operation: '%s').\n", cls ? (const char*) cls : "unknown"); if (die_task) GNUNET_SCHEDULER_cancel (die_task); @@ -105,7 +104,7 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle) op_task = NULL; } - const char* name = GNUNET_MESSENGER_get_name(handle); + const char *name = GNUNET_MESSENGER_get_name (handle); if (NULL != name) { @@ -113,26 +112,21 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle) return; } - if (GNUNET_SYSERR != GNUNET_MESSENGER_update(handle)) + 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); + const struct GNUNET_IDENTITY_PublicKey *key = GNUNET_MESSENGER_get_key (handle); - if (0 != GNUNET_memcmp(key, (&anonymous_key))) + if (key) { op_task = GNUNET_SCHEDULER_add_now (&end_operation, "key-anonymous"); return; } - GNUNET_MESSENGER_disconnect(handle); + GNUNET_MESSENGER_disconnect (handle); messenger = NULL; @@ -150,14 +144,12 @@ on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle) * @param peer Peer for testing */ static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) +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); + messenger = GNUNET_MESSENGER_connect (cfg, NULL, &on_identity, NULL, NULL, NULL); } /** @@ -168,11 +160,9 @@ run (void *cls, * @return 0 ok, 1 on error */ int -main(int argc, char **argv) +main (int argc, char **argv) { - if (0 != GNUNET_TESTING_peer_run("test-messenger", - "test_messenger_api.conf", - &run, NULL)) + 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_api.conf b/src/messenger/test_messenger_api.conf index f5837392e..968f56f6d 100644 --- a/src/messenger/test_messenger_api.conf +++ b/src/messenger/test_messenger_api.conf @@ -1,11 +1,39 @@ @INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf @INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf +[testbed] +HOSTNAME = localhost +OVERLAY_TOPOLOGY = CLIQUE + +[arm] +GLOBAL_POSTFIX = -l $GNUNET_CACHE_HOME/{}-logs -L verbose + +[transport] +IMMEDIATE_START = YES + +[core] +START_ON_DEMAND = YES +IMMEDIATE_START = YES +USE_EPHEMERAL_KEYS = NO + [PATHS] GNUNET_TEST_HOME = $GNUNET_TMP/test-messenger-api/ +[peerinfo] +NO_IO = YES + [cadet] START_ON_DEMAND = YES +REFRESH_CONNECTION_TIME = 1 s +ID_ANNOUNCE_TIME = 5 s +CONNECT_TIMEOUT = 30 s +DEFAULT_TTL = 16 +DHT_REPLICATION_LEVEL = 10 +MAX_TUNNELS = 10 +MAX_CONNECTIONS = 10 +MAX_MSGS_QUEUE = 20 +DISABLE_TRY_CONNECT = YES +REKEY_PERIOD = 2 s [identity] START_ON_DEMAND = YES @@ -14,4 +42,6 @@ START_ON_DEMAND = YES START_ON_DEMAND = YES [nat] -ENABLE_UPNP = NO \ No newline at end of file +ENABLE_UPNP = NO +RETURN_LOCAL_ADDRESSES = YES +IMMEDIATE_START = NO \ No newline at end of file diff --git a/src/messenger/test_messenger_async_client.c b/src/messenger/test_messenger_async_client.c new file mode 100644 index 000000000..1067b9a6d --- /dev/null +++ b/src/messenger/test_messenger_async_client.c @@ -0,0 +1,47 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_async_client.c + * @author Tobias Frisch + * @brief Test for the messenger service using cadet API. + */ + +#include "testing_messenger_setup.h" + +/** + * 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) +{ + unsigned int doors [] = { 0, 1 }; + unsigned int stages [] = { 0x10, 0x20 }; + + struct test_configuration cfg; + cfg.count = 2; + cfg.doors = doors; + cfg.stages = stages; + + return GNUNET_run_messenger_setup ("test_messenger_async_client", &cfg); +} diff --git a/src/messenger/test_messenger_async_p2p.c b/src/messenger/test_messenger_async_p2p.c new file mode 100644 index 000000000..d827aae16 --- /dev/null +++ b/src/messenger/test_messenger_async_p2p.c @@ -0,0 +1,47 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_async_p2p.c + * @author Tobias Frisch + * @brief Test for the messenger service using cadet API. + */ + +#include "testing_messenger_setup.h" + +/** + * 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) +{ + unsigned int doors [] = { 2, 1 }; + unsigned int stages [] = { 0x30, 0x30 }; + + struct test_configuration cfg; + cfg.count = 2; + cfg.doors = doors; + cfg.stages = stages; + + return GNUNET_run_messenger_setup ("test_messenger_async_p2p", &cfg); +} diff --git a/src/messenger/test_messenger_comm0.c b/src/messenger/test_messenger_comm0.c deleted file mode 100644 index 631b5b2c9..000000000 --- a/src/messenger/test_messenger_comm0.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - 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; -} diff --git a/src/messenger/test_messenger_growth.c b/src/messenger/test_messenger_growth.c new file mode 100644 index 000000000..3781f3ee5 --- /dev/null +++ b/src/messenger/test_messenger_growth.c @@ -0,0 +1,47 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_growth.c + * @author Tobias Frisch + * @brief Test for the messenger service using cadet API. + */ + +#include "testing_messenger_setup.h" + +/** + * 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) +{ + unsigned int doors [] = { 0, 1, 1, 1, 1, 1, 1, 1 }; + unsigned int stages [] = { 0x01, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21 }; + + struct test_configuration cfg; + cfg.count = 8; + cfg.doors = doors; + cfg.stages = stages; + + return GNUNET_run_messenger_setup ("test_messenger_growth", &cfg); +} diff --git a/src/messenger/test_messenger_ring.c b/src/messenger/test_messenger_ring.c new file mode 100644 index 000000000..07cfd0c47 --- /dev/null +++ b/src/messenger/test_messenger_ring.c @@ -0,0 +1,47 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_ring.c + * @author Tobias Frisch + * @brief Test for the messenger service using cadet API. + */ + +#include "testing_messenger_setup.h" + +/** + * 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) +{ + unsigned int doors [] = { 8, 1, 2, 3, 4, 5, 6, 7 }; + unsigned int stages [] = { 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21 }; + + struct test_configuration cfg; + cfg.count = 8; + cfg.doors = doors; + cfg.stages = stages; + + return GNUNET_run_messenger_setup ("test_messenger_ring", &cfg); +} diff --git a/src/messenger/test_messenger_server.c b/src/messenger/test_messenger_server.c new file mode 100644 index 000000000..1cf2fcc27 --- /dev/null +++ b/src/messenger/test_messenger_server.c @@ -0,0 +1,47 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_server.c + * @author Tobias Frisch + * @brief Test for the messenger service using cadet API. + */ + +#include "testing_messenger_setup.h" + +/** + * 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) +{ + unsigned int doors [] = { 0, 1, 1, 1, 1, 1, 1, 1 }; + unsigned int stages [] = { 0x01, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }; + + struct test_configuration cfg; + cfg.count = 8; + cfg.doors = doors; + cfg.stages = stages; + + return GNUNET_run_messenger_setup ("test_messenger_server", &cfg); +} diff --git a/src/messenger/test_messenger_sync_client.c b/src/messenger/test_messenger_sync_client.c new file mode 100644 index 000000000..99f26b018 --- /dev/null +++ b/src/messenger/test_messenger_sync_client.c @@ -0,0 +1,47 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_sync_client.c + * @author Tobias Frisch + * @brief Test for the messenger service using cadet API. + */ + +#include "testing_messenger_setup.h" + +/** + * 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) +{ + unsigned int doors [] = { 0, 1 }; + unsigned int stages [] = { 0x01, 0x20 }; + + struct test_configuration cfg; + cfg.count = 2; + cfg.doors = doors; + cfg.stages = stages; + + return GNUNET_run_messenger_setup ("test_messenger_sync_client", &cfg); +} diff --git a/src/messenger/test_messenger_sync_p2p.c b/src/messenger/test_messenger_sync_p2p.c new file mode 100644 index 000000000..77ce280a1 --- /dev/null +++ b/src/messenger/test_messenger_sync_p2p.c @@ -0,0 +1,47 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_sync_p2p.c + * @author Tobias Frisch + * @brief Test for the messenger service using cadet API. + */ + +#include "testing_messenger_setup.h" + +/** + * 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) +{ + unsigned int doors [] = { 2, 1 }; + unsigned int stages [] = { 0x21, 0x21 }; + + struct test_configuration cfg; + cfg.count = 2; + cfg.doors = doors; + cfg.stages = stages; + + return GNUNET_run_messenger_setup ("test_messenger_sync_p2p", &cfg); +} diff --git a/src/messenger/test_messenger_worst_client.c b/src/messenger/test_messenger_worst_client.c new file mode 100644 index 000000000..63826631c --- /dev/null +++ b/src/messenger/test_messenger_worst_client.c @@ -0,0 +1,47 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_worst_client.c + * @author Tobias Frisch + * @brief Test for the messenger service using cadet API. + */ + +#include "testing_messenger_setup.h" + +/** + * 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) +{ + unsigned int doors [] = { 0, 1 }; + unsigned int stages [] = { 0x10, 0x02 }; + + struct test_configuration cfg; + cfg.count = 2; + cfg.doors = doors; + cfg.stages = stages; + + return GNUNET_run_messenger_setup ("test_messenger_worst_client", &cfg); +} diff --git a/src/messenger/test_messenger_worst_p2p.c b/src/messenger/test_messenger_worst_p2p.c new file mode 100644 index 000000000..c89288eea --- /dev/null +++ b/src/messenger/test_messenger_worst_p2p.c @@ -0,0 +1,47 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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_worst_p2p.c + * @author Tobias Frisch + * @brief Test for the messenger service using cadet API. + */ + +#include "testing_messenger_setup.h" + +/** + * 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) +{ + unsigned int doors [] = { 2, 1 }; + unsigned int stages [] = { 0x12, 0x12 }; + + struct test_configuration cfg; + cfg.count = 2; + cfg.doors = doors; + cfg.stages = stages; + + return GNUNET_run_messenger_setup ("test_messenger_worst_p2p", &cfg); +} diff --git a/src/messenger/testing_messenger_barrier.c b/src/messenger/testing_messenger_barrier.c new file mode 100644 index 000000000..618d255b7 --- /dev/null +++ b/src/messenger/testing_messenger_barrier.c @@ -0,0 +1,170 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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/testing_messenger_barrier.c + * @author Tobias Frisch + * @brief Pseudo-barriers for simple event handling + */ + +#include "testing_messenger_barrier.h" + +struct GNUNET_BarrierHandle +{ + unsigned int requirement; + GNUNET_BarrierStatusCallback cb; + void *cls; + + struct GNUNET_BarrierWaitHandle *head; + struct GNUNET_BarrierWaitHandle *tail; + + struct GNUNET_SCHEDULER_Task* task; +}; + +struct GNUNET_BarrierHandle* +GNUNET_init_barrier (unsigned int requirement, + GNUNET_BarrierStatusCallback cb, + void *cb_cls) +{ + if (0 == requirement) + return NULL; + + struct GNUNET_BarrierHandle *barrier = GNUNET_new(struct GNUNET_BarrierHandle); + + if (!barrier) + return NULL; + + barrier->requirement = requirement; + barrier->cb = cb; + barrier->cls = cb_cls; + barrier->head = NULL; + barrier->tail = NULL; + barrier->task = NULL; + + return barrier; +} + +static void +exit_status (struct GNUNET_BarrierHandle *barrier, int status); + +static void +cancel_barrier (void *cls) +{ + exit_status ((struct GNUNET_BarrierHandle*) cls, GNUNET_SYSERR); +} + +static void +complete_barrier (void *cls) +{ + exit_status ((struct GNUNET_BarrierHandle*) cls, GNUNET_OK); +} + +void +GNUNET_cancel_barrier (struct GNUNET_BarrierHandle *barrier) +{ + if ((!barrier) || (barrier->task)) + return; + + barrier->task = GNUNET_SCHEDULER_add_now(cancel_barrier, barrier); +} + +struct GNUNET_BarrierWaitHandle +{ + GNUNET_BarrierWaitStatusCallback cb; + void *cls; + + struct GNUNET_BarrierWaitHandle *prev; + struct GNUNET_BarrierWaitHandle *next; + + struct GNUNET_BarrierHandle *barrier; +}; + +static void +exit_status (struct GNUNET_BarrierHandle *barrier, int status) +{ + struct GNUNET_BarrierWaitHandle *waiting = barrier->head; + while (waiting) + { + struct GNUNET_BarrierWaitHandle *current = waiting; + + if (current->cb) + current->cb(current->cls, current, status); + + waiting = waiting->next; + + GNUNET_CONTAINER_DLL_remove(barrier->head, barrier->tail, current); + GNUNET_free(current); + } + + if (barrier->cb) + barrier->cb(barrier->cls, barrier, status); + + GNUNET_free(barrier); +} + +struct GNUNET_BarrierWaitHandle* +GNUNET_wait_barrier (struct GNUNET_BarrierHandle *barrier, + GNUNET_BarrierWaitStatusCallback cb, + void *cb_cls) +{ + if ((!barrier) || (0 == barrier->requirement)) + return NULL; + + struct GNUNET_BarrierWaitHandle *waiting = GNUNET_new(struct GNUNET_BarrierWaitHandle); + + if (!waiting) + return NULL; + + waiting->cb = cb; + waiting->cls = cb_cls; + waiting->prev = NULL; + waiting->next = NULL; + waiting->barrier = barrier; + + GNUNET_CONTAINER_DLL_insert_tail(barrier->head, barrier->tail, waiting); + barrier->requirement--; + + if ((barrier->requirement == 0) && (!barrier->task)) + barrier->task = GNUNET_SCHEDULER_add_now(complete_barrier, barrier); + + return waiting; +} + +void +GNUNET_cancel_wait_barrier (struct GNUNET_BarrierWaitHandle *waiting) +{ + if (!waiting) + return; + + struct GNUNET_BarrierHandle *barrier = waiting->barrier; + + if (!barrier) + return; + + if ((barrier->requirement == 0) && (barrier->task)) + { + GNUNET_SCHEDULER_cancel(barrier->task); + barrier->task = NULL; + } + + barrier->requirement++; + GNUNET_CONTAINER_DLL_remove(barrier->head, barrier->tail, waiting); + + GNUNET_free(waiting); +} diff --git a/src/messenger/testing_messenger_barrier.h b/src/messenger/testing_messenger_barrier.h new file mode 100644 index 000000000..3062a393a --- /dev/null +++ b/src/messenger/testing_messenger_barrier.h @@ -0,0 +1,131 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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/testing_messenger_barrier.h + * @author Tobias Frisch + * @brief Pseudo-barriers for simple event handling + */ + +#ifndef GNUNET_TESTING_MESSENGER_BARRIER_H_ +#define GNUNET_TESTING_MESSENGER_BARRIER_H_ + +#include "platform.h" +#include "gnunet_util_lib.h" + +/** + * Handle for pseudo-barrier + */ +struct GNUNET_BarrierHandle; + + +/** + * Functions of this type are to be given as callback argument to + * GNUNET_init_barrier(). The callback will be called when status + * information is available for the pseudo-barrier. + * + * @param cls the closure given to GNUNET_init_barrier() + * @param barrier the pseudo-barrier handle + * @param status status of the pseudo-barrier. The pseudo-barrier is removed + * once it has been crossed or an error occurs while processing it. + * Therefore it is invalid to call GNUNET_cancel_barrier() on a + * crossed or errored pseudo-barrier. + */ +typedef void +(*GNUNET_BarrierStatusCallback) (void *cls, + struct GNUNET_BarrierHandle *barrier, + int status); + + +/** + * Initialise a pseudo-barrier and call the given callback when the required + * amount of peers (requirement) reach the pseudo-barrier OR upon error. + * + * @param requirement the amount of peers that is required to reach the + * pseudo-barrier. Peers signal reaching a pseudo-barrier by calling + * GNUNET_wait_barrier(). + * @param cb the callback to call when the pseudo-barrier is reached or upon + * error. Can be NULL. + * @param cls closure for the above callback + * @return pseudo-barrier handle; NULL upon error + */ +struct GNUNET_BarrierHandle* +GNUNET_init_barrier (unsigned int requirement, + GNUNET_BarrierStatusCallback cb, + void *cb_cls); + + +/** + * Cancel a pseudo-barrier. + * + * @param barrier the pseudo-barrier handle + */ +void +GNUNET_cancel_barrier (struct GNUNET_BarrierHandle *barrier); + + +/** + * Handle for pseudo-barrier wait + */ +struct GNUNET_BarrierWaitHandle; + + +/** + * Functions of this type are to be given as acallback argument to + * GNUNET_wait_barrier(). The callback will be called when the pseudo-barrier + * corresponding given in GNUNET_wait_barrier() is crossed or cancelled. + * + * @param cls closure pointer given to GNUNET_wait_barrier() + * @param waiting the pseudo-barrier wait handle + * @param status #GNUNET_SYSERR in case of error while waiting for the + * pseudo-barrier; #GNUNET_OK if the pseudo-barrier is crossed + */ +typedef void +(*GNUNET_BarrierWaitStatusCallback) (void *cls, + struct GNUNET_BarrierWaitHandle *waiting, + int status); + + +/** + * Wait for a pseudo-barrier to be crossed. This function should be called for + * the peers which have been started by the testbed. + * + * @param barrier the pseudo-barrier handle + * @param cb the pseudo-barrier wait callback + * @param cls the closure for the above callback + * @return pseudo-barrier wait handle which can be used to cancel the waiting + * at anytime before the callback is called. NULL upon error. + */ +struct GNUNET_BarrierWaitHandle* +GNUNET_wait_barrier (struct GNUNET_BarrierHandle *barrier, + GNUNET_BarrierWaitStatusCallback cb, + void *cb_cls); + + +/** + * Cancel a pseudo-barrier wait handle. Should not be called in or after the + * callback given to GNUNET_wait_barrier() has been called. + * + * @param waiting the pseudo-barrier wait handle + */ +void +GNUNET_cancel_wait_barrier (struct GNUNET_BarrierWaitHandle *waiting); + + +#endif /* GNUNET_TESTING_MESSENGER_BARRIER_H_ */ diff --git a/src/messenger/testing_messenger_setup.c b/src/messenger/testing_messenger_setup.c new file mode 100644 index 000000000..98241fa08 --- /dev/null +++ b/src/messenger/testing_messenger_setup.c @@ -0,0 +1,528 @@ +/* + This file is part of GNUnet. + Copyright (C) 2020--2021 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/testing_messenger_barrier.c + * @author Tobias Frisch + * @brief A simple test-case setup for the messenger service + */ + +#include "testing_messenger_setup.h" + +#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" +#include "testing_messenger_barrier.h" + +#define TEST_ROOM "test" +#define TEST_NAME "tester" + +struct test_properties; + +struct test_peer { + struct test_properties *props; + unsigned int num; + + struct GNUNET_SCHEDULER_Task *op_task; + struct GNUNET_TESTBED_Operation *op; + + struct GNUNET_TESTBED_Peer *peer; + struct GNUNET_PeerIdentity peer_id; + struct GNUNET_BarrierWaitHandle *wait; + + struct GNUNET_MESSENGER_Handle *handle; + struct GNUNET_MESSENGER_Room *room; + + unsigned int peer_messages; + + const char *message; +}; + +struct test_properties { + const struct test_configuration *cfg; + + unsigned int num_hosts; + + struct GNUNET_SCHEDULER_Task *die_task; + struct GNUNET_SCHEDULER_Task *end_task; + + struct GNUNET_BarrierHandle *barrier; + + struct test_peer *peers; + unsigned int num_peer; + + int status; +}; + +static void +shutdown_cb (void *cls) +{ + struct test_properties *properties = cls; + + + for (unsigned int i = 0; i < properties->num_peer; i++) + { + struct test_peer *peer = &properties->peers[i]; + + GNUNET_assert(peer != NULL); + + if (peer->op_task) + GNUNET_SCHEDULER_cancel(peer->op_task); + + peer->op_task = NULL; + + if (peer->op) + GNUNET_TESTBED_operation_done (peer->op); + + peer->op = NULL; + + if (peer->wait) + GNUNET_cancel_wait_barrier(peer->wait); + + peer->wait = NULL; + + if (peer->room) + GNUNET_MESSENGER_close_room (peer->room); + + peer->room = NULL; + + if (peer->handle) + GNUNET_MESSENGER_disconnect (peer->handle); + + peer->handle = NULL; + } + + if (properties->die_task) + GNUNET_SCHEDULER_cancel(properties->die_task); + + properties->die_task = NULL; + properties->end_task = NULL; + + if (properties->barrier) + GNUNET_cancel_barrier(properties->barrier); + + properties->barrier = NULL; +} + + + +static void +end_cb (void *cls) +{ + struct test_properties *properties = cls; + + GNUNET_assert(properties != NULL); + + properties->die_task = NULL; + + int status = 0; + + for (unsigned int i = 0; i < properties->num_peer; i++) + { + struct test_peer *peer = &properties->peers[i]; + + GNUNET_assert(peer != NULL); + + const int members = GNUNET_MESSENGER_iterate_members(peer->room, NULL, NULL); + + GNUNET_assert (members >= 0); + + if (peer->props->num_peer != (unsigned int) members) + { + fprintf (stderr, "Testcase failed (members: %d/%u).\n", members, peer->props->num_peer); + status = 1; + break; + } + } + + GNUNET_SCHEDULER_shutdown (); + + properties->status = status; +} + +static void +end_badly_cb (void *cls) +{ + struct test_properties *properties = cls; + + GNUNET_assert(properties != NULL); + + fprintf (stderr, "Testcase failed (timeout).\n"); + + end_cb (properties); + + properties->status = 1; +} + +static void +end_operation_cb (void *cls) +{ + struct test_peer *peer = cls; + + GNUNET_assert(peer != NULL); + + peer->op_task = NULL; + + fprintf (stderr, "Testcase failed (operation: '%s').\n", peer->message); + + GNUNET_SCHEDULER_shutdown (); +} + +static void +end_error_cb (void *cls) +{ + struct test_peer *peer = cls; + + GNUNET_assert(peer != NULL); + + peer->op_task = NULL; + + fprintf (stderr, "Testcase failed (error: '%s').\n", peer->message); + GNUNET_free (peer); + + GNUNET_SCHEDULER_shutdown (); +} + +static void +barrier2_wait_cb (void *cls, struct GNUNET_BarrierWaitHandle *waiting, int status) +{ + struct test_peer *peer = cls; + + GNUNET_assert(peer != NULL); + + if (peer->wait == waiting) + peer->wait = NULL; +} + +static void +barrier_wait_cb (void *cls, struct GNUNET_BarrierWaitHandle *waiting, int status) +{ + struct test_peer *peer = cls; + + GNUNET_assert(peer != NULL); + + if (peer->wait == waiting) + peer->wait = NULL; + + if (0 != (peer->props->cfg->stages[peer->num - 1] & 0x02)) + { + unsigned int door = peer->props->cfg->doors[peer->num - 1]; + + if (door == 0) + door = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, peer->props->cfg->count); + else + door = door - 1; + + struct GNUNET_HashCode hash; + GNUNET_CRYPTO_hash (TEST_ROOM, sizeof(TEST_ROOM), &hash); + + struct GNUNET_MESSENGER_Room *room; + room = GNUNET_MESSENGER_enter_room(peer->handle, &(peer->props->peers[door].peer_id), &hash); + + if (peer->room) + GNUNET_assert(room == peer->room); + else + GNUNET_assert(room != NULL); + + peer->room = room; + } +} + +/** + * Function called whenever a message is received or sent. + * + * @param cls Closure + * @param room Room + * @param sender Sender + * @param message Message + * @param hash Hash of message + * @param flags Flags of message + */ +static void +on_message (void *cls, struct GNUNET_MESSENGER_Room *room, const struct GNUNET_MESSENGER_Contact *sender, + const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash, + enum GNUNET_MESSENGER_MessageFlags flags) +{ + struct test_peer *peer = cls; + + GNUNET_assert(peer != NULL); + + fprintf (stderr, "Peer: %s; [%s] Message: %s (%s)\n", + GNUNET_i2s(&(peer->peer_id)), + GNUNET_sh2s(&(message->header.sender_id)), + GNUNET_MESSENGER_name_of_kind(message->header.kind), + GNUNET_h2s(hash)); + + if (GNUNET_MESSENGER_KIND_PEER == message->header.kind) + peer->peer_messages++; + + if (peer->props->num_hosts == peer->peer_messages) + peer->wait = GNUNET_wait_barrier (peer->props->barrier, &barrier2_wait_cb, peer); + else if (peer->props->num_hosts < peer->peer_messages) + { + if (peer->wait) + GNUNET_cancel_wait_barrier(peer->wait); + + peer->wait = NULL; + + if (peer->op_task) + GNUNET_SCHEDULER_cancel(peer->op_task); + + peer->message = "peer"; + peer->op_task = GNUNET_SCHEDULER_add_now (&end_operation_cb, peer); + } +} + +static void +second_stage (void *cls) +{ + struct test_peer *peer = cls; + + GNUNET_assert(peer != NULL); + + peer->op_task = NULL; + + struct GNUNET_HashCode hash; + GNUNET_CRYPTO_hash (TEST_ROOM, sizeof(TEST_ROOM), &hash); + + if (0 != (peer->props->cfg->stages[peer->num - 1] & 0x10)) + { + struct GNUNET_MESSENGER_Room *room; + room = GNUNET_MESSENGER_open_room (peer->handle, &hash); + + if (peer->room) + GNUNET_assert(room == peer->room); + else + GNUNET_assert(room != NULL); + + peer->room = room; + } + + if (0 != (peer->props->cfg->stages[peer->num - 1] & 0x20)) + { + unsigned int door = peer->props->cfg->doors[peer->num - 1]; + + if (door == 0) + door = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, peer->props->cfg->count); + else + door = door - 1; + + struct GNUNET_MESSENGER_Room *room; + room = GNUNET_MESSENGER_enter_room(peer->handle, &(peer->props->peers[door].peer_id), &hash); + + if (peer->room) + GNUNET_assert(room == peer->room); + else + GNUNET_assert(room != NULL); + + peer->room = room; + } +} + +static void +on_peer (void *cb_cls, struct GNUNET_TESTBED_Operation *op, const struct GNUNET_TESTBED_PeerInformation *pinfo, + const char *emsg) +{ + struct test_peer *peer = cb_cls; + + GNUNET_assert(peer != NULL); + + if (emsg) + { + peer->message = GNUNET_strdup(emsg); + peer->op_task = GNUNET_SCHEDULER_add_now (&end_error_cb, peer); + return; + } + + if (!pinfo) + { + peer->message = "info"; + peer->op_task = GNUNET_SCHEDULER_add_now (&end_operation_cb, peer); + return; + } + + if (pinfo->pit != GNUNET_TESTBED_PIT_CONFIGURATION) + { + peer->message = "config"; + peer->op_task = GNUNET_SCHEDULER_add_now (&end_operation_cb, peer); + return; + } + + peer->handle = GNUNET_MESSENGER_connect (pinfo->result.cfg, TEST_NAME, NULL, NULL, &on_message, peer); + + GNUNET_assert(GNUNET_OK == GNUNET_CRYPTO_get_peer_identity( + pinfo->result.cfg, &(peer->peer_id) + )); + + if (0 != (peer->props->cfg->stages[peer->num - 1] & 0x01)) + { + struct GNUNET_HashCode hash; + GNUNET_CRYPTO_hash (TEST_ROOM, sizeof(TEST_ROOM), &hash); + + peer->room = GNUNET_MESSENGER_open_room (peer->handle, &hash); + + GNUNET_assert(peer->room != NULL); + } + else + peer->room = NULL; + + peer->wait = GNUNET_wait_barrier (peer->props->barrier, &barrier_wait_cb, peer); +} + +/** + * 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) +{ + struct test_properties *properties = cls; + + GNUNET_assert(properties != NULL); + + if (GNUNET_TESTBED_ET_PEER_START != event->type) + { + fprintf (stderr, "Testcase failed (operation: 'start').\n"); + + GNUNET_SCHEDULER_shutdown (); + return; + } + + struct test_peer *peer = &(properties->peers[properties->num_peer++]); + + peer->props = properties; + peer->num = properties->num_peer; + + peer->peer = event->details.peer_start.peer; + peer->op = GNUNET_TESTBED_peer_get_information (peer->peer, GNUNET_TESTBED_PIT_CONFIGURATION, on_peer, peer); +} + +static void +barrier2_cb (void *cls, struct GNUNET_BarrierHandle *barrier, int status) +{ + struct test_properties *properties = cls; + + GNUNET_assert(properties != NULL); + + if (properties->barrier == barrier) + properties->barrier = NULL; + + if (GNUNET_SYSERR == status) + { + fprintf (stderr, "Testcase failed (operation: 'barrier2').\n"); + + GNUNET_SCHEDULER_shutdown (); + return; + } + else if (GNUNET_OK == status) + { + if (properties->die_task) + GNUNET_SCHEDULER_cancel(properties->die_task); + + properties->die_task = GNUNET_SCHEDULER_add_delayed ( + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, properties->cfg->count), + &end_cb, properties + ); + } +} + +static void +barrier_cb (void *cls, struct GNUNET_BarrierHandle *barrier, int status) +{ + struct test_properties *properties = cls; + + GNUNET_assert(properties != NULL); + + if (properties->barrier == barrier) + properties->barrier = NULL; + else if (!properties->barrier) + return; + + if (properties->num_peer != properties->cfg->count) + { + fprintf (stderr, "Testcase failed (operation: 'process').\n"); + + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (GNUNET_SYSERR == status) + { + fprintf (stderr, "Testcase failed (operation: 'barrier').\n"); + + GNUNET_SCHEDULER_shutdown (); + return; + } + else if (GNUNET_OK == status) + { + properties->barrier = GNUNET_init_barrier (properties->num_peer, &barrier2_cb, properties); + + for (unsigned int i = 0; i < properties->num_peer; i++) + properties->peers[i].op_task = GNUNET_SCHEDULER_add_now (&second_stage, &(properties->peers[i])); + } +} + +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) +{ + struct test_properties *properties = cls; + + GNUNET_assert(properties != NULL); + + properties->end_task = GNUNET_SCHEDULER_add_shutdown(&shutdown_cb, properties); + properties->die_task = GNUNET_SCHEDULER_add_delayed ( + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, properties->cfg->count * 5), + &end_badly_cb, properties + ); +} + +int +GNUNET_run_messenger_setup (const char* test_name, const struct test_configuration *cfg) +{ + struct test_properties properties; + memset(&properties, 0, sizeof(properties)); + + properties.cfg = cfg; + properties.peers = GNUNET_new_array(cfg->count, struct test_peer); + + for (unsigned int i = 0; i < cfg->count; i++) + if (0 != (cfg->stages[i] & 0x11)) + properties.num_hosts++; + + properties.status = 1; + properties.barrier = GNUNET_init_barrier (cfg->count, &barrier_cb, &properties); + + if (GNUNET_OK != GNUNET_TESTBED_test_run (test_name, "test_messenger_api.conf", + cfg->count, + (1LL << GNUNET_TESTBED_ET_PEER_START), + &run, &properties, + &init, &properties)) + return 1; + + GNUNET_free(properties.peers); + + return properties.status; +} diff --git a/src/messenger/testing_messenger_setup.h b/src/messenger/testing_messenger_setup.h new file mode 100644 index 000000000..5e6b5d461 --- /dev/null +++ b/src/messenger/testing_messenger_setup.h @@ -0,0 +1,39 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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/testing_messenger_setup.h + * @author Tobias Frisch + * @brief A simple test-case setup for the messenger service + */ + +#ifndef GNUNET_TESTING_MESSENGER_SETUP_H_ +#define GNUNET_TESTING_MESSENGER_SETUP_H_ + +struct test_configuration +{ + unsigned int count; + unsigned int *doors; + unsigned int *stages; +}; + +int +GNUNET_run_messenger_setup (const char* test_name, const struct test_configuration *cfg); + +#endif /* GNUNET_TESTING_MESSENGER_SETUP_H_ */ -- cgit v1.2.3