/*
This file is part of GNUnet.
(C) 2009, 2010 Christian Grothoff (and other contributing authors)
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 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.
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., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
/**
* @file transport/gnunet-service-transport.c
* @brief low-level P2P messaging
* @author Christian Grothoff
*
*/
#include "platform.h"
#include "gnunet_client_lib.h"
#include "gnunet_container_lib.h"
#include "gnunet_constants.h"
#include "gnunet_getopt_lib.h"
#include "gnunet_hello_lib.h"
#include "gnunet_os_lib.h"
#include "gnunet_peerinfo_service.h"
#include "gnunet_plugin_lib.h"
#include "gnunet_protocols.h"
#include "gnunet_service_lib.h"
#include "gnunet_signatures.h"
#include "gnunet_transport_plugin.h"
#include "gnunet-service-transport_ats.h"
#include "transport.h"
#define DEBUG_BLACKLIST GNUNET_NO
#define DEBUG_PING_PONG GNUNET_NO
#define DEBUG_TRANSPORT_HELLO GNUNET_NO
#define DEBUG_INBOUND GNUNET_NO
/**
* Should we do some additional checks (to validate behavior
* of clients)?
*/
#define EXTRA_CHECKS GNUNET_YES
/**
* How many messages can we have pending for a given client process
* before we start to drop incoming messages? We typically should
* have only one client and so this would be the primary buffer for
* messages, so the number should be chosen rather generously.
*
* The expectation here is that most of the time the queue is large
* enough so that a drop is virtually never required. Note that
* this value must be about as large as 'TOTAL_MSGS' in the
* 'test_transport_api_reliability.c', otherwise that testcase may
* fail.
*/
#define MAX_PENDING (128 * 1024)
/**
* Size of the per-transport blacklist hash maps.
*/
#define TRANSPORT_BLACKLIST_HT_SIZE 16
/**
* How often should we try to reconnect to a peer using a particular
* transport plugin before giving up? Note that the plugin may be
* added back to the list after PLUGIN_RETRY_FREQUENCY expires.
*/
#define MAX_CONNECT_RETRY 3
/**
* Limit on the number of ready-to-run tasks when validating
* HELLOs. If more tasks are ready to run, we will drop
* HELLOs instead of validating them.
*/
#define MAX_HELLO_LOAD 4
/**
* How often must a peer violate bandwidth quotas before we start
* to simply drop its messages?
*/
#define QUOTA_VIOLATION_DROP_THRESHOLD 10
/**
* How long until a HELLO verification attempt should time out?
* Must be rather small, otherwise a partially successful HELLO
* validation (some addresses working) might not be available
* before a client's request for a connection fails for good.
* Besides, if a single request to an address takes a long time,
* then the peer is unlikely worthwhile anyway.
*/
#define HELLO_VERIFICATION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
/**
* How long is a PONG signature valid? We'll recycle a signature until
* 1/4 of this time is remaining. PONGs should expire so that if our
* external addresses change an adversary cannot replay them indefinitely.
* OTOH, we don't want to spend too much time generating PONG signatures,
* so they must have some lifetime to reduce our CPU usage.
*/
#define PONG_SIGNATURE_LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
/**
* Priority to use for PONG messages.
*/
#define TRANSPORT_PONG_PRIORITY 4
/**
* How often do we re-add (cheaper) plugins to our list of plugins
* to try for a given connected peer?
*/
#define PLUGIN_RETRY_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
/**
* After how long do we expire an address in a HELLO that we just
* validated? This value is also used for our own addresses when we
* create a HELLO.
*/
#define HELLO_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
/**
* How long before an existing address expires should we again try to
* validate it? Must be (significantly) smaller than
* HELLO_ADDRESS_EXPIRATION.
*/
#define HELLO_REVALIDATION_START_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
/**
* List of addresses of other peers
*/
struct ForeignAddressList
{
/**
* This is a linked list.
*/
struct ForeignAddressList *next;
/**
* Which ready list does this entry belong to.
*/
struct ReadyList *ready_list;
/**
* How long until we auto-expire this address (unless it is
* re-confirmed by the transport)?
*/
struct GNUNET_TIME_Absolute expires;
/**
* Task used to re-validate addresses, updates latencies and
* verifies liveness.
*/
GNUNET_SCHEDULER_TaskIdentifier revalidate_task;
/**
* The address.
*/
const void *addr;
/**
* Session (or NULL if no valid session currently exists or if the
* plugin does not use sessions).
*/
struct Session *session;
struct ATS_ressource_entry *ressources;
struct ATS_quality_entry *quality;
/**
* What was the last latency observed for this address, plugin and peer?
*/
struct GNUNET_TIME_Relative latency;
/**
* If we did not successfully transmit a message to the given peer
* via this connection during the specified time, we should consider
* the connection to be dead. This is used in the case that a TCP
* transport simply stalls writing to the stream but does not
* formerly get a signal that the other peer died.
*/
struct GNUNET_TIME_Absolute timeout;
/**
* How often have we tried to connect using this plugin? Used to
* discriminate against addresses that do not work well.
* FIXME: not yet used, but should be!
*/
unsigned int connect_attempts;
/**
* DV distance to this peer (1 if no DV is used).
* FIXME: need to set this from transport plugins!
*/
uint32_t distance;
/**
* Length of addr.
*/
uint16_t addrlen;
/**
* Have we ever estimated the latency of this address? Used to
* ensure that the first time we add an address, we immediately
* probe its latency.
*/
int8_t estimated;
/**
* Are we currently connected via this address? The first time we
* successfully transmit or receive data to a peer via a particular
* address, we set this to GNUNET_YES. If we later get an error
* (disconnect notification, transmission failure, timeout), we set
* it back to GNUNET_NO.
*/
int8_t connected;
/**
* Is this plugin currently busy transmitting to the specific target?
* GNUNET_NO if not (initial, default state is GNUNET_NO). Internal
* messages do not count as 'in transmit'.
*/
int8_t in_transmit;
/**
* Has this address been validated yet?
*/
int8_t validated;
};
/**
* Entry in linked list of network addresses for ourselves. Also
* includes a cached signature for 'struct TransportPongMessage's.
*/
struct OwnAddressList
{
/**
* This is a linked list.
*/
struct OwnAddressList *next;
/**
* How long until the current signature expires? (ZERO if the
* signature was never created).
*/
struct GNUNET_TIME_Absolute pong_sig_expires;
/**
* Signature for a 'struct TransportPongMessage' for this address.
*/
struct GNUNET_CRYPTO_RsaSignature pong_signature;
/**
* Length of addr.
*/
uint32_t addrlen;
};
/**
* Entry in linked list of all of our plugins.
*/
struct TransportPlugin
{
/**
* This is a linked list.
*/
struct TransportPlugin *next;
/**
* API of the transport as returned by the plugin's
* initialization function.
*/
struct GNUNET_TRANSPORT_PluginFunctions *api;
/**
* Short name for the plugin (i.e. "tcp").
*/
char *short_name;
/**
* Name of the library (i.e. "gnunet_plugin_transport_tcp").
*/
char *lib_name;
/**
* List of our known addresses for this transport.
*/
struct OwnAddressList *addresses;
/**
* Environment this transport service is using
* for this plugin.
*/
struct GNUNET_TRANSPORT_PluginEnvironment env;
/**
* ID of task that is used to clean up expired addresses.
*/
GNUNET_SCHEDULER_TaskIdentifier address_update_task;
/**
* Set to GNUNET_YES if we need to scrap the existing list of
* "addresses" and start fresh when we receive the next address
* update from a transport. Set to GNUNET_NO if we should just add
* the new address to the list and wait for the commit call.
*/
int rebuild;
struct ATS_plugin *rc;
/**
* Hashmap of blacklisted peers for this particular transport.
*/
struct GNUNET_CONTAINER_MultiHashMap *blacklist;
};
struct NeighbourMapEntry;
/**
* For each neighbour we keep a list of messages
* that we still want to transmit to the neighbour.
*/
struct MessageQueue
{
/**
* This is a doubly linked list.
*/
struct MessageQueue *next;
/**
* This is a doubly linked list.
*/
struct MessageQueue *prev;
/**
* The message(s) we want to transmit, GNUNET_MessageHeader(s)
* stuck together in memory. Allocated at the end of this struct.
*/
const char *message_buf;
/**
* Size of the message buf
*/
size_t message_buf_size;
/**
* Client responsible for queueing the message;
* used to check that a client has no two messages
* pending for the same target. Can be NULL.
*/
struct TransportClient *client;
/**
* Using which specific address should we send this message?
*/
struct ForeignAddressList *specific_address;
/**
* Peer ID of the Neighbour this entry belongs to.
*/
struct GNUNET_PeerIdentity neighbour_id;
/**
* Plugin that we used for the transmission.
* NULL until we scheduled a transmission.
*/
struct TransportPlugin *plugin;
/**
* At what time should we fail?
*/
struct GNUNET_TIME_Absolute timeout;
/**
* Internal message of the transport system that should not be
* included in the usual SEND-SEND_OK transmission confirmation
* traffic management scheme. Typically, "internal_msg" will
* be set whenever "client" is NULL (but it is not strictly
* required).
*/
int internal_msg;
/**
* How important is the message?
*/
unsigned int priority;
};
/**
* For a given Neighbour, which plugins are available
* to talk to this peer and what are their costs?
*/
struct ReadyList
{
/**
* This is a linked list.
*/
struct ReadyList *next;
/**
* Which of our transport plugins does this entry
* represent?
*/
struct TransportPlugin *plugin;
/**
* Transport addresses, latency, and readiness for
* this particular plugin.
*/
struct ForeignAddressList *addresses;
/**
* To which neighbour does this ready list belong to?
*/
struct NeighbourMapEntry *neighbour;
};
/**
* Entry in neighbours.
*/
struct NeighbourMapEntry
{
/**
* Which of our transports is connected to this peer
* and what is their status?
*/
struct ReadyList *plugins;
/**
* Head of list of messages we would like to send to this peer;
* must contain at most one message per client.
*/
struct MessageQueue *messages_head;
/**
* Tail of list of messages we would like to send to this peer; must
* contain at most one message per client.
*/
struct MessageQueue *messages_tail;
/**
* Head of list of messages of messages we expected the continuation
* to be called to destroy the message
*/
struct MessageQueue *cont_head;
/**
* Tail of list of messages of messages we expected the continuation
* to be called to destroy the message
*/
struct MessageQueue *cont_tail;
/**
* Buffer for at most one payload message used when we receive
* payload data before our PING-PONG has succeeded. We then
* store such messages in this intermediary buffer until the
* connection is fully up.
*/
struct GNUNET_MessageHeader *pre_connect_message_buffer;
/**
* Context for peerinfo iteration.
* NULL after we are done processing peerinfo's information.
*/
struct GNUNET_PEERINFO_IteratorContext *piter;
/**
* Public key for this peer. Valid only if the respective flag is set below.
*/
struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
/**
* Identity of this neighbour.
*/
struct GNUNET_PeerIdentity id;
/**
* ID of task scheduled to run when this peer is about to
* time out (will free resources associated with the peer).
*/
GNUNET_SCHEDULER_TaskIdentifier timeout_task;
/**
* ID of task scheduled to run when we should retry transmitting
* the head of the message queue. Actually triggered when the
* transmission is timing out (we trigger instantly when we have
* a chance of success).
*/
GNUNET_SCHEDULER_TaskIdentifier retry_task;
/**
* How long until we should consider this peer dead
* (if we don't receive another message in the
* meantime)?
*/
struct GNUNET_TIME_Absolute peer_timeout;
/**
* Tracker for inbound bandwidth.
*/
struct GNUNET_BANDWIDTH_Tracker in_tracker;
/**
* The latency we have seen for this particular address for
* this particular peer. This latency may have been calculated
* over multiple transports. This value reflects how long it took
* us to receive a response when SENDING via this particular
* transport/neighbour/address combination!
*
* FIXME: we need to periodically send PINGs to update this
* latency (at least more often than the current "huge" (11h?)
* update interval).
*/
struct GNUNET_TIME_Relative latency;
/**
* How often has the other peer (recently) violated the
* inbound traffic limit? Incremented by 10 per violation,
* decremented by 1 per non-violation (for each
* time interval).
*/
unsigned int quota_violation_count;
/**
* DV distance to this peer (1 if no DV is used).
*/
uint32_t distance;
/**
* Have we seen an PONG from this neighbour in the past (and
* not had a disconnect since)?
*/
int received_pong;
/**
* Do we have a valid public key for this neighbour?
*/
int public_key_valid;
/**
* Are we already in the process of disconnecting this neighbour?
*/
int in_disconnect;
/**
* Performance data for the peer.
*/
struct GNUNET_TRANSPORT_ATS_Information *ats;
};
/**
* Message used to ask a peer to validate receipt (to check an address
* from a HELLO). Followed by the address we are trying to validate,
* or an empty address if we are just sending a PING to confirm that a
* connection which the receiver (of the PING) initiated is still valid.
*/
struct TransportPingMessage
{
/**
* Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PING
*/
struct GNUNET_MessageHeader header;
/**
* Challenge code (to ensure fresh reply).
*/
uint32_t challenge GNUNET_PACKED;
/**
* Who is the intended recipient?
*/
struct GNUNET_PeerIdentity target;
};
/**
* Message used to validate a HELLO. The challenge is included in the
* confirmation to make matching of replies to requests possible. The
* signature signs our public key, an expiration time and our address.
*
* This message is followed by our transport address that the PING tried
* to confirm (if we liked it). The address can be empty (zero bytes)
* if the PING had not address either (and we received the request via
* a connection that we initiated).
*/
struct TransportPongMessage
{
/**
* Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PONG
*/
struct GNUNET_MessageHeader header;
/**
* Challenge code from PING (showing freshness). Not part of what
* is signed so that we can re-use signatures.
*/
uint32_t challenge GNUNET_PACKED;
/**
* Signature.
*/
struct GNUNET_CRYPTO_RsaSignature signature;
/**
* What are we signing and why? Two possible reason codes can be here:
* GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN to confirm that this is a
* plausible address for this peer (pid is set to identity of signer); or
* GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING to confirm that this is
* an address we used to connect to the peer with the given pid.
*/
struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
/**
* When does this signature expire?
*/
struct GNUNET_TIME_AbsoluteNBO expiration;
/**
* Either the identity of the peer Who signed this message, or the
* identity of the peer that we're connected to using the given
* address (depending on purpose.type).
*/
struct GNUNET_PeerIdentity pid;
/**
* Size of address appended to this message (part of what is
* being signed, hence not redundant).
*/
uint32_t addrlen;
};
/**
* Linked list of messages to be transmitted to the client. Each
* entry is followed by the actual message.
*/
struct ClientMessageQueueEntry
{
/**
* This is a doubly-linked list.
*/
struct ClientMessageQueueEntry *next;
/**
* This is a doubly-linked list.
*/
struct ClientMessageQueueEntry *prev;
};
/**
* Client connected to the transport service.
*/
struct TransportClient
{
/**
* This is a linked list.
*/
struct TransportClient *next;
/**
* Handle to the client.
*/
struct GNUNET_SERVER_Client *client;
/**
* Linked list of messages yet to be transmitted to
* the client.
*/
struct ClientMessageQueueEntry *message_queue_head;
/**
* Tail of linked list of messages yet to be transmitted to the
* client.
*/
struct ClientMessageQueueEntry *message_queue_tail;
/**
* Current transmit request handle.
*/
struct GNUNET_CONNECTION_TransmitHandle *th;
/**
* Is a call to "transmit_send_continuation" pending? If so, we
* must not free this struct (even if the corresponding client
* disconnects) and instead only remove it from the linked list and
* set the "client" field to NULL.
*/
int tcs_pending;
/**
* Length of the list of messages pending for this client.
*/
unsigned int message_count;
};
/**
* Context of currently active requests to peerinfo
* for validation of HELLOs.
*/
struct CheckHelloValidatedContext;
/**
* Entry in map of all HELLOs awaiting validation.
*/
struct ValidationEntry
{
/**
* NULL if this entry is not part of a larger HELLO validation.
*/
struct CheckHelloValidatedContext *chvc;
/**
* The address, actually a pointer to the end
* of this struct. Do not free!
*/
const void *addr;
/**
* Name of the transport.
*/
char *transport_name;
/**
* The public key of the peer.
*/
struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
/**
* ID of task that will clean up this entry if we don't succeed
* with the validation first.
*/
GNUNET_SCHEDULER_TaskIdentifier timeout_task;
/**
* At what time did we send this validation?
*/
struct GNUNET_TIME_Absolute send_time;
/**
* Session being validated (or NULL for none).
*/
struct Session *session;
/**
* Challenge number we used.
*/
uint32_t challenge;
/**
* Length of addr.
*/
uint16_t addrlen;
};
/**
* Context of currently active requests to peerinfo
* for validation of HELLOs.
*/
struct CheckHelloValidatedContext
{
/**
* This is a doubly-linked list.
*/
struct CheckHelloValidatedContext *next;
/**
* This is a doubly-linked list.
*/
struct CheckHelloValidatedContext *prev;
/**
* Hello that we are validating.
*/
const struct GNUNET_HELLO_Message *hello;
/**
* Context for peerinfo iteration.
* NULL after we are done processing peerinfo's information.
*/
struct GNUNET_PEERINFO_IteratorContext *piter;
/**
* Was a HELLO known for this peer to peerinfo?
*/
int hello_known;
/**
* Number of validation entries currently referring to this
* CHVC.
*/
unsigned int ve_count;
};
/**
* All zero hash for comparison.
*/
static GNUNET_HashCode null_hash;
/**
* Our HELLO message.
*/
static struct GNUNET_HELLO_Message *our_hello;
/**
* Our public key.
*/
static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
/**
* Our identity.
*/
static struct GNUNET_PeerIdentity my_identity;
/**
* Our private key.
*/
static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
/**
* Our configuration.
*/
const struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* Linked list of all clients to this service.
*/
static struct TransportClient *clients;
/**
* All loaded plugins.
*/
static struct TransportPlugin *plugins;
/**
* Handle to peerinfo service.
*/
static struct GNUNET_PEERINFO_Handle *peerinfo;
/**
* All known neighbours and their HELLOs.
*/
static struct GNUNET_CONTAINER_MultiHashMap *neighbours;
/**
* Number of neighbours we'd like to have.
*/
static uint32_t max_connect_per_transport;
/**
* Head of linked list.
*/
static struct CheckHelloValidatedContext *chvc_head;
/**
* Tail of linked list.
*/
static struct CheckHelloValidatedContext *chvc_tail;
/**
* Map of PeerIdentities to 'struct ValidationEntry*'s (addresses
* of the given peer that we are currently validating).
*/
static struct GNUNET_CONTAINER_MultiHashMap *validation_map;
/**
* Handle for reporting statistics.
*/
static struct GNUNET_STATISTICS_Handle *stats;
/**
* Identifier of 'refresh_hello' task.
*/
static GNUNET_SCHEDULER_TaskIdentifier hello_task;
/**
* Identifier of ats scheduler task.
*/
static GNUNET_SCHEDULER_TaskIdentifier ats_task;
/**
* Is transport service shutting down ?
*/
static int shutdown_in_progress;
/**
* Handle for ats information
*/
static struct ATS_Handle *ats;
/**
* Time of last ats execution
*/
struct GNUNET_TIME_Absolute last_ats_execution;
/**
* Minimum interval between two ATS executions
*/
struct GNUNET_TIME_Relative ats_minimum_interval;
/**
* Regular interval when ATS execution is triggered
*/
struct GNUNET_TIME_Relative ats_regular_interval;
/**
* The peer specified by the given neighbour has timed-out or a plugin
* has disconnected. We may either need to do nothing (other plugins
* still up), or trigger a full disconnect and clean up. This
* function updates our state and do the necessary notifications.
* Also notifies our clients that the neighbour is now officially
* gone.
*
* @param n the neighbour list entry for the peer
* @param check should we just check if all plugins
* disconnected or must we ask all plugins to
* disconnect?
*/
static void
disconnect_neighbour (struct NeighbourMapEntry *n, int check);
/**
* Check the ready list for the given neighbour and if a plugin is
* ready for transmission (and if we have a message), do so!
*
* @param nexi target peer for which to transmit
*/
static void
try_transmission_to_peer (struct NeighbourMapEntry *n);
struct ForeignAddressList *
get_preferred_ats_address (struct NeighbourMapEntry *n);
/**
* Find an entry in the neighbour list for a particular peer.
*
* @return NULL if not found.
*/
static struct NeighbourMapEntry *
find_neighbour (const struct GNUNET_PeerIdentity *key)
{
return GNUNET_CONTAINER_multihashmap_get (neighbours, &key->hashPubKey);
}
static int
update_addr_value (struct ForeignAddressList *fal, uint32_t value,
int ats_index)
{
int c;
int set = GNUNET_NO;
for (c = 0; c < available_quality_metrics; c++)
{
if (ats_index == qm[c].atis_index)
{
fal->quality[c].values[0] = fal->quality[c].values[1];
fal->quality[c].values[1] = fal->quality[c].values[2];
fal->quality[c].values[2] = value;
set = GNUNET_YES;
#if HAVE_LIBGLPK
ats_modify_problem_state (ats, ATS_QUALITY_UPDATED);
#endif
}
}
if (set == GNUNET_NO)
{
for (c = 0; c < available_ressources; c++)
{
if (ats_index == ressources[c].atis_index)
{
fal->ressources[c].c = value;
set = GNUNET_YES;
#if HAVE_LIBGLPK
ats_modify_problem_state (ats, ATS_COST_UPDATED);
#endif
}
}
}
return set;
}
static int
update_addr_ats (struct ForeignAddressList *fal,
const struct GNUNET_TRANSPORT_ATS_Information *ats_data,
int ats_count)
{
int c1, set;
set = GNUNET_NO;
for (c1 = 0; c1 < ats_count; c1++)
{
set =
update_addr_value (fal, ntohl (ats_data[c1].value),
ntohl (ats_data[c1].type));
}
return set;
}
/**
* Find an entry in the transport list for a particular transport.
*
* @return NULL if not found.
*/
static struct TransportPlugin *
find_transport (const char *short_name)
{
struct TransportPlugin *head = plugins;
while ((head != NULL) && (0 != strcmp (short_name, head->short_name)))
head = head->next;
return head;
}
/**
* Is a particular peer blacklisted for a particular transport?
*
* @param peer the peer to check for
* @param plugin the plugin used to connect to the peer
*
* @return GNUNET_YES if the peer is blacklisted, GNUNET_NO if not
*/
static int
is_blacklisted (const struct GNUNET_PeerIdentity *peer,
struct TransportPlugin *plugin)
{
if (plugin->blacklist != NULL)
{
if (GNUNET_CONTAINER_multihashmap_contains
(plugin->blacklist, &peer->hashPubKey) == GNUNET_YES)
{
#if DEBUG_BLACKLIST
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s:%s' is blacklisted!\n",
plugin->short_name, GNUNET_i2s (peer));
#endif
if (stats != NULL)
GNUNET_STATISTICS_update (stats, "# blacklisted peers refused", 1,
GNUNET_NO);
return GNUNET_YES;
}
}
return GNUNET_NO;
}
static void
add_peer_to_blacklist (struct GNUNET_PeerIdentity *peer, char *transport_name)
{
struct TransportPlugin *plugin;
plugin = find_transport (transport_name);
if (plugin == NULL) /* Nothing to do */
return;
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Adding peer `%s' with plugin `%s' to blacklist\n",
GNUNET_i2s (peer), transport_name);
#endif
if (plugin->blacklist == NULL)
plugin->blacklist =
GNUNET_CONTAINER_multihashmap_create (TRANSPORT_BLACKLIST_HT_SIZE);
GNUNET_assert (plugin->blacklist != NULL);
GNUNET_CONTAINER_multihashmap_put (plugin->blacklist, &peer->hashPubKey, NULL,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
}
/**
* Read the blacklist file, containing transport:peer entries.
* Provided the transport is loaded, set up hashmap with these
* entries to blacklist peers by transport.
*
*/
static void
read_blacklist_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
{
char *fn;
char *data;
size_t pos;
size_t colon_pos;
int tsize;
struct GNUNET_PeerIdentity pid;
struct stat frstat;
struct GNUNET_CRYPTO_HashAsciiEncoded enc;
unsigned int entries_found;
char *transport_name;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_filename (cfg, "TRANSPORT",
"BLACKLIST_FILE", &fn))
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Option `%s' in section `%s' not specified!\n",
"BLACKLIST_FILE", "TRANSPORT");
#endif
return;
}
if (GNUNET_OK != GNUNET_DISK_file_test (fn))
GNUNET_DISK_fn_write (fn, NULL, 0,
GNUNET_DISK_PERM_USER_READ |
GNUNET_DISK_PERM_USER_WRITE);
if (0 != STAT (fn, &frstat))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Could not read blacklist file `%s'\n"), fn);
GNUNET_free (fn);
return;
}
if (frstat.st_size == 0)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Blacklist file `%s' is empty.\n"),
fn);
#endif
GNUNET_free (fn);
return;
}
/* FIXME: use mmap */
data = GNUNET_malloc_large (frstat.st_size);
GNUNET_assert (data != NULL);
if (frstat.st_size != GNUNET_DISK_fn_read (fn, data, frstat.st_size))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Failed to read blacklist from `%s'\n"), fn);
GNUNET_free (fn);
GNUNET_free (data);
return;
}
entries_found = 0;
pos = 0;
while ((pos < frstat.st_size) && isspace ((unsigned char) data[pos]))
pos++;
while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
(pos <=
frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
{
colon_pos = pos;
while ((colon_pos < frstat.st_size) && (data[colon_pos] != ':') &&
!isspace ((unsigned char) data[colon_pos]))
colon_pos++;
if (colon_pos >= frstat.st_size)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_
("Syntax error in blacklist file at offset %llu, giving up!\n"),
(unsigned long long) colon_pos);
GNUNET_free (fn);
GNUNET_free (data);
return;
}
if (isspace ((unsigned char) data[colon_pos]))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_
("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
(unsigned long long) colon_pos);
pos = colon_pos;
while ((pos < frstat.st_size) && isspace ((unsigned char) data[pos]))
pos++;
continue;
}
tsize = colon_pos - pos;
if ((pos >= frstat.st_size) || (pos + tsize >= frstat.st_size) ||
(tsize == 0))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_
("Syntax error in blacklist file at offset %llu, giving up!\n"),
(unsigned long long) colon_pos);
GNUNET_free (fn);
GNUNET_free (data);
return;
}
if (tsize < 1)
continue;
transport_name = GNUNET_malloc (tsize + 1);
memcpy (transport_name, &data[pos], tsize);
pos = colon_pos + 1;
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Read transport name %s in blacklist file.\n", transport_name);
#endif
memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
if (!isspace
((unsigned char)
enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_
("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
(unsigned long long) pos);
pos++;
while ((pos < frstat.st_size) && (!isspace ((unsigned char) data[pos])))
pos++;
GNUNET_free_non_null (transport_name);
continue;
}
enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
if (GNUNET_OK !=
GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_
("Syntax error in blacklist file at offset %llu, skipping bytes `%s'.\n"),
(unsigned long long) pos, &enc);
}
else
{
if (0 != memcmp (&pid, &my_identity, sizeof (struct GNUNET_PeerIdentity)))
{
entries_found++;
add_peer_to_blacklist (&pid, transport_name);
}
else
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_("Found myself `%s' in blacklist (useless, ignored)\n"),
GNUNET_i2s (&pid));
}
}
pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
GNUNET_free_non_null (transport_name);
while ((pos < frstat.st_size) && isspace ((unsigned char) data[pos]))
pos++;
}
GNUNET_STATISTICS_update (stats, "# Transport entries blacklisted",
entries_found, GNUNET_NO);
GNUNET_free (data);
GNUNET_free (fn);
}
/**
* Function called to notify a client about the socket being ready to
* queue more data. "buf" will be NULL and "size" zero if the socket
* was closed for writing in the meantime.
*
* @param cls closure
* @param size number of bytes available in buf
* @param buf where the callee should write the message
* @return number of bytes written to buf
*/
static size_t
transmit_to_client_callback (void *cls, size_t size, void *buf)
{
struct TransportClient *client = cls;
struct ClientMessageQueueEntry *q;
uint16_t msize;
size_t tsize;
const struct GNUNET_MessageHeader *msg;
char *cbuf;
client->th = NULL;
if (buf == NULL)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Transmission to client failed, closing connection.\n");
#endif
/* fatal error with client, free message queue! */
while (NULL != (q = client->message_queue_head))
{
GNUNET_STATISTICS_update (stats,
gettext_noop
("# bytes discarded (could not transmit to client)"),
ntohs (((const struct GNUNET_MessageHeader *)
&q[1])->size), GNUNET_NO);
GNUNET_CONTAINER_DLL_remove (client->message_queue_head,
client->message_queue_tail, q);
GNUNET_free (q);
}
client->message_count = 0;
return 0;
}
cbuf = buf;
tsize = 0;
while (NULL != (q = client->message_queue_head))
{
msg = (const struct GNUNET_MessageHeader *) &q[1];
msize = ntohs (msg->size);
if (msize + tsize > size)
break;
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Transmitting message of type %u to client.\n",
ntohs (msg->type));
#endif
GNUNET_CONTAINER_DLL_remove (client->message_queue_head,
client->message_queue_tail, q);
memcpy (&cbuf[tsize], msg, msize);
tsize += msize;
GNUNET_free (q);
client->message_count--;
}
if (NULL != q)
{
GNUNET_assert (msize >= sizeof (struct GNUNET_MessageHeader));
client->th =
GNUNET_SERVER_notify_transmit_ready (client->client, msize,
GNUNET_TIME_UNIT_FOREVER_REL,
&transmit_to_client_callback,
client);
GNUNET_assert (client->th != NULL);
}
return tsize;
}
/**
* Convert an address to a string.
*
* @param plugin name of the plugin responsible for the address
* @param addr binary address
* @param addr_len number of bytes in addr
* @return NULL on error, otherwise address string
*/
static const char *
a2s (const char *plugin, const void *addr, uint16_t addr_len)
{
struct TransportPlugin *p;
if (plugin == NULL)
return NULL;
p = find_transport (plugin);
if ((p == NULL) || (addr_len == 0) || (addr == NULL))
return NULL;
return p->api->address_to_string (NULL, addr, addr_len);
}
/**
* Iterator to free entries in the validation_map.
*
* @param cls closure (unused)
* @param key current key code
* @param value value in the hash map (validation to abort)
* @return GNUNET_YES (always)
*/
static int
abort_validation (void *cls, const GNUNET_HashCode * key, void *value)
{
struct ValidationEntry *va = value;
if (GNUNET_SCHEDULER_NO_TASK != va->timeout_task)
GNUNET_SCHEDULER_cancel (va->timeout_task);
GNUNET_free (va->transport_name);
if (va->chvc != NULL)
{
va->chvc->ve_count--;
if (va->chvc->ve_count == 0)
{
GNUNET_CONTAINER_DLL_remove (chvc_head, chvc_tail, va->chvc);
GNUNET_free (va->chvc);
}
va->chvc = NULL;
}
GNUNET_free (va);
return GNUNET_YES;
}
/**
* HELLO validation cleanup task (validation failed).
*
* @param cls the 'struct ValidationEntry' that failed
* @param tc scheduler context (unused)
*/
static void
timeout_hello_validation (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
struct ValidationEntry *va = cls;
struct GNUNET_PeerIdentity pid;
va->timeout_task = GNUNET_SCHEDULER_NO_TASK;
GNUNET_STATISTICS_update (stats,
gettext_noop ("# address validation timeouts"), 1,
GNUNET_NO);
GNUNET_CRYPTO_hash (&va->publicKey,
sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
&pid.hashPubKey);
GNUNET_break (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_remove (validation_map,
&pid.hashPubKey, va));
abort_validation (NULL, NULL, va);
}
/**
* Send the specified message to the specified client. Since multiple
* messages may be pending for the same client at a time, this code
* makes sure that no message is lost.
*
* @param client client to transmit the message to
* @param msg the message to send
* @param may_drop can this message be dropped if the
* message queue for this client is getting far too large?
*/
static void
transmit_to_client (struct TransportClient *client,
const struct GNUNET_MessageHeader *msg, int may_drop)
{
struct ClientMessageQueueEntry *q;
uint16_t msize;
/* Client==NULL when GNUNET_SERVER_Client disconnected and was
* freed in client_disconnect_notification
*/
if (client->client == NULL)
{
GNUNET_break (0);
return;
}
if ((client->message_count >= MAX_PENDING) && (GNUNET_YES == may_drop))
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_
("Dropping message of type %u and size %u, have %u messages pending (%u is the soft limit)\n"),
ntohs (msg->type), ntohs (msg->size), client->message_count,
MAX_PENDING);
GNUNET_STATISTICS_update (stats,
gettext_noop
("# messages dropped due to slow client"), 1,
GNUNET_NO);
return;
}
msize = ntohs (msg->size);
GNUNET_assert (msize >= sizeof (struct GNUNET_MessageHeader));
q = GNUNET_malloc (sizeof (struct ClientMessageQueueEntry) + msize);
memcpy (&q[1], msg, msize);
GNUNET_CONTAINER_DLL_insert_tail (client->message_queue_head,
client->message_queue_tail, q);
client->message_count++;
if (client->th == NULL)
{
client->th =
GNUNET_SERVER_notify_transmit_ready (client->client, msize,
GNUNET_TIME_UNIT_FOREVER_REL,
&transmit_to_client_callback,
client);
GNUNET_assert (client->th != NULL);
}
}
/**
* Transmit a 'SEND_OK' notification to the given client for the
* given neighbour.
*
* @param client who to notify
* @param n neighbour to notify about, can be NULL (on failure)
* @param target target of the transmission
* @param result status code for the transmission request
*/
static void
transmit_send_ok (struct TransportClient *client, struct NeighbourMapEntry *n,
const struct GNUNET_PeerIdentity *target, int result)
{
struct SendOkMessage send_ok_msg;
send_ok_msg.header.size = htons (sizeof (send_ok_msg));
send_ok_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK);
send_ok_msg.success = htonl (result);
if (n != NULL)
send_ok_msg.latency = GNUNET_TIME_relative_hton (n->latency);
else
send_ok_msg.latency =
GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_FOREVER_REL);
send_ok_msg.peer = *target;
transmit_to_client (client, &send_ok_msg.header, GNUNET_NO);
}
/**
* Mark the given FAL entry as 'connected' (and hence preferred for
* sending); also mark all others for the same peer as 'not connected'
* (since only one can be preferred).
*
* @param fal address to set to 'connected'
*/
static void
mark_address_connected (struct ForeignAddressList *fal);
/**
* We should re-try transmitting to the given peer,
* hopefully we've learned something in the meantime.
*/
static void
retry_transmission_task (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
struct NeighbourMapEntry *n = cls;
n->retry_task = GNUNET_SCHEDULER_NO_TASK;
try_transmission_to_peer (n);
}
/**
* Function called by the GNUNET_TRANSPORT_TransmitFunction
* upon "completion" of a send request. This tells the API
* that it is now legal to send another message to the given
* peer.
*
* @param cls closure, identifies the entry on the
* message queue that was transmitted and the
* client responsible for queuing the message
* @param target the peer receiving the message
* @param result GNUNET_OK on success, if the transmission
* failed, we should not tell the client to transmit
* more messages
*/
static void
transmit_send_continuation (void *cls, const struct GNUNET_PeerIdentity *target,
int result)
{
struct MessageQueue *mq = cls;
struct NeighbourMapEntry *n;
GNUNET_STATISTICS_update (stats,
gettext_noop ("# bytes pending with plugins"),
-(int64_t) mq->message_buf_size, GNUNET_NO);
if (result == GNUNET_OK)
{
GNUNET_STATISTICS_update (stats,
gettext_noop
("# bytes successfully transmitted by plugins"),
mq->message_buf_size, GNUNET_NO);
}
else
{
GNUNET_STATISTICS_update (stats,
gettext_noop
("# bytes with transmission failure by plugins"),
mq->message_buf_size, GNUNET_NO);
}
if (mq->specific_address != NULL)
{
if (result == GNUNET_OK)
{
mq->specific_address->timeout =
GNUNET_TIME_relative_to_absolute
(GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
if (mq->specific_address->validated == GNUNET_YES)
mark_address_connected (mq->specific_address);
}
else
{
if (mq->specific_address->connected == GNUNET_YES)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Marking address `%s' as no longer connected (due to transmission problem)\n",
a2s (mq->specific_address->ready_list->plugin->short_name,
mq->specific_address->addr,
mq->specific_address->addrlen));
#endif
GNUNET_STATISTICS_update (stats, gettext_noop ("# connected addresses"),
-1, GNUNET_NO);
mq->specific_address->connected = GNUNET_NO;
}
}
if (!mq->internal_msg)
mq->specific_address->in_transmit = GNUNET_NO;
}
n = find_neighbour (&mq->neighbour_id);
if (mq->client != NULL)
transmit_send_ok (mq->client, n, target, result);
GNUNET_assert (n != NULL);
GNUNET_CONTAINER_DLL_remove (n->cont_head, n->cont_tail, mq);
GNUNET_free (mq);
if (result == GNUNET_OK)
try_transmission_to_peer (n);
else if (GNUNET_SCHEDULER_NO_TASK == n->retry_task)
n->retry_task = GNUNET_SCHEDULER_add_now (&retry_transmission_task, n);
}
/**
* Check the ready list for the given neighbour and if a plugin is
* ready for transmission (and if we have a message), do so!
*
* @param neighbour target peer for which to transmit
*/
static void
try_transmission_to_peer (struct NeighbourMapEntry *n)
{
struct ReadyList *rl;
struct MessageQueue *mq;
struct GNUNET_TIME_Relative timeout;
ssize_t ret;
int force_address;
if (n->messages_head == NULL)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Transmission queue for `%4s' is empty\n", GNUNET_i2s (&n->id));
#endif
return; /* nothing to do */
}
rl = NULL;
mq = n->messages_head;
force_address = GNUNET_YES;
if (mq->specific_address == NULL)
{
/* TODO: ADD ATS */
mq->specific_address = get_preferred_ats_address (n);
GNUNET_STATISTICS_update (stats,
gettext_noop
("# transport selected peer address freely"), 1,
GNUNET_NO);
force_address = GNUNET_NO;
}
if (mq->specific_address == NULL)
{
GNUNET_STATISTICS_update (stats,
gettext_noop
("# transport failed to selected peer address"),
1, GNUNET_NO);
timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
if (timeout.rel_value == 0)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"No destination address available to transmit message of size %u to peer `%4s'\n",
mq->message_buf_size, GNUNET_i2s (&mq->neighbour_id));
#endif
GNUNET_STATISTICS_update (stats,
gettext_noop
("# bytes in message queue for other peers"),
-(int64_t) mq->message_buf_size, GNUNET_NO);
GNUNET_STATISTICS_update (stats,
gettext_noop
("# bytes discarded (no destination address available)"),
mq->message_buf_size, GNUNET_NO);
if (mq->client != NULL)
transmit_send_ok (mq->client, n, &n->id, GNUNET_NO);
GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
GNUNET_free (mq);
return; /* nobody ready */
}
GNUNET_STATISTICS_update (stats,
gettext_noop
("# message delivery deferred (no address)"), 1,
GNUNET_NO);
if (n->retry_task != GNUNET_SCHEDULER_NO_TASK)
GNUNET_SCHEDULER_cancel (n->retry_task);
n->retry_task =
GNUNET_SCHEDULER_add_delayed (timeout, &retry_transmission_task, n);
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"No validated destination address available to transmit message of size %u to peer `%4s', will wait %llums to find an address.\n",
mq->message_buf_size, GNUNET_i2s (&mq->neighbour_id),
timeout.rel_value);
#endif
/* FIXME: might want to trigger peerinfo lookup here
* (unless that's already pending...) */
return;
}
GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
if (mq->specific_address->connected == GNUNET_NO)
mq->specific_address->connect_attempts++;
rl = mq->specific_address->ready_list;
mq->plugin = rl->plugin;
if (!mq->internal_msg)
mq->specific_address->in_transmit = GNUNET_YES;
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Sending message of size %u for `%4s' to `%s' via plugin `%s'\n",
mq->message_buf_size, GNUNET_i2s (&n->id),
(mq->specific_address->addr !=
NULL) ? a2s (mq->plugin->short_name, mq->specific_address->addr,
mq->specific_address->addrlen) : "",
rl->plugin->short_name);
#endif
GNUNET_STATISTICS_update (stats,
gettext_noop
("# bytes in message queue for other peers"),
-(int64_t) mq->message_buf_size, GNUNET_NO);
GNUNET_STATISTICS_update (stats,
gettext_noop ("# bytes pending with plugins"),
mq->message_buf_size, GNUNET_NO);
GNUNET_CONTAINER_DLL_insert (n->cont_head, n->cont_tail, mq);
ret =
rl->plugin->api->send (rl->plugin->api->cls, &mq->neighbour_id,
mq->message_buf, mq->message_buf_size,
mq->priority,
GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
mq->specific_address->session,
mq->specific_address->addr,
mq->specific_address->addrlen, force_address,
&transmit_send_continuation, mq);
if (ret == -1)
{
/* failure, but 'send' would not call continuation in this case,
* so we need to do it here! */
transmit_send_continuation (mq, &mq->neighbour_id, GNUNET_SYSERR);
}
}
/**
* Send the specified message to the specified peer.
*
* @param client source of the transmission request (can be NULL)
* @param peer_address ForeignAddressList where we should send this message
* @param priority how important is the message
* @param timeout how long do we have to transmit?
* @param message_buf message(s) to send GNUNET_MessageHeader(s)
* @param message_buf_size total size of all messages in message_buf
* @param is_internal is this an internal message; these are pre-pended and
* also do not count for plugins being "ready" to transmit
* @param neighbour handle to the neighbour for transmission
*/
static void
transmit_to_peer (struct TransportClient *client,
struct ForeignAddressList *peer_address,
unsigned int priority, struct GNUNET_TIME_Relative timeout,
const char *message_buf, size_t message_buf_size,
int is_internal, struct NeighbourMapEntry *neighbour)
{
struct MessageQueue *mq;
#if EXTRA_CHECKS
if (client != NULL)
{
/* check for duplicate submission */
mq = neighbour->messages_head;
while (NULL != mq)
{
if (mq->client == client)
{
/* client transmitted to same peer twice
* before getting SEND_OK! */
GNUNET_break (0);
return;
}
mq = mq->next;
}
}
#endif
GNUNET_STATISTICS_update (stats,
gettext_noop
("# bytes in message queue for other peers"),
message_buf_size, GNUNET_NO);
mq = GNUNET_malloc (sizeof (struct MessageQueue) + message_buf_size);
mq->specific_address = peer_address;
mq->client = client;
/* FIXME: this memcpy can be up to 7% of our total runtime! */
memcpy (&mq[1], message_buf, message_buf_size);
mq->message_buf = (const char *) &mq[1];
mq->message_buf_size = message_buf_size;
memcpy (&mq->neighbour_id, &neighbour->id,
sizeof (struct GNUNET_PeerIdentity));
mq->internal_msg = is_internal;
mq->priority = priority;
mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
if (is_internal)
GNUNET_CONTAINER_DLL_insert (neighbour->messages_head,
neighbour->messages_tail, mq);
else
GNUNET_CONTAINER_DLL_insert_after (neighbour->messages_head,
neighbour->messages_tail,
neighbour->messages_tail, mq);
try_transmission_to_peer (neighbour);
}
/**
* Send a plain PING (without address or our HELLO) to the given
* foreign address to try to establish a connection (and validate
* that the other peer is really who he claimed he is).
*
* @param n neighbour to PING
*/
static void
transmit_plain_ping (struct NeighbourMapEntry *n)
{
struct ValidationEntry *ve;
struct TransportPingMessage ping;
struct ReadyList *rl;
struct TransportPlugin *plugin;
struct ForeignAddressList *fal;
if (!n->public_key_valid)
{
/* This should not happen since the other peer
* should send us a HELLO prior to sending his
* PING */
GNUNET_break_op (0);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Could not transmit plain PING to `%s': public key not known\n",
GNUNET_i2s (&n->id));
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Looking for addresses to transmit plain PING to `%s'\n",
GNUNET_i2s (&n->id));
for (rl = n->plugins; rl != NULL; rl = rl->next)
{
plugin = rl->plugin;
for (fal = rl->addresses; fal != NULL; fal = fal->next)
{
if (!fal->connected)
continue;
ve = GNUNET_malloc (sizeof (struct ValidationEntry));
ve->transport_name = GNUNET_strdup (plugin->short_name);
ve->challenge =
GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT_MAX);
ve->send_time = GNUNET_TIME_absolute_get ();
ve->session = fal->session;
memcpy (&ve->publicKey, &n->publicKey,
sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
ve->timeout_task =
GNUNET_SCHEDULER_add_delayed (HELLO_VERIFICATION_TIMEOUT,
&timeout_hello_validation, ve);
GNUNET_CONTAINER_multihashmap_put (validation_map, &n->id.hashPubKey, ve,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
ping.header.size = htons (sizeof (struct TransportPingMessage));
ping.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
ping.challenge = htonl (ve->challenge);
memcpy (&ping.target, &n->id, sizeof (struct GNUNET_PeerIdentity));
GNUNET_STATISTICS_update (stats,
gettext_noop
("# PING without HELLO messages sent"), 1,
GNUNET_NO);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting plain PING to `%s'\n",
GNUNET_i2s (&n->id));
transmit_to_peer (NULL, fal, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
HELLO_VERIFICATION_TIMEOUT, (const char *) &ping,
sizeof (ping), GNUNET_YES, n);
}
}
}
/**
* Mark the given FAL entry as 'connected' (and hence preferred for
* sending); also mark all others for the same peer as 'not connected'
* (since only one can be preferred).
*
* @param fal address to set to 'connected'
*/
static void
mark_address_connected (struct ForeignAddressList *fal)
{
struct ForeignAddressList *pos;
struct ForeignAddressList *inbound;
struct ForeignAddressList *outbound;
GNUNET_assert (GNUNET_YES == fal->validated);
if (fal->connected == GNUNET_YES)
return; /* nothing to do */
inbound = NULL;
outbound = NULL;
pos = fal->ready_list->addresses;
while (pos != NULL)
{
/* Already have inbound address, and this is also an inbound address, don't switch!! */
if ((GNUNET_YES == pos->connected) && (0 == pos->addrlen) &&
(0 == fal->addrlen))
return;
if ((0 == pos->addrlen) && (GNUNET_YES == pos->connected))
inbound = pos;
pos = pos->next;
}
pos = fal->ready_list->addresses;
while (pos != NULL)
{
/* Already have outbound address, and this is also an outbound address, don't switch!! */
if ((GNUNET_YES == pos->connected) && (0 < pos->addrlen) &&
(0 < fal->addrlen))
return;
if ((0 < pos->addrlen) && (GNUNET_YES == pos->connected))
outbound = pos;
pos = pos->next;
}
#if DEBUG_INBOUND
if (inbound != NULL)
fprintf (stderr, "Peer: %s, have inbound connection.\n",
GNUNET_i2s (&my_identity));
if (outbound != NULL)
fprintf (stderr, "Peer: %s, have outbound connection.\n",
GNUNET_i2s (&my_identity));
#endif
/* Have an inbound connection to this peer which is valid; our id is lower, ignore outbound connection! */
if ((inbound != NULL) && (0 != fal->addrlen) &&
(1 ==
GNUNET_CRYPTO_hash_xorcmp (&inbound->ready_list->neighbour->id.
hashPubKey, &my_identity.hashPubKey,
&null_hash)))
{
#if DEBUG_INBOUND
fprintf (stderr, "Peer: %s, had inbound connection, ignoring outbound!\n",
GNUNET_i2s (&my_identity));
#endif
return;
}
else if ((outbound != NULL) && (0 == fal->addrlen) &&
((-1 ==
GNUNET_CRYPTO_hash_xorcmp (&outbound->ready_list->neighbour->
id.hashPubKey, &my_identity.hashPubKey,
&null_hash))))
{
#if DEBUG_INBOUND
fprintf (stderr, "Peer: %s, have outbound connection, ignoring inbound!\n",
GNUNET_i2s (&my_identity));
#endif
return;
}
pos = fal->ready_list->addresses;
while (pos != NULL)
{
if ((GNUNET_YES == pos->connected) && (0 < pos->addrlen))
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Marking address `%s' as no longer connected (due to connect on other address)\n",
a2s (pos->ready_list->plugin->short_name, pos->addr,
pos->addrlen));
#endif
#if DEBUG_INBOUND
fprintf (stderr, "Peer: %s, setting %s connection to disconnected.\n",
GNUNET_i2s (&my_identity),
(0 == pos->addrlen) ? "INBOUND" : "OUTBOUND");
#endif
pos->connected = GNUNET_NO;
GNUNET_STATISTICS_update (stats, gettext_noop ("# connected addresses"),
-1, GNUNET_NO);
}
pos = pos->next;
}
GNUNET_assert (GNUNET_NO == fal->connected);
fal->connected = GNUNET_YES;
GNUNET_STATISTICS_update (stats, gettext_noop ("# connected addresses"), 1,
GNUNET_NO);
}
/**
* Find an address in any of the available transports for
* the given neighbour that would be good for message
* transmission. This is essentially the transport selection
* routine.
*
* @param neighbour for whom to select an address
* @return selected address, NULL if we have none
*/
struct ForeignAddressList *
find_ready_address (struct NeighbourMapEntry *neighbour)
{
struct ReadyList *head = neighbour->plugins;
struct ForeignAddressList *addresses;
struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
struct ForeignAddressList *best_address;
/* Hack to prefer unix domain sockets */
struct ForeignAddressList *unix_address = NULL;
best_address = NULL;
while (head != NULL)
{
addresses = head->addresses;
while (addresses != NULL)
{
if ((addresses->timeout.abs_value < now.abs_value) &&
(addresses->connected == GNUNET_YES))
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Marking long-time inactive connection to `%4s' as down.\n",
GNUNET_i2s (&neighbour->id));
#endif
GNUNET_STATISTICS_update (stats, gettext_noop ("# connected addresses"),
-1, GNUNET_NO);
addresses->connected = GNUNET_NO;
}
addresses = addresses->next;
}
addresses = head->addresses;
while (addresses != NULL)
{
#if DEBUG_TRANSPORT
if (addresses->addr != NULL)
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Have address `%s' for peer `%4s' (status: %d, %d, %d, %u, %llums, %u)\n",
a2s (head->plugin->short_name, addresses->addr,
addresses->addrlen), GNUNET_i2s (&neighbour->id),
addresses->connected, addresses->in_transmit,
addresses->validated, addresses->connect_attempts,
(unsigned long long) addresses->timeout.abs_value,
(unsigned int) addresses->distance);
#endif
if (0 == strcmp (head->plugin->short_name, "unix"))
{
if ((unix_address == NULL) ||
((unix_address != NULL) &&
(addresses->latency.rel_value < unix_address->latency.rel_value)))
unix_address = addresses;
}
if (((best_address == NULL) || (addresses->connected == GNUNET_YES) ||
(best_address->connected == GNUNET_NO)) &&
(addresses->in_transmit == GNUNET_NO) && ((best_address == NULL) ||
(addresses->
latency.rel_value <
best_address->
latency.rel_value)))
best_address = addresses;
/* FIXME: also give lower-latency addresses that are not
* connected a chance some times... */
addresses = addresses->next;
}
if (unix_address != NULL)
break;
head = head->next;
}
if (unix_address != NULL)
{
best_address = unix_address;
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Found UNIX address, forced this address\n");
#endif
}
if (best_address != NULL)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Best address found (`%s') has latency of %llu ms.\n",
(best_address->addrlen >
0) ? a2s (best_address->ready_list->plugin->short_name,
best_address->addr,
best_address->addrlen) : "",
best_address->latency.rel_value);
#endif
}
else
{
GNUNET_STATISTICS_update (stats,
gettext_noop
("# transmission attempts failed (no address)"),
1, GNUNET_NO);
}
return best_address;
}
/**
* FIXME: document.
*/
struct GeneratorContext
{
struct TransportPlugin *plug_pos;
struct OwnAddressList *addr_pos;
struct GNUNET_TIME_Absolute expiration;
};
/**
* FIXME: document.
*/
static size_t
address_generator (void *cls, size_t max, void *buf)
{
struct GeneratorContext *gc = cls;
size_t ret;
while ((gc->addr_pos == NULL) && (gc->plug_pos != NULL))
{
gc->plug_pos = gc->plug_pos->next;
gc->addr_pos = (gc->plug_pos != NULL) ? gc->plug_pos->addresses : NULL;
}
if (NULL == gc->plug_pos)
{
return 0;
}
ret =
GNUNET_HELLO_add_address (gc->plug_pos->short_name, gc->expiration,
&gc->addr_pos[1], gc->addr_pos->addrlen, buf,
max);
gc->addr_pos = gc->addr_pos->next;
return ret;
}
static int
transmit_our_hello_if_pong (void *cls, const GNUNET_HashCode * key, void *value)
{
struct NeighbourMapEntry *npos = value;
if (GNUNET_YES != npos->received_pong)
return GNUNET_OK;
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
"Transmitting updated `%s' to neighbour `%4s'\n", "HELLO",
GNUNET_i2s (&npos->id));
#endif
GNUNET_STATISTICS_update (stats,
gettext_noop
("# transmitted my HELLO to other peers"), 1,
GNUNET_NO);
transmit_to_peer (NULL, NULL, 0, HELLO_ADDRESS_EXPIRATION,
(const char *) our_hello, GNUNET_HELLO_size (our_hello),
GNUNET_NO, npos);
return GNUNET_OK;
}
/**
* Construct our HELLO message from all of the addresses of
* all of the transports.
*
* @param cls unused
* @param tc scheduler context
*/
static void
refresh_hello_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
struct GNUNET_HELLO_Message *hello;
struct TransportClient *cpos;
struct GeneratorContext gc;
hello_task = GNUNET_SCHEDULER_NO_TASK;
gc.plug_pos = plugins;
gc.addr_pos = plugins != NULL ? plugins->addresses : NULL;
gc.expiration = GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION);
hello = GNUNET_HELLO_create (&my_public_key, &address_generator, &gc);
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
"Refreshed my `%s', new size is %d\n", "HELLO",
GNUNET_HELLO_size (hello));
#endif
GNUNET_STATISTICS_update (stats, gettext_noop ("# refreshed my HELLO"), 1,
GNUNET_NO);
cpos = clients;
while (cpos != NULL)
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting my HELLO to client!\n");
transmit_to_client (cpos, (const struct GNUNET_MessageHeader *) hello,
GNUNET_NO);
cpos = cpos->next;
}
GNUNET_free_non_null (our_hello);
our_hello = hello;
GNUNET_PEERINFO_add_peer (peerinfo, our_hello);
GNUNET_CONTAINER_multihashmap_iterate (neighbours,
&transmit_our_hello_if_pong, NULL);
}
/**
* Schedule task to refresh hello (unless such a
* task exists already).
*/
static void
refresh_hello ()
{
#if DEBUG_TRANSPORT_HELLO
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "refresh_hello() called!\n");
#endif
if (hello_task != GNUNET_SCHEDULER_NO_TASK)
return;
hello_task = GNUNET_SCHEDULER_add_now (&refresh_hello_task, NULL);
}
/**
* Iterator over hash map entries that NULLs the session of validation
* entries that match the given session.
*
* @param cls closure (the 'struct Session*' to match against)
* @param key current key code (peer ID, not used)
* @param value value in the hash map ('struct ValidationEntry*')
* @return GNUNET_YES (we should continue to iterate)
*/
static int
remove_session_validations (void *cls, const GNUNET_HashCode * key, void *value)
{
struct Session *session = cls;
struct ValidationEntry *ve = value;
if (session == ve->session)
ve->session = NULL;
return GNUNET_YES;
}
/**
* We've been disconnected from the other peer (for some
* connection-oriented transport). Either quickly
* re-establish the connection or signal the disconnect
* to the CORE.
*
* Only signal CORE level disconnect if ALL addresses
* for the peer are exhausted.
*
* @param p overall plugin context
* @param nl neighbour that was disconnected
*/
static void
try_fast_reconnect (struct TransportPlugin *p, struct NeighbourMapEntry *nl)
{
/* FIXME-MW: fast reconnect / transport switching not implemented... */
GNUNET_log (GNUNET_ERROR_TYPE_INFO, "try_fast_reconnect not implemented!\n");
/* Note: the idea here is to hide problems with transports (or
* switching between plugins) from the core to eliminate the need to
* re-negotiate session keys and the like; OTOH, we should tell core
* quickly (much faster than timeout) `if a connection was lost and
* could not be re-established (i.e. other peer went down or is
* unable / refuses to communicate);
*
* So we should consider:
* 1) ideally: our own willingness / need to connect
* 2) prior failures to connect to this peer (by plugin)
* 3) ideally: reasons why other peer terminated (as far as knowable)
*
* Most importantly, it must be POSSIBLE for another peer to terminate
* a connection for a while (without us instantly re-establishing it).
* Similarly, if another peer is gone we should quickly notify CORE.
* OTOH, if there was a minor glitch (i.e. crash of gnunet-service-transport
* on the other end), we should reconnect in such a way that BOTH CORE
* services never even notice.
* Furthermore, the same mechanism (or small variation) could be used
* to switch to a better-performing plugin (ATS).
*
* Finally, this needs to be tested throughly... */
/*
* GNUNET_NO in the call below makes transport disconnect the peer,
* even if only a single address (out of say, six) went away. This
* function must be careful to ONLY disconnect if the peer is gone,
* not just a specific address.
*
* More specifically, half the places it was used had it WRONG.
*/
/* No reconnect, signal disconnect instead! */
#if DEBUG_TRANSPORT
#endif
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting peer `%4s', %s\n",
GNUNET_i2s (&nl->id), "try_fast_reconnect");
GNUNET_STATISTICS_update (stats,
gettext_noop
("# disconnects due to try_fast_reconnect"), 1,
GNUNET_NO);
#if DISCONNECT || 1
disconnect_neighbour (nl, GNUNET_YES);
#endif
}
/**
* Function that will be called whenever the plugin internally
* cleans up a session pointer and hence the service needs to
* discard all of those sessions as well. Plugins that do not
* use sessions can simply omit calling this function and always
* use NULL wherever a session pointer is needed.
*
* @param cls closure
* @param peer which peer was the session for
* @param session which session is being destoyed
*/
static void
plugin_env_session_end (void *cls, const struct GNUNET_PeerIdentity *peer,
struct Session *session)
{
struct TransportPlugin *p = cls;
struct NeighbourMapEntry *nl;
struct ReadyList *rl;
struct ForeignAddressList *pos;
struct ForeignAddressList *prev;
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Session ended with peer `%4s', %s\n",
GNUNET_i2s (peer), "plugin_env_session_end");
#endif
GNUNET_CONTAINER_multihashmap_iterate (validation_map,
&remove_session_validations, session);
nl = find_neighbour (peer);
if (nl == NULL)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"No neighbour record found for peer `%4s'\n",
GNUNET_i2s (peer));
#endif
return; /* was never marked as connected */
}
rl = nl->plugins;
while (rl != NULL)
{
if (rl->plugin == p)
break;
rl = rl->next;
}
if (rl == NULL)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Plugin was associated with peer `%4s'\n", GNUNET_i2s (peer));
#endif
GNUNET_STATISTICS_update (stats,
gettext_noop ("# disconnects due to session end"),
1, GNUNET_NO);
disconnect_neighbour (nl, GNUNET_YES);
return;
}
prev = NULL;
pos = rl->addresses;
while ((pos != NULL) && (pos->session != session))
{
prev = pos;
pos = pos->next;
}
if (pos == NULL)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Session was never marked as ready for peer `%4s'\n",
GNUNET_i2s (peer));
#endif
int validations_pending =
GNUNET_CONTAINER_multihashmap_contains (validation_map,
&peer->hashPubKey);
/* No session was marked as ready, but we have pending validations so do not disconnect from neighbour */
if (validations_pending == GNUNET_YES)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Not disconnecting from peer `%4s due to pending address validations\n",
GNUNET_i2s (peer));
#endif
return;
}
//FIXME: This conflicts with inbound tcp connections and tcp nat ... debugging in progress
GNUNET_STATISTICS_update (stats,
gettext_noop
("# disconnects due to unready session"), 1,
GNUNET_NO);
disconnect_neighbour (nl, GNUNET_YES);
return; /* was never marked as connected */
}
pos->session = NULL;
if (GNUNET_YES == pos->connected)
{
pos->connected = GNUNET_NO;
GNUNET_STATISTICS_update (stats, gettext_noop ("# connected addresses"), -1,
GNUNET_NO);
}
if (GNUNET_SCHEDULER_NO_TASK != pos->revalidate_task)
{
GNUNET_SCHEDULER_cancel (pos->revalidate_task);
pos->revalidate_task = GNUNET_SCHEDULER_NO_TASK;
}
if (pos->addrlen != 0)
{
if (nl->received_pong != GNUNET_NO)
{
GNUNET_STATISTICS_update (stats,
gettext_noop
("# try_fast_reconnect thanks to plugin_env_session_end"),
1, GNUNET_NO);
if (GNUNET_YES == pos->connected)
try_fast_reconnect (p, nl);
}
else
{
GNUNET_STATISTICS_update (stats,
gettext_noop
("# disconnects due to missing pong"), 1,
GNUNET_NO);
/* FIXME this is never true?! See: line 2416 */
if (GNUNET_YES == pos->connected)
disconnect_neighbour (nl, GNUNET_YES);
}
return;
}
/* was inbound connection, free 'pos' */
if (prev == NULL)
rl->addresses = pos->next;
else
prev->next = pos->next;
if (GNUNET_SCHEDULER_NO_TASK != pos->revalidate_task)
{
GNUNET_SCHEDULER_cancel (pos->revalidate_task);
pos->revalidate_task = GNUNET_SCHEDULER_NO_TASK;
}
GNUNET_free_non_null (pos->ressources);
GNUNET_free_non_null (pos->quality);
#if HAVE_LIBGLPK
ats_modify_problem_state (ats, ATS_MODIFIED);
#endif
if (GNUNET_YES != pos->connected)
{
/* nothing else to do, connection was never up... */
GNUNET_free (pos);
return;
}
pos->connected = GNUNET_NO;
GNUNET_STATISTICS_update (stats, gettext_noop ("# connected addresses"), -1,
GNUNET_NO);
GNUNET_free (pos);
if (nl->received_pong == GNUNET_NO)
{
GNUNET_STATISTICS_update (stats,
gettext_noop ("# disconnects due to NO pong"), 1,
GNUNET_NO);
disconnect_neighbour (nl, GNUNET_YES);
return; /* nothing to do, never connected... */
}
/* check if we have any validated addresses left */
pos = rl->addresses;
while (pos != NULL)
{
if (GNUNET_YES == pos->validated)
{
GNUNET_STATISTICS_update (stats,
gettext_noop
("# try_fast_reconnect thanks to validated_address"),
1, GNUNET_NO);
try_fast_reconnect (p, nl);
return;
}
pos = pos->next;
}
/* no valid addresses left, signal disconnect! */
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting peer `%4s', %s\n",
GNUNET_i2s (peer), "plugin_env_session_end");
#endif
/* FIXME: This doesn't mean there are no addresses left for this PEER,
* it means there aren't any left for this PLUGIN/PEER combination! So
* calling disconnect_neighbour here with GNUNET_NO forces disconnect
* when it isn't necessary. Using GNUNET_YES at least checks to see
* if there are any addresses that work first, so as not to overdo it.
* --NE
*/
GNUNET_STATISTICS_update (stats,
gettext_noop
("# disconnects due to plugin_env_session_end"), 1,
GNUNET_NO);
disconnect_neighbour (nl, GNUNET_YES);
}
/**
* Function that must be called by each plugin to notify the
* transport service about the addresses under which the transport
* provided by the plugin can be reached.
*
* @param cls closure
* @param add_remove GNUNET_YES to add, GNUNET_NO to remove the address
* @param addr one of the addresses of the host, NULL for the last address
* the specific address format depends on the transport
* @param addrlen length of the address
*/
static void
plugin_env_notify_address (void *cls, int add_remove, const void *addr,
size_t addrlen)
{
struct TransportPlugin *p = cls;
struct OwnAddressList *al;
struct OwnAddressList *prev;
GNUNET_assert (p->api != NULL);
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
(add_remove ==
GNUNET_YES) ? "Adding `%s':%s to the set of our addresses\n" :
"Removing `%s':%s from the set of our addresses\n",
a2s (p->short_name, addr, addrlen), p->short_name);
#endif
GNUNET_assert (addr != NULL);
if (GNUNET_NO == add_remove)
{
prev = NULL;
al = p->addresses;
while (al != NULL)
{
if ((addrlen == al->addrlen) && (0 == memcmp (addr, &al[1], addrlen)))
{
if (prev == NULL)
p->addresses = al->next;
else
prev->next = al->next;
GNUNET_free (al);
refresh_hello ();
return;
}
prev = al;
al = al->next;
}
GNUNET_break (0);
return;
}
al = GNUNET_malloc (sizeof (struct OwnAddressList) + addrlen);
al->next = p->addresses;
p->addresses = al;
al->addrlen = addrlen;
memcpy (&al[1], addr, addrlen);
refresh_hello ();
}
/**
* Notify all of our clients about a peer connecting.
*/
static void
notify_clients_connect (const struct GNUNET_PeerIdentity *peer,
struct GNUNET_TIME_Relative latency, uint32_t distance)
{
struct ConnectInfoMessage *cim;
struct TransportClient *cpos;
uint32_t ats_count;
size_t size;
if (0 == memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity)))
{
GNUNET_break (0);
return;
}
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Notifying clients about connection with `%s'\n",
GNUNET_i2s (peer));
#endif
GNUNET_STATISTICS_update (stats, gettext_noop ("# peers connected"), 1,
GNUNET_NO);
ats_count = 2;
size =
sizeof (struct ConnectInfoMessage) +
ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
cim = GNUNET_malloc (size);
cim->header.size = htons (size);
cim->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
cim->ats_count = htonl (2);
(&cim->ats)[0].type = htonl (GNUNET_TRANSPORT_ATS_QUALITY_NET_DISTANCE);
(&cim->ats)[0].value = htonl (distance);
(&cim->ats)[1].type = htonl (GNUNET_TRANSPORT_ATS_QUALITY_NET_DELAY);
(&cim->ats)[1].value = htonl ((uint32_t) latency.rel_value);
(&cim->ats)[2].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
(&cim->ats)[2].value = htonl (0);
memcpy (&cim->id, peer, sizeof (struct GNUNET_PeerIdentity));
/* notify ats about connecting peer */
if ((ats != NULL) && (shutdown_in_progress == GNUNET_NO))
{
#if HAVE_LIBGLPK
ats_modify_problem_state (ats, ATS_MODIFIED);
ats_calculate_bandwidth_distribution (ats);
#endif
}
cpos = clients;
while (cpos != NULL)
{
transmit_to_client (cpos, &cim->header, GNUNET_NO);
cpos = cpos->next;
}
GNUNET_free (cim);
}
/**
* Notify all of our clients about a peer disconnecting.
*/
static void
notify_clients_disconnect (const struct GNUNET_PeerIdentity *peer)
{
struct DisconnectInfoMessage dim;
struct TransportClient *cpos;
if (0 == memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity)))
{
GNUNET_break (0);
return;
}
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Notifying clients about lost connection to `%s'\n",
GNUNET_i2s (peer));
#endif
GNUNET_STATISTICS_update (stats, gettext_noop ("# peers connected"), -1,
GNUNET_NO);
dim.header.size = htons (sizeof (struct DisconnectInfoMessage));
dim.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT);
dim.reserved = htonl (0);
memcpy (&dim.peer, peer, sizeof (struct GNUNET_PeerIdentity));
/* notify ats about connecting peer */
if ((ats != NULL) && (shutdown_in_progress == GNUNET_NO))
{
#if HAVE_LIBGLPK
ats_modify_problem_state (ats, ATS_MODIFIED);
ats_calculate_bandwidth_distribution (ats);
#endif
}
cpos = clients;
while (cpos != NULL)
{
transmit_to_client (cpos, &dim.header, GNUNET_NO);
cpos = cpos->next;
}
}
/**
* Find a ForeignAddressList entry for the given neighbour
* that matches the given address and transport.
*
* @param neighbour which peer we care about
* @param tname name of the transport plugin
* @param session session to look for, NULL for 'any'; otherwise
* can be used for the service to "learn" this session ID
* if 'addr' matches
* @param addr binary address
* @param addrlen length of addr
* @return NULL if no such entry exists
*/
static struct ForeignAddressList *
find_peer_address (struct NeighbourMapEntry *neighbour, const char *tname,
struct Session *session, const char *addr, uint16_t addrlen)
{
struct ReadyList *head;
struct ForeignAddressList *pos;
head = neighbour->plugins;
while (head != NULL)
{
if (0 == strcmp (tname, head->plugin->short_name))
break;
head = head->next;
}
if (head == NULL)
return NULL;
pos = head->addresses;
while ((pos != NULL) &&
((pos->addrlen != addrlen) ||
(memcmp (pos->addr, addr, addrlen) != 0)))
{
if ((session != NULL) && (pos->session == session))
return pos;
pos = pos->next;
}
if ((session != NULL) && (pos != NULL))
pos->session = session; /* learn it! */
return pos;
}
/**
* Get the peer address struct for the given neighbour and
* address. If it doesn't yet exist, create it.
*
* @param neighbour which peer we care about
* @param tname name of the transport plugin
* @param session session of the plugin, or NULL for none
* @param addr binary address
* @param addrlen length of addr
* @return NULL if we do not have a transport plugin for 'tname'
*/
static struct ForeignAddressList *
add_peer_address (struct NeighbourMapEntry *neighbour, const char *tname,
struct Session *session, const char *addr, uint16_t addrlen)
{
struct ReadyList *head;
struct ForeignAddressList *ret;
int c;
ret = find_peer_address (neighbour, tname, session, addr, addrlen);
if (ret != NULL)
return ret;
head = neighbour->plugins;
while (head != NULL)
{
if (0 == strcmp (tname, head->plugin->short_name))
break;
head = head->next;
}
if (head == NULL)
return NULL;
ret = GNUNET_malloc (sizeof (struct ForeignAddressList) + addrlen);
ret->session = session;
if ((addrlen > 0) && (addr != NULL))
{
ret->addr = (const char *) &ret[1];
memcpy (&ret[1], addr, addrlen);
}
else
{
ret->addr = NULL;
}
ret->ressources =
GNUNET_malloc (available_ressources *
sizeof (struct ATS_ressource_entry));
for (c = 0; c < available_ressources; c++)
{
struct ATS_ressource_entry *r = ret->ressources;
r[c].index = c;
r[c].atis_index = ressources[c].atis_index;
if (0 == strcmp (neighbour->plugins->plugin->short_name, "unix"))
{
r[c].c = ressources[c].c_unix;
}
else if (0 == strcmp (neighbour->plugins->plugin->short_name, "udp"))
{
r[c].c = ressources[c].c_udp;
}
else if (0 == strcmp (neighbour->plugins->plugin->short_name, "tcp"))
{
r[c].c = ressources[c].c_tcp;
}
else if (0 == strcmp (neighbour->plugins->plugin->short_name, "http"))
{
r[c].c = ressources[c].c_http;
}
else if (0 == strcmp (neighbour->plugins->plugin->short_name, "https"))
{
r[c].c = ressources[c].c_https;
}
else if (0 == strcmp (neighbour->plugins->plugin->short_name, "wlan"))
{
r[c].c = ressources[c].c_wlan;
}
else
{
r[c].c = ressources[c].c_default;
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Assigning default cost to peer `%s' addr plugin `%s'! This should not happen!\n",
GNUNET_i2s (&neighbour->id),
neighbour->plugins->plugin->short_name);
}
}
ret->quality =
GNUNET_malloc (available_quality_metrics *
sizeof (struct ATS_quality_entry));
ret->addrlen = addrlen;
ret->expires =
GNUNET_TIME_relative_to_absolute
(GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
ret->latency = GNUNET_TIME_relative_get_forever ();
ret->distance = -1;
ret->timeout =
GNUNET_TIME_relative_to_absolute
(GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
ret->ready_list = head;
ret->next = head->addresses;
head->addresses = ret;
return ret;
}
/**
* Closure for 'add_validated_address'.
*/
struct AddValidatedAddressContext
{
/**
* Entry that has been validated.
*/
const struct ValidationEntry *ve;
/**
* Flag set after we have added the address so
* that we terminate the iteration next time.
*/
int done;
};
/**
* Callback function used to fill a buffer of max bytes with a list of
* addresses in the format used by HELLOs. Should use
* "GNUNET_HELLO_add_address" as a helper function.
*
* @param cls the 'struct AddValidatedAddressContext' with the validated address
* @param max maximum number of bytes that can be written to buf
* @param buf where to write the address information
* @return number of bytes written, 0 to signal the
* end of the iteration.
*/
static size_t
add_validated_address (void *cls, size_t max, void *buf)
{
struct AddValidatedAddressContext *avac = cls;
const struct ValidationEntry *ve = avac->ve;
if (GNUNET_YES == avac->done)
return 0;
avac->done = GNUNET_YES;
return GNUNET_HELLO_add_address (ve->transport_name,
GNUNET_TIME_relative_to_absolute
(HELLO_ADDRESS_EXPIRATION), ve->addr,
ve->addrlen, buf, max);
}
/**
* Closure for 'check_address_exists'.
*/
struct CheckAddressExistsClosure
{
/**
* Address to check for.
*/
const void *addr;
/**
* Name of the transport.
*/
const char *tname;
/**
* Session, or NULL.
*/
struct Session *session;
/**
* Set to GNUNET_YES if the address exists.
*/
int exists;
/**
* Length of addr.
*/
uint16_t addrlen;
};
/**
* Iterator over hash map entries. Checks if the given
* validation entry is for the same address as what is given
* in the closure.
*
* @param cls the 'struct CheckAddressExistsClosure*'
* @param key current key code (ignored)
* @param value value in the hash map ('struct ValidationEntry')
* @return GNUNET_YES if we should continue to
* iterate (mismatch), GNUNET_NO if not (entry matched)
*/
static int
check_address_exists (void *cls, const GNUNET_HashCode * key, void *value)
{
struct CheckAddressExistsClosure *caec = cls;
struct ValidationEntry *ve = value;
if ((0 == strcmp (caec->tname, ve->transport_name)) &&
(caec->addrlen == ve->addrlen) &&
(0 == memcmp (caec->addr, ve->addr, caec->addrlen)))
{
caec->exists = GNUNET_YES;
return GNUNET_NO;
}
if ((ve->session != NULL) && (caec->session == ve->session))
{
caec->exists = GNUNET_YES;
return GNUNET_NO;
}
return GNUNET_YES;
}
static void
neighbour_timeout_task (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
struct NeighbourMapEntry *n = cls;
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
"Neighbour `%4s' has timed out!\n", GNUNET_i2s (&n->id));
#endif
GNUNET_STATISTICS_update (stats,
gettext_noop ("# disconnects due to timeout"), 1,
GNUNET_NO);
n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
disconnect_neighbour (n, GNUNET_NO);
}
/**
* Schedule the job that will cause us to send a PING to the
* foreign address to evaluate its validity and latency.
*
* @param fal address to PING
*/
static void
schedule_next_ping (struct ForeignAddressList *fal);
/**
* Add the given address to the list of foreign addresses
* available for the given peer (check for duplicates).
*
* @param cls the respective 'struct NeighbourMapEntry' to update
* @param tname name of the transport
* @param expiration expiration time
* @param addr the address
* @param addrlen length of the address
* @return GNUNET_OK (always)
*/
static int
add_to_foreign_address_list (void *cls, const char *tname,
struct GNUNET_TIME_Absolute expiration,
const void *addr, uint16_t addrlen)
{
struct NeighbourMapEntry *n = cls;
struct ForeignAddressList *fal;
int try;
GNUNET_STATISTICS_update (stats,
gettext_noop
("# valid peer addresses returned by PEERINFO"), 1,
GNUNET_NO);
try = GNUNET_NO;
fal = find_peer_address (n, tname, NULL, addr, addrlen);
if (fal == NULL)
{
#if DEBUG_TRANSPORT_HELLO
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Adding address `%s' (%s) for peer `%4s' due to PEERINFO data for %llums.\n",
a2s (tname, addr, addrlen), tname, GNUNET_i2s (&n->id),
expiration.abs_value);
#endif
fal = add_peer_address (n, tname, NULL, addr, addrlen);
if (fal == NULL)
{
GNUNET_STATISTICS_update (stats,
gettext_noop
("# previously validated addresses lacking transport"),
1, GNUNET_NO);
}
else
{
fal->expires = GNUNET_TIME_absolute_max (expiration, fal->expires);
schedule_next_ping (fal);
}
try = GNUNET_YES;
}
else
{
fal->expires = GNUNET_TIME_absolute_max (expiration, fal->expires);
}
if (fal == NULL)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Failed to add new address for `%4s'\n", GNUNET_i2s (&n->id));
#endif
return GNUNET_OK;
}
if (fal->validated == GNUNET_NO)
{
fal->validated = GNUNET_YES;
GNUNET_STATISTICS_update (stats,
gettext_noop
("# peer addresses considered valid"), 1,
GNUNET_NO);
}
if (try == GNUNET_YES)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Have new addresses, will try to trigger transmissions.\n");
#endif
try_transmission_to_peer (n);
}
return GNUNET_OK;
}
/**
* Add addresses in validated HELLO "h" to the set of addresses
* we have for this peer.
*
* @param cls closure ('struct NeighbourMapEntry*')
* @param peer id of the peer, NULL for last call
* @param h hello message for the peer (can be NULL)
* @param err_msg NULL if successful, otherwise contains error message
*/
static void
add_hello_for_peer (void *cls, const struct GNUNET_PeerIdentity *peer,
const struct GNUNET_HELLO_Message *h, const char *err_msg)
{
struct NeighbourMapEntry *n = cls;
if (err_msg != NULL)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
_("Error in communication with PEERINFO service: %s\n"),
err_msg);
#endif
/* return; */
}
if (peer == NULL)
{
GNUNET_STATISTICS_update (stats,
gettext_noop
("# outstanding peerinfo iterate requests"), -1,
GNUNET_NO);
n->piter = NULL;
return;
}
if (h == NULL)
return; /* no HELLO available */
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Peerinfo had `%s' message for peer `%4s', adding existing addresses.\n",
"HELLO", GNUNET_i2s (peer));
#endif
if (GNUNET_YES != n->public_key_valid)
{
GNUNET_HELLO_get_key (h, &n->publicKey);
n->public_key_valid = GNUNET_YES;
}
GNUNET_HELLO_iterate_addresses (h, GNUNET_NO, &add_to_foreign_address_list,
n);
}
/**
* Create a fresh entry in our neighbour list for the given peer.
* Will try to transmit our current HELLO to the new neighbour.
* Do not call this function directly, use 'setup_peer_check_blacklist.
*
* @param peer the peer for which we create the entry
* @param do_hello should we schedule transmitting a HELLO
* @return the new neighbour list entry
*/
static struct NeighbourMapEntry *
setup_new_neighbour (const struct GNUNET_PeerIdentity *peer, int do_hello)
{
struct NeighbourMapEntry *n;
struct TransportPlugin *tp;
struct ReadyList *rl;
GNUNET_assert (0 !=
memcmp (peer, &my_identity,
sizeof (struct GNUNET_PeerIdentity)));
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Setting up state for neighbour `%4s'\n",
GNUNET_i2s (peer));
#endif
GNUNET_STATISTICS_update (stats, gettext_noop ("# active neighbours"), 1,
GNUNET_NO);
n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
n->id = *peer;
n->peer_timeout =
GNUNET_TIME_relative_to_absolute
(GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
MAX_BANDWIDTH_CARRY_S);
tp = plugins;
while (tp != NULL)
{
if ((tp->api->send != NULL) && (!is_blacklisted (peer, tp)))
{
rl = GNUNET_malloc (sizeof (struct ReadyList));
rl->neighbour = n;
rl->next = n->plugins;
n->plugins = rl;
rl->plugin = tp;
rl->addresses = NULL;
}
tp = tp->next;
}
n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
n->distance = -1;
n->timeout_task =
GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
&neighbour_timeout_task, n);
GNUNET_CONTAINER_multihashmap_put (neighbours, &n->id.hashPubKey, n,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
if (do_hello)
{
GNUNET_STATISTICS_update (stats,
gettext_noop
("# peerinfo new neighbor iterate requests"), 1,
GNUNET_NO);
GNUNET_STATISTICS_update (stats,
gettext_noop
("# outstanding peerinfo iterate requests"), 1,
GNUNET_NO);
n->piter =
GNUNET_PEERINFO_iterate (peerinfo, peer, GNUNET_TIME_UNIT_FOREVER_REL,
&add_hello_for_peer, n);
GNUNET_STATISTICS_update (stats,
gettext_noop ("# HELLO's sent to new neighbors"),
1, GNUNET_NO);
if (NULL != our_hello)
transmit_to_peer (NULL, NULL, 0, HELLO_ADDRESS_EXPIRATION,
(const char *) our_hello, GNUNET_HELLO_size (our_hello),
GNUNET_NO, n);
}
return n;
}
/**
* Function called after we have checked if communicating
* with a given peer is acceptable.
*
* @param cls closure
* @param n NULL if communication is not acceptable
*/
typedef void (*SetupContinuation) (void *cls, struct NeighbourMapEntry * n);
/**
* Information kept for each client registered to perform
* blacklisting.
*/
struct Blacklisters
{
/**
* This is a linked list.
*/
struct Blacklisters *next;
/**
* This is a linked list.
*/
struct Blacklisters *prev;
/**
* Client responsible for this entry.
*/
struct GNUNET_SERVER_Client *client;
/**
* Blacklist check that we're currently performing.
*/
struct BlacklistCheck *bc;
};
/**
* Head of DLL of blacklisting clients.
*/
static struct Blacklisters *bl_head;
/**
* Tail of DLL of blacklisting clients.
*/
static struct Blacklisters *bl_tail;
/**
* Context we use when performing a blacklist check.
*/
struct BlacklistCheck
{
/**
* This is a linked list.
*/
struct BlacklistCheck *next;
/**
* This is a linked list.
*/
struct BlacklistCheck *prev;
/**
* Peer being checked.
*/
struct GNUNET_PeerIdentity peer;
/**
* Option for setup neighbour afterwards.
*/
int do_hello;
/**
* Continuation to call with the result.
*/
SetupContinuation cont;
/**
* Closure for cont.
*/
void *cont_cls;
/**
* Current transmission request handle for this client, or NULL if no
* request is pending.
*/
struct GNUNET_CONNECTION_TransmitHandle *th;
/**
* Our current position in the blacklisters list.
*/
struct Blacklisters *bl_pos;
/**
* Current task performing the check.
*/
GNUNET_SCHEDULER_TaskIdentifier task;
};
/**
* Head of DLL of active blacklisting queries.
*/
static struct BlacklistCheck *bc_head;
/**
* Tail of DLL of active blacklisting queries.
*/
static struct BlacklistCheck *bc_tail;
/**
* Perform next action in the blacklist check.
*
* @param cls the 'struct BlacklistCheck*'
* @param tc unused
*/
static void
do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
/**
* Transmit blacklist query to the client.
*
* @param cls the 'struct BlacklistCheck'
* @param size number of bytes allowed
* @param buf where to copy the message
* @return number of bytes copied to buf
*/
static size_t
transmit_blacklist_message (void *cls, size_t size, void *buf)
{
struct BlacklistCheck *bc = cls;
struct Blacklisters *bl;
struct BlacklistMessage bm;
bc->th = NULL;
if (size == 0)
{
GNUNET_assert (bc->task == GNUNET_SCHEDULER_NO_TASK);
bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to send blacklist test for peer `%s' to client\n",
GNUNET_i2s (&bc->peer));
return 0;
}
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Sending blacklist test for peer `%s' to client\n",
GNUNET_i2s (&bc->peer));
#endif
bl = bc->bl_pos;
bm.header.size = htons (sizeof (struct BlacklistMessage));
bm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY);
bm.is_allowed = htonl (0);
bm.peer = bc->peer;
memcpy (buf, &bm, sizeof (bm));
GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
return sizeof (bm);
}
/**
* Perform next action in the blacklist check.
*
* @param cls the 'struct BlacklistCheck*'
* @param tc unused
*/
static void
do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
struct BlacklistCheck *bc = cls;
struct Blacklisters *bl;
bc->task = GNUNET_SCHEDULER_NO_TASK;
bl = bc->bl_pos;
if (bl == NULL)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"No blacklist clients active, will now setup neighbour record for peer `%s'\n",
GNUNET_i2s (&bc->peer));
#endif
bc->cont (bc->cont_cls, setup_new_neighbour (&bc->peer, bc->do_hello));
GNUNET_free (bc);
return;
}
if (bl->bc == NULL)
{
bl->bc = bc;
bc->th =
GNUNET_SERVER_notify_transmit_ready (bl->client,
sizeof (struct BlacklistMessage),
GNUNET_TIME_UNIT_FOREVER_REL,
&transmit_blacklist_message, bc);
}
}
/**
* Obtain a 'struct NeighbourMapEntry' for the given peer. If such an entry
* does not yet exist, check the blacklist. If the blacklist says creating
* one is acceptable, create one and call the continuation; otherwise
* call the continuation with NULL.
*
* @param peer peer to setup or look up a struct NeighbourMapEntry for
* @param do_hello should we also schedule sending our HELLO to the peer
* if this is a new record
* @param cont function to call with the 'struct NeigbhbourList*'
* @param cont_cls closure for cont
*/
static void
setup_peer_check_blacklist (const struct GNUNET_PeerIdentity *peer,
int do_hello, SetupContinuation cont,
void *cont_cls)
{
struct NeighbourMapEntry *n;
struct BlacklistCheck *bc;
n = find_neighbour (peer);
if (n != NULL)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Neighbour record exists for peer `%s'\n", GNUNET_i2s (peer));
#endif
if (cont != NULL)
cont (cont_cls, n);
return;
}
if (bl_head == NULL)
{
if (cont != NULL)
cont (cont_cls, setup_new_neighbour (peer, do_hello));
else
setup_new_neighbour (peer, do_hello);
return;
}
bc = GNUNET_malloc (sizeof (struct BlacklistCheck));
GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
bc->peer = *peer;
bc->do_hello = do_hello;
bc->cont = cont;
bc->cont_cls = cont_cls;
bc->bl_pos = bl_head;
bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
}
/**
* Function called with the result of querying a new blacklister about
* it being allowed (or not) to continue to talk to an existing neighbour.
*
* @param cls the original 'struct NeighbourMapEntry'
* @param n NULL if we need to disconnect
*/
static void
confirm_or_drop_neighbour (void *cls, struct NeighbourMapEntry *n)
{
struct NeighbourMapEntry *orig = cls;
if (n == NULL)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting peer `%4s', %s\n",
GNUNET_i2s (&orig->id), "confirm_or_drop_neighboUr");
#endif
GNUNET_STATISTICS_update (stats,
gettext_noop ("# disconnects due to blacklist"),
1, GNUNET_NO);
disconnect_neighbour (orig, GNUNET_NO);
}
}
struct TestConnectionContext
{
int first;
struct Blacklisters *bl;
};
static int
test_connection_ok (void *cls, const GNUNET_HashCode * key, void *value)
{
struct TestConnectionContext *tcc = cls;
struct NeighbourMapEntry *n = value;
struct BlacklistCheck *bc;
bc = GNUNET_malloc (sizeof (struct BlacklistCheck));
GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
bc->peer = n->id;
bc->do_hello = GNUNET_NO;
bc->cont = &confirm_or_drop_neighbour;
bc->cont_cls = n;
bc->bl_pos = tcc->bl;
if (GNUNET_YES == tcc->first)
{
/* all would wait for the same client, no need to
* create more than just the first task right now */
bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
tcc->first = GNUNET_NO;
}
return GNUNET_OK;
}
/**
* Handle a request to start a blacklist.
*
* @param cls closure (always NULL)
* @param client identification of the client
* @param message the actual message
*/
static void
handle_blacklist_init (void *cls, struct GNUNET_SERVER_Client *client,
const struct GNUNET_MessageHeader *message)
{
struct Blacklisters *bl;
struct TestConnectionContext tcc;
bl = bl_head;
while (bl != NULL)
{
if (bl->client == client)
{
GNUNET_break (0);
GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
return;
}
bl = bl->next;
}
bl = GNUNET_malloc (sizeof (struct Blacklisters));
bl->client = client;
GNUNET_SERVER_client_keep (client);
GNUNET_CONTAINER_DLL_insert_after (bl_head, bl_tail, bl_tail, bl);
/* confirm that all existing connections are OK! */
tcc.bl = bl;
tcc.first = GNUNET_YES;
GNUNET_CONTAINER_multihashmap_iterate (neighbours, &test_connection_ok, &tcc);
}
/**
* Handle a request to blacklist a peer.
*
* @param cls closure (always NULL)
* @param client identification of the client
* @param message the actual message
*/
static void
handle_blacklist_reply (void *cls, struct GNUNET_SERVER_Client *client,
const struct GNUNET_MessageHeader *message)
{
const struct BlacklistMessage *msg =
(const struct BlacklistMessage *) message;
struct Blacklisters *bl;
struct BlacklistCheck *bc;
bl = bl_head;
while ((bl != NULL) && (bl->client != client))
bl = bl->next;
if (bl == NULL)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist client disconnected\n");
#endif
/* FIXME: other error handling here!? */
GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
return;
}
bc = bl->bc;
bl->bc = NULL;
if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Blacklist check failed, peer not allowed\n");
#endif
bc->cont (bc->cont_cls, NULL);
GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
GNUNET_free (bc);
}
else
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Blacklist check succeeded, continuing with checks\n");
#endif
bc->bl_pos = bc->bl_pos->next;
bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
}
/* check if any other bc's are waiting for this blacklister */
bc = bc_head;
while (bc != NULL)
{
if ((bc->bl_pos == bl) && (GNUNET_SCHEDULER_NO_TASK == bc->task))
bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
bc = bc->next;
}
}
/**
* Send periodic PING messages to a given foreign address.
*
* @param cls our 'struct PeriodicValidationContext*'
* @param tc task context
*/
static void
send_periodic_ping (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
struct ForeignAddressList *peer_address = cls;
struct TransportPlugin *tp;
struct ValidationEntry *va;
struct NeighbourMapEntry *neighbour;
struct TransportPingMessage ping;
struct CheckAddressExistsClosure caec;
char *message_buf;
uint16_t hello_size;
size_t slen;
size_t tsize;
peer_address->revalidate_task = GNUNET_SCHEDULER_NO_TASK;
if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
return;
GNUNET_assert (peer_address != NULL);
tp = peer_address->ready_list->plugin;
neighbour = peer_address->ready_list->neighbour;
if (GNUNET_YES != neighbour->public_key_valid)
{
/* no public key yet, try again later */
schedule_next_ping (peer_address);
return;
}
caec.addr = peer_address->addr;
caec.addrlen = peer_address->addrlen;
caec.tname = tp->short_name;
caec.session = peer_address->session;
caec.exists = GNUNET_NO;
GNUNET_CONTAINER_multihashmap_iterate (validation_map, &check_address_exists,
&caec);
if (caec.exists == GNUNET_YES)
{
/* During validation attempts we will likely trigger the other
* peer trying to validate our address which in turn will cause
* it to send us its HELLO, so we expect to hit this case rather
* frequently. Only print something if we are very verbose. */
#if DEBUG_TRANSPORT > 1
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Some validation of address `%s' via `%s' for peer `%4s' already in progress.\n",
(peer_address->addr != NULL) ? a2s (tp->short_name,
peer_address->addr,
peer_address->addrlen) :
"", tp->short_name, GNUNET_i2s (&neighbour->id));
#endif
schedule_next_ping (peer_address);
return;
}
va = GNUNET_malloc (sizeof (struct ValidationEntry) + peer_address->addrlen);
va->transport_name = GNUNET_strdup (tp->short_name);
va->challenge =
GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT_MAX);
va->send_time = GNUNET_TIME_absolute_get ();
va->session = peer_address->session;
if (peer_address->addr != NULL)
{
va->addr = (const void *) &va[1];
memcpy (&va[1], peer_address->addr, peer_address->addrlen);
va->addrlen = peer_address->addrlen;
}
memcpy (&va->publicKey, &neighbour->publicKey,
sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
va->timeout_task =
GNUNET_SCHEDULER_add_delayed (HELLO_VERIFICATION_TIMEOUT,
&timeout_hello_validation, va);
GNUNET_CONTAINER_multihashmap_put (validation_map, &neighbour->id.hashPubKey,
va,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
if (peer_address->validated != GNUNET_YES)
hello_size = GNUNET_HELLO_size (our_hello);
else
hello_size = 0;
tsize = sizeof (struct TransportPingMessage) + hello_size;
if (peer_address->addr != NULL)
{
slen = strlen (tp->short_name) + 1;
tsize += slen + peer_address->addrlen;
}
else
{
slen = 0; /* make gcc happy */
}
message_buf = GNUNET_malloc (tsize);
ping.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
ping.challenge = htonl (va->challenge);
memcpy (&ping.target, &neighbour->id, sizeof (struct GNUNET_PeerIdentity));
if (peer_address->validated != GNUNET_YES)
{
memcpy (message_buf, our_hello, hello_size);
}
if (peer_address->addr != NULL)
{
ping.header.size =
htons (sizeof (struct TransportPingMessage) + peer_address->addrlen +
slen);
memcpy (&message_buf[hello_size + sizeof (struct TransportPingMessage)],
tp->short_name, slen);
memcpy (&message_buf
[hello_size + sizeof (struct TransportPingMessage) + slen],
peer_address->addr, peer_address->addrlen);
}
else
{
ping.header.size = htons (sizeof (struct TransportPingMessage));
}
memcpy (&message_buf[hello_size], &ping,
sizeof (struct TransportPingMessage));
#if DEBUG_TRANSPORT_REVALIDATION
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Performing re-validation of address `%s' via `%s' for peer `%4s' sending `%s' (%u bytes) and `%s'\n",
(peer_address->addr !=
NULL) ? a2s (peer_address->plugin->short_name,
peer_address->addr,
peer_address->addrlen) : "",
tp->short_name, GNUNET_i2s (&neighbour->id), "HELLO", hello_size,
"PING");
#endif
if (peer_address->validated != GNUNET_YES)
GNUNET_STATISTICS_update (stats,
gettext_noop ("# PING with HELLO messages sent"),
1, GNUNET_NO);
else
GNUNET_STATISTICS_update (stats,
gettext_noop
("# PING without HELLO messages sent"), 1,
GNUNET_NO);
GNUNET_STATISTICS_update (stats,
gettext_noop
("# PING messages sent for re-validation"), 1,
GNUNET_NO);
transmit_to_peer (NULL, peer_address, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
HELLO_VERIFICATION_TIMEOUT, message_buf, tsize, GNUNET_YES,
neighbour);
GNUNET_free (message_buf);
schedule_next_ping (peer_address);
}
/**
* Schedule the job that will cause us to send a PING to the
* foreign address to evaluate its validity and latency.
*
* @param fal address to PING
*/
static void
schedule_next_ping (struct ForeignAddressList *fal)
{
struct GNUNET_TIME_Relative delay;
if (fal->revalidate_task != GNUNET_SCHEDULER_NO_TASK)
{
GNUNET_SCHEDULER_cancel (fal->revalidate_task);
fal->revalidate_task = GNUNET_SCHEDULER_NO_TASK;
}
delay = GNUNET_TIME_absolute_get_remaining (fal->expires);
delay.rel_value /= 2; /* do before expiration */
delay = GNUNET_TIME_relative_min (delay, LATENCY_EVALUATION_MAX_DELAY);
if (GNUNET_YES != fal->estimated)
{
delay = GNUNET_TIME_UNIT_ZERO;
fal->estimated = GNUNET_YES;
}
if (GNUNET_YES == fal->connected)
{
delay =
GNUNET_TIME_relative_min (delay,
CONNECTED_LATENCY_EVALUATION_MAX_DELAY);
}
/* FIXME: also adjust delay based on how close the last
* observed latency is to the latency of the best alternative */
/* bound how fast we can go */
delay = GNUNET_TIME_relative_max (delay, GNUNET_TIME_UNIT_SECONDS);
/* randomize a bit (to avoid doing all at the same time) */
delay.rel_value +=
GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1000);
GNUNET_assert (fal->revalidate_task == GNUNET_SCHEDULER_NO_TASK);
fal->revalidate_task =
GNUNET_SCHEDULER_add_delayed (delay, &send_periodic_ping, fal);
}
/**
* Function that will be called if we receive some payload
* from another peer.
*
* @param message the payload
* @param n peer who claimed to be the sender
*/
static void
handle_payload_message (const struct GNUNET_MessageHeader *message,
struct NeighbourMapEntry *n)
{
struct InboundMessage *im;
struct TransportClient *cpos;
uint16_t msize;
msize = ntohs (message->size);
if (n->received_pong == GNUNET_NO)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received message of type %u and size %u from `%4s', but no pong yet!\n",
ntohs (message->type), ntohs (message->size),
GNUNET_i2s (&n->id));
#endif
GNUNET_free_non_null (n->pre_connect_message_buffer);
n->pre_connect_message_buffer = GNUNET_malloc (msize);
memcpy (n->pre_connect_message_buffer, message, msize);
return;
}
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received message of type %u and size %u from `%4s', sending to all clients.\n",
ntohs (message->type), ntohs (message->size),
GNUNET_i2s (&n->id));
#endif
if (GNUNET_YES ==
GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, (ssize_t) msize))
{
n->quota_violation_count++;
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Bandwidth quota (%u b/s) violation detected (total of %u).\n",
n->in_tracker.available_bytes_per_s__,
n->quota_violation_count);
#endif
/* Discount 32k per violation */
GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024);
}
else
{
if (n->quota_violation_count > 0)
{
/* try to add 32k back */
GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024);
n->quota_violation_count--;
}
}
GNUNET_STATISTICS_update (stats,
gettext_noop
("# payload received from other peers"), msize,
GNUNET_NO);
/* transmit message to all clients */
uint32_t ats_count = 2;
size_t size =
sizeof (struct InboundMessage) +
ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information) + msize;
if (size > GNUNET_SERVER_MAX_MESSAGE_SIZE)
GNUNET_break (0);
im = GNUNET_malloc (size);
im->header.size = htons (size);
im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
im->peer = n->id;
im->ats_count = htonl (ats_count);
/* Setting ATS data */
(&(im->ats))[0].type = htonl (GNUNET_TRANSPORT_ATS_QUALITY_NET_DISTANCE);
(&(im->ats))[0].value = htonl (n->distance);
(&(im->ats))[1].type = htonl (GNUNET_TRANSPORT_ATS_QUALITY_NET_DELAY);
(&(im->ats))[1].value = htonl ((uint32_t) n->latency.rel_value);
(&(im->ats))[ats_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
(&(im->ats))[ats_count].value = htonl (0);
memcpy (&((&(im->ats))[ats_count + 1]), message, msize);
cpos = clients;
while (cpos != NULL)
{
transmit_to_client (cpos, &im->header, GNUNET_YES);
cpos = cpos->next;
}
GNUNET_free (im);
}
/**
* Iterator over hash map entries. Checks if the given validation
* entry is for the same challenge as what is given in the PONG.
*
* @param cls the 'struct TransportPongMessage*'
* @param key peer identity
* @param value value in the hash map ('struct ValidationEntry')
* @return GNUNET_YES if we should continue to
* iterate (mismatch), GNUNET_NO if not (entry matched)
*/
static int
check_pending_validation (void *cls, const GNUNET_HashCode * key, void *value)
{
const struct TransportPongMessage *pong = cls;
struct ValidationEntry *ve = value;
struct AddValidatedAddressContext avac;
unsigned int challenge = ntohl (pong->challenge);
struct GNUNET_HELLO_Message *hello;
struct GNUNET_PeerIdentity target;
struct NeighbourMapEntry *n;
struct ForeignAddressList *fal;
struct OwnAddressList *oal;
struct TransportPlugin *tp;
struct GNUNET_MessageHeader *prem;
uint16_t ps;
const char *addr;
size_t slen;
size_t alen;
ps = ntohs (pong->header.size);
if (ps < sizeof (struct TransportPongMessage))
{
GNUNET_break_op (0);
return GNUNET_NO;
}
addr = (const char *) &pong[1];
slen = strlen (ve->transport_name) + 1;
if ((ps - sizeof (struct TransportPongMessage) < slen) ||
(ve->challenge != challenge) || (addr[slen - 1] != '\0') ||
(0 != strcmp (addr, ve->transport_name)) ||
(ntohl (pong->purpose.size) !=
sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + sizeof (uint32_t) +
sizeof (struct GNUNET_TIME_AbsoluteNBO) +
sizeof (struct GNUNET_PeerIdentity) + ps -
sizeof (struct TransportPongMessage)))
{
return GNUNET_YES;
}
alen = ps - sizeof (struct TransportPongMessage) - slen;
switch (ntohl (pong->purpose.purpose))
{
case GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN:
if ((ve->addrlen + slen != ntohl (pong->addrlen)) ||
(0 != memcmp (&addr[slen], ve->addr, ve->addrlen)))
{
return GNUNET_YES; /* different entry, keep trying! */
}
if (0 != memcmp (&pong->pid, key, sizeof (struct GNUNET_PeerIdentity)))
{
GNUNET_break_op (0);
return GNUNET_NO;
}
if (GNUNET_OK !=
GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
&pong->purpose, &pong->signature,
&ve->publicKey))
{
GNUNET_break_op (0);
return GNUNET_NO;
}
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Confirmed validity of address, peer `%4s' has address `%s' (%s).\n",
GNUNET_h2s (key), a2s (ve->transport_name,
(const struct sockaddr *) ve->addr,
ve->addrlen), ve->transport_name);
#endif
break;
case GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING:
if (0 !=
memcmp (&pong->pid, &my_identity, sizeof (struct GNUNET_PeerIdentity)))
{
char *peer;
GNUNET_asprintf (&peer, "%s", GNUNET_i2s (&pong->pid));
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received PONG for different identity: I am `%s', PONG identity: `%s'\n",
GNUNET_i2s (&my_identity), peer);
#endif
GNUNET_free (peer);
return GNUNET_NO;
}
if (ve->addrlen != 0)
{
/* must have been for a different validation entry */
return GNUNET_YES;
}
tp = find_transport (ve->transport_name);
if (tp == NULL)
{
GNUNET_break (0);
return GNUNET_YES;
}
oal = tp->addresses;
while (NULL != oal)
{
if ((oal->addrlen == alen) && (0 == memcmp (&oal[1], &addr[slen], alen)))
break;
oal = oal->next;
}
if (oal == NULL)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_
("Not accepting PONG from `%s' with address `%s' since I cannot confirm using this address.\n"),
GNUNET_i2s (&pong->pid), a2s (ve->transport_name, &addr[slen],
alen));
/* FIXME: since the sender of the PONG currently uses the
* wrong address (see FIMXE there!), we cannot run a
* proper check here... */
#if FIXME_URGENT
return GNUNET_NO;
#endif
}
if (GNUNET_OK !=
GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING,
&pong->purpose, &pong->signature,
&ve->publicKey))
{
GNUNET_break_op (0);
return GNUNET_NO;
}
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Confirmed that peer `%4s' is talking to us using address `%s' (%s) for us.\n",
GNUNET_h2s (key), a2s (ve->transport_name, &addr[slen], alen),
ve->transport_name);
#endif
break;
default:
GNUNET_break_op (0);
return GNUNET_NO;
}
if (GNUNET_TIME_absolute_get_remaining
(GNUNET_TIME_absolute_ntoh (pong->expiration)).rel_value == 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_("Received expired signature. Check system time.\n"));
return GNUNET_NO;
}
GNUNET_STATISTICS_update (stats,
gettext_noop ("# address validation successes"), 1,
GNUNET_NO);
/* create the updated HELLO */
GNUNET_CRYPTO_hash (&ve->publicKey,
sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
&target.hashPubKey);
if (ve->addr != NULL)
{
avac.done = GNUNET_NO;
avac.ve = ve;
hello = GNUNET_HELLO_create (&ve->publicKey, &add_validated_address, &avac);
GNUNET_PEERINFO_add_peer (peerinfo, hello);
GNUNET_free (hello);
}
n = find_neighbour (&target);
if (n != NULL)
{
n->publicKey = ve->publicKey;
n->public_key_valid = GNUNET_YES;
fal =
add_peer_address (n, ve->transport_name, ve->session, ve->addr,
ve->addrlen);
GNUNET_assert (fal != NULL);
fal->expires = GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION);
fal->validated = GNUNET_YES;
mark_address_connected (fal);
GNUNET_STATISTICS_update (stats,
gettext_noop
("# peer addresses considered valid"), 1,
GNUNET_NO);
fal->latency = GNUNET_TIME_absolute_get_duration (ve->send_time);
update_addr_value (fal,
GNUNET_TIME_absolute_get_duration (ve->
send_time).rel_value,
GNUNET_TRANSPORT_ATS_QUALITY_NET_DELAY);
schedule_next_ping (fal);
if (n->latency.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
n->latency = fal->latency;
else
n->latency.rel_value =
(fal->latency.rel_value + n->latency.rel_value) / 2;
n->distance = fal->distance;
if (GNUNET_NO == n->received_pong)
{
n->received_pong = GNUNET_YES;
notify_clients_connect (&target, n->latency, n->distance);
if (NULL != (prem = n->pre_connect_message_buffer))
{
n->pre_connect_message_buffer = NULL;
handle_payload_message (prem, n);
GNUNET_free (prem);
}
}
if (n->retry_task != GNUNET_SCHEDULER_NO_TASK)
{
GNUNET_SCHEDULER_cancel (n->retry_task);
n->retry_task = GNUNET_SCHEDULER_NO_TASK;
try_transmission_to_peer (n);
}
}
/* clean up validation entry */
GNUNET_assert (GNUNET_YES ==
GNUNET_CONTAINER_multihashmap_remove (validation_map, key,
ve));
abort_validation (NULL, NULL, ve);
return GNUNET_NO;
}
/**
* Function that will be called if we receive a validation
* of an address challenge that we transmitted to another
* peer. Note that the validation should only be considered
* acceptable if the challenge matches AND if the sender
* address is at least a plausible address for this peer
* (otherwise we may be seeing a MiM attack).
*
* @param cls closure
* @param message the pong message
* @param peer who responded to our challenge
* @param sender_address string describing our sender address (as observed
* by the other peer in binary format)
* @param sender_address_len number of bytes in 'sender_address'
*/
static void
handle_pong (void *cls, const struct GNUNET_MessageHeader *message,
const struct GNUNET_PeerIdentity *peer, const char *sender_address,
size_t sender_address_len)
{
if (0 == memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity)))
{
/* PONG send to self, ignore */
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receiving `%s' message from myself\n",
"PONG");
return;
}
#if DEBUG_TRANSPORT > 1
/* we get tons of these that just get discarded, only log
* if we are quite verbose */
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receiving `%s' message from `%4s'.\n",
"PONG", GNUNET_i2s (peer));
#endif
GNUNET_STATISTICS_update (stats, gettext_noop ("# PONG messages received"), 1,
GNUNET_NO);
if (GNUNET_SYSERR !=
GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
&peer->hashPubKey,
&check_pending_validation,
(void *) message))
{
/* This is *expected* to happen a lot since we send
* PONGs to *all* known addresses of the sender of
* the PING, so most likely we get multiple PONGs
* per PING, and all but the first PONG will end up
* here. So really we should not print anything here
* unless we want to be very, very verbose... */
#if DEBUG_TRANSPORT > 2
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received `%s' message from `%4s' but have no record of a matching `%s' message. Ignoring.\n",
"PONG", GNUNET_i2s (peer), "PING");
#endif
return;
}
}
/**
* Try to validate a neighbour's address by sending him our HELLO and a PING.
*
* @param cls the 'struct ValidationEntry*'
* @param neighbour neighbour to validate, NULL if validation failed
*/
static void
transmit_hello_and_ping (void *cls, struct NeighbourMapEntry *neighbour)
{
struct ValidationEntry *va = cls;
struct ForeignAddressList *peer_address;
struct TransportPingMessage ping;
uint16_t hello_size;
size_t tsize;
char *message_buf;
struct GNUNET_PeerIdentity id;
size_t slen;
GNUNET_CRYPTO_hash (&va->publicKey,
sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
&id.hashPubKey);
if (neighbour == NULL)
{
/* FIXME: stats... */
GNUNET_break (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_remove (validation_map,
&id.hashPubKey, va));
abort_validation (NULL, NULL, va);
return;
}
neighbour->publicKey = va->publicKey;
neighbour->public_key_valid = GNUNET_YES;
peer_address =
add_peer_address (neighbour, va->transport_name, NULL,
(const void *) &va[1], va->addrlen);
if (peer_address == NULL)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to add peer `%4s' for plugin `%s'\n",
GNUNET_i2s (&neighbour->id), va->transport_name);
GNUNET_break (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_remove (validation_map,
&id.hashPubKey, va));
abort_validation (NULL, NULL, va);
return;
}
if (NULL == our_hello)
refresh_hello_task (NULL, NULL);
hello_size = GNUNET_HELLO_size (our_hello);
slen = strlen (va->transport_name) + 1;
tsize =
sizeof (struct TransportPingMessage) + hello_size + va->addrlen + slen;
message_buf = GNUNET_malloc (tsize);
ping.challenge = htonl (va->challenge);
ping.header.size =
htons (sizeof (struct TransportPingMessage) + slen + va->addrlen);
ping.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
memcpy (&ping.target, &neighbour->id, sizeof (struct GNUNET_PeerIdentity));
memcpy (message_buf, our_hello, hello_size);
memcpy (&message_buf[hello_size], &ping,
sizeof (struct TransportPingMessage));
memcpy (&message_buf[hello_size + sizeof (struct TransportPingMessage)],
va->transport_name, slen);
memcpy (&message_buf
[hello_size + sizeof (struct TransportPingMessage) + slen], &va[1],
va->addrlen);
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Performing validation of address `%s' via `%s' for peer `%4s' sending `%s' (%u bytes) and `%s' (%u bytes)\n",
(va->addrlen == 0) ? "" : a2s (va->transport_name,
(const void *) &va[1],
va->addrlen),
va->transport_name, GNUNET_i2s (&neighbour->id), "HELLO",
hello_size, "PING",
sizeof (struct TransportPingMessage) + va->addrlen + slen);
#endif
GNUNET_STATISTICS_update (stats,
gettext_noop
("# PING messages sent for initial validation"), 1,
GNUNET_NO);
transmit_to_peer (NULL, peer_address, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
HELLO_VERIFICATION_TIMEOUT, message_buf, tsize, GNUNET_YES,
neighbour);
GNUNET_free (message_buf);
}
/**
* Check if the given address is already being validated; if not,
* append the given address to the list of entries that are being be
* validated and initiate validation.
*
* @param cls closure ('struct CheckHelloValidatedContext *')
* @param tname name of the transport
* @param expiration expiration time
* @param addr the address
* @param addrlen length of the address
* @return GNUNET_OK (always)
*/
static int
run_validation (void *cls, const char *tname,
struct GNUNET_TIME_Absolute expiration, const void *addr,
uint16_t addrlen)
{
struct CheckHelloValidatedContext *chvc = cls;
struct GNUNET_PeerIdentity id;
struct TransportPlugin *tp;
struct ValidationEntry *va;
struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
struct CheckAddressExistsClosure caec;
struct OwnAddressList *oal;
GNUNET_assert (addr != NULL);
GNUNET_STATISTICS_update (stats,
gettext_noop
("# peer addresses scheduled for validation"), 1,
GNUNET_NO);
tp = find_transport (tname);
if (tp == NULL)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
_
("Transport `%s' not loaded, will not try to validate peer address using this transport.\n"),
tname);
GNUNET_STATISTICS_update (stats,
gettext_noop
("# peer addresses not validated (plugin not available)"),
1, GNUNET_NO);
return GNUNET_OK;
}
/* check if this is one of our own addresses */
oal = tp->addresses;
while (NULL != oal)
{
if ((oal->addrlen == addrlen) && (0 == memcmp (&oal[1], addr, addrlen)))
{
/* not plausible, this address is equivalent to our own address! */
GNUNET_STATISTICS_update (stats,
gettext_noop
("# peer addresses not validated (loopback)"),
1, GNUNET_NO);
return GNUNET_OK;
}
oal = oal->next;
}
GNUNET_HELLO_get_key (chvc->hello, &pk);
GNUNET_CRYPTO_hash (&pk,
sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
&id.hashPubKey);
if (is_blacklisted (&id, tp))
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Attempted to validate blacklisted peer `%s' using `%s'!\n",
GNUNET_i2s (&id), tname);
#endif
return GNUNET_OK;
}
caec.addr = addr;
caec.addrlen = addrlen;
caec.session = NULL;
caec.tname = tname;
caec.exists = GNUNET_NO;
GNUNET_CONTAINER_multihashmap_iterate (validation_map, &check_address_exists,
&caec);
if (caec.exists == GNUNET_YES)
{
/* During validation attempts we will likely trigger the other
* peer trying to validate our address which in turn will cause
* it to send us its HELLO, so we expect to hit this case rather
* frequently. Only print something if we are very verbose. */
#if DEBUG_TRANSPORT > 1
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Validation of address `%s' via `%s' for peer `%4s' already in progress.\n",
a2s (tname, addr, addrlen), tname, GNUNET_i2s (&id));
#endif
GNUNET_STATISTICS_update (stats,
gettext_noop
("# peer addresses not validated (in progress)"),
1, GNUNET_NO);
return GNUNET_OK;
}
va = GNUNET_malloc (sizeof (struct ValidationEntry) + addrlen);
va->chvc = chvc;
chvc->ve_count++;
va->transport_name = GNUNET_strdup (tname);
va->challenge =
GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT_MAX);
va->send_time = GNUNET_TIME_absolute_get ();
va->addr = (const void *) &va[1];
memcpy (&va[1], addr, addrlen);
va->addrlen = addrlen;
GNUNET_HELLO_get_key (chvc->hello, &va->publicKey);
va->timeout_task =
GNUNET_SCHEDULER_add_delayed (HELLO_VERIFICATION_TIMEOUT,
&timeout_hello_validation, va);
GNUNET_CONTAINER_multihashmap_put (validation_map, &id.hashPubKey, va,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
setup_peer_check_blacklist (&id, GNUNET_NO, &transmit_hello_and_ping, va);
return GNUNET_OK;
}
/**
* Check if addresses in validated hello "h" overlap with
* those in "chvc->hello" and validate the rest.
*
* @param cls closure
* @param peer id of the peer, NULL for last call
* @param h hello message for the peer (can be NULL)
* @param err_msg NULL if successful, otherwise contains error message
*/
static void
check_hello_validated (void *cls, const struct GNUNET_PeerIdentity *peer,
const struct GNUNET_HELLO_Message *h,
const char *err_msg)
{
struct CheckHelloValidatedContext *chvc = cls;
struct GNUNET_HELLO_Message *plain_hello;
struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
struct GNUNET_PeerIdentity target;
struct NeighbourMapEntry *n;
if (err_msg != NULL)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
_("Error in communication with PEERINFO service: %s\n"),
err_msg);
#endif
/* return; */
}
if (peer == NULL)
{
GNUNET_STATISTICS_update (stats,
gettext_noop
("# outstanding peerinfo iterate requests"), -1,
GNUNET_NO);
chvc->piter = NULL;
if (GNUNET_NO == chvc->hello_known)
{
/* notify PEERINFO about the peer now, so that we at least
* have the public key if some other component needs it */
GNUNET_HELLO_get_key (chvc->hello, &pk);
GNUNET_CRYPTO_hash (&pk,
sizeof (struct
GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
&target.hashPubKey);
plain_hello = GNUNET_HELLO_create (&pk, NULL, NULL);
GNUNET_PEERINFO_add_peer (peerinfo, plain_hello);
GNUNET_free (plain_hello);
#if DEBUG_TRANSPORT_HELLO
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"PEERINFO had no `%s' message for peer `%4s', full validation needed.\n",
"HELLO", GNUNET_i2s (&target));
#endif
GNUNET_STATISTICS_update (stats,
gettext_noop
("# new HELLOs requiring full validation"), 1,
GNUNET_NO);
GNUNET_HELLO_iterate_addresses (chvc->hello, GNUNET_NO, &run_validation,
chvc);
}
else
{
GNUNET_STATISTICS_update (stats,
gettext_noop ("# duplicate HELLO (peer known)"),
1, GNUNET_NO);
}
chvc->ve_count--;
if (chvc->ve_count == 0)
{
GNUNET_CONTAINER_DLL_remove (chvc_head, chvc_tail, chvc);
GNUNET_free (chvc);
}
return;
}
if (h == NULL)
return;
#if DEBUG_TRANSPORT_HELLO
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"PEERINFO had `%s' message for peer `%4s', validating only new addresses.\n",
"HELLO", GNUNET_i2s (peer));
#endif
chvc->hello_known = GNUNET_YES;
n = find_neighbour (peer);
if (n != NULL)
{
#if DEBUG_TRANSPORT_HELLO
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Calling hello_iterate_addresses for %s!\n", GNUNET_i2s (peer));
#endif
GNUNET_HELLO_iterate_addresses (h, GNUNET_NO, &add_to_foreign_address_list,
n);
try_transmission_to_peer (n);
}
else
{
#if DEBUG_TRANSPORT_HELLO
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"No existing neighbor record for %s!\n", GNUNET_i2s (peer));
#endif
GNUNET_STATISTICS_update (stats,
gettext_noop
("# no existing neighbour record (validating HELLO)"),
1, GNUNET_NO);
}
GNUNET_STATISTICS_update (stats,
gettext_noop ("# HELLO validations (update case)"),
1, GNUNET_NO);
GNUNET_HELLO_iterate_new_addresses (chvc->hello, h,
GNUNET_TIME_relative_to_absolute
(HELLO_REVALIDATION_START_TIME),
&run_validation, chvc);
}
/**
* Process HELLO-message.
*
* @param plugin transport involved, may be NULL
* @param message the actual message
* @return GNUNET_OK if the HELLO was well-formed, GNUNET_SYSERR otherwise
*/
static int
process_hello (struct TransportPlugin *plugin,
const struct GNUNET_MessageHeader *message)
{
uint16_t hsize;
struct GNUNET_PeerIdentity target;
const struct GNUNET_HELLO_Message *hello;
struct CheckHelloValidatedContext *chvc;
struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
struct NeighbourMapEntry *n;
#if DEBUG_TRANSPORT_HELLO > 2
char *my_id;
#endif
hsize = ntohs (message->size);
if ((ntohs (message->type) != GNUNET_MESSAGE_TYPE_HELLO) ||
(hsize < sizeof (struct GNUNET_MessageHeader)))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
GNUNET_STATISTICS_update (stats,
gettext_noop ("# HELLOs received for validation"),
1, GNUNET_NO);
hello = (const struct GNUNET_HELLO_Message *) message;
if (GNUNET_OK != GNUNET_HELLO_get_key (hello, &publicKey))
{
#if DEBUG_TRANSPORT_HELLO
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Unable to get public key from `%s' for `%4s'!\n", "HELLO",
GNUNET_i2s (&target));
#endif
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
GNUNET_CRYPTO_hash (&publicKey,
sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
&target.hashPubKey);
#if DEBUG_TRANSPORT_HELLO
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message for `%4s'\n",
"HELLO", GNUNET_i2s (&target));
#endif
if (0 == memcmp (&my_identity, &target, sizeof (struct GNUNET_PeerIdentity)))
{
GNUNET_STATISTICS_update (stats,
gettext_noop
("# HELLOs ignored for validation (is my own HELLO)"),
1, GNUNET_NO);
return GNUNET_OK;
}
n = find_neighbour (&target);
if ((NULL != n) && (!n->public_key_valid))
{
GNUNET_HELLO_get_key (hello, &n->publicKey);
n->public_key_valid = GNUNET_YES;
}
/* check if load is too high before doing expensive stuff */
if (GNUNET_SCHEDULER_get_load (GNUNET_SCHEDULER_PRIORITY_BACKGROUND) >
MAX_HELLO_LOAD)
{
GNUNET_STATISTICS_update (stats,
gettext_noop
("# HELLOs ignored due to high load"), 1,
GNUNET_NO);
#if DEBUG_TRANSPORT_HELLO
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Ignoring `%s' for `%4s', load too high.\n", "HELLO",
GNUNET_i2s (&target));
#endif
return GNUNET_OK;
}
chvc = chvc_head;
while (NULL != chvc)
{
if (GNUNET_HELLO_equals
(hello, chvc->hello, GNUNET_TIME_absolute_get ()).abs_value > 0)
{
#if DEBUG_TRANSPORT_HELLO > 2
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received duplicate `%s' message for `%4s'; ignored\n",
"HELLO", GNUNET_i2s (&target));
#endif
return GNUNET_OK; /* validation already pending */
}
if (GNUNET_HELLO_size (hello) == GNUNET_HELLO_size (chvc->hello))
GNUNET_break (0 !=
memcmp (hello, chvc->hello, GNUNET_HELLO_size (hello)));
chvc = chvc->next;
}
#if BREAK_TESTS
struct NeighbourMapEntry *temp_neighbor = find_neighbour (&target);
if ((NULL != temp_neighbor))
{
fprintf (stderr, "Already know peer, ignoring hello\n");
return GNUNET_OK;
}
#endif
#if DEBUG_TRANSPORT_HELLO > 2
if (plugin != NULL)
{
#if DEBUG_TRANSPORT
my_id = GNUNET_strdup (GNUNET_i2s (plugin->env.my_identity));
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"%s: Starting validation of `%s' message for `%4s' via '%s' of size %u\n",
my_id, "HELLO", GNUNET_i2s (&target), plugin->short_name,
GNUNET_HELLO_size (hello));
GNUNET_free (my_id);
#endif
}
#endif
chvc = GNUNET_malloc (sizeof (struct CheckHelloValidatedContext) + hsize);
chvc->ve_count = 1;
chvc->hello = (const struct GNUNET_HELLO_Message *) &chvc[1];
memcpy (&chvc[1], hello, hsize);
GNUNET_CONTAINER_DLL_insert (chvc_head, chvc_tail, chvc);
/* finally, check if HELLO was previously validated
* (continuation will then schedule actual validation) */
GNUNET_STATISTICS_update (stats,
gettext_noop
("# peerinfo process hello iterate requests"), 1,
GNUNET_NO);
GNUNET_STATISTICS_update (stats,
gettext_noop
("# outstanding peerinfo iterate requests"), 1,
GNUNET_NO);
chvc->piter =
GNUNET_PEERINFO_iterate (peerinfo, &target, HELLO_VERIFICATION_TIMEOUT,
&check_hello_validated, chvc);
return GNUNET_OK;
}
/**
* The peer specified by the given neighbour has timed-out or a plugin
* has disconnected. We may either need to do nothing (other plugins
* still up), or trigger a full disconnect and clean up. This
* function updates our state and does the necessary notifications.
* Also notifies our clients that the neighbour is now officially
* gone.
*
* @param n the neighbour list entry for the peer
* @param check GNUNET_YES to check if ALL addresses for this peer
* are gone, GNUNET_NO to force a disconnect of the peer
* regardless of whether other addresses exist.
*/
static void
disconnect_neighbour (struct NeighbourMapEntry *n, int check)
{
struct ReadyList *rpos;
struct MessageQueue *mq;
struct ForeignAddressList *peer_addresses;
struct ForeignAddressList *peer_pos;
if (GNUNET_YES == n->in_disconnect)
return;
if (GNUNET_YES == check)
{
rpos = n->plugins;
while (NULL != rpos)
{
peer_addresses = rpos->addresses;
while (peer_addresses != NULL)
{
/* Do not disconnect if: an address is connected or an inbound address exists */
if ((GNUNET_YES == peer_addresses->connected) ||
(peer_addresses->addrlen == 0))
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"NOT Disconnecting from `%4s', still have live address `%s'!\n",
GNUNET_i2s (&n->id),
a2s (peer_addresses->ready_list->plugin->short_name,
peer_addresses->addr, peer_addresses->addrlen));
#endif
return; /* still connected */
}
peer_addresses = peer_addresses->next;
}
rpos = rpos->next;
}
}
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
"Disconnecting from `%4s'\n", GNUNET_i2s (&n->id));
#endif
n->in_disconnect = GNUNET_YES; /* prevent recursive entry */
/* notify all clients about disconnect */
if (GNUNET_YES == n->received_pong)
{
n->received_pong = GNUNET_NO;
notify_clients_disconnect (&n->id);
}
#if HAVE_LIBGLPK
ats_modify_problem_state (ats, ATS_MODIFIED);
#endif
/* clean up all plugins, cancel connections and pending transmissions */
while (NULL != (rpos = n->plugins))
{
n->plugins = rpos->next;
rpos->plugin->api->disconnect (rpos->plugin->api->cls, &n->id);
while (rpos->addresses != NULL)
{
peer_pos = rpos->addresses;
rpos->addresses = peer_pos->next;
if (peer_pos->connected == GNUNET_YES)
{
GNUNET_STATISTICS_update (stats, gettext_noop ("# connected addresses"),
-1, GNUNET_NO);
peer_pos->connected = GNUNET_NO;
}
if (GNUNET_YES == peer_pos->validated)
GNUNET_STATISTICS_update (stats,
gettext_noop
("# peer addresses considered valid"), -1,
GNUNET_NO);
if (GNUNET_SCHEDULER_NO_TASK != peer_pos->revalidate_task)
{
GNUNET_SCHEDULER_cancel (peer_pos->revalidate_task);
peer_pos->revalidate_task = GNUNET_SCHEDULER_NO_TASK;
}
GNUNET_free (peer_pos->ressources);
peer_pos->ressources = NULL;
GNUNET_free (peer_pos->quality);
peer_pos->ressources = NULL;
GNUNET_free (peer_pos);
}
GNUNET_free (rpos);
}
/* free all messages on the queue */
while (NULL != (mq = n->messages_head))
{
GNUNET_STATISTICS_update (stats,
gettext_noop
("# bytes in message queue for other peers"),
-(int64_t) mq->message_buf_size, GNUNET_NO);
GNUNET_STATISTICS_update (stats,
gettext_noop
("# bytes discarded due to disconnect"),
mq->message_buf_size, GNUNET_NO);
GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
GNUNET_assert (0 ==
memcmp (&mq->neighbour_id, &n->id,
sizeof (struct GNUNET_PeerIdentity)));
GNUNET_free (mq);
}
while (NULL != (mq = n->cont_head))
{
GNUNET_CONTAINER_DLL_remove (n->cont_head, n->cont_tail, mq);
GNUNET_assert (0 ==
memcmp (&mq->neighbour_id, &n->id,
sizeof (struct GNUNET_PeerIdentity)));
GNUNET_free (mq);
}
if (n->timeout_task != GNUNET_SCHEDULER_NO_TASK)
{
GNUNET_SCHEDULER_cancel (n->timeout_task);
n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
}
if (n->retry_task != GNUNET_SCHEDULER_NO_TASK)
{
GNUNET_SCHEDULER_cancel (n->retry_task);
n->retry_task = GNUNET_SCHEDULER_NO_TASK;
}
if (n->piter != NULL)
{
GNUNET_PEERINFO_iterate_cancel (n->piter);
GNUNET_STATISTICS_update (stats,
gettext_noop
("# outstanding peerinfo iterate requests"), -1,
GNUNET_NO);
n->piter = NULL;
}
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_remove (neighbours,
&n->id.hashPubKey, n));
/* finally, free n itself */
GNUNET_STATISTICS_update (stats, gettext_noop ("# active neighbours"), -1,
GNUNET_NO);
GNUNET_free_non_null (n->pre_connect_message_buffer);
GNUNET_free (n);
}
/**
* We have received a PING message from someone. Need to send a PONG message
* in response to the peer by any means necessary.
*/
static int
handle_ping (void *cls, const struct GNUNET_MessageHeader *message,
const struct GNUNET_PeerIdentity *peer, struct Session *session,
const char *sender_address, uint16_t sender_address_len)
{
struct TransportPlugin *plugin = cls;
struct SessionHeader *session_header = (struct SessionHeader *) session;
struct TransportPingMessage *ping;
struct TransportPongMessage *pong;
struct NeighbourMapEntry *n;
struct ReadyList *rl;
struct ForeignAddressList *fal;
struct OwnAddressList *oal;
const char *addr;
size_t alen;
size_t slen;
int did_pong;
if (ntohs (message->size) < sizeof (struct TransportPingMessage))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
ping = (struct TransportPingMessage *) message;
if (0 !=
memcmp (&ping->target, plugin->env.my_identity,
sizeof (struct GNUNET_PeerIdentity)))
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
_
("Received `%s' message from `%s' destined for `%s' which is not me!\n"),
"PING", (sender_address != NULL) ? a2s (plugin->short_name,
(const struct sockaddr
*) sender_address,
sender_address_len) :
"", GNUNET_i2s (&ping->target));
#endif
return GNUNET_SYSERR;
}
#if DEBUG_PING_PONG
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
"Processing `%s' from `%s'\n", "PING",
(sender_address != NULL) ? a2s (plugin->short_name,
(const struct sockaddr *)
sender_address,
sender_address_len) :
"");
#endif
GNUNET_STATISTICS_update (stats, gettext_noop ("# PING messages received"), 1,
GNUNET_NO);
addr = (const char *) &ping[1];
alen = ntohs (message->size) - sizeof (struct TransportPingMessage);
slen = strlen (plugin->short_name) + 1;
if (alen == 0)
{
/* peer wants to confirm that we have an outbound connection to him */
if (session == NULL)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_
("Refusing to create PONG since I do not have a session with `%s'.\n"),
GNUNET_i2s (peer));
return GNUNET_SYSERR;
}
/* FIXME-urg: the use of 'sender_address' in the code below is doubly-wrong:
* 1) it is NULL when we need to have a real value
* 2) it is documented to be the address of the sender (source-IP), where
* what we actually want is our LISTEN IP (what we 'bound' to); which we don't even
* have...
*/
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Creating PONG indicating that we received a connection at our address `%s' from `%s'.\n",
a2s (plugin->short_name, sender_address, sender_address_len),
GNUNET_i2s (peer));
pong =
GNUNET_malloc (sizeof (struct TransportPongMessage) +
sender_address_len + slen);
pong->header.size =
htons (sizeof (struct TransportPongMessage) + sender_address_len +
slen);
pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
pong->purpose.size =
htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO) +
sizeof (struct GNUNET_PeerIdentity) + sender_address_len + slen);
pong->purpose.purpose =
htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING);
pong->challenge = ping->challenge;
pong->addrlen = htonl (sender_address_len + slen);
memcpy (&pong->pid, peer, sizeof (struct GNUNET_PeerIdentity));
memcpy (&pong[1], plugin->short_name, slen);
if ((sender_address != NULL) && (sender_address_len > 0))
memcpy (&((char *) &pong[1])[slen], sender_address, sender_address_len);
if (GNUNET_TIME_absolute_get_remaining
(session_header->pong_sig_expires).rel_value <
PONG_SIGNATURE_LIFETIME.rel_value / 4)
{
/* create / update cached sig */
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Creating PONG signature to indicate active connection.\n");
#endif
session_header->pong_sig_expires =
GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME);
pong->expiration =
GNUNET_TIME_absolute_hton (session_header->pong_sig_expires);
GNUNET_assert (GNUNET_OK ==
GNUNET_CRYPTO_rsa_sign (my_private_key, &pong->purpose,
&session_header->pong_signature));
}
else
{
pong->expiration =
GNUNET_TIME_absolute_hton (session_header->pong_sig_expires);
}
memcpy (&pong->signature, &session_header->pong_signature,
sizeof (struct GNUNET_CRYPTO_RsaSignature));
}
else
{
/* peer wants to confirm that this is one of our addresses */
addr += slen;
alen -= slen;
if (GNUNET_OK != plugin->api->check_address (plugin->api->cls, addr, alen))
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_
("Not confirming PING with address `%s' since I cannot confirm having this address.\n"),
a2s (plugin->short_name, addr, alen));
return GNUNET_NO;
}
oal = plugin->addresses;
while (NULL != oal)
{
if ((oal->addrlen == alen) && (0 == memcmp (addr, &oal[1], alen)))
break;
oal = oal->next;
}
pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + alen + slen);
pong->header.size =
htons (sizeof (struct TransportPongMessage) + alen + slen);
pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
pong->purpose.size =
htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO) +
sizeof (struct GNUNET_PeerIdentity) + alen + slen);
pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN);
pong->challenge = ping->challenge;
pong->addrlen = htonl (alen + slen);
memcpy (&pong->pid, &my_identity, sizeof (struct GNUNET_PeerIdentity));
memcpy (&pong[1], plugin->short_name, slen);
memcpy (&((char *) &pong[1])[slen], addr, alen);
if ((oal != NULL) &&
(GNUNET_TIME_absolute_get_remaining (oal->pong_sig_expires).rel_value <
PONG_SIGNATURE_LIFETIME.rel_value / 4))
{
/* create / update cached sig */
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Creating PONG signature to indicate ownership.\n");
#endif
oal->pong_sig_expires =
GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME);
pong->expiration = GNUNET_TIME_absolute_hton (oal->pong_sig_expires);
GNUNET_assert (GNUNET_OK ==
GNUNET_CRYPTO_rsa_sign (my_private_key, &pong->purpose,
&oal->pong_signature));
memcpy (&pong->signature, &oal->pong_signature,
sizeof (struct GNUNET_CRYPTO_RsaSignature));
}
else if (oal == NULL)
{
/* not using cache (typically DV-only) */
pong->expiration =
GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute
(PONG_SIGNATURE_LIFETIME));
GNUNET_assert (GNUNET_OK ==
GNUNET_CRYPTO_rsa_sign (my_private_key, &pong->purpose,
&pong->signature));
}
else
{
/* can used cached version */
pong->expiration = GNUNET_TIME_absolute_hton (oal->pong_sig_expires);
memcpy (&pong->signature, &oal->pong_signature,
sizeof (struct GNUNET_CRYPTO_RsaSignature));
}
}
n = find_neighbour (peer);
GNUNET_assert (n != NULL);
did_pong = GNUNET_NO;
/* first try reliable response transmission */
rl = n->plugins;
while (rl != NULL)
{
fal = rl->addresses;
while (fal != NULL)
{
if (-1 !=
rl->plugin->api->send (rl->plugin->api->cls, peer,
(const char *) pong, ntohs (pong->header.size),
TRANSPORT_PONG_PRIORITY,
HELLO_VERIFICATION_TIMEOUT, fal->session,
fal->addr, fal->addrlen, GNUNET_SYSERR, NULL,
NULL))
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Transmitted PONG to `%s' via reliable mechanism\n",
GNUNET_i2s (peer));
/* done! */
GNUNET_STATISTICS_update (stats,
gettext_noop
("# PONGs unicast via reliable transport"), 1,
GNUNET_NO);
GNUNET_free (pong);
return GNUNET_OK;
}
did_pong = GNUNET_YES;
fal = fal->next;
}
rl = rl->next;
}
/* no reliable method found, do multicast */
GNUNET_STATISTICS_update (stats,
gettext_noop
("# PONGs multicast to all available addresses"), 1,
GNUNET_NO);
rl = n->plugins;
while (rl != NULL)
{
fal = rl->addresses;
while (fal != NULL)
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Transmitting PONG to `%s' via unreliable mechanism `%s':%s\n",
GNUNET_i2s (peer), a2s (rl->plugin->short_name, fal->addr,
fal->addrlen),
rl->plugin->short_name);
transmit_to_peer (NULL, fal, TRANSPORT_PONG_PRIORITY,
HELLO_VERIFICATION_TIMEOUT, (const char *) pong,
ntohs (pong->header.size), GNUNET_YES, n);
did_pong = GNUNET_YES;
fal = fal->next;
}
rl = rl->next;
}
GNUNET_free (pong);
if (GNUNET_YES != did_pong)
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_("Could not send PONG to `%s': no address available\n"),
GNUNET_i2s (peer));
return GNUNET_OK;
}
/**
* Function called by the plugin for each received message. Update
* data volumes, possibly notify plugins about reducing the rate at
* which they read from the socket and generally forward to our
* receive callback.
*
* @param cls the "struct TransportPlugin *" we gave to the plugin
* @param peer (claimed) identity of the other peer
* @param message the message, NULL if we only care about
* learning about the delay until we should receive again
* @param ats_data information for automatic transport selection
* @param ats_count number of elements in ats not including 0-terminator
* @param session identifier used for this session (can be NULL)
* @param sender_address binary address of the sender (if observed)
* @param sender_address_len number of bytes in sender_address
* @return how long in ms the plugin should wait until receiving more data
* (plugins that do not support this, can ignore the return value)
*/
static struct GNUNET_TIME_Relative
plugin_env_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
const struct GNUNET_MessageHeader *message,
const struct GNUNET_TRANSPORT_ATS_Information *ats_data,
uint32_t ats_count, struct Session *session,
const char *sender_address, uint16_t sender_address_len)
{
struct TransportPlugin *plugin = cls;
struct ReadyList *service_context;
struct ForeignAddressList *peer_address;
uint16_t msize;
struct NeighbourMapEntry *n;
struct GNUNET_TIME_Relative ret;
uint32_t distance;
int c;
if (0 == memcmp (peer, &my_identity, sizeof (struct GNUNET_PeerIdentity)))
{
/* refuse to receive from myself */
GNUNET_break (0);
return GNUNET_TIME_UNIT_FOREVER_REL;
}
if (is_blacklisted (peer, plugin))
return GNUNET_TIME_UNIT_FOREVER_REL;
n = find_neighbour (peer);
if (n == NULL)
n = setup_new_neighbour (peer, GNUNET_YES);
service_context = n->plugins;
while ((service_context != NULL) && (plugin != service_context->plugin))
service_context = service_context->next;
GNUNET_assert ((plugin->api->send == NULL) || (service_context != NULL));
peer_address = NULL;
distance = 1;
for (c = 0; c < ats_count; c++)
if (ntohl (ats_data[c].type) == GNUNET_TRANSPORT_ATS_QUALITY_NET_DISTANCE)
distance = ntohl (ats_data[c].value);
if (message != NULL)
{
if ((session != NULL) || (sender_address != NULL))
peer_address =
add_peer_address (n, plugin->short_name, session, sender_address,
sender_address_len);
if (peer_address != NULL)
{
update_addr_ats (peer_address, ats_data, ats_count);
update_addr_value (peer_address, distance,
GNUNET_TRANSPORT_ATS_QUALITY_NET_DISTANCE);
peer_address->distance = distance;
if (GNUNET_YES == peer_address->validated)
{
mark_address_connected (peer_address);
schedule_next_ping (peer_address);
}
else
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"New address is unvalidated, trying to validate it now\n");
#endif
if (peer_address->revalidate_task != GNUNET_SCHEDULER_NO_TASK)
{
GNUNET_SCHEDULER_cancel (peer_address->revalidate_task);
peer_address->revalidate_task = GNUNET_SCHEDULER_NO_TASK;
}
peer_address->revalidate_task =
GNUNET_SCHEDULER_add_now (&send_periodic_ping, peer_address);
}
peer_address->timeout =
GNUNET_TIME_relative_to_absolute
(GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
}
/* update traffic received amount ... */
msize = ntohs (message->size);
GNUNET_STATISTICS_update (stats,
gettext_noop
("# bytes received from other peers"), msize,
GNUNET_NO);
n->distance = distance;
n->peer_timeout =
GNUNET_TIME_relative_to_absolute
(GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
GNUNET_SCHEDULER_cancel (n->timeout_task);
n->timeout_task =
GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
&neighbour_timeout_task, n);
if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
{
/* dropping message due to frequent inbound volume violations! */
GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
_
("Dropping incoming message due to repeated bandwidth quota (%u b/s) violations (total of %u).\n"),
n->in_tracker.available_bytes_per_s__,
n->quota_violation_count);
GNUNET_STATISTICS_update (stats,
gettext_noop
("# bandwidth quota violations by other peers"),
1, GNUNET_NO);
return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
}
if ((ntohs (message->type) == GNUNET_MESSAGE_TYPE_TRANSPORT_ATS) &&
(ntohs (message->size) ==
(sizeof (struct GNUNET_MessageHeader) + sizeof (uint32_t))))
{
#if HAVE_LIBGLPK
uint32_t value = ntohl (*((uint32_t *) & message[1]));
//GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "GNUNET_MESSAGE_TYPE_TRANSPORT_ATS: %i \n", value);
/* Force ressource and quality update */
if ((value == 4) && (ats != NULL))
ats_modify_problem_state (ats, ATS_QUALITY_COST_UPDATED);
/* Force cost update */
if ((value == 3) && (ats != NULL))
ats_modify_problem_state (ats, ATS_COST_UPDATED);
/* Force quality update */
if ((value == 2) && (ats != NULL))
ats_modify_problem_state (ats, ATS_QUALITY_UPDATED);
/* Force full rebuild */
if ((value == 1) && (ats != NULL))
ats_modify_problem_state (ats, ATS_MODIFIED);
#endif
}
#if DEBUG_PING_PONG
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received message of type %u and size %u from `%4s', sending to all clients.\n",
ntohs (message->type), ntohs (message->size),
GNUNET_i2s (peer));
#endif
switch (ntohs (message->type))
{
case GNUNET_MESSAGE_TYPE_HELLO:
GNUNET_STATISTICS_update (stats,
gettext_noop
("# HELLO messages received from other peers"),
1, GNUNET_NO);
process_hello (plugin, message);
break;
case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
handle_ping (plugin, message, peer, session, sender_address,
sender_address_len);
if (GNUNET_YES != n->received_pong)
transmit_plain_ping (n);
break;
case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
handle_pong (plugin, message, peer, sender_address, sender_address_len);
break;
case GNUNET_MESSAGE_TYPE_TRANSPORT_ATS:
break;
default:
handle_payload_message (message, n);
break;
}
}
ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 0);
if (ret.rel_value > 0)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Throttling read (%llu bytes excess at %u b/s), waiting %llu ms before reading more.\n",
(unsigned long long) n->
in_tracker.consumption_since_last_update__,
(unsigned int) n->in_tracker.available_bytes_per_s__,
(unsigned long long) ret.rel_value);
#endif
GNUNET_STATISTICS_update (stats, gettext_noop ("# ms throttling suggested"),
(int64_t) ret.rel_value, GNUNET_NO);
}
return ret;
}
static int
notify_client_about_neighbour (void *cls, const GNUNET_HashCode * key,
void *value)
{
struct TransportClient *c = cls;
struct NeighbourMapEntry *n = value;
struct ConnectInfoMessage *cim;
uint32_t ats_count;
size_t size;
if (GNUNET_YES != n->received_pong)
return GNUNET_OK;
ats_count = 2;
size =
sizeof (struct ConnectInfoMessage) +
ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
cim = GNUNET_malloc (size);
cim->header.size = htons (size);
cim->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
cim->ats_count = htonl (ats_count);
(&(cim->ats))[2].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
(&(cim->ats))[2].value = htonl (0);
if (GNUNET_YES == n->received_pong)
{
(&cim->ats)[0].type = htonl (GNUNET_TRANSPORT_ATS_QUALITY_NET_DISTANCE);
(&cim->ats)[0].value = htonl (n->distance);
(&cim->ats)[1].type = htonl (GNUNET_TRANSPORT_ATS_QUALITY_NET_DELAY);
(&cim->ats)[1].value = htonl ((uint32_t) n->latency.rel_value);
cim->id = n->id;
transmit_to_client (c, &cim->header, GNUNET_NO);
}
GNUNET_free (cim);
return GNUNET_OK;
}
/**
* Handle START-message. This is the first message sent to us
* by any client which causes us to add it to our list.
*
* @param cls closure (always NULL)
* @param client identification of the client
* @param message the actual message
*/
static void
handle_start (void *cls, struct GNUNET_SERVER_Client *client,
const struct GNUNET_MessageHeader *message)
{
const struct StartMessage *start;
struct TransportClient *c;
start = (const struct StartMessage *) message;
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' request from client\n",
"START");
#endif
c = clients;
while (c != NULL)
{
if (c->client == client)
{
/* client already on our list! */
GNUNET_break (0);
GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
return;
}
c = c->next;
}
if ((GNUNET_NO != ntohl (start->do_check)) &&
(0 !=
memcmp (&start->self, &my_identity,
sizeof (struct GNUNET_PeerIdentity))))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_
("Rejecting control connection from peer `%s', which is not me!\n"),
GNUNET_i2s (&start->self));
GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
return;
}
c = GNUNET_malloc (sizeof (struct TransportClient));
c->next = clients;
clients = c;
c->client = client;
if (our_hello != NULL)
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending our own `%s' to new client\n",
"HELLO");
#endif
transmit_to_client (c, (const struct GNUNET_MessageHeader *) our_hello,
GNUNET_NO);
/* tell new client about all existing connections */
GNUNET_CONTAINER_multihashmap_iterate (neighbours,
¬ify_client_about_neighbour, c);
}
else
{
#if DEBUG_TRANSPORT_HELLO
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"No HELLO created yet, will transmit HELLO to client later!\n");
#endif
refresh_hello ();
}
GNUNET_SERVER_receive_done (client, GNUNET_OK);
}
/**
* Handle HELLO-message.
*
* @param cls closure (always NULL)
* @param client identification of the client
* @param message the actual message
*/
static void
handle_hello (void *cls, struct GNUNET_SERVER_Client *client,
const struct GNUNET_MessageHeader *message)
{
int ret;
GNUNET_STATISTICS_update (stats,
gettext_noop ("# HELLOs received from clients"), 1,
GNUNET_NO);
ret = process_hello (NULL, message);
GNUNET_SERVER_receive_done (client, ret);
}
/**
* Closure for 'transmit_client_message'; followed by
* 'msize' bytes of the actual message.
*/
struct TransmitClientMessageContext
{
/**
* Client on whom's behalf we are sending.
*/
struct GNUNET_SERVER_Client *client;
/**
* Timeout for the transmission.
*/
struct GNUNET_TIME_Absolute timeout;
/**
* Message priority.
*/
uint32_t priority;
/**
* Size of the message in bytes.
*/
uint16_t msize;
};
/**
* Schedule transmission of a message we got from a client to a peer.
*
* @param cls the 'struct TransmitClientMessageContext*'
* @param n destination, or NULL on error (in that case, drop the message)
*/
static void
transmit_client_message (void *cls, struct NeighbourMapEntry *n)
{
struct TransmitClientMessageContext *tcmc = cls;
struct TransportClient *tc;
tc = clients;
while ((tc != NULL) && (tc->client != tcmc->client))
tc = tc->next;
if (n != NULL)
{
transmit_to_peer (tc, NULL, tcmc->priority,
GNUNET_TIME_absolute_get_remaining (tcmc->timeout),
(char *) &tcmc[1], tcmc->msize, GNUNET_NO, n);
}
GNUNET_SERVER_receive_done (tcmc->client, GNUNET_OK);
GNUNET_SERVER_client_drop (tcmc->client);
GNUNET_free (tcmc);
}
/**
* Handle SEND-message.
*
* @param cls closure (always NULL)
* @param client identification of the client
* @param message the actual message
*/
static void
handle_send (void *cls, struct GNUNET_SERVER_Client *client,
const struct GNUNET_MessageHeader *message)
{
const struct OutboundMessage *obm;
const struct GNUNET_MessageHeader *obmm;
struct TransmitClientMessageContext *tcmc;
uint16_t size;
uint16_t msize;
size = ntohs (message->size);
if (size <
sizeof (struct OutboundMessage) + sizeof (struct GNUNET_MessageHeader))
{
GNUNET_break (0);
GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
return;
}
GNUNET_STATISTICS_update (stats,
gettext_noop ("# payload received for other peers"),
size, GNUNET_NO);
obm = (const struct OutboundMessage *) message;
obmm = (const struct GNUNET_MessageHeader *) &obm[1];
msize = size - sizeof (struct OutboundMessage);
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received `%s' request from client with target `%4s' and message of type %u and size %u\n",
"SEND", GNUNET_i2s (&obm->peer), ntohs (obmm->type), msize);
#endif
tcmc = GNUNET_malloc (sizeof (struct TransmitClientMessageContext) + msize);
tcmc->client = client;
tcmc->priority = ntohl (obm->priority);
tcmc->timeout =
GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_ntoh
(obm->timeout));
tcmc->msize = msize;
/* FIXME: this memcpy can be up to 7% of our total runtime */
memcpy (&tcmc[1], obmm, msize);
GNUNET_SERVER_client_keep (client);
setup_peer_check_blacklist (&obm->peer, GNUNET_YES, &transmit_client_message,
tcmc);
}
/**
* Handle request connect message
*
* @param cls closure (always NULL)
* @param client identification of the client
* @param message the actual message
*/
static void
handle_request_connect (void *cls, struct GNUNET_SERVER_Client *client,
const struct GNUNET_MessageHeader *message)
{
const struct TransportRequestConnectMessage *trcm =
(const struct TransportRequestConnectMessage *) message;
GNUNET_STATISTICS_update (stats,
gettext_noop
("# REQUEST CONNECT messages received"), 1,
GNUNET_NO);
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received a request connect message for peer `%s'\n",
GNUNET_i2s (&trcm->peer));
#endif
setup_peer_check_blacklist (&trcm->peer, GNUNET_YES, NULL, NULL);
GNUNET_SERVER_receive_done (client, GNUNET_OK);
}
/**
* Handle SET_QUOTA-message.
*
* @param cls closure (always NULL)
* @param client identification of the client
* @param message the actual message
*/
static void
handle_set_quota (void *cls, struct GNUNET_SERVER_Client *client,
const struct GNUNET_MessageHeader *message)
{
const struct QuotaSetMessage *qsm = (const struct QuotaSetMessage *) message;
struct NeighbourMapEntry *n;
GNUNET_STATISTICS_update (stats,
gettext_noop ("# SET QUOTA messages received"), 1,
GNUNET_NO);
n = find_neighbour (&qsm->peer);
if (n == NULL)
{
GNUNET_SERVER_receive_done (client, GNUNET_OK);
GNUNET_STATISTICS_update (stats,
gettext_noop
("# SET QUOTA messages ignored (no such peer)"),
1, GNUNET_NO);
return;
}
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received `%s' request (new quota %u, old quota %u) from client for peer `%4s'\n",
"SET_QUOTA", (unsigned int) ntohl (qsm->quota.value__),
(unsigned int) n->in_tracker.available_bytes_per_s__,
GNUNET_i2s (&qsm->peer));
#endif
GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker, qsm->quota);
if (0 == ntohl (qsm->quota.value__))
{
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting peer `%4s', %s\n",
GNUNET_i2s (&n->id), "SET_QUOTA");
#endif
GNUNET_STATISTICS_update (stats,
gettext_noop ("# disconnects due to quota of 0"),
1, GNUNET_NO);
disconnect_neighbour (n, GNUNET_NO);
}
GNUNET_SERVER_receive_done (client, GNUNET_OK);
}
/**
* Take the given address and append it to the set of results sent back to
* the client.
*
* @param cls the transmission context used ('struct GNUNET_SERVER_TransmitContext*')
* @param address the resolved name, NULL to indicate the last response
*/
static void
transmit_address_to_client (void *cls, const char *address)
{
struct GNUNET_SERVER_TransmitContext *tc = cls;
size_t slen;
if (NULL != address)
{
slen = strlen (address) + 1;
GNUNET_SERVER_transmit_context_append_data (tc, address, slen,
GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_REPLY);
}
else
{
GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_REPLY);
GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
}
}
/**
* Handle AddressLookup-message.
*
* @param cls closure (always NULL)
* @param client identification of the client
* @param message the actual message
*/
static void
handle_address_lookup (void *cls, struct GNUNET_SERVER_Client *client,
const struct GNUNET_MessageHeader *message)
{
const struct AddressLookupMessage *alum;
struct TransportPlugin *lsPlugin;
const char *nameTransport;
const char *address;
uint16_t size;
struct GNUNET_SERVER_TransmitContext *tc;
struct GNUNET_TIME_Relative rtimeout;
int32_t numeric;
size = ntohs (message->size);
if (size < sizeof (struct AddressLookupMessage))
{
GNUNET_break_op (0);
GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
return;
}
alum = (const struct AddressLookupMessage *) message;
uint32_t addressLen = ntohl (alum->addrlen);
if (size <= sizeof (struct AddressLookupMessage) + addressLen)
{
GNUNET_break_op (0);
GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
return;
}
address = (const char *) &alum[1];
nameTransport = (const char *) &address[addressLen];
if (nameTransport
[size - sizeof (struct AddressLookupMessage) - addressLen - 1] != '\0')
{
GNUNET_break_op (0);
GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
return;
}
rtimeout = GNUNET_TIME_relative_ntoh (alum->timeout);
numeric = ntohl (alum->numeric_only);
lsPlugin = find_transport (nameTransport);
if (NULL == lsPlugin)
{
tc = GNUNET_SERVER_transmit_context_create (client);
GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_REPLY);
GNUNET_SERVER_transmit_context_run (tc, rtimeout);
return;
}
GNUNET_SERVER_disable_receive_done_warning (client);
tc = GNUNET_SERVER_transmit_context_create (client);
lsPlugin->api->address_pretty_printer (lsPlugin->api->cls, nameTransport,
address, addressLen, numeric, rtimeout,
&transmit_address_to_client, tc);
}
/**
* Handle PeerAddressLookupMessage.
*
* @param cls closure (always NULL)
* @param client identification of the client
* @param message the actual message
*/
static void
handle_peer_address_lookup (void *cls, struct GNUNET_SERVER_Client *client,
const struct GNUNET_MessageHeader *message)
{
const struct PeerAddressLookupMessage *peer_address_lookup;
struct NeighbourMapEntry *neighbor_iterator;
struct ReadyList *ready_iterator;
struct ForeignAddressList *foreign_address_iterator;
struct TransportPlugin *transport_plugin;
uint16_t size;
struct GNUNET_SERVER_TransmitContext *tc;
struct GNUNET_TIME_Relative rtimeout;
char *addr_buf;
size = ntohs (message->size);
if (size < sizeof (struct PeerAddressLookupMessage))
{
GNUNET_break_op (0);
GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
return;
}
peer_address_lookup = (const struct PeerAddressLookupMessage *) message;
rtimeout = GNUNET_TIME_relative_ntoh (peer_address_lookup->timeout);
neighbor_iterator = find_neighbour (&peer_address_lookup->peer);
/* Found no neighbor matching this peer id (shouldn't be possible, but...) */
if (neighbor_iterator == NULL)
{
GNUNET_break (0);
tc = GNUNET_SERVER_transmit_context_create (client);
GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_REPLY);
GNUNET_SERVER_transmit_context_run (tc, rtimeout);
return;
}
ready_iterator = neighbor_iterator->plugins;
GNUNET_SERVER_disable_receive_done_warning (client);
tc = GNUNET_SERVER_transmit_context_create (client);
while (ready_iterator != NULL)
{
foreign_address_iterator = ready_iterator->addresses;
while (foreign_address_iterator != NULL)
{
transport_plugin = foreign_address_iterator->ready_list->plugin;
if (foreign_address_iterator->addr != NULL)
{
GNUNET_asprintf (&addr_buf, "%s --- %s, %s",
a2s (transport_plugin->short_name,
foreign_address_iterator->addr,
foreign_address_iterator->addrlen),
(foreign_address_iterator->connected ==
GNUNET_YES) ? "CONNECTED" : "DISCONNECTED",
(foreign_address_iterator->validated ==
GNUNET_YES) ? "VALIDATED" : "UNVALIDATED");
transmit_address_to_client (tc, addr_buf);
GNUNET_free (addr_buf);
}
else if (foreign_address_iterator->addrlen == 0)
{
GNUNET_asprintf (&addr_buf, "%s --- %s, %s", "",
(foreign_address_iterator->connected ==
GNUNET_YES) ? "CONNECTED" : "DISCONNECTED",
(foreign_address_iterator->validated ==
GNUNET_YES) ? "VALIDATED" : "UNVALIDATED");
transmit_address_to_client (tc, addr_buf);
GNUNET_free (addr_buf);
}
foreign_address_iterator = foreign_address_iterator->next;
}
ready_iterator = ready_iterator->next;
}
GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_REPLY);
GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
}
static int
output_addresses (void *cls, const GNUNET_HashCode * key, void *value)
{
struct GNUNET_SERVER_TransmitContext *tc = cls;
struct NeighbourMapEntry *neighbor_iterator = value;
struct ForeignAddressList *foreign_address_iterator;
struct TransportPlugin *transport_plugin;
struct ReadyList *ready_iterator;
char *addr_buf;
ready_iterator = neighbor_iterator->plugins;
while (ready_iterator != NULL)
{
foreign_address_iterator = ready_iterator->addresses;
while (foreign_address_iterator != NULL)
{
transport_plugin = foreign_address_iterator->ready_list->plugin;
if (foreign_address_iterator->addr != NULL)
{
GNUNET_asprintf (&addr_buf, "%s:%s --- %s, %s",
GNUNET_i2s (&neighbor_iterator->id),
a2s (transport_plugin->short_name,
foreign_address_iterator->addr,
foreign_address_iterator->addrlen),
(foreign_address_iterator->connected ==
GNUNET_YES) ? "CONNECTED" : "DISCONNECTED",
(foreign_address_iterator->validated ==
GNUNET_YES) ? "VALIDATED" : "UNVALIDATED");
transmit_address_to_client (tc, addr_buf);
GNUNET_free (addr_buf);
}
else if (foreign_address_iterator->addrlen == 0)
{
GNUNET_asprintf (&addr_buf, "%s:%s --- %s, %s",
GNUNET_i2s (&neighbor_iterator->id), "",
(foreign_address_iterator->connected ==
GNUNET_YES) ? "CONNECTED" : "DISCONNECTED",
(foreign_address_iterator->validated ==
GNUNET_YES) ? "VALIDATED" : "UNVALIDATED");
transmit_address_to_client (tc, addr_buf);
GNUNET_free (addr_buf);
}
foreign_address_iterator = foreign_address_iterator->next;
}
ready_iterator = ready_iterator->next;
}
return GNUNET_OK;
}
/**
* Handle AddressIterateMessage
*
* @param cls closure (always NULL)
* @param client identification of the client
* @param message the actual message
*/
static void
handle_address_iterate (void *cls, struct GNUNET_SERVER_Client *client,
const struct GNUNET_MessageHeader *message)
{
struct GNUNET_SERVER_TransmitContext *tc;
uint16_t size;
size = ntohs (message->size);
if (size < sizeof (struct AddressIterateMessage))
{
GNUNET_break_op (0);
GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
return;
}
GNUNET_SERVER_disable_receive_done_warning (client);
tc = GNUNET_SERVER_transmit_context_create (client);
GNUNET_CONTAINER_multihashmap_iterate (neighbours, &output_addresses, tc);
GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_REPLY);
GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
}
static const struct GNUNET_MessageHeader *
do_get_our_hello ()
{
return (const struct GNUNET_MessageHeader *) our_hello;
}
/**
* Setup the environment for this plugin.
*/
static void
create_environment (struct TransportPlugin *plug)
{
plug->env.cfg = cfg;
plug->env.my_identity = &my_identity;
plug->env.get_our_hello = &do_get_our_hello;
plug->env.cls = plug;
plug->env.receive = &plugin_env_receive;
plug->env.notify_address = &plugin_env_notify_address;
plug->env.session_end = &plugin_env_session_end;
plug->env.max_connections = max_connect_per_transport;
plug->env.stats = stats;
}
/**
* Start the specified transport (load the plugin).
*/
static void
start_transport (struct GNUNET_SERVER_Handle *server, const char *name)
{
struct TransportPlugin *plug;
char *libname;
GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Loading `%s' transport plugin\n"),
name);
GNUNET_asprintf (&libname, "libgnunet_plugin_transport_%s", name);
plug = GNUNET_malloc (sizeof (struct TransportPlugin));
create_environment (plug);
plug->short_name = GNUNET_strdup (name);
plug->lib_name = libname;
plug->next = plugins;
plugins = plug;
plug->api = GNUNET_PLUGIN_load (libname, &plug->env);
if (plug->api == NULL)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Failed to load transport plugin for `%s'\n"), name);
GNUNET_free (plug->short_name);
plugins = plug->next;
GNUNET_free (libname);
GNUNET_free (plug);
}
}
static int
null_mq_client_pointers (void *cls, const GNUNET_HashCode * key, void *value)
{
struct TransportClient *pos = cls;
struct NeighbourMapEntry *n = value;
struct MessageQueue *mq;
for (mq = n->messages_head; mq != NULL; mq = mq->next)
{
if (mq->client == pos)
mq->client = NULL; /* do not use anymore! */
}
return GNUNET_OK;
}
/**
* Called whenever a client is disconnected. Frees our
* resources associated with that client.
*
* @param cls closure
* @param client identification of the client
*/
static void
client_disconnect_notification (void *cls, struct GNUNET_SERVER_Client *client)
{
struct TransportClient *pos;
struct TransportClient *prev;
struct ClientMessageQueueEntry *mqe;
struct Blacklisters *bl;
struct BlacklistCheck *bc;
if (client == NULL)
return;
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
"Client disconnected, cleaning up.\n");
#endif
/* clean up blacklister */
bl = bl_head;
while (bl != NULL)
{
if (bl->client == client)
{
bc = bc_head;
while (bc != NULL)
{
if (bc->bl_pos == bl)
{
bc->bl_pos = bl->next;
if (bc->th != NULL)
{
GNUNET_CONNECTION_notify_transmit_ready_cancel (bc->th);
bc->th = NULL;
}
if (bc->task == GNUNET_SCHEDULER_NO_TASK)
bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
break;
}
bc = bc->next;
}
GNUNET_CONTAINER_DLL_remove (bl_head, bl_tail, bl);
GNUNET_SERVER_client_drop (bl->client);
GNUNET_free (bl);
break;
}
bl = bl->next;
}
/* clean up 'normal' clients */
prev = NULL;
pos = clients;
while ((pos != NULL) && (pos->client != client))
{
prev = pos;
pos = pos->next;
}
if (pos == NULL)
return;
while (NULL != (mqe = pos->message_queue_head))
{
GNUNET_CONTAINER_DLL_remove (pos->message_queue_head,
pos->message_queue_tail, mqe);
pos->message_count--;
GNUNET_free (mqe);
}
if (NULL != neighbours)
GNUNET_CONTAINER_multihashmap_iterate (neighbours, &null_mq_client_pointers,
pos);
if (prev == NULL)
clients = pos->next;
else
prev->next = pos->next;
if (GNUNET_YES == pos->tcs_pending)
{
pos->client = NULL;
return;
}
if (pos->th != NULL)
{
GNUNET_CONNECTION_notify_transmit_ready_cancel (pos->th);
pos->th = NULL;
}
GNUNET_break (0 == pos->message_count);
GNUNET_free (pos);
}
static int
disconnect_all_neighbours (void *cls, const GNUNET_HashCode * key, void *value)
{
struct NeighbourMapEntry *n = value;
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting peer `%4s', %s\n",
GNUNET_i2s (&n->id), "SHUTDOWN_TASK");
#endif
disconnect_neighbour (n, GNUNET_NO);
return GNUNET_OK;
}
/**
* Function called when the service shuts down. Unloads our plugins
* and cancels pending validations.
*
* @param cls closure, unused
* @param tc task context (unused)
*/
static void
shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
struct TransportPlugin *plug;
struct OwnAddressList *al;
struct CheckHelloValidatedContext *chvc;
shutdown_in_progress = GNUNET_YES;
GNUNET_CONTAINER_multihashmap_iterate (neighbours, &disconnect_all_neighbours,
NULL);
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Transport service is unloading plugins...\n");
#endif
while (NULL != (plug = plugins))
{
if (plug->address_update_task != GNUNET_SCHEDULER_NO_TASK)
{
GNUNET_SCHEDULER_cancel (plug->address_update_task);
plug->address_update_task = GNUNET_SCHEDULER_NO_TASK;
}
GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
GNUNET_free (plug->lib_name);
GNUNET_free (plug->short_name);
while (NULL != (al = plug->addresses))
{
plug->addresses = al->next;
GNUNET_free (al);
}
plugins = plug->next;
GNUNET_free (plug);
}
if (my_private_key != NULL)
GNUNET_CRYPTO_rsa_key_free (my_private_key);
GNUNET_free_non_null (our_hello);
GNUNET_CONTAINER_multihashmap_iterate (validation_map, &abort_validation,
NULL);
GNUNET_CONTAINER_multihashmap_destroy (validation_map);
validation_map = NULL;
if (ats_task != GNUNET_SCHEDULER_NO_TASK)
{
GNUNET_SCHEDULER_cancel (ats_task);
ats_task = GNUNET_SCHEDULER_NO_TASK;
}
#if HAVE_LIBGLPK
if (ats != NULL)
ats_shutdown (ats);
#endif
/* free 'chvc' data structure */
while (NULL != (chvc = chvc_head))
{
chvc_head = chvc->next;
if (chvc->piter != NULL)
{
GNUNET_PEERINFO_iterate_cancel (chvc->piter);
GNUNET_STATISTICS_update (stats,
gettext_noop
("# outstanding peerinfo iterate requests"), -1,
GNUNET_NO);
chvc->ve_count--;
}
else
GNUNET_break (0);
GNUNET_assert (chvc->ve_count == 0);
GNUNET_free (chvc);
}
chvc_tail = NULL;
if (stats != NULL)
{
GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
stats = NULL;
}
if (peerinfo != NULL)
{
GNUNET_PEERINFO_disconnect (peerinfo);
peerinfo = NULL;
}
if (GNUNET_SCHEDULER_NO_TASK != hello_task)
{
GNUNET_SCHEDULER_cancel (hello_task);
hello_task = GNUNET_SCHEDULER_NO_TASK;
}
/* Can we assume those are gone by now, or do we need to clean up
* explicitly!? */
GNUNET_break (bl_head == NULL);
GNUNET_break (bc_head == NULL);
GNUNET_CONTAINER_multihashmap_destroy (neighbours);
neighbours = NULL;
}
void
ats_result_cb ()
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ATS Result callback\n");
}
#if HAVE_LIBGLPK
struct AtsBuildContext
{
struct ATS_mechanism *mechanisms;
struct ATS_peer *peers;
int c_peers;
int c_mechs;
};
static int
find_and_count_addresses (void *cls, const GNUNET_HashCode * key, void *value)
{
struct AtsBuildContext *abc = cls;
struct NeighbourMapEntry *next = value;
int found_addresses = GNUNET_NO;
struct ReadyList *r_next = next->plugins;
while (r_next != NULL)
{
struct ForeignAddressList *a_next = r_next->addresses;
while (a_next != NULL)
{
abc->c_mechs++;
found_addresses = GNUNET_YES;
a_next = a_next->next;
}
r_next = r_next->next;
}
if (found_addresses)
abc->c_peers++;
return GNUNET_OK;
}
static int
setup_ats_problem (void *cls, const GNUNET_HashCode * key, void *value)
{
struct AtsBuildContext *abc = cls;
struct NeighbourMapEntry *next = value;
int found_addresses = GNUNET_NO;
struct ReadyList *r_next = next->plugins;
while (r_next != NULL)
{
struct ForeignAddressList *a_next = r_next->addresses;
while (a_next != NULL)
{
if (found_addresses == GNUNET_NO)
{
abc->peers[abc->c_peers].peer = next->id;
abc->peers[abc->c_peers].m_head = NULL;
abc->peers[abc->c_peers].m_tail = NULL;
abc->peers[abc->c_peers].f = 1.0 / abc->c_mechs;
}
abc->mechanisms[abc->c_mechs].addr = a_next;
abc->mechanisms[abc->c_mechs].col_index = abc->c_mechs;
abc->mechanisms[abc->c_mechs].peer = &abc->peers[abc->c_peers];
abc->mechanisms[abc->c_mechs].next = NULL;
abc->mechanisms[abc->c_mechs].plugin = r_next->plugin;
abc->mechanisms[abc->c_mechs].ressources = a_next->ressources;
abc->mechanisms[abc->c_mechs].quality = a_next->quality;
GNUNET_CONTAINER_DLL_insert_tail (abc->peers[abc->c_peers].m_head,
abc->peers[abc->c_peers].m_tail,
&abc->mechanisms[abc->c_mechs]);
found_addresses = GNUNET_YES;
abc->c_mechs++;
a_next = a_next->next;
}
r_next = r_next->next;
}
if (found_addresses == GNUNET_YES)
abc->c_peers++;
return GNUNET_OK;
}
static void
create_ats_information (struct ATS_peer **p, int *c_p, struct ATS_mechanism **m,
int *c_m)
{
struct AtsBuildContext abc;
#if VERBOSE_ATS
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"ATS requires clean address information\n");
#endif
abc.c_peers = 0;
abc.c_mechs = 0;
GNUNET_CONTAINER_multihashmap_iterate (neighbours, &find_and_count_addresses,
&abc);
#if VERBOSE_ATS
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Found %u peers with % u transport mechanisms\n", c_peers,
c_mechs);
#endif
if ((abc.c_peers == 0) && (abc.c_mechs == 0))
{
*p = NULL;
(*c_p) = 0;
*m = NULL;
(*c_m) = 0;
return;
}
abc.mechanisms =
GNUNET_malloc ((1 + abc.c_mechs) * sizeof (struct ATS_mechanism));
abc.peers = GNUNET_malloc ((1 + abc.c_peers) * sizeof (struct ATS_peer));
abc.c_mechs = 1;
abc.c_peers = 1;
GNUNET_CONTAINER_multihashmap_iterate (neighbours, &setup_ats_problem, &abc);
abc.c_mechs--;
abc.c_peers--;
(*c_m) = abc.c_mechs;
(*c_p) = abc.c_peers;
(*p) = abc.peers;
(*m) = abc.mechanisms;
}
static void
schedule_ats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
struct ATS_Handle *ats = (struct ATS_Handle *) cls;
if (ats == NULL)
return;
ats_task = GNUNET_SCHEDULER_NO_TASK;
if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
return;
if (shutdown_in_progress == GNUNET_YES)
return;
struct GNUNET_TIME_Relative delta =
GNUNET_TIME_absolute_get_difference (last_ats_execution,
GNUNET_TIME_absolute_get ());
if (delta.rel_value < ats_minimum_interval.rel_value)
{
#if DEBUG_ATS
GNUNET_log (GNUNET_ERROR_TYPE_BULK,
"Minimum time between cycles not reached\n");
#endif
return;
}
#if DEBUG_ATS
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Running scheduled calculation\n");
#endif
#if HAVE_LIBGLPK
ats_calculate_bandwidth_distribution (ats);
#endif
last_ats_execution = GNUNET_TIME_absolute_get ();
ats_task =
GNUNET_SCHEDULER_add_delayed (ats_regular_interval, &schedule_ats, ats);
}
#endif
struct ForeignAddressList *
get_preferred_ats_address (struct NeighbourMapEntry *n)
{
// TODO get ATS prefered address
return find_ready_address (n);
}
/**
* Initiate transport service.
*
* @param cls closure
* @param server the initialized server
* @param c configuration to use
*/
static void
run (void *cls, struct GNUNET_SERVER_Handle *server,
const struct GNUNET_CONFIGURATION_Handle *c)
{
static const struct GNUNET_SERVER_MessageHandler handlers[] = {
{&handle_start, NULL,
GNUNET_MESSAGE_TYPE_TRANSPORT_START, sizeof (struct StartMessage)},
{&handle_hello, NULL,
GNUNET_MESSAGE_TYPE_HELLO, 0},
{&handle_send, NULL,
GNUNET_MESSAGE_TYPE_TRANSPORT_SEND, 0},
{&handle_request_connect, NULL,
GNUNET_MESSAGE_TYPE_TRANSPORT_REQUEST_CONNECT,
sizeof (struct TransportRequestConnectMessage)},
{&handle_set_quota, NULL,
GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA, sizeof (struct QuotaSetMessage)},
{&handle_address_lookup, NULL,
GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_LOOKUP,
0},
{&handle_peer_address_lookup, NULL,
GNUNET_MESSAGE_TYPE_TRANSPORT_PEER_ADDRESS_LOOKUP,
0},
{&handle_address_iterate, NULL,
GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_ITERATE,
0},
{&handle_blacklist_init, NULL,
GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_INIT,
sizeof (struct GNUNET_MessageHeader)},
{&handle_blacklist_reply, NULL,
GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_REPLY,
sizeof (struct BlacklistMessage)},
{NULL, NULL, 0, 0}
};
char *plugs;
char *pos;
int no_transports;
unsigned long long tneigh;
char *keyfile;
shutdown_in_progress = GNUNET_NO;
cfg = c;
stats = GNUNET_STATISTICS_create ("transport", cfg);
validation_map = GNUNET_CONTAINER_multihashmap_create (64);
neighbours = GNUNET_CONTAINER_multihashmap_create (256);
/* parse configuration */
if ((GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_number (c, "TRANSPORT", "NEIGHBOUR_LIMIT",
&tneigh)) ||
(GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_filename (c, "GNUNETD", "HOSTKEY",
&keyfile)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_
("Transport service is lacking key configuration settings. Exiting.\n"));
GNUNET_SCHEDULER_shutdown ();
if (stats != NULL)
{
GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
stats = NULL;
}
GNUNET_CONTAINER_multihashmap_destroy (validation_map);
validation_map = NULL;
GNUNET_CONTAINER_multihashmap_destroy (neighbours);
neighbours = NULL;
return;
}
max_connect_per_transport = (uint32_t) tneigh;
peerinfo = GNUNET_PEERINFO_connect (cfg);
if (peerinfo == NULL)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Could not access PEERINFO service. Exiting.\n"));
GNUNET_SCHEDULER_shutdown ();
if (stats != NULL)
{
GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
stats = NULL;
}
GNUNET_CONTAINER_multihashmap_destroy (validation_map);
validation_map = NULL;
GNUNET_CONTAINER_multihashmap_destroy (neighbours);
neighbours = NULL;
GNUNET_free (keyfile);
return;
}
my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
GNUNET_free (keyfile);
if (my_private_key == NULL)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Transport service could not access hostkey. Exiting.\n"));
GNUNET_SCHEDULER_shutdown ();
if (stats != NULL)
{
GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
stats = NULL;
}
GNUNET_CONTAINER_multihashmap_destroy (validation_map);
validation_map = NULL;
GNUNET_CONTAINER_multihashmap_destroy (neighbours);
neighbours = NULL;
return;
}
GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key),
&my_identity.hashPubKey);
/* setup notification */
GNUNET_SERVER_disconnect_notify (server, &client_disconnect_notification,
NULL);
/* load plugins... */
no_transports = 1;
if (GNUNET_OK ==
GNUNET_CONFIGURATION_get_value_string (c, "TRANSPORT", "PLUGINS", &plugs))
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting transport plugins `%s'\n"),
plugs);
pos = strtok (plugs, " ");
while (pos != NULL)
{
start_transport (server, pos);
no_transports = 0;
pos = strtok (NULL, " ");
}
GNUNET_free (plugs);
}
GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
NULL);
if (no_transports)
refresh_hello ();
/* Initializing ATS */
int co;
char *section;
unsigned long long value;
#if HAVE_LIBGLPK
double D = 1.0;
double U = 1.0;
double R = 1.0;
int v_b_min = 64000;
int v_n_min = 5;
#endif
ats_minimum_interval = ATS_MIN_INTERVAL;
ats_regular_interval = ATS_EXEC_INTERVAL;
/* loading cost ressources */
for (co = 0; co < available_ressources; co++)
{
GNUNET_asprintf (§ion, "%s_UP", ressources[co].cfg_param);
if (GNUNET_CONFIGURATION_have_value (cfg, "transport", section))
{
if (GNUNET_OK ==
GNUNET_CONFIGURATION_get_value_number (cfg, "transport", section,
&value))
{
#if DEBUG_ATS
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Found ressource cost: [%s] = %llu\n", section, value);
#endif
ressources[co].c_max = value;
}
}
GNUNET_free (section);
GNUNET_asprintf (§ion, "%s_DOWN", ressources[co].cfg_param);
if (GNUNET_CONFIGURATION_have_value (cfg, "transport", section))
{
if (GNUNET_OK ==
GNUNET_CONFIGURATION_get_value_number (cfg, "transport", section,
&value))
{
#if DEBUG_ATS
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Found ressource cost: [%s] = %llu\n", section, value);
#endif
ressources[co].c_min = value;
}
}
GNUNET_free (section);
}
#if HAVE_LIBGLPK
ats =
ats_init (D, U, R, v_b_min, v_n_min, ATS_MAX_ITERATIONS,
ATS_MAX_EXEC_DURATION, &create_ats_information, ats_result_cb);
ats_set_logging_options (ats, stats, cfg);
GNUNET_break (GNUNET_OK ==
GNUNET_CONFIGURATION_get_value_time (cfg, "transport",
"ATS_EXEC_INTERVAL",
&ats_regular_interval));
GNUNET_break (GNUNET_OK ==
GNUNET_CONFIGURATION_get_value_time (cfg, "transport",
"ATS_MIN_INTERVAL",
&ats_minimum_interval));
if (ats != NULL)
ats_task = GNUNET_SCHEDULER_add_now (&schedule_ats, ats);
#endif
#if DEBUG_TRANSPORT
GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transport service ready.\n"));
#endif
/* If we have a blacklist file, read from it */
read_blacklist_file (cfg);
/* process client requests */
GNUNET_SERVER_add_handlers (server, handlers);
}
/**
* The main function for the transport service.
*
* @param argc number of arguments from the command line
* @param argv command line arguments
* @return 0 ok, 1 on error
*/
int
main (int argc, char *const *argv)
{
a2s (NULL, NULL, 0); /* make compiler happy */
return (GNUNET_OK ==
GNUNET_SERVICE_run (argc, argv, "transport",
GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;
}
/* end of gnunet-service-transport.c */