gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [gnunet] branch master updated (cadf55989 -> d80214feb)


From: gnunet
Subject: [GNUnet-SVN] [gnunet] branch master updated (cadf55989 -> d80214feb)
Date: Mon, 28 Jan 2019 18:08:01 +0100

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

grothoff pushed a change to branch master
in repository gnunet.

    from cadf55989 more work on TCP communicator
     new 07533eec5 more work on TCP communicator, almost there
     new 0157a3800 rekeys
     new 5f8301275 improve NAT API: allow client to store associated data with 
address
     new d80214feb first iteraton of TCP communicator done

The 4 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:
 src/include/gnunet_nat_service.h             |   3 +
 src/nat-auto/nat_auto_api_test.c             |   4 +
 src/nat/gnunet-nat.c                         |   6 +
 src/nat/nat_api.c                            |   8 +
 src/transport/.gitignore                     |   1 +
 src/transport/Makefile.am                    |   2 +
 src/transport/gnunet-communicator-tcp.c      | 723 ++++++++++++++++++++++-----
 src/transport/plugin_transport_http_server.c |   4 +
 src/transport/plugin_transport_tcp.c         |   4 +
 src/transport/plugin_transport_udp.c         |   4 +
 10 files changed, 642 insertions(+), 117 deletions(-)

diff --git a/src/include/gnunet_nat_service.h b/src/include/gnunet_nat_service.h
index c1e2f0a39..b96b2386e 100644
--- a/src/include/gnunet_nat_service.h
+++ b/src/include/gnunet_nat_service.h
@@ -279,6 +279,8 @@ enum GNUNET_NAT_Type
  * a function to call whenever our set of 'valid' addresses changes.
  *
  * @param cls closure
+ * @param app_ctx[in,out] location where the app can store stuff
+ *                  on add and retrieve it on remove
  * @param add_remove #GNUNET_YES to add a new public IP address, 
  *                   #GNUNET_NO to remove a previous (now invalid) one
  * @param ac address class the address belongs to
