From a0b3e179abb44828f76843739487b2686b14cc8e Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 6 Feb 2011 23:27:01 +0000 Subject: more chat code from Mantis #1657 --- src/chat/Makefile.am | 82 +++++++++++++ src/chat/chat.c | 96 ++++++++++++--- src/chat/chat.h | 38 ++++-- src/chat/gnunet-chat.c | 120 +++++++++++-------- src/chat/gnunet-service-chat.c | 257 ++++++++++++++++++++++++++++++++++------- 5 files changed, 485 insertions(+), 108 deletions(-) (limited to 'src/chat') diff --git a/src/chat/Makefile.am b/src/chat/Makefile.am index ad099e6b1..f10796c23 100644 --- a/src/chat/Makefile.am +++ b/src/chat/Makefile.am @@ -37,3 +37,85 @@ gnunet_chat_LDADD = \ $(top_builddir)/src/chat/libgnunetchat.la \ $(top_builddir)/src/util/libgnunetutil.la \ $(GN_LIBINTL) + +check_PROGRAMS = \ + test_chat \ + test_chat_acknowledgement \ + test_chat_anonymous \ + test_chat_authentication \ + test_chat_p2p \ + test_chat_acknowledgement_p2p \ + test_chat_anonymous_p2p \ + test_chat_authentication_p2p \ + test_chat_private \ + test_chat_private_p2p + +if !DISABLE_TEST_RUN +TESTS = $(check_PROGRAMS) +endif + +test_chat_SOURCES = \ + test_chat.c +test_chat_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_acknowledgement_SOURCES = \ + test_chat.c +test_chat_acknowledgement_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_anonymous_SOURCES = \ + test_chat.c +test_chat_anonymous_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_authentication_SOURCES = \ + test_chat.c +test_chat_authentication_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_p2p_SOURCES = \ + test_chat.c +test_chat_p2p_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_acknowledgement_p2p_SOURCES = \ + test_chat.c +test_chat_acknowledgement_p2p_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_anonymous_p2p_SOURCES = \ + test_chat.c +test_chat_anonymous_p2p_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_authentication_p2p_SOURCES = \ + test_chat.c +test_chat_authentication_p2p_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_private_SOURCES = \ + test_chat_private.c +test_chat_private_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_private_p2p_SOURCES = \ + test_chat_private.c +test_chat_private_p2p_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +EXTRA_DIST = \ + test_chat_data.conf \ + test_chat_peer1.conf \ + test_chat_peer2.conf \ + test_chat_peer3.conf diff --git a/src/chat/chat.c b/src/chat/chat.c index da1237d84..f3e3470a0 100644 --- a/src/chat/chat.c +++ b/src/chat/chat.c @@ -32,12 +32,12 @@ #include "gnunet_signatures.h" #include "chat.h" -#define DEBUG_CHAT GNUNET_YES +#define DEBUG_CHAT GNUNET_NO #define NICK_IDENTITY_PREFIX ".chat_identity_" /** - * Handle for a (joined) chat room. + * Handle for a chat room. */ struct GNUNET_CHAT_Room { @@ -53,6 +53,12 @@ struct GNUNET_CHAT_Room struct MemberList *members; + int is_joined; + + GNUNET_CHAT_JoinCallback join_callback; + + void *join_callback_cls; + GNUNET_CHAT_MessageCallback message_callback; void *message_callback_cls; @@ -221,7 +227,9 @@ process_result (struct GNUNET_CHAT_Room *room, struct JoinNotificationMessage *join_msg; struct ReceiveNotificationMessage *received_msg; struct ConfirmationReceiptMessage *receipt; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; GNUNET_HashCode id; + const GNUNET_HashCode *sender; struct GNUNET_CONTAINER_MetaData *meta; struct GNUNET_CHAT_SendReceiptContext *src; struct MemberList *pos; @@ -261,11 +269,30 @@ process_result (struct GNUNET_CHAT_Room *room, sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), &pos->id); GNUNET_PSEUDONYM_add (room->cfg, &pos->id, meta); + pos->next = room->members; + room->members = pos; + if (GNUNET_NO == room->is_joined) + { + GNUNET_CRYPTO_rsa_key_get_public (room->my_private_key, &pkey); + if (0 == memcmp (&join_msg->public_key, + &pkey, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded))) + { + room->join_callback (room->join_callback_cls); + room->is_joined = GNUNET_YES; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("The current user must be the the first one joined\n")); + GNUNET_break (0); + return; + } + } + else room->member_list_callback (room->member_list_callback_cls, meta, &join_msg->public_key, ntohl (join_msg->msg_options)); - pos->next = room->members; - room->members = pos; break; case GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION: #if DEBUG_CHAT @@ -347,6 +374,13 @@ process_result (struct GNUNET_CHAT_Room *room, memcpy (message_content, &received_msg[1], msg_len); } message_content[msg_len] = '\0'; + if (0 != (ntohl (received_msg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS)) + { + sender = NULL; + meta = NULL; + } + else + { pos = room->members; while ((NULL != pos) && (0 != memcmp (&pos->id, @@ -354,11 +388,15 @@ process_result (struct GNUNET_CHAT_Room *room, sizeof (GNUNET_HashCode)))) pos = pos->next; GNUNET_assert (NULL != pos); + sender = &received_msg->sender; + meta = pos->meta; + } room->message_callback (room->message_callback_cls, room, - &received_msg->sender, - pos->meta, + sender, + meta, message_content, + GNUNET_TIME_absolute_ntoh (received_msg->timestamp), ntohl (received_msg->msg_options)); if (message_content != decrypted_msg) GNUNET_free (message_content); @@ -378,9 +416,7 @@ process_result (struct GNUNET_CHAT_Room *room, room, ntohl (receipt->sequence_number), GNUNET_TIME_absolute_ntoh (receipt->timestamp), - &receipt->target, - &receipt->content, - &receipt->signature); + &receipt->target); break; default: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -510,8 +546,9 @@ transmit_join_request (void *cls, { #if DEBUG_CHAT GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Could not transmit join request\n"); + "Could not transmit join request, retrying...\n"); #endif + GNUNET_CHAT_rejoin_room (chat_room); return 0; } #if DEBUG_CHAT @@ -542,6 +579,10 @@ transmit_join_request (void *cls, _("Could not serialize metadata\n")); return 0; } + GNUNET_CLIENT_receive (chat_room->client, + &receive_results, + chat_room, + GNUNET_TIME_UNIT_FOREVER_REL); return size_of_join; } @@ -623,6 +664,8 @@ GNUNET_CHAT_join_room (const struct GNUNET_CONFIGURATION_Handle *cfg, struct GNUNET_CONTAINER_MetaData *member_info, const char *room_name, enum GNUNET_CHAT_MsgOptions msg_options, + GNUNET_CHAT_JoinCallback joinCallback, + void *join_cls, GNUNET_CHAT_MessageCallback messageCallback, void *message_cls, GNUNET_CHAT_MemberListCallback memberCallback, @@ -654,11 +697,32 @@ GNUNET_CHAT_join_room (const struct GNUNET_CONFIGURATION_Handle *cfg, _("Failed to connect to the chat service\n")); return NULL; } + if (NULL == joinCallback) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Undefined mandatory parameter: joinCallback\n")); + return NULL; + } + if (NULL == messageCallback) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Undefined mandatory parameter: messageCallback\n")); + return NULL; + } + if (NULL == memberCallback) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Undefined mandatory parameter: memberCallback\n")); + return NULL; + } chat_room = GNUNET_malloc (sizeof (struct GNUNET_CHAT_Room)); chat_room->msg_options = msg_options; chat_room->room_name = GNUNET_strdup (room_name); chat_room->member_info = GNUNET_CONTAINER_meta_data_duplicate (member_info); chat_room->my_private_key = priv_key; + chat_room->is_joined = GNUNET_NO; + chat_room->join_callback = joinCallback; + chat_room->join_callback_cls = join_cls; chat_room->message_callback = messageCallback; chat_room->message_callback_cls = message_cls; chat_room->member_list_callback = memberCallback; @@ -668,10 +732,6 @@ GNUNET_CHAT_join_room (const struct GNUNET_CONFIGURATION_Handle *cfg, chat_room->cfg = cfg; chat_room->client = client; chat_room->members = NULL; - GNUNET_CLIENT_receive (client, - &receive_results, - chat_room, - GNUNET_TIME_UNIT_FOREVER_REL); if (GNUNET_SYSERR == GNUNET_CHAT_rejoin_room (chat_room)) { GNUNET_CHAT_leave_room (chat_room); @@ -717,6 +777,8 @@ transmit_send_request (void *cls, msg_to_send->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST); msg_to_send->msg_options = htonl (smc->options); msg_to_send->sequence_number = htonl (smc->sequence_number); + msg_to_send->timestamp = + GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); msg_to_send->reserved = htonl (0); if (NULL == smc->receiver) memset (&msg_to_send->target, 0, sizeof (GNUNET_HashCode)); @@ -770,13 +832,15 @@ GNUNET_CHAT_send_message (struct GNUNET_CHAT_Room *room, #if DEBUG_CHAT GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending a message\n"); #endif - *sequence_number = ++room->sequence_number; + room->sequence_number++; + if (NULL != sequence_number) + *sequence_number = room->sequence_number; smc = GNUNET_malloc (sizeof (struct GNUNET_CHAT_SendMessageContext)); smc->chat_room = room; smc->message = GNUNET_strdup (message); smc->options = options; smc->receiver = receiver; - smc->sequence_number = *sequence_number; + smc->sequence_number = room->sequence_number; msg_size = strlen (message) + sizeof (struct TransmitRequestMessage); GNUNET_CLIENT_notify_transmit_ready (room->client, msg_size, diff --git a/src/chat/chat.h b/src/chat/chat.h index a9cf83a8c..9a52a2d75 100644 --- a/src/chat/chat.h +++ b/src/chat/chat.h @@ -69,9 +69,14 @@ struct ReceiveNotificationMessage */ uint32_t reserved GNUNET_PACKED; + /** + * Timestamp of the message. + */ + struct GNUNET_TIME_AbsoluteNBO timestamp; + /** * Hash of the public key of the pseudonym of the sender of the message. - * TBD: Should be all zeros for anonymous. + * Should be all zeros for anonymous. */ GNUNET_HashCode sender; @@ -121,6 +126,11 @@ struct TransmitRequestMessage */ uint32_t sequence_number GNUNET_PACKED; + /** + * Timestamp of the message. + */ + struct GNUNET_TIME_AbsoluteNBO timestamp; + /** * Who should receive this message? Set to all zeros for "everyone". */ @@ -131,12 +141,15 @@ struct TransmitRequestMessage /** * Receipt sent from a message receiver to the service to confirm delivery of - * a chat message. + * a chat message and from the service to sender of the original message to + * acknowledge delivery. */ struct ConfirmationReceiptMessage { /** - * Message type will be GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT + * Message type will be + * GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT when sending from client, + * GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION when sending to client. */ struct GNUNET_MessageHeader header; @@ -351,8 +364,9 @@ struct P2PLeaveNotificationMessage /** * Message send by one peer to another to indicate receiving of a chat message. - * After this struct, the remaining bytes are the actual text message. If the - * mesasge is private, then the text is encrypted, otherwise it's plaintext. + * This struct is followed by the room name (only if the message is anonymous) + * and then the remaining bytes are the actual text message. If the mesasge is + * private, then the text is encrypted, otherwise it's plaintext. */ struct P2PReceiveNotificationMessage { @@ -371,14 +385,24 @@ struct P2PReceiveNotificationMessage */ uint32_t sequence_number GNUNET_PACKED; + /** + * Length of the room name. This is only used for anonymous messages. + */ + uint16_t room_name_len GNUNET_PACKED; + /** * Reserved (for alignment). */ - uint32_t reserved GNUNET_PACKED; + uint16_t reserved GNUNET_PACKED; + + /** + * Timestamp of the message. + */ + struct GNUNET_TIME_AbsoluteNBO timestamp; /** * Hash of the public key of the pseudonym of the sender of the message - * TBD: Should be all zeros for anonymous. + * Should be all zeros for anonymous. */ GNUNET_HashCode sender; diff --git a/src/chat/gnunet-chat.c b/src/chat/gnunet-chat.c index 450996382..22bdcc237 100644 --- a/src/chat/gnunet-chat.c +++ b/src/chat/gnunet-chat.c @@ -77,6 +77,20 @@ free_user_list () static int do_help (const char *args, const void *xtra); +/** + * Callback used for notification that we have joined the room. + * + * @param cls closure + * @return GNUNET_OK + */ +static int +join_cb (void *cls) +{ + fprintf (stdout, _("Joined\n")); + return GNUNET_OK; +} + + /** * Callback used for notification about incoming messages. * @@ -93,11 +107,13 @@ static int receive_cb (void *cls, struct GNUNET_CHAT_Room *room, const GNUNET_HashCode *sender, - const struct GNUNET_CONTAINER_MetaData *meta, + const struct GNUNET_CONTAINER_MetaData *member_info, const char *message, + struct GNUNET_TIME_Absolute timestamp, enum GNUNET_CHAT_MsgOptions options) { char *nick; + char *time; const char *fmt; if (NULL != sender) @@ -109,43 +125,43 @@ receive_cb (void *cls, { case GNUNET_CHAT_MSG_OPTION_NONE: case GNUNET_CHAT_MSG_ANONYMOUS: - fmt = _("`%s' said: %s\n"); + fmt = _("(%s) `%s' said: %s\n"); break; case GNUNET_CHAT_MSG_PRIVATE: - fmt = _("`%s' said to you: %s\n"); + fmt = _("(%s) `%s' said to you: %s\n"); break; case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ANONYMOUS: - fmt = _("`%s' said to you: %s\n"); + fmt = _("(%s) `%s' said to you: %s\n"); break; case GNUNET_CHAT_MSG_AUTHENTICATED: - fmt = _("`%s' said for sure: %s\n"); + fmt = _("(%s) `%s' said for sure: %s\n"); break; case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_AUTHENTICATED: - fmt = _("`%s' said to you for sure: %s\n"); + fmt = _("(%s) `%s' said to you for sure: %s\n"); break; case GNUNET_CHAT_MSG_ACKNOWLEDGED: - fmt = _("`%s' was confirmed that you received: %s\n"); + fmt = _("(%s) `%s' was confirmed that you received: %s\n"); break; case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED: - fmt = _("`%s' was confirmed that you and only you received: %s\n"); + fmt = _("(%s) `%s' was confirmed that you and only you received: %s\n"); break; case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_ACKNOWLEDGED: - fmt = _("`%s' was confirmed that you received from him or her: %s\n"); + fmt = _("(%s) `%s' was confirmed that you received from him or her: %s\n"); break; case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED: - fmt = - _ - ("`%s' was confirmed that you and only you received from him or her: %s\n"); + fmt = _("(%s) `%s' was confirmed that you and only you received from him or her: %s\n"); break; case GNUNET_CHAT_MSG_OFF_THE_RECORD: - fmt = _("`%s' said off the record: %s\n"); + fmt = _("(%s) `%s' said off the record: %s\n"); break; default: - fmt = _("<%s> said using an unknown message type: %s\n"); + fmt = _("(%s) <%s> said using an unknown message type: %s\n"); break; } - fprintf (stdout, fmt, nick, message); + time = GNUNET_STRINGS_absolute_time_to_string (timestamp); + fprintf (stdout, fmt, time, nick, message); GNUNET_free (nick); + GNUNET_free (time); return GNUNET_OK; } @@ -168,9 +184,7 @@ confirmation_cb (void *cls, struct GNUNET_CHAT_Room *room, uint32_t orig_seq_number, struct GNUNET_TIME_Absolute timestamp, - const GNUNET_HashCode *receiver, - const GNUNET_HashCode *msg_hash, - const struct GNUNET_CRYPTO_RsaSignature *receipt) + const GNUNET_HashCode *receiver) { char *nick; @@ -247,18 +261,6 @@ member_list_cb (void *cls, } -static int -do_transmit (const char *msg, const void *xtra) -{ - uint32_t seq; - GNUNET_CHAT_send_message (room, - msg, - GNUNET_CHAT_MSG_OPTION_NONE, - NULL, &seq); - return GNUNET_OK; -} - - static int do_join (const char *arg, const void *xtra) { @@ -276,6 +278,7 @@ do_join (const char *arg, const void *xtra) meta, room_name, -1, + &join_cb, NULL, &receive_cb, NULL, &member_list_cb, NULL, &confirmation_cb, NULL, &me); @@ -285,7 +288,7 @@ do_join (const char *arg, const void *xtra) return GNUNET_SYSERR; } my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me); - fprintf (stdout, _("Joined room `%s' as user `%s'\n"), room_name, my_name); + fprintf (stdout, _("Joining room `%s' as user `%s'...\n"), room_name, my_name); GNUNET_free (my_name); return GNUNET_OK; } @@ -315,6 +318,7 @@ do_nick (const char *msg, const void *xtra) meta, room_name, -1, + &join_cb, NULL, &receive_cb, NULL, &member_list_cb, NULL, &confirmation_cb, NULL, &me); @@ -355,7 +359,19 @@ do_names (const char *msg, const void *xtra) static int -do_pm (const char *msg, const void *xtra) +do_send (const char *msg, const void *xtra) +{ + uint32_t seq; + GNUNET_CHAT_send_message (room, + msg, + GNUNET_CHAT_MSG_OPTION_NONE, + NULL, &seq); + return GNUNET_OK; +} + + +static int +do_send_pm (const char *msg, const void *xtra) { char *user; GNUNET_HashCode uid; @@ -404,7 +420,7 @@ do_pm (const char *msg, const void *xtra) static int -do_transmit_sig (const char *msg, const void *xtra) +do_send_sig (const char *msg, const void *xtra) { uint32_t seq; GNUNET_CHAT_send_message (room, @@ -416,7 +432,7 @@ do_transmit_sig (const char *msg, const void *xtra) static int -do_transmit_ack (const char *msg, const void *xtra) +do_send_ack (const char *msg, const void *xtra) { uint32_t seq; GNUNET_CHAT_send_message (room, @@ -427,6 +443,18 @@ do_transmit_ack (const char *msg, const void *xtra) } +static int +do_send_anonymous (const char *msg, const void *xtra) +{ + uint32_t seq; + GNUNET_CHAT_send_message (room, + msg, + GNUNET_CHAT_MSG_ANONYMOUS, + NULL, &seq); + return GNUNET_OK; +} + + static int do_quit (const char *args, const void *xtra) { @@ -454,19 +482,24 @@ static struct ChatCommand commands[] = { gettext_noop ("Use `/nick nickname' to change your nickname. This will cause you to" " leave the current room and immediately rejoin it with the new name.")}, - {"/msg ", &do_pm, + {"/msg ", &do_send_pm, gettext_noop ("Use `/msg nickname message' to send a private message to the specified" " user")}, - {"/notice ", &do_pm, + {"/notice ", &do_send_pm, gettext_noop ("The `/notice' command is an alias for `/msg'")}, - {"/query ", &do_pm, + {"/query ", &do_send_pm, gettext_noop ("The `/query' command is an alias for `/msg'")}, - {"/sig ", &do_transmit_sig, + {"/sig ", &do_send_sig, gettext_noop ("Use `/sig message' to send a signed public message")}, - {"/ack ", &do_transmit_ack, + {"/ack ", &do_send_ack, gettext_noop ("Use `/ack message' to require signed acknowledgment of the message")}, + {"/anonymous ", &do_send_anonymous, + gettext_noop + ("Use `/anonymous message' to send a public anonymous message")}, + {"/anon ", &do_send_anonymous, + gettext_noop ("The `/anon' command is an alias for `/anonymous'")}, {"/quit", &do_quit, gettext_noop ("Use `/quit' to terminate gnunet-chat")}, {"/leave", &do_quit, @@ -479,13 +512,9 @@ static struct ChatCommand commands[] = { /* Add standard commands: /whois (print metadata), /ignore (set flag, check on receive!) */ - /* Add special commands (currently supported): - + anonymous msgs - + authenticated msgs - */ /* the following three commands must be last! */ {"/", &do_unknown, NULL}, - {"", &do_transmit, NULL}, + {"", &do_send, NULL}, {NULL, NULL, NULL}, }; @@ -615,6 +644,7 @@ run (void *cls, meta, room_name, -1, + &join_cb, NULL, &receive_cb, NULL, &member_list_cb, NULL, &confirmation_cb, NULL, &me); @@ -628,7 +658,7 @@ run (void *cls, return; } my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me); - fprintf (stdout, _("Joined room `%s' as user `%s'\n"), room_name, my_name); + fprintf (stdout, _("Joining room `%s' as user `%s'...\n"), room_name, my_name); GNUNET_free (my_name); handle_cmd_task = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI, diff --git a/src/chat/gnunet-service-chat.c b/src/chat/gnunet-service-chat.c index e0a46abc4..69318e4e8 100644 --- a/src/chat/gnunet-service-chat.c +++ b/src/chat/gnunet-service-chat.c @@ -33,9 +33,10 @@ #include "gnunet_signatures.h" #include "chat.h" -#define DEBUG_CHAT_SERVICE GNUNET_YES +#define DEBUG_CHAT_SERVICE GNUNET_NO #define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60) #define QUEUE_SIZE 16 +#define MAX_ANONYMOUS_MSG_LIST_LENGTH 16 /** @@ -93,6 +94,20 @@ struct ChatClient }; +/** + * Linked list of recent anonymous messages. + */ +struct AnonymousMessage +{ + struct AnonymousMessage *next; + + /** + * Hash of the message. + */ + GNUNET_HashCode hash; + +}; + /** * Handle to the core service (NULL until we've connected to it). @@ -119,6 +134,56 @@ static struct ChatClient *client_list_head = NULL; */ struct GNUNET_SERVER_NotificationContext *nc = NULL; +/** + * Head of the list of recent anonymous messages. + */ +static struct AnonymousMessage *anonymous_list_head = NULL; + + +static void +remember_anonymous_message (const struct P2PReceiveNotificationMessage *p2p_rnmsg) +{ + static GNUNET_HashCode hash; + struct AnonymousMessage *anon_msg; + struct AnonymousMessage *prev; + int anon_list_len; + + GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash); + anon_msg = GNUNET_malloc (sizeof (struct AnonymousMessage)); + anon_msg->hash = hash; + anon_msg->next = anonymous_list_head; + anonymous_list_head = anon_msg; + anon_list_len = 1; + prev = NULL; + while ((NULL != anon_msg->next)) + { + prev = anon_msg; + anon_msg = anon_msg->next; + anon_list_len++; + } + if (anon_list_len == MAX_ANONYMOUS_MSG_LIST_LENGTH) + { + GNUNET_free (anon_msg); + if (NULL != prev) + prev->next = NULL; + } +} + + +static int +lookup_anonymous_message (const struct P2PReceiveNotificationMessage *p2p_rnmsg) +{ + static GNUNET_HashCode hash; + struct AnonymousMessage *anon_msg; + + GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash); + anon_msg = anonymous_list_head; + while ((NULL != anon_msg) && + (0 != memcmp (&anon_msg->hash, &hash, sizeof (GNUNET_HashCode)))) + anon_msg = anon_msg->next; + return (NULL != anon_msg); +} + /** * Transmit a message notification to the peer. @@ -141,9 +206,17 @@ transmit_message_notification_to_peer (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P message notification\n"); #endif + if (buf == NULL) + { + /* client disconnected */ +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Buffer is NULL, dropping the message\n"); +#endif + return 0; + } msg_size = ntohs (my_msg->header.size); GNUNET_assert (size >= msg_size); - GNUNET_assert (NULL != buf); memcpy (m, my_msg, msg_size); GNUNET_free (my_msg); return msg_size; @@ -205,8 +278,10 @@ handle_transmit_request (void *cls, struct GNUNET_CRYPTO_AesSessionKey key; char encrypted_msg[MAX_MESSAGE_LENGTH]; const char *room; + size_t room_len; int msg_len; - int priv_msg; + int is_priv; + int is_anon; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a chat message\n"); if (ntohs (message->size) <= sizeof (struct TransmitRequestMessage)) @@ -218,8 +293,8 @@ handle_transmit_request (void *cls, } trmsg = (const struct TransmitRequestMessage *) message; msg_len = ntohs (trmsg->header.size) - sizeof (struct TransmitRequestMessage); - priv_msg = (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_PRIVATE) != 0; - if (priv_msg) + is_priv = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_PRIVATE)); + if (is_priv) { #if DEBUG_CHAT_SERVICE GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting the message text\n"); @@ -244,7 +319,7 @@ handle_transmit_request (void *cls, msg_len); rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION); rnmsg->msg_options = trmsg->msg_options; - rnmsg->sequence_number = trmsg->sequence_number; + rnmsg->timestamp = trmsg->timestamp; pos = client_list_head; while ((NULL != pos) && (pos->client != client)) pos = pos->next; @@ -260,11 +335,18 @@ handle_transmit_request (void *cls, } room = pos->room; pos->msg_sequence_number = ntohl (trmsg->sequence_number); - if (0 == (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS)) - rnmsg->sender = pos->id; - else + is_anon = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS)); + if (is_anon) + { memset (&rnmsg->sender, 0, sizeof (GNUNET_HashCode)); - if (priv_msg) + rnmsg->sequence_number = 0; + } + else + { + rnmsg->sender = pos->id; + rnmsg->sequence_number = trmsg->sequence_number; + } + if (is_priv) { #if DEBUG_CHAT_SERVICE GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -323,7 +405,7 @@ handle_transmit_request (void *cls, (NULL != pos->client) && (pos->client != client)) { - if (((!priv_msg) || + if (((!is_priv) || (0 == memcmp (&trmsg->target, &pos->id, sizeof (GNUNET_HashCode)))) && @@ -341,16 +423,25 @@ handle_transmit_request (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Broadcasting message to neighbour peers\n"); #endif + if (is_anon) + { + room_len = strlen (room); + p2p_rnmsg = GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) + + msg_len + room_len); + p2p_rnmsg->header.size = + htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len + + room_len); + p2p_rnmsg->room_name_len = htons (room_len); + memcpy ((char *) &p2p_rnmsg[1], room, room_len); + memcpy ((char *) &p2p_rnmsg[1] + room_len, &trmsg[1], msg_len); + } + else + { p2p_rnmsg = GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) + msg_len); - p2p_rnmsg->header.size = htons (sizeof (struct P2PReceiveNotificationMessage) + - msg_len); - p2p_rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION); - p2p_rnmsg->msg_options = trmsg->msg_options; - p2p_rnmsg->sequence_number = trmsg->sequence_number; - memcpy (&p2p_rnmsg->sender, &rnmsg->sender, sizeof (GNUNET_HashCode)); - p2p_rnmsg->target = trmsg->target; - if (priv_msg) + p2p_rnmsg->header.size = + htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len); + if (is_priv) { memcpy (&p2p_rnmsg[1], encrypted_msg, msg_len); memcpy (&p2p_rnmsg->encrypted_key, @@ -358,9 +449,17 @@ handle_transmit_request (void *cls, sizeof (struct GNUNET_CRYPTO_RsaEncryptedData)); } else - { memcpy (&p2p_rnmsg[1], &trmsg[1], msg_len); } + p2p_rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION); + p2p_rnmsg->msg_options = trmsg->msg_options; + p2p_rnmsg->sequence_number = trmsg->sequence_number; + p2p_rnmsg->timestamp = trmsg->timestamp; + p2p_rnmsg->reserved = 0; + p2p_rnmsg->sender = rnmsg->sender; + p2p_rnmsg->target = trmsg->target; + if (is_anon) + remember_anonymous_message (p2p_rnmsg); GNUNET_CORE_iterate_peers (cfg, &send_message_noficiation, p2p_rnmsg); @@ -591,9 +690,17 @@ transmit_confirmation_receipt_to_peer (void *cls, "Transmitting P2P confirmation receipt to '%s'\n", GNUNET_h2s (&receipt->target)); #endif + if (buf == NULL) + { + /* client disconnected */ +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Buffer is NULL, dropping the message\n"); +#endif + return 0; + } msg_size = sizeof (struct P2PConfirmationReceiptMessage); GNUNET_assert (size >= msg_size); - GNUNET_assert (NULL != buf); memcpy (buf, receipt, msg_size); GNUNET_free (receipt); return msg_size; @@ -765,9 +872,17 @@ transmit_leave_notification_to_peer (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P leave notification\n"); #endif + if (buf == NULL) + { + /* client disconnected */ +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Buffer is NULL, dropping the message\n"); +#endif + return 0; + } msg_size = sizeof (struct P2PLeaveNotificationMessage); GNUNET_assert (size >= msg_size); - GNUNET_assert (NULL != buf); m = buf; m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION); m->header.size = htons (msg_size); @@ -787,7 +902,6 @@ send_leave_noficiation (void *cls, const struct GNUNET_TRANSPORT_ATS_Information *atsi) { struct ChatClient *entry = cls; - struct GNUNET_CORE_TransmitHandle *th; struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key; size_t msg_size; @@ -806,14 +920,15 @@ send_leave_noficiation (void *cls, msg_size = sizeof (struct P2PLeaveNotificationMessage); public_key = GNUNET_memdup (&entry->public_key, sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); - th = GNUNET_CORE_notify_transmit_ready (core, + if (NULL == GNUNET_CORE_notify_transmit_ready (core, 1, MAX_TRANSMIT_DELAY, peer, msg_size, &transmit_leave_notification_to_peer, - public_key); - GNUNET_assert (NULL != th); + public_key)) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to queue a leave notification\n")); } } @@ -1108,8 +1223,12 @@ handle_p2p_message_notification (void *cls, struct ChatClient *sender; struct ChatClient *pos; static GNUNET_HashCode all_zeros; - int priv_msg; + int is_priv; + int is_anon; uint16_t msg_len; + uint16_t room_name_len; + char *room_name = NULL; + char *text; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P message notification\n"); if (ntohs (message->size) <= sizeof (struct P2PReceiveNotificationMessage)) @@ -1119,6 +1238,37 @@ handle_p2p_message_notification (void *cls, return GNUNET_SYSERR; } p2p_rnmsg = (const struct P2PReceiveNotificationMessage *) message; + msg_len = ntohs (p2p_rnmsg->header.size) - + sizeof (struct P2PReceiveNotificationMessage); + + is_anon = (0 != (ntohl (p2p_rnmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS)); + if (is_anon) + { + room_name_len = ntohs (p2p_rnmsg->room_name_len); + if (msg_len <= room_name_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Malformed message: wrong length of the room name\n"); + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + msg_len -= room_name_len; + if (lookup_anonymous_message (p2p_rnmsg)) + { +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "This anonymous message has already been handled."); +#endif + return GNUNET_OK; + } + remember_anonymous_message (p2p_rnmsg); + room_name = GNUNET_malloc (room_name_len + 1); + memcpy (room_name, (char *) &p2p_rnmsg[1], room_name_len); + room_name[room_name_len] = '\0'; + text = (char *) &p2p_rnmsg[1] + room_name_len; + } + else + { sender = client_list_head; while ((NULL != sender) && (0 != memcmp (&sender->id, @@ -1127,10 +1277,13 @@ handle_p2p_message_notification (void *cls, sender = sender->next; if (NULL == sender) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + /* not an error since the sender may have left before we got the + message */ +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Unknown source. Rejecting the message\n"); - GNUNET_break_op (0); - return GNUNET_SYSERR; +#endif + return GNUNET_OK; } if (sender->msg_sequence_number >= ntohl (p2p_rnmsg->sequence_number)) { @@ -1138,15 +1291,19 @@ handle_p2p_message_notification (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "This message has already been handled." " Sequence numbers (msg/sender): %u/%u\n", - ntohl (p2p_rnmsg->sequence_number), sender->msg_sequence_number); + ntohl (p2p_rnmsg->sequence_number), + sender->msg_sequence_number); #endif return GNUNET_OK; } sender->msg_sequence_number = ntohl (p2p_rnmsg->sequence_number); - msg_len = ntohs (p2p_rnmsg->header.size) - - sizeof (struct P2PReceiveNotificationMessage); + room_name = sender->room; + text = (char *) &p2p_rnmsg[1]; + } + #if DEBUG_CHAT_SERVICE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message to local room members\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending message to local room members\n"); #endif rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len); rnmsg->header.size = htons (sizeof (struct ReceiveNotificationMessage) + @@ -1154,21 +1311,22 @@ handle_p2p_message_notification (void *cls, rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION); rnmsg->msg_options = p2p_rnmsg->msg_options; rnmsg->sequence_number = p2p_rnmsg->sequence_number; - priv_msg = (0 != memcmp (&all_zeros, + rnmsg->timestamp = p2p_rnmsg->timestamp; + is_priv = (0 != memcmp (&all_zeros, &p2p_rnmsg->target, sizeof (GNUNET_HashCode))); - if (priv_msg) + if (is_priv) memcpy (&rnmsg->encrypted_key, &p2p_rnmsg->encrypted_key, sizeof (struct GNUNET_CRYPTO_RsaEncryptedData)); - memcpy (&rnmsg->sender, &p2p_rnmsg->sender, sizeof (GNUNET_HashCode)); - memcpy (&rnmsg[1], &p2p_rnmsg[1], msg_len); + rnmsg->sender = p2p_rnmsg->sender; + memcpy (&rnmsg[1], text, msg_len); pos = client_list_head; while (NULL != pos) { - if ((0 == strcmp (sender->room, pos->room)) && + if ((0 == strcmp (room_name, pos->room)) && (NULL != pos->client)) { - if (((!priv_msg) || + if (((!is_priv) || (0 == memcmp (&p2p_rnmsg->target, &pos->id, sizeof (GNUNET_HashCode)))) && @@ -1182,6 +1340,8 @@ handle_p2p_message_notification (void *cls, } pos = pos->next; } + if (is_anon) + GNUNET_free (room_name); #if DEBUG_CHAT_SERVICE GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Broadcasting message notification to neighbour peers\n"); @@ -1441,6 +1601,9 @@ static void cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { + struct AnonymousMessage *next_msg; + struct ChatClient *next_client; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Cleaning up\n"); if (NULL != core) { @@ -1452,6 +1615,20 @@ cleanup_task (void *cls, GNUNET_SERVER_notification_context_destroy (nc); nc = NULL; } + while (NULL != client_list_head) + { + next_client = client_list_head->next; + GNUNET_free (client_list_head->room); + GNUNET_free_non_null (client_list_head->member_info); + GNUNET_free (client_list_head); + client_list_head = next_client; + } + while (NULL != anonymous_list_head) + { + next_msg = anonymous_list_head->next; + GNUNET_free (anonymous_list_head); + anonymous_list_head = next_msg; + } } -- cgit v1.2.3