mirror of
https://github.com/transmission/transmission.git
synced 2026-04-20 00:40:03 +01:00
Revert [619]
This commit is contained in:
564
libtransmission/peerparse.h
Normal file
564
libtransmission/peerparse.h
Normal file
@@ -0,0 +1,564 @@
|
||||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2005-2006 Transmission authors and contributors
|
||||
*
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
* This file handles all incoming messages
|
||||
**********************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
* parseChoke
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
static inline int parseChoke( tr_torrent_t * tor, tr_peer_t * peer,
|
||||
int len, int choking )
|
||||
{
|
||||
tr_request_t * r;
|
||||
int i;
|
||||
|
||||
if( len != 1 )
|
||||
{
|
||||
peer_dbg( "GET %schoke, invalid", choking ? "" : "un" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
peer_dbg( "GET %schoke", choking ? "" : "un" );
|
||||
|
||||
peer->peerChoking = choking;
|
||||
|
||||
if( choking )
|
||||
{
|
||||
/* Discard all pending requests */
|
||||
for( i = 0; i < peer->inRequestCount; i++ )
|
||||
{
|
||||
r = &peer->inRequests[i];
|
||||
tr_cpDownloaderRem( tor->completion, tr_block(r->index,r->begin) );
|
||||
}
|
||||
peer->inRequestCount = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* parseInterested
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
static inline int parseInterested( tr_peer_t * peer, int len,
|
||||
int interested )
|
||||
{
|
||||
if( len != 1 )
|
||||
{
|
||||
peer_dbg( "GET %sinterested, invalid", interested ? "" : "un" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
peer_dbg( "GET %sinterested", interested ? "" : "un" );
|
||||
|
||||
peer->peerInterested = interested;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* parseHave
|
||||
***********************************************************************
|
||||
*
|
||||
**********************************************************************/
|
||||
static inline int parseHave( tr_torrent_t * tor, tr_peer_t * peer,
|
||||
uint8_t * p, int len )
|
||||
{
|
||||
uint32_t piece;
|
||||
|
||||
if( len != 5 )
|
||||
{
|
||||
peer_dbg( "GET have, invalid" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
TR_NTOHL( p, piece );
|
||||
|
||||
peer_dbg( "GET have %d", piece );
|
||||
|
||||
if( !peer->bitfield )
|
||||
{
|
||||
peer->bitfield = calloc( ( tor->info.pieceCount + 7 ) / 8, 1 );
|
||||
}
|
||||
tr_bitfieldAdd( peer->bitfield, piece );
|
||||
updateInterest( tor, peer );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int parseBitfield( tr_torrent_t * tor, tr_peer_t * peer,
|
||||
uint8_t * p, int len )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
peer_dbg( "GET bitfield, ok" );
|
||||
|
||||
if( !peer->bitfield )
|
||||
{
|
||||
peer->bitfield = malloc( bitfieldSize );
|
||||
}
|
||||
memcpy( peer->bitfield, p, bitfieldSize );
|
||||
updateInterest( tor, peer );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int parseRequest( tr_peer_t * peer, uint8_t * p, int len )
|
||||
{
|
||||
int index, begin, length;
|
||||
tr_request_t * r;
|
||||
|
||||
if( len != 13 )
|
||||
{
|
||||
peer_dbg( "GET request, invalid" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( peer->amChoking )
|
||||
{
|
||||
/* Didn't he get it? */
|
||||
sendChoke( peer, 1 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_err( "Too many requests" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = &peer->outRequests[peer->outRequestCount];
|
||||
r->index = index;
|
||||
r->begin = begin;
|
||||
r->length = length;
|
||||
|
||||
(peer->outRequestCount)++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int parsePiece( tr_torrent_t * tor, tr_peer_t * peer,
|
||||
uint8_t * p, int len )
|
||||
{
|
||||
int index, begin, block, i, j;
|
||||
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" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
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];
|
||||
tr_cpDownloaderRem( tor->completion,
|
||||
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 );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( len - 9 != r->length )
|
||||
{
|
||||
peer_dbg( "wrong size (expecting %d)", r->length );
|
||||
return 1;
|
||||
}
|
||||
|
||||
tor->downloaded += r->length;
|
||||
|
||||
block = tr_block( r->index, r->begin );
|
||||
if( tr_cpBlockIsComplete( tor->completion, block ) )
|
||||
{
|
||||
peer_dbg( "have this block already" );
|
||||
(peer->inRequestCount)--;
|
||||
memmove( &peer->inRequests[0], &peer->inRequests[1],
|
||||
peer->inRequestCount * sizeof( tr_request_t ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set blame/credit for this piece */
|
||||
if( !peer->blamefield )
|
||||
{
|
||||
peer->blamefield = calloc( ( tor->info.pieceCount + 7 ) / 8, 1 );
|
||||
}
|
||||
tr_bitfieldAdd( peer->blamefield, index );
|
||||
|
||||
tr_cpBlockAdd( tor->completion, block );
|
||||
tr_ioWrite( tor->io, index, begin, len - 9, &p[8] );
|
||||
tr_cpDownloaderRem( tor->completion, block );
|
||||
|
||||
sendCancel( tor, block );
|
||||
|
||||
if( tr_cpPieceIsComplete( tor->completion, 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 ) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int parseCancel( tr_peer_t * peer, uint8_t * p, int len )
|
||||
{
|
||||
int index, begin, length;
|
||||
int i;
|
||||
tr_request_t * r;
|
||||
|
||||
if( len != 13 )
|
||||
{
|
||||
peer_dbg( "GET cancel, invalid" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int parsePort( tr_peer_t * peer, uint8_t * p, int len )
|
||||
{
|
||||
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 ) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int parseMessage( tr_torrent_t * tor, tr_peer_t * peer,
|
||||
uint8_t * p, int len )
|
||||
{
|
||||
char id;
|
||||
|
||||
/* Type of the message */
|
||||
id = *(p++);
|
||||
|
||||
switch( id )
|
||||
{
|
||||
case 0:
|
||||
return parseChoke( tor, peer, len, 1 );
|
||||
case 1:
|
||||
return parseChoke( tor, peer, len, 0 );
|
||||
case 2:
|
||||
return parseInterested( peer, len, 1 );
|
||||
case 3:
|
||||
return parseInterested( peer, len, 0 );
|
||||
case 4:
|
||||
return parseHave( tor, peer, p, len );
|
||||
case 5:
|
||||
return parseBitfield( tor, peer, p, len );
|
||||
case 6:
|
||||
return parseRequest( peer, p, len );
|
||||
case 7:
|
||||
return parsePiece( tor, peer, p, len );
|
||||
case 8:
|
||||
return parseCancel( peer, p, len );
|
||||
case 9:
|
||||
return parsePort( peer, p, len );
|
||||
}
|
||||
|
||||
peer_dbg( "Unknown message '%d'", id );
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int parseBufHeader( tr_peer_t * peer )
|
||||
{
|
||||
uint8_t * p = peer->buf;
|
||||
|
||||
if( 4 > peer->pos )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if( memcmp( &p[4], "Torrent protocol", 16 ) )
|
||||
{
|
||||
peer_dbg( "GET handshake, invalid" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t * parseBufHash( tr_peer_t * peer )
|
||||
{
|
||||
if( 48 > peer->pos )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return peer->buf + 28;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int parseBuf( tr_torrent_t * tor, tr_peer_t * peer )
|
||||
{
|
||||
tr_info_t * inf = &tor->info;
|
||||
|
||||
int i;
|
||||
int len;
|
||||
uint8_t * p = peer->buf;
|
||||
uint8_t * end = &p[peer->pos];
|
||||
|
||||
if( peer->banned )
|
||||
{
|
||||
/* Don't even parse, we only stay connected */
|
||||
peer->pos = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
while( peer->pos >= 4 )
|
||||
{
|
||||
if( peer->status & PEER_STATUS_HANDSHAKE )
|
||||
{
|
||||
char * client;
|
||||
|
||||
if( parseBufHeader( peer ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( peer->pos < 68 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
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 should never happen. Drop that peer */
|
||||
peer_dbg( "message too large (%d bytes)", len );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( !len )
|
||||
{
|
||||
/* keep-alive */
|
||||
peer_dbg( "GET keep-alive" );
|
||||
peer->pos -= 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( &p[len] > end )
|
||||
{
|
||||
/* We do not have the entire message */
|
||||
p -= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Remaining data after this message */
|
||||
peer->pos -= 4 + len;
|
||||
|
||||
if( parseMessage( tor, peer, p, len ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
p += len;
|
||||
}
|
||||
|
||||
memmove( peer->buf, p, peer->pos );
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user