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

Last change on this file since 10201 was 10201, checked in by charles, 11 years ago

(trunk libT) #2903 "Don't send DHT port messages to peers if we didn't advertise support for DHT in the handshake" -- fixed in trunk for 1.90

File size: 19.9 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 <netdb.h>
34#include <unistd.h> /* close() */
35
36/* third party */
37#include <event.h>
38#include <dht/dht.h>
39
40/* libT */
41#include "transmission.h"
42#include "bencode.h"
43#include "crypto.h"
44#include "net.h"
45#include "peer-mgr.h" /* tr_peerMgrCompactToPex() */
46#include "platform.h" /* tr_threadNew() */
47#include "session.h"
48#include "torrent.h" /* tr_torrentFindFromHash() */
49#include "tr-dht.h"
50#include "trevent.h" /* tr_runInEventThread() */
51#include "utils.h"
52#include "version.h"
53
54static int dht_socket = -1, dht6_socket = -1;
55static struct event dht_event, dht6_event;
56static tr_port dht_port;
57static unsigned char myid[20];
58static tr_session *session = NULL;
59
60static void event_callback(int s, short type, void *ignore);
61
62struct bootstrap_closure {
63    tr_session *session;
64    uint8_t *nodes;
65    uint8_t *nodes6;
66    size_t len, len6;
67};
68
69static int
70bootstrap_done( tr_session *session, int af )
71{
72    int status;
73
74    if(af == 0)
75        return
76            bootstrap_done(session, AF_INET) &&
77            bootstrap_done(session, AF_INET6);
78
79    status = tr_dhtStatus(session, af, NULL);
80    return status == TR_DHT_STOPPED || status >= TR_DHT_FIREWALLED;
81}
82
83static void
84nap( int roughly )
85{
86    struct timeval tv;
87    tv.tv_sec = roughly / 2 + tr_cryptoWeakRandInt( roughly );
88    tv.tv_usec = tr_cryptoWeakRandInt( 1000000 );
89    select( 0, NULL, NULL, NULL, &tv );
90}
91
92static int
93bootstrap_af(tr_session *session)
94{
95    if( bootstrap_done(session, AF_INET6) )
96        return AF_INET;
97    else if ( bootstrap_done(session, AF_INET) )
98        return AF_INET6;
99    else
100        return 0;
101}
102
103static void
104bootstrap_from_name( const char *name, short int port, int af )
105{
106    struct addrinfo hints, *info, *infop;
107    char pp[10];
108    int rc;
109
110    memset(&hints, 0, sizeof(hints));
111    hints.ai_socktype = SOCK_DGRAM;
112    hints.ai_family = af;
113    /* No, just passing p + 1 to gai won't work. */
114    tr_snprintf(pp, sizeof(pp), "%d", port);
115
116    rc = getaddrinfo(name, pp, &hints, &info);
117    if(rc != 0) {
118        tr_nerr("DHT", "%s:%s: %s", name, pp, gai_strerror(rc));
119        return;
120    }
121
122    infop = info;
123    while(infop) {
124        dht_ping_node(infop->ai_addr, infop->ai_addrlen);
125
126        nap(15);
127
128        if(bootstrap_done(session, af))
129            break;
130        infop = infop->ai_next;
131    }
132    freeaddrinfo(info);
133}
134
135static void
136dht_bootstrap(void *closure)
137{
138    struct bootstrap_closure *cl = closure;
139    int i;
140    int num = cl->len / 6, num6 = cl->len6 / 18;
141
142    if(session != cl->session)
143        return;
144
145    if(cl->len > 0)
146        tr_ninf( "DHT", "Bootstrapping from %d nodes", num );
147
148    if(cl->len6 > 0)
149        tr_ninf( "DHT", "Bootstrapping from %d IPv6 nodes", num6 );
150
151
152    for(i = 0; i < MAX(num, num6); i++) {
153        if( i < num && !bootstrap_done(cl->session, AF_INET) ) {
154            tr_port port;
155            struct tr_address addr;
156
157            memset(&addr, 0, sizeof(addr));
158            addr.type = TR_AF_INET;
159            memcpy(&addr.addr.addr4, &cl->nodes[i * 6], 4);
160            memcpy(&port, &cl->nodes[i * 6 + 4], 2);
161            port = ntohs(port);
162            tr_dhtAddNode(cl->session, &addr, port, 1);
163        }
164        if( i < num6 && !bootstrap_done(cl->session, AF_INET6) ) {
165            tr_port port;
166            struct tr_address addr;
167
168            memset(&addr, 0, sizeof(addr));
169            addr.type = TR_AF_INET6;
170            memcpy(&addr.addr.addr6, &cl->nodes6[i * 18], 16);
171            memcpy(&port, &cl->nodes6[i * 18 + 16], 2);
172            port = ntohs(port);
173            tr_dhtAddNode(cl->session, &addr, port, 1);
174        }
175
176        /* Our DHT code is able to take up to 9 nodes in a row without
177           dropping any.  After that, it takes some time to split buckets.
178           So ping the first 8 nodes quickly, then slow down. */
179        if(i < 8)
180            nap(2);
181        else
182            nap(15);
183
184        if(bootstrap_done( session, 0 ))
185            break;
186    }
187
188    if(!bootstrap_done(cl->session, 0)) {
189        char *bootstrap_file;
190        FILE *f = NULL;
191
192        bootstrap_file =
193            tr_buildPath(cl->session->configDir, "dht.bootstrap", NULL);
194
195        if(bootstrap_file)
196            f = fopen(bootstrap_file, "r");
197        if(f != NULL) {
198            tr_ninf("DHT", "Attempting manual bootstrap");
199            for(;;) {
200                char buf[201];
201                char *p;
202                int port = 0;
203
204                p = fgets(buf, 200, f);
205                if( p == NULL )
206                    break;
207
208                p = memchr(buf, ' ', strlen(buf));
209                if(p != NULL)
210                    port = atoi(p + 1);
211                if(p == NULL || port <= 0 || port >= 0x10000) {
212                    tr_nerr("DHT", "Couldn't parse %s", buf);
213                    continue;
214                }
215
216                *p = '\0';
217
218                bootstrap_from_name( buf, port, bootstrap_af(session) );
219
220                if(bootstrap_done(cl->session, 0))
221                    break;
222            }
223        }
224
225        tr_free( bootstrap_file );
226    }
227
228    /* We really don't want to abuse our bootstrap nodes.
229       Be glacially slow. */
230    if(!bootstrap_done(cl->session, 0))
231        nap(30);
232
233    if(!bootstrap_done(cl->session, 0)) {
234        tr_ninf("DHT", "Attempting bootstrap from dht.transmissionbt.com");
235        bootstrap_from_name( "dht.transmissionbt.com", 6881,
236                             bootstrap_af(session) );
237    }
238
239    if( cl->nodes )
240        tr_free( cl->nodes );
241    if( cl->nodes6 )
242        tr_free( cl->nodes6 );
243    tr_free( closure );
244    tr_ndbg( "DHT", "Finished bootstrapping" );
245}
246
247/* BEP-32 has a rather nice explanation of why we need to bind to one
248   IPv6 address, if I may say so myself. */
249
250static int
251rebind_ipv6(int force)
252{
253    struct sockaddr_in6 sin6;
254    const unsigned char *ipv6 = tr_globalIPv6();
255    static unsigned char *last_bound = NULL;
256    int rc;
257
258    if(dht6_socket < 0)
259        return 0;
260
261    if(!force &&
262       ((ipv6 == NULL && last_bound == NULL) ||
263        (ipv6 != NULL && last_bound != NULL &&
264         memcmp(ipv6, last_bound, 16) == 0)))
265        return 0;
266
267    memset(&sin6, 0, sizeof(sin6));
268    sin6.sin6_family = AF_INET6;
269    if(ipv6)
270        memcpy(&sin6.sin6_addr, ipv6, 16);
271    sin6.sin6_port = htons(dht_port);
272    rc = bind(dht6_socket, (struct sockaddr*)&sin6, sizeof(sin6));
273
274    if(last_bound)
275        free(last_bound);
276    last_bound = NULL;
277
278    if(rc >= 0 && ipv6) {
279        last_bound = malloc(16);
280        if(last_bound)
281            memcpy(last_bound, ipv6, 16);
282    }
283    return rc;
284}
285
286int
287tr_dhtInit(tr_session *ss, const tr_address * tr_addr)
288{
289    struct sockaddr_in sin;
290    tr_benc benc;
291    int rc;
292    tr_bool have_id = FALSE;
293    char * dat_file;
294    uint8_t * nodes = NULL, * nodes6 = NULL;
295    const uint8_t * raw;
296    size_t len, len6;
297    char v[5];
298    struct bootstrap_closure * cl;
299
300    if( session ) /* already initialized */
301        return -1;
302
303    dht_port = tr_sessionGetPeerPort(ss);
304    if(dht_port <= 0)
305        return -1;
306
307    tr_ndbg( "DHT", "Initializing DHT" );
308
309    dht_socket = socket(PF_INET, SOCK_DGRAM, 0);
310    if(dht_socket < 0)
311        goto fail;
312
313    memset(&sin, 0, sizeof(sin));
314    sin.sin_family = AF_INET;
315    memcpy(&sin.sin_addr, &tr_addr->addr.addr4, sizeof (struct in_addr));
316    sin.sin_port = htons(dht_port);
317    rc = bind(dht_socket, (struct sockaddr*)&sin, sizeof(sin));
318    if(rc < 0)
319        goto fail;
320
321    if(tr_globalIPv6()) {
322        int one = 1;
323        dht6_socket = socket(PF_INET6, SOCK_DGRAM, 0);
324        if(dht6_socket < 0)
325            goto fail;
326
327#ifdef IPV6_V6ONLY
328        /* Since we always open an IPv4 socket on the same port, this
329           shouldn't matter.  But I'm superstitious. */
330        setsockopt(dht6_socket, IPPROTO_IPV6, IPV6_V6ONLY,
331                   &one, sizeof(one));
332#endif
333
334        rebind_ipv6(1);
335
336    }
337
338    if( getenv( "TR_DHT_VERBOSE" ) != NULL )
339        dht_debug = stderr;
340
341    dat_file = tr_buildPath( ss->configDir, "dht.dat", NULL );
342    rc = tr_bencLoadFile( &benc, TR_FMT_BENC, dat_file );
343    tr_free( dat_file );
344    if(rc == 0) {
345        have_id = tr_bencDictFindRaw(&benc, "id", &raw, &len);
346        if( have_id && len==20 )
347            memcpy( myid, raw, len );
348        if( dht_socket >= 0 &&
349            tr_bencDictFindRaw( &benc, "nodes", &raw, &len ) && !(len%6) ) {
350                nodes = tr_memdup( raw, len );
351        }
352        if( dht6_socket > 0 &&
353            tr_bencDictFindRaw( &benc, "nodes6", &raw, &len6 ) && !(len6%18) ) {
354            nodes6 = tr_memdup( raw, len6 );
355        }
356        tr_bencFree( &benc );
357    }
358
359    if(nodes == NULL)
360        len = 0;
361    if(nodes6 == NULL)
362        len6 = 0;
363
364    if( have_id )
365        tr_ninf( "DHT", "Reusing old id" );
366    else {
367        /* Note that DHT ids need to be distributed uniformly,
368         * so it should be something truly random. */
369        tr_ninf( "DHT", "Generating new id" );
370        tr_cryptoRandBuf( myid, 20 );
371    }
372
373    v[0] = 'T';
374    v[1] = 'R';
375    v[2] = (SVN_REVISION_NUM >> 8) & 0xFF;
376    v[3] = SVN_REVISION_NUM & 0xFF;
377    rc = dht_init( dht_socket, dht6_socket, myid, (const unsigned char*)v );
378    if(rc < 0)
379        goto fail;
380
381    session = ss;
382
383    cl = tr_new( struct bootstrap_closure, 1 );
384    cl->session = session;
385    cl->nodes = nodes;
386    cl->nodes6 = nodes6;
387    cl->len = len;
388    cl->len6 = len6;
389    tr_threadNew( dht_bootstrap, cl );
390
391    event_set( &dht_event, dht_socket, EV_READ, event_callback, NULL );
392    tr_timerAdd( &dht_event, 0, tr_cryptoWeakRandInt( 1000000 ) );
393
394    if( dht6_socket >= 0 )
395    {
396        event_set( &dht6_event, dht6_socket, EV_READ, event_callback, NULL );
397        tr_timerAdd( &dht6_event, 0, tr_cryptoWeakRandInt( 1000000 ) );
398    }
399
400    tr_ndbg( "DHT", "DHT initialized" );
401
402    return 1;
403
404    fail:
405    {
406        const int save = errno;
407        close(dht_socket);
408        if( dht6_socket >= 0 )
409            close(dht6_socket);
410        dht_socket = dht6_socket = -1;
411        session = NULL;
412        tr_ndbg( "DHT", "DHT initialization failed (errno = %d)", save );
413        errno = save;
414    }
415
416    return -1;
417}
418
419void
420tr_dhtUninit(tr_session *ss)
421{
422    if(session != ss)
423        return;
424
425    tr_ndbg( "DHT", "Uninitializing DHT" );
426
427    event_del( &dht_event );
428
429    if( dht6_socket >= 0 )
430        event_del( &dht6_event );
431
432    /* Since we only save known good nodes, avoid erasing older data if we
433       don't know enough nodes. */
434    if(tr_dhtStatus(ss, AF_INET, NULL) < TR_DHT_FIREWALLED)
435        tr_ninf( "DHT", "Not saving nodes, DHT not ready" );
436    else {
437        tr_benc benc;
438        struct sockaddr_in sins[300];
439        struct sockaddr_in6 sins6[300];
440        char compact[300 * 6], compact6[300 * 18];
441        char *dat_file;
442        int i, j, num = 300, num6 = 300;
443        int n = dht_get_nodes(sins, &num, sins6, &num6);
444
445        tr_ninf( "DHT", "Saving %d (%d + %d) nodes", n, num, num6 );
446
447        j = 0;
448        for( i=0; i<num; ++i ) {
449            memcpy( compact + j, &sins[i].sin_addr, 4 );
450            memcpy( compact + j + 4, &sins[i].sin_port, 2 );
451            j += 6;
452        }
453        j = 0;
454        for( i=0; i<num6; ++i ) {
455            memcpy( compact6 + j, &sins6[i].sin6_addr, 16 );
456            memcpy( compact6 + j + 16, &sins6[i].sin6_port, 2 );
457            j += 18;
458        }
459        tr_bencInitDict( &benc, 3 );
460        tr_bencDictAddRaw( &benc, "id", myid, 20 );
461        if(num > 0)
462            tr_bencDictAddRaw( &benc, "nodes", compact, num * 6 );
463        if(num6 > 0)
464            tr_bencDictAddRaw( &benc, "nodes6", compact6, num6 * 18 );
465        dat_file = tr_buildPath( ss->configDir, "dht.dat", NULL );
466        tr_bencToFile( &benc, TR_FMT_BENC, dat_file );
467        tr_bencFree( &benc );
468        tr_free( dat_file );
469    }
470
471    dht_uninit( 1 );
472    tr_netCloseSocket( dht_socket );
473    dht_socket = -1;
474    if(dht6_socket > 0) {
475        tr_netCloseSocket( dht6_socket );
476        dht6_socket = -1;
477    }
478
479    tr_ndbg("DHT", "Done uninitializing DHT");
480
481    session = NULL;
482}
483
484tr_bool
485tr_dhtEnabled( const tr_session * ss )
486{
487    return ss && ( ss == session );
488}
489
490struct getstatus_closure
491{
492    int af;
493    sig_atomic_t status;
494    sig_atomic_t count;
495};
496
497static void
498getstatus( void * cl )
499{
500    struct getstatus_closure * closure = cl;
501    int good, dubious, incoming;
502
503    dht_nodes( closure->af, &good, &dubious, NULL, &incoming );
504
505    closure->count = good + dubious;
506
507    if( good < 4 || good + dubious <= 8 )
508        closure->status = TR_DHT_BROKEN;
509    else if( good < 40 )
510        closure->status = TR_DHT_POOR;
511    else if( incoming < 8 )
512        closure->status = TR_DHT_FIREWALLED;
513    else
514        closure->status = TR_DHT_GOOD;
515}
516
517int
518tr_dhtStatus( tr_session * session, int af, int * nodes_return )
519{
520    struct getstatus_closure closure = { af, -1, -1 };
521
522    if( !tr_dhtEnabled( session ) ||
523        (af == AF_INET && dht_socket < 0) ||
524        (af == AF_INET6 && dht6_socket < 0) ) {
525        if( nodes_return )
526            *nodes_return = 0;
527        return TR_DHT_STOPPED;
528    }
529
530    tr_runInEventThread( session, getstatus, &closure );
531    while( closure.status < 0 )
532        tr_wait_msec( 10 /*msec*/ );
533
534    if( nodes_return )
535        *nodes_return = closure.count;
536
537    return closure.status;
538}
539
540tr_port
541tr_dhtPort( tr_session *ss )
542{
543    return tr_dhtEnabled( ss ) ? dht_port : 0;
544}
545
546int
547tr_dhtAddNode( tr_session       * ss,
548               const tr_address * address,
549               tr_port            port,
550               tr_bool            bootstrap )
551{
552    int af = address->type == TR_AF_INET ? AF_INET : AF_INET6;
553
554    if( !tr_dhtEnabled( ss ) )
555        return 0;
556
557    /* Since we don't want to abuse our bootstrap nodes,
558     * we don't ping them if the DHT is in a good state. */
559
560    if(bootstrap) {
561        if(tr_dhtStatus(ss, af, NULL) >= TR_DHT_FIREWALLED)
562            return 0;
563    }
564
565    if( address->type == TR_AF_INET ) {
566        struct sockaddr_in sin;
567        memset(&sin, 0, sizeof(sin));
568        sin.sin_family = AF_INET;
569        memcpy(&sin.sin_addr, &address->addr.addr4, 4);
570        sin.sin_port = htons(port);
571        dht_ping_node((struct sockaddr*)&sin, sizeof(sin));
572        return 1;
573    } else if( address->type == TR_AF_INET6 ) {
574        struct sockaddr_in6 sin6;
575        memset(&sin6, 0, sizeof(sin6));
576        sin6.sin6_family = AF_INET6;
577        memcpy(&sin6.sin6_addr, &address->addr.addr6, 16);
578        sin6.sin6_port = htons(port);
579        dht_ping_node((struct sockaddr*)&sin6, sizeof(sin6));
580        return 1;
581    }
582
583    return 0;
584}
585
586const char *
587tr_dhtPrintableStatus(int status)
588{
589    switch(status) {
590    case TR_DHT_STOPPED: return "stopped";
591    case TR_DHT_BROKEN: return "broken";
592    case TR_DHT_POOR: return "poor";
593    case TR_DHT_FIREWALLED: return "firewalled";
594    case TR_DHT_GOOD: return "good";
595    default: return "???";
596    }
597}
598
599static void
600callback( void *ignore UNUSED, int event,
601          unsigned char *info_hash, void *data, size_t data_len )
602{
603    if( event == DHT_EVENT_VALUES || event == DHT_EVENT_VALUES6 ) {
604        tr_torrent *tor;
605        tr_sessionLock( session );
606        tor = tr_torrentFindFromHash( session, info_hash );
607        if( tor && tr_torrentAllowsDHT( tor ))
608        {
609            size_t i, n;
610            tr_pex * pex;
611            if( event == DHT_EVENT_VALUES )
612                pex = tr_peerMgrCompactToPex(data, data_len, NULL, 0, &n);
613            else
614                pex = tr_peerMgrCompact6ToPex(data, data_len, NULL, 0, &n);
615            for( i=0; i<n; ++i )
616                tr_peerMgrAddPex( tor, TR_PEER_FROM_DHT, pex+i );
617            tr_free(pex);
618            tr_tordbg(tor, "Learned %d%s peers from DHT",
619                      (int)n,
620                      event == DHT_EVENT_VALUES6 ? " IPv6" : "");
621        }
622        tr_sessionUnlock( session );
623    } else if( event == DHT_EVENT_SEARCH_DONE ||
624               event == DHT_EVENT_SEARCH_DONE6) {
625        tr_torrent * tor = tr_torrentFindFromHash( session, info_hash );
626        if( tor ) {
627            if( event == DHT_EVENT_SEARCH_DONE ) {
628                tr_torinf(tor, "DHT announce done");
629                tor->dhtAnnounceInProgress = 0;
630            } else {
631                tr_torinf(tor, "IPv6 DHT announce done");
632                tor->dhtAnnounce6InProgress = 0;
633            }
634        }
635    }
636}
637
638int
639tr_dhtAnnounce(tr_torrent *tor, int af, tr_bool announce)
640{
641    int rc, status, numnodes, ret = 0;
642
643    if( !tr_torrentAllowsDHT( tor ) )
644        return -1;
645
646    status = tr_dhtStatus( tor->session, af, &numnodes );
647
648    if( status == TR_DHT_STOPPED ) {
649        /* Let the caller believe everything is all right. */
650        return 1;
651    }
652
653    if(status >= TR_DHT_POOR ) {
654        rc = dht_search( tor->info.hash,
655                         announce ? tr_sessionGetPeerPort(session) : 0,
656                         af, callback, NULL);
657        if( rc >= 1 ) {
658            tr_torinf(tor, "Starting%s DHT announce (%s, %d nodes)",
659                      af == AF_INET6 ? " IPv6" : "",
660                      tr_dhtPrintableStatus(status), numnodes);
661            if(af == AF_INET)
662                tor->dhtAnnounceInProgress = TRUE;
663            else
664                tor->dhtAnnounce6InProgress = TRUE;
665            ret = 1;
666        } else {
667            tr_torerr(tor, "%sDHT announce failed, errno = %d (%s, %d nodes)",
668                      af == AF_INET6 ? "IPv6 " : "",
669                      errno, tr_dhtPrintableStatus(status), numnodes);
670        }
671    } else {
672        tr_tordbg(tor, "%sDHT not ready (%s, %d nodes)",
673                  af == AF_INET6 ? "IPv6 " : "",
674                  tr_dhtPrintableStatus(status), numnodes);
675    }
676
677    return ret;
678}
679
680static void
681event_callback(int s, short type, void *ignore UNUSED )
682{
683    struct event *event = (s == dht_socket) ? &dht_event : &dht6_event;
684    time_t tosleep;
685    static int count = 0;
686
687    if( dht_periodic( type == EV_READ, &tosleep, callback, NULL) < 0 ) {
688        if(errno == EINTR) {
689            tosleep = 0;
690        } else {
691            tr_nerr("DHT", "dht_periodic failed (errno = %d)", errno);
692            if(errno == EINVAL || errno == EFAULT)
693                    abort();
694            tosleep = 1;
695        }
696    }
697
698    /* Only do this once in a while.  Counting rather than measuring time
699       avoids a system call. */
700    count++;
701    if(count >= 128) {
702        rebind_ipv6(0);
703        count = 0;
704    }
705
706    /* Being slightly late is fine,
707       and has the added benefit of adding some jitter. */
708    tr_timerAdd( event, tosleep, tr_cryptoWeakRandInt( 1000000 ) );
709}
710
711void
712dht_hash(void *hash_return, int hash_size,
713         const void *v1, int len1,
714         const void *v2, int len2,
715         const void *v3, int len3)
716{
717    unsigned char sha1[SHA_DIGEST_LENGTH];
718    tr_sha1( sha1, v1, len1, v2, len2, v3, len3, NULL );
719    memset( hash_return, 0, hash_size );
720    memcpy( hash_return, sha1, MIN( hash_size, SHA_DIGEST_LENGTH ) );
721}
722
723int
724dht_random_bytes( void * buf, size_t size )
725{
726    tr_cryptoRandBuf( buf, size );
727    return size;
728}
Note: See TracBrowser for help on using the repository browser.