diff --git a/Makefile.am b/Makefile.am
index add903b1623d0ae4c63482e7f894806bd3ef816e..b0b8e632cd5f889635e605480189e6707da856bc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,6 +8,7 @@ src_coincerd_SOURCES = \
src/crypto.c src/crypto.h \
src/daemon_events.c src/daemon_events.h \
src/daemon_messages.c src/daemon_messages.h \
+ src/daemon_messages_processor.c src/daemon_messages_processor.h \
src/global_state.c src/global_state.h \
src/hosts.c src/hosts.h \
src/json_parser.c src/json_parser.h \
@@ -15,7 +16,9 @@ src_coincerd_SOURCES = \
src/log.c src/log.h \
src/neighbours.c src/neighbours.h \
src/p2p.c src/p2p.h \
- src/paths.c src/paths.h
+ src/paths.c src/paths.h \
+ src/peers.c src/peers.h \
+ src/routing.c src/routing.h
src_coincerd_CFLAGS = $(AM_CFLAGS) $(JANSSON_CFLAGS) $(LIBEVENT_CFLAGS) $(LIBSODIUM_CFLAGS)
src_coincerd_LDADD = $(AM_LDADD) $(JANSSON_LIBS) $(LIBEVENT_LIBS) $(LIBSODIUM_LIBS)
diff --git a/README b/README
deleted file mode 100644
index e5e06d186bddd3efcdc800a2b4fb8a693b937d51..0000000000000000000000000000000000000000
--- a/README
+++ /dev/null
@@ -1,2 +0,0 @@
-Coincer
-=======
diff --git a/README.md b/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..8897953c5e7ad5a12d90dcc9c3e1b98a015bfc0e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,16 @@
+# Coincer
+
+## Terminology
+- Host: A device that is possibly connected to the Coincer network
+- Identifier: The public key of a cryptographic keypair
+- Identity: A peer representation that we own (identifier + its corresponding
+secret key)
+- Message trace: Contains a peer-to-peer message's nonce, a pointer to a
+ neighbour from which we've received the message and the originator's
+ identifier. The message traces are being used for routing loop detection
+- n2n: neighbour-to-neighbour
+- Neighbour: A host that we are interconnected with
+- Nonce: Ever-increasing integer tied to every message sent for message
+ repetiton or replay attack detection
+- p2p: peer-to-peer
+- Peer: The Coincer network participant
diff --git a/src/daemon_events.c b/src/daemon_events.c
index 639a08241f78563fe34465f0f8d0db1e2dac9d05..4307d5db117d21a84fd34d30dbc8dddc00e2c109 100644
--- a/src/daemon_events.c
+++ b/src/daemon_events.c
@@ -25,31 +25,46 @@
#include "log.h"
#include "neighbours.h"
#include "p2p.h"
+#include "peers.h"
+#include "routing.h"
+
+/** The time interval in seconds between loop_update_long calls. */
+#define UPDATE_TIME_LONG 60
+/** The time interval in seconds between loop_update_medium calls. */
+#define UPDATE_TIME_MEDIUM 30
+/** The time interval in seconds between loop_update_short calls. */
+#define UPDATE_TIME_SHORT 10
+
+static void loop_update_long(evutil_socket_t fd __attribute__((unused)),
+ short events __attribute__((unused)),
+ void *ctx);
+static void loop_update_medium(evutil_socket_t fd __attribute__((unused)),
+ short events __attribute__((unused)),
+ void *ctx);
+static void loop_update_short(evutil_socket_t fd __attribute__((unused)),
+ short events __attribute__((unused)),
+ void *ctx);
+
+static void remove_stale_records(global_state_t *global_state);
static void signal_cb(evutil_socket_t signal __attribute__((unused)),
short events __attribute__((unused)),
void *ctx);
/**
- * Actively check the number of neighbours and ask for more if needed.
+ * Check the number of neighbours and ask for more if needed.
*
- * @param fd File descriptor.
- * @param events Event flags.
- * @param ctx Global state.
+ * @param global_state The global state.
*/
-static void conns_cb(int fd __attribute__((unused)),
- short events __attribute__((unused)),
- void *ctx)
+static void connections_maintain(global_state_t *global_state)
{
- global_state_t *global_state;
- int needed_conns;
-
- global_state = (global_state_t *) ctx;
+ int needed_conns;
needed_conns = MIN_NEIGHBOURS -
linkedlist_size(&global_state->neighbours);
if (needed_conns > 0) {
- log_debug("conns_cb - we need %d more neighbours");
+ log_debug("connections_maintain - need %d more neighbours",
+ needed_conns);
/* ask twice more hosts than we need; it's preferable
* to have more neighbours than minimum */
add_more_connections(global_state, 2 * needed_conns);
@@ -96,25 +111,180 @@ int daemon_events_setup(global_state_t *global_state)
return 1;
}
- /* check the number of neighbours every 10 seconds */
- interval.tv_sec = 10;
+ /* register the short time-interval loop updates */
+ interval.tv_sec = UPDATE_TIME_SHORT;
+ interval.tv_usec = 0;
+
+ event = event_new(global_state->event_loop,
+ -1,
+ EV_PERSIST,
+ loop_update_short,
+ global_state);
+ if (!event ||
+ event_add(event, &interval) < 0 ||
+ !linkedlist_append(events, event)) {
+ log_error("Creating or adding short loop update callback");
+ return 1;
+ }
+
+ /* register the medium time-interval loop updates */
+ interval.tv_sec = UPDATE_TIME_MEDIUM;
+ interval.tv_usec = 0;
+
+ event = event_new(global_state->event_loop,
+ -1,
+ EV_PERSIST,
+ loop_update_medium,
+ global_state);
+
+ if (!event ||
+ event_add(event, &interval) < 0 ||
+ !linkedlist_append(events, event)) {
+ log_error("Creating or adding medium loop update callback");
+ return 1;
+ }
+
+ /* register the long time-interval loop updates */
+ interval.tv_sec = UPDATE_TIME_LONG;
interval.tv_usec = 0;
event = event_new(global_state->event_loop,
-1,
EV_PERSIST,
- conns_cb,
+ loop_update_long,
global_state);
+
if (!event ||
event_add(event, &interval) < 0 ||
!linkedlist_append(events, event)) {
- log_error("Creating or adding Connections event");
+ log_error("Creating or adding long loop update callback");
return 1;
}
return 0;
}
+/**
+ * The long time-triggered loop update.
+ *
+ * @param fd File descriptor.
+ * @param events Event flags.
+ * @param ctx Global state.
+ */
+static void loop_update_long(evutil_socket_t fd __attribute__((unused)),
+ short events __attribute__((unused)),
+ void *ctx)
+{
+ global_state_t *global_state = (global_state_t *) ctx;
+
+ remove_stale_records(global_state);
+}
+
+/**
+ * The medium time-triggered loop update.
+ *
+ * @param fd File descriptor.
+ * @param events Event flags.
+ * @param ctx Global state.
+ */
+static void loop_update_medium(evutil_socket_t fd __attribute__((unused)),
+ short events __attribute__((unused)),
+ void *ctx)
+{
+ global_state_t *global_state = (global_state_t *) ctx;
+
+ if (linkedlist_size(&global_state->neighbours) >= MIN_NEIGHBOURS) {
+ send_p2p_route_adv(&global_state->neighbours,
+ global_state->true_identity);
+ }
+}
+
+/**
+ * The short time-triggered loop update.
+ *
+ * @param fd File descriptor.
+ * @param events Event flags.
+ * @param ctx Global state.
+ */
+static void loop_update_short(evutil_socket_t fd __attribute__((unused)),
+ short events __attribute__((unused)),
+ void *ctx)
+{
+ global_state_t *global_state = (global_state_t *) ctx;
+
+ connections_maintain(global_state);
+}
+
+/**
+ * Remove old records from every global state's container.
+ *
+ * @param global_state The global state.
+ */
+static void remove_stale_records(global_state_t *global_state)
+{
+ identity_t *current_identity;
+ linkedlist_t *current_list;
+ neighbour_t *current_neighbour;
+ linkedlist_node_t *current_node;
+ peer_t *current_peer;
+ time_t current_time;
+ linkedlist_node_t *next_node;
+
+ current_time = time(NULL);
+
+ /* if a route is stale, delete it from the routing table */
+ linkedlist_apply_if(&global_state->routing_table,
+ ¤t_time,
+ route_is_stale,
+ route_clear,
+ linkedlist_delete);
+
+ /* remove stale nonces of known peers */
+ current_list = &global_state->peers;
+ current_node = linkedlist_get_first(current_list);
+ while (current_node) {
+ current_peer = (peer_t *) current_node->data;
+
+ nonces_remove_stale(¤t_peer->nonces);
+
+ current_node = linkedlist_get_next(current_list, current_node);
+ }
+
+ /* remove stale nonces of the pseudonyms of our neighbours */
+ current_list = &global_state->neighbours;
+ current_node = linkedlist_get_first(current_list);
+ while (current_node) {
+ current_neighbour = (neighbour_t *) current_node->data;
+
+ nonces_remove_stale(¤t_neighbour->pseudonym.nonces);
+
+ current_node = linkedlist_get_next(current_list, current_node);
+ }
+
+ /* remove stale message traces */
+ linkedlist_apply_if(&global_state->message_traces,
+ ¤t_time,
+ message_trace_is_stale,
+ NULL,
+ linkedlist_delete);
+
+ /* remove unneeded identities */
+ current_list = &global_state->identities;
+ current_node = linkedlist_get_first(current_list);
+ while (current_node) {
+ current_identity = (identity_t *) current_node->data;
+ next_node = linkedlist_get_next(current_list, current_node);
+
+ if (current_identity->flags & IDENTITY_TMP) {
+ send_p2p_bye(&global_state->neighbours,
+ current_identity);
+ linkedlist_delete(current_node);
+ }
+
+ current_node = next_node;
+ }
+}
+
/**
* Callback function for a received signal.
*
diff --git a/src/daemon_messages_processor.c b/src/daemon_messages_processor.c
new file mode 100644
index 0000000000000000000000000000000000000000..3e1168b3d2282a8b9559cad243a58f832f8a2e03
--- /dev/null
+++ b/src/daemon_messages_processor.c
@@ -0,0 +1,693 @@
+/*
+ * Coincer
+ * Copyright (C) 2017-2018 Coincer Developers
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "crypto.h"
+#include "daemon_messages.h"
+#include "daemon_messages_processor.h"
+#include "global_state.h"
+#include "hosts.h"
+#include "json_parser.h"
+#include "log.h"
+#include "neighbours.h"
+#include "routing.h"
+
+/** The minimum time in seconds that has to pass between the announcements of
+ * presence of one identity. */
+#define ADV_GAP_TIME 10
+
+static enum process_message_result
+ process_message(const message_t *message,
+ const char *json_message,
+ neighbour_t *sender,
+ global_state_t *global_state);
+
+static int process_p2p_bye(const message_t *message,
+ const char *json_message,
+ neighbour_t *sender,
+ peer_t *sender_peer,
+ global_state_t *global_state);
+static int process_p2p_hello(const message_t *message,
+ neighbour_t *sender,
+ linkedlist_t *neighbours,
+ unsigned short port);
+static int process_p2p_peers_adv(const message_t *message,
+ neighbour_t *sender,
+ linkedlist_t *hosts);
+static int process_p2p_peers_sol(neighbour_t *sender,
+ const linkedlist_t *hosts);
+static int process_p2p_ping(neighbour_t *sender);
+static int process_p2p_pong(neighbour_t *sender);
+static int process_p2p_route_adv(const message_t *message,
+ const char *json_message,
+ neighbour_t *sender,
+ peer_t *sender_peer,
+ global_state_t *global_state);
+static int process_p2p_route_sol(const message_t *message,
+ const char *json_message,
+ neighbour_t *sender,
+ global_state_t *global_state);
+
+/**
+ * Process a JSON message received from its forwarder/sender (our neighbour).
+ *
+ * @param json_message The received JSON message.
+ * @param sender The message sender/forwarder.
+ * @param global_state The global state.
+ *
+ * @return PMR_DONE The received message was successfully
+ * processed.
+ * @return PMR_ERR_INTEGRITY Someone tampered with the message.
+ * @return PMR_ERR_INTERNAL Internal processing error.
+ * @return PMR_ERR_PARSING Parsing failure.
+ * @return PMR_ERR_SEMANTIC Semantic error.
+ * @return PMR_ERR_VERSION The message is of different
+ * protocol version.
+ */
+enum process_message_result
+ process_encoded_message(const char *json_message,
+ neighbour_t *sender,
+ global_state_t *global_state)
+{
+ char *json_message_body;
+ message_t message;
+ enum process_message_result ret;
+
+ /* decode JSON message; if parsing JSON message into message_t failed */
+ if (decode_message(json_message, &message, &json_message_body)) {
+ log_debug("process_encoded_message - decoding a received "
+ "message has failed. The message:\n%s", json_message);
+ return PMR_ERR_PARSING;
+ }
+
+ if (message.version != PROTOCOL_VERSION) {
+ free(json_message_body);
+ message_delete(&message);
+ return PMR_ERR_VERSION;
+ }
+
+ /* integrity verification part; if the message integrity is violated */
+ if (verify_signature(json_message_body, message.from, message.sig)) {
+ log_warn("Someone tampered with a received message");
+ log_debug("The tampered message:\n%s", json_message);
+ free(json_message_body);
+ message_delete(&message);
+ return PMR_ERR_INTEGRITY;
+ }
+ /* integrity verification done */
+ free(json_message_body);
+
+ /* process the message */
+ ret = process_message(&message, json_message, sender, global_state);
+
+ message_delete(&message);
+ return ret;
+}
+
+/**
+ * Process a message received from its forwarder/sender (our neighbour).
+ *
+ * @param message The received message.
+ * @param json_message The received message in the JSON format.
+ * @param sender The message sender/forwarder.
+ * @param global_state The global state.
+ *
+ * @return PMR_DONE The received message was successfully
+ * processed.
+ * @return PMR_ERR_INTERNAL Internal processing error.
+ * @return PMR_ERR_SEMANTIC Semantic error.
+ */
+static enum process_message_result
+ process_message(const message_t *message,
+ const char *json_message,
+ neighbour_t *sender,
+ global_state_t *global_state)
+{
+ int cmp_val;
+ linkedlist_t *hosts;
+ linkedlist_t *identities;
+ identity_t *identity;
+ const message_body_t *msg_body;
+ const linkedlist_t *msg_traces;
+ enum message_type msg_type;
+ linkedlist_t *neighbours;
+ uint64_t nonce_value;
+ linkedlist_t *peers;
+ int res;
+ linkedlist_t *routing_table;
+ peer_t *sender_peer;
+
+ msg_body = &message->body;
+ msg_type = msg_body->type;
+ nonce_value = msg_body->nonce;
+
+ hosts = &global_state->hosts;
+ identities = &global_state->identities;
+ msg_traces = &global_state->message_traces;
+ neighbours = &global_state->neighbours;
+ peers = &global_state->peers;
+ routing_table = &global_state->routing_table;
+
+ /* if we haven't yet received p2p.hello from the neighbour who's
+ * sent/forwarded the received message */
+ if (!(sender->flags & NEIGHBOUR_ACTIVE)) {
+ /* and the message is not p2p.hello */
+ if (msg_type != P2P_HELLO) {
+ log_debug("process_message - received non-hello msg "
+ "before p2p.hello");
+ return PMR_ERR_SEMANTIC;
+ }
+ res = process_p2p_hello(message,
+ sender,
+ neighbours,
+ global_state->port);
+ if (!res) {
+ nonce_store(&sender->pseudonym.nonces, nonce_value);
+ return PMR_DONE;
+ }
+
+ return PMR_ERR_INTERNAL;
+ }
+
+ /* if we've received a message that we've created */
+ if (identity_find(identities, message->from)) {
+ /* and if it is a p2p message */
+ if (!identifier_empty(msg_body->to)) {
+ routing_loop_remove(routing_table,
+ neighbours,
+ identities,
+ msg_body->to);
+ }
+ return PMR_DONE;
+ }
+
+ /* let's get peer representations of the sender and appropriate ours */
+
+ cmp_val = 0;
+ sender_peer = NULL;
+ /* all bytes of 'to' are set to 0x0 => we are one of the recipients */
+ if (identifier_empty(msg_body->to)) {
+ /* 'from' different from the neighbour's pseudonym =>
+ * the message should be a broadcast (we will check later) */
+ if (memcmp(message->from,
+ sender->pseudonym.identifier,
+ crypto_box_PUBLICKEYBYTES)) {
+ /* process the broadcast with the true identity */
+ identity = global_state->true_identity;
+ /* otherwise it is a n2n message */
+ } else {
+ identity = &sender->my_pseudonym;
+ sender_peer = &sender->pseudonym;
+
+ /* for later parity check */
+ cmp_val = memcmp(message->from,
+ identity->keypair.public_key,
+ crypto_box_PUBLICKEYBYTES);
+ }
+ /* the message is of type p2p */
+ } else {
+ /* identity will be NULL if the message is not meant for us */
+ identity = identity_find(identities, msg_body->to);
+ /* for later parity check */
+ cmp_val = memcmp(message->from,
+ msg_body->to,
+ crypto_box_PUBLICKEYBYTES);
+ }
+
+ /* nonce parity check; the message must have:
+ * sender ID > our ID => odd nonce; sender ID < our ID => even nonce */
+ if ((cmp_val > 0 && !(nonce_value & 0x01)) ||
+ (cmp_val < 0 && (nonce_value & 0x01))) {
+ log_debug("process_mesage - wrong nonce parity");
+ return PMR_ERR_SEMANTIC;
+ }
+
+ /* if the message is not n2n */
+ if (!sender_peer) {
+ /* from what peer is this message? */
+ sender_peer = peer_find(peers, message->from);
+ }
+ /* if we don't know this peer yet */
+ if (!sender_peer) {
+ sender_peer = peer_store(peers, message->from);
+ /* storing the new peer has failed */
+ if (!sender_peer) {
+ return PMR_ERR_INTERNAL;
+ }
+ } else {
+ if (msg_type == P2P_ROUTE_ADV) {
+ /* if it's an old announcement of presence, skip it */
+ if (nonce_value < sender_peer->presence_nonce.value) {
+ return PMR_DONE;
+ }
+ } else {
+ /* have we already processed this message? */
+ if (nonces_find(&sender_peer->nonces, nonce_value)) {
+ /* if broadcast or n2n msg */
+ if (identifier_empty(msg_body->to) ||
+ identity == &sender->my_pseudonym) {
+ return PMR_DONE;
+ }
+ /* check if there's a routing loop */
+ if (routing_loop_detect(msg_traces,
+ sender,
+ nonce_value,
+ message->from)) {
+ routing_loop_remove(routing_table,
+ neighbours,
+ identities,
+ msg_body->to);
+ }
+ return PMR_DONE;
+ }
+ /* if the message smells like replay attack */
+ if (!linkedlist_empty(&sender_peer->nonces) &&
+ nonce_value <
+ (nonces_get_oldest(&sender_peer->nonces))->value) {
+ log_warn("Potential replay attack detected");
+ return PMR_ERR_SEMANTIC;
+ }
+ }
+ }
+
+ /* no corresponding identity to process the message => the message is
+ * not meant for us */
+ if (!identity) {
+ /* if forwarding succeeded */
+ if (!message_forward(message,
+ json_message,
+ sender,
+ global_state)) {
+ /* store message's nonce */
+ nonce_store(&sender_peer->nonces, nonce_value);
+ }
+ return PMR_DONE;
+ } else if (identity->flags & IDENTITY_TMP) {
+ /* no one is supposed to send a message to this identity */
+ return PMR_ERR_SEMANTIC;
+ }
+
+ /* the message is meant for us; process it */
+
+ switch (msg_type) {
+ case P2P_BYE:
+ res = process_p2p_bye(message,
+ json_message,
+ sender,
+ sender_peer,
+ global_state);
+ break;
+ case P2P_HELLO:
+ res = process_p2p_hello(message,
+ sender,
+ neighbours,
+ global_state->port);
+ break;
+ case P2P_PEERS_ADV:
+ /* if the message was not sent by the same peer
+ * pseudonym of the sender as their p2p.hello */
+ if (&sender->pseudonym != sender_peer) {
+ log_debug("process_message - incorrect sender");
+ /* since the message was sent by other ID than
+ * neighbour's pseudonym, we've treated it as a
+ * new peer; that's unwanted for n2n messages */
+ peer_delete(sender_peer);
+ return PMR_ERR_SEMANTIC;
+ }
+ res = process_p2p_peers_adv(message, sender, hosts);
+ break;
+ case P2P_PEERS_SOL:
+ res = process_p2p_peers_sol(sender, hosts);
+ break;
+ case P2P_PING:
+ res = process_p2p_ping(sender);
+ break;
+ case P2P_PONG:
+ if (&sender->pseudonym != sender_peer) {
+ log_debug("process_message - incorrect sender");
+ peer_delete(sender_peer);
+ return PMR_ERR_SEMANTIC;
+ }
+ res = process_p2p_pong(sender);
+ break;
+ case P2P_ROUTE_ADV:
+ res = process_p2p_route_adv(message,
+ json_message,
+ sender,
+ sender_peer,
+ global_state);
+ break;
+ case P2P_ROUTE_SOL:
+ res = process_p2p_route_sol(message,
+ json_message,
+ sender,
+ global_state);
+ break;
+ default:
+ log_debug("process_message - unknown message type");
+ return PMR_ERR_SEMANTIC;
+ }
+
+ /* if the message processing has failed */
+ if (res) {
+ return PMR_ERR_INTERNAL;
+ }
+
+ /* sucessfully processed; store message's nonce */
+ if (msg_type == P2P_ROUTE_ADV) {
+ presence_nonce_store(sender_peer, nonce_value);
+ } else if (msg_type != P2P_BYE) {
+ nonce_store(&sender_peer->nonces, nonce_value);
+ }
+
+ sender->failed_pings = 0;
+
+ return PMR_DONE;
+}
+
+/**
+ * Process p2p.bye.
+ *
+ * @param message Process this message.
+ * @param json_message The received message in the JSON format.
+ * @param sender We've received the message from this neighbour.
+ * @param sender_peer Sender's peer representation.
+ * @param global_state The global state.
+ *
+ * @return 0 Successfully processed.
+ */
+static int process_p2p_bye(const message_t *message,
+ const char *json_message,
+ neighbour_t *sender,
+ peer_t *sender_peer,
+ global_state_t *global_state)
+{
+ route_delete(&global_state->routing_table, message->from);
+ peer_delete(sender_peer);
+
+ message_forward(message, json_message, sender, global_state);
+
+ return 0;
+}
+
+/**
+ * Process p2p.hello.
+ *
+ * @param message Process this message.
+ * @param sender We've received the message from this neighbour.
+ * @param neighbours Our neighbours.
+ * @param port Our listening port.
+ *
+ * @return 0 Successfully processed.
+ * @return 1 Failure.
+ */
+static int process_p2p_hello(const message_t *message,
+ neighbour_t *sender,
+ linkedlist_t *neighbours,
+ unsigned short port)
+{
+ p2p_hello_t *hello;
+ neighbour_t *neighbour;
+
+ /* don't take hello from already active neighbour */
+ if (sender->flags & NEIGHBOUR_ACTIVE) {
+ return 1;
+ }
+
+ /* check self-neighbouring; if we have a neighbour with 'my_pseudonym'
+ * the same as the received message's sender ID, then we've
+ * detected self-neighbouring; delete the mentioned neighbour,
+ * and also delete the message sender (our neighbour representation of
+ * the sending counterparty)
+ */
+ if ((neighbour = find_neighbour(neighbours,
+ message->from,
+ compare_neighbour_my_pseudonyms))) {
+ linkedlist_delete_safely(neighbour->node, clear_neighbour);
+ linkedlist_delete_safely(sender->node, clear_neighbour);
+ return 1;
+ }
+
+ hello = (p2p_hello_t *) message->body.data;
+
+ sender->client = (char *) malloc((strlen(hello->client) + 1) *
+ sizeof(char));
+ if (!sender->client) {
+ log_error("Processing p2p.hello");
+ return 1;
+ }
+ strcpy(sender->client, hello->client);
+
+ /* if the new neighbour is not one of the default hosts */
+ if (sender->host) {
+ sender->host->port = hello->port;
+ }
+
+ memcpy(sender->pseudonym.identifier,
+ message->from,
+ crypto_box_PUBLICKEYBYTES);
+
+ set_neighbour_flags(sender, NEIGHBOUR_ACTIVE);
+
+ return send_p2p_hello(sender, port);
+}
+
+/**
+ * Process p2p.peers.adv.
+ *
+ * @param message Process this message.
+ * @param sender We've received the message from this neighbour.
+ * @param hosts Our known hosts.
+ *
+ * @return 0 Successfully processed.
+ * @return 1 Failure.
+ */
+static int process_p2p_peers_adv(const message_t *message,
+ neighbour_t *sender,
+ linkedlist_t *hosts)
+{
+ struct in6_addr addr;
+ char addr_str[INET6_ADDRSTRLEN];
+ size_t n;
+ p2p_peers_adv_t *peers_adv;
+ unsigned short port;
+ char *pos;
+ char tuple[55]; /* string address = 45 chars,
+ * string port = 5 chars,
+ * ',' + 3x' ' + '\0' = 5 chars,
+ * total = 55 chars
+ */
+
+ /* if we haven't asked this neighbour for addresses */
+ if (!(sender->flags & NEIGHBOUR_ADDRS_REQ)) {
+ log_debug("process_p2p_peers_adv - unwanted addrs arrived");
+ return 1;
+ }
+ unset_neighbour_flags(sender, NEIGHBOUR_ADDRS_REQ);
+
+ peers_adv = (p2p_peers_adv_t *) message->body.data;
+
+ /* if the list doesn't have enough chars to be even empty ("[ ]",
+ * which is 4 chars) */
+ if (strlen(peers_adv->addresses) < 4) {
+ log_debug("process_p2p_peers_adv - wrong addrs format");
+ return 1;
+ }
+
+ /* set initial pointer position; skip the outer list '[' */
+ pos = peers_adv->addresses + 1;
+ /* get position of the first '[' */
+ pos = strchr(pos, '[');
+
+ /* while there is a tuple to be processed */
+ while (pos) {
+ /* get the number of chars between the current '[' and the next
+ * ']', both boundary chars excluding */
+ n = strcspn(++pos, "]");
+
+ /* the tuple can not have more than 54 chars (plus '\0') */
+ if (n > 54) {
+ log_debug("process_p2p_peers_adv - wrong addrs format");
+ return 1;
+ }
+
+ /* copy the chars between '[' and ']', both boundary
+ * chars excluding */
+ strncpy(tuple, pos, n);
+ tuple[n] = '\0';
+
+ /* get string addr and numerical port from the tuple */
+ if (sscanf(tuple, " %[^,], %hu ", addr_str, &port) != 2) {
+ log_debug("process_p2p_peers_adv - wrong addrs format");
+ return 1;
+ }
+
+ /* if conversion of addr_str into addr succeeded */
+ if (inet_pton(AF_INET6, addr_str, &addr) == 1) {
+ save_host(hosts, &addr, port, HOST_AVAILABLE);
+ }
+
+ /* go to next tuple */
+ pos = strchr(pos, '[');
+ }
+
+ return 0;
+}
+
+/**
+ * Process p2p.peers.sol.
+ *
+ * @param sender We've received the message from this neighbour.
+ * @param hosts Hosts known to us.
+ *
+ * @return 0 Successfully processed.
+ * @return 1 Failure.
+ */
+static int process_p2p_peers_sol(neighbour_t *sender, const linkedlist_t *hosts)
+{
+ return send_p2p_peers_adv(sender, hosts);
+}
+
+/**
+ * Process p2p.ping.
+ *
+ * @param sender We've received the message from this neighbour.
+ *
+ * @return 0 Successfully processed.
+ * @return 1 Failure.
+ */
+static int process_p2p_ping(neighbour_t *sender)
+{
+ return send_p2p_pong(sender);
+}
+
+/**
+ * Process p2p.pong.
+ *
+ * @param sender We've received the message from this neighbour.
+ *
+ * @return 0 Successfully processed.
+ */
+static int process_p2p_pong(neighbour_t *sender)
+{
+ sender->failed_pings = 0;
+
+ return 0;
+}
+
+/**
+ * Process p2p.route.adv.
+ *
+ * @param message Process this message.
+ * @param json_message The received message in the JSON format.
+ * @param sender We've received the message from this neighbour.
+ * @param sender_peer Sender's peer representation.
+ * @param global_state The global state.
+ *
+ * @return 0 Successfully processed.
+ * @return 1 Failure.
+ */
+static int process_p2p_route_adv(const message_t *message,
+ const char *json_message,
+ neighbour_t *sender,
+ peer_t *sender_peer,
+ global_state_t *global_state)
+{
+ route_t *route;
+ linkedlist_t *routing_table;
+
+ routing_table = &global_state->routing_table;
+
+ route = route_find(routing_table, message->from);
+ if (!route) {
+ if (!(route = route_add(routing_table, sender_peer, sender))) {
+ log_error("Adding a new route");
+ return 1;
+ }
+ }
+
+ route->last_update = time(NULL);
+
+ /* if we are dealing with the newest p2p.route.adv */
+ if (message->body.nonce > route->destination->presence_nonce.value) {
+ if (route_reset(route, sender)) {
+ log_error("Reseting a route");
+ return 1;
+ }
+ /* otherwise just add another next hop */
+ } else if (route_next_hop_add(route, sender)) {
+ log_error("Adding a new next hop");
+ return 1;
+ }
+
+ message_forward(message, json_message, sender, global_state);
+ return 0;
+}
+
+/**
+ * Process p2p.route.sol.
+ *
+ * @param message Process this message.
+ * @param json_message The received message in the JSON format.
+ * @param sender We've received the message from this neighbour.
+ * @param global_state The global state.
+ *
+ * @return 0 Successfully processed.
+ * @return 1 Failure.
+ */
+static int process_p2p_route_sol(const message_t *message,
+ const char *json_message,
+ neighbour_t *sender,
+ global_state_t *global_state)
+{
+ identity_t *identity;
+ p2p_route_sol_t *route_sol;
+
+ route_sol = (p2p_route_sol_t *) message->body.data;
+ identity = identity_find(&global_state->identities, route_sol->target);
+
+ /* if we wouldn't rebroadcast the p2p.route.sol, and just sent
+ * a p2p.route.adv, it could be obvious the 'target' is us */
+ if (message_forward(message,
+ json_message,
+ sender,
+ global_state)) {
+ return 1;
+ }
+ /* if someone's looking for us */
+ if (identity) {
+ if (difftime(time(NULL),
+ identity->last_adv) > ADV_GAP_TIME) {
+ /* broadcast the identity */
+ return send_p2p_route_adv(
+ &global_state->neighbours,
+ identity);
+ }
+ /* returning 1 means not storing the message's nonce,
+ * and so if we receive this message again, we can
+ * re-check the p2p.route.adv gap time */
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/daemon_messages_processor.h b/src/daemon_messages_processor.h
new file mode 100644
index 0000000000000000000000000000000000000000..9bc0effb467612e69d6374e9c24309c04fea7eea
--- /dev/null
+++ b/src/daemon_messages_processor.h
@@ -0,0 +1,40 @@
+/*
+ * Coincer
+ * Copyright (C) 2017-2018 Coincer Developers
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see .
+ */
+
+#ifndef DAEMON_MESSAGES_PROCESSOR_H
+#define DAEMON_MESSAGES_PROCESSOR_H
+
+#include "global_state.h"
+#include "neighbours.h"
+
+/** Result of message processing. */
+enum process_message_result {
+ PMR_DONE,
+ PMR_ERR_INTEGRITY,
+ PMR_ERR_INTERNAL,
+ PMR_ERR_PARSING,
+ PMR_ERR_SEMANTIC,
+ PMR_ERR_VERSION
+};
+
+enum process_message_result
+ process_encoded_message(const char *json_message,
+ neighbour_t *sender,
+ global_state_t *global_state);
+
+#endif /* DAEMON_MESSAGES_PROCESSOR_H */
diff --git a/src/global_state.c b/src/global_state.c
index d3af9facb2424e2ffc30103ca1a4f72f7fa54e0e..98203fed9f40d01e9af46d7f335327e7c0d26fd2 100644
--- a/src/global_state.c
+++ b/src/global_state.c
@@ -23,6 +23,8 @@
#include "log.h"
#include "neighbours.h"
#include "paths.h"
+#include "peers.h"
+#include "routing.h"
/**
* Clear global state variables.
@@ -31,11 +33,15 @@
*/
void global_state_clear(global_state_t *global_state)
{
+ linkedlist_destroy(&global_state->routing_table, route_clear);
linkedlist_destroy(&global_state->pending_neighbours, clear_neighbour);
+ linkedlist_destroy(&global_state->peers, peer_clear);
linkedlist_destroy(&global_state->neighbours, clear_neighbour);
+ linkedlist_destroy(&global_state->message_traces, NULL);
linkedlist_destroy(&global_state->hosts, NULL);
/* event nodes' data are not malloc'd; just apply the removal */
linkedlist_apply(&global_state->events, event_free, linkedlist_remove);
+ linkedlist_destroy(&global_state->identities, NULL);
clear_paths(&global_state->filepaths);
event_base_free(global_state->event_loop);
@@ -54,10 +60,23 @@ int global_state_init(global_state_t *global_state)
return 1;
}
+ linkedlist_init(&global_state->identities);
+ if (!(global_state->true_identity = identity_generate(0x00))) {
+ log_error("Creating our true identity");
+ return 1;
+ }
+ linkedlist_append(&global_state->identities,
+ global_state->true_identity);
+
linkedlist_init(&global_state->events);
linkedlist_init(&global_state->hosts);
+ linkedlist_init(&global_state->message_traces);
linkedlist_init(&global_state->neighbours);
+ linkedlist_init(&global_state->peers);
linkedlist_init(&global_state->pending_neighbours);
+ linkedlist_init(&global_state->routing_table);
+
+ global_state->port = 0;
return 0;
}
diff --git a/src/global_state.h b/src/global_state.h
index 2054cd7dc7632f52195702ccc0c7d20545245585..9515ae4471ecf753e3ac64f5d5beb2e72162a36b 100644
--- a/src/global_state.h
+++ b/src/global_state.h
@@ -23,6 +23,7 @@
#include "linkedlist.h"
#include "paths.h"
+#include "peers.h"
/**
* Event loop works with the data stored in an instance of this struct.
@@ -36,10 +37,23 @@ typedef struct s_global_state {
filepaths_t filepaths;
/** Linked list of some known hosts in the network. */
linkedlist_t hosts;
+ /** List of our identities. */
+ linkedlist_t identities;
+ /** Linked list of recently received message traces (their hash and
+ * a pointer to a neighbour who's sent us the message). */
+ linkedlist_t message_traces;
/** Linked list of our neighbours. */
linkedlist_t neighbours;
+ /** Known peers. */
+ linkedlist_t peers;
/** Hosts that didn't accept/reject us yet. */
linkedlist_t pending_neighbours;
+ /** We listen on this port. */
+ unsigned short port;
+ /** Routes to hosts. */
+ linkedlist_t routing_table;
+ /** Our true identity. */
+ identity_t *true_identity;
} global_state_t;
void global_state_clear(global_state_t *global_state);
diff --git a/src/hosts.c b/src/hosts.c
index 8df6eb479d9eece7afa7d49e60c97b31e4501325..ff56f278adb27e0d56e94151f0db83e0c02b2e1d 100644
--- a/src/hosts.c
+++ b/src/hosts.c
@@ -16,6 +16,8 @@
* along with this program. If not, see .
*/
+#define _BSD_SOURCE /* snprintf */
+
#include
#include
#include
@@ -27,17 +29,6 @@
#include "linkedlist.h"
#include "log.h"
-/**
- * Delete all hosts and their data.
- *
- * @param hosts Linkedlist of hosts.
- */
-void clear_hosts(linkedlist_t *hosts)
-{
- /* host_t has no dynamically allocated variables */
- linkedlist_destroy(hosts, NULL);
-}
-
/**
* Fetch hosts from file into linkedlist.
*
@@ -49,7 +40,7 @@ void clear_hosts(linkedlist_t *hosts)
*/
int fetch_hosts(const char *hosts_path, linkedlist_t *hosts)
{
- struct in6_addr addr;
+ host_t current_host;
FILE *hosts_file;
hosts_file = fopen(hosts_path, "rb");
@@ -59,8 +50,11 @@ int fetch_hosts(const char *hosts_path, linkedlist_t *hosts)
return 1;
}
- while (fread(&addr, sizeof(struct in6_addr), 1, hosts_file) == 1) {
- save_host(hosts, &addr);
+ while (fread(¤t_host, sizeof(host_t), 1, hosts_file) == 1) {
+ save_host(hosts,
+ ¤t_host.addr,
+ current_host.port,
+ current_host.flags);
}
fclose(hosts_file);
@@ -144,64 +138,118 @@ host_t *find_host(const linkedlist_t *hosts,
}
/**
- * '\n' separated output string of host addresses in readable form.
+ * Fetch output string of hosts. For every host get its address in readable
+ * form and its port. Output format: [ [ addr, port ], ... ].
*
- * @param hosts List of hosts.
- * @param output Output string.
+ * @param hosts List of hosts.
+ * @param output '\0' terminated, dynamically allocated, output
+ * string of hosts.
*
- * @return 0 Success.
- * @return 1 Failure.
+ * @return 0 Success.
+ * @return 1 Failure.
*/
int hosts_to_str(const linkedlist_t *hosts, char **output)
{
- linkedlist_node_t *current_node;
+ const char *closing = " ]";
host_t *current_host;
- size_t output_size = 0;
+ linkedlist_node_t *current_node;
+ char line[64];
+ const char *opening = "[ ";
+ size_t opening_len;
+ size_t output_buff_size;
+ size_t output_size;
+ const char *separator = ", ";
+ size_t separator_len;
char text_ip[INET6_ADDRSTRLEN];
+ char *tmp_out;
+
+ opening_len = strlen(opening);
+ separator_len = strlen(separator);
- /* TODO: Don't assume any buffer size. This will be fixed in the
- * next commit. */
- *output = (char *) malloc(4096 * sizeof(char));
+ output_size = 0;
+ output_buff_size = 1024;
+
+ *output = (char *) malloc(output_buff_size * sizeof(char));
if (!*output) {
- log_error("Hosts to string");
+ log_error("Allocating string of hosts");
return 1;
}
- *output[0] = '\0';
+ (*output)[output_size++] = '\0';
+
+ /* opening of the outer list */
+ strcat(*output, opening);
+ output_size += opening_len;
+
current_node = linkedlist_get_first(hosts);
- while (current_node != NULL) {
+ while (current_node) {
current_host = (host_t *) current_node->data;
+ /* IPv6 can have max 45 chars, port 5, both opening and closing
+ * of the inner list 2 each, the separator 2, possible closing
+ * of the outer list 2, and '\0' 1 => max 59 chars, hence 128
+ * has to be enough as a buffer size reserve. */
+ if (output_buff_size - output_size < 128) {
+ output_buff_size *= 2;
+
+ tmp_out = (char *) realloc(*output, output_buff_size *
+ sizeof(char));
+ if (!tmp_out) {
+ log_error("Reallocating string of hosts");
+ free(output);
+ return 1;
+ }
+ *output = tmp_out;
+ }
+
/* binary ip to text ip conversion */
inet_ntop(AF_INET6,
¤t_host->addr,
text_ip,
INET6_ADDRSTRLEN);
- output_size += strlen(text_ip);
- strcat(*output, text_ip);
current_node = linkedlist_get_next(hosts, current_node);
- /* if it's not the last host, append '\n' */
- if (current_node != NULL) {
- *output[output_size++] = '\n';
+
+ /* [ addr, port ] into 'line', 64 has to be enough from
+ * the reason described above */
+ if (snprintf(line, 64, "%s%s%s%hu%s", opening,
+ text_ip,
+ separator,
+ current_host->port,
+ closing) <= 59) {
+
+ strcat(*output, line);
+ output_size += strlen(line);
+ /* not the last host => append inner lists separator */
+ if (current_node) {
+ strcat(*output, separator);
+ output_size += separator_len;
+ }
}
- *output[output_size] = '\0';
}
+ /* closing of the outer list */
+ strcat(*output, closing);
+
return 0;
}
/**
* Save new host into sorted linkedlist of hosts.
*
- * @param hosts The linkedlist of hosts.
- * @param addr Address of the new host.
+ * @param hosts The linkedlist of hosts.
+ * @param addr Address of the new host.
+ * @param port Host's listening port.
+ * @param flags Host's flags.
*
- * @return host_t Newly saved host.
- * @return NULL Host is already saved, default or
- * allocation failure.
+ * @return host_t Newly saved host.
+ * @return NULL Host is already saved, default or
+ * allocation failure.
*/
-host_t *save_host(linkedlist_t *hosts, const struct in6_addr *addr)
+host_t *save_host(linkedlist_t *hosts,
+ const struct in6_addr *addr,
+ unsigned short port,
+ int flags)
{
int cmp_value;
struct in6_addr curr_addr;
@@ -230,7 +278,9 @@ host_t *save_host(linkedlist_t *hosts, const struct in6_addr *addr)
/* initialize all attributes of the new host */
memcpy(&new_host->addr, addr, 16);
- set_host_flags(new_host, HOST_AVAILABLE);
+ new_host->port = port;
+ new_host->flags = 0x0;
+ set_host_flags(new_host, flags);
/* get textual representation of 'addr' */
inet_ntop(AF_INET6, addr, text_ip, INET6_ADDRSTRLEN);
@@ -330,8 +380,8 @@ int store_hosts(const char *hosts_path, const linkedlist_t *hosts)
while (current) {
current_host = (host_t *) current->data;
/* if fwrite fails, terminate storing */
- if (fwrite(¤t_host->addr,
- sizeof(struct in6_addr),
+ if (fwrite(current_host,
+ sizeof(host_t),
1,
hosts_file) != 1) {
log_error("Storing hosts");
diff --git a/src/hosts.h b/src/hosts.h
index 4996e16ffc8101dc390295ac02fee2029cc68f9e..b2793f1100c68f2646b9cabca3a170895f94705f 100644
--- a/src/hosts.h
+++ b/src/hosts.h
@@ -56,11 +56,11 @@ typedef struct s_host {
struct in6_addr addr;
/** A set of flags for this host. */
int flags;
+ /* Host's listening port. */
+ unsigned short port;
/* TODO: add uptime */
} host_t;
-void clear_hosts(linkedlist_t *hosts);
-
int fetch_hosts(const char *hosts_path, linkedlist_t *hosts);
int fetch_specific_hosts(const linkedlist_t *hosts,
@@ -74,7 +74,10 @@ int hosts_to_str(const linkedlist_t *hosts, char **output);
void reset_hosts_availability(linkedlist_t *hosts);
-host_t *save_host(linkedlist_t *hosts, const struct in6_addr *addr);
+host_t *save_host(linkedlist_t *hosts,
+ const struct in6_addr *addr,
+ unsigned short port,
+ int flags);
void set_host_flags(host_t *host, int flags);
diff --git a/src/json_parser.c b/src/json_parser.c
index 88d5359ac8f435be284e0bd335ca68bff6639494..70d93f12af4048b773f5258e8c406a762ebe1db2 100644
--- a/src/json_parser.c
+++ b/src/json_parser.c
@@ -29,13 +29,16 @@
/**
* Decode a JSON message (including its JSON body) into daemon message.
*
- * @param json_message Decode this JSON message.
- * @param message Store decoded data in here.
+ * @param json_message Decode this JSON message.
+ * @param message Store decoded data in here.
+ * @param json_message_body Store the message's JSON body in here.
*
- * @return 0 Decoding successful.
- * @return 1 Failure.
+ * @return 0 Decoding successful.
+ * @return 1 Failure.
*/
-int decode_message(const char *json_message, message_t *message)
+int decode_message(const char *json_message,
+ message_t *message,
+ char **json_message_body)
{
char from_hex[65];
char to_hex[65];
diff --git a/src/json_parser.h b/src/json_parser.h
index 21c5635e238cbee49d30b7b8d8d40543cd1358c7..36b53663431c8dca0c8c291c657b2baadcaef3e9 100644
--- a/src/json_parser.h
+++ b/src/json_parser.h
@@ -34,7 +34,9 @@ static const char *msg_type_str[] = {
"p2p.route.sol"
};
-int decode_message(const char *json_message, message_t *message);
+int decode_message(const char *json_message,
+ message_t *message,
+ char **json_message_body);
int decode_message_body(const char *json_body, message_body_t *body);
int encode_message(const message_t *message, char **json_message);
int encode_message_body(const message_body_t *body, char **json_body);
diff --git a/src/neighbours.c b/src/neighbours.c
index 5dbebc764717093c203e98170a8d542cc05efde1..08c58bbeab8249e4dc41e0903897a3bb552538b0 100644
--- a/src/neighbours.c
+++ b/src/neighbours.c
@@ -19,13 +19,16 @@
#include
#include
#include
+#include
#include
#include
#include
+#include "crypto.h"
#include "linkedlist.h"
#include "log.h"
#include "neighbours.h"
+#include "peers.h"
/**
* Add new neighbour into neighbours.
@@ -62,9 +65,21 @@ neighbour_t *add_new_neighbour(linkedlist_t *neighbours,
/* initialize new neighbour */
memcpy(&new_neighbour->addr, addr, sizeof(struct in6_addr));
new_neighbour->buffer_event = bev;
+ new_neighbour->client = NULL;
new_neighbour->failed_pings = 0;
new_neighbour->flags = 0x0;
new_neighbour->host = NULL;
+ /* create our pseudonym */
+ generate_keypair(&new_neighbour->my_pseudonym.keypair);
+ new_neighbour->my_pseudonym.nonce_value = get_random_uint64_t() >> 1;
+ /* initialize neighbour's pseudonym */
+ memset(new_neighbour->pseudonym.identifier,
+ 0x0,
+ crypto_box_PUBLICKEYBYTES);
+ linkedlist_init(&new_neighbour->pseudonym.nonces);
+ memset(&new_neighbour->pseudonym.presence_nonce,
+ 0x0,
+ sizeof(nonce_t));
if (!(new_neighbour->node = linkedlist_append(neighbours,
new_neighbour))) {
@@ -84,6 +99,10 @@ neighbour_t *add_new_neighbour(linkedlist_t *neighbours,
void clear_neighbour(neighbour_t *neighbour)
{
bufferevent_free(neighbour->buffer_event);
+ if (neighbour->client) {
+ free(neighbour->client);
+ }
+ peer_clear(&neighbour->pseudonym);
}
/**
@@ -117,6 +136,42 @@ int compare_neighbour_bufferevents(const neighbour_t *neighbour,
return neighbour->buffer_event != bev;
}
+/**
+ * Comparing function between our neighbour-specific pseudonym and a public key.
+ *
+ * @param neighbour Use this neighbour.
+ * @param public_key Compare to this public key.
+ *
+ * @return 0 The public keys equal.
+ * @return <0 'public_key' is greater.
+ * @return >0 The pseudonym's public key is greater.
+ */
+int compare_neighbour_my_pseudonyms(const neighbour_t *neighbour,
+ unsigned char *public_key)
+{
+ return memcmp(neighbour->my_pseudonym.keypair.public_key,
+ public_key,
+ crypto_box_PUBLICKEYBYTES);
+}
+
+/**
+ * Comparing function between neighbour's pseudonym and a public key.
+ *
+ * @param neighbour Use this neighbour.
+ * @param public_key Compare to this public key.
+ *
+ * @return 0 The public keys equal.
+ * @return <0 'public_key' is greater.
+ * @return >0 The pseudonym's public key is greater.
+ */
+int compare_neighbour_pseudonyms(const neighbour_t *neighbour,
+ unsigned char *public_key)
+{
+ return memcmp(neighbour->pseudonym.identifier,
+ public_key,
+ crypto_box_PUBLICKEYBYTES);
+}
+
/**
* Fetch pointers to neighbours with specific flags set, into an array
* that is being allocated in here.
diff --git a/src/neighbours.h b/src/neighbours.h
index 1757884893fe8a561f9c890a9b9c186a97ceecc0..332833b21b2f56e0ee1682670d5138b251b6a1e3 100644
--- a/src/neighbours.h
+++ b/src/neighbours.h
@@ -25,9 +25,13 @@
#include "hosts.h"
#include "linkedlist.h"
+#include "peers.h"
/** Request for addresses. */
#define NEIGHBOUR_ADDRS_REQ 0x01
+/** Practically, this means that we've received p2p.hello from this
+ * neighbour (and so we know their pseudonym). */
+#define NEIGHBOUR_ACTIVE 0x02
/** Data type for the linkedlist of neighbours. */
typedef struct s_neighbour {
@@ -36,14 +40,20 @@ typedef struct s_neighbour {
struct in6_addr addr;
/** Bufferevent belonging to this neighbour. */
struct bufferevent *buffer_event;
+ /** Client info. */
+ char *client;
/** Number of failed ping attempts -- max 3, then disconnect. */
size_t failed_pings;
/** A set of flags for this neighbour. */
int flags;
/** Corresponding host. */
host_t *host;
+ /** Our peer pseudonym for this neighbour. */
+ identity_t my_pseudonym;
/** Neighbour's node in the neighbours container. */
linkedlist_node_t *node;
+ /** Neighbour's peer pseudonym for us. */
+ peer_t pseudonym;
} neighbour_t;
neighbour_t *add_new_neighbour(linkedlist_t *neighbours,
@@ -58,6 +68,11 @@ int compare_neighbour_addrs(const neighbour_t *neighbour,
int compare_neighbour_bufferevents(const neighbour_t *neighbour,
const struct bufferevent *bev);
+int compare_neighbour_my_pseudonyms(const neighbour_t *neighbour,
+ unsigned char *public_key);
+
+int compare_neighbour_pseudonyms(const neighbour_t *neighbour,
+ unsigned char *public_key);
int fetch_specific_neighbours(const linkedlist_t *neighbours,
neighbour_t ***output,
diff --git a/src/p2p.c b/src/p2p.c
index f0983c9596a2112a458de1f31f11b6f1bca30668..512c1a8bc033b11d8183028954adfc8992ba69d0 100644
--- a/src/p2p.c
+++ b/src/p2p.c
@@ -16,8 +16,6 @@
* along with this program. If not, see .
*/
-#define _POSIX_SOURCE /* strtok_r */
-
#include /* inet_ntop */
#include
#include
@@ -30,12 +28,14 @@
#include
#include "crypto.h"
+#include "daemon_messages_processor.h"
#include "global_state.h"
#include "hosts.h"
#include "linkedlist.h"
#include "log.h"
#include "neighbours.h"
#include "p2p.h"
+#include "routing.h"
/**
* Simple helper for conversion of binary IP to readable IP address.
@@ -48,50 +48,6 @@ static void ip_to_string(const struct in6_addr *binary_ip, char *ip)
inet_ntop(AF_INET6, binary_ip, ip, INET6_ADDRSTRLEN);
}
-/**
- * Ask a neighbour for a list of addresses.
- *
- * @param global_state Data for the event loop to work with.
- * @param neighbour The neighbour to be asked.
- */
-static void ask_for_addresses(neighbour_t *neighbour)
-{
- struct bufferevent *bev;
-
- if (!neighbour || !(bev = neighbour->buffer_event)) {
- return;
- }
-
- /* send message "hosts" to the neighbour, as a request
- * for the list of hosts; 6 is the number of bytes to be transmitted */
- evbuffer_add(bufferevent_get_output(bev), "hosts", 6);
- /* accept addresses only from those neighbours that we've asked */
- set_neighbour_flags(neighbour, NEIGHBOUR_ADDRS_REQ);
-}
-
-/**
- * Process received list of host addresses.
- *
- * @param global_state Data for the event loop to work with.
- * @param hosts '\n'-separated list of host addresses.
- */
-static void process_hosts(global_state_t *global_state, char *hosts)
-{
- struct in6_addr addr;
- const char delim[2] = "\n";
- char *line;
- char *save_ptr;
-
- line = strtok_r(hosts, delim, &save_ptr);
-
- while (line) {
- if (inet_pton(AF_INET6, line, &addr) == 1) {
- save_host(&global_state->hosts, &addr);
- }
- line = strtok_r(NULL, delim, &save_ptr);
- }
-}
-
/**
* Processing a P2P message.
*
@@ -100,14 +56,11 @@ static void process_hosts(global_state_t *global_state, char *hosts)
*/
static void p2p_process(struct bufferevent *bev, void *ctx)
{
- char *addrs = NULL;
global_state_t *global_state;
struct evbuffer *input;
size_t len;
- char *message;
+ char *json_message;
neighbour_t *neighbour;
- struct evbuffer *output;
- char response[2048]; /* TODO: adjust size */
char text_ip[INET6_ADDRSTRLEN];
global_state = (global_state_t *) ctx;
@@ -118,67 +71,37 @@ static void p2p_process(struct bufferevent *bev, void *ctx)
compare_neighbour_bufferevents);
assert(neighbour != NULL);
- /* reset neighbour's failed pings */
- neighbour->failed_pings = 0;
-
- /* read from the input buffer, write to output buffer */
- input = bufferevent_get_input(bev);
- output = bufferevent_get_output(bev);
+ /* read from the input buffer */
+ input = bufferevent_get_input(bev);
/* get length of the input message */
len = evbuffer_get_length(input);
/* allocate memory for the input message including '\0' */
- message = (char *) malloc((len + 1) * sizeof(char));
- if (!message) {
+ json_message = (char *) malloc((len + 1) * sizeof(char));
+ if (!json_message) {
log_error("Received message allocation");
return;
}
/* drain input buffer into data; -1 if evbuffer_remove failed */
- if (evbuffer_remove(input, message, len) == -1) {
- free(message);
+ if (evbuffer_remove(input, json_message, len) == -1) {
+ free(json_message);
return;
} else {
- message[len] = '\0';
+ json_message[len] = '\0';
}
ip_to_string(&neighbour->addr, text_ip);
- log_debug("p2p_process - received: %s from %s", message, text_ip);
-
- response[0] = '\0';
-
- /* TODO: Replace with JSON messages */
- if (strcmp(message, "ping") == 0) {
- strcpy(response, "pong");
- /* ignore "pong" */
- } else if (strcmp(message, "pong") == 0) {
- /* "hosts" is a request for list of addresses */
- } else if (strcmp(message, "hosts") == 0) {
- hosts_to_str(&global_state->hosts, &addrs);
- /* list of addresses */
- } else {
- if (neighbour->flags & NEIGHBOUR_ADDRS_REQ) {
- process_hosts(global_state, message);
- unset_neighbour_flags(neighbour, NEIGHBOUR_ADDRS_REQ);
- }
- }
+ log_debug("p2p_process - received: %s from %s", json_message, text_ip);
- if (response[0] != '\0') {
- log_debug("p2p_process - responding with: %s", response);
- /* copy response to the output buffer */
- evbuffer_add_printf(output, "%s", response);
- /* note: this will be replaced in the next commit */
- } else if (addrs) {
- log_debug("p2p_process - responding with: %s", addrs);
- /* copy response to the output buffer */
- evbuffer_add_printf(output, "%s", addrs);
-
- free(addrs);
+ if (process_encoded_message(json_message,
+ neighbour,
+ global_state) != PMR_DONE) {
+ log_warn("Message processing has failed");
}
- /* deallocate input message */
- free(message);
+ free(json_message);
}
/**
@@ -186,9 +109,11 @@ static void p2p_process(struct bufferevent *bev, void *ctx)
*
* @param neighbours Linked list of neighbours.
* @param neighbour Timeout invoked on this neighbour.
+ * @param routing_table Routing table.
*/
static void timeout_process(linkedlist_t *neighbours,
- neighbour_t *neighbour)
+ neighbour_t *neighbour,
+ linkedlist_t *routing_table)
{
char text_ip[INET6_ADDRSTRLEN];
@@ -204,14 +129,12 @@ static void timeout_process(linkedlist_t *neighbours,
log_debug("timeout_process - sending ping to %s. "
"Failed pings: %lu", text_ip,
neighbour->failed_pings);
- /* send ping to the inactive neighbour;
- * 5 is the length of bytes to be transmitted */
- evbuffer_add(bufferevent_get_output(neighbour->buffer_event),
- "ping", 5);
-
+ send_p2p_ping(neighbour);
neighbour->failed_pings++;
} else {
log_info("%s timed out", text_ip);
+ /* remove this neighbour (next hop) from all routes */
+ routing_table_remove_next_hop(routing_table, neighbour);
linkedlist_delete_safely(neighbour->node, clear_neighbour);
}
}
@@ -246,13 +169,17 @@ static void process_pending_neighbour(global_state_t *global_state,
/* move the neighbour from pending into neighbours */
linkedlist_move(neighbour->node, &global_state->neighbours);
+ /* send p2p.hello */
+ send_p2p_hello(neighbour, global_state->port);
+
needed_conns = MIN_NEIGHBOURS -
linkedlist_size(&global_state->neighbours);
/* if we need more neighbours */
if (needed_conns > 0) {
/* and we don't have enough hosts available */
if (available_hosts_size < needed_conns) {
- ask_for_addresses(neighbour);
+ /* ask for some */
+ send_p2p_peers_sol(neighbour);
}
}
/* connecting to the neighbour was unsuccessful */
@@ -282,13 +209,23 @@ static void process_neighbour(global_state_t *global_state,
if (events & BEV_EVENT_ERROR) {
log_info("Connection error, removing %s", text_ip);
+
+ /* remove this neighbour (next hop) from all routes */
+ routing_table_remove_next_hop(&global_state->routing_table,
+ neighbour);
linkedlist_delete_safely(neighbour->node, clear_neighbour);
} else if (events & BEV_EVENT_EOF) {
log_info("%s disconnected", text_ip);
+
+ /* remove this neighbour (next hop) from all routes */
+ routing_table_remove_next_hop(&global_state->routing_table,
+ neighbour);
linkedlist_delete_safely(neighbour->node, clear_neighbour);
/* timeout flag on 'bev' */
} else if (events & BEV_EVENT_TIMEOUT) {
- timeout_process(&global_state->neighbours, neighbour);
+ timeout_process(&global_state->neighbours,
+ neighbour,
+ &global_state->routing_table);
}
}
@@ -341,6 +278,7 @@ static void accept_connection(struct evconnlistener *listener,
neighbour_t *neigh;
struct in6_addr *new_addr;
host_t *host;
+ unsigned short port;
char text_ip[INET6_ADDRSTRLEN];
struct timeval timeout;
@@ -349,6 +287,7 @@ static void accept_connection(struct evconnlistener *listener,
/* put binary representation of IP to 'new_addr' */
new_addr = &addr_in6->sin6_addr;
+ port = addr_in6->sin6_port;
ip_to_string(new_addr, text_ip);
@@ -390,7 +329,7 @@ static void accept_connection(struct evconnlistener *listener,
log_info("New connection from [%s]:%d", text_ip,
ntohs(addr_in6->sin6_port));
- host = save_host(&global_state->hosts, new_addr);
+ host = save_host(&global_state->hosts, new_addr, port, 0x0);
if (!host) {
host = find_host(&global_state->hosts, new_addr);
}
@@ -441,7 +380,7 @@ int listen_init(struct evconnlistener **listener,
struct event_base **base = &global_state->event_loop;
struct sockaddr_in6 sock_addr;
- int port = DEFAULT_PORT;
+ int port = global_state->port = DEFAULT_PORT;
*base = event_base_new();
if (!*base) {
@@ -473,18 +412,20 @@ int listen_init(struct evconnlistener **listener,
}
/**
- * Attempt to connect to a particular addr.
+ * Attempt to connect to a particular host.
*
* @param global_state Data for the event loop to work with.
* @param addr Binary IP of a host that we want to connect to.
+ * @param port Host's listening port.
*
* @return 0 The connection attempt was successful.
* @return 1 The host is already our neighbour.
* @return 2 The host is pending to become our neighbour.
* @return 3 Adding new pending neighbour unsuccessful.
*/
-int connect_to_addr(global_state_t *global_state,
- const struct in6_addr *addr)
+int connect_to_host(global_state_t *global_state,
+ const struct in6_addr *addr,
+ unsigned short port)
{
struct bufferevent *bev;
host_t *host;
@@ -516,8 +457,8 @@ int connect_to_addr(global_state_t *global_state,
memset(&sock_addr6, 0x0, sizeof(sock_addr6));
sock_addr6.sin6_family = AF_INET6;
- sock_addr6.sin6_port = htons(DEFAULT_PORT);
- memcpy(&sock_addr6.sin6_addr, addr, 16);
+ sock_addr6.sin6_port = htons(port);
+ memcpy(&sock_addr6.sin6_addr, addr, sizeof(struct in6_addr));
sock_addr = (struct sockaddr *) &sock_addr6;
sock_len = sizeof(sock_addr6);
@@ -603,14 +544,14 @@ void add_more_connections(global_state_t *global_state, size_t conns_amount)
"connecting to a default host...");
/* choose random default host */
idx = get_random_uint32_t(DEFAULT_HOSTS_SIZE);
- memcpy(&addr, DEFAULT_HOSTS[idx], 16);
+ memcpy(&addr, DEFAULT_HOSTS[idx], sizeof(struct in6_addr));
/* if the host becomes our neighbour, and we need more
* connections, get a list of hosts from them and attempt to
* connect to them; it's our goal to use as few default
* hosts as possible
*/
- result = connect_to_addr(global_state, &addr);
+ result = connect_to_host(global_state, &addr, DEFAULT_PORT);
/* the host is already our neighbour; ask them for more addrs */
if (result == 1) {
@@ -618,7 +559,7 @@ void add_more_connections(global_state_t *global_state, size_t conns_amount)
&addr,
compare_neighbour_addrs);
assert(neigh != NULL);
- ask_for_addresses(neigh);
+ send_p2p_peers_sol(neigh);
log_debug("add_more_connections - "
"asking for hosts");
}
@@ -635,8 +576,9 @@ void add_more_connections(global_state_t *global_state, size_t conns_amount)
idx = cur_conn_attempts;
selected_host = available_hosts[idx];
/* perform a connection attempt */
- connect_to_addr(global_state,
- &selected_host->addr);
+ connect_to_host(global_state,
+ &selected_host->addr,
+ selected_host->port);
cur_conn_attempts++;
}
}
diff --git a/src/p2p.h b/src/p2p.h
index 232d1e860f178c5dd0c52a49c39b8a5ffd4af73a..0f3acaf0ad8aa28fac09215f40980fd760612250 100644
--- a/src/p2p.h
+++ b/src/p2p.h
@@ -33,8 +33,9 @@
void add_more_connections(global_state_t *global_state, size_t conns_amount);
-int connect_to_addr(global_state_t *global_state,
- const struct in6_addr *addr);
+int connect_to_host(global_state_t *global_state,
+ const struct in6_addr *addr,
+ unsigned short port);
int listen_init(struct evconnlistener **listener,
global_state_t *global_state);
diff --git a/src/peers.c b/src/peers.c
new file mode 100644
index 0000000000000000000000000000000000000000..abea46ce4d4e254a0107a63bd6079f59d503940a
--- /dev/null
+++ b/src/peers.c
@@ -0,0 +1,391 @@
+/*
+ * Coincer
+ * Copyright (C) 2017-2018 Coincer Developers
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "crypto.h"
+#include "linkedlist.h"
+#include "log.h"
+#include "peers.h"
+
+/**
+ * Determine whether an identifier is empty (if all its bytes are set to 0x0).
+ *
+ * @param id Check this identifier.
+ *
+ * @return 1 The identifier is empty.
+ * @return 0 The identifier is not empty.
+ */
+int identifier_empty(const unsigned char id[crypto_box_PUBLICKEYBYTES])
+{
+ return id[0] == 0x0 && !memcmp(id,
+ id + 1,
+ crypto_box_PUBLICKEYBYTES - 1);
+}
+
+/**
+ * Find the identity for an identifier.
+ *
+ * @param identities List of our identities.
+ * @param identifier Search for this identifier.
+ *
+ * @return identity_t Corresponding identity for the
+ * identifier.
+ * @return NULL The identifier doesn't belong to any
+ * of our identities.
+ */
+identity_t *identity_find(const linkedlist_t *identities,
+ const unsigned char *identifier)
+{
+ identity_t *identity;
+ const linkedlist_node_t *node;
+
+ node = linkedlist_get_first(identities);
+ while (node) {
+ identity = (identity_t *) node->data;
+
+ /* if the identifier represents one of our identities */
+ if (!memcmp(identity->keypair.public_key,
+ identifier,
+ crypto_box_PUBLICKEYBYTES)) {
+ return identity;
+ }
+
+ node = linkedlist_get_next(identities, node);
+ }
+
+ return NULL;
+}
+
+/**
+ * Set flags on given identity.
+ *
+ * @param identity Set flags on this identity.
+ * @param flags Set these flags on the identity.
+ */
+void identity_flags_set(identity_t *identity, int flags)
+{
+ identity->flags |= flags;
+}
+
+/**
+ * Unset flags on given identity.
+ *
+ * @param identity Unset flags on this identity.
+ * @param flags Unset these flags on the identity.
+ */
+void identity_flags_unset(identity_t *identity, int flags)
+{
+ identity->flags &= ~flags;
+}
+
+/**
+ * Generate an identity, including its initial nonce value.
+ *
+ * @param flags The flags of the identity.
+ *
+ * @return identity_t Dynamically allocated identity.
+ * @return NULL Allocation failure.
+ */
+identity_t *identity_generate(int flags)
+{
+ identity_t *identity;
+
+ identity = (identity_t *) malloc(sizeof(identity_t));
+ if (!identity) {
+ log_error("Creating a new identity");
+ return NULL;
+ }
+
+ generate_keypair(&identity->keypair);
+ identity->nonce_value = get_random_uint64_t();
+ identity->last_adv = 0;
+ identity->flags = 0x0;
+ identity_flags_set(identity, flags);
+
+ return identity;
+}
+
+/**
+ * Nonce staleness predicate.
+ *
+ * @param nonce Check staleness of this nonce.
+ * @param current_time The time to compare to.
+ *
+ * @return 1 The nonce is stale.
+ * @return 0 The nonce is not stale.
+ */
+int nonce_is_stale(const nonce_t *nonce, const time_t current_time)
+{
+ return difftime(current_time, nonce->creation) >= NONCE_STALE_TIME;
+}
+
+/**
+ * Store a nonce into ascendingly sorted (by nonce value) container of nonces.
+ *
+ * @param nonces The nonces container.
+ * @param value The nonce's value.
+ *
+ * @return 0 Successfully stored.
+ * @return 1 Failure.
+ */
+int nonce_store(linkedlist_t *nonces, uint64_t value)
+{
+ linkedlist_node_t *cur_node;
+ nonce_t *cur_nonce;
+ nonce_t *nonce;
+
+ nonce = (nonce_t *) malloc(sizeof(nonce_t));
+ if (!nonce) {
+ log_error("Creating a nonce");
+ return 1;
+ }
+
+ nonce->value = value;
+ nonce->creation = time(NULL);
+
+ /* place the nonce into appropriate position */
+ cur_node = linkedlist_get_last(nonces);
+ while (cur_node) {
+ cur_nonce = (nonce_t *) cur_node->data;
+ /* appropriate position found */
+ if (value > cur_nonce->value) {
+ if (cur_node != linkedlist_get_last(nonces)) {
+ nonce->creation = cur_nonce->creation;
+ }
+
+ if (!linkedlist_insert_after(nonces, cur_node, nonce)) {
+ log_error("Storing a nonce");
+ free(nonce);
+ return 1;
+ }
+
+ return 0;
+ }
+
+ cur_node = linkedlist_get_prev(nonces, cur_node);
+ }
+
+ /* at this point we know the nonce should be placed at the beginning
+ * of the container; do that only if the container is empty */
+ if (!linkedlist_empty(nonces) ||
+ !linkedlist_insert_after(nonces, &nonces->first, nonce)) {
+ log_error("Storing a nonce");
+ free(nonce);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Find nonce by its value in a list of nonces.
+ *
+ * @param nonces Choose the nonce from these nonces.
+ * @param value Nonce's value.
+ *
+ * @return nonce_t Requested nonce.
+ * @return NULL The nonce is absent.
+ */
+nonce_t *nonces_find(const linkedlist_t *nonces, uint64_t value)
+{
+ const linkedlist_node_t *node;
+ nonce_t *nonce;
+
+ node = linkedlist_get_first(nonces);
+ while (node) {
+ nonce = (nonce_t *) node->data;
+ if (nonce->value == value) {
+ return nonce;
+ /* the nonces are ascendingly sorted */
+ } else if (nonce->value > value) {
+ return NULL;
+ }
+ node = linkedlist_get_next(nonces, node);
+ }
+
+ return NULL;
+}
+
+/**
+ * Get the newest nonce (the nonce with the highest value) from a list
+ * of nonces.
+ *
+ * @param nonces Use these nonces.
+ *
+ * @return nonce_t The last nonce.
+ * @return NULL The nonces are empty.
+ */
+nonce_t *nonces_get_newest(const linkedlist_t *nonces)
+{
+ linkedlist_node_t *node;
+ if ((node = linkedlist_get_last(nonces))) {
+ return node->data;
+ }
+ return NULL;
+}
+
+/**
+ * Get the oldest nonce (the nonce with the lowest value) from a list of nonces.
+ *
+ * @param nonces Use these nonces.
+ *
+ * @return nonce_t The first nonce.
+ * @return NULL The nonces are empty.
+ */
+nonce_t *nonces_get_oldest(const linkedlist_t *nonces)
+{
+ linkedlist_node_t *node;
+ if ((node = linkedlist_get_first(nonces))) {
+ return node->data;
+ }
+ return NULL;
+}
+
+/**
+ * Remove stale nonces from a list of nonces.
+ *
+ * @param nonces Use these nonces.
+ */
+void nonces_remove_stale(linkedlist_t *nonces)
+{
+ time_t current_time;
+ linkedlist_node_t *node;
+ nonce_t *nonce;
+
+ current_time = time(NULL);
+
+ node = linkedlist_get_last(nonces);
+ /* skip the last nonce, as we always want at least one to be stored;
+ * if the 'node' is NULL, linkedlist_get_prev returns NULL */
+ node = linkedlist_get_prev(nonces, node);
+
+ while (node && node != &nonces->first) {
+ /* we are going to process this nonce */
+ nonce = (nonce_t *) node->data;
+ /* and move to previous node */
+ node = linkedlist_get_prev(nonces, node);
+
+ if (nonce_is_stale(nonce, current_time)) {
+ /* remove the nonce's node; note that it is
+ * safe to assume 'node' isn't NULL, thanks to
+ * the usage of the linkedlist stub nodes */
+ linkedlist_delete(node->next);
+ }
+ /* 'node' has already been moved to previous */
+ }
+}
+
+/**
+ * Safely delete peer's variables.
+ *
+ * @param peer Clear this peer.
+ */
+void peer_clear(peer_t *peer)
+{
+ linkedlist_destroy(&peer->nonces, NULL);
+}
+
+/**
+ * Delete a peer from a list of peers.
+ *
+ * @param peer Delete this peer.
+ */
+void peer_delete(peer_t *peer)
+{
+ linkedlist_delete_safely(peer->node, peer_clear);
+}
+
+/**
+ * Find a peer by its identifier in a list of peers.
+ *
+ * @param peers Use these peers.
+ * @param identifier Peer's identifier.
+ *
+ * @param peer_t The requested peer.
+ * @param NULL Unknown peer.
+ */
+peer_t *peer_find(const linkedlist_t *peers,
+ const unsigned char *identifier)
+{
+ const linkedlist_node_t *node;
+ peer_t *peer;
+
+ node = linkedlist_get_first(peers);
+ while (node) {
+ peer = (peer_t *) node->data;
+ if (!memcmp(peer->identifier,
+ identifier,
+ crypto_box_PUBLICKEYBYTES)) {
+ return peer;
+ }
+ node = linkedlist_get_next(peers, node);
+ }
+
+ return NULL;
+}
+
+/**
+ * Store a new peer in a list of known peers.
+ *
+ * @param peers Store into this list.
+ * @param identifier The new peer's identifier.
+ *
+ * @return peer_t Newly stored peer.
+ * @return NULL Failure.
+ */
+peer_t *peer_store(linkedlist_t *peers,
+ const unsigned char *identifier)
+{
+ peer_t *new_peer;
+
+ new_peer = (peer_t *) malloc(sizeof(peer_t));
+ if (!new_peer) {
+ log_error("Allocating a new peer");
+ return NULL;
+ }
+
+ linkedlist_init(&new_peer->nonces);
+ memcpy(new_peer->identifier, identifier, crypto_box_PUBLICKEYBYTES);
+ memset(&new_peer->presence_nonce, 0x0, sizeof(nonce_t));
+
+ if (!(new_peer->node = linkedlist_append(peers, new_peer))) {
+ log_error("Storing a new peer");
+ peer_clear(new_peer);
+ free(new_peer);
+ return NULL;
+ }
+
+ return new_peer;
+}
+
+/**
+ * Store a presence nonce of a peer.
+ *
+ * @param peer Use this peer.
+ * @param value The nonce's value.
+ */
+void presence_nonce_store(peer_t *peer, uint64_t value)
+{
+ peer->presence_nonce.value = value;
+ peer->presence_nonce.creation = time(NULL);
+}
diff --git a/src/peers.h b/src/peers.h
new file mode 100644
index 0000000000000000000000000000000000000000..e87c09f176a4287bc1dca678fe2e66a2ec6b8f8c
--- /dev/null
+++ b/src/peers.h
@@ -0,0 +1,92 @@
+/*
+ * Coincer
+ * Copyright (C) 2017-2018 Coincer Developers
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see .
+ */
+
+#ifndef PEERS_H
+#define PEERS_H
+
+#include
+#include
+#include
+
+#include "crypto.h"
+#include "linkedlist.h"
+
+/** After this many seconds we consider a nonce to be stale. */
+#define NONCE_STALE_TIME 60
+
+/** If set, the identity will be removed ASAP. */
+#define IDENTITY_TMP 0x01
+
+/** Nonce representation. */
+typedef struct s_nonce {
+ /** A number with maximum size of (2^64 - 1) representing a nonce. */
+ uint64_t value;
+ /** Creation timestamp. */
+ time_t creation;
+} nonce_t;
+
+/** Peer representation. */
+typedef struct s_peer {
+ /** Peer's public key. */
+ unsigned char identifier[crypto_box_PUBLICKEYBYTES];
+ /** A sorted list of nonces tied to this peer. */
+ linkedlist_t nonces;
+ /** The last known 'announcement of presence' nonce of this peer. */
+ nonce_t presence_nonce;
+ /** The peer's node in the list of peers. */
+ linkedlist_node_t *node;
+} peer_t;
+
+/** Representation of one of our identities. */
+typedef struct s_identity {
+ /** A keypair including a public key as our identifier. */
+ keypair_t keypair;
+ /** Flags of this identity. */
+ int flags;
+ /** The time of the last p2p.route.adv of this identity.*/
+ time_t last_adv;
+ /** The last used nonce with this identity. */
+ uint64_t nonce_value;
+} identity_t;
+
+int identifier_empty(const unsigned char id[crypto_box_PUBLICKEYBYTES]);
+
+identity_t *identity_find(const linkedlist_t *identities,
+ const unsigned char *identifier);
+void identity_flags_set(identity_t *identity, int flags);
+void identity_flags_unset(identity_t *identity, int flags);
+identity_t *identity_generate(int flags);
+
+int nonce_is_stale(const nonce_t *nonce, const time_t current_time);
+int nonce_store(linkedlist_t *nonces, uint64_t value);
+
+nonce_t *nonces_find(const linkedlist_t *nonces, uint64_t value);
+nonce_t *nonces_get_newest(const linkedlist_t *nonces);
+nonce_t *nonces_get_oldest(const linkedlist_t *nonces);
+void nonces_remove_stale(linkedlist_t *nonces);
+
+void peer_clear(peer_t *peer);
+void peer_delete(peer_t *peer);
+peer_t *peer_find(const linkedlist_t *peers,
+ const unsigned char *identifier);
+peer_t *peer_store(linkedlist_t *peers,
+ const unsigned char *identifier);
+
+void presence_nonce_store(peer_t *peer, uint64_t value);
+
+#endif /* PEERS_H */
diff --git a/src/routing.c b/src/routing.c
new file mode 100644
index 0000000000000000000000000000000000000000..a743f8f2a69b0b6d01d1efd41dcd3904fc02650d
--- /dev/null
+++ b/src/routing.c
@@ -0,0 +1,913 @@
+/*
+ * Coincer
+ * Copyright (C) 2017-2018 Coincer Developers
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see .
+ */
+
+#define _BSD_SOURCE /* usleep */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "autoconfig.h"
+#include "crypto.h"
+#include "daemon_messages.h"
+#include "global_state.h"
+#include "json_parser.h"
+#include "linkedlist.h"
+#include "log.h"
+#include "neighbours.h"
+#include "peers.h"
+#include "routing.h"
+
+/**
+ * Simple helper to determine the destination visibility of a message.
+ */
+enum dest_visibility {
+ DV_SHOWN, /**< The message will include the destination's ID. */
+ DV_HIDDEN /**< The message won't include the destination's ID. */
+};
+
+static int message_send_n2n(message_t *message, neighbour_t *dest);
+static int message_send_p2p(const message_t *message,
+ const linkedlist_t *routing_table);
+static int message_send_to_neighbour(const message_t *message,
+ neighbour_t *dest);
+
+static int message_trace_store(linkedlist_t *msg_traces,
+ const neighbour_t *sender,
+ uint64_t nonce_value,
+ const unsigned char *from);
+
+static void string_broadcast(const char *string,
+ const linkedlist_t *neighbours,
+ const neighbour_t *exception);
+static void string_send_to_neighbour(const char *string,
+ neighbour_t *dest);
+
+/**
+ * Pause the program execution for a random amount of time.
+ *
+ * @param ms_min Minimum random time in milliseconds.
+ * @param ms_max Maximum random time in milliseconds. Must be
+ * lower than 1000000.
+ */
+static void execution_pause_random(uint32_t ms_min, uint32_t ms_max)
+{
+ uint32_t pause_range_ms;
+ uint32_t pause_val_ms;
+
+ pause_range_ms = get_random_uint32_t(ms_max - ms_min + 1);
+ pause_val_ms = ms_min + pause_range_ms;
+
+ usleep(pause_val_ms);
+}
+
+/**
+ * Broadcast a message in a neighbour to neighbour manner - replace the field
+ * 'from' with our neighbour-speicific pseudonym.
+ *
+ * @param message The message to be sent. The message content
+ * must be initialized (the type (and the data)).
+ * The rest is initialized in this function.
+ * @param neighbours Broadcast to these neighbours.
+ */
+static void message_broadcast_n2n(message_t *message,
+ const linkedlist_t *neighbours)
+{
+ neighbour_t *current_neigh;
+ linkedlist_node_t *current_node;
+
+ current_node = linkedlist_get_first(neighbours);
+ while (current_node) {
+ current_neigh = (neighbour_t *) current_node->data;
+
+ message_send_n2n(message, current_neigh);
+
+ current_node = linkedlist_get_next(neighbours, current_node);
+ }
+}
+
+/**
+ * Broadcast a message in a peer-to-peer manner (send the message as is).
+ *
+ * @param message The message to be sent.
+ * @param neighbours Broadcast to these neighbours.
+ * @param exception Don't send the message to this neighbour.
+ * This is typically the neighbour from whom
+ * we've received the message.
+ *
+ * @return 0 Success.
+ * @return 1 Failure.
+ */
+static int message_broadcast_p2p(const message_t *message,
+ const linkedlist_t *neighbours,
+ const neighbour_t *exception)
+{
+ char *json_message;
+ neighbour_t *neigh;
+ linkedlist_node_t *node;
+
+ if (encode_message(message, &json_message)) {
+ log_debug("message_send_to_neighbour - encoding message");
+ return 1;
+ }
+
+ node = linkedlist_get_first(neighbours);
+ while (node) {
+ neigh = (neighbour_t *) node->data;
+
+ if (neigh != exception) {
+ string_send_to_neighbour(json_message, neigh);
+ }
+
+ node = linkedlist_get_next(neighbours, node);
+ }
+
+ free(json_message);
+ return 0;
+}
+
+/**
+ * Set up a message to be sent. The type (and the data) of the message
+ * body must be initialized before calling this function.
+ *
+ * @param message The message we want to set up. After successful
+ * completion of this function, this is a
+ * ready to be sent message.
+ * @param from We want to send the message under this identity.
+ * @param to Identifier of the destination. Can be NULL, but
+ * 'dest_vis' has to be set to DV_HIDDEN.
+ * @param dest_vis Message's destination visibility.
+ *
+ * @return 0 Successfully set up.
+ * @return 1 Failure.
+ */
+static int message_finalize(message_t *message,
+ identity_t *from,
+ const unsigned char *to,
+ const enum dest_visibility dest_vis)
+{
+ int cmp_val;
+ char *json_body;
+ uint64_t nonce_value;
+
+ message->version = PROTOCOL_VERSION;
+ memcpy(message->from,
+ from->keypair.public_key,
+ crypto_box_PUBLICKEYBYTES);
+
+ if (dest_vis == DV_HIDDEN) {
+ memset(message->body.to, 0x0, crypto_box_PUBLICKEYBYTES);
+ } else {
+ memcpy(message->body.to, to, crypto_box_PUBLICKEYBYTES);
+ }
+
+ nonce_value = from->nonce_value + 1;
+
+ cmp_val = memcmp(message->from,
+ message->body.to,
+ crypto_box_PUBLICKEYBYTES);
+ /* from ID > to ID => odd nonce; from ID < to ID => even nonce */
+ if ((cmp_val > 0 && !(nonce_value & 0x01)) ||
+ (cmp_val < 0 && (nonce_value & 0x01))) {
+ nonce_value++;
+ }
+
+ message->body.nonce = from->nonce_value = nonce_value;
+
+ if (encode_message_body(&message->body, &json_body)) {
+ return 1;
+ }
+
+ sign_message(json_body, from->keypair.secret_key, message->sig);
+ free(json_body);
+ return 0;
+}
+
+/**
+ * Forward someone's message on its way to destination.
+ *
+ * @param message Forward this message.
+ * @param json_message The message to be forwarded, just encoded.
+ * @param sender Neighbour who's sent us the message.
+ * @param global_state The global state.
+ *
+ * @return 0 Success.
+ * @return 1 Failure.
+ */
+int message_forward(const message_t *message,
+ const char *json_message,
+ const neighbour_t *sender,
+ global_state_t *global_state)
+{
+ neighbour_t *next_hop;
+ route_t *route;
+
+ /* if the 'to' bytes are set to 0x0, the message is a broadcast */
+ if (identifier_empty(message->body.to)) {
+ /* don't send the message to the one who's sent it to us */
+ string_broadcast(json_message,
+ &global_state->neighbours,
+ sender);
+ return 0;
+ }
+
+ route = route_find(&global_state->routing_table, message->body.to);
+ next_hop = route ? route_next_hop_get(route) : NULL;
+ if (!next_hop) {
+ return 1;
+ }
+
+ message_trace_store(&global_state->message_traces,
+ sender,
+ message->body.nonce,
+ message->from);
+
+ string_send_to_neighbour(json_message, next_hop);
+ return 0;
+}
+
+/**
+ * Send a message in a neighbour-to-neighbour manner.
+ *
+ * @param message The message to be sent.
+ * @param dest Send the message to this neighbour.
+ *
+ * @return 0 Successfully sent.
+ * @return 1 Failure.
+ */
+static int message_send_n2n(message_t *message, neighbour_t *dest)
+{
+ if (message_finalize(message,
+ &dest->my_pseudonym,
+ dest->pseudonym.identifier,
+ DV_HIDDEN)) {
+ return 1;
+ }
+
+ return message_send_to_neighbour(message, dest);
+}
+
+/**
+ * Send a p2p message towards its destination via next hop.
+ *
+ * @param message Send this p2p message. The message must contain
+ * destination's identifier for the next hop
+ * selection.
+ * @param routing_table The routing table.
+ *
+ * @return 0 Successfully sent.
+ * @return 1 Failure.
+ */
+static int message_send_p2p(const message_t *message,
+ const linkedlist_t *routing_table)
+{
+ neighbour_t *next_hop;
+ route_t *route;
+
+ route = route_find(routing_table, message->body.to);
+ next_hop = route ? route_next_hop_get(route) : NULL;
+ if (!next_hop) {
+ return 1;
+ }
+
+ /* send the message to the next hop */
+ return message_send_to_neighbour(message, next_hop);
+}
+
+/**
+ * Send a message to a neighbour.
+ *
+ * @param message Send this message.
+ * @param dest The neighbour.
+ *
+ * @return 0 Successfully sent.
+ * @return 1 Failure.
+ */
+static int message_send_to_neighbour(const message_t *message,
+ neighbour_t *dest)
+{
+ char *json_message;
+
+ if (encode_message(message, &json_message)) {
+ log_debug("message_send_to_neighbour - encoding message");
+ return 1;
+ }
+
+ string_send_to_neighbour(json_message, dest);
+
+ free(json_message);
+ return 0;
+}
+
+/**
+ * A message trace staleness predicate.
+ *
+ * @param msg_trace The message trace.
+ * @param current_time Time to compare to.
+ *
+ * @return 1 The message trace is stale.
+ * @return 0 The message trace is not stale.
+ */
+int message_trace_is_stale(const message_trace_t *msg_trace,
+ const time_t *current_time)
+{
+ return difftime(*current_time, msg_trace->creation) >=
+ MESSAGE_TRACE_STALE_TIME;
+}
+
+/**
+ * Store a new message trace.
+ *
+ * @param msg_traces Store to this container.
+ * @param sender The neighbour whose message we've forwarded.
+ * @param nonce_value The forwarded message's nonce value.
+ * @param from The originator's identifier.
+ *
+ * @return 0 Successfully stored.
+ * @return 1 Failure.
+ */
+static int message_trace_store(linkedlist_t *msg_traces,
+ const neighbour_t *sender,
+ uint64_t nonce_value,
+ const unsigned char *from)
+{
+ message_trace_t *msg_trace;
+
+ msg_trace = (message_trace_t *) malloc(sizeof(message_trace_t));
+ if (!msg_trace) {
+ log_error("Allocating a message trace");
+ return 1;
+ }
+
+ msg_trace->sender = sender;
+ msg_trace->nonce_value = nonce_value;
+ msg_trace->creation = time(NULL);
+ memcpy(msg_trace->from, from, crypto_box_PUBLICKEYBYTES);
+
+ if (!linkedlist_append(msg_traces, msg_trace)) {
+ log_error("Storing a message trace");
+ free(msg_trace);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Add a new route to the routing table.
+ *
+ * @param routing_table Add the new route in here.
+ * @param dest Destination peer.
+ * @param next_hop The neighbour who's announced the new route.
+ *
+ * @return route_t The new route.
+ * @return NULL Failure.
+ */
+route_t *route_add(linkedlist_t *routing_table,
+ peer_t *dest,
+ neighbour_t *next_hop)
+{
+ route_t *new_route;
+
+ new_route = (route_t *) malloc(sizeof(route_t));
+ if (!new_route) {
+ log_error("Creating a new route");
+ return NULL;
+ }
+
+ linkedlist_init(&new_route->next_hops);
+ if (!linkedlist_append(&new_route->next_hops, next_hop)) {
+ free(new_route);
+ log_error("Adding a new route's next hop");
+ return NULL;
+ }
+ if (!(new_route->node = linkedlist_append(routing_table, new_route))) {
+ linkedlist_remove_all(&new_route->next_hops);
+ free(new_route);
+ log_error("Storing a new route");
+ return NULL;
+ }
+
+ new_route->destination = dest;
+ new_route->last_update = time(NULL);
+
+ return new_route;
+}
+
+/**
+ * Safely delete all route's data.
+ *
+ * @param route Delete this route's data.
+ */
+void route_clear(route_t *route)
+{
+ linkedlist_remove_all(&route->next_hops);
+}
+
+/**
+ * Delete a route from a routing table.
+ *
+ * @param routing_table The routing table.
+ * @param dest_id The route's destination id.
+ */
+void route_delete(linkedlist_t *routing_table, const unsigned char *dest_id)
+{
+ route_t *route;
+
+ route = route_find(routing_table, dest_id);
+ if (route) {
+ linkedlist_delete_safely(route->node, route_clear);
+ }
+}
+
+/**
+ * Find a route in a routing table.
+ *
+ * @param routing_table The routing table.
+ * @param dest_id The route's destination identififer.
+ *
+ * @return route_t The requested route.
+ * @return NULL Route not found.
+ */
+route_t *route_find(const linkedlist_t *routing_table,
+ const unsigned char *dest_id)
+{
+ const unsigned char *id;
+ const linkedlist_node_t *node;
+ route_t *route;
+
+ /* get the first node of our routing table */
+ node = linkedlist_get_first(routing_table);
+ while (node) {
+ route = (route_t *) node->data;
+ id = route->destination->identifier;
+
+ if (!memcmp(id, dest_id, crypto_box_PUBLICKEYBYTES)) {
+ return route;
+ }
+
+ node = linkedlist_get_next(routing_table, node);
+ }
+
+ /* destination unknown */
+ return NULL;
+}
+
+/**
+ * A route staleness predicate.
+ *
+ * @param route The route.
+ * @param current_time Time to compare to.
+ *
+ * @return 1 The route is stale.
+ * @return 0 The route is not stale.
+ */
+int route_is_stale(const route_t *route, const time_t *current_time)
+{
+ return difftime(*current_time, route->last_update) >= ROUTE_STALE_TIME;
+}
+
+/**
+ * Add new next hop, unless it's already added.
+ *
+ * @param route Add the next hop to this route.
+ * @param next_hop Add this next hop.
+ *
+ * @return 0 Added or next hop has already been there.
+ * @return 1 Failure.
+ */
+int route_next_hop_add(route_t *route, neighbour_t *next_hop)
+{
+ if (!linkedlist_find(&route->next_hops, next_hop)) {
+ return linkedlist_append(&route->next_hops, next_hop) == NULL;
+ }
+
+ return 0;
+}
+
+/**
+ * Get a next hop for a given route.
+ *
+ * @param route The route.
+ *
+ * @return neighbour_t The next hop.
+ * @return NULL The route has no next hops.
+ */
+neighbour_t *route_next_hop_get(const route_t *route)
+{
+ linkedlist_node_t *next_hop_node;
+
+ next_hop_node = route ? linkedlist_get_first(&route->next_hops) : NULL;
+ if (!next_hop_node) {
+ return NULL;
+ }
+
+ return next_hop_node->data;
+}
+
+/**
+ * Remove a next hop from a route, if exists.
+ *
+ * @param route Remove the next hop from this route.
+ * @param next_hop Remove this next hop.
+ */
+void route_next_hop_remove(route_t *route, neighbour_t *next_hop)
+{
+ linkedlist_node_t *node = linkedlist_find(&route->next_hops, next_hop);
+
+ if (node) {
+ linkedlist_remove(node);
+ }
+}
+
+/**
+ * Reset a route - remove its next hops and add a new one.
+ *
+ * @param route Reset this route.
+ * @param next_hop The new next hop.
+ *
+ * @return 0 Success.
+ * @return 1 Failure.
+ */
+int route_reset(route_t *route, neighbour_t *next_hop)
+{
+ linkedlist_remove_all(&route->next_hops);
+
+ return linkedlist_append(&route->next_hops, next_hop) == NULL;
+}
+
+/**
+ * Detect a routing loop.
+ *
+ * @param msg_traces Recent message traces.
+ * @param neighbour The forwarder of a new message.
+ * @param nonce_value The message's nonce value.
+ * @param from The identifier of the message originator.
+ *
+ * @return 1 A routing loop detected.
+ * @return 0 No routing loop detected.
+ */
+int routing_loop_detect(const linkedlist_t *msg_traces,
+ const neighbour_t *neighbour,
+ uint64_t nonce_value,
+ const unsigned char *from)
+{
+ const message_trace_t *msg_trace;
+ const linkedlist_node_t *node;
+
+ node = linkedlist_get_first(msg_traces);
+ while (node) {
+ msg_trace = (message_trace_t *) node->data;
+
+ /* the same message but different neighbour => routing loop */
+ if (msg_trace->nonce_value == nonce_value &&
+ msg_trace->sender != neighbour &&
+ !memcmp(msg_trace->from, from, crypto_box_PUBLICKEYBYTES)) {
+ return 1;
+ }
+
+ node = linkedlist_get_next(msg_traces, node);
+ }
+
+ return 0;
+}
+
+/**
+ * Remove a routing loop from a route given by its destination's identifier.
+ * Send p2p.route.sol if we've removed our last next hop.
+ *
+ * @param routing_table The routing table.
+ * @param neighbours Our neighbours.
+ * @param identities Our identities.
+ * @param dest_id The identifier of the destination of the route
+ * that's caused a routing loop.
+ */
+void routing_loop_remove(linkedlist_t *routing_table,
+ linkedlist_t *neighbours,
+ linkedlist_t *identities,
+ const unsigned char *dest_id)
+{
+ identity_t *identity;
+ neighbour_t *next_hop;
+ route_t *route;
+
+ route = route_find(routing_table, dest_id);
+
+ /* get the next hop or NULL if the message destination is unknown
+ * or if we have no next hop to reach the destination */
+ next_hop = route ? route_next_hop_get(route) : NULL;
+ if (next_hop) {
+ route_next_hop_remove(route, next_hop);
+
+ next_hop = route_next_hop_get(route);
+ if (!next_hop) {
+ /* create a temporary identity for the p2p.route.sol */
+ identity = identity_generate(IDENTITY_TMP);
+ /* if appending succeeded */
+ if (linkedlist_append(identities, identity)) {
+ /* if successfully sent */
+ if (!send_p2p_route_sol(neighbours,
+ identity,
+ dest_id)) {
+ return;
+ }
+ }
+ free(identity);
+ route_delete(routing_table, dest_id);
+ }
+ }
+}
+
+/**
+ * Remove a next hop from all routes.
+ *
+ * @param routing_table The routing table.
+ * @param next_hop Remove this next hop.
+ */
+void routing_table_remove_next_hop(linkedlist_t *routing_table,
+ neighbour_t *next_hop)
+{
+ const linkedlist_node_t *node;
+ route_t *route;
+
+ node = linkedlist_get_first(routing_table);
+ while (node) {
+ route = (route_t *) node->data;
+
+ route_next_hop_remove(route, next_hop);
+
+ node = linkedlist_get_next(routing_table, node);
+ }
+}
+
+/**
+ * Send p2p.bye message.
+ *
+ * @param neighbours Our neighbours.
+ * @param identity Announce departure of this identity.
+ *
+ * @return 0 Successfully sent.
+ * @return 1 Failure.
+ */
+int send_p2p_bye(linkedlist_t *neighbours, identity_t *identity)
+{
+ message_t p2p_bye_msg;
+ int ret;
+
+ if (create_p2p_bye(&p2p_bye_msg)) {
+ return 1;
+ }
+ if (!(ret = message_finalize(&p2p_bye_msg,
+ identity,
+ NULL,
+ DV_HIDDEN))) {
+ /* route as P2P (don't use our neighbour pseudonym) */
+ ret = message_broadcast_p2p(&p2p_bye_msg,
+ neighbours,
+ NULL);
+ }
+
+ message_delete(&p2p_bye_msg);
+ return ret;
+}
+
+/**
+ * Send p2p.hello message.
+ *
+ * @param dest Send the message to this neighbour.
+ * @param port Our listening port.
+ *
+ * @return 0 Successfully sent.
+ * @return 1 Failure.
+ */
+int send_p2p_hello(neighbour_t *dest, unsigned short port)
+{
+ message_t p2p_hello_msg;
+ int ret;
+
+ if (create_p2p_hello(&p2p_hello_msg, port)) {
+ return 1;
+ }
+ ret = message_send_n2n(&p2p_hello_msg, dest);
+
+ message_delete(&p2p_hello_msg);
+ return ret;
+}
+
+/**
+ * Send p2p.peers.adv message.
+ *
+ * @param dest Send the message to this neighbour.
+ * @param hosts Hosts known to us.
+ *
+ * @return 0 Successfully sent.
+ * @return 1 Failure.
+ */
+int send_p2p_peers_adv(neighbour_t *dest, const linkedlist_t *hosts)
+{
+ message_t p2p_peers_adv_msg;
+ int ret;
+
+ if (create_p2p_peers_adv(&p2p_peers_adv_msg, hosts)) {
+ return 1;
+ }
+ ret = message_send_n2n(&p2p_peers_adv_msg, dest);
+
+ message_delete(&p2p_peers_adv_msg);
+ return ret;
+}
+
+/**
+ * Send p2p.peers.sol message.
+ *
+ * @param dest Send the message to this neighbour.
+ *
+ * @return 0 Successfully sent.
+ * @return 1 Failure.
+ */
+int send_p2p_peers_sol(neighbour_t *dest)
+{
+ message_t p2p_peers_sol_msg;
+ int ret;
+
+ if (create_p2p_peers_sol(&p2p_peers_sol_msg)) {
+ return 1;
+ }
+ if (!(ret = message_send_n2n(&p2p_peers_sol_msg, dest))) {
+ set_neighbour_flags(dest, NEIGHBOUR_ADDRS_REQ);
+ }
+
+ message_delete(&p2p_peers_sol_msg);
+ return ret;
+}
+
+/**
+ * Send p2p.ping message.
+ *
+ * @param dest Send the message to this neighbour.
+ *
+ * @return 0 Successfully sent.
+ * @return 1 Failure.
+ */
+int send_p2p_ping(neighbour_t *dest)
+{
+ message_t p2p_ping_msg;
+ int ret;
+
+ if (create_p2p_ping(&p2p_ping_msg)) {
+ return 1;
+ }
+ ret = message_send_n2n(&p2p_ping_msg, dest);
+
+ message_delete(&p2p_ping_msg);
+ return ret;
+}
+
+/**
+ * Send p2p.pong message.
+ *
+ * @param dest Send the message to this neighbour.
+ *
+ * @return 0 Successfully sent.
+ * @return 1 Failure.
+ */
+int send_p2p_pong(neighbour_t *dest)
+{
+ message_t p2p_pong_msg;
+ int ret;
+
+ if (create_p2p_pong(&p2p_pong_msg)) {
+ return 1;
+ }
+ ret = message_send_n2n(&p2p_pong_msg, dest);
+
+ message_delete(&p2p_pong_msg);
+ return ret;
+}
+
+/**
+ * Send p2p.route.adv message.
+ *
+ * @param neighbours Our neighbours.
+ * @param identity Advertise this identity.
+ *
+ * @return 0 Successfully sent.
+ * @return 1 Failure.
+ */
+int send_p2p_route_adv(linkedlist_t *neighbours, identity_t *identity)
+{
+ message_t p2p_route_adv_msg;
+ int ret;
+
+ if (create_p2p_route_adv(&p2p_route_adv_msg)) {
+ return 1;
+ }
+ if (!(ret = message_finalize(&p2p_route_adv_msg,
+ identity,
+ NULL,
+ DV_HIDDEN))) {
+ /* timing attack protection */
+ execution_pause_random(250, 2000);
+
+ /* route as P2P (don't use our neighbour pseudonym) */
+ if (!(ret = message_broadcast_p2p(&p2p_route_adv_msg,
+ neighbours,
+ NULL))) {
+ identity->last_adv = time(NULL);
+ }
+ }
+
+ message_delete(&p2p_route_adv_msg);
+ return ret;
+}
+
+/**
+ * Send p2p.route.sol message.
+ *
+ * @param neighbours Our neighbours.
+ * @param identity Make a solicitation under this identity.
+ * @param target Destination peer's identifier.
+ *
+ * @return 0 Successfully sent.
+ * @return 1 Failure.
+ */
+int send_p2p_route_sol(linkedlist_t *neighbours,
+ identity_t *identity,
+ const unsigned char *target)
+{
+ message_t p2p_route_sol_msg;
+ int ret;
+
+ if (create_p2p_route_sol(&p2p_route_sol_msg, target)) {
+ return 1;
+ }
+ if (!(ret = message_finalize(&p2p_route_sol_msg,
+ identity,
+ NULL,
+ DV_HIDDEN))) {
+ /* route as P2P (don't use our neighbour pseudonym) */
+ ret = message_broadcast_p2p(&p2p_route_sol_msg,
+ neighbours,
+ NULL);
+ }
+
+ message_delete(&p2p_route_sol_msg);
+ return ret;
+}
+
+/**
+ * Broadcast a text (json message) to our neighbour.
+ *
+ * @param string Send this text.
+ * @param neighbours Our neighbours.
+ * @param exception Don't send the message to this neighbour.
+ * This is typically the neighbour from whom
+ * we've received the message.
+ */
+static void string_broadcast(const char *string,
+ const linkedlist_t *neighbours,
+ const neighbour_t *exception)
+{
+ linkedlist_node_t *node;
+ neighbour_t *neighbour;
+
+ node = linkedlist_get_first(neighbours);
+ while (node) {
+ neighbour = (neighbour_t *) node->data;
+ if (neighbour != exception) {
+ string_send_to_neighbour(string, neighbour);
+ }
+ node = linkedlist_get_next(neighbours, node);
+ }
+}
+
+/**
+ * Send a text (json message) to a neighbour.
+ *
+ * @param string Send this text.
+ * @param dest To this neighbour.
+ */
+static void string_send_to_neighbour(const char *string, neighbour_t *dest)
+{
+ struct evbuffer *output;
+
+ output = bufferevent_get_output(dest->buffer_event);
+ evbuffer_add_printf(output, "%s", string);
+}
diff --git a/src/routing.h b/src/routing.h
new file mode 100644
index 0000000000000000000000000000000000000000..e5da6172a3174e72fbcef44cc6a033589ded6c4c
--- /dev/null
+++ b/src/routing.h
@@ -0,0 +1,107 @@
+/*
+ * Coincer
+ * Copyright (C) 2017-2018 Coincer Developers
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see .
+ */
+
+#ifndef ROUTING_H
+#define ROUTING_H
+
+#include
+#include
+#include
+
+#include "daemon_messages.h"
+#include "global_state.h"
+#include "linkedlist.h"
+#include "neighbours.h"
+#include "peers.h"
+
+/** After this many seconds we consider a message trace to be stale. */
+#define MESSAGE_TRACE_STALE_TIME 60
+/** After this many seconds we consider a route to be stale. */
+#define ROUTE_STALE_TIME 180
+
+/** A message trace representation. */
+typedef struct s_message_trace {
+ /** Message's nonce value. */
+ uint64_t nonce_value;
+ /** Identifier of the sender. */
+ char from[crypto_box_PUBLICKEYBYTES];
+ /** Neighbour who's sent us the message */
+ const neighbour_t *sender;
+ /** Creation timestamp. */
+ time_t creation;
+} message_trace_t;
+
+/** Data type for a routing table. */
+typedef struct s_route {
+ /** Destination peer. */
+ peer_t *destination;
+ /** List of possible next hops (pointers to our neighbours)
+ * to the destination. Naturally sorted by delay viability. */
+ linkedlist_t next_hops;
+ /** The time of the last destination's announcement of presence or
+ * the time of this route's creation. */
+ time_t last_update;
+ /** The route's node in the routing table. */
+ linkedlist_node_t *node;
+} route_t;
+
+int message_forward(const message_t *message,
+ const char *json_message,
+ const neighbour_t *sender,
+ global_state_t *global_state);
+
+int message_trace_is_stale(const message_trace_t *msg_trace,
+ const time_t *current_time);
+
+route_t *route_add(linkedlist_t *routing_table,
+ peer_t *dest,
+ neighbour_t *next_hop);
+void route_clear(route_t *route);
+void route_delete(linkedlist_t *routing_table, const unsigned char *dest_id);
+route_t *route_find(const linkedlist_t *routing_table,
+ const unsigned char *dest_id);
+int route_is_stale(const route_t *route, const time_t *current_time);
+int route_next_hop_add(route_t *route, neighbour_t *next_hop);
+neighbour_t *route_next_hop_get(const route_t *route);
+void route_next_hop_remove(route_t *route, neighbour_t *next_hop);
+int route_reset(route_t *route, neighbour_t *next_hop);
+
+int routing_loop_detect(const linkedlist_t *msg_traces,
+ const neighbour_t *neighbour,
+ uint64_t nonce_value,
+ const unsigned char *from);
+void routing_loop_remove(linkedlist_t *routing_table,
+ linkedlist_t *neighbours,
+ linkedlist_t *identities,
+ const unsigned char *dest_id);
+
+void routing_table_remove_next_hop(linkedlist_t *routing_table,
+ neighbour_t *next_hop);
+
+int send_p2p_bye(linkedlist_t *neighbours, identity_t *identity);
+int send_p2p_hello(neighbour_t *dest, unsigned short port);
+int send_p2p_peers_adv(neighbour_t *dest, const linkedlist_t *hosts);
+int send_p2p_peers_sol(neighbour_t *dest);
+int send_p2p_ping(neighbour_t *dest);
+int send_p2p_pong(neighbour_t *dest);
+int send_p2p_route_adv(linkedlist_t *neighbours, identity_t *identity);
+int send_p2p_route_sol(linkedlist_t *neighbours,
+ identity_t *identity,
+ const unsigned char *target);
+
+#endif /* ROUTING_H */