(trunk libT) add ipv6 support by jhujhiti. I think this is the largest user-contributed patch we've ever used... thanks jhujhiti :)

This commit is contained in:
Charles Kerr 2008-12-15 00:17:08 +00:00
parent da6b7ee137
commit 54be88dec3
11 changed files with 671 additions and 232 deletions

View File

@ -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;

View File

@ -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,

View File

@ -26,6 +26,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
@ -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;
}

View File

@ -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,

View File

@ -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; i<peerCount; ++i, ++walk )
{
const tr_peer * peer = peers[i];
const struct peer_atom * atom = getExistingAtom( t, &peer->addr );
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;

View File

@ -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 );

View File

@ -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; i<n; ++i )
tr_peerMgrAddPex( msgs->session->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 ) );

View File

@ -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
closeCb( int * const socket,
tr_address * const addr,
void * const userData )
{
tr_shared * s = ( tr_shared * )userData;
if( *socket >= 0 )
{
tr_ninf( getKey( ), _( "Closing port %d on %s" ), s->publicPort,
tr_ntop_non_ts( addr ) );
tr_netClose( *socket );
}
}
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 )
{
tr_deepLog( __FILE__, __LINE__, NULL,
"New INCOMING connection %d (%s)",
clientSocket, tr_peerIoAddrStr( &clientAddr, clientPort ) );
tr_peerMgrAddIncoming( s->session->peerMgr, &clientAddr, clientPort,
clientSocket );
}
}
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 )
{
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 ) );
}
}
static void
incomingPeersPulse( tr_shared * s )
{
tr_bool allPaused;
int allPaused;
tr_torrent * tor;
if( s->bindSocket >= 0 && ( s->bindPort != s->publicPort ) )
if( s->shouldChange )
{
tr_ninf( getKey( ), _( "Closing port %d" ), s->bindPort );
tr_netClose( s->bindSocket );
s->bindSocket = -1;
tr_socketListForEach( s->bindSockets, &closeCb, s );
s->shouldChange = FALSE;
if( s->publicPort > 0 )
tr_socketListForEach( s->bindSockets, &bindCb, s );
}
if( ( s->publicPort > 0 ) && ( s->bindPort != s->publicPort ) )
/* see if any torrents aren't paused */
allPaused = 1;
tor = NULL;
while( ( tor = tr_torrentNext( s->session, tor ) ) )
{
int socket;
errno = 0;
socket = tr_netBindTCP( &tr_inaddr_any, s->publicPort );
if( socket >= 0 )
if( TR_STATUS_IS_ACTIVE( tr_torrentGetActivity( tor ) ) )
{
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;
allPaused = 0;
break;
}
}
/* 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 )
{
int socket;
tr_port port;
tr_address addr;
if( s->bindSocket < 0 )
break;
socket = tr_netAccept( s->session, s->bindSocket, &addr, &port );
if( socket < 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 );

View File

@ -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;
}

View File

@ -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 );

View File

@ -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 );
}
}
}