unet-cli: strip initial newline in usage message
[project/unetd.git] / udht.c
1 #include <stdio.h>
2 #include <fcntl.h>
3 #include <errno.h>
4 #include <arpa/inet.h>
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <netdb.h>
8
9 #include <libubox/usock.h>
10 #include <libubox/uloop.h>
11 #include <libubox/blob.h>
12
13 #include "curve25519.h"
14 #include "siphash.h"
15 #include "sha512.h"
16 #include "dht.h"
17 #include "udht.h"
18 #include "pex-msg.h"
19
20 static struct uloop_timeout periodic_timer, peer_timer, status_timer, disconnect_timer;
21 static struct uloop_fd dht_fd;
22 static int dht_unix_fd;
23 static LIST_HEAD(bootstrap_peers);
24 static LIST_HEAD(networks);
25 static struct blob_buf b;
26 static uint8_t local_id[20];
27 static const char *node_file;
28 static const char *unix_path;
29 static bool udht_connected;
30
31 static struct {
32 unsigned int tick;
33 unsigned int peer_count;
34 bool bootstrap_added;
35 bool dht_ready;
36 } state;
37
38 struct network_entry {
39 struct list_head list;
40 uint8_t auth_key[CURVE25519_KEY_SIZE];
41 uint8_t id[20];
42 struct uloop_timeout search_timer;
43 int search_count;
44 int seq;
45 };
46
47 struct peer_entry {
48 struct list_head list;
49
50 struct sockaddr_storage sa;
51 int sa_len;
52 };
53
54 void dht_hash(void *hash_return, int hash_size,
55 const void *v1, int len1,
56 const void *v2, int len2,
57 const void *v3, int len3)
58 {
59 siphash_key_t key = {};
60
61 if (hash_size != 8)
62 abort();
63
64 key.key[0] = siphash(v1, len1, &key);
65 key.key[1] = siphash(v2, len2, &key);
66 siphash_to_le64(hash_return, v3, len3, &key);
67 }
68
69 int dht_sendto(int sockfd, const void *buf, int len, int flags,
70 const struct sockaddr *to, int tolen)
71 {
72 struct iovec iov[2] = {
73 { .iov_base = (void *)to },
74 { .iov_base = (void *)buf, .iov_len = len },
75 };
76 struct msghdr msg = {
77 .msg_iov = iov,
78 .msg_iovlen = ARRAY_SIZE(iov),
79 };
80 int ret;
81
82 if (to->sa_family == AF_INET)
83 iov[0].iov_len = sizeof(struct sockaddr_in);
84 else if (to->sa_family == AF_INET6)
85 iov[0].iov_len = sizeof(struct sockaddr_in6);
86 else
87 return -1;
88
89 ret = sendmsg(sockfd, &msg, flags);
90 if (ret < 0) {
91 perror("send");
92 if (errno == ECONNRESET || errno == EDESTADDRREQ ||
93 errno == ENOTCONN || errno == ECONNREFUSED)
94 uloop_timeout_set(&disconnect_timer, 1);
95 }
96 return ret;
97 }
98
99 int dht_blacklisted(const struct sockaddr *sa, int salen)
100 {
101 return 0;
102 }
103
104 int dht_random_bytes(void *buf, size_t size)
105 {
106 int fd, rc, save;
107
108 fd = open("/dev/urandom", O_RDONLY);
109 if(fd < 0)
110 return -1;
111
112 rc = read(fd, buf, size);
113
114 save = errno;
115 close(fd);
116 errno = save;
117
118 return rc;
119 }
120
121 static void
122 udht_start_search(void)
123 {
124 struct network_entry *n;
125
126 if (!state.dht_ready)
127 return;
128
129 list_for_each_entry(n, &networks, list) {
130 if (n->search_timer.pending)
131 continue;
132
133 uloop_timeout_set(&n->search_timer, 1);
134 }
135 }
136
137 static void
138 udht_send_v4_node(const void *id, const void *data)
139 {
140 struct network_entry *n;
141
142 struct {
143 struct sockaddr sa;
144 struct pex_msg_local_control local;
145 } msg = {
146 .sa = {
147 .sa_family = AF_LOCAL
148 },
149 .local = {
150 .ep.in = {
151 .sin_family = AF_INET,
152 .sin_addr = *(const struct in_addr *)data,
153 .sin_port = *(const uint16_t *)(data + 4),
154 },
155 .timeout = 15 * 60,
156 }
157 };
158
159 list_for_each_entry(n, &networks, list) {
160 if (memcmp(n->id, id, sizeof(n->id)) != 0)
161 continue;
162
163 memcpy(&msg.local.auth_id, n->auth_key, sizeof(msg.local.auth_id));
164 goto found;
165 }
166
167 found:
168 send(dht_unix_fd, &msg, sizeof(msg), 0);
169 }
170
171 static void
172 udht_cb(void *closure, int event, const unsigned char *info_hash,
173 const void *data, size_t data_len)
174 {
175 char addrbuf[INET6_ADDRSTRLEN];
176 int i;
177
178 if (!udht_connected)
179 return;
180
181 if (event == DHT_EVENT_SEARCH_DONE) {
182 printf("Search done.\n");
183 udht_start_search();
184 } else if (event == DHT_EVENT_SEARCH_DONE6) {
185 printf("IPv6 search done.\n");
186 } else if (event == DHT_EVENT_VALUES) {
187 printf("Received %d values.\n", (int)(data_len / 6));
188 for (i = 0; i < data_len / 6; i++) {
189 fprintf(stderr, "Node: %s:%d\n", inet_ntop(AF_INET, data, addrbuf, sizeof(addrbuf)), ntohs(*(uint16_t *)(data + 4)));
190 udht_send_v4_node(info_hash, data);
191 data += 6;
192 }
193 }
194 else if (event == DHT_EVENT_VALUES6)
195 printf("Received %d IPv6 values.\n", (int)(data_len / 18));
196 else
197 printf("Unknown DHT event %d.\n", event);
198 }
199
200 static void
201 udht_search_timer_cb(struct uloop_timeout *t)
202 {
203 struct network_entry *n = container_of(t, struct network_entry, search_timer);
204 char id_str[42];
205 int i;
206
207 for (i = 0; i < sizeof(n->id); i++)
208 snprintf(&id_str[i * 2], sizeof(id_str) - i * 2, "%02x", n->id[i]);
209
210 fprintf(stderr, "Start search for network, id=%s\n", id_str);
211 dht_search(n->id, UNETD_GLOBAL_PEX_PORT, AF_INET, udht_cb, NULL);
212
213 if (++n->search_count > 2)
214 uloop_timeout_set(&n->search_timer, 30 * 1000);
215 }
216
217 static void
218 udht_timer_cb(struct uloop_timeout *t)
219 {
220 time_t tosleep = 1;
221
222 dht_periodic(NULL, 0, NULL, 0, &tosleep, udht_cb, NULL);
223 if (!tosleep)
224 tosleep = 1;
225 uloop_timeout_set(t, tosleep * 1000);
226 }
227
228 static void
229 udht_fd_cb(struct uloop_fd *fd, unsigned int events)
230 {
231 static char buf[4096];
232 struct sockaddr *sa = (struct sockaddr *)buf;
233 time_t tosleep = 1;
234 int len;
235
236 while (1) {
237 socklen_t fromlen;
238
239 len = recv(fd->fd, buf, sizeof(buf) - 1, 0);
240 if (len < 0) {
241 if (errno == EINTR)
242 continue;
243
244 if (errno == EAGAIN)
245 break;
246
247 perror("recvfrom");
248 uloop_timeout_set(&disconnect_timer, 1);
249 return;
250 }
251
252 if (len <= sizeof(struct sockaddr))
253 continue;
254
255 if (sa->sa_family == AF_INET)
256 fromlen = sizeof(struct sockaddr_in);
257 else if (sa->sa_family == AF_INET6)
258 fromlen = sizeof(struct sockaddr_in6);
259 else
260 continue;
261
262 if (len <= fromlen)
263 continue;
264
265 buf[len] = 0;
266 dht_periodic(buf + fromlen, len - fromlen, sa, fromlen,
267 &tosleep, udht_cb, NULL);
268 if (!tosleep)
269 tosleep = 1;
270 uloop_timeout_set(&periodic_timer, tosleep * 1000);
271 }
272 }
273
274 static int
275 udht_open_socket(const char *unix_path)
276 {
277 uint8_t fd_buf[CMSG_SPACE(sizeof(int))] = { 0 };
278 static struct sockaddr sa = {
279 .sa_family = AF_LOCAL,
280 };
281 static struct iovec iov = {
282 .iov_base = &sa,
283 .iov_len = sizeof(sa),
284 };
285 struct msghdr msg = {
286 .msg_iov = &iov,
287 .msg_iovlen = 1,
288 .msg_control = fd_buf,
289 .msg_controllen = CMSG_LEN(sizeof(int)),
290 };
291 struct cmsghdr *cmsg;
292 int sfd[2];
293 int fd;
294
295 fd = usock(USOCK_UNIX | USOCK_UDP, unix_path, NULL);
296 if (fd < 0)
297 return -1;
298
299 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sfd) < 0)
300 close(fd);
301
302 dht_unix_fd = fd;
303 dht_fd.fd = sfd[1];
304 dht_fd.cb = udht_fd_cb;
305 uloop_fd_add(&dht_fd, ULOOP_READ);
306
307 cmsg = CMSG_FIRSTHDR(&msg);
308 cmsg->cmsg_type = SCM_RIGHTS;
309 cmsg->cmsg_level = SOL_SOCKET;
310 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
311 *(int *)CMSG_DATA(cmsg) = sfd[0];
312
313 sendmsg(dht_unix_fd, &msg, 0);
314 close(sfd[0]);
315
316 return 0;
317 }
318
319 static void
320 udht_close_socket(void)
321 {
322 uloop_fd_delete(&dht_fd);
323 close(dht_fd.fd);
324 close(dht_unix_fd);
325 }
326
327 static void udht_id_hash(uint8_t *dest, const void *data, int len)
328 {
329 struct sha512_state s;
330 uint8_t hash[SHA512_HASH_SIZE];
331
332 sha512_init(&s);
333 sha512_add(&s, data, len);
334 sha512_final(&s, hash);
335 memcpy(dest, hash, 20);
336 }
337
338 static void udht_add_peer(const void *data, int len)
339 {
340 const struct sockaddr *sa = data;
341 struct peer_entry *p;
342
343 p = calloc(1, sizeof(*p));
344 memcpy(&p->sa, sa, len);
345 p->sa_len = len;
346 list_add_tail(&p->list, &bootstrap_peers);
347
348 if (!peer_timer.pending)
349 uloop_timeout_set(&peer_timer, 1);
350 }
351
352 static void udht_add_bootstrap_peer(void)
353 {
354 const struct addrinfo hints = {
355 .ai_family = AF_INET,
356 .ai_socktype = SOCK_DGRAM,
357 .ai_flags = AI_ADDRCONFIG,
358 };
359 struct addrinfo *res, *cur;
360 static const char * const bootstrap_hosts[] = {
361 "router.bittorrent.com",
362 "router.utorrent.com",
363 };
364 int i;
365
366 for (i = 0; i < ARRAY_SIZE(bootstrap_hosts); i++) {
367 if (getaddrinfo(bootstrap_hosts[i], "6881", &hints, &res) < 0)
368 continue;
369
370 for (cur = res; cur; cur = cur->ai_next)
371 udht_add_peer(cur->ai_addr, cur->ai_addrlen);
372
373 freeaddrinfo(res);
374 }
375
376 state.bootstrap_added = true;
377 }
378
379 static void udht_peer_timer_cb(struct uloop_timeout *t)
380 {
381 struct peer_entry *p;
382 struct sockaddr_in *sin;
383 char buf[INET6_ADDRSTRLEN];
384
385 if (list_empty(&bootstrap_peers)) {
386 if (!state.peer_count && !state.bootstrap_added)
387 udht_add_bootstrap_peer();
388
389 return;
390 }
391
392 p = list_first_entry(&bootstrap_peers, struct peer_entry, list);
393 list_del(&p->list);
394 sin = (struct sockaddr_in *)&p->sa;
395 fprintf(stderr, "Ping node %s\n", inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf)));
396 dht_ping_node((struct sockaddr *)&p->sa, p->sa_len);
397 free(p);
398
399 if (state.peer_count++ < 8)
400 uloop_timeout_set(t, 2000);
401 else
402 uloop_timeout_set(t, 15000);
403 }
404
405 void udht_network_add(const uint8_t *auth_key, int seq)
406 {
407 struct network_entry *n;
408
409 list_for_each_entry(n, &networks, list) {
410 if (memcmp(n->auth_key, auth_key, sizeof(n->auth_key)) != 0)
411 continue;
412
413 goto out;
414 }
415
416 n = calloc(1, sizeof(*n));
417 n->search_timer.cb = udht_search_timer_cb;
418 memcpy(n->auth_key, auth_key, sizeof(n->auth_key));
419 udht_id_hash(n->id, n->auth_key, sizeof(n->auth_key));
420 list_add_tail(&n->list, &networks);
421
422 if (state.dht_ready)
423 uloop_timeout_set(&n->search_timer, 1);
424
425 out:
426 n->seq = seq;
427 }
428
429 void udht_network_flush(int seq)
430 {
431 struct network_entry *n, *tmp;
432
433 list_for_each_entry_safe(n, tmp, &networks, list) {
434 if (seq >= 0 && (n->seq < 0 || n->seq == seq))
435 continue;
436
437 list_del(&n->list);
438 uloop_timeout_cancel(&n->search_timer);
439 free(n);
440 }
441 }
442
443
444 static void
445 udht_status_check(struct uloop_timeout *t)
446 {
447 int good = 0, dubious = 0, incoming = 0;
448 static int prev_good, prev_dubious, prev_incoming;
449
450 state.tick++;
451 uloop_timeout_set(t, 1000);
452
453 dht_nodes(AF_INET, &good, &dubious, NULL, &incoming);
454 if (good != prev_good || dubious != prev_dubious || incoming != prev_incoming)
455 fprintf(stderr, "DHT status: good=%d, dubious=%d, incoming=%d\n", good, dubious, incoming);
456
457 prev_good = good;
458 prev_dubious = dubious;
459 prev_incoming = incoming;
460
461 if (state.dht_ready)
462 return;
463
464 if (good < 4 || good + dubious < 8) {
465 if (state.tick > 45 && !state.bootstrap_added)
466 udht_add_bootstrap_peer();
467
468 return;
469 }
470
471 state.dht_ready = true;
472 fprintf(stderr, "DHT is ready\n");
473 udht_start_search();
474 }
475
476 static void
477 udht_load_nodes(const char *filename)
478 {
479 struct blob_attr *data, *cur;
480 size_t len;
481 FILE *f;
482 int rem;
483
484 f = fopen(filename, "r");
485 if (!f)
486 return;
487
488 data = malloc(sizeof(struct blob_attr));
489 if (fread(data, sizeof(struct blob_attr), 1, f) != 1)
490 goto out;
491
492 len = blob_pad_len(data);
493 if (len <= sizeof(struct blob_attr))
494 goto out;
495
496 if (len >= 256 * 1024)
497 goto out;
498
499 data = realloc(data, len);
500 if (fread(data + 1, len - sizeof(struct blob_attr), 1, f) != 1)
501 goto out;
502
503 blob_for_each_attr(cur, data, rem) {
504 void *entry = blob_data(cur);
505
506 if (blob_len(cur) == 6) {
507 struct sockaddr_in sin = {
508 .sin_family = AF_INET,
509 .sin_addr = *(struct in_addr *)entry,
510 .sin_port = *(uint16_t *)(entry + 4),
511 };
512 udht_add_peer(&sin, sizeof(sin));
513 } else {
514 continue;
515 }
516 }
517
518 out:
519 free(data);
520 }
521
522 static void
523 udht_save_nodes(const char *filename)
524 {
525 struct sockaddr_in sin[128];
526 struct sockaddr_in6 sin6[128];
527 int n_sin = ARRAY_SIZE(sin);
528 int n_sin6 = ARRAY_SIZE(sin6);
529 FILE *f;
530 int i;
531
532 if (!filename)
533 return;
534
535 if (dht_get_nodes(sin, &n_sin, sin6, &n_sin6) <= 0)
536 return;
537
538 if (n_sin < 8)
539 return;
540
541 blob_buf_init(&b, 0);
542 for (i = 0; i < n_sin; i++) {
543 struct {
544 struct in_addr addr;
545 uint16_t port;
546 } __attribute__((packed)) data = {
547 .addr = sin[i].sin_addr,
548 .port = sin[i].sin_port,
549 };
550 blob_put(&b, 4, &data, sizeof(data));
551 }
552
553 f = fopen(filename, "w");
554 if (!f)
555 return;
556
557 fwrite(b.head, blob_pad_len(b.head), 1, f);
558
559 fclose(f);
560 }
561
562 static int usage(const char *progname)
563 {
564 fprintf(stderr, "Usage: %s [<options>] <id string>\n"
565 "Options:\n"
566 " -d Enable debug mode\n"
567 " -n <file> Set node filename\n"
568 " -N <key> Add network key\n"
569 "\n",
570 progname);
571 return 1;
572 }
573
574 static void udht_disconnect(struct uloop_timeout *t)
575 {
576 struct peer_entry *p, *tmp;
577
578 if (!udht_connected)
579 return;
580
581 list_for_each_entry_safe(p, tmp, &bootstrap_peers, list) {
582 list_del(&p->list);
583 free(p);
584 }
585
586 uloop_timeout_cancel(&disconnect_timer);
587 udht_connected = false;
588 udht_network_flush(-1);
589
590 uloop_timeout_cancel(&peer_timer);
591 uloop_timeout_cancel(&status_timer);
592 uloop_timeout_cancel(&periodic_timer);
593
594 udht_save_nodes(node_file);
595 dht_uninit();
596
597 memset(&state, 0, sizeof(state));
598
599 udht_close_socket();
600
601 #ifndef UBUS_SUPPORT
602 uloop_end();
603 #endif
604 }
605
606 int udht_reconnect(void)
607 {
608 udht_disconnect(&disconnect_timer);
609
610 if (udht_open_socket(unix_path) < 0)
611 return -1;
612
613 if (dht_init(dht_unix_fd, -1, local_id, NULL) < 0) {
614 udht_close_socket();
615 return -1;
616 }
617
618 udht_connected = true;
619 fprintf(stderr, "DHT connected\n");
620
621 udht_load_nodes(node_file);
622
623 uloop_timeout_set(&peer_timer, 1);
624 uloop_timeout_set(&status_timer, 1000);
625
626 return 0;
627 }
628
629 int main(int argc, char **argv)
630 {
631 const char *progname = argv[0];
632 uint8_t auth_key[CURVE25519_KEY_SIZE];
633 int ch;
634
635 while ((ch = getopt(argc, argv, "dN:n:u:")) != -1) {
636 switch (ch) {
637 case 'N':
638 if (b64_decode(optarg, auth_key, CURVE25519_KEY_SIZE) != CURVE25519_KEY_SIZE) {
639 fprintf(stderr, "Invalid network key\n");
640 return 1;
641 }
642
643 udht_network_add(auth_key, -1);
644 break;
645 case 'n':
646 node_file = optarg;
647 break;
648 case 'd':
649 dht_debug = stderr;
650 break;
651 case 'u':
652 unix_path = optarg;
653 break;
654 default:
655 return usage(progname);
656 }
657 }
658
659 argv += optind;
660 argc -= optind;
661
662 if (argc != 1)
663 return usage(progname);
664
665 udht_id_hash(local_id, argv[0], strlen(argv[0]));
666
667 status_timer.cb = udht_status_check;
668 periodic_timer.cb = udht_timer_cb;
669 peer_timer.cb = udht_peer_timer_cb;
670 disconnect_timer.cb = udht_disconnect;
671 uloop_init();
672
673 #ifdef UBUS_SUPPORT
674 udht_ubus_init();
675 #else
676 if (udht_reconnect() < 0) {
677 fprintf(stderr, "Failed to connect to unetd\n");
678 return 1;
679 }
680 #endif
681
682 uloop_run();
683 uloop_done();
684
685 udht_disconnect(&disconnect_timer);
686 blob_buf_free(&b);
687
688 return 0;
689 }