Merge branch 'tox-bootstrapd-docker-support' of https://github.com/nurupo/InsertProjectNameHere

This commit is contained in:
irungentoo 2016-01-04 22:43:36 -05:00
commit c6bed82d47
No known key found for this signature in database
GPG Key ID: 10349DC9BED89E98
22 changed files with 1581 additions and 772 deletions

View File

@ -10,5 +10,5 @@ include ../toxencryptsave/Makefile.inc
include ../toxav/Makefile.inc include ../toxav/Makefile.inc
include ../other/Makefile.inc include ../other/Makefile.inc
include ../testing/Makefile.inc include ../testing/Makefile.inc
include ../other/bootstrap_daemon/Makefile.inc include ../other/bootstrap_daemon/src/Makefile.inc
include ../auto_tests/Makefile.inc include ../auto_tests/Makefile.inc

View File

@ -40,7 +40,7 @@
#include "../testing/misc_tools.c" #include "../testing/misc_tools.c"
#ifdef DHT_NODE_EXTRA_PACKETS #ifdef DHT_NODE_EXTRA_PACKETS
#include "./bootstrap_node_packets.c" #include "./bootstrap_node_packets.h"
#define DHT_VERSION_NUMBER 1 #define DHT_VERSION_NUMBER 1
#define DHT_MOTD "This is a test motd" #define DHT_MOTD "This is a test motd"

View File

@ -2,7 +2,9 @@ bin_PROGRAMS += DHT_bootstrap
DHT_bootstrap_SOURCES = ../other/DHT_bootstrap.c \ DHT_bootstrap_SOURCES = ../other/DHT_bootstrap.c \
../toxcore/DHT.h \ ../toxcore/DHT.h \
../toxcore/friend_requests.h ../toxcore/friend_requests.h \
../other/bootstrap_node_packets.h \
../other/bootstrap_node_packets.c
DHT_bootstrap_CFLAGS = -I$(top_srcdir)/other \ DHT_bootstrap_CFLAGS = -I$(top_srcdir)/other \
$(LIBSODIUM_CFLAGS) \ $(LIBSODIUM_CFLAGS) \

View File

@ -1,27 +0,0 @@
if BUILD_DHT_BOOTSTRAP_DAEMON
bin_PROGRAMS += tox-bootstrapd
tox_bootstrapd_SOURCES = \
../other/bootstrap_daemon/tox-bootstrapd.c
tox_bootstrapd_CFLAGS = \
-I$(top_srcdir)/other/bootstrap_daemon \
$(LIBSODIUM_CFLAGS) \
$(NACL_CFLAGS) \
$(LIBCONFIG_CFLAGS)
tox_bootstrapd_LDADD = \
$(LIBSODIUM_LDFLAGS) \
$(NACL_LDFLAGS) \
libtoxcore.la \
$(LIBCONFIG_LIBS) \
$(LIBSODIUM_LIBS) \
$(NACL_LIBS)
endif
EXTRA_DIST += \
$(top_srcdir)/other/bootstrap_daemon/tox-bootstrapd.conf \
$(top_srcdir)/other/bootstrap_daemon/tox-bootstrapd.sh

View File

