2007-09-20 16:32:01 +00:00
|
|
|
/*
|
2014-01-19 01:09:44 +00:00
|
|
|
* This file Copyright (C) 2007-2014 Mnemosyne LLC
|
2007-09-20 16:32:01 +00:00
|
|
|
*
|
2014-01-21 03:10:30 +00:00
|
|
|
* It may be used under the GNU GPL versions 2 or 3
|
2014-01-19 01:09:44 +00:00
|
|
|
* or any future license endorsed by Mnemosyne LLC.
|
2007-09-20 16:32:01 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2021-10-22 02:40:55 +00:00
|
|
|
#include <algorithm>
|
2021-10-17 20:17:18 +00:00
|
|
|
#include <cerrno>
|
|
|
|
#include <cstring> /* strcmp(), strlen(), strncmp() */
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2011-03-24 22:45:04 +00:00
|
|
|
#include <event2/buffer.h>
|
2010-12-20 02:07:51 +00:00
|
|
|
#include <event2/event.h>
|
2007-09-20 16:32:01 +00:00
|
|
|
|
|
|
|
#include "transmission.h"
|
2007-12-25 05:37:32 +00:00
|
|
|
#include "clients.h"
|
2014-12-04 11:27:38 +00:00
|
|
|
#include "crypto-utils.h"
|
2007-09-20 16:32:01 +00:00
|
|
|
#include "handshake.h"
|
2013-01-25 23:34:20 +00:00
|
|
|
#include "log.h"
|
2007-09-20 16:32:01 +00:00
|
|
|
#include "peer-io.h"
|
2008-04-17 03:48:56 +00:00
|
|
|
#include "peer-mgr.h"
|
2009-07-01 14:58:57 +00:00
|
|
|
#include "session.h"
|
2007-12-25 05:37:32 +00:00
|
|
|
#include "torrent.h"
|
2017-06-08 07:24:12 +00:00
|
|
|
#include "tr-assert.h"
|
2009-05-19 18:38:26 +00:00
|
|
|
#include "tr-dht.h"
|
2007-09-20 16:32:01 +00:00
|
|
|
#include "utils.h"
|
|
|
|
|
|
|
|
/* enable LibTransmission extension protocol */
|
2008-09-23 19:11:04 +00:00
|
|
|
#define ENABLE_LTEP * /
|
2008-12-02 17:10:54 +00:00
|
|
|
/* fast extensions */
|
|
|
|
#define ENABLE_FAST * /
|
2009-05-19 18:38:26 +00:00
|
|
|
/* DHT */
|
|
|
|
#define ENABLE_DHT * /
|
2007-09-20 16:32:01 +00:00
|
|
|
|
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
|
|
|
#define HANDSHAKE_NAME "\023BitTorrent protocol"
|
|
|
|
|
2021-10-21 00:39:05 +00:00
|
|
|
// bittorrent handshake constants
|
|
|
|
static auto constexpr HANDSHAKE_NAME_LEN = int{ 20 };
|
|
|
|
static auto constexpr HANDSHAKE_FLAGS_LEN = int{ 8 };
|
|
|
|
static auto constexpr HANDSHAKE_SIZE = int{ 68 };
|
|
|
|
static auto constexpr INCOMING_HANDSHAKE_LEN = int{ 48 };
|
|
|
|
|
|
|
|
// encryption constants
|
|
|
|
static auto constexpr PadA_MAXLEN = int{ 512 };
|
|
|
|
static auto constexpr PadB_MAXLEN = int{ 512 };
|
|
|
|
static auto constexpr VC_LENGTH = int{ 8 };
|
|
|
|
static auto constexpr CRYPTO_PROVIDE_PLAINTEXT = int{ 1 };
|
|
|
|
static auto constexpr CRYPTO_PROVIDE_CRYPTO = int{ 2 };
|
|
|
|
|
|
|
|
// how long to wait before giving up on a handshake
|
|
|
|
static auto constexpr HANDSHAKE_TIMEOUT_SEC = int{ 30 };
|
2007-09-20 16:32:01 +00:00
|
|
|
|
|
|
|
#ifdef ENABLE_LTEP
|
2017-04-19 12:04:45 +00:00
|
|
|
#define HANDSHAKE_HAS_LTEP(bits) (((bits)[5] & 0x10) != 0)
|
|
|
|
#define HANDSHAKE_SET_LTEP(bits) ((bits)[5] |= 0x10)
|
2007-09-20 16:32:01 +00:00
|
|
|
#else
|
2017-04-19 12:04:45 +00:00
|
|
|
#define HANDSHAKE_HAS_LTEP(bits) (false)
|
|
|
|
#define HANDSHAKE_SET_LTEP(bits) ((void)0)
|
2007-09-20 16:32:01 +00:00
|
|
|
#endif
|
|
|
|
|
2009-08-10 20:04:08 +00:00
|
|
|
#ifdef ENABLE_FAST
|
2017-04-19 12:04:45 +00:00
|
|
|
#define HANDSHAKE_HAS_FASTEXT(bits) (((bits)[7] & 0x04) != 0)
|
|
|
|
#define HANDSHAKE_SET_FASTEXT(bits) ((bits)[7] |= 0x04)
|
2009-08-10 20:04:08 +00:00
|
|
|
#else
|
2017-04-19 12:04:45 +00:00
|
|
|
#define HANDSHAKE_HAS_FASTEXT(bits) (false)
|
|
|
|
#define HANDSHAKE_SET_FASTEXT(bits) ((void)0)
|
2009-08-10 20:04:08 +00:00
|
|
|
#endif
|
2008-12-02 17:10:54 +00:00
|
|
|
|
2009-05-19 18:38:26 +00:00
|
|
|
#ifdef ENABLE_DHT
|
2017-04-19 12:04:45 +00:00
|
|
|
#define HANDSHAKE_HAS_DHT(bits) (((bits)[7] & 0x01) != 0)
|
|
|
|
#define HANDSHAKE_SET_DHT(bits) ((bits)[7] |= 0x01)
|
2009-05-19 18:38:26 +00:00
|
|
|
#else
|
2017-04-19 12:04:45 +00:00
|
|
|
#define HANDSHAKE_HAS_DHT(bits) (false)
|
|
|
|
#define HANDSHAKE_SET_DHT(bits) ((void)0)
|
2009-08-10 20:04:08 +00:00
|
|
|
#endif
|
2009-05-19 18:38:26 +00:00
|
|
|
|
2007-09-20 16:32:01 +00:00
|
|
|
/**
|
|
|
|
***
|
|
|
|
**/
|
|
|
|
|
2021-10-06 14:26:07 +00:00
|
|
|
enum handshake_state_t
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
/* 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
|
2021-10-06 14:26:07 +00:00
|
|
|
};
|
2015-03-15 11:43:32 +00:00
|
|
|
|
|
|
|
struct tr_handshake
|
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
bool haveReadAnythingFromPeer;
|
|
|
|
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];
|
|
|
|
struct event* timeout_timer;
|
2021-10-22 02:40:55 +00:00
|
|
|
|
|
|
|
std::optional<tr_peer_id_t> peer_id;
|
|
|
|
|
|
|
|
tr_handshake_done_func done_func;
|
|
|
|
void* done_func_user_data;
|
2007-09-20 16:32:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
***
|
|
|
|
**/
|
|
|
|
|
2020-09-13 21:43:29 +00:00
|
|
|
#define dbgmsg(handshake, ...) \
|
2021-08-15 09:41:48 +00:00
|
|
|
do \
|
|
|
|
{ \
|
2021-09-10 17:22:46 +00:00
|
|
|
if (tr_logGetDeepEnabled()) \
|
|
|
|
{ \
|
|
|
|
char addrstr[TR_ADDRSTRLEN]; \
|
|
|
|
tr_peerIoGetAddrStr(handshake->io, addrstr, sizeof(addrstr)); \
|
|
|
|
tr_logAddDeep(__FILE__, __LINE__, addrstr, __VA_ARGS__); \
|
|
|
|
} \
|
2021-08-15 09:41:48 +00:00
|
|
|
} while (0)
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
static char const* getStateName(handshake_state_t const state)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
static char const* const state_strings[N_STATES] = {
|
2017-04-19 12:04:45 +00:00
|
|
|
"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 state < N_STATES ? state_strings[state] : "unknown state";
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static void setState(tr_handshake* handshake, handshake_state_t state)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "setting to state [%s]", getStateName(state));
|
|
|
|
handshake->state = state;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static void setReadState(tr_handshake* handshake, handshake_state_t state)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
setState(handshake, state);
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static bool buildHandshakeMessage(tr_handshake* handshake, uint8_t* buf)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2020-11-01 21:47:57 +00:00
|
|
|
uint8_t const* const torrent_hash = tr_cryptoGetTorrentHash(handshake->crypto);
|
2021-09-15 00:18:09 +00:00
|
|
|
tr_torrent* const tor = torrent_hash == nullptr ? nullptr : tr_torrentFindFromHash(handshake->session, torrent_hash);
|
2021-10-22 02:40:55 +00:00
|
|
|
bool const success = tor != nullptr;
|
2013-08-05 04:39:43 +00:00
|
|
|
|
2020-11-01 21:47:57 +00:00
|
|
|
if (success)
|
2013-08-05 04:39:43 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
uint8_t* walk = buf;
|
|
|
|
|
2021-10-22 02:40:55 +00:00
|
|
|
walk = std::copy_n(HANDSHAKE_NAME, HANDSHAKE_NAME_LEN, walk);
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
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;
|
|
|
|
|
2021-10-22 02:40:55 +00:00
|
|
|
walk = std::copy_n(torrent_hash, SHA_DIGEST_LENGTH, walk);
|
|
|
|
|
|
|
|
auto const& peer_id = tr_torrentGetPeerId(tor);
|
|
|
|
std::copy_n(std::data(peer_id), std::size(peer_id), walk);
|
|
|
|
|
|
|
|
TR_ASSERT(walk + std::size(peer_id) - buf == HANDSHAKE_SIZE);
|
2013-08-05 04:39:43 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return success;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static ReadState tr_handshakeDone(tr_handshake* handshake, bool isConnected);
|
2007-09-22 00:53:11 +00:00
|
|
|
|
2021-10-06 14:26:07 +00:00
|
|
|
enum handshake_parse_err_t
|
2007-09-22 03:37:37 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
HANDSHAKE_OK,
|
|
|
|
HANDSHAKE_ENCRYPTION_WRONG,
|
|
|
|
HANDSHAKE_BAD_TORRENT,
|
|
|
|
HANDSHAKE_PEER_IS_SELF,
|
2021-10-06 14:26:07 +00:00
|
|
|
};
|
2007-09-22 03:37:37 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static handshake_parse_err_t parseHandshake(tr_handshake* handshake, struct evbuffer* inbuf)
|
2007-09-22 03:37:37 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
uint8_t name[HANDSHAKE_NAME_LEN];
|
|
|
|
uint8_t reserved[HANDSHAKE_FLAGS_LEN];
|
|
|
|
uint8_t hash[SHA_DIGEST_LENGTH];
|
|
|
|
|
|
|
|
dbgmsg(handshake, "payload: need %d, got %zu", 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) != 0)
|
|
|
|
{
|
|
|
|
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));
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(tr_peerIoHasTorrentHash(handshake->io));
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
if (!tr_torrentExists(handshake->session, hash) ||
|
|
|
|
memcmp(hash, tr_peerIoGetTorrentHash(handshake->io), SHA_DIGEST_LENGTH) != 0)
|
2008-06-05 16:25:22 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "peer returned the wrong hash. wtf?");
|
|
|
|
return HANDSHAKE_BAD_TORRENT;
|
2007-09-22 03:37:37 +00:00
|
|
|
}
|
|
|
|
|
2021-10-22 02:40:55 +00:00
|
|
|
// peer_id
|
|
|
|
auto peer_id = tr_peer_id_t{};
|
|
|
|
tr_peerIoReadBytes(handshake->io, inbuf, std::data(peer_id), std::size(peer_id));
|
|
|
|
handshake->peer_id = peer_id;
|
2007-09-22 03:37:37 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* peer id */
|
2021-11-01 22:14:17 +00:00
|
|
|
dbgmsg(handshake, "peer-id is [%" TR_PRIsv "]", TR_PRIsv_ARG(peer_id));
|
2008-12-10 05:20:28 +00:00
|
|
|
|
2021-10-22 02:40:55 +00:00
|
|
|
auto* const tor = tr_torrentFindFromHash(handshake->session, hash);
|
|
|
|
if (peer_id == tr_torrentGetPeerId(tor))
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "streuth! we've connected to ourselves.");
|
|
|
|
return HANDSHAKE_PEER_IS_SELF;
|
2007-09-22 03:37:37 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/**
|
|
|
|
*** Extensions
|
|
|
|
**/
|
2008-12-02 17:10:54 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoEnableDHT(handshake->io, HANDSHAKE_HAS_DHT(reserved));
|
|
|
|
tr_peerIoEnableLTEP(handshake->io, HANDSHAKE_HAS_LTEP(reserved));
|
|
|
|
tr_peerIoEnableFEXT(handshake->io, HANDSHAKE_HAS_FASTEXT(reserved));
|
2007-10-17 18:53:17 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return HANDSHAKE_OK;
|
2007-09-22 03:37:37 +00:00
|
|
|
}
|
|
|
|
|
2007-09-20 16:32:01 +00:00
|
|
|
/***
|
|
|
|
****
|
|
|
|
**** OUTGOING CONNECTIONS
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
|
|
|
/* 1 A->B: Diffie Hellman Ya, PadA */
|
2017-04-19 12:04:45 +00:00
|
|
|
static void sendYa(tr_handshake* handshake)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
/* add our public key (Ya) */
|
2021-10-29 22:43:25 +00:00
|
|
|
|
|
|
|
int len = 0;
|
2021-10-26 18:02:07 +00:00
|
|
|
uint8_t const* const public_key = tr_cryptoGetMyPublicKey(handshake->crypto, &len);
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(len == KEY_LEN);
|
2021-09-15 00:18:09 +00:00
|
|
|
TR_ASSERT(public_key != nullptr);
|
2021-10-29 22:43:25 +00:00
|
|
|
|
|
|
|
char outbuf[KEY_LEN + PadA_MAXLEN];
|
|
|
|
char* walk = outbuf;
|
|
|
|
walk = std::copy_n(public_key, len, walk);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
/* 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);
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
static uint32_t getCryptoProvide(tr_handshake const* handshake)
|
2007-09-22 00:53:11 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
uint32_t provide = 0;
|
2007-09-22 00:53:11 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
switch (handshake->encryptionMode)
|
2007-11-08 21:20:08 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
case TR_ENCRYPTION_REQUIRED:
|
|
|
|
case TR_ENCRYPTION_PREFERRED:
|
2012-12-27 22:03:58 +00:00
|
|
|
provide |= CRYPTO_PROVIDE_CRYPTO;
|
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case TR_CLEAR_PREFERRED:
|
2012-12-27 22:03:58 +00:00
|
|
|
provide |= CRYPTO_PROVIDE_CRYPTO | CRYPTO_PROVIDE_PLAINTEXT;
|
|
|
|
break;
|
2007-11-08 21:20:08 +00:00
|
|
|
}
|
2007-09-22 00:53:11 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return provide;
|
2007-11-08 21:20:08 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
static uint32_t getCryptoSelect(tr_handshake const* handshake, uint32_t crypto_provide)
|
2007-11-08 21:20:08 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
uint32_t choices[2];
|
|
|
|
int nChoices = 0;
|
2007-11-08 21:20:08 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
switch (handshake->encryptionMode)
|
2007-11-08 21:20:08 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
case TR_ENCRYPTION_REQUIRED:
|
2012-12-27 22:03:58 +00:00
|
|
|
choices[nChoices++] = CRYPTO_PROVIDE_CRYPTO;
|
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case TR_ENCRYPTION_PREFERRED:
|
2012-12-27 22:03:58 +00:00
|
|
|
choices[nChoices++] = CRYPTO_PROVIDE_CRYPTO;
|
|
|
|
choices[nChoices++] = CRYPTO_PROVIDE_PLAINTEXT;
|
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case TR_CLEAR_PREFERRED:
|
2012-12-27 22:03:58 +00:00
|
|
|
choices[nChoices++] = CRYPTO_PROVIDE_PLAINTEXT;
|
|
|
|
choices[nChoices++] = CRYPTO_PROVIDE_CRYPTO;
|
|
|
|
break;
|
2007-11-08 21:20:08 +00:00
|
|
|
}
|
|
|
|
|
2017-05-13 22:38:31 +00:00
|
|
|
for (int i = 0; i < nChoices; ++i)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2019-07-14 12:40:41 +00:00
|
|
|
if ((crypto_provide & choices[i]) != 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return choices[i];
|
|
|
|
}
|
|
|
|
}
|
2007-11-08 21:20:08 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return 0;
|
2007-09-22 00:53:11 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
static void computeRequestHash(tr_handshake const* handshake, char const* name, uint8_t* hash)
|
#4400, #5462: Move DH helpers to crypto-utils
On a way to factoring out OpenSSL support to a standalone file to ease
addition of other crypto libraries support in the future, move helpers
providing DH key exchange to crypto-utils.{c,h}. OpenSSL-related
functionality (DH context management) is moved to crypto-utils-openssl.c.
Since we know in advance that DH secret key management code will be the
same for most of backends, implement common functionality in separate
crypto-utils-fallback.c.
Add new tr_dh_ctx_t and tr_dh_secret_t types and functions to be
implemented by crypto backends:
* tr_dh_new - allocate DH context,
* tr_dh_free - free the context,
* tr_dh_make_key - generate private/public keypair,
* tr_dh_agree - perform DH key exchange and generate secret key,
* tr_dh_secret_derive - calculate secret key hash,
* tr_dh_secret_free - free the secret key,
* tr_dh_align_key - align some DH key in the buffer allocated for it.
Make DH secret key not accessible in plain form outside the crypto
backend. This allows for implementations where the key is managed by
the underlying library and is not even exposed to our backend.
2014-12-04 19:18:08 +00:00
|
|
|
{
|
2021-09-15 00:18:09 +00:00
|
|
|
tr_cryptoSecretKeySha1(handshake->crypto, name, 4, nullptr, 0, hash);
|
#4400, #5462: Move DH helpers to crypto-utils
On a way to factoring out OpenSSL support to a standalone file to ease
addition of other crypto libraries support in the future, move helpers
providing DH key exchange to crypto-utils.{c,h}. OpenSSL-related
functionality (DH context management) is moved to crypto-utils-openssl.c.
Since we know in advance that DH secret key management code will be the
same for most of backends, implement common functionality in separate
crypto-utils-fallback.c.
Add new tr_dh_ctx_t and tr_dh_secret_t types and functions to be
implemented by crypto backends:
* tr_dh_new - allocate DH context,
* tr_dh_free - free the context,
* tr_dh_make_key - generate private/public keypair,
* tr_dh_agree - perform DH key exchange and generate secret key,
* tr_dh_secret_derive - calculate secret key hash,
* tr_dh_secret_free - free the secret key,
* tr_dh_align_key - align some DH key in the buffer allocated for it.
Make DH secret key not accessible in plain form outside the crypto
backend. This allows for implementations where the key is managed by
the underlying library and is not even exposed to our backend.
2014-12-04 19:18:08 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static ReadState readYb(tr_handshake* handshake, struct evbuffer* inbuf)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
uint8_t yb[KEY_LEN];
|
|
|
|
size_t needlen = HANDSHAKE_NAME_LEN;
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (evbuffer_get_length(inbuf) < needlen)
|
|
|
|
{
|
|
|
|
return READ_LATER;
|
|
|
|
}
|
|
|
|
|
2021-10-26 18:02:07 +00:00
|
|
|
bool const isEncrypted = memcmp(evbuffer_pullup(inbuf, HANDSHAKE_NAME_LEN), HANDSHAKE_NAME, HANDSHAKE_NAME_LEN) != 0;
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (isEncrypted)
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
needlen = KEY_LEN;
|
2012-12-27 22:03:58 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (evbuffer_get_length(inbuf) < needlen)
|
|
|
|
{
|
|
|
|
return READ_LATER;
|
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "got an %s handshake", (isEncrypted ? "encrypted" : "plain"));
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoSetEncryption(handshake->io, isEncrypted ? PEER_ENCRYPTION_RC4 : PEER_ENCRYPTION_NONE);
|
|
|
|
|
|
|
|
if (!isEncrypted)
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
setState(handshake, AWAITING_HANDSHAKE);
|
|
|
|
return READ_NOW;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
handshake->haveReadAnythingFromPeer = true;
|
2010-04-20 21:54:03 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* compute the secret */
|
|
|
|
evbuffer_remove(inbuf, yb, KEY_LEN);
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (!tr_cryptoComputeSecret(handshake->crypto, yb))
|
|
|
|
{
|
|
|
|
return tr_handshakeDone(handshake, false);
|
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-21 07:40:57 +00:00
|
|
|
/* now send these: HASH('req1', S), HASH('req2', SKEY) xor HASH('req3', S),
|
|
|
|
* ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA)), ENCRYPT(IA) */
|
2021-10-26 18:02:07 +00:00
|
|
|
evbuffer* const outbuf = evbuffer_new();
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-21 07:40:57 +00:00
|
|
|
/* HASH('req1', S) */
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
uint8_t req1[SHA_DIGEST_LENGTH];
|
|
|
|
computeRequestHash(handshake, "req1", req1);
|
|
|
|
evbuffer_add(outbuf, req1, SHA_DIGEST_LENGTH);
|
|
|
|
}
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-21 07:40:57 +00:00
|
|
|
/* HASH('req2', SKEY) xor HASH('req3', S) */
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
uint8_t req2[SHA_DIGEST_LENGTH];
|
|
|
|
uint8_t req3[SHA_DIGEST_LENGTH];
|
|
|
|
uint8_t buf[SHA_DIGEST_LENGTH];
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2021-09-15 00:18:09 +00:00
|
|
|
tr_sha1(req2, "req2", 4, tr_cryptoGetTorrentHash(handshake->crypto), SHA_DIGEST_LENGTH, nullptr);
|
2017-04-19 12:04:45 +00:00
|
|
|
computeRequestHash(handshake, "req3", req3);
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-05-13 22:38:31 +00:00
|
|
|
for (int i = 0; i < SHA_DIGEST_LENGTH; ++i)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
buf[i] = req2[i] ^ req3[i];
|
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
evbuffer_add(outbuf, buf, SHA_DIGEST_LENGTH);
|
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-21 07:40:57 +00:00
|
|
|
/* ENCRYPT(VC, crypto_provide, len(PadC), PadC
|
2017-04-19 12:04:45 +00:00
|
|
|
* 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 };
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoWriteBuf(handshake->io, outbuf, false);
|
|
|
|
tr_cryptoEncryptInit(handshake->crypto);
|
|
|
|
tr_peerIoSetEncryption(handshake->io, PEER_ENCRYPTION_RC4);
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
evbuffer_add(outbuf, vc, VC_LENGTH);
|
|
|
|
evbuffer_add_uint32(outbuf, getCryptoProvide(handshake));
|
|
|
|
evbuffer_add_uint16(outbuf, 0);
|
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-21 07:40:57 +00:00
|
|
|
/* ENCRYPT len(IA)), ENCRYPT(IA) */
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
uint8_t msg[HANDSHAKE_SIZE];
|
2012-12-27 22:03:58 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (!buildHandshakeMessage(handshake, msg))
|
|
|
|
{
|
|
|
|
return tr_handshakeDone(handshake, false);
|
|
|
|
}
|
2012-12-27 22:03:58 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
evbuffer_add_uint16(outbuf, sizeof(msg));
|
|
|
|
evbuffer_add(outbuf, msg, sizeof(msg));
|
2012-12-27 22:03:58 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
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;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static ReadState readVC(tr_handshake* handshake, struct evbuffer* inbuf)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
uint8_t tmp[VC_LENGTH];
|
2017-04-20 16:02:19 +00:00
|
|
|
int const key_len = VC_LENGTH;
|
|
|
|
uint8_t const key[VC_LENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
/* 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 (;;)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
if (evbuffer_get_length(inbuf) < VC_LENGTH)
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "not enough bytes... returning read_more");
|
|
|
|
return READ_LATER;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
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) == 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
evbuffer_drain(inbuf, 1);
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "got it!");
|
|
|
|
evbuffer_drain(inbuf, key_len);
|
|
|
|
setState(handshake, AWAITING_CRYPTO_SELECT);
|
|
|
|
return READ_NOW;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static ReadState readCryptoSelect(tr_handshake* handshake, struct evbuffer* inbuf)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
static size_t const needlen = sizeof(uint32_t) + sizeof(uint16_t);
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (evbuffer_get_length(inbuf) < needlen)
|
|
|
|
{
|
|
|
|
return READ_LATER;
|
|
|
|
}
|
|
|
|
|
2021-10-29 22:43:25 +00:00
|
|
|
uint32_t crypto_select = 0;
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoReadUint32(handshake->io, inbuf, &crypto_select);
|
|
|
|
handshake->crypto_select = crypto_select;
|
|
|
|
dbgmsg(handshake, "crypto select is %d", (int)crypto_select);
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if ((crypto_select & getCryptoProvide(handshake)) == 0)
|
2007-09-22 00:53:11 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "peer selected an encryption option we didn't offer");
|
|
|
|
return tr_handshakeDone(handshake, false);
|
2007-09-22 00:53:11 +00:00
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2021-10-29 22:43:25 +00:00
|
|
|
uint16_t pad_d_len = 0;
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoReadUint16(handshake->io, inbuf, &pad_d_len);
|
|
|
|
dbgmsg(handshake, "pad_d_len is %d", (int)pad_d_len);
|
2008-01-14 16:17:02 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (pad_d_len > 512)
|
2008-01-14 16:17:02 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "encryption handshake: pad_d_len is too long");
|
|
|
|
return tr_handshakeDone(handshake, false);
|
2008-01-14 16:17:02 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
handshake->pad_d_len = pad_d_len;
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
setState(handshake, AWAITING_PAD_D);
|
|
|
|
return READ_NOW;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static ReadState readPadD(tr_handshake* handshake, struct evbuffer* inbuf)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
size_t const needlen = handshake->pad_d_len;
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "pad d: need %zu, got %zu", needlen, evbuffer_get_length(inbuf));
|
|
|
|
|
|
|
|
if (evbuffer_get_length(inbuf) < needlen)
|
|
|
|
{
|
|
|
|
return READ_LATER;
|
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoDrain(handshake->io, inbuf, needlen);
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2021-09-12 17:41:49 +00:00
|
|
|
tr_peerIoSetEncryption(handshake->io, static_cast<tr_encryption_type>(handshake->crypto_select));
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
setState(handshake, AWAITING_HANDSHAKE);
|
|
|
|
return READ_NOW;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
****
|
|
|
|
**** INCOMING CONNECTIONS
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static ReadState readHandshake(tr_handshake* handshake, struct evbuffer* inbuf)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "payload: need %d, got %zu", INCOMING_HANDSHAKE_LEN, evbuffer_get_length(inbuf));
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (evbuffer_get_length(inbuf) < INCOMING_HANDSHAKE_LEN)
|
|
|
|
{
|
|
|
|
return READ_LATER;
|
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
handshake->haveReadAnythingFromPeer = true;
|
2010-04-20 21:54:03 +00:00
|
|
|
|
2021-10-29 22:43:25 +00:00
|
|
|
uint8_t pstrlen = evbuffer_pullup(inbuf, 1)[0]; /* peek, don't read. We may be handing inbuf to AWAITING_YA */
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (pstrlen == 19) /* unencrypted */
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoSetEncryption(handshake->io, PEER_ENCRYPTION_NONE);
|
2007-09-26 03:52:30 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (handshake->encryptionMode == TR_ENCRYPTION_REQUIRED)
|
2007-09-26 03:52:30 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "peer is unencrypted, and we're disallowing that");
|
|
|
|
return tr_handshakeDone(handshake, false);
|
2007-09-26 03:52:30 +00:00
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else /* encrypted or corrupt */
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoSetEncryption(handshake->io, PEER_ENCRYPTION_RC4);
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (tr_peerIoIsIncoming(handshake->io))
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "I think peer is sending us an encrypted handshake...");
|
|
|
|
setState(handshake, AWAITING_YA);
|
|
|
|
return READ_NOW;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_cryptoDecrypt(handshake->crypto, 1, &pstrlen, &pstrlen);
|
2012-12-27 22:03:58 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (pstrlen != 19)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "I think peer has sent us a corrupt handshake...");
|
|
|
|
return tr_handshakeDone(handshake, false);
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
evbuffer_drain(inbuf, 1);
|
|
|
|
|
|
|
|
/* pstr (BitTorrent) */
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(pstrlen == 19);
|
2021-10-29 22:43:25 +00:00
|
|
|
uint8_t pstr[20];
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoReadBytes(handshake->io, inbuf, pstr, pstrlen);
|
|
|
|
pstr[pstrlen] = '\0';
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
if (strncmp((char const*)pstr, "BitTorrent protocol", 19) != 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return tr_handshakeDone(handshake, false);
|
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* reserved bytes */
|
2021-10-29 22:43:25 +00:00
|
|
|
uint8_t reserved[HANDSHAKE_FLAGS_LEN];
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoReadBytes(handshake->io, inbuf, reserved, sizeof(reserved));
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/**
|
|
|
|
*** Extensions
|
|
|
|
**/
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoEnableDHT(handshake->io, HANDSHAKE_HAS_DHT(reserved));
|
|
|
|
tr_peerIoEnableLTEP(handshake->io, HANDSHAKE_HAS_LTEP(reserved));
|
|
|
|
tr_peerIoEnableFEXT(handshake->io, HANDSHAKE_HAS_FASTEXT(reserved));
|
2008-01-01 00:20:07 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* torrent hash */
|
2021-10-29 22:43:25 +00:00
|
|
|
uint8_t hash[SHA_DIGEST_LENGTH];
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoReadBytes(handshake->io, inbuf, hash, sizeof(hash));
|
2008-12-02 17:10:54 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (tr_peerIoIsIncoming(handshake->io))
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
if (!tr_torrentExists(handshake->session, hash))
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "peer is trying to connect to us for a torrent we don't have.");
|
|
|
|
return tr_handshakeDone(handshake, false);
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
2021-10-14 19:26:38 +00:00
|
|
|
|
|
|
|
TR_ASSERT(!tr_peerIoHasTorrentHash(handshake->io));
|
|
|
|
tr_peerIoSetTorrentHash(handshake->io, hash);
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else /* outgoing */
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(tr_peerIoHasTorrentHash(handshake->io));
|
2012-12-27 22:03:58 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (memcmp(hash, tr_peerIoGetTorrentHash(handshake->io), SHA_DIGEST_LENGTH) != 0)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "peer returned the wrong hash. wtf?");
|
|
|
|
return tr_handshakeDone(handshake, false);
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
}
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/**
|
|
|
|
*** If it's an incoming message, we need to send a response handshake
|
|
|
|
**/
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (!handshake->haveSentBitTorrentHandshake)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
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;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
setReadState(handshake, AWAITING_PEER_ID);
|
|
|
|
return READ_NOW;
|
2008-01-01 00:20:07 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static ReadState readPeerId(tr_handshake* handshake, struct evbuffer* inbuf)
|
2008-01-01 00:20:07 +00:00
|
|
|
{
|
2021-10-22 02:40:55 +00:00
|
|
|
// read the peer_id
|
|
|
|
auto peer_id = tr_peer_id_t{};
|
|
|
|
if (evbuffer_get_length(inbuf) < std::size(peer_id))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return READ_LATER;
|
|
|
|
}
|
2021-10-22 02:40:55 +00:00
|
|
|
tr_peerIoReadBytes(handshake->io, inbuf, std::data(peer_id), std::size(peer_id));
|
|
|
|
handshake->peer_id = peer_id;
|
2008-01-01 00:20:07 +00:00
|
|
|
|
2021-10-22 02:40:55 +00:00
|
|
|
char client[128] = {};
|
2021-10-22 20:24:30 +00:00
|
|
|
tr_clientForId(client, sizeof(client), peer_id);
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "peer-id is [%s] ... isIncoming is %d", client, tr_peerIoIsIncoming(handshake->io));
|
2012-12-27 22:03:58 +00:00
|
|
|
|
2021-10-22 02:40:55 +00:00
|
|
|
// if we've somehow connected to ourselves, don't keep the connection
|
|
|
|
auto* const tor = tr_torrentFindFromHash(handshake->session, tr_peerIoGetTorrentHash(handshake->io));
|
|
|
|
bool const connected_to_self = peer_id == tr_torrentGetPeerId(tor);
|
2013-02-02 05:31:43 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return tr_handshakeDone(handshake, !connected_to_self);
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static ReadState readYa(tr_handshake* handshake, struct evbuffer* inbuf)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "in readYa... need %d, have %zu", KEY_LEN, evbuffer_get_length(inbuf));
|
|
|
|
|
|
|
|
if (evbuffer_get_length(inbuf) < KEY_LEN)
|
|
|
|
{
|
|
|
|
return READ_LATER;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read the incoming peer's public key */
|
2021-10-29 22:43:25 +00:00
|
|
|
uint8_t ya[KEY_LEN];
|
2017-04-19 12:04:45 +00:00
|
|
|
evbuffer_remove(inbuf, ya, KEY_LEN);
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (!tr_cryptoComputeSecret(handshake->crypto, ya))
|
|
|
|
{
|
|
|
|
return tr_handshakeDone(handshake, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
computeRequestHash(handshake, "req1", handshake->myReq1);
|
|
|
|
|
|
|
|
/* send our public key to the peer */
|
|
|
|
dbgmsg(handshake, "sending B->A: Diffie Hellman Yb, PadB");
|
2021-10-29 22:43:25 +00:00
|
|
|
uint8_t outbuf[KEY_LEN + PadB_MAXLEN];
|
|
|
|
uint8_t* walk = outbuf;
|
|
|
|
int len = 0;
|
2021-10-26 18:02:07 +00:00
|
|
|
uint8_t const* const myKey = tr_cryptoGetMyPublicKey(handshake->crypto, &len);
|
2021-10-29 22:43:25 +00:00
|
|
|
walk = std::copy_n(myKey, len, walk);
|
2017-04-19 12:04:45 +00:00
|
|
|
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;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static ReadState readPadA(tr_handshake* handshake, struct evbuffer* inbuf)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-30 16:30:03 +00:00
|
|
|
/* resynchronizing on HASH('req1', S) */
|
2021-09-15 00:18:09 +00:00
|
|
|
struct evbuffer_ptr ptr = evbuffer_search(inbuf, (char const*)handshake->myReq1, SHA_DIGEST_LENGTH, nullptr);
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (ptr.pos != -1) /* match */
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
evbuffer_drain(inbuf, ptr.pos);
|
|
|
|
dbgmsg(handshake, "found it... looking setting to awaiting_crypto_provide");
|
|
|
|
setState(handshake, AWAITING_CRYPTO_PROVIDE);
|
|
|
|
return READ_NOW;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-10-14 19:26:38 +00:00
|
|
|
size_t const len = evbuffer_get_length(inbuf);
|
|
|
|
if (len > SHA_DIGEST_LENGTH)
|
|
|
|
{
|
|
|
|
evbuffer_drain(inbuf, len - SHA_DIGEST_LENGTH);
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
2021-10-14 19:26:38 +00:00
|
|
|
|
|
|
|
return READ_LATER;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static ReadState readCryptoProvide(tr_handshake* handshake, struct evbuffer* inbuf)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-21 07:40:57 +00:00
|
|
|
/* HASH('req2', SKEY) xor HASH('req3', S), ENCRYPT(VC, crypto_provide, len(PadC)) */
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
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;
|
2017-05-01 15:47:49 +00:00
|
|
|
size_t const needlen = SHA_DIGEST_LENGTH + /* HASH('req1', s) */
|
2017-04-21 07:40:57 +00:00
|
|
|
SHA_DIGEST_LENGTH + /* HASH('req2', SKEY) xor HASH('req3', S) */
|
2017-04-19 12:04:45 +00:00
|
|
|
VC_LENGTH + sizeof(crypto_provide) + sizeof(padc_len);
|
|
|
|
|
|
|
|
if (evbuffer_get_length(inbuf) < needlen)
|
|
|
|
{
|
|
|
|
return READ_LATER;
|
|
|
|
}
|
|
|
|
|
2017-04-21 07:40:57 +00:00
|
|
|
/* TODO: confirm they sent HASH('req1',S) here? */
|
2017-04-19 12:04:45 +00:00
|
|
|
evbuffer_drain(inbuf, SHA_DIGEST_LENGTH);
|
|
|
|
|
2017-04-21 07:40:57 +00:00
|
|
|
/* This next piece is HASH('req2', SKEY) xor HASH('req3', S) ...
|
2017-04-19 12:04:45 +00:00
|
|
|
* 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);
|
|
|
|
|
2017-05-13 22:38:31 +00:00
|
|
|
for (int i = 0; i < SHA_DIGEST_LENGTH; ++i)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
obfuscatedTorrentHash[i] = req2[i] ^ req3[i];
|
|
|
|
}
|
|
|
|
|
2021-10-26 18:02:07 +00:00
|
|
|
tr_torrent const* const tor = tr_torrentFindFromObfuscatedHash(handshake->session, obfuscatedTorrentHash);
|
|
|
|
if (tor != nullptr)
|
2008-04-17 03:48:56 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
bool const clientIsSeed = tr_torrentIsSeed(tor);
|
2021-09-15 00:18:09 +00:00
|
|
|
bool const peerIsSeed = tr_peerMgrPeerIsSeed(tor, tr_peerIoGetAddress(handshake->io, nullptr));
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "got INCOMING connection's encrypted handshake for torrent [%s]", tr_torrentName(tor));
|
|
|
|
tr_peerIoSetTorrentHash(handshake->io, tor->info.hash);
|
2009-01-07 22:06:40 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (clientIsSeed && peerIsSeed)
|
2008-04-17 03:48:56 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "another seed tried to reconnect to us!");
|
|
|
|
return tr_handshakeDone(handshake, false);
|
2008-04-17 03:48:56 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else
|
2008-04-17 03:48:56 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "can't find that torrent...");
|
|
|
|
return tr_handshakeDone(handshake, false);
|
2007-09-26 03:52:30 +00:00
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-21 07:40:57 +00:00
|
|
|
/* next part: ENCRYPT(VC, crypto_provide, len(PadC), */
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_cryptoDecryptInit(handshake->crypto);
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoReadBytes(handshake->io, inbuf, vc_in, VC_LENGTH);
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoReadUint32(handshake->io, inbuf, &crypto_provide);
|
|
|
|
handshake->crypto_provide = crypto_provide;
|
|
|
|
dbgmsg(handshake, "crypto_provide is %d", (int)crypto_provide);
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
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;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static ReadState readPadC(tr_handshake* handshake, struct evbuffer* inbuf)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2021-10-29 22:43:25 +00:00
|
|
|
uint16_t ia_len = 0;
|
2017-04-20 16:02:19 +00:00
|
|
|
size_t const needlen = handshake->pad_c_len + sizeof(uint16_t);
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (evbuffer_get_length(inbuf) < needlen)
|
|
|
|
{
|
|
|
|
return READ_LATER;
|
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* read the throwaway padc */
|
2021-10-26 18:02:07 +00:00
|
|
|
char* const padc = tr_new(char, handshake->pad_c_len);
|
2017-04-19 12:04:45 +00:00
|
|
|
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;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2020-11-05 22:46:21 +00:00
|
|
|
static ReadState readIA(tr_handshake* handshake, struct evbuffer const* inbuf)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
size_t const needlen = handshake->ia_len;
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "reading IA... have %zu, need %zu", evbuffer_get_length(inbuf), needlen);
|
|
|
|
|
|
|
|
if (evbuffer_get_length(inbuf) < needlen)
|
|
|
|
{
|
|
|
|
return READ_LATER;
|
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/**
|
2017-04-21 07:40:57 +00:00
|
|
|
*** B->A: ENCRYPT(VC, crypto_select, len(padD), padD), ENCRYPT2(Payload Stream)
|
2017-04-19 12:04:45 +00:00
|
|
|
**/
|
2007-09-26 03:52:30 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_cryptoEncryptInit(handshake->crypto);
|
2021-10-26 18:02:07 +00:00
|
|
|
evbuffer* const outbuf = evbuffer_new();
|
2007-09-26 03:52:30 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
/* 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 */
|
2021-10-26 18:02:07 +00:00
|
|
|
uint32_t const crypto_select = getCryptoSelect(handshake, handshake->crypto_provide);
|
2007-09-22 03:37:37 +00:00
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (crypto_select != 0)
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "selecting crypto mode '%d'", (int)crypto_select);
|
|
|
|
evbuffer_add_uint32(outbuf, crypto_select);
|
2008-09-23 19:11:04 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "peer didn't offer an encryption mode we like.");
|
|
|
|
evbuffer_free(outbuf);
|
|
|
|
return tr_handshakeDone(handshake, false);
|
2007-09-22 03:37:37 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "sending pad d");
|
|
|
|
|
2017-04-21 07:40:57 +00:00
|
|
|
/* ENCRYPT(VC, crypto_provide, len(PadD), PadD
|
2017-04-19 12:04:45 +00:00
|
|
|
* PadD is reserved for future extensions to the handshake...
|
|
|
|
* standard practice at this time is for it to be zero-length */
|
2007-09-22 03:37:37 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
uint16_t const len = 0;
|
2017-04-19 12:04:45 +00:00
|
|
|
evbuffer_add_uint16(outbuf, len);
|
2007-09-22 03:37:37 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* maybe de-encrypt our connection */
|
|
|
|
if (crypto_select == CRYPTO_PROVIDE_PLAINTEXT)
|
|
|
|
{
|
|
|
|
tr_peerIoWriteBuf(handshake->io, outbuf, false);
|
|
|
|
tr_peerIoSetEncryption(handshake->io, PEER_ENCRYPTION_NONE);
|
|
|
|
}
|
2009-05-30 21:45:40 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "sending handshake");
|
2007-09-22 03:37:37 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* send our handshake */
|
|
|
|
{
|
|
|
|
uint8_t msg[HANDSHAKE_SIZE];
|
2007-09-22 03:37:37 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
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;
|
2008-10-29 20:18:56 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static ReadState readPayloadStream(tr_handshake* handshake, struct evbuffer* inbuf)
|
2008-10-29 20:18:56 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
size_t const needlen = HANDSHAKE_SIZE;
|
2008-10-29 20:18:56 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
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 ... */
|
2021-10-26 18:02:07 +00:00
|
|
|
handshake_parse_err_t const i = parseHandshake(handshake, inbuf);
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "parseHandshake returned %d", i);
|
2008-10-29 20:18:56 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (i != HANDSHAKE_OK)
|
|
|
|
{
|
|
|
|
return tr_handshakeDone(handshake, false);
|
|
|
|
}
|
2008-10-29 20:18:56 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* we've completed the BT handshake... pass the work on to peer-msgs */
|
|
|
|
return tr_handshakeDone(handshake, true);
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
****
|
|
|
|
****
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2021-10-11 21:54:16 +00:00
|
|
|
static ReadState canRead(tr_peerIo* io, void* vhandshake, size_t* piece)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-06-13 02:24:09 +00:00
|
|
|
TR_ASSERT(tr_isPeerIo(io));
|
|
|
|
|
2021-09-12 17:41:49 +00:00
|
|
|
auto* handshake = static_cast<tr_handshake*>(vhandshake);
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
struct evbuffer* inbuf = tr_peerIoGetReadBuffer(io);
|
|
|
|
bool readyForMore = true;
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* no piece data in handshake */
|
|
|
|
*piece = 0;
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "handling canRead; state is [%s]", getStateName(handshake->state));
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2021-10-29 22:43:25 +00:00
|
|
|
ReadState ret = READ_NOW;
|
2017-04-19 12:04:45 +00:00
|
|
|
while (readyForMore)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
switch (handshake->state)
|
2008-10-29 19:30:17 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
case AWAITING_HANDSHAKE:
|
|
|
|
ret = readHandshake(handshake, inbuf);
|
2012-12-27 22:03:58 +00:00
|
|
|
break;
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case AWAITING_PEER_ID:
|
|
|
|
ret = readPeerId(handshake, inbuf);
|
2012-12-27 22:03:58 +00:00
|
|
|
break;
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case AWAITING_YA:
|
|
|
|
ret = readYa(handshake, inbuf);
|
2012-12-27 22:03:58 +00:00
|
|
|
break;
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case AWAITING_PAD_A:
|
|
|
|
ret = readPadA(handshake, inbuf);
|
2012-12-27 22:03:58 +00:00
|
|
|
break;
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case AWAITING_CRYPTO_PROVIDE:
|
|
|
|
ret = readCryptoProvide(handshake, inbuf);
|
2012-12-27 22:03:58 +00:00
|
|
|
break;
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case AWAITING_PAD_C:
|
|
|
|
ret = readPadC(handshake, inbuf);
|
2012-12-27 22:03:58 +00:00
|
|
|
break;
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case AWAITING_IA:
|
|
|
|
ret = readIA(handshake, inbuf);
|
2012-12-27 22:03:58 +00:00
|
|
|
break;
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case AWAITING_PAYLOAD_STREAM:
|
|
|
|
ret = readPayloadStream(handshake, inbuf);
|
2012-12-27 22:03:58 +00:00
|
|
|
break;
|
2008-10-29 20:18:56 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case AWAITING_YB:
|
|
|
|
ret = readYb(handshake, inbuf);
|
2012-12-27 22:03:58 +00:00
|
|
|
break;
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case AWAITING_VC:
|
|
|
|
ret = readVC(handshake, inbuf);
|
2012-12-27 22:03:58 +00:00
|
|
|
break;
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case AWAITING_CRYPTO_SELECT:
|
|
|
|
ret = readCryptoSelect(handshake, inbuf);
|
2012-12-27 22:03:58 +00:00
|
|
|
break;
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case AWAITING_PAD_D:
|
|
|
|
ret = readPadD(handshake, inbuf);
|
2012-12-27 22:03:58 +00:00
|
|
|
break;
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
default:
|
2021-09-15 02:23:53 +00:00
|
|
|
#ifdef TR_ENABLE_ASSERTS
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT_MSG(false, "unhandled handshake state %d", (int)handshake->state);
|
2021-09-15 02:23:53 +00:00
|
|
|
#else
|
|
|
|
ret = READ_ERR;
|
|
|
|
break;
|
|
|
|
#endif
|
2008-10-29 19:30:17 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
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;
|
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return ret;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static bool fireDoneFunc(tr_handshake* handshake, bool isConnected)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2021-10-22 02:40:55 +00:00
|
|
|
auto result = tr_handshake_result{};
|
|
|
|
result.handshake = handshake;
|
|
|
|
result.io = handshake->io;
|
|
|
|
result.readAnythingFromPeer = handshake->haveReadAnythingFromPeer;
|
|
|
|
result.isConnected = isConnected;
|
|
|
|
result.userData = handshake->done_func_user_data;
|
|
|
|
result.peer_id = handshake->peer_id;
|
|
|
|
bool const success = (*handshake->done_func)(result);
|
2017-04-19 12:04:45 +00:00
|
|
|
return success;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static void tr_handshakeFree(tr_handshake* handshake)
|
2008-12-20 22:19:34 +00:00
|
|
|
{
|
2021-09-15 00:18:09 +00:00
|
|
|
if (handshake->io != nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
tr_peerIoUnref(handshake->io); /* balanced by the ref in tr_handshakeNew */
|
|
|
|
}
|
2008-12-20 22:19:34 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
event_free(handshake->timeout_timer);
|
|
|
|
tr_free(handshake);
|
2008-12-20 22:19:34 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static ReadState tr_handshakeDone(tr_handshake* handshake, bool isOK)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "handshakeDone: %s", isOK ? "connected" : "aborting");
|
2021-09-15 00:18:09 +00:00
|
|
|
tr_peerIoSetIOFuncs(handshake->io, nullptr, nullptr, nullptr, nullptr);
|
2007-09-29 13:47:15 +00:00
|
|
|
|
2021-10-22 02:40:55 +00:00
|
|
|
bool const success = fireDoneFunc(handshake, isOK);
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_handshakeFree(handshake);
|
|
|
|
return success ? READ_LATER : READ_ERR;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
void tr_handshakeAbort(tr_handshake* handshake)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2021-09-15 00:18:09 +00:00
|
|
|
if (handshake != nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
tr_handshakeDone(handshake, false);
|
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static void gotError(tr_peerIo* io, short what, void* vhandshake)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
int errcode = errno;
|
2021-09-12 17:41:49 +00:00
|
|
|
auto* handshake = static_cast<tr_handshake*>(vhandshake);
|
2012-12-27 22:03:58 +00:00
|
|
|
|
2021-10-11 21:54:16 +00:00
|
|
|
if (io->socket.type == TR_PEER_SOCKET_TYPE_UTP && !tr_peerIoIsIncoming(io) && handshake->state == AWAITING_YB)
|
2012-12-27 22:03:58 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
/* This peer probably doesn't speak uTP. */
|
2012-12-27 22:03:58 +00:00
|
|
|
|
2021-10-26 18:02:07 +00:00
|
|
|
tr_torrent* const tor = tr_peerIoHasTorrentHash(io) ?
|
|
|
|
tr_torrentFindFromHash(handshake->session, tr_peerIoGetTorrentHash(io)) :
|
|
|
|
nullptr;
|
2012-12-27 22:03:58 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* Don't mark a peer as non-uTP unless it's really a connect failure. */
|
|
|
|
if ((errcode == ETIMEDOUT || errcode == ECONNREFUSED) && tr_isTorrent(tor))
|
|
|
|
{
|
2021-09-15 00:18:09 +00:00
|
|
|
tr_peerMgrSetUtpFailed(tor, tr_peerIoGetAddress(io, nullptr), true);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2012-12-27 22:03:58 +00:00
|
|
|
|
2017-06-28 15:46:06 +00:00
|
|
|
if (tr_peerIoReconnect(handshake->io) == 0)
|
2012-12-27 22:03:58 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
uint8_t msg[HANDSHAKE_SIZE];
|
|
|
|
buildHandshakeMessage(handshake, msg);
|
|
|
|
handshake->haveSentBitTorrentHandshake = true;
|
|
|
|
setReadState(handshake, AWAITING_HANDSHAKE);
|
|
|
|
tr_peerIoWriteBytes(handshake->io, msg, sizeof(msg), false);
|
2011-02-18 00:43:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* 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 */
|
2017-04-30 16:25:26 +00:00
|
|
|
if ((handshake->state == AWAITING_YB || handshake->state == AWAITING_VC) &&
|
|
|
|
handshake->encryptionMode != TR_ENCRYPTION_REQUIRED && tr_peerIoReconnect(handshake->io) == 0)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
uint8_t msg[HANDSHAKE_SIZE];
|
2009-05-30 21:45:40 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "handshake failed, trying plaintext...");
|
|
|
|
buildHandshakeMessage(handshake, msg);
|
|
|
|
handshake->haveSentBitTorrentHandshake = true;
|
|
|
|
setReadState(handshake, AWAITING_HANDSHAKE);
|
|
|
|
tr_peerIoWriteBytes(handshake->io, msg, sizeof(msg), false);
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "libevent got an error what==%d, errno=%d (%s)", (int)what, errno, tr_strerror(errno));
|
|
|
|
tr_handshakeDone(handshake, false);
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
***
|
|
|
|
**/
|
|
|
|
|
2021-10-24 16:41:54 +00:00
|
|
|
static void handshakeTimeout(evutil_socket_t /*s*/, short /*type*/, void* handshake)
|
2009-02-12 20:43:07 +00:00
|
|
|
{
|
2021-09-12 17:41:49 +00:00
|
|
|
tr_handshakeAbort(static_cast<tr_handshake*>(handshake));
|
2009-02-12 20:43:07 +00:00
|
|
|
}
|
|
|
|
|
2021-10-22 02:40:55 +00:00
|
|
|
tr_handshake* tr_handshakeNew(
|
|
|
|
tr_peerIo* io,
|
|
|
|
tr_encryption_mode encryptionMode,
|
|
|
|
tr_handshake_done_func done_func,
|
|
|
|
void* done_func_user_data)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_session* session = tr_peerIoGetSession(io);
|
|
|
|
|
2021-10-26 18:02:07 +00:00
|
|
|
auto* const handshake = tr_new0(tr_handshake, 1);
|
2017-04-19 12:04:45 +00:00
|
|
|
handshake->io = io;
|
|
|
|
handshake->crypto = tr_peerIoGetCrypto(io);
|
|
|
|
handshake->encryptionMode = encryptionMode;
|
2021-10-22 02:40:55 +00:00
|
|
|
handshake->done_func = done_func;
|
|
|
|
handshake->done_func_user_data = done_func_user_data;
|
2017-04-19 12:04:45 +00:00
|
|
|
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 */
|
2021-09-15 00:18:09 +00:00
|
|
|
tr_peerIoSetIOFuncs(handshake->io, canRead, nullptr, gotError, handshake);
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoSetEncryption(io, PEER_ENCRYPTION_NONE);
|
|
|
|
|
|
|
|
if (tr_peerIoIsIncoming(handshake->io))
|
2012-12-27 22:03:58 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
setReadState(handshake, AWAITING_HANDSHAKE);
|
2012-12-27 22:03:58 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else if (encryptionMode != TR_CLEAR_PREFERRED)
|
2012-12-27 22:03:58 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
sendYa(handshake);
|
2012-12-27 22:03:58 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
uint8_t msg[HANDSHAKE_SIZE];
|
|
|
|
buildHandshakeMessage(handshake, msg);
|
2009-05-30 21:45:40 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
handshake->haveSentBitTorrentHandshake = true;
|
|
|
|
setReadState(handshake, AWAITING_HANDSHAKE);
|
|
|
|
tr_peerIoWriteBytes(handshake->io, msg, sizeof(msg), false);
|
2007-11-08 21:20:08 +00:00
|
|
|
}
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return handshake;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2021-10-11 21:54:16 +00:00
|
|
|
tr_peerIo* tr_handshakeStealIO(tr_handshake* handshake)
|
2008-12-20 22:19:34 +00:00
|
|
|
{
|
2021-09-15 00:18:09 +00:00
|
|
|
TR_ASSERT(handshake != nullptr);
|
|
|
|
TR_ASSERT(handshake->io != nullptr);
|
2008-12-20 22:19:34 +00:00
|
|
|
|
2021-10-11 21:54:16 +00:00
|
|
|
tr_peerIo* io = handshake->io;
|
2021-09-15 00:18:09 +00:00
|
|
|
handshake->io = nullptr;
|
2017-04-19 12:04:45 +00:00
|
|
|
return io;
|
2008-12-20 22:19:34 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
tr_address const* tr_handshakeGetAddr(struct tr_handshake const* handshake, tr_port* port)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2021-09-15 00:18:09 +00:00
|
|
|
TR_ASSERT(handshake != nullptr);
|
|
|
|
TR_ASSERT(handshake->io != nullptr);
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return tr_peerIoGetAddress(handshake->io, port);
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|