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
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2007-11-09 20:07:52 +00:00
|
|
|
#include <assert.h>
|
2007-09-20 16:32:01 +00:00
|
|
|
#include <errno.h>
|
2017-04-21 07:40:57 +00:00
|
|
|
#include <string.h> /* 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"
|
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"
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
/* 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
|
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
|
|
|
/* 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 */
|
2017-04-19 12:04:45 +00:00
|
|
|
#define HANDSHAKE_GET_EXTPREF(reserved) ((reserved)[5] & 0x03)
|
|
|
|
#define HANDSHAKE_SET_EXTPREF(reserved, val) ((reserved)[5] |= 0x03 & (val))
|
2007-09-20 16:32:01 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
***
|
|
|
|
**/
|
|
|
|
|
2015-03-15 11:43:32 +00:00
|
|
|
typedef enum
|
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
|
2015-03-15 11:43:32 +00:00
|
|
|
}
|
|
|
|
handshake_state_t;
|
|
|
|
|
|
|
|
struct tr_handshake
|
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
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;
|
2007-09-20 16:32:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
***
|
|
|
|
**/
|
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
#define dbgmsg(handshake, ...) \
|
2017-04-19 12:04:45 +00:00
|
|
|
do \
|
|
|
|
{ \
|
|
|
|
if (tr_logGetDeepEnabled()) \
|
|
|
|
{ \
|
|
|
|
tr_logAddDeep(__FILE__, __LINE__, tr_peerIoGetAddrStr(handshake->io), __VA_ARGS__); \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
while (0)
|
|
|
|
|
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
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
static char const* const state_strings[N_STATES] =
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
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
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
unsigned char const* peer_id = NULL;
|
|
|
|
uint8_t const* torrentHash;
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_torrent* tor;
|
|
|
|
bool success;
|
2013-08-05 04:39:43 +00:00
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if ((torrentHash = tr_cryptoGetTorrentHash(handshake->crypto)) != NULL)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2017-04-30 16:25:26 +00:00
|
|
|
if ((tor = tr_torrentFindFromHash(handshake->session, torrentHash)) != NULL)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
peer_id = tr_torrentGetPeerId(tor);
|
|
|
|
}
|
|
|
|
}
|
2013-08-05 04:39:43 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (peer_id == NULL)
|
2013-08-05 04:39:43 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
success = false;
|
2013-08-05 04:39:43 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else
|
2013-08-05 04:39:43 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
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;
|
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
|
|
|
|
2015-03-15 11:43:32 +00:00
|
|
|
typedef enum
|
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,
|
2015-03-15 11:43:32 +00:00
|
|
|
}
|
|
|
|
handshake_parse_err_t;
|
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];
|
|
|
|
tr_torrent* tor;
|
|
|
|
uint8_t peer_id[PEER_ID_LEN];
|
|
|
|
|
|
|
|
dbgmsg(handshake, "payload: need %d, got %zu", HANDSHAKE_SIZE, evbuffer_get_length(inbuf));
|
|
|
|
|
|
|
|
if (evbuffer_get_length(inbuf) < HANDSHAKE_SIZE)
|
|
|
|
{
|
|
|
|
return 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));
|
|
|
|
assert(tr_peerIoHasTorrentHash(handshake->io));
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* peer_id */
|
|
|
|
tr_peerIoReadBytes(handshake->io, inbuf, peer_id, sizeof(peer_id));
|
|
|
|
tr_peerIoSetPeersId(handshake->io, peer_id);
|
2007-09-22 03:37:37 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* peer id */
|
|
|
|
handshake->havePeerID = true;
|
|
|
|
dbgmsg(handshake, "peer-id is [%*.*s]", PEER_ID_LEN, PEER_ID_LEN, peer_id);
|
2008-12-10 05:20:28 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tor = tr_torrentFindFromHash(handshake->session, hash);
|
|
|
|
|
|
|
|
if (memcmp(peer_id, tr_torrentGetPeerId(tor), PEER_ID_LEN) == 0)
|
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
|
|
|
int len;
|
2017-04-20 16:02:19 +00:00
|
|
|
uint8_t const* public_key;
|
2017-04-19 12:04:45 +00:00
|
|
|
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);
|
2017-04-30 16:25:26 +00:00
|
|
|
assert(public_key != NULL);
|
2017-04-19 12:04:45 +00:00
|
|
|
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);
|
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 i;
|
|
|
|
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-04-19 12:04:45 +00:00
|
|
|
for (i = 0; i < nChoices; ++i)
|
|
|
|
{
|
|
|
|
if (crypto_provide & choices[i])
|
|
|
|
{
|
|
|
|
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
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_cryptoSecretKeySha1(handshake->crypto, name, 4, NULL, 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
|
|
|
bool isEncrypted;
|
|
|
|
uint8_t yb[KEY_LEN];
|
|
|
|
struct evbuffer* outbuf;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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) */
|
2017-04-19 12:04:45 +00:00
|
|
|
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
|
|
|
{
|
|
|
|
int i;
|
|
|
|
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
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_sha1(req2, "req2", 4, tr_cryptoGetTorrentHash(handshake->crypto), SHA_DIGEST_LENGTH, NULL);
|
|
|
|
computeRequestHash(handshake, "req3", req3);
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
for (i = 0; i < SHA_DIGEST_LENGTH; ++i)
|
|
|
|
{
|
|
|
|
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-19 12:04:45 +00:00
|
|
|
uint16_t pad_d_len;
|
|
|
|
uint32_t crypto_select;
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
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
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_peerIoSetEncryption(handshake->io, 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
|
|
|
uint8_t pstrlen;
|
|
|
|
uint8_t pstr[20];
|
|
|
|
uint8_t reserved[HANDSHAKE_FLAGS_LEN];
|
|
|
|
uint8_t hash[SHA_DIGEST_LENGTH];
|
2007-09-22 03:37:37 +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
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
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) */
|
|
|
|
assert(pstrlen == 19);
|
|
|
|
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 */
|
|
|
|
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 */
|
|
|
|
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
|
|
|
}
|
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
|
|
|
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-04-19 12:04:45 +00:00
|
|
|
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
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
bool connected_to_self;
|
|
|
|
char client[128];
|
|
|
|
uint8_t peer_id[PEER_ID_LEN];
|
|
|
|
tr_torrent* tor;
|
2008-01-01 00:20:07 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (evbuffer_get_length(inbuf) < PEER_ID_LEN)
|
|
|
|
{
|
|
|
|
return READ_LATER;
|
|
|
|
}
|
2008-01-01 00:20:07 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* 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));
|
2012-12-27 22:03:58 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* if we've somehow connected to ourselves, don't keep the connection */
|
|
|
|
tor = tr_torrentFindFromHash(handshake->session, tr_peerIoGetTorrentHash(handshake->io));
|
2017-04-30 16:25:26 +00:00
|
|
|
connected_to_self = tor != NULL && memcmp(peer_id, tr_torrentGetPeerId(tor), PEER_ID_LEN) == 0;
|
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
|
|
|
uint8_t ya[KEY_LEN];
|
2017-05-01 15:46:41 +00:00
|
|
|
uint8_t* walk;
|
|
|
|
uint8_t outbuf[KEY_LEN + PadB_MAXLEN];
|
2017-04-20 16:02:19 +00:00
|
|
|
uint8_t const* myKey;
|
2017-04-19 12:04:45 +00:00
|
|
|
int len;
|
|
|
|
|
|
|
|
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 */
|
|
|
|
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");
|
|
|
|
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;
|
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) */
|
2017-04-20 16:02:19 +00:00
|
|
|
struct evbuffer_ptr ptr = evbuffer_search(inbuf, (char const*)handshake->myReq1, SHA_DIGEST_LENGTH, NULL);
|
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
|
|
|
else
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
size_t const len = evbuffer_get_length(inbuf);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
if (len > SHA_DIGEST_LENGTH)
|
|
|
|
{
|
|
|
|
evbuffer_drain(inbuf, len - SHA_DIGEST_LENGTH);
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
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;
|
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);
|
|
|
|
|
|
|
|
for (i = 0; i < SHA_DIGEST_LENGTH; ++i)
|
|
|
|
{
|
|
|
|
obfuscatedTorrentHash[i] = req2[i] ^ req3[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((tor = tr_torrentFindFromObfuscatedHash(handshake->session, obfuscatedTorrentHash)))
|
2008-04-17 03:48:56 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
bool const clientIsSeed = tr_torrentIsSeed(tor);
|
|
|
|
bool const peerIsSeed = tr_peerMgrPeerIsSeed(tor, tr_peerIoGetAddress(handshake->io, NULL));
|
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
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
char* padc;
|
|
|
|
uint16_t ia_len;
|
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 */
|
|
|
|
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;
|
2007-09-20 16:32:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static ReadState readIA(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->ia_len;
|
2017-04-19 12:04:45 +00:00
|
|
|
struct evbuffer* outbuf;
|
|
|
|
uint32_t crypto_select;
|
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);
|
|
|
|
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 */
|
|
|
|
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-19 12:04:45 +00:00
|
|
|
handshake_parse_err_t i;
|
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 ... */
|
|
|
|
i = parseHandshake(handshake, inbuf);
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
****
|
|
|
|
****
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static ReadState canRead(struct tr_peerIo* io, void* arg, size_t* piece)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
ReadState ret;
|
|
|
|
tr_handshake* handshake = arg;
|
|
|
|
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
|
|
|
assert(tr_isPeerIo(io));
|
2009-10-10 17:37:34 +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
|
|
|
|
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:
|
|
|
|
assert(0);
|
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
|
|
|
{
|
2017-04-30 16:25:26 +00:00
|
|
|
uint8_t const* peer_id = (isConnected && handshake->havePeerID) ? tr_peerIoGetPeersId(handshake->io) : NULL;
|
2017-04-20 16:02:19 +00:00
|
|
|
bool const success = (*handshake->doneCB)(handshake, handshake->io, handshake->haveReadAnythingFromPeer, isConnected,
|
2017-04-19 12:04:45 +00:00
|
|
|
peer_id, handshake->doneUserData);
|
|
|
|
|
|
|
|
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
|
|
|
{
|
2017-04-30 16:25:26 +00:00
|
|
|
if (handshake->io != NULL)
|
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
|
|
|
bool success;
|
2008-09-19 17:03:25 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
dbgmsg(handshake, "handshakeDone: %s", isOK ? "connected" : "aborting");
|
|
|
|
tr_peerIoSetIOFuncs(handshake->io, NULL, NULL, NULL, NULL);
|
2007-09-29 13:47:15 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
success = fireDoneFunc(handshake, isOK);
|
2007-09-29 13:47:15 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_handshakeFree(handshake);
|
2009-01-06 00:24:44 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
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
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
if (handshake != NULL)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
tr_handshake* handshake = vhandshake;
|
2012-12-27 22:03:58 +00:00
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (io->utp_socket != NULL && !io->isIncoming && 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
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_torrent* tor;
|
2012-12-27 22:03:58 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (tr_peerIoHasTorrentHash(io))
|
|
|
|
{
|
|
|
|
tor = tr_torrentFindFromHash(handshake->session, tr_peerIoGetTorrentHash(io));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tor = NULL;
|
|
|
|
}
|
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))
|
|
|
|
{
|
|
|
|
tr_peerMgrSetUtpFailed(tor, tr_peerIoGetAddress(io, NULL), true);
|
|
|
|
}
|
2012-12-27 22:03:58 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (!tr_peerIoReconnect(handshake->io))
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
***
|
|
|
|
**/
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static void handshakeTimeout(evutil_socket_t foo UNUSED, short bar UNUSED, void* handshake)
|
2009-02-12 20:43:07 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_handshakeAbort(handshake);
|
2009-02-12 20:43:07 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_handshake* tr_handshakeNew(tr_peerIo* io, tr_encryption_mode encryptionMode, handshakeDoneCB doneCB, void* doneUserData)
|
2007-09-20 16:32:01 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
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))
|
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
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
struct tr_peerIo* tr_handshakeGetIO(tr_handshake* handshake)
|
2008-09-17 19:44:24 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
assert(handshake != NULL);
|
|
|
|
assert(handshake->io != NULL);
|
2008-09-17 19:44:24 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return handshake->io;
|
2008-09-17 19:44:24 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
struct tr_peerIo* tr_handshakeStealIO(tr_handshake* handshake)
|
2008-12-20 22:19:34 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
struct tr_peerIo* io;
|
2008-12-20 22:19:34 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
assert(handshake != NULL);
|
|
|
|
assert(handshake->io != NULL);
|
2008-12-20 22:19:34 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
io = handshake->io;
|
|
|
|
handshake->io = NULL;
|
|
|
|
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
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
assert(handshake != NULL);
|
|
|
|
assert(handshake->io != NULL);
|
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
|
|
|
}
|