REST/NAMESTORE: rework API
[oweals/gnunet.git] / src / cadet / gnunet-service-cadet_channel.c
index 68e29b66b59d6b6caa58fcadd53250ca4f6575d3..bd95428be107b32734f8e378b95634ba8b7504d2 100644 (file)
@@ -2,20 +2,20 @@
      This file is part of GNUnet.
      Copyright (C) 2001-2017 GNUnet e.V.
 
-     GNUnet is free software; you can redistribute it and/or modify
-     it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 3, or (at your
-     option) any later version.
+     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
-     General Public License for more details.
+     Affero General Public License for more details.
 
-     You should have received a copy of the GNU General Public License
-     along with GNUnet; see the file COPYING.  If not, write to the
-     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-     Boston, MA 02110-1301, USA.
+     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 cadet/gnunet-service-cadet_channel.c
@@ -302,6 +302,11 @@ struct CadetChannel
    */
   struct GNUNET_HashCode port;
 
+  /**
+   * Hash'ed port of the channel with initiator and destination PID.
+   */
+  struct GNUNET_HashCode h_port;
+
   /**
    * Counter for exponential backoff.
    */
@@ -405,6 +410,37 @@ GCCH_2s (const struct CadetChannel *ch)
 }
 
 
+/**
+ * Hash the @a port and @a initiator and @a listener to
+ * calculate the "challenge" @a h_port we send to the other
+ * peer on #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN.
+ *
+ * @param[out] h_port set to the hash of @a port, @a initiator and @a listener
+ * @param port cadet port, as seen by CADET clients
+ * @param listener peer that is listining on @a port
+ */
+void
+GCCH_hash_port (struct GNUNET_HashCode *h_port,
+               const struct GNUNET_HashCode *port,
+               const struct GNUNET_PeerIdentity *listener)
+{
+  struct GNUNET_HashContext *hc;
+
+  hc = GNUNET_CRYPTO_hash_context_start ();
+  GNUNET_CRYPTO_hash_context_read (hc,
+                                  port,
+                                  sizeof (*port));
+  GNUNET_CRYPTO_hash_context_read (hc,
+                                  listener,
+                                  sizeof (*listener));
+  GNUNET_CRYPTO_hash_context_finish (hc,
+                                    h_port);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Calculated port hash %s\n",
+       GNUNET_h2s (h_port));
+}
+
+
 /**
  * Get the channel's public ID.
  *
@@ -466,6 +502,11 @@ channel_destroy (struct CadetChannel *ch)
     GNUNET_free (crm->data_message);
     GNUNET_free (crm);
   }
+  if (CADET_CHANNEL_LOOSE == ch->state)
+  {
+    GSC_drop_loose_channel (&ch->h_port,
+                           ch);
+  }
   if (NULL != ch->owner)
   {
     free_channel_client (ch->owner);
@@ -566,7 +607,7 @@ send_channel_open (void *cls)
   msgcc.header.size = htons (sizeof (msgcc));
   msgcc.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN);
   msgcc.opt = htonl (options);
-  msgcc.port = ch->port;
+  msgcc.h_port = ch->h_port;
   msgcc.ctn = ch->ctn;
   ch->state = CADET_CHANNEL_OPEN_SENT;
   if (NULL != ch->last_control_qe)
@@ -635,21 +676,23 @@ GCCH_channel_local_new (struct CadetClient *owner,
   ch->max_pending_messages = (ch->nobuffer) ? 1 : 4; /* FIXME: 4!? Do not hardcode! */
   ch->owner = ccco;
   ch->port = *port;
