source: trunk/libtransmission/tr-dht.c @ 9476

Last change on this file since 9476 was 9476, checked in by livings124, 13 years ago

more typo fixes

File size: 12.7 KB
Line 
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/* ansi */
24#include <errno.h>
25#include <stdio.h>
26
27/* posix */
28#include <netinet/in.h> /* sockaddr_in */
29#include <signal.h> /* sig_atomic_t */
30#include <sys/time.h>
31#include <sys/types.h>
32#include <sys/socket.h> /* socket(), bind() */
33#include <unistd.h> /* close() */
34
35/* third party */
36#include <event.h>
37#include <dht/dht.h>
38
39/* libT */
40#include "transmission.h"
41#include "bencode.h"
42#include "crypto.h"
43#include "net.h"
44#include "peer-mgr.h" /* tr_peerMgrCompactToPex() */
45#include "platform.h" /* tr_threadNew() */
46#include "session.h"
47#include "torrent.h" /* tr_torrentFindFromHash() */
48#include "tr-dht.h"
49#include "trevent.h" /* tr_runInEventThread() */
50#include "utils.h"
51#include "version.h"
52
53#ifdef WITHOUT_DHT
54
55  /**
56  *** These are the stubs for when we're building without DHT support
57  **/
58
59  int tr_dhtInit( tr_session * session UNUSED,
60                  tr_address * address UNUSED ) { return TR_DHT_STOPPED; }
61
62  void tr_dhtUninit( tr_session * session UNUSED ) { }
63
64  tr_bool tr_dhtEnabled( const tr_session * session UNUSED ) { return FALSE; }
65
66  tr_port tr_dhtPort ( const tr_session * sesssion UNUSED ) { return 0; }
67
68  int tr_dhtStatus( tr_session * session     UNUSED,
69                    int        * setmeCount  UNUSED ) { return TR_DHT_STOPPED; }
70
71  int tr_dhtAddNode( tr_session       * session    UNUSED,
72                     const tr_address * addr       UNUSED,
73                     tr_port            port       UNUSED,
74                     tr_bool            bootstrap  UNUSED ) { return 0; }
75
76  int tr_dhtAnnounce( tr_torrent * session UNUSED,
77                      tr_bool announce UNUSED ) { return -1; }
78
79
80#else
81
82static int dht_socket;
83static struct event dht_event;
84static tr_port dht_port;
85static unsigned char myid[20];
86static tr_session *session = NULL;
87
88static void event_callback(int s, short type, void *ignore);
89
90struct bootstrap_closure {
91    tr_session *session;
92    uint8_t *nodes;
93    size_t len;
94};
95
96static void
97dht_bootstrap(void *closure)
98{
99    struct bootstrap_closure *cl = closure;
100    size_t i;
101
102    if(session != cl->session)
103        return;
104
105    for(i = 0; i < cl->len; i += 6)
106    {
107        struct timeval tv;
108        tr_port port;
109        struct tr_address addr;
110        int status;
111
112        memset(&addr, 0, sizeof(addr));
113        addr.type = TR_AF_INET;
114        memcpy(&addr.addr.addr4, &cl->nodes[i], 4);
115        memcpy(&port, &cl->nodes[i + 4], 2);
116        port = ntohs(port);
117        /* There's no race here -- if we uninit between the test and the
118           AddNode, the AddNode will be ignored. */
119        status = tr_dhtStatus(cl->session, NULL);
120        if(status == TR_DHT_STOPPED || status >= TR_DHT_FIREWALLED)
121            break;
122        tr_dhtAddNode(cl->session, &addr, port, 1);
123        tr_timevalSet( &tv, 2 + tr_cryptoWeakRandInt( 5 ), tr_cryptoWeakRandInt( 1000000 ) );
124        select( 0, NULL, NULL, NULL, &tv );
125    }
126    tr_free( cl->nodes );
127    tr_free( closure );
128    tr_ndbg( "DHT", "Finished bootstrapping" );
129}
130
131int
132tr_dhtInit(tr_session *ss, tr_address * tr_addr)
133{
134    struct sockaddr_in sin;
135    tr_benc benc;
136    int rc;
137    tr_bool have_id = FALSE;
138    char * dat_file;
139    uint8_t * nodes = NULL;
140    const uint8_t * raw;
141    size_t len;
142    char v[5];
143
144    if( session ) /* already initialized */
145        return -1;
146
147    dht_port = tr_sessionGetPeerPort(ss);
148    if(dht_port <= 0)
149        return -1;
150
151    tr_ndbg( "DHT", "Initializing DHT" );
152
153    dht_socket = socket(PF_INET, SOCK_DGRAM, 0);
154    if(dht_socket < 0)
155        goto fail;
156
157    memset(&sin, 0, sizeof(sin));
158    sin.sin_family = AF_INET;
159    memcpy(&(sin.sin_addr), &(tr_addr->addr.addr4), sizeof (struct in_addr));
160    sin.sin_port = htons(dht_port);
161    rc = bind(dht_socket, (struct sockaddr*)&sin, sizeof(sin));
162    if(rc < 0)
163        goto fail;
164
165    if( getenv( "TR_DHT_VERBOSE" ) != NULL )
166        dht_debug = stderr;
167
168    dat_file = tr_buildPath( ss->configDir, "dht.dat", NULL );
169    rc = tr_bencLoadFile( &benc, TR_FMT_BENC, dat_file );
170    tr_free( dat_file );
171    if(rc == 0) {
172        if(( have_id = tr_bencDictFindRaw( &benc, "id", &raw, &len ) && len==20 ))
173            memcpy( myid, raw, len );
174        if( tr_bencDictFindRaw( &benc, "nodes", &raw, &len ) && !(len%6) ) {
175            nodes = tr_memdup( raw, len );
176            tr_ninf( "DHT", "Bootstrapping from %d old nodes", (int)(len/6) );
177        }
178        tr_bencFree( &benc );
179    }
180
181    if( have_id )
182        tr_ninf( "DHT", "Reusing old id" );
183    else {
184        /* Note that DHT ids need to be distributed uniformly,
185         * so it should be something truly random. */
186        tr_ninf( "DHT", "Generating new id" );
187        tr_cryptoRandBuf( myid, 20 );
188    }
189
190    v[0] = 'T';
191    v[1] = 'R';
192    v[2] = (SVN_REVISION_NUM >> 8) & 0xFF;
193    v[3] = SVN_REVISION_NUM & 0xFF;
194    rc = dht_init( dht_socket, myid, (const unsigned char*)v );
195    if(rc < 0)
196        goto fail;
197
198    session = ss;
199
200    if(nodes) {
201        struct bootstrap_closure * cl = tr_new( struct bootstrap_closure, 1 );
202        cl->session = session;
203        cl->nodes = nodes;
204        cl->len = len;
205        tr_threadNew( dht_bootstrap, cl );
206    }
207
208    event_set( &dht_event, dht_socket, EV_READ, event_callback, NULL );
209    tr_timerAdd( &dht_event, 0, tr_cryptoWeakRandInt( 1000000 ) );
210
211    tr_ndbg( "DHT", "DHT initialized" );
212
213    return 1;
214
215    fail:
216    {
217        const int save = errno;
218        close(dht_socket);
219        dht_socket = -1;
220        session = NULL;
221        tr_ndbg( "DHT", "DHT initialization failed (errno = %d)", save );
222        errno = save;
223    }
224
225    return -1;
226}
227
228void
229tr_dhtUninit(tr_session *ss)
230{
231    if(session != ss)
232        return;
233
234    tr_ndbg( "DHT", "Uninitializing DHT" );
235
236    event_del(&dht_event);
237
238    /* Since we only save known good nodes, avoid erasing older data if we
239       don't know enough nodes. */
240    if(tr_dhtStatus(ss, NULL) < TR_DHT_FIREWALLED)
241        tr_ninf( "DHT", "Not saving nodes, DHT not ready" );
242    else {
243        tr_benc benc;
244        struct sockaddr_in sins[300];
245        char compact[300 * 6];
246        char *dat_file;
247        int i;
248        int n = dht_get_nodes(sins, 300);
249        int j = 0;
250
251        tr_ninf( "DHT", "Saving %d nodes", n );
252        for( i=0; i<n; ++i ) {
253            memcpy( compact + j, &sins[i].sin_addr, 4 );
254            memcpy( compact + j + 4, &sins[i].sin_port, 2 );
255            j += 6;
256        }
257        tr_bencInitDict( &benc, 2 );
258        tr_bencDictAddRaw( &benc, "id", myid, 20 );
259        tr_bencDictAddRaw( &benc, "nodes", compact, j );
260        dat_file = tr_buildPath( ss->configDir, "dht.dat", NULL );
261        tr_bencToFile( &benc, TR_FMT_BENC, dat_file );
262        tr_bencFree( &benc );
263        tr_free( dat_file );
264    }
265
266    dht_uninit( dht_socket, 0 );
267    tr_netCloseSocket( dht_socket );
268
269    tr_ndbg("DHT", "Done uninitializing DHT");
270
271    session = NULL;
272}
273
274tr_bool
275tr_dhtEnabled( const tr_session * ss )
276{
277    return ss && ( ss == session );
278}
279
280struct getstatus_closure
281{
282    sig_atomic_t status;
283    sig_atomic_t count;
284};
285
286static void
287getstatus( void * closure )
288{
289    struct getstatus_closure * ret = closure;
290    int good, dubious, incoming;
291
292    dht_nodes( &good, &dubious, NULL, &incoming );
293
294    ret->count = good + dubious;
295
296    if( good < 4 || good + dubious <= 8 )
297        ret->status = TR_DHT_BROKEN;
298    else if( good < 40 )
299        ret->status = TR_DHT_POOR;
300    else if( incoming < 8 )
301        ret->status = TR_DHT_FIREWALLED;
302    else
303        ret->status = TR_DHT_GOOD;
304}
305
306int
307tr_dhtStatus( tr_session * ss, int * nodes_return )
308{
309    struct getstatus_closure ret = { -1, - 1 };
310
311    if( !tr_dhtEnabled( ss ) )
312        return TR_DHT_STOPPED;
313
314    tr_runInEventThread( ss, getstatus, &ret );
315    while( ret.status < 0 )
316        tr_wait( 10 /*msec*/ );
317
318    if( nodes_return )
319        *nodes_return = ret.count;
320
321    return ret.status;
322}
323
324tr_port
325tr_dhtPort( const tr_session *ss )
326{
327    return tr_dhtEnabled( ss ) ? dht_port : 0;
328}
329
330int
331tr_dhtAddNode( tr_session       * ss,
332               const tr_address * address,
333               tr_port            port,
334               tr_bool            bootstrap )
335{
336    struct sockaddr_in sin;
337
338    if( !tr_dhtEnabled( ss ) )
339        return 0;
340
341    if( address->type != TR_AF_INET )
342        return 0;
343
344    /* Since we don't want to abuse our bootstrap nodes,
345     * we don't ping them if the DHT is in a good state. */
346    if(bootstrap) {
347        if(tr_dhtStatus(ss, NULL) >= TR_DHT_FIREWALLED)
348            return 0;
349    }
350
351    memset(&sin, 0, sizeof(sin));
352    sin.sin_family = AF_INET;
353    memcpy(&sin.sin_addr, &address->addr.addr4, 4);
354    sin.sin_port = htons(port);
355    dht_ping_node(dht_socket, &sin);
356
357    return 1;
358}
359
360const char *
361tr_dhtPrintableStatus(int status)
362{
363    switch(status) {
364    case TR_DHT_STOPPED: return "stopped";
365    case TR_DHT_BROKEN: return "broken";
366    case TR_DHT_POOR: return "poor";
367    case TR_DHT_FIREWALLED: return "firewalled";
368    case TR_DHT_GOOD: return "good";
369    default: return "???";
370    }
371}
372
373static void
374callback( void *ignore UNUSED, int event,
375          unsigned char *info_hash, void *data, size_t data_len )
376{
377    if( event == DHT_EVENT_VALUES )
378    {
379        tr_torrent *tor;
380        tr_globalLock( session );
381        tor = tr_torrentFindFromHash( session, info_hash );
382        if( tor && tr_torrentAllowsDHT( tor ))
383        {
384            size_t i, n;
385            tr_pex * pex = tr_peerMgrCompactToPex(data, data_len, NULL, 0, &n);
386            for( i=0; i<n; ++i )
387                tr_peerMgrAddPex( tor, TR_PEER_FROM_DHT, pex+i );
388            tr_free(pex);
389            tr_torinf(tor, "Learned %d peers from DHT", (int)n);
390        }
391        tr_globalUnlock( session );
392    }
393    else if( event == DHT_EVENT_SEARCH_DONE )
394    {
395        tr_torrent * tor = tr_torrentFindFromHash( session, info_hash );
396        if( tor ) {
397            tr_torinf(tor, "DHT announce done");
398            tor->dhtAnnounceInProgress = 0;
399        }
400    }
401}
402
403int
404tr_dhtAnnounce(tr_torrent *tor, tr_bool announce)
405{
406    int rc, status, numnodes;
407
408    if( !tr_torrentAllowsDHT( tor ) )
409        return -1;
410
411    status = tr_dhtStatus( tor->session, &numnodes );
412    if(status < TR_DHT_POOR ) {
413        tr_tordbg(tor, "DHT not ready (%s, %d nodes)",
414                  tr_dhtPrintableStatus(status), numnodes);
415        return 0;
416    }
417
418    rc = dht_search( dht_socket, tor->info.hash,
419                     announce ? tr_sessionGetPeerPort(session) : 0,
420                     callback, NULL);
421
422    if( rc >= 1 ) {
423        tr_torinf(tor, "Starting DHT announce (%s, %d nodes)",
424                  tr_dhtPrintableStatus(status), numnodes);
425        tor->dhtAnnounceInProgress = TRUE;
426    } else {
427        tr_torerr(tor, "DHT announce failed, errno = %d (%s, %d nodes)",
428                  errno, tr_dhtPrintableStatus(status), numnodes);
429    }
430
431    return 1;
432}
433
434static void
435event_callback(int s, short type, void *ignore UNUSED )
436{
437    time_t tosleep;
438
439    if( dht_periodic(s, type == EV_READ, &tosleep, callback, NULL) < 0 ) {
440        if(errno == EINTR) {
441            tosleep = 0;
442        } else {
443            tr_nerr("DHT", "dht_periodic failed (errno = %d)", errno);
444            if(errno == EINVAL || errno == EFAULT)
445                    abort();
446            tosleep = 1;
447        }
448    }
449
450    /* Being slightly late is fine,
451       and has the added benefit of adding some jitter. */
452    tr_timerAdd( &dht_event, tosleep, tr_cryptoWeakRandInt( 1000000 ) );
453}
454
455void
456dht_hash(void *hash_return, int hash_size,
457         const void *v1, int len1,
458         const void *v2, int len2,
459         const void *v3, int len3)
460{
461    unsigned char sha1[SHA_DIGEST_LENGTH];
462    tr_sha1( sha1, v1, len1, v2, len2, v3, len3, NULL );
463    memset( hash_return, 0, hash_size );
464    memcpy( hash_return, sha1, MIN( hash_size, SHA_DIGEST_LENGTH ) );
465}
466
467int
468dht_random_bytes( void * buf, size_t size )
469{
470    tr_cryptoRandBuf( buf, size );
471    return size;
472}
473
474#endif
Note: See TracBrowser for help on using the repository browser.