]> Sergey Matveev's repositories - dht-bootstrap.git/blobdiff - dht-bootstrap.c
Stats in process title
[dht-bootstrap.git] / dht-bootstrap.c
index 2e6e77dc25fe1aac8a5c858886a64e4b1b84554e..07ac332d8d5befe169585494109d81dfb7ff221d 100644 (file)
@@ -20,28 +20,29 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 */
 
-#include <arpa/inet.h>
+#include <err.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <netdb.h>
 #include <netinet/in.h>
+#include <poll.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/resource.h>
 #include <sys/socket.h>
-#include <sys/time.h>
-#include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
 
-#ifndef MSG_CONFIRM
-#define MSG_CONFIRM 0
-#endif
-
-#define MAX(x, y) ((x) >= (y) ? (x) : (y))
 #define MIN(x, y) ((x) <= (y) ? (x) : (y))
 
+#ifdef __linux__
+// clang-format off
+void setproctitle_fast(const char *_fmt, ...) {}
+// clang-format on
+#endif
+
 static int
 send_ping(struct sockaddr *sa, int salen, const unsigned char *tid, int tid_len);
 static int
@@ -53,8 +54,7 @@ send_find_node(
     const unsigned char *tid,
     int tid_len,
     const unsigned char *target,
-    int want,
-    int confirm);
+    int want);
 static int
 send_nodes(
     struct sockaddr *sa,
@@ -139,24 +139,22 @@ struct circular_list {
     struct node nodes[CIRCULAR_LIST_SIZE];
 };
 
-struct circular_list v4_new, v6_new, v4_confirmed, v6_confirmed;
+static struct circular_list v4_new, v6_new, v4_confirmed, v6_confirmed;
 
 #define MAX_TOKEN_BUCKET_TOKENS 40
 static time_t token_bucket_time;
 static int token_bucket_tokens;
 
-FILE *dht_debug = NULL;
+static FILE *dht_debug = NULL;
 
-#ifdef __GNUC__
-__attribute__((format(printf, 1, 2)))
-#endif
 static void
 debugf(const char *format, ...)
 {
+    if (dht_debug == NULL)
+        return;
     va_list args;
     va_start(args, format);
-    if (dht_debug)
-        vfprintf(dht_debug, format, args);
+    vfprintf(dht_debug, format, args);
     va_end(args);
     fflush(dht_debug);
 }
@@ -362,24 +360,61 @@ send_request(struct circular_list *list, int dopop, int doping, int want)
         return send_ping((struct sockaddr *)&ss, sslen, ttid, 4);
     } else {
         unsigned char id[20];
-        int i;
-        for (i = 0; i < 20; i++)
-            id[i] = random() & 0xFF;
+        arc4random_buf(id, sizeof id);
         make_tid(ttid, "fn", 0);
         debugf("Sending find_node.\n");
-        return send_find_node((struct sockaddr *)&ss, sslen, ttid, 4, id, want, 0);
+        return send_find_node((struct sockaddr *)&ss, sslen, ttid, 4, id, want);
+    }
+}
+
+static int
+newSock(const char *host, const char *port)
+{
+    struct addrinfo hints;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = SOCK_DGRAM;
+    struct addrinfo *res = NULL;
+    int rc = getaddrinfo(host, port, &hints, &res);
+    if (rc != 0)
+        err(EXIT_FAILURE, "getaddrinfo: %s\n", gai_strerror(rc));
+    int sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+    if (sock == -1)
+        err(EXIT_FAILURE, "socket()");
+    if (bind(sock, res->ai_addr, res->ai_addrlen) != 0)
+        err(EXIT_FAILURE, "bind()");
+    rc = fcntl(sock, F_GETFL, 0);
+    if (rc < 0)
+        err(EXIT_FAILURE, "F_GETFL");
+    rc = fcntl(sock, F_SETFL, (rc | O_NONBLOCK));
+    if (rc < 0)
+        err(EXIT_FAILURE, "F_SETFL");
+    freeaddrinfo(res);
+    return sock;
+}
+
+static void
+rlimited(int res)
+{
+    struct rlimit r;
+    r.rlim_cur = 0;
+    r.rlim_max = 0;
+    if (setrlimit(res, &r) == -1) {
+        err(EXIT_FAILURE, "can not setrlimit()");
     }
 }
 
 int
 main(int argc, char **argv)
 {
-    int port = 6881, quiet = 0, ipv4 = 1, ipv6 = 1;
-    int opt, rc, i, send4;
-    unsigned char ttid[4];
+    errno = 0;
+    int quiet = 0;
+    char *ipv4addr = NULL;
+    char *ipv6addr = NULL;
+    int opt = 0;
 
     while (1) {
-        opt = getopt(argc, argv, "q46");
+        opt = getopt(argc, argv, "q4:6:");
         if (opt < 0)
             break;
 
@@ -388,126 +423,30 @@ main(int argc, char **argv)
             quiet = 1;
             break;
         case '4':
-            ipv6 = 0;
+            ipv4addr = strdup(optarg);
             break;
         case '6':
-            ipv4 = 0;
+            ipv6addr = strdup(optarg);
             break;
         default:
             goto usage;
         }
     }
 