@@ -287,6 +289,7 @@ enum GNUNET_NAT_Type
  */
 typedef void
 (*GNUNET_NAT_AddressCallback) (void *cls,
+                              void **app_ctx,
                                int add_remove,
                               enum GNUNET_NAT_AddressClass ac,
                                const struct sockaddr *addr,
diff --git a/src/nat-auto/nat_auto_api_test.c b/src/nat-auto/nat_auto_api_test.c
index 42ce08721..1511d91ba 100644
--- a/src/nat-auto/nat_auto_api_test.c
+++ b/src/nat-auto/nat_auto_api_test.c
@@ -357,6 +357,8 @@ mq_error_handler (void *cls,
  * Address-callback, used to send message to gnunet-nat-server.
  *
  * @param cls closure
+ * @param app_ctx[in,out] location where the app can store stuff
+ *                  on add and retrieve it on remove
  * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO 
to mean
  *     the previous (now invalid) one
  * @param ac address class the address belongs to
@@ -365,6 +367,7 @@ mq_error_handler (void *cls,
  */
 static void
 addr_cb (void *cls,
+        void **app_ctx,
          int add_remove,
          enum GNUNET_NAT_AddressClass ac,
          const struct sockaddr *addr,
@@ -376,6 +379,7 @@ addr_cb (void *cls,
   struct GNUNET_NAT_AUTO_TestMessage *msg;
   const struct sockaddr_in *sa;
 
+  (void) app_ctx;
   if (GNUNET_YES != add_remove)
     return;
   if (addrlen != sizeof (struct sockaddr_in))
diff --git a/src/nat/gnunet-nat.c b/src/nat/gnunet-nat.c
index 31b6a7268..275553203 100644
--- a/src/nat/gnunet-nat.c
+++ b/src/nat/gnunet-nat.c
@@ -110,6 +110,8 @@ test_finished ()
  * a function to call whenever our set of 'valid' addresses changes.
  *
  * @param cls closure, NULL
+ * @param app_ctx[in,out] location where the app can store stuff
+ *                  on add and retrieve it on remove
  * @param add_remove #GNUNET_YES to add a new public IP address,
  *                   #GNUNET_NO to remove a previous (now invalid) one
  * @param ac address class the address belongs to
@@ -118,11 +120,15 @@ test_finished ()
  */
 static void
 address_cb (void *cls,
+           void **app_ctx,
            int add_remove,
            enum GNUNET_NAT_AddressClass ac,
            const struct sockaddr *addr,
            socklen_t addrlen)
 {
+  (void) cls;
+  (void) app_ctx;
+  
   fprintf (stdout,
            "%s %s (%d)\n",
            add_remove ? "+" : "-",
diff --git a/src/nat/nat_api.c b/src/nat/nat_api.c
index 6ae689b63..04fa366aa 100644
--- a/src/nat/nat_api.c
+++ b/src/nat/nat_api.c
@@ -48,6 +48,12 @@ struct AddrEntry
    */
   struct AddrEntry *prev;
 
+  /**
+   * Place where the application can store data (on add,
+   * and retrieve on remove).
+   */
+  void *app_ctx;
+  
   /**
    * Address class of the address.
    */
@@ -148,6 +154,7 @@ reconnect (struct GNUNET_NAT_Handle *nh)
                                 nh->ae_tail,
                                 ae);
     nh->address_callback (nh->callback_cls,
+                         &ae->app_ctx,
                          GNUNET_NO,
                          ae->ac,
                          (const struct sockaddr *) &ae[1],
@@ -299,6 +306,7 @@ handle_address_change_notification (void *cls,
     GNUNET_free (ae);
   }
   nh->address_callback (nh->callback_cls,
+                       &ae->app_ctx,
                        ntohl (acn->add_remove),
                        ac,
                        sa,
diff --git a/src/transport/.gitignore b/src/transport/.gitignore
index e2f12c230..169604467 100644
--- a/src/transport/.gitignore
+++ b/src/transport/.gitignore
@@ -85,3 +85,4 @@ test_transport_testing_restart
 test_transport_testing_startstop
 gnunet-communicator-unix
 gnunet-service-tng
+gnunet-communicator-tcp
diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am
index 0df3e4e27..80b7f2252 100644
--- a/src/transport/Makefile.am
+++ b/src/transport/Makefile.am
@@ -256,6 +256,8 @@ gnunet_communicator_tcp_SOURCES = \
  gnunet-communicator-tcp.c
 gnunet_communicator_tcp_LDADD = \
   libgnunettransportcommunicator.la \
+  $(top_builddir)/src/nat/libgnunetnatnew.la \
+  $(top_builddir)/src/nt/libgnunetnt.la \
   $(top_builddir)/src/statistics/libgnunetstatistics.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(LIBGCRYPT_LIBS) 
diff --git a/src/transport/gnunet-communicator-tcp.c 
b/src/transport/gnunet-communicator-tcp.c
index a94559bd2..2980ad532 100644
--- a/src/transport/gnunet-communicator-tcp.c
+++ b/src/transport/gnunet-communicator-tcp.c
@@ -24,14 +24,9 @@
  * @author Christian Grothoff
  *
  * TODO:
- * - lots of basic adaptations (see FIXMEs), need NAT service
- *   to determine our own listen IPs! Parsing of bindto spec!
- * - actual decryption and handling of boxes and rekeys!
- * - message queue management: flow control towards CORE!
- *   (stop reading from socket until MQ send to core is done;
- *    will need a counter as ONE read from socket may generate
- *    multiple messages en route to CORE; tricky bit: queue
- *    may die before we get MQ sent-done callbacks!)
+ * - support DNS names in BINDTO option
+ * - support NAT connection reversal method
+ * - support other TCP-specific NAT traversal methods
  */
 #include "platform.h"
 #include "gnunet_util_lib.h"
@@ -39,6 +34,7 @@
 #include "gnunet_signatures.h"
 #include "gnunet_constants.h"
 #include "gnunet_nt_lib.h"
+#include "gnunet_nat_service.h"
 #include "gnunet_statistics_service.h"
 #include "gnunet_transport_communication_service.h"
 
@@ -390,11 +386,20 @@ struct Queue
    */
   struct GNUNET_TIME_Absolute timeout;
 
+  /**
+   * How may messages did we pass from this queue to CORE for which we
+   * have yet to receive an acknoweldgement that CORE is done with
+   * them? If "large" (or even just non-zero), we should throttle
+   * reading to provide flow control.  See also #DEFAULT_MAX_QUEUE_LENGTH
+   * and #max_queue_length.
+   */ 
+  unsigned int backpressure;
+  
   /**
    * Which network type does this queue use?
    */
   enum GNUNET_NetworkType nt;
-
+  
   /**
    * Is MQ awaiting a #GNUNET_MQ_impl_send_continue() call?
    */
@@ -405,12 +410,26 @@ struct Queue
    */
   int finishing;
 
+  /**
+   * Did we technically destroy this queue, but kept the allocation
+   * around because of @e backpressure not being zero yet? Used
+   * simply to delay the final #GNUNET_free() operation until
+   * #core_read_finished_cb() has been called.
+   */
+  int destroyed;
+
   /**
    * #GNUNET_YES after #inject_key() placed the rekey message into the
    * plaintext buffer. Once the plaintext buffer is drained, this
    * means we must switch to the new key material.
    */
   int rekey_state;
+
+  /**
+   * #GNUNET_YES if we just rekeyed and must thus possibly
+   * re-decrypt ciphertext.
+   */
+  int rekeyed;
 };
 
 
@@ -474,11 +493,6 @@ struct ProtoQueue
  */
 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.
  */
@@ -504,11 +518,6 @@ static struct GNUNET_CONTAINER_MultiPeerMap *queue_map;
  */
 static struct GNUNET_NETWORK_Handle *listen_sock;
 
-/**
- * Handle to the operation that publishes our address.
- */
-static struct GNUNET_TRANSPORT_AddressIdentifier *ai;
-
 /**
  * Our public key.
  */
@@ -524,6 +533,16 @@ static struct GNUNET_CRYPTO_EddsaPrivateKey 
*my_private_key;
  */
 static const struct GNUNET_CONFIGURATION_Handle *cfg;
 
+/**
+ * Network scanner to determine network types.
+ */
+static struct GNUNET_NT_InterfaceScanner *is;
+
+/**
+ * Connection to NAT service.
+ */
+static struct GNUNET_NAT_Handle *nat;
+
 /**
  * Protoqueues DLL head.
  */ 
@@ -588,7 +607,10 @@ queue_destroy (struct Queue *queue)
   gcry_cipher_close (queue->in_cipher);
   gcry_cipher_close (queue->out_cipher);
   GNUNET_free (queue->address);
-  GNUNET_free (queue);
+  if (0 != queue->backpressure)
+    queue->destroyed = GNUNET_YES;
+  else
+    GNUNET_free (queue);
   if (NULL == listen_task)
     listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
                                                 listen_sock,
@@ -685,85 +707,83 @@ reschedule_queue_timeout (struct Queue *queue)
  * @param cls the `struct Queue *` to disconnect
  */
 static void
-queue_read (void *cls)
+queue_read (void *cls);
+
+
+/**
+ * Core tells us it is done processing a message that transport
+ * received on a queue with status @a success.
+ *
+ * @param cls a `struct Queue *` where the message originally came from
+ * @param success #GNUNET_OK on success
+ */
+static void
+core_read_finished_cb (void *cls,
+                      int success)
 {
   struct Queue *queue = cls;
-  struct GNUNET_TIME_Relative left;
-  ssize_t rcvd;
 
-  queue->read_task = NULL;
-  rcvd = GNUNET_NETWORK_socket_recv (queue->sock,
-                                    &queue->cread_buf[queue->cread_off],
-                                    BUF_SIZE - queue->cread_off);
-  if (-1 == rcvd)
+  if (GNUNET_OK != success)
+    GNUNET_STATISTICS_update (stats,
+                             "# messages lost in communicator API towards 
CORE",
+                             1,
+                             GNUNET_NO);
+  queue->backpressure--;
+  /* handle deferred queue destruction */
+  if ( (queue->destroyed) &&
+       (0 == queue->backpressure) )
   {
-    if ( (EAGAIN != errno) &&
-        (EINTR != errno) )
-    {
-      GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG,
-                          "recv");
-      queue_finish (queue);
-      return;
-    }
-    /* try again */
-    queue->read_task
-      = GNUNET_SCHEDULER_add_read_net (left,
-                                      queue->sock,
-                                      &queue_read,
-                                      queue);
+    GNUNET_free (queue);
     return;
   }
-  if (0 != rcvd)
-    reschedule_queue_timeout (queue);
-  queue->cread_off += rcvd;
-  if (queue->pread_off < sizeof (queue->pread_buf))
-  {
-    /* FIXME: decrypt */
-  
-    /* FIXME: check plaintext for complete messages, if complete, hand to CORE 
*/
-    /* FIXME: CORE flow control: suspend doing more until CORE has ACKed */
-  }
-  
-  if (BUF_SIZE == queue->cread_off)
-    return; /* buffer full, suspend reading */
-  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 ... */
+  reschedule_queue_timeout (queue);
+  /* possibly unchoke reading, now that CORE made progress */
+  if (NULL == queue->read_task)
     queue->read_task
-      = GNUNET_SCHEDULER_add_read_net (left,
+      = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining 
(queue->timeout),
                                       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));
-  queue_finish (queue);
 }
 
 
 /**
- * Convert TCP bind specification to a `struct sockaddr *`
+ * We received @a plaintext_len bytes of @a plaintext on @a queue.
+ * Pass it on to CORE.  If transmission is actually happening,
+ * increase backpressure counter.
  *
- * @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)
+ * @param queue the queue that received the plaintext
+ * @param plaintext the plaintext that was received
+ * @param plaintext_len number of bytes of plaintext received
+ */ 
+static void
+pass_plaintext_to_core (struct Queue *queue,
+                       const void *plaintext,
+                       size_t plaintext_len)
 {
-  struct sockaddr *in;
-  size_t slen;
+  const struct GNUNET_MessageHeader *hdr = plaintext;
+  int ret;
 
-  /* FIXME: parse, allocate, return! */
-  return NULL;
+  if (ntohs (hdr->size) != plaintext_len)
+  {
+    /* NOTE: If we ever allow multiple CORE messages in one
+       BOX, this will have to change! */
+    GNUNET_break (0);
+    return;
+  }
+  ret = GNUNET_TRANSPORT_communicator_receive (ch,
+                                              &queue->target,
+                                              hdr,
+                                              &core_read_finished_cb,
+                                              queue);
+  if (GNUNET_OK == ret)
+    queue->backpressure++;
+  GNUNET_break (GNUNET_NO != ret); /* backpressure not working!? */
+  if (GNUNET_SYSERR == ret)
+    GNUNET_STATISTICS_update (stats,
+                             "# bytes lost due to CORE not running",
+                             plaintext_len,
+                             GNUNET_NO);
 }
 
 
@@ -850,6 +870,405 @@ setup_in_cipher (const struct 
GNUNET_CRYPTO_EcdhePublicKey *ephemeral,
 }
                
 
+/**
+ * Handle @a rekey message on @a queue. The message was already
+ * HMAC'ed, but we should additionally still check the signature.
+ * Then we need to stop the old cipher and start afresh.
+ *
+ * @param queue the queue @a rekey was received on
+ * @param rekey the rekey message
+ */ 
+static void
+do_rekey (struct Queue *queue,
+         const struct TCPRekey *rekey)
+{
+  struct TcpHandshakeSignature thp;
+
+  thp.purpose.purpose = htonl (GNUNET_SIGNATURE_COMMUNICATOR_TCP_REKEY);
+  thp.purpose.size = htonl (sizeof (thp));
+  thp.sender = queue->target;
+  thp.receiver = my_identity;
+  thp.ephemeral = rekey->ephemeral;
+  thp.monotonic_time = rekey->monotonic_time;
+  if (GNUNET_OK !=
+      GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_COMMUNICATOR_TCP_REKEY,
+                                 &thp.purpose,
+                                 &rekey->sender_sig,
+                                 &queue->target.public_key))
+  {
+    GNUNET_break (0);
+    queue_finish (queue);
+    return;
+  }
+  gcry_cipher_close (queue->in_cipher);
+  queue->rekeyed = GNUNET_YES;
+  setup_in_cipher (&rekey->ephemeral,
+                  queue);
+}
+
+
+/**
+ * Test if we have received a full message in plaintext.
+ * If so, handle it.
+ *
+ * @param queue queue to process inbound plaintext for
+ * @return number of bytes of plaintext handled, 0 for none
+ */ 
+static size_t
+try_handle_plaintext (struct Queue *queue)
+{
+  const struct GNUNET_MessageHeader *hdr
+    = (const struct GNUNET_MessageHeader *) queue->pread_buf;
+  const struct TCPBox *box
+    = (const struct TCPBox *) queue->pread_buf;
+  const struct TCPRekey *rekey
+    = (const struct TCPRekey *) queue->pread_buf;
+  const struct TCPFinish *fin
+    = (const struct TCPFinish *) queue->pread_buf;
+  struct TCPRekey rekeyz;
+  struct TCPFinish finz;
+  struct GNUNET_ShortHashCode tmac;
+  uint16_t type;
+  size_t size = 0; /* make compiler happy */
+
+  if (sizeof (*hdr) > queue->pread_off)
+    return 0; /* not even a header */
+  type = ntohs (hdr->type);
+  switch (type)
+  {
+  case GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_BOX:
+    /* Special case: header size excludes box itself! */
+    if (ntohs (hdr->size) + sizeof (struct TCPBox) > queue->pread_off)
+      return 0;
+    hmac (&queue->in_hmac,
+         &box[1],
+         ntohs (hdr->size),
+         &tmac);
+    if (0 != memcmp (&tmac,
+                    &box->hmac,
+                    sizeof (tmac)))
+    {
+      GNUNET_break_op (0);
+      queue_finish (queue);
+      return 0;
+    }
+    pass_plaintext_to_core (queue,
+                           (const void *) &box[1],
+                           ntohs (hdr->size));
+    size = ntohs (hdr->size) + sizeof (*box);
+    break;
+  case GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_REKEY:
+    if (sizeof (*rekey) > queue->pread_off)
+      return 0;
+    if (ntohs (hdr->size) != sizeof (*rekey))
+    {
+      GNUNET_break_op (0);
+      queue_finish (queue);
+      return 0;
+    }
+    rekeyz = *rekey;
+    memset (&rekeyz.hmac,
+           0,
+           sizeof (rekeyz.hmac));
+    hmac (&queue->in_hmac,
+         &rekeyz,
+         sizeof (rekeyz),
+         &tmac);
+    if (0 != memcmp (&tmac,
+                    &box->hmac,
+                    sizeof (tmac)))
+    {
+      GNUNET_break_op (0);
+      queue_finish (queue);
+      return 0;
+    }
+    do_rekey (queue,
+             rekey);
+    size = ntohs (hdr->size);
+    break;
+  case GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_FINISH:
+    if (sizeof (*fin) > queue->pread_off)
+      return 0;
+    if (ntohs (hdr->size) != sizeof (*fin))
+    {
+      GNUNET_break_op (0);
+      queue_finish (queue);
+      return 0;
+    }
+    finz = *fin;
+    memset (&finz.hmac,
+           0,
+           sizeof (finz.hmac));
+    hmac (&queue->in_hmac,
+         &rekeyz,
+         sizeof (rekeyz),
+         &tmac);
+    if (0 != memcmp (&tmac,
+                    &fin->hmac,
+                    sizeof (tmac)))
+    {
+      GNUNET_break_op (0);
+      queue_finish (queue);
+      return 0;
+    }
+    /* handle FINISH by destroying queue */
+    queue_destroy (queue);
+    break;
+  default:
+    GNUNET_break_op (0);
+    queue_finish (queue);
+    return 0;
+  }
+  GNUNET_assert (0 != size);
+  return size;
+}
+
+
+/**
+ * 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;
+  ssize_t rcvd;
+
+  queue->read_task = NULL;
+  rcvd = GNUNET_NETWORK_socket_recv (queue->sock,
+                                    &queue->cread_buf[queue->cread_off],
+                                    BUF_SIZE - queue->cread_off);
+  if (-1 == rcvd)
+  {
+    if ( (EAGAIN != errno) &&
+        (EINTR != errno) )
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG,
+                          "recv");
+      queue_finish (queue);
+      return;
+    }
+    /* try again */
+    queue->read_task
+      = GNUNET_SCHEDULER_add_read_net (left,
+                                      queue->sock,
+                                      &queue_read,
+                                      queue);
+    return;
+  }
+  if (0 != rcvd)
+    reschedule_queue_timeout (queue);
+  queue->cread_off += rcvd;
+  while ( (queue->pread_off < sizeof (queue->pread_buf)) &&
+         (queue->cread_off > 0) )
+  {
+    size_t max = GNUNET_MIN (sizeof (queue->pread_buf) - queue->pread_off,
+                            queue->cread_off);
+    size_t done;
+    size_t total;
+    
+    GNUNET_assert (0 ==
+                  gcry_cipher_decrypt (queue->in_cipher,
+                                       &queue->pread_buf[queue->pread_off],
+                                       max,
+                                       queue->cread_buf,
+                                       max));
+    queue->pread_off += max;
+    total = 0;
+    while ( (GNUNET_NO == queue->rekeyed) &&
+           (0 != (done = try_handle_plaintext (queue))) )          
+    {
+      /* 'done' bytes of plaintext were used, shift buffer */
+      GNUNET_assert (done <= queue->pread_off);
+      /* NOTE: this memmove() could possibly sometimes be
+        avoided if we pass 'total' into try_handle_plaintext()
+        and use it at an offset into the buffer there! */
+      memmove (queue->pread_buf,
+              &queue->pread_buf[done],
+              queue->pread_off - done);
+      queue->pread_off -= done;
+      total += done;
+    }
+    /* when we encounter a rekey message, the decryption above uses the
+       wrong key for everything after the rekey; in that case, we have
+       to re-do the decryption at 'total' instead of at 'max'. If there
+       is no rekey and the last message is incomplete (max > total),
+       it is safe to keep the decryption so we shift by 'max' */
+    if (GNUNET_YES == queue->rekeyed)
+    {
+      max = total;
+      queue->rekeyed = GNUNET_NO;
+    }
+    memmove (queue->cread_buf,
+            &queue->cread_buf[max],
+            queue->cread_off - max);
+    queue->cread_off -= max; 
+  }
+  
+  if (BUF_SIZE == queue->cread_off)
+    return; /* buffer full, suspend reading */
+  left = GNUNET_TIME_absolute_get_remaining (queue->timeout);
+  if (0 != left.rel_value_us) 
+  {
+    if (max_queue_length < queue->backpressure)
+    {
+      /* continue reading */
+      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));
+  queue_finish (queue);
+}
+
+
+/**
+ * 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;
+  unsigned int port;
+  char dummy[2];
+  char *colon;
+  char *cp;
+  
+  if (1 == SSCANF (bindto,
+                  "%u%1s",
+                  &port,
+                  dummy))
+  {
+    /* interpreting value as just a PORT number */
+    if (port > UINT16_MAX)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 "BINDTO specification `%s' invalid: value too large for 
port\n",
+                 bindto);
+      return NULL;
+    }
+    if (GNUNET_YES ==
+       GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                             COMMUNICATOR_CONFIG_SECTION,
+                                             "DISABLE_V6"))
+    {
+      struct sockaddr_in *i4;
+      
+      i4 = GNUNET_malloc (sizeof (struct sockaddr_in));
+      i4->sin_family = AF_INET;
+      i4->sin_port = htons ((uint16_t) port);
+      *sock_len = sizeof (struct sockaddr_in);
+      in = (struct sockaddr *) i4;
+    }
+    else
+    {
+      struct sockaddr_in6 *i6;
+      
+      i6 = GNUNET_malloc (sizeof (struct sockaddr_in6));
+      i6->sin6_family = AF_INET6;
+      i6->sin6_port = htons ((uint16_t) port);
+      *sock_len = sizeof (struct sockaddr_in6);
+      in = (struct sockaddr *) i6;
+    }
+    return in;
+  }
+  cp = GNUNET_strdup (bindto);
+  colon = strrchr (cp, ':');
+  if (NULL != colon)
+  {
+    /* interpet value after colon as port */
+    *colon = '\0';
+    colon++;
+    if (1 == SSCANF (colon,
+                    "%u%1s",
+                    &port,
+                    dummy))
+    {
+      /* interpreting value as just a PORT number */
+      if (port > UINT16_MAX)
+      {
+       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                   "BINDTO specification `%s' invalid: value too large for 
port\n",
+                   bindto);
+       GNUNET_free (cp);
+       return NULL;
+      }
+    }
+    else
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 "BINDTO specification `%s' invalid: last ':' not followed by 
number\n",
+                 bindto);
+      GNUNET_free (cp);
+      return NULL;
+    }
+  }
+  else
+  {
+    /* interpret missing port as 0, aka pick any free one */
+    port = 0;
+  }
+  {
+    /* try IPv4 */
+    struct sockaddr_in v4;
+
+    if (1 == inet_pton (AF_INET,
+                       cp,
+                       &v4))
+    {
+      v4.sin_port = htons ((uint16_t) port);
+      in = GNUNET_memdup (&v4,
+                         sizeof (v4));
+      *sock_len = sizeof (v4);
+      GNUNET_free (cp);
+      return in;
+    }
+  }
+  {
+    /* try IPv6 */
+    struct sockaddr_in6 v6;
+    const char *start;
+
+    start = cp;
+    if ( ('[' == *cp) &&
+        (']' == cp[strlen (cp)-1]) )
+    {
+      start++; /* skip over '[' */
+      cp[strlen (cp) -1] = '\0'; /* eat ']' */
+    }
+    if (1 == inet_pton (AF_INET6,
+                       start,
+                       &v6))
+    {
+      v6.sin6_port = htons ((uint16_t) port);
+      in = GNUNET_memdup (&v6,
+                         sizeof (v6));
+      *sock_len = sizeof (v6);
+      GNUNET_free (cp);
+      return in;
+    }
+  }
+  /* FIXME (feature!): maybe also try getnameinfo()? */
+  GNUNET_free (cp);
+  return NULL;
+}
+
+
 /**
  * Setup cipher for outgoing data stream based on target and
  * our ephemeral private key.
@@ -966,8 +1385,8 @@ queue_write (void *cls)
     size_t usent = (size_t) sent;
 
     memmove (queue->cwrite_buf,
-            &queue->cwrite_buf[sent],
-            queue->cwrite_off - sent);
+            &queue->cwrite_buf[usent],
+            queue->cwrite_off - usent);
     reschedule_queue_timeout (queue);
  }
   /* can we encrypt more? (always encrypt full messages, needed
@@ -1136,7 +1555,9 @@ static void
 boot_queue (struct Queue *queue,
            enum GNUNET_TRANSPORT_ConnectionStatus cs)
 {
-  queue->nt = 0; // FIXME: determine NT!
+  queue->nt = GNUNET_NT_scanner_get_type (is,
+                                         queue->address,
+                                         queue->address_len);
   (void) GNUNET_CONTAINER_multipeermap_put (queue_map,
                                            &queue->target,
                                            queue,
@@ -1162,17 +1583,17 @@ boot_queue (struct Queue *queue,
     {
     case AF_INET:
       GNUNET_asprintf (&foreign_addr,
-                      "%s-%s:%d",
+                      "%s-%s",
                       COMMUNICATOR_ADDRESS_PREFIX,
-                      "inet-ntop-fixme",
-                      4242);
+                      GNUNET_a2s(queue->address,
+                                 queue->address_len));
       break;
     case AF_INET6:
       GNUNET_asprintf (&foreign_addr,
-                      "%s-%s:%d",
+                      "%s-%s",
                       COMMUNICATOR_ADDRESS_PREFIX,
-                      "inet-ntop-fixme",
-                      4242);
+                      GNUNET_a2s(queue->address,
+                                 queue->address_len));
       break;
     default:
       GNUNET_assert (0);
@@ -1670,6 +2091,11 @@ get_queue_delete_it (void *cls,
 static void
 do_shutdown (void *cls)
 {
+  if (NULL != nat)
+  {
+     GNUNET_NAT_unregister (nat);
+     nat = NULL;
+  }
   if (NULL != listen_task)
   {
     GNUNET_SCHEDULER_cancel (listen_task);
@@ -1685,11 +2111,6 @@ do_shutdown (void *cls)
                                         &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);
@@ -1706,6 +2127,11 @@ do_shutdown (void *cls)
     GNUNET_free (my_private_key);
     my_private_key = NULL;
   }
+  if (NULL != is)
+  {
+     GNUNET_NT_scanner_done (is);
+     is = NULL;
+  }
 }
 
 
@@ -1732,6 +2158,58 @@ enc_notify_cb (void *cls,
 }
 
 
+/**
+ * Signature of the callback passed to #GNUNET_NAT_register() for
+ * a function to call whenever our set of 'valid' addresses changes.
+ *
+ * @param cls closure
+ * @param app_ctx[in,out] location where the app can store stuff
+ *                  on add and retrieve it on remove
+ * @param add_remove #GNUNET_YES to add a new public IP address, 
+ *                   #GNUNET_NO to remove a previous (now invalid) one
+ * @param ac address class the address belongs to
+ * @param addr either the previous or the new public IP address
+ * @param addrlen actual length of the @a addr
+ */
+static void
+nat_address_cb (void *cls,
+               void **app_ctx,
+               int add_remove,
+               enum GNUNET_NAT_AddressClass ac,
+               const struct sockaddr *addr,
+               socklen_t addrlen)
+{
+  char *my_addr;
+  struct GNUNET_TRANSPORT_AddressIdentifier *ai;
+
+  if (GNUNET_YES == add_remove)
+  {
+    enum GNUNET_NetworkType nt;
+
+    GNUNET_asprintf (&my_addr,
+                    "%s-%s",
+                    COMMUNICATOR_ADDRESS_PREFIX,
+                    GNUNET_a2s (addr,
+                                addrlen));
+    nt = GNUNET_NT_scanner_get_type (is,
+                                    addr,
+                                    addrlen); 
+    ai = GNUNET_TRANSPORT_communicator_address_add (ch,
+                                                   my_addr,
+                                                   nt,
+                                                   
GNUNET_TIME_UNIT_FOREVER_REL);
+    GNUNET_free (my_addr);
+    *app_ctx = ai;
+  }
+  else
+  {
+    ai = *app_ctx;
+    GNUNET_TRANSPORT_communicator_address_remove (ai);
+    *app_ctx = NULL;
+  }
+}
+
+
 /**
  * Setup communicator and launch network interactions.
  *
@@ -1749,9 +2227,10 @@ run (void *cls,
   char *bindto;
   struct sockaddr *in;
   socklen_t in_len;
-  char *my_addr;
+  struct sockaddr_storage in_sto;
+  socklen_t sto_len;
+  
   (void) cls;
-
   cfg = c;
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_filename (cfg,
@@ -1806,14 +2285,31 @@ run (void *cls,
     GNUNET_free (bindto);
     return;
   }
+  /* We might have bound to port 0, allowing the OS to figure it out;
+     thus, get the real IN-address from the socket */
+  sto_len = sizeof (in_sto);
+  if (0 != getsockname (GNUNET_NETWORK_get_fd (listen_sock),
+                       (struct sockaddr *) &in_sto,
+                       &sto_len))
+  {
+    memcpy (&in_sto,
+           in,
+           in_len);
+    sto_len = in_len;
+  }
   GNUNET_free (in);
