diff --git a/Makefile.am b/Makefile.am index 5daab0d8f2de89dd34887f65e01a25a0e5c97631..dcf5574aca22ca9e863735d4f30320b3562ace02 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,9 +4,13 @@ bin_PROGRAMS = src/coincerd src/coincer src_coincerd_SOURCES = \ src/coincerd.c \ + src/configuration.c src/configuration.h \ src/linkedlist.c src/linkedlist.h \ + src/log.c src/log.h \ src/neighbours.c src/neighbours.h \ - src/p2p.c src/p2p.h + src/p2p.c src/p2p.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/src/coincerd.c b/src/coincerd.c index 141d749bbf4bac9f5dcd52058cff21955140ecc1..083fcfcf600295a4dd47251b069255a8098843d1 100644 --- a/src/coincerd.c +++ b/src/coincerd.c @@ -1,6 +1,6 @@ /* * Coincer - * Copyright (C) 2017 Coincer Developers + * 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 @@ -18,9 +18,18 @@ #include #include +#include +#include +#include +#include "log.h" #include "neighbours.h" #include "p2p.h" +#include "paths.h" +#include "peers.h" + +static void signal_cb(evutil_socket_t fd, short events, void *ctx); +static void conns_cb(evutil_socket_t fd, short events, void *ctx); int main(void) { @@ -42,21 +51,128 @@ int main(void) * - terminate on SIGTERM */ - struct evconnlistener *listener; - struct s_global_state global_state; + struct event *conns_event; + struct timeval conns_interval; + global_state_t global_state; + struct evconnlistener *listener; + struct event *sigint_event; + struct event *sigterm_event; - global_state.event_loop = NULL; - neighbours_init(&global_state.neighbours); + /* TODO: use randombytes (from libsodium?) for the seed of randomness */ + srand((unsigned) time(NULL)); - if (listen_init(&listener, &global_state) != 0) { + /* initialize all global_state variables */ + global_state.event_loop = NULL; + if (setup_paths(&global_state.filepaths)) { + log_error("Initializing paths to needed files/dirs"); return 1; } + linkedlist_init(&global_state.pending_neighbours); + linkedlist_init(&global_state.neighbours); + linkedlist_init(&global_state.peers); + + fetch_peers(global_state.filepaths.peers, &global_state.peers); + + /* setup everything needed for TCP listening */ + if (listen_init(&listener, &global_state) != 0) { + log_error("Initialization of TCP listening"); + return 2; + } + + /* register SIGINT event to its callback */ + sigint_event = evsignal_new(global_state.event_loop, + SIGINT, + signal_cb, + &global_state); + if (!sigint_event || event_add(sigint_event, NULL) < 0) { + log_error("Creating or adding SIGINT event"); + return 3; + } + + /* register SIGTERM event too */ + sigterm_event = evsignal_new(global_state.event_loop, + SIGTERM, + signal_cb, + &global_state); + if (!sigterm_event || event_add(sigterm_event, NULL) < 0) { + log_error("Creating or adding SIGTERM event"); + return 3; + } + + /* setup a function that actively checks the number of neighbours */ + conns_interval.tv_sec = 10; + conns_interval.tv_usec = 0; + + conns_event = event_new(global_state.event_loop, + -1, + EV_PERSIST, + conns_cb, + &global_state); + if (!conns_event || event_add(conns_event, &conns_interval) < 0) { + log_error("Creating or adding Connections event"); + return 3; + } + + /* initiate joining the coincer network */ + add_more_connections(&global_state, MIN_NEIGHBOURS); + + /* start the event loop */ event_base_dispatch(global_state.event_loop); - event_base_free(global_state.event_loop); - evconnlistener_free(listener); + /* SIGINT received, loop terminated; the clean-up part */ + clear_neighbours(&global_state.pending_neighbours); clear_neighbours(&global_state.neighbours); + /* store peers into 'peers' file before cleaning them */ + store_peers(global_state.filepaths.peers, &global_state.peers); + clear_peers(&global_state.peers); + clear_paths(&global_state.filepaths); + + evconnlistener_free(listener); + event_free(sigint_event); + event_free(sigterm_event); + event_free(conns_event); + event_base_free(global_state.event_loop); return 0; } + +/** + * Callback function for a received signal. + * + * @param signal What signal was invoked. + * @param events Flags of the event occurred. + * @param ctx Global state. + */ +static void signal_cb(evutil_socket_t signal __attribute__((unused)), + short events __attribute__((unused)), + void *ctx) +{ + global_state_t *global_state = (global_state_t *) ctx; + + event_base_loopexit(global_state->event_loop, NULL); +} + +/** + * Actively check the number of neighbours and add more if needed. + * + * @param fd File descriptor. + * @param events Event flags. + * @param ctx Global state. + */ +static void conns_cb(int fd __attribute__((unused)), + short events __attribute__((unused)), + void *ctx) +{ + int needed_conns; + + global_state_t *global_state = (global_state_t *) ctx; + needed_conns = MIN_NEIGHBOURS - + linkedlist_size(&global_state->neighbours); + if (needed_conns > 0) { + log_debug("conns_cb - we need %d more neighbours"); + /* ask twice more peers than we need; it's preferable + * to have more neighbours than minimum */ + add_more_connections(global_state, 2 * needed_conns); + } +} diff --git a/src/configuration.c b/src/configuration.c new file mode 100644 index 0000000000000000000000000000000000000000..ecdf44a207d394dabcfca4452470706eaa3b7717 --- /dev/null +++ b/src/configuration.c @@ -0,0 +1,194 @@ +/* + * 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 "autoconfig.h" +#include "configuration.h" +#include "log.h" + +/** + * Set home directory. + * + * @param homedir Path to homedir. + * + * @return 0 Path successfully set. + * @return 1 Home directory retrieval failure. + */ +static int set_homedir(char **homedir) +{ + *homedir = getenv("HOME"); + if (*homedir == NULL || *homedir[0] == '\0') { + log_error("Can not find home directory"); + return 1; + } + return 0; +} + +/** + * Set paths to config and data directories. + * + * @param config_dir Config directory. + * @param data_dir Data directory. + * + * @return 0 Paths successfully set. + * @return 1 Allocation failure. + */ +static int set_directories(char **config_dir, char **data_dir) +{ + char *homedir = NULL, + *tmpchar = NULL; + size_t tmpsize; + + /* configuration directory */ + tmpchar = getenv("XDG_CONFIG_HOME"); + if (tmpchar == NULL || tmpchar[0] == '\0') { + if (set_homedir(&homedir)) { + return 1; + } + tmpsize = strlen(homedir); + *config_dir = (char *) malloc(tmpsize + + sizeof("/.config/" PACKAGE_NAME + "/")); + if (*config_dir == NULL) { + log_error("Setting configdir"); + return 1; + } + strcpy(*config_dir, homedir); + strcpy(*config_dir + tmpsize, "/.config/" PACKAGE_NAME "/"); + } else { + tmpsize = strlen(tmpchar); + *config_dir = (char *) malloc(tmpsize + + sizeof("/" PACKAGE_NAME "/")); + if (*config_dir == NULL) { + log_error("Setting configdir"); + return 1; + } + strcpy(*config_dir, tmpchar); + strcpy(*config_dir + tmpsize, "/" PACKAGE_NAME "/"); + } + + /* data directory */ + tmpchar = getenv("XDG_DATA_HOME"); + if (tmpchar == NULL || tmpchar[0] == '\0') { + if (homedir == NULL && set_homedir(&homedir)) { + free(config_dir); + return 1; + } + tmpsize = strlen(homedir); + *data_dir = (char *) malloc(tmpsize + + sizeof("/.local/share/" PACKAGE_NAME + "/")); + if (*data_dir == NULL) { + log_error("Setting datadir"); + free(config_dir); + return 1; + } + strcpy(*data_dir, homedir); + strcpy(*data_dir + tmpsize, "/.local/share/" PACKAGE_NAME "/"); + } else { + tmpsize = strlen(tmpchar); + *data_dir = (char *) malloc(tmpsize + + sizeof("/" PACKAGE_NAME "/")); + if (*data_dir == NULL) { + log_error("Setting datadir"); + free(config_dir); + return 1; + } + strcpy(*data_dir, tmpchar); + strcpy(*data_dir + tmpsize, "/" PACKAGE_NAME "/"); + } + + return 0; +} + +/** + * Creates directories if they don't exist yet. + * + * @param config_dir Config directory. + * @param data_dir Data directory. + * + * @return 0 Directories created. + * @return 1 Creating directories failed. + */ +static int create_dirs(const char *config_dir, const char *data_dir) +{ + struct stat buffer; + + /* configuration directory */ + if (stat(config_dir, &buffer)) { + if (errno == ENOENT) { + /* create */ + if (mkdir(config_dir, S_IRWXU)) { + log_error("Could not create configuration " + "directory %s", config_dir); + return 1; + } else { + log_debug("create_dirs - created configuration " + "directory %s", config_dir); + } + } else { + log_error("Could not open configuration " + "directory %s", config_dir); + return 1; + } + } + + /* data directory */ + if (stat(data_dir, &buffer)) { + if (errno == ENOENT) { + /* create */ + if (mkdir(data_dir, S_IRWXU)) { + log_error("Could not create " + "data directory %s", data_dir); + return 1; + } else { + log_debug("create_dirs - created data " + "directory %s", data_dir); + } + } else { + log_error("Could not open data directory %s", data_dir); + return 1; + } + } + + return 0; +} + +/** + * Create needed directories and fetch their paths into params. + * + * @param config_dir Config directory. + * @param data_dir Data directory. + * + * @return 0 Directories created. + * @return 1 Directories setup failure. + */ +int setup_directories(char **config_dir, char **data_dir) +{ + if (set_directories(config_dir, data_dir) || + create_dirs(*config_dir, *data_dir)) { + return 1; + } + + return 0; +} diff --git a/src/configuration.h b/src/configuration.h new file mode 100644 index 0000000000000000000000000000000000000000..34d6b7cf589aec4c5393568b141acbe97a1b69b5 --- /dev/null +++ b/src/configuration.h @@ -0,0 +1,24 @@ +/* + * 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 CONFIGURATION_H +#define CONFIGURATION_H + +int setup_directories(char **config_dir, char **data_dir); + +#endif /* CONFIGURATION_H */ diff --git a/src/linkedlist.c b/src/linkedlist.c index aff6aef82ec8ce60fac92757e11d746a461d2cdf..bd98ac667da22c1bb9903099029070a6fc4a16d8 100644 --- a/src/linkedlist.c +++ b/src/linkedlist.c @@ -1,6 +1,6 @@ /* * Coincer - * Copyright (C) 2017 Coincer Developers + * 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 @@ -20,30 +20,96 @@ #include #include "linkedlist.h" +#include "log.h" /** - * Initializes linked list to default values. + * Append new node at the end of the linked list. * - * @param root Linked list to initialize. + * @param root Root of the linked list. + * @param data Data of the new node. + * + * @return linkedlist_node_t Pointer to created node if succeeded. + * @return NULL If failure. */ -void linkedlist_init(linkedlist_t *root) +linkedlist_node_t *linkedlist_append(linkedlist_t *root, void *data) { - root->first.prev = NULL; - root->first.next = &root->last; - root->last.prev = &root->first; - root->last.next = NULL; + return linkedlist_insert_after(root, + root->last.prev, + data); +} - root->first.data = NULL; - root->last.data = NULL; +/** + * Delete node from the linked list. + * + * @param node Node to be deleted from the linked list. + */ +void linkedlist_delete(linkedlist_node_t *node) +{ + /* fix the links of neighbour nodes */ + node->prev->next = node->next; + node->next->prev = node->prev; + + /* node's data deletion part */ + if (node->data != NULL) { + free(node->data); + } + + /* node deletion part */ + free(node); } /** - * Get first node of the linked list. + * Destroys a linked list. * - * @param root Root of the linked list. + * @param root Root of the linked list to destroy. + */ +void linkedlist_destroy(linkedlist_t *root) +{ + linkedlist_node_t *tmp; + + tmp = root->first.next; + while (tmp->next != NULL) { + tmp = tmp->next; + if (tmp->prev->data != NULL) { + free(tmp->prev->data); + } + free(tmp->prev); + } +} + +/** + * Find node in the linked list by data. + * + * @param root Root of the linked list. + * @param data Data of the requested node. + * + * @return linkedlist_node_t Node containing 'data'. + * @return NULL If the node doesn't exist. + */ +linkedlist_node_t *linkedlist_find(const linkedlist_t *root, const void *data) +{ + /* start the search from the first node of the linked list */ + linkedlist_node_t *current = linkedlist_get_first(root); + while (current != NULL) { + /* node found */ + if (current->data == data) { + /* return the node containing 'data' */ + return current; + } + /* move to the next node and continue with the search */ + current = linkedlist_get_next(root, current); + } + /* node not found */ + return NULL; +} + +/** + * Get the first node of the linked list. * - * @return First node of the linked list. - * @return NULL if linkedlist is empty. + * @param root Root of the linked list. + * + * @return linkedlist_node_t First node of the linked list. + * @return NULL If the linkedlist is empty. */ linkedlist_node_t *linkedlist_get_first(const linkedlist_t *root) { @@ -55,12 +121,12 @@ linkedlist_node_t *linkedlist_get_first(const linkedlist_t *root) } /** - * Get last node of the linkedlist. + * Get the last node of the linkedlist. * - * @param root Root of the linked list. + * @param root Root of the linked list. * - * @return Last node of the linked list. - * @return NULL if linkedlist is empty. + * @return linkedlist_node_t Last node of the linked list. + * @return NULL If linkedlist is empty. */ linkedlist_node_t *linkedlist_get_last(const linkedlist_t *root) { @@ -72,13 +138,14 @@ linkedlist_node_t *linkedlist_get_last(const linkedlist_t *root) } /** - * Get node that is right after 'node' in the linked list. + * Get a node that is right after 'node' in the linked list. * - * @param root Root of the linked list. - * @param node We want node that is right after this node. + * @param root Root of the linked list. + * @param node A node right after this node. * - * @return The node we requested. - * @return NULL if 'node' was last in the linked list or NULL. + * @return linkedlist_node_t The node we requested. + * @return NULL If 'node' was the last or 'root' + * is empty. */ linkedlist_node_t *linkedlist_get_next(const linkedlist_t *root, const linkedlist_node_t *node) @@ -91,110 +158,116 @@ linkedlist_node_t *linkedlist_get_next(const linkedlist_t *root, } /** - * Destroys a linked list. + * Get a node that is right before 'node' in the linked list. * - * @param root Root of the linked list to destroy. + * @param root Root of the linked list. + * @param node A node right before this node. + * + * @return linkedlist_node The node we requested. + * @return NULL If 'node' was the first or 'root' + * is empty. */ -void linkedlist_destroy(linkedlist_t *root) +linkedlist_node_t *linkedlist_get_prev(const linkedlist_t *root, + const linkedlist_node_t *node) { - linkedlist_node_t *tmp; - - tmp = root->first.next; - while (tmp->next != NULL) { - tmp = tmp->next; - free(tmp->prev); + if (node == NULL || node->prev == &root->first) { + return NULL; } + + return node->prev; } /** - * Append new node at the end of the linked list. - * - * @param root Root of the linked list. - * @param data Data to append to the list. + * Initializes linked list to default values. * - * @return Pointer to created node if succeeded. - * @return NULL if failed. + * @param root Linked list to initialize. */ -linkedlist_node_t *linkedlist_append(linkedlist_t *root, void *data) +void linkedlist_init(linkedlist_t *root) { - linkedlist_node_t *node; - - if ((node = (linkedlist_node_t *) malloc(sizeof(linkedlist_node_t))) == - NULL) { - perror("linkedlist_append malloc"); - return NULL; - } - - node->data = data; - - node->prev = root->last.prev; - node->next = &root->last; - root->last.prev->next = node; - root->last.prev = node; + root->first.prev = NULL; + root->first.next = &root->last; + root->last.prev = &root->first; + root->last.next = NULL; - return node; + root->first.data = NULL; + root->last.data = NULL; } /** - * Delete node from the linked list. + * Insert new node into linkedlist after 'node'. In case of 'node' being the + * last (stub) node, insert before it. * - * @param root Root of the linked list. - * @param node Node to be deleted from the linked list. + * @param root Root of the linked list. + * @param node Insert new node right after this node. + * @param data Data of the new node. + * + * @return linkedlist_node_t Pointer to the new node. + * @return NULL If failure. */ -void linkedlist_delete(linkedlist_t *root, linkedlist_node_t *node) +linkedlist_node_t *linkedlist_insert_after(linkedlist_t *root, + linkedlist_node_t *node, + void *data) { - /* start the search from the first node of the linked list */ - linkedlist_node_t *current = linkedlist_get_first(root); + linkedlist_node_t *new_node; - while (current != NULL) { + if (node == &root->last) { + node = root->last.prev; + } - /* node found */ - if (current == node) { + new_node = (linkedlist_node_t *) malloc(sizeof(linkedlist_node_t)); + if (new_node == NULL) { + log_error("Inserting a new node"); + return NULL; + } - /* fix the links of neighbour nodes */ - node->prev->next = node->next; - node->next->prev = node->prev; + new_node->data = data; - /* node deletion part */ - free(node); - node = NULL; + new_node->prev = node; + new_node->next = node->next; + node->next->prev = new_node; + node->next = new_node; - /* no need to continue */ - return; - } + return new_node; +} - /* go to next node and continue the search */ - current = linkedlist_get_next(root, current); +/** + * Insert new node into linkedlist before 'node'. In case of 'node' being + * the first (stub) node, insert after it. + * + * @param root Root of the linked list. + * @param node Insert new node right before this node. + * @param data Data of the new node. + * + * @return linkedlist_node_t Pointer to the new node. + * @return NULL If failure. + */ +linkedlist_node_t *linkedlist_insert_before(linkedlist_t *root, + linkedlist_node_t *node, + void *data) +{ + if (node == &root->first) { + node = root->first.next; } + return linkedlist_insert_after(root, node->prev, data); } /** - * Find node in the linked list by data. + * Get the number of elements in the linked list. * * @param root Root of the linked list. - * @param data Data of the requested node. * - * @return Node containing 'data'. - * @return NULL if the node doesn't exist. + * @return n Number of linked list elements. */ -linkedlist_node_t *linkedlist_find(const linkedlist_t *root, const void *data) +size_t linkedlist_size(const linkedlist_t *root) { - /* start the search from the first node of the linked list */ - linkedlist_node_t *current = linkedlist_get_first(root); + linkedlist_node_t *tmp; + size_t n = 0; - while (current != NULL) { - - /* node found */ - if (current->data == data) { - - /* return the node containing 'data' */ - return current; - } - - /* move to the next node and continue with the search */ - current = linkedlist_get_next(root, current); + tmp = root->first.next; + while (tmp->next != NULL) { + n++; + tmp = tmp->next; } - /* node not found */ - return NULL; + return n; } diff --git a/src/linkedlist.h b/src/linkedlist.h index c013bf55f81ada469151c9d2364bcfe1a96cd6b2..82f2ac55296891b4185b04f7a3b8e170cef65225 100644 --- a/src/linkedlist.h +++ b/src/linkedlist.h @@ -1,6 +1,6 @@ /* * Coincer - * Copyright (C) 2017 Coincer Developers + * 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 @@ -19,6 +19,8 @@ #ifndef LINKEDLIST_H #define LINKEDLIST_H +#include + /** * Node of the linked list structure. */ @@ -36,7 +38,13 @@ typedef struct s_linkedlist { struct s_linkedlist_node last; /**< Auxiliary last node. */ } linkedlist_t; -void linkedlist_init(linkedlist_t *root); +linkedlist_node_t *linkedlist_append(linkedlist_t *root, void *data); + +void linkedlist_delete(linkedlist_node_t *node); + +void linkedlist_destroy(linkedlist_t *root); + +linkedlist_node_t *linkedlist_find(const linkedlist_t *root, const void *data); linkedlist_node_t *linkedlist_get_first(const linkedlist_t *root); @@ -45,12 +53,19 @@ linkedlist_node_t *linkedlist_get_last(const linkedlist_t *root); linkedlist_node_t *linkedlist_get_next(const linkedlist_t *root, const linkedlist_node_t *node); -void linkedlist_destroy(linkedlist_t *root); +linkedlist_node_t *linkedlist_get_prev(const linkedlist_t *root, + const linkedlist_node_t *node); -linkedlist_node_t *linkedlist_append(linkedlist_t *root, void *data); +void linkedlist_init(linkedlist_t *root); -void linkedlist_delete(linkedlist_t *root, linkedlist_node_t *node); +linkedlist_node_t *linkedlist_insert_after(linkedlist_t *root, + linkedlist_node_t *node, + void *data); -linkedlist_node_t *linkedlist_find(const linkedlist_t *root, const void *data); +linkedlist_node_t *linkedlist_insert_before(linkedlist_t *root, + linkedlist_node_t *node, + void *data); + +size_t linkedlist_size(const linkedlist_t *root); #endif /* LINKEDLIST_H */ diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000000000000000000000000000000000000..56acae4cc26a77c55b8008768ede6df7962660a3 --- /dev/null +++ b/src/log.c @@ -0,0 +1,101 @@ +/* + * 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 "autoconfig.h" +#include "log.h" + +/** + * Logs debugging stuff to stdout, but only when compiled with debug enabled. + * + * @param msg Formatted message. + * @param ... Parameters to be included in the message. + */ +void log_debug(const char *msg, ...) +{ +#ifdef DEBUG + va_list args; + + fprintf(stdout, "[Debug] "); + + va_start(args, msg); + vfprintf(stdout, msg, args); + va_end(args); + + fprintf(stdout, "\n"); +#endif /* DEBUG */ +} + +/** + * Logs information to stdout. + * + * @param msg Formatted message. + * @param ... Parameters to be included in the message. + */ +void log_info(const char *msg, ...) +{ + va_list args; + + fprintf(stdout, "[Info] "); + + va_start(args, msg); + vfprintf(stdout, msg, args); + va_end(args); + + fprintf(stdout, "\n"); +} + +/** + * Logs warnings to stderr. + * + * @param msg Formatted message. + * @param ... Parameters to be included in the message. + */ +void log_warn(const char *msg, ...) +{ + va_list args; + + fprintf(stderr, "[Warning] "); + + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + + fprintf(stderr, "\n"); +} + +/** + * Logs errors to stderr. + * + * @param msg Formatted message. + * @param ... Parameters to be included in the message. + */ +void log_error(const char *msg, ...) +{ + va_list args; + + fprintf(stderr, "[Error] "); + + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); + + fprintf(stderr, "\n"); +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000000000000000000000000000000000000..141597729bdc7df071030500fed2e1de6331589c --- /dev/null +++ b/src/log.h @@ -0,0 +1,27 @@ +/* + * 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 LOG_H +#define LOG_H + +void log_debug(const char *msg, ...); +void log_info(const char *msg, ...); +void log_warn(const char *msg, ...); +void log_error(const char *msg, ...); + +#endif /* LOG_H */ diff --git a/src/neighbours.c b/src/neighbours.c index aae77e8f2207364c1b318213f57f321250735ee9..3c74f5a82c39769795326b9123c785cb9ae2303c 100644 --- a/src/neighbours.c +++ b/src/neighbours.c @@ -16,137 +16,90 @@ * along with this program. If not, see . */ +#include #include +#include #include #include #include #include "linkedlist.h" +#include "log.h" #include "neighbours.h" /** - * Initialize the linked list of neighbours to default values. - * - * @param neighbours Linked list to be initialized. - */ -void neighbours_init(linkedlist_t *neighbours) -{ - linkedlist_init(neighbours); -} - -/** - * Find neighbour in neighbours based on their bufferevent. - * - * @param neighbours Linked list of our neighbours. - * @param bev Neighbour's bufferevent. - * - * @return Desired neighbour. - * @return NULL if not found. - */ -neighbour_t *find_neighbour(const linkedlist_t *neighbours, - const struct bufferevent *bev) -{ - /* start the search from the first linked list node */ - const linkedlist_node_t *current = linkedlist_get_first(neighbours); - - while (current != NULL) { - - /* data of the 'current' node; struct s_neighbour */ - neighbour_t *current_data = (neighbour_t *) current->data; - - /* bufferevents equal => neighbour found */ - if (current_data->buffer_event == bev) { - - /* return node's data; struct s_neighbour */ - return current_data; - } - - /* get next node in the linked list */ - current = linkedlist_get_next(neighbours, current); - } - - return NULL; -} - -/** - * Find neighbour in neighbours based on their ip_addr. - * - * @param neighbours Linked list of our neighbours. - * @param ip_addr Binary ip address stored in 16 bytes. - * - * @return Desired neighbour. - * @return NULL if not found. - */ -neighbour_t *find_neighbour_by_ip(const linkedlist_t *neighbours, - const unsigned char *ip_addr) -{ - /* start the search from the first linked list node */ - const linkedlist_node_t *current = linkedlist_get_first(neighbours); - - while (current != NULL) { - - /* data of the 'current' node; struct s_neighbour */ - neighbour_t *current_data = (neighbour_t *) current->data; - - /* ip addresses match => neighbour found */ - if (memcmp(current_data->ip_addr, ip_addr, 16) == 0) { - - /* return node's data; struct s_neighbour */ - return current_data; - } - - /* get next node in the linked list */ - current = linkedlist_get_next(neighbours, current); - } - - return NULL; -} - -/** * Add new neighbour into neighbours. * - * @param neighbours Linked list of our neighbours. - * @param ip_addr Binary ip address stored in 16 bytes. + * @param neighbours Linked list of our neighbours. + * @param addr Binary ip address stored in 16 bytes. * @param bev Neighbour's bufferevent. * - * @return Newly added neighbour. - * @return NULL if neighbour already exists or allocation failure occured. + * @return neighbour_t Newly added neighbour. + * @return NULL If the neighbour already exists or allocation + * failure occured. */ neighbour_t *add_new_neighbour(linkedlist_t *neighbours, - const unsigned char *ip_addr, + const struct in6_addr *addr, struct bufferevent *bev) { - /* create new neighbour */ neighbour_t *new_neighbour; - new_neighbour = (neighbour_t *) malloc(sizeof(neighbour_t)); + /* create new neighbour */ + new_neighbour = (neighbour_t *) malloc(sizeof(neighbour_t)); /* allocation failure */ if (new_neighbour == NULL) { - /* WIP */ - perror("malloc for a new neighbour"); + log_error("add_new_neighbour - new_neighbour malloc"); return NULL; } /* don't add duplicates */ - if (find_neighbour_by_ip(neighbours, ip_addr)) { + if (find_neighbour_by_addr(neighbours, addr) || + find_neighbour(neighbours, bev)) { + free(new_neighbour); return NULL; } /* initialize new neighbour */ - memcpy(new_neighbour->ip_addr, ip_addr, 16); + memcpy(&new_neighbour->addr, addr, 16); new_neighbour->failed_pings = 0; new_neighbour->buffer_event = bev; - /* add new neighbour into linked list; NULL if allocation failed */ + /* add new neighbour into linked list; NULL if the allocation failed */ if (linkedlist_append(neighbours, new_neighbour) == NULL) { - /* WIP */ - perror("malloc for a new neighbour of linkedlist node"); + free(new_neighbour); return NULL; } return new_neighbour; } +/** + * Delete all neighbours. + * + * @param neighbours Linked list of our neighbours. + */ +void clear_neighbours(linkedlist_t *neighbours) +{ + neighbour_t *current; + linkedlist_node_t *current_node; + + current_node = linkedlist_get_first(neighbours); + /* safely delete data from the linked list nodes */ + while (current_node != NULL) { + /* load current node's data into 'current' */ + current = (neighbour_t *) current_node->data; + + /* deallocate neighbour's bufferevent */ + bufferevent_free(current->buffer_event); + + /* get next node in the linked list */ + current_node = linkedlist_get_next(neighbours, current_node); + } + + /* safely delete all nodes and their data in the linked list */ + linkedlist_destroy(neighbours); +} + /** * Delete neighbour from neighbours. * @@ -159,59 +112,149 @@ void delete_neighbour(linkedlist_t *neighbours, struct bufferevent *bev) linkedlist_node_t *neighbour_node; neighbour = find_neighbour(neighbours, bev); - /* no neighbour with bufferevent 'bev' => nothing to delete */ if (neighbour == NULL) { return; } neighbour_node = linkedlist_find(neighbours, neighbour); - /* since neighbour was found, neighbour_node should never be NULL */ - if (neighbour_node == NULL) { - /* WIP */ - perror("delete_neighbour"); - return; - } + assert(neighbour_node != NULL); /* free neighbour's bufferevent */ bufferevent_free(bev); - - /* free the linked list node's data; neighbour_t * */ - free(neighbour_node->data); - /* delete the neighbour from the linked list */ - linkedlist_delete(neighbours, neighbour_node); + linkedlist_delete(neighbour_node); } /** - * Delete all neighbours. + * Fetch pointers to neighbours with specific flags set, into an array + * that is being allocated in here. * - * @param neighbours Linked list of our neighbours. + * @param neighbours Our neighbours. + * @param output Output array of pointers to satisfying + * neighbours. If set to NULL, function just + * returns the number of them. + * @param flags Choose neighbours based on these flags. + * Fetches output with all neighbours if set to 0. + * + * @return >=0 The number of satisfying neighbours. + * @return -1 Allocation failure. */ -void clear_neighbours(linkedlist_t *neighbours) +int fetch_specific_neighbours(const linkedlist_t *neighbours, + neighbour_t ***output, + int flags) { - linkedlist_node_t *current_node = linkedlist_get_first(neighbours); + linkedlist_node_t *current_node; + neighbour_t *current_neighbour; + size_t n = 0; + + if (output != NULL) { + *output = (neighbour_t **) malloc(linkedlist_size(neighbours) * + sizeof(neighbour_t *)); + if (*output == NULL) { + log_error("Fetching specific neighbours"); + return -1; + } + } - /* safely delete data from the linked list nodes */ + current_node = linkedlist_get_first(neighbours); while (current_node != NULL) { + current_neighbour = (neighbour_t *) current_node->data; + /* if all specified flags are being set on this neighbour */ + if ((current_neighbour->flags & flags) == flags) { + if (output != NULL) { + (*output)[n++] = current_neighbour; + } else { + n++; + } + } + current_node = linkedlist_get_next(neighbours, current_node); + } - neighbour_t *current; + return n; +} - /* load current node's data into 'current' */ - current = (neighbour_t *) current_node->data; +/** + * Find neighbour in neighbours based on their bufferevent. + * + * @param neighbours Our neighbours. + * @param bev Neighbour's bufferevent. + * + * @return neighbour_t Requested neighbour. + * @return NULL If not found. + */ +neighbour_t *find_neighbour(const linkedlist_t *neighbours, + const struct bufferevent *bev) +{ + /* start the search from the first linked list node */ + const linkedlist_node_t *current = linkedlist_get_first(neighbours); - /* deallocate neighbour's bufferevent */ - bufferevent_free(current->buffer_event); + while (current != NULL) { + /* data of the 'current' node; struct s_neighbour */ + neighbour_t *current_data = (neighbour_t *) current->data; - /* deallocate data */ - free(current_node->data); - current_node->data = NULL; + /* bufferevents equal => neighbour found */ + if (current_data->buffer_event == bev) { + /* return node's data; struct s_neighbour */ + return current_data; + } + /* get next node in the linked list */ + current = linkedlist_get_next(neighbours, current); + } + /* neighbour not found */ + return NULL; +} +/** + * Find neighbour in neighbours based on their addr. + * + * @param neighbours Our neighbours. + * @param addr Binary ip address stored in 16 bytes. + * + * @return neighbour_t Requested neighbour. + * @return NULL If not found. + */ +neighbour_t *find_neighbour_by_addr(const linkedlist_t *neighbours, + const struct in6_addr *addr) +{ + /* start the search from the first linked list node */ + const linkedlist_node_t *current = linkedlist_get_first(neighbours); + + while (current != NULL) { + /* data of the 'current' node; struct s_neighbour */ + neighbour_t *current_data = (neighbour_t *) current->data; + + /* ip addresses match => neighbour found */ + if (memcmp(¤t_data->addr, addr, 16) == 0) { + /* return node's data; struct s_neighbour */ + return current_data; + } /* get next node in the linked list */ - current_node = linkedlist_get_next(neighbours, current_node); + current = linkedlist_get_next(neighbours, current); } + /* neighbour not found */ + return NULL; +} - /* safely delete all nodes in the linked list */ - linkedlist_destroy(neighbours); +/** + * Set flags on given neighbour. + * + * @param neighbour Set flags on this neighbour. + * @param flags Set these flags on the neighbour. + */ +void set_neighbour_flags(neighbour_t *neighbour, int flags) +{ + neighbour->flags |= flags; +} + +/** + * Unset flags on given neighbour. + * + * @param neighbour Unset flags on this neighbour. + * @param flags Unset these flags on the neighbour. + */ +void unset_neighbour_flags(neighbour_t *neighbour, int flags) +{ + neighbour->flags &= ~flags; } diff --git a/src/neighbours.h b/src/neighbours.h index 5ee91143bfbe563765e7fe942de0f199651395e8..79c048563dc47f6c7abafd78344f37382ef49fea 100644 --- a/src/neighbours.h +++ b/src/neighbours.h @@ -20,37 +20,49 @@ #define NEIGHBOURS_H #include +#include #include #include "linkedlist.h" -/** Data type for the linked list of neighbours. */ -typedef struct s_neighbour { - /**< Neighbours's IPv6 address; - * also allows storing IPv4-mapped IPv6 addresses. */ - unsigned char ip_addr[16]; +/* minimum number of peers we need to be connected to */ +#define MIN_NEIGHBOURS 3 - /**< Bufferevent belonging to this neighbour. */ - struct bufferevent *buffer_event; +/* request for addresses */ +#define NEIGHBOUR_ADDRS_REQ 0x01 - /**< Number of failed ping attempts -- max 3, then disconnect. */ - size_t failed_pings; +/** Data type for the linkedlist of neighbours. */ +typedef struct s_neighbour { + /** Neighbour's IPv6 address. + * Also allows storing IPv4-mapped IPv6 addresses. */ + struct in6_addr addr; + /** Bufferevent belonging to this neighbour. */ + struct bufferevent *buffer_event; + /** Number of failed ping attempts -- max 3, then disconnect. */ + size_t failed_pings; + /** A set of flags for this neighbour. */ + int flags; } neighbour_t; -void neighbours_init(linkedlist_t *neighbours); - -neighbour_t *find_neighbour(const linkedlist_t *neighbours, - const struct bufferevent *bev); - -neighbour_t *find_neighbour_by_ip(const linkedlist_t *neighbours, - const unsigned char *ip_addr); - neighbour_t *add_new_neighbour(linkedlist_t *neighbours, - const unsigned char *ip_addr, + const struct in6_addr *ip_addr, struct bufferevent *bev); +void clear_neighbours(linkedlist_t *neighbours); + void delete_neighbour(linkedlist_t *neighbours, struct bufferevent *bev); -void clear_neighbours(linkedlist_t *neighbours); +int fetch_specific_neighbours(const linkedlist_t *neighbours, + neighbour_t ***output, + int flags); + +neighbour_t *find_neighbour(const linkedlist_t *neighbours, + const struct bufferevent *bev); + +neighbour_t *find_neighbour_by_addr(const linkedlist_t *neighbours, + const struct in6_addr *ip_addr); + +void set_neighbour_flags(neighbour_t *neighbour, int flags); +void unset_neighbour_flags(neighbour_t *neighbour, int flags); #endif /* NEIGHBOURS_H */ diff --git a/src/p2p.c b/src/p2p.c index 903245712283b0097eb55a09c02945c5b8ba7f1e..d29221bae133336d98aea8458d474217b607bb06 100644 --- a/src/p2p.c +++ b/src/p2p.c @@ -1,6 +1,6 @@ /* * Coincer - * Copyright (C) 2017 Coincer Developers + * 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 @@ -16,7 +16,10 @@ * along with this program. If not, see . */ +#define _POSIX_SOURCE /* strtok_r */ + #include /* inet_ntop */ +#include #include #include #include @@ -25,10 +28,13 @@ #include #include #include +#include /* sleep */ #include "linkedlist.h" +#include "log.h" #include "neighbours.h" #include "p2p.h" +#include "peers.h" /** * Simple helper for conversion of binary IP to readable IP address. @@ -36,11 +42,55 @@ * @param binary_ip Binary represented IP address. * @param ip Readable IP address. */ -static void ip_to_string(unsigned char *binary_ip, char *ip) +static void ip_to_string(const struct in6_addr *binary_ip, char *ip) { inet_ntop(AF_INET6, binary_ip, ip, INET6_ADDRSTRLEN); } +/** + * Ask the 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 == NULL || (bev = neighbour->buffer_event) == NULL) { + return; + } + + /* send message "peers" to the neighbour, as a request + * for the list of peers; 6 is the number of bytes to be transmitted */ + evbuffer_add(bufferevent_get_output(bev), "peers", 6); + /* accept addresses only from those neighbours that we've asked */ + set_neighbour_flags(neighbour, NEIGHBOUR_ADDRS_REQ); +} + +/** + * Process received list of peer addresses. + * + * @param global_state Data for the event loop to work with. + * @param peers '\n'-separated list of peer addresses. + */ +static void process_peers(global_state_t *global_state, char *peers) +{ + struct in6_addr addr; + const char delim[2] = "\n"; + char *line; + char *save_ptr; + + line = strtok_r(peers, delim, &save_ptr); + + while (line != NULL) { + if (inet_pton(AF_INET6, line, &addr) == 1) { + save_peer(&global_state->peers, &addr); + } + line = strtok_r(NULL, delim, &save_ptr); + } +} + /** * Processing a P2P message. * @@ -49,53 +99,76 @@ static void ip_to_string(unsigned char *binary_ip, char *ip) */ static void p2p_process(struct bufferevent *bev, void *ctx) { - char *data; global_state_t *global_state; struct evbuffer *input; size_t len; + char *message; neighbour_t *neighbour; struct evbuffer *output; + char response[2048]; /* TODO: adjust size */ + char text_ip[INET6_ADDRSTRLEN]; global_state = (global_state_t *) ctx; - /* find the neighbour based on their buffer event */ + /* find the neighbour based on their bufferevent */ neighbour = find_neighbour(&global_state->neighbours, bev); - /* message from unknown neighbour; quit p2p_process */ - if (neighbour == NULL) { - return; - } + assert(neighbour != NULL); - /* refresh neighbour's failed pings */ + /* 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); - /* get length of the input messaage */ + /* get length of the input message */ len = evbuffer_get_length(input); /* allocate memory for the input message including '\0' */ - data = (char *) malloc((len + 1) * sizeof(char)); - - /* FOR TESTING PURPOSES */ + message = (char *) malloc((len + 1) * sizeof(char)); + if (message == NULL) { + log_error("Received message allocation"); + return; + } /* drain input buffer into data; -1 if evbuffer_remove failed */ - if (evbuffer_remove(input, data, len) == -1) { - free(data); + if (evbuffer_remove(input, message, len) == -1) { + free(message); return; } else { - data[len] = '\0'; - } + message[len] = '\0'; + } - printf("Sending back: %s", data); + ip_to_string(&neighbour->addr, text_ip); + log_debug("p2p_process - received: %s from %s", message, text_ip); - /* copy string data to the output buffer */ - evbuffer_add_printf(output, "%s", data); + response[0] = '\0'; + + /* TODO: Replace with JSON messages */ + if (strcmp(message, "ping") == 0) { + strcpy(response, "pong"); + /* ignore "pong" */ + } else if (strcmp(message, "pong") == 0) { + /* "peers" is a request for list of addresses */ + } else if (strcmp(message, "peers") == 0) { + peers_to_str(&global_state->peers, response); + /* list of addresses */ + } else { + if (neighbour->flags & NEIGHBOUR_ADDRS_REQ) { + process_peers(global_state, message); + unset_neighbour_flags(neighbour, NEIGHBOUR_ADDRS_REQ); + } + } + + if (response[0] != '\0') { + log_debug("p2p_process - responding with: %s", response); + /* copy response to the output buffer */ + evbuffer_add_printf(output, "%s", response); + } /* deallocate input message */ - free(data); + free(message); } /** @@ -110,76 +183,171 @@ static void timeout_process(linkedlist_t *neighbours, char text_ip[INET6_ADDRSTRLEN]; /* initialize text_ip */ - ip_to_string(neighbour->ip_addr, text_ip); + ip_to_string(&neighbour->addr, text_ip); /* the neighbour hasn't failed enough pings to be deleted */ if (neighbour->failed_pings < 3) { - /* bufferevent was disabled when timeout flag was set */ bufferevent_enable(neighbour->buffer_event, EV_READ | EV_WRITE | EV_TIMEOUT); - printf("Sending ping to %s. Failed pings: %lu\n", - text_ip, neighbour->failed_pings); - + 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 - */ + * 5 is the length of bytes to be transmitted */ evbuffer_add(bufferevent_get_output(neighbour->buffer_event), "ping", 5); neighbour->failed_pings++; } else { - printf("3 failed pings. Removing %s from neighbours\n", - text_ip); + log_info("%s timed out", text_ip); delete_neighbour(neighbours, neighbour->buffer_event); } } /** - * Callback for bufferevent event detection. + * Delete neighbour from pending and add it into neighbours. * - * @param bev bufferevent on which the event occured. - * @param events Flags of the events occured. - * @param ctx Pointer to global_state_t to determine the neighbour. + * @param global_state Global state. + * @param neighbour Neighbour to be moved. + * + * @param neighbour_t The new neighbour added into neighbours. + * @param NULL If adding failed. */ -static void event_cb(struct bufferevent *bev, short events, void *ctx) +static neighbour_t *move_neighbour_from_pending(global_state_t *global_state, + neighbour_t *neighbour) { - neighbour_t *neighbour; - char text_ip[INET6_ADDRSTRLEN]; + linkedlist_node_t *neighbour_node; + neighbour_t *new_neighbour; + + new_neighbour = add_new_neighbour(&global_state->neighbours, + &neighbour->addr, + neighbour->buffer_event); + /* if the add was unsuccessful, perform just the full delete */ + if (new_neighbour == NULL) { + bufferevent_free(neighbour->buffer_event); + } + neighbour_node = linkedlist_find(&global_state->pending_neighbours, + neighbour); + linkedlist_delete(neighbour_node); - global_state_t *global_state = (global_state_t *) ctx; + return new_neighbour; +} - /* find neighbour with 'bev' */ - neighbour = find_neighbour(&global_state->neighbours, bev); +/** + * Process the event that occurred on our pending neighbour. + * + * @param global_state Global state. + * @param neighbour The event occurred on this pending neighbour. + * @param events What event occurred. + */ +static void process_pending_neighbour(global_state_t *global_state, + neighbour_t *neighbour, + short events) +{ + int available_peers_size; + int needed_conns; + neighbour_t *new_neighbour; + char text_ip[INET6_ADDRSTRLEN]; - /* unknown neighbour; release 'bev' and stop processing the event */ - if (neighbour == NULL) { - bufferevent_free(bev); - return; + /* fetch peers with PEER_AVAILABLE flag set; + * no allocation with NULL parameter -> result always >=0 */ + available_peers_size = fetch_specific_peers(&global_state->peers, + NULL, + PEER_AVAILABLE); + /* initialize text_ip */ + ip_to_string(&neighbour->addr, text_ip); + + /* we've successfully connected to the neighbour */ + if (events & BEV_EVENT_CONNECTED) { + log_info("%s successfully connected", text_ip); + /* we've got a new neighbour; + * we can't just delete the neighbour from pending + * and add it into 'neighbours' as the delete would + * free'd the bufferevent + */ + new_neighbour = move_neighbour_from_pending(global_state, + neighbour); + if (new_neighbour == NULL) { + return; + } + needed_conns = MIN_NEIGHBOURS - + linkedlist_size(&global_state->neighbours); + /* if we need more neighbours */ + if (needed_conns > 0) { + /* and we don't have enough available */ + if (available_peers_size < needed_conns) { + ask_for_addresses(new_neighbour); + } + } + /* connecting to the neighbour was unsuccessful */ + } else { + log_debug("process_pending_neighbour - connecting to " + "%s was unsuccessful", text_ip); + /* the peer is no longer a pending neighbour */ + delete_neighbour(&global_state->pending_neighbours, + neighbour->buffer_event); } +} + +/** + * Process the event that occurred on our neighbour. + * + * @param global_state Global state. + * @param neighbour The event occurred on this neighbour. + * @param events What event occurred. + */ +static void process_neighbour(global_state_t *global_state, + neighbour_t *neighbour, + short events) +{ + char text_ip[INET6_ADDRSTRLEN]; /* initialize text_ip */ - ip_to_string(neighbour->ip_addr, text_ip); + ip_to_string(&neighbour->addr, text_ip); if (events & BEV_EVENT_ERROR) { - perror("Error from bufferevent"); - printf("%s was removed\n", text_ip); - delete_neighbour(&global_state->neighbours, bev); - return; - } - if (events & (BEV_EVENT_EOF)) { - printf("%s disconnected\n", text_ip); - delete_neighbour(&global_state->neighbours, bev); - return; - } - + log_info("Connection error, removing %s", text_ip); + delete_neighbour(&global_state->neighbours, + neighbour->buffer_event); + } else if (events & BEV_EVENT_EOF) { + log_info("%s disconnected", text_ip); + delete_neighbour(&global_state->neighbours, + neighbour->buffer_event); /* timeout flag on 'bev' */ - if (events & BEV_EVENT_TIMEOUT) { + } else if (events & BEV_EVENT_TIMEOUT) { timeout_process(&global_state->neighbours, neighbour); } } +/** + * Callback for bufferevent event detection. + * + * @param bev bufferevent on which the event occurred. + * @param events Flags of the events occurred. + * @param ctx Pointer to global_state_t to determine the neighbour. + */ +static void event_cb(struct bufferevent *bev, short events, void *ctx) +{ + neighbour_t *neighbour; + global_state_t *global_state = (global_state_t *) ctx; + + /* find neighbour with 'bev' */ + neighbour = find_neighbour(&global_state->neighbours, bev); + if (neighbour != NULL) { + process_neighbour(global_state, neighbour, events); + /* no such neighbour found; try finding it at pending_neighbours */ + } else { + neighbour = find_neighbour(&global_state->pending_neighbours, + bev); + /* 'bev' must belong to either 'neighbours' or + * 'pending_neighbours' */ + assert(neighbour != NULL); + process_pending_neighbour(global_state, neighbour, events); + } +} + /** * Callback function for accepting new connections. * @@ -194,60 +362,67 @@ static void accept_connection(struct evconnlistener *listener, int socklen __attribute__((unused)), void *ctx) { - struct event_base *base; struct bufferevent *bev; - unsigned char inet6_ip[sizeof(struct in6_addr)]; - struct timeval read_timeout; + struct in6_addr *new_addr; + peer_t *new_peer; char text_ip[INET6_ADDRSTRLEN]; - struct timeval write_timeout; + struct timeval timeout; global_state_t *global_state = (struct s_global_state *) ctx; struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *) addr; + /* put binary representation of IP to 'new_addr' */ + new_addr = &addr_in6->sin6_addr; + + ip_to_string(new_addr, text_ip); + + if (find_neighbour_by_addr(&global_state->pending_neighbours, + new_addr)) { + log_debug("accept_connection - peer %s already at " + "pending neighbours", text_ip); + return; + } + /* get the event_base */ base = evconnlistener_get_base(listener); /* setup a bufferevent for the new connection */ bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); - /* subscribe every received P2P message to be processed */ + /* subscribe every received P2P message to be processed; + * p2p_process for read callback, NULL for write callback */ bufferevent_setcb(bev, p2p_process, NULL, event_cb, ctx); bufferevent_enable(bev, EV_READ | EV_WRITE | EV_TIMEOUT); - /* after READ_TIMEOUT or WRITE_TIMEOUT seconds invoke event_cb */ - write_timeout.tv_sec = WRITE_TIMEOUT; - write_timeout.tv_usec = 0; - - read_timeout.tv_sec = READ_TIMEOUT; - read_timeout.tv_usec = 0; + timeout.tv_sec = TIMEOUT_TIME; + timeout.tv_usec = 0; - bufferevent_set_timeouts(bev, &read_timeout, &write_timeout); - - /* put binary representation of IP to inet6_ip */ - memcpy(inet6_ip, addr_in6->sin6_addr.s6_addr, sizeof(struct in6_addr)); + /* after TIMEOUT_TIME seconds invoke event_cb */ + bufferevent_set_timeouts(bev, &timeout, NULL); /* add the new connection to the list of our neighbours */ if (!add_new_neighbour(&global_state->neighbours, - inet6_ip, + new_addr, bev)) { - - /* free the bufferevent if adding failed */ + log_debug("accept_connection - adding failed"); bufferevent_free(bev); return; } - ip_to_string(inet6_ip, text_ip); - - printf("New connection from [%s]:%d\n", text_ip, + log_info("New connection from [%s]:%d", text_ip, ntohs(addr_in6->sin6_port)); + if ((new_peer = save_peer(&global_state->peers, new_addr))) { + /* we are now connected to this peer, hence unavailable */ + unset_peer_flags(new_peer, PEER_AVAILABLE); + } } /** * Callback for listener error detection. * - * @param listener Listener on which the error occured. + * @param listener Listener on which the error occurred. * @param ctx Pointer to global_state_t. */ static void accept_error_cb(struct evconnlistener *listener, @@ -257,10 +432,10 @@ static void accept_error_cb(struct evconnlistener *listener, global_state_t *global_state = (struct s_global_state *) ctx; int err = EVUTIL_SOCKET_ERROR(); - fprintf(stderr, "Got an error %d (%s) on the listener. " - "Shutting down.\n", err, evutil_socket_error_to_string(err)); - /* WIP */ + /* TODO: If we have enough connections, don't stop the event loop */ + log_error("Error %d (%s) on the listener, shutting down", + err, evutil_socket_error_to_string(err)); /* stop the event loop */ event_base_loopexit(base, NULL); @@ -268,14 +443,15 @@ static void accept_error_cb(struct evconnlistener *listener, /* delete neighbours */ clear_neighbours(&global_state->neighbours); } + /** * Initialize listening and set up callbacks. * - * @param listener The even loop listener. + * @param listener The event loop listener. * @param global_state Data for the event loop to work with. * - * @return 0 if successfully initialized. - * @return 1 if an error occured. + * @return 0 If successfully initialized. + * @return 1 If an error occurred. */ int listen_init(struct evconnlistener **listener, global_state_t *global_state) @@ -287,7 +463,7 @@ int listen_init(struct evconnlistener **listener, *base = event_base_new(); if (!*base) { - puts("Couldn't open event base"); + log_error("Creating eventbase"); return 1; } @@ -298,14 +474,14 @@ int listen_init(struct evconnlistener **listener, sock_addr.sin6_port = htons(port); *listener = evconnlistener_new_bind(*base, accept_connection, - global_state, - LEV_OPT_CLOSE_ON_FREE | - LEV_OPT_REUSEABLE, -1, - (struct sockaddr *) &sock_addr, - sizeof(sock_addr)); + global_state, + LEV_OPT_CLOSE_ON_FREE | + LEV_OPT_REUSEABLE, -1, + (struct sockaddr *) &sock_addr, + sizeof(sock_addr)); if (!*listener) { - perror("Couldn't create listener: evconnlistener_new_bind"); + log_error("Creating listener"); return 1; } @@ -313,3 +489,180 @@ int listen_init(struct evconnlistener **listener, return 0; } + +/** + * Attempt to connect to a particular addr. + * + * @param global_state Data for the event loop to work with. + * @param addr Binary IP of a peer that we want to connect to. + * + * @return 0 The connection attempt was successful. + * @return 1 The peer is already our neighbour. + * @return 2 The peer 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) +{ + struct bufferevent *bev; + peer_t *peer; + struct sockaddr *sock_addr; + struct sockaddr_in6 sock_addr6; + int sock_len; + char text_ip[INET6_ADDRSTRLEN]; + struct timeval timeout; + + /* get textual representation of the input ip address */ + ip_to_string(addr, text_ip); + + /* don't connect to already connected peer */ + if (find_neighbour_by_addr(&global_state->neighbours, addr) != NULL) { + log_debug("connect_to_addr - peer already connected"); + return 1; + } + + /* don't attempt to connect to already pending connection */ + if (linkedlist_find(&global_state->pending_neighbours, addr) != NULL) { + log_debug("connect_to_addr - peer is in the pending conns"); + return 2; + } + + 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_addr = (struct sockaddr *) &sock_addr6; + sock_len = sizeof(sock_addr6); + + /* it is safe to set file descriptor to -1 if we define it later */ + bev = bufferevent_socket_new(global_state->event_loop, + -1, + BEV_OPT_CLOSE_ON_FREE); + + /* subscribe every received P2P message to be processed; + * p2p_process as read callback, NULL as write callback */ + bufferevent_setcb(bev, + p2p_process, + NULL, + event_cb, + global_state); + + /* enable bufferevent for read, write and timeout */ + bufferevent_enable(bev, EV_READ | EV_WRITE | EV_TIMEOUT); + + /* set the timeout value */ + timeout.tv_sec = TIMEOUT_TIME; + timeout.tv_usec = 0; + + /* after TIMEOUT_TIME seconds invoke event_cb */ + bufferevent_set_timeouts(bev, &timeout, &timeout); + + /* add peer to the list of pending neighbours and let event_cb + * determine whether the peer is our neighbour now */ + if (!add_new_neighbour(&global_state->pending_neighbours, addr, bev)) { + log_debug("connect_to_addr - neighbour %s NOT ADDED into " + "pending neighbours", text_ip); + bufferevent_free(bev); + return 3; + } else { + log_debug("connect_to_addr - neighbour %s ADDED into " + "pending neighbours", text_ip); + } + + peer = find_peer_by_addr(&global_state->peers, addr); + if (peer != NULL) { + unset_peer_flags(peer, PEER_AVAILABLE); + } + + /* connect to the peer; socket_connect also assigns fd */ + bufferevent_socket_connect(bev, sock_addr, sock_len); + + return 0; +} + +/** + * Attempt to connect to more peers. + * + * @param global_state Data for the event loop to work with. + * @param conns_amount Prefered number of new connections. + */ +void add_more_connections(global_state_t *global_state, size_t conns_amount) +{ + struct in6_addr addr; + int available_peers_size; + peer_t **available_peers; + size_t cur_conn_attempts = 0; + size_t idx; + neighbour_t *neigh; + size_t result; + peer_t *selected_peer; + + available_peers_size = fetch_specific_peers(&global_state->peers, + &available_peers, + PEER_AVAILABLE); + /* if fetch_specific_peers had allocation failure */ + if (available_peers_size == -1) { + log_error("Adding more connections"); + return; + } + + /* only if we don't have any non-default peers available */ + if (available_peers_size == 0) { + log_debug("add_more_connections - " + "choosing random default peer"); + /* choose random default peer */ + idx = rand() % DEFAULT_PEERS_SIZE; + memcpy(&addr, DEFAULT_PEERS[idx], 16); + + result = connect_to_addr(global_state, &addr); + /* the connecting attempt was successful */ + if (result == 0) { + /* if the peer becomes our neighbour, + * and we need more connections, + * get a list of peers from them and + * attempt to connect to them; + * it's our goal to use as few + * default peers as possible + */ + log_debug("add_more_connections - " + "connect attempt succeeded"); + /* the peer is our neighbour; ask them for more addrs */ + } else if (result == 1) { + neigh = find_neighbour_by_addr(&global_state-> + neighbours, + &addr); + assert(neigh != NULL); + ask_for_addresses(neigh); + log_debug("add_more_connections - " + "asking for peers"); + /* the peer is a pending connection */ + } else if (result == 2) { + /* wait for them to reject/accept us */ + log_debug("add_more_connections - " + "pending, do nothing"); + } else { + log_debug("add_more_connections - " + "connect attempt didn't succeed"); + } + /* we've got some available peers */ + } else { + /* we need to choose 'conns_amount' of random connections */ + shuffle_peers_arr(available_peers, available_peers_size); + /* clamp to 'available_peers_size' */ + if (conns_amount > (size_t) available_peers_size) { + conns_amount = available_peers_size; + } + + while (cur_conn_attempts < conns_amount) { + idx = cur_conn_attempts; + selected_peer = available_peers[idx]; + /* perform a connection attempt */ + connect_to_addr(global_state, + &selected_peer->addr); + cur_conn_attempts++; + } + } + + free(available_peers); +} diff --git a/src/p2p.h b/src/p2p.h index 7ead35335255be015265e9b440ae33066daedc7f..49587a7e1f6df69e98fb1bc6df3657474db8a2b9 100644 --- a/src/p2p.h +++ b/src/p2p.h @@ -1,6 +1,6 @@ /* * Coincer - * Copyright (C) 2017 Coincer Developers + * 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 @@ -20,23 +20,39 @@ #define P2P_H #include +#include #include "linkedlist.h" +#include "neighbours.h" +#include "paths.h" +/* default port for TCP listening */ #define DEFAULT_PORT 31070 -/* after (READ/WRITE)_TIMEOUT seconds invoke timeout callback */ -#define READ_TIMEOUT 30 -#define WRITE_TIMEOUT 30 +/* after TIMEOUT_TIME seconds invoke timeout callback */ +#define TIMEOUT_TIME 30 /** * Event loop works with the data stored in an instance of this struct. */ typedef struct s_global_state { - struct event_base *event_loop; /**< For holding and polling events. */ - linkedlist_t neighbours; /**< Linked list of our neighbours. */ + /** For holding and polling events. */ + struct event_base *event_loop; + /** Holder of paths to needed files/dirs. */ + filepaths_t filepaths; + /** Linked list of our neighbours. */ + linkedlist_t neighbours; + /** Linked list of some peers in the network. */ + linkedlist_t peers; + /** Peers that didn't accept/reject us yet. */ + linkedlist_t pending_neighbours; } global_state_t; -int listen_init(struct evconnlistener **listener, - struct s_global_state *global_state); +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 listen_init(struct evconnlistener **listener, + global_state_t *global_state); #endif /* P2P_H */ diff --git a/src/paths.c b/src/paths.c new file mode 100644 index 0000000000000000000000000000000000000000..fa5b5f00ca3996e48f9ff596a6e0f50f96749b55 --- /dev/null +++ b/src/paths.c @@ -0,0 +1,87 @@ +/* + * 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 "configuration.h" +#include "log.h" +#include "paths.h" + +#define PEERS_FILE_NAME "peers" + +/** + * Sets path to peers file. + * + * @param data_dir Path to data dir. + * @param peers The file path of peers. + * + * @return 0 Path successfully set. + * @return 1 Allocation failure. + */ +static int set_peers_path(char *data_dir, char **peers) +{ + FILE *peers_file; + + /* size of data_dir + PEERS_FILE_NAME */ + *peers = (char *) malloc(strlen(data_dir) + + sizeof(PEERS_FILE_NAME)); + if (*peers == NULL) { + log_error("set_peers_path - peers file malloc"); + return 1; + } + + strcpy(*peers, data_dir); + strcat(*peers, PEERS_FILE_NAME); + + return 0; +} + +/** + * Initializes a filepaths_t instance. + * + * @param filepaths filepaths_t to be initialized. + * + * @return 0 Successfully initialized. + * @return 1 Setup failure. + */ +int setup_paths(filepaths_t *filepaths) +{ + if (setup_directories(&filepaths->config_dir, + &filepaths->data_dir)) { + return 1; + } + + set_peers_path(filepaths->data_dir, &filepaths->peers); + + return 0; +} + +/** + * Clear allocated path strings. + * + * @param filepaths filepaths_t to be cleared. + */ +void clear_paths(filepaths_t *filepaths) +{ + free(filepaths->config_dir); + free(filepaths->data_dir); + + free(filepaths->peers); +} diff --git a/src/paths.h b/src/paths.h new file mode 100644 index 0000000000000000000000000000000000000000..8587f0922e547efc2859294eefd52c9012019e62 --- /dev/null +++ b/src/paths.h @@ -0,0 +1,36 @@ +/* + * 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 PATHS_H +#define PATHS_H + +/** Paths to needed files and directories. */ +typedef struct s_filepaths { + /**< Path to config directory. */ + char *config_dir; + /**< Path to data directory. */ + char *data_dir; + + /**< Path to file with addresses of peers. */ + char *peers; +} filepaths_t; + +void clear_paths(filepaths_t *filepaths); +int setup_paths(filepaths_t *filepaths); + +#endif /* PATHS_H */ diff --git a/src/peers.c b/src/peers.c new file mode 100644 index 0000000000000000000000000000000000000000..d7e1436bce033d71709e7e10bee5406788878dd5 --- /dev/null +++ b/src/peers.c @@ -0,0 +1,343 @@ +/* + * 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 "linkedlist.h" +#include "log.h" +#include "peers.h" + +/** + * Delete all peers and their data. + * + * @param peers Linkedlist of peers. + */ +void clear_peers(linkedlist_t *peers) +{ + /* peer_t has no dynamically allocated variables */ + linkedlist_destroy(peers); +} + +/** + * Fetch peers from file into linkedlist. + * + * @param peers_path Path to peers file. + * @param peers Fetch loaded peers in here. + * + * @return 0 Successfully fetched. + * @return 1 Peers file could not be opened. + */ +int fetch_peers(const char *peers_path, linkedlist_t *peers) +{ + struct in6_addr addr; + FILE *peers_file; + + peers_file = fopen(peers_path, "rb"); + if (peers_file == NULL) { + log_warning("Peers file not found at %", peers_path); + return 1; + } + + while (fread(&addr, sizeof(struct in6_addr), 1, peers_file) == 1) { + save_peer(peers, &addr); + } + + fclose(peers_file); + return 0; +} + +/** + * Fetch pointers to peers with specific flags set, into array that is being + * allocated in here. + * + * @param peers All peers known to us. + * @param output Output array of pointers to satisfying peers. + * If set to NULL, function just returns + * the number of them. + * @param flags Choose peers based on these flags. + * Fetches output with all known peers if set to 0. + * + * @return >=0 The number of satisfying peers. + * @return -1 Allocation failure. + */ +int fetch_specific_peers(const linkedlist_t *peers, + peer_t ***output, + int flags) +{ + linkedlist_node_t *current_node; + peer_t *current_peer; + size_t n = 0; + + if (output != NULL) { + *output = (peer_t **) malloc(linkedlist_size(peers) * + sizeof(peer_t *)); + if (*output == NULL) { + log_error("Fetching specific peers"); + return -1; + } + } + + current_node = linkedlist_get_first(peers); + while (current_node != NULL) { + current_peer = (peer_t *) current_node->data; + /* if all specified flags are being set on this peer */ + if ((current_peer->flags & flags) == flags) { + if (output != NULL) { + (*output)[n++] = current_peer; + } else { + n++; + } + } + current_node = linkedlist_get_next(peers, current_node); + } + + return n; +} + +/** + * Find peer in the linkedlist by their address. + * + * @param peers Linkedlist of peers. + * @param addr Address of the peer we want. + * + * @return peer_t Requested peer. + * @return NULL Peer not found. + */ +peer_t *find_peer_by_addr(const linkedlist_t *peers, + const struct in6_addr *addr) +{ + const linkedlist_node_t *current = linkedlist_get_first(peers); + + while (current != NULL) { + peer_t *current_data = (peer_t *) current->data; + + /* ip addresses match => requested peer found */ + if (memcmp(¤t_data->addr, addr, 16) == 0) { + /* return node's data; struct s_peer */ + return current_data; + } + current = linkedlist_get_next(peers, current); + } + /* requested peer not found */ + return NULL; +} + +/* TODO: allocate memory for the 'output', don't assume any buffer size */ +/** + * '\n' separated output string of peer addresses in readable form. + * + * @param peers List of peers. + * @param output Output string. + */ +void peers_to_str(const linkedlist_t *peers, char *output) +{ + linkedlist_node_t *current_node; + peer_t *current_peer; + size_t output_size = 0; + char text_ip[INET6_ADDRSTRLEN]; + + output[0] = '\0'; + current_node = linkedlist_get_first(peers); + while (current_node != NULL) { + current_peer = (peer_t *) current_node->data; + + /* binary ip to text ip conversion */ + inet_ntop(AF_INET6, + ¤t_peer->addr, + text_ip, + INET6_ADDRSTRLEN); + output_size += strlen(text_ip); + strcat(output, text_ip); + + current_node = linkedlist_get_next(peers, current_node); + /* if it's not the last peer, append '\n' */ + if (current_node != NULL) { + output[output_size++] = '\n'; + } + output[output_size] = '\0'; + } +} + +/** + * Save new peer into sorted linkedlist of peers. + * + * @param peers The linkedlist of peers. + * @param addr Address of the new peer. + * + * @return peer_t Newly saved peer. + * @return NULL Peer is already saved, default or + * allocation failure. + */ +peer_t *save_peer(linkedlist_t *peers, const struct in6_addr *addr) +{ + int cmp_value; + struct in6_addr curr_addr; + linkedlist_node_t *current_node; + peer_t *current_peer; + int i; + linkedlist_node_t *new_node; + peer_t *new_peer; + char text_ip[INET6_ADDRSTRLEN]; + + /* save peer to the list of known peers, unless it's a default peer */ + for (i = 0; i < DEFAULT_PEERS_SIZE; i++) { + memcpy(&curr_addr, DEFAULT_PEERS[i], 16); + /* it's a default peer, don't save it into 'peers' */ + if (memcmp(&curr_addr, addr, 16) == 0) { + return NULL; + } + } + + /* allocate memory for a new peer */ + new_peer = (peer_t *) malloc(sizeof(peer_t)); + if (new_peer == NULL) { + log_error("Saving new peer"); + return NULL; + } + + /* initialize all attributes of the new peer */ + memcpy(&new_peer->addr, addr, 16); + set_peer_flags(new_peer, PEER_AVAILABLE); + + /* get textual representation of 'addr' */ + inet_ntop(AF_INET6, addr, text_ip, INET6_ADDRSTRLEN); + + /* insert 'new_peer' to its proper position in the sorted linkedlist; + * start from the last node of 'peers', as 'fetch_peers' (using this + * function) is likely to save in ascending order => better performance + */ + current_node = linkedlist_get_last(peers); + while (current_node != NULL) { + current_peer = (peer_t *) current_node->data; + + cmp_value = memcmp(&new_peer->addr, ¤t_peer->addr, 16); + /* the linkedlist already contains this peer */ + if (cmp_value == 0) { + free(new_peer); + return NULL; + } else if (cmp_value > 0) { + /* the proper position found */ + new_node = linkedlist_insert_after(peers, + current_node, + new_peer); + if (new_node != NULL) { + log_debug("save_peer - %s successfully saved", + text_ip); + } + return new_peer; + } + current_node = linkedlist_get_prev(peers, current_node); + } + /* the new peer's addr is lexicographically the lowest */ + new_node = linkedlist_insert_after(peers, &peers->first, new_peer); + if (new_node != NULL) { + log_debug("save_peer - %s successfully saved", text_ip); + } + + return new_peer; +} + +/** + * Set flags on given peer. + * + * @param peer Set flags on this peer. + * @param flags Set these flags on the peer. + */ +void set_peer_flags(peer_t *peer, int flags) +{ + peer->flags |= flags; +} + +/** + * Shuffle an input array of peers. + * + * @param peers The array of peers to be shuffled. + * @param peers_size The number of peers to be shuffled. + */ +void shuffle_peers_arr(peer_t **peers, size_t peers_size) +{ + size_t i, j; + peer_t *tmp; + + for (i = 0; i < peers_size; i++) { + /* don't swap with already swapped; + * swapping peers[i] with peers[j], where j is a random index + * such that j >= i AND j < peers_size + */ + j = i + rand() % (peers_size - i); + + tmp = peers[i]; + peers[i] = peers[j]; + peers[j] = tmp; + } +} + +/** + * Store peers from a linkedlist into a file. + * + * @param peers_path Path to 'peers' file. + * @param peers Linkedlist of the peers to be stored. + * + * @return 0 Successfully stored. + * @return 1 Failure. + */ +int store_peers(const char *peers_path, const linkedlist_t *peers) +{ + const linkedlist_node_t *current; + const peer_t *current_peer; + FILE *peers_file; + + peers_file = fopen(peers_path, "wb"); + if (peers_file == NULL) { + log_error("Can not create peers file at %s", peers_path); + return 1; + } + + current = linkedlist_get_first(peers); + while (current != NULL) { + current_peer = (peer_t *) current->data; + /* if fwrite fails, terminate storing */ + if (fwrite(¤t_peer->addr, + sizeof(struct in6_addr), + 1, + peers_file) != 1) { + log_error("Storing peers"); + fclose(peers_file); + return 1; + } + current = linkedlist_get_next(peers, current); + } + + fclose(peers_file); + return 0; +} + +/** + * Unset flags on given peer. + * + * @param peer Unset flags on this peer. + * @param flags Unset these flags on the peer. + */ +void unset_peer_flags(peer_t *peer, int flags) +{ + peer->flags &= ~flags; +} diff --git a/src/peers.h b/src/peers.h new file mode 100644 index 0000000000000000000000000000000000000000..5577f17b7d90b62ebba8fc1950714bb5b8ff1c21 --- /dev/null +++ b/src/peers.h @@ -0,0 +1,89 @@ +/* + * 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 "linkedlist.h" + +/* number of peers guaranteed to be in the network */ +#define DEFAULT_PEERS_SIZE 2 +/* maximum number of peers we store */ +#define MAX_PEERS_SIZE 50 + +/** A peer is not available if they is already our neighbour, + * pending to become one, or if we are unnable to connect to them. + * 1 if available, 0 if not. + */ +#define PEER_AVAILABLE 0x01 + +/* IPv6 addresses of peers guaranteed to be in the network */ +static const unsigned char DEFAULT_PEERS[DEFAULT_PEERS_SIZE][16] = { +/* TODO: Replace with real default peers */ + { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, + 192, 168, 0, 124 + }, + { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, + 192, 168, 0, 125 + } +}; + +/** Peer info holder. */ +typedef struct s_peer { + /** Binary IPv6 address. */ + struct in6_addr addr; + /** A set of flags for this peer. */ + int flags; + /* TODO: add uptime */ +} peer_t; + +void clear_peers(linkedlist_t *peers); + +int fetch_peers(const char *peers_path, linkedlist_t *peers); + +int fetch_specific_peers(const linkedlist_t *peers, + peer_t ***output, + int flags); + +peer_t *find_peer_by_addr(const linkedlist_t *peers, + const struct in6_addr *addr); + +void peers_to_str(const linkedlist_t *peers, char *output); + +void reset_peers_availability(linkedlist_t *peers); + +peer_t *save_peer(linkedlist_t *peers, const struct in6_addr *addr); + +void set_peer_flags(peer_t *peer, int flags); + +void shuffle_peers_arr(peer_t **peers, size_t peers_size); + +int store_peers(const char *peers_path, const linkedlist_t *peers); + +void unset_peer_flags(peer_t *peer, int flags); + +#endif /* PEERS_H */