source: branches/encryption/libtransmission/peer-mgr.c @ 2995

Last change on this file since 2995 was 2995, checked in by charles, 15 years ago

add an `isEncrypted' field to tr_peer_stat, so the GUI can show which connections are encrypted with a lock icon or something

  • Property svn:keywords set to Date Rev Author Id
File size: 24.9 KB
Line 
1/*
2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: peer-mgr.c 2995 2007-09-09 01:52:14Z charles $
11 */
12
13#include <assert.h>
14#include <string.h> /* memcpy, memcmp */
15#include <stdlib.h> /* qsort */
16#include <stdio.h> /* printf */
17
18#include "transmission.h"
19#include "handshake.h"
20#include "completion.h"
21#include "net.h"
22#include "peer-io.h"
23#include "peer-mgr.h"
24#include "peer-mgr-private.h"
25#include "peer-msgs.h"
26#include "ptrarray.h"
27#include "timer.h"
28#include "utils.h"
29
30#define MINUTES_TO_MSEC(N) ((N) * 60 * 1000)
31
32/* how frequently to change which peers are choked */
33#define RECHOKE_PERIOD_SECONDS (MINUTES_TO_MSEC(10))
34
35/* how many downloaders to unchoke per-torrent.
36 * http://wiki.theory.org/BitTorrentSpecification#Choking_and_Optimistic_Unchoking */
37#define NUM_DOWNLOADERS_TO_UNCHOKE 4
38
39/* across all torrents, how many peers maximum do we want connected? */
40#define MAX_CONNECTED_PEERS 80
41
42/**
43***
44**/
45
46struct tr_block
47{
48    unsigned int have          : 1;
49    unsigned int dnd           : 1;
50    unsigned int low_priority  : 1;
51    unsigned int high_priority : 1;
52    uint8_t requestCount;
53    uint8_t scarcity;
54    uint32_t block;
55};
56
57#define MAX_SCARCITY UINT8_MAX
58#define MAX_REQ_COUNT UINT8_MAX
59
60static void
61incrementReqCount( struct tr_block * block )
62{
63    assert( block != NULL );
64
65    if( block->requestCount < MAX_REQ_COUNT )
66        block->requestCount++;
67}
68
69static void
70incrementScarcity( struct tr_block * block )
71{
72    assert( block != NULL );
73
74    if( block->scarcity < MAX_SCARCITY )
75        block->scarcity++;
76}
77
78static int
79compareBlockByIndex( const void * va, const void * vb )
80{
81    const struct tr_block * a = (const struct tr_block *) va;
82    const struct tr_block * b = (const struct tr_block *) vb;
83    return tr_compareUint32( a->block, b->block );
84}
85
86static int
87compareBlockByInterest( const void * va, const void * vb )
88{
89    const struct tr_block * a = (const struct tr_block *) va;
90    const struct tr_block * b = (const struct tr_block *) vb;
91    int i;
92
93    if( a->dnd != b->dnd )
94        return a->dnd ? 1 : -1;
95
96    if( a->have != b->have )
97        return a->have ? 1 : -1;
98
99    if(( i = tr_compareUint8( a->requestCount, b->requestCount )))
100        return i;
101
102    if( a->high_priority != b->high_priority )
103        return a->high_priority ? -1 : 1;
104
105    if( a->low_priority != b->low_priority )
106        return a->low_priority ? 1 : -1;
107
108    if(( i = tr_compareUint16( a->scarcity, b->scarcity )))
109        return i;
110
111    if(( i = tr_compareUint32( a->block, b->block )))
112        return i;
113
114    return 0;
115}
116
117/**
118***
119**/
120
121typedef struct
122{
123    uint8_t hash[SHA_DIGEST_LENGTH];
124    tr_ptrArray * peers; /* tr_peer */
125    tr_timer_tag choke_tag;
126    tr_timer_tag refill_tag;
127    tr_torrent * tor;
128
129    struct tr_block * blocks;
130    uint32_t blockCount;
131
132    struct tr_peerMgr * manager;
133}
134Torrent;
135
136struct tr_peerMgr
137{
138    tr_handle * handle;
139    tr_ptrArray * torrents; /* Torrent */
140    int connectionCount;
141};
142
143/**
144***
145**/
146
147static int
148torrentCompare( const void * va, const void * vb )
149{
150    const Torrent * a = (const Torrent*) va;
151    const Torrent * b = (const Torrent*) vb;
152    return memcmp( a->hash, b->hash, SHA_DIGEST_LENGTH );
153}
154
155static int
156torrentCompareToHash( const void * va, const void * vb )
157{
158    const Torrent * a = (const Torrent*) va;
159    const uint8_t * b_hash = (const uint8_t*) vb;
160    return memcmp( a->hash, b_hash, SHA_DIGEST_LENGTH );
161}
162
163static Torrent*
164getExistingTorrent( tr_peerMgr * manager, const uint8_t * hash )
165{
166    return (Torrent*) tr_ptrArrayFindSorted( manager->torrents,
167                                             hash,
168                                             torrentCompareToHash );
169}
170
171static int chokePulse( void * vtorrent );
172
173static int
174peerCompare( const void * va, const void * vb )
175{
176    const tr_peer * a = (const tr_peer *) va;
177    const tr_peer * b = (const tr_peer *) vb;
178    return memcmp( &a->in_addr, &b->in_addr, sizeof(struct in_addr) );
179}
180
181static int
182peerCompareToAddr( const void * va, const void * vb )
183{
184    const tr_peer * a = (const tr_peer *) va;
185    const struct in_addr * b = (const struct in_addr *) vb;
186    return memcmp( &a->in_addr, b, sizeof(struct in_addr) );
187}
188
189static tr_peer*
190getExistingPeer( Torrent * torrent, const struct in_addr * in_addr )
191{
192    return (tr_peer*) tr_ptrArrayFindSorted( torrent->peers,
193                                             in_addr,
194                                             peerCompareToAddr );
195}
196
197static tr_peer*
198getPeer( Torrent * torrent, const struct in_addr * in_addr )
199{
200    tr_peer * peer = getExistingPeer( torrent, in_addr );
201    if( peer == NULL )
202    {
203        peer = tr_new0( tr_peer, 1 );
204        memcpy( &peer->in_addr, in_addr, sizeof(struct in_addr) );
205        tr_ptrArrayInsertSorted( torrent->peers, peer, peerCompare );
206fprintf( stderr, "getPeer: torrent %p now has %d peers\n", torrent, tr_ptrArraySize(torrent->peers) );
207    }
208    return peer;
209}
210
211static void
212freePeer( tr_peer * peer )
213{
214    tr_peerMsgsFree( peer->msgs );
215    tr_bitfieldFree( peer->have );
216    tr_bitfieldFree( peer->blame );
217    tr_bitfieldFree( peer->banned );
218    tr_peerIoFree( peer->io );
219    tr_free( peer->client );
220    tr_free( peer );
221}
222
223static void
224freeTorrent( tr_peerMgr * manager, Torrent * t )
225{
226    int i, size;
227    tr_peer ** peers;
228
229    assert( manager != NULL );
230    assert( t != NULL );
231    assert( t->peers != NULL );
232
233    peers = (tr_peer **) tr_ptrArrayPeek( t->peers, &size );
234    tr_timerFree( &t->choke_tag );
235    tr_timerFree( &t->refill_tag );
236    for( i=0; i<size; ++i )
237        freePeer( peers[i] );
238    tr_ptrArrayFree( t->peers );
239    tr_ptrArrayRemoveSorted( manager->torrents, t, torrentCompare );
240    tr_free( t->blocks );
241    tr_free( t );
242}
243
244/**
245***
246**/
247
248tr_peerMgr*
249tr_peerMgrNew( tr_handle * handle )
250{
251    tr_peerMgr * m = tr_new0( tr_peerMgr, 1 );
252    m->handle = handle;
253    m->torrents = tr_ptrArrayNew( );
254    return m;
255}
256
257void
258tr_peerMgrFree( tr_peerMgr * manager )
259{
260    while( !tr_ptrArrayEmpty( manager->torrents ) )
261        freeTorrent( manager, (Torrent*)tr_ptrArrayNth( manager->torrents,0) );
262    tr_ptrArrayFree( manager->torrents );
263    tr_free( manager );
264}
265
266/**
267***
268**/
269
270static tr_peer**
271getConnectedPeers( Torrent * t, int * setmeCount )
272{
273    int i, peerCount, connectionCount;
274    tr_peer **peers = (tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
275    tr_peer **ret = tr_new( tr_peer*, peerCount );
276
277    for( i=connectionCount=0; i<peerCount; ++i )
278        if( peers[i]->msgs != NULL )
279            ret[connectionCount++] = peers[i];
280
281    *setmeCount = connectionCount;
282    return ret;
283}
284
285static int
286refillPulse( void * vtorrent )
287{
288    uint32_t i;
289    int size;
290    Torrent * t = (Torrent *) vtorrent;
291    tr_peer ** peers = getConnectedPeers( t, &size );
292fprintf( stderr, "in refill pulse for [%s]... sorting blocks by interest...", t->tor->info.name );
293
294    /* sort the blocks by interest */
295    qsort( t->blocks, t->blockCount, sizeof(struct tr_block), compareBlockByInterest );
296fprintf( stderr, " .done.\n" );
297
298    /* walk through all the most interesting blocks */
299    for( i=0; i<t->blockCount; ++i )
300    {
301        const uint32_t b = t->blocks[i].block;
302        const uint32_t index = tr_torBlockPiece( t->tor, b );
303        const uint32_t begin = ( b * t->tor->blockSize )-( index * t->tor->info.pieceSize );
304        const uint32_t length = tr_torBlockCountBytes( t->tor, (int)b );
305        int j;
306
307        if( t->blocks[i].have || t->blocks[i].dnd )
308            continue;
309
310        if( !size ) { /* all peers full */
311            fprintf( stderr, "all peers full...\n" );
312            break;
313        }
314
315        /* find a peer who can ask for this block */
316        for( j=0; j<size; )
317        {
318            const int val = tr_peerMsgsAddRequest( peers[j]->msgs, index, begin, length );
319//fprintf( stderr, " block %"PRIu64", peer %"PRIu64, (uint64_t)i,  (uint64_t)j );
320            if( val == TR_ADDREQ_FULL ) {
321fprintf( stderr, "peer %d of %d is full\n", (int)j, size );
322                peers[j] = peers[--size];
323            }
324            else if( val == TR_ADDREQ_MISSING ) {
325//fprintf( stderr, "peer doesn't have it\n" );
326                ++j;
327            }
328            else if( val == TR_ADDREQ_OK ) {
329fprintf( stderr, "peer %d took the request for block %d\n", j, i );
330                incrementReqCount( &t->blocks[i] );
331                j = size;
332            }
333        }
334    }
335
336    /* put the blocks back by index */
337    qsort( t->blocks, t->blockCount, sizeof(struct tr_block), compareBlockByIndex );
338
339    /* cleanup */
340    tr_free( peers );
341
342    /* let the timer expire */
343    t->refill_tag = NULL;
344    return FALSE;
345}
346
347static void
348ensureRefillTag( Torrent * t )
349{
350    if( t->refill_tag == NULL )
351        t->refill_tag = tr_timerNew( t->manager->handle,
352                                     refillPulse, t, NULL, 5000 );
353}
354
355static void
356msgsCallbackFunc( void * source UNUSED, void * vevent, void * vt )
357{
358    Torrent * t = (Torrent *) vt;
359    const tr_peermsgs_event * e = (const tr_peermsgs_event *) vevent;
360
361    switch( e->eventType )
362    {
363        case TR_PEERMSG_GOT_BITFIELD: {
364            const uint32_t begin = 0;
365            const uint32_t end = begin + t->blockCount;
366            uint32_t i;
367            for( i=begin; i<end; ++i ) {
368                if( !tr_bitfieldHas( e->bitfield, i ) )
369                    continue;
370                assert( t->blocks[i].block == i );
371                incrementScarcity( &t->blocks[i] );
372            }
373            break;
374        }
375
376        case TR_PEERMSG_GOT_HAVE: {
377            const uint32_t begin = tr_torPieceFirstBlock( t->tor, e->pieceIndex );
378            const uint32_t end = begin + tr_torPieceCountBlocks( t->tor, (int)e->pieceIndex );
379            uint32_t i;
380            for( i=begin; i<end; ++i ) {
381                assert( t->blocks[i].block == i );
382                incrementScarcity( &t->blocks[i] );
383            }
384            break;
385        }
386
387        case TR_PEERMSG_GOT_BLOCK: {
388            uint32_t i = e->blockIndex;
389            assert( t->blocks[i].block == i );
390            t->blocks[i].have = 1;
391            break;
392        }
393
394        case TR_PEERMSG_GOT_PEX:
395            /* FIXME */
396            break;
397
398        case TR_PEERMSG_GOT_ERROR:
399            /* FIXME */
400            break;
401
402        case TR_PEERMSG_BLOCKS_RUNNING_LOW:
403            ensureRefillTag( t );
404            break;
405
406        default:
407            assert(0);
408    }
409}
410
411static void
412myHandshakeDoneCB( tr_peerIo * io, int isConnected, void * vmanager )
413{
414    int ok = isConnected;
415    uint16_t port;
416    const struct in_addr * in_addr;
417    tr_peerMgr * manager = (tr_peerMgr*) vmanager;
418    const uint8_t * hash = NULL;
419    Torrent * t;
420
421    assert( io != NULL );
422
423    in_addr = tr_peerIoGetAddress( io, &port );
424
425    if( !tr_peerIoHasTorrentHash( io ) ) /* incoming connection gone wrong? */
426    {
427        tr_peerIoFree( io );
428        --manager->connectionCount;
429        return;
430    }
431
432    hash = tr_peerIoGetTorrentHash( io );
433    t = getExistingTorrent( manager, hash );
434    if( t == NULL )
435    {
436        tr_peerIoFree( io );
437        --manager->connectionCount;
438        return;
439    }
440
441    fprintf( stderr, "peer-mgr: torrent [%s] finished a handshake; isConnected is %d\n", t->tor->info.name, isConnected );
442
443    /* if we couldn't connect or were snubbed,
444     * the peer's probably not worth remembering. */
445    if( !ok ) {
446        tr_peer * peer = getExistingPeer( t, in_addr );
447        fprintf( stderr, "peer-mgr: torrent [%s] got a bad one, and you know what? fuck them.\n", t->tor->info.name );
448        if( peer ) {
449            tr_ptrArrayRemoveSorted( t->peers, peer, peerCompare );
450            freePeer( peer );
451        } else  {
452            tr_peerIoFree( io );
453        }
454        --manager->connectionCount;
455        return;
456    }
457
458#if 0
459    /* ONLY DO THIS TEST FOR INCOMING CONNECTIONS */
460    /* check for duplicates */
461    if( getExistingPeer( t, in_addr ) ) {
462        tr_dbg( "dropping a duplicate connection... dropping." );
463        tr_peerIoFree( io );
464        return;
465    }
466#endif
467
468    if( 1 ) {
469        tr_peer * peer = getPeer( t, in_addr );
470        peer->port = port;
471        peer->io = io;
472        peer->msgs = tr_peerMsgsNew( t->tor, peer );
473        peer->msgsTag = tr_peerMsgsSubscribe( peer->msgs, msgsCallbackFunc, t );
474        chokePulse( t );
475    }
476}
477
478void
479tr_peerMgrAddIncoming( tr_peerMgr      * manager,
480                       struct in_addr  * addr,
481                       int               socket )
482{
483    ++manager->connectionCount;
484
485fprintf( stderr, "peer-mgr: new INCOMING CONNECTION...\n" );
486    tr_handshakeAdd( tr_peerIoNewIncoming( manager->handle, addr, socket ),
487                     HANDSHAKE_ENCRYPTION_PREFERRED,
488                     myHandshakeDoneCB,
489                     manager );
490}
491
492static void
493maybeConnect( tr_peerMgr * manager, Torrent * t, tr_peer * peer )
494{
495    if( tr_peerMgrIsAcceptingConnections( manager ) )
496    {
497        fprintf( stderr, "peer-mgr: torrent [%s] is handshaking with a new peer %08x:%04x\n",
498                 t->tor->info.name,
499                 (uint32_t) peer->in_addr.s_addr, peer->port );
500
501        ++manager->connectionCount;
502
503        peer->io = tr_peerIoNewOutgoing( manager->handle,
504                                         &peer->in_addr,
505                                         peer->port,
506                                         t->hash );
507
508        tr_handshakeAdd( peer->io, HANDSHAKE_ENCRYPTION_PREFERRED,
509                         myHandshakeDoneCB, manager );
510    }
511}
512
513void
514tr_peerMgrAddPex( tr_peerMgr     * manager,
515                  const uint8_t  * torrentHash,
516                  int              from,
517                  const tr_pex   * pex,
518                  int              pexCount )
519{
520    int i;
521    const tr_pex * walk = pex;
522    Torrent * t = getExistingTorrent( manager, torrentHash );
523    for( i=0; i<pexCount; ++i )
524    {
525        tr_peer * peer = getPeer( t, &walk->in_addr );
526        peer->port = walk->port;
527        peer->from = from;
528        maybeConnect( manager, t, peer );
529    }
530}
531
532void
533tr_peerMgrAddPeers( tr_peerMgr    * manager,
534                    const uint8_t * torrentHash,
535                    int             from,
536                    const uint8_t * peerCompact,
537                    int             peerCount )
538{
539    int i;
540    const uint8_t * walk = peerCompact;
541    Torrent * t = getExistingTorrent( manager, torrentHash );
542    for( i=0; i<peerCount; ++i )
543    {
544        tr_peer * peer;
545        struct in_addr addr;
546        uint16_t port;
547        memcpy( &addr, walk, 4 ); walk += 4;
548        memcpy( &port, walk, 2 ); walk += 2;
549        peer = getPeer( t, &addr );
550        peer->port = port;
551        peer->from = from;
552        maybeConnect( manager, t, peer );
553    }
554}
555
556/**
557***
558**/
559
560int
561tr_peerMgrIsAcceptingConnections( const tr_peerMgr * manager )
562{
563    return manager->connectionCount < MAX_CONNECTED_PEERS;
564}
565
566void
567tr_peerMgrSetBlame( tr_peerMgr     * manager UNUSED,
568                    const uint8_t  * torrentHash UNUSED,
569                    int              pieceIndex UNUSED,
570                    int              success UNUSED )
571{
572    assert( 0 );
573}
574
575int
576tr_pexCompare( const void * va, const void * vb )
577{
578    const tr_pex * a = (const tr_pex *) va;
579    const tr_pex * b = (const tr_pex *) vb;
580    int i = memcmp( &a->in_addr, &b->in_addr, sizeof(struct in_addr) );
581    if( i ) return i;
582    if( a->port < b->port ) return -1;
583    if( a->port > b->port ) return 1;
584    return 0;
585}
586
587int tr_pexCompare( const void * a, const void * b );
588
589
590int
591tr_peerMgrGetPeers( tr_peerMgr      * manager,
592                    const uint8_t   * torrentHash,
593                    tr_pex         ** setme_pex )
594{
595    const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
596    int i, peerCount;
597    const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
598    tr_pex * pex = tr_new( tr_pex, peerCount );
599    tr_pex * walk = pex;
600
601    for( i=0; i<peerCount; ++i, ++walk )
602    {
603        walk->in_addr = peers[i]->in_addr;
604        walk->port = peers[i]->port;
605        walk->flags = '\0'; /* FIXME */
606    }
607
608    assert( ( walk - pex ) == peerCount );
609    qsort( pex, peerCount, sizeof(tr_pex), tr_pexCompare );
610    *setme_pex = pex;
611    return peerCount;
612}
613
614void
615tr_peerMgrStartTorrent( tr_peerMgr     * manager UNUSED,
616                        const uint8_t  * torrentHash UNUSED)
617{
618    //fprintf( stderr, "FIXME\n" );
619}
620
621void
622tr_peerMgrStopTorrent( tr_peerMgr     * manager UNUSED,
623                       const uint8_t  * torrentHash UNUSED )
624{
625    //fprintf( stderr, "FIXME\n" );
626}
627
628void
629tr_peerMgrUpdateCompletion( tr_peerMgr     * manager,
630                            const uint8_t  * torrentHash )
631{
632    uint32_t i;
633    Torrent * t = getExistingTorrent( manager, torrentHash );
634
635    for( i=0; i<t->blockCount; ++i ) {
636        assert( t->blocks[i].block == i );
637        t->blocks[i].have = tr_cpBlockIsComplete( t->tor->completion, i ) ? 1 : 0;
638    }
639}
640
641void
642tr_peerMgrAddTorrent( tr_peerMgr * manager,
643                      tr_torrent * tor )
644{
645    Torrent * t;
646    uint32_t i;
647
648    assert( tor != NULL );
649    assert( getExistingTorrent( manager, tor->info.hash ) == NULL );
650
651    t = tr_new0( Torrent, 1 );
652    t->manager = manager;
653    t->tor = tor;
654    t->peers = tr_ptrArrayNew( );
655    t->choke_tag = tr_timerNew( manager->handle,
656                                chokePulse, t, NULL, 
657                                RECHOKE_PERIOD_SECONDS );
658
659    t->blockCount = tor->blockCount;
660    t->blocks = tr_new( struct tr_block, t->blockCount );
661    for( i=0; i<t->blockCount; ++i ) {
662        const int index = tr_torBlockPiece( tor, i );
663        t->blocks[i].have = tr_cpBlockIsComplete( t->tor->completion, i ) ? 1 : 0;
664if( tr_cpBlockIsComplete( t->tor->completion, i ) ) fprintf( stderr, "have block %d\n", (int)i );
665        t->blocks[i].dnd = tor->info.pieces[index].dnd;
666        t->blocks[i].low_priority = tor->info.pieces[index].priority == TR_PRI_LOW;
667        t->blocks[i].high_priority = tor->info.pieces[index].priority == TR_PRI_HIGH;
668        t->blocks[i].requestCount = 0;
669        t->blocks[i].scarcity = 0;
670        t->blocks[i].block = i;
671    }
672
673    memcpy( t->hash, tor->info.hash, SHA_DIGEST_LENGTH );
674    tr_ptrArrayInsertSorted( manager->torrents, t, torrentCompare );
675}
676
677void
678tr_peerMgrRemoveTorrent( tr_peerMgr     * manager,
679                         const uint8_t  * torrentHash )
680{
681    Torrent * t = getExistingTorrent( manager, torrentHash );
682    if( t != NULL ) {
683        tr_peerMgrStopTorrent( manager, torrentHash );
684        freeTorrent( manager, t );
685    }
686}
687
688void
689tr_peerMgrTorrentAvailability( const tr_peerMgr * manager,
690                               const uint8_t    * torrentHash,
691                               int8_t           * tab,
692                               int                tabCount )
693{
694    int i;
695    const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
696    const tr_torrent * tor = t->tor;
697    const float interval = tor->info.pieceCount / (float)tabCount;
698
699    for( i=0; i<tabCount; ++i )
700    {
701        const int piece = i * interval;
702
703        if( tor == NULL )
704            tab[i] = 0;
705        else if( tr_cpPieceIsComplete( tor->completion, piece ) )
706            tab[i] = -1;
707        else {
708            int j, peerCount;
709            const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
710            for( j=0; j<peerCount; ++j )
711                if( tr_bitfieldHas( peers[j]->have, i ) )
712                    ++tab[i];
713        }
714    }
715}
716
717
718void
719tr_peerMgrTorrentStats( const tr_peerMgr * manager,
720                        const uint8_t    * torrentHash,
721                        int              * setmePeersTotal,
722                        int              * setmePeersConnected,
723                        int              * setmePeersSendingToUs,
724                        int              * setmePeersGettingFromUs,
725                        int              * setmePeersFrom )
726{
727    int i, size;
728    const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
729    const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &size );
730
731    *setmePeersTotal          = size;
732    *setmePeersConnected      = 0;
733    *setmePeersSendingToUs    = 0;
734    *setmePeersGettingFromUs  = 0;
735
736    for( i=0; i<size; ++i )
737    {
738        const tr_peer * peer = peers[i];
739
740        if( peer->io == NULL ) /* not connected */
741            continue;
742
743        ++*setmePeersConnected;
744
745        ++setmePeersFrom[peer->from];
746
747        if( tr_peerIoGetRateToPeer( peer->io ) > 0.01 )
748            ++*setmePeersGettingFromUs;
749
750        if( tr_peerIoGetRateToClient( peer->io ) > 0.01 )
751            ++*setmePeersSendingToUs;
752    }
753}
754
755struct tr_peer_stat *
756tr_peerMgrPeerStats( const tr_peerMgr  * manager,
757                     const uint8_t     * torrentHash,
758                     int               * setmeCount UNUSED )
759{
760    int i, size;
761    const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
762    const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &size );
763    tr_peer_stat * ret;
764
765    ret = tr_new0( tr_peer_stat, size );
766
767    for( i=0; i<size; ++i )
768    {
769        const tr_peer * peer = peers[i];
770        const int live = peer->io != NULL;
771        tr_peer_stat * stat = ret + i;
772
773        tr_netNtop( &peer->in_addr, stat->addr, sizeof(stat->addr) );
774        stat->port             = peer->port;
775        stat->from             = peer->from;
776        stat->client           = peer->client;
777        stat->progress         = peer->progress;
778        stat->isConnected      = live ? 1 : 0;
779        stat->isEncrypted      = tr_peerIoIsEncrypted( peer->io ) ? 1 : 0;
780        stat->uploadToRate     = tr_peerIoGetRateToPeer( peer->io );
781        stat->downloadFromRate = tr_peerIoGetRateToClient( peer->io );
782        stat->isDownloading    = stat->uploadToRate > 0.01;
783        stat->isUploading      = stat->downloadFromRate > 0.01;
784    }
785
786    *setmeCount = size;
787    return ret;
788}
789
790void
791tr_peerMgrDisablePex( tr_peerMgr    * manager,
792                      const uint8_t * torrentHash,
793                      int             disable)
794{
795    Torrent * t = getExistingTorrent( manager, torrentHash );
796    tr_torrent * tor = t->tor;
797
798    if( ( tor->pexDisabled != disable ) && ! ( TR_FLAG_PRIVATE & tor->info.flags ) )
799    {
800        int i, size;
801        tr_peer ** peers = (tr_peer **) tr_ptrArrayPeek( t->peers, &size );
802        for( i=0; i<size; ++i ) {
803            peers[i]->pexEnabled = disable ? 0 : 1;
804            peers[i]->lastPexTime = 0;
805        }
806
807        tor->pexDisabled = disable;
808    }
809}
810
811/**
812***
813**/
814
815typedef struct
816{
817    tr_peer * peer;
818    float rate;
819    int isInterested;
820}
821ChokeData;
822
823static int
824compareChokeByRate( const void * va, const void * vb )
825{
826    const ChokeData * a = ( const ChokeData * ) va;
827    const ChokeData * b = ( const ChokeData * ) vb;
828    if( a->rate > b->rate ) return -1;
829    if( a->rate < b->rate ) return 1;
830    return 0;
831}
832
833static int
834compareChokeByDownloader( const void * va, const void * vb )
835{
836    const ChokeData * a = ( const ChokeData * ) va;
837    const ChokeData * b = ( const ChokeData * ) vb;
838
839    /* primary key: interest */
840    if(  a->isInterested && !b->isInterested ) return -1;
841    if( !a->isInterested &&  b->isInterested ) return 1;
842
843    /* second key: rate */
844    return compareChokeByRate( va, vb );
845}
846
847static int
848chokePulse( void * vtorrent )
849{
850    Torrent * t = (Torrent *) vtorrent;
851    int i, size, unchoked;
852    const int done = tr_cpGetStatus( t->tor->completion ) != TR_CP_INCOMPLETE;
853    float bestDownloaderRate;
854    ChokeData * data;
855    tr_peer ** peers = getConnectedPeers( t, &size );
856
857fprintf( stderr, "rechoking torrent %p, with %d peers\n", t, size );
858
859    if( size < 1 )
860        return TRUE;
861
862    data = tr_new( ChokeData, size );
863    for( i=0; i<size; ++i ) {
864        data[i].peer = peers[i];
865        data[i].isInterested = peers[i]->peerIsInterested;
866        data[i].rate = done ? tr_peerIoGetRateToPeer( peers[i]->io )
867                            : tr_peerIoGetRateToClient( peers[i]->io );
868    }
869
870    /* find the best downloaders and unchoke them */
871    qsort( data, size, sizeof(ChokeData), compareChokeByDownloader );
872    bestDownloaderRate = data[0].rate;
873    for( i=unchoked=0; i<size && unchoked<NUM_DOWNLOADERS_TO_UNCHOKE; ++i ) {
874        tr_peerMsgsSetChoke( data[i].peer->msgs, FALSE );
875        ++unchoked;
876    }
877    memmove( data, data+i, sizeof(ChokeData)*(size-i) );
878    size -= i;
879
880    /* of those remaining, unchoke those that are faster than the downloaders */
881    qsort( data, size, sizeof(ChokeData), compareChokeByRate );
882    for( i=0; i<size && data[i].rate >= bestDownloaderRate; ++i )
883        tr_peerMsgsSetChoke( data[i].peer->msgs, FALSE );
884    memmove( data, data+i, sizeof(ChokeData)*(size-i) );
885    size -= i;
886
887    /* of those remaining, optimistically unchoke one; choke the rest */
888    if( size > 0 ) {
889        const int optimistic = tr_rand( size );
890        for( i=0; i<size; ++i )
891            tr_peerMsgsSetChoke( data[i].peer->msgs, i!=optimistic );
892    }
893
894    /* cleanup */
895    tr_free( data );
896    tr_free( peers );
897    return TRUE;
898}
Note: See TracBrowser for help on using the repository browser.