+  GNUNET_free (bindto);
+  in = (struct sockaddr *) &in_sto;
+  in_len = sto_len;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Bound to `%s'\n",
-             bindto);
+             GNUNET_a2s ((const struct sockaddr *) &in_sto,
+                         sto_len));
   stats = GNUNET_STATISTICS_create ("C-TCP",
                                    cfg);
   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
                                 NULL);
+  is = GNUNET_NT_scanner_init ();
   my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
   if (NULL == my_private_key)
   {
@@ -1824,13 +2320,13 @@ run (void *cls,
   }
   GNUNET_CRYPTO_eddsa_key_get_public (my_private_key,
                                       &my_identity.public_key);
-
+  /* start listening */
   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);
+                                                   GNUNET_NO);
   ch = GNUNET_TRANSPORT_communicator_connect (cfg,
                                              COMMUNICATOR_CONFIG_SECTION,
                                              COMMUNICATOR_ADDRESS_PREFIX,
@@ -1843,24 +2339,17 @@ run (void *cls,
   {
     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);
+  nat = GNUNET_NAT_register (cfg,
+                            COMMUNICATOR_CONFIG_SECTION,
+                            IPPROTO_TCP,
+                            1 /* one address */,
+                            (const struct sockaddr **) &in,
+                            &in_len,
+                            &nat_address_cb,
+                            NULL /* FIXME: support reversal! */,
+                            NULL /* closure */);
 }
 
 
diff --git a/src/transport/plugin_transport_http_server.c 
b/src/transport/plugin_transport_http_server.c
index c522904d3..495105fe3 100644
--- a/src/transport/plugin_transport_http_server.c
+++ b/src/transport/plugin_transport_http_server.c
@@ -2535,6 +2535,8 @@ server_remove_address (void *cls,
  * Our external IP address/port mapping has changed.
  *
  * @param cls closure, the 'struct LocalAddrList'
+ * @param app_ctx[in,out] location where the app can store stuff
+ *                  on add and retrieve it on remove
  * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO 
to mean
  *     the previous (now invalid) one
  * @param ac address class the address belongs to
@@ -2543,6 +2545,7 @@ server_remove_address (void *cls,
  */
 static void
 server_nat_port_map_callback (void *cls,
+                             void **app_ctx,
                               int add_remove,
                              enum GNUNET_NAT_AddressClass ac,
                               const struct sockaddr *addr,
@@ -2550,6 +2553,7 @@ server_nat_port_map_callback (void *cls,
 {
   struct HTTP_Server_Plugin *plugin = cls;
 
+  (void) app_ctx;
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "NAT called to %s address `%s'\n",
        (add_remove == GNUNET_NO) ? "remove" : "add",
