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.master
@@ -62,6 +62,7 @@ parse_reply(struct torrent *tp, const char *content, size_t size, int parse, | |||||
const char *buf; | const char *buf; | ||||
size_t len; | size_t len; | ||||
const char *peers; | const char *peers; | ||||
const char *v6key[] = {"peers6", "peers_ipv6"}; | |||||
if (benc_validate(content, size) != 0) | if (benc_validate(content, size) != 0) | ||||
goto bad_data; | goto bad_data; | ||||
@@ -92,12 +93,24 @@ parse_reply(struct torrent *tp, const char *content, size_t size, int parse, | |||||
peers = benc_next(peers)) | peers = benc_next(peers)) | ||||
maybe_connect_to(tp, peers); | maybe_connect_to(tp, peers); | ||||
} else if (benc_isstr(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 | } else | ||||
goto bad_data; | 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; | return 0; | ||||
bad_data: | bad_data: | ||||
@@ -178,7 +191,7 @@ nc_cb(void *arg, int error, int sd) | |||||
struct http_tr_req * | struct http_tr_req * | ||||
http_tr_req(struct torrent *tp, enum tr_event event, const char *aurl) | 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(); | const uint8_t *peer_id = btpd_get_peer_id(); | ||||
struct http_url *http_url; | 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++) | for (int i = 0; i < 20; i++) | ||||
snprintf(e_id + i * 3, 4, "%%%.2x", peer_id[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), | 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", | "&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), | (long long)tp->total_length - cm_content(tp), | ||||
event == TR_EV_EMPTY ? "" : "&event=", m_tr_events[event]); | event == TR_EV_EMPTY ? "" : "&event=", m_tr_events[event]); | ||||
@@ -103,6 +103,12 @@ usage(void) | |||||
"Usage: btpd [-d dir] [-p port] [more options...]\n" | "Usage: btpd [-d dir] [-p port] [more options...]\n" | ||||
"\n" | "\n" | ||||
"Options:\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" | "--bw-in n\n" | ||||
"\tLimit incoming BitTorrent traffic to n kB/s.\n" | "\tLimit incoming BitTorrent traffic to n kB/s.\n" | ||||
"\tDefault is 0 which means unlimited.\n" | "\tDefault is 0 which means unlimited.\n" | ||||
@@ -121,9 +127,8 @@ usage(void) | |||||
"\tShow this text.\n" | "\tShow this text.\n" | ||||
"\n" | "\n" | ||||
"--ip addr\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" | "\n" | ||||
"--ipcprot mode\n" | "--ipcprot mode\n" | ||||
"\tSet the protection mode of the command socket.\n" | "\tSet the protection mode of the command socket.\n" | ||||
@@ -183,9 +188,15 @@ main(int argc, char **argv) | |||||
int daemonize = 1; | int daemonize = 1; | ||||
for (;;) { | for (;;) { | ||||
switch (getopt_long(argc, argv, "d:p:", longopts, NULL)) { | |||||
switch (getopt_long(argc, argv, "46d:p:", longopts, NULL)) { | |||||
case -1: | case -1: | ||||
goto args_done; | goto args_done; | ||||
case '6': | |||||
net_ipv6 ^= 1; | |||||
break; | |||||
case '4': | |||||
net_ipv4 ^= 1; | |||||
break; | |||||
case 'd': | case 'd': | ||||
dir = optarg; | dir = optarg; | ||||
break; | break; | ||||
@@ -222,16 +233,7 @@ main(int argc, char **argv) | |||||
empty_start = 1; | empty_start = 1; | ||||
break; | break; | ||||
case 10: | 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; | break; | ||||
default: | default: | ||||
usage(); | usage(); | ||||
@@ -246,6 +248,9 @@ args_done: | |||||
argc -= optind; | argc -= optind; | ||||
argv += optind; | argv += optind; | ||||
if (!net_ipv4 && !net_ipv6) | |||||
btpd_err("You need to enable at least one ip version.\n"); | |||||
if (argc > 0) | if (argc > 0) | ||||
usage(); | usage(); | ||||
@@ -98,7 +98,7 @@ btpd_name_connect(const char *name, short port, void (*cb)(void *, int, int), | |||||
nc->sd = -1; | nc->sd = -1; | ||||
bzero(&hints, sizeof(hints)); | bzero(&hints, sizeof(hints)); | ||||
hints.ai_flags = AI_ADDRCONFIG; | hints.ai_flags = AI_ADDRCONFIG; | ||||
hints.ai_family = AF_UNSPEC; | |||||
hints.ai_family = net_af_spec(); | |||||
hints.ai_socktype = SOCK_STREAM; | hints.ai_socktype = SOCK_STREAM; | ||||
nc->ai_handle = btpd_addrinfo(name, port, &hints, nc_ai_cb, nc); | nc->ai_handle = btpd_addrinfo(name, port, &hints, nc_ai_cb, nc); | ||||
return nc; | return nc; | ||||
@@ -9,7 +9,12 @@ static unsigned long m_bw_bytes_out; | |||||
static unsigned long m_rate_up; | static unsigned long m_rate_up; | ||||
static unsigned long m_rate_dwn; | 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; | unsigned net_npeers; | ||||
@@ -438,9 +443,9 @@ out: | |||||
} | } | ||||
int | 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; | return errno; | ||||
set_nonblocking(*sd); | set_nonblocking(*sd); | ||||
@@ -456,7 +461,7 @@ net_connect2(struct sockaddr *sa, socklen_t salen, int *sd) | |||||
} | } | ||||
int | int | ||||
net_connect(const char *ip, int port, int *sd) | |||||
net_connect_name(const char *ip, int port, int *sd) | |||||
{ | { | ||||
struct addrinfo hints, *res; | struct addrinfo hints, *res; | ||||
char portstr[6]; | 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)) | if (snprintf(portstr, sizeof(portstr), "%d", port) >= sizeof(portstr)) | ||||
return EINVAL; | return EINVAL; | ||||
bzero(&hints, sizeof(hints)); | bzero(&hints, sizeof(hints)); | ||||
hints.ai_family = AF_UNSPEC; | |||||
hints.ai_family = net_af_spec(); | |||||
hints.ai_flags = AI_NUMERICHOST; | hints.ai_flags = AI_NUMERICHOST; | ||||
hints.ai_socktype = SOCK_STREAM; | hints.ai_socktype = SOCK_STREAM; | ||||
if (getaddrinfo(ip, portstr, &hints, &res) != 0) | if (getaddrinfo(ip, portstr, &hints, &res) != 0) | ||||
return errno; | 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); | freeaddrinfo(res); | ||||
return error; | return error; | ||||
} | } | ||||
@@ -487,7 +493,7 @@ net_connection_cb(int sd, short type, void *arg) | |||||
if (errno == EWOULDBLOCK || errno == ECONNABORTED) | if (errno == EWOULDBLOCK || errno == ECONNABORTED) | ||||
return; | return; | ||||
else | else | ||||
btpd_err("accept4: %s\n", strerror(errno)); | |||||
btpd_err("accept: %s\n", strerror(errno)); | |||||
} | } | ||||
if (set_nonblocking(nsd) != 0) { | 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 | void | ||||
net_init(void) | net_init(void) | ||||
{ | { | ||||
@@ -661,20 +678,44 @@ net_init(void) | |||||
if (net_max_peers == 0 || net_max_peers > safe_fds) | if (net_max_peers == 0 || net_max_peers > safe_fds) | ||||
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); | |||||
} | } |
@@ -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); | 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 | #endif |
@@ -14,4 +14,6 @@ int net_port = 6881; | |||||
off_t cm_alloc_size = 2048 * 1024; | off_t cm_alloc_size = 2048 * 1024; | ||||
int ipcprot = 0600; | int ipcprot = 0600; | ||||
int empty_start = 0; | int empty_start = 0; | ||||
uint32_t tr_ip_arg = INADDR_ANY; | |||||
const char *tr_ip_arg; | |||||
int net_ipv4 = 1; | |||||
int net_ipv6 = 1; |
@@ -11,6 +11,7 @@ extern int net_port; | |||||
extern off_t cm_alloc_size; | extern off_t cm_alloc_size; | ||||
extern int ipcprot; | extern int ipcprot; | ||||
extern int empty_start; | extern int empty_start; | ||||
extern uint32_t tr_ip_arg; | |||||
extern const char *tr_ip_arg; | |||||
extern int net_ipv4, net_ipv6; | |||||
#endif | #endif |
@@ -295,7 +295,7 @@ peer_create_out(struct net *n, const uint8_t *id, | |||||
int sd; | int sd; | ||||
struct peer *p; | struct peer *p; | ||||
if (net_connect(ip, port, &sd) != 0) | |||||
if (net_connect_name(ip, port, &sd) != 0) | |||||
return; | return; | ||||
p = peer_create_common(sd); | p = peer_create_common(sd); | ||||
@@ -304,17 +304,36 @@ peer_create_out(struct net *n, const uint8_t *id, | |||||
} | } | ||||
void | 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; | int sd; | ||||
struct peer *p; | 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; | return; | ||||
p = peer_create_common(sd); | p = peer_create_common(sd); | ||||
@@ -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_in(int sd); | ||||
void peer_create_out(struct net *n, const uint8_t *id, | void peer_create_out(struct net *n, const uint8_t *id, | ||||
const char *ip, int port); | 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_kill(struct peer *p); | ||||
void peer_on_no_reqs(struct peer *p); | void peer_on_no_reqs(struct peer *p); | ||||