about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--btpd/http_tr_if.c35
-rw-r--r--btpd/main.c33
-rw-r--r--btpd/nameconn.c2
-rw-r--r--btpd/net.c87
-rw-r--r--btpd/net.h7
-rw-r--r--btpd/opts.c4
-rw-r--r--btpd/opts.h3
-rw-r--r--btpd/peer.c37
-rw-r--r--btpd/peer.h2
9 files changed, 144 insertions, 66 deletions
diff --git a/btpd/http_tr_if.c b/btpd/http_tr_if.c
index e1a86ed..376bb02 100644
--- a/btpd/http_tr_if.c
+++ b/btpd/http_tr_if.c
@@ -62,6 +62,7 @@ parse_reply(struct torrent *tp, const char *content, size_t size, int parse,
     const char *buf;
     size_t len;
     const char *peers;
+    const char *v6key[] = {"peers6", "peers_ipv6"};
 
     if (benc_validate(content, size) != 0)
         goto bad_data;
@@ -92,12 +93,24 @@ parse_reply(struct torrent *tp, const char *content, size_t size, int parse,
              peers = benc_next(peers))
             maybe_connect_to(tp, peers);
     } else if (benc_isstr(peers)) {
-        peers = benc_dget_mem(content, "peers", &len);
-        for (size_t i = 0; i < len && net_npeers < net_max_peers; i += 6)
-            peer_create_out_compact(tp->net, peers + i);
+        if (net_ipv4) {
+            peers = benc_dget_mem(content, "peers", &len);
+            for (size_t i = 0; i < len && net_npeers < net_max_peers; i += 6)
+                peer_create_out_compact(tp->net, AF_INET, peers + i);
+        }
     } else
         goto bad_data;
 
+    if (!net_ipv6)
+        return 0;
+    for (int k = 0; k < 2; k++) {
+        peers = benc_dget_any(content, v6key[k]);
+        if (peers != NULL && benc_isstr(peers)) {
+            peers = benc_dget_mem(content, v6key[k], &len);
+            for (size_t i = 0; i < len && net_npeers < net_max_peers; i += 18)
+                peer_create_out_compact(tp->net, AF_INET6, peers + i);
+        }
+    }
     return 0;
 
 bad_data:
