Squashed 'external/toxcore/c-toxcore/' content from commit 67badf69

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: 67badf69416a74e74f6d7eb51dd96f37282b8455
This commit is contained in:
2023-07-25 11:53:09 +02:00
commit 227425b90e
467 changed files with 116591 additions and 0 deletions

View File

@ -0,0 +1,26 @@
load("@rules_cc//cc:defs.bzl", "cc_binary")
package(features = ["layering_check"])
cc_binary(
name = "bootstrap_daemon",
srcs = glob([
"src/*.c",
"src/*.h",
]),
deps = [
"//c-toxcore/other:bootstrap_node_packets",
"//c-toxcore/toxcore:DHT",
"//c-toxcore/toxcore:LAN_discovery",
"//c-toxcore/toxcore:TCP_server",
"//c-toxcore/toxcore:announce",
"//c-toxcore/toxcore:ccompat",
"//c-toxcore/toxcore:group_onion_announce",
"//c-toxcore/toxcore:logger",
"//c-toxcore/toxcore:mono_time",
"//c-toxcore/toxcore:onion_announce",
"//c-toxcore/toxcore:tox",
"//c-toxcore/toxcore:util",
"@libconfig",
],
)

View File

@ -0,0 +1,24 @@
# Unity build broken because of _XOPEN_SOURCE macro
set_source_files_properties(
src/tox-bootstrapd.c
PROPERTIES SKIP_UNITY_BUILD_INCLUSION TRUE)
add_executable(tox-bootstrapd
src/command_line_arguments.c
src/command_line_arguments.h
src/config.c
src/config.h
src/config_defaults.h
src/global.h
src/log.c
src/log.h
src/log_backend_stdout.c
src/log_backend_stdout.h
src/log_backend_syslog.c
src/log_backend_syslog.h
src/tox-bootstrapd.c
../bootstrap_node_packets.c
../bootstrap_node_packets.h)
target_link_modules(tox-bootstrapd toxcore ${LIBCONFIG_LIBRARIES})
install(TARGETS tox-bootstrapd RUNTIME DESTINATION bin)
install(FILES bash-completion/completions/tox-bootstrapd DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/bash-completion/completions")

View File

@ -0,0 +1,308 @@
# Instructions
- [For `systemd` users](#for-systemd-users)
- [Setting up](#setting-up)
- [Updating](#updating)
- [Troubleshooting](#troubleshooting)
- [For `SysVinit` users](#for-sysvinit-users)
- [Setting up](#setting-up-1)
- [Updating](#updating-1)
- [Troubleshooting](#troubleshooting-1)
- [For `Docker` users](#for-docker-users)
- [Setting up](#setting-up-2)
- [Updating](#updating-2)
- [Troubleshooting](#troubleshooting-2)
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.
## For `systemd` users
### Setting up
For security reasons we run the daemon under its own user.
Create a new user by executing the following:
```sh
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
```
Restrict access to home directory:
```sh
sudo chmod 700 /var/lib/tox-bootstrapd
```
Copy `tox-bootstrapd.conf` file to where `ExecStart=` from `tox-bootstrapd.service` points to. By default it's `/etc/tox-bootstrapd.conf`.
```sh
sudo cp tox-bootstrapd.conf /etc/tox-bootstrapd.conf
```
Go over everything in the copied `tox-bootstrapd.conf` file. Set options you want and add actual working nodes to the `bootstrap_nodes` list, instead of the example ones, if you want your node to connect to the Tox network. Make sure `pid_file_path` matches `PIDFile=` from `tox-bootstrapd.service`.
Copy `tox-bootstrapd.service` to `/etc/systemd/system/`:
```sh
sudo cp tox-bootstrapd.service /etc/systemd/system/
```
You must uncomment the next line in tox-bootstrapd.service, if you want to use port number < 1024:
```
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
```
and, possibly, install `libcap2-bin` or `libcap2` package, depending of your distribution.
Reload systemd units definitions, enable service for automatic start (if needed), start it and verify it's running:
```sh
sudo systemctl daemon-reload
sudo systemctl enable tox-bootstrapd.service
sudo systemctl start tox-bootstrapd.service
sudo systemctl status tox-bootstrapd.service
```
Get your public key and check that the daemon initialized correctly:
```sh
sudo grep "tox-bootstrapd" /var/log/syslog
```
### 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.
Reload `tox-bootstrapd.service` if you have updated modified it:
```sh
sudo systemctl daemon-reload
```
After all of this is done, simply start the daemon back again:
```sh
sudo systemctl start tox-bootstrapd.service
```
### Troubleshooting
- Check daemon's status:
```sh
sudo systemctl status tox-bootstrapd.service
```
- Check the log for errors:
```sh
sudo grep "tox-bootstrapd" /var/log/syslog
# or
sudo journalctl --pager-end
# or
sudo journalctl -f _SYSTEMD_UNIT=tox-bootstrapd.service
```
- Make sure tox-bootstrapd user has write permission for keys and pid files.
- Make sure tox-bootstrapd has read permission for the config file.
- Make sure tox-bootstrapd location matches its path in tox-bootstrapd.service file.
## For `SysVinit` users
### Setting up
For security reasons we run the daemon under its own user.
Create a new user by executing the following:
```sh
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
```
Restrict access to home directory:
```sh
sudo chmod 700 /var/lib/tox-bootstrapd
```
Copy `tox-bootstrapd.conf` file to where `CFGFILE` variable from `tox-bootstrapd.sh` points to. By default it's `/etc/tox-bootstrapd.conf`.
```sh
sudo cp tox-bootstrapd.conf /etc/tox-bootstrapd.conf
```
Go over everything in the copied `tox-bootstrapd.conf` file. Set options you want and add actual working nodes to the `bootstrap_nodes` list, instead of the example ones, if you want your node to connect to the Tox network. Make sure `pid_file_path` matches `PIDFILE` from `tox-bootstrapd.sh`.
Look at the variable declarations in the beginning of `tox-bootstrapd.sh` init script to see if you need to change anything for it to work on your system. The default values must be fine for most users and we assume that you use those next.
If you have configured the daemon to use any port numbers that are lower than 1024, you need to execute the command below, as by default non-privileged users cannot open ports <1024. The change persists through reboot:
```sh
sudo setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/tox-bootstrapd
```
Copy `tox-bootstrapd.sh` init script to `/etc/init.d/tox-bootstrapd` (note the disappearance of ".sh" ending):
```sh
sudo cp tox-bootstrapd.sh /etc/init.d/tox-bootstrapd
```
Set permissions for the init system to run the script:
```sh
sudo chmod 755 /etc/init.d/tox-bootstrapd
```
Make the init system aware of the script, start the daemon and verify it's running:
```sh
sudo update-rc.d tox-bootstrapd defaults
sudo service tox-bootstrapd start
sudo service tox-bootstrapd status
```
Get your public key and check that the daemon initialized correctly:
```sh
sudo grep "tox-bootstrapd" /var/log/syslog
```
### 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
```
### Troubleshooting
- Check daemon's status:
```sh
sudo service tox-bootstrapd status
```
- Check the log for errors:
```sh
sudo grep "tox-bootstrapd" /var/log/syslog
```
- Check that variables in the beginning of `/etc/init.d/tox-bootstrapd` are valid.
- Make sure tox-bootstrapd user has write permission for keys and pid files.
- 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.
## For `Docker` users:
### Setting up
If you are familiar with Docker and would rather run the daemon in a Docker container, you may download the latest official docker image. To download the latest image run:
```sh
docker pull toxchat/bootstrap-node:latest
docker run --rm -it --entrypoint=sha256sum toxchat/bootstrap-node:latest /usr/local/bin/tox-bootstrapd
```
This will print the SHA256 checksum of the latest binary, which should agree with the SHA256 checksum in the Dockerfile.
If you want to build the bootstrap node from source, check out the latest release:
```sh
git checkout $(git tag --list | grep -P '^v(\d+).(\d+).(\d+)$' | \
sed 's/v/v /g' | sed 's/\./ /g' | \
sort -snk4,4 | sort -snk3,3 | sort -snk2,2 | tail -n 1 | \
sed 's/v /v/g' | sed 's/ /\./g')
```
and run the following from the top level c-toxcore directory:
```sh
tar c $(git ls-files) | docker build -f other/bootstrap_daemon/docker/Dockerfile -t toxchat/bootstrap-node -
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
docker run -d --name tox-bootstrapd --restart always \
--user "$(id -u tox-bootstrapd):$(id -g tox-bootstrapd)" \
-v /var/lib/tox-bootstrapd/:/var/lib/tox-bootstrapd/ \
--ulimit nofile=32768:32768 \
-p 443:443 \
-p 3389:3389 \
-p 33445:33445 \
-p 33445:33445/udp \
toxchat/bootstrap-node
```
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
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.
### 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
docker stop tox-bootstrapd
docker rm tox-bootstrapd
docker rmi toxchat/bootstrap-node
```
Then rebuild and run the image again:
```sh
tar c $(git ls-files) | docker build -f other/bootstrap_daemon/docker/Dockerfile -t toxchat/bootstrap-node -
docker run -d --name tox-bootstrapd --restart always \
--user "$(id -u tox-bootstrapd):$(id -g tox-bootstrapd)" \
-v /var/lib/tox-bootstrapd/:/var/lib/tox-bootstrapd/ \
--ulimit nofile=32768:32768 \
-p 443:443 \
-p 3389:3389 \
-p 33445:33445 \
-p 33445:33445/udp \
toxchat/bootstrap-node
```
### Troubleshooting
- Check if the container is running:
```sh
docker ps -a
```
- Check the log for errors:
```sh
docker logs tox-bootstrapd
```

View File

@ -0,0 +1,27 @@
# tox-bootstrapd completion -*- shell-script -*-
_tox_bootstrapd()
{
local cur prev words cword split
_init_completion -s || return
case $prev in
--config)
_filedir
return
;;
--log-backend)
COMPREPLY=($(compgen -W 'stdout syslog' -- "$cur"))
return
;;
esac
$split && return
if type _bashcomp_try_faketty &>/dev/null; then
COMPREPLY=($(compgen -W '$(_parse_help _bashcomp_try_faketty "$1" --help)' -- "$cur"))
[[ ${COMPREPLY-} == *= ]] && compopt -o nospace
fi
} && complete -F _tox_bootstrapd tox-bootstrapd
# ex: filetype=sh

View File

@ -0,0 +1,90 @@
###########################################################
# Builder image: we compile the code here (static build)
FROM alpine:3.15.0 AS build
RUN ["apk", "--no-cache", "add",\
"build-base",\
"cmake",\
"linux-headers",\
"libconfig-dev",\
"libconfig-static",\
"libsodium-dev",\
"libsodium-static",\
"ninja",\
"python3"\
]
WORKDIR /src/c-toxcore
# Very selectively add files to the image, because we may have random stuff
# lying around. In particular, we don't need to rebuild the docker image when
# toxav changes or the Dockerfile changes down from the build.
COPY cmake cmake
COPY other/bootstrap_daemon/bash-completion other/bootstrap_daemon/bash-completion
COPY other/bootstrap_daemon/src other/bootstrap_daemon/src
COPY other/bootstrap_node_packets.[ch] other/
COPY other/DHT_bootstrap.c other/
COPY other/pkgconfig other/pkgconfig
COPY other/rpm other/rpm
COPY testing/misc_tools.[ch] testing/
COPY toxcore toxcore
COPY toxencryptsave toxencryptsave
COPY third_party third_party
COPY CMakeLists.txt so.version ./
COPY other/bootstrap_daemon/CMakeLists.txt other/bootstrap_daemon/CMakeLists.txt
COPY testing/CMakeLists.txt testing/CMakeLists.txt
RUN cmake -B_build -H. \
-GNinja \
-DCMAKE_C_FLAGS="-DTCP_SERVER_USE_EPOLL -fstack-protector-all -fisolate-erroneous-paths-attribute" \
-DCMAKE_UNITY_BUILD=ON \
-DCMAKE_BUILD_TYPE=Release \
-DFULLY_STATIC=ON \
-DMIN_LOGGER_LEVEL=DEBUG \
-DBUILD_TOXAV=OFF \
-DBOOTSTRAP_DAEMON=ON && \
cmake --build _build --target install
# Verify checksum from dev-built binary, so we can be sure Docker Hub doesn't
# mess with your binaries.
COPY other/bootstrap_daemon/docker/tox-bootstrapd.sha256 other/bootstrap_daemon/docker/
RUN sha256sum /usr/local/bin/tox-bootstrapd && \
sha256sum -c other/bootstrap_daemon/docker/tox-bootstrapd.sha256
# Remove all the example bootstrap nodes from the config file.
COPY other/bootstrap_daemon/tox-bootstrapd.conf other/bootstrap_daemon/
# hadolint ignore=SC2086,SC2154
RUN ["sed", "-i", "/^bootstrap_nodes = /,$d", "other/bootstrap_daemon/tox-bootstrapd.conf"]
# Add bootstrap nodes from https://nodes.tox.chat/.
COPY other/bootstrap_daemon/docker/get-nodes.py other/bootstrap_daemon/docker/
RUN ["other/bootstrap_daemon/docker/get-nodes.py", "other/bootstrap_daemon/tox-bootstrapd.conf"]
###########################################################
# Final image build: this is what runs the bootstrap node
FROM debian:bullseye-slim
COPY --from=build /usr/local/bin/tox-bootstrapd /usr/local/bin/
COPY --from=build /src/c-toxcore/other/bootstrap_daemon/tox-bootstrapd.conf /etc/tox-bootstrapd.conf
RUN useradd --home-dir /var/lib/tox-bootstrapd --create-home \
--system --shell /sbin/nologin \
--comment "Account to run the Tox DHT bootstrap daemon" \
--user-group tox-bootstrapd && \
chmod 644 /etc/tox-bootstrapd.conf && \
chmod 700 /var/lib/tox-bootstrapd
WORKDIR /var/lib/tox-bootstrapd
USER tox-bootstrapd
# Smoke-test: make sure the binary actually starts up.
# hadolint ignore=DL4006
RUN script /usr/local/bin/tox-bootstrapd --help | grep "Usage"
ENTRYPOINT ["/usr/local/bin/tox-bootstrapd",\
"--config", "/etc/tox-bootstrapd.conf",\
"--log-backend", "stdout",\
"--foreground"\
]
EXPOSE 443/tcp 3389/tcp 33445/tcp 33445/udp

View File

@ -0,0 +1,55 @@
#!/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 json
import sys
import urllib.request
from typing import Dict
response = urllib.request.urlopen("https://nodes.tox.chat/json")
raw_json = response.read().decode("ascii", "ignore")
nodes = json.loads(raw_json)["nodes"]
def node_to_string(node: Dict[str, str]) -> str:
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:
return node_output + node["ipv4"] + '"\n }'
if len(node["ipv6"]) > 4:
return node_output + node["ipv6"] + '"\n }'
raise Exception("no IP address found for node " + json.dumps(node))
output = "bootstrap_nodes = (\n" + ",\n".join(map(node_to_string,
nodes)) + "\n)"
if len(sys.argv) > 1:
with open(sys.argv[1], "a") as fh:
fh.write(output + "\n")
print("Wrote %d nodes to %s" % (len(nodes), sys.argv[1]))
else:
print(output)

View File

@ -0,0 +1 @@
4f5b47978dc26aed78719526f862a44693f821db12f5ff6d70b338d67fb6f784 /usr/local/bin/tox-bootstrapd

View File

@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -eux
docker_build() {
docker build -f other/bootstrap_daemon/docker/Dockerfile -t toxchat/bootstrap-node .
}
# Run Docker build once. If it succeeds, we're good.
if docker_build; then
exit 0
fi
# We're not good. Run it again, but now capture the output.
OUTPUT=$(docker_build || true 2>&1)
if echo "$OUTPUT" | grep '/usr/local/bin/tox-bootstrapd: FAILED'; then
# This is a checksum warning, so we need to update it.
IMAGE=$(echo "$OUTPUT" | grep '^ ---> [0-9a-f]*$' | grep -o '[0-9a-f]*$' | tail -n1)
docker run --rm "$IMAGE" sha256sum /usr/local/bin/tox-bootstrapd >other/bootstrap_daemon/docker/tox-bootstrapd.sha256
fi
# Run once last time to complete the build.
docker_build

View File

@ -0,0 +1,47 @@
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.h \
../other/bootstrap_daemon/src/config_defaults.h \
../other/bootstrap_daemon/src/global.h \
../other/bootstrap_daemon/src/log.c \
../other/bootstrap_daemon/src/log.h \
../other/bootstrap_daemon/src/log_backend_stdout.c \
../other/bootstrap_daemon/src/log_backend_stdout.h \
../other/bootstrap_daemon/src/log_backend_syslog.c \
../other/bootstrap_daemon/src/log_backend_syslog.h \
../other/bootstrap_daemon/src/tox-bootstrapd.c \
../other/bootstrap_daemon/src/global.h \
../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_OBJECTS) \
$(NACL_LIBS)
bashcompdir = $(datarootdir)/bash-completion/completions
dist_bashcomp_DATA = $(top_builddir)/other/bootstrap_daemon/bash-completion/completions/tox-bootstrapd
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
endif

View File

@ -0,0 +1,134 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2015-2016 Tox project.
*/
/*
* Tox DHT bootstrap daemon.
* Command line argument handling.
*/
#include "command_line_arguments.h"
#include "global.h"
#include "../../../toxcore/ccompat.h"
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
/**
* Prints --help message
*/
static void print_help(void)
{
// 2 space ident
// make sure all lines fit into 80 columns
// make sure options are listed in alphabetical order
log_write(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) {
log_write(LOG_LEVEL_ERROR, "Error: No arguments provided.\n\n");
print_help();
exit(1);
}
opterr = 0;
static const struct option long_options[] = {
{"config", required_argument, nullptr, 'c'}, // required option
{"foreground", no_argument, nullptr, 'f'},
{"help", no_argument, nullptr, 'h'},
{"log-backend", required_argument, nullptr, 'l'}, // optional, defaults to syslog
{"version", no_argument, nullptr, 'v'},
{nullptr, 0, nullptr, 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, nullptr)) != -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 {
log_write(LOG_LEVEL_ERROR, "Error: Invalid BACKEND value for --log-backend option passed: %s\n\n", optarg);
print_help();
exit(1);
}
break;
case 'v':
log_write(LOG_LEVEL_INFO, "Version: %lu\n", DAEMON_VERSION_NUMBER);
exit(0);
case '?':
log_write(LOG_LEVEL_ERROR, "Error: Unrecognized option %s\n\n", argv[optind - 1]);
print_help();
exit(1);
case ':':
log_write(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) {
log_write(LOG_LEVEL_ERROR, "Error: The required --config option wasn't specified\n\n");
print_help();
exit(1);
}
}

View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2015-2016 Tox project.
*/
/*
* Tox DHT bootstrap daemon.
* Command line argument handling.
*/
#ifndef C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_COMMAND_LINE_ARGUMENTS_H
#define C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_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 // C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_COMMAND_LINE_ARGUMENTS_H

View File

@ -0,0 +1,414 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2014-2016 Tox project.
*/
/*
* Tox DHT bootstrap daemon.
* Functionality related to dealing with the config file.
*/
#include "config.h"
#include "config_defaults.h"
#include "log.h"
#include <stdio.h>
#include <stdlib.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`.
*/
static 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 == nullptr) {
log_write(LOG_LEVEL_WARNING, "No '%s' setting in the configuration file.\n", NAME_TCP_RELAY_PORTS);
log_write(LOG_LEVEL_WARNING, "Using default '%s':\n", NAME_TCP_RELAY_PORTS);
uint16_t default_ports[DEFAULT_TCP_RELAY_PORTS_COUNT] = {DEFAULT_TCP_RELAY_PORTS};
for (int i = 0; i < DEFAULT_TCP_RELAY_PORTS_COUNT; ++i) {
log_write(LOG_LEVEL_INFO, "Port #%d: %u\n", i, default_ports[i]);
}
// similar procedure to the one of reading config file below
*tcp_relay_ports = (uint16_t *)malloc(DEFAULT_TCP_RELAY_PORTS_COUNT * sizeof(uint16_t));
for (int 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) {
log_write(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 = (uint16_t *)realloc(*tcp_relay_ports, (*tcp_relay_port_count) * sizeof(uint16_t));
} else {
free(*tcp_relay_ports);
*tcp_relay_ports = nullptr;
}
return;
}
if (config_setting_is_array(ports_array) == CONFIG_FALSE) {
log_write(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) {
log_write(LOG_LEVEL_ERROR, "'%s' is empty.\n", NAME_TCP_RELAY_PORTS);
return;
}
*tcp_relay_ports = (uint16_t *)malloc(config_port_count * sizeof(uint16_t));
for (int i = 0; i < config_port_count; ++i) {
config_setting_t *elem = config_setting_get_elem(ports_array, i);
if (elem == nullptr) {
// 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
log_write(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) {
log_write(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) {
log_write(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 = (uint16_t *)realloc(*tcp_relay_ports, (*tcp_relay_port_count) * sizeof(uint16_t));
} else {
free(*tcp_relay_ports);
*tcp_relay_ports = nullptr;
}
}
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) {
log_write(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) {
log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_PORT);
log_write(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) {
log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_PID_FILE_PATH);
log_write(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_PID_FILE_PATH, DEFAULT_PID_FILE_PATH);
tmp_pid_file = DEFAULT_PID_FILE_PATH;
}
const size_t pid_file_path_len = strlen(tmp_pid_file) + 1;
*pid_file_path = (char *)malloc(pid_file_path_len);
memcpy(*pid_file_path, tmp_pid_file, pid_file_path_len);
// Get keys file location
const char *tmp_keys_file;
if (config_lookup_string(&cfg, NAME_KEYS_FILE_PATH, &tmp_keys_file) == CONFIG_FALSE) {
log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_KEYS_FILE_PATH);
log_write(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_KEYS_FILE_PATH, DEFAULT_KEYS_FILE_PATH);
tmp_keys_file = DEFAULT_KEYS_FILE_PATH;
}
const size_t keys_file_path_len = strlen(tmp_keys_file) + 1;
*keys_file_path = (char *)malloc(keys_file_path_len);
memcpy(*keys_file_path, tmp_keys_file, keys_file_path_len);
// Get IPv6 option
if (config_lookup_bool(&cfg, NAME_ENABLE_IPV6, enable_ipv6) == CONFIG_FALSE) {
log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_IPV6);
log_write(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) {
log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_IPV4_FALLBACK);
log_write(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) {
log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_LAN_DISCOVERY);
log_write(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) {
log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_TCP_RELAY);
log_write(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) {
log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_MOTD);
log_write(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) {
log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_MOTD);
log_write(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 = (char *)malloc(motd_length);
snprintf(*motd, motd_length, "%s", tmp_motd);
}
config_destroy(&cfg);
log_write(LOG_LEVEL_INFO, "Successfully read:\n");
log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_PID_FILE_PATH, *pid_file_path);
log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_KEYS_FILE_PATH, *keys_file_path);
log_write(LOG_LEVEL_INFO, "'%s': %d\n", NAME_PORT, *port);
log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_IPV6, *enable_ipv6 ? "true" : "false");
log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_IPV4_FALLBACK, *enable_ipv4_fallback ? "true" : "false");
log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_LAN_DISCOVERY, *enable_lan_discovery ? "true" : "false");
log_write(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) {
log_write(LOG_LEVEL_ERROR, "No TCP ports could be read.\n");
} else {
log_write(LOG_LEVEL_INFO, "Read %d TCP ports:\n", *tcp_relay_port_count);
for (int i = 0; i < *tcp_relay_port_count; ++i) {
log_write(LOG_LEVEL_INFO, "Port #%d: %u\n", i, (*tcp_relay_ports)[i]);
}
}
}
log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_MOTD, *enable_motd ? "true" : "false");
if (*enable_motd) {
log_write(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.
*/
static uint8_t *bootstrap_hex_string_to_bin(const char *hex_string)
{
if (strlen(hex_string) % 2 != 0) {
return nullptr;
}
size_t len = strlen(hex_string) / 2;
uint8_t *ret = (uint8_t *)malloc(len);
const char *pos = hex_string;
for (size_t i = 0; i < len; ++i, pos += 2) {
unsigned int val;
sscanf(pos, "%02x", &val);
ret[i] = val;
}
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) {
log_write(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 == nullptr) {
log_write(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) {
log_write(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)) {
int address_resolved;
uint8_t *bs_public_key_bin;
node = config_setting_get_elem(node_list, 0);
if (node == nullptr) {
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) {
log_write(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) {
log_write(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) {
log_write(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_PUBLIC_KEY_SIZE * 2) {
log_write(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) {
log_write(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;
}
bs_public_key_bin = bootstrap_hex_string_to_bin(bs_public_key);
address_resolved = dht_bootstrap_from_address(dht, bs_address, enable_ipv6, net_htons(bs_port),
bs_public_key_bin);
free(bs_public_key_bin);
if (!address_resolved) {
log_write(LOG_LEVEL_WARNING, "Bootstrap node #%d: Invalid '%s': %s. Skipping the node.\n", i, NAME_ADDRESS, bs_address);
goto next;
}
log_write(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,37 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2014-2016 Tox project.
*/
/*
* Tox DHT bootstrap daemon.
* Functionality related to dealing with the config file.
*/
#ifndef C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_CONFIG_H
#define C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_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 // C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_CONFIG_H

View File

@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2014-2016 Tox project.
*/
/*
* Tox DHT bootstrap daemon.
* Default config options for when they are missing in the config file.
*/
#ifndef C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_CONFIG_DEFAULTS_H
#define C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_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 // C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_CONFIG_DEFAULTS_H

View File

@ -0,0 +1,44 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2014-2016 Tox project.
*/
/*
* Tox DHT bootstrap daemon.
* Globally used defines.
*/
#ifndef C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_GLOBAL_H
#define C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_GLOBAL_H
#include "../../../toxcore/tox.h"
#define DAEMON_NAME "tox-bootstrapd"
#define DAEMON_VERSION_MAJOR TOX_VERSION_MAJOR
#define DAEMON_VERSION_MINOR TOX_VERSION_MINOR
#define DAEMON_VERSION_PATCH TOX_VERSION_PATCH
// Make sure versions are within the limit
#define VERSION_IS_OK(NUM) ( NUM >= 0 && NUM <= 999 )
#if !VERSION_IS_OK(DAEMON_VERSION_MAJOR) || !VERSION_IS_OK(DAEMON_VERSION_MINOR) || !VERSION_IS_OK(DAEMON_VERSION_PATCH)
#error "At least one of major, minor or patch parts of the version is out of bounds of [0, 999]. Current version: " DAEMON_VERSION_MAJOR "." DAEMON_VERSION_MINOR "." DAEMON_VERSION_PATCH
#endif
#undef VERSION_IS_OK
// New version scheme of 1AAABBBCCC, where A B and C are major, minor and patch
// versions of toxcore. The leading 1 is there just to keep the leading zeros,
// so that it would be easier to read the version when printed as a number.
// The version is in a visual decimal format rather than in any other format,
// because the original version was using a similar format, it was using
// YYYYMMDDVV date-based format for the version, with VV being an incremental
// counter in case more than one version was released at that day. Due to this
// some tools started showing the version to users as a plain number, rather
// than some binary format that needs to be parsed before being shown to users
// so we decided to keep this display format compatibility and adopted this
// weird scheme with a leading 1.
#define DAEMON_VERSION_NUMBER (1000000000UL + DAEMON_VERSION_MAJOR*1000000UL + DAEMON_VERSION_MINOR*1000UL + DAEMON_VERSION_PATCH*1UL)
#define MIN_ALLOWED_PORT 1
#define MAX_ALLOWED_PORT 65535
#endif // C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_GLOBAL_H

View File

@ -0,0 +1,82 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2015-2016 Tox project.
*/
/*
* Tox DHT bootstrap daemon.
* Logging utility with support of multiple logging backends.
*/
#include "log.h"
#include "log_backend_stdout.h"
#include "log_backend_syslog.h"
#define INVALID_BACKEND ((LOG_BACKEND)-1u)
static LOG_BACKEND current_backend = INVALID_BACKEND;
bool log_open(LOG_BACKEND backend)
{
if (current_backend != INVALID_BACKEND) {
return false;
}
current_backend = backend;
switch (current_backend) {
case LOG_BACKEND_STDOUT:
// nothing to do here
break;
case LOG_BACKEND_SYSLOG:
log_backend_syslog_open();
break;
}
return true;
}
bool log_close(void)
{
if (current_backend == INVALID_BACKEND) {
return false;
}
switch (current_backend) {
case LOG_BACKEND_STDOUT:
// nothing to do here
break;
case LOG_BACKEND_SYSLOG:
log_backend_syslog_close();
break;
}
current_backend = INVALID_BACKEND;
return true;
}
bool log_write(LOG_LEVEL level, const char *format, ...)
{
if (current_backend == INVALID_BACKEND) {
return false;
}
va_list args;
va_start(args, format);
switch (current_backend) {
case LOG_BACKEND_STDOUT:
log_backend_stdout_write(level, format, args);
break;
case LOG_BACKEND_SYSLOG:
log_backend_syslog_write(level, format, args);
break;
}
va_end(args);
return true;
}

View File

@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2015-2016 Tox project.
*/
/*
* Tox DHT bootstrap daemon.
* Logging utility with support of multiple logging backends.
*/
#ifndef C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_LOG_H
#define C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_LOG_H
#include <stdbool.h>
#include "../../../toxcore/ccompat.h"
typedef enum LOG_BACKEND {
LOG_BACKEND_STDOUT,
LOG_BACKEND_SYSLOG
} 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, false if log is already opened.
*/
bool log_open(LOG_BACKEND backend);
/**
* Releases all used resources by the logger.
* @return true on success, false if log is already closed.
*/
bool log_close(void);
/**
* 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, false if log is closed.
*/
bool log_write(LOG_LEVEL level, const char *format, ...) GNU_PRINTF(2, 3);
#endif // C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_LOG_H

View File

@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2015-2016 Tox project.
*/
/*
* Tox DHT bootstrap daemon.
* Stdout logging backend.
*/
#include "log_backend_stdout.h"
#include <stdio.h>
static FILE *log_backend_stdout_level(LOG_LEVEL level)
{
switch (level) {
case LOG_LEVEL_INFO:
return stdout;
case LOG_LEVEL_WARNING: // intentional fallthrough
case LOG_LEVEL_ERROR:
return stderr;
}
return stdout;
}
void log_backend_stdout_write(LOG_LEVEL level, const char *format, va_list args)
{
vfprintf(log_backend_stdout_level(level), format, args);
fflush(log_backend_stdout_level(level));
}

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2015-2016 Tox project.
*/
/*
* Tox DHT bootstrap daemon.
* Stdout logging backend.
*/
#ifndef C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_LOG_BACKEND_STDOUT_H
#define C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_LOG_BACKEND_STDOUT_H
#include "log.h"
#include <stdarg.h>
void log_backend_stdout_write(LOG_LEVEL level, const char *format, va_list args) GNU_PRINTF(2, 0);
#endif // C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_LOG_BACKEND_STDOUT_H

View File

@ -0,0 +1,66 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2015-2016 Tox project.
*/
/*
* Tox DHT bootstrap daemon.
* Syslog logging backend.
*/
#include "log_backend_syslog.h"
#include "global.h"
#include "../../../toxcore/ccompat.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
void log_backend_syslog_open(void)
{
openlog(DAEMON_NAME, LOG_NOWAIT | LOG_PID, LOG_DAEMON);
}
void log_backend_syslog_close(void)
{
closelog();
}
static int log_backend_syslog_level(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;
}
return LOG_INFO;
}
void log_backend_syslog_write(LOG_LEVEL level, const char *format, va_list args)
{
va_list args2;
va_copy(args2, args);
int size = vsnprintf(nullptr, 0, format, args2);
va_end(args2);
assert(size >= 0);
if (size < 0) {
return;
}
char *buf = (char *)malloc(size + 1);
vsnprintf(buf, size + 1, format, args);
syslog(log_backend_syslog_level(level), "%s", buf);
free(buf);
}

View File

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2015-2016 Tox project.
*/
/*
* Tox DHT bootstrap daemon.
* Syslog logging backend.
*/
#ifndef C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_LOG_BACKEND_SYSLOG_H
#define C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_LOG_BACKEND_SYSLOG_H
#include "log.h"
#include <stdarg.h>
void log_backend_syslog_open(void);
void log_backend_syslog_close(void);
void log_backend_syslog_write(LOG_LEVEL level, const char *format, va_list args) GNU_PRINTF(2, 0);
#endif // C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_LOG_BACKEND_SYSLOG_H

View File

@ -0,0 +1,624 @@
/* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2018 The TokTok team.
* Copyright © 2014-2016 Tox project.
*/
/*
* Tox DHT bootstrap daemon.
* Main file.
*/
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif
// system provided
#include <signal.h> // system header, rather than C, because we need it for POSIX sigaction(2)
#include <sys/resource.h>
#include <sys/stat.h>
#include <unistd.h>
// C
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// toxcore
#include "../../../toxcore/tox.h"
#include "../../../toxcore/LAN_discovery.h"
#include "../../../toxcore/TCP_server.h"
#include "../../../toxcore/announce.h"
#include "../../../toxcore/group_onion_announce.h"
#include "../../../toxcore/logger.h"
#include "../../../toxcore/mono_time.h"
#include "../../../toxcore/onion_announce.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"
static void sleep_milliseconds(uint32_t ms)
{
struct timespec req;
req.tv_sec = ms / 1000;
req.tv_nsec = (long)ms % 1000 * 1000 * 1000;
nanosleep(&req, nullptr);
}
// Uses the already existing key or creates one if it didn't exist
//
// returns 1 on success
// 0 on failure - no keys were read or stored
static int manage_keys(DHT *dht, char *keys_file_path)
{
enum { KEYS_SIZE = CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_SECRET_KEY_SIZE };
uint8_t keys[KEYS_SIZE];
FILE *keys_file;
// Check if file exits, proceed to open and load keys
keys_file = fopen(keys_file_path, "rb");
if (keys_file != nullptr) {
const size_t read_size = fread(keys, sizeof(uint8_t), KEYS_SIZE, keys_file);
if (read_size != KEYS_SIZE) {
fclose(keys_file);
return 0;
}
dht_set_self_public_key(dht, keys);
dht_set_self_secret_key(dht, keys + CRYPTO_PUBLIC_KEY_SIZE);
} else {
// Otherwise save new keys
memcpy(keys, dht_get_self_public_key(dht), CRYPTO_PUBLIC_KEY_SIZE);
memcpy(keys + CRYPTO_PUBLIC_KEY_SIZE, dht_get_self_secret_key(dht), CRYPTO_SECRET_KEY_SIZE);
keys_file = fopen(keys_file_path, "wb");
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
static void print_public_key(const uint8_t *public_key)
{
char buffer[2 * CRYPTO_PUBLIC_KEY_SIZE + 1];
int index = 0;
for (size_t i = 0; i < CRYPTO_PUBLIC_KEY_SIZE; i++) {
index += snprintf(buffer + index, sizeof(buffer) - index, "%02X", public_key[i]);
}
log_write(LOG_LEVEL_INFO, "Public Key: %s\n", buffer);
}
// Demonizes the process, appending PID to the PID file and closing file descriptors based on log backend
// Terminates the application if the daemonization fails.
static 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"))) {
log_write(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 == nullptr) {
log_write(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);
log_write(LOG_LEVEL_INFO, "Forked successfully: PID: %d.\n", pid);
exit(0);
} else {
fclose(pid_file);
}
if (pid < 0) {
log_write(LOG_LEVEL_ERROR, "Forking failed. Exiting.\n");
exit(1);
}
// Create a new SID for the child process
if (setsid() < 0) {
log_write(LOG_LEVEL_ERROR, "SID creation failure. Exiting.\n");
exit(1);
}
// Change the current working directory
if ((chdir("/")) < 0) {
log_write(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);
}
}
// Logs toxcore logger message using our logger facility
static void toxcore_logger_callback(void *context, Logger_Level level, const char *file, int line,
const char *func, const char *message, void *userdata)
{
LOG_LEVEL log_level;
switch (level) {
case LOGGER_LEVEL_TRACE:
log_level = LOG_LEVEL_INFO;
break;
case LOGGER_LEVEL_DEBUG:
log_level = LOG_LEVEL_INFO;
break;
case LOGGER_LEVEL_INFO:
log_level = LOG_LEVEL_INFO;
break;
case LOGGER_LEVEL_WARNING:
log_level = LOG_LEVEL_WARNING;
break;
case LOGGER_LEVEL_ERROR:
log_level = LOG_LEVEL_ERROR;
break;
default:
log_level = LOG_LEVEL_INFO;
break;
}
log_write(log_level, "%s:%d(%s) %s\n", file, line, func, message);
}
static volatile sig_atomic_t caught_signal = 0;
static void handle_signal(int signum)
{
caught_signal = signum;
}
int main(int argc, char *argv[])
{
umask(077);
char *cfg_file_path = nullptr;
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;
log_open(log_backend);
handle_command_line_arguments(argc, argv, &cfg_file_path, &log_backend, &run_in_foreground);
log_close();
log_open(log_backend);
log_write(LOG_LEVEL_INFO, "Running \"%s\" version %lu.\n", DAEMON_NAME, DAEMON_VERSION_NUMBER);
char *pid_file_path = nullptr;
char *keys_file_path = nullptr;
int start_port;
int enable_ipv6;
int enable_ipv4_fallback;
int enable_lan_discovery;
int enable_tcp_relay;
uint16_t *tcp_relay_ports = nullptr;
int tcp_relay_port_count;
int enable_motd;
char *motd = nullptr;
if (get_general_config(cfg_file_path, &pid_file_path, &keys_file_path, &start_port, &enable_ipv6, &enable_ipv4_fallback,
&enable_lan_discovery, &enable_tcp_relay, &tcp_relay_ports, &tcp_relay_port_count, &enable_motd, &motd)) {
log_write(LOG_LEVEL_INFO, "General config read successfully\n");
} else {
log_write(LOG_LEVEL_ERROR, "Couldn't read config file: %s. Exiting.\n", cfg_file_path);
return 1;
}
if (start_port < MIN_ALLOWED_PORT || start_port > MAX_ALLOWED_PORT) {
log_write(LOG_LEVEL_ERROR, "Invalid port: %d, should be in [%d, %d]. Exiting.\n", start_port, MIN_ALLOWED_PORT,
MAX_ALLOWED_PORT);
free(motd);
free(tcp_relay_ports);
free(keys_file_path);
free(pid_file_path);
return 1;
}
if (!run_in_foreground) {
daemonize(log_backend, pid_file_path);
}
free(pid_file_path);
IP ip;
ip_init(&ip, enable_ipv6);
Logger *logger = logger_new();
if (MIN_LOGGER_LEVEL == LOGGER_LEVEL_TRACE || MIN_LOGGER_LEVEL == LOGGER_LEVEL_DEBUG) {
logger_callback_log(logger, toxcore_logger_callback, nullptr, nullptr);
}
const uint16_t end_port = start_port + (TOX_PORTRANGE_TO - TOX_PORTRANGE_FROM);
const Network *ns = system_network();
Networking_Core *net = new_networking_ex(logger, ns, &ip, start_port, end_port, nullptr);
if (net == nullptr) {
if (enable_ipv6 && enable_ipv4_fallback) {
log_write(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_ex(logger, ns, &ip, start_port, end_port, nullptr);
if (net == nullptr) {
log_write(LOG_LEVEL_ERROR, "Couldn't fallback to IPv4. Exiting.\n");
logger_kill(logger);
free(motd);
free(tcp_relay_ports);
free(keys_file_path);
return 1;
}
} else {
log_write(LOG_LEVEL_ERROR, "Couldn't initialize networking. Exiting.\n");
logger_kill(logger);
free(motd);
free(tcp_relay_ports);
free(keys_file_path);
return 1;
}
}
Mono_Time *const mono_time = mono_time_new(nullptr, nullptr);
if (mono_time == nullptr) {
log_write(LOG_LEVEL_ERROR, "Couldn't initialize monotonic timer. Exiting.\n");
kill_networking(net);
logger_kill(logger);
free(motd);
free(tcp_relay_ports);
free(keys_file_path);
return 1;
}
mono_time_update(mono_time);
const Random *rng = system_random();
DHT *const dht = new_dht(logger, rng, ns, mono_time, net, true, enable_lan_discovery);
if (dht == nullptr) {
log_write(LOG_LEVEL_ERROR, "Couldn't initialize Tox DHT instance. Exiting.\n");
mono_time_free(mono_time);
kill_networking(net);
logger_kill(logger);
free(motd);
free(tcp_relay_ports);
free(keys_file_path);
return 1;
}
Forwarding *forwarding = new_forwarding(logger, rng, mono_time, dht);
if (forwarding == nullptr) {
log_write(LOG_LEVEL_ERROR, "Couldn't initialize forwarding. Exiting.\n");
kill_dht(dht);
mono_time_free(mono_time);
kill_networking(net);
logger_kill(logger);
free(motd);
free(tcp_relay_ports);
free(keys_file_path);
return 1;
}
Announcements *announce = new_announcements(logger, rng, mono_time, forwarding);
if (announce == nullptr) {
log_write(LOG_LEVEL_ERROR, "Couldn't initialize DHT announcements. Exiting.\n");
kill_forwarding(forwarding);
kill_dht(dht);
mono_time_free(mono_time);
kill_networking(net);
logger_kill(logger);
free(motd);
free(tcp_relay_ports);
free(keys_file_path);
return 1;
}
GC_Announces_List *group_announce = new_gca_list();
if (group_announce == nullptr) {
log_write(LOG_LEVEL_ERROR, "Couldn't initialize group announces. Exiting.\n");
kill_announcements(announce);
kill_forwarding(forwarding);
kill_dht(dht);
mono_time_free(mono_time);
kill_networking(net);
logger_kill(logger);
free(motd);
free(tcp_relay_ports);
free(keys_file_path);
return 1;
}
Onion *onion = new_onion(logger, mono_time, rng, dht);
if (!onion) {
log_write(LOG_LEVEL_ERROR, "Couldn't initialize Tox Onion. Exiting.\n");
kill_announcements(announce);
kill_forwarding(forwarding);
kill_dht(dht);
mono_time_free(mono_time);
kill_networking(net);
logger_kill(logger);
free(motd);
free(tcp_relay_ports);
free(keys_file_path);
return 1;
}
Onion_Announce *onion_a = new_onion_announce(logger, rng, mono_time, dht);
if (!onion_a) {
log_write(LOG_LEVEL_ERROR, "Couldn't initialize Tox Onion Announce. Exiting.\n");
kill_gca(group_announce);
kill_onion(onion);
kill_announcements(announce);
kill_forwarding(forwarding);
kill_dht(dht);
mono_time_free(mono_time);
kill_networking(net);
logger_kill(logger);
free(motd);
free(tcp_relay_ports);
free(keys_file_path);
return 1;
}
gca_onion_init(group_announce, onion_a);
if (enable_motd) {
if (bootstrap_set_callbacks(dht_get_net(dht), DAEMON_VERSION_NUMBER, (uint8_t *)motd, strlen(motd) + 1) == 0) {
log_write(LOG_LEVEL_INFO, "Set MOTD successfully.\n");
free(motd);
} else {
log_write(LOG_LEVEL_ERROR, "Couldn't set MOTD: %s. Exiting.\n", motd);
kill_onion_announce(onion_a);
kill_gca(group_announce);
kill_onion(onion);
kill_announcements(announce);
kill_forwarding(forwarding);
kill_dht(dht);
mono_time_free(mono_time);
kill_networking(net);
logger_kill(logger);
free(motd);
free(tcp_relay_ports);
free(keys_file_path);
return 1;
}
}
if (manage_keys(dht, keys_file_path)) {
log_write(LOG_LEVEL_INFO, "Keys are managed successfully.\n");
free(keys_file_path);
} else {
log_write(LOG_LEVEL_ERROR, "Couldn't read/write: %s. Exiting.\n", keys_file_path);
kill_onion_announce(onion_a);
kill_gca(group_announce);
kill_onion(onion);
kill_announcements(announce);
kill_forwarding(forwarding);
kill_dht(dht);
mono_time_free(mono_time);
kill_networking(net);
logger_kill(logger);
free(tcp_relay_ports);
free(keys_file_path);
return 1;
}
TCP_Server *tcp_server = nullptr;
if (enable_tcp_relay) {
if (tcp_relay_port_count == 0) {
log_write(LOG_LEVEL_ERROR, "No TCP relay ports read. Exiting.\n");
kill_onion_announce(onion_a);
kill_gca(group_announce);
kill_announcements(announce);
kill_forwarding(forwarding);
kill_onion(onion);
kill_dht(dht);
mono_time_free(mono_time);
kill_networking(net);
logger_kill(logger);
free(tcp_relay_ports);
return 1;
}
tcp_server = new_TCP_server(logger, rng, ns, enable_ipv6, tcp_relay_port_count, tcp_relay_ports,
dht_get_self_secret_key(dht), onion, forwarding);
free(tcp_relay_ports);
if (tcp_server != nullptr) {
log_write(LOG_LEVEL_INFO, "Initialized Tox TCP server successfully.\n");
struct rlimit limit;
const rlim_t rlim_suggested = 32768;
const rlim_t rlim_min = 4096;
assert(rlim_suggested >= rlim_min);
if (!getrlimit(RLIMIT_NOFILE, &limit)) {
if (limit.rlim_cur < limit.rlim_max) {
// Some systems have a hard limit of over 1000000 open file descriptors, so let's cap it at something reasonable
// so that we don't set it to an unreasonably high number.
limit.rlim_cur = limit.rlim_max > rlim_suggested ? rlim_suggested : limit.rlim_max;
setrlimit(RLIMIT_NOFILE, &limit);
}
}
if (!getrlimit(RLIMIT_NOFILE, &limit) && limit.rlim_cur < rlim_min) {
log_write(LOG_LEVEL_WARNING,
"Current limit on the number of files this process can open (%ju) is rather low for the proper functioning of the TCP server. "
"Consider raising the limit to at least %ju or the recommended %ju. "
"Continuing using the current limit (%ju).\n",
(uintmax_t)limit.rlim_cur, (uintmax_t)rlim_min, (uintmax_t)rlim_suggested, (uintmax_t)limit.rlim_cur);
}
} else {
log_write(LOG_LEVEL_ERROR, "Couldn't initialize Tox TCP server. Exiting.\n");
kill_onion_announce(onion_a);
kill_gca(group_announce);
kill_onion(onion);
kill_announcements(announce);
kill_forwarding(forwarding);
kill_dht(dht);
mono_time_free(mono_time);
kill_networking(net);
logger_kill(logger);
return 1;
}
}
if (bootstrap_from_config(cfg_file_path, dht, enable_ipv6)) {
log_write(LOG_LEVEL_INFO, "List of bootstrap nodes read successfully.\n");
} else {
log_write(LOG_LEVEL_ERROR, "Couldn't read list of bootstrap nodes in %s. Exiting.\n", cfg_file_path);
kill_TCP_server(tcp_server);
kill_onion_announce(onion_a);
kill_gca(group_announce);
kill_onion(onion);
kill_announcements(announce);
kill_forwarding(forwarding);
kill_dht(dht);
mono_time_free(mono_time);
kill_networking(net);
logger_kill(logger);
return 1;
}
print_public_key(dht_get_self_public_key(dht));
uint64_t last_LANdiscovery = 0;
const uint16_t net_htons_port = net_htons(start_port);
int waiting_for_dht_connection = 1;
Broadcast_Info *broadcast = nullptr;
if (enable_lan_discovery) {
broadcast = lan_discovery_init(ns);
log_write(LOG_LEVEL_INFO, "Initialized LAN discovery successfully.\n");
}
struct sigaction sa;
sa.sa_handler = handle_signal;
// Try to restart interrupted system calls if they are restartable
sa.sa_flags = SA_RESTART;
// Prevent the signal handler from being called again before it returns
sigfillset(&sa.sa_mask);
if (sigaction(SIGINT, &sa, nullptr)) {
log_write(LOG_LEVEL_WARNING, "Couldn't set signal handler for SIGINT. Continuing without the signal handler set.\n");
}
if (sigaction(SIGTERM, &sa, nullptr)) {
log_write(LOG_LEVEL_WARNING, "Couldn't set signal handler for SIGTERM. Continuing without the signal handler set.\n");
}
while (!caught_signal) {
mono_time_update(mono_time);
do_dht(dht);
if (enable_lan_discovery && mono_time_is_timeout(mono_time, last_LANdiscovery, LAN_DISCOVERY_INTERVAL)) {
lan_discovery_send(dht_get_net(dht), broadcast, dht_get_self_public_key(dht), net_htons_port);
last_LANdiscovery = mono_time_get(mono_time);
}
if (enable_tcp_relay) {
do_TCP_server(tcp_server, mono_time);
}
networking_poll(dht_get_net(dht), nullptr);
if (waiting_for_dht_connection && dht_isconnected(dht)) {
log_write(LOG_LEVEL_INFO, "Connected to another bootstrap node successfully.\n");
waiting_for_dht_connection = 0;
}
sleep_milliseconds(30);
}
switch (caught_signal) {
case SIGINT:
log_write(LOG_LEVEL_INFO, "Received SIGINT (%d) signal. Exiting.\n", SIGINT);
break;
case SIGTERM:
log_write(LOG_LEVEL_INFO, "Received SIGTERM (%d) signal. Exiting.\n", SIGTERM);
break;
default:
log_write(LOG_LEVEL_INFO, "Received (%d) signal. Exiting.\n", caught_signal);
}
lan_discovery_kill(broadcast);
kill_TCP_server(tcp_server);
kill_onion_announce(onion_a);
kill_gca(group_announce);
kill_onion(onion);
kill_announcements(announce);
kill_forwarding(forwarding);
kill_dht(dht);
mono_time_free(mono_time);
kill_networking(net);
logger_kill(logger);
return 0;
}

View File

@ -0,0 +1,65 @@
// Tox DHT bootstrap daemon configuration file.
// Listening port (UDP).
port = 33445
// A key file is like a password, so keep it where no one can read it.
// If there is no key file, a new one will be generated.
// The daemon should have permission to read/write it.
keys_file_path = "/var/lib/tox-bootstrapd/keys"
// The PID file written to by the daemon.
// Make sure that the user that daemon runs as has permissions to write to the
// PID file.
pid_file_path = "/var/run/tox-bootstrapd/tox-bootstrapd.pid"
// Enable IPv6.
enable_ipv6 = true
// Fallback to IPv4 in case IPv6 fails.
enable_ipv4_fallback = true
// Automatically bootstrap with nodes on local area network.
enable_lan_discovery = true
enable_tcp_relay = true
// While Tox uses 33445 port by default, 443 (https) and 3389 (rdp) ports are very
// common among nodes, so it's encouraged to keep them in place.
tcp_relay_ports = [443, 3389, 33445]
// Reply to MOTD (Message Of The Day) requests.
enable_motd = true
// Just a message that is sent when someone requests MOTD.
// Put anything you want, but note that it will be trimmed to fit into 255 bytes.
motd = "tox-bootstrapd"
// Any number of nodes the daemon will bootstrap itself off.
//
// Remember to replace the provided example with your own node list.
// There is a maintained list of bootstrap nodes on Tox's wiki, if you need it
// (https://wiki.tox.chat/doku.php?id=users:nodes).
//
// You may leave the list empty or remove "bootstrap_nodes" completely,
// in both cases this will be interpreted as if you don't want to bootstrap
// from anyone.
//
// address = any IPv4 or IPv6 address and also any US-ASCII domain name.
bootstrap_nodes = (
{ // Example Node 1 (IPv4)
address = "127.0.0.1"
port = 33445
public_key = "728925473812C7AAC482BE7250BCCAD0B8CB9F737BF3D42ABD34459C1768F854"
},
{ // Example Node 2 (IPv6)
address = "::1/128"
port = 33445
public_key = "3E78BACF0F84235B30054B54898F56793E1DEF8BD46B1038B9D822E8460FAB67"
},
{ // Example Node 3 (US-ASCII domain name)
address = "example.org"
port = 33445
public_key = "8CD5A9BF0A6CE358BA36F7A653F99FA6B258FF756E490F52C1F98CC420F78858"
}
)

View File

@ -0,0 +1,20 @@
[Unit]
Description=Tox DHT Bootstrap Daemon
After=network.target
[Service]
Type=forking
RuntimeDirectory=tox-bootstrapd
RuntimeDirectoryMode=750
PIDFile=/var/run/tox-bootstrapd/tox-bootstrapd.pid
WorkingDirectory=/var/lib/tox-bootstrapd
ExecStart=/usr/local/bin/tox-bootstrapd --config /etc/tox-bootstrapd.conf
User=tox-bootstrapd
Group=tox-bootstrapd
# TCP Server needs to be able to have lots of TCP sockets open.
LimitNOFILE=32768
# Uncomment to allow binding to ports < 1024, e.g. 443 (HTTPS) for TCP Server
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,147 @@
#! /bin/bash
### BEGIN INIT INFO
# Provides: tox-bootstrapd
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Starts the Tox DHT bootstrapping server daemon
# Description: Starts the Tox DHT bootstrapping server daemon
### END INIT INFO
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Tox DHT bootstrap daemon"
NAME=tox-bootstrapd
DAEMON=/usr/local/bin/$NAME
CFGFILE=/etc/$NAME.conf
DAEMON_ARGS="--config $CFGFILE"
PIDDIR=/var/run/$NAME
PIDFILE=$PIDDIR/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
USER=tox-bootstrapd
GROUP=tox-bootstrapd
# Set ulimit -n based on number of fds available.
# This check is borrowed from Debian's tor package, with a few modifications.
if [ -r /proc/sys/fs/file-max ]; then
system_max=$(cat /proc/sys/fs/file-max)
if [ "$system_max" -gt "80000" ] ; then
MAX_FILEDESCRIPTORS=32768
elif [ "$system_max" -gt "40000" ] ; then
MAX_FILEDESCRIPTORS=16384
elif [ "$system_max" -gt "20000" ] ; then
MAX_FILEDESCRIPTORS=8192
elif [ "$system_max" -gt "10000" ] ; then
MAX_FILEDESCRIPTORS=4096
else
MAX_FILEDESCRIPTORS=1024
cat << EOF
Warning: Your system has very few file descriptors available in total.
Maybe you should try raising that by adding 'fs.file-max=100000' to your
/etc/sysctl.conf file. Feel free to pick any number that you deem appropriate.
Then run 'sysctl -p'. See /proc/sys/fs/file-max for the current value, and
file-nr in the same directory for how many of those are used at the moment.
EOF
fi
else
MAX_FILEDESCRIPTORS=32768
fi
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
if [ ! -d $PIDDIR ]
then
mkdir $PIDDIR
fi
chown $USER:$GROUP $PIDDIR
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test --chuid $USER > /dev/null || return 1
# TCP Server needs to be able to have lots of TCP sockets open.
ulimit -n $MAX_FILEDESCRIPTORS
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --chuid $USER -- $DAEMON_ARGS || return 2
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry 5 --pidfile $PIDFILE --name $NAME --chuid $USER
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc -p $PIDFILE "$DAEMON" "$NAME" && exit 0 || exit $?
;;
restart)
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2
exit 3
;;
esac
exit 0

View File

@ -0,0 +1,29 @@
# Stage 1 - Compile websockify.
FROM toxchat/bootstrap-node:latest AS tox
FROM golang:1.17-alpine AS websockify
COPY websockify /work/websockify
RUN cd /work/websockify && go mod download github.com/gorilla/websocket && go install
# Stage 2 - Create the run-time image with bootstrap daemon and websockify.
FROM alpine:latest
RUN addgroup -S tox && adduser -SDH -G tox tox
COPY --from=websockify /go/bin/websockify /usr/local/bin/
COPY --from=tox /usr/local /usr/local/
COPY --from=tox /etc/tox-bootstrapd.conf /etc/
COPY entrypoint.sh /
RUN mkdir -p /var/lib/tox-bootstrapd/ /var/run/tox-bootstrapd/ \
&& chown tox:tox /var/lib/tox-bootstrapd/ /var/run/tox-bootstrapd/
# Public Key: 122837CCDD474DD1183A83152164D51427044B3EAAA52ED683F6BA602568673B
COPY keys /var/lib/tox-bootstrapd/
USER tox
# Use this to generate a keys file:
#RUN /usr/local/bin/tox-bootstrapd --config /etc/tox-bootstrapd.conf --log-backend stdout \
# && sleep 1
WORKDIR /web
CMD ["/entrypoint.sh"]

View File

@ -0,0 +1,6 @@
#!/bin/sh
set -eux
/usr/local/bin/tox-bootstrapd --config /etc/tox-bootstrapd.conf --log-backend stdout
/usr/local/bin/websockify -l "0.0.0.0:$PORT" -t 127.0.0.1:33445

View File

@ -0,0 +1 @@
(7<><37>GM<47>:<3A>!d<>'K><3E><><><D683>`%hg;<3B>G<EFBFBD><47>Y<>:<3A>Zy5<79><35>S<EFBFBD>$<24>|Cڠ<43>Ǹ<EFBFBD>2<10> <09>t

View File

@ -0,0 +1,17 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
package(features = ["-layering_check"])
go_library(
name = "go_default_library",
srcs = ["websockify.go"],
importpath = "github.com/TokTok/c-toxcore/other/bootstrap_daemon/websocket/websockify",
visibility = ["//visibility:private"],
deps = ["@com_github_gorilla_websocket//:go_default_library"],
)
go_binary(
name = "websockify",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,7 @@
module github.com/TokTok/c-toxcore/other/bootstrap_daemon/websocket/websockify
go 1.17
require (
github.com/gorilla/websocket master
)

View File

@ -0,0 +1,111 @@
// A Go version WebSocket to TCP socket proxy
//
// This is a heavily modified version of this file:
// https://github.com/novnc/websockify-other/blob/master/golang/websockify.go
//
// Changes include:
// - Fix infinite loop on error.
// - Proper logging.
// - Proper error handling in general.
// - Support both websocket and regular GET requests on /.
//
// Copyright 2022 The TokTok team.
// Copyright 2021 Michael.liu.
// See LICENSE for licensing conditions.
package main
import (
"encoding/hex"
"flag"
"log"
"net"
"net/http"
"github.com/gorilla/websocket"
)
var (
sourceAddr = flag.String("l", "127.0.0.1:8080", "http service address")
targetAddr = flag.String("t", "127.0.0.1:5900", "tcp service address")
)
var upgrader = websocket.Upgrader{
// Should be enough to fit any Tox TCP packets.
ReadBufferSize: 2048,
WriteBufferSize: 2048,
Subprotocols: []string{"binary"},
CheckOrigin: func(r *http.Request) bool {
return true
},
}
func forwardTCP(wsconn *websocket.Conn, conn net.Conn) {
var tcpbuffer [2048]byte
defer wsconn.Close()
defer conn.Close()
for {
n, err := conn.Read(tcpbuffer[0:])
if err != nil {
log.Println("TCP READ :", err)
break
}
log.Println("TCP READ :", n, hex.EncodeToString(tcpbuffer[0:n]))
if err := wsconn.WriteMessage(websocket.BinaryMessage, tcpbuffer[0:n]); err != nil {
log.Println("WS WRITE :", err)
break
}
log.Println("WS WRITE :", n)
}
}
func forwardWeb(wsconn *websocket.Conn, conn net.Conn) {
defer wsconn.Close()
defer conn.Close()
for {
_, buffer, err := wsconn.ReadMessage()
if err != nil {
log.Println("WS READ :", err)
break
}
log.Println("WS READ :", len(buffer), hex.EncodeToString(buffer))
m, err := conn.Write(buffer)
if err != nil {
log.Println("TCP WRITE:", err)
break
}
log.Println("TCP WRITE:", m)
}
}
func serveWs(w http.ResponseWriter, r *http.Request) {
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("upgrade:", err)
return
}
vnc, err := net.Dial("tcp", *targetAddr)
if err != nil {
log.Println("dial:", err)
return
}
go forwardTCP(ws, vnc)
go forwardWeb(ws, vnc)
}
func main() {
flag.Parse()
log.Println("Starting up websockify endpoint")
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Upgrade") == "websocket" {
serveWs(w, r)
} else {
http.ServeFile(w, r, r.URL.Path[1:])
}
})
log.Fatal(http.ListenAndServe(*sourceAddr, nil))
}