Пример #1
0
static int
readCryptoSelect( tr_handshake *    handshake,
                  struct evbuffer * inbuf )
{
    uint32_t     crypto_select;
    uint16_t     pad_d_len;
    const size_t needlen = sizeof( uint32_t ) + sizeof( uint16_t );

    if( evbuffer_get_length( inbuf ) < needlen )
        return READ_LATER;

    tr_peerIoReadUint32( handshake->io, inbuf, &crypto_select );
    handshake->crypto_select = crypto_select;
    dbgmsg( handshake, "crypto select is %d", (int)crypto_select );
    if( !( crypto_select & getCryptoProvide( handshake ) ) )
    {
        dbgmsg( handshake,
                "peer selected an encryption option we didn't provide" );
        return tr_handshakeDone( handshake, FALSE );
    }

    tr_peerIoReadUint16( handshake->io, inbuf, &pad_d_len );
    dbgmsg( handshake, "pad_d_len is %d", (int)pad_d_len );

    if( pad_d_len > 512 )
    {
        dbgmsg( handshake, "encryption handshake: pad_d_len is too long" );
        return tr_handshakeDone( handshake, FALSE );
    }

    handshake->pad_d_len = pad_d_len;

    setState( handshake, AWAITING_PAD_D );
    return READ_NOW;
}
Пример #2
0
static int
readPeerId( tr_handshake    * handshake,
            struct evbuffer * inbuf )
{
    tr_bool peerIsGood;
    char client[128];
    tr_torrent * tor;
    const uint8_t * tor_peer_id;
    uint8_t peer_id[PEER_ID_LEN];

    if( evbuffer_get_length( inbuf ) < PEER_ID_LEN )
        return READ_LATER;

    /* peer id */
    tr_peerIoReadBytes( handshake->io, inbuf, peer_id, PEER_ID_LEN );
    tr_peerIoSetPeersId( handshake->io, peer_id );
    handshake->havePeerID = TRUE;
    tr_clientForId( client, sizeof( client ), peer_id );
    dbgmsg( handshake, "peer-id is [%s] ... isIncoming is %d", client,
            tr_peerIoIsIncoming( handshake->io ) );

    /* if we've somehow connected to ourselves, don't keep the connection */
    tor = tr_torrentFindFromHash( handshake->session, tr_peerIoGetTorrentHash( handshake->io ) );
    tor_peer_id = tor && tor->peer_id ? tor->peer_id : tr_getPeerId( );
    peerIsGood = memcmp( peer_id, tor_peer_id, PEER_ID_LEN ) != 0;
    dbgmsg( handshake, "isPeerGood == %d", (int)peerIsGood );
    return tr_handshakeDone( handshake, peerIsGood );
}
Пример #3
0
static int
readPeerId (tr_handshake    * handshake,
            struct evbuffer * inbuf)
{
  bool connected_to_self;
  char client[128];
  uint8_t peer_id[PEER_ID_LEN];
  tr_torrent * tor;

  if (evbuffer_get_length (inbuf) < PEER_ID_LEN)
    return READ_LATER;

  /* peer id */
  tr_peerIoReadBytes (handshake->io, inbuf, peer_id, PEER_ID_LEN);
  tr_peerIoSetPeersId (handshake->io, peer_id);
  handshake->havePeerID = true;
  tr_clientForId (client, sizeof (client), peer_id);
  dbgmsg (handshake, "peer-id is [%s] ... isIncoming is %d", client,
          tr_peerIoIsIncoming (handshake->io));

  /* if we've somehow connected to ourselves, don't keep the connection */
  tor = tr_torrentFindFromHash (handshake->session, tr_peerIoGetTorrentHash (handshake->io));
  connected_to_self = (tor != NULL) && !memcmp (peer_id, tr_torrentGetPeerId(tor), PEER_ID_LEN);

