diff options
Diffstat (limited to 'other/bootstrap_daemon/src/tox-bootstrapd.c')
-rw-r--r-- | other/bootstrap_daemon/src/tox-bootstrapd.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/other/bootstrap_daemon/src/tox-bootstrapd.c b/other/bootstrap_daemon/src/tox-bootstrapd.c new file mode 100644 index 00000000..e252a37d --- /dev/null +++ b/other/bootstrap_daemon/src/tox-bootstrapd.c | |||
@@ -0,0 +1,342 @@ | |||
1 | /* tox-bootstrapd.c | ||
2 | * | ||
3 | * Tox DHT bootstrap daemon. | ||
4 | * Main file. | ||
5 | * | ||
6 | * Copyright (C) 2014-2016 Tox project All Rights Reserved. | ||
7 | * | ||
8 | * This file is part of Tox. | ||
9 | * | ||
10 | * Tox is free software: you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation, either version 3 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * Tox is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | // system provided | ||
26 | #include <unistd.h> | ||
27 | |||
28 | // C | ||
29 | #include <stdio.h> | ||
30 | #include <stdlib.h> | ||
31 | #include <string.h> | ||
32 | |||
33 | // toxcore | ||
34 | #include "../../../toxcore/LAN_discovery.h" | ||
35 | #include "../../../toxcore/onion_announce.h" | ||
36 | #include "../../../toxcore/TCP_server.h" | ||
37 | #include "../../../toxcore/util.h" | ||
38 | |||
39 | // misc | ||
40 | #include "../../bootstrap_node_packets.h" | ||
41 | |||
42 | #include "command_line_arguments.h" | ||
43 | #include "config.h" | ||
44 | #include "global.h" | ||
45 | #include "log.h" | ||
46 | |||
47 | |||
48 | #define SLEEP_MILLISECONDS(MS) usleep(1000*MS) | ||
49 | |||
50 | // Uses the already existing key or creates one if it didn't exist | ||
51 | // | ||
52 | // retirns 1 on success | ||
53 | // 0 on failure - no keys were read or stored | ||
54 | |||
55 | int manage_keys(DHT *dht, char *keys_file_path) | ||
56 | { | ||
57 | const uint32_t KEYS_SIZE = crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES; | ||
58 | uint8_t keys[KEYS_SIZE]; | ||
59 | FILE *keys_file; | ||
60 | |||
61 | // Check if file exits, proceed to open and load keys | ||
62 | keys_file = fopen(keys_file_path, "r"); | ||
63 | |||
64 | if (keys_file != NULL) { | ||
65 | const size_t read_size = fread(keys, sizeof(uint8_t), KEYS_SIZE, keys_file); | ||
66 | |||
67 | if (read_size != KEYS_SIZE) { | ||
68 | fclose(keys_file); | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | memcpy(dht->self_public_key, keys, crypto_box_PUBLICKEYBYTES); | ||
73 | memcpy(dht->self_secret_key, keys + crypto_box_PUBLICKEYBYTES, crypto_box_SECRETKEYBYTES); | ||
74 | } else { | ||
75 | // Otherwise save new keys | ||
76 | memcpy(keys, dht->self_public_key, crypto_box_PUBLICKEYBYTES); | ||
77 | memcpy(keys + crypto_box_PUBLICKEYBYTES, dht->self_secret_key, crypto_box_SECRETKEYBYTES); | ||
78 | |||
79 | keys_file = fopen(keys_file_path, "w"); | ||
80 | |||
81 | if (!keys_file) | ||
82 | return 0; | ||
83 | |||
84 | const size_t write_size = fwrite(keys, sizeof(uint8_t), KEYS_SIZE, keys_file); | ||
85 | |||
86 | if (write_size != KEYS_SIZE) { | ||
87 | fclose(keys_file); | ||
88 | return 0; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | fclose(keys_file); | ||
93 | |||
94 | return 1; | ||
95 | } | ||
96 | |||
97 | // Prints public key | ||
98 | |||
99 | void print_public_key(const uint8_t *public_key) | ||
100 | { | ||
101 | char buffer[2 * crypto_box_PUBLICKEYBYTES + 1]; | ||
102 | int index = 0; | ||
103 | |||
104 | size_t i; | ||
105 | |||
106 | for (i = 0; i < crypto_box_PUBLICKEYBYTES; i++) { | ||
107 | index += sprintf(buffer + index, "%02hhX", public_key[i]); | ||
108 | } | ||
109 | |||
110 | write_log(LOG_LEVEL_INFO, "Public Key: %s\n", buffer); | ||
111 | |||
112 | return; | ||
113 | } | ||
114 | |||
115 | // Demonizes the process, appending PID to the PID file and closing file descriptors based on log backend | ||
116 | // Terminates the application if the daemonization fails. | ||
117 | |||
118 | void daemonize(LOG_BACKEND log_backend, char *pid_file_path) | ||
119 | { | ||
120 | // Check if the PID file exists | ||
121 | FILE *pid_file; | ||
122 | |||
123 | if ((pid_file = fopen(pid_file_path, "r"))) { | ||
124 | write_log(LOG_LEVEL_WARNING, "Another instance of the daemon is already running, PID file %s exists.\n", pid_file_path); | ||
125 | fclose(pid_file); | ||
126 | } | ||
127 | |||
128 | // Open the PID file for writing | ||
129 | pid_file = fopen(pid_file_path, "a+"); | ||
130 | if (pid_file == NULL) { | ||
131 | write_log(LOG_LEVEL_ERROR, "Couldn't open the PID file for writing: %s. Exiting.\n", pid_file_path); | ||
132 | exit(1); | ||
133 | } | ||
134 | |||
135 | // Fork off from the parent process | ||
136 | const pid_t pid = fork(); | ||
137 | |||
138 | if (pid > 0) { | ||
139 | fprintf(pid_file, "%d", pid); | ||
140 | fclose(pid_file); | ||
141 | write_log(LOG_LEVEL_INFO, "Forked successfully: PID: %d.\n", pid); | ||
142 | exit(0); | ||
143 | } else { | ||
144 | fclose(pid_file); | ||
145 | } | ||
146 | |||
147 | if (pid < 0) { | ||
148 | write_log(LOG_LEVEL_ERROR, "Forking failed. Exiting.\n"); | ||
149 | exit(1); | ||
150 | } | ||
151 | |||
152 | // Create a new SID for the child process | ||
153 | if (setsid() < 0) { | ||
154 | write_log(LOG_LEVEL_ERROR, "SID creation failure. Exiting.\n"); | ||
155 | exit(1); | ||
156 | } | ||
157 | |||
158 | // Change the file mode mask | ||
159 | umask(0); | ||
160 | |||
161 | // Change the current working directory | ||
162 | if ((chdir("/")) < 0) { | ||
163 | write_log(LOG_LEVEL_ERROR, "Couldn't change working directory to '/'. Exiting.\n"); | ||
164 | exit(1); | ||
165 | } | ||
166 | |||
167 | // Go quiet | ||
168 | if (log_backend != LOG_BACKEND_STDOUT) { | ||
169 | close(STDOUT_FILENO); | ||
170 | close(STDIN_FILENO); | ||
171 | close(STDERR_FILENO); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | int main(int argc, char *argv[]) | ||
176 | { | ||
177 | char *cfg_file_path; | ||
178 | LOG_BACKEND log_backend; | ||
179 | bool run_in_foreground; | ||
180 | |||
181 | // choose backend for printing command line argument parsing output based on whether the daemon is being run from a terminal | ||
182 | log_backend = isatty(STDOUT_FILENO) ? LOG_BACKEND_STDOUT : LOG_BACKEND_SYSLOG; | ||
183 | |||
184 | open_log(log_backend); | ||
185 | handle_command_line_arguments(argc, argv, &cfg_file_path, &log_backend, &run_in_foreground); | ||
186 | close_log(); | ||
187 | |||
188 | open_log(log_backend); | ||
189 | |||
190 | write_log(LOG_LEVEL_INFO, "Running \"%s\" version %lu.\n", DAEMON_NAME, DAEMON_VERSION_NUMBER); | ||
191 | |||
192 | char *pid_file_path, *keys_file_path; | ||
193 | int port; | ||
194 | int enable_ipv6; | ||
195 | int enable_ipv4_fallback; | ||
196 | int enable_lan_discovery; | ||
197 | int enable_tcp_relay; | ||
198 | uint16_t *tcp_relay_ports; | ||
199 | int tcp_relay_port_count; | ||
200 | int enable_motd; | ||
201 | char *motd; | ||
202 | |||
203 | if (get_general_config(cfg_file_path, &pid_file_path, &keys_file_path, &port, &enable_ipv6, &enable_ipv4_fallback, | ||
204 | &enable_lan_discovery, &enable_tcp_relay, &tcp_relay_ports, &tcp_relay_port_count, &enable_motd, &motd)) { | ||
205 | write_log(LOG_LEVEL_INFO, "General config read successfully\n"); | ||
206 | } else { | ||
207 | write_log(LOG_LEVEL_ERROR, "Couldn't read config file: %s. Exiting.\n", cfg_file_path); | ||
208 | return 1; | ||
209 | } | ||
210 | |||
211 | if (port < MIN_ALLOWED_PORT || port > MAX_ALLOWED_PORT) { | ||
212 | write_log(LOG_LEVEL_ERROR, "Invalid port: %d, should be in [%d, %d]. Exiting.\n", port, MIN_ALLOWED_PORT, MAX_ALLOWED_PORT); | ||
213 | return 1; | ||
214 | } | ||
215 | |||
216 | if (!run_in_foreground) { | ||
217 | daemonize(log_backend, pid_file_path); | ||
218 | } | ||
219 | |||
220 | free(pid_file_path); | ||
221 | |||
222 | IP ip; | ||
223 | ip_init(&ip, enable_ipv6); | ||
224 | |||
225 | Networking_Core *net = new_networking(ip, port); | ||
226 | |||
227 | if (net == NULL) { | ||
228 | if (enable_ipv6 && enable_ipv4_fallback) { | ||
229 | write_log(LOG_LEVEL_WARNING, "Couldn't initialize IPv6 networking. Falling back to using IPv4.\n"); | ||
230 | enable_ipv6 = 0; | ||
231 | ip_init(&ip, enable_ipv6); | ||
232 | net = new_networking(ip, port); | ||
233 | |||
234 | if (net == NULL) { | ||
235 | write_log(LOG_LEVEL_ERROR, "Couldn't fallback to IPv4. Exiting.\n"); | ||
236 | return 1; | ||
237 | } | ||
238 | } else { | ||
239 | write_log(LOG_LEVEL_ERROR, "Couldn't initialize networking. Exiting.\n"); | ||
240 | return 1; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | DHT *dht = new_DHT(net); | ||
245 | |||
246 | if (dht == NULL) { | ||
247 | write_log(LOG_LEVEL_ERROR, "Couldn't initialize Tox DHT instance. Exiting.\n"); | ||
248 | return 1; | ||
249 | } | ||
250 | |||
251 | Onion *onion = new_onion(dht); | ||
252 | Onion_Announce *onion_a = new_onion_announce(dht); | ||
253 | |||
254 | if (!(onion && onion_a)) { | ||
255 | write_log(LOG_LEVEL_ERROR, "Couldn't initialize Tox Onion. Exiting.\n"); | ||
256 | return 1; | ||
257 | } | ||
258 | |||
259 | if (enable_motd) { | ||
260 | if (bootstrap_set_callbacks(dht->net, DAEMON_VERSION_NUMBER, (uint8_t *)motd, strlen(motd) + 1) == 0) { | ||
261 | write_log(LOG_LEVEL_INFO, "Set MOTD successfully.\n"); | ||
262 | } else { | ||
263 | write_log(LOG_LEVEL_ERROR, "Couldn't set MOTD: %s. Exiting.\n", motd); | ||
264 | return 1; | ||
265 | } | ||
266 | |||
267 | free(motd); | ||
268 | } | ||
269 | |||
270 | if (manage_keys(dht, keys_file_path)) { | ||
271 | write_log(LOG_LEVEL_INFO, "Keys are managed successfully.\n"); | ||
272 | } else { | ||
273 | write_log(LOG_LEVEL_ERROR, "Couldn't read/write: %s. Exiting.\n", keys_file_path); | ||
274 | return 1; | ||
275 | } | ||
276 | |||
277 | free(keys_file_path); | ||
278 | |||
279 | TCP_Server *tcp_server = NULL; | ||
280 | |||
281 | if (enable_tcp_relay) { | ||
282 | if (tcp_relay_port_count == 0) { | ||
283 | write_log(LOG_LEVEL_ERROR, "No TCP relay ports read. Exiting.\n"); | ||
284 | return 1; | ||
285 | } | ||
286 | |||
287 | tcp_server = new_TCP_server(enable_ipv6, tcp_relay_port_count, tcp_relay_ports, dht->self_secret_key, onion); | ||
288 | |||
289 | // tcp_relay_port_count != 0 at this point | ||
290 | free(tcp_relay_ports); | ||
291 | |||
292 | if (tcp_server != NULL) { | ||
293 | write_log(LOG_LEVEL_INFO, "Initialized Tox TCP server successfully.\n"); | ||
294 | } else { | ||
295 | write_log(LOG_LEVEL_ERROR, "Couldn't initialize Tox TCP server. Exiting.\n"); | ||
296 | return 1; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | if (bootstrap_from_config(cfg_file_path, dht, enable_ipv6)) { | ||
301 | write_log(LOG_LEVEL_INFO, "List of bootstrap nodes read successfully.\n"); | ||
302 | } else { | ||
303 | write_log(LOG_LEVEL_ERROR, "Couldn't read list of bootstrap nodes in %s. Exiting.\n", cfg_file_path); | ||
304 | return 1; | ||
305 | } | ||
306 | |||
307 | print_public_key(dht->self_public_key); | ||
308 | |||
309 | uint64_t last_LANdiscovery = 0; | ||
310 | const uint16_t htons_port = htons(port); | ||
311 | |||
312 | int waiting_for_dht_connection = 1; | ||
313 | |||
314 | if (enable_lan_discovery) { | ||
315 | LANdiscovery_init(dht); | ||
316 | write_log(LOG_LEVEL_INFO, "Initialized LAN discovery successfully.\n"); | ||
317 | } | ||
318 | |||
319 | while (1) { | ||
320 | do_DHT(dht); | ||
321 | |||
322 | if (enable_lan_discovery && is_timeout(last_LANdiscovery, LAN_DISCOVERY_INTERVAL)) { | ||
323 | send_LANdiscovery(htons_port, dht); | ||
324 | last_LANdiscovery = unix_time(); | ||
325 | } | ||
326 | |||
327 | if (enable_tcp_relay) { | ||
328 | do_TCP_server(tcp_server); | ||
329 | } | ||
330 | |||
331 | networking_poll(dht->net); | ||
332 | |||
333 | if (waiting_for_dht_connection && DHT_isconnected(dht)) { | ||
334 | write_log(LOG_LEVEL_INFO, "Connected to another bootstrap node successfully.\n"); | ||
335 | waiting_for_dht_connection = 0; | ||
336 | } | ||
337 | |||
338 | SLEEP_MILLISECONDS(30); | ||
339 | } | ||
340 | |||
341 | return 1; | ||
342 | } | ||