summaryrefslogtreecommitdiff
path: root/other/bootstrap_serverdaemon/tox_dht_bootstrap_server_daemon.c
diff options
context:
space:
mode:
authorirungentoo <irungentoo@tox.im>2014-01-19 11:51:25 -0800
committerirungentoo <irungentoo@tox.im>2014-01-19 11:51:25 -0800
commit6b7dff37c822d63c2dbe626d091e1c40f48b9b7e (patch)
tree5df58f2b305392ec23b0a60897287de8da4b3134 /other/bootstrap_serverdaemon/tox_dht_bootstrap_server_daemon.c
parent91b06da0c3aa399edf7e00e33a8f380b42e44f43 (diff)
parentbffc3d96e5a9c08d8457737cfd2cb2cb54c35670 (diff)
Merge pull request #688 from nurupo/dht_bootstrap_daemon
Improved DHT bootstrap daemon
Diffstat (limited to 'other/bootstrap_serverdaemon/tox_dht_bootstrap_server_daemon.c')
-rw-r--r--other/bootstrap_serverdaemon/tox_dht_bootstrap_server_daemon.c459
1 files changed, 459 insertions, 0 deletions
diff --git a/other/bootstrap_serverdaemon/tox_dht_bootstrap_server_daemon.c b/other/bootstrap_serverdaemon/tox_dht_bootstrap_server_daemon.c
new file mode 100644
index 00000000..134346a1
--- /dev/null
+++ b/other/bootstrap_serverdaemon/tox_dht_bootstrap_server_daemon.c
@@ -0,0 +1,459 @@
1/* tox_dht_bootstrap_server_daemon
2 *
3 * A simple DHT bootstrap server for tox - daemon edition.
4 *
5 * Copyright (C) 2014 Tox project All Rights Reserved.
6 *
7 * This file is part of Tox.
8 *
9 * Tox is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * Tox is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <unistd.h>
27#include <syslog.h>
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <libconfig.h>
32#include <arpa/inet.h>
33#include <string.h>
34
35#ifdef HAVE_CONFIG_H
36#include "config.h"
37#endif
38
39#include "../../toxcore/DHT.h"
40#include "../../toxcore/friend_requests.h"
41#include "../../toxcore/LAN_discovery.h"
42
43#include "../../testing/misc_tools.c"
44
45#define DAEMON_NAME "tox_dht_bootstrap_server_daemon"
46
47#define SLEEP_TIME_MILLISECONDS 30
48#define sleep usleep(1000*SLEEP_TIME_MILLISECONDS)
49
50#define DEFAULT_PID_FILE_PATH ".tox_dht_bootstrap_server_daemon.pid"
51#define DEFAULT_KEYS_FILE_PATH ".tox_dht_bootstrap_server_daemon.keys"
52#define DEFAULT_PORT 33445
53#define DEFAULT_ENABLE_IPV6 0 // 1 - true, 0 - false
54#define DEFAULT_ENABLE_LAN_DISCOVERY 1 // 1 - true, 0 - false
55
56
57// Uses the already existing key or creates one if it didn't exist
58//
59// retirns 1 on success
60// 0 on failure - no keys were read or stored
61
62int manage_keys(DHT *dht, char *keys_file_path)
63{
64 const uint32_t KEYS_SIZE = crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES;
65 uint8_t keys[KEYS_SIZE];
66 FILE *keys_file;
67
68 // Check if file exits, proceed to open and load keys
69 keys_file = fopen(keys_file_path, "r");
70
71 if (keys_file != NULL) {
72 size_t read_size = fread(keys, sizeof(uint8_t), KEYS_SIZE, keys_file);
73
74 if (read_size != KEYS_SIZE) {
75 return 0;
76 }
77
78 load_keys(dht->c, keys);
79 } else {
80 // Otherwise save new keys
81 new_keys(dht->c);
82 save_keys(dht->c, keys);
83
84 keys_file = fopen(keys_file_path, "w");
85
86 size_t write_size = fwrite(keys, sizeof(uint8_t), KEYS_SIZE, keys_file);
87
88 if (write_size != KEYS_SIZE) {
89 return 0;
90 }
91 }
92
93 fclose(keys_file);
94
95 return 1;
96}
97
98// Gets general config options
99//
100// Important: you are responsible for freeing `pid_file_path` and `keys_file_path`
101//
102// returns 1 on success
103// 0 on failure, doesn't modify any data pointed by arguments
104
105int get_general_config(char *cfg_file_path, char **pid_file_path, char **keys_file_path, int *port, int *enable_ipv6,
106 int *enable_lan_discovery)
107{
108 config_t cfg;
109
110 const char *NAME_PORT = "port";
111 const char *NAME_PID_FILE_PATH = "pid_file_path";
112 const char *NAME_KEYS_FILE_PATH = "keys_file_path";
113 const char *NAME_ENABLE_IPV6 = "enable_ipv6";
114 const char *NAME_ENABLE_LAN_DISCOVERY = "enable_lan_discovery";
115
116 config_init(&cfg);
117
118 // Read the file. If there is an error, report it and exit.
119 if (config_read_file(&cfg, cfg_file_path) == CONFIG_FALSE) {
120 syslog(LOG_ERR, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
121 config_destroy(&cfg);
122 return 0;
123 }
124
125 // Get port
126 if (config_lookup_int(&cfg, NAME_PORT, port) == CONFIG_FALSE) {
127 syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_PORT);
128 syslog(LOG_WARNING, "Using default '%s': %d\n", NAME_PORT, DEFAULT_PORT);
129 *port = DEFAULT_PORT;
130 }
131
132 // Get PID file location
133 const char *tmp_pid_file;
134
135 if (config_lookup_string(&cfg, NAME_PID_FILE_PATH, &tmp_pid_file) == CONFIG_FALSE) {
136 syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_PID_FILE_PATH);
137 syslog(LOG_WARNING, "Using default '%s': %s\n", NAME_PID_FILE_PATH, DEFAULT_PID_FILE_PATH);
138 tmp_pid_file = DEFAULT_PID_FILE_PATH;
139 }
140
141 *pid_file_path = malloc(strlen(tmp_pid_file) + 1);
142 strcpy(*pid_file_path, tmp_pid_file);
143
144 // Get keys file location
145 const char *tmp_keys_file;
146
147 if (config_lookup_string(&cfg, NAME_KEYS_FILE_PATH, &tmp_keys_file) == CONFIG_FALSE) {
148 syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_KEYS_FILE_PATH);
149 syslog(LOG_WARNING, "Using default '%s': %s\n", NAME_KEYS_FILE_PATH, DEFAULT_KEYS_FILE_PATH);
150 tmp_keys_file = DEFAULT_KEYS_FILE_PATH;
151 }
152
153 *keys_file_path = malloc(strlen(tmp_keys_file) + 1);
154 strcpy(*keys_file_path, tmp_keys_file);
155
156 // Get IPv6 option
157 if (config_lookup_bool(&cfg, NAME_ENABLE_IPV6, enable_ipv6) == CONFIG_FALSE) {
158 syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_IPV6);
159 syslog(LOG_WARNING, "Using default '%s': %s\n", NAME_ENABLE_IPV6, DEFAULT_ENABLE_IPV6 ? "true" : "false");
160 *enable_ipv6 = DEFAULT_ENABLE_IPV6;
161 }
162
163 // Get LAN discovery option
164 if (config_lookup_bool(&cfg, NAME_ENABLE_LAN_DISCOVERY, enable_lan_discovery) == CONFIG_FALSE) {
165 syslog(LOG_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_LAN_DISCOVERY);
166 syslog(LOG_WARNING, "Using default '%s': %s\n", NAME_ENABLE_LAN_DISCOVERY,
167 DEFAULT_ENABLE_LAN_DISCOVERY ? "true" : "false");
168 *enable_lan_discovery = DEFAULT_ENABLE_LAN_DISCOVERY;
169 }
170
171 config_destroy(&cfg);
172
173 syslog(LOG_DEBUG, "Successfully read:\n");
174 syslog(LOG_DEBUG, "'%s': %s\n", NAME_PID_FILE_PATH, *pid_file_path);
175 syslog(LOG_DEBUG, "'%s': %s\n", NAME_KEYS_FILE_PATH, *keys_file_path);
176 syslog(LOG_DEBUG, "'%s': %d\n", NAME_PORT, *port);
177 syslog(LOG_DEBUG, "'%s': %s\n", NAME_ENABLE_IPV6, *enable_ipv6 ? "true" : "false");
178 syslog(LOG_DEBUG, "'%s': %s\n", NAME_ENABLE_LAN_DISCOVERY, *enable_lan_discovery ? "true" : "false");
179
180 return 1;
181}
182
183// Bootstraps servers listed in the config file
184//
185// returns 1 on success
186// 0 on failure, either no or only some servers were bootstrapped
187
188int bootstrap_from_config(char *cfg_file_path, DHT *dht, int enable_ipv6)
189{
190 const char *NAME_BOOTSTRAP_SERVERS = "bootstrap_servers";
191
192 const char *NAME_PUBLIC_KEY = "public_key";
193 const char *NAME_PORT = "port";
194 const char *NAME_ADDRESS = "address";
195
196 config_t cfg;
197
198 config_init(&cfg);
199
200 if (config_read_file(&cfg, cfg_file_path) == CONFIG_FALSE) {
201 syslog(LOG_ERR, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
202 config_destroy(&cfg);
203 return 0;
204 }
205
206 config_setting_t *server_list = config_lookup(&cfg, NAME_BOOTSTRAP_SERVERS);
207
208 if (server_list == NULL) {
209 syslog(LOG_ERR, "No '%s' setting in configuration file.\n", NAME_BOOTSTRAP_SERVERS);
210 config_destroy(&cfg);
211 return 0;
212 }
213
214 int bs_port;
215 const char *bs_address;
216 const char *bs_public_key;
217
218 config_setting_t *server;
219
220 int i = 0;
221
222 while (config_setting_length(server_list)) {
223
224 server = config_setting_get_elem(server_list, 0);
225
226 if (server == NULL) {
227 return 0;
228 }
229
230 // Proceed only if all parts are present
231 if (config_setting_lookup_string(server, NAME_PUBLIC_KEY, &bs_public_key) == CONFIG_FALSE ||
232 config_setting_lookup_int (server, NAME_PORT, &bs_port) == CONFIG_FALSE ||
233 config_setting_lookup_string(server, NAME_ADDRESS, &bs_address) == CONFIG_FALSE ) {
234 goto next;
235 }
236
237 if (strlen(bs_public_key) != 64) {
238 syslog(LOG_WARNING, "bootstrap_server #%d: Invalid '%s': %s.\n", i, NAME_PUBLIC_KEY, bs_public_key);
239 goto next;
240 }
241
242 // not (1 <= port <= 65535)
243 if (bs_port < 1 || bs_port > 65535) {
244 syslog(LOG_WARNING, "bootstrap_server #%d: Invalid '%s': %d.\n", i, NAME_PORT, bs_port);
245 goto next;
246 }
247
248 const int address_resolved = DHT_bootstrap_from_address(dht, bs_address, enable_ipv6, htons(bs_port),
249 hex_string_to_bin((char *)bs_public_key));
250
251 if (!address_resolved) {
252 syslog(LOG_WARNING, "bootstrap_server #%d: Invalid '%s': %s.\n", i, NAME_ADDRESS, bs_address);
253 goto next;
254 }
255
256 syslog(LOG_DEBUG, "Successfully connected to %s:%d %s\n", bs_address, bs_port, bs_public_key);
257
258next:
259 // config_setting_lookup_string() allocates string inside and doesn't allow us to free it
260 // so in order to reuse `bs_public_key` and `bs_address` we have to remove the element
261 // which will cause libconfig to free allocated strings
262 config_setting_remove_elem(server_list, 0);
263 i++;
264 }
265
266 config_destroy(&cfg);
267
268 return 1;
269}
270
271// Checks if we are connected to the DHT
272//
273// returns 1 on success
274// 0 on failure
275
276int is_conencted(DHT *dht, int port, int enable_lan_discovery)
277{
278 uint16_t htons_port = htons(port);
279
280 int i;
281
282 for (i = 0; i < 100; i ++) {
283 do_DHT(dht);
284
285 if (enable_lan_discovery) {
286 send_LANdiscovery(htons_port, dht->c);
287 }
288
289 networking_poll(dht->c->lossless_udp->net);
290
291 if (DHT_isconnected(dht)) {
292 return 1;
293 }
294
295 sleep;
296 }
297
298 return 0;
299}
300
301// Prints public key
302
303void print_public_key(uint8_t *public_key)
304{
305 char buffer[64 + 1];
306 int index = 0;
307
308 int i;
309
310 for (i = 0; i < 32; i++) {
311 if (public_key[i] < 16) {
312 index += sprintf(buffer + index, "0");
313 }
314
315 index += sprintf(buffer + index, "%hhX", public_key[i]);
316 }
317
318 syslog(LOG_INFO, "Public Key: %s\n", buffer);
319
320 return;
321}
322
323int main(int argc, char *argv[])
324{
325 openlog(DAEMON_NAME, LOG_NOWAIT | LOG_PID, LOG_DAEMON);
326
327 if (argc < 2) {
328 syslog(LOG_ERR, "Please specify a configuration file. Exiting.\n");
329 return 1;
330 }
331
332 char *cfg_file_path = argv[1];
333 char *pid_file_path, *keys_file_path;
334 int port;
335 int enable_ipv6;
336 int enable_lan_discovery;
337
338 if (get_general_config(cfg_file_path, &pid_file_path, &keys_file_path, &port, &enable_ipv6, &enable_lan_discovery)) {
339 syslog(LOG_DEBUG, "General config read successfully\n");
340 } else {
341 syslog(LOG_ERR, "Couldn't read config file: %s. Exiting.\n", cfg_file_path);
342 return 1;
343 }
344
345 // not (1 <= port <= 65535)
346 if (port < 1 || port > 65535) {
347 syslog(LOG_ERR, "Invalid port: %d, must be 1 <= port <= 65535. Exiting.\n", port);
348 return 1;
349 }
350
351 // Check if the PID file exists
352 if (fopen(pid_file_path, "r")) {
353 syslog(LOG_ERR, "Another instance of the daemon is already running, PID file %s exists. Exiting.\n", pid_file_path);
354 return 1;
355 }
356
357 IP ip;
358 ip_init(&ip, enable_ipv6);
359
360 DHT *dht = new_DHT(new_net_crypto(new_networking(ip, port)));
361
362 if (dht == NULL) {
363 syslog(LOG_ERR, "Couldn't initialize Tox DHT instance. Exiting.\n");
364 return 1;
365 }
366
367 if (enable_lan_discovery) {
368 LANdiscovery_init(dht);
369 }
370
371 if (manage_keys(dht, keys_file_path)) {
372 syslog(LOG_DEBUG, "Keys are managed successfully\n");
373 } else {
374 syslog(LOG_ERR, "Couldn't read/write: %s. Exiting.\n", keys_file_path);
375 return 1;
376 }
377
378 if (bootstrap_from_config(cfg_file_path, dht, enable_ipv6)) {
379 syslog(LOG_DEBUG, "List of bootstrap servers read successfully\n");
380 } else {
381 syslog(LOG_ERR, "Couldn't read list of bootstrap servers in %s. Exiting.\n", cfg_file_path);
382 return 1;
383 }
384
385 if (is_conencted(dht, port, enable_lan_discovery)) {
386 syslog(LOG_INFO, "Successfully connected to DHT\n");
387 } else {
388 syslog(LOG_ERR, "Couldn't connect to the DHT. Check settings and network connections. Exiting.\n");
389 return 1;
390 }
391
392 print_public_key(dht->c->self_public_key);
393
394 // Write the PID file
395 FILE *pidf = fopen(pid_file_path, "w");
396
397 if (pidf == NULL) {
398 syslog(LOG_ERR, "Can't open the PID file for writing: %s. Exiting.\n", pid_file_path);
399 return 1;
400 }
401
402 free(pid_file_path);
403 free(keys_file_path);
404
405 // Fork off from the parent process
406 pid_t pid = fork();
407
408 if (pid < 0) {
409 syslog(LOG_ERR, "Forking failed. Exiting.\n");
410 return 1;
411 }
412
413 if (pid > 0) {
414 syslog(LOG_DEBUG, "Forked successfully: PID: %d.\n", pid);
415
416 return 0;
417 }
418
419 // Change the file mode mask
420 umask(0);
421
422 fprintf(pidf, "%d\n", pid);
423 fclose(pidf);
424
425 // Create a new SID for the child process
426 if (setsid() < 0) {
427 syslog(LOG_ERR, "SID creation failure. Exiting.\n");
428 return 1;
429 }
430
431 // Change the current working directory
432 if ((chdir("/")) < 0) {
433 syslog(LOG_ERR, "Couldn't change working directory to '/'. Exiting.\n");
434 return 1;
435 }
436
437 // Go quiet
438 close(STDOUT_FILENO);
439 close(STDIN_FILENO);
440 close(STDERR_FILENO);
441
442 uint64_t last_LANdiscovery = 0;
443 uint16_t htons_port = htons(port);
444
445 while (1) {
446 do_DHT(dht);
447
448 if (enable_lan_discovery && is_timeout(last_LANdiscovery, LAN_DISCOVERY_INTERVAL)) {
449 send_LANdiscovery(htons_port, dht->c);
450 last_LANdiscovery = unix_time();
451 }
452
453 networking_poll(dht->c->lossless_udp->net);
454
455 sleep;
456 }
457
458 return 1;
459}