  return tr_handshakeDone (handshake, !connected_to_self);
}
Пример #4
0
static void
gotError( tr_peerIo  * io,
          short        what,
          void       * vhandshake )
{
    int errcode = errno;
    tr_handshake * handshake = vhandshake;

    if( io->utp_socket && !io->isIncoming && handshake->state == AWAITING_YB ) {
        /* This peer probably doesn't speak uTP. */
        tr_torrent *tor =
            tr_peerIoHasTorrentHash( io ) ?
            tr_torrentFindFromHash( handshake->session,
                                    tr_peerIoGetTorrentHash( io ) ) :
            NULL;
        /* Don't mark a peer as non-uTP unless it's really a connect failure. */
        if( tor && ( errcode == ETIMEDOUT || errcode == ECONNREFUSED ) ) {
            tr_torrentLock( tor );
            tr_peerMgrSetUtpFailed( tor,
                                    tr_peerIoGetAddress( io, NULL ),
                                    TRUE );
            tr_torrentUnlock( tor );
        }

        if( !tr_peerIoReconnect( handshake->io ) ) {
            uint8_t msg[HANDSHAKE_SIZE];
            buildHandshakeMessage( handshake, msg );
            handshake->haveSentBitTorrentHandshake = 1;
            setReadState( handshake, AWAITING_HANDSHAKE );
            tr_peerIoWriteBytes( handshake->io, msg, sizeof( msg ), FALSE );
        }
    }

    /* if the error happened while we were sending a public key, we might
     * have encountered a peer that doesn't do encryption... reconnect and
     * try a plaintext handshake */
    if( ( ( handshake->state == AWAITING_YB )
        || ( handshake->state == AWAITING_VC ) )
      && ( handshake->encryptionMode != TR_ENCRYPTION_REQUIRED )
      && ( !tr_peerIoReconnect( handshake->io ) ) )
    {
        uint8_t msg[HANDSHAKE_SIZE];

        dbgmsg( handshake, "handshake failed, trying plaintext..." );
        buildHandshakeMessage( handshake, msg );
        handshake->haveSentBitTorrentHandshake = 1;
        setReadState( handshake, AWAITING_HANDSHAKE );
        tr_peerIoWriteBytes( handshake->io, msg, sizeof( msg ), FALSE );
    }
    else
    {
        dbgmsg( handshake, "libevent got an error what==%d, errno=%d (%s)",
               (int)what, errno, tr_strerror( errno ) );
        tr_handshakeDone( handshake, FALSE );
    }
}
Пример #5
0
static int
readPayloadStream( tr_handshake    * handshake,
                   struct evbuffer * inbuf )
{
    int i;
    const size_t      needlen = HANDSHAKE_SIZE;

    dbgmsg( handshake, "reading payload stream... have %zu, need %zu",
            evbuffer_get_length( inbuf ), needlen );
    if( evbuffer_get_length( inbuf ) < needlen )
        return READ_LATER;

    /* parse the handshake ... */
    i = parseHandshake( handshake, inbuf );
    dbgmsg( handshake, "parseHandshake returned %d", i );
    if( i != HANDSHAKE_OK )
        return tr_handshakeDone( handshake, FALSE );

    /* we've completed the BT handshake... pass the work on to peer-msgs */
    return tr_handshakeDone( handshake, TRUE );
}
Пример #6
0
static int
readIA( tr_handshake *    handshake,
        struct evbuffer * inbuf )
{
    const size_t      needlen = handshake->ia_len;
    struct evbuffer * outbuf;
    uint32_t          crypto_select;

    dbgmsg( handshake, "reading IA... have %zu, need %zu",
            evbuffer_get_length( inbuf ), needlen );
    if( evbuffer_get_length( inbuf ) < needlen )
        return READ_LATER;

    /**
    ***  B->A: ENCRYPT(VC, crypto_select, len(padD), padD), ENCRYPT2(Payload Stream)
    **/

    tr_cryptoEncryptInit( handshake->crypto );
    outbuf = evbuffer_new( );

    dbgmsg( handshake, "sending vc" );
    /* send VC */
    {
        uint8_t vc[VC_LENGTH];
        memset( vc, 0, VC_LENGTH );
        evbuffer_add( outbuf, vc, VC_LENGTH );
    }

    /* send crypto_select */
    crypto_select = getCryptoSelect( handshake, handshake->crypto_provide );
    if( crypto_select )
    {
        dbgmsg( handshake, "selecting crypto mode '%d'", (int)crypto_select );
        evbuffer_add_uint32( outbuf, crypto_select );
    }
    else
    {
        dbgmsg( handshake, "peer didn't offer an encryption mode we like." );
        evbuffer_free( outbuf );
        return tr_handshakeDone( handshake, FALSE );
    }

    dbgmsg( handshake, "sending pad d" );
    /* ENCRYPT(VC, crypto_provide, len(PadD), PadD
     * PadD is reserved for future extensions to the handshake...
     * standard practice at this time is for it to be zero-length */
    {
        const uint16_t len = 0;
        evbuffer_add_uint16( outbuf, len );
    }