-    i = optind;
+    int i = optind;
 
     if (argc < i + 1)
         goto usage;
 
-    port = atoi(argv[i++]);
-    if (port <= 0 || port >= 0x10000)
-        goto usage;
-
-    if (ipv4) {
-        dht_socket = socket(PF_INET, SOCK_DGRAM, 0);
-        if (dht_socket < 0)
-            perror("socket(IPv4)");
+    const char *ourPort = strdup(argv[i++]);
+    if (ipv4addr != NULL) {
+        dht_socket = newSock(ipv4addr, ourPort);
     }
-
-    if (ipv6) {
-        dht_socket6 = socket(PF_INET6, SOCK_DGRAM, 0);
-        if (dht_socket6 < 0)
-            perror("socket(IPv6)");
+    if (ipv6addr != NULL) {
+        dht_socket6 = newSock(ipv6addr, ourPort);
     }
 
-    if (dht_socket < 0 && dht_socket6 < 0) {
-        fprintf(stderr, "Eek!\n");
-        exit(1);
-    }
-
-    if (dht_socket >= 0) {
-        struct sockaddr_in sin;
-        int rc;
-        memset(&sin, 0, sizeof(sin));
-        sin.sin_family = AF_INET;
-        sin.sin_port = htons(port);
-        rc = bind(dht_socket, (struct sockaddr *)&sin, sizeof(sin));
-        if (rc < 0) {
-            perror("bind(IPv4)");
-            exit(1);
-        }
-
-        rc = fcntl(dht_socket, F_GETFL, 0);
-        if (rc < 0) {
-            perror("F_GETFL");
-            exit(1);
-        }
-
-        rc = fcntl(dht_socket, F_SETFL, (rc | O_NONBLOCK));
-        if (rc < 0) {
-            perror("F_SETFL");
-            exit(1);
-        }
-    }
-
-    if (dht_socket6 >= 0) {
-        struct sockaddr_in6 sin6;
-        int rc;
-        int val = 1;
-
-        rc = setsockopt(
-            dht_socket6, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&val, sizeof(val));
-        if (rc < 0) {
-            perror("setsockopt(IPV6_V6ONLY)");
-            exit(1);
-        }
-
-        /* BEP-32 mandates that we should bind this socket to one of our
-           global IPv6 addresses.  In this program, this only happens if
-           the user used the -b flag. */
-
-        memset(&sin6, 0, sizeof(sin6));
-        sin6.sin6_family = AF_INET6;
-        sin6.sin6_port = htons(port);
-        rc = bind(dht_socket6, (struct sockaddr *)&sin6, sizeof(sin6));
-        if (rc < 0) {
-            perror("bind(IPv6)");
-            exit(1);
-        }
-
-        rc = fcntl(dht_socket6, F_GETFL, 0);
-        if (rc < 0) {
-            perror("F_GETFL");
-            exit(1);
-        }
-
-        rc = fcntl(dht_socket6, F_SETFL, (rc | O_NONBLOCK));
-        if (rc < 0) {
-            perror("F_SETFL");
-            exit(1);
-        }
-    }
-
-    {
-        int fd;
-        unsigned int seed;
-
-        fd = open("/dev/urandom", O_RDONLY);
-        if (fd < 0) {
-            perror("open(random)");
-            exit(1);
-        }
-
-        rc = read(fd, myid, 20);
-        if (rc < 20) {
-            perror("open(random)");
-            exit(1);
-        }
-
-        rc = read(fd, &seed, sizeof(seed));
-        srandom(seed);
-
-        close(fd);
-    }
+    arc4random_buf(myid, sizeof myid);
 
     memcpy(my_v, "1:v4:JB\0\0", 9);
     have_v = 1;
@@ -515,6 +454,9 @@ main(int argc, char **argv)
     if (!quiet)
         dht_debug = stdout;
 
+    int rc = 0;
+    unsigned char ttid[4];
+
     while (i < argc) {
         struct addrinfo hints, *info, *infop;
         memset(&hints, 0, sizeof(hints));
@@ -525,10 +467,8 @@ main(int argc, char **argv)
         else if (dht_socket6 < 0)
             hints.ai_family |= AF_INET;
         rc = getaddrinfo(argv[i], argv[i + 1], &hints, &info);
-        if (rc != 0) {
-            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rc));
-            exit(1);
-        }
+        if (rc != 0)
+            err(EXIT_FAILURE, "getaddrinfo: %s\n", gai_strerror(rc));
 
         i++;
         if (i >= argc)
@@ -548,44 +488,46 @@ main(int argc, char **argv)
 
     token_bucket_time = time(NULL);
     token_bucket_tokens = MAX_TOKEN_BUCKET_TOKENS;
+    int send4 = 0;
+    struct pollfd fds[2];
+    fds[0].fd = dht_socket;
+    fds[0].events = POLLIN;
+    fds[1].fd = dht_socket6;
+    fds[1].events = POLLIN;
+
+    close(STDIN_FILENO);
+    if (quiet)
+        close(STDOUT_FILENO);
+    rlimited(RLIMIT_NPROC);
+    rlimited(RLIMIT_FSIZE);
+#if __FreeBSD__
+    rlimited(RLIMIT_NOFILE);
+#endif // __FreeBSD__
 
     while (1) {
-        struct timeval tv;
-        fd_set readfds;
-        int rc;
-
+        int tv_sec = 0;
         if ((dht_socket >= 0 && list_elements(&v4_confirmed) <= 16) ||
             (dht_socket6 >= 0 && list_elements(&v6_confirmed) <= 16))
-            tv.tv_sec = 0;
+            tv_sec = 0;
         else
-            tv.tv_sec = random() % 30;
-        tv.tv_usec = random() % 1000000;
-
-        FD_ZERO(&readfds);
-        if (dht_socket >= 0)
-            FD_SET(dht_socket, &readfds);
-        if (dht_socket6 >= 0)
-            FD_SET(dht_socket6, &readfds);
-
-        if (dht_debug)
-            debugf(
-                "%d+%d %d+%d\n",
-                list_elements(&v4_confirmed),
-                list_elements(&v6_confirmed),
-                list_elements(&v4_new),
-                list_elements(&v6_new));
+            tv_sec = random() % 30;
+        int tv_msec = random() % 1000;
 
-        rc = select(MAX(dht_socket, dht_socket6) + 1, &readfds, NULL, NULL, &tv);
+        setproctitle_fast(
+            "%d+%d %d+%d",
+            list_elements(&v4_confirmed),
+            list_elements(&v6_confirmed),
+            list_elements(&v4_new),
+            list_elements(&v6_new));
 
+        rc = poll(fds, 2, tv_sec * 1000 + tv_msec);
         if (rc < 0) {
-            if (errno != EINTR) {
-                perror("select");
-                sleep(1);
-            }
+            perror("poll");
+            sleep(1);
         }
 
         if (rc > 0) {
-            int rc, message;
+            int message;
             unsigned char tid[16], id[20], info_hash[20], target[20];
             unsigned char buf[1536], nodes[256], nodes6[1024], token[128];
             int tid_len = 16, token_len = 128;
@@ -597,10 +539,20 @@ main(int argc, char **argv)
             struct sockaddr_storage source_storage;
             struct sockaddr *source = (struct sockaddr *)&source_storage;
             socklen_t sourcelen = sizeof(source_storage);
-            if (dht_socket >= 0 && FD_ISSET(dht_socket, &readfds)) {
-                rc = recvfrom(dht_socket, buf, 1536, 0, source, &sourcelen);
-            } else if (dht_socket6 >= 0 && FD_ISSET(dht_socket6, &readfds)) {
-                rc = recvfrom(dht_socket6, buf, 1536, 0, source, &sourcelen);
+            if (fds[0].revents != 0) {
+                if ((fds[0].revents & (POLLERR | POLLNVAL)) > 0) {
+                    fprintf(stderr, "error in fds[0]");
+                    rc = -1;
+                } else {
+                    rc = recvfrom(dht_socket, buf, 1536, 0, source, &sourcelen);
+                }
+            } else if (fds[1].revents != 0) {
+                if ((fds[1].revents & (POLLERR | POLLNVAL)) > 0) {
+                    fprintf(stderr, "error in fds[1]");
+                    rc = -1;
+                } else {
+                    rc = recvfrom(dht_socket6, buf, 1536, 0, source, &sourcelen);
+                }
             }
 
             if (rc < 0 || sourcelen > sizeof(struct sockaddr_storage))
@@ -755,11 +707,9 @@ main(int argc, char **argv)
         }
     }
 
-    return 0;
-
 usage:
-    fprintf(stderr, "dht-bootstrap [-q] [-4] [-6] port [node port...]\n");
-    exit(1);
+    fprintf(stderr, "dht-bootstrap [-q] [-4 ADDR4] [-6 ADDR6] port [node port...]\n");
+    exit(EXIT_FAILURE);
 }
 
 /* We could use a proper bencoding printer and parser, but the format of
@@ -855,8 +805,7 @@ send_find_node(
     const unsigned char *tid,
     int tid_len,
     const unsigned char *target,
-    int want,
-    int confirm)
+    int want)
 {
     char buf[512];
     int i = 0, rc;
@@ -881,7 +830,7 @@ send_find_node(
     ADD_V(buf, i, 512);
     rc = snprintf(buf + i, 512 - i, "1:y1:qe");
     INC(i, rc, 512);
-    return dht_send(buf, i, confirm ? MSG_CONFIRM : 0, sa, salen);
+    return dht_send(buf, i, 0, sa, salen);
 
 fail:
     errno = ENOSPC;