@ -1,17 +1,29 @@
#Instructions #Instructions
- [For `systemd` users](#systemd) - [For `systemd` users](#systemd)
- [Setting up](#systemd-setting-up)
- [Updating](#systemd-updating)
- [Troubleshooting](#systemd-troubleshooting) - [Troubleshooting](#systemd-troubleshooting)
<br> <br>
- [For `init.d` users](#initd) - [For `SysVinit` users](#sysvinit)
- [Troubleshooting](#initd-troubleshooting) - [Setting up](#sysvinit-setting-up)
- [Updating](#sysvinit-updating)
- [Troubleshooting](#sysvinit-troubleshooting)
<br>
- [For `Docker` users](#docker)
- [Setting up](#docker-setting-up)
- [Updating](#docker-updating)
- [Troubleshooting](#docker-troubleshooting)
These instructions are primarily tested on Debian Linux, Wheezy for init.d and Jessie for systemd, but they should work on other POSIX-compliant systems too. These instructions are primarily tested on Debian Linux, Wheezy for SysVinit and Jessie for systemd, but they should work on other POSIX-compliant systems too.
<a name="systemd" /> <a name="systemd" />
##For `systemd` users: ##For `systemd` users
<a name="systemd-setting-up" />
###Setting up
For security reasons we run the daemon under its own user. For security reasons we run the daemon under its own user.
@ -56,8 +68,31 @@ Get your public key and check that the daemon initialized correctly:
sudo grep "tox-bootstrapd" /var/log/syslog sudo grep "tox-bootstrapd" /var/log/syslog
``` ```
<a name="systemd-updating" />
###Updating
You want to make sure that the daemon uses the newest toxcore, as there might have been some changes done to the DHT, so it's advised to update the daemon at least once every month.
To update the daemon first stop it:
```sh
sudo systemctl stop tox-bootstrapd.service
```
Then update your toxcore git repository, rebuild the toxcore and the daemon and make sure to install them.
Check if `tox-bootstrapd.service` in toxcore git repository was modified since the last time you copied it, as you might need to update it too.
After all of this is done, simply start the daemon back again:
```sh
sudo systemctl start tox-bootstrapd.service
```
Note that `tox-bootstrapd.service` file might
<a name="systemd-troubleshooting" /> <a name="systemd-troubleshooting" />
###Troubleshooting: ###Troubleshooting
- Check daemon's status: - Check daemon's status:
```sh ```sh
@ -80,8 +115,11 @@ sudo journalctl -f _SYSTEMD_UNIT=tox-bootstrapd.service
- Make sure tox-bootstrapd location matches its path in tox-bootstrapd.service file. - Make sure tox-bootstrapd location matches its path in tox-bootstrapd.service file.
<a name="initd" /> <a name="sysvinit" />
##For `init.d` users ##For `SysVinit` users
<a name="sysvinit-setting-up" />
###Setting up
For security reasons we run the daemon under its own user. For security reasons we run the daemon under its own user.
@ -126,8 +164,29 @@ Get your public key and check that the daemon initialized correctly:
sudo grep "tox-bootstrapd" /var/log/syslog sudo grep "tox-bootstrapd" /var/log/syslog
``` ```
<a name="initd-troubleshooting" /> <a name="sysvinit-updating" />
###Troubleshooting: ###Updating
You want to make sure that the daemon uses the newest toxcore, as there might have been some changes done to the DHT, so it's advised to update the daemon at least once every month.
To update the daemon first stop it:
```sh
sudo service tox-bootstrapd stop
```
Then update your toxcore git repository, rebuild the toxcore and the daemon and make sure to install them.
Check if `tox-bootstrapd.sh` in toxcore git repository was modified since the last time you copied it, as you might need to update it too.
After all of this is done, simply start the daemon back again:
```sh
sudo service tox-bootstrapd start
```
<a name="sysvinit-troubleshooting" />
###Troubleshooting
- Check daemon's status: - Check daemon's status:
```sh ```sh
@ -146,3 +205,63 @@ sudo grep "tox-bootstrapd" /var/log/syslog
- Make sure tox-bootstrapd has read permission for the config file. - Make sure tox-bootstrapd has read permission for the config file.
- Make sure tox-bootstrapd location matches its path in the `/etc/init.d/tox-bootstrapd` init script. - Make sure tox-bootstrapd location matches its path in the `/etc/init.d/tox-bootstrapd` init script.
<a name="docker" />
##For `Docker` users:
<a name="docker-setting-up" />
###Setting up
If you are familiar with Docker and would rather run the daemon in a Docker container, run the following from this directory:
```sh
sudo docker build -t tox-bootstrapd docker/
sudo useradd --home-dir /var/lib/tox-bootstrapd --create-home --system --shell /sbin/nologin --comment "Account to run Tox's DHT bootstrap daemon" --user-group tox-bootstrapd
sudo chmod 700 /var/lib/tox-bootstrapd
sudo docker run -d --name tox-bootstrapd --restart always -v /var/lib/tox-bootstrapd/:/var/lib/tox-bootstrapd/ -p 443:443 -p 3389:3389 -p 33445:33445 -p 33445:33445/udp tox-bootstrapd
```
We create a new user and protect its home directory in order to mount it in the Docker image, so that the kyepair the daemon uses would be stored on the host system, which makes it less likely that you would loose the keypair while playing with or updating the Docker container.
You can check logs for your public key or any errors:
```sh
sudo docker logs tox-bootstrapd
```
Note that the Docker container runs a script which pulls a list of bootstrap nodes off https://nodes.tox.chat/ and adds them in the config file.
<a name="docker-updating" />
###Updating
You want to make sure that the daemon uses the newest toxcore, as there might have been some changes done to the DHT, so it's advised to update the daemon at least once every month.
To update the daemon, all you need is to erase current container with its image:
```sh
sudo docker stop tox-bootstrapd
sudo docker rm tox-bootstrapd
sudo docker rmi tox-bootstrapd
```
Then rebuild and run the image again:
```sh
sudo docker build -t tox-bootstrapd docker/
sudo docker run -d --name tox-bootstrapd --restart always -v /var/lib/tox-bootstrapd/:/var/lib/tox-bootstrapd/ -p 443:443 -p 3389:3389 -p 33445:33445 -p 33445:33445/udp tox-bootstrapd
```
<a name="docker-troubleshooting" />
###Troubleshooting
- Check if the container is running:
```sh
sudo docker ps -a
```
- Check the log for errors:
```sh
sudo docker logs tox-bootstrapd
```

View File

@ -0,0 +1,59 @@
FROM debian:jessie
# get all deps
RUN apt-get update && apt-get install -y \
build-essential \
libtool \
autotools-dev \
automake \
checkinstall \
check \
git \
yasm \
libsodium-dev \
libconfig-dev \
python3 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# install toxcore and daemon
WORKDIR /root/
RUN git clone https://github.com/irungentoo/toxcore
WORKDIR /root/toxcore/
RUN ./autogen.sh
RUN ./configure --enable-daemon
RUN make -j`nproc`
RUN make install -j`nproc`
RUN ldconfig
WORKDIR /root/toxcore/other/bootstrap_daemon/
# add new user
RUN useradd --home-dir /var/lib/tox-bootstrapd --create-home \
--system --shell /sbin/nologin \
--comment "Account to run Tox's DHT bootstrap daemon" \
--user-group tox-bootstrapd
RUN chmod 700 /var/lib/tox-bootstrapd
RUN cp tox-bootstrapd.conf /etc/tox-bootstrapd.conf
# remove all the example bootstrap nodes from the config file
RUN N=-1 && \
while grep -q "bootstrap_nodes =" /etc/tox-bootstrapd.conf; \
do \
head -n $N tox-bootstrapd.conf > /etc/tox-bootstrapd.conf; \
N=$((N-1)); \
done
# add bootstrap nodes from https://nodes.tox.chat/
RUN python3 docker/get-nodes.py >> /etc/tox-bootstrapd.conf
USER tox-bootstrapd
ENTRYPOINT /usr/local/bin/tox-bootstrapd \
--config /etc/tox-bootstrapd.conf \
--log-backend stdout \
--foreground
EXPOSE 443 3389 33445
EXPOSE 33445/udp

View File

@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""
Copyright (c) 2016 by nurupo <nurupo.contributions@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
# Gets a list of nodes from https://nodes.tox.chat/json and prints them out
# in the format of tox-bootstrapd config file.
import urllib.request
import json
response = urllib.request.urlopen('https://nodes.tox.chat/json')
raw_json = response.read().decode('ascii', 'ignore')
nodes = json.loads(raw_json)['nodes']
output = 'bootstrap_nodes = ('
for node in nodes:
node_output = ' { // ' + node['maintainer'] + '\n'
node_output += ' public_key = "' + node['public_key'] + '"\n'
node_output += ' port = ' + str(node['port']) + '\n'
node_output += ' address = "'
if len(node['ipv4']) > 4:
output += node_output + node['ipv4'] + '"\n },\n'
if len(node['ipv6']) > 4:
output += node_output + node['ipv6'] + '"\n },\n'
# remove last comma
output = output[:-2] + '\n)\n'
print(output)

View File

@ -0,0 +1,38 @@
if BUILD_DHT_BOOTSTRAP_DAEMON
bin_PROGRAMS += tox-bootstrapd
tox_bootstrapd_SOURCES = \
../other/bootstrap_daemon/src/command_line_arguments.c \
../other/bootstrap_daemon/src/command_line_arguments.h \
../other/bootstrap_daemon/src/config.c \
../other/bootstrap_daemon/src/config_defaults.h \
../other/bootstrap_daemon/src/config.h \
../other/bootstrap_daemon/src/log.c \
../other/bootstrap_daemon/src/log.h \
../other/bootstrap_daemon/src/tox-bootstrapd.c \
../other/bootstrap_node_packets.c \
../other/bootstrap_node_packets.h
tox_bootstrapd_CFLAGS = \
-I$(top_srcdir)/other/bootstrap_daemon \
$(LIBSODIUM_CFLAGS) \
$(NACL_CFLAGS) \
$(LIBCONFIG_CFLAGS)
tox_bootstrapd_LDADD = \
$(LIBSODIUM_LDFLAGS) \
$(NACL_LDFLAGS) \
libtoxcore.la \
$(LIBCONFIG_LIBS) \
$(LIBSODIUM_LIBS) \
$(NACL_LIBS)
endif
EXTRA_DIST += \
$(top_srcdir)/other/bootstrap_daemon/tox-bootstrapd.conf \
$(top_srcdir)/other/bootstrap_daemon/tox-bootstrapd.service \
$(top_srcdir)/other/bootstrap_daemon/tox-bootstrapd.sh

View File

@ -0,0 +1,145 @@
/* command_line_arguments.c
*
* Tox DHT bootstrap daemon.
* Command line argument handling.
*
* Copyright (C) 2015-2016 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 "command_line_arguments.h"
#include "global.h"
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
/**
* Prints --help message
*/
void print_help()
{
// 2 space ident
// make sure all lines fit into 80 columns
// make sure options are listed in alphabetical order
write_log(LOG_LEVEL_INFO,
"Usage: tox-bootstrapd [OPTION]... --config=FILE_PATH\n"
"\n"
"Options:\n"
" --config=FILE_PATH Specify path to the config file.\n"
" This is a required option.\n"
" Set FILE_PATH to a path to an empty file in order to\n"
" use default settings.\n"
" --foreground Run the daemon in foreground. The daemon won't fork\n"
" (detach from the terminal) and won't use the PID file.\n"
" --help Print this help message.\n"
" --log-backend=BACKEND Specify which logging backend to use.\n"
" Valid BACKEND values (case sensetive):\n"
" syslog Writes log messages to syslog.\n"
" Default option when no --log-backend is\n"
" specified.\n"
" stdout Writes log messages to stdout/stderr.\n"
" --version Print version information.\n");
}
void handle_command_line_arguments(int argc, char *argv[], char **cfg_file_path, LOG_BACKEND *log_backend, bool *run_in_foreground)
{
if (argc < 2) {
write_log(LOG_LEVEL_ERROR, "Error: No arguments provided.\n\n");
print_help();
exit(1);
}
opterr = 0;
static struct option long_options[] = {
{"config", required_argument, 0, 'c'}, // required option
{"foreground", no_argument, 0, 'f'},
{"help", no_argument, 0, 'h'},
{"log-backend", required_argument, 0, 'l'}, // optional, defaults to syslog
{"version", no_argument, 0, 'v'},
{0, 0, 0, 0 }
};
bool cfg_file_path_set = false;
bool log_backend_set = false;
*run_in_foreground = false;
int opt;
while ((opt = getopt_long(argc, argv, ":", long_options, NULL)) != -1) {
switch (opt) {
case 'c':
*cfg_file_path = optarg;
cfg_file_path_set = true;
break;
case 'f':
*run_in_foreground = true;
break;
case 'h':
print_help();
exit(0);
case 'l':
if (strcmp(optarg, "syslog") == 0) {
*log_backend = LOG_BACKEND_SYSLOG;
log_backend_set = true;
} else if (strcmp(optarg, "stdout") == 0) {
*log_backend = LOG_BACKEND_STDOUT;
log_backend_set = true;
} else {
write_log(LOG_LEVEL_ERROR, "Error: Invalid BACKEND value for --log-backend option passed: %s\n\n", optarg);
print_help();
exit(1);
}
break;
case 'v':
write_log(LOG_LEVEL_INFO, "Version: %lu\n", DAEMON_VERSION_NUMBER);
exit(0);
case '?':
write_log(LOG_LEVEL_ERROR, "Error: Unrecognized option %s\n\n", argv[optind-1]);
print_help();
exit(1);
case ':':
write_log(LOG_LEVEL_ERROR, "Error: No argument provided for option %s\n\n", argv[optind-1]);
print_help();
exit(1);
}
}
if (!log_backend_set) {
*log_backend = LOG_BACKEND_SYSLOG;
}
if (!cfg_file_path_set) {
write_log(LOG_LEVEL_ERROR, "Error: The required --config option wasn't specified\n\n");
print_help();
exit(1);
}
}

View File

@ -0,0 +1,42 @@
/* command_line_arguments.h
*
* Tox DHT bootstrap daemon.
* Command line argument handling.
*
* Copyright (C) 2015-2016 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/>.
*
*/
#ifndef COMMAND_LINE_ARGUMENTS_H
#define COMMAND_LINE_ARGUMENTS_H
#include "log.h"
/**
* Handles command line arguments, setting cfg_file_path and log_backend.
* Terminates the application if incorrect arguments are specified.
*
* @param argc Argc passed into main().
* @param argv Argv passed into main().
* @param cfg_file_path Sets to the provided by the user config file path.
* @param log_backend Sets to the provided by the user log backend option.
* @param run_in_foreground Sets to the provided by the user foreground option.
*/
void handle_command_line_arguments(int argc, char *argv[], char **cfg_file_path, LOG_BACKEND *log_backend, bool *run_in_foreground);
#endif // COMMAND_LINE_ARGUMENTS_H

View File

@ -0,0 +1,426 @@
/* config.c
*
* Tox DHT bootstrap daemon.
* Functionality related to dealing with the config file.
*
* Copyright (C) 2014-2016 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 "config.h"
#include "config_defaults.h"
#include "log.h"
#include <stdlib.h>
#include <string.h>
#include <string.h>
#include <libconfig.h>
#include "../../bootstrap_node_packets.h"
/**
* Parses tcp relay ports from `cfg` and puts them into `tcp_relay_ports` array.
*
* Supposed to be called from get_general_config only.
*
* Important: iff `tcp_relay_port_count` > 0, then you are responsible for freeing `tcp_relay_ports`.
*/
void parse_tcp_relay_ports_config(config_t *cfg, uint16_t **tcp_relay_ports, int *tcp_relay_port_count)
{
const char *NAME_TCP_RELAY_PORTS = "tcp_relay_ports";
*tcp_relay_port_count = 0;
config_setting_t *ports_array = config_lookup(cfg, NAME_TCP_RELAY_PORTS);
if (ports_array == NULL) {
write_log(LOG_LEVEL_WARNING, "No '%s' setting in the configuration file.\n", NAME_TCP_RELAY_PORTS);
write_log(LOG_LEVEL_WARNING, "Using default '%s':\n", NAME_TCP_RELAY_PORTS);
uint16_t default_ports[DEFAULT_TCP_RELAY_PORTS_COUNT] = {DEFAULT_TCP_RELAY_PORTS};
int i;
for (i = 0; i < DEFAULT_TCP_RELAY_PORTS_COUNT; i ++) {
write_log(LOG_LEVEL_INFO, "Port #%d: %u\n", i, default_ports[i]);
}
// similar procedure to the one of reading config file below
*tcp_relay_ports = malloc(DEFAULT_TCP_RELAY_PORTS_COUNT * sizeof(uint16_t));
for (i = 0; i < DEFAULT_TCP_RELAY_PORTS_COUNT; i ++) {
(*tcp_relay_ports)[*tcp_relay_port_count] = default_ports[i];
if ((*tcp_relay_ports)[*tcp_relay_port_count] < MIN_ALLOWED_PORT
|| (*tcp_relay_ports)[*tcp_relay_port_count] > MAX_ALLOWED_PORT) {
write_log(LOG_LEVEL_WARNING, "Port #%d: Invalid port: %u, should be in [%d, %d]. Skipping.\n", i,
(*tcp_relay_ports)[*tcp_relay_port_count], MIN_ALLOWED_PORT, MAX_ALLOWED_PORT);
continue;
}
(*tcp_relay_port_count) ++;
}
// the loop above skips invalid ports, so we adjust the allocated memory size
if ((*tcp_relay_port_count) > 0) {
*tcp_relay_ports = realloc(*tcp_relay_ports, (*tcp_relay_port_count) * sizeof(uint16_t));
} else {
free(*tcp_relay_ports);
*tcp_relay_ports = NULL;
}
return;
}
if (config_setting_is_array(ports_array) == CONFIG_FALSE) {
write_log(LOG_LEVEL_ERROR, "'%s' setting should be an array. Array syntax: 'setting = [value1, value2, ...]'.\n",
NAME_TCP_RELAY_PORTS);
return;
}
int config_port_count = config_setting_length(ports_array);
if (config_port_count == 0) {
write_log(LOG_LEVEL_ERROR, "'%s' is empty.\n", NAME_TCP_RELAY_PORTS);
return;
}
*tcp_relay_ports = malloc(config_port_count * sizeof(uint16_t));
int i;
for (i = 0; i < config_port_count; i ++) {
config_setting_t *elem = config_setting_get_elem(ports_array, i);
if (elem == NULL) {
// it's NULL if `ports_array` is not an array (we have that check earlier) or if `i` is out of range, which should not be
write_log(LOG_LEVEL_WARNING, "Port #%d: Something went wrong while parsing the port. Stopping reading ports.\n", i);
break;
}
if (config_setting_is_number(elem) == CONFIG_FALSE) {
write_log(LOG_LEVEL_WARNING, "Port #%d: Not a number. Skipping.\n", i);
continue;
}
(*tcp_relay_ports)[*tcp_relay_port_count] = config_setting_get_int(elem);
if ((*tcp_relay_ports)[*tcp_relay_port_count] < MIN_ALLOWED_PORT
|| (*tcp_relay_ports)[*tcp_relay_port_count] > MAX_ALLOWED_PORT) {
write_log(LOG_LEVEL_WARNING, "Port #%d: Invalid port: %u, should be in [%d, %d]. Skipping.\n", i,
(*tcp_relay_ports)[*tcp_relay_port_count], MIN_ALLOWED_PORT, MAX_ALLOWED_PORT);
continue;
}
(*tcp_relay_port_count) ++;
}
// the loop above skips invalid ports, so we adjust the allocated memory size
if ((*tcp_relay_port_count) > 0) {
*tcp_relay_ports = realloc(*tcp_relay_ports, (*tcp_relay_port_count) * sizeof(uint16_t));
} else {
free(*tcp_relay_ports);
*tcp_relay_ports = NULL;
}
}
int get_general_config(const char *cfg_file_path, char **pid_file_path, char **keys_file_path, int *port, int *enable_ipv6,
int *enable_ipv4_fallback, int *enable_lan_discovery, int *enable_tcp_relay, uint16_t **tcp_relay_ports,
int *tcp_relay_port_count, int *enable_motd, char **motd)
{
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_IPV4_FALLBACK = "enable_ipv4_fallback";
const char *NAME_ENABLE_LAN_DISCOVERY = "enable_lan_discovery";
const char *NAME_ENABLE_TCP_RELAY = "enable_tcp_relay";
const char *NAME_ENABLE_MOTD = "enable_motd";
const char *NAME_MOTD = "motd";
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) {
write_log(LOG_LEVEL_ERROR, "%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) {
write_log(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_PORT);
write_log(LOG_LEVEL_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) {
write_log(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_PID_FILE_PATH);
write_log(LOG_LEVEL_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) {
write_log(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_KEYS_FILE_PATH);
write_log(LOG_LEVEL_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) {
write_log(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_IPV6);
write_log(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_ENABLE_IPV6, DEFAULT_ENABLE_IPV6 ? "true" : "false");
*enable_ipv6 = DEFAULT_ENABLE_IPV6;
}
// Get IPv4 fallback option
if (config_lookup_bool(&cfg, NAME_ENABLE_IPV4_FALLBACK, enable_ipv4_fallback) == CONFIG_FALSE) {
write_log(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_IPV4_FALLBACK);
write_log(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_ENABLE_IPV4_FALLBACK,
DEFAULT_ENABLE_IPV4_FALLBACK ? "true" : "false");
*enable_ipv4_fallback = DEFAULT_ENABLE_IPV4_FALLBACK;
}
// Get LAN discovery option
if (config_lookup_bool(&cfg, NAME_ENABLE_LAN_DISCOVERY, enable_lan_discovery) == CONFIG_FALSE) {
write_log(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_LAN_DISCOVERY);
write_log(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_ENABLE_LAN_DISCOVERY,
DEFAULT_ENABLE_LAN_DISCOVERY ? "true" : "false");
*enable_lan_discovery = DEFAULT_ENABLE_LAN_DISCOVERY;
}
// Get TCP relay option
if (config_lookup_bool(&cfg, NAME_ENABLE_TCP_RELAY, enable_tcp_relay) == CONFIG_FALSE) {
write_log(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_TCP_RELAY);
write_log(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_ENABLE_TCP_RELAY,
DEFAULT_ENABLE_TCP_RELAY ? "true" : "false");
*enable_tcp_relay = DEFAULT_ENABLE_TCP_RELAY;
}
if (*enable_tcp_relay) {
parse_tcp_relay_ports_config(&cfg, tcp_relay_ports, tcp_relay_port_count);
} else {
*tcp_relay_port_count = 0;
}
// Get MOTD option
if (config_lookup_bool(&cfg, NAME_ENABLE_MOTD, enable_motd) == CONFIG_FALSE) {
write_log(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_MOTD);
write_log(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_ENABLE_MOTD,
DEFAULT_ENABLE_MOTD ? "true" : "false");
*enable_motd = DEFAULT_ENABLE_MOTD;
}
if (*enable_motd) {
// Get MOTD
const char *tmp_motd;
if (config_lookup_string(&cfg, NAME_MOTD, &tmp_motd) == CONFIG_FALSE) {
write_log(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_MOTD);
write_log(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_MOTD, DEFAULT_MOTD);
tmp_motd = DEFAULT_MOTD;
}
size_t tmp_motd_length = strlen(tmp_motd) + 1;
size_t motd_length = tmp_motd_length > MAX_MOTD_LENGTH ? MAX_MOTD_LENGTH : tmp_motd_length;
*motd = malloc(motd_length);
strncpy(*motd, tmp_motd, motd_length);
(*motd)[motd_length - 1] = '\0';
}
config_destroy(&cfg);
write_log(LOG_LEVEL_INFO, "Successfully read:\n");
write_log(LOG_LEVEL_INFO, "'%s': %s\n", NAME_PID_FILE_PATH, *pid_file_path);
write_log(LOG_LEVEL_INFO, "'%s': %s\n", NAME_KEYS_FILE_PATH, *keys_file_path);
write_log(LOG_LEVEL_INFO, "'%s': %d\n", NAME_PORT, *port);
write_log(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_IPV6, *enable_ipv6 ? "true" : "false");
write_log(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_IPV4_FALLBACK, *enable_ipv4_fallback ? "true" : "false");
write_log(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_LAN_DISCOVERY, *enable_lan_discovery ? "true" : "false");
write_log(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_TCP_RELAY, *enable_tcp_relay ? "true" : "false");
// show info about tcp ports only if tcp relay is enabled
if (*enable_tcp_relay) {
if (*tcp_relay_port_count == 0) {
write_log(LOG_LEVEL_ERROR, "No TCP ports could be read.\n");
} else {
write_log(LOG_LEVEL_INFO, "Read %d TCP ports:\n", *tcp_relay_port_count);
int i;
for (i = 0; i < *tcp_relay_port_count; i ++) {
write_log(LOG_LEVEL_INFO, "Port #%d: %u\n", i, (*tcp_relay_ports)[i]);
}
}
}
write_log(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_MOTD, *enable_motd ? "true" : "false");
if (*enable_motd) {
write_log(LOG_LEVEL_INFO, "'%s': %s\n", NAME_MOTD, *motd);
}
return 1;
}
/**
*
* Converts a hex string with even number of characters into binary.
*
* Important: You are responsible for freeing the return value.
*
* @return binary on success,
* NULL on failure.
*/
uint8_t *hex_string_to_bin(char *hex_string)
{
if (strlen(hex_string) % 2 != 0) {
return NULL;
}
size_t len = strlen(hex_string) / 2;
uint8_t *ret = malloc(len);
char *pos = hex_string;
size_t i;
for (i = 0; i < len; ++i, pos += 2) {
sscanf(pos, "%2hhx", &ret[i]);
}
return ret;
}
int bootstrap_from_config(const char *cfg_file_path, DHT *dht, int enable_ipv6)
{
const char *NAME_BOOTSTRAP_NODES = "bootstrap_nodes";
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) {
write_log(LOG_LEVEL_ERROR, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
return 0;
}
config_setting_t *node_list = config_lookup(&cfg, NAME_BOOTSTRAP_NODES);
if (node_list == NULL) {
write_log(LOG_LEVEL_WARNING, "No '%s' setting in the configuration file. Skipping bootstrapping.\n", NAME_BOOTSTRAP_NODES);
config_destroy(&cfg);
return 1;
}
if (config_setting_length(node_list) == 0) {
write_log(LOG_LEVEL_WARNING, "No bootstrap nodes found. Skipping bootstrapping.\n");
config_destroy(&cfg);
return 1;
}
int bs_port;
const char *bs_address;
const char *bs_public_key;
config_setting_t *node;
int i = 0;
while (config_setting_length(node_list)) {
node = config_setting_get_elem(node_list, 0);
if (node == NULL) {
config_destroy(&cfg);
return 0;
}
// Check that all settings are present
if (config_setting_lookup_string(node, NAME_PUBLIC_KEY, &bs_public_key) == CONFIG_FALSE) {
write_log(LOG_LEVEL_WARNING, "Bootstrap node #%d: Couldn't find '%s' setting. Skipping the node.\n", i, NAME_PUBLIC_KEY);
goto next;
}
if (config_setting_lookup_int(node, NAME_PORT, &bs_port) == CONFIG_FALSE) {
write_log(LOG_LEVEL_WARNING, "Bootstrap node #%d: Couldn't find '%s' setting. Skipping the node.\n", i, NAME_PORT);
goto next;
}
if (config_setting_lookup_string(node, NAME_ADDRESS, &bs_address) == CONFIG_FALSE) {
write_log(LOG_LEVEL_WARNING, "Bootstrap node #%d: Couldn't find '%s' setting. Skipping the node.\n", i, NAME_ADDRESS);
goto next;
}
// Process settings
if (strlen(bs_public_key) != crypto_box_PUBLICKEYBYTES * 2) {
write_log(LOG_LEVEL_WARNING, "Bootstrap node #%d: Invalid '%s': %s. Skipping the node.\n", i, NAME_PUBLIC_KEY,
bs_public_key);
goto next;
}
if (bs_port < MIN_ALLOWED_PORT || bs_port > MAX_ALLOWED_PORT) {
write_log(LOG_LEVEL_WARNING, "Bootstrap node #%d: Invalid '%s': %d, should be in [%d, %d]. Skipping the node.\n", i, NAME_PORT,
bs_port, MIN_ALLOWED_PORT, MAX_ALLOWED_PORT);
goto next;
}
uint8_t *bs_public_key_bin = hex_string_to_bin((char *)bs_public_key);
const int address_resolved = DHT_bootstrap_from_address(dht, bs_address, enable_ipv6, htons(bs_port),
bs_public_key_bin);
free(bs_public_key_bin);
if (!address_resolved) {
write_log(LOG_LEVEL_WARNING, "Bootstrap node #%d: Invalid '%s': %s. Skipping the node.\n", i, NAME_ADDRESS, bs_address);
goto next;
}
write_log(LOG_LEVEL_INFO, "Successfully added bootstrap node #%d: %s:%d %s\n", i, bs_address, bs_port, bs_public_key);
next:
// config_setting_lookup_string() allocates string inside and doesn't allow us to free it direcly
// though it's freed when the element is removed, so we free it right away in order to keep memory
// consumption minimal
config_setting_remove_elem(node_list, 0);
i++;
}
config_destroy(&cfg);
return 1;
}

View File

@ -0,0 +1,52 @@
/* config.h
*
* Tox DHT bootstrap daemon.
* Functionality related to dealing with the config file.
*
* Copyright (C) 2014-2016 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/>.
*
*/
#ifndef CONFIG_H
#define CONFIG_H
#include "../../../toxcore/DHT.h"
/**
* Gets general config options from the config file.
*
* Important: You are responsible for freeing `pid_file_path` and `keys_file_path`
* also, iff `tcp_relay_ports_count` > 0, then you are responsible for freeing `tcp_relay_ports`
* and also `motd` iff `enable_motd` is set.
*
* @return 1 on success,
* 0 on failure, doesn't modify any data pointed by arguments.
*/
int get_general_config(const char *cfg_file_path, char **pid_file_path, char **keys_file_path, int *port, int *enable_ipv6,
int *enable_ipv4_fallback, int *enable_lan_discovery, int *enable_tcp_relay, uint16_t **tcp_relay_ports,
int *tcp_relay_port_count, int *enable_motd, char **motd);
/**
* Bootstraps off nodes listed in the config file.
*
* @return 1 on success, some or no bootstrap nodes were added
* 0 on failure, a error accured while parsing config file.
*/
int bootstrap_from_config(const char *cfg_file_path, DHT *dht, int enable_ipv6);
#endif // CONFIG_H

View File

@ -0,0 +1,42 @@
/* config_defaults.h
*
* Tox DHT bootstrap daemon.
* Default config options for when they are missing in the config file.
*
* Copyright (C) 2014-2016 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/>.
*
*/
#ifndef CONFIG_DEFAULTS_H
#define CONFIG_DEFAULTS_H
#include "global.h"
#define DEFAULT_PID_FILE_PATH "tox-bootstrapd.pid"
#define DEFAULT_KEYS_FILE_PATH "tox-bootstrapd.keys"
#define DEFAULT_PORT 33445
#define DEFAULT_ENABLE_IPV6 1 // 1 - true, 0 - false
#define DEFAULT_ENABLE_IPV4_FALLBACK 1 // 1 - true, 0 - false
#define DEFAULT_ENABLE_LAN_DISCOVERY 1 // 1 - true, 0 - false
#define DEFAULT_ENABLE_TCP_RELAY 1 // 1 - true, 0 - false
#define DEFAULT_TCP_RELAY_PORTS 443, 3389, 33445 // comma-separated list of ports. make sure to adjust DEFAULT_TCP_RELAY_PORTS_COUNT accordingly
#define DEFAULT_TCP_RELAY_PORTS_COUNT 3
#define DEFAULT_ENABLE_MOTD 1 // 1 - true, 0 - false
#define DEFAULT_MOTD DAEMON_NAME
#endif // CONFIG_DEFAULTS_H

View File

@ -0,0 +1,34 @@
/* global.h
*
* Tox DHT bootstrap daemon.
* Globally used defines.
*
* Copyright (C) 2014-2016 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/>.
*
*/
#ifndef GLOBAL_H
#define GLOBAL_H
#define DAEMON_NAME "tox-bootstrapd"
#define DAEMON_VERSION_NUMBER 2016010100UL // yyyymmmddvv format: yyyy year, mm month, dd day, vv version change count for that day
#define MIN_ALLOWED_PORT 1
#define MAX_ALLOWED_PORT 65535
#endif // GLOBAL_H

View File

@ -0,0 +1,117 @@
/* log.c
*
* Tox DHT bootstrap daemon.
* Logging utility with support of multipel logging backends.
*
* Copyright (C) 2015-2016 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 "log.h"
#include "global.h"
#include <syslog.h>
#include <stdarg.h>
#include <stdio.h>
LOG_BACKEND current_backend = -1;
bool open_log(LOG_BACKEND backend)
{
if (current_backend != -1) {
return false;
}
if (backend == LOG_BACKEND_SYSLOG) {
openlog(DAEMON_NAME, LOG_NOWAIT | LOG_PID, LOG_DAEMON);
}
current_backend = backend;
return true;
}
bool close_log()
{
if (current_backend == -1) {
return false;
}
if (current_backend == LOG_BACKEND_SYSLOG) {
closelog();
}
current_backend = -1;
return true;
}
int level_syslog(LOG_LEVEL level)
{
switch (level) {
case LOG_LEVEL_INFO:
return LOG_INFO;
case LOG_LEVEL_WARNING:
return LOG_WARNING;
case LOG_LEVEL_ERROR:
return LOG_ERR;
}
}
void log_syslog(LOG_LEVEL level, const char *format, va_list args)
{
vsyslog(level_syslog(level), format, args);
}
FILE* level_stdout(LOG_LEVEL level)
{
switch (level) {
case LOG_LEVEL_INFO:
return stdout;
case LOG_LEVEL_WARNING: // intentional fallthrough
case LOG_LEVEL_ERROR:
return stderr;
}
}
void log_stdout(LOG_LEVEL level, const char *format, va_list args)
{
vfprintf(level_stdout(level), format, args);
fflush(level_stdout(level));
}
bool write_log(LOG_LEVEL level, const char *format, ...)
{
va_list args;
va_start(args, format);
switch (current_backend) {
case LOG_BACKEND_SYSLOG:
log_syslog(level, format, args);
break;
case LOG_BACKEND_STDOUT:
log_stdout(level, format, args);
break;
}
va_end(args);
return current_backend != -1;
}

View File

@ -0,0 +1,64 @@
/* log.h
*
* Tox DHT bootstrap daemon.
* Logging utility with support of multipel logging backends.
*
* Copyright (C) 2015-2016 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/>.
*
*/
#ifndef LOG_H
#define LOG_H
#include <stdbool.h>
typedef enum LOG_BACKEND {
LOG_BACKEND_SYSLOG,
LOG_BACKEND_STDOUT
} LOG_BACKEND;
typedef enum LOG_LEVEL {
LOG_LEVEL_INFO,
LOG_LEVEL_WARNING,
LOG_LEVEL_ERROR
} LOG_LEVEL;
/**
* Initializes logger.
* @param backend Specifies which backend to use.
* @return true on success, flase if log is already opened.
*/
bool open_log(LOG_BACKEND backend);
/**
* Releases all used resources by the logger.
* @return true on success, flase if log is already closed.
*/
bool close_log();
/**
* Writes a message to the log.
* @param level Log level to use.
* @param format printf-like format string.
* @param ... Zero or more arguments, similar to printf function.
* @return true on success, flase if log is closed.
*/
bool write_log(LOG_LEVEL level, const char *format, ...);
#endif // LOG_H

View File

@ -0,0 +1,342 @@
/* tox-bootstrapd.c
*
* Tox DHT bootstrap daemon.
* Main file.
*
* Copyright (C) 2014-2016 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/>.
*
*/
// system provided
#include <unistd.h>
// C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// toxcore
#include "../../../toxcore/LAN_discovery.h"
#include "../../../toxcore/onion_announce.h"
#include "../../../toxcore/TCP_server.h"
#include "../../../toxcore/util.h"
// misc
#include "../../bootstrap_node_packets.h"
#include "command_line_arguments.h"
#include "config.h"
#include "global.h"
#include "log.h"
#define SLEEP_MILLISECONDS(MS) usleep(1000*MS)
// 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) {
const size_t read_size = fread(keys, sizeof(uint8_t), KEYS_SIZE, keys_file);
if (read_size != KEYS_SIZE) {
fclose(keys_file);
return 0;
}
memcpy(dht->self_public_key, keys, crypto_box_PUBLICKEYBYTES);
memcpy(dht->self_secret_key, keys + crypto_box_PUBLICKEYBYTES, crypto_box_SECRETKEYBYTES);
} else {
// Otherwise save new keys
memcpy(keys, dht->self_public_key, crypto_box_PUBLICKEYBYTES);
memcpy(keys + crypto_box_PUBLICKEYBYTES, dht->self_secret_key, crypto_box_SECRETKEYBYTES);
keys_file = fopen(keys_file_path, "w");
if (!keys_file)
return 0;
const size_t write_size = fwrite(keys, sizeof(uint8_t), KEYS_SIZE, keys_file);
if (write_size != KEYS_SIZE) {
fclose(keys_file);
return 0;
}
}
fclose(keys_file);
return 1;
}
// Prints public key
void print_public_key(const uint8_t *public_key)
{
char buffer[2 * crypto_box_PUBLICKEYBYTES + 1];
int index = 0;
size_t i;
for (i = 0; i < crypto_box_PUBLICKEYBYTES; i++) {
index += sprintf(buffer + index, "%02hhX", public_key[i]);
}
write_log(LOG_LEVEL_INFO, "Public Key: %s\n", buffer);
return;
}
// Demonizes the process, appending PID to the PID file and closing file descriptors based on log backend
// Terminates the application if the daemonization fails.
void daemonize(LOG_BACKEND log_backend, char *pid_file_path)
{
// Check if the PID file exists
FILE *pid_file;
if ((pid_file = fopen(pid_file_path, "r"))) {
write_log(LOG_LEVEL_WARNING, "Another instance of the daemon is already running, PID file %s exists.\n", pid_file_path);
fclose(pid_file);
}
// Open the PID file for writing
pid_file = fopen(pid_file_path, "a+");
if (pid_file == NULL) {
write_log(LOG_LEVEL_ERROR, "Couldn't open the PID file for writing: %s. Exiting.\n", pid_file_path);
exit(1);
}
// Fork off from the parent process
const pid_t pid = fork();
if (pid > 0) {
fprintf(pid_file, "%d", pid);
fclose(pid_file);
write_log(LOG_LEVEL_INFO, "Forked successfully: PID: %d.\n", pid);
exit(0);
} else {
fclose(pid_file);
}
if (pid < 0) {
write_log(LOG_LEVEL_ERROR, "Forking failed. Exiting.\n");
exit(1);
}
// Create a new SID for the child process
if (setsid() < 0) {
write_log(LOG_LEVEL_ERROR, "SID creation failure. Exiting.\n");
exit(1);
}
// Change the file mode mask
umask(0);
// Change the current working directory
if ((chdir("/")) < 0) {
write_log(LOG_LEVEL_ERROR, "Couldn't change working directory to '/'. Exiting.\n");
exit(1);
}
// Go quiet
if (log_backend != LOG_BACKEND_STDOUT) {
close(STDOUT_FILENO);
close(STDIN_FILENO);
close(STDERR_FILENO);
}
}
int main(int argc, char *argv[])
{
char *cfg_file_path;
LOG_BACKEND log_backend;
bool run_in_foreground;
// choose backend for printing command line argument parsing output based on whether the daemon is being run from a terminal
log_backend = isatty(STDOUT_FILENO) ? LOG_BACKEND_STDOUT : LOG_BACKEND_SYSLOG;
open_log(log_backend);
handle_command_line_arguments(argc, argv, &cfg_file_path, &log_backend, &run_in_foreground);
close_log();
open_log(log_backend);
write_log(LOG_LEVEL_INFO, "Running \"%s\" version %lu.\n", DAEMON_NAME, DAEMON_VERSION_NUMBER);
char *pid_file_path, *keys_file_path;
int port;
int enable_ipv6;
int enable_ipv4_fallback;
int enable_lan_discovery;
int enable_tcp_relay;
uint16_t *tcp_relay_ports;
int tcp_relay_port_count;
int enable_motd;
char *motd;
if (get_general_config(cfg_file_path, &pid_file_path, &keys_file_path, &port, &enable_ipv6, &enable_ipv4_fallback,
&enable_lan_discovery, &enable_tcp_relay, &tcp_relay_ports, &tcp_relay_port_count, &enable_motd, &motd)) {
write_log(LOG_LEVEL_INFO, "General config read successfully\n");
} else {
write_log(LOG_LEVEL_ERROR, "Couldn't read config file: %s. Exiting.\n", cfg_file_path);
return 1;
}
if (port < MIN_ALLOWED_PORT || port > MAX_ALLOWED_PORT) {
write_log(LOG_LEVEL_ERROR, "Invalid port: %d, should be in [%d, %d]. Exiting.\n", port, MIN_ALLOWED_PORT, MAX_ALLOWED_PORT);
return 1;
}
if (!run_in_foreground) {
daemonize(log_backend, pid_file_path);
}
free(pid_file_path);
IP ip;
ip_init(&ip, enable_ipv6);
Networking_Core *net = new_networking(ip, port);
if (net == NULL) {
if (enable_ipv6 && enable_ipv4_fallback) {
write_log(LOG_LEVEL_WARNING, "Couldn't initialize IPv6 networking. Falling back to using IPv4.\n");
enable_ipv6 = 0;
ip_init(&ip, enable_ipv6);
net = new_networking(ip, port);
if (net == NULL) {
write_log(LOG_LEVEL_ERROR, "Couldn't fallback to IPv4. Exiting.\n");
return 1;
}
} else {
write_log(LOG_LEVEL_ERROR, "Couldn't initialize networking. Exiting.\n");
return 1;
}
}
DHT *dht = new_DHT(net);
if (dht == NULL) {
write_log(LOG_LEVEL_ERROR, "Couldn't initialize Tox DHT instance. Exiting.\n");
return 1;
}
Onion *onion = new_onion(dht);
Onion_Announce *onion_a = new_onion_announce(dht);
if (!(onion && onion_a)) {
write_log(LOG_LEVEL_ERROR, "Couldn't initialize Tox Onion. Exiting.\n");
return 1;
}
if (enable_motd) {
if (bootstrap_set_callbacks(dht->net, DAEMON_VERSION_NUMBER, (uint8_t *)motd, strlen(motd) + 1) == 0) {
write_log(LOG_LEVEL_INFO, "Set MOTD successfully.\n");
} else {
write_log(LOG_LEVEL_ERROR, "Couldn't set MOTD: %s. Exiting.\n", motd);
return 1;
}
free(motd);
}
if (manage_keys(dht, keys_file_path)) {
write_log(LOG_LEVEL_INFO, "Keys are managed successfully.\n");
} else {
write_log(LOG_LEVEL_ERROR, "Couldn't read/write: %s. Exiting.\n", keys_file_path);
return 1;
}
free(keys_file_path);
TCP_Server *tcp_server = NULL;
if (enable_tcp_relay) {
if (tcp_relay_port_count == 0) {
write_log(LOG_LEVEL_ERROR, "No TCP relay ports read. Exiting.\n");
return 1;
}
tcp_server = new_TCP_server(enable_ipv6, tcp_relay_port_count, tcp_relay_ports, dht->self_secret_key, onion);
// tcp_relay_port_count != 0 at this point
free(tcp_relay_ports);
if (tcp_server != NULL) {
write_log(LOG_LEVEL_INFO, "Initialized Tox TCP server successfully.\n");
} else {
write_log(LOG_LEVEL_ERROR, "Couldn't initialize Tox TCP server. Exiting.\n");
return 1;
}
}
if (bootstrap_from_config(cfg_file_path, dht, enable_ipv6)) {
write_log(LOG_LEVEL_INFO, "List of bootstrap nodes read successfully.\n");
} else {
write_log(LOG_LEVEL_ERROR, "Couldn't read list of bootstrap nodes in %s. Exiting.\n", cfg_file_path);
return 1;
}
print_public_key(dht->self_public_key);
uint64_t last_LANdiscovery = 0;
const uint16_t htons_port = htons(port);
int waiting_for_dht_connection = 1;
if (enable_lan_discovery) {
LANdiscovery_init(dht);
write_log(LOG_LEVEL_INFO, "Initialized LAN discovery successfully.\n");
}
while (1) {
do_DHT(dht);
if (enable_lan_discovery && is_timeout(last_LANdiscovery, LAN_DISCOVERY_INTERVAL)) {
send_LANdiscovery(htons_port, dht);
last_LANdiscovery = unix_time();
}
if (enable_tcp_relay) {
do_TCP_server(tcp_server);
}
networking_poll(dht->net);
if (waiting_for_dht_connection && DHT_isconnected(dht)) {
write_log(LOG_LEVEL_INFO, "Connected to another bootstrap node successfully.\n");
waiting_for_dht_connection = 0;
}
SLEEP_MILLISECONDS(30);
}
return 1;
}

View File

@ -1,730 +0,0 @@
/* tox-bootstrapd.c
*
* Tox DHT bootstrap daemon.
*
* 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/>.
*
*/
// system provided
#include <arpa/inet.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
// C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 3rd party
#include <libconfig.h>
// ./configure
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
// toxcore
#include "../../toxcore/LAN_discovery.h"
#include "../../toxcore/onion_announce.h"
#include "../../toxcore/TCP_server.h"
#include "../../toxcore/util.h"
// misc
#include "../bootstrap_node_packets.c"
#include "../../testing/misc_tools.c"
#define DAEMON_NAME "tox-bootstrapd"
#define DAEMON_VERSION_NUMBER 2014101200UL // yyyymmmddvv format: yyyy year, mm month, dd day, vv version change count for that day
#define SLEEP_TIME_MILLISECONDS 30
#define sleep usleep(1000*SLEEP_TIME_MILLISECONDS)
#define DEFAULT_PID_FILE_PATH "tox-bootstrapd.pid"
#define DEFAULT_KEYS_FILE_PATH "tox-bootstrapd.keys"
#define DEFAULT_PORT 33445
#define DEFAULT_ENABLE_IPV6 1 // 1 - true, 0 - false
#define DEFAULT_ENABLE_IPV4_FALLBACK 1 // 1 - true, 0 - false
#define DEFAULT_ENABLE_LAN_DISCOVERY 1 // 1 - true, 0 - false
#define DEFAULT_ENABLE_TCP_RELAY 1 // 1 - true, 0 - false
#define DEFAULT_TCP_RELAY_PORTS 443, 3389, 33445 // comma-separated list of ports. make sure to adjust DEFAULT_TCP_RELAY_PORTS_COUNT accordingly
#define DEFAULT_TCP_RELAY_PORTS_COUNT 3
#define DEFAULT_ENABLE_MOTD 1 // 1 - true, 0 - false
#define DEFAULT_MOTD DAEMON_NAME
#define MIN_ALLOWED_PORT 1
#define MAX_ALLOWED_PORT 65535
// 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) {
const size_t read_size = fread(keys, sizeof(uint8_t), KEYS_SIZE, keys_file);
if (read_size != KEYS_SIZE) {
fclose(keys_file);
return 0;
}
memcpy(dht->self_public_key, keys, crypto_box_PUBLICKEYBYTES);
memcpy(dht->self_secret_key, keys + crypto_box_PUBLICKEYBYTES, crypto_box_SECRETKEYBYTES);
} else {
// Otherwise save new keys
memcpy(keys, dht->self_public_key, crypto_box_PUBLICKEYBYTES);
memcpy(keys + crypto_box_PUBLICKEYBYTES, dht->self_secret_key, crypto_box_SECRETKEYBYTES);
keys_file = fopen(keys_file_path, "w");
if (!keys_file)
return 0;
const size_t write_size = fwrite(keys, sizeof(uint8_t), KEYS_SIZE, keys_file);
if (write_size != KEYS_SIZE) {
fclose(keys_file);
return 0;
}
}
fclose(keys_file);
return 1;
}
// Parses tcp relay ports from `cfg` and puts them into `tcp_relay_ports` array
//
// Supposed to be called from get_general_config only
//
// Important: iff `tcp_relay_port_count` > 0, then you are responsible for freeing `tcp_relay_ports`
void parse_tcp_relay_ports_config(config_t *cfg, uint16_t **tcp_relay_ports, int *tcp_relay_port_count)
{
const char *NAME_TCP_RELAY_PORTS = "tcp_relay_ports";
*tcp_relay_port_count = 0;
config_setting_t *ports_array = config_lookup(cfg, NAME_TCP_RELAY_PORTS);
if (ports_array == NULL) {
syslog(LOG_WARNING, "No '%s' setting in the configuration file.\n", NAME_TCP_RELAY_PORTS);
syslog(LOG_WARNING, "Using default '%s':\n", NAME_TCP_RELAY_PORTS);
uint16_t default_ports[DEFAULT_TCP_RELAY_PORTS_COUNT] = {DEFAULT_TCP_RELAY_PORTS};
int i;
for (i = 0; i < DEFAULT_TCP_RELAY_PORTS_COUNT; i ++) {
syslog(LOG_WARNING, "Port #%d: %u\n", i, default_ports[i]);
}
// similar procedure to the one of reading config file below
*tcp_relay_ports = malloc(DEFAULT_TCP_RELAY_PORTS_COUNT * sizeof(uint16_t));
for (i = 0; i < DEFAULT_TCP_RELAY_PORTS_COUNT; i ++) {
(*tcp_relay_ports)[*tcp_relay_port_count] = default_ports[i];
if ((*tcp_relay_ports)[*tcp_relay_port_count] < MIN_ALLOWED_PORT
|| (*tcp_relay_ports)[*tcp_relay_port_count] > MAX_ALLOWED_PORT) {
syslog(LOG_WARNING, "Port #%d: Invalid port: %u, should be in [%d, %d]. Skipping.\n", i,
(*tcp_relay_ports)[*tcp_relay_port_count], MIN_ALLOWED_PORT, MAX_ALLOWED_PORT);
continue;
}
(*tcp_relay_port_count) ++;
}
// the loop above skips invalid ports, so we adjust the allocated memory size
if ((*tcp_relay_port_count) > 0) {
*tcp_relay_ports = realloc(*tcp_relay_ports, (*tcp_relay_port_count) * sizeof(uint16_t));
} else {
free(*tcp_relay_ports);
*tcp_relay_ports = NULL;
}
return;
}
if (config_setting_is_array(ports_array) == CONFIG_FALSE) {
syslog(LOG_WARNING, "'%s' setting should be an array. Array syntax: 'setting = [value1, value2, ...]'.\n",
NAME_TCP_RELAY_PORTS);
return;
}
int config_port_count = config_setting_length(ports_array);
if (config_port_count == 0) {
syslog(LOG_WARNING, "'%s' is empty.\n", NAME_TCP_RELAY_PORTS);
return;
}
*tcp_relay_ports = malloc(config_port_count * sizeof(uint16_t));
int i;
for (i = 0; i < config_port_count; i ++) {
config_setting_t *elem = config_setting_get_elem(ports_array, i);
if (elem == NULL) {
// it's NULL if `ports_array` is not an array (we have that check earlier) or if `i` is out of range, which should not be
syslog(LOG_WARNING, "Port #%d: Something went wrong while parsing the port. Stopping reading ports.\n", i);
break;
}
if (config_setting_is_number(elem) == CONFIG_FALSE) {
syslog(LOG_WARNING, "Port #%d: Not a number. Skipping.\n", i);
continue;
}
(*tcp_relay_ports)[*tcp_relay_port_count] = config_setting_get_int(elem);
if ((*tcp_relay_ports)[*tcp_relay_port_count] < MIN_ALLOWED_PORT
|| (*tcp_relay_ports)[*tcp_relay_port_count] > MAX_ALLOWED_PORT) {
syslog(LOG_WARNING, "Port #%d: Invalid port: %u, should be in [%d, %d]. Skipping.\n", i,
(*tcp_relay_ports)[*tcp_relay_port_count], MIN_ALLOWED_PORT, MAX_ALLOWED_PORT);
continue;
}
(*tcp_relay_port_count) ++;
}
// the loop above skips invalid ports, so we adjust the allocated memory size
if ((*tcp_relay_port_count) > 0) {
*tcp_relay_ports = realloc(*tcp_relay_ports, (*tcp_relay_port_count) * sizeof(uint16_t));
} else {
free(*tcp_relay_ports);
*tcp_relay_ports = NULL;
}
}
// Gets general config options
//
// Important: you are responsible for freeing `pid_file_path` and `keys_file_path`
// also, iff `tcp_relay_ports_count` > 0, then you are responsible for freeing `tcp_relay_ports`
// and also `motd` iff `enable_motd` is set
//
// returns 1 on success
// 0 on failure, doesn't modify any data pointed by arguments
int get_general_config(const char *cfg_file_path, char **pid_file_path, char **keys_file_path, int *port,
int *enable_ipv6,
int *enable_ipv4_fallback, int *enable_lan_discovery, int *enable_tcp_relay, uint16_t **tcp_relay_ports,
int *tcp_relay_port_count, int *enable_motd, char **motd)
{
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_IPV4_FALLBACK = "enable_ipv4_fallback";
const char *NAME_ENABLE_LAN_DISCOVERY = "enable_lan_discovery";
const char *NAME_ENABLE_TCP_RELAY = "enable_tcp_relay";
const char *NAME_ENABLE_MOTD = "enable_motd";
const char *NAME_MOTD = "motd";
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 IPv4 fallback option
if (config_lookup_bool(&cfg, NAME_ENABLE_IPV4_FALLBACK, enable_ipv4_fallback) == CONFIG_FALSE) {
syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_IPV4_FALLBACK);
syslog(LOG_WARNING, "Using default '%s': %s\n", NAME_ENABLE_IPV4_FALLBACK,
DEFAULT_ENABLE_IPV4_FALLBACK ? "true" : "false");
*enable_ipv4_fallback = DEFAULT_ENABLE_IPV4_FALLBACK;
}
// 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;
}
// Get TCP relay option
if (config_lookup_bool(&cfg, NAME_ENABLE_TCP_RELAY, enable_tcp_relay) == CONFIG_FALSE) {
syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_TCP_RELAY);
syslog(LOG_WARNING, "Using default '%s': %s\n", NAME_ENABLE_TCP_RELAY,
DEFAULT_ENABLE_TCP_RELAY ? "true" : "false");
*enable_tcp_relay = DEFAULT_ENABLE_TCP_RELAY;
}
if (*enable_tcp_relay) {
parse_tcp_relay_ports_config(&cfg, tcp_relay_ports, tcp_relay_port_count);
} else {
*tcp_relay_port_count = 0;
}
// Get MOTD option
if (config_lookup_bool(&cfg, NAME_ENABLE_MOTD, enable_motd) == CONFIG_FALSE) {
syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_MOTD);
syslog(LOG_WARNING, "Using default '%s': %s\n", NAME_ENABLE_MOTD,
DEFAULT_ENABLE_MOTD ? "true" : "false");
*enable_motd = DEFAULT_ENABLE_MOTD;
}
if (*enable_motd) {
// Get MOTD
const char *tmp_motd;
if (config_lookup_string(&cfg, NAME_MOTD, &tmp_motd) == CONFIG_FALSE) {
syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_MOTD);
syslog(LOG_WARNING, "Using default '%s': %s\n", NAME_MOTD, DEFAULT_MOTD);
tmp_motd = DEFAULT_MOTD;
}
size_t tmp_motd_length = strlen(tmp_motd) + 1;
size_t motd_length = tmp_motd_length > MAX_MOTD_LENGTH ? MAX_MOTD_LENGTH : tmp_motd_length;
*motd = malloc(motd_length);
strncpy(*motd, tmp_motd, motd_length);
(*motd)[motd_length - 1] = '\0';
}
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_IPV4_FALLBACK, *enable_ipv4_fallback ? "true" : "false");
syslog(LOG_DEBUG, "'%s': %s\n", NAME_ENABLE_LAN_DISCOVERY, *enable_lan_discovery ? "true" : "false");
syslog(LOG_DEBUG, "'%s': %s\n", NAME_ENABLE_TCP_RELAY, *enable_tcp_relay ? "true" : "false");
// show info about tcp ports only if tcp relay is enabled
if (*enable_tcp_relay) {
if (*tcp_relay_port_count == 0) {
syslog(LOG_DEBUG, "No TCP ports could be read.\n");
} else {
syslog(LOG_DEBUG, "Read %d TCP ports:\n", *tcp_relay_port_count);
int i;
for (i = 0; i < *tcp_relay_port_count; i ++) {
syslog(LOG_DEBUG, "Port #%d: %u\n", i, (*tcp_relay_ports)[i]);
}
}
}
syslog(LOG_DEBUG, "'%s': %s\n", NAME_ENABLE_MOTD, *enable_motd ? "true" : "false");
if (*enable_motd) {
syslog(LOG_DEBUG, "'%s': %s\n", NAME_MOTD, *motd);
}
return 1;
}
// Bootstraps nodes listed in the config file
//
// returns 1 on success, some or no bootstrap nodes were added
// 0 on failure, a error accured while parsing config file
int bootstrap_from_config(const char *cfg_file_path, DHT *dht, int enable_ipv6)
{
const char *NAME_BOOTSTRAP_NODES = "bootstrap_nodes";
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 *node_list = config_lookup(&cfg, NAME_BOOTSTRAP_NODES);
if (node_list == NULL) {
syslog(LOG_WARNING, "No '%s' setting in the configuration file. Skipping bootstrapping.\n", NAME_BOOTSTRAP_NODES);
config_destroy(&cfg);
return 1;
}
if (config_setting_length(node_list) == 0) {
syslog(LOG_WARNING, "No bootstrap nodes found. Skipping bootstrapping.\n");
config_destroy(&cfg);
return 1;
}
int bs_port;
const char *bs_address;
const char *bs_public_key;
config_setting_t *node;
int i = 0;
while (config_setting_length(node_list)) {
node = config_setting_get_elem(node_list, 0);
if (node == NULL) {
config_destroy(&cfg);
return 0;
}
// Check that all settings are present
if (config_setting_lookup_string(node, NAME_PUBLIC_KEY, &bs_public_key) == CONFIG_FALSE) {
syslog(LOG_WARNING, "Bootstrap node #%d: Couldn't find '%s' setting. Skipping the node.\n", i, NAME_PUBLIC_KEY);
goto next;
}
if (config_setting_lookup_int(node, NAME_PORT, &bs_port) == CONFIG_FALSE) {
syslog(LOG_WARNING, "Bootstrap node #%d: Couldn't find '%s' setting. Skipping the node.\n", i, NAME_PORT);
goto next;
}
if (config_setting_lookup_string(node, NAME_ADDRESS, &bs_address) == CONFIG_FALSE) {
syslog(LOG_WARNING, "Bootstrap node #%d: Couldn't find '%s' setting. Skipping the node.\n", i, NAME_ADDRESS);
goto next;
}
// Process settings
if (strlen(bs_public_key) != crypto_box_PUBLICKEYBYTES * 2) {
syslog(LOG_WARNING, "Bootstrap node #%d: Invalid '%s': %s. Skipping the node.\n", i, NAME_PUBLIC_KEY,
bs_public_key);
goto next;
}
if (bs_port < MIN_ALLOWED_PORT || bs_port > MAX_ALLOWED_PORT) {
syslog(LOG_WARNING, "Bootstrap node #%d: Invalid '%s': %d, should be in [%d, %d]. Skipping the node.\n", i, NAME_PORT,
bs_port, MIN_ALLOWED_PORT, MAX_ALLOWED_PORT);
goto next;
}
uint8_t *bs_public_key_bin = hex_string_to_bin((char *)bs_public_key);
const int address_resolved = DHT_bootstrap_from_address(dht, bs_address, enable_ipv6, htons(bs_port),
bs_public_key_bin);
free(bs_public_key_bin);
if (!address_resolved) {
syslog(LOG_WARNING, "Bootstrap node #%d: Invalid '%s': %s. Skipping the node.\n", i, NAME_ADDRESS, bs_address);
goto next;
}
syslog(LOG_DEBUG, "Successfully added bootstrap node #%d: %s:%d %s\n", i, bs_address, bs_port, bs_public_key);
next:
// config_setting_lookup_string() allocates string inside and doesn't allow us to free it direcly
// though it's freed when the element is removed, so we free it right away in order to keep memory
// consumption minimal
config_setting_remove_elem(node_list, 0);
i++;
}
config_destroy(&cfg);
return 1;
}
// Prints public key
void print_public_key(const uint8_t *public_key)
{
char buffer[2 * crypto_box_PUBLICKEYBYTES + 1];
int index = 0;
size_t i;
for (i = 0; i < crypto_box_PUBLICKEYBYTES; i++) {
index += sprintf(buffer + index, "%02hhX", 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);
syslog(LOG_INFO, "Running \"%s\" version %lu.\n", DAEMON_NAME, DAEMON_VERSION_NUMBER);
if (argc < 2) {
syslog(LOG_ERR, "Please specify a path to a configuration file as the first argument. Exiting.\n");
return 1;
}
const char *cfg_file_path = argv[1];
char *pid_file_path, *keys_file_path;
int port;
int enable_ipv6;
int enable_ipv4_fallback;
int enable_lan_discovery;
int enable_tcp_relay;
uint16_t *tcp_relay_ports;
int tcp_relay_port_count;
int enable_motd;
char *motd;
if (get_general_config(cfg_file_path, &pid_file_path, &keys_file_path, &port, &enable_ipv6, &enable_ipv4_fallback,
&enable_lan_discovery, &enable_tcp_relay, &tcp_relay_ports, &tcp_relay_port_count, &enable_motd, &motd)) {
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;
}
if (port < MIN_ALLOWED_PORT || port > MAX_ALLOWED_PORT) {
syslog(LOG_ERR, "Invalid port: %d, should be in [%d, %d]. Exiting.\n", port, MIN_ALLOWED_PORT, MAX_ALLOWED_PORT);
return 1;
}
// Check if the PID file exists
FILE *pid_file;
if ((pid_file = fopen(pid_file_path, "r"))) {
syslog(LOG_ERR, "Another instance of the daemon is already running, PID file %s exists.\n", pid_file_path);
fclose(pid_file);
}
IP ip;
ip_init(&ip, enable_ipv6);
Networking_Core *net = new_networking(ip, port);
if (net == NULL) {
if (enable_ipv6 && enable_ipv4_fallback) {
syslog(LOG_DEBUG, "Couldn't initialize IPv6 networking. Falling back to using IPv4.\n");
enable_ipv6 = 0;
ip_init(&ip, enable_ipv6);
net = new_networking(ip, port);
if (net == NULL) {
syslog(LOG_DEBUG, "Couldn't fallback to IPv4. Exiting.\n");
return 1;
}
} else {
syslog(LOG_DEBUG, "Couldn't initialize networking. Exiting.\n");
return 1;
}
}
DHT *dht = new_DHT(net);
if (dht == NULL) {
syslog(LOG_ERR, "Couldn't initialize Tox DHT instance. Exiting.\n");
return 1;
}
Onion *onion = new_onion(dht);
Onion_Announce *onion_a = new_onion_announce(dht);
if (!(onion && onion_a)) {
syslog(LOG_ERR, "Couldn't initialize Tox Onion. Exiting.\n");
return 1;
}
if (enable_motd) {
if (bootstrap_set_callbacks(dht->net, DAEMON_VERSION_NUMBER, (uint8_t *)motd, strlen(motd) + 1) == 0) {
syslog(LOG_DEBUG, "Set MOTD successfully.\n");
} else {
syslog(LOG_ERR, "Couldn't set MOTD: %s. Exiting.\n", motd);
return 1;
}
free(motd);
}
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;
}
TCP_Server *tcp_server = NULL;
if (enable_tcp_relay) {
if (tcp_relay_port_count == 0) {
syslog(LOG_ERR, "No TCP relay ports read. Exiting.\n");
return 1;
}
tcp_server = new_TCP_server(enable_ipv6, tcp_relay_port_count, tcp_relay_ports, dht->self_secret_key, onion);
// tcp_relay_port_count != 0 at this point
free(tcp_relay_ports);
if (tcp_server != NULL) {
syslog(LOG_DEBUG, "Initialized Tox TCP server successfully.\n");
} else {
syslog(LOG_ERR, "Couldn't initialize Tox TCP server. Exiting.\n");
return 1;
}
}
if (bootstrap_from_config(cfg_file_path, dht, enable_ipv6)) {
syslog(LOG_DEBUG, "List of bootstrap nodes read successfully.\n");
} else {
syslog(LOG_ERR, "Couldn't read list of bootstrap nodes in %s. Exiting.\n", cfg_file_path);
return 1;
}
print_public_key(dht->self_public_key);
// Write the PID file
FILE *pidf = fopen(pid_file_path, "a+");
if (pidf == NULL) {
syslog(LOG_ERR, "Couldn'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
const pid_t pid = fork();
if (pid > 0) {
fprintf(pidf, "%d", pid);
fclose(pidf);
syslog(LOG_DEBUG, "Forked successfully: PID: %d.\n", pid);
return 0;
} else {
fclose(pidf);
}
if (pid < 0) {
syslog(LOG_ERR, "Forking failed. Exiting.\n");
return 1;
}
// Change the file mode mask
umask(0);
// 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;
const uint16_t htons_port = htons(port);
int waiting_for_dht_connection = 1;
if (enable_lan_discovery) {
LANdiscovery_init(dht);
syslog(LOG_DEBUG, "Initialized LAN discovery.\n");
}
while (1) {
do_DHT(dht);
if (enable_lan_discovery && is_timeout(last_LANdiscovery, LAN_DISCOVERY_INTERVAL)) {
send_LANdiscovery(htons_port, dht);
last_LANdiscovery = unix_time();
}
if (enable_tcp_relay) {
do_TCP_server(tcp_server);
}
networking_poll(dht->net);
if (waiting_for_dht_connection && DHT_isconnected(dht)) {
syslog(LOG_DEBUG, "Connected to other bootstrap node successfully.\n");
waiting_for_dht_connection = 0;
}
sleep;
}
return 1;
}

View File

@ -8,7 +8,7 @@ RuntimeDirectory=tox-bootstrapd
RuntimeDirectoryMode=750 RuntimeDirectoryMode=750
PIDFile=/var/run/tox-bootstrapd/tox-bootstrapd.pid PIDFile=/var/run/tox-bootstrapd/tox-bootstrapd.pid
WorkingDirectory=/var/lib/tox-bootstrapd WorkingDirectory=/var/lib/tox-bootstrapd
ExecStart=/usr/local/bin/tox-bootstrapd /etc/tox-bootstrapd.conf ExecStart=/usr/local/bin/tox-bootstrapd --config /etc/tox-bootstrapd.conf
User=tox-bootstrapd User=tox-bootstrapd
Group=tox-bootstrapd Group=tox-bootstrapd
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE #CapabilityBoundingSet=CAP_NET_BIND_SERVICE

View File

@ -15,7 +15,7 @@ DESC="Tox DHT bootstrap daemon"
NAME=tox-bootstrapd NAME=tox-bootstrapd
DAEMON=/usr/local/bin/$NAME DAEMON=/usr/local/bin/$NAME
CFGFILE=/etc/$NAME.conf CFGFILE=/etc/$NAME.conf
DAEMON_ARGS="$CFGFILE" DAEMON_ARGS="--config $CFGFILE"
PIDDIR=/var/run/$NAME PIDDIR=/var/run/$NAME
PIDFILE=$PIDDIR/$NAME.pid PIDFILE=$PIDDIR/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME SCRIPTNAME=/etc/init.d/$NAME

View File

@ -23,7 +23,7 @@
* *
*/ */
#define MAX_MOTD_LENGTH 256 /* I recommend you use a maximum of 96 bytes. The hard maximum is this though. */ #include "bootstrap_node_packets.h"
#define INFO_REQUEST_PACKET_LENGTH 78 #define INFO_REQUEST_PACKET_LENGTH 78

View File

@ -0,0 +1,35 @@
/* bootstrap_node_packets.h
*
* Special bootstrap node only packets.
*
* Include it in your bootstrap node and use: bootstrap_set_callbacks() to enable.
*
* Copyright (C) 2015 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/>.
*
*/
#ifndef BOOTSTRAP_NODE_PACKETS_H
#define BOOTSTRAP_NODE_PACKETS_H
#include "../toxcore/network.h"
#define MAX_MOTD_LENGTH 256 /* I recommend you use a maximum of 96 bytes. The hard maximum is this though. */
int bootstrap_set_callbacks(Networking_Core *net, uint32_t version, uint8_t *motd, uint16_t motd_length);
#endif // BOOTSTRAP_NODE_PACKETS_H