From 28edc23329ff630a7ce3d594f2d972d47eb5c6f9 Mon Sep 17 00:00:00 2001 From: Maxim Biro Date: Sun, 19 Jan 2014 00:43:31 -0500 Subject: [PATCH] Improved DHT bootstrap daemon Supports "unlimited" number of bootstrap nodes in the config file, instead of just 32. PID and keys file paths are not limited by 512 chars anymore. Doesn't read the whole list of bootstrap servers into a global datastructure that just sits there after being processed once -- reads bootstrap servers one by one, processing them between reads. Supports IPv6. Has an option for IPv6. Supports LAN discovery. Has an option for LAN discovery. Writes to syslog. Uses new functions introduced in the core. `status` in the bash script now works. Has a simple README, with instructions for Debian-based distros. --- .../DHT_bootstrap_daemon.c | 460 ------------------ other/bootstrap_serverdaemon/Makefile.inc | 8 +- other/bootstrap_serverdaemon/README | 34 ++ other/bootstrap_serverdaemon/conf | 40 ++ other/bootstrap_serverdaemon/server.cfg | 34 -- ...mon.sh => tox-dht-bootstrap-server-daemon} | 16 +- .../tox-dht-bootstrap-server-daemon.c | 446 +++++++++++++++++ 7 files changed, 532 insertions(+), 506 deletions(-) delete mode 100644 other/bootstrap_serverdaemon/DHT_bootstrap_daemon.c create mode 100644 other/bootstrap_serverdaemon/README create mode 100644 other/bootstrap_serverdaemon/conf delete mode 100644 other/bootstrap_serverdaemon/server.cfg rename other/bootstrap_serverdaemon/{DHT_bootstrap_daemon.sh => tox-dht-bootstrap-server-daemon} (87%) mode change 100755 => 100644 create mode 100644 other/bootstrap_serverdaemon/tox-dht-bootstrap-server-daemon.c diff --git a/other/bootstrap_serverdaemon/DHT_bootstrap_daemon.c b/other/bootstrap_serverdaemon/DHT_bootstrap_daemon.c deleted file mode 100644 index dc519448..00000000 --- a/other/bootstrap_serverdaemon/DHT_bootstrap_daemon.c +++ /dev/null @@ -1,460 +0,0 @@ -/* DHT boostrap - * - * A simple DHT boostrap server for tox - daemon edition. - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox 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. - * - * Tox 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 Tox. If not, see . - * - */ - -#include /* pid_t */ -#include /* umask */ -#include /* POSIX things */ -#include - -#include -#include -#include -#include /* htons() */ -#include /* strcpy() */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "../../toxcore/DHT.h" -#include "../../toxcore/friend_requests.h" - -#define DEFAULT_PORT 33445 -#define DEFAULT_PID_FILE "bootstrap_server.pid" -#define DEFAULT_KEYS_FILE "bootstrap_server.keys" - -/* Server info struct */ -struct server_info_s { - int valid; - IP_Port conn; - uint8_t bs_pk[32]; -}; - -/* This is the struct configure_server() uses to return its data to */ -struct server_conf_s { - int err; - int port; - char pid_file[512]; - char keys_file[512]; - struct server_info_s info[32]; -}; - -int b16_to_key(char b16_string[], uint8_t *bs_pubkey) -{ - - int i; - unsigned int num1 = 0, num2 = 0; - - for (i = 0; i < 32; ++i) { - sscanf(&b16_string[i * 2], "%1X", &num1); - sscanf(&b16_string[i * 2 + 1], "%1X", &num2); - num1 = num1 << 4; - bs_pubkey[i] = bs_pubkey[i] | num1; - bs_pubkey[i] = bs_pubkey[i] | num2; - } - - return 0; -} - -/* - resolve_addr(): - address should represent IPv4 or a hostname with a record - - returns a data in network byte order that can be used to set IP.i or IP_Port.ip.i - returns 0 on failure - - TODO: Fix ipv6 support -*/ - -uint32_t resolve_addr(const char *address) -{ - struct addrinfo *server = NULL; - struct addrinfo hints; - int rc; - uint32_t addr; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; // IPv4 only right now. - hints.ai_socktype = SOCK_DGRAM; // Type of socket Tox uses. - - rc = getaddrinfo(address, "echo", &hints, &server); - - // Lookup failed. - if (rc != 0) { - return 0; - } - - // IPv4 records only.. - if (server->ai_family != AF_INET) { - freeaddrinfo(server); - return 0; - } - - - addr = ((struct sockaddr_in *)server->ai_addr)->sin_addr.s_addr; - - freeaddrinfo(server); - return addr; -} - -/* This function connects to all specified servers -and connect to them. -returns 1 if the connection to the DHT is up -returns -1 if all attempts failed -*/ -int connect_to_servers(DHT *dht, struct server_info_s *info) -{ - int i; - int c; - - for (i = 0; i < 32; ++i) { - if (info[i].valid) { - /* Actual bootstrapping code goes here */ - //puts("Calling DHT_bootstrap"); - DHT_bootstrap(dht, info[i].conn, info[i].bs_pk); - } - } - - /* Check if we're connected to the DHT */ - for (c = 0; c != 100; ++c) { - usleep(10000); - - if (DHT_isconnected(dht)) { - //puts("Connected"); - return 1; - break; - } - - if (DHT_isconnected(dht) == 0 && c == 99) { - //puts("Not connected"); - return -1; - break; - } - - do_DHT(dht); - - networking_poll(dht->c->lossless_udp->net); - } - - /* This probably never happens */ - return 0; -} - -void manage_keys(DHT *dht, char *keys_file) -{ - const uint32_t KEYS_SIZE = crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES; - uint8_t keys[KEYS_SIZE]; - struct stat existence; - FILE *keysf; - - /* Check if file exits, proceed to open and load keys */ - if (stat(keys_file, &existence) >= 0) { - keysf = fopen(keys_file, "r"); - size_t read_size = fread(keys, sizeof(uint8_t), KEYS_SIZE, keysf); - - if (read_size != KEYS_SIZE) { - printf("Error while reading the key file\nExiting.\n"); - exit(1); - } else { - printf("Keys loaded successfully\n"); - } - - load_keys(dht->c, keys); - - } else { - /* Otherwise save new keys */ - /* Silly work-around to ignore any errors coming from new_keys() */ - new_keys(dht->c); - save_keys(dht->c, keys); - keysf = fopen(keys_file, "w"); - - if (fwrite(keys, sizeof(uint8_t), KEYS_SIZE, keysf) != KEYS_SIZE) { - printf("Error while writing the key file.\nExiting.\n"); - exit(1); - } else { - printf("Keys saved successfully\n"); - } - } - - fclose(keysf); -} - -/* This reads the configuration file, and returns a struct server_conf_s with: - *an error number: - *-1 = file wasn't read, for whatever reason - *-2 = no bootstrap servers found - *the port - *the location of the keys file - *the location of the PID file - *the list of bootstrap servers -*/ -struct server_conf_s configure_server(char *cfg_file) -{ - config_t cfg; - config_setting_t *server_list; - - /* This one will be strcpy'd into the pid_file array in server_conf */ - const char *pid_file_tmp; - const char *keys_file_tmp; - - /* Remote bootstrap server variables */ - int bs_port; - const char *bs_ip; - const char *bs_pk; - - /* The big struct */ - static struct server_conf_s server_conf; - - /* Set both to their default values. If there's an error - with opening/reading the config file, we return right away */ - server_conf.port = DEFAULT_PORT; - strcpy(server_conf.pid_file, DEFAULT_PID_FILE); - strcpy(server_conf.keys_file, DEFAULT_KEYS_FILE); - - config_init(&cfg); - - /* Read the file. If there is an error, report it and exit. */ - if (! config_read_file(&cfg, cfg_file)) { - fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), - config_error_line(&cfg), config_error_text(&cfg)); - config_destroy(&cfg); - server_conf.err = -1; - return server_conf; - } - - /* Get the port to listen on */ - if (config_lookup_int(&cfg, "port", &server_conf.port)) { - //printf("Port: %d\n", port); - } else { - fprintf(stderr, "No 'port' setting in configuration file.\n"); - } - - /* Get PID file location */ - if (config_lookup_string(&cfg, "pid_file", &pid_file_tmp)) { - //printf("PID file: %s\n", pid_file_tmp); - strcpy(server_conf.pid_file, pid_file_tmp); - } else { - fprintf(stderr, "No 'pid_file' setting in configuration file.\n"); - } - - /* Get keys file location */ - if (config_lookup_string(&cfg, "keys_file", &keys_file_tmp)) { - //printf("Keys file: %s\n", keys_file_tmp); - strcpy(server_conf.keys_file, keys_file_tmp); - } else { - fprintf(stderr, "No 'keys_file' setting in configuration file.\n"); - } - - /* Get all the servers in the list */ - server_list = config_lookup(&cfg, "bootstrap_servers"); - - if (server_list != NULL) { - int count = config_setting_length(server_list); - int i; - - char tmp_ip[30]; /* IP */ - char tmp_pk[64]; /* bs_pk */ - - for (i = 0; i < count; ++i) { - config_setting_t *server = config_setting_get_elem(server_list, i); - /* Get a pointer on the key array */ - uint8_t *bs_pk_p = server_conf.info[i].bs_pk; - - /* Only output the record if all of the expected fields are present. */ - if (!(config_setting_lookup_string(server, "ip", &bs_ip) - && config_setting_lookup_int(server, "port", &bs_port) - && config_setting_lookup_string(server, "bs_pk", &bs_pk))) - continue; - - /* Converting all that stuff into usable formats and storing - it away in the server_info struct */ - server_conf.info[i].valid = 1; - - if (resolve_addr(strcpy(tmp_ip, bs_ip)) == 0) { - server_conf.info[i].valid = 0; - printf("bootstrap_server %d: Invalid IP.\n", i); - } - - if (strlen(bs_pk) != 64) { - server_conf.info[i].valid = 0; - printf("bootstrap_server %d: Invalid public key.\n", i); - } - - if (!bs_port) { - server_conf.info[i].valid = 0; - printf("bootstrap_server %d: Invalid port.\n", i); - } - - server_conf.info[i].conn.ip.family = AF_INET; - server_conf.info[i].conn.ip.ip4.uint32 = resolve_addr(strcpy(tmp_ip, bs_ip)); - server_conf.info[i].conn.port = htons(bs_port); - b16_to_key(strcpy(tmp_pk, bs_pk), bs_pk_p); - } - - /* Check if at least one server entry is valid */ - for (i = 0; i < 32; ++i) { - if (server_conf.info[i].valid) - break; - else - server_conf.err = -2; - } - - } else { - server_conf.err = -2; - } - - config_destroy(&cfg); - return server_conf; -} - -int main(int argc, char *argv[]) -{ - - pid_t pid, sid; /* Process- and Session-ID */ - struct server_conf_s server_conf; - - FILE *pidf; /* The PID file */ - - if (argc < 2) { - printf("Please specify a configuration file.\n"); - exit(EXIT_FAILURE); - } - - server_conf = configure_server(argv[1]); - - /* Initialize networking - bind to ip 0.0.0.0:PORT */ - IP ip; - ip_init(&ip, 0); - DHT *dht = new_DHT(new_net_crypto(new_networking(ip, server_conf.port))); - /* Read the config file */ - printf("PID file: %s\n", server_conf.pid_file); - printf("Key file: %s\n", server_conf.keys_file); - - if (server_conf.err == -1) - printf("Config file not read.\n"); - - if (server_conf.err == -2) - printf("No valid servers in list.\n"); - - /* Open PID file for writing - if an error happens, - it will be caught down the line */ - pidf = fopen(server_conf.pid_file, "w"); - - /* Manage the keys */ - /* for now, just ignore any errors after this call. */ - int tmperr = errno; - manage_keys(dht, server_conf.keys_file); - errno = tmperr; - - /* Public key */ - int i; - printf("\nPublic Key: "); - - for (i = 0; i < 32; ++i) { - uint8_t ln, hn; - ln = 0x0F & dht->c->self_public_key[i]; - hn = 0xF0 & dht->c->self_public_key[i]; - hn = hn >> 4; - printf("%X%X", hn, ln); - } - - printf("\n"); - - /* Bootstrap the DHT - This one throws odd errors, too. Ignore. I assume they come - from somewhere in the core. */ - tmperr = errno; - connect_to_servers(dht, server_conf.info); - errno = tmperr; - - if (!DHT_isconnected(dht)) { - puts("Could not establish DHT connection. Check server settings.\n"); - exit(EXIT_FAILURE); - } else { - printf("Connected to DHT successfully.\n"); - } - - /* If there's been an error, exit before forking off */ - if (errno != 0) { - perror("Error"); - printf("Error(s) occured during start-up. Exiting.\n"); - exit(EXIT_FAILURE); - } - - /* Things that make the daemon work come past here. - There should be nothing here but the daemon code and - the main loop. */ - - /* Fork off from the parent process */ - pid = fork(); - - if (pid < 0) { - printf("Forking failed.\n"); - exit(EXIT_FAILURE); - } - - /* If we got a good PID, then - we can exit the parent process. */ - if (pid > 0) { - printf("Forked successfully: %d.\n", pid); - - /* Write the PID file */ - fprintf(pidf, "%d\n", pid); - fclose(pidf); - - /* Exit parent */ - exit(EXIT_SUCCESS); - } - - /* Change the file mode mask */ - umask(0); - - /* Create a new SID for the child process */ - sid = setsid(); - - if (sid < 0) { - printf("SID creation failure.\n"); - exit(EXIT_FAILURE); - } - - /* Change the current working directory */ - if ((chdir("/")) < 0) { - exit(EXIT_FAILURE); - } - - /* Go quiet */ - close(STDOUT_FILENO); - close(STDIN_FILENO); - close(STDERR_FILENO); - - while (1) { - do_DHT(dht); - - networking_poll(dht->c->lossless_udp->net); - usleep(10000); - } - - //shutdown_networking(); - exit(EXIT_SUCCESS); -} diff --git a/other/bootstrap_serverdaemon/Makefile.inc b/other/bootstrap_serverdaemon/Makefile.inc index 73b078c3..2607bfee 100644 --- a/other/bootstrap_serverdaemon/Makefile.inc +++ b/other/bootstrap_serverdaemon/Makefile.inc @@ -1,9 +1,9 @@ if BUILD_DHT_BOOTSTRAP_DAEMON -noinst_PROGRAMS += DHT_bootstrap_daemon +noinst_PROGRAMS += tox-dht-bootstrap-server-daemon DHT_bootstrap_daemon_SOURCES = \ - ../other/bootstrap_serverdaemon/DHT_bootstrap_daemon.c + ../other/bootstrap_serverdaemon/tox-dht-bootstrap-server-daemon.c DHT_bootstrap_daemon_CFLAGS = \ -I$(top_srcdir)/other/bootstrap_serverdaemon \ @@ -22,5 +22,5 @@ DHT_bootstrap_daemon_LDADD = \ endif EXTRA_DIST += \ - $(top_srcdir)/other/bootstrap_serverdaemon/server.cfg \ - $(top_srcdir)/other/bootstrap_serverdaemon/DHT_bootstrap_daemon.sh + $(top_srcdir)/other/bootstrap_serverdaemon/conf \ + $(top_srcdir)/other/bootstrap_serverdaemon/tox-dht-bootstrap-server-daemon diff --git a/other/bootstrap_serverdaemon/README b/other/bootstrap_serverdaemon/README new file mode 100644 index 00000000..d1444ee6 --- /dev/null +++ b/other/bootstrap_serverdaemon/README @@ -0,0 +1,34 @@ +Instructions for Debian + +The following commands are to be executed as root: + +1. In `tox-dht-bootstrap-server-daemon` file change: + - `CFG` to where your config file (`conf`) will be; read rights required + - `DAEMON` to point to the executable + - `PIDFILE` to point to a pid file daemon would have rights to create + +2. Go over everything in `conf`. Make sure `pid_file_path` matches `PIDFILE` from `tox-dht-bootstrap-server-daemon` + +3. Execute: `mv tox-dht-bootstrap-server-daemon /etc/init.d/tox-dht-bootstrap-server-daemon` + +4. Give the right permissions to this file: `chmod 755 /etc/init.d/tox-dht-bootstrap-server-daemon` + +5. Execute: `update-rc.d tox-dht-bootstrap-server-daemon defaults` + +6. Start the service: `service tox-dht-bootstrap-server-daemon start` + +7. Verify that the service is running: `service tox-dht-bootstrap-server-daemon status` + +You can see daemon's log with `grep "tox-dht-bootstrap-server-daemon" /var/log/syslog` + +Troubleshooting: + +1. Check the log for errors with `grep "tox-dht-bootstrap-server-daemon" /var/log/syslog` + +2. Check that paths in the beginning of `/etc/init.d/tox-dht-bootstrap-server-daemon` are valid + +3. Make sure that `PIDFILE` from `/etc/init.d/tox-dht-bootstrap-server-daemon` matches with the `pid_file_path` from `conf` + +4. Make sure you have write premmision to keys and pid files + +5. Make sure you have read premission to config file \ No newline at end of file diff --git a/other/bootstrap_serverdaemon/conf b/other/bootstrap_serverdaemon/conf new file mode 100644 index 00000000..b24e1e6c --- /dev/null +++ b/other/bootstrap_serverdaemon/conf @@ -0,0 +1,40 @@ +// ProjectTox bootstrap server configuration file + +// listening port +port = 33445 + +// The key file is like a password, so keep it where no one can read it +// The daemon should have premission to read/write to it +// Remember to replace the provided example with +// your own path +keys_file_path = "/home/tom/.tox-dht-bootstrap-server-daemon/keys" + +// The PID file written to by daemon, +// make sure that the user who runs the server +// does have permissions to write to it +// Remember to replace the provided example with +// your own path +pid_file_path = "/home/tom/.tox-dht-bootstrap-server-daemon/pid" + +enable_ipv6 = false + +// Automatically bootstrap with nodes on local network +enable_lan_discovery = true + +// Any number of nodes the daemon will bootstrap itself from +// Remember to replace the provided example with +// your own server list +bootstrap_servers = ( + { // Server 1 + // any ipv4 or ipv6, depending if `enable_ipv6` is set or not + // also any US-ASCII domain name + address = "198.46.136.167" + port = 33445 + public_key = "728925473812C7AAC482BE7250BCCAD0B8CB9F737BF3D42ABD34459C1768F854" + }, + { // Server 2 + address = "example.org" + port = 33445 + public_key = "8CD5A9BF0A6CE358BA36F7A653F99FA6B258FF756E490F52C1F98CC420F78858" + } +) diff --git a/other/bootstrap_serverdaemon/server.cfg b/other/bootstrap_serverdaemon/server.cfg deleted file mode 100644 index 527c2a72..00000000 --- a/other/bootstrap_serverdaemon/server.cfg +++ /dev/null @@ -1,34 +0,0 @@ -// ProjectTox bootstrap server configuration file - -// The port used by bootstrap_server to listen on -port = 33445; - -// The key file -// make sure that the user who runs the server -// does have permissions to read it/write to it -// Remember to replace the provided example with -// the directory the DHT server will run in. -keys_file = "/home/tom/.bootstrap_server.keys" - -// The PID file written to by bootstrap_server, -// make sure that the user who runs the server -// does have permissions to write to it -// Remember to replace the provided example with -// the directory the DHT server will run in. -pid_file = "/home/tom/.bootstrap_server.pid"; - -// The info of the node bootstap_server will -// bootstrap itself from. -bootstrap_servers = ( - { // Server 1 - ip = "198.46.136.167"; - port = 33445; - bs_pk = "728925473812C7AAC482BE7250BCCAD0B8CB9F737BF3D42ABD34459C1768F854"; -// } - }, - { // Server 2 - ip = "192.81.133.111"; - port = 33445; - bs_pk = "8CD5A9BF0A6CE358BA36F7A653F99FA6B258FF756E490F52C1F98CC420F78858"; - } -); diff --git a/other/bootstrap_serverdaemon/DHT_bootstrap_daemon.sh b/other/bootstrap_serverdaemon/tox-dht-bootstrap-server-daemon old mode 100755 new mode 100644 similarity index 87% rename from other/bootstrap_serverdaemon/DHT_bootstrap_daemon.sh rename to other/bootstrap_serverdaemon/tox-dht-bootstrap-server-daemon index 936bc808..20639af8 --- a/other/bootstrap_serverdaemon/DHT_bootstrap_daemon.sh +++ b/other/bootstrap_serverdaemon/tox-dht-bootstrap-server-daemon @@ -1,23 +1,23 @@ #! /bin/sh ### BEGIN INIT INFO -# Provides: DHT_bootstrap_daemon +# Provides: tox-dht-bootstrap-server-daemon # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 -# Short-Description: Start the Tox bootstrapping server -# Description: Use this piece of junk to start the Tox -# bootstrap server. +# Short-Description: Starts the Tox bootstrapping server +# Description: Starts the Tox bootstrapping server ### END INIT INFO # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="ProjectTox bootstrap server daemon" -NAME=DHT_bootstrap_daemon -CFG=/home/$USER/server.cfg +NAME=tox-dht-bootstrap-server-daemon +USER=tom +CFG=/home/$USER/.$NAME/conf DAEMON=/home/$USER/$NAME DAEMON_ARGS="$CFG" -PIDFILE=/home/$USER/.$NAME.pid +PIDFILE=/home/$USER/.$NAME/pid SCRIPTNAME=/etc/init.d/$NAME # Exit if the package is not installed @@ -81,7 +81,7 @@ case "$1" in esac ;; status) - status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + status_of_proc -p $PIDFILE "$DAEMON" "$NAME" && exit 0 || exit $? ;; restart) #|force-reload) diff --git a/other/bootstrap_serverdaemon/tox-dht-bootstrap-server-daemon.c b/other/bootstrap_serverdaemon/tox-dht-bootstrap-server-daemon.c new file mode 100644 index 00000000..9aa28533 --- /dev/null +++ b/other/bootstrap_serverdaemon/tox-dht-bootstrap-server-daemon.c @@ -0,0 +1,446 @@ +/* tox-dht-bootstrap-server-daemon + * + * A simple DHT boostrap server for tox - daemon edition. + * + * Copyright (C) 2014 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox 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. + * + * Tox 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 Tox. If not, see . + * + */ + +#include /* pid_t */ +#include /* umask */ +#include /* POSIX things */ +#include + +#include +#include +#include +#include /* htons() */ +#include /* strcpy() */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../../toxcore/DHT.h" +#include "../../toxcore/friend_requests.h" +#include "../../toxcore/LAN_discovery.h" + +#include "../../testing/misc_tools.c" + +#define DAEMON_NAME "tox-dht-bootstrap-server-daemon" + +#define SLEEP_TIME_MILLISECONDS 30 +#define sleep usleep(1000*SLEEP_TIME_MILLISECONDS) + +#define DEFAULT_PID_FILE_PATH ".tox-dht-bootstrap-server-daemon.pid" +#define DEFAULT_KEYS_FILE_PATH ".tox-dht-bootstrap-server-daemon.keys" +#define DEFAULT_PORT 33445 +#define DEFAULT_ENABLE_IPV6 0 // 1 - true, 0 - false +#define DEFAULT_ENABLE_LAN_DISCOVERY 1 // 1 - true, 0 - false + +// Uses the already existing key or creates one if it didn't exist +// +// retirns 1 on success +// 0 on failure - no keys were read or stored + +int manage_keys(DHT *dht, char *keys_file_path) +{ + const uint32_t KEYS_SIZE = crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES; + uint8_t keys[KEYS_SIZE]; + FILE *keys_file; + + // Check if file exits, proceed to open and load keys + keys_file = fopen(keys_file_path, "r"); + if (keys_file != NULL) { + size_t read_size = fread(keys, sizeof(uint8_t), KEYS_SIZE, keys_file); + + if (read_size != KEYS_SIZE) { + return 0; + } + + load_keys(dht->c, keys); + } else { + // Otherwise save new keys + new_keys(dht->c); + save_keys(dht->c, keys); + + keys_file = fopen(keys_file_path, "w"); + + if (fwrite(keys, sizeof(uint8_t), KEYS_SIZE, keys_file) != KEYS_SIZE) { + return 0; + } + } + + fclose(keys_file); + + return 1; +} + +// Gets general config options +// +// Important: you are responsibl for freeing `pid_file_path` and `keys_file_path` +// +// returns 1 on success +// 0 on failure, doesn't modify any data pointed by arguments + +int get_general_config(char *cfg_file_path, char **pid_file_path, char **keys_file_path, int *port, int *enable_ipv6, int *enable_lan_discovery) +{ + config_t cfg; + + const char *NAME_PORT = "port"; + const char *NAME_PID_FILE = "pid_file_path"; + const char *NAME_KEYS_FILE = "keys_file_path"; + const char *NAME_ENABLE_IPV6 = "enable_ipv6"; + const char *NAME_ENABLE_LAN_DISCOVERY = "enable_lan_discovery"; + + config_init(&cfg); + + // Read the file. If there is an error, report it and exit. + if (config_read_file(&cfg, cfg_file_path) == CONFIG_FALSE) { + syslog(LOG_ERR, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + return 0; + } + + // Get port + if (config_lookup_int(&cfg, NAME_PORT, port) == CONFIG_FALSE) { + syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_PORT); + syslog(LOG_WARNING, "Using default '%s': %d\n", NAME_PORT, DEFAULT_PORT); + *port = DEFAULT_PORT; + } + + // Get PID file location + const char *tmp_pid_file; + + if (config_lookup_string(&cfg, NAME_PID_FILE, &tmp_pid_file) == CONFIG_FALSE) { + syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_PID_FILE); + syslog(LOG_WARNING, "Using default '%s': %s\n", NAME_PID_FILE, DEFAULT_PID_FILE_PATH); + tmp_pid_file = DEFAULT_PID_FILE_PATH; + } + *pid_file_path = malloc(strlen(tmp_pid_file) + 1); + strcpy(*pid_file_path, tmp_pid_file); + + // Get keys file location + const char *tmp_keys_file; + + if (config_lookup_string(&cfg, NAME_KEYS_FILE, &tmp_keys_file) == CONFIG_FALSE) { + syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_KEYS_FILE); + syslog(LOG_WARNING, "Using default '%s': %s\n", NAME_KEYS_FILE, DEFAULT_KEYS_FILE_PATH); + tmp_keys_file = DEFAULT_KEYS_FILE_PATH; + } + *keys_file_path = malloc(strlen(tmp_keys_file) + 1); + strcpy(*keys_file_path, tmp_keys_file); + + // Get IPv6 option + if (config_lookup_bool(&cfg, NAME_ENABLE_IPV6, enable_ipv6) == CONFIG_FALSE) { + syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_IPV6); + syslog(LOG_WARNING, "Using default '%s': %s\n", NAME_ENABLE_IPV6, DEFAULT_ENABLE_IPV6 ? "true" : "false"); + *enable_ipv6 = DEFAULT_ENABLE_IPV6; + } + + // Get LAN discovery option + if (config_lookup_bool(&cfg, NAME_ENABLE_LAN_DISCOVERY, enable_lan_discovery) == CONFIG_FALSE) { + syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_LAN_DISCOVERY); + syslog(LOG_WARNING, "Using default '%s': %s\n", NAME_ENABLE_LAN_DISCOVERY, DEFAULT_ENABLE_LAN_DISCOVERY ? "true" : "false"); + *enable_lan_discovery = DEFAULT_ENABLE_LAN_DISCOVERY; + } + + config_destroy(&cfg); + + syslog(LOG_DEBUG, "Successfully read:\n"); + syslog(LOG_DEBUG, "'%s': %s\n", NAME_PID_FILE, *pid_file_path); + syslog(LOG_DEBUG, "'%s': %s\n", NAME_KEYS_FILE, *keys_file_path); + syslog(LOG_DEBUG, "'%s': %d\n", NAME_PORT, *port); + syslog(LOG_DEBUG, "'%s': %s\n", NAME_ENABLE_IPV6, *enable_ipv6 ? "true" : "false"); + syslog(LOG_DEBUG, "'%s': %s\n", NAME_ENABLE_LAN_DISCOVERY, *enable_lan_discovery ? "true" : "false"); + + return 1; +} + +// Bootstraps servers listed in the config file +// +// returns 1 on success +// 0 on failure, either no or only some servers were bootstraped + +int bootstrap_from_config(char *cfg_file_path, DHT *dht, int enable_ipv6) +{ + const char *NAME_BOOTSTRAP_SERVERS = "bootstrap_servers"; + + const char *NAME_PUBLIC_KEY = "public_key"; + const char *NAME_PORT = "port"; + const char *NAME_ADDRESS = "address"; + + config_t cfg; + + config_init(&cfg); + + if (config_read_file(&cfg, cfg_file_path) == CONFIG_FALSE) { + syslog(LOG_ERR, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + return 0; + } + + config_setting_t *server_list = config_lookup(&cfg, NAME_BOOTSTRAP_SERVERS); + + if (server_list == NULL) { + syslog(LOG_ERR, "No '%s' setting in configuration file.\n", NAME_BOOTSTRAP_SERVERS); + config_destroy(&cfg); + return 0; + } + + int bs_port; + const char *bs_address; + const char *bs_public_key; + + config_setting_t *server; + + int i = 0; + + while (config_setting_length(server_list)) { + + server = config_setting_get_elem(server_list, 0); + + if (server == NULL) { + return 0; + } + + /* Proceed only if all parts are present */ + if (config_setting_lookup_string(server, NAME_PUBLIC_KEY, &bs_public_key) == CONFIG_FALSE || + config_setting_lookup_int (server, NAME_PORT, &bs_port) == CONFIG_FALSE || + config_setting_lookup_string(server, NAME_ADDRESS, &bs_address) == CONFIG_FALSE ) { + goto next; + } + + if (strlen(bs_public_key) != 64) { + syslog(LOG_WARNING, "bootstrap_server #%d: Invalid '%s': %s.\n", i, NAME_PUBLIC_KEY, bs_public_key); + goto next; + } + + // not (1 <= port <= 65535) + if (bs_port < 1 || bs_port > 65535) { + syslog(LOG_WARNING, "bootstrap_server #%d: Invalid '%s': %d.\n", i, NAME_PORT, bs_port); + goto next; + } + + const int address_resolved = DHT_bootstrap_from_address(dht, bs_address, enable_ipv6, htons(bs_port), hex_string_to_bin((char*)bs_public_key)); + + if (!address_resolved) { + syslog(LOG_WARNING, "bootstrap_server #%d: Invalid '%s': %s.\n", i, NAME_ADDRESS, bs_address); + goto next; + } + + syslog(LOG_DEBUG, "Successfully connected to %s:%d %s\n", bs_address, bs_port, bs_public_key); + + next: + // config_setting_lookup_string() allocates string inside and doesn't allow us to free it + // so in order to reuse `bs_public_key` and `bs_address` we have to remove the element + // which will cause libconfig to free allocated strings + config_setting_remove_elem(server_list, 0); + i++; + } + + config_destroy(&cfg); + + return 1; +} + +// Checks if we are connected to the DHT +// +// returns 1 on success +// 0 on failure + +int is_conencted(DHT *dht, int port, int enable_lan_discovery) +{ + uint16_t htons_port = htons(port); + + int i; + for (i = 0; i < 100; i ++) { + do_DHT(dht); + + if (enable_lan_discovery) { + send_LANdiscovery(htons_port, dht->c); + } + + networking_poll(dht->c->lossless_udp->net); + + if (DHT_isconnected(dht)) { + return 1; + } + + sleep; + } + + return 0; +} + +// Prints public key + +void print_public_key(uint8_t *public_key) +{ + char buffer[64 + 1]; + int index = 0; + + int i; + for (i = 0; i < 32; i++) { + if (public_key[i] < 16) { + index += sprintf(buffer + index, "0"); + } + index += sprintf(buffer + index, "%hhX", public_key[i]); + } + + syslog(LOG_INFO, "Public Key: %s\n", buffer); + + return; +} + +int main(int argc, char *argv[]) +{ + openlog(DAEMON_NAME, LOG_NOWAIT | LOG_PID, LOG_DAEMON); + + if (argc < 2) { + syslog(LOG_ERR, "Please specify a configuration file. Exiting.\n"); + return 1; + } + + char *cfg_file_path = argv[1]; + char *pid_file_path, *keys_file_path; + int port; + int enable_ipv6; + int enable_lan_discovery; + + if (get_general_config(cfg_file_path, &pid_file_path, &keys_file_path, &port, &enable_ipv6, &enable_lan_discovery)) { + syslog(LOG_DEBUG, "General config read successfully\n"); + } else { + syslog(LOG_ERR, "Couldn't read config file: %s. Exiting.\n", cfg_file_path); + return 1; + } + + // not (1 <= port <= 65535) + if (port < 1 || port > 65535) { + syslog(LOG_ERR, "Invalid port: %d, must be 1 <= port <= 65535. Exiting.\n", port); + return 1; + } + + // Check if the PId file exists + if (fopen(pid_file_path, "r")) { + syslog(LOG_ERR, "Another instance of the daemon is already running, PID file %s exists. Exiting.\n", pid_file_path); + return 1; + } + + IP ip; + ip_init(&ip, enable_ipv6); + + DHT *dht = new_DHT(new_net_crypto(new_networking(ip, port))); + if (dht == NULL) { + syslog(LOG_ERR, "Couldn't initialize Tox DHT instance. Exiting.\n"); + return 1; + } + + if (enable_lan_discovery) { + LANdiscovery_init(dht); + } + + if (manage_keys(dht, keys_file_path)) { + syslog(LOG_DEBUG, "Keys are managed successfully\n"); + } else { + syslog(LOG_ERR, "Couldn't read/write: %s. Exiting.\n", keys_file_path); + return 1; + } + + if (bootstrap_from_config(cfg_file_path, dht, enable_ipv6)) { + syslog(LOG_DEBUG, "List of bootstrap servers read successfully\n"); + } else { + syslog(LOG_ERR, "Couldn't read list of bootstrap servers in %s. Exiting.\n", cfg_file_path); + return 1; + } + + if (is_conencted(dht, port, enable_lan_discovery)) { + syslog(LOG_INFO, "Successfully connected to DHT\n"); + } else { + syslog(LOG_ERR, "Couldn't connect to the DHT. Check settings and network connections. Exiting.\n"); + return 1; + } + + print_public_key(dht->c->self_public_key); + + // Write the PID file + FILE *pidf = fopen(pid_file_path, "w"); + if (pidf == NULL) { + syslog(LOG_ERR, "Can't open the PID file for writing: %s. Exiting.\n", pid_file_path); + return 1; + } + + free(pid_file_path); + free(keys_file_path); + + // Fork off from the parent process + pid_t pid = fork(); + + if (pid < 0) { + syslog(LOG_ERR, "Forking failed. Exiting.\n"); + return 1; + } + + if (pid > 0) { + syslog(LOG_DEBUG, "Forked successfully: PID: %d.\n", pid); + + return 0; + } + + // Change the file mode mask + umask(0); + + fprintf(pidf, "%d\n", pid); + fclose(pidf); + + /* Create a new SID for the child process */ + + if (setsid() < 0) { + syslog(LOG_ERR, "SID creation failure. Exiting.\n"); + return 1; + } + + /* Change the current working directory */ + if ((chdir("/")) < 0) { + syslog(LOG_ERR, "Couldn't change working directory to '/'. Exiting.\n"); + return 1; + } + + /* Go quiet */ + close(STDOUT_FILENO); + close(STDIN_FILENO); + close(STDERR_FILENO); + + uint64_t last_LANdiscovery = 0; + uint16_t htons_port = htons(port); + + while (1) { + do_DHT(dht); + + if (enable_lan_discovery && is_timeout(last_LANdiscovery, LAN_DISCOVERY_INTERVAL)) { + send_LANdiscovery(htons_port, dht->c); + last_LANdiscovery = unix_time(); + } + + networking_poll(dht->c->lossless_udp->net); + + sleep; + } + + return 1; +}