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

Last change on this file since 8483 was 8483, checked in by charles, 13 years ago

(trunk libT) fix a couple of dead assignments, and a possible null pointer dereference, found by clang

File size: 10.1 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
53static int dht_socket;
54static struct event dht_event;
55static tr_port dht_port;
56static unsigned char myid[20];
57static tr_session *session = NULL;
58
59static void event_callback(int s, short type, void *ignore);
60
61struct bootstrap_closure {
62    tr_session *session;
63    uint8_t *nodes;
64    size_t len;
65};
66
67static void
68dht_bootstrap(void *closure)
69{
70    struct bootstrap_closure *cl = closure;
71    size_t i;
72
73    if(session != cl->session)
74        return;
75
76    for(i = 0; i < cl->len; i += 6)
77    {
78        struct timeval tv;
79        tr_port port;
80        struct tr_address addr;
81        int status;
82
83        memset(&addr, 0, sizeof(addr));
84        addr.type = TR_AF_INET;
85        memcpy(&addr.addr.addr4, &cl->nodes[i], 4);
86        memcpy(&port, &cl->nodes[i + 4], 2);
87        port = ntohs(port);
88        /* There's no race here -- if we uninit between the test and the
89           AddNode, the AddNode will be ignored. */
90        status = tr_dhtStatus(cl->session, NULL);
91        if(status == TR_DHT_STOPPED || status >= TR_DHT_FIREWALLED)
92            break;
93        tr_dhtAddNode(cl->session, &addr, port, 1);
94        tv.tv_sec = 2 + tr_cryptoWeakRandInt( 5 );
95        tv.tv_usec = tr_cryptoWeakRandInt( 1000000 );
96        select(0, NULL, NULL, NULL, &tv);
97    }
98    tr_free( cl->nodes );
99    tr_free( closure );
100}
101
102int
103tr_dhtInit(tr_session *ss)
104{
105    struct sockaddr_in sin;
106    struct timeval tv;
107    tr_benc benc;
108    int rc;
109    tr_bool have_id = FALSE;
110    char * dat_file;
111    uint8_t * nodes = NULL;
112    const uint8_t * raw;
113    size_t len;
114    char v[5];
115
116    if( session ) /* already initialized */
117        return -1;
118
119    dht_socket = socket(PF_INET, SOCK_DGRAM, 0);
120    if(dht_socket < 0)
121        return -1;
122
123    dht_port = tr_sessionGetPeerPort(ss);
124    if(dht_port <= 0)
125        return -1;
126
127    memset(&sin, 0, sizeof(sin));
128    sin.sin_family = AF_INET;
129    sin.sin_port = htons(dht_port);
130    rc = bind(dht_socket, (struct sockaddr*)&sin, sizeof(sin));
131    if(rc < 0)
132        goto fail;
133
134    if( getenv( "TR_DHT_VERBOSE" ) != NULL )
135        dht_debug = stderr;
136
137    dat_file = tr_buildPath( ss->configDir, "dht.dat", NULL );
138    rc = tr_bencLoadFile(dat_file, &benc);
139    tr_free( dat_file );
140    if(rc == 0) {
141        if(( have_id = tr_bencDictFindRaw( &benc, "id", &raw, &len ) && len==20 ))
142            memcpy( myid, raw, len );
143        if( tr_bencDictFindRaw( &benc, "nodes", &raw, &len ) && !(len%6) )
144            nodes = tr_memdup( raw, len );
145        tr_bencFree( &benc );
146    }
147
148    if(!have_id) {
149        /* Note that DHT ids need to be distributed uniformly,
150         * so it should be something truly random. */
151        tr_cryptoRandBuf( myid, 20 );
152    }
153
154    v[0] = 'T';
155    v[1] = 'R';
156    v[2] = (SVN_REVISION_NUM >> 8) & 0xFF; 
157    v[3] = SVN_REVISION_NUM & 0xFF; 
158    rc = dht_init( dht_socket, myid, (const unsigned char*)v );
159    if(rc < 0)
160        goto fail;
161
162    session = ss;
163
164    if(nodes) {
165        struct bootstrap_closure * cl = tr_new( struct bootstrap_closure, 1 );
166        cl->session = session;
167        cl->nodes = nodes;
168        cl->len = len;
169        tr_threadNew( dht_bootstrap, cl );
170    }
171
172    tv.tv_sec = 0;
173    tv.tv_usec = tr_cryptoWeakRandInt( 1000000 );
174    event_set( &dht_event, dht_socket, EV_READ, event_callback, NULL );
175    event_add( &dht_event, &tv );
176
177    return 1;
178
179    fail:
180    {
181        const int save = errno;
182        close(dht_socket);
183        dht_socket = -1;
184        session = NULL;
185        errno = save;
186    }
187
188    return -1;
189}
190
191void
192tr_dhtUninit(tr_session *ss)
193{
194    if(session != ss)
195        return;
196
197    event_del(&dht_event);
198
199    /* Since we only save known good nodes, avoid erasing older data if we
200       don't know enough nodes. */
201    if(tr_dhtStatus(ss, NULL) >= TR_DHT_FIREWALLED) {
202        tr_benc benc;
203        struct sockaddr_in sins[300];
204        char compact[300 * 6];
205        char *dat_file;
206        int i;
207        int n = dht_get_nodes(sins, 300);
208        int j = 0;
209        for( i=0; i<n; ++i ) {
210            memcpy( compact + j, &sins[i].sin_addr, 4 );
211            memcpy( compact + j + 4, &sins[i].sin_port, 2 );
212            j += 6;
213        }
214        tr_bencInitDict( &benc, 2 );
215        tr_bencDictAddRaw( &benc, "id", myid, 20 );
216        tr_bencDictAddRaw( &benc, "nodes", compact, j );
217        dat_file = tr_buildPath( ss->configDir, "dht.dat", NULL );
218        tr_bencSaveFile( dat_file, &benc );
219        tr_free( dat_file );
220    }
221
222    dht_uninit( dht_socket, 0 );
223    EVUTIL_CLOSESOCKET( dht_socket );
224
225    session = NULL;
226}
227
228tr_bool
229tr_dhtEnabled( const tr_session * ss )
230{
231    return ss && ( ss == session );
232}
233
234struct getstatus_closure
235{
236    sig_atomic_t status;
237    sig_atomic_t count;
238};
239
240static void
241getstatus( void * closure )
242{
243    struct getstatus_closure * ret = closure;
244    int good, dubious, incoming;
245
246    dht_nodes( &good, &dubious, NULL, &incoming );
247
248    ret->count = good + dubious;
249
250    if( good < 4 || good + dubious <= 8 )
251        ret->status = TR_DHT_BROKEN;
252    else if( good < 40 )
253        ret->status = TR_DHT_POOR;
254    else if( incoming < 8 )
255        ret->status = TR_DHT_FIREWALLED;
256    else
257        ret->status = TR_DHT_GOOD;
258}
259
260int
261tr_dhtStatus( tr_session * ss, int * nodes_return )
262{
263    struct getstatus_closure ret = { -1, - 1 };
264
265    if( !tr_dhtEnabled( ss ) )
266        return TR_DHT_STOPPED;
267
268    tr_runInEventThread( ss, getstatus, &ret );
269    while( ret.status < 0 )
270        tr_wait( 10 /*msec*/ );
271
272    if( nodes_return )
273        *nodes_return = ret.count;
274
275    return ret.status;
276}
277
278tr_port
279tr_dhtPort( const tr_session *ss )
280{
281    return tr_dhtEnabled( ss ) ? dht_port : 0;
282}
283
284int
285tr_dhtAddNode(tr_session *ss, tr_address *address, tr_port port, tr_bool bootstrap)
286{
287    struct sockaddr_in sin;
288
289    if( !tr_dhtEnabled( ss ) )
290        return 0;
291
292    if( address->type != TR_AF_INET )
293        return 0;
294
295    /* Since we don't want to abuse our bootstrap nodes,
296     * we don't ping them if the DHT is in a good state. */
297    if(bootstrap) {
298        if(tr_dhtStatus(ss, NULL) >= TR_DHT_FIREWALLED)
299            return 0;
300    }
301
302    memset(&sin, 0, sizeof(sin));
303    sin.sin_family = AF_INET;
304    memcpy(&sin.sin_addr, &address->addr.addr4, 4);
305    sin.sin_port = htons(port);
306    dht_ping_node(dht_socket, &sin);
307
308    return 1;
309}
310
311static void
312callback( void *ignore UNUSED, int event,
313          unsigned char *info_hash, void *data, size_t data_len )
314{
315    if( event == DHT_EVENT_VALUES )
316    {
317        tr_torrent *tor;
318        tr_globalLock( session );
319        tor = tr_torrentFindFromHash( session, info_hash );
320        if( tor && tr_torrentAllowsDHT( tor ))
321        {
322            size_t i, n;
323            tr_pex * pex = tr_peerMgrCompactToPex(data, data_len, NULL, 0, &n);
324            for( i=0; i<n; ++i )
325                tr_peerMgrAddPex( tor, TR_PEER_FROM_DHT, pex+i );
326            tr_free(pex);
327        }
328        tr_globalUnlock( session );
329    }
330    else if( event == DHT_EVENT_SEARCH_DONE )
331    {
332        tr_torrent * tor = tr_torrentFindFromHash( session, info_hash );
333        if( tor )
334            tor->dhtAnnounceInProgress = 0;
335    }
336}
337
338int
339tr_dhtAnnounce(tr_torrent *tor, tr_bool announce)
340{
341    if( !tr_torrentAllowsDHT( tor ) )
342        return -1;
343
344    if( tr_dhtStatus( tor->session, NULL ) < TR_DHT_POOR )
345        return 0;
346
347    dht_search( dht_socket, tor->info.hash,
348                announce ? tr_sessionGetPeerPort(session) : 0,
349                callback, NULL);
350
351    tor->dhtAnnounceInProgress = TRUE;
352    return 1;
353}
354
355static void
356event_callback(int s, short type, void *ignore UNUSED )
357{
358    time_t tosleep;
359    struct timeval tv;
360
361    if( dht_periodic(s, type == EV_READ, &tosleep, callback, NULL) < 0 ) {
362        if(errno == EINTR) {
363            tosleep = 0;
364        } else {
365            perror("dht_periodic");
366            if(errno == EINVAL || errno == EFAULT)
367                    abort();
368            tosleep = 1;
369        }
370    }
371
372    /* Being slightly late is fine,
373       and has the added benefit of adding some jitter. */
374    tv.tv_sec = tosleep;
375    tv.tv_usec = tr_cryptoWeakRandInt( 1000000 );
376    event_add(&dht_event, &tv);
377}
378
379void
380dht_hash(void *hash_return, int hash_size,
381         const void *v1, int len1,
382         const void *v2, int len2,
383         const void *v3, int len3)
384{
385    unsigned char sha1[SHA_DIGEST_LENGTH];
386    tr_sha1( sha1, v1, len1, v2, len2, v3, len3, NULL );
387    memset( hash_return, 0, hash_size );
388    memcpy( hash_return, sha1, MIN( hash_size, SHA_DIGEST_LENGTH ) );
389}
390
391int
392dht_random_bytes( void * buf, size_t size )
393{
394    tr_cryptoRandBuf( buf, size );
395    return size;
396}
Note: See TracBrowser for help on using the repository browser.