diff --git a/src/transport/plugin_transport_tcp.c 
b/src/transport/plugin_transport_tcp.c
index 5afea593f..d93c4423c 100644
--- a/src/transport/plugin_transport_tcp.c
+++ b/src/transport/plugin_transport_tcp.c
@@ -1430,6 +1430,8 @@ notify_session_monitor (struct Plugin *plugin,
  * Our external IP address/port mapping has changed.
  *
  * @param cls closure, the `struct Plugin`
+ * @param app_ctx[in,out] location where the app can store stuff
+ *                  on add and retrieve it on remove
  * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO 
to mean
  *     the previous (now invalid) one
  * @param ac address class the address belongs to
@@ -1438,6 +1440,7 @@ notify_session_monitor (struct Plugin *plugin,
  */
 static void
 tcp_nat_port_map_callback (void *cls,
+                          void **app_ctx,
                            int add_remove,
                           enum GNUNET_NAT_AddressClass ac,
                           const struct sockaddr *addr,
@@ -1450,6 +1453,7 @@ tcp_nat_port_map_callback (void *cls,
   void *arg;
   size_t args;
 
+  (void) app_ctx;
   LOG (GNUNET_ERROR_TYPE_INFO,
        "NAT notification to %s address `%s'\n",
        (GNUNET_YES == add_remove) ? "add" : "remove",
diff --git a/src/transport/plugin_transport_udp.c 
b/src/transport/plugin_transport_udp.c
index 30a54cb17..b05192e06 100644
--- a/src/transport/plugin_transport_udp.c
+++ b/src/transport/plugin_transport_udp.c
@@ -1309,6 +1309,8 @@ udp_plugin_check_address (void *cls,
  * Our external IP address/port mapping has changed.
  *
  * @param cls closure, the `struct Plugin`
+ * @param app_ctx[in,out] location where the app can store stuff
+ *                  on add and retrieve it on remove
  * @param add_remove #GNUNET_YES to mean the new public IP address,
  *                   #GNUNET_NO to mean the previous (now invalid) one
  * @param ac address class the address belongs to
@@ -1317,6 +1319,7 @@ udp_plugin_check_address (void *cls,
  */
 static void
 udp_nat_port_map_callback (void *cls,
+                          void **app_ctx,
                            int add_remove,
                           enum GNUNET_NAT_AddressClass ac,
                            const struct sockaddr *addr,
@@ -1329,6 +1332,7 @@ udp_nat_port_map_callback (void *cls,
   void *arg;
   size_t args;
 
+  (void) app_ctx;
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        (GNUNET_YES == add_remove)
        ? "NAT notification to add address `%s'\n"

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



reply via email to

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