2007-09-20 16:32:01 +00:00
/*
2010-01-04 21:00:47 +00:00
* This file Copyright ( C ) 2007 - 2010 Mnemosyne LLC
2007-09-20 16:32:01 +00:00
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2 ( b )
2008-09-23 19:11:04 +00:00
* so that the bulk of its code can remain under the MIT license .
2007-09-20 16:32:01 +00:00
* This exemption does not extend to derived works not owned by
* the Transmission project .
*
* $ Id $
*/
2007-11-09 20:07:52 +00:00
# include <assert.h>
2007-11-25 16:57:08 +00:00
# include <string.h> /* memcpy, memcmp, strstr */
2007-09-20 16:32:01 +00:00
# include <stdlib.h> /* qsort */
2007-10-02 14:35:02 +00:00
# include <event.h>
2007-09-20 16:32:01 +00:00
# include "transmission.h"
2009-12-02 05:30:46 +00:00
# include "announcer.h"
2008-11-24 04:21:23 +00:00
# include "bandwidth.h"
# include "bencode.h"
2008-03-29 21:05:51 +00:00
# include "blocklist.h"
2007-09-20 16:32:01 +00:00
# include "clients.h"
# include "completion.h"
2007-09-28 16:40:21 +00:00
# include "crypto.h"
2007-09-20 16:32:01 +00:00
# include "handshake.h"
2008-06-07 21:26:41 +00:00
# include "inout.h" /* tr_ioTestPiece */
2007-09-20 16:32:01 +00:00
# include "net.h"
# include "peer-io.h"
# include "peer-mgr.h"
# include "peer-msgs.h"
# include "ptrarray.h"
2009-07-01 14:58:57 +00:00
# include "session.h"
2008-11-04 21:33:24 +00:00
# include "stats.h" /* tr_statsAddUploaded, tr_statsAddDownloaded */
2007-12-25 05:37:32 +00:00
# include "torrent.h"
2007-09-20 16:32:01 +00:00
# include "utils.h"
2008-06-07 21:26:41 +00:00
# include "webseed.h"
2007-09-20 16:32:01 +00:00
2007-09-22 14:18:52 +00:00
enum
{
2009-11-26 05:13:58 +00:00
/* how frequently to cull old atoms */
ATOM_PERIOD_MSEC = ( 60 * 1000 ) ,
2007-09-22 14:18:52 +00:00
/* how frequently to change which peers are choked */
2008-09-23 19:11:04 +00:00
RECHOKE_PERIOD_MSEC = ( 10 * 1000 ) ,
2007-09-22 14:18:52 +00:00
2009-02-04 16:58:52 +00:00
/* how frequently to reallocate bandwidth */
BANDWIDTH_PERIOD_MSEC = 500 ,
2009-02-18 17:19:36 +00:00
/* how frequently to age out old piece request lists */
2009-05-13 20:54:35 +00:00
REFILL_UPKEEP_PERIOD_MSEC = ( 10 * 1000 ) ,
2009-02-18 17:19:36 +00:00
2009-02-04 16:58:52 +00:00
/* how frequently to decide which peers live and die */
RECONNECT_PERIOD_MSEC = 500 ,
2007-09-22 00:22:10 +00:00
2007-10-13 13:54:05 +00:00
/* when many peers are available, keep idle ones this long */
2009-10-29 18:11:10 +00:00
MIN_UPLOAD_IDLE_SECS = ( 60 ) ,
2007-10-13 13:54:05 +00:00
/* when few peers are available, keep idle ones this long */
2008-12-11 07:04:46 +00:00
MAX_UPLOAD_IDLE_SECS = ( 60 * 5 ) ,
2007-10-13 13:54:05 +00:00
2008-01-11 18:13:09 +00:00
/* max number of peers to ask for per second overall.
2008-09-23 19:11:04 +00:00
* this throttle is to avoid overloading the router */
2010-02-03 01:00:08 +00:00
MAX_CONNECTIONS_PER_SECOND = 12 ,
2008-01-11 18:13:09 +00:00
2010-04-20 21:54:03 +00:00
MAX_CONNECTIONS_PER_PULSE = ( int ) ( MAX_CONNECTIONS_PER_SECOND * ( RECONNECT_PERIOD_MSEC / 1000.0 ) ) ,
2007-10-08 01:31:27 +00:00
/* number of bad pieces a peer is allowed to send before we ban them */
2008-11-24 04:21:23 +00:00
MAX_BAD_PIECES_PER_PEER = 5 ,
2007-12-15 04:26:31 +00:00
2009-02-18 17:19:36 +00:00
/* amount of time to keep a list of request pieces lying around
before it ' s considered too old and needs to be rebuilt */
PIECE_LIST_SHELF_LIFE_SECS = 60 ,
2007-10-08 01:31:27 +00:00
/* use for bitwise operations w/peer_atom.myflags */
2007-12-15 04:26:31 +00:00
MYFLAG_BANNED = 1 ,
2009-02-04 16:58:52 +00:00
/* use for bitwise operations w/peer_atom.myflags */
2008-09-17 19:44:24 +00:00
/* unreachable for now... but not banned.
* if they try to connect to us it ' s okay */
2008-09-05 05:14:49 +00:00
MYFLAG_UNREACHABLE = 2 ,
2008-09-17 19:44:24 +00:00
/* the minimum we'll wait before attempting to reconnect to a peer */
2009-12-16 18:20:01 +00:00
MINIMUM_RECONNECT_INTERVAL_SECS = 5 ,
/** how long we'll let requests we've made linger before we cancel them */
2010-03-22 02:27:57 +00:00
REQUEST_TTL_SECS = 120 ,
2010-03-08 04:29:58 +00:00
2010-03-10 15:55:00 +00:00
CANCEL_HISTORY_SEC = 120
2009-11-01 02:10:47 +00:00
} ;
2007-10-01 16:31:17 +00:00
2009-11-02 00:17:30 +00:00
2007-09-20 16:32:01 +00:00
/**
* * *
* */
2008-12-02 19:46:51 +00:00
enum
{
UPLOAD_ONLY_UKNOWN ,
UPLOAD_ONLY_YES ,
UPLOAD_ONLY_NO
} ;
2008-12-11 17:02:34 +00:00
/**
* Peer information that should be kept even before we ' ve connected and
* after we ' ve disconnected . These are kept in a pool of peer_atoms to decide
* which ones would make good candidates for connecting to , and to watch out
* for banned peers .
*
* @ see tr_peer
* @ see tr_peermsgs
*/
2007-09-30 23:55:49 +00:00
struct peer_atom
2008-09-23 19:11:04 +00:00
{
2008-12-02 03:41:58 +00:00
uint8_t from ;
2010-04-20 21:54:03 +00:00
uint8_t flags ; /* these match the added_f flags */
uint8_t myflags ; /* flags that aren't defined in added_f */
uint8_t uploadOnly ; /* UPLOAD_ONLY_ */
int8_t seedProbability ; /* how likely is this to be a seed... [0..100] or -1 for unknown */
2008-12-02 03:41:58 +00:00
tr_port port ;
uint16_t numFails ;
2010-04-20 21:54:03 +00:00
time_t time ; /* when the peer's connection status last changed */
2008-12-02 03:41:58 +00:00
time_t piece_data_time ;
2009-11-26 05:13:58 +00:00
2010-04-20 21:54:03 +00:00
time_t lastConnectionAttemptAt ;
time_t lastConnectionAt ;
2009-11-26 05:13:58 +00:00
/* similar to a TTL field, but less rigid --
* if the swarm is small , the atom will be kept past this date . */
time_t shelf_date ;
2010-04-20 21:54:03 +00:00
tr_peer * peer ; /* will be NULL if not connected */
tr_address addr ;
2007-09-30 23:55:49 +00:00
} ;
2009-12-28 23:27:17 +00:00
# ifdef NDEBUG
# define tr_isAtom(a) (TRUE)
# else
2009-09-10 03:28:55 +00:00
static tr_bool
tr_isAtom ( const struct peer_atom * atom )
{
return ( atom ! = NULL )
& & ( atom - > from < TR_PEER_FROM__MAX )
& & ( tr_isAddress ( & atom - > addr ) ) ;
}
2009-12-28 23:27:17 +00:00
# endif
2009-09-10 03:28:55 +00:00
2009-10-29 16:10:03 +00:00
static const char *
tr_atomAddrStr ( const struct peer_atom * atom )
{
return tr_peerIoAddrStr ( & atom - > addr , atom - > port ) ;
}
2009-11-08 23:20:00 +00:00
struct block_request
2009-02-18 17:19:36 +00:00
{
2009-11-08 23:20:00 +00:00
tr_block_index_t block ;
tr_peer * peer ;
time_t sentAt ;
} ;
struct weighted_piece
{
tr_piece_index_t index ;
2010-01-12 20:17:22 +00:00
int16_t salt ;
2009-11-08 23:20:00 +00:00
int16_t requestCount ;
2009-02-18 17:19:36 +00:00
} ;
2010-01-19 19:37:00 +00:00
/** @brief Opaque, per-torrent data structure for peer connection information */
2009-01-13 21:00:05 +00:00
typedef struct tr_torrent_peers
2007-09-20 16:32:01 +00:00
{
2009-02-18 17:19:36 +00:00
tr_ptrArray outgoingHandshakes ; /* tr_handshake */
tr_ptrArray pool ; /* struct peer_atom */
tr_ptrArray peers ; /* tr_peer */
tr_ptrArray webseeds ; /* tr_webseed */
2009-06-24 03:54:11 +00:00
2009-02-18 17:19:36 +00:00
tr_torrent * tor ;
tr_peer * optimistic ; /* the optimistic peer, or NULL if none */
struct tr_peerMgr * manager ;
2009-06-24 03:54:11 +00:00
tr_bool isRunning ;
2009-11-09 06:36:47 +00:00
tr_bool needsCompletenessCheck ;
2009-11-08 23:20:00 +00:00
struct block_request * requests ;
int requestCount ;
int requestAlloc ;
struct weighted_piece * pieces ;
int pieceCount ;
2010-03-08 04:29:58 +00:00
int interestedCount ;
/* An arbitrary metric of how congested the downloads are.
* Based on how many of requests are cancelled and how many are completed .
* Lower values indicate less congestion . */
2010-04-22 03:12:31 +00:00
double cancelRate ;
2007-09-20 16:32:01 +00:00
}
Torrent ;
struct tr_peerMgr
{
2009-12-10 05:52:46 +00:00
tr_session * session ;
tr_ptrArray incomingHandshakes ; /* tr_handshake */
struct event * bandwidthTimer ;
struct event * rechokeTimer ;
struct event * refillUpkeepTimer ;
struct event * atomTimer ;
2007-09-20 16:32:01 +00:00
} ;
2008-10-14 01:29:33 +00:00
# define tordbg( t, ... ) \
2008-10-26 15:39:04 +00:00
do { \
if ( tr_deepLoggingIsActive ( ) ) \
2009-09-17 03:24:35 +00:00
tr_deepLog ( __FILE__ , __LINE__ , tr_torrentName ( t - > tor ) , __VA_ARGS__ ) ; \
2008-10-26 15:39:04 +00:00
} while ( 0 )
2008-09-17 19:44:24 +00:00
2008-10-14 01:29:33 +00:00
# define dbgmsg( ... ) \
2008-10-26 15:39:04 +00:00
do { \
if ( tr_deepLoggingIsActive ( ) ) \
tr_deepLog ( __FILE__ , __LINE__ , NULL , __VA_ARGS__ ) ; \
} while ( 0 )
2007-10-02 14:35:02 +00:00
/**
* * *
* */
2010-01-01 22:26:35 +00:00
static inline void
2008-02-28 19:33:11 +00:00
managerLock ( const struct tr_peerMgr * manager )
2007-09-29 06:37:03 +00:00
{
2010-01-04 09:05:02 +00:00
tr_sessionLock ( manager - > session ) ;
2007-09-29 06:37:03 +00:00
}
2008-09-23 19:11:04 +00:00
2010-01-01 22:26:35 +00:00
static inline void
2008-02-28 19:33:11 +00:00
managerUnlock ( const struct tr_peerMgr * manager )
2007-09-29 06:37:03 +00:00
{
2010-01-04 09:05:02 +00:00
tr_sessionUnlock ( manager - > session ) ;
2007-09-29 06:37:03 +00:00
}
2008-09-23 19:11:04 +00:00
2010-01-01 22:26:35 +00:00
static inline void
2007-09-29 06:37:03 +00:00
torrentLock ( Torrent * torrent )
{
managerLock ( torrent - > manager ) ;
}
2008-09-23 19:11:04 +00:00
2010-01-01 22:26:35 +00:00
static inline void
2007-09-29 06:37:03 +00:00
torrentUnlock ( Torrent * torrent )
{
managerUnlock ( torrent - > manager ) ;
}
2008-09-23 19:11:04 +00:00
2010-01-01 22:26:35 +00:00
static inline int
2007-09-30 23:55:49 +00:00
torrentIsLocked ( const Torrent * t )
2007-09-29 06:37:03 +00:00
{
2010-01-04 09:05:02 +00:00
return tr_sessionIsLocked ( t - > manager - > session ) ;
2007-09-29 06:37:03 +00:00
}
/**
* * *
* */
2007-09-20 16:32:01 +00:00
static int
2008-12-22 00:52:44 +00:00
handshakeCompareToAddr ( const void * va , const void * vb )
2007-09-20 16:32:01 +00:00
{
const tr_handshake * a = va ;
2008-09-23 19:11:04 +00:00
2008-12-02 03:41:58 +00:00
return tr_compareAddresses ( tr_handshakeGetAddr ( a , NULL ) , vb ) ;
2007-09-20 16:32:01 +00:00
}
static int
2008-12-22 00:52:44 +00:00
handshakeCompare ( const void * a , const void * b )
2007-09-20 16:32:01 +00:00
{
2007-09-21 15:31:46 +00:00
return handshakeCompareToAddr ( a , tr_handshakeGetAddr ( b , NULL ) ) ;
2007-09-20 16:32:01 +00:00
}
static tr_handshake *
2008-12-02 03:41:58 +00:00
getExistingHandshake ( tr_ptrArray * handshakes ,
const tr_address * addr )
2007-09-20 16:32:01 +00:00
{
2008-12-02 03:41:58 +00:00
return tr_ptrArrayFindSorted ( handshakes , addr , handshakeCompareToAddr ) ;
2007-09-20 16:32:01 +00:00
}
2007-09-30 23:55:49 +00:00
static int
2008-12-02 03:41:58 +00:00
comparePeerAtomToAddress ( const void * va , const void * vb )
2007-09-30 23:55:49 +00:00
{
const struct peer_atom * a = va ;
2008-09-23 19:11:04 +00:00
2008-12-02 03:41:58 +00:00
return tr_compareAddresses ( & a - > addr , vb ) ;
2007-09-30 23:55:49 +00:00
}
static int
2009-11-26 05:13:58 +00:00
compareAtomsByAddress ( const void * va , const void * vb )
2007-09-30 23:55:49 +00:00
{
const struct peer_atom * b = vb ;
2008-09-23 19:11:04 +00:00
2009-09-10 03:28:55 +00:00
assert ( tr_isAtom ( b ) ) ;
2009-09-10 03:07:54 +00:00
2007-09-30 23:55:49 +00:00
return comparePeerAtomToAddress ( va , & b - > addr ) ;
}
2007-09-20 16:32:01 +00:00
/**
* * *
* */
2009-10-29 16:10:03 +00:00
const tr_address *
tr_peerAddress ( const tr_peer * peer )
{
return & peer - > atom - > addr ;
}
2007-09-20 16:32:01 +00:00
static Torrent *
2008-09-23 19:11:04 +00:00
getExistingTorrent ( tr_peerMgr * manager ,
const uint8_t * hash )
2007-09-20 16:32:01 +00:00
{
2009-01-13 21:00:05 +00:00
tr_torrent * tor = tr_torrentFindFromHash ( manager - > session , hash ) ;
return tor = = NULL ? NULL : tor - > torrentPeers ;
2007-09-20 16:32:01 +00:00
}
static int
2009-10-29 16:10:03 +00:00
peerCompare ( const void * a , const void * b )
2007-09-20 16:32:01 +00:00
{
2009-10-29 16:10:03 +00:00
return tr_compareAddresses ( tr_peerAddress ( a ) , tr_peerAddress ( b ) ) ;
2007-09-20 16:32:01 +00:00
}
2007-09-30 23:55:49 +00:00
static struct peer_atom *
2008-12-02 03:41:58 +00:00
getExistingAtom ( const Torrent * t ,
const tr_address * addr )
2007-09-30 23:55:49 +00:00
{
2008-12-29 08:54:36 +00:00
Torrent * tt = ( Torrent * ) t ;
2007-09-30 23:55:49 +00:00
assert ( torrentIsLocked ( t ) ) ;
2008-12-29 08:54:36 +00:00
return tr_ptrArrayFindSorted ( & tt - > pool , addr , comparePeerAtomToAddress ) ;
2007-09-30 23:55:49 +00:00
}
2008-12-05 22:56:19 +00:00
static tr_bool
2009-11-28 07:15:31 +00:00
peerIsInUse ( const Torrent * ct , const struct peer_atom * atom )
2007-09-30 23:55:49 +00:00
{
2007-10-02 16:12:44 +00:00
Torrent * t = ( Torrent * ) ct ;
2007-09-30 23:55:49 +00:00
assert ( torrentIsLocked ( t ) ) ;
2009-11-28 07:15:31 +00:00
return ( atom - > peer ! = NULL )
| | getExistingHandshake ( & t - > outgoingHandshakes , & atom - > addr )
| | getExistingHandshake ( & t - > manager - > incomingHandshakes , & atom - > addr ) ;
2007-10-03 16:42:43 +00:00
}
static tr_peer *
2009-10-29 16:10:03 +00:00
peerConstructor ( struct peer_atom * atom )
2007-10-03 16:42:43 +00:00
{
2009-10-29 16:10:03 +00:00
tr_peer * peer = tr_new0 ( tr_peer , 1 ) ;
2010-03-08 04:29:58 +00:00
2009-11-24 02:16:31 +00:00
tr_bitsetConstructor ( & peer - > have , 0 ) ;
2010-03-08 04:29:58 +00:00
2009-10-29 16:10:03 +00:00
peer - > atom = atom ;
2009-11-28 07:15:31 +00:00
atom - > peer = peer ;
2010-03-08 04:29:58 +00:00
2010-03-10 15:55:00 +00:00
peer - > blocksSentToClient = tr_historyNew ( CANCEL_HISTORY_SEC , 1 ) ;
peer - > blocksSentToPeer = tr_historyNew ( CANCEL_HISTORY_SEC , 1 ) ;
peer - > cancelsSentToClient = tr_historyNew ( CANCEL_HISTORY_SEC , 1 ) ;
peer - > cancelsSentToPeer = tr_historyNew ( CANCEL_HISTORY_SEC , 1 ) ;
2010-03-08 04:29:58 +00:00
2009-10-29 16:10:03 +00:00
return peer ;
2007-09-30 23:55:49 +00:00
}
2007-09-20 16:32:01 +00:00
static tr_peer *
2009-10-29 16:10:03 +00:00
getPeer ( Torrent * torrent , struct peer_atom * atom )
2007-09-20 16:32:01 +00:00
{
2007-09-29 06:37:03 +00:00
tr_peer * peer ;
assert ( torrentIsLocked ( torrent ) ) ;
2009-11-28 07:15:31 +00:00
peer = atom - > peer ;
2007-09-20 16:32:01 +00:00
2008-09-23 19:11:04 +00:00
if ( peer = = NULL )
{
2009-10-29 16:10:03 +00:00
peer = peerConstructor ( atom ) ;
2008-12-29 08:54:36 +00:00
tr_ptrArrayInsertSorted ( & torrent - > peers , peer , peerCompare ) ;
2007-09-20 16:32:01 +00:00
}
return peer ;
}
2009-11-08 23:20:00 +00:00
static void peerDeclinedAllRequests ( Torrent * , const tr_peer * ) ;
2007-09-20 16:32:01 +00:00
static void
2009-11-08 23:20:00 +00:00
peerDestructor ( Torrent * t , tr_peer * peer )
2007-09-20 16:32:01 +00:00
{
2009-11-08 23:20:00 +00:00
assert ( peer ! = NULL ) ;
peerDeclinedAllRequests ( t , peer ) ;
2007-09-20 16:32:01 +00:00
2008-12-02 17:10:54 +00:00
if ( peer - > msgs ! = NULL )
{
tr_peerMsgsUnsubscribe ( peer - > msgs , peer - > msgsTag ) ;
tr_peerMsgsFree ( peer - > msgs ) ;
}
2007-09-20 16:32:01 +00:00
2009-01-05 18:20:47 +00:00
tr_peerIoClear ( peer - > io ) ;
2009-01-05 06:45:08 +00:00
tr_peerIoUnref ( peer - > io ) ; /* balanced by the ref in handshakeDoneCB() */
2007-09-20 16:32:01 +00:00
2010-03-08 04:29:58 +00:00
tr_historyFree ( peer - > blocksSentToClient ) ;
tr_historyFree ( peer - > blocksSentToPeer ) ;
tr_historyFree ( peer - > cancelsSentToClient ) ;
tr_historyFree ( peer - > cancelsSentToPeer ) ;
2009-11-24 02:16:31 +00:00
tr_bitsetDestructor ( & peer - > have ) ;
2007-09-20 16:32:01 +00:00
tr_bitfieldFree ( peer - > blame ) ;
tr_free ( peer - > client ) ;
2009-11-28 07:15:31 +00:00
peer - > atom - > peer = NULL ;
2008-11-06 02:56:51 +00:00
2007-09-20 16:32:01 +00:00
tr_free ( peer ) ;
}
2007-09-30 23:55:49 +00:00
static void
2009-05-09 06:08:21 +00:00
removePeer ( Torrent * t , tr_peer * peer )
2007-09-30 23:55:49 +00:00
{
2009-05-09 06:08:21 +00:00
tr_peer * removed ;
struct peer_atom * atom = peer - > atom ;
2007-09-30 23:55:49 +00:00
assert ( torrentIsLocked ( t ) ) ;
2008-08-01 16:43:22 +00:00
assert ( atom ) ;
2009-05-09 06:08:21 +00:00
2009-11-26 18:47:08 +00:00
atom - > time = tr_time ( ) ;
2007-09-30 23:55:49 +00:00
2008-12-29 08:54:36 +00:00
removed = tr_ptrArrayRemoveSorted ( & t - > peers , peer , peerCompare ) ;
2007-09-30 23:55:49 +00:00
assert ( removed = = peer ) ;
2009-11-08 23:20:00 +00:00
peerDestructor ( t , removed ) ;
2007-09-30 23:55:49 +00:00
}
2007-10-01 16:50:51 +00:00
static void
removeAllPeers ( Torrent * t )
{
2008-12-29 08:54:36 +00:00
while ( ! tr_ptrArrayEmpty ( & t - > peers ) )
removePeer ( t , tr_ptrArrayNth ( & t - > peers , 0 ) ) ;
2007-10-01 16:50:51 +00:00
}
2007-09-20 16:32:01 +00:00
static void
2008-09-17 19:44:24 +00:00
torrentDestructor ( void * vt )
2007-09-20 16:32:01 +00:00
{
2008-09-17 19:44:24 +00:00
Torrent * t = vt ;
2007-09-20 16:32:01 +00:00
2008-08-01 16:43:22 +00:00
assert ( t ) ;
2007-10-02 16:12:44 +00:00
assert ( ! t - > isRunning ) ;
2007-09-29 06:37:03 +00:00
assert ( torrentIsLocked ( t ) ) ;
2008-12-29 08:54:36 +00:00
assert ( tr_ptrArrayEmpty ( & t - > outgoingHandshakes ) ) ;
assert ( tr_ptrArrayEmpty ( & t - > peers ) ) ;
2007-09-20 16:32:01 +00:00
2008-12-29 08:54:36 +00:00
tr_ptrArrayDestruct ( & t - > webseeds , ( PtrArrayForeachFunc ) tr_webseedFree ) ;
tr_ptrArrayDestruct ( & t - > pool , ( PtrArrayForeachFunc ) tr_free ) ;
tr_ptrArrayDestruct ( & t - > outgoingHandshakes , NULL ) ;
tr_ptrArrayDestruct ( & t - > peers , NULL ) ;
2007-10-02 16:12:44 +00:00
2009-11-08 23:20:00 +00:00
tr_free ( t - > requests ) ;
tr_free ( t - > pieces ) ;
2007-09-20 16:32:01 +00:00
tr_free ( t ) ;
}
2009-11-23 21:57:10 +00:00
static void peerCallbackFunc ( void * vpeer , void * vevent , void * vt ) ;
2008-06-07 21:26:41 +00:00
2007-10-02 16:12:44 +00:00
static Torrent *
2008-09-23 19:11:04 +00:00
torrentConstructor ( tr_peerMgr * manager ,
tr_torrent * tor )
2007-10-02 16:12:44 +00:00
{
2008-09-23 19:11:04 +00:00
int i ;
2007-10-02 16:12:44 +00:00
Torrent * t ;
t = tr_new0 ( Torrent , 1 ) ;
t - > manager = manager ;
t - > tor = tor ;
2008-12-29 08:54:36 +00:00
t - > pool = TR_PTR_ARRAY_INIT ;
t - > peers = TR_PTR_ARRAY_INIT ;
t - > webseeds = TR_PTR_ARRAY_INIT ;
t - > outgoingHandshakes = TR_PTR_ARRAY_INIT ;
2007-10-02 16:12:44 +00:00
2008-09-23 19:11:04 +00:00
for ( i = 0 ; i < tor - > info . webseedCount ; + + i )
{
tr_webseed * w =
2008-12-29 08:54:36 +00:00
tr_webseedNew ( tor , tor - > info . webseeds [ i ] , peerCallbackFunc , t ) ;
tr_ptrArrayAppend ( & t - > webseeds , w ) ;
2008-06-07 21:26:41 +00:00
}
2007-10-02 16:12:44 +00:00
return t ;
}
2007-09-20 16:32:01 +00:00
tr_peerMgr *
2008-09-17 19:44:24 +00:00
tr_peerMgrNew ( tr_session * session )
2007-09-20 16:32:01 +00:00
{
tr_peerMgr * m = tr_new0 ( tr_peerMgr , 1 ) ;
2008-09-17 19:44:24 +00:00
m - > session = session ;
2008-12-29 08:54:36 +00:00
m - > incomingHandshakes = TR_PTR_ARRAY_INIT ;
2009-05-13 20:54:35 +00:00
return m ;
}
2009-02-04 16:58:52 +00:00
2009-05-13 20:54:35 +00:00
static void
2009-12-10 05:52:46 +00:00
deleteTimer ( struct event * * t )
2009-05-13 20:54:35 +00:00
{
2009-12-10 05:52:46 +00:00
if ( * t ! = NULL )
{
evtimer_del ( * t ) ;
tr_free ( * t ) ;
* t = NULL ;
}
}
2009-11-02 00:17:30 +00:00
2009-12-10 05:52:46 +00:00
static void
deleteTimers ( struct tr_peerMgr * m )
{
deleteTimer ( & m - > atomTimer ) ;
deleteTimer ( & m - > bandwidthTimer ) ;
deleteTimer ( & m - > rechokeTimer ) ;
deleteTimer ( & m - > refillUpkeepTimer ) ;
2007-09-20 16:32:01 +00:00
}
void
tr_peerMgrFree ( tr_peerMgr * manager )
{
2007-09-29 06:37:03 +00:00
managerLock ( manager ) ;
2009-05-13 20:54:35 +00:00
deleteTimers ( manager ) ;
2008-09-17 19:44:24 +00:00
2007-10-02 14:35:02 +00:00
/* free the handshakes. Abort invokes handshakeDoneCB(), which removes
* the item from manager - > handshakes , so this is a little roundabout . . . */
2008-12-29 08:54:36 +00:00
while ( ! tr_ptrArrayEmpty ( & manager - > incomingHandshakes ) )
tr_handshakeAbort ( tr_ptrArrayNth ( & manager - > incomingHandshakes , 0 ) ) ;
2008-09-23 19:11:04 +00:00
2008-12-29 08:54:36 +00:00
tr_ptrArrayDestruct ( & manager - > incomingHandshakes , NULL ) ;
2007-10-02 14:35:02 +00:00
2007-09-29 06:37:03 +00:00
managerUnlock ( manager ) ;
2007-09-20 16:32:01 +00:00
tr_free ( manager ) ;
}
2008-02-10 04:03:19 +00:00
static int
2009-11-24 15:51:16 +00:00
clientIsDownloadingFrom ( const tr_torrent * tor , const tr_peer * peer )
2008-02-10 04:03:19 +00:00
{
2009-11-24 15:51:16 +00:00
if ( ! tr_torrentHasMetadata ( tor ) )
return TRUE ;
2008-02-10 04:03:19 +00:00
return peer - > clientIsInterested & & ! peer - > clientIsChoked ;
}
static int
clientIsUploadingTo ( const tr_peer * peer )
{
return peer - > peerIsInterested & & ! peer - > peerIsChoked ;
}
2008-04-17 03:48:56 +00:00
/***
* * * *
* * */
2010-04-20 21:54:03 +00:00
static void
atomSetSeedProbability ( struct peer_atom * atom , int seedProbability )
{
assert ( atom ! = NULL ) ;
assert ( - 1 < = seedProbability & & seedProbability < = 100 ) ;
atom - > seedProbability = seedProbability ;
if ( seedProbability = = 100 )
atom - > flags | = ADDED_F_SEED_FLAG ;
else if ( seedProbability ! = - 1 )
atom - > flags & = ~ ADDED_F_SEED_FLAG ;
}
static void
atomSetSeed ( struct peer_atom * atom )
{
atomSetSeedProbability ( atom , 100 ) ;
}
static tr_bool
atomIsSeed ( const struct peer_atom * atom )
{
return atom - > seedProbability = = 100 ;
}
2008-12-05 22:56:19 +00:00
tr_bool
2009-01-13 21:00:05 +00:00
tr_peerMgrPeerIsSeed ( const tr_torrent * tor ,
2008-12-22 00:52:44 +00:00
const tr_address * addr )
2008-04-17 03:48:56 +00:00
{
2008-12-05 22:56:19 +00:00
tr_bool isSeed = FALSE ;
2009-01-13 21:00:05 +00:00
const Torrent * t = tor - > torrentPeers ;
const struct peer_atom * atom = getExistingAtom ( t , addr ) ;
2008-04-17 03:48:56 +00:00
if ( atom )
2010-04-20 21:54:03 +00:00
isSeed = atomIsSeed ( atom ) ;
2008-04-17 03:48:56 +00:00
return isSeed ;
}
2009-11-08 23:20:00 +00:00
/**
* * * REQUESTS
* * *
* * * There are two data structures associated with managing block requests :
2009-11-22 03:57:36 +00:00
* * *
2009-11-08 23:20:00 +00:00
* * * 1. Torrent : : requests , an array of " struct block_request " which keeps
* * * track of which blocks have been requested , and when , and by which peers .
* * * This is list is used for ( a ) cancelling requests that have been pending
* * * for too long and ( b ) avoiding duplicate requests before endgame .
2009-11-22 03:57:36 +00:00
* * *
2009-11-08 23:20:00 +00:00
* * * 2. Torrent : : pieces , an array of " struct weighted_piece " which lists the
2010-03-03 04:16:18 +00:00
* * * pieces that we want to request . It ' s used to decide which blocks to
2009-11-08 23:20:00 +00:00
* * * return next when tr_peerMgrGetBlockRequests ( ) is called .
2009-11-22 03:57:36 +00:00
* */
2009-11-08 23:20:00 +00:00
/**
* * * struct block_request
* */
2008-10-25 02:20:16 +00:00
2007-09-20 16:32:01 +00:00
static int
2009-11-08 23:20:00 +00:00
compareReqByBlock ( const void * va , const void * vb )
2007-09-20 16:32:01 +00:00
{
2009-11-08 23:20:00 +00:00
const struct block_request * a = va ;
const struct block_request * b = vb ;
2009-12-15 20:13:34 +00:00
/* primary key: block */
2009-11-08 23:20:00 +00:00
if ( a - > block < b - > block ) return - 1 ;
if ( a - > block > b - > block ) return 1 ;
2009-12-15 20:13:34 +00:00
/* secondary key: peer */
if ( a - > peer < b - > peer ) return - 1 ;
if ( a - > peer > b - > peer ) return 1 ;
2009-11-08 23:20:00 +00:00
return 0 ;
2007-09-20 16:32:01 +00:00
}
2009-11-08 23:20:00 +00:00
static void
2009-12-16 18:20:01 +00:00
requestListAdd ( Torrent * t , tr_block_index_t block , tr_peer * peer )
2009-11-08 23:20:00 +00:00
{
struct block_request key ;
2009-11-02 00:17:30 +00:00
2009-11-08 23:20:00 +00:00
/* ensure enough room is available... */
if ( t - > requestCount + 1 > = t - > requestAlloc )
{
const int CHUNK_SIZE = 128 ;
t - > requestAlloc + = CHUNK_SIZE ;
t - > requests = tr_renew ( struct block_request ,
t - > requests , t - > requestAlloc ) ;
}
2007-09-20 16:32:01 +00:00
2009-11-08 23:20:00 +00:00
/* populate the record we're inserting */
key . block = block ;
key . peer = peer ;
2009-12-16 18:20:01 +00:00
key . sentAt = tr_time ( ) ;
2009-11-02 00:17:30 +00:00
2009-11-08 23:20:00 +00:00
/* insert the request to our array... */
{
2010-02-22 15:07:14 +00:00
tr_bool exact ;
const int pos = tr_lowerBound ( & key , t - > requests , t - > requestCount ,
sizeof ( struct block_request ) ,
compareReqByBlock , & exact ) ;
assert ( ! exact ) ;
memmove ( t - > requests + pos + 1 ,
t - > requests + pos ,
sizeof ( struct block_request ) * ( t - > requestCount + + - pos ) ) ;
t - > requests [ pos ] = key ;
2009-11-08 23:20:00 +00:00
}
2009-12-16 18:20:01 +00:00
if ( peer ! = NULL )
{
+ + peer - > pendingReqsToPeer ;
assert ( peer - > pendingReqsToPeer > = 0 ) ;
}
/*fprintf( stderr, "added request of block %lu from peer %s... "
2009-12-15 21:33:24 +00:00
" there are now %d block \n " ,
2009-12-16 18:20:01 +00:00
( unsigned long ) block , tr_atomAddrStr ( peer - > atom ) , t - > requestCount ) ; */
2008-10-25 02:20:16 +00:00
}
2008-10-11 04:07:50 +00:00
2009-11-08 23:20:00 +00:00
static struct block_request *
2009-12-15 20:13:34 +00:00
requestListLookup ( Torrent * t , tr_block_index_t block , const tr_peer * peer )
2008-10-25 02:20:16 +00:00
{
2009-11-08 23:20:00 +00:00
struct block_request key ;
key . block = block ;
2009-12-15 20:13:34 +00:00
key . peer = ( tr_peer * ) peer ;
2008-10-11 04:07:50 +00:00
2009-11-08 23:20:00 +00:00
return bsearch ( & key , t - > requests , t - > requestCount ,
sizeof ( struct block_request ) ,
compareReqByBlock ) ;
}
2009-11-02 00:17:30 +00:00
2009-12-15 21:33:24 +00:00
/* how many peers are we currently requesting this block from... */
2009-12-15 20:13:34 +00:00
static int
countBlockRequests ( Torrent * t , tr_block_index_t block )
{
tr_bool exact ;
int i , n , pos ;
struct block_request key ;
key . block = block ;
key . peer = NULL ;
pos = tr_lowerBound ( & key , t - > requests , t - > requestCount ,
sizeof ( struct block_request ) ,
compareReqByBlock , & exact ) ;
assert ( ! exact ) ; /* shouldn't have a request with .peer == NULL */
n = 0 ;
for ( i = pos ; i < t - > requestCount ; + + i ) {
if ( t - > requests [ i ] . block = = block )
+ + n ;
else
break ;
}
return n ;
}
2010-01-25 05:19:54 +00:00
static void
decrementPendingReqCount ( const struct block_request * b )
{
if ( b - > peer ! = NULL )
if ( b - > peer - > pendingReqsToPeer > 0 )
- - b - > peer - > pendingReqsToPeer ;
}
2009-11-08 23:20:00 +00:00
static void
2009-12-15 20:13:34 +00:00
requestListRemove ( Torrent * t , tr_block_index_t block , const tr_peer * peer )
2009-11-08 23:20:00 +00:00
{
2009-12-15 20:13:34 +00:00
const struct block_request * b = requestListLookup ( t , block , peer ) ;
2009-11-08 23:20:00 +00:00
if ( b ! = NULL )
2008-10-11 04:07:50 +00:00
{
2009-11-08 23:20:00 +00:00
const int pos = b - t - > requests ;
assert ( pos < t - > requestCount ) ;
2009-12-16 18:20:01 +00:00
2010-01-25 05:19:54 +00:00
decrementPendingReqCount ( b ) ;
2009-12-16 18:20:01 +00:00
2010-03-03 04:16:18 +00:00
tr_removeElementFromArray ( t - > requests ,
pos ,
sizeof ( struct block_request ) ,
t - > requestCount - - ) ;
2009-12-16 18:20:01 +00:00
/*fprintf( stderr, "removing request of block %lu from peer %s... "
2009-12-15 21:33:24 +00:00
" there are now %d block requests left \n " ,
2009-12-16 18:20:01 +00:00
( unsigned long ) block , tr_atomAddrStr ( peer - > atom ) , t - > requestCount ) ; */
2009-11-08 23:20:00 +00:00
}
}
2008-10-11 04:07:50 +00:00
2009-11-08 23:20:00 +00:00
/**
* * * struct weighted_piece
* */
enum
{
PIECES_UNSORTED ,
PIECES_SORTED_BY_INDEX ,
PIECES_SORTED_BY_WEIGHT
} ;
2008-10-11 04:07:50 +00:00
2009-11-08 23:20:00 +00:00
const tr_torrent * weightTorrent ;
2009-11-02 00:17:30 +00:00
2009-11-08 23:20:00 +00:00
/* we try to create a "weight" s.t. high-priority pieces come before others,
* and that partially - complete pieces come before empty ones . */
static int
comparePieceByWeight ( const void * va , const void * vb )
{
const struct weighted_piece * a = va ;
const struct weighted_piece * b = vb ;
int ia , ib , missing , pending ;
const tr_torrent * tor = weightTorrent ;
2009-11-02 00:17:30 +00:00
2009-11-08 23:20:00 +00:00
/* primary key: weight */
missing = tr_cpMissingBlocksInPiece ( & tor - > completion , a - > index ) ;
pending = a - > requestCount ;
ia = missing > pending ? missing - pending : ( int ) ( tor - > blockCountInPiece + pending ) ;
missing = tr_cpMissingBlocksInPiece ( & tor - > completion , b - > index ) ;
pending = b - > requestCount ;
ib = missing > pending ? missing - pending : ( int ) ( tor - > blockCountInPiece + pending ) ;
if ( ia < ib ) return - 1 ;
if ( ia > ib ) return 1 ;
2008-10-11 04:07:50 +00:00
2009-11-08 23:20:00 +00:00
/* secondary key: higher priorities go first */
ia = tor - > info . pieces [ a - > index ] . priority ;
ib = tor - > info . pieces [ b - > index ] . priority ;
if ( ia > ib ) return - 1 ;
if ( ia < ib ) return 1 ;
/* tertiary key: random */
2009-12-24 01:02:54 +00:00
if ( a - > salt < b - > salt ) return - 1 ;
if ( a - > salt > b - > salt ) return 1 ;
/* okay, they're equal */
return 0 ;
2009-02-25 13:04:51 +00:00
}
2009-11-08 23:20:00 +00:00
static int
comparePieceByIndex ( const void * va , const void * vb )
2008-10-25 02:20:16 +00:00
{
2009-11-08 23:20:00 +00:00
const struct weighted_piece * a = va ;
const struct weighted_piece * b = vb ;
if ( a - > index < b - > index ) return - 1 ;
if ( a - > index > b - > index ) return 1 ;
return 0 ;
2009-11-01 02:10:47 +00:00
}
2009-02-18 17:19:36 +00:00
2009-11-08 23:20:00 +00:00
static void
pieceListSort ( Torrent * t , int mode )
2009-11-01 02:10:47 +00:00
{
2009-11-08 23:20:00 +00:00
int ( * compar ) ( const void * , const void * ) ;
2009-11-02 00:17:30 +00:00
2009-11-08 23:20:00 +00:00
assert ( mode = = PIECES_SORTED_BY_INDEX
| | mode = = PIECES_SORTED_BY_WEIGHT ) ;
2010-01-12 20:17:22 +00:00
switch ( mode ) {
case PIECES_SORTED_BY_WEIGHT : compar = comparePieceByWeight ; break ;
case PIECES_SORTED_BY_INDEX : compar = comparePieceByIndex ; break ;
default : assert ( 0 & & " unhandled " ) ; break ;
2009-11-02 00:17:30 +00:00
}
2009-11-01 02:10:47 +00:00
2010-01-12 20:17:22 +00:00
weightTorrent = t - > tor ;
qsort ( t - > pieces , t - > pieceCount ,
sizeof ( struct weighted_piece ) , compar ) ;
}
2009-11-01 02:10:47 +00:00
2010-01-12 20:17:22 +00:00
static tr_bool
isInEndgame ( Torrent * t )
{
2010-03-03 04:16:18 +00:00
tr_bool endgame = FALSE ;
2009-11-02 00:17:30 +00:00
2010-01-12 20:17:22 +00:00
if ( ( t - > pieces ! = NULL ) & & ( t - > pieceCount > 0 ) )
{
const struct weighted_piece * p = t - > pieces ;
const int pending = p - > requestCount ;
2010-03-03 04:16:18 +00:00
const int missing = tr_cpMissingBlocksInPiece ( & t - > tor - > completion , p - > index ) ;
2010-01-12 20:17:22 +00:00
endgame = pending > = missing ;
2009-11-08 23:20:00 +00:00
}
2010-01-12 20:17:22 +00:00
/*if( endgame ) fprintf( stderr, "ENDGAME reached\n" );*/
return endgame ;
2009-11-02 00:17:30 +00:00
}
2010-03-07 22:56:14 +00:00
/**
* This function is useful for sanity checking ,
2010-03-17 17:07:40 +00:00
* but is too expensive even for nightly builds . . .
2010-03-07 22:56:14 +00:00
* let ' s leave it disabled but add an easy hook to compile it back in
*/
#if 0
static void
assertWeightedPiecesAreSorted ( Torrent * t )
{
if ( ! isInEndgame ( t ) )
{
int i ;
weightTorrent = t - > tor ;
for ( i = 0 ; i < t - > pieceCount - 1 ; + + i )
assert ( comparePieceByWeight ( & t - > pieces [ i ] , & t - > pieces [ i + 1 ] ) < = 0 ) ;
}
}
# else
# define assertWeightedPiecesAreSorted(t)
# endif
2009-11-08 23:20:00 +00:00
static struct weighted_piece *
pieceListLookup ( Torrent * t , tr_piece_index_t index )
2009-11-02 00:17:30 +00:00
{
2010-03-03 04:16:18 +00:00
int i ;
2009-11-08 23:20:00 +00:00
2010-03-03 04:16:18 +00:00
for ( i = 0 ; i < t - > pieceCount ; + + i )
if ( t - > pieces [ i ] . index = = index )
return & t - > pieces [ i ] ;
2009-11-08 23:20:00 +00:00
2010-03-03 04:16:18 +00:00
return NULL ;
2009-11-01 02:10:47 +00:00
}
2009-02-18 17:19:36 +00:00
2009-11-01 02:10:47 +00:00
static void
2009-11-08 23:20:00 +00:00
pieceListRebuild ( Torrent * t )
2009-11-01 02:10:47 +00:00
{
2010-03-03 04:16:18 +00:00
assertWeightedPiecesAreSorted ( t ) ;
2009-11-08 23:20:00 +00:00
if ( ! tr_torrentIsSeed ( t - > tor ) )
2009-11-02 00:17:30 +00:00
{
2009-11-08 23:20:00 +00:00
tr_piece_index_t i ;
tr_piece_index_t * pool ;
tr_piece_index_t poolCount = 0 ;
const tr_torrent * tor = t - > tor ;
const tr_info * inf = tr_torrentInfo ( tor ) ;
struct weighted_piece * pieces ;
int pieceCount ;
/* build the new list */
pool = tr_new ( tr_piece_index_t , inf - > pieceCount ) ;
for ( i = 0 ; i < inf - > pieceCount ; + + i )
if ( ! inf - > pieces [ i ] . dnd )
if ( ! tr_cpPieceIsComplete ( & tor - > completion , i ) )
pool [ poolCount + + ] = i ;
pieceCount = poolCount ;
pieces = tr_new0 ( struct weighted_piece , pieceCount ) ;
for ( i = 0 ; i < poolCount ; + + i ) {
struct weighted_piece * piece = pieces + i ;
piece - > index = pool [ i ] ;
piece - > requestCount = 0 ;
2009-12-24 18:01:48 +00:00
piece - > salt = tr_cryptoWeakRandInt ( 4096 ) ;
2009-11-08 23:20:00 +00:00
}
2009-11-02 00:17:30 +00:00
2009-11-08 23:20:00 +00:00
/* if we already had a list of pieces, merge it into
* the new list so we don ' t lose its requestCounts */
if ( t - > pieces ! = NULL )
{
struct weighted_piece * o = t - > pieces ;
struct weighted_piece * oend = o + t - > pieceCount ;
struct weighted_piece * n = pieces ;
struct weighted_piece * nend = n + pieceCount ;
pieceListSort ( t , PIECES_SORTED_BY_INDEX ) ;
while ( o ! = oend & & n ! = nend ) {
if ( o - > index < n - > index )
+ + o ;
else if ( o - > index > n - > index )
+ + n ;
else
* n + + = * o + + ;
}
2008-10-11 04:07:50 +00:00
2009-11-08 23:20:00 +00:00
tr_free ( t - > pieces ) ;
}
2008-02-10 04:03:19 +00:00
2009-11-08 23:20:00 +00:00
t - > pieces = pieces ;
t - > pieceCount = pieceCount ;
2010-01-12 20:17:22 +00:00
pieceListSort ( t , PIECES_SORTED_BY_WEIGHT ) ;
2009-11-08 23:20:00 +00:00
/* cleanup */
tr_free ( pool ) ;
2008-02-10 04:03:19 +00:00
}
}
2009-11-08 23:20:00 +00:00
static void
pieceListRemovePiece ( Torrent * t , tr_piece_index_t piece )
2008-10-25 15:19:46 +00:00
{
2010-03-03 04:16:18 +00:00
struct weighted_piece * p ;
assertWeightedPiecesAreSorted ( t ) ;
2008-10-25 15:19:46 +00:00
2010-03-03 04:16:18 +00:00
if ( ( p = pieceListLookup ( t , piece ) ) )
2009-11-08 23:20:00 +00:00
{
const int pos = p - t - > pieces ;
2009-11-02 00:17:30 +00:00
2010-03-03 04:16:18 +00:00
tr_removeElementFromArray ( t - > pieces ,
pos ,
sizeof ( struct weighted_piece ) ,
t - > pieceCount - - ) ;
2009-11-08 23:20:00 +00:00
if ( t - > pieceCount = = 0 )
{
tr_free ( t - > pieces ) ;
t - > pieces = NULL ;
2009-11-02 00:17:30 +00:00
}
}
2010-03-03 04:16:18 +00:00
assertWeightedPiecesAreSorted ( t ) ;
2009-11-02 00:17:30 +00:00
}
2009-10-27 20:06:55 +00:00
static void
2010-03-03 04:16:18 +00:00
pieceListResortPiece ( Torrent * t , struct weighted_piece * p )
2009-11-08 23:20:00 +00:00
{
2010-03-03 04:16:18 +00:00
int pos ;
tr_bool isSorted = TRUE ;
if ( p = = NULL )
return ;
/* is the torrent already sorted? */
pos = p - t - > pieces ;
weightTorrent = t - > tor ;
if ( isSorted & & ( pos > 0 ) & & ( comparePieceByWeight ( p - 1 , p ) > 0 ) )
isSorted = FALSE ;
if ( isSorted & & ( pos < t - > pieceCount - 1 ) & & ( comparePieceByWeight ( p , p + 1 ) > 0 ) )
isSorted = FALSE ;
2010-01-12 20:17:22 +00:00
2010-03-03 04:16:18 +00:00
/* if it's not sorted, move it around */
if ( ! isSorted )
2010-01-12 20:17:22 +00:00
{
tr_bool exact ;
2010-03-03 04:16:18 +00:00
const struct weighted_piece tmp = * p ;
2009-10-27 20:06:55 +00:00
2010-03-03 04:16:18 +00:00
tr_removeElementFromArray ( t - > pieces ,
pos ,
sizeof ( struct weighted_piece ) ,
t - > pieceCount - - ) ;
2010-01-12 20:17:22 +00:00
2010-03-03 04:16:18 +00:00
pos = tr_lowerBound ( & tmp , t - > pieces , t - > pieceCount ,
sizeof ( struct weighted_piece ) ,
comparePieceByWeight , & exact ) ;
2010-01-12 20:17:22 +00:00
2010-03-03 04:16:18 +00:00
memmove ( & t - > pieces [ pos + 1 ] ,
& t - > pieces [ pos ] ,
sizeof ( struct weighted_piece ) * ( t - > pieceCount + + - pos ) ) ;
2009-11-08 23:20:00 +00:00
2010-03-03 04:16:18 +00:00
t - > pieces [ pos ] = tmp ;
}
2010-01-12 20:17:22 +00:00
2010-03-03 04:16:18 +00:00
assertWeightedPiecesAreSorted ( t ) ;
}
2010-01-12 20:17:22 +00:00
2010-03-03 04:16:18 +00:00
static void
pieceListRemoveRequest ( Torrent * t , tr_block_index_t block )
{
struct weighted_piece * p ;
const tr_piece_index_t index = tr_torBlockPiece ( t - > tor , block ) ;
2010-01-12 20:17:22 +00:00
2010-03-03 04:16:18 +00:00
assertWeightedPiecesAreSorted ( t ) ;
if ( ( ( p = pieceListLookup ( t , index ) ) ) & & ( p - > requestCount > 0 ) )
{
- - p - > requestCount ;
pieceListResortPiece ( t , p ) ;
2010-01-12 20:17:22 +00:00
}
2010-03-03 04:16:18 +00:00
assertWeightedPiecesAreSorted ( t ) ;
2009-11-08 23:20:00 +00:00
}
/**
* * *
* */
void
tr_peerMgrRebuildRequests ( tr_torrent * tor )
2007-09-20 16:32:01 +00:00
{
2009-11-08 23:20:00 +00:00
assert ( tr_isTorrent ( tor ) ) ;
2007-09-20 16:32:01 +00:00
2009-11-08 23:20:00 +00:00
pieceListRebuild ( tor - > torrentPeers ) ;
}
void
tr_peerMgrGetNextRequests ( tr_torrent * tor ,
tr_peer * peer ,
int numwant ,
tr_block_index_t * setme ,
int * numgot )
{
int i ;
int got ;
Torrent * t ;
2010-01-12 20:17:22 +00:00
tr_bool endgame ;
2009-11-08 23:20:00 +00:00
struct weighted_piece * pieces ;
2009-11-24 02:16:31 +00:00
const tr_bitset * have = & peer - > have ;
2007-09-29 06:37:03 +00:00
2009-11-08 23:20:00 +00:00
/* sanity clause */
assert ( tr_isTorrent ( tor ) ) ;
2010-03-06 14:56:15 +00:00
assert ( peer - > clientIsInterested ) ;
assert ( ! peer - > clientIsChoked ) ;
2009-11-08 23:20:00 +00:00
assert ( numwant > 0 ) ;
2009-02-18 17:19:36 +00:00
2009-11-08 23:20:00 +00:00
/* walk through the pieces and find blocks that should be requested */
got = 0 ;
t = tor - > torrentPeers ;
2010-03-03 04:16:18 +00:00
assertWeightedPiecesAreSorted ( t ) ;
2008-06-07 21:26:41 +00:00
2009-11-08 23:20:00 +00:00
/* prep the pieces list */
if ( t - > pieces = = NULL )
pieceListRebuild ( t ) ;
2010-01-12 20:17:22 +00:00
endgame = isInEndgame ( t ) ;
2008-02-10 04:03:19 +00:00
2009-11-08 23:20:00 +00:00
pieces = t - > pieces ;
for ( i = 0 ; i < t - > pieceCount & & got < numwant ; + + i )
{
struct weighted_piece * p = pieces + i ;
2009-12-15 20:13:34 +00:00
const int missing = tr_cpMissingBlocksInPiece ( & tor - > completion , p - > index ) ;
2010-01-12 20:17:22 +00:00
const int maxDuplicatesPerBlock = endgame ? 3 : 1 ;
2009-12-15 20:13:34 +00:00
if ( p - > requestCount > ( missing * maxDuplicatesPerBlock ) )
continue ;
2009-02-18 22:25:13 +00:00
2009-11-08 23:20:00 +00:00
/* if the peer has this piece that we want... */
2009-11-24 02:16:31 +00:00
if ( tr_bitsetHasFast ( have , p - > index ) )
2007-09-20 16:32:01 +00:00
{
2009-11-08 23:20:00 +00:00
tr_block_index_t b = tr_torPieceFirstBlock ( tor , p - > index ) ;
const tr_block_index_t e = b + tr_torPieceCountBlocks ( tor , p - > index ) ;
2007-09-20 16:32:01 +00:00
2009-11-08 23:20:00 +00:00
for ( ; b ! = e & & got < numwant ; + + b )
2008-06-07 21:26:41 +00:00
{
2009-11-08 23:20:00 +00:00
/* don't request blocks we've already got */
if ( tr_cpBlockIsCompleteFast ( & tor - > completion , b ) )
continue ;
2009-12-15 20:13:34 +00:00
/* don't send the same request to the same peer twice */
if ( tr_peerMgrDidPeerRequest ( tor , peer , b ) )
continue ;
/* don't send the same request to any peer too many times */
2009-12-15 21:33:24 +00:00
if ( countBlockRequests ( t , b ) > = maxDuplicatesPerBlock )
2009-12-15 20:13:34 +00:00
continue ;
2009-11-08 23:20:00 +00:00
2009-12-15 20:13:34 +00:00
/* update the caller's table */
2009-11-08 23:20:00 +00:00
setme [ got + + ] = b ;
/* update our own tables */
2009-12-16 18:20:01 +00:00
requestListAdd ( t , b , peer ) ;
2009-11-08 23:20:00 +00:00
+ + p - > requestCount ;
2007-09-20 16:32:01 +00:00
}
}
}
2009-12-02 15:16:29 +00:00
/* In most cases we've just changed the weights of a small number of pieces.
2010-01-12 20:17:22 +00:00
* So rather than qsort ( ) ing the entire array , it ' s faster to apply an
* adaptive insertion sort algorithm . */
2009-11-08 23:20:00 +00:00
if ( got > 0 )
{
2010-01-12 20:17:22 +00:00
/* not enough requests || last piece modified */
if ( i = = t - > pieceCount ) - - i ;
2009-11-08 23:20:00 +00:00
weightTorrent = t - > tor ;
2010-01-12 20:17:22 +00:00
while ( - - i > = 0 )
{
tr_bool exact ;
2009-02-04 16:58:52 +00:00
2010-01-12 20:17:22 +00:00
/* relative position! */
const int newpos = tr_lowerBound ( & t - > pieces [ i ] , & t - > pieces [ i + 1 ] ,
t - > pieceCount - ( i + 1 ) ,
sizeof ( struct weighted_piece ) ,
comparePieceByWeight , & exact ) ;
if ( newpos > 0 )
{
const struct weighted_piece piece = t - > pieces [ i ] ;
memmove ( & t - > pieces [ i ] ,
& t - > pieces [ i + 1 ] ,
sizeof ( struct weighted_piece ) * ( newpos ) ) ;
t - > pieces [ i + newpos ] = piece ;
}
}
2009-11-02 00:17:30 +00:00
}
2010-03-03 04:16:18 +00:00
assertWeightedPiecesAreSorted ( t ) ;
2009-11-08 23:20:00 +00:00
* numgot = got ;
2007-09-20 16:32:01 +00:00
}
2009-11-08 23:20:00 +00:00
tr_bool
tr_peerMgrDidPeerRequest ( const tr_torrent * tor ,
const tr_peer * peer ,
tr_block_index_t block )
2008-10-11 04:07:50 +00:00
{
2009-11-08 23:20:00 +00:00
const Torrent * t = tor - > torrentPeers ;
2009-12-15 20:13:34 +00:00
return requestListLookup ( ( Torrent * ) t , block , peer ) ! = NULL ;
2009-11-08 23:20:00 +00:00
}
2008-10-11 04:07:50 +00:00
2009-11-08 23:20:00 +00:00
/* cancel requests that are too old */
2009-12-10 05:52:46 +00:00
static void
refillUpkeep ( int foo UNUSED , short bar UNUSED , void * vmgr )
2009-11-08 23:20:00 +00:00
{
time_t now ;
2010-03-08 04:29:58 +00:00
uint64_t now_msec ;
2009-11-08 23:20:00 +00:00
time_t too_old ;
tr_torrent * tor ;
tr_peerMgr * mgr = vmgr ;
managerLock ( mgr ) ;
2009-11-26 18:47:08 +00:00
now = tr_time ( ) ;
2010-03-08 04:29:58 +00:00
now_msec = tr_date ( ) ;
2009-11-08 23:20:00 +00:00
too_old = now - REQUEST_TTL_SECS ;
tor = NULL ;
while ( ( tor = tr_torrentNext ( mgr - > session , tor ) ) )
{
Torrent * t = tor - > torrentPeers ;
const int n = t - > requestCount ;
if ( n > 0 )
{
int keepCount = 0 ;
int cancelCount = 0 ;
struct block_request * cancel = tr_new ( struct block_request , n ) ;
const struct block_request * it ;
const struct block_request * end ;
for ( it = t - > requests , end = it + n ; it ! = end ; + + it )
2010-01-20 18:48:52 +00:00
{
2010-03-06 14:56:15 +00:00
if ( ( it - > sentAt < = too_old ) & & ! tr_peerMsgsIsReadingBlock ( it - > peer - > msgs , it - > block ) )
2009-11-08 23:20:00 +00:00
cancel [ cancelCount + + ] = * it ;
else
2010-01-20 18:48:52 +00:00
{
if ( it ! = & t - > requests [ keepCount ] )
t - > requests [ keepCount ] = * it ;
keepCount + + ;
}
}
2009-11-08 23:20:00 +00:00
/* prune out the ones we aren't keeping */
t - > requestCount = keepCount ;
2008-10-11 04:07:50 +00:00
2009-11-08 23:20:00 +00:00
/* send cancel messages for all the "cancel" ones */
2010-01-25 04:54:04 +00:00
for ( it = cancel , end = it + cancelCount ; it ! = end ; + + it ) {
if ( ( it - > peer ! = NULL ) & & ( it - > peer - > msgs ! = NULL ) ) {
2010-03-08 04:29:58 +00:00
tr_historyAdd ( it - > peer - > cancelsSentToPeer , now_msec , 1 ) ;
2009-11-08 23:20:00 +00:00
tr_peerMsgsCancel ( it - > peer - > msgs , it - > block ) ;
2010-01-25 05:19:54 +00:00
decrementPendingReqCount ( it ) ;
2010-01-25 04:54:04 +00:00
}
}
2008-12-30 08:20:16 +00:00
2009-11-08 23:20:00 +00:00
/* decrement the pending request counts for the timed-out blocks */
for ( it = cancel , end = it + cancelCount ; it ! = end ; + + it )
pieceListRemoveRequest ( t , it - > block ) ;
/* cleanup loop */
tr_free ( cancel ) ;
}
}
2009-12-10 19:04:10 +00:00
tr_timerAddMsec ( mgr - > refillUpkeepTimer , REFILL_UPKEEP_PERIOD_MSEC ) ;
2009-11-08 23:20:00 +00:00
managerUnlock ( mgr ) ;
2008-10-11 04:07:50 +00:00
}
2007-11-13 05:36:43 +00:00
static void
2009-05-09 06:08:21 +00:00
addStrike ( Torrent * t , tr_peer * peer )
2007-11-13 05:36:43 +00:00
{
2008-09-23 19:11:04 +00:00
tordbg ( t , " increasing peer %s strike count to %d " ,
2009-10-29 16:10:03 +00:00
tr_atomAddrStr ( peer - > atom ) , peer - > strikes + 1 ) ;
2007-11-13 05:36:43 +00:00
if ( + + peer - > strikes > = MAX_BAD_PIECES_PER_PEER )
{
2009-05-09 06:08:21 +00:00
struct peer_atom * atom = peer - > atom ;
2007-11-13 05:36:43 +00:00
atom - > myflags | = MYFLAG_BANNED ;
peer - > doPurge = 1 ;
2009-10-29 16:10:03 +00:00
tordbg ( t , " banning peer %s " , tr_atomAddrStr ( atom ) ) ;
2007-11-13 05:36:43 +00:00
}
}
2007-09-20 16:32:01 +00:00
static void
2009-11-08 23:20:00 +00:00
gotBadPiece ( Torrent * t , tr_piece_index_t pieceIndex )
2007-09-20 16:32:01 +00:00
{
2008-09-23 19:11:04 +00:00
tr_torrent * tor = t - > tor ;
2008-06-07 21:26:41 +00:00
const uint32_t byteCount = tr_torPieceCountBytes ( tor , pieceIndex ) ;
2008-09-23 19:11:04 +00:00
2008-06-07 21:26:41 +00:00
tor - > corruptCur + = byteCount ;
tor - > downloadedCur - = MIN ( tor - > downloadedCur , byteCount ) ;
2010-02-08 16:47:30 +00:00
tr_announcerAddBytes ( tor , TR_ANN_CORRUPT , byteCount ) ;
2008-06-07 21:26:41 +00:00
}
2008-12-02 17:10:54 +00:00
static void
2008-12-22 00:52:44 +00:00
peerSuggestedPiece ( Torrent * t UNUSED ,
tr_peer * peer UNUSED ,
tr_piece_index_t pieceIndex UNUSED ,
int isFastAllowed UNUSED )
2008-12-02 17:10:54 +00:00
{
2008-12-22 00:52:44 +00:00
#if 0
2008-12-02 17:10:54 +00:00
assert ( t ) ;
assert ( peer ) ;
assert ( peer - > msgs ) ;
/* is this a valid piece? */
if ( pieceIndex > = t - > tor - > info . pieceCount )
return ;
/* don't ask for it if we've already got it */
if ( tr_cpPieceIsComplete ( t - > tor - > completion , pieceIndex ) )
return ;
/* don't ask for it if they don't have it */
if ( ! tr_bitfieldHas ( peer - > have , pieceIndex ) )
return ;
/* don't ask for it if we're choked and it's not fast */
if ( ! isFastAllowed & & peer - > clientIsChoked )
return ;
/* request the blocks that we don't have in this piece */
{
tr_block_index_t block ;
const tr_torrent * tor = t - > tor ;
const tr_block_index_t start = tr_torPieceFirstBlock ( tor , pieceIndex ) ;
const tr_block_index_t end = start + tr_torPieceCountBlocks ( tor , pieceIndex ) ;
for ( block = start ; block < end ; + + block )
{
if ( ! tr_cpBlockIsComplete ( tor - > completion , block ) )
{
const uint32_t offset = getBlockOffsetInPiece ( tor , block ) ;
const uint32_t length = tr_torBlockCountBytes ( tor , block ) ;
2008-12-22 00:52:44 +00:00
tr_peerMsgsAddRequest ( peer - > msgs , pieceIndex , offset , length ) ;
2008-12-02 17:10:54 +00:00
incrementPieceRequests ( t , pieceIndex ) ;
}
}
}
2008-12-22 00:52:44 +00:00
# endif
2008-12-02 17:10:54 +00:00
}
2009-11-08 23:20:00 +00:00
static void
2009-12-15 20:13:34 +00:00
removeRequestFromTables ( Torrent * t , tr_block_index_t block , const tr_peer * peer )
2009-11-08 23:20:00 +00:00
{
2009-12-15 20:13:34 +00:00
requestListRemove ( t , block , peer ) ;
2009-11-08 23:20:00 +00:00
pieceListRemoveRequest ( t , block ) ;
}
/* peer choked us, or maybe it disconnected.
either way we need to remove all its requests */
static void
peerDeclinedAllRequests ( Torrent * t , const tr_peer * peer )
{
int i , n ;
tr_block_index_t * blocks = tr_new ( tr_block_index_t , t - > requestCount ) ;
for ( i = n = 0 ; i < t - > requestCount ; + + i )
if ( peer = = t - > requests [ i ] . peer )
blocks [ n + + ] = t - > requests [ i ] . block ;
for ( i = 0 ; i < n ; + + i )
2009-12-15 20:13:34 +00:00
removeRequestFromTables ( t , blocks [ i ] , peer ) ;
2009-11-08 23:20:00 +00:00
tr_free ( blocks ) ;
}
2008-06-07 21:26:41 +00:00
static void
2008-12-22 00:52:44 +00:00
peerCallbackFunc ( void * vpeer , void * vevent , void * vt )
2008-06-07 21:26:41 +00:00
{
2008-12-22 00:52:44 +00:00
tr_peer * peer = vpeer ; /* may be NULL if peer is a webseed */
Torrent * t = vt ;
2008-06-07 21:26:41 +00:00
const tr_peer_event * e = vevent ;
2007-09-20 16:32:01 +00:00
2007-10-01 15:17:15 +00:00
torrentLock ( t ) ;
2007-09-29 06:37:03 +00:00
2007-09-20 16:32:01 +00:00
switch ( e - > eventType )
{
2008-09-23 19:11:04 +00:00
case TR_PEER_PEER_GOT_DATA :
{
2009-11-26 18:47:08 +00:00
const time_t now = tr_time ( ) ;
2008-06-07 21:26:41 +00:00
tr_torrent * tor = t - > tor ;
2008-11-17 04:00:57 +00:00
2009-03-28 16:47:01 +00:00
tr_torrentSetActivityDate ( tor , now ) ;
2008-11-17 04:00:57 +00:00
2009-08-07 05:29:37 +00:00
if ( e - > wasPieceData ) {
2008-11-17 04:00:57 +00:00
tor - > uploadedCur + = e - > length ;
2010-02-08 16:47:30 +00:00
tr_announcerAddBytes ( tor , TR_ANN_UP , e - > length ) ;
2009-08-07 05:29:37 +00:00
tr_torrentSetDirty ( tor ) ;
}
2008-11-17 04:00:57 +00:00
/* update the stats */
if ( e - > wasPieceData )
tr_statsAddUploaded ( tor - > session , e - > length ) ;
/* update our atom */
2009-05-11 16:04:31 +00:00
if ( peer & & e - > wasPieceData )
2009-05-09 06:08:21 +00:00
peer - > atom - > piece_data_time = now ;
2008-11-17 04:00:57 +00:00
2008-01-05 18:17:56 +00:00
break ;
}
2009-11-08 23:20:00 +00:00
case TR_PEER_CLIENT_GOT_REJ :
2009-12-15 20:13:34 +00:00
removeRequestFromTables ( t , _tr_block ( t - > tor , e - > pieceIndex , e - > offset ) , peer ) ;
2009-11-08 23:20:00 +00:00
break ;
case TR_PEER_CLIENT_GOT_CHOKE :
peerDeclinedAllRequests ( t , peer ) ;
break ;
2009-10-29 16:10:03 +00:00
case TR_PEER_CLIENT_GOT_PORT :
if ( peer )
peer - > atom - > port = e - > port ;
break ;
2008-12-02 17:10:54 +00:00
case TR_PEER_CLIENT_GOT_SUGGEST :
if ( peer )
peerSuggestedPiece ( t , peer , e - > pieceIndex , FALSE ) ;
break ;
case TR_PEER_CLIENT_GOT_ALLOWED_FAST :
if ( peer )
peerSuggestedPiece ( t , peer , e - > pieceIndex , TRUE ) ;
break ;
2008-09-23 19:11:04 +00:00
case TR_PEER_CLIENT_GOT_DATA :
{
2009-11-26 18:47:08 +00:00
const time_t now = tr_time ( ) ;
2008-06-07 21:26:41 +00:00
tr_torrent * tor = t - > tor ;
2009-03-28 16:47:01 +00:00
tr_torrentSetActivityDate ( tor , now ) ;
2008-11-17 04:00:57 +00:00
2010-02-20 15:36:25 +00:00
if ( e - > wasPieceData ) {
2008-06-09 22:53:45 +00:00
tor - > downloadedCur + = e - > length ;
2009-08-07 05:29:37 +00:00
tr_torrentSetDirty ( tor ) ;
}
2008-11-17 04:00:57 +00:00
2009-08-10 20:04:08 +00:00
/* update the stats */
2008-11-17 04:00:57 +00:00
if ( e - > wasPieceData )
tr_statsAddDownloaded ( tor - > session , e - > length ) ;
/* update our atom */
2009-05-11 16:04:31 +00:00
if ( peer & & e - > wasPieceData )
2009-05-09 06:08:21 +00:00
peer - > atom - > piece_data_time = now ;
2008-11-17 04:00:57 +00:00
2007-09-20 16:32:01 +00:00
break ;
2008-06-07 21:26:41 +00:00
}
2007-09-20 16:32:01 +00:00
2008-09-23 19:11:04 +00:00
case TR_PEER_PEER_PROGRESS :
{
if ( peer )
{
2009-05-09 06:08:21 +00:00
struct peer_atom * atom = peer - > atom ;
2009-08-07 05:41:33 +00:00
if ( e - > progress > = 1.0 ) {
2010-04-20 21:54:03 +00:00
tordbg ( t , " marking peer %s as a seed " , tr_atomAddrStr ( atom ) ) ;
atomSetSeed ( atom ) ;
2008-06-07 21:26:41 +00:00
}
}
break ;
2007-09-20 23:07:36 +00:00
}
2008-06-07 21:26:41 +00:00
case TR_PEER_CLIENT_GOT_BLOCK :
{
2009-01-12 21:59:53 +00:00
tr_torrent * tor = t - > tor ;
2008-12-05 22:56:19 +00:00
tr_block_index_t block = _tr_block ( tor , e - > pieceIndex , e - > offset ) ;
2008-06-07 21:26:41 +00:00
2009-12-15 20:13:34 +00:00
requestListRemove ( t , block , peer ) ;
2010-02-20 15:36:25 +00:00
pieceListRemoveRequest ( t , block ) ;
2008-06-07 21:26:41 +00:00
2010-03-08 04:29:58 +00:00
if ( peer ! = NULL )
2010-03-10 15:55:00 +00:00
tr_historyAdd ( peer - > blocksSentToClient , tr_date ( ) , 1 ) ;
2010-03-08 04:29:58 +00:00
2009-11-08 23:20:00 +00:00
if ( tr_cpBlockIsComplete ( & tor - > completion , block ) )
2008-06-07 21:26:41 +00:00
{
2010-02-20 15:36:25 +00:00
/* we already have this block... */
const uint32_t n = tr_torBlockCountBytes ( tor , block ) ;
tor - > downloadedCur - = MIN ( tor - > downloadedCur , n ) ;
2009-11-08 23:20:00 +00:00
tordbg ( t , " we have this block already... " ) ;
}
else
{
tr_cpBlockAdd ( & tor - > completion , block ) ;
2010-03-03 04:16:18 +00:00
pieceListResortPiece ( t , pieceListLookup ( t , e - > pieceIndex ) ) ;
2009-11-08 23:20:00 +00:00
tr_torrentSetDirty ( tor ) ;
2008-06-07 21:26:41 +00:00
2009-11-08 23:20:00 +00:00
if ( tr_cpPieceIsComplete ( & tor - > completion , e - > pieceIndex ) )
2008-09-23 19:11:04 +00:00
{
2009-11-08 23:20:00 +00:00
const tr_piece_index_t p = e - > pieceIndex ;
2010-02-23 06:05:18 +00:00
const tr_bool ok = tr_ioTestPiece ( tor , p ) ;
2009-11-08 23:20:00 +00:00
if ( ! ok )
{
tr_torerr ( tor , _ ( " Piece %lu, which was just downloaded, failed its checksum test " ) ,
( unsigned long ) p ) ;
}
2008-06-07 21:26:41 +00:00
2009-11-08 23:20:00 +00:00
tr_torrentSetHasPiece ( tor , p , ok ) ;
tr_torrentSetPieceChecked ( tor , p , TRUE ) ;
tr_peerMgrSetBlame ( tor , p , ok ) ;
2008-06-07 21:26:41 +00:00
2009-11-08 23:20:00 +00:00
if ( ! ok )
{
gotBadPiece ( t , p ) ;
}
else
{
int i ;
int peerCount ;
tr_peer * * peers ;
tr_file_index_t fileIndex ;
2010-02-20 15:36:25 +00:00
/* only add this to downloadedCur if we got it from a peer --
* webseeds shouldn ' t count against our ratio . As one tracker
* admin put it , " Those pieces are downloaded directly from the
* content distributor , not the peers , it is the tracker ' s job
* to manage the swarms , not the web server and does not fit
* into the jurisdiction of the tracker . " */
if ( peer ! = NULL ) {
const uint32_t n = tr_torPieceCountBytes ( tor , p ) ;
tr_announcerAddBytes ( tor , TR_ANN_DOWN , n ) ;
}
2009-11-08 23:20:00 +00:00
peerCount = tr_ptrArraySize ( & t - > peers ) ;
peers = ( tr_peer * * ) tr_ptrArrayBase ( & t - > peers ) ;
for ( i = 0 ; i < peerCount ; + + i )
tr_peerMsgsHave ( peers [ i ] - > msgs , p ) ;
for ( fileIndex = 0 ; fileIndex < tor - > info . fileCount ; + + fileIndex ) {
const tr_file * file = & tor - > info . files [ fileIndex ] ;
if ( ( file - > firstPiece < = p ) & & ( p < = file - > lastPiece ) )
if ( tr_cpFileIsComplete ( & tor - > completion , fileIndex ) )
tr_torrentFileCompleted ( tor , fileIndex ) ;
}
pieceListRemovePiece ( t , p ) ;
2009-01-12 21:59:53 +00:00
}
2008-06-21 01:03:31 +00:00
}
2009-11-09 06:36:47 +00:00
t - > needsCompletenessCheck = TRUE ;
2008-06-07 21:26:41 +00:00
}
2007-09-20 16:32:01 +00:00
break ;
2008-06-07 21:26:41 +00:00
}
2007-09-20 16:32:01 +00:00
2008-06-07 21:26:41 +00:00
case TR_PEER_ERROR :
2009-08-13 17:25:26 +00:00
if ( ( e - > err = = ERANGE ) | | ( e - > err = = EMSGSIZE ) | | ( e - > err = = ENOTCONN ) )
2008-09-23 19:11:04 +00:00
{
2008-10-03 04:49:06 +00:00
/* some protocol error from the peer */
peer - > doPurge = 1 ;
2009-08-13 17:25:26 +00:00
tordbg ( t , " setting %s doPurge flag because we got an ERANGE, EMSGSIZE, or ENOTCONN error " ,
2009-10-29 16:10:03 +00:00
tr_atomAddrStr ( peer - > atom ) ) ;
2008-10-03 04:49:06 +00:00
}
2009-11-22 03:57:36 +00:00
else
2008-10-03 04:49:06 +00:00
{
2009-08-13 17:25:26 +00:00
tordbg ( t , " unhandled error: %s " , tr_strerror ( e - > err ) ) ;
2008-01-18 19:13:32 +00:00
}
2007-09-20 16:32:01 +00:00
break ;
default :
2008-09-23 19:11:04 +00:00
assert ( 0 ) ;
2007-09-20 16:32:01 +00:00
}
2007-09-29 06:37:03 +00:00
2007-10-01 15:17:15 +00:00
torrentUnlock ( t ) ;
2007-09-20 16:32:01 +00:00
}
2009-11-26 05:13:58 +00:00
static int
getDefaultShelfLife ( uint8_t from )
{
/* in general, peers obtained from firsthand contact
* are better than those from secondhand , etc etc */
switch ( from )
{
2009-11-26 18:38:37 +00:00
case TR_PEER_FROM_INCOMING : return 60 * 60 * 6 ;
case TR_PEER_FROM_LTEP : return 60 * 60 * 6 ;
case TR_PEER_FROM_TRACKER : return 60 * 60 * 3 ;
case TR_PEER_FROM_DHT : return 60 * 60 * 3 ;
2009-11-26 05:13:58 +00:00
case TR_PEER_FROM_PEX : return 60 * 60 * 2 ;
case TR_PEER_FROM_RESUME : return 60 * 60 ;
2009-11-26 18:38:37 +00:00
default : return 60 * 60 ;
2009-11-26 05:13:58 +00:00
}
}
2007-10-01 05:32:34 +00:00
static void
2009-11-26 05:13:58 +00:00
ensureAtomExists ( Torrent * t ,
const tr_address * addr ,
const tr_port port ,
const uint8_t flags ,
2010-04-20 21:54:03 +00:00
const int8_t seedProbability ,
2009-11-26 05:13:58 +00:00
const uint8_t from )
2007-10-01 05:32:34 +00:00
{
2010-04-20 21:54:03 +00:00
struct peer_atom * a ;
2009-09-10 03:07:54 +00:00
assert ( tr_isAddress ( addr ) ) ;
2009-09-10 03:28:55 +00:00
assert ( from < TR_PEER_FROM__MAX ) ;
2009-09-10 03:07:54 +00:00
2010-04-20 21:54:03 +00:00
a = getExistingAtom ( t , addr ) ;
if ( a = = NULL )
2007-10-01 05:32:34 +00:00
{
2009-11-26 18:38:37 +00:00
const int jitter = tr_cryptoWeakRandInt ( 60 * 10 ) ;
2007-10-16 02:16:57 +00:00
a = tr_new0 ( struct peer_atom , 1 ) ;
2007-10-01 05:32:34 +00:00
a - > addr = * addr ;
a - > port = port ;
a - > flags = flags ;
2007-10-16 03:14:07 +00:00
a - > from = from ;
2009-11-26 18:47:08 +00:00
a - > shelf_date = tr_time ( ) + getDefaultShelfLife ( from ) + jitter ;
2010-04-20 21:54:03 +00:00
atomSetSeedProbability ( a , seedProbability ) ;
2009-11-26 05:13:58 +00:00
tr_ptrArrayInsertSorted ( & t - > pool , a , compareAtomsByAddress ) ;
2009-10-29 16:10:03 +00:00
tordbg ( t , " got a new atom: %s " , tr_atomAddrStr ( a ) ) ;
2007-10-01 05:32:34 +00:00
}
2010-04-20 21:54:03 +00:00
else if ( a - > seedProbability = = - 1 )
{
atomSetSeedProbability ( a , seedProbability ) ;
}
2007-10-01 05:32:34 +00:00
}
2008-01-20 03:21:51 +00:00
static int
2008-09-05 05:14:49 +00:00
getMaxPeerCount ( const tr_torrent * tor )
2008-01-20 03:21:51 +00:00
{
2008-01-28 21:05:50 +00:00
return tor - > maxConnectedPeers ;
2008-01-20 03:21:51 +00:00
}
2008-09-05 05:14:49 +00:00
static int
getPeerCount ( const Torrent * t )
{
2009-05-08 14:56:11 +00:00
return tr_ptrArraySize ( & t - > peers ) ; /* + tr_ptrArraySize( &t->outgoingHandshakes ); */
2008-09-05 05:14:49 +00:00
}
2007-10-02 16:12:44 +00:00
/* FIXME: this is kind of a mess. */
2008-12-05 22:56:19 +00:00
static tr_bool
2008-12-22 00:52:44 +00:00
myHandshakeDoneCB ( tr_handshake * handshake ,
2008-12-20 22:19:34 +00:00
tr_peerIo * io ,
2010-04-20 21:54:03 +00:00
tr_bool readAnythingFromPeer ,
2009-01-24 17:20:07 +00:00
tr_bool isConnected ,
2008-09-23 19:11:04 +00:00
const uint8_t * peer_id ,
2008-12-20 22:19:34 +00:00
void * vmanager )
2008-09-23 19:11:04 +00:00
{
2008-12-05 22:56:19 +00:00
tr_bool ok = isConnected ;
tr_bool success = FALSE ;
2008-12-02 03:41:58 +00:00
tr_port port ;
const tr_address * addr ;
tr_peerMgr * manager = vmanager ;
Torrent * t ;
tr_handshake * ours ;
2007-09-20 16:32:01 +00:00
2008-08-01 16:43:22 +00:00
assert ( io ) ;
2009-01-24 17:20:07 +00:00
assert ( tr_isBool ( ok ) ) ;
2007-09-20 16:32:01 +00:00
2007-10-02 16:12:44 +00:00
t = tr_peerIoHasTorrentHash ( io )
? getExistingTorrent ( manager , tr_peerIoGetTorrentHash ( io ) )
: NULL ;
2007-09-20 16:32:01 +00:00
2007-10-02 16:12:44 +00:00
if ( tr_peerIoIsIncoming ( io ) )
2008-12-29 08:54:36 +00:00
ours = tr_ptrArrayRemoveSorted ( & manager - > incomingHandshakes ,
2007-10-02 16:12:44 +00:00
handshake , handshakeCompare ) ;
2008-08-01 16:43:22 +00:00
else if ( t )
2008-12-29 08:54:36 +00:00
ours = tr_ptrArrayRemoveSorted ( & t - > outgoingHandshakes ,
2007-10-02 16:12:44 +00:00
handshake , handshakeCompare ) ;
else
ours = handshake ;
2007-09-20 16:32:01 +00:00
2008-08-01 16:43:22 +00:00
assert ( ours ) ;
2007-10-02 16:12:44 +00:00
assert ( ours = = handshake ) ;
2007-09-29 06:37:03 +00:00
2008-08-01 16:43:22 +00:00
if ( t )
2007-09-29 06:37:03 +00:00
torrentLock ( t ) ;
2007-10-16 03:14:07 +00:00
addr = tr_peerIoGetAddress ( io , & port ) ;
2007-10-02 16:12:44 +00:00
2007-10-10 15:59:59 +00:00
if ( ! ok | | ! t | | ! t - > isRunning )
2007-09-20 16:32:01 +00:00
{
2008-09-23 19:11:04 +00:00
if ( t )
{
2007-11-08 04:11:09 +00:00
struct peer_atom * atom = getExistingAtom ( t , addr ) ;
if ( atom )
2010-04-20 21:54:03 +00:00
{
2008-08-08 22:44:32 +00:00
+ + atom - > numFails ;
2010-04-20 21:54:03 +00:00
if ( ! readAnythingFromPeer )
{
tordbg ( t , " marking peer %s as unreachable... numFails is %d " , tr_atomAddrStr ( atom ) , ( int ) atom - > numFails ) ;
atom - > myflags | = MYFLAG_UNREACHABLE ;
}
}
2007-11-08 04:11:09 +00:00
}
2007-09-20 16:32:01 +00:00
}
2007-09-29 06:37:03 +00:00
else /* looking good */
{
2007-10-08 01:31:27 +00:00
struct peer_atom * atom ;
2009-11-26 05:13:58 +00:00
2010-04-20 21:54:03 +00:00
ensureAtomExists ( t , addr , port , 0 , - 1 , TR_PEER_FROM_INCOMING ) ;
2007-10-08 01:31:27 +00:00
atom = getExistingAtom ( t , addr ) ;
2009-11-26 18:47:08 +00:00
atom - > time = tr_time ( ) ;
2008-12-11 07:04:46 +00:00
atom - > piece_data_time = 0 ;
2010-04-20 21:54:03 +00:00
atom - > lastConnectionAt = tr_time ( ) ;
atom - > myflags & = ~ MYFLAG_UNREACHABLE ;
2007-10-11 00:09:58 +00:00
2007-10-08 01:31:27 +00:00
if ( atom - > myflags & MYFLAG_BANNED )
{
2009-10-22 19:08:45 +00:00
tordbg ( t , " banned peer %s tried to reconnect " ,
2009-10-29 16:10:03 +00:00
tr_atomAddrStr ( atom ) ) ;
2007-10-08 01:31:27 +00:00
}
2008-09-05 05:14:49 +00:00
else if ( tr_peerIoIsIncoming ( io )
2008-09-23 19:11:04 +00:00
& & ( getPeerCount ( t ) > = getMaxPeerCount ( t - > tor ) ) )
2008-09-05 05:14:49 +00:00
2007-11-09 15:19:12 +00:00
{
}
2007-10-08 01:31:27 +00:00
else
{
2009-11-28 07:15:31 +00:00
tr_peer * peer = atom - > peer ;
2007-10-11 00:09:58 +00:00
2008-08-01 16:43:22 +00:00
if ( peer ) /* we already have this peer */
2007-10-11 00:09:58 +00:00
{
}
else
{
2009-10-29 16:10:03 +00:00
peer = getPeer ( t , atom ) ;
2007-10-11 00:09:58 +00:00
tr_free ( peer - > client ) ;
2008-04-29 16:57:16 +00:00
if ( ! peer_id )
peer - > client = NULL ;
2008-11-25 21:35:17 +00:00
else {
2008-04-29 16:57:16 +00:00
char client [ 128 ] ;
tr_clientForId ( client , sizeof ( client ) , peer_id ) ;
peer - > client = tr_strdup ( client ) ;
}
2008-11-25 21:35:17 +00:00
2009-01-06 03:21:22 +00:00
peer - > io = tr_handshakeStealIO ( handshake ) ; /* this steals its refcount too, which is
balanced by our unref in peerDestructor ( ) */
2009-06-16 19:31:17 +00:00
tr_peerIoSetParent ( peer - > io , t - > tor - > bandwidth ) ;
2008-12-02 19:06:08 +00:00
tr_peerMsgsNew ( t - > tor , peer , peerCallbackFunc , t , & peer - > msgsTag ) ;
2008-09-19 17:03:25 +00:00
success = TRUE ;
2007-10-11 00:09:58 +00:00
}
2007-09-20 16:32:01 +00:00
}
}
2007-09-29 06:37:03 +00:00
2008-08-01 16:43:22 +00:00
if ( t )
2007-09-29 06:37:03 +00:00
torrentUnlock ( t ) ;
2008-09-19 17:03:25 +00:00
return success ;
2007-09-20 16:32:01 +00:00
}
void
2008-12-02 03:41:58 +00:00
tr_peerMgrAddIncoming ( tr_peerMgr * manager ,
tr_address * addr ,
tr_port port ,
int socket )
2007-09-20 16:32:01 +00:00
{
2009-10-23 03:41:36 +00:00
tr_session * session ;
2007-09-29 06:37:03 +00:00
managerLock ( manager ) ;
2009-10-23 03:41:36 +00:00
assert ( tr_isSession ( manager - > session ) ) ;
session = manager - > session ;
if ( tr_sessionIsAddressBlocked ( session , addr ) )
2008-03-30 00:57:55 +00:00
{
2008-12-05 22:56:19 +00:00
tr_dbg ( " Banned IP address \" %s \" tried to connect to us " , tr_ntop_non_ts ( addr ) ) ;
2009-10-23 03:41:36 +00:00
tr_netClose ( session , socket ) ;
2008-03-30 00:57:55 +00:00
}
2008-12-29 08:54:36 +00:00
else if ( getExistingHandshake ( & manager - > incomingHandshakes , addr ) )
2007-10-25 13:38:34 +00:00
{
2009-10-23 03:41:36 +00:00
tr_netClose ( session , socket ) ;
2007-10-25 13:38:34 +00:00
}
2009-11-29 18:35:02 +00:00
else /* we don't have a connection to them yet... */
2007-09-20 16:32:01 +00:00
{
2008-09-23 19:11:04 +00:00
tr_peerIo * io ;
2007-10-15 16:01:42 +00:00
tr_handshake * handshake ;
2007-10-02 16:12:44 +00:00
2009-10-23 03:41:36 +00:00
io = tr_peerIoNewIncoming ( session , session - > bandwidth , addr , port , socket ) ;
2007-10-15 16:01:42 +00:00
handshake = tr_handshakeNew ( io ,
2009-10-23 03:41:36 +00:00
session - > encryptionMode ,
2007-10-15 16:01:42 +00:00
myHandshakeDoneCB ,
manager ) ;
2007-10-02 16:12:44 +00:00
2009-01-05 06:45:08 +00:00
tr_peerIoUnref ( io ) ; /* balanced by the implicit ref in tr_peerIoNewIncoming() */
2008-12-29 08:54:36 +00:00
tr_ptrArrayInsertSorted ( & manager - > incomingHandshakes , handshake ,
2008-09-23 19:11:04 +00:00
handshakeCompare ) ;
2007-09-20 16:32:01 +00:00
}
2007-09-29 06:37:03 +00:00
managerUnlock ( manager ) ;
2007-09-20 16:32:01 +00:00
}
2008-12-22 00:52:44 +00:00
static tr_bool
2008-12-16 23:31:05 +00:00
tr_isPex ( const tr_pex * pex )
{
return pex & & tr_isAddress ( & pex - > addr ) ;
}
2007-09-20 16:32:01 +00:00
void
2010-04-20 21:54:03 +00:00
tr_peerMgrAddPex ( tr_torrent * tor , uint8_t from ,
const tr_pex * pex , int8_t seedProbability )
2007-09-20 16:32:01 +00:00
{
2008-12-17 03:38:02 +00:00
if ( tr_isPex ( pex ) ) /* safeguard against corrupt data */
{
2009-01-13 21:00:05 +00:00
Torrent * t = tor - > torrentPeers ;
managerLock ( t - > manager ) ;
2007-09-29 06:37:03 +00:00
2008-12-17 03:38:02 +00:00
if ( ! tr_sessionIsAddressBlocked ( t - > manager - > session , & pex - > addr ) )
2009-01-17 23:14:35 +00:00
if ( tr_isValidPeerAddress ( & pex - > addr , pex - > port ) )
2010-04-20 21:54:03 +00:00
ensureAtomExists ( t , & pex - > addr , pex - > port , pex - > flags , seedProbability , from ) ;
2008-12-22 00:52:44 +00:00
2009-01-13 21:00:05 +00:00
managerUnlock ( t - > manager ) ;
2008-12-17 03:38:02 +00:00
}
2007-09-20 16:32:01 +00:00
}
2010-04-20 21:54:03 +00:00
void
tr_peerMgrMarkAllAsSeeds ( tr_torrent * tor )
{
Torrent * t = tor - > torrentPeers ;
const int n = tr_ptrArraySize ( & t - > pool ) ;
struct peer_atom * * it = ( struct peer_atom * * ) tr_ptrArrayBase ( & t - > pool ) ;
struct peer_atom * * end = it + n ;
while ( it ! = end )
atomSetSeed ( * it + + ) ;
}
2008-04-19 15:07:59 +00:00
tr_pex *
2008-09-23 19:11:04 +00:00
tr_peerMgrCompactToPex ( const void * compact ,
size_t compactLen ,
const uint8_t * added_f ,
size_t added_f_len ,
size_t * pexCount )
{
size_t i ;
size_t n = compactLen / 6 ;
2008-04-19 15:07:59 +00:00
const uint8_t * walk = compact ;
2008-09-23 19:11:04 +00:00
tr_pex * pex = tr_new0 ( tr_pex , n ) ;
2008-08-09 16:17:59 +00:00
2008-09-23 19:11:04 +00:00
for ( i = 0 ; i < n ; + + i )
{
2008-12-02 03:41:58 +00:00
pex [ i ] . addr . type = TR_AF_INET ;
memcpy ( & pex [ i ] . addr . addr , walk , 4 ) ; walk + = 4 ;
2008-04-19 15:07:59 +00:00
memcpy ( & pex [ i ] . port , walk , 2 ) ; walk + = 2 ;
2008-08-10 14:58:11 +00:00
if ( added_f & & ( n = = added_f_len ) )
2008-04-19 15:07:59 +00:00
pex [ i ] . flags = added_f [ i ] ;
2007-09-20 16:32:01 +00:00
}
2008-08-10 14:58:11 +00:00
2008-04-19 15:07:59 +00:00
* pexCount = n ;
return pex ;
2007-09-20 16:32:01 +00:00
}
2008-12-15 00:17:08 +00:00
tr_pex *
tr_peerMgrCompact6ToPex ( const void * compact ,
size_t compactLen ,
const uint8_t * added_f ,
size_t added_f_len ,
size_t * pexCount )
{
size_t i ;
size_t n = compactLen / 18 ;
const uint8_t * walk = compact ;
tr_pex * pex = tr_new0 ( tr_pex , n ) ;
2009-08-10 20:04:08 +00:00
2008-12-15 00:17:08 +00:00
for ( i = 0 ; i < n ; + + i )
{
pex [ i ] . addr . type = TR_AF_INET6 ;
memcpy ( & pex [ i ] . addr . addr . addr6 . s6_addr , walk , 16 ) ; walk + = 16 ;
memcpy ( & pex [ i ] . port , walk , 2 ) ; walk + = 2 ;
if ( added_f & & ( n = = added_f_len ) )
pex [ i ] . flags = added_f [ i ] ;
}
2009-08-10 20:04:08 +00:00
2008-12-15 00:17:08 +00:00
* pexCount = n ;
return pex ;
}
tr_pex *
tr_peerMgrArrayToPex ( const void * array ,
size_t arrayLen ,
size_t * pexCount )
{
size_t i ;
size_t n = arrayLen / ( sizeof ( tr_address ) + 2 ) ;
/*size_t n = arrayLen / sizeof( tr_peerArrayElement );*/
const uint8_t * walk = array ;
tr_pex * pex = tr_new0 ( tr_pex , n ) ;
2009-08-10 20:04:08 +00:00
2008-12-15 00:17:08 +00:00
for ( i = 0 ; i < n ; i + + ) {
memcpy ( & pex [ i ] . addr , walk , sizeof ( tr_address ) ) ;
memcpy ( & pex [ i ] . port , walk + sizeof ( tr_address ) , 2 ) ;
pex [ i ] . flags = 0x00 ;
walk + = sizeof ( tr_address ) + 2 ;
}
2009-08-10 20:04:08 +00:00
2008-12-15 00:17:08 +00:00
* pexCount = n ;
return pex ;
}
2007-09-20 16:32:01 +00:00
/**
* * *
* */
void
2009-01-13 21:00:05 +00:00
tr_peerMgrSetBlame ( tr_torrent * tor ,
2008-08-20 13:45:52 +00:00
tr_piece_index_t pieceIndex ,
2007-10-08 01:31:27 +00:00
int success )
2007-09-20 16:32:01 +00:00
{
2007-10-08 01:31:27 +00:00
if ( ! success )
{
2008-09-23 19:11:04 +00:00
int peerCount , i ;
2009-01-13 21:00:05 +00:00
Torrent * t = tor - > torrentPeers ;
2007-10-08 01:31:27 +00:00
tr_peer * * peers ;
assert ( torrentIsLocked ( t ) ) ;
2008-12-29 08:54:36 +00:00
peers = ( tr_peer * * ) tr_ptrArrayPeek ( & t - > peers , & peerCount ) ;
2008-09-23 19:11:04 +00:00
for ( i = 0 ; i < peerCount ; + + i )
2007-10-08 01:31:27 +00:00
{
2007-11-13 05:36:43 +00:00
tr_peer * peer = peers [ i ] ;
if ( tr_bitfieldHas ( peer - > blame , pieceIndex ) )
{
2008-12-05 22:56:19 +00:00
tordbg ( t , " peer %s contributed to corrupt piece (%d); now has %d strikes " ,
2009-10-29 16:10:03 +00:00
tr_atomAddrStr ( peer - > atom ) ,
2008-12-05 22:56:19 +00:00
pieceIndex , ( int ) peer - > strikes + 1 ) ;
2007-11-13 05:36:43 +00:00
addStrike ( t , peer ) ;
}
2007-10-08 01:31:27 +00:00
}
}
2007-09-20 16:32:01 +00:00
}
int
2008-12-05 22:56:19 +00:00
tr_pexCompare ( const void * va , const void * vb )
2007-09-20 16:32:01 +00:00
{
2008-08-01 16:43:22 +00:00
const tr_pex * a = va ;
const tr_pex * b = vb ;
2008-12-16 23:31:05 +00:00
int i ;
assert ( tr_isPex ( a ) ) ;
assert ( tr_isPex ( b ) ) ;
if ( ( i = tr_compareAddresses ( & a - > addr , & b - > addr ) ) )
return i ;
if ( a - > port ! = b - > port )
return a - > port < b - > port ? - 1 : 1 ;
2008-09-23 19:11:04 +00:00
2007-10-02 02:01:57 +00:00
return 0 ;
2007-09-20 16:32:01 +00:00
}
2009-08-07 05:41:33 +00:00
#if 0
2007-09-29 00:46:41 +00:00
static int
peerPrefersCrypto ( const tr_peer * peer )
{
if ( peer - > encryption_preference = = ENCRYPTION_PREFERENCE_YES )
return TRUE ;
if ( peer - > encryption_preference = = ENCRYPTION_PREFERENCE_NO )
return FALSE ;
return tr_peerIoIsEncrypted ( peer - > io ) ;
2008-09-23 19:11:04 +00:00
}
2009-08-07 05:41:33 +00:00
# endif
2007-09-20 16:32:01 +00:00
2009-09-10 02:20:35 +00:00
/* better goes first */
static int
compareAtomsByUsefulness ( const void * va , const void * vb )
{
const struct peer_atom * a = * ( const struct peer_atom * * ) va ;
const struct peer_atom * b = * ( const struct peer_atom * * ) vb ;
2009-09-10 03:28:55 +00:00
assert ( tr_isAtom ( a ) ) ;
assert ( tr_isAtom ( b ) ) ;
2009-09-10 02:20:35 +00:00
if ( a - > piece_data_time ! = b - > piece_data_time )
return a - > piece_data_time > b - > piece_data_time ? - 1 : 1 ;
if ( a - > from ! = b - > from )
return a - > from < b - > from ? - 1 : 1 ;
if ( a - > numFails ! = b - > numFails )
return a - > numFails < b - > numFails ? - 1 : 1 ;
return 0 ;
}
2007-09-20 16:32:01 +00:00
int
2009-11-20 07:47:31 +00:00
tr_peerMgrGetPeers ( tr_torrent * tor ,
tr_pex * * setme_pex ,
uint8_t af ,
uint8_t list_mode ,
int maxCount )
2007-09-20 16:32:01 +00:00
{
2009-11-20 07:47:31 +00:00
int i ;
int n ;
2009-08-07 05:41:33 +00:00
int count = 0 ;
2009-11-20 07:47:31 +00:00
int atomCount = 0 ;
2009-01-13 21:00:05 +00:00
const Torrent * t = tor - > torrentPeers ;
2009-11-20 07:47:31 +00:00
struct peer_atom * * atoms = NULL ;
tr_pex * pex ;
tr_pex * walk ;
assert ( tr_isTorrent ( tor ) ) ;
assert ( setme_pex ! = NULL ) ;
assert ( af = = TR_AF_INET | | af = = TR_AF_INET6 ) ;
assert ( list_mode = = TR_PEERS_CONNECTED | | list_mode = = TR_PEERS_ALL ) ;
2007-09-20 16:32:01 +00:00
2009-01-13 21:00:05 +00:00
managerLock ( t - > manager ) ;
2007-09-29 06:37:03 +00:00
2009-11-20 07:47:31 +00:00
/**
* * * build a list of atoms
* */
if ( list_mode = = TR_PEERS_CONNECTED ) /* connected peers only */
2008-10-05 22:51:18 +00:00
{
int i ;
2009-11-20 07:47:31 +00:00
const tr_peer * * peers = ( const tr_peer * * ) tr_ptrArrayBase ( & t - > peers ) ;
atomCount = tr_ptrArraySize ( & t - > peers ) ;
atoms = tr_new ( struct peer_atom * , atomCount ) ;
for ( i = 0 ; i < atomCount ; + + i )
atoms [ i ] = peers [ i ] - > atom ;
}
else /* TR_PEERS_ALL */
{
2009-09-10 02:20:35 +00:00
const struct peer_atom * * atomsBase = ( const struct peer_atom * * ) tr_ptrArrayBase ( & t - > pool ) ;
2009-11-20 07:47:31 +00:00
atomCount = tr_ptrArraySize ( & t - > pool ) ;
atoms = tr_memdup ( atomsBase , atomCount * sizeof ( struct peer_atom * ) ) ;
}
2007-09-20 16:32:01 +00:00
2009-11-20 07:47:31 +00:00
qsort ( atoms , atomCount , sizeof ( struct peer_atom * ) , compareAtomsByUsefulness ) ;
2009-09-10 02:20:35 +00:00
2009-11-20 07:47:31 +00:00
/**
* * * add the first N of them into our return list
* */
n = MIN ( atomCount , maxCount ) ;
pex = walk = tr_new0 ( tr_pex , n ) ;
for ( i = 0 ; i < atomCount & & count < n ; + + i )
{
const struct peer_atom * atom = atoms [ i ] ;
if ( atom - > addr . type = = af )
2008-10-05 22:51:18 +00:00
{
2009-11-20 07:47:31 +00:00
assert ( tr_isAddress ( & atom - > addr ) ) ;
walk - > addr = atom - > addr ;
walk - > port = atom - > port ;
walk - > flags = atom - > flags ;
+ + count ;
+ + walk ;
2008-10-05 22:51:18 +00:00
}
2009-11-20 07:47:31 +00:00
}
2007-09-20 16:32:01 +00:00
2009-11-20 07:47:31 +00:00
qsort ( pex , count , sizeof ( tr_pex ) , tr_pexCompare ) ;
2009-09-10 02:20:35 +00:00
2009-11-20 07:47:31 +00:00
assert ( ( walk - pex ) = = count ) ;
* setme_pex = pex ;
2007-09-20 16:32:01 +00:00
2009-11-20 07:47:31 +00:00
/* cleanup */
tr_free ( atoms ) ;
2009-01-13 21:00:05 +00:00
managerUnlock ( t - > manager ) ;
2009-08-07 05:41:33 +00:00
return count ;
2007-09-20 16:32:01 +00:00
}
2009-12-10 05:52:46 +00:00
static void atomPulse ( int , short , void * ) ;
static void bandwidthPulse ( int , short , void * ) ;
static void rechokePulse ( int , short , void * ) ;
static void reconnectPulse ( int , short , void * ) ;
static struct event *
createTimer ( int msec , void ( * callback ) ( int , short , void * ) , void * cbdata )
{
struct event * timer = tr_new0 ( struct event , 1 ) ;
evtimer_set ( timer , callback , cbdata ) ;
2009-12-10 19:04:10 +00:00
tr_timerAddMsec ( timer , msec ) ;
2009-12-10 05:52:46 +00:00
return timer ;
}
2009-11-26 05:13:58 +00:00
2009-05-13 20:54:35 +00:00
static void
ensureMgrTimersExist ( struct tr_peerMgr * m )
{
2009-11-26 05:13:58 +00:00
if ( m - > atomTimer = = NULL )
2009-12-10 05:52:46 +00:00
m - > atomTimer = createTimer ( ATOM_PERIOD_MSEC , atomPulse , m ) ;
2009-11-26 05:13:58 +00:00
2009-05-13 20:54:35 +00:00
if ( m - > bandwidthTimer = = NULL )
2009-12-10 05:52:46 +00:00
m - > bandwidthTimer = createTimer ( BANDWIDTH_PERIOD_MSEC , bandwidthPulse , m ) ;
2009-05-13 20:54:35 +00:00
if ( m - > rechokeTimer = = NULL )
2009-12-10 05:52:46 +00:00
m - > rechokeTimer = createTimer ( RECHOKE_PERIOD_MSEC , rechokePulse , m ) ;
2009-05-13 20:54:35 +00:00
2010-04-20 21:54:03 +00:00
if ( m - > refillUpkeepTimer = = NULL )
2009-12-10 05:52:46 +00:00
m - > refillUpkeepTimer = createTimer ( REFILL_UPKEEP_PERIOD_MSEC , refillUpkeep , m ) ;
2009-05-13 20:54:35 +00:00
}
2007-09-20 16:32:01 +00:00
void
2009-01-13 21:00:05 +00:00
tr_peerMgrStartTorrent ( tr_torrent * tor )
2007-09-20 16:32:01 +00:00
{
2009-02-11 16:34:35 +00:00
Torrent * t = tor - > torrentPeers ;
2009-05-13 20:54:35 +00:00
assert ( t ! = NULL ) ;
2009-02-11 16:34:35 +00:00
managerLock ( t - > manager ) ;
2009-05-13 20:54:35 +00:00
ensureMgrTimersExist ( t - > manager ) ;
2009-02-11 16:34:35 +00:00
2009-11-08 23:20:00 +00:00
t - > isRunning = TRUE ;
2009-02-11 16:34:35 +00:00
2009-12-10 05:52:46 +00:00
rechokePulse ( 0 , 0 , t - > manager ) ;
2009-02-11 16:34:35 +00:00
managerUnlock ( t - > manager ) ;
2007-09-20 16:32:01 +00:00
}
2007-09-29 06:37:03 +00:00
static void
stopTorrent ( Torrent * t )
2007-09-20 16:32:01 +00:00
{
2009-11-08 23:20:00 +00:00
int i , n ;
2007-09-29 06:37:03 +00:00
assert ( torrentIsLocked ( t ) ) ;
2009-02-04 16:58:52 +00:00
t - > isRunning = FALSE ;
2007-10-02 02:01:57 +00:00
2007-10-02 16:12:44 +00:00
/* disconnect the peers. */
2009-11-08 23:20:00 +00:00
for ( i = 0 , n = tr_ptrArraySize ( & t - > peers ) ; i < n ; + + i )
peerDestructor ( t , tr_ptrArrayNth ( & t - > peers , i ) ) ;
2008-12-29 08:54:36 +00:00
tr_ptrArrayClear ( & t - > peers ) ;
2007-10-02 02:01:57 +00:00
2007-10-02 16:12:44 +00:00
/* disconnect the handshakes. handshakeAbort calls handshakeDoneCB(),
* which removes the handshake from t - > outgoingHandshakes . . . */
2008-12-29 08:54:36 +00:00
while ( ! tr_ptrArrayEmpty ( & t - > outgoingHandshakes ) )
tr_handshakeAbort ( tr_ptrArrayNth ( & t - > outgoingHandshakes , 0 ) ) ;
2007-09-29 06:37:03 +00:00
}
2008-09-23 19:11:04 +00:00
2007-09-29 06:37:03 +00:00
void
2009-01-13 21:00:05 +00:00
tr_peerMgrStopTorrent ( tr_torrent * tor )
2007-09-29 06:37:03 +00:00
{
2009-01-13 21:00:05 +00:00
Torrent * t = tor - > torrentPeers ;
2007-09-29 06:37:03 +00:00
2009-01-13 21:00:05 +00:00
managerLock ( t - > manager ) ;
2007-09-29 06:37:03 +00:00
2009-01-13 21:00:05 +00:00
stopTorrent ( t ) ;
managerUnlock ( t - > manager ) ;
2007-09-20 16:32:01 +00:00
}
void
tr_peerMgrAddTorrent ( tr_peerMgr * manager ,
tr_torrent * tor )
{
2007-09-29 06:37:03 +00:00
managerLock ( manager ) ;
2008-08-01 16:43:22 +00:00
assert ( tor ) ;
2009-01-13 21:00:05 +00:00
assert ( tor - > torrentPeers = = NULL ) ;
2007-09-20 16:32:01 +00:00
2009-01-13 21:00:05 +00:00
tor - > torrentPeers = torrentConstructor ( manager , tor ) ;
2007-09-29 06:37:03 +00:00
managerUnlock ( manager ) ;
2007-09-20 16:32:01 +00:00
}
void
2009-01-13 21:00:05 +00:00
tr_peerMgrRemoveTorrent ( tr_torrent * tor )
2007-09-20 16:32:01 +00:00
{
2009-01-13 21:00:05 +00:00
tr_torrentLock ( tor ) ;
2007-09-29 06:37:03 +00:00
2009-01-13 21:00:05 +00:00
stopTorrent ( tor - > torrentPeers ) ;
torrentDestructor ( tor - > torrentPeers ) ;
2007-09-29 06:37:03 +00:00
2009-01-13 21:00:05 +00:00
tr_torrentUnlock ( tor ) ;
2007-09-20 16:32:01 +00:00
}
void
2009-01-13 21:00:05 +00:00
tr_peerMgrTorrentAvailability ( const tr_torrent * tor ,
int8_t * tab ,
2008-08-20 13:45:52 +00:00
unsigned int tabCount )
2007-09-20 16:32:01 +00:00
{
2008-09-23 19:11:04 +00:00
tr_piece_index_t i ;
const Torrent * t ;
float interval ;
2008-12-22 00:52:44 +00:00
tr_bool isSeed ;
2008-09-23 19:11:04 +00:00
int peerCount ;
const tr_peer * * peers ;
2009-01-13 21:00:05 +00:00
tr_torrentLock ( tor ) ;
2007-09-29 06:37:03 +00:00
2009-01-13 21:00:05 +00:00
t = tor - > torrentPeers ;
2007-09-29 06:37:03 +00:00
tor = t - > tor ;
interval = tor - > info . pieceCount / ( float ) tabCount ;
2009-01-02 17:01:55 +00:00
isSeed = tor & & ( tr_cpGetStatus ( & tor - > completion ) = = TR_SEED ) ;
2009-01-04 16:59:15 +00:00
peers = ( const tr_peer * * ) tr_ptrArrayBase ( & t - > peers ) ;
peerCount = tr_ptrArraySize ( & t - > peers ) ;
2007-09-20 16:32:01 +00:00
memset ( tab , 0 , tabCount ) ;
2008-09-23 19:11:04 +00:00
for ( i = 0 ; tor & & i < tabCount ; + + i )
2007-09-20 16:32:01 +00:00
{
const int piece = i * interval ;
2009-01-02 17:01:55 +00:00
if ( isSeed | | tr_cpPieceIsComplete ( & tor - > completion , piece ) )
2007-09-20 16:32:01 +00:00
tab [ i ] = - 1 ;
2008-12-22 00:52:44 +00:00
else if ( peerCount ) {
2008-06-09 23:11:15 +00:00
int j ;
2008-09-23 19:11:04 +00:00
for ( j = 0 ; j < peerCount ; + + j )
2009-11-24 02:16:31 +00:00
if ( tr_bitsetHas ( & peers [ j ] - > have , i ) )
2007-09-20 16:32:01 +00:00
+ + tab [ i ] ;
}
}
2007-09-29 06:37:03 +00:00
2009-01-13 21:00:05 +00:00
tr_torrentUnlock ( tor ) ;
2007-09-20 16:32:01 +00:00
}
2008-04-11 23:05:14 +00:00
/* Returns the pieces that are available from peers */
2007-09-27 03:03:38 +00:00
tr_bitfield *
2009-01-13 21:00:05 +00:00
tr_peerMgrGetAvailable ( const tr_torrent * tor )
2007-09-27 03:03:38 +00:00
{
2008-12-30 08:20:16 +00:00
int i ;
int peerCount ;
2009-01-13 21:00:05 +00:00
Torrent * t = tor - > torrentPeers ;
2008-12-30 08:20:16 +00:00
const tr_peer * * peers ;
2007-09-27 03:03:38 +00:00
tr_bitfield * pieces ;
2009-01-13 21:00:05 +00:00
managerLock ( t - > manager ) ;
2007-09-29 06:37:03 +00:00
2008-04-11 23:05:14 +00:00
pieces = tr_bitfieldNew ( t - > tor - > info . pieceCount ) ;
2009-01-04 16:59:15 +00:00
peerCount = tr_ptrArraySize ( & t - > peers ) ;
peers = ( const tr_peer * * ) tr_ptrArrayBase ( & t - > peers ) ;
2008-12-30 08:20:16 +00:00
for ( i = 0 ; i < peerCount ; + + i )
2009-11-24 02:16:31 +00:00
tr_bitsetOr ( pieces , & peers [ i ] - > have ) ;
2007-09-27 03:03:38 +00:00
2009-01-13 21:00:05 +00:00
managerUnlock ( t - > manager ) ;
2007-09-27 03:03:38 +00:00
return pieces ;
}
2007-09-20 16:32:01 +00:00
void
2009-01-13 21:00:05 +00:00
tr_peerMgrTorrentStats ( tr_torrent * tor ,
2008-12-05 22:56:19 +00:00
int * setmePeersKnown ,
int * setmePeersConnected ,
int * setmeSeedsConnected ,
int * setmeWebseedsSendingToUs ,
int * setmePeersSendingToUs ,
int * setmePeersGettingFromUs ,
int * setmePeersFrom )
{
int i , size ;
2009-01-13 21:00:05 +00:00
const Torrent * t = tor - > torrentPeers ;
2008-12-05 22:56:19 +00:00
const tr_peer * * peers ;
2008-06-10 02:36:52 +00:00
const tr_webseed * * webseeds ;
2007-09-29 06:37:03 +00:00
2009-01-13 21:00:05 +00:00
managerLock ( t - > manager ) ;
2007-09-29 06:37:03 +00:00
2009-01-04 16:59:15 +00:00
peers = ( const tr_peer * * ) tr_ptrArrayBase ( & t - > peers ) ;
size = tr_ptrArraySize ( & t - > peers ) ;
2007-09-20 16:32:01 +00:00
2008-12-29 08:54:36 +00:00
* setmePeersKnown = tr_ptrArraySize ( & t - > pool ) ;
2008-06-10 02:36:52 +00:00
* setmePeersConnected = 0 ;
* setmeSeedsConnected = 0 ;
* setmePeersGettingFromUs = 0 ;
* setmePeersSendingToUs = 0 ;
* setmeWebseedsSendingToUs = 0 ;
2007-09-20 16:32:01 +00:00
2008-12-05 22:56:19 +00:00
for ( i = 0 ; i < TR_PEER_FROM__MAX ; + + i )
2007-09-21 15:13:23 +00:00
setmePeersFrom [ i ] = 0 ;
2008-12-05 22:56:19 +00:00
for ( i = 0 ; i < size ; + + i )
2007-09-20 16:32:01 +00:00
{
2008-12-05 22:56:19 +00:00
const tr_peer * peer = peers [ i ] ;
2009-05-09 06:08:21 +00:00
const struct peer_atom * atom = peer - > atom ;
2007-09-20 16:32:01 +00:00
if ( peer - > io = = NULL ) /* not connected */
continue ;
2008-12-05 22:56:19 +00:00
+ + * setmePeersConnected ;
2007-09-20 16:32:01 +00:00
2007-10-01 15:36:31 +00:00
+ + setmePeersFrom [ atom - > from ] ;
2007-09-20 16:32:01 +00:00
2009-11-24 15:51:16 +00:00
if ( clientIsDownloadingFrom ( tor , peer ) )
2008-12-05 22:56:19 +00:00
+ + * setmePeersSendingToUs ;
2008-01-16 17:52:47 +00:00
if ( clientIsUploadingTo ( peer ) )
2008-12-05 22:56:19 +00:00
+ + * setmePeersGettingFromUs ;
2008-05-31 05:22:10 +00:00
2010-04-20 21:54:03 +00:00
if ( atomIsSeed ( atom ) )
2008-12-05 22:56:19 +00:00
+ + * setmeSeedsConnected ;
2007-09-20 16:32:01 +00:00
}
2007-09-29 06:37:03 +00:00
2009-01-04 16:59:15 +00:00
webseeds = ( const tr_webseed * * ) tr_ptrArrayBase ( & t - > webseeds ) ;
size = tr_ptrArraySize ( & t - > webseeds ) ;
2008-12-05 22:56:19 +00:00
for ( i = 0 ; i < size ; + + i )
2008-06-10 02:36:52 +00:00
if ( tr_webseedIsActive ( webseeds [ i ] ) )
2008-12-05 22:56:19 +00:00
+ + * setmeWebseedsSendingToUs ;
2008-06-10 02:36:52 +00:00
2009-01-13 21:00:05 +00:00
managerUnlock ( t - > manager ) ;
2008-06-10 01:38:12 +00:00
}
2009-05-28 02:28:14 +00:00
float
tr_peerMgrGetWebseedSpeed ( const tr_torrent * tor , uint64_t now )
{
int i ;
float tmp ;
float ret = 0 ;
const Torrent * t = tor - > torrentPeers ;
const int n = tr_ptrArraySize ( & t - > webseeds ) ;
const tr_webseed * * webseeds = ( const tr_webseed * * ) tr_ptrArrayBase ( & t - > webseeds ) ;
for ( i = 0 ; i < n ; + + i )
if ( tr_webseedGetSpeed ( webseeds [ i ] , now , & tmp ) )
ret + = tmp ;
return ret ;
}
2008-06-10 01:38:12 +00:00
float *
2009-01-13 21:00:05 +00:00
tr_peerMgrWebSpeeds ( const tr_torrent * tor )
2008-06-10 01:38:12 +00:00
{
2009-01-13 21:00:05 +00:00
const Torrent * t = tor - > torrentPeers ;
2008-06-10 01:38:12 +00:00
const tr_webseed * * webseeds ;
2008-12-05 22:56:19 +00:00
int i ;
int webseedCount ;
float * ret ;
2009-01-05 04:27:54 +00:00
uint64_t now ;
2008-06-10 01:38:12 +00:00
2009-01-13 21:00:05 +00:00
assert ( t - > manager ) ;
managerLock ( t - > manager ) ;
2008-06-10 01:38:12 +00:00
2009-01-04 16:59:15 +00:00
webseeds = ( const tr_webseed * * ) tr_ptrArrayBase ( & t - > webseeds ) ;
webseedCount = tr_ptrArraySize ( & t - > webseeds ) ;
2009-01-13 21:00:05 +00:00
assert ( webseedCount = = tor - > info . webseedCount ) ;
2008-06-10 01:38:12 +00:00
ret = tr_new0 ( float , webseedCount ) ;
2009-01-05 04:27:54 +00:00
now = tr_date ( ) ;
2008-06-10 01:38:12 +00:00
2008-12-05 22:56:19 +00:00
for ( i = 0 ; i < webseedCount ; + + i )
2009-01-05 04:27:54 +00:00
if ( ! tr_webseedGetSpeed ( webseeds [ i ] , now , & ret [ i ] ) )
2008-06-10 01:46:36 +00:00
ret [ i ] = - 1.0 ;
2008-06-10 01:38:12 +00:00
2009-01-13 21:00:05 +00:00
managerUnlock ( t - > manager ) ;
2008-06-10 01:38:12 +00:00
return ret ;
2007-09-20 16:32:01 +00:00
}
2008-11-06 02:56:51 +00:00
double
2009-01-05 04:27:54 +00:00
tr_peerGetPieceSpeed ( const tr_peer * peer , uint64_t now , tr_direction direction )
2008-11-06 02:56:51 +00:00
{
2009-01-05 04:27:54 +00:00
return peer - > io ? tr_peerIoGetPieceSpeed ( peer - > io , now , direction ) : 0.0 ;
2008-11-06 02:56:51 +00:00
}
2007-09-20 16:32:01 +00:00
struct tr_peer_stat *
2009-01-13 21:00:05 +00:00
tr_peerMgrPeerStats ( const tr_torrent * tor ,
2009-01-01 18:36:58 +00:00
int * setmeCount )
2007-09-20 16:32:01 +00:00
{
2008-12-30 08:20:16 +00:00
int i , size ;
2009-01-13 21:00:05 +00:00
const Torrent * t = tor - > torrentPeers ;
2008-12-30 08:20:16 +00:00
const tr_peer * * peers ;
tr_peer_stat * ret ;
2009-01-05 04:27:54 +00:00
uint64_t now ;
2007-09-20 16:32:01 +00:00
2009-01-13 21:00:05 +00:00
assert ( t - > manager ) ;
managerLock ( t - > manager ) ;
2007-09-29 06:37:03 +00:00
2009-01-04 16:59:15 +00:00
size = tr_ptrArraySize ( & t - > peers ) ;
peers = ( const tr_peer * * ) tr_ptrArrayBase ( & t - > peers ) ;
2007-09-20 16:32:01 +00:00
ret = tr_new0 ( tr_peer_stat , size ) ;
2009-01-05 04:27:54 +00:00
now = tr_date ( ) ;
2007-09-20 16:32:01 +00:00
2009-05-09 06:08:21 +00:00
for ( i = 0 ; i < size ; + + i )
2007-09-20 16:32:01 +00:00
{
2008-09-23 19:11:04 +00:00
char * pch ;
const tr_peer * peer = peers [ i ] ;
2009-05-09 06:08:21 +00:00
const struct peer_atom * atom = peer - > atom ;
2008-09-23 19:11:04 +00:00
tr_peer_stat * stat = ret + i ;
2007-09-20 16:32:01 +00:00
2009-10-29 16:10:03 +00:00
tr_ntop ( & atom - > addr , stat - > addr , sizeof ( stat - > addr ) ) ;
2008-12-15 00:17:08 +00:00
tr_strlcpy ( stat - > client , ( peer - > client ? peer - > client : " " ) ,
sizeof ( stat - > client ) ) ;
2009-12-16 18:20:01 +00:00
stat - > port = ntohs ( peer - > atom - > port ) ;
stat - > from = atom - > from ;
stat - > progress = peer - > progress ;
stat - > isEncrypted = tr_peerIoIsEncrypted ( peer - > io ) ? 1 : 0 ;
stat - > rateToPeer = tr_peerGetPieceSpeed ( peer , now , TR_CLIENT_TO_PEER ) ;
stat - > rateToClient = tr_peerGetPieceSpeed ( peer , now , TR_PEER_TO_CLIENT ) ;
stat - > peerIsChoked = peer - > peerIsChoked ;
stat - > peerIsInterested = peer - > peerIsInterested ;
stat - > clientIsChoked = peer - > clientIsChoked ;
stat - > clientIsInterested = peer - > clientIsInterested ;
stat - > isIncoming = tr_peerIoIsIncoming ( peer - > io ) ;
stat - > isDownloadingFrom = clientIsDownloadingFrom ( tor , peer ) ;
stat - > isUploadingTo = clientIsUploadingTo ( peer ) ;
stat - > isSeed = ( atom - > uploadOnly = = UPLOAD_ONLY_YES ) | | ( peer - > progress > = 1.0 ) ;
2010-03-08 04:29:58 +00:00
2010-03-10 15:55:00 +00:00
stat - > blocksToPeer = tr_historyGet ( peer - > blocksSentToPeer , now , CANCEL_HISTORY_SEC * 1000 ) ;
stat - > blocksToClient = tr_historyGet ( peer - > blocksSentToClient , now , CANCEL_HISTORY_SEC * 1000 ) ;
stat - > cancelsToPeer = tr_historyGet ( peer - > cancelsSentToPeer , now , CANCEL_HISTORY_SEC * 1000 ) ;
stat - > cancelsToClient = tr_historyGet ( peer - > cancelsSentToClient , now , CANCEL_HISTORY_SEC * 1000 ) ;
2010-03-08 04:29:58 +00:00
2009-12-16 18:20:01 +00:00
stat - > pendingReqsToPeer = peer - > pendingReqsToPeer ;
stat - > pendingReqsToClient = peer - > pendingReqsToClient ;
2008-01-09 17:33:43 +00:00
pch = stat - > flagStr ;
2008-01-11 02:09:20 +00:00
if ( t - > optimistic = = peer ) * pch + + = ' O ' ;
2008-01-10 00:52:02 +00:00
if ( stat - > isDownloadingFrom ) * pch + + = ' D ' ;
2008-01-09 17:33:43 +00:00
else if ( stat - > clientIsInterested ) * pch + + = ' d ' ;
2008-01-10 00:52:02 +00:00
if ( stat - > isUploadingTo ) * pch + + = ' U ' ;
2008-01-09 17:33:43 +00:00
else if ( stat - > peerIsInterested ) * pch + + = ' u ' ;
2008-12-05 22:56:19 +00:00
if ( ! stat - > clientIsChoked & & ! stat - > clientIsInterested ) * pch + + = ' K ' ;
2008-01-09 17:33:43 +00:00
if ( ! stat - > peerIsChoked & & ! stat - > peerIsInterested ) * pch + + = ' ? ' ;
if ( stat - > isEncrypted ) * pch + + = ' E ' ;
2009-05-19 18:38:26 +00:00
if ( stat - > from = = TR_PEER_FROM_DHT ) * pch + + = ' H ' ;
2008-01-09 17:33:43 +00:00
if ( stat - > from = = TR_PEER_FROM_PEX ) * pch + + = ' X ' ;
if ( stat - > isIncoming ) * pch + + = ' I ' ;
* pch = ' \0 ' ;
2007-09-20 16:32:01 +00:00
}
* setmeCount = size ;
2007-09-29 06:37:03 +00:00
2009-01-13 21:00:05 +00:00
managerUnlock ( t - > manager ) ;
2007-09-20 16:32:01 +00:00
return ret ;
}
/**
* * *
* */
2010-03-08 04:29:58 +00:00
/* do we still want this piece and does the peer have it? */
static tr_bool
isPieceInteresting ( const tr_torrent * tor , const tr_peer * peer , tr_piece_index_t index )
{
return ( ! tor - > info . pieces [ index ] . dnd ) /* we want it */
& & ( ! tr_cpPieceIsComplete ( & tor - > completion , index ) ) /* we don't have it */
& & ( tr_bitsetHas ( & peer - > have , index ) ) ; /* peer has it */
}
/* does this peer have any pieces that we want? */
static tr_bool
isPeerInteresting ( const tr_torrent * tor , const tr_peer * peer )
{
tr_piece_index_t i , n ;
if ( tr_torrentIsSeed ( tor ) )
return FALSE ;
if ( ! tr_torrentIsPieceTransferAllowed ( tor , TR_PEER_TO_CLIENT ) )
return FALSE ;
for ( i = 0 , n = tor - > info . pieceCount ; i < n ; + + i )
if ( isPieceInteresting ( tor , peer , i ) )
return TRUE ;
return FALSE ;
}
/* determines who we send "interested" messages to */
static void
rechokeDownloads ( Torrent * t )
{
int i ;
const uint64_t now = tr_date ( ) ;
const int msec = 60 * 1000 ;
const int MIN_INTERESTING_PEERS = 5 ;
const int peerCount = tr_ptrArraySize ( & t - > peers ) ;
int maxPeers ;
int badCount = 0 ;
int goodCount = 0 ;
int untestedCount = 0 ;
tr_peer * * bad = tr_new ( tr_peer * , peerCount ) ;
tr_peer * * good = tr_new ( tr_peer * , peerCount ) ;
tr_peer * * untested = tr_new ( tr_peer * , peerCount ) ;
/* decide how many peers to be interested in */
{
2010-03-10 15:34:27 +00:00
int blocks = 0 ;
int cancels = 0 ;
/* Count up how many blocks & cancels each peer has.
*
* There are two situations where we send out cancels - -
*
* 1. We ' ve got unresponsive peers , which is handled by deciding
* - which - peers to be interested in .
*
* 2. We ' ve hit our bandwidth cap , which is handled by deciding
* - how many - peers to be interested in .
*
* We ' re working on 2. here , so we need to ignore unresponsive
* peers in our calculations lest they confuse Transmission into
2010-04-22 03:12:31 +00:00
* thinking it ' s hit its bandwidth cap .
2010-03-10 15:34:27 +00:00
*/
for ( i = 0 ; i < peerCount ; + + i )
{
const tr_peer * peer = tr_ptrArrayNth ( & t - > peers , i ) ;
const int b = tr_historyGet ( peer - > blocksSentToClient , now , msec ) ;
const int c = tr_historyGet ( peer - > cancelsSentToPeer , now , msec ) ;
if ( b = = 0 ) /* ignore unresponsive peers, as described above */
continue ;
blocks + = b ;
cancels + = c ;
}
2010-03-08 04:29:58 +00:00
2010-03-10 15:34:27 +00:00
if ( ! t - > interestedCount )
2010-03-08 04:29:58 +00:00
{
/* this is the torrent's first time to call this function...
* start off optimistically by allowing interest in many peers */
maxPeers = t - > tor - > maxConnectedPeers ;
}
else if ( ! blocks )
{
/* we've gotten cancels but zero blocks...
* something is seriously wrong . throttle back sharply */
maxPeers = t - > interestedCount * 0.5 ;
}
else
{
2010-04-22 03:12:31 +00:00
const double cancelRate = cancels / ( double ) ( cancels + blocks ) ;
if ( cancelRate > = 0.20 ) maxPeers = t - > interestedCount * 0.7 ;
else if ( cancelRate > = 0.10 ) maxPeers = t - > interestedCount * 0.8 ;
else if ( cancelRate > = 0.05 ) maxPeers = t - > interestedCount * 0.9 ;
else if ( cancelRate > = 0.01 ) maxPeers = t - > interestedCount ;
else maxPeers = t - > interestedCount + 1 ;
2010-03-08 04:29:58 +00:00
/* if things are getting worse, don't add more peers */
2010-04-22 03:12:31 +00:00
if ( ( t - > cancelRate > 0.01 ) & & ( cancelRate > t - > cancelRate ) )
2010-03-08 04:29:58 +00:00
maxPeers = MIN ( maxPeers , t - > interestedCount ) ;
2010-04-22 03:12:31 +00:00
t - > cancelRate = cancelRate ;
tordbg ( t , " cancel rate is %.3f -- changing the "
" number of peers we're interested in from %d to %d " ,
cancelRate , t - > interestedCount , maxPeers ) ;
2010-03-08 04:29:58 +00:00
}
}
/* don't let the previous paragraph's number tweaking go too far... */
2010-04-22 03:12:31 +00:00
if ( maxPeers < MIN_INTERESTING_PEERS )
maxPeers = MIN_INTERESTING_PEERS ;
if ( maxPeers > t - > tor - > maxConnectedPeers )
maxPeers = t - > tor - > maxConnectedPeers ;
2010-03-08 04:29:58 +00:00
/* separate the peers into "good" (ones with a low cancel-to-block ratio),
* untested peers , and " bad " ( ones with a high cancel - to - block ratio ) .
* That ' s the order in which we ' ll choose who to show interest in */
for ( i = 0 ; i < peerCount ; + + i )
{
tr_peer * peer = tr_ptrArrayNth ( & t - > peers , i ) ;
if ( ! isPeerInteresting ( t - > tor , peer ) )
{
tr_peerMsgsSetInterested ( peer - > msgs , FALSE ) ;
}
else
{
const int blocks = tr_historyGet ( peer - > blocksSentToClient , now , msec ) ;
const int cancels = tr_historyGet ( peer - > cancelsSentToPeer , now , msec ) ;
if ( ! blocks & & ! cancels )
untested [ untestedCount + + ] = peer ;
else if ( ! cancels )
good [ goodCount + + ] = peer ;
else if ( ! blocks )
bad [ badCount + + ] = peer ;
2010-04-22 03:12:31 +00:00
else if ( ( cancels * 10 ) < blocks )
2010-03-08 04:29:58 +00:00
good [ goodCount + + ] = peer ;
else
bad [ badCount + + ] = peer ;
}
}
2010-03-17 17:07:40 +00:00
2010-03-08 04:29:58 +00:00
t - > interestedCount = 0 ;
/* We've decided (1) how many peers to be interested in,
* and ( 2 ) which peers are the best candidates ,
* Now it ' s time to update our ` interest ' flags . */
for ( i = 0 ; i < goodCount ; + + i ) {
const tr_bool b = t - > interestedCount < maxPeers ;
tr_peerMsgsSetInterested ( good [ i ] - > msgs , b ) ;
if ( b )
+ + t - > interestedCount ;
}
for ( i = 0 ; i < untestedCount ; + + i ) {
const tr_bool b = t - > interestedCount < maxPeers ;
tr_peerMsgsSetInterested ( untested [ i ] - > msgs , b ) ;
if ( b )
+ + t - > interestedCount ;
}
for ( i = 0 ; i < badCount ; + + i ) {
const tr_bool b = t - > interestedCount < maxPeers ;
tr_peerMsgsSetInterested ( bad [ i ] - > msgs , b ) ;
if ( b )
+ + t - > interestedCount ;
}
/*fprintf( stderr, "num interested: %d\n", t->interestedCount );*/
2010-03-17 17:07:40 +00:00
2010-03-08 04:29:58 +00:00
/* cleanup */
tr_free ( untested ) ;
tr_free ( good ) ;
tr_free ( bad ) ;
}
/**
* * *
* */
2007-10-03 16:42:43 +00:00
struct ChokeData
2007-09-20 16:32:01 +00:00
{
2008-11-28 22:11:41 +00:00
tr_bool doUnchoke ;
tr_bool isInterested ;
tr_bool isChoked ;
2008-11-15 01:07:08 +00:00
int rate ;
2008-09-23 19:11:04 +00:00
tr_peer * peer ;
2007-10-03 16:42:43 +00:00
} ;
2007-09-20 16:32:01 +00:00
static int
2008-09-23 19:11:04 +00:00
compareChoke ( const void * va ,
const void * vb )
2007-09-20 16:32:01 +00:00
{
2007-10-03 16:42:43 +00:00
const struct ChokeData * a = va ;
const struct ChokeData * b = vb ;
2008-06-27 02:42:44 +00:00
2008-11-15 01:07:08 +00:00
if ( a - > rate ! = b - > rate ) /* prefer higher overall speeds */
return a - > rate > b - > rate ? - 1 : 1 ;
2008-11-15 00:46:51 +00:00
2008-11-15 01:07:08 +00:00
if ( a - > isChoked ! = b - > isChoked ) /* prefer unchoked */
return a - > isChoked ? 1 : - 1 ;
2008-06-27 02:42:44 +00:00
2008-11-15 01:07:08 +00:00
return 0 ;
2007-09-20 16:32:01 +00:00
}
2010-03-06 14:56:15 +00:00
/* is this a new connection? */
2008-01-11 02:09:20 +00:00
static int
isNew ( const tr_peer * peer )
{
return peer & & peer - > io & & tr_peerIoGetAge ( peer - > io ) < 45 ;
}
2007-09-20 16:32:01 +00:00
static void
2010-03-08 04:29:58 +00:00
rechokeUploads ( Torrent * t , const uint64_t now )
2007-09-20 16:32:01 +00:00
{
2008-12-30 08:20:16 +00:00
int i , size , unchokedInterested ;
2009-01-04 16:59:15 +00:00
const int peerCount = tr_ptrArraySize ( & t - > peers ) ;
tr_peer * * peers = ( tr_peer * * ) tr_ptrArrayBase ( & t - > peers ) ;
2007-10-03 16:42:43 +00:00
struct ChokeData * choke = tr_new0 ( struct ChokeData , peerCount ) ;
2009-01-09 15:45:44 +00:00
const tr_session * session = t - > manager - > session ;
2008-12-30 08:20:16 +00:00
const int chokeAll = ! tr_torrentIsPieceTransferAllowed ( t - > tor , TR_CLIENT_TO_PEER ) ;
2007-09-29 06:37:03 +00:00
assert ( torrentIsLocked ( t ) ) ;
2008-09-23 19:11:04 +00:00
2007-09-20 16:32:01 +00:00
/* sort the peers by preference and rate */
2008-09-23 19:11:04 +00:00
for ( i = 0 , size = 0 ; i < peerCount ; + + i )
2007-09-22 00:22:10 +00:00
{
2007-09-20 16:32:01 +00:00
tr_peer * peer = peers [ i ] ;
2009-05-09 06:08:21 +00:00
struct peer_atom * atom = peer - > atom ;
2008-12-02 19:46:51 +00:00
2008-01-09 17:33:43 +00:00
if ( peer - > progress > = 1.0 ) /* choke all seeds */
2008-12-02 19:46:51 +00:00
{
2008-01-09 17:33:43 +00:00
tr_peerMsgsSetChoke ( peer - > msgs , TRUE ) ;
2008-12-02 19:46:51 +00:00
}
else if ( atom - > uploadOnly = = UPLOAD_ONLY_YES ) /* choke partial seeds */
{
2008-11-07 04:10:27 +00:00
tr_peerMsgsSetChoke ( peer - > msgs , TRUE ) ;
2008-12-02 19:46:51 +00:00
}
else if ( chokeAll ) /* choke everyone if we're not uploading */
{
tr_peerMsgsSetChoke ( peer - > msgs , TRUE ) ;
}
else
{
2008-11-06 02:56:51 +00:00
struct ChokeData * n = & choke [ size + + ] ;
n - > peer = peer ;
n - > isInterested = peer - > peerIsInterested ;
2008-11-15 01:07:08 +00:00
n - > isChoked = peer - > peerIsChoked ;
2009-01-05 04:27:54 +00:00
n - > rate = tr_peerGetPieceSpeed ( peer , now , TR_CLIENT_TO_PEER ) * 1024 ;
2007-11-18 06:15:13 +00:00
}
2007-09-20 16:32:01 +00:00
}
2007-09-22 00:22:10 +00:00
2008-09-23 19:11:04 +00:00
qsort ( choke , size , sizeof ( struct ChokeData ) , compareChoke ) ;
2007-09-20 16:32:01 +00:00
2008-01-09 17:33:43 +00:00
/**
* Reciprocation and number of uploads capping is managed by unchoking
* the N peers which have the best upload rate and are interested .
* This maximizes the client ' s download rate . These N peers are
* referred to as downloaders , because they are interested in downloading
* from the client .
*
* Peers which have a better upload rate ( as compared to the downloaders )
* but aren ' t interested get unchoked . If they become interested , the
* downloader with the worst upload rate gets choked . If a client has
* a complete file , it uses its upload rate rather than its download
2008-09-23 19:11:04 +00:00
* rate to decide which peers to unchoke .
2008-01-09 17:33:43 +00:00
*/
unchokedInterested = 0 ;
2009-01-09 15:45:44 +00:00
for ( i = 0 ; i < size & & unchokedInterested < session - > uploadSlotsPerTorrent ; + + i ) {
2007-09-20 16:32:01 +00:00
choke [ i ] . doUnchoke = 1 ;
2008-01-09 17:33:43 +00:00
if ( choke [ i ] . isInterested )
+ + unchokedInterested ;
2007-11-18 06:15:13 +00:00
}
2007-09-20 16:32:01 +00:00
2008-01-09 17:33:43 +00:00
/* optimistic unchoke */
2008-01-11 02:09:20 +00:00
if ( i < size )
{
2008-12-05 22:56:19 +00:00
int n ;
2008-01-11 02:09:20 +00:00
struct ChokeData * c ;
2008-12-29 08:54:36 +00:00
tr_ptrArray randPool = TR_PTR_ARRAY_INIT ;
2008-06-03 04:29:56 +00:00
2008-12-05 22:56:19 +00:00
for ( ; i < size ; + + i )
2008-01-11 02:09:20 +00:00
{
2008-06-03 04:29:56 +00:00
if ( choke [ i ] . isInterested )
{
const tr_peer * peer = choke [ i ] . peer ;
2008-12-05 22:56:19 +00:00
int x = 1 , y ;
2008-06-03 04:29:56 +00:00
if ( isNew ( peer ) ) x * = 3 ;
2008-12-05 22:56:19 +00:00
for ( y = 0 ; y < x ; + + y )
2008-12-29 08:54:36 +00:00
tr_ptrArrayAppend ( & randPool , & choke [ i ] ) ;
2008-06-03 04:29:56 +00:00
}
2008-01-11 02:09:20 +00:00
}
2008-06-03 04:29:56 +00:00
2008-12-29 08:54:36 +00:00
if ( ( n = tr_ptrArraySize ( & randPool ) ) )
2008-06-03 04:29:56 +00:00
{
2008-12-29 08:54:36 +00:00
c = tr_ptrArrayNth ( & randPool , tr_cryptoWeakRandInt ( n ) ) ;
2008-06-03 04:29:56 +00:00
c - > doUnchoke = 1 ;
t - > optimistic = c - > peer ;
}
2008-12-29 08:54:36 +00:00
tr_ptrArrayDestruct ( & randPool , NULL ) ;
2007-09-20 16:32:01 +00:00
}
2008-12-22 00:52:44 +00:00
for ( i = 0 ; i < size ; + + i )
2007-09-20 16:32:01 +00:00
tr_peerMsgsSetChoke ( choke [ i ] . peer - > msgs , ! choke [ i ] . doUnchoke ) ;
/* cleanup */
tr_free ( choke ) ;
}
2009-12-10 05:52:46 +00:00
static void
rechokePulse ( int foo UNUSED , short bar UNUSED , void * vmgr )
2007-09-20 16:32:01 +00:00
{
2009-07-05 18:28:36 +00:00
uint64_t now ;
2009-02-04 16:58:52 +00:00
tr_torrent * tor = NULL ;
tr_peerMgr * mgr = vmgr ;
managerLock ( mgr ) ;
2008-09-23 19:11:04 +00:00
2009-07-05 18:28:36 +00:00
now = tr_date ( ) ;
2010-03-06 14:56:15 +00:00
while ( ( tor = tr_torrentNext ( mgr - > session , tor ) ) ) {
if ( tor - > isRunning ) {
2010-03-08 04:29:58 +00:00
rechokeUploads ( tor - > torrentPeers , now ) ;
if ( ! tr_torrentIsSeed ( tor ) )
rechokeDownloads ( tor - > torrentPeers ) ;
2010-03-06 14:56:15 +00:00
}
}
2009-02-04 16:58:52 +00:00
2009-12-10 19:04:10 +00:00
tr_timerAddMsec ( mgr - > rechokeTimer , RECHOKE_PERIOD_MSEC ) ;
2009-02-04 16:58:52 +00:00
managerUnlock ( mgr ) ;
2007-09-20 16:32:01 +00:00
}
2007-09-22 00:22:10 +00:00
2007-09-30 23:55:49 +00:00
/***
* * * *
2007-10-13 13:54:05 +00:00
* * * * Life and Death
2007-09-30 23:55:49 +00:00
* * * *
* * */
2009-01-20 15:47:25 +00:00
typedef enum
{
TR_CAN_KEEP ,
TR_CAN_CLOSE ,
TR_MUST_CLOSE ,
}
tr_close_type_t ;
static tr_close_type_t
shouldPeerBeClosed ( const Torrent * t ,
const tr_peer * peer ,
2009-11-19 03:00:20 +00:00
int peerCount ,
const time_t now )
2007-09-30 23:55:49 +00:00
{
2008-09-23 19:11:04 +00:00
const tr_torrent * tor = t - > tor ;
2009-05-09 06:08:21 +00:00
const struct peer_atom * atom = peer - > atom ;
2007-09-22 14:18:52 +00:00
2007-10-13 13:54:05 +00:00
/* if it's marked for purging, close it */
2008-09-23 19:11:04 +00:00
if ( peer - > doPurge )
{
tordbg ( t , " purging peer %s because its doPurge flag is set " ,
2009-10-29 16:10:03 +00:00
tr_atomAddrStr ( atom ) ) ;
2009-01-20 15:47:25 +00:00
return TR_MUST_CLOSE ;
2007-10-13 13:54:05 +00:00
}
2007-09-29 06:37:03 +00:00
2008-05-06 19:06:46 +00:00
/* if we're seeding and the peer has everything we have,
* and enough time has passed for a pex exchange , then disconnect */
2008-09-23 19:11:04 +00:00
if ( tr_torrentIsSeed ( tor ) )
{
2010-04-20 21:54:03 +00:00
tr_bool peerHasEverything ;
if ( atom - > seedProbability ! = - 1 )
{
peerHasEverything = atomIsSeed ( atom ) ;
}
else
{
2009-01-02 17:01:55 +00:00
tr_bitfield * tmp = tr_bitfieldDup ( tr_cpPieceBitfield ( & tor - > completion ) ) ;
2009-11-24 02:16:31 +00:00
tr_bitsetDifference ( tmp , & peer - > have ) ;
2008-05-06 19:06:46 +00:00
peerHasEverything = tr_bitfieldCountTrueBits ( tmp ) = = 0 ;
tr_bitfieldFree ( tmp ) ;
}
2008-12-22 00:52:44 +00:00
if ( peerHasEverything & & ( ! tr_torrentAllowsPex ( tor ) | | ( now - atom - > time > = 30 ) ) )
{
2008-09-23 19:11:04 +00:00
tordbg ( t , " purging peer %s because we're both seeds " ,
2009-10-29 16:10:03 +00:00
tr_atomAddrStr ( atom ) ) ;
2009-01-20 15:47:25 +00:00
return TR_MUST_CLOSE ;
2007-10-11 03:12:48 +00:00
}
2007-10-13 13:54:05 +00:00
}
2007-10-01 03:24:52 +00:00
2007-10-13 13:54:05 +00:00
/* disconnect if it's been too long since piece data has been transferred.
* this is on a sliding scale based on number of available peers . . . */
2008-05-06 20:31:05 +00:00
{
2008-12-05 22:56:19 +00:00
const int relaxStrictnessIfFewerThanN = ( int ) ( ( getMaxPeerCount ( tor ) * 0.9 ) + 0.5 ) ;
2007-10-13 13:54:05 +00:00
/* if we have >= relaxIfFewerThan, strictness is 100%.
* if we have zero connections , strictness is 0 % */
2008-12-05 22:56:19 +00:00
const float strictness = peerCount > = relaxStrictnessIfFewerThanN
2009-01-20 03:32:54 +00:00
? 1.0
: peerCount / ( float ) relaxStrictnessIfFewerThanN ;
2008-12-05 22:56:19 +00:00
const int lo = MIN_UPLOAD_IDLE_SECS ;
const int hi = MAX_UPLOAD_IDLE_SECS ;
2008-12-11 07:04:46 +00:00
const int limit = hi - ( ( hi - lo ) * strictness ) ;
const int idleTime = now - MAX ( atom - > time , atom - > piece_data_time ) ;
/*fprintf( stderr, "strictness is %.3f, limit is %d seconds... time since connect is %d, time since piece is %d ... idleTime is %d, doPurge is %d\n", (double)strictness, limit, (int)(now - atom->time), (int)(now - atom->piece_data_time), idleTime, idleTime > limit );*/
2008-12-05 22:56:19 +00:00
if ( idleTime > limit ) {
tordbg ( t , " purging peer %s because it's been %d secs since we shared anything " ,
2009-10-29 16:10:03 +00:00
tr_atomAddrStr ( atom ) , idleTime ) ;
2009-01-20 15:47:25 +00:00
return TR_CAN_CLOSE ;
2007-10-13 13:54:05 +00:00
}
2007-09-22 14:18:52 +00:00
}
2009-01-20 15:47:25 +00:00
return TR_CAN_KEEP ;
2007-10-13 13:54:05 +00:00
}
2009-10-27 20:06:55 +00:00
static void sortPeersByLivelinessReverse ( tr_peer * * peers , void * * clientData , int n , uint64_t now ) ;
2007-10-13 13:54:05 +00:00
static tr_peer * *
2009-11-19 20:31:34 +00:00
getPeersToClose ( Torrent * t , tr_close_type_t closeType , const time_t now , int * setmeSize )
2007-10-13 13:54:05 +00:00
{
2008-12-05 22:56:19 +00:00
int i , peerCount , outsize ;
2008-12-29 08:54:36 +00:00
tr_peer * * peers = ( tr_peer * * ) tr_ptrArrayPeek ( & t - > peers , & peerCount ) ;
2008-09-23 19:11:04 +00:00
struct tr_peer * * ret = tr_new ( tr_peer * , peerCount ) ;
2007-10-13 13:54:05 +00:00
assert ( torrentIsLocked ( t ) ) ;
2008-09-23 19:11:04 +00:00
for ( i = outsize = 0 ; i < peerCount ; + + i )
2009-11-19 03:00:20 +00:00
if ( shouldPeerBeClosed ( t , peers [ i ] , peerCount , now ) = = closeType )
2007-10-13 13:54:05 +00:00
ret [ outsize + + ] = peers [ i ] ;
2009-10-27 20:06:55 +00:00
sortPeersByLivelinessReverse ( ret , NULL , outsize , tr_date ( ) ) ;
2007-09-30 23:55:49 +00:00
* setmeSize = outsize ;
return ret ;
2007-09-22 14:18:52 +00:00
}
2008-08-16 05:12:55 +00:00
static int
2009-11-19 03:00:20 +00:00
getReconnectIntervalSecs ( const struct peer_atom * atom , const time_t now )
2008-08-16 05:12:55 +00:00
{
2009-11-19 03:00:20 +00:00
int sec ;
2008-08-16 05:12:55 +00:00
2008-09-05 05:14:49 +00:00
/* if we were recently connected to this peer and transferring piece
* data , try to reconnect to them sooner rather that later - - we don ' t
* want network troubles to get in the way of a good peer . */
2008-12-05 22:56:19 +00:00
if ( ( now - atom - > piece_data_time ) < = ( MINIMUM_RECONNECT_INTERVAL_SECS * 2 ) )
2008-09-05 05:14:49 +00:00
sec = MINIMUM_RECONNECT_INTERVAL_SECS ;
/* don't allow reconnects more often than our minimum */
else if ( ( now - atom - > time ) < MINIMUM_RECONNECT_INTERVAL_SECS )
sec = MINIMUM_RECONNECT_INTERVAL_SECS ;
/* otherwise, the interval depends on how many times we've tried
* and failed to connect to the peer */
2008-12-05 22:56:19 +00:00
else switch ( atom - > numFails ) {
case 0 : sec = 0 ; break ;
case 1 : sec = 5 ; break ;
case 2 : sec = 2 * 60 ; break ;
case 3 : sec = 15 * 60 ; break ;
case 4 : sec = 30 * 60 ; break ;
case 5 : sec = 60 * 60 ; break ;
default : sec = 120 * 60 ; break ;
}
2008-08-16 05:12:55 +00:00
2010-04-20 21:54:03 +00:00
/* penalize peers that were unreachable the last time we tried */
if ( atom - > myflags & MYFLAG_UNREACHABLE )
sec + = sec ;
2007-09-26 04:44:54 +00:00
2010-04-20 21:54:03 +00:00
dbgmsg ( " reconnect interval for %s is %d seconds " , tr_atomAddrStr ( atom ) , sec ) ;
return sec ;
2007-09-30 23:55:49 +00:00
}
2007-09-22 14:18:52 +00:00
2009-01-20 15:47:25 +00:00
static void
closePeer ( Torrent * t , tr_peer * peer )
{
struct peer_atom * atom ;
assert ( t ! = NULL ) ;
assert ( peer ! = NULL ) ;
2009-05-09 06:08:21 +00:00
atom = peer - > atom ;
2009-01-20 15:47:25 +00:00
/* if we transferred piece data, then they might be good peers,
so reset their ` numFails ' weight to zero . otherwise we connected
to them fruitlessly , so mark it as another fail */
2010-04-20 21:54:03 +00:00
if ( atom - > piece_data_time ) {
tordbg ( t , " resetting atom %s numFails to 0 " , tr_atomAddrStr ( atom ) ) ;
2009-01-20 15:47:25 +00:00
atom - > numFails = 0 ;
2010-04-20 21:54:03 +00:00
} else {
2009-01-20 15:47:25 +00:00
+ + atom - > numFails ;
2010-04-20 21:54:03 +00:00
tordbg ( t , " incremented atom %s numFails to %d " , tr_atomAddrStr ( atom ) , ( int ) atom - > numFails ) ;
}
2009-01-20 15:47:25 +00:00
tordbg ( t , " removing bad peer %s " , tr_peerIoGetAddrStr ( peer - > io ) ) ;
removePeer ( t , peer ) ;
}
2009-02-04 16:58:52 +00:00
static void
2010-04-20 21:54:03 +00:00
closeBadPeers ( Torrent * t )
2007-09-30 23:55:49 +00:00
{
2009-11-26 18:47:08 +00:00
const time_t now = tr_time ( ) ;
2007-09-22 14:18:52 +00:00
2007-10-01 16:50:51 +00:00
if ( ! t - > isRunning )
{
removeAllPeers ( t ) ;
2007-09-22 14:18:52 +00:00
}
2007-10-01 16:50:51 +00:00
else
{
2009-01-20 15:47:25 +00:00
int i ;
int mustCloseCount ;
2009-11-29 18:35:02 +00:00
struct tr_peer * * mustClose ;
2009-01-20 15:47:25 +00:00
/* disconnect the really bad peers */
2009-11-29 18:35:02 +00:00
mustClose = getPeersToClose ( t , TR_MUST_CLOSE , now , & mustCloseCount ) ;
2009-01-20 15:47:25 +00:00
for ( i = 0 ; i < mustCloseCount ; + + i )
closePeer ( t , mustClose [ i ] ) ;
2009-11-29 18:35:02 +00:00
tr_free ( mustClose ) ;
2007-09-30 23:55:49 +00:00
}
2009-02-04 16:58:52 +00:00
}
2007-09-30 23:55:49 +00:00
2009-07-05 18:28:36 +00:00
struct peer_liveliness
{
tr_peer * peer ;
2009-07-06 12:27:24 +00:00
void * clientData ;
2009-07-05 18:28:36 +00:00
time_t pieceDataTime ;
time_t time ;
2009-07-06 12:27:24 +00:00
int speed ;
tr_bool doPurge ;
2009-07-05 18:28:36 +00:00
} ;
static int
comparePeerLiveliness ( const void * va , const void * vb )
{
const struct peer_liveliness * a = va ;
const struct peer_liveliness * b = vb ;
2009-07-06 12:27:24 +00:00
if ( a - > doPurge ! = b - > doPurge )
return a - > doPurge ? 1 : - 1 ;
2009-07-05 18:28:36 +00:00
if ( a - > speed ! = b - > speed ) /* faster goes first */
return a - > speed > b - > speed ? - 1 : 1 ;
2009-07-06 12:27:24 +00:00
/* the one to give us data more recently goes first */
if ( a - > pieceDataTime ! = b - > pieceDataTime )
2009-07-05 18:28:36 +00:00
return a - > pieceDataTime > b - > pieceDataTime ? - 1 : 1 ;
2009-07-06 12:27:24 +00:00
/* the one we connected to most recently goes first */
if ( a - > time ! = b - > time )
2009-07-05 18:28:36 +00:00
return a - > time > b - > time ? - 1 : 1 ;
return 0 ;
}
2009-10-27 20:06:55 +00:00
static int
comparePeerLivelinessReverse ( const void * va , const void * vb )
{
return - comparePeerLiveliness ( va , vb ) ;
}
2009-07-05 18:28:36 +00:00
static void
2009-10-27 20:06:55 +00:00
sortPeersByLivelinessImpl ( tr_peer * * peers ,
void * * clientData ,
int n ,
uint64_t now ,
int ( * compare ) ( const void * va , const void * vb ) )
2009-07-05 18:28:36 +00:00
{
int i ;
2009-07-06 12:27:24 +00:00
struct peer_liveliness * lives , * l ;
2009-07-05 18:28:36 +00:00
/* build a sortable array of peer + extra info */
2009-07-06 12:27:24 +00:00
lives = l = tr_new0 ( struct peer_liveliness , n ) ;
2009-10-27 20:06:55 +00:00
for ( i = 0 ; i < n ; + + i , + + l )
{
2009-07-05 18:28:36 +00:00
tr_peer * p = peers [ i ] ;
2009-07-06 12:27:24 +00:00
l - > peer = p ;
l - > doPurge = p - > doPurge ;
l - > pieceDataTime = p - > atom - > piece_data_time ;
l - > time = p - > atom - > time ;
l - > speed = 1024.0 * ( tr_peerGetPieceSpeed ( p , now , TR_UP )
+ tr_peerGetPieceSpeed ( p , now , TR_DOWN ) ) ;
2009-07-05 18:28:36 +00:00
if ( clientData )
2009-07-06 12:27:24 +00:00
l - > clientData = clientData [ i ] ;
2009-07-05 18:28:36 +00:00
}
/* sort 'em */
2009-07-06 12:27:24 +00:00
assert ( n = = ( l - lives ) ) ;
2009-10-27 20:06:55 +00:00
qsort ( lives , n , sizeof ( struct peer_liveliness ) , compare ) ;
2009-07-05 18:28:36 +00:00
/* build the peer array */
2009-07-06 12:27:24 +00:00
for ( i = 0 , l = lives ; i < n ; + + i , + + l ) {
peers [ i ] = l - > peer ;
2009-07-05 18:28:36 +00:00
if ( clientData )
2009-07-06 12:27:24 +00:00
clientData [ i ] = l - > clientData ;
2009-07-05 18:28:36 +00:00
}
2009-07-06 12:27:24 +00:00
assert ( n = = ( l - lives ) ) ;
2009-07-05 18:28:36 +00:00
/* cleanup */
2009-07-06 12:27:24 +00:00
tr_free ( lives ) ;
2009-07-05 18:28:36 +00:00
}
2009-10-27 20:06:55 +00:00
static void
sortPeersByLiveliness ( tr_peer * * peers , void * * clientData , int n , uint64_t now )
{
sortPeersByLivelinessImpl ( peers , clientData , n , now , comparePeerLiveliness ) ;
}
static void
sortPeersByLivelinessReverse ( tr_peer * * peers , void * * clientData , int n , uint64_t now )
{
sortPeersByLivelinessImpl ( peers , clientData , n , now , comparePeerLivelinessReverse ) ;
}
2009-07-05 18:28:36 +00:00
static void
enforceTorrentPeerLimit ( Torrent * t , uint64_t now )
{
int n = tr_ptrArraySize ( & t - > peers ) ;
2009-08-31 15:59:43 +00:00
const int max = tr_torrentGetPeerLimit ( t - > tor ) ;
2009-07-05 18:28:36 +00:00
if ( n > max )
{
2009-07-06 12:27:24 +00:00
void * base = tr_ptrArrayBase ( & t - > peers ) ;
tr_peer * * peers = tr_memdup ( base , n * sizeof ( tr_peer * ) ) ;
sortPeersByLiveliness ( peers , NULL , n , now ) ;
2009-07-05 18:28:36 +00:00
while ( n > max )
closePeer ( t , peers [ - - n ] ) ;
tr_free ( peers ) ;
}
}
static void
enforceSessionPeerLimit ( tr_session * session , uint64_t now )
{
2009-07-06 12:27:24 +00:00
int n = 0 ;
tr_torrent * tor = NULL ;
2009-07-05 18:28:36 +00:00
const int max = tr_sessionGetPeerLimit ( session ) ;
/* count the total number of peers */
while ( ( tor = tr_torrentNext ( session , tor ) ) )
n + = tr_ptrArraySize ( & tor - > torrentPeers - > peers ) ;
2009-08-10 20:04:08 +00:00
/* if there are too many, prune out the worst */
2009-07-05 18:28:36 +00:00
if ( n > max )
{
tr_peer * * peers = tr_new ( tr_peer * , n ) ;
Torrent * * torrents = tr_new ( Torrent * , n ) ;
/* populate the peer array */
2009-07-06 12:27:24 +00:00
n = 0 ;
2009-07-05 18:28:36 +00:00
tor = NULL ;
while ( ( tor = tr_torrentNext ( session , tor ) ) ) {
int i ;
Torrent * t = tor - > torrentPeers ;
const int tn = tr_ptrArraySize ( & t - > peers ) ;
for ( i = 0 ; i < tn ; + + i , + + n ) {
peers [ n ] = tr_ptrArrayNth ( & t - > peers , i ) ;
torrents [ n ] = t ;
}
}
2009-08-10 20:04:08 +00:00
2009-07-05 18:28:36 +00:00
/* sort 'em */
2009-07-06 12:27:24 +00:00
sortPeersByLiveliness ( peers , ( void * * ) torrents , n , now ) ;
2009-07-05 18:28:36 +00:00
/* cull out the crappiest */
while ( n - - > max )
closePeer ( torrents [ n ] , peers [ n ] ) ;
/* cleanup */
tr_free ( torrents ) ;
tr_free ( peers ) ;
}
}
2010-04-20 21:54:03 +00:00
static void makeNewPeerConnections ( tr_peerMgr * mgr , const int max ) ;
2010-01-05 23:47:50 +00:00
2009-12-10 05:52:46 +00:00
static void
reconnectPulse ( int foo UNUSED , short bar UNUSED , void * vmgr )
2009-02-04 16:58:52 +00:00
{
2009-07-05 18:28:36 +00:00
tr_torrent * tor ;
2009-02-04 16:58:52 +00:00
tr_peerMgr * mgr = vmgr ;
2010-04-20 21:54:03 +00:00
const uint64_t now = tr_date ( ) ;
2009-07-05 18:28:36 +00:00
2009-12-12 17:05:31 +00:00
/**
* * * enforce the per - session and per - torrent peer limits
* */
2009-07-05 18:28:36 +00:00
/* if we're over the per-torrent peer limits, cull some peers */
tor = NULL ;
while ( ( tor = tr_torrentNext ( mgr - > session , tor ) ) )
if ( tor - > isRunning )
enforceTorrentPeerLimit ( tor - > torrentPeers , now ) ;
/* if we're over the per-session peer limits, cull some peers */
enforceSessionPeerLimit ( mgr - > session , now ) ;
2010-04-20 21:54:03 +00:00
/* remove crappy peers */
tor = NULL ;
while ( ( tor = tr_torrentNext ( mgr - > session , tor ) ) )
closeBadPeers ( tor - > torrentPeers ) ;
2009-12-12 17:05:31 +00:00
2010-04-20 21:54:03 +00:00
/* try to make new peer connections */
makeNewPeerConnections ( mgr , MAX_CONNECTIONS_PER_PULSE ) ;
2007-09-22 00:22:10 +00:00
}
2008-09-17 19:44:24 +00:00
/****
* * * * *
2008-10-25 02:20:16 +00:00
* * * * * BANDWIDTH ALLOCATION
2008-09-17 19:44:24 +00:00
* * * * *
* * * */
2008-11-24 04:21:23 +00:00
static void
pumpAllPeers ( tr_peerMgr * mgr )
{
2009-01-13 21:00:05 +00:00
tr_torrent * tor = NULL ;
2008-11-24 04:21:23 +00:00
2009-01-13 21:00:05 +00:00
while ( ( tor = tr_torrentNext ( mgr - > session , tor ) ) )
2008-11-24 04:21:23 +00:00
{
2009-01-13 21:00:05 +00:00
int j ;
Torrent * t = tor - > torrentPeers ;
2008-12-29 08:54:36 +00:00
for ( j = 0 ; j < tr_ptrArraySize ( & t - > peers ) ; + + j )
2008-11-24 04:21:23 +00:00
{
2008-12-29 08:54:36 +00:00
tr_peer * peer = tr_ptrArrayNth ( & t - > peers , j ) ;
2008-11-24 04:21:23 +00:00
tr_peerMsgsPulse ( peer - > msgs ) ;
}
}
}
2009-12-10 05:52:46 +00:00
static void
bandwidthPulse ( int foo UNUSED , short bar UNUSED , void * vmgr )
2008-09-17 19:44:24 +00:00
{
2010-03-06 14:56:15 +00:00
tr_torrent * tor ;
2008-09-17 19:44:24 +00:00
tr_peerMgr * mgr = vmgr ;
managerLock ( mgr ) ;
2008-12-20 22:19:34 +00:00
/* FIXME: this next line probably isn't necessary... */
2008-11-25 21:35:17 +00:00
pumpAllPeers ( mgr ) ;
2008-12-20 22:19:34 +00:00
/* allocate bandwidth to the peers */
2008-11-25 21:35:17 +00:00
tr_bandwidthAllocate ( mgr - > session - > bandwidth , TR_UP , BANDWIDTH_PERIOD_MSEC ) ;
tr_bandwidthAllocate ( mgr - > session - > bandwidth , TR_DOWN , BANDWIDTH_PERIOD_MSEC ) ;
2008-12-20 22:19:34 +00:00
2009-04-09 14:10:31 +00:00
/* possibly stop torrents that have seeded enough */
2010-03-06 14:56:15 +00:00
tor = NULL ;
2010-04-15 13:43:54 +00:00
while ( ( tor = tr_torrentNext ( mgr - > session , tor ) ) )
tr_torrentCheckSeedRatio ( tor ) ;
2009-04-09 14:10:31 +00:00
2009-11-09 06:36:47 +00:00
/* run the completeness check for any torrents that need it */
tor = NULL ;
while ( ( tor = tr_torrentNext ( mgr - > session , tor ) ) ) {
if ( tor - > torrentPeers - > needsCompletenessCheck ) {
tor - > torrentPeers - > needsCompletenessCheck = FALSE ;
tr_torrentRecheckCompleteness ( tor ) ;
}
}
2009-08-13 17:25:26 +00:00
/* possibly stop torrents that have an error */
tor = NULL ;
while ( ( tor = tr_torrentNext ( mgr - > session , tor ) ) )
2009-08-31 16:41:54 +00:00
if ( tor - > isRunning & & ( tor - > error = = TR_STAT_LOCAL_ERROR ) )
2009-08-13 17:25:26 +00:00
tr_torrentStop ( tor ) ;
2010-04-20 21:54:03 +00:00
reconnectPulse ( 0 , 0 , mgr ) ;
2009-12-10 19:04:10 +00:00
tr_timerAddMsec ( mgr - > bandwidthTimer , BANDWIDTH_PERIOD_MSEC ) ;
2008-09-17 19:44:24 +00:00
managerUnlock ( mgr ) ;
}
2009-11-26 05:13:58 +00:00
/***
* * * *
* * */
static int
compareAtomPtrsByAddress ( const void * va , const void * vb )
{
const struct peer_atom * a = * ( const struct peer_atom * * ) va ;
const struct peer_atom * b = * ( const struct peer_atom * * ) vb ;
assert ( tr_isAtom ( a ) ) ;
assert ( tr_isAtom ( b ) ) ;
return tr_compareAddresses ( & a - > addr , & b - > addr ) ;
}
/* best come first, worst go last */
static int
compareAtomPtrsByShelfDate ( const void * va , const void * vb )
{
time_t atime ;
time_t btime ;
const struct peer_atom * a = * ( const struct peer_atom * * ) va ;
const struct peer_atom * b = * ( const struct peer_atom * * ) vb ;
const int data_time_cutoff_secs = 60 * 60 ;
2009-12-16 18:20:01 +00:00
const time_t tr_now = tr_time ( ) ;
2009-11-26 05:13:58 +00:00
assert ( tr_isAtom ( a ) ) ;
assert ( tr_isAtom ( b ) ) ;
/* primary key: the last piece data time *if* it was within the last hour */
atime = a - > piece_data_time ; if ( atime + data_time_cutoff_secs < tr_now ) atime = 0 ;
btime = b - > piece_data_time ; if ( btime + data_time_cutoff_secs < tr_now ) btime = 0 ;
if ( atime ! = btime )
return atime > btime ? - 1 : 1 ;
/* secondary key: shelf date. */
if ( a - > shelf_date ! = b - > shelf_date )
return a - > shelf_date > b - > shelf_date ? - 1 : 1 ;
return 0 ;
}
static int
getMaxAtomCount ( const tr_torrent * tor )
{
/* FIXME: this curve should be smoother... */
const int n = tor - > maxConnectedPeers ;
if ( n > = 200 ) return n * 1.5 ;
if ( n > = 100 ) return n * 2 ;
if ( n > = 50 ) return n * 3 ;
if ( n > = 20 ) return n * 5 ;
return n * 10 ;
}
2009-12-10 05:52:46 +00:00
static void
atomPulse ( int foo UNUSED , short bar UNUSED , void * vmgr )
2009-11-26 05:13:58 +00:00
{
tr_torrent * tor = NULL ;
tr_peerMgr * mgr = vmgr ;
managerLock ( mgr ) ;
while ( ( tor = tr_torrentNext ( mgr - > session , tor ) ) )
{
int atomCount ;
Torrent * t = tor - > torrentPeers ;
const int maxAtomCount = getMaxAtomCount ( tor ) ;
struct peer_atom * * atoms = ( struct peer_atom * * ) tr_ptrArrayPeek ( & t - > pool , & atomCount ) ;
if ( atomCount > maxAtomCount ) /* we've got too many atoms... time to prune */
{
int i ;
int keepCount = 0 ;
int testCount = 0 ;
struct peer_atom * * keep = tr_new ( struct peer_atom * , atomCount ) ;
struct peer_atom * * test = tr_new ( struct peer_atom * , atomCount ) ;
/* keep the ones that are in use */
for ( i = 0 ; i < atomCount ; + + i ) {
struct peer_atom * atom = atoms [ i ] ;
2009-11-28 07:15:31 +00:00
if ( peerIsInUse ( t , atom ) )
2009-11-26 05:13:58 +00:00
keep [ keepCount + + ] = atom ;
else
test [ testCount + + ] = atom ;
}
/* if there's room, keep the best of what's left */
i = 0 ;
if ( keepCount < maxAtomCount ) {
qsort ( test , testCount , sizeof ( struct peer_atom * ) , compareAtomPtrsByShelfDate ) ;
while ( i < testCount & & keepCount < maxAtomCount )
keep [ keepCount + + ] = test [ i + + ] ;
}
/* free the culled atoms */
while ( i < testCount )
tr_free ( test [ i + + ] ) ;
/* rebuild Torrent.pool with what's left */
tr_ptrArrayDestruct ( & t - > pool , NULL ) ;
t - > pool = TR_PTR_ARRAY_INIT ;
qsort ( keep , keepCount , sizeof ( struct peer_atom * ) , compareAtomPtrsByAddress ) ;
2009-11-27 14:27:44 +00:00
for ( i = 0 ; i < keepCount ; + + i )
2009-11-26 05:13:58 +00:00
tr_ptrArrayAppend ( & t - > pool , keep [ i ] ) ;
2009-11-27 14:27:44 +00:00
tordbg ( t , " max atom count is %d... pruned from %d to %d \n " , maxAtomCount , atomCount , keepCount ) ;
2009-11-26 05:13:58 +00:00
/* cleanup */
tr_free ( test ) ;
tr_free ( keep ) ;
}
}
2009-12-10 19:04:10 +00:00
tr_timerAddMsec ( mgr - > atomTimer , ATOM_PERIOD_MSEC ) ;
2009-11-26 05:13:58 +00:00
managerUnlock ( mgr ) ;
}
2010-04-20 21:54:03 +00:00
/***
* * * *
* * * *
* * * *
* * */
static inline tr_bool
isBandwidthMaxedOut ( const tr_bandwidth * b ,
const uint64_t now_msec , tr_direction dir )
{
if ( ! tr_bandwidthIsLimited ( b , dir ) )
return FALSE ;
else {
const double got = tr_bandwidthGetPieceSpeed ( b , now_msec , dir ) ;
const double want = tr_bandwidthGetDesiredSpeed ( b , dir ) ;
return got > = want ;
}
}
/* is this atom someone that we'd want to initiate a connection to? */
static tr_bool
isPeerCandidate ( const tr_torrent * tor , const struct peer_atom * atom , const time_t now )
{
/* not if we've already got a connection to them... */
if ( peerIsInUse ( tor - > torrentPeers , atom ) )
return FALSE ;
/* not if they're banned... */
if ( atom - > myflags & MYFLAG_BANNED )
return FALSE ;
/* not if we just tried them already */
if ( ( now - atom - > time ) < getReconnectIntervalSecs ( atom , now ) )
return FALSE ;
/* not if we're both seeds */
if ( tr_torrentIsSeed ( tor ) )
if ( atomIsSeed ( atom ) | | ( atom - > uploadOnly = = UPLOAD_ONLY_YES ) )
return FALSE ;
/* not if they're blocklisted */
/* FIXME: maybe we should remove this atom altogether? */
if ( tr_sessionIsAddressBlocked ( tor - > session , & atom - > addr ) )
return FALSE ;
return TRUE ;
}
struct peer_candidate
{
int salt ;
tr_torrent * tor ;
struct peer_atom * atom ;
} ;
static int
compareSeedProbabilities ( int a , int b )
{
/* 1. smaller numbers are better
2. prefer leechers to unknown
3. prefer unknown to seeds ( FIXME : this is a simplistic test ) */
if ( a = = 100 ) a = 101 ;
if ( b = = 100 ) b = 101 ;
if ( a = = - 1 ) a = 100 ;
if ( b = = - 1 ) b = 100 ;
return a - b ;
}
static tr_bool
torrentWasRecentlyStarted ( const tr_torrent * tor )
{
return difftime ( tr_time ( ) , tor - > startDate ) < 120 ;
}
/* sort an array of peer candidates */
static int
comparePeerCandidates ( const void * va , const void * vb )
{
int i , ai , bi ;
tr_bool af , bf ;
const struct peer_candidate * a = va ;
const struct peer_candidate * b = vb ;
/* prefer peers we've connected to, or never tried, over peers we failed to connect to. */
af = a - > atom - > lastConnectionAt < a - > atom - > lastConnectionAttemptAt ;
bf = b - > atom - > lastConnectionAt < b - > atom - > lastConnectionAttemptAt ;
if ( af ! = bf )
return af ? 1 : - 1 ;
/* prefer the one we attempted least recently (to cycle through all peers) */
if ( a - > atom - > lastConnectionAttemptAt ! = b - > atom - > lastConnectionAttemptAt )
return a - > atom - > lastConnectionAttemptAt < b - > atom - > lastConnectionAttemptAt ? - 1 : 1 ;
/* prefer peers belonging to a torrent of a higher priority */
ai = tr_torrentGetPriority ( a - > tor ) ;
bi = tr_torrentGetPriority ( b - > tor ) ;
if ( ai ! = bi )
return ai > bi ? - 1 : 1 ;
/* prefer recently-started torrents */
af = torrentWasRecentlyStarted ( a - > tor ) ;
bf = torrentWasRecentlyStarted ( a - > tor ) ;
if ( af ! = bf )
return af ? - 1 : 1 ;
/* prefer peers that we might have a chance of uploading to */
if ( ( i = compareSeedProbabilities ( a - > atom - > seedProbability , b - > atom - > seedProbability ) ) )
return i ;
/* salt */
return a - > salt - b - > salt ;
}
/** @return an array of all the atoms we might want to connect to */
static struct peer_candidate *
getPeerCandidates ( tr_session * session , int * candidateCount )
{
int n ;
tr_torrent * tor ;
struct peer_candidate * candidates ;
struct peer_candidate * walk ;
const time_t now = tr_time ( ) ;
const uint64_t now_msec = tr_date ( ) ;
2010-04-29 03:21:54 +00:00
/* leave 5% of connection slots for incoming connections -- ticket #2609 */
const int maxCandidates = tr_sessionGetPeerLimit ( session ) * 0.95 ;
2010-04-20 21:54:03 +00:00
/* don't start any new handshakes if we're full up */
n = 0 ;
tor = NULL ;
while ( ( tor = tr_torrentNext ( session , tor ) ) )
n + = tr_ptrArraySize ( & tor - > torrentPeers - > peers ) ;
2010-04-29 03:21:54 +00:00
if ( maxCandidates < = n ) {
2010-04-20 21:54:03 +00:00
* candidateCount = 0 ;
return NULL ;
}
/* allocate an array of candidates */
n = 0 ;
tor = NULL ;
while ( ( tor = tr_torrentNext ( session , tor ) ) )
n + = tr_ptrArraySize ( & tor - > torrentPeers - > pool ) ;
walk = candidates = tr_new ( struct peer_candidate , n ) ;
/* populate the candidate array */
tor = NULL ;
while ( ( tor = tr_torrentNext ( session , tor ) ) )
{
int i , nAtoms ;
struct peer_atom * * atoms ;
if ( ! tor - > torrentPeers - > isRunning )
continue ;
/* if we've already got enough peers in this torrent... */
if ( tr_torrentGetPeerLimit ( tor ) < = tr_ptrArraySize ( & tor - > torrentPeers - > peers ) )
continue ;
/* if we've already got enough speed in this torrent... */
if ( tr_torrentIsSeed ( tor ) & & isBandwidthMaxedOut ( tor - > bandwidth , now_msec , TR_UP ) )
continue ;
atoms = ( struct peer_atom * * ) tr_ptrArrayPeek ( & tor - > torrentPeers - > pool , & nAtoms ) ;
for ( i = 0 ; i < nAtoms ; + + i )
{
struct peer_atom * atom = atoms [ i ] ;
if ( isPeerCandidate ( tor , atom , now ) )
{
walk - > tor = tor ;
walk - > atom = atom ;
walk - > salt = tr_cryptoWeakRandInt ( 4096 ) ;
+ + walk ;
}
}
}
* candidateCount = walk - candidates ;
if ( * candidateCount > 1 )
qsort ( candidates , * candidateCount , sizeof ( struct peer_candidate ) , comparePeerCandidates ) ;
return candidates ;
}
static void
initiateConnection ( tr_peerMgr * mgr , Torrent * t , struct peer_atom * atom )
{
tr_peerIo * io ;
const time_t now = tr_time ( ) ;
tordbg ( t , " Starting an OUTGOING connection with %s " , tr_atomAddrStr ( atom ) ) ;
io = tr_peerIoNewOutgoing ( mgr - > session ,
mgr - > session - > bandwidth ,
& atom - > addr ,
atom - > port ,
t - > tor - > info . hash ,
t - > tor - > completeness = = TR_SEED ) ;
if ( io = = NULL )
{
tordbg ( t , " peerIo not created; marking peer %s as unreachable " ,
tr_atomAddrStr ( atom ) ) ;
atom - > myflags | = MYFLAG_UNREACHABLE ;
atom - > numFails + + ;
}
else
{
tr_handshake * handshake = tr_handshakeNew ( io ,
mgr - > session - > encryptionMode ,
myHandshakeDoneCB ,
mgr ) ;
assert ( tr_peerIoGetTorrentHash ( io ) ) ;
2010-04-22 03:12:31 +00:00
tr_peerIoUnref ( io ) ; /* balanced by the initial ref
in tr_peerIoNewOutgoing ( ) */
2010-04-20 21:54:03 +00:00
tr_ptrArrayInsertSorted ( & t - > outgoingHandshakes , handshake ,
handshakeCompare ) ;
}
atom - > lastConnectionAttemptAt = now ;
atom - > time = now ;
}
static void
initiateCandidateConnection ( tr_peerMgr * mgr , struct peer_candidate * c )
{
#if 0
fprintf ( stderr , " Starting an OUTGOING connection with %s - [%s] seedProbability==%d; %s, %s \n " ,
tr_atomAddrStr ( c - > atom ) ,
tr_torrentName ( c - > tor ) ,
( int ) c - > atom - > seedProbability ,
tr_torrentIsPrivate ( c - > tor ) ? " private " : " public " ,
tr_torrentIsSeed ( c - > tor ) ? " seed " : " downloader " ) ;
# endif
initiateConnection ( mgr , c - > tor - > torrentPeers , c - > atom ) ;
}
static void
makeNewPeerConnections ( struct tr_peerMgr * mgr , const int max )
{
int i , n ;
struct peer_candidate * candidates ;
candidates = getPeerCandidates ( mgr - > session , & n ) ;
for ( i = 0 ; i < n & & i < max ; + + i )
initiateCandidateConnection ( mgr , & candidates [ i ] ) ;
tr_free ( candidates ) ;
}