gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [gnunet] branch master updated: skeleton for TCP communicat


From: gnunet
Subject: [GNUnet-SVN] [gnunet] branch master updated: skeleton for TCP communicator
Date: Sat, 26 Jan 2019 19:51:35 +0100

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

grothoff pushed a commit to branch master
in repository gnunet.

The following commit(s) were added to refs/heads/master by this push:
     new a5a54c510 skeleton for TCP communicator
a5a54c510 is described below

commit a5a54c51011a9498e72b2a9341b35333c6beef1a
Author: Christian Grothoff <address@hidden>
AuthorDate: Sat Jan 26 19:51:30 2019 +0100

    skeleton for TCP communicator
---
 src/include/gnunet_signatures.h         |    5 +
 src/transport/Makefile.am               |    9 +
 src/transport/gnunet-communicator-tcp.c | 1093 +++++++++++++++++++++++++++++++
 3 files changed, 1107 insertions(+)

diff --git a/src/include/gnunet_signatures.h b/src/include/gnunet_signatures.h
index 3c2413d99..738a414ab 100644
--- a/src/include/gnunet_signatures.h
+++ b/src/include/gnunet_signatures.h
@@ -202,6 +202,11 @@ extern "C"
  */
 #define GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL 30
 
+/**
+ * Signature used by TCP communicator handshake,
+ */ 
+#define GNUNET_SIGNATURE_COMMUNICATOR_TCP_HANDSHAKE 31
+
 #if 0                           /* keep Emacsens' auto-indent happy */
 {
 #endif
diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am
index ab4ef4bfa..0df3e4e27 100644
--- a/src/transport/Makefile.am
+++ b/src/transport/Makefile.am
@@ -142,6 +142,7 @@ endif
 noinst_PROGRAMS = \
  gnunet-transport-profiler \
  gnunet-communicator-unix \
+ gnunet-communicator-tcp \
  gnunet-service-tng \
  $(WLAN_BIN_SENDER) \
  $(WLAN_BIN_RECEIVER)
@@ -251,6 +252,14 @@ gnunet_communicator_unix_LDADD = \
   $(top_builddir)/src/statistics/libgnunetstatistics.la \
   $(top_builddir)/src/util/libgnunetutil.la
 
+gnunet_communicator_tcp_SOURCES = \
+ gnunet-communicator-tcp.c
+gnunet_communicator_tcp_LDADD = \
+  libgnunettransportcommunicator.la \
+  $(top_builddir)/src/statistics/libgnunetstatistics.la \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(LIBGCRYPT_LIBS) 
+
 
 gnunet_helper_transport_wlan_SOURCES = \
  gnunet-helper-transport-wlan.c
diff --git a/src/transport/gnunet-communicator-tcp.c 
b/src/transport/gnunet-communicator-tcp.c
new file mode 100644
index 000000000..6e3faa44a
--- /dev/null
+++ b/src/transport/gnunet-communicator-tcp.c
@@ -0,0 +1,1093 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2010-2014, 2018 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 transport/gnunet-communicator-tcp.c
+ * @brief Transport plugin using TCP.
+ * @author Christian Grothoff
+ *
+ * TODO:
+ * - lots of basic adaptations (see FIXMEs)
+ * - better message queue management
+ * - actually encrypt, hmac, decrypt
+ * - actually transmit
+ * - 
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_protocols.h"
+#include "gnunet_constants.h"
+#include "gnunet_nt_lib.h"
+#include "gnunet_statistics_service.h"
+#include "gnunet_transport_communication_service.h"
+
+/**
+ * How many messages do we keep at most in the queue to the
+ * transport service before we start to drop (default,
+ * can be changed via the configuration file).
+ * Should be _below_ the level of the communicator API, as
+ * otherwise we may read messages just to have them dropped
+ * by the communicator API.
+ */
+#define DEFAULT_MAX_QUEUE_LENGTH 8
+
+/**
+ * Address prefix used by the communicator.
+ */
+#define COMMUNICATOR_ADDRESS_PREFIX "tcp"
+
+/**
+ * Configuration section used by the communicator.
+ */
+#define COMMUNICATOR_CONFIG_SECTION "communicator-tcp"
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * TCP initial bytes on the wire (in either direction), used to 
+ * establish a shared secret.
+ */
+struct TCPHandshake
+{
+  /**
+   * First bytes: ephemeral key for KX.
+   */
+  struct GNUNET_CRYPTO_EcdhePublicKey ephemeral;
+
+};
+
+
+/**
+ * Signature we use to verify that the ephemeral key was really chosen by
+ * the specified sender.
+ */
+struct TcpHandshakeSignature
+{
+  /**
+   * Purpose must be #GNUNET_SIGNATURE_COMMUNICATOR_TCP_HANDSHAKE
+   */
+  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+  /**
+   * Identity of the inititor of the TCP connection (TCP client).
+   */ 
+  struct GNUNET_PeerIdentity sender;
+
+  /**
+   * Presumed identity of the target of the TCP connection (TCP server)
+   */ 
+  struct GNUNET_PeerIdentity receiver;
+
+  /**
+   * Ephemeral key used by the @e sender.
+   */ 
+  struct GNUNET_CRYPTO_EcdhePublicKey ephemeral;
+
+  /**
+   * Monotonic time of @e sender, to possibly help detect replay attacks
+   * (if receiver persists times by sender).
+   */ 
+  struct GNUNET_TIME_AbsoluteNBO monotonic_time;
+};
+
+
+/**
+ * Encrypted continuation of TCP initial handshake.
+ */
+struct TCPConfirmation
+{
+  /**
+   * Sender's identity
+   */
+  struct GNUNET_PeerIdentity sender;
+
+  /**
+   * Sender's signature of type #GNUNET_SIGNATURE_COMMUNICATOR_TCP_HANDSHAKE
+   */
+  struct GNUNET_CRYPTO_EddsaSignature sender_sig;
+
+  /**
+   * Monotonic time of @e sender, to possibly help detect replay attacks
+   * (if receiver persists times by sender).
+   */ 
+  struct GNUNET_TIME_AbsoluteNBO monotonic_time;
+
+};
+
+
+/**
+ * TCP message box.  Always sent encrypted!
+ */ 
+struct TCPBox
+{
+  
+  /**
+   * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_BOX.
+   */ 
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * HMAC for the following encrypted message.  Yes, we MUST use
+   * mac-then-encrypt here, as we want to hide the message sizes on
+   * the wire (zero plaintext design!).  Using CTR mode padding oracle
+   * attacks do not apply.  Besides, due to the use of ephemeral keys
+   * (hopefully with effective replay protection from monotonic time!)
+   * the attacker is limited in using the oracle.
+   */ 
+  struct GNUNET_ShortHashCode hmac;
+
+  /* followed by as may bytes of payload as indicated in @e header */
+  
+};
+
+
+/**
+ * TCP rekey message box.  Always sent encrypted!  Data after
+ * this message will use the new key.
+ */ 
+struct TCPRekey
+{
+
+  /**
+   * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_REKEY.
+   */ 
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * HMAC for the following encrypted message.  Yes, we MUST use
+   * mac-then-encrypt here, as we want to hide the message sizes on
+   * the wire (zero plaintext design!).  Using CTR mode padding oracle
+   * attacks do not apply.  Besides, due to the use of ephemeral keys
+   * (hopefully with effective replay protection from monotonic time!)
+   * the attacker is limited in using the oracle.
+   */ 
+  struct GNUNET_ShortHashCode hmac;
+
+  /**
+   * New ephemeral key.
+   */ 
+  struct GNUNET_CRYPTO_EcdhePublicKey ephemeral;
+  
+  /**
+   * Sender's signature of type #GNUNET_SIGNATURE_COMMUNICATOR_TCP_HANDSHAKE
+   */
+  struct GNUNET_CRYPTO_EddsaSignature sender_sig;
+
+  /**
+   * Monotonic time of @e sender, to possibly help detect replay attacks
+   * (if receiver persists times by sender).
+   */ 
+  struct GNUNET_TIME_AbsoluteNBO monotonic_time;
+
+};
+
+
+/**
+ * TCP finish. Sender asks for the connection to be closed.
+ * Needed/useful in case we drop RST/FIN packets on the GNUnet
+ * port due to the possibility of malicious RST/FIN injection.
+ */ 
+struct TCPFinish
+{
+
+  /**
+   * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_FINISH.
+   */ 
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * HMAC for the following encrypted message.  Yes, we MUST use
+   * mac-then-encrypt here, as we want to hide the message sizes on
+   * the wire (zero plaintext design!).  Using CTR mode padding oracle
+   * attacks do not apply.  Besides, due to the use of ephemeral keys
+   * (hopefully with effective replay protection from monotonic time!)
+   * the attacker is limited in using the oracle.
+   */ 
+  struct GNUNET_ShortHashCode hmac;
+
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+
+/**
+ * Handle for a queue.
+ */
+struct Queue
+{
+
+  /**
+   * To whom are we talking to.
+   */
+  struct GNUNET_PeerIdentity target;
+
+  /**
+   * socket that we transmit all data with on this queue
+   */
+  struct GNUNET_NETWORK_Handle *sock;
+
+  /**
+   * cipher for decryption of incoming data.
+   */ 
+  gcry_cipher_hd_t in_cipher;
+
+  /**
+   * cipher for encryption of outgoing data.
+   */
+  gcry_cipher_hd_t out_cipher;
+
+  /**
+   * Shared secret for HMAC verification on incoming data.
+   */ 
+  struct GNUNET_HashCode in_hmac;
+
+  /**
+   * Shared secret for HMAC generation on outgoing data.
+   */ 
+  struct GNUNET_HashCode out_hmac;
+
+  /**
+   * ID of read task for this connection.
+   */
+  struct GNUNET_SCHEDULER_Task *read_task;
+
+  /**
+   * ID of write task for this connection.
+   */
+  struct GNUNET_SCHEDULER_Task *write_task;
+
+  /**
+   * Address of the other peer.
+   */
+  struct sockaddr *address;
+  
+  /**
+   * How many more bytes may we sent with the current @e out_cipher
+   * before we should rekey?
+   */
+  uint64_t rekey_left_bytes;
+
+  /**
+   * Until what time may we sent with the current @e out_cipher
+   * before we should rekey?
+   */
+  struct GNUNET_TIME_Absolute rekey_time;
+  
+  /**
+   * Length of the address.
+   */
+  socklen_t address_len;
+
+  /**
+   * Message currently scheduled for transmission, non-NULL if and only
+   * if this queue is in the #queue_head DLL.
+   */
+  const struct GNUNET_MessageHeader *msg;
+
+  /**
+   * Message queue we are providing for the #ch.
+   */
+  struct GNUNET_MQ_Handle *mq;
+
+  /**
+   * handle for this queue with the #ch.
+   */
+  struct GNUNET_TRANSPORT_QueueHandle *qh;
+
+  /**
+   * Number of bytes we currently have in our write queue.
+   */
+  unsigned long long bytes_in_queue;
+
+  /**
+   * Timeout for this queue.
+   */
+  struct GNUNET_TIME_Absolute timeout;
+
+  /**
+   * Which network type does this queue use?
+   */
+  enum GNUNET_NetworkType nt;
+  
+};
+
+
+/**
+ * ID of listen task
+ */
+static struct GNUNET_SCHEDULER_Task *listen_task;
+
+/**
+ * Number of messages we currently have in our queues towards the transport 
service.
+ */
+static unsigned long long delivering_messages;
+
+/**
+ * Maximum queue length before we stop reading towards the transport service.
+ */
+static unsigned long long max_queue_length;
+
+/**
+ * For logging statistics.
+ */
+static struct GNUNET_STATISTICS_Handle *stats;
+
+/**
+ * Our environment.
+ */
+static struct GNUNET_TRANSPORT_CommunicatorHandle *ch;
+
+/**
+ * Queues (map from peer identity to `struct Queue`)
+ */
+static struct GNUNET_CONTAINER_MultiPeerMap *queue_map;
+
+/**
+ * Listen socket.
+ */
+static struct GNUNET_NETWORK_Handle *listen_sock;
+
+/**
+ * Handle to the operation that publishes our address.
+ */
+static struct GNUNET_TRANSPORT_AddressIdentifier *ai;
+
+
+/**
+ * We have been notified that our listen socket has something to
+ * read. Do the read and reschedule this function to be called again
+ * once more is available.
+ *
+ * @param cls NULL
+ */
+static void
+listen_cb (void *cls);
+
+
+/**
+ * Functions with this signature are called whenever we need
+ * to close a queue due to a disconnect or failure to
+ * establish a connection.
+ *
+ * @param queue queue to close down
+ */
+static void
+queue_destroy (struct Queue *queue)
+{
+  struct GNUNET_MQ_Handle *mq;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Disconnecting queue for peer `%s'\n",
+             GNUNET_i2s (&queue->target));
+  if (NULL != (mq = queue->mq))
+  {
+    queue->mq = NULL;
+    GNUNET_MQ_destroy (mq);
+  }
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CONTAINER_multipeermap_remove (queue_map,
+                                                      &queue->target,
+                                                      queue));
+  GNUNET_STATISTICS_set (stats,
+                        "# UNIX queues active",
+                        GNUNET_CONTAINER_multipeermap_size (queue_map),
+                        GNUNET_NO);
+  if (NULL != queue->read_task)
+  {
+    GNUNET_SCHEDULER_cancel (queue->read_task);
+    queue->read_task = NULL;
+  }
+  if (NULL != queue->write_task)
+  {
+    GNUNET_SCHEDULER_cancel (queue->write_task);
+    queue->write_task = NULL;
+  }
+  GNUNET_NETWORK_socket_close (queue->sock);
+  gcry_cipher_close (queue->in_cipher);
+  gcry_cipher_close (queue->out_cipher);
+  GNUNET_free (queue->address);
+  GNUNET_free (queue);
+  if (NULL == listen_task)
+    listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                                listen_sock,
+                                                &listen_cb,
+                                                NULL);
+
+}
+
+
+/**
+ * Queue read task. If we hit the timeout, disconnect it
+ *
+ * @param cls the `struct Queue *` to disconnect
+ */
+static void
+queue_read (void *cls)
+{
+  struct Queue *queue = cls;
+  struct GNUNET_TIME_Relative left;
+
+  queue->read_task = NULL;
+  /* CHECK IF READ-ready, then perform read! */
+  
+  left = GNUNET_TIME_absolute_get_remaining (queue->timeout);
+  if (0 != left.rel_value_us)
+  {
+    /* not actually our turn yet, but let's at least update
+       the monitor, it may think we're about to die ... */
+    queue->read_task
+      = GNUNET_SCHEDULER_add_read_net (left,
+                                      queue->sock,
+                                      &queue_read,
+                                      queue);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Queue %p was idle for %s, disconnecting\n",
+             queue,
+             GNUNET_STRINGS_relative_time_to_string 
(GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
+                                                     GNUNET_YES));
+  // FIXME: try to send 'finish' message first!?
+  queue_destroy (queue);
+}
+
+
+/**
+ * Increment queue timeout due to activity.  We do not immediately
+ * notify the monitor here as that might generate excessive
+ * signalling.
+ *
+ * @param queue queue for which the timeout should be rescheduled
+ */
+static void
+reschedule_queue_timeout (struct Queue *queue)
+{
+  GNUNET_assert (NULL != queue->read_task);
+  queue->timeout
+    = GNUNET_TIME_relative_to_absolute 
(GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
+}
+
+
+/**
+ * Convert TCP bind specification to a `struct sockaddr *`
+ *
+ * @param bindto bind specification to convert
+ * @param[out] sock_len set to the length of the address
+ * @return converted bindto specification
+ */
+static struct sockaddr *
+tcp_address_to_sockaddr (const char *bindto,
+                        socklen_t *sock_len)
+{
+  struct sockaddr *in;
+  size_t slen;
+
+  /* FIXME: parse, allocate, return! */
+  return NULL;
+}
+
+
+/**
+ * We have been notified that our socket is ready to write.
+ * Then reschedule this function to be called again once more is available.
+ *
+ * @param cls a `struct Queue`
+ */
+static void
+queue_write (void *cls)
+{
+  struct Queue *queue = cls;
+  const struct GNUNET_MessageHeader *msg = queue->msg;
+  size_t msg_size = ntohs (msg->size);
+
+  queue->write_task = NULL;
+  /* FIXME: send 'msg' */
+  /* FIXME: check if we have more messages pending */
+  queue->write_task 
+    = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                     queue->sock,
+                                     &queue_write,
+                                     queue);
+}
+
+
+/**
+ * Signature of functions implementing the sending functionality of a
+ * message queue.
+ *
+ * @param mq the message queue
+ * @param msg the message to send
+ * @param impl_state our `struct Queue`
+ */
+static void
+mq_send (struct GNUNET_MQ_Handle *mq,
+        const struct GNUNET_MessageHeader *msg,
+        void *impl_state)
+{
+  struct Queue *queue = impl_state;
+
+  GNUNET_assert (mq == queue->mq);
+  GNUNET_assert (NULL == queue->msg);
+  queue->msg = msg;
+  GNUNET_assert (NULL != queue->sock);
+  if (NULL == queue->write_task)
+    queue->write_task =
+      GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                      queue->sock,
+                                      &queue_write,
+                                     queue);
+}
+
+
+/**
+ * Signature of functions implementing the destruction of a message
+ * queue.  Implementations must not free @a mq, but should take care
+ * of @a impl_state.
+ *
+ * @param mq the message queue to destroy
+ * @param impl_state our `struct Queue`
+ */
+static void
+mq_destroy (struct GNUNET_MQ_Handle *mq,
+           void *impl_state)
+{
+  struct Queue *queue = impl_state;
+
+  if (mq == queue->mq)
+  {
+    queue->mq = NULL;
+    queue_destroy (queue);
+  }
+}
+
+
+/**
+ * Implementation function that cancels the currently sent message.
+ *
+ * @param mq message queue
+ * @param impl_state our `struct Queue`
+ */
+static void
+mq_cancel (struct GNUNET_MQ_Handle *mq,
+          void *impl_state)
+{
+  struct Queue *queue = impl_state;
+
+  GNUNET_assert (NULL != queue->msg);
+  queue->msg = NULL;
+  GNUNET_assert (NULL != queue->write_task);
+  if (1) // FIXME?
+  {
+    GNUNET_SCHEDULER_cancel (queue->write_task);
+    queue->write_task = NULL;
+  }
+}
+
+
+/**
+ * Generic error handler, called with the appropriate
+ * error code and the same closure specified at the creation of
+ * the message queue.
+ * Not every message queue implementation supports an error handler.
+ *
+ * @param cls our `struct Queue`
+ * @param error error code
+ */
+static void
+mq_error (void *cls,
+         enum GNUNET_MQ_Error error)
+{
+  struct Queue *queue = cls;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+             "TCP MQ error in queue to %s: %d\n",
+             GNUNET_i2s (&queue->target),
+             (int) error);
+  queue_destroy (queue);
+}
+
+
+/**
+ * Creates a new outbound queue the transport service will use to send
+ * data to another peer.
+ *
+ * @param peer the target peer
+ * @param cs inbound or outbound queue
+ * @param in the address
+ * @param in_len number of bytes in @a in
+ * @return the queue or NULL of max connections exceeded
+ */
+static struct Queue *
+setup_queue (struct GNUNET_NETWORK_Handle *sock,
+            enum GNUNET_TRANSPORT_ConnectionStatus cs,
+            const struct sockaddr *in,
+            socklen_t in_len)
+{
+  struct Queue *queue;
+
+  queue = GNUNET_new (struct Queue);
+  // queue->target = *target; // FIXME: handle case that we don't know the 
target yet!
+  queue->address = GNUNET_memdup (in,
+                                 in_len);
+  queue->address_len = in_len;
+  queue->sock = sock; 
+  queue->nt = 0; // FIXME: determine NT!
+  (void) GNUNET_CONTAINER_multipeermap_put (queue_map,
+                                           &queue->target,
+                                           queue,
+                                           
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+  GNUNET_STATISTICS_set (stats,
+                        "# queues active",
+                        GNUNET_CONTAINER_multipeermap_size (queue_map),
+                        GNUNET_NO);
+  queue->timeout
+    = GNUNET_TIME_relative_to_absolute 
(GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
+  queue->read_task
+    = GNUNET_SCHEDULER_add_read_net (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
+                                    queue->sock,
+                                    &queue_read,
+                                    queue);
+  queue->mq
+    = GNUNET_MQ_queue_for_callbacks (&mq_send,
+                                    &mq_destroy,
+                                    &mq_cancel,
+                                    queue,
+                                    NULL,
+                                    &mq_error,
+                                    queue);
+  {
+    char *foreign_addr;
+
+    switch (queue->address->sa_family)
+    {
+    case AF_INET:
+      GNUNET_asprintf (&foreign_addr,
+                      "%s-%s:%d",
+                      COMMUNICATOR_ADDRESS_PREFIX,
+                      "inet-ntop-fixme",
+                      4242);
+      break;
+    case AF_INET6:
+      GNUNET_asprintf (&foreign_addr,
+                      "%s-%s:%d",
+                      COMMUNICATOR_ADDRESS_PREFIX,
+                      "inet-ntop-fixme",
+                      4242);
+      break;
+    default:
+      GNUNET_assert (0);
+    }
+    queue->qh
+      = GNUNET_TRANSPORT_communicator_mq_add (ch,
+                                             &queue->target,
+                                             foreign_addr,
+                                             0 /* no MTU */,
+                                             queue->nt,
+                                             cs,
+                                             queue->mq);
+    GNUNET_free (foreign_addr);
+  }
+  return queue;
+}
+
+
+/**
+ * We have been notified that our listen socket has something to
+ * read. Do the read and reschedule this function to be called again
+ * once more is available.
+ *
+ * @param cls NULL
+ */
+static void
+listen_cb (void *cls);
+
+
+/**
+ * We have been notified that our listen socket has something to
+ * read. Do the read and reschedule this function to be called again
+ * once more is available.
+ *
+ * @param cls NULL
+ */
+static void
+listen_cb (void *cls)
+{
+  char buf[65536] GNUNET_ALIGN;
+  struct Queue *queue;
+  struct sockaddr_storage in;
+  socklen_t addrlen;
+  ssize_t ret;
+  uint16_t msize;
+  struct GNUNET_NETWORK_Handle *sock;
+
+  listen_task = NULL;
+  GNUNET_assert (NULL != listen_sock);
+  addrlen = sizeof (in);
+  memset (&in,
+         0,
+         sizeof (in));
+  sock = GNUNET_NETWORK_socket_accept (listen_sock,
+                                      (struct sockaddr *) &in,
+                                      &addrlen);
+  if ( (NULL == sock) &&
+       ( (EMFILE == errno) ||
+        (ENFILE == errno) ) )
+    return; /* system limit reached, wait until connection goes down */
+  listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                              listen_sock,
+                                              &listen_cb,
+                                              NULL);
+  if ( (NULL == sock) &&
+       ( (EAGAIN == errno) ||
+        (ENOBUFS == errno) ) )
+    return;
+  if (NULL == sock)
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                         "accept");
+    return;
+  }
+  queue = setup_queue (sock,
+                      GNUNET_TRANSPORT_CS_INBOUND,
+                      (struct sockaddr *) &in,
+                      addrlen);
+  if (NULL == queue)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               _("Maximum number of TCP connections exceeded, dropping 
incoming connection\n"));
+    return;
+  }
+}
+
+
+/**
+ * Function called by the transport service to initialize a
+ * message queue given address information about another peer.
+ * If and when the communication channel is established, the
+ * communicator must call #GNUNET_TRANSPORT_communicator_mq_add()
+ * to notify the service that the channel is now up.  It is
+ * the responsibility of the communicator to manage sane
+ * retries and timeouts for any @a peer/@a address combination
+ * provided by the transport service.  Timeouts and retries
+ * do not need to be signalled to the transport service.
+ *
+ * @param cls closure
+ * @param peer identity of the other peer
+ * @param address where to send the message, human-readable
+ *        communicator-specific format, 0-terminated, UTF-8
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if the provided address is 
invalid
+ */
+static int
+mq_init (void *cls,
+        const struct GNUNET_PeerIdentity *peer,
+        const char *address)
+{
+  struct Queue *queue;
+  const char *path;
+  struct sockaddr *in;
+  socklen_t in_len;
+
+  if (0 != strncmp (address,
+                   COMMUNICATOR_ADDRESS_PREFIX "-",
+                   strlen (COMMUNICATOR_ADDRESS_PREFIX "-")))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  path = &address[strlen (COMMUNICATOR_ADDRESS_PREFIX "-")];
+  in = tcp_address_to_sockaddr (path,
+                               &in_len);
+#if FIXME
+  queue = setup_queue (peer,
+                      GNUNET_TRANSPORT_CS_OUTBOUND,
+                      in,
+                      in_len);
+#endif
+  GNUNET_free (in);
+  if (NULL == queue)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+               "Failed to setup queue to %s at `%s'\n",
+               GNUNET_i2s (peer),
+               path);
+    return GNUNET_NO;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Iterator over all message queues to clean up.
+ *
+ * @param cls NULL
+ * @param target unused
+ * @param value the queue to destroy
+ * @return #GNUNET_OK to continue to iterate
+ */
+static int
+get_queue_delete_it (void *cls,
+                    const struct GNUNET_PeerIdentity *target,
+                    void *value)
+{
+  struct Queue *queue = value;
+
+  (void) cls;
+  (void) target;
+  queue_destroy (queue);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Shutdown the UNIX communicator.
+ *
+ * @param cls NULL (always)
+ */
+static void
+do_shutdown (void *cls)
+{
+  if (NULL != listen_task)
+  {
+    GNUNET_SCHEDULER_cancel (listen_task);
+    listen_task = NULL;
+  }
+  if (NULL != listen_sock)
+  {
+    GNUNET_break (GNUNET_OK ==
+                  GNUNET_NETWORK_socket_close (listen_sock));
+    listen_sock = NULL;
+  }
+  GNUNET_CONTAINER_multipeermap_iterate (queue_map,
+                                        &get_queue_delete_it,
+                                         NULL);
+  GNUNET_CONTAINER_multipeermap_destroy (queue_map);
+  if (NULL != ai)
+  {
+    GNUNET_TRANSPORT_communicator_address_remove (ai);
+    ai = NULL;
+  }
+  if (NULL != ch)
+  {
+    GNUNET_TRANSPORT_communicator_disconnect (ch);
+    ch = NULL;
+  }
+  if (NULL != stats)
+  {
+    GNUNET_STATISTICS_destroy (stats,
+                              GNUNET_NO);
+    stats = NULL;
+  }
+}
+
+
+/**
+ * Function called when the transport service has received an
+ * acknowledgement for this communicator (!) via a different return
+ * path.
+ *
+ * Not applicable for UNIX.
+ *
+ * @param cls closure
+ * @param sender which peer sent the notification
+ * @param msg payload
+ */
+static void
+enc_notify_cb (void *cls,
+               const struct GNUNET_PeerIdentity *sender,
+               const struct GNUNET_MessageHeader *msg)
+{
+  (void) cls;
+  (void) sender;
+  (void) msg;
+  GNUNET_break_op (0);
+}
+
+
+/**
+ * Setup communicator and launch network interactions.
+ *
+ * @param cls NULL (always)
+ * @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)
+{
+  char *bindto;
+  struct sockaddr *in;
+  socklen_t in_len;
+  char *my_addr;
+  (void) cls;
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                              COMMUNICATOR_CONFIG_SECTION,
+                                              "BINDTO",
+                                              &bindto))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               COMMUNICATOR_CONFIG_SECTION,
+                               "BINDTO");
+    return;
+  }
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_number (cfg,
+                                            COMMUNICATOR_CONFIG_SECTION,
+                                            "MAX_QUEUE_LENGTH",
+                                            &max_queue_length))
+    max_queue_length = DEFAULT_MAX_QUEUE_LENGTH;
+
+  in = tcp_address_to_sockaddr (bindto,
+                               &in_len);
+  if (NULL == in)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Failed to setup TCP socket address with path `%s'\n",
+               bindto);
+    GNUNET_free (bindto);
+    return;
+  }
+  listen_sock = GNUNET_NETWORK_socket_create (in->sa_family,
+                                             SOCK_STREAM,
+                                             IPPROTO_TCP);
+  if (NULL == listen_sock)
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+                        "socket");
+    GNUNET_free (in);
+    GNUNET_free (bindto);
+    return;
+  }
+  if (GNUNET_OK !=
+      GNUNET_NETWORK_socket_bind (listen_sock,
+                                  in,
+                                 in_len))
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                             "bind",
+                             bindto);
+    GNUNET_NETWORK_socket_close (listen_sock);
+    listen_sock = NULL;
+    GNUNET_free (in);
+    GNUNET_free (bindto);
+    return;
+  }
+  GNUNET_free (in);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Bound to `%s'\n",
+             bindto);
+  stats = GNUNET_STATISTICS_create ("C-TCP",
+                                   cfg);
+  GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
+                                NULL);
+  listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                              listen_sock,
+                                              &listen_cb,
+                                              NULL);
+  queue_map = GNUNET_CONTAINER_multipeermap_create (10,
+                                                     GNUNET_NO);
+  ch = GNUNET_TRANSPORT_communicator_connect (cfg,
+                                             COMMUNICATOR_CONFIG_SECTION,
+                                             COMMUNICATOR_ADDRESS_PREFIX,
+                                              GNUNET_TRANSPORT_CC_RELIABLE,
+                                             &mq_init,
+                                             NULL,
+                                              &enc_notify_cb,
+                                              NULL);
+  if (NULL == ch)
+  {
+    GNUNET_break (0);
+    GNUNET_SCHEDULER_shutdown ();
+    GNUNET_free (bindto);
+    return;
+  }
+  // FIXME: bindto is wrong here, we MUST get our external
+  // IP address and really look at 'in' here as we might
+  // be bound to loopback or some other specific IP address!
+  GNUNET_asprintf (&my_addr,
+                  "%s-%s",
+                  COMMUNICATOR_ADDRESS_PREFIX,
+                  bindto);
+  GNUNET_free (bindto);
+  // FIXME: based on our bindto, we might not be able to tell the
+  // network type yet! What to do here!?
+  ai = GNUNET_TRANSPORT_communicator_address_add (ch,
+                                                 my_addr,
+                                                 GNUNET_NT_LOOPBACK, // FIXME: 
wrong NT!
+                                                 GNUNET_TIME_UNIT_FOREVER_REL);
+  GNUNET_free (my_addr);
+}
+
+
+/**
+ * The main function for the UNIX communicator.
+ *
+ * @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 *const *argv)
+{
+  static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+    GNUNET_GETOPT_OPTION_END
+  };
+  int ret;
+
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_get_utf8_args (argc, argv,
+                                   &argc, &argv))
+    return 2;
+
+  ret =
+      (GNUNET_OK ==
+       GNUNET_PROGRAM_run (argc, argv,
+                           "gnunet-communicator-tcp",
+                           _("GNUnet TCP communicator"),
+                           options,
+                          &run,
+                          NULL)) ? 0 : 1;
+  GNUNET_free ((void*) argv);
+  return ret;
+}
+
+
+#if defined(LINUX) && defined(__GLIBC__)
+#include <malloc.h>
+
+/**
+ * MINIMIZE heap size (way below 128k) since this process doesn't need much.
+ */
+void __attribute__ ((constructor))
+GNUNET_ARM_memory_init ()
+{
+  mallopt (M_TRIM_THRESHOLD, 4 * 1024);
+  mallopt (M_TOP_PAD, 1 * 1024);
+  malloc_trim (0);
+}
+#endif
+
+/* end of gnunet-communicator-tcp.c */

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

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