static int parseHandshake (tr_handshake * handshake, struct evbuffer * inbuf) { uint8_t name[HANDSHAKE_NAME_LEN]; uint8_t reserved[HANDSHAKE_FLAGS_LEN]; uint8_t hash[SHA_DIGEST_LENGTH]; tr_torrent * tor; uint8_t peer_id[PEER_ID_LEN]; dbgmsg (handshake, "payload: need %d, got %zu", HANDSHAKE_SIZE, evbuffer_get_length (inbuf)); if (evbuffer_get_length (inbuf) < HANDSHAKE_SIZE) return READ_LATER; /* confirm the protocol */ tr_peerIoReadBytes (handshake->io, inbuf, name, HANDSHAKE_NAME_LEN); if (memcmp (name, HANDSHAKE_NAME, HANDSHAKE_NAME_LEN)) return HANDSHAKE_ENCRYPTION_WRONG; /* read the reserved bytes */ tr_peerIoReadBytes (handshake->io, inbuf, reserved, HANDSHAKE_FLAGS_LEN); /* torrent hash */ tr_peerIoReadBytes (handshake->io, inbuf, hash, sizeof (hash)); assert (tr_peerIoHasTorrentHash (handshake->io)); if (!tr_torrentExists (handshake->session, hash) || memcmp (hash, tr_peerIoGetTorrentHash (handshake->io), SHA_DIGEST_LENGTH)) { dbgmsg (handshake, "peer returned the wrong hash. wtf?"); return HANDSHAKE_BAD_TORRENT; } /* peer_id */ tr_peerIoReadBytes (handshake->io, inbuf, peer_id, sizeof (peer_id)); tr_peerIoSetPeersId (handshake->io, peer_id); /* peer id */ handshake->havePeerID = true; dbgmsg (handshake, "peer-id is [%*.*s]", PEER_ID_LEN, PEER_ID_LEN, peer_id); tor = tr_torrentFindFromHash (handshake->session, hash); if (!memcmp (peer_id, tr_torrentGetPeerId(tor), PEER_ID_LEN)) { dbgmsg (handshake, "streuth! we've connected to ourselves."); return HANDSHAKE_PEER_IS_SELF; } /** *** Extensions **/ tr_peerIoEnableDHT (handshake->io, HANDSHAKE_HAS_DHT (reserved)); tr_peerIoEnableLTEP (handshake->io, HANDSHAKE_HAS_LTEP (reserved)); tr_peerIoEnableFEXT (handshake->io, HANDSHAKE_HAS_FASTEXT (reserved)); return HANDSHAKE_OK; }
static int readPeerId( tr_handshake * handshake, struct evbuffer * inbuf ) { tr_bool peerIsGood; char client[128]; tr_torrent * tor; const uint8_t * tor_peer_id; uint8_t peer_id[PEER_ID_LEN]; if( evbuffer_get_length( inbuf ) < PEER_ID_LEN ) return READ_LATER; /* peer id */ tr_peerIoReadBytes( handshake->io, inbuf, peer_id, PEER_ID_LEN ); tr_peerIoSetPeersId( handshake->io, peer_id ); handshake->havePeerID = TRUE; tr_clientForId( client, sizeof( client ), peer_id ); dbgmsg( handshake, "peer-id is [%s] ... isIncoming is %d", client, tr_peerIoIsIncoming( handshake->io ) ); /* if we've somehow connected to ourselves, don't keep the connection */ tor = tr_torrentFindFromHash( handshake->session, tr_peerIoGetTorrentHash( handshake->io ) ); tor_peer_id = tor && tor->peer_id ? tor->peer_id : tr_getPeerId( ); peerIsGood = memcmp( peer_id, tor_peer_id, PEER_ID_LEN ) != 0; dbgmsg( handshake, "isPeerGood == %d", (int)peerIsGood ); return tr_handshakeDone( handshake, peerIsGood ); }
static int readPeerId (tr_handshake * handshake, struct evbuffer * inbuf) { bool connected_to_self; char client[128]; uint8_t peer_id[PEER_ID_LEN]; tr_torrent * tor; if (evbuffer_get_length (inbuf) < PEER_ID_LEN) return READ_LATER; /* peer id */ tr_peerIoReadBytes (handshake->io, inbuf, peer_id, PEER_ID_LEN); tr_peerIoSetPeersId (handshake->io, peer_id); handshake->havePeerID = true; tr_clientForId (client, sizeof (client), peer_id); dbgmsg (handshake, "peer-id is [%s] ... isIncoming is %d", client, tr_peerIoIsIncoming (handshake->io)); /* if we've somehow connected to ourselves, don't keep the connection */ tor = tr_torrentFindFromHash (handshake->session, tr_peerIoGetTorrentHash (handshake->io)); connected_to_self = (tor != NULL) && !memcmp (peer_id, tr_torrentGetPeerId(tor), PEER_ID_LEN); return tr_handshakeDone (handshake, !connected_to_self); }
void tr_peerIoReadUint32( tr_peerIo * io, struct evbuffer * inbuf, uint32_t * setme ) { uint32_t tmp; tr_peerIoReadBytes( io, inbuf, &tmp, sizeof( uint32_t ) ); *setme = ntohl( tmp ); }
void tr_peerIoDrain( tr_peerIo * io, struct evbuffer * inbuf, size_t byteCount ) { void * buf = tr_sessionGetBuffer( io->session ); const size_t buflen = SESSION_BUFFER_SIZE; while( byteCount > 0 ) { const size_t thisPass = MIN( byteCount, buflen ); tr_peerIoReadBytes( io, inbuf, buf, thisPass ); byteCount -= thisPass; } tr_sessionReleaseBuffer( io->session ); }
static int readPadD( tr_handshake * handshake, struct evbuffer * inbuf ) { const size_t needlen = handshake->pad_d_len; uint8_t * tmp; dbgmsg( handshake, "pad d: need %zu, got %zu", needlen, evbuffer_get_length( inbuf ) ); if( evbuffer_get_length( inbuf ) < needlen ) return READ_LATER; tmp = tr_new( uint8_t, needlen ); tr_peerIoReadBytes( handshake->io, inbuf, tmp, needlen ); tr_free( tmp ); tr_peerIoSetEncryption( handshake->io, handshake->crypto_select ); setState( handshake, AWAITING_HANDSHAKE ); return READ_NOW; }
static int readPadC (tr_handshake * handshake, struct evbuffer * inbuf) { char * padc; uint16_t ia_len; const size_t needlen = handshake->pad_c_len + sizeof (uint16_t); if (evbuffer_get_length (inbuf) < needlen) return READ_LATER; /* read the throwaway padc */ padc = tr_new (char, handshake->pad_c_len); tr_peerIoReadBytes (handshake->io, inbuf, padc, handshake->pad_c_len); tr_free (padc); /* read ia_len */ tr_peerIoReadUint16 (handshake->io, inbuf, &ia_len); dbgmsg (handshake, "ia_len is %d", (int)ia_len); handshake->ia_len = ia_len; setState (handshake, AWAITING_IA); return READ_NOW; }
static int readCryptoProvide( tr_handshake * handshake, struct evbuffer * inbuf ) { /* HASH('req2', SKEY) xor HASH('req3', S), ENCRYPT(VC, crypto_provide, len(PadC)) */ int i; uint8_t vc_in[VC_LENGTH]; uint8_t req2[SHA_DIGEST_LENGTH]; uint8_t req3[SHA_DIGEST_LENGTH]; uint8_t obfuscatedTorrentHash[SHA_DIGEST_LENGTH]; uint16_t padc_len = 0; uint32_t crypto_provide = 0; const size_t needlen = SHA_DIGEST_LENGTH /* HASH('req1',s) */ + SHA_DIGEST_LENGTH /* HASH('req2', SKEY) xor HASH('req3', S) */ + VC_LENGTH + sizeof( crypto_provide ) + sizeof( padc_len ); tr_torrent * tor = NULL; if( evbuffer_get_length( inbuf ) < needlen ) return READ_LATER; /* TODO: confirm they sent HASH('req1',S) here? */ evbuffer_drain( inbuf, SHA_DIGEST_LENGTH ); /* This next piece is HASH('req2', SKEY) xor HASH('req3', S) ... * we can get the first half of that (the obufscatedTorrentHash) * by building the latter and xor'ing it with what the peer sent us */ dbgmsg( handshake, "reading obfuscated torrent hash..." ); evbuffer_remove( inbuf, req2, SHA_DIGEST_LENGTH ); tr_sha1( req3, "req3", 4, handshake->mySecret, KEY_LEN, NULL ); for( i = 0; i < SHA_DIGEST_LENGTH; ++i ) obfuscatedTorrentHash[i] = req2[i] ^ req3[i]; if(( tor = tr_torrentFindFromObfuscatedHash( handshake->session, obfuscatedTorrentHash ))) { const tr_bool clientIsSeed = tr_torrentIsSeed( tor ); const tr_bool peerIsSeed = tr_peerMgrPeerIsSeed( tor, tr_peerIoGetAddress( handshake->io, NULL ) ); dbgmsg( handshake, "got INCOMING connection's encrypted handshake for torrent [%s]", tr_torrentName( tor ) ); tr_peerIoSetTorrentHash( handshake->io, tor->info.hash ); if( clientIsSeed && peerIsSeed ) { dbgmsg( handshake, "another seed tried to reconnect to us!" ); return tr_handshakeDone( handshake, FALSE ); } } else { dbgmsg( handshake, "can't find that torrent..." ); return tr_handshakeDone( handshake, FALSE ); } /* next part: ENCRYPT(VC, crypto_provide, len(PadC), */ tr_cryptoDecryptInit( handshake->crypto ); tr_peerIoReadBytes( handshake->io, inbuf, vc_in, VC_LENGTH ); tr_peerIoReadUint32( handshake->io, inbuf, &crypto_provide ); handshake->crypto_provide = crypto_provide; dbgmsg( handshake, "crypto_provide is %d", (int)crypto_provide ); tr_peerIoReadUint16( handshake->io, inbuf, &padc_len ); dbgmsg( handshake, "padc is %d", (int)padc_len ); handshake->pad_c_len = padc_len; setState( handshake, AWAITING_PAD_C ); return READ_NOW; }
static int readHandshake( tr_handshake * handshake, struct evbuffer * inbuf ) { uint8_t pstrlen; uint8_t * pstr; uint8_t reserved[HANDSHAKE_FLAGS_LEN]; uint8_t hash[SHA_DIGEST_LENGTH]; dbgmsg( handshake, "payload: need %d, got %zu", INCOMING_HANDSHAKE_LEN, evbuffer_get_length( inbuf ) ); if( evbuffer_get_length( inbuf ) < INCOMING_HANDSHAKE_LEN ) return READ_LATER; handshake->haveReadAnythingFromPeer = TRUE; pstrlen = evbuffer_pullup( inbuf, 1 )[0]; /* peek, don't read. We may be handing inbuf to AWAITING_YA */ if( pstrlen == 19 ) /* unencrypted */ { tr_peerIoSetEncryption( handshake->io, PEER_ENCRYPTION_NONE ); if( handshake->encryptionMode == TR_ENCRYPTION_REQUIRED ) { dbgmsg( handshake, "peer is unencrypted, and we're disallowing that" ); return tr_handshakeDone( handshake, FALSE ); } } else /* encrypted or corrupt */ { tr_peerIoSetEncryption( handshake->io, PEER_ENCRYPTION_RC4 ); if( tr_peerIoIsIncoming( handshake->io ) ) { dbgmsg( handshake, "I think peer is sending us an encrypted handshake..." ); setState( handshake, AWAITING_YA ); return READ_NOW; } tr_cryptoDecrypt( handshake->crypto, 1, &pstrlen, &pstrlen ); if( pstrlen != 19 ) { dbgmsg( handshake, "I think peer has sent us a corrupt handshake..." ); return tr_handshakeDone( handshake, FALSE ); } } evbuffer_drain( inbuf, 1 ); /* pstr (BitTorrent) */ pstr = tr_new( uint8_t, pstrlen + 1 ); tr_peerIoReadBytes( handshake->io, inbuf, pstr, pstrlen ); pstr[pstrlen] = '\0'; if( strcmp( (char*)pstr, "BitTorrent protocol" ) ) { tr_free( pstr ); return tr_handshakeDone( handshake, FALSE ); } tr_free( pstr ); /* reserved bytes */ tr_peerIoReadBytes( handshake->io, inbuf, reserved, sizeof( reserved ) ); /** *** Extensions **/ tr_peerIoEnableLTEP( handshake->io, HANDSHAKE_HAS_LTEP( reserved ) ); tr_peerIoEnableFEXT( handshake->io, HANDSHAKE_HAS_FASTEXT( reserved ) ); tr_peerIoEnableDHT( handshake->io, HANDSHAKE_HAS_DHT( reserved ) ); /* torrent hash */ tr_peerIoReadBytes( handshake->io, inbuf, hash, sizeof( hash ) ); if( tr_peerIoIsIncoming( handshake->io ) ) { if( !tr_torrentExists( handshake->session, hash ) ) { dbgmsg( handshake, "peer is trying to connect to us for a torrent we don't have." ); return tr_handshakeDone( handshake, FALSE ); } else { assert( !tr_peerIoHasTorrentHash( handshake->io ) ); tr_peerIoSetTorrentHash( handshake->io, hash ); } } else /* outgoing */ { assert( tr_peerIoHasTorrentHash( handshake->io ) ); if( memcmp( hash, tr_peerIoGetTorrentHash( handshake->io ), SHA_DIGEST_LENGTH ) ) { dbgmsg( handshake, "peer returned the wrong hash. wtf?" ); return tr_handshakeDone( handshake, FALSE ); } } /** *** If it's an incoming message, we need to send a response handshake **/ if( !handshake->haveSentBitTorrentHandshake ) { uint8_t msg[HANDSHAKE_SIZE]; buildHandshakeMessage( handshake, msg ); tr_peerIoWriteBytes( handshake->io, msg, sizeof( msg ), FALSE ); handshake->haveSentBitTorrentHandshake = 1; } setReadState( handshake, AWAITING_PEER_ID ); return READ_NOW; }