(trunk libT) #2502: announce own IPv6 address to peers

This commit is contained in:
Charles Kerr 2009-11-09 05:45:16 +00:00
parent 10126fbf07
commit 225384fcc5
4 changed files with 201 additions and 7 deletions

View File

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

View File

@ -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_ */

View File

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

View File

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