static int readCryptoSelect( tr_handshake * handshake, struct evbuffer * inbuf ) { uint32_t crypto_select; uint16_t pad_d_len; const size_t needlen = sizeof( uint32_t ) + sizeof( uint16_t ); if( evbuffer_get_length( inbuf ) < needlen ) return READ_LATER; tr_peerIoReadUint32( handshake->io, inbuf, &crypto_select ); handshake->crypto_select = crypto_select; dbgmsg( handshake, "crypto select is %d", (int)crypto_select ); if( !( crypto_select & getCryptoProvide( handshake ) ) ) { dbgmsg( handshake, "peer selected an encryption option we didn't provide" ); return tr_handshakeDone( handshake, FALSE ); } tr_peerIoReadUint16( handshake->io, inbuf, &pad_d_len ); dbgmsg( handshake, "pad_d_len is %d", (int)pad_d_len ); if( pad_d_len > 512 ) { dbgmsg( handshake, "encryption handshake: pad_d_len is too long" ); return tr_handshakeDone( handshake, FALSE ); } handshake->pad_d_len = pad_d_len; setState( handshake, AWAITING_PAD_D ); return READ_NOW; }
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); }
static void gotError( tr_peerIo * io, short what, void * vhandshake ) { int errcode = errno; tr_handshake * handshake = vhandshake; if( io->utp_socket && !io->isIncoming && handshake->state == AWAITING_YB ) { /* This peer probably doesn't speak uTP. */ tr_torrent *tor = tr_peerIoHasTorrentHash( io ) ? tr_torrentFindFromHash( handshake->session, tr_peerIoGetTorrentHash( io ) ) : NULL; /* Don't mark a peer as non-uTP unless it's really a connect failure. */ if( tor && ( errcode == ETIMEDOUT || errcode == ECONNREFUSED ) ) { tr_torrentLock( tor ); tr_peerMgrSetUtpFailed( tor, tr_peerIoGetAddress( io, NULL ), TRUE ); tr_torrentUnlock( tor ); } if( !tr_peerIoReconnect( handshake->io ) ) { uint8_t msg[HANDSHAKE_SIZE]; buildHandshakeMessage( handshake, msg ); handshake->haveSentBitTorrentHandshake = 1; setReadState( handshake, AWAITING_HANDSHAKE ); tr_peerIoWriteBytes( handshake->io, msg, sizeof( msg ), FALSE ); } } /* if the error happened while we were sending a public key, we might * have encountered a peer that doesn't do encryption... reconnect and * try a plaintext handshake */ if( ( ( handshake->state == AWAITING_YB ) || ( handshake->state == AWAITING_VC ) ) && ( handshake->encryptionMode != TR_ENCRYPTION_REQUIRED ) && ( !tr_peerIoReconnect( handshake->io ) ) ) { uint8_t msg[HANDSHAKE_SIZE]; dbgmsg( handshake, "handshake failed, trying plaintext..." ); buildHandshakeMessage( handshake, msg ); handshake->haveSentBitTorrentHandshake = 1; setReadState( handshake, AWAITING_HANDSHAKE ); tr_peerIoWriteBytes( handshake->io, msg, sizeof( msg ), FALSE ); } else { dbgmsg( handshake, "libevent got an error what==%d, errno=%d (%s)", (int)what, errno, tr_strerror( errno ) ); tr_handshakeDone( handshake, FALSE ); } }
static int readPayloadStream( tr_handshake * handshake, struct evbuffer * inbuf ) { int i; const size_t needlen = HANDSHAKE_SIZE; dbgmsg( handshake, "reading payload stream... have %zu, need %zu", evbuffer_get_length( inbuf ), needlen ); if( evbuffer_get_length( inbuf ) < needlen ) return READ_LATER; /* parse the handshake ... */ i = parseHandshake( handshake, inbuf ); dbgmsg( handshake, "parseHandshake returned %d", i ); if( i != HANDSHAKE_OK ) return tr_handshakeDone( handshake, FALSE ); /* we've completed the BT handshake... pass the work on to peer-msgs */ return tr_handshakeDone( handshake, TRUE ); }
static int readIA( tr_handshake * handshake, struct evbuffer * inbuf ) { const size_t needlen = handshake->ia_len; struct evbuffer * outbuf; uint32_t crypto_select; dbgmsg( handshake, "reading IA... have %zu, need %zu", evbuffer_get_length( inbuf ), needlen ); if( evbuffer_get_length( inbuf ) < needlen ) return READ_LATER; /** *** B->A: ENCRYPT(VC, crypto_select, len(padD), padD), ENCRYPT2(Payload Stream) **/ tr_cryptoEncryptInit( handshake->crypto ); outbuf = evbuffer_new( ); dbgmsg( handshake, "sending vc" ); /* send VC */ { uint8_t vc[VC_LENGTH]; memset( vc, 0, VC_LENGTH ); evbuffer_add( outbuf, vc, VC_LENGTH ); } /* send crypto_select */ crypto_select = getCryptoSelect( handshake, handshake->crypto_provide ); if( crypto_select ) { dbgmsg( handshake, "selecting crypto mode '%d'", (int)crypto_select ); evbuffer_add_uint32( outbuf, crypto_select ); } else { dbgmsg( handshake, "peer didn't offer an encryption mode we like." ); evbuffer_free( outbuf ); return tr_handshakeDone( handshake, FALSE ); } dbgmsg( handshake, "sending pad d" ); /* ENCRYPT(VC, crypto_provide, len(PadD), PadD * PadD is reserved for future extensions to the handshake... * standard practice at this time is for it to be zero-length */ { const uint16_t len = 0; evbuffer_add_uint16( outbuf, len ); } /* maybe de-encrypt our connection */ if( crypto_select == CRYPTO_PROVIDE_PLAINTEXT ) { tr_peerIoWriteBuf( handshake->io, outbuf, FALSE ); tr_peerIoSetEncryption( handshake->io, PEER_ENCRYPTION_NONE ); } dbgmsg( handshake, "sending handshake" ); /* send our handshake */ { uint8_t msg[HANDSHAKE_SIZE]; buildHandshakeMessage( handshake, msg ); evbuffer_add( outbuf, msg, sizeof( msg ) ); handshake->haveSentBitTorrentHandshake = 1; } /* send it out */ tr_peerIoWriteBuf( handshake->io, outbuf, FALSE ); evbuffer_free( outbuf ); /* now await the handshake */ setState( handshake, AWAITING_PAYLOAD_STREAM ); 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; }
void tr_handshakeAbort( tr_handshake * handshake ) { if( handshake != NULL ) tr_handshakeDone( handshake, FALSE ); }
static int readYb (tr_handshake * handshake, struct evbuffer * inbuf) { int isEncrypted; const uint8_t * secret; uint8_t yb[KEY_LEN]; struct evbuffer * outbuf; size_t needlen = HANDSHAKE_NAME_LEN; if (evbuffer_get_length (inbuf) < needlen) return READ_LATER; isEncrypted = memcmp (evbuffer_pullup (inbuf, HANDSHAKE_NAME_LEN), HANDSHAKE_NAME, HANDSHAKE_NAME_LEN); if (isEncrypted) { needlen = KEY_LEN; if (evbuffer_get_length (inbuf) < needlen) return READ_LATER; } dbgmsg (handshake, "got an %s handshake", (isEncrypted ? "encrypted" : "plain")); tr_peerIoSetEncryption (handshake->io, isEncrypted ? PEER_ENCRYPTION_RC4 : PEER_ENCRYPTION_NONE); if (!isEncrypted) { setState (handshake, AWAITING_HANDSHAKE); return READ_NOW; } handshake->haveReadAnythingFromPeer = true; /* compute the secret */ evbuffer_remove (inbuf, yb, KEY_LEN); secret = tr_cryptoComputeSecret (handshake->crypto, yb); memcpy (handshake->mySecret, secret, KEY_LEN); /* now send these: HASH ('req1', S), HASH ('req2', SKEY) xor HASH ('req3', S), * ENCRYPT (VC, crypto_provide, len (PadC), PadC, len (IA)), ENCRYPT (IA) */ outbuf = evbuffer_new (); /* HASH ('req1', S) */ { uint8_t req1[SHA_DIGEST_LENGTH]; tr_sha1 (req1, "req1", 4, secret, KEY_LEN, NULL); evbuffer_add (outbuf, req1, SHA_DIGEST_LENGTH); } /* HASH ('req2', SKEY) xor HASH ('req3', S) */ { int i; uint8_t req2[SHA_DIGEST_LENGTH]; uint8_t req3[SHA_DIGEST_LENGTH]; uint8_t buf[SHA_DIGEST_LENGTH]; tr_sha1 (req2, "req2", 4, tr_cryptoGetTorrentHash (handshake->crypto), SHA_DIGEST_LENGTH, NULL); tr_sha1 (req3, "req3", 4, secret, KEY_LEN, NULL); for (i=0; i<SHA_DIGEST_LENGTH; ++i) buf[i] = req2[i] ^ req3[i]; evbuffer_add (outbuf, buf, SHA_DIGEST_LENGTH); } /* ENCRYPT (VC, crypto_provide, len (PadC), PadC * PadC is reserved for future extensions to the handshake... * standard practice at this time is for it to be zero-length */ { uint8_t vc[VC_LENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0 }; tr_peerIoWriteBuf (handshake->io, outbuf, false); tr_cryptoEncryptInit (handshake->crypto); tr_peerIoSetEncryption (handshake->io, PEER_ENCRYPTION_RC4); evbuffer_add (outbuf, vc, VC_LENGTH); evbuffer_add_uint32 (outbuf, getCryptoProvide (handshake)); evbuffer_add_uint16 (outbuf, 0); } /* ENCRYPT len (IA)), ENCRYPT (IA) */ { uint8_t msg[HANDSHAKE_SIZE]; if (!buildHandshakeMessage (handshake, msg)) return tr_handshakeDone (handshake, false); evbuffer_add_uint16 (outbuf, sizeof (msg)); evbuffer_add (outbuf, msg, sizeof (msg)); handshake->haveSentBitTorrentHandshake = 1; } /* send it */ tr_cryptoDecryptInit (handshake->crypto); setReadState (handshake, AWAITING_VC); tr_peerIoWriteBuf (handshake->io, outbuf, false); /* cleanup */ evbuffer_free (outbuf); return READ_LATER; }
void tr_handshakeAbort (tr_handshake * handshake) { if (handshake != NULL) tr_handshakeDone (handshake, false); }