-  if (0 == memcmp (&my_full_id,
-                   GCP_get_id (destination),
-                   sizeof (struct GNUNET_PeerIdentity)))
+  GCCH_hash_port (&ch->h_port,
+                 port,
+                 GCP_get_id (destination));
+  if (0 == GNUNET_memcmp (&my_full_id,
+                   GCP_get_id (destination)))
   {
-    struct CadetClient *c;
+    struct OpenPort *op;
 
     ch->is_loopback = GNUNET_YES;
-    c = GNUNET_CONTAINER_multihashmap_get (open_ports,
-                                           port);
-    if (NULL == c)
+    op = GNUNET_CONTAINER_multihashmap_get (open_ports,
+                                           &ch->h_port);
+    if (NULL == op)
     {
       /* port closed, wait for it to possibly open */
       ch->state = CADET_CHANNEL_LOOSE;
       (void) GNUNET_CONTAINER_multihashmap_put (loose_channels,
-                                                port,
+                                                &ch->h_port,
                                                 ch,
                                                 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
       LOG (GNUNET_ERROR_TYPE_DEBUG,
@@ -659,7 +702,8 @@ GCCH_channel_local_new (struct CadetClient *owner,
     else
     {
       GCCH_bind (ch,
-                 c);
+                 op->c,
+                &op->port);
     }
   }
   else
@@ -709,21 +753,21 @@ timeout_closed_cb (void *cls)
  *
  * @param t tunnel to the remote peer
  * @param ctn identifier of this channel in the tunnel
- * @param port desired local port
+ * @param h_port desired hash of local port
  * @param options options for the channel
  * @return handle to the new channel
  */
 struct CadetChannel *
 GCCH_channel_incoming_new (struct CadetTunnel *t,
                            struct GNUNET_CADET_ChannelTunnelNumber ctn,
-                           const struct GNUNET_HashCode *port,
+                           const struct GNUNET_HashCode *h_port,
                            uint32_t options)
 {
   struct CadetChannel *ch;
-  struct CadetClient *c;
+  struct OpenPort *op;
 
   ch = GNUNET_new (struct CadetChannel);
-  ch->port = *port;
+  ch->h_port = *h_port;
   ch->t = t;
   ch->ctn = ctn;
   ch->retry_time = CADET_INITIAL_RETRANSMIT_TIME;
@@ -736,14 +780,14 @@ GCCH_channel_incoming_new (struct CadetTunnel *t,
                             1,
                             GNUNET_NO);
 
-  c = GNUNET_CONTAINER_multihashmap_get (open_ports,
-                                         port);
-  if (NULL == c)
+  op = GNUNET_CONTAINER_multihashmap_get (open_ports,
+                                         h_port);
+  if (NULL == op)
   {
     /* port closed, wait for it to possibly open */
     ch->state = CADET_CHANNEL_LOOSE;
     (void) GNUNET_CONTAINER_multihashmap_put (loose_channels,
-                                              port,
+                                              &ch->h_port,
                                               ch,
                                               GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
     GNUNET_assert (NULL == ch->retry_control_task);
@@ -759,7 +803,8 @@ GCCH_channel_incoming_new (struct CadetTunnel *t,
   else
   {
     GCCH_bind (ch,
-               c);
+               op->c,
+              &op->port);
   }
   GNUNET_STATISTICS_update (stats,
                             "# channels",
@@ -830,7 +875,7 @@ static void
 send_open_ack (void *cls)
 {
   struct CadetChannel *ch = cls;
-  struct GNUNET_CADET_ChannelManageMessage msg;
+  struct GNUNET_CADET_ChannelOpenAckMessage msg;
 
   ch->retry_control_task = NULL;
   LOG (GNUNET_ERROR_TYPE_DEBUG,
@@ -840,6 +885,7 @@ send_open_ack (void *cls)
   msg.header.size = htons (sizeof (msg));
   msg.reserved = htonl (0);
   msg.ctn = ch->ctn;
+  msg.port = ch->port;
   if (NULL != ch->last_control_qe)
     GCT_send_cancel (ch->last_control_qe);
   ch->last_control_qe = GCT_send (ch->t,
@@ -928,11 +974,13 @@ send_ack_to_client (struct CadetChannel *ch,
  * request and establish the link with the client.
  *
  * @param ch open incoming channel
- * @param c client listening on the respective port
+ * @param c client listening on the respective @a port
+ * @param port the port @a is listening on
  */
 void
 GCCH_bind (struct CadetChannel *ch,
-           struct CadetClient *c)
+           struct CadetClient *c,
+          const struct GNUNET_HashCode *port)
 {
   uint32_t options;
   struct CadetChannelClient *cccd;
@@ -959,6 +1007,7 @@ GCCH_bind (struct CadetChannel *ch,
   cccd = GNUNET_new (struct CadetChannelClient);
   GNUNET_assert (NULL == ch->dest);
   ch->dest = cccd;
+  ch->port = *port;
   cccd->c = c;
   cccd->client_ready = GNUNET_YES;
   cccd->ccn = GSC_bind (c,
@@ -967,7 +1016,7 @@ GCCH_bind (struct CadetChannel *ch,
                         ? GCP_get (&my_full_id,
                                    GNUNET_YES)
                         : GCT_get_destination (ch->t),
-                        &ch->port,
+                        port,
                         options);
   GNUNET_assert (ntohl (cccd->ccn.channel_of_client) <
                  GNUNET_CADET_LOCAL_CHANNEL_ID_CLI);
@@ -976,7 +1025,8 @@ GCCH_bind (struct CadetChannel *ch,
   {
     ch->state = CADET_CHANNEL_OPEN_SENT;
     GCCH_handle_channel_open_ack (ch,
-                                  NULL);
+                                  NULL,
+                                 port);
   }
   else
   {
@@ -1092,8 +1142,6 @@ GCCH_channel_local_destroy (struct CadetChannel *ch,
          target, but that never went anywhere. Nothing to do here. */
       break;
     case CADET_CHANNEL_LOOSE:
-      GSC_drop_loose_channel (&ch->port,
-                              ch);
       break;
     default:
       GCT_send_channel_destroy (ch->t,
@@ -1107,14 +1155,17 @@ GCCH_channel_local_destroy (struct CadetChannel *ch,
 
 /**
  * We got an acknowledgement for the creation of the channel
- * (the port is open on the other side). Begin transmissions.
+ * (the port is open on the other side).  Verify that the
+ * other end really has the right port, and begin transmissions.
  *
  * @param ch channel to destroy
  * @param cti identifier of the connection that delivered the message
+ * @param port port number (needed to verify receiver knows the port)
  */
 void
 GCCH_handle_channel_open_ack (struct CadetChannel *ch,
-                              const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti)
+                              const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti,
+                             const struct GNUNET_HashCode *port)
 {
   switch (ch->state)
   {
@@ -1133,6 +1184,14 @@ GCCH_handle_channel_open_ack (struct CadetChannel *ch,
       GNUNET_break_op (0);
       return;
     }
+    if (0 != GNUNET_memcmp (&ch->port,
+                    port))
+    {
+      /* Other peer failed to provide the right port,
+        refuse connection. */
+      GNUNET_break_op (0);
+      return;
+    }
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Received CHANNEL_OPEN_ACK for waiting %s, entering READY state\n",
          GCCH_2s (ch));
@@ -1221,8 +1280,7 @@ GCCH_handle_channel_plaintext_data (struct CadetChannel *ch,
   uint32_t delta;
 
   GNUNET_assert (GNUNET_NO == ch->is_loopback);
-  if ( (GNUNET_YES == ch->destroy) &&
-       (NULL == ch->owner) &&
+  if ( (NULL == ch->owner) &&
        (NULL == ch->dest) )
   {
     /* This client is gone, but we still have messages to send to
@@ -1232,8 +1290,9 @@ GCCH_handle_channel_plaintext_data (struct CadetChannel *ch,
          "Dropping incoming payload on %s as this end is already closed\n",
          GCCH_2s (ch));
     /* send back DESTROY notification to stop further retransmissions! */
-    GCT_send_channel_destroy (ch->t,
-                              ch->ctn);
+    if (GNUNET_YES == ch->destroy)
+      GCT_send_channel_destroy (ch->t,
+                                ch->ctn);
     return;
   }
   payload_size = ntohs (msg->header.size) - sizeof (*msg);
@@ -1245,23 +1304,51 @@ GCCH_handle_channel_plaintext_data (struct CadetChannel *ch,
                  &msg[1],
                  payload_size);
   ccc = (NULL != ch->owner) ? ch->owner : ch->dest;
-  if ( (GNUNET_YES == ccc->client_ready) &&
-       ( (GNUNET_YES == ch->out_of_order) ||
-         (msg->mid.mid == ch->mid_recv.mid) ) )
+  if (GNUNET_YES == ccc->client_ready)
   {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "Giving %u bytes of payload with MID %u from %s to client %s\n",
-         (unsigned int) payload_size,
-         ntohl (msg->mid.mid),
-         GCCH_2s (ch),
-         GSC_2s (ccc->c));
-    ccc->client_ready = GNUNET_NO;
-    GSC_send_to_client (ccc->c,
-                        env);
-    ch->mid_recv.mid = htonl (1 + ntohl (ch->mid_recv.mid));
-    ch->mid_futures >>= 1;
-    send_channel_data_ack (ch);
-    return;
+    /*
+     * We ad-hoc send the message if
+     * - The channel is out-of-order
+     * - The channel is reliable and MID matches next expected MID
+     * - The channel is unreliable and MID is before lowest seen MID
+     */
+    if ( (GNUNET_YES == ch->out_of_order) ||
+         ((msg->mid.mid == ch->mid_recv.mid) &&
+          (GNUNET_YES == ch->reliable)) ||
+         ((GNUNET_NO == ch->reliable) &&
+          (ntohl (msg->mid.mid) >= ntohl (ch->mid_recv.mid)) &&
+          ((NULL == ccc->head_recv) ||
+           (ntohl (msg->mid.mid) < ntohl (ccc->head_recv->mid.mid)))) )
+    {
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "Giving %u bytes of payload with MID %u from %s to client %s\n",
+           (unsigned int) payload_size,
+           ntohl (msg->mid.mid),
+           GCCH_2s (ch),
+           GSC_2s (ccc->c));
+      ccc->client_ready = GNUNET_NO;
+      GSC_send_to_client (ccc->c,
+                          env);
+      if (GNUNET_NO == ch->out_of_order)
+        ch->mid_recv.mid = htonl (1 + ntohl (msg->mid.mid));
+      else
+        ch->mid_recv.mid = htonl (1 + ntohl (ch->mid_recv.mid));
+      ch->mid_futures >>= 1;
+      if ( (GNUNET_YES == ch->out_of_order) &&
+          (GNUNET_NO == ch->reliable) )
+      {
+       /* possibly shift by more if we skipped messages */
+       uint64_t delta = htonl (msg->mid.mid) - 1 - ntohl (ch->mid_recv.mid);
+       
+       if (delta > 63)
+         ch->mid_futures = 0;
+       else
+         ch->mid_futures >>= delta;
+       ch->mid_recv.mid = htonl (1 + ntohl (msg->mid.mid));
+      }
+      send_channel_data_ack (ch);
+      return;
+    }
   }
 
   if (GNUNET_YES == ch->reliable)
@@ -1318,6 +1405,57 @@ GCCH_handle_channel_plaintext_data (struct CadetChannel *ch,
   }
   else /* ! ch->reliable */
   {
+    struct CadetOutOfOrderMessage *next_msg;
+
+    /**
+     * We always send if possible in this case.
+     * It is guaranteed that the queued MID < received MID
+     **/
+    if ((NULL != ccc->head_recv) &&
+        (GNUNET_YES == ccc->client_ready))
+    {
+      next_msg = ccc->head_recv;
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "Giving queued MID %u from %s to client %s\n",
+           ntohl (next_msg->mid.mid),
+           GCCH_2s (ch),
+           GSC_2s (ccc->c));
+      ccc->client_ready = GNUNET_NO;
+      GSC_send_to_client (ccc->c,
+                          next_msg->env);
+      ch->mid_recv.mid = htonl (1 + ntohl (next_msg->mid.mid));
+      ch->mid_futures >>= 1;
+      send_channel_data_ack (ch);
+      GNUNET_CONTAINER_DLL_remove (ccc->head_recv,
+                                   ccc->tail_recv,
+                                   next_msg);
+      ccc->num_recv--;
+      /* Do not process duplicate MID */
+      if (msg->mid.mid == next_msg->mid.mid) /* Duplicate */
+      {
+        /* Duplicate within the queue, drop */
+        LOG (GNUNET_ERROR_TYPE_DEBUG,
+             "Message on %s (mid %u) dropped, duplicate\n",
+             GCCH_2s (ch),
+             ntohl (msg->mid.mid));
+        GNUNET_free (next_msg);
+        GNUNET_MQ_discard (env);
+        return;
+      }
+      GNUNET_free (next_msg);
+    }
+
+    if (ntohl (msg->mid.mid) < ntohl (ch->mid_recv.mid)) /* Old */
+    {
+      /* Duplicate within the queue, drop */
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "Message on %s (mid %u) dropped, old.\n",
+           GCCH_2s (ch),
+           ntohl (msg->mid.mid));
+      GNUNET_MQ_discard (env);
+      return;
+    }
+
     /* Channel is unreliable, so we do not ACK. But we also cannot
        allow buffering everything, so check if we have space... */
     if (ccc->num_recv >= ch->max_pending_messages)
@@ -1472,9 +1610,8 @@ handle_matching_ack (struct CadetChannel *ch,
        (NULL != cti) )
   {
     GCC_ack_observed (cti);
-    if (0 == memcmp (cti,
-                     &crm->connection_taken,
-                     sizeof (struct GNUNET_CADET_ConnectionTunnelIdentifier)))
+    if (0 == GNUNET_memcmp (cti,
+                     &crm->connection_taken))
     {
       GCC_latency_observed (cti,
                             GNUNET_TIME_absolute_get_duration (crm->first_transmission_time));
@@ -1523,7 +1660,7 @@ GCCH_handle_channel_plaintext_data_ack (struct CadetChannel *ch,
   mid_mask = GNUNET_htonll (ack->futures);
   found = GNUNET_NO;
   for (crm = ch->head_sent;
-        NULL != crm;
+       NULL != crm;
        crm = crmn)
   {
     crmn = crm->next;
@@ -1764,9 +1901,9 @@ GCCH_handle_local_data (struct CadetChannel *ch,
 {
   struct CadetReliableMessage *crm;
 
-  if (ch->pending_messages > ch->max_pending_messages)
+  if (ch->pending_messages >= ch->max_pending_messages)
   {
-    GNUNET_break (0);
+    GNUNET_break (0); /* Fails: #5370 */
     return GNUNET_SYSERR;
   }
   if (GNUNET_YES == ch->destroy)
@@ -1991,6 +2128,7 @@ void
 GCCH_debug (struct CadetChannel *ch,
             enum GNUNET_ErrorType level)
 {
+#if !defined(GNUNET_CULL_LOGGING)
   int do_log;
 
   do_log = GNUNET_get_log_call_status (level & (~GNUNET_ERROR_TYPE_BULK),
@@ -2030,6 +2168,7 @@ GCCH_debug (struct CadetChannel *ch,
         ntohl (ch->mid_recv.mid),
         (unsigned long long) ch->mid_futures,
         ntohl (ch->mid_send.mid));
+#endif
 }