/* This file is part of GNUnet. Copyright (C) 2020--2024 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . SPDX-License-Identifier: AGPL3.0-or-later */ /** * @author Tobias Frisch * @file src/messenger/gnunet-service-messenger_room.c * @brief GNUnet MESSENGER service */ #include "platform.h" #include "gnunet-service-messenger_room.h" #include "gnunet-service-messenger_basement.h" #include "gnunet-service-messenger_member.h" #include "gnunet-service-messenger_member_session.h" #include "gnunet-service-messenger_sender_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_service.h" #include "gnunet-service-messenger_tunnel.h" #include "messenger_api_util.h" static void idle_request_room_messages (void *cls); struct GNUNET_MESSENGER_SrvRoom* create_srv_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key) { GNUNET_assert ((handle) && (key)); struct GNUNET_MESSENGER_SrvRoom *room = GNUNET_new (struct GNUNET_MESSENGER_SrvRoom); room->service = handle->service; room->host = handle; room->port = NULL; GNUNET_memcpy (&(room->key), key, sizeof(struct GNUNET_HashCode)); room->tunnels = GNUNET_CONTAINER_multipeermap_create (8, GNUNET_NO); init_peer_store (get_srv_room_peer_store (room), room->service); init_member_store (get_srv_room_member_store (room), room); init_message_store (get_srv_room_message_store (room)); init_operation_store (get_srv_room_operation_store (room), room); init_list_tunnels (&(room->basement)); init_message_state (&(room->state)); room->peer_message = NULL; init_list_messages (&(room->handling)); room->idle = NULL; if (room->service->dir) load_srv_room (room); room->idle = GNUNET_SCHEDULER_add_with_priority ( GNUNET_SCHEDULER_PRIORITY_IDLE, idle_request_room_messages, room); return room; } static enum GNUNET_GenericReturnValue iterate_destroy_tunnels (void *cls, const struct GNUNET_PeerIdentity *key, void *value) { struct GNUNET_MESSENGER_SrvTunnel *tunnel = value; destroy_tunnel (tunnel); return GNUNET_YES; } static void close_srv_room (struct GNUNET_MESSENGER_SrvRoom *room); static void handle_room_messages (struct GNUNET_MESSENGER_SrvRoom *room); void destroy_srv_room (struct GNUNET_MESSENGER_SrvRoom *room, enum GNUNET_GenericReturnValue deletion) { GNUNET_assert (room); if (room->idle) { GNUNET_SCHEDULER_cancel (room->idle); room->idle = NULL; } close_srv_room (room); GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, iterate_destroy_tunnels, NULL); handle_room_messages (room); if (! (room->service->dir)) goto skip_saving; if (GNUNET_YES == deletion) remove_srv_room (room); else save_srv_room (room); skip_saving: clear_peer_store (get_srv_room_peer_store (room)); clear_member_store (get_srv_room_member_store (room)); clear_message_store (get_srv_room_message_store (room)); clear_operation_store (get_srv_room_operation_store (room)); GNUNET_CONTAINER_multipeermap_destroy (room->tunnels); clear_list_tunnels (&(room->basement)); clear_message_state (&(room->state)); if (room->peer_message) GNUNET_free (room->peer_message); GNUNET_free (room); } struct GNUNET_MESSENGER_PeerStore* get_srv_room_peer_store (struct GNUNET_MESSENGER_SrvRoom *room) { GNUNET_assert (room); return &(room->peer_store); } struct GNUNET_MESSENGER_MemberStore* get_srv_room_member_store (struct GNUNET_MESSENGER_SrvRoom *room) { GNUNET_assert (room); return &(room->member_store); } struct GNUNET_MESSENGER_MessageStore* get_srv_room_message_store (struct GNUNET_MESSENGER_SrvRoom *room) { GNUNET_assert (room); return &(room->message_store); } struct GNUNET_MESSENGER_OperationStore* get_srv_room_operation_store (struct GNUNET_MESSENGER_SrvRoom *room) { GNUNET_assert (room); return &(room->operation_store); } static enum GNUNET_GenericReturnValue send_room_info (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, struct GNUNET_MESSENGER_SrvTunnel *tunnel) { if ((! handle) || (! is_tunnel_connected (tunnel))) return GNUNET_NO; return send_tunnel_message (tunnel, handle, create_message_info ( room->service)); } static void* callback_room_connect (void *cls, struct GNUNET_CADET_Channel *channel, const struct GNUNET_PeerIdentity *source) { struct GNUNET_MESSENGER_SrvRoom *room = cls; struct GNUNET_MESSENGER_SrvTunnel *tunnel = create_tunnel (room, source); if ((tunnel) && (GNUNET_OK != GNUNET_CONTAINER_multipeermap_put (room->tunnels, source, tunnel, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE))) { destroy_tunnel (tunnel); tunnel = NULL; } if (! tunnel) { delayed_disconnect_channel (channel); return NULL; } bind_tunnel (tunnel, channel); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "New tunnel in room (%s) established to peer: %s\n", GNUNET_h2s (get_srv_room_key (room)), GNUNET_i2s (source)); 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 enum GNUNET_GenericReturnValue join_room (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, struct GNUNET_MESSENGER_Member *member, const struct GNUNET_ShortHashCode *id) { GNUNET_assert ((room) && (handle) && (member)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Joining room: %s (%s)\n", GNUNET_h2s ( get_srv_room_key (room)), GNUNET_sh2s (get_member_id (member))); const struct GNUNET_ShortHashCode *member_id = get_member_id (member); if (GNUNET_OK != change_srv_handle_member_id (handle, get_srv_room_key (room), member_id)) return GNUNET_NO; enum GNUNET_GenericReturnValue reset; if ((! id) || (0 != GNUNET_memcmp (id, member_id))) reset = GNUNET_YES; else reset = GNUNET_NO; notify_srv_handle_member_id (handle, room, member_id, reset); return GNUNET_YES; } static enum GNUNET_GenericReturnValue join_room_locally (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle) { const struct GNUNET_ShortHashCode *member_id = get_srv_handle_member_id ( handle, get_srv_room_key (room)); struct GNUNET_MESSENGER_MemberStore *member_store = get_srv_room_member_store (room); struct GNUNET_MESSENGER_Member *member = add_store_member (member_store, member_id); if (GNUNET_NO == join_room (room, handle, member, member_id)) return GNUNET_NO; return GNUNET_YES; } extern enum GNUNET_GenericReturnValue check_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header); extern void handle_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header); extern void callback_tunnel_disconnect (void *cls, const struct GNUNET_CADET_Channel *channel); enum GNUNET_GenericReturnValue open_srv_room (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle) { GNUNET_assert (room); if (handle) room->host = handle; if (room->port) { if (! handle) return GNUNET_YES; return join_room_locally (room, handle); } struct GNUNET_CADET_Handle *cadet = get_srv_room_cadet (room); const struct GNUNET_HashCode *key = get_srv_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 () }; struct GNUNET_HashCode port; convert_messenger_key_to_port (key, &port); room->port = GNUNET_CADET_open_port (cadet, &port, callback_room_connect, room, NULL, callback_tunnel_disconnect, handlers); if (room->port) GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Port of room (%s) was opened!\n", GNUNET_h2s (get_srv_room_key (room))); else GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Port of room (%s) could not be opened!\n", GNUNET_h2s (get_srv_room_key (room))); if (! handle) goto complete_opening; const struct GNUNET_ShortHashCode *member_id = get_srv_handle_member_id ( handle, get_srv_room_key (room)); struct GNUNET_MESSENGER_MemberStore *member_store = get_srv_room_member_store (room); struct GNUNET_MESSENGER_Member *member = add_store_member (member_store, member_id); if ((GNUNET_NO == join_room (room, handle, member, member_id)) && (room->port)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "You could not join the room, therefore it keeps closed!\n"); close_srv_room (room); return GNUNET_NO; } complete_opening: if (! room->port) return GNUNET_NO; return send_srv_room_message (room, handle, create_message_peer ( room->service)); } static void close_srv_room (struct GNUNET_MESSENGER_SrvRoom *room) { GNUNET_assert (room); if (! room->port) return; struct GNUNET_PeerIdentity peer; if ((room->peer_message) && (GNUNET_OK == get_service_peer_identity (room->service, &peer))) send_srv_room_message (room, room->host, create_message_miss (&peer)); GNUNET_CADET_close_port (room->port); room->port = NULL; } enum GNUNET_GenericReturnValue enter_srv_room_at (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_PeerIdentity *door) { GNUNET_assert ((room) && (handle) && (door)); 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) { tunnel = create_tunnel (room, door); if (GNUNET_OK != GNUNET_CONTAINER_multipeermap_put (room->tunnels, door, tunnel, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "You could not connect to that door!\n"); destroy_tunnel (tunnel); return GNUNET_NO; } } if (GNUNET_SYSERR == connect_tunnel (tunnel)) { 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; } return join_room_locally (room, handle); } static void sign_srv_room_message_by_peer (const void *cls, struct GNUNET_MESSENGER_Message *message, uint16_t length, char *buffer, const struct GNUNET_HashCode *hash) { const struct GNUNET_MESSENGER_SrvHandle *handle = cls; GNUNET_assert ((handle) && (handle->service)); sign_message_by_peer (message, length, buffer, hash, handle->service->config); } struct GNUNET_MQ_Envelope* pack_srv_room_message (const struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_MESSENGER_SrvHandle *handle, struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash, enum GNUNET_MESSENGER_PackMode mode) { GNUNET_assert ((room) && (handle) && (message) && (hash)); if (GNUNET_YES != is_peer_message (message)) return pack_message (message, hash, NULL, mode, NULL); message->header.timestamp = GNUNET_TIME_absolute_hton ( GNUNET_TIME_absolute_get ()); struct GNUNET_PeerIdentity peer; if (GNUNET_OK != get_service_peer_identity (handle->service, &peer)) return NULL; convert_peer_identity_to_id (&peer, &(message->header.sender_id)); get_message_state_chain_hash (&(room->state), &(message->header.previous)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Packing message with peer signature: %s\n", GNUNET_sh2s (&(message->header.sender_id))); message->header.signature.type = htonl (GNUNET_PUBLIC_KEY_TYPE_EDDSA); return pack_message (message, hash, sign_srv_room_message_by_peer, mode, handle); } struct GNUNET_MESSENGER_ClosureSendRoom { struct GNUNET_MESSENGER_SrvRoom *room; struct GNUNET_MESSENGER_SrvHandle *handle; struct GNUNET_MESSENGER_SrvTunnel *exclude; struct GNUNET_MESSENGER_Message *message; struct GNUNET_HashCode *hash; enum GNUNET_GenericReturnValue packed; }; static enum GNUNET_GenericReturnValue iterate_send_room_message (void *cls, const struct GNUNET_PeerIdentity *key, void *value) { struct GNUNET_MESSENGER_SrvTunnel *tunnel = value; if ((! is_tunnel_connected (tunnel)) || (get_tunnel_messenger_version (tunnel) < GNUNET_MESSENGER_VERSION)) return GNUNET_YES; struct GNUNET_MESSENGER_ClosureSendRoom *closure = cls; if (tunnel == closure->exclude) return GNUNET_YES; struct GNUNET_MQ_Envelope *env = NULL; if (closure->packed == GNUNET_NO) { env = pack_srv_room_message (closure->room, closure->handle, closure->message, closure->hash, GNUNET_MESSENGER_PACK_MODE_ENVELOPE); if (env) closure->packed = GNUNET_YES; } else env = pack_message (closure->message, NULL, NULL, GNUNET_MESSENGER_PACK_MODE_ENVELOPE, NULL); if (env) send_tunnel_envelope (tunnel, env, closure->hash); return GNUNET_YES; } enum GNUNET_GenericReturnValue 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, const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash); enum GNUNET_GenericReturnValue send_srv_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, struct GNUNET_MESSENGER_Message *message) { GNUNET_assert ((room) && (handle)); if (! message) return GNUNET_NO; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message from handle in room: %s (%s)\n", GNUNET_h2s (&(room->key)), GNUNET_MESSENGER_name_of_kind (message->header.kind)); struct GNUNET_HashCode hash; struct GNUNET_MESSENGER_ClosureSendRoom closure; closure.room = room; closure.handle = handle; closure.exclude = NULL; closure.message = message; closure.hash = &hash; closure.packed = GNUNET_NO; GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, iterate_send_room_message, &closure); if (GNUNET_NO == closure.packed) pack_srv_room_message (room, handle, message, &hash, GNUNET_MESSENGER_PACK_MODE_UNKNOWN); enum GNUNET_GenericReturnValue new_message; new_message = update_room_message (room, message, &hash); if (GNUNET_YES != new_message) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Sending duplicate message failed!\n"); return GNUNET_SYSERR; } switch (message->header.kind) { case GNUNET_MESSENGER_KIND_JOIN: send_message_join (room, handle, message, &hash); break; case GNUNET_MESSENGER_KIND_KEY: send_message_key (room, handle, message, &hash); break; case GNUNET_MESSENGER_KIND_PEER: send_message_peer (room, handle, message, &hash); break; case GNUNET_MESSENGER_KIND_ID: send_message_id (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, message, &hash); return GNUNET_YES; } void forward_srv_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel, 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; GNUNET_memcpy (&message_hash, hash, sizeof(struct GNUNET_HashCode)); closure.room = room; closure.handle = NULL; closure.exclude = tunnel; closure.message = message; closure.hash = &message_hash; closure.packed = GNUNET_YES; GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, iterate_send_room_message, &closure); } void check_srv_room_peer_status (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel) { if (! room->peer_message) return; struct GNUNET_MESSENGER_MessageStore *message_store = get_srv_room_message_store (room); const struct GNUNET_MESSENGER_Message *message = get_store_message ( message_store, room->peer_message); if (! message) { GNUNET_free (room->peer_message); room->peer_message = NULL; return; } if (tunnel) forward_tunnel_message (tunnel, message, room->peer_message); } void merge_srv_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle) { GNUNET_assert (room); if (! handle) return; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Merging messages by handle in room: %s\n", GNUNET_h2s (&(room->key))); const struct GNUNET_HashCode *hash; merge_next: hash = get_message_state_merge_hash (&(room->state)); if (! hash) return; send_srv_room_message (room, handle, create_message_merge (hash)); goto merge_next; } void callback_room_deletion (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_HashCode *hash) { if (GNUNET_OK != delete_store_message (get_srv_room_message_store (room), hash)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Deletion of message failed! (%s)\n", GNUNET_h2s (hash)); return; } } void callback_room_merge (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_HashCode *hash) { if (! room->host) return; send_srv_room_message (room, room->host, create_message_merge (hash)); } enum GNUNET_GenericReturnValue delete_srv_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session, const struct GNUNET_HashCode *hash, const struct GNUNET_TIME_Relative delay) { GNUNET_assert ((room) && (session) && (hash)); const struct GNUNET_TIME_Relative forever = GNUNET_TIME_relative_get_forever_ (); if (0 == GNUNET_memcmp (&forever, &delay)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Deletion is delayed forever: operation is impossible!\n"); return GNUNET_SYSERR; } struct GNUNET_MESSENGER_MessageStore *message_store = get_srv_room_message_store (room); const struct GNUNET_MESSENGER_Message *message = get_store_message ( message_store, hash); if (! message) return GNUNET_YES; if (GNUNET_YES != check_member_session_history (session, hash, GNUNET_YES)) { 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)); return GNUNET_NO; } struct GNUNET_MESSENGER_OperationStore *operation_store = get_srv_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; } struct GNUNET_CADET_Handle* get_srv_room_cadet (struct GNUNET_MESSENGER_SrvRoom *room) { GNUNET_assert (room); return room->service->cadet; } const struct GNUNET_HashCode* get_srv_room_key (const struct GNUNET_MESSENGER_SrvRoom *room) { GNUNET_assert (room); return &(room->key); } const struct GNUNET_MESSENGER_SrvTunnel* get_srv_room_tunnel (const struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_PeerIdentity *peer) { GNUNET_assert ((room) && (peer)); return GNUNET_CONTAINER_multipeermap_get (room->tunnels, peer); } static enum GNUNET_GenericReturnValue request_room_message_step (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_HashCode *hash, const struct GNUNET_MESSENGER_MemberSession *session, GNUNET_MESSENGER_MessageRequestCallback callback, void *cls) { const struct GNUNET_MESSENGER_Message *message; struct GNUNET_MESSENGER_MessageStore *message_store = get_srv_room_message_store (room); const struct GNUNET_MESSENGER_MessageLink *link = get_store_message_link ( message_store, hash, GNUNET_YES ); if (! link) goto forward; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Requesting link of message with hash: %s\n", GNUNET_h2s (hash)); enum GNUNET_GenericReturnValue result; result = request_room_message_step (room, &(link->first), session, callback, cls); if ((GNUNET_YES == link->multiple) && (GNUNET_YES == request_room_message_step (room, &(link->second), session, callback, cls))) return GNUNET_YES; else return result; forward: message = get_store_message (message_store, hash); if (! message) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Requested message is missing in local storage: %s\n", GNUNET_h2s (hash)); return GNUNET_NO; } if (GNUNET_YES != check_member_session_history (session, hash, GNUNET_NO)) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unpermitted request for access by member (%s) of message (%s)!\n", GNUNET_sh2s (get_member_session_id (session)), GNUNET_h2s ( hash)); else if (callback) callback (cls, room, message, hash); return GNUNET_YES; } enum GNUNET_GenericReturnValue request_srv_room_message (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_HashCode *hash, const struct GNUNET_MESSENGER_MemberSession *session, GNUNET_MESSENGER_MessageRequestCallback callback, void *cls) { GNUNET_assert ((room) && (hash)); enum GNUNET_GenericReturnValue result; result = request_room_message_step (room, hash, session, callback, cls); if ((GNUNET_NO == result) && (callback)) callback (cls, room, NULL, hash); return result; } void callback_room_disconnect (struct GNUNET_MESSENGER_SrvRoom *room, void *cls) { struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls; if (! room->host) return; struct GNUNET_PeerIdentity identity; get_tunnel_peer_identity (tunnel, &identity); if ((GNUNET_YES != GNUNET_CONTAINER_multipeermap_remove (room->tunnels, &identity, tunnel)) || (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains (room->tunnels, &identity))) return; if (GNUNET_YES == contains_list_tunnels (&(room->basement), &identity)) send_srv_room_message (room, room->host, create_message_miss (&identity)); if ((0 < GNUNET_CONTAINER_multipeermap_size (room->tunnels)) || (GNUNET_NO == room->service->auto_connecting)) return; struct GNUNET_MESSENGER_ListTunnel *element; element = find_list_tunnels_alternate (&(room->basement), &identity); if (! element) return; GNUNET_PEER_resolve (element->peer, &identity); enter_srv_room_at (room, room->host, &identity); } enum GNUNET_GenericReturnValue 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) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Message error: Kind is unknown! (%d)\n", message->header.kind); return GNUNET_SYSERR; } struct GNUNET_MESSENGER_MessageStore *message_store = get_srv_room_message_store (room); const struct GNUNET_MESSENGER_Message *previous = get_store_message ( message_store, &(message->header.previous)); if (! previous) goto skip_time_comparison; struct GNUNET_TIME_Absolute timestamp = GNUNET_TIME_absolute_ntoh ( message->header.timestamp); struct GNUNET_TIME_Absolute last = GNUNET_TIME_absolute_ntoh ( previous->header.timestamp); if (GNUNET_TIME_relative_get_zero_ ().rel_value_us != GNUNET_TIME_absolute_get_difference (timestamp, last).rel_value_us) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Message warning: Timestamp does not check out!\n"); } skip_time_comparison: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receiving message of kind: %s!\n", GNUNET_MESSENGER_name_of_kind (message->header.kind)); return GNUNET_OK; } static void idle_request_room_messages (void *cls) { struct GNUNET_MESSENGER_SrvRoom *room = cls; room->idle = NULL; struct GNUNET_MESSENGER_OperationStore *operation_store = get_srv_room_operation_store (room); const struct GNUNET_HashCode *hash = get_message_state_merge_hash ( &(room->state)); if ((hash) && (GNUNET_MESSENGER_OP_UNKNOWN == get_store_operation_type (operation_store, hash))) use_store_operation ( operation_store, 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 solve_srv_room_member_collisions (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_CRYPTO_PublicKey *public_key, const struct GNUNET_ShortHashCode *member_id, struct GNUNET_TIME_Absolute timestamp) { GNUNET_assert ((room) && (public_key) && (member_id)); struct GNUNET_MESSENGER_MemberStore *member_store = get_srv_room_member_store (room); struct GNUNET_MESSENGER_Member *member = get_store_member (member_store, member_id); if ((! member) || (1 >= GNUNET_CONTAINER_multihashmap_size ( member->sessions))) return; struct GNUNET_MESSENGER_ListHandles *handles = &(room->service->handles); struct GNUNET_MESSENGER_ListHandle *element; const struct GNUNET_CRYPTO_PublicKey *pubkey; for (element = handles->head; element; element = element->next) { if (0 != GNUNET_memcmp (member_id, get_srv_handle_member_id ( element->handle, get_srv_room_key (room)))) continue; pubkey = get_srv_handle_key (element->handle); if (0 == GNUNET_memcmp (public_key, pubkey)) continue; struct GNUNET_MESSENGER_MemberSession *session = get_member_session (member, pubkey); if (! session) continue; struct GNUNET_TIME_Absolute start = get_member_session_start (session); if (GNUNET_TIME_relative_get_zero_ ().rel_value_us != GNUNET_TIME_absolute_get_difference (start, timestamp).rel_value_us) continue; struct GNUNET_ShortHashCode random_id; generate_free_member_id (&random_id, member_store->members); notify_srv_handle_member_id (element->handle, room, &random_id, GNUNET_NO); } } void rebuild_srv_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)) return; size_t count = count_of_tunnels (&(room->basement)); if (! find_list_tunnels (&(room->basement), &peer, &src)) return; if ((count > room->service->min_routers) && (GNUNET_NO == is_srv_handle_routing (room->host, &(room->key))) && (GNUNET_OK == verify_list_tunnels_flag_token (&(room->basement), &peer, GNUNET_MESSENGER_FLAG_CONNECTION_AUTO))) { close_srv_room (room); return; } struct GNUNET_MESSENGER_ListTunnel *element = room->basement.head; struct GNUNET_MESSENGER_SrvTunnel *tunnel; size_t dst = 0; while (element) { GNUNET_PEER_resolve (element->peer, &peer); tunnel = GNUNET_CONTAINER_multipeermap_get (room->tunnels, &peer); if (! tunnel) { element = remove_from_list_tunnels (&(room->basement), element); continue; } if (GNUNET_YES == required_connection_between (count, src, dst)) { if (GNUNET_SYSERR == connect_tunnel (tunnel)) { element = remove_from_list_tunnels (&(room->basement), element); continue; } } else disconnect_tunnel (tunnel); element = element->next; dst++; } } uint32_t get_srv_room_amount_of_tunnels (const struct GNUNET_MESSENGER_SrvRoom *room) { GNUNET_assert (room); return GNUNET_CONTAINER_multipeermap_size (room->tunnels); } uint32_t get_srv_room_connection_flags (const struct GNUNET_MESSENGER_SrvRoom *room) { GNUNET_assert (room); uint32_t flags = GNUNET_MESSENGER_FLAG_CONNECTION_NONE; if (GNUNET_YES == room->service->auto_routing) flags |= GNUNET_MESSENGER_FLAG_CONNECTION_AUTO; return flags; } static void handle_room_messages (struct GNUNET_MESSENGER_SrvRoom *room) { struct GNUNET_MESSENGER_MessageStore *message_store = get_srv_room_message_store (room); struct GNUNET_MESSENGER_MemberStore *member_store = get_srv_room_member_store (room); struct GNUNET_MESSENGER_PeerStore *peer_store = get_srv_room_peer_store ( room); while (room->handling.head) { struct GNUNET_MESSENGER_ListMessage *element = room->handling.head; const struct GNUNET_MESSENGER_Message *message = get_store_message ( message_store, &(element->hash)); if (! message) goto finish_handling; struct GNUNET_MESSENGER_SenderSession session; if (GNUNET_YES == is_peer_message (message)) { session.peer = get_store_peer_of (peer_store, message, &(element->hash)); if (! session.peer) goto finish_handling; } else { struct GNUNET_MESSENGER_Member *member = get_store_member_of ( member_store, message); if (! member) goto finish_handling; session.member = get_member_session_of (member, message, &(element->hash)); if (! session.member) goto finish_handling; } handle_service_message (room->service, room, &session, message, &(element->hash)); finish_handling: GNUNET_CONTAINER_DLL_remove (room->handling.head, room->handling.tail, element); GNUNET_free (element); } } enum GNUNET_GenericReturnValue 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_srv_room_operation_store (room); enum GNUNET_GenericReturnValue requested; 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); struct GNUNET_MESSENGER_MessageStore *message_store = get_srv_room_message_store (room); const struct GNUNET_MESSENGER_Message *old_message = get_store_message ( message_store, hash); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Handle a message in room (%s).\n", GNUNET_h2s (get_srv_room_key (room))); if ((old_message) || (GNUNET_OK != put_store_message (message_store, hash, message))) { if (old_message != message) destroy_message (message); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Duplicate message got dropped!\n"); return GNUNET_NO; } update_message_state (&(room->state), requested, message, hash); 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) && (GNUNET_MESSENGER_OP_MERGE == get_store_operation_type (operation_store, &(message->body. merge.previous)))) cancel_store_operation (operation_store, &(message->body.merge.previous)); if (GNUNET_MESSENGER_OP_MERGE == get_store_operation_type (operation_store, &(message->header. previous))) cancel_store_operation (operation_store, &(message->header.previous)); 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 enum GNUNET_GenericReturnValue iterate_update_member_sessions (void *cls, const struct GNUNET_CRYPTO_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; } static void remove_room_member_session (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session); void callback_room_handle_message (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash) { GNUNET_assert ((room) && (message) && (hash)); struct GNUNET_MESSENGER_PeerStore *peer_store = get_srv_room_peer_store ( room); struct GNUNET_MESSENGER_MemberStore *member_store = get_srv_room_member_store (room); struct GNUNET_MESSENGER_SenderSession session; if (GNUNET_YES == is_peer_message (message)) { session.peer = get_store_peer_of (peer_store, message, hash); if (! session.peer) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Message handling dropped: Peer is missing!\n"); return; } } else { 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; } session.member = get_member_session_of (member, message, hash); if (! session.member) { 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; remove_room_member_session (room, element->session); GNUNET_CONTAINER_DLL_remove (update.head, update.tail, element); GNUNET_free (element); } enum GNUNET_GenericReturnValue start_handle; start_handle = room->handling.head ? GNUNET_NO : GNUNET_YES; add_to_list_messages (&(room->handling), hash); switch (message->header.kind) { case GNUNET_MESSENGER_KIND_JOIN: handle_message_join (room, &session, message, hash); break; case GNUNET_MESSENGER_KIND_LEAVE: handle_message_leave (room, &session, message, hash); break; case GNUNET_MESSENGER_KIND_KEY: handle_message_key (room, &session, message, hash); break; case GNUNET_MESSENGER_KIND_PEER: handle_message_peer (room, &session, message, hash); break; case GNUNET_MESSENGER_KIND_ID: handle_message_id (room, &session, message, hash); break; case GNUNET_MESSENGER_KIND_MISS: handle_message_miss (room, &session, message, hash); break; case GNUNET_MESSENGER_KIND_DELETE: handle_message_delete (room, &session, message, hash); break; case GNUNET_MESSENGER_KIND_CONNECTION: handle_message_connection (room, &session, message, hash); break; default: break; } if (GNUNET_YES == start_handle) handle_room_messages (room); } 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_srv_room_key (room)), DIR_SEPARATOR); } void load_srv_room (struct GNUNET_MESSENGER_SrvRoom *room) { GNUNET_assert (room); char *room_dir; get_room_data_subdir (room, &room_dir); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Load room from directory: %s\n", room_dir); if (GNUNET_YES == GNUNET_DISK_directory_test (room_dir, GNUNET_YES)) { char *peers_file; GNUNET_asprintf (&peers_file, "%s%s", room_dir, "peers.list"); load_peer_store (get_srv_room_peer_store (room), peers_file); GNUNET_free (peers_file); load_member_store (get_srv_room_member_store (room), room_dir); load_message_store (get_srv_room_message_store (room), room_dir); load_operation_store (get_srv_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); load_message_state (&(room->state), room_dir); } GNUNET_free (room_dir); } void save_srv_room (struct GNUNET_MESSENGER_SrvRoom *room) { GNUNET_assert (room); char *room_dir; get_room_data_subdir (room, &room_dir); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Save room to directory: %s\n", room_dir); if ((GNUNET_YES == GNUNET_DISK_directory_test (room_dir, GNUNET_NO)) || (GNUNET_OK == GNUNET_DISK_directory_create (room_dir))) { char *peers_file; GNUNET_asprintf (&peers_file, "%s%s", room_dir, "peers.list"); save_peer_store (get_srv_room_peer_store (room), peers_file); GNUNET_free (peers_file); save_member_store (get_srv_room_member_store (room), room_dir); save_message_store (get_srv_room_message_store (room), room_dir); save_operation_store (get_srv_room_operation_store (room), room_dir); char *basement_file; GNUNET_asprintf (&basement_file, "%s%s", room_dir, "basement.list"); save_list_tunnels (&(room->basement), basement_file); GNUNET_free (basement_file); save_message_state (&(room->state), room_dir); } GNUNET_free (room_dir); } void remove_srv_room (struct GNUNET_MESSENGER_SrvRoom *room) { GNUNET_assert (room); char *room_dir; get_room_data_subdir (room, &room_dir); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Remove room from directory: %s\n", room_dir); if (GNUNET_YES == GNUNET_DISK_directory_test (room_dir, GNUNET_YES)) GNUNET_DISK_directory_remove (room_dir); GNUNET_free (room_dir); } static void remove_room_member_session (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_MemberSession *session) { GNUNET_assert ((room) && (session)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Remove member session from room: %s (%s)\n", GNUNET_sh2s (get_member_session_id (session)), GNUNET_h2s (get_srv_room_key (room))); remove_member_session (session->member, session); const struct GNUNET_CRYPTO_PublicKey *public_key = get_member_session_public_key (session); struct GNUNET_HashCode hash; GNUNET_CRYPTO_hash (public_key, sizeof(*public_key), &hash); char *room_dir; get_room_data_subdir (room, &room_dir); char *session_dir; GNUNET_asprintf ( &session_dir, "%s%s%c%s%c%s%c%s%c", room_dir, "members", DIR_SEPARATOR, GNUNET_sh2s (get_member_session_id (session)), DIR_SEPARATOR, "sessions", DIR_SEPARATOR, GNUNET_h2s (&hash), DIR_SEPARATOR ); GNUNET_free (room_dir); GNUNET_DISK_directory_remove (session_dir); GNUNET_free (session_dir); destroy_member_session (session); }