(trunk) #7: DHT support. This is a work in progress... no gui/rpc support yet

This commit is contained in:
Charles Kerr 2009-05-19 18:38:26 +00:00
parent f4d30c07ac
commit 3d7cc36424
29 changed files with 3367 additions and 10 deletions

View File

@ -7,6 +7,7 @@ Lead Developers <dev@transmissionbt.com>
Project Contributors
Tomas Carnecky (Profiling, patches, and detection of sneaky bugs)
Juliusz Chroboczek (DHT)
John Clay (Website maintenance and troubleshooting)
Rashid Eissing (Mac OS X Transfers preferences icon)
Hugo van Heuven, madebysofa (Main icon design)

View File

@ -20,6 +20,7 @@ transmissioncli_LDADD = \
$(top_builddir)/third-party/libevent/libevent.la \
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
$(top_builddir)/third-party/dht/libdht.a \
$(INTLLIBS) \
$(LIBCURL_LIBS) \
$(ZLIB_LIBS) \

View File

@ -263,7 +263,7 @@ fi
use_nls=no
if test "x$enable_nls" = "xyes" ; then
use_nls=yes
IT_PROG_INTLTOOL([0.40.0],[no-xml])
IT_PROG_INTLTOOL([0.35],[no-xml])
AC_CHECK_HEADERS([libintl.h])
GETTEXT_PACKAGE=transmission
AC_SUBST(GETTEXT_PACKAGE)
@ -355,6 +355,7 @@ AC_CONFIG_FILES([Makefile
third-party/Makefile
third-party/miniupnp/Makefile
third-party/libnatpmp/Makefile
third-party/dht/Makefile
macosx/Makefile
gtk/Makefile
gtk/icons/Makefile

View File

@ -22,6 +22,7 @@ LDADD = \
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
$(top_builddir)/third-party/libevent/libevent.la \
$(top_builddir)/third-party/dht/libdht.a \
$(INTLLIBS) \
$(LIBCURL_LIBS) \
$(ZLIB_LIBS) \

View File

@ -103,6 +103,7 @@ transmission_LDADD = \
$(top_builddir)/third-party/libevent/libevent.la \
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
$(top_builddir)/third-party/dht/libdht.a \
$(GTK_LIBS) \
$(GIO_LIBS) \
$(LIBNOTIFY_LIBS) \

View File

@ -941,7 +941,7 @@ refreshInfo( struct DetailsImpl * di, tr_torrent ** torrents, int n )
if( i!=n )
str = mixed;
else if( baseline )
str = _( "Private to this tracker -- PEX disabled" );
str = _( "Private to this tracker -- DHT and PEX disabled" );
else
str = _( "Public torrent" );
}
@ -1503,6 +1503,7 @@ onPeerViewQueryTooltip( GtkWidget * widget,
case '?': s = _( "We unchoked this peer, but they're not interested" ); break;
case 'E': s = _( "Encrypted connection" ); break;
case 'X': s = _( "Peer was discovered through Peer Exchange (PEX)" ); break;
case 'H': s = _( "Peer was discovered through DHT" ); break;
case 'I': s = _( "Peer is an incoming connection" ); break;
}
if( s )

View File

@ -49,6 +49,7 @@ libtransmission_a_SOURCES = \
torrent-ctor.c \
tr-getopt.c \
tracker.c \
tr-dht.c \
trevent.c \
upnp.c \
utils.c \
@ -96,6 +97,7 @@ noinst_HEADERS = \
tracker.h \
tr-getopt.h \
transmission.h \
tr-dht.h \
trevent.h \
upnp.h \
utils.h \
@ -124,6 +126,7 @@ apps_ldadd = \
$(top_builddir)/third-party/miniupnp/libminiupnp.a \
$(top_builddir)/third-party/libnatpmp/libnatpmp.a \
$(top_builddir)/third-party/libevent/libevent.la \
$(top_builddir)/third-party/dht/libdht.a \
$(INTLLIBS) \
$(LIBCURL_LIBS) \
$(OPENSSL_LIBS) \

View File

@ -28,6 +28,7 @@
#include "peer-io.h"
#include "peer-mgr.h"
#include "torrent.h"
#include "tr-dht.h"
#include "trevent.h"
#include "utils.h"
@ -35,6 +36,8 @@
#define ENABLE_LTEP * /
/* fast extensions */
#define ENABLE_FAST * /
/* DHT */
#define ENABLE_DHT * /
/***
****
@ -82,6 +85,14 @@ enum
#define HANDSHAKE_SET_FASTEXT( bits ) ( (void)0 )
#endif
#ifdef ENABLE_DHT
#define HANDSHAKE_HAS_DHT( bits ) ( ( ( bits )[7] & 0x01 ) ? 1 : 0 )
#define HANDSHAKE_SET_DHT( bits ) ( ( bits )[7] |= 0x01 )
#else
#define HANDSHAKE_HAS_DHT( bits ) ( 0 )
#define HANDSHAKE_SET_DHT( bits ) ( (void)0 )
#endif
/* http://www.azureuswiki.com/index.php/Extension_negotiation_protocol
these macros are to be used if both extended messaging and the
azureus protocol is supported, they indicate which protocol is preferred */
@ -219,6 +230,12 @@ buildHandshakeMessage( tr_handshake * handshake,
HANDSHAKE_SET_LTEP( walk );
HANDSHAKE_SET_FASTEXT( walk );
/* Note that this doesn't depend on whether the torrent is private. We
don't accept DHT peers for a private torrent, but we participate in
the DHT regardless. */
if(tr_dhtEnabled(handshake->session))
HANDSHAKE_SET_DHT( walk );
walk += HANDSHAKE_FLAGS_LEN;
memcpy( walk, torrentHash, SHA_DIGEST_LENGTH );
walk += SHA_DIGEST_LENGTH;
@ -303,6 +320,10 @@ parseHandshake( tr_handshake * handshake,
tr_peerIoEnableFEXT( handshake->io, HANDSHAKE_HAS_FASTEXT( reserved ) );
/* This doesn't depend on whether the torrent is private. */
if( tor->session->isDHTEnabled )
tr_peerIoEnableDHT( handshake->io, HANDSHAKE_HAS_DHT( reserved ) );
return HANDSHAKE_OK;
}

View File

@ -617,6 +617,16 @@ tr_peerIoEnableLTEP( tr_peerIo * io,
io->extendedProtocolSupported = flag;
}
void
tr_peerIoEnableDHT( tr_peerIo * io, tr_bool flag )
{
assert( tr_isPeerIo( io ) );
assert( tr_isBool( flag ) );
dbgmsg( io, "setting DHT support flag to %d", (flag!=0) );
io->dhtSupported = flag;
}
/**
***
**/

View File

@ -63,6 +63,7 @@ typedef struct tr_peerIo
tr_bool peerIdIsSet;
tr_bool extendedProtocolSupported;
tr_bool fastExtensionSupported;
tr_bool dhtSupported;
/* we create the socket in a nonblocking way, so this flag is initially
* false and then set to true when libevent says that the socket is ready
@ -159,6 +160,15 @@ static TR_INLINE tr_bool tr_peerIoSupportsFEXT( const tr_peerIo * io )
return io->fastExtensionSupported;
}
void tr_peerIoEnableDHT( tr_peerIo * io, tr_bool flag );
static TR_INLINE tr_bool tr_peerIoSupportsDHT( const tr_peerIo * io )
{
assert( tr_isPeerIo( io ) );
return io->dhtSupported;
}
/**
***
**/

View File

@ -1873,6 +1873,7 @@ tr_peerMgrPeerStats( const tr_torrent * tor,
if( !stat->clientIsChoked && !stat->clientIsInterested ) *pch++ = 'K';
if( !stat->peerIsChoked && !stat->peerIsInterested ) *pch++ = '?';
if( stat->isEncrypted ) *pch++ = 'E';
if( stat->from == TR_PEER_FROM_DHT ) *pch++ = 'H';
if( stat->from == TR_PEER_FROM_PEX ) *pch++ = 'X';
if( stat->isIncoming ) *pch++ = 'I';
*pch = '\0';
@ -2163,8 +2164,8 @@ compareCandidates( const void * va,
if( a->time != b->time )
return a->time < b->time ? -1 : 1;
/* all other things being equal, prefer peers whose
* information comes from a more reliable source */
/* In order to avoid fragmenting the swarm, peers from trackers and
* from the DHT should be preferred to peers from PEX. */
if( a->from != b->from )
return a->from < b->from ? -1 : 1;

View File

@ -36,6 +36,7 @@
#include "request-list.h"
#include "stats.h"
#include "torrent.h"
#include "tr-dht.h"
#include "trevent.h"
#include "utils.h"
#include "version.h"
@ -306,6 +307,18 @@ protocolSendCancel( tr_peermsgs * msgs,
pokeBatchPeriod( msgs, IMMEDIATE_PRIORITY_INTERVAL_SECS );
}
static void
protocolSendPort(tr_peermsgs *msgs, uint16_t port)
{
tr_peerIo * io = msgs->peer->io;
struct evbuffer * out = msgs->outMessages;
dbgmsg( msgs, "sending Port %u", port);
tr_peerIoWriteUint32( io, out, 3 );
tr_peerIoWriteUint8 ( io, out, BT_PORT );
tr_peerIoWriteUint16( io, out, port);
}
static void
protocolSendHave( tr_peermsgs * msgs,
uint32_t index )
@ -2123,6 +2136,9 @@ tr_peerMsgsNew( struct tr_torrent * torrent,
if( tr_peerIoSupportsLTEP( peer->io ) )
sendLtepHandshake( m );
if(tr_peerIoSupportsDHT(peer->io))
protocolSendPort(m, tr_dhtPort(torrent->session));
tellPeerWhatWeHave( m );
tr_peerIoSetIOFuncs( m->peer->io, canRead, didWrite, gotError, m );

View File

@ -45,6 +45,7 @@
#include "version.h"
#include "verify.h"
#include "web.h"
#include "tr-dht.h"
#define dbgmsg( ... ) \
do { \
@ -621,6 +622,8 @@ tr_sessionInitImpl( void * vdata )
found = tr_bencDictFindBool( &settings, TR_PREFS_KEY_PEX_ENABLED, &boolVal );
assert( found );
session->isPexEnabled = boolVal;
/* This really ought to be a separate preference. */
session->isDHTEnabled = boolVal;
found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_ENCRYPTION, &i );
assert( found );
@ -830,6 +833,9 @@ tr_sessionInitImpl( void * vdata )
metainfoLookupRescan( session );
session->isWaiting = FALSE;
dbgmsg( "returning session %p; session->tracker is %p", session, session->tracker );
if( session->isDHTEnabled )
tr_dhtInit(session);
}
/***
@ -1392,7 +1398,7 @@ compareTorrentByCur( const void * va, const void * vb )
}
static void
tr_closeAllConnections( void * vsession )
sessionCloseImpl( void * vsession )
{
tr_session * session = vsession;
tr_torrent * tor;
@ -1403,6 +1409,9 @@ tr_closeAllConnections( void * vsession )
free_incoming_peer_port( session );
if( session->isDHTEnabled )
tr_dhtUninit( session );
evtimer_del( session->altTimer );
tr_free( session->altTimer );
session->altTimer = NULL;
@ -1455,7 +1464,7 @@ tr_sessionClose( tr_session * session )
dbgmsg( "shutting down transmission session %p", session );
/* close the session */
tr_runInEventThread( session, tr_closeAllConnections, session );
tr_runInEventThread( session, sessionCloseImpl, session );
while( !session->isClosed && !deadlineReached( deadline ) )
{
dbgmsg(
@ -1579,6 +1588,14 @@ tr_sessionIsPexEnabled( const tr_session * session )
return session->isPexEnabled;
}
tr_bool
tr_sessionIsDHTEnabled( const tr_session * session )
{
assert( tr_isSession( session ) );
return session->isDHTEnabled;
}
/***
****
***/

View File

@ -61,6 +61,7 @@ struct tr_session
{
tr_bool isPortRandom;
tr_bool isPexEnabled;
tr_bool isDHTEnabled;
tr_bool isBlocklistEnabled;
tr_bool isProxyEnabled;
tr_bool isProxyAuthEnabled;

View File

@ -1271,20 +1271,23 @@ freeTorrent( tr_torrent * tor )
static void
checkAndStartImpl( void * vtor )
{
time_t now;
tr_torrent * tor = vtor;
assert( tr_isTorrent( tor ) );
tr_globalLock( tor->session );
now = time( NULL );
tor->isRunning = TRUE;
tor->needsSeedRatioCheck = TRUE;
*tor->errorString = '\0';
tr_torrentResetTransferStats( tor );
tor->completeness = tr_cpGetStatus( &tor->completion );
tr_torrentSaveResume( tor );
tor->startDate = tor->anyDate = time( NULL );
tor->startDate = tor->anyDate = now;
tr_trackerStart( tor->tracker );
tor->dhtAnnounceAt = now + tr_cryptoWeakRandInt( 20 );
tr_peerMgrStartTorrent( tor );
tr_globalUnlock( tor->session );

View File

@ -175,6 +175,9 @@ struct tr_torrent
struct tr_tracker * tracker;
struct tr_publisher_tag * trackerSubscription;
time_t dhtAnnounceAt;
tr_bool dhtAnnounceInProgress;
uint64_t downloadedCur;
uint64_t downloadedPrev;
uint64_t uploadedCur;
@ -288,6 +291,11 @@ static TR_INLINE tr_bool tr_torrentAllowsPex( const tr_torrent * tor )
return ( tor != NULL ) && tor->session->isPexEnabled && !tr_torrentIsPrivate( tor );
}
static TR_INLINE tr_bool tr_torrentAllowsDHT( const tr_torrent * tor )
{
return ( tor != NULL ) && tor->session->isDHTEnabled && !tr_torrentIsPrivate( tor );
}
static TR_INLINE tr_bool tr_torrentIsPieceChecked( const tr_torrent * tor, tr_piece_index_t i )
{
return tr_bitfieldHasFast( &tor->checkedPieces, i );

397
libtransmission/tr-dht.c Normal file
View File

@ -0,0 +1,397 @@
/*
Copyright (c) 2009 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <event.h>
#include <dht/dht.h>
#include "transmission.h"
#include "crypto.h"
#include "peer-mgr.h"
#include "platform.h"
#include "session.h"
#include "torrent.h"
#include "trevent.h"
#include "tr-dht.h"
#include "utils.h"
static int dht_socket;
static struct event dht_event;
static tr_port dht_port;
static unsigned char myid[20];
static tr_session *session = NULL;
static void event_callback(int s, short type, void *ignore);
struct bootstrap_closure {
tr_session *session;
uint8_t *nodes;
size_t len;
};
static void
dht_bootstrap(void *closure)
{
struct bootstrap_closure *cl = closure;
size_t i;
if(session != cl->session)
return;
for(i = 0; i < cl->len; i += 6)
{
struct timeval tv;
tr_port port;
struct tr_address addr;
int status;
memset(&addr, 0, sizeof(addr));
addr.type = TR_AF_INET;
memcpy(&addr.addr.addr4, &cl->nodes[i], 4);
memcpy(&port, &cl->nodes[i + 4], 2);
port = ntohs(port);
/* There's no race here -- if we uninit between the test and the
AddNode, the AddNode will be ignored. */
status = tr_dhtStatus(cl->session);
if(status == TR_DHT_STOPPED || status >= TR_DHT_FIREWALLED)
break;
tr_dhtAddNode(cl->session, &addr, port, 1);
tv.tv_sec = 2 + tr_cryptoWeakRandInt( 5 );
tv.tv_usec = tr_cryptoWeakRandInt( 1000000 );
select(0, NULL, NULL, NULL, &tv);
}
tr_free( cl->nodes );
tr_free( closure );
}
int
tr_dhtInit(tr_session *ss)
{
struct sockaddr_in sin;
struct timeval tv;
tr_benc benc;
int rc;
tr_bool have_id = FALSE;
char * dat_file;
uint8_t * nodes = NULL;
const uint8_t * raw;
size_t len;
if(session)
return -1;
dht_socket = socket(PF_INET, SOCK_DGRAM, 0);
if(dht_socket < 0)
return -1;
dht_port = tr_sessionGetPeerPort(ss);
if(dht_port <= 0)
return -1;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(dht_port);
rc = bind(dht_socket, (struct sockaddr*)&sin, sizeof(sin));
if(rc < 0)
goto fail;
#ifdef DEBUG_DHT
dht_debug = stdout;
#endif
dat_file = tr_buildPath( ss->configDir, "dht.dat", NULL );
rc = tr_bencLoadFile(dat_file, &benc);
tr_free( dat_file );
if(rc == 0) {
if(tr_bencDictFindRaw(&benc, "id", &raw, &len)) {
if(raw && len == 20) {
memcpy(myid, raw, len);
have_id = TRUE;
}
}
if(tr_bencDictFindRaw(&benc, "nodes", &raw, &len)) {
if(len % 6 == 2) {
/* This hack allows reading of uTorrent files, which I find
convenient. */
len -= 2;
}
nodes = tr_new( uint8_t, len );
memcpy( nodes, raw, len );
}
tr_bencFree(&benc);
}
if(!have_id) {
/* Note that you cannot just use your BT id -- DHT ids need to be
distributed uniformly, so it should either be the SHA-1 of
something, or truly random. */
tr_cryptoRandBuf( myid, 20 );
have_id = TRUE;
}
rc = dht_init(dht_socket, myid);
if(rc < 0)
goto fail;
session = ss;
if(nodes) {
struct bootstrap_closure * cl = tr_new( struct bootstrap_closure, 1 );
if( !cl )
tr_free( nodes );
else {
cl->session = session;
cl->nodes = nodes;
cl->len = len;
tr_threadNew( dht_bootstrap, cl );
}
}
tv.tv_sec = 0;
tv.tv_usec = tr_cryptoWeakRandInt( 1000000 );
event_set( &dht_event, dht_socket, EV_READ, event_callback, NULL );
event_add( &dht_event, &tv );
return 1;
fail:
{
const int save = errno;
close(dht_socket);
dht_socket = -1;
session = NULL;
errno = save;
}
return -1;
}
void
tr_dhtUninit(tr_session *ss)
{
if(session != ss)
return;
event_del(&dht_event);
/* Since we only save known good nodes, avoid erasing older data if we
don't know enough nodes. */
if(tr_dhtStatus(ss) >= TR_DHT_FIREWALLED) {
tr_benc benc;
struct sockaddr_in sins[300];
char compact[300 * 6];
char *dat_file;
int n, i, j;
n = dht_get_nodes(sins, 300);
j = 0;
for(i = 0; i < n; i++) {
memcpy(compact + j, &sins[i].sin_addr, 4);
memcpy(compact + j + 4, &sins[i].sin_port, 2);
j += 6;
}
tr_bencInitDict(&benc, 2);
tr_bencDictAddRaw(&benc, "id", myid, 20);
tr_bencDictAddRaw(&benc, "nodes", compact, j);
dat_file = tr_buildPath( ss->configDir, "dht.dat", NULL );
tr_bencSaveFile( dat_file, &benc );
tr_free( dat_file );
}
dht_uninit(dht_socket, 0);
session = NULL;
}
tr_bool
tr_dhtEnabled(tr_session *ss)
{
return (ss && session == ss);
}
static void
getstatus(void *closure)
{
sig_atomic_t *ret = (sig_atomic_t*)closure;
int good, dubious, incoming;
dht_nodes(&good, &dubious, NULL, &incoming);
if(good < 4 || good + dubious <= 8)
*ret = TR_DHT_BROKEN;
else if(good < 40)
*ret = TR_DHT_POOR;
else if(incoming < 8)
*ret = TR_DHT_FIREWALLED;
else
*ret = TR_DHT_GOOD;
}
int
tr_dhtStatus(tr_session *ss)
{
sig_atomic_t ret = -1;
if(!tr_dhtEnabled(ss))
return TR_DHT_STOPPED;
tr_runInEventThread(ss, getstatus, &ret);
while( ret < 0 )
tr_wait( 1 /* msec */ );
return ret;
}
tr_port
tr_dhtPort(tr_session *ss)
{
return tr_dhtEnabled( ss ) ? dht_port : 0;
}
int
tr_dhtAddNode(tr_session *ss, tr_address *address, tr_port port, tr_bool bootstrap)
{
struct sockaddr_in sin;
if(!tr_dhtEnabled(ss))
return 0;
if(address->type != TR_AF_INET)
return 0;
/* Since we don't want to abuse our bootstrap nodes, we don't ping them
if the DHT is in a good state. */
if(bootstrap) {
if(tr_dhtStatus(ss) >= TR_DHT_FIREWALLED)
return 0;
}
{
char buf[50];
inet_ntop(AF_INET, &address->addr.addr4, buf, 50);
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
memcpy(&sin.sin_addr, &address->addr.addr4, 4);
sin.sin_port = htons(port);
dht_ping_node(dht_socket, &sin);
return 1;
}
static void
callback(void *ignore, int event,
unsigned char *info_hash, void *data, size_t data_len)
{
(void)ignore; /* sigh */
if(event == DHT_EVENT_VALUES) {
tr_torrent *tor;
tr_pex *pex;
size_t i, n;
pex = tr_peerMgrCompactToPex(data, data_len, NULL, 0, &n);
tr_globalLock(session);
tor = tr_torrentFindFromHash(session, info_hash);
if(tor && tr_torrentAllowsDHT(tor)) {
for(i = 0; i < n; i++)
tr_peerMgrAddPex(tor, TR_PEER_FROM_DHT, pex + i);
}
tr_globalUnlock(session);
tr_free(pex);
} else if(event == DHT_EVENT_SEARCH_DONE) {
tr_torrent *tor;
tor = tr_torrentFindFromHash(session, info_hash);
if(tor)
tor->dhtAnnounceInProgress = 0;
}
}
int
tr_dhtAnnounce(tr_torrent *tor, tr_bool announce)
{
if(!tr_torrentAllowsDHT(tor))
return -1;
if(tr_dhtStatus(tor->session) < TR_DHT_POOR)
return 0;
dht_search(dht_socket, tor->info.hash,
announce ? tr_sessionGetPeerPort(session) : 0,
callback, NULL);
tor->dhtAnnounceInProgress = 1;
return 1;
}
static void
event_callback(int s, short type, void *ignore)
{
int rc;
time_t tosleep;
struct timeval now, tv;
(void)ignore;
gettimeofday(&now, NULL);
rc = dht_periodic(s, type == EV_READ, &tosleep, callback, NULL);
if(rc < 0) {
if(errno == EINTR) {
tosleep = 0;
} else {
perror("dht_periodic");
if(rc == EINVAL || rc == EFAULT)
abort();
tosleep = 1;
}
}
/* Being slightly late is fine, and has the added benefit of adding
some jitter. */
tv.tv_sec = tosleep;
tv.tv_usec = tr_cryptoWeakRandInt( 1000000 );
event_add(&dht_event, &tv);
}
void
dht_hash(void *hash_return, int hash_size,
const void *v1, int len1,
const void *v2, int len2,
const void *v3, int len3)
{
unsigned char sha1[20];
tr_sha1(sha1, v1, len1, v2, len2, v3, len3, NULL);
if(hash_size > 20) {
memset((char*)hash_return + 20, 0, hash_size - 20);
}
memcpy(hash_return, sha1, hash_size > 20 ? 20 : hash_size);
}

35
libtransmission/tr-dht.h Normal file
View File

@ -0,0 +1,35 @@
/*
Copyright (c) 2009 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#define TR_DHT_STOPPED 0
#define TR_DHT_BROKEN 1
#define TR_DHT_POOR 2
#define TR_DHT_FIREWALLED 3
#define TR_DHT_GOOD 4
int tr_dhtInit(tr_session *ss);
void tr_dhtUninit(tr_session *ss);
tr_bool tr_dhtEnabled(tr_session *ss);
int tr_dhtStatus(tr_session *ss);
tr_port tr_dhtPort(tr_session *ss);
int tr_dhtAddNode(tr_session *ss, tr_address *address, tr_port port, tr_bool bootstrap);
int tr_dhtAnnounce(tr_torrent *tor, tr_bool announce);

View File

@ -26,6 +26,7 @@
#include "resume.h"
#include "torrent.h"
#include "tracker.h"
#include "tr-dht.h"
#include "trevent.h"
#include "utils.h"
#include "web.h"
@ -977,6 +978,18 @@ trackerPulse( void * vsession )
t->manualAnnounceAllowedAt = TR_TRACKER_BUSY;
enqueueRequest( session, t, TR_REQ_REANNOUNCE );
}
if( tor->dhtAnnounceAt <= now ) {
int rc = 1;
if( tr_torrentAllowsDHT(tor) )
rc = tr_dhtAnnounce(tor, 1);
if(rc == 0)
/* The DHT is not ready yet. Try again soon. */
tor->dhtAnnounceAt = now + 5 + tr_cryptoWeakRandInt( 5 );
else
/* We should announce at least once every 30 minutes. */
tor->dhtAnnounceAt = now + 25 * 60 + tr_cryptoWeakRandInt( 3 * 60 );
}
}
if( th->runningCount )

View File

@ -530,6 +530,8 @@ void tr_sessionSetPexEnabled( tr_session * session,
tr_bool tr_sessionIsPexEnabled( const tr_session * session );
tr_bool tr_sessionIsDHTEnabled( const tr_session * session );
void tr_sessionSetLazyBitfieldEnabled( tr_session * session,
tr_bool enabled );
@ -1343,8 +1345,9 @@ enum
{
TR_PEER_FROM_INCOMING = 0, /* connections made to the listening port */
TR_PEER_FROM_TRACKER = 1, /* peers received from a tracker */
TR_PEER_FROM_CACHE = 2, /* peers read from the peer cache */
TR_PEER_FROM_PEX = 3, /* peers discovered via PEX */
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__MAX
};

View File

@ -1,7 +1,8 @@
SUBDIRS = \
libevent \
libnatpmp \
miniupnp
miniupnp \
dht
EXTRA_DIST = \
macosx-libevent-config.h

27
third-party/dht/CHANGES vendored Normal file
View File

@ -0,0 +1,27 @@
18 May 2009: dht-0.4
* Fixed the handling of tokens in announce_peer messages.
* Implemented backtracking during search when nodes turn out to be dead.
17 May 2009: dht-0.3
* Fixed a number of incorrectly formatted messages.
* Changed reply to find_peers to spread the load more uniformly.
* Fixed a bug that could cause premature splitting.
* Implemented rate limiting.
* Changed some time constants to be less chatty.
* When determining if a bucket is fresh enough, we now only take replies
into account.
* dht_get_nodes now returns nodes starting with our own bucket.
* Tweaked the memory allocation strategy for stored peers.
17 May 2009: dht-0.2
* Fixed a crash in dht_uninit.
* Added support for saving the list of known-good nodes.
* Changed the interface of dht_nodes to provide the number of nodes that
recently sent incoming requests.
13 May 2009: dht-0.1
* Initial public release.

19
third-party/dht/LICENCE vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2009 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

4
third-party/dht/Makefile.am vendored Normal file
View File

@ -0,0 +1,4 @@
noinst_LIBRARIES = libdht.a
libdht_a_SOURCES = dht.c
noinst_HEADERS = dht.h
extra_DIST = CHANGES dht-example.c LICENCE README

192
third-party/dht/README vendored Normal file
View File

@ -0,0 +1,192 @@
The files dht.c and dht.h implement the variant of the Kademlia Distributed
Hash Table (DHT) used in the Bittorrent network (``mainline'' variant).
The file dht-example.c is a stand-alone program that participates in the
DHT. Another example is a patch against Transmission, which you might or
might not be able to find somewhere.
The code is designed to work well in both event-driven and threaded code.
The caller, which is either an event-loop or a dedicated thread, must
periodically call the function dht_periodic. In addition, it must call
dht_periodic whenever any data has arrived from the network.
All functions return -1 in case of failure, in which case errno is set, or
a positive value in case of success.
Initialisation
**************
* dht_init
This must be called before using the library. You pass it a bound IPv4
datagram socket, and your node id, a 20-octet array that should be globally
unique.
Node ids must be well distributed, so you cannot just use your Bittorrent
id; you should either generate a truly random value (using plenty of
entropy), or at least take the SHA-1 of something. However, it is a good
idea to keep the id stable, so you may want to store it in stable storage
at client shutdown.
* dht_uninit
This may be called at the end of the session. If dofree is true, it frees
all the memory allocated for the DHT. If dofree is false, this function
currently does nothing.
Bootstrapping
*************
The DHT needs to be taught a small number of contacts to begin functioning.
You can hard-wire a small number of stable nodes in your application, but
this obviously fails to scale. You may save the list of known good nodes
at shutdown, and restore it at restart. You may also grab nodes from
torrent files (the nodes field), and you may exchange contacts with other
Bittorrent peers using the PORT extension.
* dht_ping
This is the main bootstrapping primitive. You pass it an address at which
you believe that a DHT node may be living, and a query will be sent. If
a node replies, and if there is space in the routing table, it will be
inserted.
* dht_insert_node
This is a softer bootstrapping method, which doesn't actually send
a query -- it only stores the node in the routing table for later use. It
is a good idea to use that when e.g. restoring your routing table from
disk.
Note that dht_insert_node requires that you supply a node id. If the id
turns out to be wrong, the DHT will eventually recover; still, inserting
massive amounts of incorrect information into your routing table is
certainly not a good idea.
An additionaly difficulty with dht_insert_node is that, for various
reasons, a Kademlia routing table cannot absorb nodes faster than a certain
rate. Dumping a large number of nodes into a table using dht_insert_node
will probably cause most of these nodes to be discarded straight away.
(The tolerable rate is difficult to estimate; it is probably on the order
of one node every few seconds per node already in the table divided by 8,
for some suitable value of 8.)
Doing some work
***************
* dht_periodic
This function should be called by your main loop periodically, and also
whenever data is available on the socket. The time after which
dht_periodic should be called if no data is available is returned in the
parameter tosleep. (You do not need to be particularly accurate; actually,
it is a good idea to be late by a random value.)
The parameter available indicates whether any data is available on the
socket. If it is 0, dht_periodic will not try to read data; if it is 1, it
will.
Dht_periodic also takes a callback, which will be called whenever something
interesting happens (see below).
* dht_search
This schedules a search for information about the info-hash specified in
id. If port is not 0, it specifies the TCP port on which the current peer
is litening; in that case, when the search is complete it will be announced
to the network. The port is in host order, beware if you got it from
a struct sockaddr_in.
In either case, data is passed to the callback function as soon as it is
available, possibly in multiple pieces. The callback function will
additionally be called when the search is complete.
Up to DHT_MAX_SEARCHES (20) searches can be in progress at a given time;
any more, and dht_search will return -1. If you specify a new search for
the same info hash as a search still in progress, the previous search is
combined with the new one -- you will only receive a completion indication
once.
Information queries
*******************
* dht_nodes
This returns the number of known good, dubious and cached nodes in our
routing table. This can be used to decide whether it's reasonable to start
a search; a search is likely to be successful as long as we have a few good
nodes; however, in order to avoid overloading your bootstrap nodes, you may
want to wait until good is at least 4 and good + doubtful is at least 30 or
so.
It also includes the number of nodes that recently send us an unsolicited
request; this can be used to determine if the UDP port used for the DHT is
firewalled.
If you want to display a single figure to the user, you should display good
+ doubtful, which is the total number of nodes in your routing table. Some
clients try to estimate the total number of nodes, but this doesn't make
much sense -- since the result is exponential in the number of nodes in the
routing table, small variations in the latter cause huge jumps in the
former.
* dht_get_nodes
This retrieves the list of known good nodes, starting with the nodes in our
own bucket. It is a good idea to save the list of known good nodes at
shutdown, and ping them at startup.
* dht_dump_tables
* dht_debug
These are debugging aids.
Functions provided by you
*************************
* The callback function
The callback function is called with 5 arguments. Closure is simply the
value that you passed to dht_periodic. Event is one of DHT_EVENT_VALUES,
which indicates that we have new values, or DHT_EVENT_SEARCH_DONE, which
indicates that a search has completed. In either case, info_hash is set to
the info-hash of the search.
In the case of DHT_EVENT_VALUES, data is a list of nodes in ``compact''
format -- 6 bytes per node, 4 for the IP address and 2 for the port. It's
length in bytes is in data_len.
* dht_hash
This should compute a reasonably strong cryptographic hash of the passed
values. It should map cleanly to your favourite crypto toolkit's MD5 or
SHA-1 function.
Final notes
***********
* NAT
Nothing works well across NATs, but Kademlia is somewhat less impacted than
many other protocols. The implementation takes care to distinguish between
unidirectional and bidirectional reachability, and NATed nodes will
eventually fall out from other nodes' routing tables.
While there is no periodic pinging in this implementation, maintaining
a full routing table requires slightly more than one packet exchange per
minute, even in a completely idle network; this should be sufficient to
make most full cone NATs happy.
* Missing functionality
Some of the code has had very little testing. If it breaks, you get to
keep both pieces.
There is currently no good way to save and restore your routing table.
IPv6 support is deliberately not included: designing a double-stack
distributed hash table raises some tricky issues, and doing it naively may
break connectivity for everyone.
Juliusz Chroboczek
<jch@pps.jussieu.fr>

329
third-party/dht/dht-example.c vendored Normal file
View File

@ -0,0 +1,329 @@
/* This example code was written by Juliusz Chroboczek.
You are free to cut'n'paste from it to your heart's content. */
/* For crypt */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/signal.h>
#include "dht.h"
#define MAX_BOOTSTRAP_NODES 20
static struct sockaddr_in bootstrap_nodes[MAX_BOOTSTRAP_NODES];
static int num_bootstrap_nodes = 0;
static volatile sig_atomic_t dumping = 0, searching = 0, exiting = 0;
static void
sigdump(int signo)
{
dumping = 1;
}
static void
sigtest(int signo)
{
searching = 1;
}
static void
sigexit(int signo)
{
exiting = 1;
}
static void
init_signals(void)
{
struct sigaction sa;
sigset_t ss;
sigemptyset(&ss);
sa.sa_handler = sigdump;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGUSR1, &sa, NULL);
sigemptyset(&ss);
sa.sa_handler = sigtest;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGUSR2, &sa, NULL);
sigemptyset(&ss);
sa.sa_handler = sigexit;
sa.sa_mask = ss;
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
}
const unsigned char hash[20] = {
0x54, 0x57, 0x87, 0x89, 0xdf, 0xc4, 0x23, 0xee, 0xf6, 0x03,
0x1f, 0x81, 0x94, 0xa9, 0x3a, 0x16, 0x98, 0x8b, 0x72, 0x7b
};
/* The call-back function is called by the DHT whenever something
interesting happens. Right now, it only happens when we get a new value or
when a search completes, but this may be extended in future versions. */
static void
callback(void *closure,
int event,
unsigned char *info_hash,
void *data, size_t data_len)
{
if(event == DHT_EVENT_SEARCH_DONE)
printf("Search done.\n");
else if(event == DHT_EVENT_VALUES)
printf("Received %d values.\n", (int)(data_len / 6));
}
int
main(int argc, char **argv)
{
int i, rc, fd;
int s, port;
int have_id = 0;
unsigned char myid[20];
time_t tosleep = 0;
/* Ids need to be distributed evenly, so you cannot just use your
bittorrent id. Either generate it randomly, or take the SHA-1 of
something. */
fd = open("dht-example.id", O_RDONLY);
if(fd >= 0) {
rc = read(fd, myid, 20);
if(rc == 20)
have_id = 1;
close(fd);
}
if(!have_id) {
fd = open("/dev/urandom", O_RDONLY);
if(fd < 0) {
perror("open(random)");
exit(1);
}
rc = read(fd, myid, 20);
if(rc < 0) {
perror("read(random)");
exit(1);
}
have_id = 1;
close(fd);
fd = open("dht-example.id", O_WRONLY | O_CREAT | O_TRUNC, 0666);
if(fd >= 0) {
rc = write(fd, myid, 20);
if(rc < 20)
unlink("dht-example.id");
close(fd);
}
}
if(argc < 2)
goto usage;
i = 1;
if(argc < i + 1)
goto usage;
port = atoi(argv[i++]);
if(port <= 0 || port >= 0x10000)
goto usage;
while(i < argc) {
struct addrinfo hints, *info, *infop;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
rc = getaddrinfo(argv[i], NULL, &hints, &info);
if(rc != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(1);
}
i++;
infop = info;
while(infop) {
if(infop->ai_addr->sa_family == AF_INET) {
struct sockaddr_in sin;
memcpy(&sin, infop->ai_addr, infop->ai_addrlen);
sin.sin_port = htons(atoi(argv[i]));
bootstrap_nodes[num_bootstrap_nodes] = sin;
num_bootstrap_nodes++;
}
infop = infop->ai_next;
}
freeaddrinfo(info);
i++;
}
if(i < argc)
goto usage;
/* If you set dht_debug to a stream, every action taken by the DHT will
be logged. */
dht_debug = stdout;
/* We need an IPv4 socket, bound to a stable port. Rumour has it that
uTorrent works better when it is the same as your Bittorrent port. */
s = socket(PF_INET, SOCK_DGRAM, 0);
if(s < 0) {
perror("socket");
exit(1);
}
{
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
rc = bind(s, (struct sockaddr*)&sin, sizeof(sin));
if(rc < 0) {
perror("bind");
exit(1);
}
}
/* Init the dht. This sets the socket into non-blocking mode. */
rc = dht_init(s, myid);
if(rc < 0) {
perror("dht_init");
exit(1);
}
init_signals();
/* For bootstrapping, we need an initial list of nodes. This could be
hard-wired, but can also be obtained from the nodes key of a torrent
file, or from the PORT bittorrent message.
Dht_ping_node is the brutal way of bootstrapping -- it actually
sends a message to the peer. If you're going to bootstrap from
a massive number of nodes (for example because you're restoring from
a dump) and you already know their ids, it's better to use
dht_insert_node. If the ids are incorrect, the DHT will recover. */
for(i = 0; i < num_bootstrap_nodes; i++) {
dht_ping_node(s, &bootstrap_nodes[i]);
usleep(random() % 100000);
}
while(1) {
struct timeval tv;
fd_set readfds;
tv.tv_sec = tosleep;
tv.tv_usec = random() % 1000000;
FD_ZERO(&readfds);
FD_SET(s, &readfds);
rc = select(s + 1, &readfds, NULL, NULL, &tv);
if(rc < 0) {
if(errno != EINTR) {
perror("select");
sleep(1);
}
}
if(exiting)
break;
rc = dht_periodic(s, rc > 0, &tosleep, callback, NULL);
if(rc < 0) {
if(errno == EINTR) {
continue;
} else {
perror("dht_periodic");
if(rc == EINVAL || rc == EFAULT)
abort();
tosleep = 1;
}
}
/* This is how you trigger a search for a torrent hash. If port
(the third argument) is non-zero, it also performs an announce.
Since peers expire announced data after 30 minutes, it's a good
idea to reannounce every 28 minutes or so. */
if(searching) {
dht_search(s, hash, 0, callback, NULL);
searching = 0;
}
/* For debugging, or idle curiosity. */
if(dumping) {
dht_dump_tables(stdout);
dumping = 0;
}
}
{
struct sockaddr_in sins[500];
int i;
i = dht_get_nodes(sins, 500);
printf("Found %d good nodes.\n", i);
}
dht_uninit(s, 1);
return 0;
usage:
fprintf(stderr, "Foo!\n");
exit(1);
}
/* We need to provide a reasonably strong cryptographic hashing function.
Here's how we'd do it if we had RSA's MD5 code. */
#if 0
void
dht_hash(void *hash_return, int hash_size,
const void *v1, int len1,
const void *v2, int len2,
const void *v3, int len3)
{
static MD5_CTX ctx;
MD5Init(&ctx);
MD5Update(&ctx, v1, len1);
MD5Update(&ctx, v2, len2);
MD5Update(&ctx, v3, len3);
MD5Final(&ctx);
if(hash_size > 16)
memset((char*)hash_return + 16, 0, hash_size - 16);
memcpy(hash_return, ctx.digest, hash_size > 16 ? 16 : hash_size);
}
#else
/* But for this example, we might as well use something weaker. */
void
dht_hash(void *hash_return, int hash_size,
const void *v1, int len1,
const void *v2, int len2,
const void *v3, int len3)
{
const char *c1 = v1, *c2 = v2, *c3 = v3;
char key[9]; /* crypt is limited to 8 characters */
int i;
memset(key, 0, 9);
#define CRYPT_HAPPY(c) ((c % 0x60) + 0x20)
for(i = 0; i < 2 && i < len1; i++)
key[i] = CRYPT_HAPPY(c1[i]);
for(i = 0; i < 4 && i < len1; i++)
key[2 + i] = CRYPT_HAPPY(c2[i]);
for(i = 0; i < 2 && i < len1; i++)
key[6 + i] = CRYPT_HAPPY(c3[i]);
strncpy(hash_return, crypt(key, "jc"), hash_size);
}
#endif

2183
third-party/dht/dht.c vendored Normal file

File diff suppressed because it is too large Load Diff

51
third-party/dht/dht.h vendored Normal file
View File

@ -0,0 +1,51 @@
/*
Copyright (c) 2009 by Juliusz Chroboczek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
typedef void
dht_callback(void *closure, int event,
unsigned char *info_hash,
void *data, size_t data_len);
#define DHT_EVENT_NONE 0
#define DHT_EVENT_VALUES 1
#define DHT_EVENT_SEARCH_DONE 2
extern FILE *dht_debug;
int dht_init(int s, const unsigned char *id);
int dht_insert_node(int s, const unsigned char *id, struct sockaddr_in *sin);
int dht_ping_node(int s, struct sockaddr_in *sin);
int dht_periodic(int s, int available, time_t *tosleep,
dht_callback *callback, void *closure);
int dht_search(int s, const unsigned char *id, int port,
dht_callback *callback, void *closure);
int dht_nodes(int *good_return, int *dubious_return, int *cached_return,
int *incoming_return);
void dht_dump_tables(FILE *f);
int dht_get_nodes(struct sockaddr_in *sins, int num);
int dht_uninit(int s, int dofree);
/* This must be provided by the user. */
void dht_hash(void *hash_return, int hash_size,
const void *v1, int len1,
const void *v2, int len2,
const void *v3, int len3);

7
third-party/dht/makelog vendored Normal file
View File

@ -0,0 +1,7 @@
if gcc -DPACKAGE_NAME=\"transmission\" -DPACKAGE_TARNAME=\"transmission\" -DPACKAGE_VERSION=\"1.61+\" -DPACKAGE_STRING=\"transmission\ 1.61+\" -DPACKAGE_BUGREPORT=\"http://trac.transmissionbt.com/newticket\" -DPACKAGE=\"transmission\" -DVERSION=\"1.61+\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_DLFCN_H=1 -DTR_NIGHTLY_RELEASE=1 -DSTDC_HEADERS=1 -DTIME_WITH_SYS_TIME=1 -DHAVE_DAEMON=1 -DHAVE_DIRNAME=1 -DHAVE_BASENAME=1 -DHAVE_DAEMON=1 -DHAVE_STRCASECMP=1 -DHAVE_LOCALTIME_R=1 -DHAVE_POSIX_FALLOCATE=1 -DHAVE_MEMMEM=1 -DHAVE_PTHREAD=1 -DHAVE__TMP_DUMMY1_ZLIB_H=1 -DHAVE_ZLIB=1 -DHAVE_DECL_POSIX_FADVISE=1 -DHAVE_POSIX_FADVISE=1 -DWITH_INOTIFY=1 -DHAVE_DBUS_GLIB=1 -DHAVE_LIBINTL_H=1 -DGETTEXT_PACKAGE=\"transmission\" -DHAVE_LOCALE_H=1 -DHAVE_LC_MESSAGES=1 -DHAVE_BIND_TEXTDOMAIN_CODESET=1 -DHAVE_GETTEXT=1 -DHAVE_DCGETTEXT=1 -DENABLE_NLS=1 -I. -I. -I/mnt/home/charles/opt/easytag/include -I/mnt/home/charles/wx/include -I/mnt/home/charles/wx/include -Os -Wall -W -ggdb3 -g -O0 -std=gnu99 -ggdb3 -Wall -W -Wpointer-arith -Wformat-security -Wcast-align -Wundef -Wcast-align -Wstrict-prototypes -Wmissing-declarations -Wmissing-format-attribute -Wredundant-decls -Wnested-externs -Wunused-parameter -Wwrite-strings -Wextra -Wdeclaration-after-statement -Winit-self -MT dht.o -MD -MP -MF ".deps/dht.Tpo" -c -o dht.o dht.c; \
then mv -f ".deps/dht.Tpo" ".deps/dht.Po"; else rm -f ".deps/dht.Tpo"; exit 1; fi
dht.c:1321: warning: unused parameter s
dht.c:1952: warning: unused parameter port
rm -f libdht.a
ar cru libdht.a dht.o
ranlib libdht.a