    /* maybe de-encrypt our connection */
    if( crypto_select == CRYPTO_PROVIDE_PLAINTEXT )
    {
        tr_peerIoWriteBuf( handshake->io, outbuf, FALSE );
        tr_peerIoSetEncryption( handshake->io, PEER_ENCRYPTION_NONE );
    }

    dbgmsg( handshake, "sending handshake" );
    /* send our handshake */
    {
        uint8_t msg[HANDSHAKE_SIZE];
        buildHandshakeMessage( handshake, msg );

        evbuffer_add( outbuf, msg, sizeof( msg ) );
        handshake->haveSentBitTorrentHandshake = 1;
    }

    /* send it out */
    tr_peerIoWriteBuf( handshake->io, outbuf, FALSE );
    evbuffer_free( outbuf );

    /* now await the handshake */
    setState( handshake, AWAITING_PAYLOAD_STREAM );
    return READ_NOW;
}
Пример #7
0
static int
readCryptoProvide( tr_handshake *    handshake,
                   struct evbuffer * inbuf )
{
    /* HASH('req2', SKEY) xor HASH('req3', S), ENCRYPT(VC, crypto_provide,
      len(PadC)) */

    int          i;
    uint8_t      vc_in[VC_LENGTH];
    uint8_t      req2[SHA_DIGEST_LENGTH];
    uint8_t      req3[SHA_DIGEST_LENGTH];
    uint8_t      obfuscatedTorrentHash[SHA_DIGEST_LENGTH];
    uint16_t     padc_len = 0;
    uint32_t     crypto_provide = 0;
    const size_t needlen = SHA_DIGEST_LENGTH /* HASH('req1',s) */
                           + SHA_DIGEST_LENGTH /* HASH('req2', SKEY) xor
                                                 HASH('req3', S) */
                           + VC_LENGTH
                           + sizeof( crypto_provide )
                           + sizeof( padc_len );
    tr_torrent * tor = NULL;

    if( evbuffer_get_length( inbuf ) < needlen )
        return READ_LATER;

    /* TODO: confirm they sent HASH('req1',S) here? */
    evbuffer_drain( inbuf, SHA_DIGEST_LENGTH );

    /* This next piece is HASH('req2', SKEY) xor HASH('req3', S) ...
     * we can get the first half of that (the obufscatedTorrentHash)
     * by building the latter and xor'ing it with what the peer sent us */
    dbgmsg( handshake, "reading obfuscated torrent hash..." );
    evbuffer_remove( inbuf, req2, SHA_DIGEST_LENGTH );
    tr_sha1( req3, "req3", 4, handshake->mySecret, KEY_LEN, NULL );
    for( i = 0; i < SHA_DIGEST_LENGTH; ++i )
        obfuscatedTorrentHash[i] = req2[i] ^ req3[i];
    if(( tor = tr_torrentFindFromObfuscatedHash( handshake->session, obfuscatedTorrentHash )))
    {
        const tr_bool clientIsSeed = tr_torrentIsSeed( tor );
        const tr_bool peerIsSeed = tr_peerMgrPeerIsSeed( tor, tr_peerIoGetAddress( handshake->io, NULL ) );
        dbgmsg( handshake, "got INCOMING connection's encrypted handshake for torrent [%s]",
                tr_torrentName( tor ) );
        tr_peerIoSetTorrentHash( handshake->io, tor->info.hash );

        if( clientIsSeed && peerIsSeed )
        {
            dbgmsg( handshake, "another seed tried to reconnect to us!" );
            return tr_handshakeDone( handshake, FALSE );
        }
    }
    else
    {
        dbgmsg( handshake, "can't find that torrent..." );
        return tr_handshakeDone( handshake, FALSE );
    }

    /* next part: ENCRYPT(VC, crypto_provide, len(PadC), */

    tr_cryptoDecryptInit( handshake->crypto );

    tr_peerIoReadBytes( handshake->io, inbuf, vc_in, VC_LENGTH );

    tr_peerIoReadUint32( handshake->io, inbuf, &crypto_provide );
    handshake->crypto_provide = crypto_provide;
    dbgmsg( handshake, "crypto_provide is %d", (int)crypto_provide );

    tr_peerIoReadUint16( handshake->io, inbuf, &padc_len );
    dbgmsg( handshake, "padc is %d", (int)padc_len );
    handshake->pad_c_len = padc_len;
    setState( handshake, AWAITING_PAD_C );
    return READ_NOW;
}
Пример #8
0
static int
readHandshake( tr_handshake *    handshake,
               struct evbuffer * inbuf )
{
    uint8_t   pstrlen;
    uint8_t * pstr;
    uint8_t   reserved[HANDSHAKE_FLAGS_LEN];
    uint8_t   hash[SHA_DIGEST_LENGTH];

