828 lines
24 KiB
C
828 lines
24 KiB
C
/******************************************************************************
|
|
* Copyright (c) 2005 Eric Petit
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*****************************************************************************/
|
|
|
|
static void updateInterest( tr_torrent_t * tor, tr_peer_t * peer );
|
|
|
|
/***********************************************************************
|
|
* peerInit
|
|
***********************************************************************
|
|
* Returns NULL if we reached the maximum authorized number of peers.
|
|
* Otherwise, allocates a new tr_peer_t, add it to the peers list and
|
|
* returns a pointer to it.
|
|
**********************************************************************/
|
|
static tr_peer_t * peerInit( tr_torrent_t * tor )
|
|
{
|
|
tr_peer_t * peer;
|
|
|
|
if( tor->peerCount >= TR_MAX_PEER_COUNT )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
peer = calloc( sizeof( tr_peer_t ), 1 );
|
|
peer->amChoking = 1;
|
|
peer->peerChoking = 1;
|
|
peer->date = tr_date();
|
|
peer->keepAlive = peer->date;
|
|
|
|
tor->peers[tor->peerCount++] = peer;
|
|
return peer;
|
|
}
|
|
|
|
static int peerCmp( tr_peer_t * peer1, tr_peer_t * peer2 )
|
|
{
|
|
/* Wait until we got the peers' ids */
|
|
if( peer1->status < PEER_STATUS_CONNECTED ||
|
|
peer2->status < PEER_STATUS_CONNECTED )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return memcmp( peer1->id, peer2->id, 20 );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* addWithAddr
|
|
***********************************************************************
|
|
* Does nothing if we already have a peer matching 'addr' and 'port'.
|
|
* Otherwise adds such a new peer.
|
|
**********************************************************************/
|
|
static void addWithAddr( tr_torrent_t * tor, struct in_addr addr,
|
|
in_port_t port )
|
|
{
|
|
int i;
|
|
tr_peer_t * peer;
|
|
|
|
for( i = 0; i < tor->peerCount; i++ )
|
|
{
|
|
peer = tor->peers[i];
|
|
if( peer->addr.s_addr == addr.s_addr &&
|
|
peer->port == port )
|
|
{
|
|
/* We are already connected to this peer */
|
|
return;
|
|
}
|
|
}
|
|
|
|
if( !( peer = peerInit( tor ) ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
peer->addr = addr;
|
|
peer->port = port;
|
|
peer->status = PEER_STATUS_IDLE;
|
|
}
|
|
|
|
static int checkPeer( tr_torrent_t * tor, int i )
|
|
{
|
|
tr_peer_t * peer = tor->peers[i];
|
|
|
|
if( peer->status < PEER_STATUS_CONNECTED &&
|
|
tr_date() > peer->date + 8000 )
|
|
{
|
|
/* If it has been too long, don't wait for the socket
|
|
to timeout - forget about it now */
|
|
peer_dbg( "connection timeout" );
|
|
return 1;
|
|
}
|
|
|
|
/* Drop peers who haven't even sent a keep-alive within the
|
|
last 3 minutes */
|
|
if( tr_date() > peer->date + 180000 )
|
|
{
|
|
peer_dbg( "read timeout" );
|
|
return 1;
|
|
}
|
|
|
|
/* Drop peers which are supposed to upload but actually
|
|
haven't sent anything within the last minute */
|
|
if( peer->inRequestCount && tr_date() > peer->date + 60000 )
|
|
{
|
|
peer_dbg( "bad uploader" );
|
|
return 1;
|
|
}
|
|
|
|
#if 0
|
|
/* Choke unchoked peers we are not sending anything to */
|
|
if( !peer->amChoking && tr_date() > peer->outDate + 10000 )
|
|
{
|
|
peer_dbg( "not worth the unchoke" );
|
|
if( sendChoke( peer, 1 ) )
|
|
{
|
|
goto dropPeer;
|
|
}
|
|
peer->outSlow = 1;
|
|
tr_uploadChoked( tor->upload );
|
|
}
|
|
#endif
|
|
|
|
if( peer->status & PEER_STATUS_CONNECTED )
|
|
{
|
|
/* Send keep-alive every 2 minutes */
|
|
if( tr_date() > peer->keepAlive + 120000 )
|
|
{
|
|
sendKeepAlive( peer );
|
|
peer->keepAlive = tr_date();
|
|
}
|
|
|
|
/* Choke or unchoke some people */
|
|
/* TODO: prefer people who upload to us */
|
|
if( !peer->amChoking && !peer->peerInterested )
|
|
{
|
|
/* He doesn't need us */
|
|
sendChoke( peer, 1 );
|
|
tr_uploadChoked( tor->upload );
|
|
}
|
|
if( peer->amChoking && peer->peerInterested &&
|
|
!peer->outSlow && tr_uploadCanUnchoke( tor->upload ) )
|
|
{
|
|
sendChoke( peer, 0 );
|
|
tr_uploadUnchoked( tor->upload );
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int parseMessage( tr_torrent_t * tor, tr_peer_t * peer,
|
|
int newBytes )
|
|
{
|
|
tr_info_t * inf = &tor->info;
|
|
|
|
int i, j;
|
|
int len;
|
|
char id;
|
|
uint8_t * p = peer->buf;
|
|
uint8_t * end = &p[peer->pos];
|
|
|
|
for( ;; )
|
|
{
|
|
if( peer->pos < 4 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if( peer->status & PEER_STATUS_HANDSHAKE )
|
|
{
|
|
char * client;
|
|
|
|
if( p[0] != 19 || memcmp( &p[1], "Bit", 3 ) )
|
|
{
|
|
/* Don't wait until we get 68 bytes, this is wrong
|
|
already */
|
|
peer_dbg( "GET handshake, invalid" );
|
|
tr_netSend( peer->socket, (uint8_t *) "Nice try...\r\n", 13 );
|
|
return 1;
|
|
}
|
|
|
|
if( peer->pos < 68 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if( memcmp( &p[4], "Torrent protocol", 16 ) )
|
|
{
|
|
peer_dbg( "GET handshake, invalid" );
|
|
return 1;
|
|
}
|
|
|
|
if( memcmp( &p[28], inf->hash, 20 ) )
|
|
{
|
|
peer_dbg( "GET handshake, wrong torrent hash" );
|
|
return 1;
|
|
}
|
|
|
|
if( !memcmp( &p[48], tor->id, 20 ) )
|
|
{
|
|
/* We are connected to ourselves... */
|
|
peer_dbg( "GET handshake, that is us" );
|
|
return 1;
|
|
}
|
|
|
|
peer->status = PEER_STATUS_CONNECTED;
|
|
memcpy( peer->id, &p[48], 20 );
|
|
p += 68;
|
|
peer->pos -= 68;
|
|
|
|
for( i = 0; i < tor->peerCount; i++ )
|
|
{
|
|
if( tor->peers[i] == peer )
|
|
{
|
|
continue;
|
|
}
|
|
if( !peerCmp( peer, tor->peers[i] ) )
|
|
{
|
|
peer_dbg( "GET handshake, duplicate" );
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
client = tr_clientForId( (uint8_t *) peer->id );
|
|
peer_dbg( "GET handshake, ok (%s)", client );
|
|
free( client );
|
|
|
|
sendBitfield( tor, peer );
|
|
|
|
continue;
|
|
}
|
|
|
|
/* Get payload size */
|
|
TR_NTOHL( p, len );
|
|
p += 4;
|
|
|
|
if( len > 9 + tor->blockSize )
|
|
{
|
|
/* This shouldn't happen. Forget about that peer */
|
|
peer_dbg( "message too large" );
|
|
return 1;
|
|
}
|
|
|
|
if( !len )
|
|
{
|
|
/* keep-alive */
|
|
peer_dbg( "GET keep-alive" );
|
|
peer->pos -= 4;
|
|
continue;
|
|
}
|
|
|
|
/* That's a piece coming */
|
|
if( p < end && *p == 7 )
|
|
{
|
|
/* XXX */
|
|
tor->downloaded[9] += newBytes;
|
|
peer->inTotal += newBytes;
|
|
newBytes = 0;
|
|
}
|
|
|
|
if( &p[len] > end )
|
|
{
|
|
/* We do not have the entire message */
|
|
p -= 4;
|
|
break;
|
|
}
|
|
|
|
/* Remaining data after this message */
|
|
peer->pos -= 4 + len;
|
|
|
|
/* Type of the message */
|
|
id = *(p++);
|
|
|
|
switch( id )
|
|
{
|
|
case 0: /* choke */
|
|
{
|
|
tr_request_t * r;
|
|
|
|
if( len != 1 )
|
|
{
|
|
peer_dbg( "GET choke, invalid" );
|
|
return 1;
|
|
}
|
|
|
|
peer_dbg( "GET choke" );
|
|
peer->peerChoking = 1;
|
|
|
|
for( i = 0; i < peer->inRequestCount; i++ )
|
|
{
|
|
r = &peer->inRequests[i];
|
|
if( tor->blockHave[tr_block(r->index,r->begin)] > 0 )
|
|
{
|
|
tor->blockHave[tr_block(r->index,r->begin)]--;
|
|
}
|
|
}
|
|
peer->inRequestCount = 0;
|
|
|
|
break;
|
|
}
|
|
case 1: /* unchoke */
|
|
if( len != 1 )
|
|
{
|
|
peer_dbg( "GET unchoke, invalid" );
|
|
return 1;
|
|
}
|
|
peer_dbg( "GET unchoke" );
|
|
peer->peerChoking = 0;
|
|
break;
|
|
case 2: /* interested */
|
|
if( len != 1 )
|
|
{
|
|
peer_dbg( "GET interested, invalid" );
|
|
return 1;
|
|
}
|
|
peer_dbg( "GET interested" );
|
|
peer->peerInterested = 1;
|
|
break;
|
|
case 3: /* uninterested */
|
|
if( len != 1 )
|
|
{
|
|
peer_dbg( "GET uninterested, invalid" );
|
|
return 1;
|
|
}
|
|
peer_dbg( "GET uninterested" );
|
|
peer->peerInterested = 0;
|
|
break;
|
|
case 4: /* have */
|
|
{
|
|
uint32_t piece;
|
|
if( len != 5 )
|
|
{
|
|
peer_dbg( "GET have, invalid" );
|
|
return 1;
|
|
}
|
|
TR_NTOHL( p, piece );
|
|
if( !peer->bitfield )
|
|
{
|
|
peer->bitfield = calloc( ( inf->pieceCount + 7 ) / 8, 1 );
|
|
}
|
|
tr_bitfieldAdd( peer->bitfield, piece );
|
|
|
|
updateInterest( tor, peer );
|
|
|
|
peer_dbg( "GET have %d", piece );
|
|
break;
|
|
}
|
|
case 5: /* bitfield */
|
|
{
|
|
int bitfieldSize;
|
|
|
|
bitfieldSize = ( inf->pieceCount + 7 ) / 8;
|
|
|
|
if( len != 1 + bitfieldSize )
|
|
{
|
|
peer_dbg( "GET bitfield, wrong size" );
|
|
return 1;
|
|
}
|
|
|
|
/* Make sure the spare bits are unset */
|
|
if( ( inf->pieceCount & 0x7 ) )
|
|
{
|
|
uint8_t lastByte;
|
|
|
|
lastByte = p[bitfieldSize-1];
|
|
lastByte <<= inf->pieceCount & 0x7;
|
|
lastByte &= 0xFF;
|
|
|
|
if( lastByte )
|
|
{
|
|
peer_dbg( "GET bitfield, spare bits set" );
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if( !peer->bitfield )
|
|
{
|
|
peer->bitfield = malloc( bitfieldSize );
|
|
}
|
|
memcpy( peer->bitfield, p, bitfieldSize );
|
|
|
|
updateInterest( tor, peer );
|
|
|
|
peer_dbg( "GET bitfield, ok" );
|
|
break;
|
|
}
|
|
case 6: /* request */
|
|
{
|
|
int index, begin, length;
|
|
|
|
if( peer->amChoking )
|
|
{
|
|
/* Didn't he get it? */
|
|
sendChoke( peer, 1 );
|
|
break;
|
|
}
|
|
|
|
TR_NTOHL( p, index );
|
|
TR_NTOHL( &p[4], begin );
|
|
TR_NTOHL( &p[8], length );
|
|
|
|
peer_dbg( "GET request %d/%d (%d bytes)",
|
|
index, begin, length );
|
|
|
|
/* TODO sanity checks (do we have the piece, etc) */
|
|
|
|
if( length > 16384 )
|
|
{
|
|
/* Sorry mate */
|
|
return 1;
|
|
}
|
|
|
|
if( peer->outRequestCount < MAX_REQUEST_COUNT )
|
|
{
|
|
tr_request_t * r;
|
|
|
|
r = &peer->outRequests[peer->outRequestCount];
|
|
r->index = index;
|
|
r->begin = begin;
|
|
r->length = length;
|
|
|
|
(peer->outRequestCount)++;
|
|
}
|
|
else
|
|
{
|
|
tr_err( "Too many requests" );
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
case 7: /* piece */
|
|
{
|
|
int index, begin;
|
|
int block;
|
|
tr_request_t * r;
|
|
|
|
TR_NTOHL( p, index );
|
|
TR_NTOHL( &p[4], begin );
|
|
|
|
peer_dbg( "GET piece %d/%d (%d bytes)",
|
|
index, begin, len - 9 );
|
|
|
|
if( peer->inRequestCount < 1 )
|
|
{
|
|
/* Our "cancel" was probably late */
|
|
peer_dbg( "not expecting a block" );
|
|
break;
|
|
}
|
|
|
|
r = &peer->inRequests[0];
|
|
if( index != r->index || begin != r->begin )
|
|
{
|
|
int suckyClient;
|
|
|
|
/* Either our "cancel" was late, or this is a sucky
|
|
client that cannot deal with multiple requests */
|
|
suckyClient = 0;
|
|
for( i = 0; i < peer->inRequestCount; i++ )
|
|
{
|
|
r = &peer->inRequests[i];
|
|
|
|
if( index != r->index || begin != r->begin )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* Sucky client, he dropped the previous requests */
|
|
peer_dbg( "block was expected later" );
|
|
for( j = 0; j < i; j++ )
|
|
{
|
|
r = &peer->inRequests[j];
|
|
if( tor->blockHave[tr_block(r->index,r->begin)] > 0 )
|
|
{
|
|
tor->blockHave[tr_block(r->index,r->begin)]--;
|
|
}
|
|
}
|
|
suckyClient = 1;
|
|
peer->inRequestCount -= i;
|
|
memmove( &peer->inRequests[0], &peer->inRequests[i],
|
|
peer->inRequestCount * sizeof( tr_request_t ) );
|
|
r = &peer->inRequests[0];
|
|
break;
|
|
}
|
|
|
|
if( !suckyClient )
|
|
{
|
|
r = &peer->inRequests[0];
|
|
peer_dbg( "wrong block (expecting %d/%d)",
|
|
r->index, r->begin );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( len - 9 != r->length )
|
|
{
|
|
peer_dbg( "wrong size (expecting %d)", r->length );
|
|
return 1;
|
|
}
|
|
|
|
block = tr_block( r->index, r->begin );
|
|
if( tor->blockHave[block] < 0 )
|
|
{
|
|
peer_dbg( "have this block already" );
|
|
(peer->inRequestCount)--;
|
|
memmove( &peer->inRequests[0], &peer->inRequests[1],
|
|
peer->inRequestCount * sizeof( tr_request_t ) );
|
|
break;
|
|
}
|
|
|
|
tor->blockHave[block] = -1;
|
|
tor->blockHaveCount += 1;
|
|
tr_ioWrite( tor->io, index, begin, len - 9, &p[8] );
|
|
|
|
sendCancel( tor, block );
|
|
|
|
if( tr_bitfieldHas( tor->bitfield, index ) )
|
|
{
|
|
tr_peer_t * otherPeer;
|
|
|
|
for( i = 0; i < tor->peerCount; i++ )
|
|
{
|
|
otherPeer = tor->peers[i];
|
|
|
|
if( otherPeer->status < PEER_STATUS_CONNECTED )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
sendHave( otherPeer, index );
|
|
updateInterest( tor, otherPeer );
|
|
}
|
|
}
|
|
|
|
(peer->inRequestCount)--;
|
|
memmove( &peer->inRequests[0], &peer->inRequests[1],
|
|
peer->inRequestCount * sizeof( tr_request_t ) );
|
|
break;
|
|
}
|
|
case 8: /* cancel */
|
|
{
|
|
int index, begin, length;
|
|
int i;
|
|
tr_request_t * r;
|
|
|
|
TR_NTOHL( p, index );
|
|
TR_NTOHL( &p[4], begin );
|
|
TR_NTOHL( &p[8], length );
|
|
|
|
peer_dbg( "GET cancel %d/%d (%d bytes)",
|
|
index, begin, length );
|
|
|
|
for( i = 0; i < peer->outRequestCount; i++ )
|
|
{
|
|
r = &peer->outRequests[i];
|
|
if( r->index == index && r->begin == begin &&
|
|
r->length == length )
|
|
{
|
|
(peer->outRequestCount)--;
|
|
memmove( &r[0], &r[1], sizeof( tr_request_t ) *
|
|
( peer->outRequestCount - i ) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 9:
|
|
{
|
|
in_port_t port;
|
|
|
|
if( len != 3 )
|
|
{
|
|
peer_dbg( "GET port, invalid" );
|
|
return 1;
|
|
}
|
|
|
|
port = *( (in_port_t *) p );
|
|
peer_dbg( "GET port %d", ntohs( port ) );
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
peer_dbg( "Unknown message '%d'", id );
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
p += len - 1;
|
|
}
|
|
|
|
memmove( peer->buf, p, peer->pos );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* isInteresting
|
|
***********************************************************************
|
|
* Returns 1 if 'peer' has at least one piece that we haven't completed,
|
|
* or 0 otherwise.
|
|
**********************************************************************/
|
|
static int isInteresting( tr_torrent_t * tor, tr_peer_t * peer )
|
|
{
|
|
tr_info_t * inf = &tor->info;
|
|
|
|
int i;
|
|
int bitfieldSize = ( inf->pieceCount + 7 ) / 8;
|
|
|
|
if( !peer->bitfield )
|
|
{
|
|
/* We don't know what this peer has */
|
|
return 0;
|
|
}
|
|
|
|
for( i = 0; i < bitfieldSize; i++ )
|
|
{
|
|
if( ( peer->bitfield[i] & ~(tor->bitfield[i]) ) & 0xFF )
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
static void updateInterest( tr_torrent_t * tor, tr_peer_t * peer )
|
|
{
|
|
int interested = isInteresting( tor, peer );
|
|
|
|
if( interested && !peer->amInterested )
|
|
{
|
|
sendInterest( peer, 1 );
|
|
}
|
|
if( !interested && peer->amInterested )
|
|
{
|
|
sendInterest( peer, 0 );
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
* chooseBlock
|
|
***********************************************************************
|
|
* At this point, we know the peer has at least one block we have an
|
|
* interest in. If he has more than one, we choose which one we are
|
|
* going to ask first.
|
|
* Our main goal is to complete pieces, so we look the pieces which are
|
|
* missing less blocks.
|
|
**********************************************************************/
|
|
static int chooseBlock( tr_torrent_t * tor, tr_peer_t * peer )
|
|
{
|
|
tr_info_t * inf = &tor->info;
|
|
|
|
int i, j;
|
|
int startBlock, endBlock, countBlocks;
|
|
int missingBlocks, minMissing;
|
|
int poolSize, * pool;
|
|
int block, minDownloading;
|
|
|
|
/* Choose a piece */
|
|
pool = malloc( inf->pieceCount * sizeof( int ) );
|
|
poolSize = 0;
|
|
minMissing = tor->blockCount + 1;
|
|
for( i = 0; i < inf->pieceCount; i++ )
|
|
{
|
|
if( !tr_bitfieldHas( peer->bitfield, i ) )
|
|
{
|
|
/* The peer doesn't have this piece */
|
|
continue;
|
|
}
|
|
if( tr_bitfieldHas( tor->bitfield, i ) )
|
|
{
|
|
/* We already have it */
|
|
continue;
|
|
}
|
|
|
|
/* Count how many blocks from this piece are missing */
|
|
startBlock = tr_pieceStartBlock( i );
|
|
countBlocks = tr_pieceCountBlocks( i );
|
|
endBlock = startBlock + countBlocks;
|
|
missingBlocks = countBlocks;
|
|
for( j = startBlock; j < endBlock; j++ )
|
|
{
|
|
/* TODO: optimize */
|
|
if( tor->blockHave[j] )
|
|
{
|
|
missingBlocks--;
|
|
}
|
|
if( missingBlocks > minMissing )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( missingBlocks < 1 )
|
|
{
|
|
/* We are already downloading all blocks */
|
|
continue;
|
|
}
|
|
|
|
/* We are interested in this piece, remember it */
|
|
if( missingBlocks < minMissing )
|
|
{
|
|
minMissing = missingBlocks;
|
|
poolSize = 0;
|
|
}
|
|
if( missingBlocks <= minMissing )
|
|
{
|
|
pool[poolSize++] = i;
|
|
}
|
|
}
|
|
|
|
if( poolSize )
|
|
{
|
|
/* All pieces in 'pool' have 'minMissing' missing blocks. Find
|
|
the rarest ones. */
|
|
uint8_t * bitfield;
|
|
int piece;
|
|
int min, foo, j;
|
|
int * pool2;
|
|
int pool2Size;
|
|
|
|
pool2 = malloc( poolSize * sizeof( int ) );
|
|
pool2Size = 0;
|
|
min = TR_MAX_PEER_COUNT + 1;
|
|
for( i = 0; i < poolSize; i++ )
|
|
{
|
|
foo = 0;
|
|
for( j = 0; j < tor->peerCount; j++ )
|
|
{
|
|
bitfield = tor->peers[j]->bitfield;
|
|
if( bitfield && tr_bitfieldHas( bitfield, pool[i] ) )
|
|
{
|
|
foo++;
|
|
}
|
|
}
|
|
if( foo < min )
|
|
{
|
|
min = foo;
|
|
pool2Size = 0;
|
|
}
|
|
if( foo <= min )
|
|
{
|
|
pool2[pool2Size++] = pool[i];
|
|
}
|
|
}
|
|
free( pool );
|
|
|
|
if( pool2Size < 1 )
|
|
{
|
|
/* Shouldn't happen */
|
|
free( pool2 );
|
|
return -1;
|
|
}
|
|
|
|
/* All pieces in pool2 have the same number of missing blocks,
|
|
and are availabme from the same number of peers. Pick a
|
|
random one */
|
|
piece = pool2[tr_rand(pool2Size)];
|
|
free( pool2 );
|
|
|
|
/* Pick a block in this piece */
|
|
startBlock = tr_pieceStartBlock( piece );
|
|
endBlock = startBlock + tr_pieceCountBlocks( piece );
|
|
for( i = startBlock; i < endBlock; i++ )
|
|
{
|
|
if( !tor->blockHave[i] )
|
|
{
|
|
block = i;
|
|
goto check;
|
|
}
|
|
}
|
|
|
|
/* Shouldn't happen */
|
|
return -1;
|
|
}
|
|
|
|
free( pool );
|
|
|
|
/* "End game" mode */
|
|
block = -1;
|
|
minDownloading = TR_MAX_PEER_COUNT + 1;
|
|
for( i = 0; i < tor->blockCount; i++ )
|
|
{
|
|
/* TODO: optimize */
|
|
if( tor->blockHave[i] > 0 && tor->blockHave[i] < minDownloading )
|
|
{
|
|
block = i;
|
|
minDownloading = tor->blockHave[i];
|
|
}
|
|
}
|
|
|
|
if( block < 0 )
|
|
{
|
|
/* Shouldn't happen */
|
|
return -1;
|
|
}
|
|
|
|
check:
|
|
for( i = 0; i < peer->inRequestCount; i++ )
|
|
{
|
|
tr_request_t * r;
|
|
r = &peer->inRequests[i];
|
|
if( tr_block( r->index, r->begin ) == block )
|
|
{
|
|
/* We are already asking this peer for this block */
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return block;
|
|
}
|