/* * This file Copyright (C) 2007-2014 Mnemosyne LLC * * It may be used under the GNU GPL versions 2 or 3 * or any future license endorsed by Mnemosyne LLC. * * $Id$ */ #include #include #include /* strcmp (), strlen () */ #include #include #include "transmission.h" #include "clients.h" #include "crypto-utils.h" #include "handshake.h" #include "log.h" #include "peer-io.h" #include "peer-mgr.h" #include "session.h" #include "torrent.h" #include "tr-dht.h" #include "utils.h" /* enable LibTransmission extension protocol */ #define ENABLE_LTEP * / /* fast extensions */ #define ENABLE_FAST * / /* DHT */ #define ENABLE_DHT * / /*** **** ***/ #define HANDSHAKE_NAME "\023BitTorrent protocol" enum { /* BitTorrent Handshake Constants */ HANDSHAKE_NAME_LEN = 20, HANDSHAKE_FLAGS_LEN = 8, HANDSHAKE_SIZE = 68, INCOMING_HANDSHAKE_LEN = 48, /* Encryption Constants */ PadA_MAXLEN = 512, PadB_MAXLEN = 512, PadC_MAXLEN = 512, PadD_MAXLEN = 512, VC_LENGTH = 8, CRYPTO_PROVIDE_PLAINTEXT = 1, CRYPTO_PROVIDE_CRYPTO = 2, /* how long to wait before giving up on a handshake */ HANDSHAKE_TIMEOUT_SEC = 30 }; #ifdef ENABLE_LTEP #define HANDSHAKE_HAS_LTEP(bits)(((bits)[5] & 0x10) != 0) #define HANDSHAKE_SET_LTEP(bits)((bits)[5] |= 0x10) #else #define HANDSHAKE_HAS_LTEP(bits)(false) #define HANDSHAKE_SET_LTEP(bits)((void)0) #endif #ifdef ENABLE_FAST #define HANDSHAKE_HAS_FASTEXT(bits)(((bits)[7] & 0x04) != 0) #define HANDSHAKE_SET_FASTEXT(bits)((bits)[7] |= 0x04) #else #define HANDSHAKE_HAS_FASTEXT(bits)(false) #define HANDSHAKE_SET_FASTEXT(bits)((void)0) #endif #ifdef ENABLE_DHT #define HANDSHAKE_HAS_DHT(bits)(((bits)[7] & 0x01) != 0) #define HANDSHAKE_SET_DHT(bits)((bits)[7] |= 0x01) #else #define HANDSHAKE_HAS_DHT(bits)(false) #define HANDSHAKE_SET_DHT(bits)((void)0) #endif /* http://www.azureuswiki.com/index.php/Extension_negotiation_protocol these macros are to be used if both extended messaging and the azureus protocol is supported, they indicate which protocol is preferred */ #define HANDSHAKE_GET_EXTPREF(reserved) ((reserved)[5] & 0x03) #define HANDSHAKE_SET_EXTPREF(reserved, val)((reserved)[5] |= 0x03 & (val)) /** *** **/ typedef enum { /* incoming */ AWAITING_HANDSHAKE, AWAITING_PEER_ID, AWAITING_YA, AWAITING_PAD_A, AWAITING_CRYPTO_PROVIDE, AWAITING_PAD_C, AWAITING_IA, AWAITING_PAYLOAD_STREAM, /* outgoing */ AWAITING_YB, AWAITING_VC, AWAITING_CRYPTO_SELECT, AWAITING_PAD_D, N_STATES } handshake_state_t; struct tr_handshake { bool haveReadAnythingFromPeer; bool havePeerID; bool haveSentBitTorrentHandshake; tr_peerIo * io; tr_crypto * crypto; tr_session * session; handshake_state_t state; tr_encryption_mode encryptionMode; uint16_t pad_c_len; uint16_t pad_d_len; uint16_t ia_len; uint32_t crypto_select; uint32_t crypto_provide; uint8_t myReq1[SHA_DIGEST_LENGTH]; handshakeDoneCB doneCB; void * doneUserData; struct event * timeout_timer; }; /** *** **/ #define dbgmsg(handshake, ...) \ do { \ if (tr_logGetDeepEnabled ()) \ tr_logAddDeep (__FILE__, __LINE__, tr_peerIoGetAddrStr (handshake->io), __VA_ARGS__); \ } while (0) static const char* getStateName (const handshake_state_t state) { static const char * const state_strings[N_STATES] = { /* AWAITING_HANDSHAKE */ "awaiting handshake", /* AWAITING_PEER_ID */ "awaiting peer id", /* AWAITING_YA */ "awaiting ya", /* AWAITING_PAD_A */ "awaiting pad a", /* AWAITING_CRYPTO_PROVIDE */ "awaiting crypto_provide", /* AWAITING_PAD_C */ "awaiting pad c", /* AWAITING_IA */ "awaiting ia", /* AWAITING_PAYLOAD_STREAM */ "awaiting payload stream", /* AWAITING_YB */ "awaiting yb", /* AWAITING_VC */ "awaiting vc", /* AWAITING_CRYPTO_SELECT */ "awaiting crypto select", /* AWAITING_PAD_D */ "awaiting pad d" }; return statestate = state; } static void setReadState (tr_handshake * handshake, handshake_state_t state) { setState (handshake, state); } static bool buildHandshakeMessage (tr_handshake * handshake, uint8_t * buf) { const unsigned char * peer_id = NULL; const uint8_t * torrentHash; tr_torrent * tor; bool success; if ((torrentHash = tr_cryptoGetTorrentHash (handshake->crypto))) if ((tor = tr_torrentFindFromHash (handshake->session, torrentHash))) peer_id = tr_torrentGetPeerId (tor); if (peer_id == NULL) { success = false; } else { uint8_t * walk = buf; memcpy (walk, HANDSHAKE_NAME, HANDSHAKE_NAME_LEN); walk += HANDSHAKE_NAME_LEN; memset (walk, 0, HANDSHAKE_FLAGS_LEN); HANDSHAKE_SET_LTEP (walk); HANDSHAKE_SET_FASTEXT (walk); /* Note that this doesn't depend on whether the torrent is private. * We don't accept DHT peers for a private torrent, * but we participate in the DHT regardless. */ if (tr_dhtEnabled (handshake->session)) HANDSHAKE_SET_DHT (walk); walk += HANDSHAKE_FLAGS_LEN; memcpy (walk, torrentHash, SHA_DIGEST_LENGTH); walk += SHA_DIGEST_LENGTH; memcpy (walk, peer_id, PEER_ID_LEN); walk += PEER_ID_LEN; assert (walk - buf == HANDSHAKE_SIZE); success = true; } return success; } static ReadState tr_handshakeDone (tr_handshake * handshake, bool isConnected); typedef enum { HANDSHAKE_OK, HANDSHAKE_ENCRYPTION_WRONG, HANDSHAKE_BAD_TORRENT, HANDSHAKE_PEER_IS_SELF, } handshake_parse_err_t; static handshake_parse_err_t 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 %"TR_PRIuSIZE, HANDSHAKE_SIZE, evbuffer_get_length (inbuf)); if (evbuffer_get_length (inbuf) < HANDSHAKE_SIZE) return HANDSHAKE_ENCRYPTION_WRONG; /* 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; } /*** **** **** OUTGOING CONNECTIONS **** ***/ /* 1 A->B: Diffie Hellman Ya, PadA */ static void sendYa (tr_handshake * handshake) { int len; const uint8_t * public_key; char outbuf[ KEY_LEN + PadA_MAXLEN ]; char *walk = outbuf; /* add our public key (Ya) */ public_key = tr_cryptoGetMyPublicKey (handshake->crypto, &len); assert (len == KEY_LEN); assert (public_key); memcpy (walk, public_key, len); walk += len; /* add some bullshit padding */ len = tr_rand_int (PadA_MAXLEN); tr_rand_buffer (walk, len); walk += len; /* send it */ setReadState (handshake, AWAITING_YB); tr_peerIoWriteBytes (handshake->io, outbuf, walk - outbuf, false); } static uint32_t getCryptoProvide (const tr_handshake * handshake) { uint32_t provide = 0; switch (handshake->encryptionMode) { case TR_ENCRYPTION_REQUIRED: case TR_ENCRYPTION_PREFERRED: provide |= CRYPTO_PROVIDE_CRYPTO; break; case TR_CLEAR_PREFERRED: provide |= CRYPTO_PROVIDE_CRYPTO | CRYPTO_PROVIDE_PLAINTEXT; break; } return provide; } static uint32_t getCryptoSelect (const tr_handshake * handshake, uint32_t crypto_provide) { uint32_t choices[2]; int i; int nChoices = 0; switch (handshake->encryptionMode) { case TR_ENCRYPTION_REQUIRED: choices[nChoices++] = CRYPTO_PROVIDE_CRYPTO; break; case TR_ENCRYPTION_PREFERRED: choices[nChoices++] = CRYPTO_PROVIDE_CRYPTO; choices[nChoices++] = CRYPTO_PROVIDE_PLAINTEXT; break; case TR_CLEAR_PREFERRED: choices[nChoices++] = CRYPTO_PROVIDE_PLAINTEXT; choices[nChoices++] = CRYPTO_PROVIDE_CRYPTO; break; } for (i=0; icrypto, name, 4, NULL, 0, hash); } static ReadState readYb (tr_handshake * handshake, struct evbuffer * inbuf) { bool isEncrypted; 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) != 0; 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); tr_cryptoComputeSecret (handshake->crypto, yb); /* 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]; computeRequestHash (handshake, "req1", req1); 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); computeRequestHash (handshake, "req3", req3); for (i=0; iio, 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 = true; } /* send it */ tr_cryptoDecryptInit (handshake->crypto); setReadState (handshake, AWAITING_VC); tr_peerIoWriteBuf (handshake->io, outbuf, false); /* cleanup */ evbuffer_free (outbuf); return READ_LATER; } static ReadState readVC (tr_handshake * handshake, struct evbuffer * inbuf) { uint8_t tmp[VC_LENGTH]; const int key_len = VC_LENGTH; const uint8_t key[VC_LENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0 }; /* note: this works w/o having to `unwind' the buffer if * we read too much, but it is pretty brute-force. * it would be nice to make this cleaner. */ for (;;) { if (evbuffer_get_length (inbuf) < VC_LENGTH) { dbgmsg (handshake, "not enough bytes... returning read_more"); return READ_LATER; } memcpy (tmp, evbuffer_pullup (inbuf, key_len), key_len); tr_cryptoDecryptInit (handshake->crypto); tr_cryptoDecrypt (handshake->crypto, key_len, tmp, tmp); if (!memcmp (tmp, key, key_len)) break; evbuffer_drain (inbuf, 1); } dbgmsg (handshake, "got it!"); evbuffer_drain (inbuf, key_len); setState (handshake, AWAITING_CRYPTO_SELECT); return READ_NOW; } static ReadState readCryptoSelect (tr_handshake * handshake, struct evbuffer * inbuf) { uint16_t pad_d_len; uint32_t crypto_select; static 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 offer"); 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 ReadState readPadD (tr_handshake * handshake, struct evbuffer * inbuf) { const size_t needlen = handshake->pad_d_len; dbgmsg (handshake, "pad d: need %"TR_PRIuSIZE", got %"TR_PRIuSIZE, needlen, evbuffer_get_length (inbuf)); if (evbuffer_get_length (inbuf) < needlen) return READ_LATER; tr_peerIoDrain (handshake->io, inbuf, needlen); tr_peerIoSetEncryption (handshake->io, handshake->crypto_select); setState (handshake, AWAITING_HANDSHAKE); return READ_NOW; } /*** **** **** INCOMING CONNECTIONS **** ***/ static ReadState readHandshake (tr_handshake * handshake, struct evbuffer * inbuf) { uint8_t pstrlen; uint8_t pstr[20]; uint8_t reserved[HANDSHAKE_FLAGS_LEN]; uint8_t hash[SHA_DIGEST_LENGTH]; dbgmsg (handshake, "payload: need %d, got %"TR_PRIuSIZE, 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) */ assert (pstrlen == 19); tr_peerIoReadBytes (handshake->io, inbuf, pstr, pstrlen); pstr[pstrlen] = '\0'; if (memcmp (pstr, "BitTorrent protocol", 19)) return tr_handshakeDone (handshake, false); /* reserved bytes */ tr_peerIoReadBytes (handshake->io, inbuf, reserved, sizeof (reserved)); /** *** 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)); /* 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]; if (!buildHandshakeMessage (handshake, msg)) return tr_handshakeDone (handshake, false); tr_peerIoWriteBytes (handshake->io, msg, sizeof (msg), false); handshake->haveSentBitTorrentHandshake = true; } setReadState (handshake, AWAITING_PEER_ID); return READ_NOW; } static ReadState 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 ReadState readYa (tr_handshake * handshake, struct evbuffer * inbuf) { uint8_t ya[KEY_LEN]; uint8_t * walk, outbuf[KEY_LEN + PadB_MAXLEN]; const uint8_t * myKey; int len; dbgmsg (handshake, "in readYa... need %d, have %"TR_PRIuSIZE, KEY_LEN, evbuffer_get_length (inbuf)); if (evbuffer_get_length (inbuf) < KEY_LEN) return READ_LATER; /* read the incoming peer's public key */ evbuffer_remove (inbuf, ya, KEY_LEN); tr_cryptoComputeSecret (handshake->crypto, ya); computeRequestHash (handshake, "req1", handshake->myReq1); /* send our public key to the peer */ dbgmsg (handshake, "sending B->A: Diffie Hellman Yb, PadB"); walk = outbuf; myKey = tr_cryptoGetMyPublicKey (handshake->crypto, &len); memcpy (walk, myKey, len); walk += len; len = tr_rand_int (PadB_MAXLEN); tr_rand_buffer (walk, len); walk += len; setReadState (handshake, AWAITING_PAD_A); tr_peerIoWriteBytes (handshake->io, outbuf, walk - outbuf, false); return READ_NOW; } static ReadState readPadA (tr_handshake * handshake, struct evbuffer * inbuf) { /* resynchronizing on HASH ('req1',S) */ struct evbuffer_ptr ptr = evbuffer_search (inbuf, (const char*)handshake->myReq1, SHA_DIGEST_LENGTH, NULL); if (ptr.pos != -1) /* match */ { evbuffer_drain (inbuf, ptr.pos); dbgmsg (handshake, "found it... looking setting to awaiting_crypto_provide"); setState (handshake, AWAITING_CRYPTO_PROVIDE); return READ_NOW; } else { const size_t len = evbuffer_get_length (inbuf); if (len > SHA_DIGEST_LENGTH) evbuffer_drain (inbuf, len - SHA_DIGEST_LENGTH); return READ_LATER; } } static ReadState 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; tr_torrent * tor; 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); 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); computeRequestHash (handshake, "req3", req3); for (i=0; isession, obfuscatedTorrentHash))) { const bool clientIsSeed = tr_torrentIsSeed (tor); const 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 ReadState 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 ReadState 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 %"TR_PRIuSIZE", need %"TR_PRIuSIZE, 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 (); { /* send VC */ uint8_t vc[VC_LENGTH]; memset (vc, 0, VC_LENGTH); evbuffer_add (outbuf, vc, VC_LENGTH); dbgmsg (handshake, "sending vc"); } /* 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]; if (!buildHandshakeMessage (handshake, msg)) return tr_handshakeDone (handshake, false); evbuffer_add (outbuf, msg, sizeof (msg)); handshake->haveSentBitTorrentHandshake = true; } /* 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 ReadState readPayloadStream (tr_handshake * handshake, struct evbuffer * inbuf) { handshake_parse_err_t i; const size_t needlen = HANDSHAKE_SIZE; dbgmsg (handshake, "reading payload stream... have %"TR_PRIuSIZE", need %"TR_PRIuSIZE, 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 ReadState canRead (struct tr_peerIo * io, void * arg, size_t * piece) { ReadState ret; tr_handshake * handshake = arg; struct evbuffer * inbuf = tr_peerIoGetReadBuffer (io); bool readyForMore = true; assert (tr_isPeerIo (io)); /* no piece data in handshake */ *piece = 0; dbgmsg (handshake, "handling canRead; state is [%s]", getStateName (handshake->state)); while (readyForMore) { switch (handshake->state) { case AWAITING_HANDSHAKE: ret = readHandshake (handshake, inbuf); break; case AWAITING_PEER_ID: ret = readPeerId (handshake, inbuf); break; case AWAITING_YA: ret = readYa (handshake, inbuf); break; case AWAITING_PAD_A: ret = readPadA (handshake, inbuf); break; case AWAITING_CRYPTO_PROVIDE: ret = readCryptoProvide (handshake, inbuf); break; case AWAITING_PAD_C: ret = readPadC (handshake, inbuf); break; case AWAITING_IA: ret = readIA (handshake, inbuf); break; case AWAITING_PAYLOAD_STREAM: ret = readPayloadStream (handshake, inbuf); break; case AWAITING_YB: ret = readYb (handshake, inbuf); break; case AWAITING_VC: ret = readVC (handshake, inbuf); break; case AWAITING_CRYPTO_SELECT: ret = readCryptoSelect (handshake, inbuf); break; case AWAITING_PAD_D: ret = readPadD (handshake, inbuf); break; default: assert (0); } if (ret != READ_NOW) readyForMore = false; else if (handshake->state == AWAITING_PAD_C) readyForMore = evbuffer_get_length (inbuf) >= handshake->pad_c_len; else if (handshake->state == AWAITING_PAD_D) readyForMore = evbuffer_get_length (inbuf) >= handshake->pad_d_len; else if (handshake->state == AWAITING_IA) readyForMore = evbuffer_get_length (inbuf) >= handshake->ia_len; } return ret; } static bool fireDoneFunc (tr_handshake * handshake, bool isConnected) { const uint8_t * peer_id = isConnected && handshake->havePeerID ? tr_peerIoGetPeersId (handshake->io) : NULL; const bool success = (*handshake->doneCB)(handshake, handshake->io, handshake->haveReadAnythingFromPeer, isConnected, peer_id, handshake->doneUserData); return success; } static void tr_handshakeFree (tr_handshake * handshake) { if (handshake->io) tr_peerIoUnref (handshake->io); /* balanced by the ref in tr_handshakeNew */ event_free (handshake->timeout_timer); tr_free (handshake); } static ReadState tr_handshakeDone (tr_handshake * handshake, bool isOK) { bool success; dbgmsg (handshake, "handshakeDone: %s", isOK ? "connected" : "aborting"); tr_peerIoSetIOFuncs (handshake->io, NULL, NULL, NULL, NULL); success = fireDoneFunc (handshake, isOK); tr_handshakeFree (handshake); return success ? READ_LATER : READ_ERR; } void tr_handshakeAbort (tr_handshake * handshake) { if (handshake != NULL) tr_handshakeDone (handshake, false); } 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; if (tr_peerIoHasTorrentHash (io)) tor = tr_torrentFindFromHash (handshake->session, tr_peerIoGetTorrentHash (io)); else tor = NULL; /* Don't mark a peer as non-uTP unless it's really a connect failure. */ if ((errcode == ETIMEDOUT || errcode == ECONNREFUSED) && tr_isTorrent(tor)) tr_peerMgrSetUtpFailed (tor, tr_peerIoGetAddress (io, NULL), true); if (!tr_peerIoReconnect (handshake->io)) { uint8_t msg[HANDSHAKE_SIZE]; buildHandshakeMessage (handshake, msg); handshake->haveSentBitTorrentHandshake = true; 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 = true; 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 void handshakeTimeout (evutil_socket_t foo UNUSED, short bar UNUSED, void * handshake) { tr_handshakeAbort (handshake); } tr_handshake* tr_handshakeNew (tr_peerIo * io, tr_encryption_mode encryptionMode, handshakeDoneCB doneCB, void * doneUserData) { tr_handshake * handshake; tr_session * session = tr_peerIoGetSession (io); handshake = tr_new0 (tr_handshake, 1); handshake->io = io; handshake->crypto = tr_peerIoGetCrypto (io); handshake->encryptionMode = encryptionMode; handshake->doneCB = doneCB; handshake->doneUserData = doneUserData; handshake->session = session; handshake->timeout_timer = evtimer_new (session->event_base, handshakeTimeout, handshake); tr_timerAdd (handshake->timeout_timer, HANDSHAKE_TIMEOUT_SEC, 0); tr_peerIoRef (io); /* balanced by the unref in tr_handshakeFree */ tr_peerIoSetIOFuncs (handshake->io, canRead, NULL, gotError, handshake); tr_peerIoSetEncryption (io, PEER_ENCRYPTION_NONE); if (tr_peerIoIsIncoming (handshake->io)) { setReadState (handshake, AWAITING_HANDSHAKE); } else if (encryptionMode != TR_CLEAR_PREFERRED) { sendYa (handshake); } else { uint8_t msg[HANDSHAKE_SIZE]; buildHandshakeMessage (handshake, msg); handshake->haveSentBitTorrentHandshake = true; setReadState (handshake, AWAITING_HANDSHAKE); tr_peerIoWriteBytes (handshake->io, msg, sizeof (msg), false); } return handshake; } struct tr_peerIo* tr_handshakeGetIO (tr_handshake * handshake) { assert (handshake != NULL); assert (handshake->io != NULL); return handshake->io; } struct tr_peerIo* tr_handshakeStealIO (tr_handshake * handshake) { struct tr_peerIo * io; assert (handshake != NULL); assert (handshake->io != NULL); io = handshake->io; handshake->io = NULL; return io; } const tr_address * tr_handshakeGetAddr (const struct tr_handshake * handshake, tr_port * port) { assert (handshake != NULL); assert (handshake->io != NULL); return tr_peerIoGetAddress (handshake->io, port); }