    dbgmsg( handshake, "payload: need %d, got %zu",
            INCOMING_HANDSHAKE_LEN, evbuffer_get_length( inbuf ) );

    if( evbuffer_get_length( inbuf ) < INCOMING_HANDSHAKE_LEN )
        return READ_LATER;

    handshake->haveReadAnythingFromPeer = TRUE;

    pstrlen = evbuffer_pullup( inbuf, 1 )[0]; /* peek, don't read. We may be
                                                 handing inbuf to AWAITING_YA */

    if( pstrlen == 19 ) /* unencrypted */
    {
        tr_peerIoSetEncryption( handshake->io, PEER_ENCRYPTION_NONE );

        if( handshake->encryptionMode == TR_ENCRYPTION_REQUIRED )
        {
            dbgmsg( handshake,
                    "peer is unencrypted, and we're disallowing that" );
            return tr_handshakeDone( handshake, FALSE );
        }
    }
    else /* encrypted or corrupt */
    {
        tr_peerIoSetEncryption( handshake->io, PEER_ENCRYPTION_RC4 );

        if( tr_peerIoIsIncoming( handshake->io ) )
        {
            dbgmsg( handshake,
                    "I think peer is sending us an encrypted handshake..." );
            setState( handshake, AWAITING_YA );
            return READ_NOW;
        }
        tr_cryptoDecrypt( handshake->crypto, 1, &pstrlen, &pstrlen );

        if( pstrlen != 19 )
        {
            dbgmsg( handshake,
                    "I think peer has sent us a corrupt handshake..." );
            return tr_handshakeDone( handshake, FALSE );
        }
    }

    evbuffer_drain( inbuf, 1 );

    /* pstr (BitTorrent) */
    pstr = tr_new( uint8_t, pstrlen + 1 );
    tr_peerIoReadBytes( handshake->io, inbuf, pstr, pstrlen );
    pstr[pstrlen] = '\0';
    if( strcmp( (char*)pstr, "BitTorrent protocol" ) )
    {
        tr_free( pstr );
        return tr_handshakeDone( handshake, FALSE );
    }
    tr_free( pstr );

    /* reserved bytes */
    tr_peerIoReadBytes( handshake->io, inbuf, reserved, sizeof( reserved ) );

    /**
    *** Extensions
    **/

    tr_peerIoEnableLTEP( handshake->io, HANDSHAKE_HAS_LTEP( reserved ) );

    tr_peerIoEnableFEXT( handshake->io, HANDSHAKE_HAS_FASTEXT( reserved ) );

    tr_peerIoEnableDHT( handshake->io, HANDSHAKE_HAS_DHT( reserved ) );

    /* torrent hash */
    tr_peerIoReadBytes( handshake->io, inbuf, hash, sizeof( hash ) );
    if( tr_peerIoIsIncoming( handshake->io ) )
    {
        if( !tr_torrentExists( handshake->session, hash ) )
        {
            dbgmsg( handshake, "peer is trying to connect to us for a torrent we don't have." );
            return tr_handshakeDone( handshake, FALSE );
        }
        else
        {
            assert( !tr_peerIoHasTorrentHash( handshake->io ) );
            tr_peerIoSetTorrentHash( handshake->io, hash );
        }
    }
    else /* outgoing */
    {
        assert( tr_peerIoHasTorrentHash( handshake->io ) );
        if( memcmp( hash, tr_peerIoGetTorrentHash( handshake->io ),
                    SHA_DIGEST_LENGTH ) )
        {
            dbgmsg( handshake, "peer returned the wrong hash. wtf?" );
            return tr_handshakeDone( handshake, FALSE );
        }
    }

    /**
    ***  If it's an incoming message, we need to send a response handshake
    **/

    if( !handshake->haveSentBitTorrentHandshake )
    {
        uint8_t msg[HANDSHAKE_SIZE];
        buildHandshakeMessage( handshake, msg );
        tr_peerIoWriteBytes( handshake->io, msg, sizeof( msg ), FALSE );
        handshake->haveSentBitTorrentHandshake = 1;
    }

