* @author Julius Bünger
*/
#include "platform.h"
+#include "gnunet_applications.h"
#include "gnunet_util_lib.h"
#include "gnunet_cadet_service.h"
#include <inttypes.h>
*
* To be canceled on shutdown.
*/
- struct GNUNET_CADET_TransmitHandle *transmit_handle;
+ struct PendingMessage *liveliness_check_pending;
/**
* Number of pending operations.
* Identity of the peer
*/
struct GNUNET_PeerIdentity peer_id;
-
+
/**
* Flags indicating status of peer
*/
*/
};
+/**
+ * @brief Closure to #valid_peer_iterator
+ */
+struct PeersIteratorCls
+{
+ /**
+ * Iterator function
+ */
+ PeersIterator iterator;
+
+ /**
+ * Closure to iterator
+ */
+ void *cls;
+};
+
/**
* @brief Hashmap of valid peers.
*/
return ctx;
}
+
/**
* @brief Create a new #PeerContext and insert it into the peer map
*
return ctx;
}
+
/**
* @brief Create or get a #PeerContext
*
return get_peer_ctx (peer);
}
+
/**
* @brief Check whether we have a connection to this @a peer
*
return GNUNET_YES;
}
+
/**
* @brief The closure to #get_rand_peer_iterator.
*/
const struct GNUNET_PeerIdentity *peer;
};
+
/**
* @brief Iterator function for #get_random_peer_from_peermap.
*
return GNUNET_YES;
}
+
/**
* @brief Get a random peer from @a peer_map
*
return ret;
}
+
/**
* @brief Add a given @a peer to valid peers.
*
return ret;
}
+
/**
* @brief Set the peer flag to living and
* call the pending operations on this peer.
struct GNUNET_PeerIdentity *peer;
unsigned int i;
- /* Cancle cadet transmit_handle if still scheduled */
- if (NULL != peer_ctx->transmit_handle)
+ peer = &peer_ctx->peer_id;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Peer %s is live and valid, calling %i pending operations on it\n",
+ GNUNET_i2s (peer),
+ peer_ctx->num_pending_ops);
+
+ if (NULL != peer_ctx->liveliness_check_pending)
{
- GNUNET_CADET_notify_transmit_ready_cancel (peer_ctx->transmit_handle);
- peer_ctx->transmit_handle = NULL;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Removing pending liveliness check for peer %s\n",
+ GNUNET_i2s (&peer_ctx->peer_id));
+ // TODO wait until cadet sets mq->cancel_impl
+ //GNUNET_MQ_send_cancel (peer_ctx->liveliness_check_pending->ev);
+ GNUNET_free (peer_ctx->liveliness_check_pending);
+ peer_ctx->liveliness_check_pending = NULL;
}
- peer = &peer_ctx->peer_id;
(void) add_valid_peer (peer);
set_peer_flag (peer_ctx, Peers_ONLINE);
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Peer %s is live and valid\n",
- GNUNET_i2s (peer));
/* Call pending operations */
for (i = 0; i < peer_ctx->num_pending_ops; i++)
GNUNET_array_grow (peer_ctx->pending_ops, peer_ctx->num_pending_ops, 0);
}
+
/**
* @brief Get the channel of a peer. If not existing, create.
*
get_channel (const struct GNUNET_PeerIdentity *peer)
{
struct PeerContext *peer_ctx;
+ struct GNUNET_HashCode port;
peer_ctx = get_peer_ctx (peer);
if (NULL == peer_ctx->send_channel)
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Trying to establish channel to peer %s\n",
GNUNET_i2s (peer));
+ GNUNET_CRYPTO_hash (GNUNET_APPLICATION_PORT_RPS,
+ strlen (GNUNET_APPLICATION_PORT_RPS),
+ &port);
peer_ctx->send_channel =
GNUNET_CADET_channel_create (cadet_handle,
peer_ctx->send_channel_flags, /* context */
peer,
- GNUNET_RPS_CADET_PORT,
+ &port,
GNUNET_CADET_OPTION_RELIABLE);
}
+ GNUNET_assert (NULL != peer_ctx->send_channel);
return peer_ctx->send_channel;
}
+
/**
* Get the message queue (#GNUNET_MQ_Handle) of a specific peer.
*
* If we already have a message queue open to this client,
* simply return it, otherways create one.
- *
+ *
* @param peer the peer to get the mq to
* @return the #GNUNET_MQ_Handle
*/
struct PeerContext *peer_ctx;
peer_ctx = get_peer_ctx (peer);
- GNUNET_assert (NULL == peer_ctx->transmit_handle);
if (NULL == peer_ctx->mq)
{
return peer_ctx->mq;
}
+
/**
- * @brief Callback that is called when a channel was effectively established.
- *
- * This is an implementation of #GNUNET_CONNECTION_TransmitReadyNotify and
- * given to #GNUNET_CADET_notify_transmit_ready_cancel and called when the
- * channel was successfully established.
- *
- * This function type was originally ment to be called to provide the data to
- * be sent. This is called when the connection is ready to queue more data.
- * However we use it to get notified about the successful establishement of a
- * cadet channel.
+ * @brief This is called in response to the first message we sent as a
+ * liveliness check.
*
- * @a buf will be NULL and @a size zero if the
- * connection was closed for writing in the meantime.
- *
- * @param cls closure
- * @param size number of bytes available in @a buf
- * @param buf where the callee should write the message
- * @return number of bytes written to @a buf
+ * @param cls #PeerContext of peer with pending liveliness check
*/
-//TODO
-static size_t
-cadet_notify_transmit_ready_cb (void *cls, size_t size, void *buf)
+static void
+mq_liveliness_check_successful (void *cls)
{
- struct PeerContext *peer_ctx = (struct PeerContext *) cls;
- // TODO make sure the context is not deleted or the establishing of the
- // channel is cancelled
-
- peer_ctx->transmit_handle = NULL;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Set ->transmit_handle = NULL for peer %s\n",
- GNUNET_i2s (&peer_ctx->peer_id));
+ struct PeerContext *peer_ctx = cls;
- if ( (NULL != buf) &&
- (0 != size) )
+ if (NULL != peer_ctx->liveliness_check_pending)
{
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Liveliness check for peer %s was successfull\n",
+ GNUNET_i2s (&peer_ctx->peer_id));
+ GNUNET_free (peer_ctx->liveliness_check_pending);
+ peer_ctx->liveliness_check_pending = NULL;
set_peer_live (peer_ctx);
}
- else
- {
- LOG (GNUNET_ERROR_TYPE_WARNING,
- "Problems establishing a connection to peer %s in order to check liveliness\n",
- GNUNET_i2s (&peer_ctx->peer_id));
- // TODO reschedule? cleanup?
- }
- return 0;
}
/**
"Get informed about peer %s getting live\n",
GNUNET_i2s (&peer_ctx->peer_id));
- if (NULL == peer_ctx->transmit_handle &&
- NULL == peer_ctx->send_channel)
- {
- (void) get_channel (&peer_ctx->peer_id);
- peer_ctx->transmit_handle =
- GNUNET_CADET_notify_transmit_ready (peer_ctx->send_channel,
- GNUNET_NO,
- GNUNET_TIME_UNIT_FOREVER_REL,
- sizeof (struct GNUNET_MessageHeader),
- cadet_notify_transmit_ready_cb,
- peer_ctx);
- }
- else if (NULL != peer_ctx->transmit_handle)
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Already waiting for notification\n");
- else if (NULL != peer_ctx->send_channel)
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Already have established channel to peer\n");
+ struct GNUNET_MQ_Handle *mq;
+ struct GNUNET_MQ_Envelope *ev;
+
+ ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_RPS_PP_CHECK_LIVE);
+ peer_ctx->liveliness_check_pending = GNUNET_new (struct PendingMessage);
+ peer_ctx->liveliness_check_pending->ev = ev;
+ peer_ctx->liveliness_check_pending->peer_ctx = peer_ctx;
+ peer_ctx->liveliness_check_pending->type = "Check liveliness";
+ mq = get_mq (&peer_ctx->peer_id);
+ GNUNET_MQ_notify_sent (ev,
+ mq_liveliness_check_successful,
+ peer_ctx);
+ GNUNET_MQ_send (mq, ev);
}
/**
return pending_msg;
}
+
/**
* @brief Remove a pending message from the respective DLL
*
GNUNET_free (pending_msg);
}
+
/**
* @brief Check whether function of type #PeerOp was already scheduled
*
return GNUNET_NO;
}
+
/**
* Iterator over hash map entries. Deletes all contexts of peers.
*
return GNUNET_YES;
}
+
/**
* @brief This is called once a message is sent.
*
remove_pending_message (pending_msg);
}
-/**
- * @brief Clear the stored valid peers.
- */
-static void
-clear_valid_peer_storage ()
-{
- struct GNUNET_DISK_FileHandle *fh;
-
- if (GNUNET_OK != GNUNET_DISK_file_test (filename_valid_peers))
- {
- return;
- }
- fh = GNUNET_DISK_file_open (filename_valid_peers,
- GNUNET_DISK_OPEN_WRITE,
- GNUNET_DISK_PERM_USER_READ |
- GNUNET_DISK_PERM_USER_WRITE);
- if (NULL == fh)
- {
- LOG (GNUNET_ERROR_TYPE_WARNING,
- "Not able to clear file `%s' containing valid peers\n",
- filename_valid_peers);
- return;
- }
- GNUNET_DISK_file_write (fh, "", 0);
- GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
-}
/**
* @brief Iterator function for #store_valid_peers.
return GNUNET_YES;
}
+
/**
* @brief Store the peers currently in #valid_peers to disk.
*/
{
struct GNUNET_DISK_FileHandle *fh;
uint32_t number_written_peers;
+ int ret;
+
+ if (0 == strncmp ("DISABLE", filename_valid_peers, 7))
+ {
+ return;
+ }
- if (GNUNET_OK !=
- GNUNET_DISK_directory_create_for_file (filename_valid_peers))
+ ret = GNUNET_DISK_directory_create_for_file (filename_valid_peers);
+ if (GNUNET_SYSERR == ret)
{
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ "Not able to create directory for file `%s'\n",
+ filename_valid_peers);
+ GNUNET_break (0);
+ }
+ else if (GNUNET_NO == ret)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ "Directory for file `%s' exists but is not writable for us\n",
+ filename_valid_peers);
GNUNET_break (0);
}
- clear_valid_peer_storage ();
fh = GNUNET_DISK_file_open (filename_valid_peers,
GNUNET_DISK_OPEN_WRITE |
- GNUNET_DISK_OPEN_CREATE |
- GNUNET_DISK_OPEN_APPEND,
+ GNUNET_DISK_OPEN_CREATE,
GNUNET_DISK_PERM_USER_READ |
GNUNET_DISK_PERM_USER_WRITE);
if (NULL == fh)
GNUNET_CONTAINER_multipeermap_size (valid_peers));
}
+
/**
* @brief Convert string representation of peer id to peer id.
*
return peer;
}
+
/**
* @brief Restore the peers on disk to #valid_peers.
*/
char *buf;
ssize_t size_read;
char *iter_buf;
- const char *str_repr;
+ char *str_repr;
const struct GNUNET_PeerIdentity *peer;
+ if (0 == strncmp ("DISABLE", filename_valid_peers, 7))
+ {
+ return;
+ }
+
if (GNUNET_OK != GNUNET_DISK_file_test (filename_valid_peers))
{
return;
}
fh = GNUNET_DISK_file_open (filename_valid_peers,
GNUNET_DISK_OPEN_READ,
- GNUNET_DISK_PERM_USER_READ);
+ GNUNET_DISK_PERM_NONE);
GNUNET_assert (NULL != fh);
GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_handle_size (fh, &file_size));
num_peers = file_size / 53;
buf = GNUNET_malloc (file_size);
size_read = GNUNET_DISK_file_read (fh, buf, file_size);
GNUNET_assert (size_read == file_size);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Restoring %" PRIu32 " peers from file `%s'\n",
+ num_peers,
+ filename_valid_peers);
for (iter_buf = buf; iter_buf < buf + file_size - 1; iter_buf += 53)
{
str_repr = GNUNET_strndup (iter_buf, 53);
peer = s2i_full (str_repr);
+ GNUNET_free (str_repr);
add_valid_peer (peer);
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Restored valid peer %s from disk\n",
GNUNET_i2s_full (peer));
}
- GNUNET_assert (num_peers == GNUNET_CONTAINER_multipeermap_size (valid_peers));
+ iter_buf = NULL;
+ GNUNET_free (buf);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "num_peers: %" PRIu32 ", _size (valid_peers): %u\n",
+ num_peers,
+ GNUNET_CONTAINER_multipeermap_size (valid_peers));
+ if (num_peers != GNUNET_CONTAINER_multipeermap_size (valid_peers))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ "Number of restored peers does not match file size. Have probably duplicates.\n");
+ }
GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Restored %u valid peers from disk\n",
- num_peers);
+ GNUNET_CONTAINER_multipeermap_size (valid_peers));
}
+
/**
* @brief Initialise storage of peers
*
restore_valid_peers ();
}
+
/**
* @brief Delete storage of peers that was created with #Peers_initialise ()
*/
GNUNET_CONTAINER_multipeermap_destroy (valid_peers);
}
-// TODO store valid peers
+
+/**
+ * Iterator over #valid_peers hash map entries.
+ *
+ * @param cls closure - unused
+ * @param peer current peer id
+ * @param value value in the hash map - unused
+ * @return #GNUNET_YES if we should continue to
+ * iterate,
+ * #GNUNET_NO if not.
+ */
+static int
+valid_peer_iterator (void *cls,
+ const struct GNUNET_PeerIdentity *peer,
+ void *value)
+{
+ struct PeersIteratorCls *it_cls = cls;
+
+ return it_cls->iterator (it_cls->cls,
+ peer);
+}
+
+
+/**
+ * @brief Get all currently known, valid peer ids.
+ *
+ * @param it function to call on each peer id
+ * @param it_cls extra argument to @a it
+ * @return the number of key value pairs processed,
+ * #GNUNET_SYSERR if it aborted iteration
+ */
+int
+Peers_get_valid_peers (PeersIterator iterator,
+ void *it_cls)
+{
+ struct PeersIteratorCls *cls;
+ int ret;
+
+ cls = GNUNET_new (struct PeersIteratorCls);
+ cls->iterator = iterator;
+ cls->cls = it_cls;
+ ret = GNUNET_CONTAINER_multipeermap_iterate (valid_peers,
+ valid_peer_iterator,
+ cls);
+ GNUNET_free (cls);
+ return ret;
+}
+
/**
* @brief Add peer to known peers.
* @param peer the new #GNUNET_PeerIdentity
*
* @return #GNUNET_YES if peer was inserted
- * #GNUNET_NO if peer was already known
+ * #GNUNET_NO otherwise (if peer was already known or
+ * peer was #own_identity)
*/
int
Peers_insert_peer (const struct GNUNET_PeerIdentity *peer)
/**
- * @brief Add peer to known peers and check for liveliness.
+ * @brief Try connecting to a peer to see whether it is online
*
- * This function is called on new peer_ids from 'external' sources
- * (client seed, cadet get_peers(), ...)
+ * If not known yet, insert into known peers
*
- * @param peer the new #GNUNET_PeerIdentity
- *
- * @return #GNUNET_YES if peer was inserted
- * #GNUNET_NO if peer was already known
+ * @param peer the peer whose liveliness is to be checked
+ * @return #GNUNET_YES if peer had to be inserted
+ * #GNUNET_NO otherwise (if peer was already known or
+ * peer was #own_identity)
*/
int
-Peers_insert_peer_check_liveliness (const struct GNUNET_PeerIdentity *peer)
+Peers_issue_peer_liveliness_check (const struct GNUNET_PeerIdentity *peer)
{
struct PeerContext *peer_ctx;
int ret;
- ret = Peers_insert_peer (peer);
if (0 == GNUNET_CRYPTO_cmp_peer_identity (peer, own_identity))
{
- return ret;
+ return GNUNET_NO;
}
+ ret = Peers_insert_peer (peer);
peer_ctx = get_peer_ctx (peer);
- if (GNUNET_NO == Peers_check_peer_valid (peer))
+ if (GNUNET_NO == Peers_check_peer_flag (peer, Peers_ONLINE))
{
check_peer_live (peer_ctx);
}
return ret;
}
+
/**
- * @brief Remove unecessary data
+ * @brief Check if peer is removable.
*
- * If the other peer is not intending to send messages, we have messages pending
- * to be sent to this peer and we are not waiting for a reply, remove the
- * information about it (its #PeerContext).
+ * Check if
+ * - a recv channel exists
+ * - there are pending messages
+ * - there is no pending pull reply
*
- * @param peer the peer to clean
- * @return #GNUNET_YES if peer was removed
- * #GNUNET_NO otherwise
+ * @param peer the peer in question
+ * @return #GNUNET_YES if peer is removable
+ * #GNUNET_NO if peer is NOT removable
+ * #GNUNET_SYSERR if peer is not known
*/
int
-Peers_clean_peer (const struct GNUNET_PeerIdentity *peer)
+Peers_check_removable (const struct GNUNET_PeerIdentity *peer)
{
struct PeerContext *peer_ctx;
- // TODO actually remove unnecessary data
-
- if (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (peer_map, peer))
+ if (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (peer_map, peer))
{
- return GNUNET_NO;
+ return GNUNET_SYSERR;
}
peer_ctx = get_peer_ctx (peer);
{
return GNUNET_NO;
}
- Peers_remove_peer (peer);
return GNUNET_YES;
}
+
/**
* @brief Remove peer
- *
+ *
* @param peer the peer to clean
* @return #GNUNET_YES if peer was removed
* #GNUNET_NO otherwise
{
struct PeerContext *peer_ctx;
- if (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (peer_map, peer))
+ if (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (peer_map, peer))
{
return GNUNET_NO;
}
Peers_unset_peer_flag (peer, Peers_ONLINE);
GNUNET_array_grow (peer_ctx->pending_ops, peer_ctx->num_pending_ops, 0);
- // TODO delete struct GNUNET_TRANSPORT_TransmitHandle *transmit_handle
- /* Cancle messages that have not been sent yet */
while (NULL != peer_ctx->pending_messages_head)
{
LOG (GNUNET_ERROR_TYPE_DEBUG,
}
/* If we are still waiting for notification whether this peer is live
* cancel the according task */
- if (NULL != peer_ctx->transmit_handle)
+ if (NULL != peer_ctx->liveliness_check_pending)
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Trying to cancle transmit_handle for peer %s\n",
+ "Removing pending liveliness check for peer %s\n",
GNUNET_i2s (&peer_ctx->peer_id));
- GNUNET_CADET_notify_transmit_ready_cancel (peer_ctx->transmit_handle);
- peer_ctx->transmit_handle = NULL;
+ // TODO wait until cadet sets mq->cancel_impl
+ //GNUNET_MQ_send_cancel (peer_ctx->liveliness_check_pending->ev);
+ GNUNET_free (peer_ctx->liveliness_check_pending);
+ peer_ctx->liveliness_check_pending = NULL;
}
if (NULL != peer_ctx->send_channel)
{
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Destroying send channel\n");
GNUNET_CADET_channel_destroy (peer_ctx->send_channel);
peer_ctx->send_channel = NULL;
}
if (NULL != peer_ctx->recv_channel)
{
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Destroying recv channel\n");
GNUNET_CADET_channel_destroy (peer_ctx->recv_channel);
peer_ctx->recv_channel = NULL;
}
return GNUNET_YES;
}
+
/**
* @brief set flags on a given peer.
*
set_peer_flag (peer_ctx, flags);
}
+
/**
* @brief unset flags on a given peer.
*
unset_peer_flag (peer_ctx, flags);
}
+
/**
* @brief Check whether flags on a peer are set.
*
* @param peer the peer to check the flag of
* @param flags the flags to check
*
- * @return #GNUNET_YES if all given flags are set
- * #GNUNET_NO otherwise
+ * @return #GNUNET_SYSERR if peer is not known
+ * #GNUNET_YES if all given flags are set
+ * #GNUNET_NO otherwise
*/
int
Peers_check_peer_flag (const struct GNUNET_PeerIdentity *peer, enum Peers_PeerFlags flags)
{
struct PeerContext *peer_ctx;
+ if (GNUNET_NO == Peers_check_peer_known (peer))
+ {
+ return GNUNET_SYSERR;
+ }
peer_ctx = get_peer_ctx (peer);
return check_peer_flag_set (peer_ctx, flags);
}
set_channel_flag (channel_flags, flags);
}
+
/**
* @brief unset flags on a given channel.
*
unset_channel_flag (channel_flags, flags);
}
+
/**
* @brief Check whether flags on a channel are set.
*
return check_channel_flag_set (channel_flags, flags);
}
+
/**
* @brief Check whether we have information about the given peer.
*
return GNUNET_CONTAINER_multipeermap_contains (peer_map, peer);
}
+
/**
* @brief Check whether @a peer is actually a peer.
*
return GNUNET_CONTAINER_multipeermap_contains (valid_peers, peer);
}
+
/**
* @brief Indicate that we want to send to the other peer
*
(void) get_channel (peer);
}
+
/**
* @brief Check whether other peer has the intention to send/opened channel
* towars us
return GNUNET_NO;
}
+
/**
* Handle the channel a peer opens to us.
*
Peers_handle_inbound_channel (void *cls,
struct GNUNET_CADET_Channel *channel,
const struct GNUNET_PeerIdentity *initiator,
- uint32_t port,
+ const struct GNUNET_HashCode *port,
enum GNUNET_CADET_ChannelOption options)
{
struct PeerContext *peer_ctx;
return peer_ctx->recv_channel_flags;
}
+
/**
* @brief Check whether a sending channel towards the given peer exists
*
return GNUNET_YES;
}
+
/**
* @brief check whether the given channel is the sending channel of the given
* peer
return GNUNET_NO;
}
+
/**
* @brief Destroy the send channel of a peer e.g. stop indicating a sending
* intention to another peer
(void) Peers_check_connected (peer);
}
-/**
- * @brief Issue a check whether peer is live
- *
- * This tries to establish a channel to the given peer. Once the channel is
- * established successfully, we know the peer is live.
- *
- * @param peer the peer to check liveliness
- */
-void
-Peers_issue_peer_liveliness_check (const struct GNUNET_PeerIdentity *peer)
-{
- struct PeerContext *peer_ctx;
-
- if (0 == GNUNET_CRYPTO_cmp_peer_identity (peer, own_identity))
- {
- return; /* We know that we are online */
- }
-
- peer_ctx = create_or_get_peer_ctx (peer);
- // TODO if LIVE/ONLINE
- check_peer_live (peer_ctx);
-}
-
/**
* @brief Send a message to another peer.
*
struct PendingMessage *pending_msg;
struct GNUNET_MQ_Handle *mq;
- pending_msg = insert_pending_message (peer, ev, "PULL REPLY");
+ pending_msg = insert_pending_message (peer, ev, type);
mq = get_mq (peer);
GNUNET_MQ_notify_sent (ev,
mq_notify_sent_cb,