struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
/**
- * Signal that shutdown is happening: prevent recover measures.
+ * Signal that shutdown is happening: prevent recovery measures.
*/
int shutting_down;
/**
- * Task run during shutdown.
- *
- * @param cls unused
+ * Shutdown everything once the clients have disconnected.
*/
static void
-shutdown_task (void *cls)
+shutdown_rest ()
{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Shutting down\n");
- shutting_down = GNUNET_YES;
- GCO_shutdown ();
if (NULL != stats)
{
GNUNET_STATISTICS_destroy (stats,
}
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ */
+static void
+shutdown_task (void *cls)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Shutting down\n");
+ shutting_down = GNUNET_YES;
+ GCO_shutdown ();
+ if (NULL == clients_head)
+ shutdown_rest ();
+}
+
+
/**
* We had a remote connection @a value to port @a port before
* client @a cls opened port @a port. Bind them now.
GNUNET_CONTAINER_multihashmap32_remove (c->channels,
ntohl (msg->ccn.channel_of_client),
ch));
- GCCH_channel_local_destroy (ch);
+ GCCH_channel_local_destroy (ch,
+ c);
GNUNET_SERVICE_client_continue (c->client);
}
GNUNET_CONTAINER_multihashmap32_remove (c->channels,
key,
ch));
- if (key < GNUNET_CADET_LOCAL_CHANNEL_ID_CLI)
- GCCH_channel_local_destroy (ch);
- else
- GCCH_channel_incoming_destroy (ch);
+ GCCH_channel_local_destroy (ch,
+ c);
return GNUNET_OK;
}
-1,
GNUNET_NO);
GNUNET_free (c);
+ if ( (NULL == clients_head) &&
+ (GNUNET_YES == shutting_down) )
+ shutdown_rest ();
}
*/
int out_of_order;
+ /**
+ * Is this channel a loopback channel, where the destination is us again?
+ */
+ int is_loopback;
+
/**
* Flag to signal the destruction of the channel. If this is set to
* #GNUNET_YES the channel will be destroyed once the queue is
ch->owner = owner;
ch->ccn = ccn;
ch->port = *port;
- ch->t = GCP_get_tunnel (destination,
- GNUNET_YES);
- ch->retry_time = CADET_INITIAL_RETRANSMIT_TIME;
- ch->ctn = GCT_add_channel (ch->t,
- ch);
+ if (0 == memcmp (&my_full_id,
+ GCP_get_id (destination),
+ sizeof (struct GNUNET_PeerIdentity)))
+ {
+ ch->is_loopback = GNUNET_YES;
+ ch->dest = GNUNET_CONTAINER_multihashmap_get (open_ports,
+ port);
+ if (NULL == ch->dest)
+ {
+ /* port closed, wait for it to possibly open */
+ (void) GNUNET_CONTAINER_multihashmap_put (loose_channels,
+ port,
+ ch,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Created loose incoming loopback channel to port %s\n",
+ GNUNET_h2s (&ch->port));
+ }
+ else
+ {
+ GCCH_bind (ch,
+ ch->dest);
+ }
+ }
+ else
+ {
+ ch->t = GCP_get_tunnel (destination,
+ GNUNET_YES);
+ ch->retry_time = CADET_INITIAL_RETRANSMIT_TIME;
+ ch->ctn = GCT_add_channel (ch->t,
+ ch);
+ }
GNUNET_STATISTICS_update (stats,
"# channels",
1,
GNUNET_h2s (port),
GCP_2s (destination),
GSC_2s (owner),
- GCT_2s (ch->t));
+ (GNUNET_YES == ch->is_loopback) ? "loopback" : GCT_2s (ch->t));
return ch;
}
&ch->port,
options);
ch->mid_recv.mid = htonl (1); /* The CONNECT counts as message 0! */
-
- /* notify other peer that we accepted the connection */
- ch->retry_control_task
- = GNUNET_SCHEDULER_add_now (&send_open_ack,
- ch);
+ if (GNUNET_YES == ch->is_loopback)
+ {
+ ch->state = CADET_CHANNEL_OPEN_SENT;
+ GCCH_handle_channel_open_ack (ch);
+ }
+ else
+ {
+ /* notify other peer that we accepted the connection */
+ ch->retry_control_task
+ = GNUNET_SCHEDULER_add_now (&send_open_ack,
+ ch);
+ }
/* give client it's initial supply of ACKs */
env = GNUNET_MQ_msg (tcm,
GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_CREATE);
/**
- * Destroy locally created channel. Called by the
- * local client, so no need to tell the client.
+ * Destroy locally created channel. Called by the local client, so no
+ * need to tell the client.
*
* @param ch channel to destroy
+ * @param c client that caused the destruction
*/
void
-GCCH_channel_local_destroy (struct CadetChannel *ch)
+GCCH_channel_local_destroy (struct CadetChannel *ch,
+ struct CadetClient *c)
{
LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Local client asks for destruction of %s which it initiated\n",
+ "%s asks for destruction of %s\n",
+ GSC_2s (c),
GCCH_2s (ch));
+ GNUNET_assert (NULL != c);
+ if (c == ch->owner)
+ ch->owner = NULL;
+ else if (c == ch->dest)
+ ch->dest = NULL;
+ else
+ GNUNET_assert (0);
if (GNUNET_YES == ch->destroy)
{
/* other end already destroyed, with the local client gone, no need
channel_destroy (ch);
return;
}
- if (NULL != ch->head_sent)
+ if ( (NULL != ch->head_sent) ||
+ (NULL != ch->owner) ||
+ (NULL != ch->dest) )
{
- /* allow send queue to train first */
+ /* Wait for other end to destroy us as well,
+ and otherwise allow send queue to be transmitted first */
ch->destroy = GNUNET_YES;
return;
}
if (CADET_CHANNEL_NEW != ch->state)
GCT_send_channel_destroy (ch->t,
ch->ctn);
- /* Now finish our clean up */
- channel_destroy (ch);
-}
-
-
-/**
- * Destroy channel that was incoming. Called by the
- * local client, so no need to tell the client.
- *
- * @param ch channel to destroy
- */
-void
-GCCH_channel_incoming_destroy (struct CadetChannel *ch)
-{
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Local client asks for destruction of %s which it accepted\n",
- GCCH_2s (ch));
- if (GNUNET_YES == ch->destroy)
- {
- /* other end already destroyed, with the remote client gone, no need
- to finish transmissions, just destroy immediately. */
- channel_destroy (ch);
- return;
- }
- if (NULL != ch->head_recv)
- {
- /* allow local client to see all data first */
- ch->destroy = GNUNET_YES;
- return;
- }
/* Nothing left to do, just finish destruction */
- GCT_send_channel_destroy (ch->t,
- ch->ctn);
channel_destroy (ch);
}
"Receicved %u bytes of application data on %s\n",
(unsigned int) payload_size,
GCCH_2s (ch));
+ if (GNUNET_YES == ch->is_loopback)
+ {
+ GNUNET_break (0); // FIXME: not implemented
+ return;
+ }
+
env = GNUNET_MQ_msg_extra (ld,
payload_size,
GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA);
{
struct CadetReliableMessage *crm;
+ GNUNET_break (GNUNET_NO == ch->is_loopback);
if (GNUNET_NO == ch->reliable)
{
/* not expecting ACKs on unreliable channel, odd */
void
GCCH_handle_remote_destroy (struct CadetChannel *ch)
{
+ GNUNET_break (GNUNET_NO == ch->is_loopback);
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Received remote channel DESTROY for %s\n",
GCCH_2s (ch));
+ if (GNUNET_YES == ch->destroy)
+ {
+ /* Local client already gone, this is instant-death. */
+ channel_destroy (ch);
+ return;
+ }
+ if (NULL != ch->head_recv)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ "Lost end of transmission due to remote shutdown on channel %s\n",
+ GCCH_2s (ch));
+ /* FIXME: change API to notify client about truncated transmission! */
+ }
ch->destroy = GNUNET_YES;
GSC_handle_remote_channel_destroy ((NULL != ch->owner) ? ch->owner : ch->dest,
ch->ccn,
}
ch->pending_messages++;
+ if (GNUNET_YES == ch->is_loopback)
+ {
+ GNUNET_break (0); // fIXME: not implemented
+ return GNUNET_SYSERR;
+ }
+
/* Everything is correct, send the message. */
crm = GNUNET_malloc (sizeof (*crm) + buf_len);
crm->ch = ch;