From 33f1a37d5090d458caa286ecd9426f5c782780e5 Mon Sep 17 00:00:00 2001 From: xpetrak2 <456484@mail.muni.cz> Date: Sat, 15 Sep 2018 21:36:15 +0200 Subject: [PATCH 1/5] Peer representation created --- Makefile.am | 3 +- README | 11 +- src/daemon_events.c | 142 ++++++++++++++-- src/global_state.c | 12 ++ src/global_state.h | 7 + src/neighbours.c | 51 ++++++ src/neighbours.h | 10 ++ src/peers.c | 397 ++++++++++++++++++++++++++++++++++++++++++++ src/peers.h | 92 ++++++++++ 9 files changed, 706 insertions(+), 19 deletions(-) create mode 100644 src/peers.c create mode 100644 src/peers.h diff --git a/Makefile.am b/Makefile.am index add903b..44675c5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,7 +15,8 @@ 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_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 index e5e06d1..01d5810 100644 --- a/README +++ b/README @@ -1,2 +1,9 @@ -Coincer -======= +# 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) +- Neighbour: A host that we are interconnected with +- Nonce: Ever-increasing integer tied to every message sent for message repetiton or replay attack detection +- Peer: The Coincer network participant diff --git a/src/daemon_events.c b/src/daemon_events.c index 639a082..fe149ff 100644 --- a/src/daemon_events.c +++ b/src/daemon_events.c @@ -25,31 +25,39 @@ #include "log.h" #include "neighbours.h" #include "p2p.h" +#include "peers.h" + +/** The time interval in seconds between loop_update_long calls. */ +#define UPDATE_TIME_LONG 60 +/** 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_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("conns_cb - 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 +104,127 @@ 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, - conns_cb, + loop_update_short, 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 short 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, + loop_update_long, + global_state); + + if (!event || + event_add(event, &interval) < 0 || + !linkedlist_append(events, 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 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; + linkedlist_node_t *next_node; + + current_time = time(NULL); + + /* 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 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) { + linkedlist_delete(current_node); + } + + current_node = next_node; + } +} + /** * Callback function for a received signal. * diff --git a/src/global_state.c b/src/global_state.c index d3af9fa..326f83c 100644 --- a/src/global_state.c +++ b/src/global_state.c @@ -23,6 +23,7 @@ #include "log.h" #include "neighbours.h" #include "paths.h" +#include "peers.h" /** * Clear global state variables. @@ -32,10 +33,12 @@ void global_state_clear(global_state_t *global_state) { 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->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,9 +57,18 @@ 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->neighbours); + linkedlist_init(&global_state->peers); linkedlist_init(&global_state->pending_neighbours); return 0; diff --git a/src/global_state.h b/src/global_state.h index 2054cd7..491718e 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,16 @@ 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 our neighbours. */ linkedlist_t neighbours; + /** Known peers. */ + linkedlist_t peers; /** Hosts that didn't accept/reject us yet. */ linkedlist_t pending_neighbours; + /** Our true identity. */ + identity_t *true_identity; } global_state_t; void global_state_clear(global_state_t *global_state); diff --git a/src/neighbours.c b/src/neighbours.c index 5dbebc7..92196df 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. @@ -65,6 +68,17 @@ neighbour_t *add_new_neighbour(linkedlist_t *neighbours, 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(); + /* 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 +98,7 @@ neighbour_t *add_new_neighbour(linkedlist_t *neighbours, void clear_neighbour(neighbour_t *neighbour) { bufferevent_free(neighbour->buffer_event); + peer_clear(&neighbour->pseudonym); } /** @@ -117,6 +132,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 1757884..be00912 100644 --- a/src/neighbours.h +++ b/src/neighbours.h @@ -25,6 +25,7 @@ #include "hosts.h" #include "linkedlist.h" +#include "peers.h" /** Request for addresses. */ #define NEIGHBOUR_ADDRS_REQ 0x01 @@ -42,8 +43,12 @@ typedef struct s_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 +63,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/peers.c b/src/peers.c new file mode 100644 index 0000000..ae4cdc3 --- /dev/null +++ b/src/peers.c @@ -0,0 +1,397 @@ +/* + * 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; + + /* 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 = time(NULL); + } else { + 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)) { + nonce->creation = time(NULL); + + if (!linkedlist_insert_after(nonces, &nonces->first, nonce)) { + log_error("Storing a nonce"); + free(nonce); + return 1; + } + + return 0; + } + + free(nonce); + return 1; +} + +/** + * 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 first 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_first(const linkedlist_t *nonces) +{ + linkedlist_node_t *node; + if ((node = linkedlist_get_first(nonces))) { + return node->data; + } + return NULL; +} + +/** + * Get the last 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_last(const linkedlist_t *nonces) +{ + linkedlist_node_t *node; + if ((node = linkedlist_get_last(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 0000000..7beda54 --- /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_first(const linkedlist_t *nonces); +nonce_t *nonces_get_last(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 */ -- GitLab From 10797b1c94b950b22e40f6e6b484ab4d96ca3f38 Mon Sep 17 00:00:00 2001 From: xpetrak2 <456484@mail.muni.cz> Date: Sun, 16 Sep 2018 17:04:33 +0200 Subject: [PATCH 2/5] Routing and sending daemon messages --- Makefile.am | 3 +- README | 3 + src/daemon_events.c | 62 +++- src/global_state.c | 7 + src/global_state.h | 7 + src/hosts.c | 45 ++- src/hosts.h | 9 +- src/p2p.c | 86 ++--- src/p2p.h | 5 +- src/routing.c | 856 ++++++++++++++++++++++++++++++++++++++++++++ src/routing.h | 102 ++++++ 11 files changed, 1113 insertions(+), 72 deletions(-) create mode 100644 src/routing.c create mode 100644 src/routing.h diff --git a/Makefile.am b/Makefile.am index 44675c5..5ccfa79 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,7 +16,8 @@ src_coincerd_SOURCES = \ src/neighbours.c src/neighbours.h \ src/p2p.c src/p2p.h \ src/paths.c src/paths.h \ - src/peers.c src/peers.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 index 01d5810..ff62c91 100644 --- a/README +++ b/README @@ -4,6 +4,9 @@ - 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: A tuple of a peer-to-peer message's nonce and a pointer to a neighbour from which we've received the message. 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 fe149ff..4307d5d 100644 --- a/src/daemon_events.c +++ b/src/daemon_events.c @@ -26,15 +26,21 @@ #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); @@ -57,7 +63,8 @@ static void connections_maintain(global_state_t *global_state) needed_conns = MIN_NEIGHBOURS - linkedlist_size(&global_state->neighbours); if (needed_conns > 0) { - log_debug("conns_cb - need %d more neighbours", needed_conns); + 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); @@ -120,6 +127,23 @@ int daemon_events_setup(global_state_t *global_state) 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; @@ -156,6 +180,25 @@ static void loop_update_long(evutil_socket_t fd __attribute__((unused)), 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. * @@ -184,10 +227,18 @@ static void remove_stale_records(global_state_t *global_state) 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); @@ -210,6 +261,13 @@ static void remove_stale_records(global_state_t *global_state) 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); @@ -218,6 +276,8 @@ static void remove_stale_records(global_state_t *global_state) 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); } diff --git a/src/global_state.c b/src/global_state.c index 326f83c..98203fe 100644 --- a/src/global_state.c +++ b/src/global_state.c @@ -24,6 +24,7 @@ #include "neighbours.h" #include "paths.h" #include "peers.h" +#include "routing.h" /** * Clear global state variables. @@ -32,9 +33,11 @@ */ 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); @@ -67,9 +70,13 @@ int global_state_init(global_state_t *global_state) 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 491718e..9515ae4 100644 --- a/src/global_state.h +++ b/src/global_state.h @@ -39,12 +39,19 @@ typedef struct s_global_state { 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; diff --git a/src/hosts.c b/src/hosts.c index 8df6eb4..1c01c1e 100644 --- a/src/hosts.c +++ b/src/hosts.c @@ -27,17 +27,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 +38,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 +48,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); @@ -194,14 +186,19 @@ int hosts_to_str(const linkedlist_t *hosts, char **output) /** * 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 +227,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 +329,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 4996e16..b2793f1 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/p2p.c b/src/p2p.c index f0983c9..6956a77 100644 --- a/src/p2p.c +++ b/src/p2p.c @@ -36,6 +36,7 @@ #include "log.h" #include "neighbours.h" #include "p2p.h" +#include "routing.h" /** * Simple helper for conversion of binary IP to readable IP address. @@ -48,27 +49,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. * @@ -86,7 +66,10 @@ static void process_hosts(global_state_t *global_state, char *hosts) while (line) { if (inet_pton(AF_INET6, line, &addr) == 1) { - save_host(&global_state->hosts, &addr); + save_host(&global_state->hosts, + &addr, + DEFAULT_PORT, + HOST_AVAILABLE); } line = strtok_r(NULL, delim, &save_ptr); } @@ -186,9 +169,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 +189,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 +229,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 +269,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 +338,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 +347,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 +389,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 +440,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 +472,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 +517,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 +604,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 +619,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 +636,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 232d1e8..0f3acaf 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/routing.c b/src/routing.c new file mode 100644 index 0000000..fc015e5 --- /dev/null +++ b/src/routing.c @@ -0,0 +1,856 @@ +/* + * 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); + +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 % 2 == 0) || + (cmp_val < 0 && nonce_value % 2 == 1)) { + nonce_value++; + } + + message->body.nonce = from->nonce_value = nonce_value; + + if (!encode_message_body(&message->body, &json_body)) { + sign_message(json_body, from->keypair.secret_key, message->sig); + free(json_body); + return 0; + } + + return 1; +} + +/** + * Forward someone's message on its way to destination. + * + * @param message Forward this message. + * @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 neighbour_t *sender, + global_state_t *global_state) +{ + const message_body_t *msg_body; + + msg_body = &message->body; + + /* if all 'to' bytes are set to 0x0, the message is a broadcast */ + if (identifier_empty(msg_body->to)) { + /* don't send the message to the one who's sent it to us */ + return message_broadcast_p2p(message, + &global_state->neighbours, + sender); + } + + message_trace_store(&global_state->message_traces, + sender, + msg_body->nonce); + + return message_send_p2p(message, &global_state->routing_table); +} + +/** + * 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 message_send_to_neighbour(message, dest); + } + + return 1; +} + +/** + * 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; + linkedlist_node_t *next_hop_node; + route_t *route; + + route = route_find(routing_table, message->body.to); + /* 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_node = route ? linkedlist_get_first(&route->next_hops) : NULL; + if (next_hop_node) { + next_hop = (neighbour_t *) next_hop_node->data; + + /* send the message to the next hop */ + return message_send_to_neighbour(message, next_hop); + } + + return 1; +} + +/** + * 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. + * + * @return 0 Successfully stored. + * @return 1 Failure. + */ +static int message_trace_store(linkedlist_t *msg_traces, + const neighbour_t *sender, + uint64_t nonce_value) +{ + 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); + + 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 (!(new_route->node = + linkedlist_append(&new_route->next_hops, next_hop))) { + 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; +} + +/** + * 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. + * + * @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 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) { + 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; + linkedlist_node_t *next_hop_node; + 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_node = route ? linkedlist_get_first(&route->next_hops) : NULL; + if (next_hop_node) { + next_hop = (neighbour_t *) next_hop_node->data; + route_next_hop_remove(route, next_hop); + + next_hop_node = linkedlist_get_first(&route->next_hops); + if (!next_hop_node) { + /* 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)) { + 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; + } + + return 1; +} + +/** + * 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)) { + ret = message_send_n2n(&p2p_hello_msg, dest); + + message_delete(&p2p_hello_msg); + return ret; + } + + return 1; +} + +/** + * 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)) { + ret = message_send_n2n(&p2p_peers_adv_msg, dest); + + message_delete(&p2p_peers_adv_msg); + return ret; + } + + return 1; +} + +/** + * 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)) { + 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; + } + + return 1; +} + +/** + * 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)) { + ret = message_send_n2n(&p2p_ping_msg, dest); + + message_delete(&p2p_ping_msg); + return ret; + } + + return 1; +} + +/** + * 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)) { + ret = message_send_n2n(&p2p_pong_msg, dest); + + message_delete(&p2p_pong_msg); + return ret; + } + + return 1; +} + +/** + * 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)) { + 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; + } + + return 1; +} + +/** + * 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)) { + 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; + } + + return 1; +} + +/** + * 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 0000000..c78ad73 --- /dev/null +++ b/src/routing.h @@ -0,0 +1,102 @@ +/* + * 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 60 + +/** A message trace representation. */ +typedef struct s_message_trace { + /** Message's nonce value. */ + uint64_t nonce_value; + /** 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 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); +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); +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 */ -- GitLab From 044e117478f5e19edf38973de99ce95cb8d49a39 Mon Sep 17 00:00:00 2001 From: xpetrak2 <456484@mail.muni.cz> Date: Sun, 16 Sep 2018 19:01:46 +0200 Subject: [PATCH 3/5] Daemon messages processing --- Makefile.am | 1 + src/daemon_messages_processor.c | 676 ++++++++++++++++++++++++++++++++ src/daemon_messages_processor.h | 40 ++ src/hosts.c | 86 +++- src/neighbours.c | 4 + src/neighbours.h | 5 + src/p2p.c | 90 +---- 7 files changed, 808 insertions(+), 94 deletions(-) create mode 100644 src/daemon_messages_processor.c create mode 100644 src/daemon_messages_processor.h diff --git a/Makefile.am b/Makefile.am index 5ccfa79..b0b8e63 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 \ diff --git a/src/daemon_messages_processor.c b/src/daemon_messages_processor.c new file mode 100644 index 0000000..eff53f3 --- /dev/null +++ b/src/daemon_messages_processor.c @@ -0,0 +1,676 @@ +/* + * 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, + neighbour_t *sender, + global_state_t *global_state); + +static int process_p2p_bye(const message_t *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, + neighbour_t *sender, + peer_t *sender_peer, + global_state_t *global_state); +static int process_p2p_route_sol(const message_t *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)) { + log_debug("process_encoded_message - decoding a received " + "message has failed"); + return PMR_ERR_PARSING; + } + + if (message.version != PROTOCOL_VERSION) { + message_delete(&message); + return PMR_ERR_VERSION; + } + + /* integrity verification part; get message body in JSON format */ + if (encode_message_body(&message.body, &json_message_body)) { + log_error("Encoding what we've just decoded has failed"); + message_delete(&message); + return PMR_ERR_INTERNAL; + } + /* if the message integrity is violated */ + if (verify_signature(json_message_body, message.from, message.sig)) { + log_warn("Someone tampered with a received 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, sender, global_state); + + message_delete(&message); + return ret; +} + +/** + * Process a message received from its forwarder/sender (our neighbour). + * + * @param message The received 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_INTERNAL Internal processing error. + * @return PMR_ERR_SEMANTIC Semantic error. + */ +static enum process_message_result process_message( + const message_t *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 % 2 == 0) || + (cmp_val < 0 && nonce_value % 2 == 1)) { + 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)) { + 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_first(&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, 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, + 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, + sender, + sender_peer, + global_state); + break; + case P2P_ROUTE_SOL: + res = process_p2p_route_sol(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 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, + 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, 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 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, + 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, sender, global_state); + return 0; +} + +/** + * Process p2p.route.sol. + * + * @param message Process this message. + * @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, + 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, sender, global_state)) { + /* if someone's looking for us */ + if (identity) { + if (difftime(identity->last_adv, + time(NULL)) > 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; + } + + return 1; +} diff --git a/src/daemon_messages_processor.h b/src/daemon_messages_processor.h new file mode 100644 index 0000000..bc76fcc --- /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/hosts.c b/src/hosts.c index 1c01c1e..528e7ec 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 @@ -136,50 +138,96 @@ 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]; - /* TODO: Don't assume any buffer size. This will be fixed in the - * next commit. */ - *output = (char *) malloc(4096 * sizeof(char)); + opening_len = strlen(opening); + separator_len = strlen(separator); + + 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; + + *output = (char *) realloc(*output, output_buff_size * + sizeof(char)); + if (!*output) { + log_error("Reallocating string of hosts"); + return 1; + } + } + /* 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; } diff --git a/src/neighbours.c b/src/neighbours.c index 92196df..827b94e 100644 --- a/src/neighbours.c +++ b/src/neighbours.c @@ -65,6 +65,7 @@ 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; @@ -98,6 +99,9 @@ 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); } diff --git a/src/neighbours.h b/src/neighbours.h index be00912..332833b 100644 --- a/src/neighbours.h +++ b/src/neighbours.h @@ -29,6 +29,9 @@ /** 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 { @@ -37,6 +40,8 @@ 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. */ diff --git a/src/p2p.c b/src/p2p.c index 6956a77..512c1a8 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,6 +28,7 @@ #include #include "crypto.h" +#include "daemon_messages_processor.h" #include "global_state.h" #include "hosts.h" #include "linkedlist.h" @@ -49,32 +48,6 @@ static void ip_to_string(const struct in6_addr *binary_ip, char *ip) inet_ntop(AF_INET6, binary_ip, ip, INET6_ADDRSTRLEN); } -/** - * 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, - DEFAULT_PORT, - HOST_AVAILABLE); - } - line = strtok_r(NULL, delim, &save_ptr); - } -} - /** * Processing a P2P message. * @@ -83,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; @@ -101,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); } /** -- GitLab From b296ed5f79e63592371651237cc138b3cf17c047 Mon Sep 17 00:00:00 2001 From: xpetrak2 <456484@mail.muni.cz> Date: Sun, 30 Sep 2018 12:24:24 +0200 Subject: [PATCH 4/5] A few changes for the P2P overlay implementation README renamed to README.md Peer functions renaming nonces_get_first to nonces_get_oldest nonces_get_last to nonces_get_newest Inversed conditions, swapped branches More effective parity check Initial value of identity's nonce halved Route stale time increased --- README => README.md | 10 +- src/daemon_messages_processor.c | 45 +++++---- src/neighbours.c | 2 +- src/peers.c | 40 ++++---- src/peers.h | 4 +- src/routing.c | 174 +++++++++++++++----------------- src/routing.h | 2 +- 7 files changed, 134 insertions(+), 143 deletions(-) rename README => README.md (70%) diff --git a/README b/README.md similarity index 70% rename from README rename to README.md index ff62c91..42aad63 100644 --- a/README +++ b/README.md @@ -3,10 +3,14 @@ ## 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: A tuple of a peer-to-peer message's nonce and a pointer to a neighbour from which we've received the message. The message traces are being used for routing loop detection +- Identity: A peer representation that we own (identifier + its corresponding + secret key) +- Message trace: A tuple of a peer-to-peer message's nonce and a pointer to a + neighbour from which we've received the message. 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 +- 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_messages_processor.c b/src/daemon_messages_processor.c index eff53f3..86e363a 100644 --- a/src/daemon_messages_processor.c +++ b/src/daemon_messages_processor.c @@ -234,8 +234,8 @@ static enum process_message_result process_message( /* 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 % 2 == 0) || - (cmp_val < 0 && nonce_value % 2 == 1)) { + if ((cmp_val > 0 && !(nonce_value & 0x01)) || + (cmp_val < 0 && (nonce_value & 0x01))) { log_debug("process_mesage - wrong nonce parity"); return PMR_ERR_SEMANTIC; } @@ -279,8 +279,8 @@ static enum process_message_result process_message( } /* if the message smells like replay attack */ if (!linkedlist_empty(&sender_peer->nonces) && - nonce_value < - (nonces_get_first(&sender_peer->nonces))->value) { + nonce_value < + (nonces_get_oldest(&sender_peer->nonces))->value) { log_warn("Potential replay attack detected"); return PMR_ERR_SEMANTIC; } @@ -648,29 +648,30 @@ static int process_p2p_route_sol(const message_t *message, identity_t *identity; p2p_route_sol_t *route_sol; - route_sol = (p2p_route_sol_t *) message->body.data; + 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, sender, global_state)) { - /* if someone's looking for us */ - if (identity) { - if (difftime(identity->last_adv, - time(NULL)) > 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; + if (message_forward(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); } - - return 0; + /* 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 1; + return 0; } diff --git a/src/neighbours.c b/src/neighbours.c index 827b94e..08c58bb 100644 --- a/src/neighbours.c +++ b/src/neighbours.c @@ -71,7 +71,7 @@ neighbour_t *add_new_neighbour(linkedlist_t *neighbours, new_neighbour->host = NULL; /* create our pseudonym */ generate_keypair(&new_neighbour->my_pseudonym.keypair); - new_neighbour->my_pseudonym.nonce_value = get_random_uint64_t(); + new_neighbour->my_pseudonym.nonce_value = get_random_uint64_t() >> 1; /* initialize neighbour's pseudonym */ memset(new_neighbour->pseudonym.identifier, 0x0, diff --git a/src/peers.c b/src/peers.c index ae4cdc3..abea46c 100644 --- a/src/peers.c +++ b/src/peers.c @@ -161,6 +161,7 @@ int nonce_store(linkedlist_t *nonces, uint64_t value) } nonce->value = value; + nonce->creation = time(NULL); /* place the nonce into appropriate position */ cur_node = linkedlist_get_last(nonces); @@ -168,9 +169,7 @@ int nonce_store(linkedlist_t *nonces, uint64_t value) cur_nonce = (nonce_t *) cur_node->data; /* appropriate position found */ if (value > cur_nonce->value) { - if (cur_node == linkedlist_get_last(nonces)) { - nonce->creation = time(NULL); - } else { + if (cur_node != linkedlist_get_last(nonces)) { nonce->creation = cur_nonce->creation; } @@ -188,20 +187,14 @@ int nonce_store(linkedlist_t *nonces, uint64_t value) /* 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)) { - nonce->creation = time(NULL); - - if (!linkedlist_insert_after(nonces, &nonces->first, nonce)) { - log_error("Storing a nonce"); - free(nonce); - return 1; - } - - return 0; + if (!linkedlist_empty(nonces) || + !linkedlist_insert_after(nonces, &nonces->first, nonce)) { + log_error("Storing a nonce"); + free(nonce); + return 1; } - free(nonce); - return 1; + return 0; } /** @@ -234,34 +227,35 @@ nonce_t *nonces_find(const linkedlist_t *nonces, uint64_t value) } /** - * Get the first nonce (the nonce with the lowest value) from a list of nonces. + * Get the newest nonce (the nonce with the highest value) from a list + * of nonces. * * @param nonces Use these nonces. * - * @return nonce_t The first nonce. + * @return nonce_t The last nonce. * @return NULL The nonces are empty. */ -nonce_t *nonces_get_first(const linkedlist_t *nonces) +nonce_t *nonces_get_newest(const linkedlist_t *nonces) { linkedlist_node_t *node; - if ((node = linkedlist_get_first(nonces))) { + if ((node = linkedlist_get_last(nonces))) { return node->data; } return NULL; } /** - * Get the last nonce (the nonce with the highest value) from a list of nonces. + * Get the oldest nonce (the nonce with the lowest value) from a list of nonces. * * @param nonces Use these nonces. * - * @return nonce_t The last nonce. + * @return nonce_t The first nonce. * @return NULL The nonces are empty. */ -nonce_t *nonces_get_last(const linkedlist_t *nonces) +nonce_t *nonces_get_oldest(const linkedlist_t *nonces) { linkedlist_node_t *node; - if ((node = linkedlist_get_last(nonces))) { + if ((node = linkedlist_get_first(nonces))) { return node->data; } return NULL; diff --git a/src/peers.h b/src/peers.h index 7beda54..e87c09f 100644 --- a/src/peers.h +++ b/src/peers.h @@ -76,8 +76,8 @@ 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_first(const linkedlist_t *nonces); -nonce_t *nonces_get_last(const linkedlist_t *nonces); +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); diff --git a/src/routing.c b/src/routing.c index fc015e5..deead77 100644 --- a/src/routing.c +++ b/src/routing.c @@ -180,20 +180,20 @@ static int message_finalize(message_t *message, message->body.to, crypto_box_PUBLICKEYBYTES); /* from ID > to ID => odd nonce; from ID < to ID => even nonce */ - if ((cmp_val > 0 && nonce_value % 2 == 0) || - (cmp_val < 0 && nonce_value % 2 == 1)) { + 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)) { - sign_message(json_body, from->keypair.secret_key, message->sig); - free(json_body); - return 0; + if (encode_message_body(&message->body, &json_body)) { + return 1; } - return 1; + sign_message(json_body, from->keypair.secret_key, message->sig); + free(json_body); + return 0; } /** @@ -240,14 +240,14 @@ int message_forward(const message_t *message, */ static int message_send_n2n(message_t *message, neighbour_t *dest) { - if (!message_finalize(message, - &dest->my_pseudonym, - dest->pseudonym.identifier, - DV_HIDDEN)) { - return message_send_to_neighbour(message, dest); + if (message_finalize(message, + &dest->my_pseudonym, + dest->pseudonym.identifier, + DV_HIDDEN)) { + return 1; } - return 1; + return message_send_to_neighbour(message, dest); } /** @@ -632,22 +632,21 @@ int send_p2p_bye(linkedlist_t *neighbours, identity_t *identity) message_t p2p_bye_msg; int ret; - if (!create_p2p_bye(&p2p_bye_msg)) { - 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; + 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); } - return 1; + message_delete(&p2p_bye_msg); + return ret; } /** @@ -664,14 +663,13 @@ 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)) { - ret = message_send_n2n(&p2p_hello_msg, dest); - - message_delete(&p2p_hello_msg); - return ret; + if (create_p2p_hello(&p2p_hello_msg, port)) { + return 1; } + ret = message_send_n2n(&p2p_hello_msg, dest); - return 1; + message_delete(&p2p_hello_msg); + return ret; } /** @@ -688,14 +686,13 @@ 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)) { - ret = message_send_n2n(&p2p_peers_adv_msg, dest); - - message_delete(&p2p_peers_adv_msg); - return ret; + if (create_p2p_peers_adv(&p2p_peers_adv_msg, hosts)) { + return 1; } + ret = message_send_n2n(&p2p_peers_adv_msg, dest); - return 1; + message_delete(&p2p_peers_adv_msg); + return ret; } /** @@ -711,16 +708,15 @@ 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)) { - 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; + 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); } - return 1; + message_delete(&p2p_peers_sol_msg); + return ret; } /** @@ -736,14 +732,13 @@ int send_p2p_ping(neighbour_t *dest) message_t p2p_ping_msg; int ret; - if (!create_p2p_ping(&p2p_ping_msg)) { - ret = message_send_n2n(&p2p_ping_msg, dest); - - message_delete(&p2p_ping_msg); - return ret; + if (create_p2p_ping(&p2p_ping_msg)) { + return 1; } + ret = message_send_n2n(&p2p_ping_msg, dest); - return 1; + message_delete(&p2p_ping_msg); + return ret; } /** @@ -759,14 +754,13 @@ int send_p2p_pong(neighbour_t *dest) message_t p2p_pong_msg; int ret; - if (!create_p2p_pong(&p2p_pong_msg)) { - ret = message_send_n2n(&p2p_pong_msg, dest); - - message_delete(&p2p_pong_msg); - return ret; + if (create_p2p_pong(&p2p_pong_msg)) { + return 1; } + ret = message_send_n2n(&p2p_pong_msg, dest); - return 1; + message_delete(&p2p_pong_msg); + return ret; } /** @@ -783,27 +777,26 @@ 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)) { - 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); - } + 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; } - return 1; + message_delete(&p2p_route_adv_msg); + return ret; } /** @@ -823,22 +816,21 @@ int send_p2p_route_sol(linkedlist_t *neighbours, message_t p2p_route_sol_msg; int ret; - if (!create_p2p_route_sol(&p2p_route_sol_msg, target)) { - 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; + 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); } - return 1; + message_delete(&p2p_route_sol_msg); + return ret; } /** diff --git a/src/routing.h b/src/routing.h index c78ad73..ff2ffb3 100644 --- a/src/routing.h +++ b/src/routing.h @@ -32,7 +32,7 @@ /** 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 60 +#define ROUTE_STALE_TIME 180 /** A message trace representation. */ typedef struct s_message_trace { -- GitLab From 1f0ff7fd8b6060c8441e3fffaefec9bdeac3e3ed Mon Sep 17 00:00:00 2001 From: xpetrak2 <456484@mail.muni.cz> Date: Thu, 4 Oct 2018 14:26:02 +0200 Subject: [PATCH 5/5] Code fixes for the P2P overlay implementation Adding new route fix Fix of the hosts to string function Message trace 'from' inclusion Fixed encoded message handling --- README.md | 8 +- src/daemon_messages_processor.c | 68 +++++++++------- src/daemon_messages_processor.h | 8 +- src/hosts.c | 7 +- src/json_parser.c | 13 ++-- src/json_parser.h | 4 +- src/routing.c | 133 ++++++++++++++++++++++++-------- src/routing.h | 7 +- 8 files changed, 171 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index 42aad63..8897953 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ - 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: A tuple of a peer-to-peer message's nonce and a pointer to a - neighbour from which we've received the message. The message traces are - being used for routing loop detection +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 diff --git a/src/daemon_messages_processor.c b/src/daemon_messages_processor.c index 86e363a..3e1168b 100644 --- a/src/daemon_messages_processor.c +++ b/src/daemon_messages_processor.c @@ -36,12 +36,14 @@ * presence of one identity. */ #define ADV_GAP_TIME 10 -static enum process_message_result process_message( - const message_t *message, - neighbour_t *sender, - global_state_t *global_state); +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); @@ -57,10 +59,12 @@ static int process_p2p_peers_sol(neighbour_t *sender, 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); @@ -80,36 +84,32 @@ static int process_p2p_route_sol(const message_t *message, * @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) +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)) { + if (decode_message(json_message, &message, &json_message_body)) { log_debug("process_encoded_message - decoding a received " - "message has failed"); + "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; get message body in JSON format */ - if (encode_message_body(&message.body, &json_message_body)) { - log_error("Encoding what we've just decoded has failed"); - message_delete(&message); - return PMR_ERR_INTERNAL; - } - /* if the message integrity is violated */ + /* 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; @@ -118,7 +118,7 @@ enum process_message_result process_encoded_message( free(json_message_body); /* process the message */ - ret = process_message(&message, sender, global_state); + ret = process_message(&message, json_message, sender, global_state); message_delete(&message); return ret; @@ -128,6 +128,7 @@ enum process_message_result process_encoded_message( * 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. * @@ -136,10 +137,11 @@ enum process_message_result process_encoded_message( * @return PMR_ERR_INTERNAL Internal processing error. * @return PMR_ERR_SEMANTIC Semantic error. */ -static enum process_message_result process_message( - const message_t *message, - neighbour_t *sender, - global_state_t *global_state) +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; @@ -269,7 +271,8 @@ static enum process_message_result process_message( /* check if there's a routing loop */ if (routing_loop_detect(msg_traces, sender, - nonce_value)) { + nonce_value, + message->from)) { routing_loop_remove(routing_table, neighbours, identities, @@ -291,7 +294,10 @@ static enum process_message_result process_message( * not meant for us */ if (!identity) { /* if forwarding succeeded */ - if (!message_forward(message, sender, global_state)) { + if (!message_forward(message, + json_message, + sender, + global_state)) { /* store message's nonce */ nonce_store(&sender_peer->nonces, nonce_value); } @@ -306,6 +312,7 @@ static enum process_message_result process_message( switch (msg_type) { case P2P_BYE: res = process_p2p_bye(message, + json_message, sender, sender_peer, global_state); @@ -345,12 +352,14 @@ static enum process_message_result process_message( 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; @@ -380,6 +389,7 @@ static enum process_message_result process_message( * 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. @@ -387,6 +397,7 @@ static enum process_message_result process_message( * @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) @@ -394,7 +405,7 @@ static int process_p2p_bye(const message_t *message, route_delete(&global_state->routing_table, message->from); peer_delete(sender_peer); - message_forward(message, sender, global_state); + message_forward(message, json_message, sender, global_state); return 0; } @@ -588,6 +599,7 @@ static int process_p2p_pong(neighbour_t *sender) * 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. @@ -596,6 +608,7 @@ static int process_p2p_pong(neighbour_t *sender) * @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) @@ -627,7 +640,7 @@ static int process_p2p_route_adv(const message_t *message, return 1; } - message_forward(message, sender, global_state); + message_forward(message, json_message, sender, global_state); return 0; } @@ -635,6 +648,7 @@ static int process_p2p_route_adv(const message_t *message, * 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. * @@ -642,6 +656,7 @@ static int process_p2p_route_adv(const message_t *message, * @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) { @@ -654,6 +669,7 @@ static int process_p2p_route_sol(const message_t *message, /* 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; diff --git a/src/daemon_messages_processor.h b/src/daemon_messages_processor.h index bc76fcc..9bc0eff 100644 --- a/src/daemon_messages_processor.h +++ b/src/daemon_messages_processor.h @@ -32,9 +32,9 @@ enum process_message_result { PMR_ERR_VERSION }; -enum process_message_result process_encoded_message( - const char *json_message, - neighbour_t *sender, - global_state_t *global_state); +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/hosts.c b/src/hosts.c index 528e7ec..ff56f27 100644 --- a/src/hosts.c +++ b/src/hosts.c @@ -161,6 +161,7 @@ int hosts_to_str(const linkedlist_t *hosts, char **output) const char *separator = ", "; size_t separator_len; char text_ip[INET6_ADDRSTRLEN]; + char *tmp_out; opening_len = strlen(opening); separator_len = strlen(separator); @@ -191,12 +192,14 @@ int hosts_to_str(const linkedlist_t *hosts, char **output) if (output_buff_size - output_size < 128) { output_buff_size *= 2; - *output = (char *) realloc(*output, output_buff_size * + tmp_out = (char *) realloc(*output, output_buff_size * sizeof(char)); - if (!*output) { + if (!tmp_out) { log_error("Reallocating string of hosts"); + free(output); return 1; } + *output = tmp_out; } /* binary ip to text ip conversion */ diff --git a/src/json_parser.c b/src/json_parser.c index 88d5359..70d93f1 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 21c5635..36b5366 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/routing.c b/src/routing.c index deead77..a743f8f 100644 --- a/src/routing.c +++ b/src/routing.c @@ -51,8 +51,12 @@ static int message_send_to_neighbour(const message_t *message, static int message_trace_store(linkedlist_t *msg_traces, const neighbour_t *sender, - uint64_t nonce_value); + 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); @@ -200,6 +204,7 @@ static int message_finalize(message_t *message, * 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. * @@ -207,26 +212,35 @@ static int message_finalize(message_t *message, * @return 1 Failure. */ int message_forward(const message_t *message, + const char *json_message, const neighbour_t *sender, global_state_t *global_state) { - const message_body_t *msg_body; + neighbour_t *next_hop; + route_t *route; - msg_body = &message->body; - - /* if all 'to' bytes are set to 0x0, the message is a broadcast */ - if (identifier_empty(msg_body->to)) { + /* 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 */ - return message_broadcast_p2p(message, - &global_state->neighbours, - sender); + 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, - msg_body->nonce); + message->body.nonce, + message->from); - return message_send_p2p(message, &global_state->routing_table); + string_send_to_neighbour(json_message, next_hop); + return 0; } /** @@ -264,22 +278,17 @@ 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) { - neighbour_t *next_hop; - linkedlist_node_t *next_hop_node; - route_t *route; + neighbour_t *next_hop; + route_t *route; route = route_find(routing_table, message->body.to); - /* 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_node = route ? linkedlist_get_first(&route->next_hops) : NULL; - if (next_hop_node) { - next_hop = (neighbour_t *) next_hop_node->data; - - /* send the message to the next hop */ - return message_send_to_neighbour(message, next_hop); + next_hop = route ? route_next_hop_get(route) : NULL; + if (!next_hop) { + return 1; } - return 1; + /* send the message to the next hop */ + return message_send_to_neighbour(message, next_hop); } /** @@ -329,13 +338,15 @@ int message_trace_is_stale(const message_trace_t *msg_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) + uint64_t nonce_value, + const unsigned char *from) { message_trace_t *msg_trace; @@ -348,6 +359,7 @@ static int message_trace_store(linkedlist_t *msg_traces, 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"); @@ -381,12 +393,18 @@ route_t *route_add(linkedlist_t *routing_table, } linkedlist_init(&new_route->next_hops); - if (!(new_route->node = - linkedlist_append(&new_route->next_hops, next_hop))) { + 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); @@ -484,6 +502,26 @@ int route_next_hop_add(route_t *route, neighbour_t *next_hop) 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. * @@ -521,13 +559,15 @@ int route_reset(route_t *route, neighbour_t *next_hop) * @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) + uint64_t nonce_value, + const unsigned char *from) { const message_trace_t *msg_trace; const linkedlist_node_t *node; @@ -538,7 +578,8 @@ int routing_loop_detect(const linkedlist_t *msg_traces, /* the same message but different neighbour => routing loop */ if (msg_trace->nonce_value == nonce_value && - msg_trace->sender != neighbour) { + msg_trace->sender != neighbour && + !memcmp(msg_trace->from, from, crypto_box_PUBLICKEYBYTES)) { return 1; } @@ -565,20 +606,18 @@ void routing_loop_remove(linkedlist_t *routing_table, { identity_t *identity; neighbour_t *next_hop; - linkedlist_node_t *next_hop_node; 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_node = route ? linkedlist_get_first(&route->next_hops) : NULL; - if (next_hop_node) { - next_hop = (neighbour_t *) next_hop_node->data; + next_hop = route ? route_next_hop_get(route) : NULL; + if (next_hop) { route_next_hop_remove(route, next_hop); - next_hop_node = linkedlist_get_first(&route->next_hops); - if (!next_hop_node) { + 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 */ @@ -833,6 +872,32 @@ int send_p2p_route_sol(linkedlist_t *neighbours, 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. * diff --git a/src/routing.h b/src/routing.h index ff2ffb3..e5da617 100644 --- a/src/routing.h +++ b/src/routing.h @@ -38,6 +38,8 @@ 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. */ @@ -59,6 +61,7 @@ typedef struct s_route { } route_t; int message_forward(const message_t *message, + const char *json_message, const neighbour_t *sender, global_state_t *global_state); @@ -74,12 +77,14 @@ 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); + uint64_t nonce_value, + const unsigned char *from); void routing_loop_remove(linkedlist_t *routing_table, linkedlist_t *neighbours, linkedlist_t *identities, -- GitLab