Ticket #7: transmission-dht-20090617.patch

File transmission-dht-20090617.patch, 27.8 KB (added by charles, 13 years ago)
  • AUTHORS

    diff --git a/AUTHORS b/AUTHORS
    index aef0c50..3f65fe5 100644
    a b Lead Developers <dev@transmissionbt.com> 
    77
    88Project Contributors
    99  Tomas Carnecky  (Profiling, patches, and detection of sneaky bugs)
     10  Juliusz Chroboczek  (DHT)
    1011  John Clay  (Website maintenance and troubleshooting)
    1112  Rashid Eissing  (Mac OS X Transfers preferences icon)
    1213  Hugo van Heuven, madebysofa  (Main icon design)
  • cli/Makefile.am

    diff --git a/cli/Makefile.am b/cli/Makefile.am
    index 3244936..9fb6676 100644
    a b transmissioncli_LDADD = \ 
    2020    $(top_builddir)/third-party/libevent/libevent.la \
    2121    $(top_builddir)/third-party/libnatpmp/libnatpmp.a \
    2222    $(top_builddir)/third-party/miniupnp/libminiupnp.a \
     23    $(top_builddir)/third-party/dht/dht.o \
    2324    $(INTLLIBS) \
    2425    $(LIBCURL_LIBS) \
    2526    $(ZLIB_LIBS) \
  • daemon/Makefile.am

    diff --git a/daemon/Makefile.am b/daemon/Makefile.am
    index ea22fac..4311997 100644
    a b LDADD = \ 
    2222    $(top_builddir)/third-party/miniupnp/libminiupnp.a \
    2323    $(top_builddir)/third-party/libnatpmp/libnatpmp.a \
    2424    $(top_builddir)/third-party/libevent/libevent.la \
     25    $(top_builddir)/third-party/dht/dht.o \
    2526    $(INTLLIBS) \
    2627    $(LIBCURL_LIBS) \
    2728    $(ZLIB_LIBS) \
  • gtk/Makefile.am

    diff --git a/gtk/Makefile.am b/gtk/Makefile.am
    index ff712a3..57874f4 100644
    a b transmission_LDADD = \ 
    101101    $(top_builddir)/third-party/libevent/libevent.la \
    102102    $(top_builddir)/third-party/miniupnp/libminiupnp.a \
    103103    $(top_builddir)/third-party/libnatpmp/libnatpmp.a \
     104    $(top_builddir)/third-party/dht/dht.o \
    104105    $(GTK_LIBS) \
    105106    $(GIO_LIBS) \
    106107    $(LIBNOTIFY_LIBS) \
  • gtk/details.c

    diff --git a/gtk/details.c b/gtk/details.c
    index 83307a0..f7af3f2 100644
    a b onPeerViewQueryTooltip( GtkWidget * widget, 
    15001500                case '?': s = _( "We unchoked this peer, but they're not interested" ); break;
    15011501                case 'E': s = _( "Encrypted connection" ); break;
    15021502                case 'X': s = _( "Peer was discovered through Peer Exchange (PEX)" ); break;
     1503                case 'H': s = _( "Peer was discovered through the DHT" ); break;
    15031504                case 'I': s = _( "Peer is an incoming connection" ); break;
    15041505            }
    15051506            if( s )
  • libtransmission/Makefile.am

    diff --git a/libtransmission/Makefile.am b/libtransmission/Makefile.am
    index 4f869d3..e8ad720 100644
    a b libtransmission_a_SOURCES = \ 
    4949    torrent-ctor.c \
    5050    tr-getopt.c \
    5151    tracker.c \
     52    tr-dht.c \
    5253    trevent.c \
    5354    upnp.c \
    5455    utils.c \
    noinst_HEADERS = \ 
    9697    tracker.h \
    9798    tr-getopt.h \
    9899    transmission.h \
     100    tr-dht.h \
    99101    trevent.h \
    100102    upnp.h \
    101103    utils.h \
    apps_ldadd = \ 
    124126    $(top_builddir)/third-party/miniupnp/libminiupnp.a \
    125127    $(top_builddir)/third-party/libnatpmp/libnatpmp.a \
    126128    $(top_builddir)/third-party/libevent/libevent.la \
     129    $(top_builddir)/third-party/dht/dht.o \
    127130    $(INTLLIBS) \
    128131    $(LIBCURL_LIBS) \
    129132    $(OPENSSL_LIBS) \
  • libtransmission/bencode.c

    diff --git a/libtransmission/bencode.c b/libtransmission/bencode.c
    index e758927..318cc26 100644
    a b tr_bencGetStr( const tr_benc * val, 
    447447}
    448448
    449449tr_bool
     450tr_bencGetStrN( const tr_benc * val,
     451                const char **   setme,
     452                size_t *        length)
     453{
     454    const int success = tr_bencIsString( val );
     455
     456    if( success ) {
     457        *setme = val->val.s.s;
     458        *length = val->val.s.i;
     459    }
     460
     461    return success;
     462}
     463
     464tr_bool
    450465tr_bencGetBool( const tr_benc * val, tr_bool * setme )
    451466{
    452467    tr_bool success = FALSE;
    tr_bencDictFindStr( tr_benc * dict, const char * key, const char ** setme ) 
    521536}
    522537
    523538tr_bool
     539tr_bencDictFindStrN( tr_benc *  dict, const char *  key,
     540                     const char ** setme, size_t * len)
     541{
     542    return tr_bencGetStrN( tr_bencDictFind( dict, key ), setme, len );
     543}
     544
     545tr_bool
    524546tr_bencDictFindList( tr_benc * dict, const char * key, tr_benc ** setme )
    525547{
    526548    return tr_bencDictFindType( dict, key, TR_TYPE_LIST, setme );
    tr_bencDictAddStr( tr_benc * dict, const char * key, const char * val ) 
    780802}
    781803
    782804tr_benc*
     805tr_bencDictAddStrN( tr_benc * dict, const char * key,
     806                    const char * val, size_t len )
     807{
     808    tr_benc * child;
     809
     810    /* see if it already exists, and if so, try to reuse it */
     811    if(( child = tr_bencDictFind( dict, key ))) {
     812        if( tr_bencIsString( child ) )
     813            tr_free( child->val.s.s );
     814        else {
     815            tr_bencDictRemove( dict, key );
     816            child = NULL;
     817        }
     818    }
     819
     820    /* if it doesn't exist, create it */
     821    if( child == NULL )
     822        child = tr_bencDictAdd( dict, key );
     823
     824    /* set it */
     825    tr_bencInitStr( child, val, len );
     826
     827    return child;
     828}
     829
     830tr_benc*
    783831tr_bencDictAddList( tr_benc *    dict,
    784832                    const char * key,
    785833                    size_t       reserveCount )
  • libtransmission/bencode.h

    diff --git a/libtransmission/bencode.h b/libtransmission/bencode.h
    index 0db99f2..c979f6a 100644
    a b tr_benc * tr_bencDictAddBool( tr_benc *, const char * key, tr_bool ); 
    144144
    145145tr_benc * tr_bencDictAddStr( tr_benc *, const char * key, const char * );
    146146
     147tr_benc * tr_bencDictAddStrN( tr_benc *, const char * key, const char *, size_t length );
     148
    147149tr_benc * tr_bencDictAddList( tr_benc *, const char * key, size_t reserve );
    148150
    149151tr_benc * tr_bencDictAddDict( tr_benc *, const char * key, size_t reserve );
    tr_bool tr_bencDictFindReal( tr_benc *, const char * key, double * setme ); 
    164166tr_bool   tr_bencDictFindBool( tr_benc *, const char * key, tr_bool * setme );
    165167
    166168tr_bool   tr_bencDictFindStr( tr_benc *, const char * key, const char ** setme );
     169tr_bool   tr_bencDictFindStrN( tr_benc *, const char * key, const char ** setme, size_t *len );
    167170
    168171tr_bool   tr_bencDictFindRaw( tr_benc *, const char * key,
    169172                              const uint8_t ** setme_raw, size_t * setme_len );
  • libtransmission/handshake.c

    diff --git a/libtransmission/handshake.c b/libtransmission/handshake.c
    index 9acfaff..a78a469 100644
    a b  
    2828#include "peer-io.h"
    2929#include "peer-mgr.h"
    3030#include "torrent.h"
     31#include "tr-dht.h"
    3132#include "trevent.h"
    3233#include "utils.h"
    3334
     
    3536#define ENABLE_LTEP * /
    3637/* fast extensions */
    3738#define ENABLE_FAST * /
     39/* DHT */
     40#define ENABLE_DHT * /
    3841
    3942/***
    4043****
    enum 
    8285 #define HANDSHAKE_SET_FASTEXT( bits ) ( (void)0 )
    8386#endif
    8487
     88#ifdef ENABLE_DHT
     89 #define HANDSHAKE_HAS_DHT( bits ) ( ( ( bits )[7] & 0x01 ) ? 1 : 0 )
     90 #define HANDSHAKE_SET_DHT( bits ) ( ( bits )[7] |= 0x01 )
     91#else
     92 #define HANDSHAKE_HAS_DHT( bits ) ( 0 )
     93 #define HANDSHAKE_SET_DHT( bits ) ( (void)0 )
     94#endif
     95
    8596/* http://www.azureuswiki.com/index.php/Extension_negotiation_protocol
    8697   these macros are to be used if both extended messaging and the
    8798   azureus protocol is supported, they indicate which protocol is preferred */
    buildHandshakeMessage( tr_handshake * handshake, 
    219230    HANDSHAKE_SET_LTEP( walk );
    220231    HANDSHAKE_SET_FASTEXT( walk );
    221232
     233    /* Note that this doesn't depend on whether the torrent is private.  We
     234       don't accept DHT peers for a private torrent, but we participate in
     235       the DHT regardless. */
     236    if(tr_dhtEnabled(handshake->session))
     237        HANDSHAKE_SET_DHT( walk );
     238
    222239    walk += HANDSHAKE_FLAGS_LEN;
    223240    memcpy( walk, torrentHash, SHA_DIGEST_LENGTH );
    224241    walk += SHA_DIGEST_LENGTH;
    parseHandshake( tr_handshake * handshake, 
    303320
    304321    tr_peerIoEnableFEXT( handshake->io, HANDSHAKE_HAS_FASTEXT( reserved ) );
    305322
     323    /* This does not depend on whether the torrent is private. */
     324    if(tor->session->isPexEnabled)
     325        tr_peerIoEnableDHT( handshake->io, HANDSHAKE_HAS_DHT( reserved ) );
     326
    306327    return HANDSHAKE_OK;
    307328}
    308329
  • libtransmission/peer-io.c

    diff --git a/libtransmission/peer-io.c b/libtransmission/peer-io.c
    index be68976..c7440cd 100644
    a b tr_peerIoEnableLTEP( tr_peerIo * io, 
    617617    io->extendedProtocolSupported = flag;
    618618}
    619619
     620void
     621tr_peerIoEnableDHT( tr_peerIo  * io,
     622                    tr_bool      flag )
     623{
     624    assert( tr_isPeerIo( io ) );
     625    assert( tr_isBool( flag ) );
     626
     627    dbgmsg( io, "setting DHT support flag to %d", (flag!=0) );
     628    io->dhtSupported = flag;
     629}
     630
    620631/**
    621632***
    622633**/
  • libtransmission/peer-io.h

    diff --git a/libtransmission/peer-io.h b/libtransmission/peer-io.h
    index d730a10..32adb57 100644
    a b typedef struct tr_peerIo 
    6363    tr_bool               peerIdIsSet;
    6464    tr_bool               extendedProtocolSupported;
    6565    tr_bool               fastExtensionSupported;
     66    tr_bool               dhtSupported;
    6667
    6768    /* we create the socket in a nonblocking way, so this flag is initially
    6869     * false and then set to true when libevent says that the socket is ready
    static TR_INLINE tr_bool tr_peerIoSupportsFEXT( const tr_peerIo * io ) 
    159160    return io->fastExtensionSupported;
    160161}
    161162
     163void        tr_peerIoEnableDHT( tr_peerIo * io, tr_bool flag );
     164
     165static TR_INLINE tr_bool tr_peerIoSupportsDHT( const tr_peerIo * io )
     166{
     167    assert( tr_isPeerIo( io ) );
     168
     169    return io->dhtSupported;
     170}
     171
    162172/**
    163173***
    164174**/
  • libtransmission/peer-mgr.c

    diff --git a/libtransmission/peer-mgr.c b/libtransmission/peer-mgr.c
    index 3fef45a..47176f0 100644
    a b tr_peerMgrPeerStats( const tr_torrent * tor, 
    18491849        if( !stat->clientIsChoked && !stat->clientIsInterested ) *pch++ = 'K';
    18501850        if( !stat->peerIsChoked && !stat->peerIsInterested ) *pch++ = '?';
    18511851        if( stat->isEncrypted ) *pch++ = 'E';
     1852        if( stat->from == TR_PEER_FROM_DHT ) *pch++ = 'H';
    18521853        if( stat->from == TR_PEER_FROM_PEX ) *pch++ = 'X';
    18531854        if( stat->isIncoming ) *pch++ = 'I';
    18541855        *pch = '\0';
  • libtransmission/peer-msgs.c

    diff --git a/libtransmission/peer-msgs.c b/libtransmission/peer-msgs.c
    index acd99f5..975a830 100644
    a b  
    3636#include "request-list.h"
    3737#include "stats.h"
    3838#include "torrent.h"
     39#include "tr-dht.h"
    3940#include "trevent.h"
    4041#include "utils.h"
    4142#include "version.h"
    protocolSendCancel( tr_peermsgs * msgs, 
    307308}
    308309
    309310static void
     311protocolSendPort(tr_peermsgs *msgs, uint16_t port)
     312{
     313    tr_peerIo       * io  = msgs->peer->io;
     314    struct evbuffer * out = msgs->outMessages;
     315
     316    dbgmsg( msgs, "sending Port %u", port);
     317    tr_peerIoWriteUint32( io, out, 3 );
     318    tr_peerIoWriteUint8 ( io, out, BT_PORT );
     319    tr_peerIoWriteUint16( io, out, port);
     320}
     321
     322static void
    310323protocolSendHave( tr_peermsgs * msgs,
    311324                  uint32_t      index )
    312325{
    readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen ) 
    14281441            break;
    14291442
    14301443        case BT_PORT:
    1431             dbgmsg( msgs, "Got a BT_PORT" );
    1432             tr_peerIoReadUint16( msgs->peer->io, inbuf, &msgs->peer->port );
    1433             break;
     1444            {
     1445                tr_port port;
     1446                dbgmsg( msgs, "Got a BT_PORT" );
     1447                tr_peerIoReadUint16( msgs->peer->io, inbuf, &port );
     1448                if(msgs->peer->port > 0)
     1449                    tr_dhtAddNode(msgs->peer->io->session,
     1450                                  &msgs->peer->addr, port, 0);
     1451                break;
     1452            }
    14341453
    14351454        case BT_FEXT_SUGGEST:
    14361455            dbgmsg( msgs, "Got a BT_FEXT_SUGGEST" );
    tr_peerMsgsNew( struct tr_torrent * torrent, 
    21232142    if( tr_peerIoSupportsLTEP( peer->io ) )
    21242143        sendLtepHandshake( m );
    21252144
     2145    if(tr_peerIoSupportsDHT(peer->io))
     2146        protocolSendPort(m, tr_dhtPort(torrent->session));
     2147
    21262148    tellPeerWhatWeHave( m );
    21272149
    21282150    tr_peerIoSetIOFuncs( m->peer->io, canRead, didWrite, gotError, m );
  • libtransmission/session.c

    diff --git a/libtransmission/session.c b/libtransmission/session.c
    index 0c6fea2..8bd32b6 100644
    a b  
    4444#include "version.h"
    4545#include "verify.h"
    4646#include "web.h"
     47#include "tr-dht.h"
    4748
    4849#define dbgmsg( ... ) \
    4950    do { \
    tr_sessionInit( const char * tag, 
    492493    while( session->isWaiting )
    493494        tr_wait( 100 );
    494495
     496    if(session->isDHTEnabled)
     497        tr_dhtInit(session);
     498
    495499    return session;
    496500}
    497501
    tr_sessionInitImpl( void * vdata ) 
    542546    found = tr_bencDictFindBool( &settings, TR_PREFS_KEY_PEX_ENABLED, &boolVal );
    543547    assert( found );
    544548    session->isPexEnabled = boolVal;
     549    session->isDHTEnabled = boolVal;
    545550
    546551    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_ENCRYPTION, &i );
    547552    assert( found );
    tr_sessionClose( tr_session * session ) 
    13991404
    14001405    dbgmsg( "shutting down transmission session %p", session );
    14011406
     1407    if(session->isDHTEnabled)
     1408        tr_dhtUninit(session);
     1409
    14021410    /* close the session */
    14031411    tr_runInEventThread( session, tr_closeAllConnections, session );
    14041412    while( !session->isClosed && !deadlineReached( deadline ) )
    tr_sessionIsPexEnabled( const tr_session * session ) 
    15241532    return session->isPexEnabled;
    15251533}
    15261534
     1535tr_bool
     1536tr_sessionIsDHTEnabled( const tr_session * session )
     1537{
     1538    assert( tr_isSession( session ) );
     1539
     1540    return session->isDHTEnabled;
     1541}
     1542
    15271543/***
    15281544****
    15291545***/
  • libtransmission/session.h

    diff --git a/libtransmission/session.h b/libtransmission/session.h
    index 815bd2f..642ad4e 100644
    a b struct tr_session 
    6161    tr_bool                      isPortSet;
    6262    tr_bool                      isPortRandom;
    6363    tr_bool                      isPexEnabled;
     64    tr_bool                      isDHTEnabled;
    6465    tr_bool                      isBlocklistEnabled;
    6566    tr_bool                      isProxyEnabled;
    6667    tr_bool                      isProxyAuthEnabled;
  • libtransmission/torrent.h

    diff --git a/libtransmission/torrent.h b/libtransmission/torrent.h
    index 237b95a..f60dd58 100644
    a b static TR_INLINE tr_bool tr_torrentAllowsPex( const tr_torrent * tor ) 
    287287    return ( tor != NULL  ) && tor->session->isPexEnabled && !tr_torrentIsPrivate( tor );
    288288}
    289289
     290static TR_INLINE tr_bool tr_torrentAllowsDHT( const tr_torrent * tor )
     291{
     292    return ( tor != NULL  ) && tor->session->isDHTEnabled && !tr_torrentIsPrivate( tor );
     293}
     294
    290295static TR_INLINE tr_bool tr_torrentIsPieceChecked( const tr_torrent  * tor, tr_piece_index_t i )
    291296{
    292297    return tr_bitfieldHasFast( &tor->checkedPieces, i );
  • new file libtransmission/tr-dht.c

    diff --git a/libtransmission/tr-dht.c b/libtransmission/tr-dht.c
    new file mode 100644
    index 0000000..8078a0c
    - +  
     1/*
     2Copyright (c) 2009 by Juliusz Chroboczek
     3
     4Permission is hereby granted, free of charge, to any person obtaining a copy
     5of this software and associated documentation files (the "Software"), to deal
     6in the Software without restriction, including without limitation the rights
     7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8copies of the Software, and to permit persons to whom the Software is
     9furnished to do so, subject to the following conditions:
     10
     11The above copyright notice and this permission notice shall be included in
     12all copies or substantial portions of the Software.
     13
     14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     20THE SOFTWARE.
     21*/
     22
     23#include <stdio.h>
     24#include <stdlib.h>
     25#include <errno.h>
     26#include <string.h>
     27#include <unistd.h>
     28#include <fcntl.h>
     29#include <sys/time.h>
     30#include <sys/signal.h>
     31#include <arpa/inet.h>
     32#include <sys/types.h>
     33#include <sys/socket.h>
     34#include <netdb.h>
     35
     36#include <event.h>
     37
     38#include <dht/dht.h>
     39
     40#include "transmission.h"
     41#include "session.h"
     42#include "torrent.h"
     43#include "peer-mgr.h"
     44#include "trevent.h"
     45#include "crypto.h"
     46#include "platform.h"
     47
     48#include "tr-dht.h"
     49
     50static int dht_socket;
     51static struct event dht_event;
     52static unsigned short dht_port;
     53static unsigned char myid[20];
     54static tr_session *session = NULL;
     55static time_t announce_time;
     56
     57static void event_callback(int s, short type, void *ignore);
     58
     59struct bootstrap_closure {
     60    tr_session *session;
     61    char *nodes;
     62    size_t len;
     63};
     64
     65static void
     66dht_bootstrap(void *closure)
     67{
     68    struct bootstrap_closure *cl = (struct bootstrap_closure*)closure;
     69    size_t i;
     70
     71    if(session != cl->session)
     72        return;
     73
     74    for(i = 0; i < cl->len; i++) {
     75        struct timeval tv;
     76        unsigned short port;
     77        struct tr_address addr;
     78        int status;
     79
     80        memset(&addr, 0, sizeof(addr));
     81        addr.type = TR_AF_INET;
     82        memcpy(&addr.addr.addr4, &cl->nodes[i * 6], 4);
     83        memcpy(&port, &cl->nodes[i * 6 + 4], 2);
     84        port = ntohs(port);
     85        /* There's no race here -- if we uninit between the test and the
     86           AddNode, the AddNode will be ignored. */
     87        status = tr_dhtStatus(cl->session);
     88        if(status == TR_DHT_STOPPED || status >= TR_DHT_FIREWALLED)
     89            break;
     90        tr_dhtAddNode(cl->session, &addr, port, 1);
     91        tv.tv_sec = 2 + random() % 5;
     92        tv.tv_usec = random() % 1000000;
     93        select(0, NULL, NULL, NULL, &tv);
     94    }
     95    free(cl->nodes);
     96    free(closure);
     97}
     98
     99int
     100tr_dhtInit(tr_session *ss)
     101{
     102    struct sockaddr_in sin;
     103    struct timeval tv;
     104    tr_benc benc;
     105    int rc, fd, have_id = 0;
     106    char *dat_file, *nodes = NULL;
     107    const char *val;
     108    size_t len;
     109
     110    if(session)
     111        return -1;
     112
     113    dht_socket = socket(PF_INET, SOCK_DGRAM, 0);
     114    if(dht_socket < 0)
     115        return -1;
     116
     117    dht_port = tr_sessionGetPeerPort(ss);
     118    if(dht_port <= 0)
     119        return -1;
     120
     121    memset(&sin, 0, sizeof(sin));
     122    sin.sin_family = AF_INET;
     123    sin.sin_port = htons(dht_port);
     124    rc = bind(dht_socket, (struct sockaddr*)&sin, sizeof(sin));
     125    if(rc < 0)
     126        goto fail;
     127
     128#ifdef DEBUG_DHT
     129    dht_debug = stdout;
     130#endif
     131
     132    dat_file = tr_strdup_printf("%s/dht.dat", ss->configDir);
     133    rc = tr_bencLoadFile(dat_file, &benc);
     134    if(rc == 0) {
     135        if(tr_bencDictFindStrN(&benc, "id", &val, &len)) {
     136            if(val && len == 20) {
     137                memcpy(myid, val, len);
     138                have_id = 1;
     139            }
     140        }
     141        if(tr_bencDictFindStrN(&benc, "nodes", &val, &len)) {
     142            if(len % 6 == 2) {
     143                /* This hack allows reading of uTorrent files, which I find
     144                   convenient. */
     145                len -= 2;
     146            }
     147            nodes = malloc(len);
     148            memcpy(nodes, val, len);
     149        }
     150        tr_bencFree(&benc);
     151    }
     152
     153    if(!have_id) {
     154        /* Note that you cannot just use your BT id -- DHT ids need to be
     155           distributed uniformly, so it should either be the SHA-1 of
     156           something, or truly random. */
     157        fd = open("/dev/urandom", O_RDONLY);
     158        rc = read(fd, myid, 20);
     159        if(rc < 20)
     160            goto fail;
     161        have_id = 1;
     162    }
     163
     164    rc = dht_init(dht_socket, myid);
     165    if(rc < 0)
     166        goto fail;
     167
     168    session = ss;
     169
     170    if(nodes) {
     171        struct bootstrap_closure *cl;
     172        cl = malloc(sizeof(struct bootstrap_closure));
     173        if(cl) {
     174            cl->session = session;
     175            cl->nodes = nodes;
     176            cl->len = len;
     177            tr_threadNew(dht_bootstrap, cl);
     178        } else {
     179            free(nodes);
     180        }
     181    }
     182    announce_time = 0;
     183    tv.tv_sec = 0;
     184    tv.tv_usec = random() % 1000000;
     185    event_set(&dht_event, dht_socket, EV_READ, event_callback, NULL);
     186    event_add(&dht_event, &tv);
     187
     188    return 1;
     189
     190    fail:
     191    {
     192        int save = errno;
     193        close(dht_socket);
     194        dht_socket = -1;
     195        session = NULL;
     196        errno = save;
     197    }
     198
     199    return -1;
     200}
     201
     202void
     203tr_dhtUninit(tr_session *ss)
     204{
     205    if(session != ss)
     206        return;
     207
     208    event_del(&dht_event);
     209
     210    /* Since we only save known good nodes, avoid erasing older data if we
     211       don't know enough nodes. */
     212    if(tr_dhtStatus(ss) >= TR_DHT_FIREWALLED) {
     213        tr_benc benc;
     214        struct sockaddr_in sins[300];
     215        char compact[300 * 6];
     216        char *dat_file;
     217        int n, i, j;
     218        n = dht_get_nodes(sins, 300);
     219        j = 0;
     220        for(i = 0; i < n; i++) {
     221            memcpy(compact + j, &sins[i].sin_addr, 4);
     222            memcpy(compact + j + 4, &sins[i].sin_port, 2);
     223            j += 6;
     224        }
     225        tr_bencInitDict(&benc, 2);
     226        tr_bencDictAddStrN(&benc, "id", (char*)myid, 20);
     227        tr_bencDictAddStrN(&benc, "nodes", compact, j);
     228        dat_file = tr_strdup_printf("%s/dht.dat", ss->configDir);
     229        tr_bencSaveFile(dat_file, &benc);
     230        free(dat_file);
     231    }
     232
     233    dht_uninit(dht_socket, 0);
     234
     235    session = NULL;
     236}
     237
     238int
     239tr_dhtEnabled(tr_session *ss)
     240{
     241    return (ss && session == ss);
     242}
     243
     244static void
     245getstatus(void *closure)
     246{
     247    sig_atomic_t *ret = (sig_atomic_t*)closure;
     248    int good, dubious, incoming;
     249
     250    dht_nodes(&good, &dubious, NULL, &incoming);
     251    if(good < 4 || good + dubious <= 8)
     252        *ret = TR_DHT_BROKEN;
     253    else if(good < 40)
     254        *ret = TR_DHT_POOR;
     255    else if(incoming < 8)
     256        *ret = TR_DHT_FIREWALLED;
     257    else
     258        *ret = TR_DHT_GOOD;
     259}
     260
     261int
     262tr_dhtStatus(tr_session *ss)
     263{
     264    sig_atomic_t ret = -1;
     265
     266    if(!tr_dhtEnabled(ss))
     267        return TR_DHT_STOPPED;
     268
     269    tr_runInEventThread(ss, getstatus, &ret);
     270    do {
     271        usleep(1000);
     272    } while(ret < 0);
     273    return ret;
     274}
     275
     276int
     277tr_dhtPort(tr_session *ss)
     278{
     279    if(ss && session == ss)
     280        return dht_port;
     281    return 0;
     282}
     283
     284int
     285tr_dhtAddNode(tr_session *ss, tr_address *address, unsigned short port,
     286              int bootstrap)
     287{
     288    struct sockaddr_in sin;
     289
     290    if(!tr_dhtEnabled(ss))
     291        return 0;
     292
     293    if(address->type != TR_AF_INET)
     294        return 0;
     295
     296    /* Since we don't want to abuse our bootstrap nodes, we don't ping them
     297       if the DHT is in a good state. */
     298    if(bootstrap) {
     299        if(tr_dhtStatus(ss) >= TR_DHT_FIREWALLED)
     300            return 0;
     301    }
     302
     303    {
     304        char buf[50];
     305        inet_ntop(AF_INET, &address->addr.addr4, buf, 50);
     306    }
     307       
     308    memset(&sin, 0, sizeof(sin));
     309    sin.sin_family = AF_INET;
     310    memcpy(&sin.sin_addr, &address->addr.addr4, 4);
     311    sin.sin_port = htons(port);
     312    dht_ping_node(dht_socket, &sin);
     313
     314    return 1;
     315}
     316
     317static void
     318callback(void *ignore, int event,
     319         unsigned char *info_hash, void *data, size_t data_len)
     320{
     321    (void)ignore;               /* sigh */
     322    if(event == DHT_EVENT_VALUES) {
     323        tr_torrent *tor;
     324        tr_pex *pex;
     325        size_t i, n;
     326        pex = tr_peerMgrCompactToPex(data, data_len, NULL, 0, &n);
     327        tr_globalLock(session);
     328        tor = tr_torrentFindFromHash(session, info_hash);
     329        if(tor && tr_torrentAllowsDHT(tor)) {
     330            for(i = 0; i < n; i++)
     331                tr_peerMgrAddPex(tor, TR_PEER_FROM_DHT, pex + i);
     332        }
     333        tr_globalUnlock(session);
     334        tr_free(pex);
     335    }
     336}
     337
     338int
     339tr_dhtAnnounce(tr_torrent *tor, int announce)
     340{
     341    printf("Announce!\n");
     342    dht_search(dht_socket, tor->info.hash,
     343               announce ? tr_sessionGetPeerPort(session) : 0,
     344               callback, NULL);
     345}
     346
     347static void
     348event_callback(int s, short type, void *ignore)
     349{
     350    int rc;
     351    time_t tosleep;
     352    struct timeval now, tv;
     353    (void)ignore;
     354
     355    gettimeofday(&now, NULL);
     356
     357    rc = dht_periodic(s, type == EV_READ, &tosleep, callback, NULL);
     358    if(rc < 0) {
     359        if(errno == EINTR) {
     360            tosleep = 0;
     361        } else {
     362            perror("dht_periodic");
     363            if(rc == EINVAL || rc == EFAULT)
     364                    abort();
     365            tosleep = 1;
     366        }
     367    }
     368
     369    if(announce_time == 0) {
     370        if(tr_dhtStatus(session) >= TR_DHT_POOR)
     371            announce_time = now.tv_sec;
     372    } else if(now.tv_sec >= announce_time) {
     373        if(tr_dhtStatus(session) <= TR_DHT_BROKEN) {
     374            announce_time = 0;
     375        } else {
     376            tr_torrent *tor = NULL;
     377            tr_globalLock(session);
     378            while((tor = tr_torrentNext(session, tor)))
     379                if(tr_torrentGetActivity(tor) != TR_STATUS_STOPPED &&
     380                   tr_torrentAllowsDHT(tor))
     381                    tr_dhtAnnounce(tor, 1);
     382            tr_globalUnlock(session);
     383            announce_time = now.tv_sec + 25 * 60 + random() % (3 * 60);
     384        }
     385    }
     386
     387    /* Being slightly late is fine, and has the added benefit of adding
     388       some jitter. */
     389    tv.tv_sec = tosleep;
     390    tv.tv_usec = random() % 1000000;
     391    event_add(&dht_event, &tv);
     392}
     393
     394void
     395dht_hash(void *hash_return, int hash_size,
     396         const void *v1, int len1,
     397         const void *v2, int len2,
     398         const void *v3, int len3)
     399{
     400    unsigned char sha1[20];
     401    tr_sha1(sha1, v1, len1, v2, len2, v3, len3, NULL);
     402    if(hash_size > 20) {
     403        memset((char*)hash_return + 20, 0, hash_size - 20);
     404    }
     405    memcpy(hash_return, sha1, hash_size > 20 ? 20 : hash_size);
     406}
  • new file libtransmission/tr-dht.h

    diff --git a/libtransmission/tr-dht.h b/libtransmission/tr-dht.h
    new file mode 100644
    index 0000000..9e2c904
    - +  
     1/*
     2Copyright (c) 2009 by Juliusz Chroboczek
     3
     4Permission is hereby granted, free of charge, to any person obtaining a copy
     5of this software and associated documentation files (the "Software"), to deal
     6in the Software without restriction, including without limitation the rights
     7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8copies of the Software, and to permit persons to whom the Software is
     9furnished to do so, subject to the following conditions:
     10
     11The above copyright notice and this permission notice shall be included in
     12all copies or substantial portions of the Software.
     13
     14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     20THE SOFTWARE.
     21*/
     22
     23#define TR_DHT_STOPPED 0
     24#define TR_DHT_BROKEN 1
     25#define TR_DHT_POOR 2
     26#define TR_DHT_FIREWALLED 3
     27#define TR_DHT_GOOD 4
     28
     29int tr_dhtInit(tr_session *ss);
     30void tr_dhtUninit(tr_session *ss);
     31int tr_dhtEnabled(tr_session *ss);
     32int tr_dhtStatus(tr_session *ss);
     33int tr_dhtPort(tr_session *ss);
     34int tr_dhtAddNode(tr_session *ss, tr_address *address, unsigned short port,
     35                  int bootstrap);
     36int tr_dhtAnnounce(tr_torrent *tor, int announce);
  • libtransmission/transmission.h

    diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h
    index 4677322..94f8c9f 100644
    a b enum 
    13371337    TR_PEER_FROM_TRACKER   = 1,  /* peers received from a tracker */
    13381338    TR_PEER_FROM_CACHE     = 2,  /* peers read from the peer cache */
    13391339    TR_PEER_FROM_PEX       = 3,  /* peers discovered via PEX */
     1340    TR_PEER_FROM_DHT       = 4,  /* peers learnt from the DHT */
    13401341    TR_PEER_FROM__MAX
    13411342};
    13421343
  • third-party/Makefile.am

    diff --git a/third-party/Makefile.am b/third-party/Makefile.am
    index 4d9e661..8ed3f22 100644
    a b  
    11SUBDIRS = \
    22    libevent \
    33    libnatpmp \
    4     miniupnp
     4    miniupnp \
     5    dht
    56
    67EXTRA_DIST = \
    78    macosx-libevent-config.h