Merge pull request #688 from nurupo/dht_bootstrap_daemon

Improved DHT bootstrap daemon
This commit is contained in:
irungentoo 2014-01-19 11:51:25 -08:00
commit 6b7dff37c8
7 changed files with 576 additions and 509 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,17 +1,17 @@
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
tox_dht_bootstrap_server_daemon_SOURCES = \
../other/bootstrap_serverdaemon/tox_dht_bootstrap_server_daemon.c
DHT_bootstrap_daemon_CFLAGS = \
tox_dht_bootstrap_server_daemon_CFLAGS = \
-I$(top_srcdir)/other/bootstrap_serverdaemon \
$(LIBSODIUM_CFLAGS) \
$(NACL_CFLAGS) \
$(LIBCONFIG_CFLAGS)
DHT_bootstrap_daemon_LDADD = \
tox_dht_bootstrap_server_daemon_LDADD = \
$(LIBSODIUM_LDFLAGS) \
$(NACL_LDFLAGS) \
libtoxcore.la \
@ -22,5 +22,6 @@ 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

View File

@ -0,0 +1,61 @@
##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
```
**Note that system log is where you find your public key**
--
###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 permission to keys and pid files
5. Make sure you have read permission for 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 permission 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
### 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)

View File

@ -0,0 +1,459 @@
/* tox_dht_bootstrap_server_daemon
*
* A simple DHT bootstrap 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>
#include <sys/stat.h>
#include <unistd.h>
#include <syslog.h>
#include <stdio.h>
#include <stdlib.h>
#include <libconfig.h>
#include <arpa/inet.h>
#include <string.h>
#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");
size_t write_size = fwrite(keys, sizeof(uint8_t), KEYS_SIZE, keys_file);
if (write_size != KEYS_SIZE) {
return 0;
}
}
fclose(keys_file);
return 1;
}
// Gets general config options
//
// Important: you are responsible 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_PATH = "pid_file_path";
const char *NAME_KEYS_FILE_PATH = "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_PATH, &tmp_pid_file) == CONFIG_FALSE) {
syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_PID_FILE_PATH);
syslog(LOG_WARNING, "Using default '%s': %s\n", NAME_PID_FILE_PATH, 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_PATH, &tmp_keys_file) == CONFIG_FALSE) {
syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_KEYS_FILE_PATH);
syslog(LOG_WARNING, "Using default '%s': %s\n", NAME_KEYS_FILE_PATH, 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_PATH, *pid_file_path);
syslog(LOG_DEBUG, "'%s': %s\n", NAME_KEYS_FILE_PATH, *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 bootstrapped
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;
}