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

Last change on this file since 9836 was 9836, checked in by charles, 12 years ago

(trunk libT) fix various minor compiler warnings that show up when you build libtransmission with NDEBUG defined

File size: 19.8 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    if(dht6_socket > 0)
474        tr_netCloseSocket( dht6_socket );
475
476    tr_ndbg("DHT", "Done uninitializing DHT");
477
478    session = NULL;
479}
480
481tr_bool
482tr_dhtEnabled( tr_session * ss )
483{
484    return ss && ( ss == session );
485}
486
487struct getstatus_closure
488{
489    int af;
490    sig_atomic_t status;
491    sig_atomic_t count;
492};
493
494static void
495getstatus( void * cl )
496{
497    struct getstatus_closure * closure = cl;
498    int good, dubious, incoming;
499
500    dht_nodes( closure->af, &good, &dubious, NULL, &incoming );
501
502    closure->count = good + dubious;
503
504    if( good < 4 || good + dubious <= 8 )
505        closure->status = TR_DHT_BROKEN;
506    else if( good < 40 )
507        closure->status = TR_DHT_POOR;
508    else if( incoming < 8 )
509        closure->status = TR_DHT_FIREWALLED;
510    else
511        closure->status = TR_DHT_GOOD;
512}
513
514int
515tr_dhtStatus( tr_session * ss, int af, int * nodes_return )
516{
517    struct getstatus_closure closure = { af, -1, -1 };
518
519    if( !tr_dhtEnabled( ss ) ||
520        (af == AF_INET && dht_socket < 0) ||
521        (af == AF_INET6 && dht6_socket < 0) ) {
522        if( nodes_return )
523            *nodes_return = 0;
524        return TR_DHT_STOPPED;
525    }
526
527    tr_runInEventThread( ss, getstatus, &closure );
528    while( closure.status < 0 )
529        tr_wait( 10 /*msec*/ );
530
531    if( nodes_return )
532        *nodes_return = closure.count;
533
534    return closure.status;
535}
536
537tr_port
538tr_dhtPort( tr_session *ss )
539{
540    return tr_dhtEnabled( ss ) ? dht_port : 0;
541}
542
543int
544tr_dhtAddNode( tr_session       * ss,
545               const tr_address * address,
546               tr_port            port,
547               tr_bool            bootstrap )
548{
549    int af = address->type == TR_AF_INET ? AF_INET : AF_INET6;
550
551    if( !tr_dhtEnabled( ss ) )
552        return 0;
553
554    /* Since we don't want to abuse our bootstrap nodes,
555     * we don't ping them if the DHT is in a good state. */
556
557    if(bootstrap) {
558        if(tr_dhtStatus(ss, af, NULL) >= TR_DHT_FIREWALLED)
559            return 0;
560    }
561
562    if( address->type == TR_AF_INET ) {
563        struct sockaddr_in sin;
564        memset(&sin, 0, sizeof(sin));
565        sin.sin_family = AF_INET;
566        memcpy(&sin.sin_addr, &address->addr.addr4, 4);
567        sin.sin_port = htons(port);
568        dht_ping_node((struct sockaddr*)&sin, sizeof(sin));
569        return 1;
570    } else if( address->type == TR_AF_INET6 ) {
571        struct sockaddr_in6 sin6;
572        memset(&sin6, 0, sizeof(sin6));
573        sin6.sin6_family = AF_INET6;
574        memcpy(&sin6.sin6_addr, &address->addr.addr6, 16);
575        sin6.sin6_port = htons(port);
576        dht_ping_node((struct sockaddr*)&sin6, sizeof(sin6));
577        return 1;
578    }
579
580    return 0;
581}
582
583const char *
584tr_dhtPrintableStatus(int status)
585{
586    switch(status) {
587    case TR_DHT_STOPPED: return "stopped";
588    case TR_DHT_BROKEN: return "broken";
589    case TR_DHT_POOR: return "poor";
590    case TR_DHT_FIREWALLED: return "firewalled";
591    case TR_DHT_GOOD: return "good";
592    default: return "???";
593    }
594}
595
596static void
597callback( void *ignore UNUSED, int event,
598          unsigned char *info_hash, void *data, size_t data_len )
599{
600    if( event == DHT_EVENT_VALUES || event == DHT_EVENT_VALUES6 ) {
601        tr_torrent *tor;
602        tr_globalLock( session );
603        tor = tr_torrentFindFromHash( session, info_hash );
604        if( tor && tr_torrentAllowsDHT( tor ))
605        {
606            size_t i, n;
607            tr_pex * pex;
608            if( event == DHT_EVENT_VALUES )
609                pex = tr_peerMgrCompactToPex(data, data_len, NULL, 0, &n);
610            else
611                pex = tr_peerMgrCompact6ToPex(data, data_len, NULL, 0, &n);
612            for( i=0; i<n; ++i )
613                tr_peerMgrAddPex( tor, TR_PEER_FROM_DHT, pex+i );
614            tr_free(pex);
615            tr_torinf(tor, "Learned %d%s peers from DHT",
616                      (int)n,
617                      event == DHT_EVENT_VALUES6 ? " IPv6" : "");
618        }
619        tr_globalUnlock( session );
620    } else if( event == DHT_EVENT_SEARCH_DONE ||
621               event == DHT_EVENT_SEARCH_DONE6) {
622        tr_torrent * tor = tr_torrentFindFromHash( session, info_hash );
623        if( tor ) {
624            if( event == DHT_EVENT_SEARCH_DONE ) {
625                tr_torinf(tor, "DHT announce done");
626                tor->dhtAnnounceInProgress = 0;
627            } else {
628                tr_torinf(tor, "IPv6 DHT announce done");
629                tor->dhtAnnounce6InProgress = 0;
630            }
631        }
632    }
633}
634
635int
636tr_dhtAnnounce(tr_torrent *tor, int af, tr_bool announce)
637{
638    int rc, status, numnodes, ret = 0;
639
640    if( !tr_torrentAllowsDHT( tor ) )
641        return -1;
642
643    status = tr_dhtStatus( tor->session, af, &numnodes );
644
645    if( status == TR_DHT_STOPPED ) {
646        /* Let the caller believe everything is all right. */
647        return 1;
648    }
649
650    if(status >= TR_DHT_POOR ) {
651        rc = dht_search( tor->info.hash,
652                         announce ? tr_sessionGetPeerPort(session) : 0,
653                         af, callback, NULL);
654        if( rc >= 1 ) {
655            tr_torinf(tor, "Starting%s DHT announce (%s, %d nodes)",
656                      af == AF_INET6 ? " IPv6" : "",
657                      tr_dhtPrintableStatus(status), numnodes);
658            if(af == AF_INET)
659                tor->dhtAnnounceInProgress = TRUE;
660            else
661                tor->dhtAnnounce6InProgress = TRUE;
662            ret = 1;
663        } else {
664            tr_torerr(tor, "%sDHT announce failed, errno = %d (%s, %d nodes)",
665                      af == AF_INET6 ? "IPv6 " : "",
666                      errno, tr_dhtPrintableStatus(status), numnodes);
667        }
668    } else {
669        tr_tordbg(tor, "%sDHT not ready (%s, %d nodes)",
670                  af == AF_INET6 ? "IPv6 " : "",
671                  tr_dhtPrintableStatus(status), numnodes);
672    }
673
674    return ret;
675}
676
677static void
678event_callback(int s, short type, void *ignore UNUSED )
679{
680    struct event *event = (s == dht_socket) ? &dht_event : &dht6_event;
681    time_t tosleep;
682    static int count = 0;
683
684    if( dht_periodic( type == EV_READ, &tosleep, callback, NULL) < 0 ) {
685        if(errno == EINTR) {
686            tosleep = 0;
687        } else {
688            tr_nerr("DHT", "dht_periodic failed (errno = %d)", errno);
689            if(errno == EINVAL || errno == EFAULT)
690                    abort();
691            tosleep = 1;
692        }
693    }
694
695    /* Only do this once in a while.  Counting rather than measuring time
696       avoids a system call. */
697    count++;
698    if(count >= 128) {
699        rebind_ipv6(0);
700        count = 0;
701    }
702
703    /* Being slightly late is fine,
704       and has the added benefit of adding some jitter. */
705    tr_timerAdd( event, tosleep, tr_cryptoWeakRandInt( 1000000 ) );
706}
707
708void
709dht_hash(void *hash_return, int hash_size,
710         const void *v1, int len1,
711         const void *v2, int len2,
712         const void *v3, int len3)
713{
714    unsigned char sha1[SHA_DIGEST_LENGTH];
715    tr_sha1( sha1, v1, len1, v2, len2, v3, len3, NULL );
716    memset( hash_return, 0, hash_size );
717    memcpy( hash_return, sha1, MIN( hash_size, SHA_DIGEST_LENGTH ) );
718}
719
720int
721dht_random_bytes( void * buf, size_t size )
722{
723    tr_cryptoRandBuf( buf, size );
724    return size;
725}
Note: See TracBrowser for help on using the repository browser.