#include "gnunet-service-core_typemap.h"
#include "core.h"
+
/**
* How many messages do we queue up at most for optional
* notifications to a client? (this can cause notifications
*/
struct GNUNET_CONTAINER_MultiHashMap *requests;
+ /**
+ * Map containing all peers that this client knows we're connected to.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *connectmap;
+
/**
* Options for messages this client cares about,
* see GNUNET_CORE_OPTION_ values.
* client's queue is getting too large?
*/
static void
-send_to_client (struct GSC_Client *client,
- const struct GNUNET_MessageHeader *msg,
- int can_drop)
+send_to_client (struct GSC_Client *client,
+ const struct GNUNET_MessageHeader *msg, int can_drop)
{
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
*/
void
GSC_CLIENTS_send_to_client (struct GNUNET_SERVER_Client *client,
- const struct GNUNET_MessageHeader *msg,
- int can_drop)
+ const struct GNUNET_MessageHeader *msg,
+ int can_drop)
{
struct GSC_Client *c;
* @return GNUNET_YES if 'c' is interested, GNUNET_NO if not.
*/
static int
-type_match (uint16_t type,
- struct GSC_Client *c)
+type_match (uint16_t type, struct GSC_Client *c)
{
unsigned int i;
if (c->tcnt == 0)
- return GNUNET_YES; /* peer without handlers matches ALL */
- for (i=0;i<c->tcnt;i++)
+ return GNUNET_YES; /* peer without handlers matches ALL */
+ for (i = 0; i < c->tcnt; i++)
if (type == c->types[i])
return GNUNET_YES;
return GNUNET_NO;
* Send a message to all of our current clients that have the right
* options set.
*
+ * @param sender origin of the message (used to check that this peer is
+ * known to be connected to the respective client)
* @param msg message to multicast
* @param can_drop can this message be discarded if the queue is too long
* @param options mask to use
* @param type type of the embedded message, 0 for none
*/
static void
-send_to_all_clients (const struct GNUNET_MessageHeader *msg,
- int can_drop,
- int options,
- uint16_t type)
+send_to_all_clients (const struct GNUNET_PeerIdentity *sender,
+ const struct GNUNET_MessageHeader *msg, int can_drop,
+ int options, uint16_t type)
{
struct GSC_Client *c;
for (c = client_head; c != NULL; c = c->next)
{
- if ( (0 == (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) &&
- (GNUNET_YES == type_match (type, c)) )
- continue; /* not the full message, but we'd like the full one! */
- if ( (0 == (c->options & options)) &&
- (GNUNET_YES != type_match (type, c)) )
- continue; /* neither options nor type match permit the message */
+ if ((0 == (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) &&
+ (GNUNET_YES == type_match (type, c)))
+ continue; /* not the full message, but we'd like the full one! */
+ if ((0 == (c->options & options)) && (GNUNET_YES != type_match (type, c)))
+ continue; /* neither options nor type match permit the message */
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Sending message to client interested in messages of type %u.\n",
- (unsigned int) type);
+ "Sending message to client interested in messages of type %u.\n",
+ (unsigned int) type);
#endif
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_contains (c->connectmap,
+ &sender->hashPubKey));
send_to_client (c, msg, can_drop);
}
}
c->tcnt = msize / sizeof (uint16_t);
c->options = ntohl (im->options);
c->types = (const uint16_t *) &c[1];
+ c->connectmap = GNUNET_CONTAINER_multihashmap_create (16);
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_put (c->connectmap,
+ &GSC_my_identity.hashPubKey,
+ NULL,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
wtypes = (uint16_t *) & c[1];
for (i = 0; i < c->tcnt; i++)
wtypes[i] = ntohs (types[i]);
GSC_TYPEMAP_add (wtypes, c->tcnt);
- GNUNET_CONTAINER_DLL_insert (client_head,
- client_tail,
- c);
+ GNUNET_CONTAINER_DLL_insert (client_head, client_tail, c);
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Client connecting to core service is interested in %u message types\n",
+ "Client connecting to core service is interested in %u message types\n",
(unsigned int) c->tcnt);
#endif
/* send init reply message */
irm.reserved = htonl (0);
irm.my_identity = GSC_my_identity;
send_to_client (c, &irm.header, GNUNET_NO);
- if (0 != (c->options & GNUNET_CORE_OPTION_SEND_CONNECT))
- GSC_SESSIONS_notify_client_about_sessions (c);
+ GSC_SESSIONS_notify_client_about_sessions (c);
GNUNET_SERVER_receive_done (client, GNUNET_OK);
}
*
* @param cls unused
* @param client new client that sent CORE_SEND_REQUEST
- * @param message the 'struct InitMessage' (presumably)
+ * @param message the 'struct SendMessageRequest' (presumably)
*/
static void
handle_client_send_request (void *cls, struct GNUNET_SERVER_Client *client,
const struct SendMessageRequest *req;
struct GSC_Client *c;
struct GSC_ClientActiveRequest *car;
+ int is_loopback;
req = (const struct SendMessageRequest *) message;
c = find_client (client);
c->requests = GNUNET_CONTAINER_multihashmap_create (16);
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Client asked for transmission to `%s'\n",
- GNUNET_i2s (&req->peer));
+ "Client asked for transmission to `%s'\n",
+ GNUNET_i2s (&req->peer));
#endif
+ is_loopback =
+ (0 ==
+ memcmp (&req->peer, &GSC_my_identity,
+ sizeof (struct GNUNET_PeerIdentity)));
+ if ((!is_loopback) &&
+ (GNUNET_YES !=
+ GNUNET_CONTAINER_multihashmap_contains (c->connectmap,
+ &req->peer.hashPubKey)))
+ {
+ /* neighbour must have disconnected since request was issued,
+ * ignore (client will realize it once it processes the
+ * disconnect notification) */
+ GNUNET_STATISTICS_update (GSC_stats,
+ gettext_noop
+ ("# send requests dropped (disconnected)"), 1,
+ GNUNET_NO);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ return;
+ }
+
car = GNUNET_CONTAINER_multihashmap_get (c->requests, &req->peer.hashPubKey);
if (car == NULL)
{
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
car->client_handle = c;
}
+ else
+ {
+ GSC_SESSIONS_dequeue_request (car);
+ }
car->target = req->peer;
car->deadline = GNUNET_TIME_absolute_ntoh (req->deadline);
car->priority = ntohl (req->priority);
car->msize = ntohs (req->size);
car->smr_id = req->smr_id;
car->was_solicited = GNUNET_NO;
- if (0 ==
- memcmp (&req->peer, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))
+ if (is_loopback)
+ {
+ /* loopback, satisfy immediately */
GSC_CLIENTS_solicit_request (car);
- else
- GSC_SESSIONS_queue_request (car);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ return;
+ }
+ GSC_SESSIONS_queue_request (car);
GNUNET_SERVER_receive_done (client, GNUNET_OK);
}
/**
* Active request handle for the message.
- */
+ */
struct GSC_ClientActiveRequest *car;
/**
GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
return;
}
- tc.car = GNUNET_CONTAINER_multihashmap_get (c->requests, &sm->peer.hashPubKey);
+ tc.car =
+ GNUNET_CONTAINER_multihashmap_get (c->requests, &sm->peer.hashPubKey);
if (NULL == tc.car)
{
- /* client did not request transmission first! */
- GNUNET_break (0);
- GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ /* Must have been that we first approved the request, then got disconnected
+ * (which triggered removal of the 'car') and now the client gives us a message
+ * just *before* the client learns about the disconnect. Theoretically, we
+ * might also now be *again* connected. So this can happen (but should be
+ * rare). If it does happen, the message is discarded. */
+ GNUNET_STATISTICS_update (GSC_stats,
+ gettext_noop
+ ("# messages discarded (session disconnected)"),
+ 1, GNUNET_NO);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
return;
}
GNUNET_assert (GNUNET_YES ==
- GNUNET_CONTAINER_multihashmap_remove (c->requests,
- &sm->peer.hashPubKey,
- tc.car));
+ GNUNET_CONTAINER_multihashmap_remove (c->requests,
+ &sm->peer.hashPubKey,
+ tc.car));
tc.cork = ntohl (sm->cork);
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Client asked for transmission of %u bytes to `%s' %s\n",
- msize,
- GNUNET_i2s (&sm->peer),
- tc.cork ? "now" : "");
+ "Client asked for transmission of %u bytes to `%s' %s\n", msize,
+ GNUNET_i2s (&sm->peer), tc.cork ? "now" : "");
#endif
- GNUNET_SERVER_mst_receive (client_mst,
- &tc,
- (const char*) &sm[1], msize,
- GNUNET_YES,
- GNUNET_NO);
+ GNUNET_SERVER_mst_receive (client_mst, &tc, (const char *) &sm[1], msize,
+ GNUNET_YES, GNUNET_NO);
if (0 !=
- memcmp (&tc.car->target, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))
+ memcmp (&tc.car->target, &GSC_my_identity,
+ sizeof (struct GNUNET_PeerIdentity)))
GSC_SESSIONS_dequeue_request (tc.car);
- GNUNET_free (tc.car);
+ GNUNET_free (tc.car);
GNUNET_SERVER_receive_done (client, GNUNET_OK);
}
*/
static void
client_tokenizer_callback (void *cls, void *client,
- const struct GNUNET_MessageHeader *message)
+ const struct GNUNET_MessageHeader *message)
{
struct TokenizerContext *tc = client;
struct GSC_ClientActiveRequest *car = tc->car;
if (0 ==
- memcmp (&car->target, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))
+ memcmp (&car->target, &GSC_my_identity,
+ sizeof (struct GNUNET_PeerIdentity)))
{
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Delivering message of type %u to myself\n",
- ntohs (message->type));
+ "Delivering message of type %u to myself\n",
+ ntohs (message->type));
#endif
- GSC_CLIENTS_deliver_message (&GSC_my_identity,
- NULL, 0,
- message,
- ntohs (message->size),
- GNUNET_CORE_OPTION_SEND_FULL_INBOUND | GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND);
- GSC_CLIENTS_deliver_message (&GSC_my_identity,
- NULL, 0,
- message,
- sizeof (struct GNUNET_MessageHeader),
- GNUNET_CORE_OPTION_SEND_HDR_INBOUND | GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND);
+ GSC_CLIENTS_deliver_message (&GSC_my_identity, NULL, 0, message,
+ ntohs (message->size),
+ GNUNET_CORE_OPTION_SEND_FULL_INBOUND |
+ GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND);
+ GSC_CLIENTS_deliver_message (&GSC_my_identity, NULL, 0, message,
+ sizeof (struct GNUNET_MessageHeader),
+ GNUNET_CORE_OPTION_SEND_HDR_INBOUND |
+ GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND);
}
else
{
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Delivering message of type %u to %s\n",
- ntohs (message->type),
- GNUNET_i2s (&car->target));
+ "Delivering message of type %u to %s\n", ntohs (message->type),
+ GNUNET_i2s (&car->target));
#endif
GSC_SESSIONS_transmit (car, message, tc->cork);
}
struct GSC_ClientActiveRequest *car = value;
GNUNET_assert (GNUNET_YES ==
- GNUNET_CONTAINER_multihashmap_remove (car->client_handle->requests,
- &car->target.hashPubKey,
- car));
+ GNUNET_CONTAINER_multihashmap_remove (car->
+ client_handle->requests,
+ &car->target.hashPubKey,
+ car));
GSC_SESSIONS_dequeue_request (car);
GNUNET_free (car);
return GNUNET_YES;
* @param client identification of the client
*/
static void
-handle_client_disconnect (void *cls,
- struct GNUNET_SERVER_Client *client)
+handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
{
struct GSC_Client *c;
#endif
c = find_client (client);
if (c == NULL)
- return; /* client never sent INIT */
- GNUNET_CONTAINER_DLL_remove (client_head,
- client_tail,
- c);
+ return; /* client never sent INIT */
+ GNUNET_CONTAINER_DLL_remove (client_head, client_tail, c);
if (c->requests != NULL)
{
GNUNET_CONTAINER_multihashmap_iterate (c->requests,
NULL);
GNUNET_CONTAINER_multihashmap_destroy (c->requests);
}
+ GNUNET_CONTAINER_multihashmap_destroy (c->connectmap);
+ c->connectmap = NULL;
GSC_TYPEMAP_remove (c->types, c->tcnt);
GNUNET_free (c);
}
struct SendMessageReady smr;
c = car->client_handle;
+ if (GNUNET_YES !=
+ GNUNET_CONTAINER_multihashmap_contains (c->connectmap,
+ &car->target.hashPubKey))
+ {
+ /* connection has gone down since, drop request */
+ GNUNET_assert (0 !=
+ memcmp (&car->target, &GSC_my_identity,
+ sizeof (struct GNUNET_PeerIdentity)));
+ GSC_SESSIONS_dequeue_request (car);
+ GSC_CLIENTS_reject_request (car);
+ return;
+ }
smr.header.size = htons (sizeof (struct SendMessageReady));
smr.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND_READY);
smr.size = htons (car->msize);
GSC_CLIENTS_reject_request (struct GSC_ClientActiveRequest *car)
{
GNUNET_assert (GNUNET_YES ==
- destroy_active_client_request (NULL, &car->target.hashPubKey, car));
+ GNUNET_CONTAINER_multihashmap_remove (car->
+ client_handle->requests,
+ &car->target.hashPubKey,
+ car));
+ GNUNET_free (car);
}
*/
void
GSC_CLIENTS_notify_client_about_neighbour (struct GSC_Client *client,
- const struct GNUNET_PeerIdentity *neighbour,
- const struct GNUNET_TRANSPORT_ATS_Information *atsi,
- unsigned int atsi_count,
- const struct GSC_TypeMap *tmap_old,
- const struct GSC_TypeMap *tmap_new)
+ const struct GNUNET_PeerIdentity
+ *neighbour,
+ const struct GNUNET_ATS_Information
+ *atsi, unsigned int atsi_count,
+ const struct GSC_TypeMap *tmap_old,
+ const struct GSC_TypeMap *tmap_new)
{
struct ConnectNotifyMessage *cnm;
size_t size;
char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
- struct GNUNET_TRANSPORT_ATS_Information *a;
+ struct GNUNET_ATS_Information *a;
struct DisconnectNotifyMessage dcm;
int old_match;
int new_match;
old_match = GSC_TYPEMAP_test_match (tmap_old, client->types, client->tcnt);
new_match = GSC_TYPEMAP_test_match (tmap_new, client->types, client->tcnt);
- if (client->tcnt == 0)
- new_match = GNUNET_YES; /* empty list matches ALL */
if (old_match == new_match)
- return; /* no change */
+ {
+ GNUNET_assert (old_match ==
+ GNUNET_CONTAINER_multihashmap_contains (client->connectmap,
+ &neighbour->hashPubKey));
+ return; /* no change */
+ }
if (old_match == GNUNET_NO)
{
- /* send connect */
+ /* send connect */
+ GNUNET_assert (GNUNET_NO ==
+ GNUNET_CONTAINER_multihashmap_contains (client->connectmap,
+ &neighbour->hashPubKey));
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_put (client->connectmap,
+ &neighbour->hashPubKey,
+ NULL,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
size =
- sizeof (struct ConnectNotifyMessage) +
- (atsi_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
+ sizeof (struct ConnectNotifyMessage) +
+ (atsi_count) * sizeof (struct GNUNET_ATS_Information);
if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
- {
- GNUNET_break (0);
- /* recovery strategy: throw away performance data */
- atsi_count = 0;
- size = sizeof (struct ConnectNotifyMessage);
- }
+ {
+ GNUNET_break (0);
+ /* recovery strategy: throw away performance data */
+ atsi_count = 0;
+ size = sizeof (struct ConnectNotifyMessage);
+ }
cnm = (struct ConnectNotifyMessage *) buf;
cnm->header.size = htons (size);
cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
cnm->ats_count = htonl (atsi_count);
- a = &cnm->ats;
- memcpy (a, atsi,
- sizeof (struct GNUNET_TRANSPORT_ATS_Information) * atsi_count);
- a[atsi_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
- a[atsi_count].value = htonl (0);
+ a = (struct GNUNET_ATS_Information *) &cnm[1];
+ memcpy (a, atsi, sizeof (struct GNUNET_ATS_Information) * atsi_count);
#if DEBUG_CORE
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Sending `%s' message to client.\n",
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message to client.\n",
"NOTIFY_CONNECT");
#endif
cnm->peer = *neighbour;
else
{
/* send disconnect */
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_contains (client->connectmap,
+ &neighbour->hashPubKey));
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (client->connectmap,
+ &neighbour->hashPubKey,
+ NULL));
dcm.header.size = htons (sizeof (struct DisconnectNotifyMessage));
dcm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT);
dcm.reserved = htonl (0);
* @param tmap_new updated type map for the neighbour, NULL for disconnect
*/
void
-GSC_CLIENTS_notify_clients_about_neighbour (const struct GNUNET_PeerIdentity *neighbour,
- const struct GNUNET_TRANSPORT_ATS_Information *atsi,
- unsigned int atsi_count,
- const struct GSC_TypeMap *tmap_old,
- const struct GSC_TypeMap *tmap_new)
+GSC_CLIENTS_notify_clients_about_neighbour (const struct GNUNET_PeerIdentity
+ *neighbour,
+ const struct GNUNET_ATS_Information
+ *atsi, unsigned int atsi_count,
+ const struct GSC_TypeMap *tmap_old,
+ const struct GSC_TypeMap *tmap_new)
{
struct GSC_Client *c;
for (c = client_head; c != NULL; c = c->next)
- GSC_CLIENTS_notify_client_about_neighbour (c, neighbour, atsi,
- atsi_count,
- tmap_old, tmap_new);
+ GSC_CLIENTS_notify_client_about_neighbour (c, neighbour, atsi, atsi_count,
+ tmap_old, tmap_new);
}
/**
* Deliver P2P message to interested clients. Caller must have checked
- * that the sending peer actually lists the given message type as one
+ * that the sending peer actually lists the given message type as one
* of its types.
*
- * @param sender peer who sent us the message
+ * @param sender peer who sent us the message
* @param atsi performance information about neighbour
* @param atsi_count number of entries in 'ats' array
* @param msg the message
*/
void
GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender,
- const struct GNUNET_TRANSPORT_ATS_Information *atsi,
- unsigned int atsi_count,
- const struct GNUNET_MessageHeader *msg,
- uint16_t msize,
- int options)
+ const struct GNUNET_ATS_Information *atsi,
+ unsigned int atsi_count,
+ const struct GNUNET_MessageHeader *msg,
+ uint16_t msize, int options)
{
- size_t size = msize + sizeof (struct NotifyTrafficMessage) +
- atsi_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
+ size_t size =
+ msize + sizeof (struct NotifyTrafficMessage) +
+ atsi_count * sizeof (struct GNUNET_ATS_Information);
char buf[size];
struct NotifyTrafficMessage *ntm;
- struct GNUNET_TRANSPORT_ATS_Information *a;
+ struct GNUNET_ATS_Information *a;
if (0 == options)
{
GNUNET_snprintf (buf, sizeof (buf),
- gettext_noop ("# bytes of messages of type %u received"),
- (unsigned int) ntohs (msg->type));
+ gettext_noop ("# bytes of messages of type %u received"),
+ (unsigned int) ntohs (msg->type));
GNUNET_STATISTICS_update (GSC_stats, buf, msize, GNUNET_NO);
}
if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Core service passes message from `%4s' of type %u to client.\n",
- GNUNET_i2s (sender),
- (unsigned int) ntohs (msg->type));
+ GNUNET_i2s (sender), (unsigned int) ntohs (msg->type));
#endif
GSC_SESSIONS_add_to_typemap (sender, ntohs (msg->type));
ntm = (struct NotifyTrafficMessage *) buf;
ntm->ats_count = htonl (atsi_count);
ntm->peer = *sender;
a = &ntm->ats;
- memcpy (a, atsi,
- sizeof (struct GNUNET_TRANSPORT_ATS_Information) * atsi_count);
- a[atsi_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
+ memcpy (a, atsi, sizeof (struct GNUNET_ATS_Information) * atsi_count);
+ a[atsi_count].type = htonl (GNUNET_ATS_ARRAY_TERMINATOR);
a[atsi_count].value = htonl (0);
memcpy (&a[atsi_count + 1], msg, msize);
- send_to_all_clients (&ntm->header, GNUNET_YES,
- options, ntohs (msg->type));
+ send_to_all_clients (sender, &ntm->header, GNUNET_YES, options,
+ ntohs (msg->type));
}
{
struct GSC_Client *c;
- while (NULL != (c = client_head))
+ while (NULL != (c = client_head))
handle_client_disconnect (NULL, c->client_handle);
- GNUNET_SERVER_notification_context_destroy (notifier);
- notifier = NULL;
+ if (NULL != notifier)
+ {
+ GNUNET_SERVER_notification_context_destroy (notifier);
+ notifier = NULL;
+ }
GNUNET_SERVER_mst_destroy (client_mst);
client_mst = NULL;
}