@@ -178,7 +191,7 @@ nc_cb(void *arg, int error, int sd)
 struct http_tr_req *
 http_tr_req(struct torrent *tp, enum tr_event event, const char *aurl)
 {
-    char e_hash[61], e_id[61], ip_arg[INET_ADDRSTRLEN + 4], url[512], qc;
+    char e_hash[61], e_id[61], url[512], qc;
     const uint8_t *peer_id = btpd_get_peer_id();
     struct http_url *http_url;
 
@@ -189,18 +202,12 @@ http_tr_req(struct torrent *tp, enum tr_event event, const char *aurl)
     for (int i = 0; i < 20; i++)
         snprintf(e_id + i * 3, 4, "%%%.2x", peer_id[i]);
 
-    if (tr_ip_arg == INADDR_ANY)
-        ip_arg[0] = '\0';
-    else {
-        bcopy("&ip=", ip_arg, 4);
-        inet_ntop(AF_INET, &tr_ip_arg, ip_arg + 4, sizeof(ip_arg) - 4);
-    }
-
     snprintf(url, sizeof(url),
-        "%s%cinfo_hash=%s&peer_id=%s&key=%ld%s&port=%d&uploaded=%llu"
+        "%s%cinfo_hash=%s&peer_id=%s&key=%ld%s%s&port=%d&uploaded=%llu"
         "&downloaded=%llu&left=%llu&compact=1%s%s",
-        aurl, qc, e_hash, e_id, tr_key, ip_arg, net_port,
-        tp->net->uploaded, tp->net->downloaded,
+        aurl, qc, e_hash, e_id, tr_key,
+        tr_ip_arg == NULL ? "" : "&ip=", tr_ip_arg == NULL ? "" : tr_ip_arg,
+        net_port, tp->net->uploaded, tp->net->downloaded,
         (long long)tp->total_length - cm_content(tp),
         event == TR_EV_EMPTY ? "" : "&event=", m_tr_events[event]);
 
diff --git a/btpd/main.c b/btpd/main.c
index 2972540..60575f1 100644
--- a/btpd/main.c
+++ b/btpd/main.c
@@ -103,6 +103,12 @@ usage(void)
         "Usage: btpd [-d dir] [-p port] [more options...]\n"
         "\n"
         "Options:\n"
+        "-4\n"
+        "\tToggle use of IPv4. It's enabled by default.\n"
+        "\n"
+        "-6\n"
+        "\tToggle use of IPv6. It's enabled by default.\n"
+        "\n"
         "--bw-in n\n"
         "\tLimit incoming BitTorrent traffic to n kB/s.\n"
         "\tDefault is 0 which means unlimited.\n"
@@ -121,9 +127,8 @@ usage(void)
         "\tShow this text.\n"
         "\n"
         "--ip addr\n"
-        "\tMake other peers use the given address, instead of the one\n"
-        "\tthe tracker perceives as this peer's address, when contacting\n"
-        "\tthis peer.\n"
+        "\tLet the tracker distribute the given address instead of the one\n"
+        "\tit sees btpd connect from.\n"
         "\n"
         "--ipcprot mode\n"
         "\tSet the protection mode of the command socket.\n"
@@ -183,9 +188,15 @@ main(int argc, char **argv)
     int daemonize = 1;
 
     for (;;) {
-        switch (getopt_long(argc, argv, "d:p:", longopts, NULL)) {
+        switch (getopt_long(argc, argv, "46d:p:", longopts, NULL)) {
         case -1:
             goto args_done;
+        case '6':
+            net_ipv6 ^= 1;
+            break;
+        case '4':
+            net_ipv4 ^= 1;
+            break;
         case 'd':
             dir = optarg;
             break;
@@ -222,16 +233,7 @@ main(int argc, char **argv)
                 empty_start = 1;
                 break;
             case 10:
-                switch (inet_pton(AF_INET, optarg, &tr_ip_arg)) {
-                case 1:
-                    break;
-                case 0:
-                    btpd_err("You must specify a dotted IPv4 address.\n");
-                    break;
-                default:
-                    btpd_err("inet_ntop for '%s' failed (%s).\n", optarg,
-                        strerror(errno));
-                }
+                tr_ip_arg = optarg;
                 break;
             default:
                 usage();
@@ -246,6 +248,9 @@ args_done:
     argc -= optind;
     argv += optind;
 
+    if (!net_ipv4 && !net_ipv6)
+        btpd_err("You need to enable at least one ip version.\n");
+
     if (argc > 0)
         usage();
 
diff --git a/btpd/nameconn.c b/btpd/nameconn.c
index e4bde27..3f24591 100644
--- a/btpd/nameconn.c
+++ b/btpd/nameconn.c
@@ -98,7 +98,7 @@ btpd_name_connect(const char *name, short port, void (*cb)(void *, int, int),
     nc->sd = -1;
     bzero(&hints, sizeof(hints));
     hints.ai_flags = AI_ADDRCONFIG;
-    hints.ai_family = AF_UNSPEC;
+    hints.ai_family = net_af_spec();
     hints.ai_socktype = SOCK_STREAM;
     nc->ai_handle = btpd_addrinfo(name, port, &hints, nc_ai_cb, nc);
     return nc;
diff --git a/btpd/net.c b/btpd/net.c
index 2ba247a..0112abe 100644
--- a/btpd/net.c
+++ b/btpd/net.c
@@ -9,7 +9,12 @@ static unsigned long m_bw_bytes_out;
 static unsigned long m_rate_up;
 static unsigned long m_rate_dwn;
 
-static struct fdev m_net_incoming;
+struct net_listener {
+    int sd;
+    struct fdev ev;
+};
+
+static struct net_listener *m_net_listeners;
 
 unsigned net_npeers;
 
@@ -438,9 +443,9 @@ out:
 }
 
 int
-net_connect2(struct sockaddr *sa, socklen_t salen, int *sd)
+net_connect_addr(int family, struct sockaddr *sa, socklen_t salen, int *sd)
 {
-    if ((*sd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
+    if ((*sd = socket(family, SOCK_STREAM, 0)) == -1)
         return errno;
 
     set_nonblocking(*sd);
@@ -456,7 +461,7 @@ net_connect2(struct sockaddr *sa, socklen_t salen, int *sd)
 }
 
 int
-net_connect(const char *ip, int port, int *sd)
+net_connect_name(const char *ip, int port, int *sd)
 {
     struct addrinfo hints, *res;
     char portstr[6];
@@ -466,13 +471,14 @@ net_connect(const char *ip, int port, int *sd)
     if (snprintf(portstr, sizeof(portstr), "%d", port) >= sizeof(portstr))
         return EINVAL;
     bzero(&hints, sizeof(hints));
-    hints.ai_family = AF_UNSPEC;
+    hints.ai_family = net_af_spec();
     hints.ai_flags = AI_NUMERICHOST;
     hints.ai_socktype = SOCK_STREAM;
     if (getaddrinfo(ip, portstr, &hints, &res) != 0)
         return errno;
 
-    int error = net_connect2(res->ai_addr, res->ai_addrlen, sd);
+    int error =
+        net_connect_addr(res->ai_family, res->ai_addr, res->ai_addrlen, sd);
     freeaddrinfo(res);
     return error;
 }
@@ -487,7 +493,7 @@ net_connection_cb(int sd, short type, void *arg)
         if (errno == EWOULDBLOCK || errno == ECONNABORTED)
             return;
         else
-            btpd_err("accept4: %s\n", strerror(errno));
+            btpd_err("accept: %s\n", strerror(errno));
     }
 
     if (set_nonblocking(nsd) != 0) {
@@ -651,6 +657,17 @@ net_io_cb(int sd, short type, void *arg)
     }
 }
 
+int
+net_af_spec(void)
+{
+    if (net_ipv4 && net_ipv6)
+        return AF_UNSPEC;
+    else if (net_ipv4)
+        return AF_INET;
+    else
+        return AF_INET6;
+}
+
 void
 net_init(void)
 {
@@ -661,20 +678,44 @@ net_init(void)
     if (net_max_peers == 0 || net_max_peers > safe_fds)
         net_max_peers = safe_fds;
 
-    int sd;
-    int flag = 1;
-    struct sockaddr_in addr;
-    addr.sin_family = AF_INET;
-    addr.sin_addr.s_addr = htonl(INADDR_ANY);
-    addr.sin_port = htons(net_port);
-
-    if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
-        btpd_err("socket: %s\n", strerror(errno));
-    setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
-    if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
-        btpd_err("bind: %s\n", strerror(errno));
-    listen(sd, 10);
-    set_nonblocking(sd);
-
-    btpd_ev_new(&m_net_incoming, sd, EV_READ, net_connection_cb, NULL);
+    int count = 0, flag = 1, found_ipv4 = 0, found_ipv6 = 0, sd;
+    char portstr[6];
+    struct addrinfo hints, *res, *ai;
+    bzero(&hints, sizeof(hints));
+    hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE | AI_NUMERICSERV;
+    hints.ai_family = net_af_spec();
+    hints.ai_socktype = SOCK_STREAM;
+    snprintf(portstr, sizeof(portstr), "%hd", net_port);
+    if ((errno = getaddrinfo(NULL, portstr, &hints, &res)) != 0)
+        btpd_err("getaddrinfo failed (%s).\n", gai_strerror(errno));
+    for (ai = res; ai != NULL; ai = ai->ai_next) {
+        count++;
+        if (ai->ai_family == AF_INET)
+            found_ipv4 = 1;
+        else
+            found_ipv6 = 1;
+    }
+    net_ipv4 = found_ipv4;
+    net_ipv6 = found_ipv6;
+    if (!net_ipv4 && !net_ipv6)
+        btpd_err("no usable address found. wrong use of -4/-6 perhaps.\n");
+    m_net_listeners = btpd_calloc(count, sizeof(*m_net_listeners));
+    for (ai = res; ai != NULL; ai = ai->ai_next) {
+        count--;
+        if ((sd = socket(ai->ai_family, ai->ai_socktype, 0)) == -1)
+            btpd_err("failed to create socket (%s).\n", strerror(errno));
+        setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
+#ifdef IPV6_V6ONLY
+        if (ai->ai_family == AF_INET6)
+            setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag));
+#endif
+        if (bind(sd, ai->ai_addr, ai->ai_addrlen) == -1)
+            btpd_err("bind failed (%s).\n", strerror(errno));
+        listen(sd, 10);
+        set_nonblocking(sd);
+        m_net_listeners[count].sd = sd;
+        btpd_ev_new(&m_net_listeners[count].ev, sd, EV_READ,
+            net_connection_cb, NULL);
+    }
+    freeaddrinfo(res);
 }
diff --git a/btpd/net.h b/btpd/net.h
index 194c119..11b4506 100644
--- a/btpd/net.h
+++ b/btpd/net.h
@@ -33,7 +33,10 @@ int net_torrent_has_peer(struct net *n, const uint8_t *id);
 
 void net_io_cb(int sd, short type, void *arg);
 
-int net_connect2(struct sockaddr *sa, socklen_t salen, int *sd);
-int net_connect(const char *ip, int port, int *sd);
+int net_connect_addr(int family, struct sockaddr *sa, socklen_t salen,
+    int *sd);
+int net_connect_name(const char *ip, int port, int *sd);
+
+int net_af_spec(void);
 
 #endif
diff --git a/btpd/opts.c b/btpd/opts.c
index a46bf42..398aa8a 100644
--- a/btpd/opts.c
+++ b/btpd/opts.c
@@ -14,4 +14,6 @@ int net_port = 6881;
 off_t cm_alloc_size = 2048 * 1024;
 int ipcprot = 0600;
 int empty_start = 0;
-uint32_t tr_ip_arg = INADDR_ANY;
+const char *tr_ip_arg;
+int net_ipv4 = 1;
+int net_ipv6 = 1;
diff --git a/btpd/opts.h b/btpd/opts.h
index dc819f0..04c3a36 100644
--- a/btpd/opts.h
+++ b/btpd/opts.h
@@ -11,6 +11,7 @@ extern int net_port;
 extern off_t cm_alloc_size;
 extern int ipcprot;
 extern int empty_start;
-extern uint32_t tr_ip_arg;
+extern const char *tr_ip_arg;
+extern int net_ipv4, net_ipv6;
 
 #endif
diff --git a/btpd/peer.c b/btpd/peer.c
index 8f6f4be..db9dd17 100644
--- a/btpd/peer.c
+++ b/btpd/peer.c
@@ -295,7 +295,7 @@ peer_create_out(struct net *n, const uint8_t *id,
     int sd;
     struct peer *p;
 
-    if (net_connect(ip, port, &sd) != 0)
+    if (net_connect_name(ip, port, &sd) != 0)
         return;
 
     p = peer_create_common(sd);
@@ -304,17 +304,36 @@ peer_create_out(struct net *n, const uint8_t *id,
 }
 
 void
-peer_create_out_compact(struct net *n, const char *compact)
+peer_create_out_compact(struct net *n, int family, const char *compact)
 {
     int sd;
     struct peer *p;
-    struct sockaddr_in addr;
-
-    addr.sin_family = AF_INET;
-    bcopy(compact, &addr.sin_addr.s_addr, 4);
-    bcopy(compact + 4, &addr.sin_port, 2);
-
-    if (net_connect2((struct sockaddr *)&addr, sizeof(addr), &sd) != 0)
+    struct sockaddr_storage addr;
+    struct sockaddr_in *a4;
+    struct sockaddr_in6 *a6;
+    
+    switch (family) {
+    case AF_INET:
+        if (!net_ipv4)
+            return;
+        a4 = (struct sockaddr_in *)&addr;
+        a4->sin_family = AF_INET;
+        bcopy(compact, &a4->sin_addr.s_addr, 4);
+        bcopy(compact + 4, &a4->sin_port, 2);
+        break;
+    case AF_INET6:
+        if (!net_ipv6)
+            return;
+        a6 = (struct sockaddr_in6 *)&addr;
+        a6->sin6_family = AF_INET6;
+        bcopy(compact, &a6->sin6_addr, 16);
+        bcopy(compact + 16, &a6->sin6_port, 2);
+        break;
+    default:
+        abort();
+    }
+    if (net_connect_addr(family, (struct sockaddr *)&addr,
+            sizeof(addr), &sd) != 0)
         return;
 
     p = peer_create_common(sd);
diff --git a/btpd/peer.h b/btpd/peer.h
index 7d51c98..61b022a 100644
--- a/btpd/peer.h
+++ b/btpd/peer.h
@@ -35,7 +35,7 @@ int peer_requested(struct peer *p, uint32_t piece, uint32_t block);
 void peer_create_in(int sd);
 void peer_create_out(struct net *n, const uint8_t *id,
     const char *ip, int port);
-void peer_create_out_compact(struct net *n, const char *compact);
+void peer_create_out_compact(struct net *n, int family, const char *compact);
 void peer_kill(struct peer *p);
 
 void peer_on_no_reqs(struct peer *p);