libgnunetchat

library for GNUnet Messenger
Log | Files | Refs | README | LICENSE

commit d86c3a8e16476687b8c373e7ebd89c29b18fd58f
parent 0f10ebd9ccc82b3d4c4d0e155e3e3982963d4619
Author: TheJackiMonster <thejackimonster@gmail.com>
Date:   Thu, 22 Feb 2024 22:59:02 +0100

Implement tool to debug messenger graph via plantuml

Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>

Diffstat:
Mtools/gnunet_chat_lib_uml.c | 2--
Atools/gnunet_messenger_uml.c | 409+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtools/meson.build | 14+++++++++++++-
3 files changed, 422 insertions(+), 3 deletions(-)

diff --git a/tools/gnunet_chat_lib_uml.c b/tools/gnunet_chat_lib_uml.c @@ -176,13 +176,11 @@ chat_message (void *cls, } if (GNUNET_CHAT_KIND_REFRESH == kind) - { GNUNET_CHAT_iterate_accounts( tool->handle, accounts_iterate, tool ); - } if ((!(tool->quit)) && (!(tool->task))) tool->task = GNUNET_SCHEDULER_add_delayed_with_priority( diff --git a/tools/gnunet_messenger_uml.c b/tools/gnunet_messenger_uml.c @@ -0,0 +1,409 @@ +/* + This file is part of GNUnet. + Copyright (C) 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 <http://www.gnu.org/licenses/>. + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/* + * @author Tobias Frisch + * @file gnunet_messenger_uml.c + */ + +#include <gnunet/gnunet_common.h> +#include <gnunet/gnunet_identity_service.h> +#include <gnunet/gnunet_messenger_service.h> +#include <gnunet/gnunet_scheduler_lib.h> +#include <gnunet/gnunet_time_lib.h> +#include <gnunet/gnunet_util_lib.h> +#include <string.h> + +struct GNUNET_MESSENGER_Link +{ + struct GNUNET_MESSENGER_Link *prev; + struct GNUNET_MESSENGER_Link *next; + + struct GNUNET_HashCode hash; + struct GNUNET_HashCode previous; + + bool dotted; +}; + +struct GNUNET_MESSENGER_Tool +{ + const struct GNUNET_CONFIGURATION_Handle *cfg; + struct GNUNET_IDENTITY_EgoLookup *lookup; + struct GNUNET_MESSENGER_Handle *handle; + struct GNUNET_SCHEDULER_Task *task; + + struct GNUNET_CONTAINER_MultiHashMap *map; + + struct GNUNET_MESSENGER_Link *head; + struct GNUNET_MESSENGER_Link *tail; + + char *ego_name; + char *room_name; + bool quit; +}; + +static void +idle (void *cls) +{ + struct GNUNET_MESSENGER_Tool *tool = cls; + + tool->task = NULL; + tool->quit = true; + + if (tool->handle) + GNUNET_MESSENGER_disconnect(tool->handle); + + if (tool->lookup) + GNUNET_IDENTITY_ego_lookup_cancel(tool->lookup); + + while (tool->head) + { + struct GNUNET_MESSENGER_Link *link = tool->head; + + const struct GNUNET_HashCode *hash = &(link->hash); + const struct GNUNET_HashCode *previous = &(link->previous); + + printf( + "X%s %s> ", + GNUNET_h2s(hash), + link->dotted? ".." : "--" + ); + + printf( + "X%s\n", + GNUNET_h2s(previous) + ); + + GNUNET_CONTAINER_DLL_remove(tool->head, tool->tail, link); + GNUNET_free(link); + } +} + +static void +add_link (struct GNUNET_MESSENGER_Tool *tool, + const struct GNUNET_HashCode *hash, + const struct GNUNET_HashCode *previous, + bool dotted) +{ + size_t i; + for (i = 0; i < sizeof(*previous); i++) + if (((unsigned char*) previous)[i] != 0) + break; + + if (i == sizeof(*previous)) + return; + + struct GNUNET_MESSENGER_Link *link = GNUNET_new( + struct GNUNET_MESSENGER_Link + ); + + GNUNET_memcpy(&(link->hash), hash, sizeof(*hash)); + GNUNET_memcpy(&(link->previous), previous, sizeof(*previous)); + + link->dotted = dotted; + + GNUNET_CONTAINER_DLL_insert(tool->head, tool->tail, link); +} + +static void +message_callback (void *cls, + struct GNUNET_MESSENGER_Room *room, + const struct GNUNET_MESSENGER_Contact *sender, + const struct GNUNET_MESSENGER_Contact *recipient, + const struct GNUNET_MESSENGER_Message *message, + const struct GNUNET_HashCode *hash, + enum GNUNET_MESSENGER_MessageFlags flags) +{ + struct GNUNET_MESSENGER_Tool *tool = cls; + + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(tool->map, hash)) + return; + + if (tool->task) + { + GNUNET_SCHEDULER_cancel(tool->task); + tool->task = NULL; + } + + printf( + "json X%s {", + GNUNET_h2s(hash) + ); + + printf( + "\n \"kind\":\"%s\"", + GNUNET_MESSENGER_name_of_kind( + message->header.kind + ) + ); + + printf( + ",\n \"sender_id\":\"%s\"", + GNUNET_sh2s( + &(message->header.sender_id) + ) + ); + + const struct GNUNET_TIME_Absolute timestamp = GNUNET_TIME_absolute_ntoh( + message->header.timestamp + ); + + printf( + ",\n \"timestamp\":\"%s\"", + GNUNET_STRINGS_absolute_time_to_string( + timestamp + ) + ); + + if (sender) + { + const unsigned long long sender_address = (unsigned long long) sender; + const char *sender_name = GNUNET_MESSENGER_contact_get_name( + sender + ); + + if (sender_name) + printf( + ",\n \"sender\":[\"0x%llx\",\"%s\"]", + sender_address, + sender_name + ); + else + printf( + ",\n \"sender\":\"0x%llx\"", + sender_address + ); + } + + if (recipient) + { + const unsigned long long recipient_address = (unsigned long long) recipient; + const char *recipient_name = GNUNET_MESSENGER_contact_get_name( + recipient + ); + + if (recipient_name) + printf( + ",\n \"recipient\":[\"0x%llx\",\"%s\"]", + recipient_address, + recipient_name + ); + else + printf( + ",\n \"recipient\":\"0x%llx\"", + recipient_address + ); + } + + switch (message->header.kind) + { + case GNUNET_MESSENGER_KIND_PEER: + printf( + ",\n \"peer\":\"%s\"", + GNUNET_i2s(&(message->body.peer.peer)) + ); + break; + case GNUNET_MESSENGER_KIND_MISS: + printf( + ",\n \"peer\":\"%s\"", + GNUNET_i2s(&(message->body.miss.peer)) + ); + break; + case GNUNET_MESSENGER_KIND_TEXT: + printf( + ",\n \"text\":\"%s\"", + message->body.text.text + ); + break; + case GNUNET_MESSENGER_KIND_FILE: + printf( + ",\n \"file\":[\"%s\",\"%s\"]", + message->body.file.name, + message->body.file.uri + ); + break; + case GNUNET_MESSENGER_KIND_TAG: + printf( + ",\n \"tag\":\"%s\"", + message->body.tag.tag + ); + break; + default: + break; + } + + printf("\n}\n"); + + if (GNUNET_MESSENGER_KIND_MERGE == message->header.kind) + { + add_link(tool, hash, &(message->body.merge.previous), false); + + GNUNET_MESSENGER_get_message( + room, + &(message->body.merge.previous) + ); + } + + if (GNUNET_MESSENGER_KIND_REQUEST == message->header.kind) + add_link(tool, hash, &(message->body.request.hash), true); + + if (GNUNET_MESSENGER_KIND_DELETE == message->header.kind) + add_link(tool, hash, &(message->body.deletion.hash), true); + + if (GNUNET_MESSENGER_KIND_REQUEST == message->header.kind) + add_link(tool, hash, &(message->body.request.hash), true); + + if (GNUNET_MESSENGER_KIND_TAG == message->header.kind) + add_link(tool, hash, &(message->body.tag.hash), true); + + add_link(tool, hash, &(message->header.previous), false); + + GNUNET_MESSENGER_get_message( + room, + &(message->header.previous) + ); + + GNUNET_CONTAINER_multihashmap_put( + tool->map, + hash, + NULL, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST + ); + + if ((!(tool->quit)) && (!(tool->task))) + tool->task = GNUNET_SCHEDULER_add_delayed_with_priority( + GNUNET_TIME_relative_get_second_(), + GNUNET_SCHEDULER_PRIORITY_IDLE, + idle, + tool + ); +} + +static void +ego_lookup (void *cls, + struct GNUNET_IDENTITY_Ego *ego) +{ + struct GNUNET_MESSENGER_Tool *tool = cls; + + tool->lookup = NULL; + + const struct GNUNET_CRYPTO_PrivateKey *key; + key = ego? GNUNET_IDENTITY_ego_get_private_key(ego) : NULL; + + tool->handle = GNUNET_MESSENGER_connect( + tool->cfg, + tool->ego_name, + key, + message_callback, + tool + ); + + struct GNUNET_PeerIdentity peer; + GNUNET_CRYPTO_get_peer_identity( + tool->cfg, + &peer + ); + + struct GNUNET_HashCode hash; + + if (tool->room_name) + GNUNET_CRYPTO_hash( + tool->room_name, + strlen(tool->room_name), + &hash + ); + else + memset(&hash, 0, sizeof(hash)); + + GNUNET_MESSENGER_enter_room( + tool->handle, + &peer, + &hash + ); +} + +static void +run (void *cls, + char* const* args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_MESSENGER_Tool *tool = cls; + + tool->cfg = cfg; + + if (!(tool->ego_name)) + { + ego_lookup(tool, NULL); + return; + } + + tool->lookup = GNUNET_IDENTITY_ego_lookup( + cfg, + tool->ego_name, + &ego_lookup, + tool + ); +} + +int +main (int argc, + char* const* argv) +{ + struct GNUNET_MESSENGER_Tool tool; + memset(&tool, 0, sizeof(tool)); + + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_string( + 'e', + "ego", + "IDENTITY_NAME", + "name of identity to read messages with", + &(tool.ego_name) + ), + GNUNET_GETOPT_option_string( + 'r', + "room", + "ROOM_NAME", + "name of room to read messages from", + &(tool.room_name) + ), + GNUNET_GETOPT_OPTION_END + }; + + printf("@startuml\n"); + + tool.map = GNUNET_CONTAINER_multihashmap_create(8, GNUNET_NO); + + enum GNUNET_GenericReturnValue result = GNUNET_PROGRAM_run( + argc, + argv, + "gnunet_messenger_uml", + gettext_noop("A tool to debug the Messenger service of GNUnet."), + options, + &run, + &tool + ); + + GNUNET_CONTAINER_multihashmap_destroy(tool.map); + + printf("@enduml\n"); + + return GNUNET_OK == result? 0 : 1; +} diff --git a/tools/meson.build b/tools/meson.build @@ -21,7 +21,19 @@ libgnunetchat_uml = executable( 'libgnunetchat_uml', [ 'gnunet_chat_lib_uml.c' ], - dependencies: [ dependency('gnunetutil') ], + dependencies: [ + dependency('gnunetutil') + ], link_with: gnunetchat_lib, include_directories: tools_include, ) + +messenger_uml = executable( + 'messenger_uml', + [ 'gnunet_messenger_uml.c' ], + dependencies: [ + dependency('gnunetidentity'), + dependency('gnunetmessenger'), + dependency('gnunetutil') + ], +)