From 54be88dec3538c285d4b5f4d9ad400aa241d6ba1 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 15 Dec 2008 00:17:08 +0000 Subject: [PATCH] (trunk libT) add ipv6 support by jhujhiti. I think this is the largest user-contributed patch we've ever used... thanks jhujhiti :) --- libtransmission/fdlimit.c | 12 +- libtransmission/fdlimit.h | 2 +- libtransmission/net.c | 228 ++++++++++++++++++++++-------- libtransmission/net.h | 72 ++++++---- libtransmission/peer-mgr.c | 91 +++++++++--- libtransmission/peer-mgr.h | 13 +- libtransmission/peer-msgs.c | 123 +++++++++++++--- libtransmission/port-forwarding.c | 194 ++++++++++++++++--------- libtransmission/resume.c | 31 +++- libtransmission/torrent.c | 7 +- libtransmission/tracker.c | 126 +++++++++++++---- 11 files changed, 669 insertions(+), 230 deletions(-) diff --git a/libtransmission/fdlimit.c b/libtransmission/fdlimit.c index 87e217aec..a4423efe1 100644 --- a/libtransmission/fdlimit.c +++ b/libtransmission/fdlimit.c @@ -450,16 +450,19 @@ getSocketMax( struct tr_fd_s * gFd ) } int -tr_fdSocketCreate( int type ) +tr_fdSocketCreate( int domain, int type ) { int s = -1; tr_lockLock( gFd->lock ); if( gFd->socketCount < getSocketMax( gFd ) ) - if( ( s = socket( AF_INET, type, 0 ) ) < 0 ) + if( ( s = socket( domain, type, 0 ) ) < 0 ) + { tr_err( _( "Couldn't create socket: %s" ), tr_strerror( sockerrno ) ); + s = -sockerrno; + } if( s > -1 ) ++gFd->socketCount; @@ -485,7 +488,7 @@ tr_fdSocketAccept( int b, tr_lockLock( gFd->lock ); if( gFd->socketCount < getSocketMax( gFd ) ) { - len = sizeof( struct sockaddr ); + len = sizeof( struct sockaddr_storage ); s = accept( b, (struct sockaddr *) &sock, &len ); } if( s > -1 ) @@ -503,8 +506,7 @@ tr_fdSocketAccept( int b, { struct sockaddr_in6 * sock6 = (struct sockaddr_in6 *)&sock; addr->type = TR_AF_INET6; - memcpy( &addr->addr, &sock6->sin6_addr, - sizeof( struct sockaddr_in6 ) ); + addr->addr.addr6 = sock6->sin6_addr; *port = sock6->sin6_port; } ++gFd->socketCount; diff --git a/libtransmission/fdlimit.h b/libtransmission/fdlimit.h index b1a2bfcf2..b35bf2e93 100644 --- a/libtransmission/fdlimit.h +++ b/libtransmission/fdlimit.h @@ -85,7 +85,7 @@ void tr_fdFileClose( const char * filename ); /*********************************************************************** * Sockets **********************************************************************/ -int tr_fdSocketCreate( int type ); +int tr_fdSocketCreate( int domain, int type ); int tr_fdSocketAccept( int b, tr_address * addr, diff --git a/libtransmission/net.c b/libtransmission/net.c index 1a4680c49..5238ea818 100644 --- a/libtransmission/net.c +++ b/libtransmission/net.c @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -107,7 +108,17 @@ tr_pton( const char * src, tr_address * dst ) return NULL; dst->type = TR_AF_INET6; return dst; -} +} + +void +tr_normalizeV4Mapped( tr_address * const addr ) +{ + if( addr->type == TR_AF_INET6 && IN6_IS_ADDR_V4MAPPED( &addr->addr.addr6 ) ) + { + addr->type = TR_AF_INET; + memcpy( &addr->addr.addr4.s_addr, addr->addr.addr6.s6_addr + 12, 4 ); + } +} /* * Compare two tr_address structures. @@ -139,6 +150,98 @@ tr_compareAddresses( const tr_address * a, const tr_address * b) return retval; } +/*********************************************************************** + * Socket list housekeeping + **********************************************************************/ +struct tr_socketList +{ + int socket; + tr_address addr; + tr_socketList * next; +}; + +tr_socketList * +tr_socketListAppend( tr_socketList * const head, + const tr_address * const addr ) +{ + tr_socketList * tmp; + assert( head ); + for( tmp = head; tmp->next; tmp = tmp->next ); + tmp->next = tr_socketListNew( addr ); + return tmp->next; +} + +tr_socketList * +tr_socketListNew( const tr_address * const addr ) +{ + tr_socketList * tmp; + tmp = tr_new( tr_socketList, 1 ); + tmp->socket = -1; + tmp->addr = *addr; + tmp->next = NULL; + return tmp; +} + +void +tr_socketListFree( tr_socketList * const head ) +{ + assert( head ); + if( head->next ) + tr_socketListFree( head->next ); + tr_free( head ); +} + +void +tr_socketListRemove( tr_socketList * const head, + tr_socketList * const el) +{ + tr_socketList * tmp; + assert( head ); + assert( el ); + for( tmp = head; tmp->next && tmp->next != el; tmp = tmp->next ); + tmp->next = el->next; + el->next = NULL; + tr_socketListFree(el); +} + +void +tr_socketListTruncate( tr_socketList * const head, + tr_socketList * const start ) +{ + tr_socketList * tmp; + assert( head ); + assert( start ); + for( tmp = head; tmp->next && tmp->next != start; tmp = tmp->next ); + tr_socketListFree( start ); + tmp->next = NULL; +} + +int +tr_socketListGetSocket( const tr_socketList * const el ) +{ + assert( el ); + return el->socket; +} + +const tr_address * +tr_socketListGetAddress( const tr_socketList * const el ) +{ + assert( el ); + return &el->addr; +} + +void +tr_socketListForEach( tr_socketList * const head, + void ( * cb ) ( int * const, + tr_address * const, + void * const), + void * const userData ) +{ + tr_socketList * tmp; + for( tmp = head; tmp; tmp = tmp->next ) + cb( &tmp->socket, &tmp->addr, userData ); +} + /*********************************************************************** * TCP sockets **********************************************************************/ @@ -161,10 +264,12 @@ makeSocketNonBlocking( int fd ) { if( evutil_make_socket_nonblocking( fd ) ) { + int tmperrno; tr_err( _( "Couldn't create socket: %s" ), tr_strerror( sockerrno ) ); + tmperrno = sockerrno; tr_netClose( fd ); - fd = -1; + fd = -tmperrno; } } @@ -172,9 +277,9 @@ makeSocketNonBlocking( int fd ) } static int -createSocket( int type ) +createSocket( int domain, int type ) { - return makeSocketNonBlocking( tr_fdSocketCreate( type ) ); + return makeSocketNonBlocking( tr_fdSocketCreate( domain, type ) ); } static void @@ -191,59 +296,65 @@ setSndBuf( tr_session * session UNUSED, int fd UNUSED ) #endif } -static void -setup_sockaddr( const tr_address * addr, - tr_port port, - struct sockaddr_storage * sockaddr) -{ - struct sockaddr_in sock4; - struct sockaddr_in6 sock6; +static socklen_t +setup_sockaddr( const tr_address * addr, + tr_port port, + struct sockaddr_storage * sockaddr) +{ + struct sockaddr_in sock4; + struct sockaddr_in6 sock6; + if( addr->type == TR_AF_INET ) + { + memset( &sock4, 0, sizeof( sock4 ) ); + sock4.sin_family = AF_INET; + sock4.sin_addr.s_addr = addr->addr.addr4.s_addr; + sock4.sin_port = port; + memcpy( sockaddr, &sock4, sizeof( sock4 ) ); + return sizeof( struct sockaddr_in ); + } + else + { + memset( &sock6, 0, sizeof( sock6 ) ); + sock6.sin6_family = AF_INET6; + sock6.sin6_port = port; + sock6.sin6_flowinfo = 0; + sock6.sin6_addr = addr->addr.addr6; + memcpy( sockaddr, &sock6, sizeof( sock6 ) ); + return sizeof( struct sockaddr_in6 ); + } +} - if( addr->type == TR_AF_INET ) - { - memset( &sock4, 0, sizeof( sock4 ) ); - sock4.sin_family = AF_INET; - sock4.sin_addr.s_addr = addr->addr.addr4.s_addr; - sock4.sin_port = port; - memcpy( sockaddr, &sock4, sizeof( sock4 ) ); - } - else - { - memset( &sock6, 0, sizeof( sock6 ) ); - sock6.sin6_family = AF_INET6; - sock6.sin6_port = port; - memcpy( &sock6.sin6_addr, &addr->addr, sizeof( struct in6_addr ) ); - memcpy( sockaddr, &sock6, sizeof( sock6 ) ); - } -} - -int -tr_netOpenTCP( tr_session * session, - const tr_address * addr, - tr_port port ) -{ - int s; - struct sockaddr_storage sock; - const int type = SOCK_STREAM; +int +tr_netOpenTCP( tr_session * session, + const tr_address * addr, + tr_port port ) +{ + int s; + struct sockaddr_storage sock; + const int type = SOCK_STREAM; + socklen_t addrlen; - if( ( s = createSocket( type ) ) < 0 ) - return -1; + if( ( s = createSocket( ( addr->type == TR_AF_INET ? AF_INET : AF_INET6 ), + type ) ) < 0 ) + return s; setSndBuf( session, s ); - setup_sockaddr( addr, port, &sock ); + addrlen = setup_sockaddr( addr, port, &sock ); if( ( connect( s, (struct sockaddr *) &sock, - sizeof( struct sockaddr ) ) < 0 ) + addrlen ) < 0 ) #ifdef WIN32 && ( sockerrno != WSAEWOULDBLOCK ) #endif && ( sockerrno != EINPROGRESS ) ) { + int tmperrno; tr_err( _( "Couldn't connect socket %d to %s, port %d (errno %d - %s)" ), s, tr_ntop_non_ts( addr ), (int)port, sockerrno, tr_strerror( sockerrno ) ); + tmperrno = sockerrno; tr_netClose( s ); - s = -1; + s = -tmperrno; } tr_deepLog( __FILE__, __LINE__, NULL, "New OUTGOING connection %d (%s)", @@ -253,37 +364,42 @@ tr_netOpenTCP( tr_session * session, } int -tr_netBindTCP( const tr_address * addr, tr_port port ) +tr_netBindTCP( const tr_address * addr, tr_port port, tr_bool suppressMsgs ) { - int s; - struct sockaddr_storage sock; - const int type = SOCK_STREAM; + int s; + struct sockaddr_storage sock; + const int type = SOCK_STREAM; + int addrlen; #if defined( SO_REUSEADDR ) || defined( SO_REUSEPORT ) int optval; #endif - if( ( s = createSocket( type ) ) < 0 ) - return -1; + if( ( s = createSocket( ( addr->type == TR_AF_INET ? AF_INET : AF_INET6 ), + type ) ) < 0 ) + return s; #ifdef SO_REUSEADDR optval = 1; setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof( optval ) ); #endif - setup_sockaddr( addr, htons( port ), &sock ); + addrlen = setup_sockaddr( addr, htons( port ), &sock ); if( bind( s, (struct sockaddr *) &sock, - sizeof( struct sockaddr ) ) ) + addrlen ) ) { - tr_err( _( "Couldn't bind port %d on %s: %s" ), port, - tr_ntop_non_ts( addr ), tr_strerror( sockerrno ) ); + int tmperrno; + if( !suppressMsgs ) + tr_err( _( "Couldn't bind port %d on %s: %s" ), port, + tr_ntop_non_ts( addr ), tr_strerror( sockerrno ) ); + tmperrno = sockerrno; tr_netClose( s ); - return -1; + return -tmperrno; } - - tr_dbg( "Bound socket %d to port %d on %s", - s, port, tr_ntop_non_ts( addr ) ); + if( !suppressMsgs ) + tr_dbg( "Bound socket %d to port %d on %s", + s, port, tr_ntop_non_ts( addr ) ); return s; } diff --git a/libtransmission/net.h b/libtransmission/net.h index 5f84ce4c6..08b851fd6 100644 --- a/libtransmission/net.h +++ b/libtransmission/net.h @@ -55,30 +55,51 @@ struct tr_session; -#define TR_AF_INET 0 -#define TR_AF_INET6 1 - -typedef struct tr_address { - unsigned short type : 1; - union { - /* The order here is important for tr_in{,6}addr_any initialization, - * since we can't use C99 designated initializers */ - struct in6_addr addr6; - struct in_addr addr4; - } addr; -} tr_address; - -extern const tr_address tr_inaddr_any; -extern const tr_address tr_in6addr_any; - -const char *tr_ntop( const tr_address * src, - char * dst, - int size ); -const char *tr_ntop_non_ts( const tr_address * src ); -tr_address *tr_pton( const char * src, - tr_address * dst ); -int tr_compareAddresses( const tr_address * a, - const tr_address * b); +#define TR_AF_INET 0 +#define TR_AF_INET6 1 + +typedef struct tr_address { + uint8_t type; + union { + /* The order here is important for tr_in{,6}addr_any initialization, + * since we can't use C99 designated initializers */ + struct in6_addr addr6; + struct in_addr addr4; + } addr; +} tr_address; + +extern const tr_address tr_inaddr_any; +extern const tr_address tr_in6addr_any; + +const char *tr_ntop( const tr_address * src, + char * dst, + int size ); +const char *tr_ntop_non_ts( const tr_address * src ); +tr_address *tr_pton( const char * src, + tr_address * dst ); +int tr_compareAddresses( const tr_address * a, + const tr_address * b); +void tr_normalizeV4Mapped( tr_address * const addr ); + +/*********************************************************************** + * Socket list housekeeping + **********************************************************************/ +typedef struct tr_socketList tr_socketList; +tr_socketList *tr_socketListAppend( tr_socketList * const head, + const tr_address * const addr ); +tr_socketList *tr_socketListNew( const tr_address * const addr ); +void tr_socketListFree( tr_socketList * const head ); +void tr_socketListRemove( tr_socketList * const head, + tr_socketList * const el); +void tr_socketListTruncate( tr_socketList * const head, + tr_socketList * const start ); +int tr_socketListGetSocket( const tr_socketList * const el ); +const tr_address *tr_socketListGetAddress( const tr_socketList * const el ); +void tr_socketListForEach( tr_socketList * const head, + void ( * cb ) ( int * const, + tr_address * const, + void * const ), + void * const userData); /*********************************************************************** * Sockets @@ -88,7 +109,8 @@ int tr_netOpenTCP( struct tr_handle * session, tr_port port ); int tr_netBindTCP( const tr_address * addr, - tr_port port ); + tr_port port, + tr_bool suppressMsgs ); int tr_netAccept( struct tr_handle * session, int bound, diff --git a/libtransmission/peer-mgr.c b/libtransmission/peer-mgr.c index a4845c642..5e59e5859 100644 --- a/libtransmission/peer-mgr.c +++ b/libtransmission/peer-mgr.c @@ -1346,7 +1346,7 @@ tr_peerMgrAddPex( tr_peerMgr * manager, t = getExistingTorrent( manager, torrentHash ); if( !tr_sessionIsAddressBlocked( t->manager->session, &pex->addr ) ) ensureAtomExists( t, &pex->addr, pex->port, pex->flags, from ); - + managerUnlock( manager ); } @@ -1375,6 +1375,53 @@ tr_peerMgrCompactToPex( const void * compact, return pex; } +tr_pex * +tr_peerMgrCompact6ToPex( const void * compact, + size_t compactLen, + const uint8_t * added_f, + size_t added_f_len, + size_t * pexCount ) +{ + size_t i; + size_t n = compactLen / 18; + const uint8_t * walk = compact; + tr_pex * pex = tr_new0( tr_pex, n ); + + for( i = 0; i < n; ++i ) + { + pex[i].addr.type = TR_AF_INET6; + memcpy( &pex[i].addr.addr.addr6.s6_addr, walk, 16 ); walk += 16; + memcpy( &pex[i].port, walk, 2 ); walk += 2; + if( added_f && ( n == added_f_len ) ) + pex[i].flags = added_f[i]; + } + + *pexCount = n; + return pex; +} + +tr_pex * +tr_peerMgrArrayToPex( const void * array, + size_t arrayLen, + size_t * pexCount ) +{ + size_t i; + size_t n = arrayLen / ( sizeof( tr_address ) + 2 ); + /*size_t n = arrayLen / sizeof( tr_peerArrayElement );*/ + const uint8_t * walk = array; + tr_pex * pex = tr_new0( tr_pex, n ); + + for( i = 0 ; i < n ; i++ ) { + memcpy( &pex[i].addr, walk, sizeof( tr_address ) ); + memcpy( &pex[i].port, walk + sizeof( tr_address ), 2 ); + pex[i].flags = 0x00; + walk += sizeof( tr_address ) + 2; + } + + *pexCount = n; + return pex; +} + /** *** **/ @@ -1434,11 +1481,13 @@ peerPrefersCrypto( const tr_peer * peer ) } int -tr_peerMgrGetPeers( tr_peerMgr * manager, +tr_peerMgrGetPeers( tr_peerMgr * manager, const uint8_t * torrentHash, - tr_pex ** setme_pex ) + tr_pex ** setme_pex, + uint8_t af) { int peerCount = 0; + int peersReturning = 0; const Torrent * t; managerLock( manager ); @@ -1452,30 +1501,36 @@ tr_peerMgrGetPeers( tr_peerMgr * manager, { int i; const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount ); + /* for now, this will waste memory on torrents that have both + * ipv6 and ipv4 peers */ tr_pex * pex = tr_new( tr_pex, peerCount ); tr_pex * walk = pex; for( i=0; iaddr ); - - walk->addr = peer->addr; - walk->port = peer->port; - walk->flags = 0; - if( peerPrefersCrypto( peer ) ) - walk->flags |= ADDED_F_ENCRYPTION_FLAG; - if( ( atom->uploadOnly == UPLOAD_ONLY_YES ) || ( peer->progress >= 1.0 ) ) - walk->flags |= ADDED_F_SEED_FLAG; + if( peer->addr.type == af ) + { + const struct peer_atom * atom = getExistingAtom( t, &peer->addr ); + memcpy( &walk->addr, &peer->addr, sizeof( walk->addr ) ); + walk->port = peer->port; + walk->flags = 0; + if( peerPrefersCrypto( peer ) ) + walk->flags |= ADDED_F_ENCRYPTION_FLAG; + if( ( atom->uploadOnly == UPLOAD_ONLY_YES ) || + ( peer->progress >= 1.0 ) ) + walk->flags |= ADDED_F_SEED_FLAG; + peersReturning++; + } } assert( ( walk - pex ) == peerCount ); - qsort( pex, peerCount, sizeof( tr_pex ), tr_pexCompare ); + qsort( pex, peersReturning, sizeof( tr_pex ), tr_pexCompare ); *setme_pex = pex; } managerUnlock( manager ); - return peerCount; + return peersReturning; } static int reconnectPulse( void * vtorrent ); @@ -1785,9 +1840,13 @@ tr_peerMgrPeerStats( const tr_peerMgr * manager, const tr_peer * peer = peers[i]; const struct peer_atom * atom = getExistingAtom( t, &peer->addr ); tr_peer_stat * stat = ret + i; + tr_address norm_addr; - tr_ntop( &peer->addr, stat->addr, sizeof(stat->addr) ); - tr_strlcpy( stat->client, (peer->client ? peer->client : ""), sizeof(stat->client) ); + memcpy( &norm_addr, &peer->addr, sizeof( tr_address ) ); + tr_normalizeV4Mapped( &norm_addr ); + tr_ntop( &norm_addr, stat->addr, sizeof( stat->addr ) ); + tr_strlcpy( stat->client, ( peer->client ? peer->client : "" ), + sizeof( stat->client ) ); stat->port = ntohs( peer->port ); stat->from = atom->from; stat->progress = peer->progress; diff --git a/libtransmission/peer-mgr.h b/libtransmission/peer-mgr.h index 9aca6a35e..f14703ee8 100644 --- a/libtransmission/peer-mgr.h +++ b/libtransmission/peer-mgr.h @@ -67,6 +67,16 @@ tr_pex * tr_peerMgrCompactToPex( const void * compact, size_t added_f_len, size_t * setme_pex_count ); +tr_pex * tr_peerMgrCompact6ToPex( const void * compact, + size_t compactLen, + const uint8_t * added_f, + size_t added_f_len, + size_t * pexCount ); + +tr_pex * tr_peerMgrArrayToPex( const void * array, + size_t arrayLen, + size_t * setme_pex_count ); + void tr_peerMgrAddPex( tr_peerMgr * manager, const uint8_t * torrentHash, uint8_t from, @@ -79,7 +89,8 @@ void tr_peerMgrSetBlame( tr_peerMgr * manager, int tr_peerMgrGetPeers( tr_peerMgr * manager, const uint8_t * torrentHash, - tr_pex ** setme_pex ); + tr_pex ** setme_pex, + uint8_t af); void tr_peerMgrStartTorrent( tr_peerMgr * manager, const uint8_t * torrentHash ); diff --git a/libtransmission/peer-msgs.c b/libtransmission/peer-msgs.c index e60bf2a38..5eae36da5 100644 --- a/libtransmission/peer-msgs.c +++ b/libtransmission/peer-msgs.c @@ -282,6 +282,7 @@ struct tr_peermsgs uint8_t state; uint8_t ut_pex_id; uint16_t pexCount; + uint16_t pexCount6; uint16_t minActiveRequests; uint16_t maxActiveRequests; @@ -308,6 +309,7 @@ struct tr_peermsgs tr_timer * pexTimer; tr_pex * pex; + tr_pex * pex6; time_t clientSentPexAt; time_t clientSentAnythingAt; @@ -1181,19 +1183,40 @@ parseUtPex( tr_peermsgs * msgs, int msglen, struct evbuffer * inbuf ) tr_peerIoReadBytes( msgs->peer->io, inbuf, tmp, msglen ); if( tr_torrentAllowsPex( tor ) - && (( loaded = !tr_bencLoad( tmp, msglen, &val, NULL ))) - && tr_bencDictFindRaw( &val, "added", &added, &added_len )) + && ( ( loaded = !tr_bencLoad( tmp, msglen, &val, NULL ) ) ) ) { - const uint8_t * added_f = NULL; - tr_pex * pex; - size_t i, n; - size_t added_f_len = 0; - tr_bencDictFindRaw( &val, "added.f", &added_f, &added_f_len ); - pex = tr_peerMgrCompactToPex( added, added_len, added_f, added_f_len, &n ); - for( i=0; isession->peerMgr, tor->info.hash, - TR_PEER_FROM_PEX, pex + i ); - tr_free( pex ); + if( tr_bencDictFindRaw( &val, "added", &added, &added_len ) ) + { + const uint8_t * added_f = NULL; + tr_pex * pex; + size_t i, n; + size_t added_f_len = 0; + tr_bencDictFindRaw( &val, "added.f", &added_f, &added_f_len ); + pex = + tr_peerMgrCompactToPex( added, added_len, added_f, added_f_len, + &n ); + for( i = 0; i < n; ++i ) + tr_peerMgrAddPex( msgs->session->peerMgr, tor->info.hash, + TR_PEER_FROM_PEX, pex + i ); + tr_free( pex ); + } + + if( tr_bencDictFindRaw( &val, "added6", &added, &added_len ) ) + { + const uint8_t * added_f = NULL; + tr_pex * pex; + size_t i, n; + size_t added_f_len = 0; + tr_bencDictFindRaw( &val, "added6.f", &added_f, &added_f_len ); + pex = + tr_peerMgrCompact6ToPex( added, added_len, added_f, added_f_len, + &n ); + for( i = 0; i < n; ++i ) + tr_peerMgrAddPex( msgs->session->peerMgr, tor->info.hash, + TR_PEER_FROM_PEX, pex + i ); + tr_free( pex ); + } + } if( loaded ) @@ -2005,17 +2028,21 @@ pexElementCb( void * vpex, diffs->elements[diffs->elementCount++] = *pex; } -/* TODO: ipv6 pex */ static void sendPex( tr_peermsgs * msgs ) { if( msgs->peerSupportsPex && tr_torrentAllowsPex( msgs->torrent ) ) { PexDiffs diffs; + PexDiffs diffs6; tr_pex * newPex = NULL; + tr_pex * newPex6 = NULL; const int newCount = tr_peerMgrGetPeers( msgs->session->peerMgr, msgs->torrent->info.hash, - &newPex ); + &newPex, TR_AF_INET ); + const int newCount6 = tr_peerMgrGetPeers( msgs->session->peerMgr, + msgs->torrent->info.hash, + &newPex6, TR_AF_INET6 ); /* build the diffs */ diffs.added = tr_new( tr_pex, newCount ); @@ -2028,14 +2055,28 @@ sendPex( tr_peermsgs * msgs ) newPex, newCount, tr_pexCompare, sizeof( tr_pex ), pexDroppedCb, pexAddedCb, pexElementCb, &diffs ); + diffs6.added = tr_new( tr_pex, newCount6 ); + diffs6.addedCount = 0; + diffs6.dropped = tr_new( tr_pex, msgs->pexCount6 ); + diffs6.droppedCount = 0; + diffs6.elements = tr_new( tr_pex, newCount6 + msgs->pexCount6 ); + diffs6.elementCount = 0; + tr_set_compare( msgs->pex6, msgs->pexCount6, + newPex6, newCount6, + tr_pexCompare, sizeof( tr_pex ), + pexDroppedCb, pexAddedCb, pexElementCb, &diffs6 ); dbgmsg( msgs, "pex: old peer count %d, new peer count %d, added %d, removed %d", - msgs->pexCount, newCount, diffs.addedCount, diffs.droppedCount ); + msgs->pexCount, newCount + newCount6, + diffs.addedCount + diffs6.addedCount, + diffs.droppedCount + diffs6.droppedCount ); - if( !diffs.addedCount && !diffs.droppedCount ) + if( !diffs.addedCount && !diffs.droppedCount && !diffs6.addedCount && + !diffs6.droppedCount ) { tr_free( diffs.elements ); + tr_free( diffs6.elements ); } else { @@ -2050,13 +2091,18 @@ sendPex( tr_peermsgs * msgs ) tr_free( msgs->pex ); msgs->pex = diffs.elements; msgs->pexCount = diffs.elementCount; + tr_free( msgs->pex6 ); + msgs->pex6 = diffs6.elements; + msgs->pexCount6 = diffs6.elementCount; /* build the pex payload */ - tr_bencInitDict( &val, 3 ); + tr_bencInitDict( &val, 3 ); /* ipv6 support: left as 3: + * speed vs. likelihood? */ /* "added" */ tmp = walk = tr_new( uint8_t, diffs.addedCount * 6 ); - for( i = 0; i < diffs.addedCount; ++i ) { + for( i = 0; i < diffs.addedCount; ++i ) + { memcpy( walk, &diffs.added[i].addr.addr, 4 ); walk += 4; memcpy( walk, &diffs.added[i].port, 2 ); walk += 2; } @@ -2074,13 +2120,48 @@ sendPex( tr_peermsgs * msgs ) /* "dropped" */ tmp = walk = tr_new( uint8_t, diffs.droppedCount * 6 ); - for( i = 0; i < diffs.droppedCount; ++i ) { + for( i = 0; i < diffs.droppedCount; ++i ) + { memcpy( walk, &diffs.dropped[i].addr.addr, 4 ); walk += 4; memcpy( walk, &diffs.dropped[i].port, 2 ); walk += 2; } assert( ( walk - tmp ) == diffs.droppedCount * 6 ); tr_bencDictAddRaw( &val, "dropped", tmp, walk - tmp ); tr_free( tmp ); + + /* "added6" */ + tmp = walk = tr_new( uint8_t, diffs6.addedCount * 18 ); + for( i = 0; i < diffs6.addedCount; ++i ) + { + memcpy( walk, &diffs6.added[i].addr.addr.addr6.s6_addr, 16 ); + walk += 16; + memcpy( walk, &diffs6.added[i].port, 2 ); + walk += 2; + } + assert( ( walk - tmp ) == diffs6.addedCount * 18 ); + tr_bencDictAddRaw( &val, "added6", tmp, walk - tmp ); + tr_free( tmp ); + + /* "added6.f" */ + tmp = walk = tr_new( uint8_t, diffs6.addedCount ); + for( i = 0; i < diffs6.addedCount; ++i ) + *walk++ = diffs6.added[i].flags; + assert( ( walk - tmp ) == diffs6.addedCount ); + tr_bencDictAddRaw( &val, "added6.f", tmp, walk - tmp ); + tr_free( tmp ); + + /* "dropped6" */ + tmp = walk = tr_new( uint8_t, diffs6.droppedCount * 18 ); + for( i = 0; i < diffs6.droppedCount; ++i ) + { + memcpy( walk, &diffs6.dropped[i].addr.addr.addr6.s6_addr, 16 ); + walk += 16; + memcpy( walk, &diffs6.dropped[i].port, 2 ); + walk += 2; + } + assert( ( walk - tmp ) == diffs6.droppedCount * 18); + tr_bencDictAddRaw( &val, "dropped6", tmp, walk - tmp ); + tr_free( tmp ); /* write the pex message */ benc = tr_bencSave( &val, &bencLen ); @@ -2099,6 +2180,9 @@ sendPex( tr_peermsgs * msgs ) tr_free( diffs.added ); tr_free( diffs.dropped ); tr_free( newPex ); + tr_free( diffs6.added ); + tr_free( diffs6.dropped ); + tr_free( newPex6 ); msgs->clientSentPexAt = time( NULL ); } @@ -2175,6 +2259,7 @@ tr_peerMsgsFree( tr_peermsgs* msgs ) evbuffer_free( msgs->incoming.block ); evbuffer_free( msgs->outMessages ); + tr_free( msgs->pex6 ); tr_free( msgs->pex ); memset( msgs, ~0, sizeof( tr_peermsgs ) ); diff --git a/libtransmission/port-forwarding.c b/libtransmission/port-forwarding.c index 8999f866f..75beaef49 100644 --- a/libtransmission/port-forwarding.c +++ b/libtransmission/port-forwarding.c @@ -38,9 +38,9 @@ struct tr_shared tr_port_forwarding natpmpStatus; tr_port_forwarding upnpStatus; - int bindPort; - int bindSocket; - int publicPort; + tr_bool shouldChange; + tr_socketList * bindSockets; + tr_port publicPort; tr_timer * pulseTimer; @@ -84,10 +84,10 @@ getNatStateStr( int state ) static void natPulse( tr_shared * s ) { - const int port = s->publicPort; - const int isEnabled = s->isEnabled && !s->isShuttingDown; - int oldStatus; - int newStatus; + const tr_port port = s->publicPort; + const int isEnabled = s->isEnabled && !s->isShuttingDown; + int oldStatus; + int newStatus; oldStatus = tr_sharedTraversalStatus( s ); s->natpmpStatus = tr_natpmpPulse( s->natpmp, port, isEnabled ); @@ -100,75 +100,103 @@ natPulse( tr_shared * s ) getNatStateStr( newStatus ) ); } +/* + * Callbacks for socket list + */ static void -incomingPeersPulse( tr_shared * s ) +closeCb( int * const socket, + tr_address * const addr, + void * const userData ) { - tr_bool allPaused; - tr_torrent * tor; - - if( s->bindSocket >= 0 && ( s->bindPort != s->publicPort ) ) + tr_shared * s = ( tr_shared * )userData; + if( *socket >= 0 ) { - tr_ninf( getKey( ), _( "Closing port %d" ), s->bindPort ); - tr_netClose( s->bindSocket ); - s->bindSocket = -1; + tr_ninf( getKey( ), _( "Closing port %d on %s" ), s->publicPort, + tr_ntop_non_ts( addr ) ); + tr_netClose( *socket ); } +} - if( ( s->publicPort > 0 ) && ( s->bindPort != s->publicPort ) ) +static void +acceptCb( int * const socket, + tr_address * const addr, + void * const userData ) +{ + tr_shared * s = ( tr_shared * )userData; + tr_address clientAddr; + tr_port clientPort; + int clientSocket; + clientSocket = tr_netAccept( s->session, *socket, &clientAddr, &clientPort ); + if( clientSocket > 0 ) { - int socket; - errno = 0; - socket = tr_netBindTCP( &tr_inaddr_any, s->publicPort ); - if( socket >= 0 ) - { - tr_ninf( getKey( ), - _( - "Opened port %d to listen for incoming peer connections" ), - s->publicPort ); - s->bindPort = s->publicPort; - s->bindSocket = socket; - listen( s->bindSocket, 5 ); - } - else - { - tr_nerr( getKey( ), - _( - "Couldn't open port %d to listen for incoming peer connections (errno %d - %s)" ), - s->publicPort, errno, tr_strerror( errno ) ); - s->bindPort = -1; - s->bindSocket = -1; - } + tr_deepLog( __FILE__, __LINE__, NULL, + "New INCOMING connection %d (%s)", + clientSocket, tr_peerIoAddrStr( &clientAddr, clientPort ) ); + + tr_peerMgrAddIncoming( s->session->peerMgr, &clientAddr, clientPort, + clientSocket ); } +} - /* see if any torrents aren't paused */ - allPaused = 1; - tor = NULL; - while(( tor = tr_torrentNext( s->session, tor ))) { - if( TR_STATUS_IS_ACTIVE( tr_torrentGetActivity( tor ))) { - allPaused = 0; - break; - } - } - - /* if we have any running torrents, check for new incoming peer connections */ - while( !allPaused ) +static void +bindCb( int * const socket, + tr_address * const addr, + void * const userData ) +{ + tr_shared * s = ( tr_shared * )userData; + *socket = tr_netBindTCP( addr, s->publicPort, FALSE ); + if( *socket >= 0 ) { - int socket; - tr_port port; - tr_address addr; - - if( s->bindSocket < 0 ) - break; + tr_ninf( getKey( ), + _( "Opened port %d on %s to listen for incoming peer connections" ), + s->publicPort, tr_ntop_non_ts( addr ) ); + listen( *socket, 10 ); + } + else + { + tr_nerr( getKey( ), + _( + "Couldn't open port %d on %s to listen for incoming peer connections (errno %d - %s)" ), + s->publicPort, tr_ntop_non_ts( addr ), errno, tr_strerror( errno ) ); + } +} - socket = tr_netAccept( s->session, s->bindSocket, &addr, &port ); - if( socket < 0 ) +static void +incomingPeersPulse( tr_shared * s ) +{ + int allPaused; + tr_torrent * tor; + + if( s->shouldChange ) + { + tr_socketListForEach( s->bindSockets, &closeCb, s ); + s->shouldChange = FALSE; + if( s->publicPort > 0 ) + tr_socketListForEach( s->bindSockets, &bindCb, s ); + } + + /* see if any torrents aren't paused */ + allPaused = 1; + tor = NULL; + while( ( tor = tr_torrentNext( s->session, tor ) ) ) + { + if( TR_STATUS_IS_ACTIVE( tr_torrentGetActivity( tor ) ) ) + { + allPaused = 0; break; - - tr_deepLog( __FILE__, __LINE__, NULL, - "New INCOMING connection %d (%s)", - socket, tr_peerIoAddrStr( &addr, port ) ); - - tr_peerMgrAddIncoming( s->session->peerMgr, &addr, port, socket ); + } } + + /* if we have any running torrents, check for new incoming peer connections */ + /* (jhujhiti): + * This has been changed from a loop that will end when the listener queue + * is exhausted to one that will only check for one connection at a time. + * I think it unlikely that we get many more than one connection in the + * time between pulses (currently one second). However, just to be safe, + * I have increased the length of the listener queue from 5 to 10 + * (see acceptCb() above). */ + if( !allPaused ) + tr_socketListForEach( s->bindSockets, &acceptCb, s ); } static int @@ -187,7 +215,7 @@ sharedPulse( void * vshared ) { tr_ninf( getKey( ), _( "Stopped" ) ); tr_timerFree( &shared->pulseTimer ); - tr_netClose( shared->bindSocket ); + tr_socketListForEach( shared->bindSockets, &closeCb, shared ); tr_natpmpClose( shared->natpmp ); tr_upnpClose( shared->upnp ); shared->session->shared = NULL; @@ -202,6 +230,34 @@ sharedPulse( void * vshared ) **** ***/ +static tr_socketList * +setupBindSockets( tr_port port ) +{ + /* Do we care if an address is in use? Probably not, since it will be + * caught later. This will only set up the list of sockets to bind. */ + int s4, s6; + tr_socketList * socks = NULL; + s6 = tr_netBindTCP( &tr_in6addr_any, port, TRUE ); + if( s6 >= 0 || -s6 != EAFNOSUPPORT ) /* we support ipv6 */ + { + socks = tr_socketListNew( &tr_in6addr_any ); + listen( s6, 1 ); + } + s4 = tr_netBindTCP( &tr_inaddr_any, port, TRUE ); + if( s4 >= 0 ) /* we bound *with* the ipv6 socket bound (need both) + * or only have ipv4 */ + { + if( socks ) + tr_socketListAppend( socks, &tr_inaddr_any ); + else + socks = tr_socketListNew( &tr_inaddr_any ); + tr_netClose( s4 ); + } + if( s6 >= 0 ) + tr_netClose( s6 ); + return socks; +} + tr_shared * tr_sharedInit( tr_session * session, tr_bool isEnabled, @@ -211,8 +267,9 @@ tr_sharedInit( tr_session * session, s->session = session; s->publicPort = publicPort; - s->bindPort = -1; - s->bindSocket = -1; + s->shouldChange = TRUE; + s->bindSockets = setupBindSockets( publicPort ); + s->shouldChange = TRUE; s->natpmp = tr_natpmpInit( ); s->upnp = tr_upnpInit( ); s->pulseTimer = tr_timerNew( session, sharedPulse, s, 1000 ); @@ -234,7 +291,8 @@ tr_sharedSetPort( tr_shared * s, tr_port port ) { tr_torrent * tor = NULL; - s->publicPort = port; + s->publicPort = port; + s->shouldChange = TRUE; while( ( tor = tr_torrentNext( s->session, tor ) ) ) tr_torrentChangeMyPort( tor ); diff --git a/libtransmission/resume.c b/libtransmission/resume.c index 89f43189e..83fb1d65c 100644 --- a/libtransmission/resume.c +++ b/libtransmission/resume.c @@ -34,6 +34,7 @@ #define KEY_MAX_PEERS "max-peers" #define KEY_PAUSED "paused" #define KEY_PEERS "peers" +#define KEY_PEERS6 "peers6" #define KEY_PRIORITY "priority" #define KEY_PROGRESS "progress" #define KEY_SPEEDLIMIT "speed-limit" @@ -61,17 +62,22 @@ getResumeFilename( const tr_torrent * tor ) **** ***/ -/* TODO: resume peers6 */ static void savePeers( tr_benc * dict, const tr_torrent * tor ) { - tr_pex * pex = NULL; - const int count = tr_peerMgrGetPeers( tor->session->peerMgr, - tor->info.hash, &pex ); + tr_pex * pex = NULL; + int count = tr_peerMgrGetPeers( tor->session->peerMgr, + tor->info.hash, &pex, TR_AF_INET ); if( count > 0 ) tr_bencDictAddRaw( dict, KEY_PEERS, pex, sizeof( tr_pex ) * count ); + + count = tr_peerMgrGetPeers( tor->session->peerMgr, tor->info.hash, &pex, + TR_AF_INET6 ); + if( count > 0 ) + tr_bencDictAddRaw( dict, KEY_PEERS6, pex, sizeof( tr_pex ) * count ); + tr_free( pex ); } @@ -94,7 +100,22 @@ loadPeers( tr_benc * dict, tr_peerMgrAddPex( tor->session->peerMgr, tor->info.hash, TR_PEER_FROM_CACHE, &pex ); } - tr_tordbg( tor, "Loaded %d peers from resume file", count ); + tr_tordbg( tor, "Loaded %d IPv4 peers from resume file", count ); + ret = TR_FR_PEERS; + } + + if( tr_bencDictFindRaw( dict, KEY_PEERS6, &str, &len ) ) + { + int i; + const int count = len / sizeof( tr_pex ); + for( i = 0; i < count; ++i ) + { + tr_pex pex; + memcpy( &pex, str + ( i * sizeof( tr_pex ) ), sizeof( tr_pex ) ); + tr_peerMgrAddPex( tor->session->peerMgr, + tor->info.hash, TR_PEER_FROM_CACHE, &pex ); + } + tr_tordbg( tor, "Loaded %d IPv6 peers from resume file", count ); ret = TR_FR_PEERS; } diff --git a/libtransmission/torrent.c b/libtransmission/torrent.c index b3bd7e490..2ec6b823c 100644 --- a/libtransmission/torrent.c +++ b/libtransmission/torrent.c @@ -222,10 +222,9 @@ onTrackerResponse( void * tracker UNUSED, case TR_TRACKER_PEERS: { size_t i, n; - tr_pex * pex = tr_peerMgrCompactToPex( event->compact, - event->compactLen, - NULL, 0, &n ); - if( event->allAreSeeds ) + tr_pex * pex = tr_peerMgrArrayToPex( event->compact, + event->compactLen, &n ); + if( event->allAreSeeds ) tr_tordbg( tor, "Got %d seeds from tracker", (int)n ); else tr_torinf( tor, _( "Got %d peers from tracker" ), (int)n ); diff --git a/libtransmission/tracker.c b/libtransmission/tracker.c index 832b3a3a4..32989173e 100644 --- a/libtransmission/tracker.c +++ b/libtransmission/tracker.c @@ -240,6 +240,68 @@ publishNewPeers( tr_tracker * t, tr_publisherPublish( t->publisher, t, &event ); } +static void +publishNewPeersCompact( tr_tracker * t, + int allAreSeeds, + void * compact, + int compactLen ) +{ + int i; + uint8_t *array, *walk, *compactWalk; + const int peerCount = compactLen / 6; + const int arrayLen = peerCount * ( sizeof( tr_address ) + 2 ); + tr_address addr; + tr_port port; + + addr.type = TR_AF_INET; + memset( &addr.addr, 0x00, sizeof( addr.addr ) ); + array = tr_new( uint8_t, arrayLen ); + for ( i = 0, walk = array, compactWalk = compact ; i < peerCount ; i++ ) + { + memcpy( &addr.addr.addr4, compactWalk, 4 ); + memcpy( &port, compactWalk + 4, 2 ); + + memcpy( walk, &addr, sizeof( addr ) ); + memcpy( walk + sizeof( addr ), &port, 2 ); + + walk += sizeof( tr_address ) + 2; + compactWalk += 6; + } + publishNewPeers( t, allAreSeeds, array, arrayLen ); + tr_free( array ); +} + +static void +publishNewPeersCompact6( tr_tracker * t, + int allAreSeeds, + void * compact, + int compactLen ) +{ + int i; + uint8_t *array, *walk, *compactWalk; + const int peerCount = compactLen / 18; + const int arrayLen = peerCount * ( sizeof( tr_address ) + 2 ); + tr_address addr; + tr_port port; + + addr.type = TR_AF_INET6; + memset( &addr.addr, 0x00, sizeof( addr.addr ) ); + array = tr_new( uint8_t, arrayLen ); + for ( i = 0, walk = array, compactWalk = compact ; i < peerCount ; i++ ) + { + memcpy( &addr.addr.addr6, compactWalk, 16 ); + memcpy( &port, compactWalk + 16, 2 ); + + memcpy( walk, &addr, sizeof( addr ) ); + memcpy( walk + sizeof( addr ), &port, 2 ); + + walk += sizeof( tr_address ) + 2; + compactWalk += 6; + } + publishNewPeers( t, allAreSeeds, array, arrayLen ); + tr_free( array ); +} + /*** **** ***/ @@ -276,50 +338,43 @@ updateAddresses( tr_tracker * t, return retry; } -/* Convert to compact form */ static uint8_t * parseOldPeers( tr_benc * bePeers, size_t * byteCount ) { - /* TODO: IPv6 wtf */ int i; - uint8_t * compact, *walk; + uint8_t * array, *walk; const int peerCount = bePeers->val.l.count; assert( bePeers->type == TYPE_LIST ); - compact = tr_new( uint8_t, peerCount * 6 ); + array = tr_new( uint8_t, peerCount * ( sizeof( tr_address ) + 2 ) ); - for( i = 0, walk = compact; i < peerCount; ++i ) + for( i = 0, walk = array; i < peerCount; ++i ) { - const char * s; - int64_t itmp; - tr_address addr; - tr_port port; - tr_benc * peer = &bePeers->val.l.vals[i]; - - if( tr_bencDictFindStr( peer, "ip", &s ) ) - { - if( tr_pton( s, &addr ) == NULL ) - continue; - if( addr.type != TR_AF_INET ) - continue; - } - - memcpy( walk, &addr.addr.addr4.s_addr, 4 ); - walk += 4; + const char * s; + int64_t itmp; + tr_address addr; + tr_port port; + tr_benc * peer = &bePeers->val.l.vals[i]; + if( tr_bencDictFindStr( peer, "ip", &s ) ) + { + if( tr_pton( s, &addr ) == NULL ) + continue; + } if( !tr_bencDictFindInt( peer, "port", &itmp ) || itmp < 0 || itmp > 0xffff ) continue; + memcpy( walk, &addr, sizeof( tr_address ) ); port = htons( itmp ); - memcpy( walk, &port, 2 ); - walk += 2; + memcpy( walk + sizeof( tr_address ), &port, 2 ); + walk += sizeof( tr_address ) + 2; } - *byteCount = peerCount * 6; - return compact; + *byteCount = peerCount * sizeof( tr_address ) + 2; + return array; } static void @@ -432,15 +487,26 @@ onTrackerResponse( tr_session * session, if( tmp->type == TYPE_STR ) /* "compact" extension */ { - publishNewPeers( t, allAreSeeds, tmp->val.s.s, - tmp->val.s.i ); + publishNewPeersCompact( t, allAreSeeds, tmp->val.s.s, + tmp->val.s.i ); } else if( tmp->type == TYPE_LIST ) /* original protocol */ { size_t byteCount = 0; - uint8_t * compact = parseOldPeers( tmp, &byteCount ); - publishNewPeers( t, allAreSeeds, compact, byteCount ); - tr_free( compact ); + uint8_t * array = parseOldPeers( tmp, &byteCount ); + publishNewPeers( t, allAreSeeds, array, byteCount ); + tr_free( array ); + } + } + + if( ( tmp = tr_bencDictFind( &benc, "peers6" ) ) ) + { + const int allAreSeeds = incomplete == 0; + + if( tmp->type == TYPE_STR ) /* "compact" extension */ + { + publishNewPeersCompact6( t, allAreSeeds, tmp->val.s.s, + tmp->val.s.i ); } } } -- 2.40.0