+/**
+ * Cache a message to be sent once tunnel is online.
+ *
+ * @param t Tunnel to hold the message.
+ * @param msg Message itself (copy will be made).
+ */
+static struct MeshTunnelDelayed *
+queue_data (struct MeshTunnel3 *t, const struct GNUNET_MessageHeader *msg)
+{
+ struct MeshTunnelDelayed *tqd;
+ uint16_t size = ntohs (msg->size);
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "queue data on Tunnel %s\n", GMT_2s (t));
+
+ if (GNUNET_YES == is_ready (t))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ tqd = GNUNET_malloc (sizeof (struct MeshTunnelDelayed) + size);
+
+ tqd->t = t;
+ memcpy (&tqd[1], msg, size);
+ GNUNET_CONTAINER_DLL_insert_tail (t->tq_head, t->tq_tail, tqd);
+ return tqd;
+}
+
+
+
+/**
+ * Sends an already built message on a tunnel, encrypting it and
+ * choosing the best connection.
+ *
+ * @param message Message to send. Function modifies it.
+ * @param t Tunnel on which this message is transmitted.
+ * @param force Force the tunnel to take the message (buffer overfill).
+ * @param cont Continuation to call once message is really sent.
+ * @param cont_cls Closure for @c cont.
+ * @param existing_q In case this a transmission of previously queued data,
+ * this should be TunnelQueue given to the client.
+ * Otherwise, NULL.
+ *
+ * @return Handle to cancel message. NULL if @c cont is NULL.
+ */
+static struct MeshTunnel3Queue *
+send_prebuilt_message (const struct GNUNET_MessageHeader *message,
+ struct MeshTunnel3 *t, int force,
+ GMT_sent cont, void *cont_cls,
+ struct MeshTunnel3Queue *existing_q)
+{
+ struct MeshTunnel3Queue *tq;
+ struct MeshConnection *c;
+ struct GNUNET_MESH_Encrypted *msg;
+ size_t size = ntohs (message->size);
+ char cbuf[sizeof (struct GNUNET_MESH_Encrypted) + size];
+ uint32_t iv;
+ uint16_t type;
+ int fwd;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "GMT Send on Tunnel %s\n", GMT_2s (t));
+
+ if (GNUNET_NO == is_ready (t))
+ {
+ struct MeshTunnelDelayed *tqd;
+ /* A non null existing_q indicates sending of queued data.
+ * Should only happen after tunnel becomes ready.
+ */
+ GNUNET_assert (NULL == existing_q);
+ tqd = queue_data (t, message);
+ if (NULL == cont)
+ return NULL;
+ tq = GNUNET_new (struct MeshTunnel3Queue);
+ tq->tqd = tqd;
+ tqd->tq = tq;
+ tq->cont = cont;
+ tq->cont_cls = cont_cls;
+ return tq;
+ }
+
+ GNUNET_assert (GNUNET_NO == GMT_is_loopback (t));
+
+ iv = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
+ msg = (struct GNUNET_MESH_Encrypted *) cbuf;
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_ENCRYPTED);
+ msg->iv = iv;
+ GNUNET_assert (t_encrypt (t, &msg[1], message, size, iv) == size);
+ msg->header.size = htons (sizeof (struct GNUNET_MESH_Encrypted) + size);
+ c = tunnel_get_connection (t);
+ if (NULL == c)
+ {
+ GNUNET_break (GNUNET_YES == t->destroy);
+ return NULL;
+ }
+ type = ntohs (message->type);
+ switch (type)
+ {
+ case GNUNET_MESSAGE_TYPE_MESH_DATA:
+ case GNUNET_MESSAGE_TYPE_MESH_DATA_ACK:
+ case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_CREATE:
+ case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_DESTROY:
+ case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_ACK:
+ msg->cid = *GMC_get_id (c);
+ msg->ttl = htonl (default_ttl);
+ break;
+ default:
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "unkown type %s\n",
+ GM_m2s (type));
+ GNUNET_break (0);
+ }
+
+ fwd = GMC_is_origin (c, GNUNET_YES);
+
+ if (NULL == cont)
+ {
+ (void) GMC_send_prebuilt_message (&msg->header, c, fwd, force, NULL, NULL);
+ return NULL;
+ }
+ if (NULL == existing_q)
+ {
+ tq = GNUNET_new (struct MeshTunnel3Queue); /* FIXME valgrind: leak*/
+ }
+ else
+ {
+ tq = existing_q;
+ tq->tqd = NULL;
+ }
+ tq->cq = GMC_send_prebuilt_message (&msg->header, c, fwd, force,
+ &message_sent, tq);
+ tq->cont = cont;
+ tq->cont_cls = cont_cls;
+
+ return tq;
+}
+
+
+/**
+ * Send all cached messages that we can, tunnel is online.
+ *
+ * @param t Tunnel that holds the messages. Cannot be loopback.
+ */
+static void
+send_queued_data (struct MeshTunnel3 *t)
+{
+ struct MeshTunnelDelayed *tqd;
+ struct MeshTunnelDelayed *next;
+ unsigned int room;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "GMT_send_queued_data on tunnel %s\n",
+ GMT_2s (t));
+
+ if (GMT_is_loopback (t))
+ {
+ GNUNET_break (0);
+ return;
+ }
+
+ if (GNUNET_NO == is_ready (t))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " not ready yet: %s/%s\n",
+ estate2s (t->estate), cstate2s (t->cstate));
+ return;
+ }
+
+ room = GMT_get_connections_buffer (t);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " buffer space: %u\n", room);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " tq head: %p\n", t->tq_head);
+ for (tqd = t->tq_head; NULL != tqd && room > 0; tqd = next)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " sending queued data\n");
+ next = tqd->next;
+ room--;
+ send_prebuilt_message ((struct GNUNET_MessageHeader *) &tqd[1],
+ tqd->t, GNUNET_YES,
+ NULL != tqd->tq ? tqd->tq->cont : NULL,
+ NULL != tqd->tq ? tqd->tq->cont_cls : NULL,
+ tqd->tq);
+ unqueue_data (tqd);
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "GMT_send_queued_data end\n", GMP_2s (t->peer));
+}
+
+
+/**
+ * Sends key exchange message on a tunnel, choosing the best connection.
+ * Should not be called on loopback tunnels.
+ *
+ * @param t Tunnel on which this message is transmitted.
+ * @param message Message to send. Function modifies it.
+ */
+static void
+send_kx (struct MeshTunnel3 *t,
+ const struct GNUNET_MessageHeader *message)
+{
+ struct MeshConnection *c;
+ struct GNUNET_MESH_KX *msg;
+ size_t size = ntohs (message->size);
+ char cbuf[sizeof (struct GNUNET_MESH_KX) + size];
+ uint16_t type;
+ int fwd;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "GMT KX on Tunnel %s\n", GMT_2s (t));
+
+ /* Avoid loopback. */
+ if (GMT_is_loopback (t))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " loopback!\n");
+ GNUNET_break (0);
+ return;
+ }
+
+ /* Must have a connection. */
+ if (NULL == t->connection_head)
+ {
+ GNUNET_break (MESH_TUNNEL3_SEARCHING == t->cstate);
+ return;
+ }
+
+ msg = (struct GNUNET_MESH_KX *) cbuf;
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_KX);
+ msg->header.size = htons (sizeof (struct GNUNET_MESH_KX) + size);
+ c = tunnel_get_connection (t);
+ if (NULL == c)
+ {
+ GNUNET_break (GNUNET_YES == t->destroy || MESH_TUNNEL3_READY != t->cstate);
+ return;
+ }
+ type = ntohs (message->type);
+ switch (type)
+ {
+ case GNUNET_MESSAGE_TYPE_MESH_KX_EPHEMERAL:
+ case GNUNET_MESSAGE_TYPE_MESH_KX_PING:
+ case GNUNET_MESSAGE_TYPE_MESH_KX_PONG:
+ msg->reserved = htonl (0);
+ memcpy (&msg[1], message, size);
+ break;
+ default:
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "unkown type %s\n",
+ GM_m2s (type));
+ GNUNET_break (0);
+ }
+
+ fwd = GMC_is_origin (t->connection_head->c, GNUNET_YES);
+ /* TODO save handle and cancel in case of a unneeded retransmission */
+ GMC_send_prebuilt_message (&msg->header, c, fwd, GNUNET_YES, NULL, NULL);
+}
+
+
+/**
+ * Send the ephemeral key on a tunnel.
+ *
+ * @param t Tunnel on which to send the key.
+ */
+static void
+send_ephemeral (struct MeshTunnel3 *t)
+{
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __FUNCTION__);
+
+ kx_msg.sender_status = htonl (t->estate);
+ send_kx (t, &kx_msg.header);
+}
+
+/**
+ * Send a ping message on a tunnel.
+ *
+ * @param t Tunnel on which to send the ping.
+ */
+static void
+send_ping (struct MeshTunnel3 *t)
+{
+ struct GNUNET_MESH_KX_Ping msg;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __FUNCTION__);
+ msg.header.size = htons (sizeof (msg));
+ msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_KX_PING);
+ msg.iv = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
+ msg.target = *GMP_get_id (t->peer);
+ msg.nonce = t->kx_ctx->challenge;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " sending %u\n", msg.nonce);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " towards %s\n", GNUNET_i2s (&msg.target));
+ t_encrypt (t, &msg.target, &msg.target, ping_encryption_size(), msg.iv);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " e sending %u\n", msg.nonce);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " e towards %s\n", GNUNET_i2s (&msg.target));
+
+ send_kx (t, &msg.header);
+}
+
+
+/**
+ * Send a pong message on a tunnel.
+ *
+ * @param t Tunnel on which to send the pong.
+ * @param challenge Value sent in the ping that we have to send back.
+ */
+static void
+send_pong (struct MeshTunnel3 *t, uint32_t challenge)
+{
+ struct GNUNET_MESH_KX_Pong msg;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __FUNCTION__);
+ msg.header.size = htons (sizeof (msg));
+ msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_KX_PONG);
+ msg.iv = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
+ msg.nonce = challenge;
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " sending %u\n", msg.nonce);
+ t_encrypt (t, &msg.nonce, &msg.nonce, sizeof (msg.nonce), msg.iv);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " e sending %u\n", msg.nonce);
+
+ send_kx (t, &msg.header);
+}
+
+
+/**
+ * Initiate a rekey with the remote peer.
+ *
+ * @param cls Closure (tunnel).
+ * @param tc TaskContext.
+ */
+static void
+rekey_tunnel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct MeshTunnel3 *t = cls;
+
+ t->rekey_task = GNUNET_SCHEDULER_NO_TASK;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Re-key Tunnel\n");
+ if (NULL != tc && 0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
+ return;
+
+ if (NULL == t->kx_ctx)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " new kx ctx\n");
+ t->kx_ctx = GNUNET_new (struct MeshTunnelKXCtx);
+ t->kx_ctx->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
+ UINT32_MAX);
+ t->kx_ctx->d_key_old = t->d_key;
+ }
+ send_ephemeral (t);
+ switch (t->estate)
+ {
+ case MESH_TUNNEL3_KEY_UNINITIALIZED:
+ t->estate = MESH_TUNNEL3_KEY_SENT;
+ break;
+ case MESH_TUNNEL3_KEY_SENT:
+ break;
+ case MESH_TUNNEL3_KEY_PING:
+ case MESH_TUNNEL3_KEY_OK:
+ send_ping (t);
+ t->estate = MESH_TUNNEL3_KEY_PING;
+ break;
+ default:
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Unexpected state %u\n", t->estate);
+ }
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " next call in %s\n",
+ GNUNET_STRINGS_relative_time_to_string (REKEY_WAIT, GNUNET_YES));
+ t->rekey_task = GNUNET_SCHEDULER_add_delayed (REKEY_WAIT, &rekey_tunnel, t);
+}
+
+
+/**
+ * Out ephemeral key has changed, create new session key on all tunnels.
+ *
+ * @param cls Closure (size of the hashmap).
+ * @param key Current public key.
+ * @param value Value in the hash map (tunnel).
+ *
+ * @return #GNUNET_YES, so we should continue to iterate,
+ */
+static int
+rekey_iterator (void *cls,
+ const struct GNUNET_PeerIdentity *key,
+ void *value)
+{
+ struct MeshTunnel3 *t = value;
+ struct GNUNET_TIME_Relative delay;
+ long n = (long) cls;
+ uint32_t r;
+
+ if (GNUNET_SCHEDULER_NO_TASK != t->rekey_task)
+ return GNUNET_YES;
+
+ r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, (uint32_t) n * 100);
+ delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, r);
+ t->rekey_task = GNUNET_SCHEDULER_add_delayed (delay, &rekey_tunnel, t);
+
+ return GNUNET_YES;
+}
+
+
+/**
+ * Create a new ephemeral key and key message, schedule next rekeying.
+ *
+ * @param cls Closure (unused).
+ * @param tc TaskContext.
+ */
+static void
+rekey (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_TIME_Absolute time;
+ long n;
+
+ rekey_task = GNUNET_SCHEDULER_NO_TASK;
+
+ if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
+ return;
+
+ GNUNET_free_non_null (my_ephemeral_key);
+ my_ephemeral_key = GNUNET_CRYPTO_ecdhe_key_create ();
+
+ time = GNUNET_TIME_absolute_get ();
+ kx_msg.creation_time = GNUNET_TIME_absolute_hton (time);
+ time = GNUNET_TIME_absolute_add (time, rekey_period);
+ time = GNUNET_TIME_absolute_add (time, GNUNET_TIME_UNIT_MINUTES);
+ kx_msg.expiration_time = GNUNET_TIME_absolute_hton (time);
+ GNUNET_CRYPTO_ecdhe_key_get_public (my_ephemeral_key, &kx_msg.ephemeral_key);
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_sign (my_private_key,
+ &kx_msg.purpose,
+ &kx_msg.signature));
+
+ n = (long) GNUNET_CONTAINER_multipeermap_size (tunnels);
+ GNUNET_CONTAINER_multipeermap_iterate (tunnels, &rekey_iterator, (void *) n);
+
+ rekey_task = GNUNET_SCHEDULER_add_delayed (rekey_period, &rekey, NULL);
+}
+
+
+/**
+ * Called only on shutdown, destroy every tunnel.
+ *
+ * @param cls Closure (unused).
+ * @param key Current public key.
+ * @param value Value in the hash map (tunnel).
+ *
+ * @return #GNUNET_YES, so we should continue to iterate,
+ */
+static int
+destroy_iterator (void *cls,
+ const struct GNUNET_PeerIdentity *key,
+ void *value)
+{
+ struct MeshTunnel3 *t = value;
+
+ GMT_destroy (t);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Demultiplex data per channel and call appropriate channel handler.
+ *
+ * @param t Tunnel on which the data came.
+ * @param msg Data message.
+ * @param fwd Is this message fwd? This only is meaningful in loopback channels.
+ * #GNUNET_YES if message is FWD on the respective channel (loopback)
+ * #GNUNET_NO if message is BCK on the respective channel (loopback)
+ * #GNUNET_SYSERR if message on a one-ended channel (remote)
+ */
+static void