(trunk libT) #2502: announce own IPv6 address to peers
This commit is contained in:
parent
10126fbf07
commit
225384fcc5
|
@ -1,4 +1,5 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2005-2008 Transmission authors and contributors
|
||||
|
@ -462,3 +463,143 @@ tr_netClose( tr_session * session, int s )
|
|||
{
|
||||
tr_fdSocketClose( session, s );
|
||||
}
|
||||
|
||||
/*
|
||||
get_source_address(), get_name_source_address(), and
|
||||
global_unicast_address() were written by Juliusz Chroboczek,
|
||||
and are covered under the same license as dht.c.
|
||||
Please feel free to copy them into your software
|
||||
if it can help unbreaking the double-stack Internet. */
|
||||
|
||||
/* Get the source address used for a given destination address. Since
|
||||
there is no official interface to get this information, we create
|
||||
a connected UDP socket (connected UDP... hmm...) and check its source
|
||||
address. */
|
||||
static int
|
||||
get_source_address( const struct sockaddr * dst,
|
||||
socklen_t dst_len,
|
||||
struct sockaddr * src,
|
||||
socklen_t * src_len )
|
||||
{
|
||||
int s, rc, save;
|
||||
|
||||
s = socket(dst->sa_family, SOCK_DGRAM, 0);
|
||||
if(s < 0)
|
||||
goto fail;
|
||||
|
||||
/* Since it's a UDP socket, this doesn't actually send any packets. */
|
||||
rc = connect(s, dst, dst_len);
|
||||
if(rc < 0)
|
||||
goto fail;
|
||||
|
||||
rc = getsockname(s, src, src_len);
|
||||
if(rc < 0)
|
||||
goto fail;
|
||||
|
||||
EVUTIL_CLOSESOCKET( s );
|
||||
|
||||
return rc;
|
||||
|
||||
fail:
|
||||
save = errno;
|
||||
EVUTIL_CLOSESOCKET( s );
|
||||
errno = save;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Like above, but for a given DNS name. */
|
||||
static int
|
||||
get_name_source_address(int af, const char *name,
|
||||
struct sockaddr *src, socklen_t *src_len)
|
||||
{
|
||||
struct addrinfo hints, *info, *infop;
|
||||
int rc;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = af;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
rc = getaddrinfo(name, NULL, &hints, &info);
|
||||
if(rc != 0) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = -1;
|
||||
errno = ENOENT;
|
||||
infop = info;
|
||||
while(infop) {
|
||||
if(infop->ai_addr->sa_family == af) {
|
||||
rc = get_source_address(infop->ai_addr, infop->ai_addrlen,
|
||||
src, src_len);
|
||||
if(rc >= 0)
|
||||
break;
|
||||
}
|
||||
infop = infop->ai_next;
|
||||
}
|
||||
|
||||
freeaddrinfo(info);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* We all hate NATs. */
|
||||
static int
|
||||
global_unicast_address(struct sockaddr *sa)
|
||||
{
|
||||
if(sa->sa_family == AF_INET) {
|
||||
const unsigned char *a =
|
||||
(unsigned char*)&((struct sockaddr_in*)sa)->sin_addr;
|
||||
if(a[0] == 0 || a[0] == 127 || a[0] >= 224 ||
|
||||
a[0] == 10 || (a[0] == 172 && a[1] >= 16 && a[1] <= 31) ||
|
||||
(a[0] == 192 && a[1] == 168))
|
||||
return 0;
|
||||
return 1;
|
||||
} else if(sa->sa_family == AF_INET6) {
|
||||
const unsigned char *a =
|
||||
(unsigned char*)&((struct sockaddr_in6*)sa)->sin6_addr;
|
||||
/* 2000::/3 */
|
||||
return (a[0] & 0xE0) == 0x20;
|
||||
} else {
|
||||
errno = EAFNOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
tr_globalAddress( int af, void *addr, int *addr_len )
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t ss_len = sizeof(ss);
|
||||
int rc;
|
||||
|
||||
/* This should be a name with both IPv4 and IPv6 addresses. */
|
||||
rc = get_name_source_address( af, "www.transmissionbt.com",
|
||||
(struct sockaddr*)&ss, &ss_len );
|
||||
/* In case Charles removes IPv6 from his website. */
|
||||
if( rc < 0 )
|
||||
rc = get_name_source_address( af, "www.ietf.org",
|
||||
(struct sockaddr*)&ss, &ss_len );
|
||||
|
||||
if( rc < 0 )
|
||||
return -1;
|
||||
|
||||
if( !global_unicast_address( (struct sockaddr*)&ss) )
|
||||
return -1;
|
||||
|
||||
switch(af) {
|
||||
case AF_INET:
|
||||
if(*addr_len < 4)
|
||||
return -1;
|
||||
memcpy(addr, &((struct sockaddr_in*)&ss)->sin_addr, 4);
|
||||
*addr_len = 4;
|
||||
return 1;
|
||||
case AF_INET6:
|
||||
if(*addr_len < 16)
|
||||
return -1;
|
||||
memcpy(addr, &((struct sockaddr_in6*)&ss)->sin6_addr, 16);
|
||||
*addr_len = 16;
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,4 +120,6 @@ void tr_netCloseSocket( int fd );
|
|||
|
||||
void tr_netInit( void );
|
||||
|
||||
int tr_globalAddress(int af, void *addr, int *addr_len);
|
||||
|
||||
#endif /* _TR_NET_H_ */
|
||||
|
|
|
@ -807,6 +807,28 @@ tr_peerMsgsCancel( tr_peermsgs * msgs, tr_block_index_t block )
|
|||
***
|
||||
**/
|
||||
|
||||
/* Return our global IPv6 address, with caching. */
|
||||
|
||||
static const unsigned char *
|
||||
globalIPv6( void )
|
||||
{
|
||||
static unsigned char ipv6[16];
|
||||
static time_t last_time = 0;
|
||||
static int have_ipv6 = 0;
|
||||
const time_t now = time( NULL );
|
||||
|
||||
/* Re-check every half hour */
|
||||
if( last_time < now - 1800 )
|
||||
{
|
||||
int addrlen = 16;
|
||||
const int rc = tr_globalAddress( AF_INET6, ipv6, &addrlen );
|
||||
have_ipv6 = ( rc >= 0 ) && ( addrlen == 16 );
|
||||
last_time = now;
|
||||
}
|
||||
|
||||
return have_ipv6 ? ipv6 : NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
sendLtepHandshake( tr_peermsgs * msgs )
|
||||
{
|
||||
|
@ -815,6 +837,7 @@ sendLtepHandshake( tr_peermsgs * msgs )
|
|||
int len;
|
||||
int pex;
|
||||
struct evbuffer * out = msgs->outMessages;
|
||||
const unsigned char * ipv6 = globalIPv6();
|
||||
|
||||
if( msgs->clientSentLtepHandshake )
|
||||
return;
|
||||
|
@ -830,8 +853,10 @@ sendLtepHandshake( tr_peermsgs * msgs )
|
|||
else
|
||||
pex = 1;
|
||||
|
||||
tr_bencInitDict( &val, 5 );
|
||||
tr_bencInitDict( &val, 7 );
|
||||
tr_bencDictAddInt( &val, "e", getSession(msgs)->encryptionMode != TR_CLEAR_PREFERRED );
|
||||
if( ipv6 )
|
||||
tr_bencDictAddRaw( &val, "ipv6", ipv6, 16 );
|
||||
tr_bencDictAddInt( &val, "p", tr_sessionGetPeerPort( getSession(msgs) ) );
|
||||
tr_bencDictAddInt( &val, "reqq", REQQ );
|
||||
tr_bencDictAddInt( &val, "upload_only", tr_torrentIsSeed( msgs->torrent ) );
|
||||
|
@ -861,6 +886,11 @@ parseLtepHandshake( tr_peermsgs * msgs,
|
|||
int64_t i;
|
||||
tr_benc val, * sub;
|
||||
uint8_t * tmp = tr_new( uint8_t, len );
|
||||
const uint8_t *addr;
|
||||
size_t addr_len;
|
||||
tr_pex pex;
|
||||
|
||||
memset( &pex, 0, sizeof( tr_pex ) );
|
||||
|
||||
tr_peerIoReadBytes( msgs->peer->io, inbuf, tmp, len );
|
||||
msgs->peerSentLtepHandshake = 1;
|
||||
|
@ -875,9 +905,12 @@ parseLtepHandshake( tr_peermsgs * msgs,
|
|||
dbgmsg( msgs, "here is the handshake: [%*.*s]", len, len, tmp );
|
||||
|
||||
/* does the peer prefer encrypted connections? */
|
||||
if( tr_bencDictFindInt( &val, "e", &i ) )
|
||||
if( tr_bencDictFindInt( &val, "e", &i ) ) {
|
||||
msgs->peer->encryption_preference = i ? ENCRYPTION_PREFERENCE_YES
|
||||
: ENCRYPTION_PREFERENCE_NO;
|
||||
if( i )
|
||||
pex.flags |= ADDED_F_ENCRYPTION_FLAG;
|
||||
}
|
||||
|
||||
/* check supported messages for utorrent pex */
|
||||
msgs->peerSupportsPex = 0;
|
||||
|
@ -890,15 +923,31 @@ parseLtepHandshake( tr_peermsgs * msgs,
|
|||
}
|
||||
|
||||
/* look for upload_only (BEP 21) */
|
||||
if( tr_bencDictFindInt( &val, "upload_only", &i ) )
|
||||
if( tr_bencDictFindInt( &val, "upload_only", &i ) ) {
|
||||
fireUploadOnly( msgs, i!=0 );
|
||||
if( i )
|
||||
pex.flags |= ADDED_F_SEED_FLAG;
|
||||
}
|
||||
|
||||
/* get peer's listening port */
|
||||
if( tr_bencDictFindInt( &val, "p", &i ) ) {
|
||||
fireClientGotPort( msgs, (tr_port)i );
|
||||
pex.port = htons( (uint16_t)i );
|
||||
dbgmsg( msgs, "peer's port is now %d", (int)i );
|
||||
}
|
||||
|
||||
if( tr_bencDictFindRaw( &val, "ipv4", &addr, &addr_len) && addr_len == 4 ) {
|
||||
pex.addr.type = TR_AF_INET;
|
||||
memcpy( &pex.addr.addr.addr4, addr, 4 );
|
||||
tr_peerMgrAddPex( msgs->torrent, TR_PEER_FROM_ALT, &pex );
|
||||
}
|
||||
|
||||
if( tr_bencDictFindRaw( &val, "ipv6", &addr, &addr_len) && addr_len == 16 ) {
|
||||
pex.addr.type = TR_AF_INET6;
|
||||
memcpy( &pex.addr.addr.addr6, addr, 16 );
|
||||
tr_peerMgrAddPex( msgs->torrent, TR_PEER_FROM_ALT, &pex );
|
||||
}
|
||||
|
||||
/* get peer's maximum request queue size */
|
||||
if( tr_bencDictFindInt( &val, "reqq", &i ) )
|
||||
msgs->reqq = i;
|
||||
|
@ -1885,10 +1934,11 @@ sendPex( tr_peermsgs * msgs )
|
|||
pexDroppedCb, pexAddedCb, pexElementCb, &diffs6 );
|
||||
dbgmsg(
|
||||
msgs,
|
||||
"pex: old peer count %d, new peer count %d, added %d, removed %d",
|
||||
msgs->pexCount, newCount + newCount6,
|
||||
diffs.addedCount + diffs6.addedCount,
|
||||
diffs.droppedCount + diffs6.droppedCount );
|
||||
"pex: old peer count %d+%d, new peer count %d+%d, "
|
||||
"added %d+%d, removed %d+%d",
|
||||
msgs->pexCount, msgs->pexCount6, newCount, newCount6,
|
||||
diffs.addedCount, diffs6.addedCount,
|
||||
diffs.droppedCount, diffs6.droppedCount );
|
||||
|
||||
if( !diffs.addedCount && !diffs.droppedCount && !diffs6.addedCount &&
|
||||
!diffs6.droppedCount )
|
||||
|
|
|
@ -1571,6 +1571,7 @@ enum
|
|||
TR_PEER_FROM_DHT = 2, /* peers learnt from the DHT */
|
||||
TR_PEER_FROM_CACHE = 3, /* peers read from the peer cache */
|
||||
TR_PEER_FROM_PEX = 4, /* peers discovered via PEX */
|
||||
TR_PEER_FROM_ALT = 5, /* alternate peer address */
|
||||
TR_PEER_FROM__MAX
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue