From 05329268d5d1c2402960115872d64dfa8116e6de Mon Sep 17 00:00:00 2001 From: Richard Nyberg Date: Wed, 14 Jan 2009 00:25:31 +0100 Subject: [PATCH] Add IPv6 support. Btpd can now use both ipv4 and ipv6. The new options -4 and -6 toggles use of ip v4 and v6 respectively. They are both used by default. Remove restrictions on the --ip option since the spec allows to be a dns name. Ultimately this option may need to be changed on a per tracker/torrent basis. --- btpd/http_tr_if.c | 35 +++++++++++-------- btpd/main.c | 33 ++++++++++-------- btpd/nameconn.c | 2 +- btpd/net.c | 87 ++++++++++++++++++++++++++++++++++------------- btpd/net.h | 7 ++-- btpd/opts.c | 4 ++- btpd/opts.h | 3 +- btpd/peer.c | 37 +++++++++++++++----- btpd/peer.h | 2 +- 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);