gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnunet] branch master updated (63fe195e4 -> fec34163a)


From: gnunet
Subject: [gnunet] branch master updated (63fe195e4 -> fec34163a)
Date: Thu, 12 Nov 2020 17:30:55 +0100

This is an automated email from the git hooks/post-receive script.

thejackimonster pushed a change to branch master
in repository gnunet.

    from 63fe195e4 -fix
     new 8bf864c25 adding the messenger service and its client-side library
     new fec34163a -merge branch 'jacki/messenger'

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 README.1st                                         |    1 +
 configure.ac                                       |    2 +
 ...netconversation.pc.in => gnunetmessenger.pc.in} |    6 +-
 po/POTFILES.in                                     |   23 +
 src/Makefile.am                                    |    3 +-
 src/identity/identity_api.c                        |   22 +-
 src/include/Makefile.am                            |    1 +
 src/include/gnunet_messenger_service.h             |  436 ++++++++
 src/include/gnunet_protocols.h                     |   43 +-
 src/messenger/.gitignore                           |    4 +
 src/messenger/Makefile.am                          |  131 +++
 src/messenger/gnunet-messenger.c                   |  306 ++++++
 src/messenger/gnunet-service-messenger.c           |  306 ++++++
 src/messenger/gnunet-service-messenger.h           |  121 +++
 src/messenger/gnunet-service-messenger_basement.c  |   58 ++
 src/messenger/gnunet-service-messenger_basement.h  |   66 ++
 src/messenger/gnunet-service-messenger_contact.c   |   96 ++
 src/messenger/gnunet-service-messenger_contact.h   |  112 +++
 src/messenger/gnunet-service-messenger_handle.c    |  503 ++++++++++
 src/messenger/gnunet-service-messenger_handle.h    |  216 ++++
 .../gnunet-service-messenger_list_handles.c        |   95 ++
 .../gnunet-service-messenger_list_handles.h        |   96 ++
 .../gnunet-service-messenger_list_messages.c       |   76 ++
 .../gnunet-service-messenger_list_messages.h       |   81 ++
 .../gnunet-service-messenger_message_handle.c      |  130 +++
 .../gnunet-service-messenger_message_handle.h      |  128 +++
 .../gnunet-service-messenger_message_kind.c        |  192 ++++
 .../gnunet-service-messenger_message_kind.h        |  160 +++
 .../gnunet-service-messenger_message_recv.c        |  204 ++++
 .../gnunet-service-messenger_message_recv.h        |  159 +++
 .../gnunet-service-messenger_message_send.c        |  118 +++
 .../gnunet-service-messenger_message_send.h        |  155 +++
 .../gnunet-service-messenger_message_store.c       |  282 ++++++
 .../gnunet-service-messenger_message_store.h       |  120 +++
 src/messenger/gnunet-service-messenger_room.c      | 1051 ++++++++++++++++++++
 src/messenger/gnunet-service-messenger_room.h      |  378 +++++++
 src/messenger/gnunet-service-messenger_service.c   |  516 ++++++++++
 src/messenger/gnunet-service-messenger_service.h   |  259 +++++
 src/messenger/gnunet-service-messenger_tunnel.c    |  300 ++++++
 src/messenger/gnunet-service-messenger_tunnel.h    |  155 +++
 src/messenger/gnunet-service-messenger_util.c      |   64 ++
 src/messenger/gnunet-service-messenger_util.h      |   53 +
 src/messenger/messenger.conf.in                    |   13 +
 src/messenger/messenger_api.c                      |  568 +++++++++++
 src/messenger/messenger_api_contact.c              |   78 ++
 src/messenger/messenger_api_contact.h              |   93 ++
 .../messenger_api_ego.h}                           |   31 +-
 src/messenger/messenger_api_handle.c               |  213 ++++
 src/messenger/messenger_api_handle.h               |  174 ++++
 src/messenger/messenger_api_list_tunnels.c         |  112 +++
 src/messenger/messenger_api_list_tunnels.h         |  112 +++
 src/messenger/messenger_api_message.c              |  602 +++++++++++
 src/messenger/messenger_api_message.h              |  190 ++++
 src/messenger/messenger_api_room.c                 |  189 ++++
 src/messenger/messenger_api_room.h                 |   95 ++
 src/messenger/test_messenger.c                     |  187 ++++
 src/messenger/test_messenger_anonymous.c           |  179 ++++
 src/messenger/test_messenger_comm0.c               |  252 +++++
 58 files changed, 10285 insertions(+), 31 deletions(-)
 copy pkgconfig/{gnunetconversation.pc.in => gnunetmessenger.pc.in} (52%)
 create mode 100644 src/include/gnunet_messenger_service.h
 create mode 100644 src/messenger/.gitignore
 create mode 100644 src/messenger/Makefile.am
 create mode 100644 src/messenger/gnunet-messenger.c
 create mode 100644 src/messenger/gnunet-service-messenger.c
 create mode 100644 src/messenger/gnunet-service-messenger.h
 create mode 100644 src/messenger/gnunet-service-messenger_basement.c
 create mode 100644 src/messenger/gnunet-service-messenger_basement.h
 create mode 100644 src/messenger/gnunet-service-messenger_contact.c
 create mode 100644 src/messenger/gnunet-service-messenger_contact.h
 create mode 100644 src/messenger/gnunet-service-messenger_handle.c
 create mode 100644 src/messenger/gnunet-service-messenger_handle.h
 create mode 100644 src/messenger/gnunet-service-messenger_list_handles.c
 create mode 100644 src/messenger/gnunet-service-messenger_list_handles.h
 create mode 100644 src/messenger/gnunet-service-messenger_list_messages.c
 create mode 100644 src/messenger/gnunet-service-messenger_list_messages.h
 create mode 100644 src/messenger/gnunet-service-messenger_message_handle.c
 create mode 100644 src/messenger/gnunet-service-messenger_message_handle.h
 create mode 100644 src/messenger/gnunet-service-messenger_message_kind.c
 create mode 100644 src/messenger/gnunet-service-messenger_message_kind.h
 create mode 100644 src/messenger/gnunet-service-messenger_message_recv.c
 create mode 100644 src/messenger/gnunet-service-messenger_message_recv.h
 create mode 100644 src/messenger/gnunet-service-messenger_message_send.c
 create mode 100644 src/messenger/gnunet-service-messenger_message_send.h
 create mode 100644 src/messenger/gnunet-service-messenger_message_store.c
 create mode 100644 src/messenger/gnunet-service-messenger_message_store.h
 create mode 100644 src/messenger/gnunet-service-messenger_room.c
 create mode 100644 src/messenger/gnunet-service-messenger_room.h
 create mode 100644 src/messenger/gnunet-service-messenger_service.c
 create mode 100644 src/messenger/gnunet-service-messenger_service.h
 create mode 100644 src/messenger/gnunet-service-messenger_tunnel.c
 create mode 100644 src/messenger/gnunet-service-messenger_tunnel.h
 create mode 100644 src/messenger/gnunet-service-messenger_util.c
 create mode 100644 src/messenger/gnunet-service-messenger_util.h
 create mode 100644 src/messenger/messenger.conf.in
 create mode 100644 src/messenger/messenger_api.c
 create mode 100644 src/messenger/messenger_api_contact.c
 create mode 100644 src/messenger/messenger_api_contact.h
 copy src/{nat/gnunet-service-nat.h => messenger/messenger_api_ego.h} (60%)
 create mode 100644 src/messenger/messenger_api_handle.c
 create mode 100644 src/messenger/messenger_api_handle.h
 create mode 100644 src/messenger/messenger_api_list_tunnels.c
 create mode 100644 src/messenger/messenger_api_list_tunnels.h
 create mode 100644 src/messenger/messenger_api_message.c
 create mode 100644 src/messenger/messenger_api_message.h
 create mode 100644 src/messenger/messenger_api_room.c
 create mode 100644 src/messenger/messenger_api_room.h
 create mode 100644 src/messenger/test_messenger.c
 create mode 100644 src/messenger/test_messenger_anonymous.c
 create mode 100644 src/messenger/test_messenger_comm0.c

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

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]