    setReadState( handshake, AWAITING_PEER_ID );
    return READ_NOW;
}
Пример #9
0
void
tr_handshakeAbort( tr_handshake * handshake )
{
    if( handshake != NULL )
        tr_handshakeDone( handshake, FALSE );
}
Пример #10
0
static int
readYb (tr_handshake * handshake, struct evbuffer * inbuf)
{
  int isEncrypted;
  const uint8_t * secret;
  uint8_t yb[KEY_LEN];
  struct evbuffer * outbuf;
  size_t needlen = HANDSHAKE_NAME_LEN;

  if (evbuffer_get_length (inbuf) < needlen)
    return READ_LATER;

  isEncrypted = memcmp (evbuffer_pullup (inbuf, HANDSHAKE_NAME_LEN), HANDSHAKE_NAME, HANDSHAKE_NAME_LEN);
  if (isEncrypted)
    {
      needlen = KEY_LEN;

      if (evbuffer_get_length (inbuf) < needlen)
        return READ_LATER;
    }

  dbgmsg (handshake, "got an %s handshake", (isEncrypted ? "encrypted" : "plain"));

  tr_peerIoSetEncryption (handshake->io, isEncrypted ? PEER_ENCRYPTION_RC4
                                                     : PEER_ENCRYPTION_NONE);
  if (!isEncrypted)
    {
      setState (handshake, AWAITING_HANDSHAKE);
      return READ_NOW;
    }

  handshake->haveReadAnythingFromPeer = true;

  /* compute the secret */
  evbuffer_remove (inbuf, yb, KEY_LEN);
  secret = tr_cryptoComputeSecret (handshake->crypto, yb);
  memcpy (handshake->mySecret, secret, KEY_LEN);

  /* now send these: HASH ('req1', S), HASH ('req2', SKEY) xor HASH ('req3', S),
   * ENCRYPT (VC, crypto_provide, len (PadC), PadC, len (IA)), ENCRYPT (IA) */
  outbuf = evbuffer_new ();

  /* HASH ('req1', S) */
  {
    uint8_t req1[SHA_DIGEST_LENGTH];
    tr_sha1 (req1, "req1", 4, secret, KEY_LEN, NULL);
    evbuffer_add (outbuf, req1, SHA_DIGEST_LENGTH);
  }

  /* HASH ('req2', SKEY) xor HASH ('req3', S) */
  {
    int i;
    uint8_t req2[SHA_DIGEST_LENGTH];
    uint8_t req3[SHA_DIGEST_LENGTH];
    uint8_t buf[SHA_DIGEST_LENGTH];

    tr_sha1 (req2, "req2", 4, tr_cryptoGetTorrentHash (handshake->crypto), SHA_DIGEST_LENGTH, NULL);
    tr_sha1 (req3, "req3", 4, secret, KEY_LEN, NULL);

    for (i=0; i<SHA_DIGEST_LENGTH; ++i)
      buf[i] = req2[i] ^ req3[i];

    evbuffer_add (outbuf, buf, SHA_DIGEST_LENGTH);
  }

  /* ENCRYPT (VC, crypto_provide, len (PadC), PadC
   * PadC is reserved for future extensions to the handshake...
   * standard practice at this time is for it to be zero-length */
  {
    uint8_t vc[VC_LENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0 };

    tr_peerIoWriteBuf (handshake->io, outbuf, false);
    tr_cryptoEncryptInit (handshake->crypto);
    tr_peerIoSetEncryption (handshake->io, PEER_ENCRYPTION_RC4);

    evbuffer_add        (outbuf, vc, VC_LENGTH);
    evbuffer_add_uint32 (outbuf, getCryptoProvide (handshake));
    evbuffer_add_uint16 (outbuf, 0);
  }

  /* ENCRYPT len (IA)), ENCRYPT (IA) */
  {
    uint8_t msg[HANDSHAKE_SIZE];
    if (!buildHandshakeMessage (handshake, msg))
      return tr_handshakeDone (handshake, false);

    evbuffer_add_uint16 (outbuf, sizeof (msg));
    evbuffer_add        (outbuf, msg, sizeof (msg));

    handshake->haveSentBitTorrentHandshake = 1;
  }

  /* send it */
  tr_cryptoDecryptInit (handshake->crypto);
  setReadState (handshake, AWAITING_VC);
  tr_peerIoWriteBuf (handshake->io, outbuf, false);

  /* cleanup */
  evbuffer_free (outbuf);
  return READ_LATER;
}
Пример #11
0
void
tr_handshakeAbort (tr_handshake * handshake)
{
  if (handshake != NULL)
    tr_handshakeDone (handshake, false);
}