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.
This commit is contained in:
Maxim Biro 2014-01-19 00:43:31 -05:00
parent 91b06da0c3
commit 28edc23329
7 changed files with 532 additions and 506 deletions

View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#include <sys/types.h> /* pid_t */
#include <sys/stat.h> /* umask */
#include <unistd.h> /* POSIX things */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <libconfig.h>
#include <arpa/inet.h> /* htons() */
#include <string.h> /* 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);
}

View File

@ -1,9 +1,9 @@
if BUILD_DHT_BOOTSTRAP_DAEMON if BUILD_DHT_BOOTSTRAP_DAEMON
noinst_PROGRAMS += DHT_bootstrap_daemon noinst_PROGRAMS += tox-dht-bootstrap-server-daemon
DHT_bootstrap_daemon_SOURCES = \ DHT_bootstrap_daemon_SOURCES = \
../other/bootstrap_serverdaemon/DHT_bootstrap_daemon.c ../other/bootstrap_serverdaemon/tox-dht-bootstrap-server-daemon.c
DHT_bootstrap_daemon_CFLAGS = \ DHT_bootstrap_daemon_CFLAGS = \
-I$(top_srcdir)/other/bootstrap_serverdaemon \ -I$(top_srcdir)/other/bootstrap_serverdaemon \
@ -22,5 +22,5 @@ DHT_bootstrap_daemon_LDADD = \
endif endif
EXTRA_DIST += \ EXTRA_DIST += \
$(top_srcdir)/other/bootstrap_serverdaemon/server.cfg \ $(top_srcdir)/other/bootstrap_serverdaemon/conf \
$(top_srcdir)/other/bootstrap_serverdaemon/DHT_bootstrap_daemon.sh $(top_srcdir)/other/bootstrap_serverdaemon/tox-dht-bootstrap-server-daemon

View File

@ -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

View File

@ -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"
}
)

View File

@ -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";
}
);

View File

@ -1,23 +1,23 @@
#! /bin/sh #! /bin/sh
### BEGIN INIT INFO ### BEGIN INIT INFO
# Provides: DHT_bootstrap_daemon # Provides: tox-dht-bootstrap-server-daemon
# Required-Start: $remote_fs $syslog # Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog # Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5 # Default-Start: 2 3 4 5
# Default-Stop: 0 1 6 # Default-Stop: 0 1 6
# Short-Description: Start the Tox bootstrapping server # Short-Description: Starts the Tox bootstrapping server
# Description: Use this piece of junk to start the Tox # Description: Starts the Tox bootstrapping server
# bootstrap server.
### END INIT INFO ### END INIT INFO
# PATH should only include /usr/* if it runs after the mountnfs.sh script # PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="ProjectTox bootstrap server daemon" DESC="ProjectTox bootstrap server daemon"
NAME=DHT_bootstrap_daemon NAME=tox-dht-bootstrap-server-daemon
CFG=/home/$USER/server.cfg USER=tom
CFG=/home/$USER/.$NAME/conf
DAEMON=/home/$USER/$NAME DAEMON=/home/$USER/$NAME
DAEMON_ARGS="$CFG" DAEMON_ARGS="$CFG"
PIDFILE=/home/$USER/.$NAME.pid PIDFILE=/home/$USER/.$NAME/pid
SCRIPTNAME=/etc/init.d/$NAME SCRIPTNAME=/etc/init.d/$NAME
# Exit if the package is not installed # Exit if the package is not installed
@ -81,7 +81,7 @@ case "$1" in
esac esac
;; ;;
status) status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? status_of_proc -p $PIDFILE "$DAEMON" "$NAME" && exit 0 || exit $?
;; ;;
restart) #|force-reload) restart) #|force-reload)

View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#include <sys/types.h> /* pid_t */
#include <sys/stat.h> /* umask */
#include <unistd.h> /* POSIX things */
#include <syslog.h>
#include <stdio.h>
#include <stdlib.h>
#include <libconfig.h>
#include <arpa/inet.h> /* htons() */
#include <string.h> /* 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;
}