diff --git a/libtransmission/crypto.c b/libtransmission/crypto.c index 603b38a9a..95a4ae5d4 100644 --- a/libtransmission/crypto.c +++ b/libtransmission/crypto.c @@ -268,7 +268,8 @@ tr_cryptoHasTorrentHash( const tr_crypto * crypto ) return crypto->torrentHashIsSet ? 1 : 0; } -int tr_cryptoRandInt( int sup ) +int +tr_cryptoRandInt( int sup ) { int r; @@ -277,6 +278,21 @@ int tr_cryptoRandInt( int sup ) return ((int) (sup * (abs(r) / (INT_MAX + 1.0)))); } +int +tr_cryptoWeakRandInt( int sup ) +{ + static int init = 0; + assert( sup > 0 ); + + if( !init ) + { + srand( tr_date( ) ); + init = 1; + } + + return rand() % sup; +} + void tr_cryptoRandBuf ( unsigned char *buf, size_t len ) { RAND_pseudo_bytes ( buf, len ); diff --git a/libtransmission/crypto.h b/libtransmission/crypto.h index b6abda651..727feda06 100644 --- a/libtransmission/crypto.h +++ b/libtransmission/crypto.h @@ -79,6 +79,12 @@ void tr_sha1 ( uint8_t * setme, /** Returns a random number in the range of [0...n) */ int tr_cryptoRandInt ( int n ); +/** Returns a vaguely random number in the range of [0...n). + This is faster -- BUT WEAKER -- than tr_cryptoRandInt() + and should only be used when tr_cryptoRandInt() is too + slow, and NEVER in sensitive cases */ +int tr_cryptoWeakRandInt( int n ); + /** Fills a buffer with random bytes */ void tr_cryptoRandBuf ( unsigned char *buf, size_t len ); diff --git a/libtransmission/peer-mgr.c b/libtransmission/peer-mgr.c index 80b0c0fca..01325d972 100644 --- a/libtransmission/peer-mgr.c +++ b/libtransmission/peer-mgr.c @@ -130,27 +130,6 @@ struct tr_peerMgr *** **/ -/* The following is not a very high quality random number generator. It - * is mainly used as a simple stupid random number generator inside some - * functions below. Please don't use it for anything else unless you - * know what you're doing. Use tr_cryptoRandInt() instead. - */ - -static int -tr_stupidRandInt( int sup ) -{ - static int init = 0; - assert( sup > 0 ); - - if( !init ) - { - srand( tr_date() ); - init = 1; - } - - return rand() % sup; -} - static void managerLock( const struct tr_peerMgr * manager ) { @@ -626,7 +605,7 @@ getPreferredPieces( Torrent * t, setme->piece = piece; setme->priority = inf->pieces[piece].priority; setme->peerCount = 0; - setme->random = tr_stupidRandInt( INT_MAX ); + setme->random = tr_cryptoWeakRandInt( INT_MAX ); for( k=0; kdoUnchoke = 1; t->optimistic = c->peer; } diff --git a/libtransmission/peer-msgs.c b/libtransmission/peer-msgs.c index 6af9c4656..36343b9f8 100644 --- a/libtransmission/peer-msgs.c +++ b/libtransmission/peer-msgs.c @@ -1766,28 +1766,38 @@ static void sendBitfield( tr_peermsgs * msgs ) { struct evbuffer * out = msgs->outMessages; - const tr_piece_index_t pieceCount = msgs->torrent->info.pieceCount; tr_bitfield * field; tr_piece_index_t lazyPieces[LAZY_PIECE_COUNT]; - int i; - int lazyCount = 0; + size_t i; + size_t lazyCount = 0; field = tr_bitfieldDup( tr_cpPieceBitfield( msgs->torrent->completion ) ); if( tr_sessionIsLazyBitfieldEnabled( msgs->session ) ) { - const int maxLazyCount = MIN( LAZY_PIECE_COUNT, pieceCount ); + /** Lazy bitfields aren't a high priority or secure, so I'm opting for + speed over a truly random sample -- let's limit the pool size to + the first 1000 pieces so large torrents don't bog things down */ + size_t poolSize = MIN( msgs->torrent->info.pieceCount, 1000 ); + tr_piece_index_t * pool = tr_new( tr_piece_index_t, poolSize ); - while( lazyCount < maxLazyCount ) + /* build the pool */ + for( i=0; i 0 ) && ( lazyCount < LAZY_PIECE_COUNT ) ) { - const size_t pos = tr_cryptoRandInt ( pieceCount ); - if( !tr_bitfieldHas( field, pos ) ) /* already removed it */ - continue; - dbgmsg( msgs, "lazy bitfield #%d: piece %d of %d", - (int)(lazyCount+1), (int)pos, (int)pieceCount ); - tr_bitfieldRem( field, pos ); - lazyPieces[lazyCount++] = pos; + const int pos = tr_cryptoWeakRandInt( poolSize ); + const tr_piece_index_t piece = pool[pos]; + tr_bitfieldRem( field, piece ); + lazyPieces[lazyCount++] = piece; + pool[pos] = pool[--poolSize]; } + + /* cleanup */ + tr_free( pool ); } tr_peerIoWriteUint32( msgs->io, out, sizeof(uint8_t) + field->byteCount );