public void TestHandshake(byte[] buffer, CustomConnection connection)
        {
            // 1) Send local handshake
            SendMessage(new HandshakeMessage(rig.Manager.Torrent.infoHash, new string('g', 20), VersionInfo.ProtocolStringV100, true, false), connection);

            // 2) Receive remote handshake
            if (buffer == null || buffer.Length == 0)
            {
                buffer = new byte[68];
                Receive(connection, buffer, 0, 68);
                decryptor.Decrypt(buffer);
            }

            HandshakeMessage handshake = new HandshakeMessage();

            handshake.Decode(buffer, 0, buffer.Length);
            Assert.Equal(rig.Engine.PeerId, handshake.PeerId);
            Assert.Equal(VersionInfo.ProtocolStringV100, handshake.ProtocolString);
            Assert.Equal(ClientEngine.SupportsFastPeer, handshake.SupportsFastPeer);
            Assert.Equal(ClientEngine.SupportsExtended, handshake.SupportsExtendedMessaging);

            // 2) Send local bitfield
            SendMessage(new BitfieldMessage(rig.Manager.Bitfield), connection);

            // 3) Receive remote bitfield - have none
            PeerMessage message = ReceiveMessage(connection);

            Assert.True(message is HaveNoneMessage || message is BitfieldMessage, "HaveNone");

            // 4) Send a few allowed fast
            SendMessage(new AllowedFastMessage(1), connection);
            SendMessage(new AllowedFastMessage(2), connection);
            SendMessage(new AllowedFastMessage(3), connection);
            SendMessage(new AllowedFastMessage(0), connection);

            // 5) Receive a few allowed fast
            ReceiveMessage(connection);
            ReceiveMessage(connection);
            ReceiveMessage(connection);
            ReceiveMessage(connection);
            ReceiveMessage(connection);
            ReceiveMessage(connection);
            ReceiveMessage(connection);
            ReceiveMessage(connection);
            ReceiveMessage(connection);
            ReceiveMessage(connection);
        }
        public async Task EncryptorFactoryPeerAPlain()
        {
            await rig.Engine.StartAll();

            rig.AddConnection(conn.Incoming);

            HandshakeMessage message = new HandshakeMessage(rig.Manager.InfoHash, "ABC123ABC123ABC123AB", VersionInfo.ProtocolStringV100);

            byte[] buffer = message.Encode();

            await conn.Outgoing.SendAsync(buffer, 0, buffer.Length);

            await conn.Outgoing.ReceiveAsync(buffer, 0, buffer.Length);

            message.Decode(buffer, 0, buffer.Length);
            Assert.AreEqual(VersionInfo.ProtocolStringV100, message.ProtocolString);
        }
        async void ConnectionReceived(object sender, NewConnectionEventArgs e)
        {
            await ClientEngine.MainLoop;

            try
            {
                if (Engine.ConnectionManager.ShouldBanPeer(e.Peer))
                {
                    e.Connection.Dispose();
                    return;
                }

                if (!e.Connection.IsIncoming)
                {
                    var id = new PeerId(e.Peer, e.TorrentManager, e.Connection);
                    id.LastMessageSent.Restart();
                    id.LastMessageReceived.Restart();

                    Engine.ConnectionManager.ProcessNewOutgoingConnection(id);
                    return;
                }

                Logger.Log(e.Connection, "ListenManager - ConnectionReceived");

                var skeys = new List <InfoHash>();
                for (int i = 0; i < Engine.Torrents.Count; i++)
                {
                    skeys.Add(Engine.Torrents[i].InfoHash);
                }

                var result = await EncryptorFactory.CheckIncomingConnectionAsync(e.Connection, e.Peer.AllowedEncryption, Engine.Settings, HandshakeMessage.HandshakeLength, skeys.ToArray());

                var handshake = new HandshakeMessage();
                handshake.Decode(result.InitialData, 0, result.InitialData.Length);
                if (!await HandleHandshake(e.Peer, e.Connection, handshake, result.Decryptor, result.Encryptor))
                {
                    e.Connection.Dispose();
                }
            }
            catch
            {
                e.Connection.Dispose();
            }
        }
Exemple #4
0
        private static void HandshakeReceived(bool succeeded, int count, object state)
        {
            var result     = (EncryptorAsyncResult)state;
            var connection = result.Id.Connection;

            try
            {
                if (!succeeded)
                {
                    throw new EncryptionException("Couldn't receive the handshake");
                }

                result.Available += count;
                var message = new HandshakeMessage();
                message.Decode(result.Buffer, 0, result.Buffer.Length);
                var valid  = message.ProtocolString == VersionInfo.ProtocolStringV100;
                var usable = CheckRc4(result.Id);

                var canUseRc4 = Toolbox.HasEncryption(usable, EncryptionTypes.RC4Header) ||
                                Toolbox.HasEncryption(usable, EncryptionTypes.RC4Full);
                // If encryption is disabled and we received an invalid handshake - abort!
                if (valid)
                {
                    result.InitialData = result.Buffer;
                    result.Complete();
                    return;
                }
                if (!canUseRc4)
                {
                    result.Complete(new EncryptionException("Invalid handshake received and no decryption works"));
                    return;
                }
                // The data we just received was part of an encrypted handshake and was *not* the BitTorrent handshake
                result.EncSocket = new PeerBEncryption(result.SKeys, EncryptionTypes.All);
                result.EncSocket.BeginHandshake(connection, result.Buffer, 0, result.Buffer.Length,
                                                CompletedEncryptedHandshakeCallback, result);
            }
            catch (Exception ex)
            {
                result.Complete(ex);
            }
        }
Exemple #5
0
        private void PeerBTest(EncryptionTypes encryption)
        {
            rig.Engine.Settings.AllowedEncryption = encryption;
            rig.Engine.StartAll();
            rig.AddConnection(conn.Outgoing);

            PeerBEncryption a      = new PeerBEncryption(new InfoHash[] { rig.Manager.InfoHash }, EncryptionTypes.All);
            IAsyncResult    result = a.BeginHandshake(conn.Incoming, null, null);

#if NETSTANDARD1_5
            if (!result.AsyncWaitHandle.WaitOne(4000))
#else
            if (!result.AsyncWaitHandle.WaitOne(4000, true))
#endif
            { Assert.True(false, "Handshake timed out"); }
            a.EndHandshake(result);

            HandshakeMessage message = new HandshakeMessage();
            byte[]           buffer  = new byte[68];

            conn.Incoming.EndReceive(conn.Incoming.BeginReceive(buffer, 0, buffer.Length, null, null));

            a.Decryptor.Decrypt(buffer);
            message.Decode(buffer, 0, buffer.Length);
            Assert.Equal(VersionInfo.ProtocolStringV100, message.ProtocolString);
            if (encryption == EncryptionTypes.RC4Full)
            {
                Assert.True(a.Encryptor is RC4);
            }
            else if (encryption == EncryptionTypes.RC4Header)
            {
                Assert.True(a.Encryptor is RC4Header);
            }
            else if (encryption == EncryptionTypes.PlainText)
            {
                Assert.True(a.Encryptor is RC4Header);
            }
        }
        async Task PeerBTest(EncryptionTypes encryption)
        {
            rig.Engine.Settings.AllowedEncryption = encryption;
            await rig.Engine.StartAll();

            rig.AddConnection(conn.Outgoing);

            PeerBEncryption a      = new PeerBEncryption(new InfoHash[] { rig.Manager.InfoHash }, EncryptionTypes.All);
            var             result = a.HandshakeAsync(conn.Incoming);

            if (!result.Wait(4000))
            {
                Assert.Fail("Handshake timed out");
            }

            HandshakeMessage message = new HandshakeMessage();

            byte[] buffer = new byte[HandshakeMessage.HandshakeLength];

            await conn.Incoming.ReceiveAsync(buffer, 0, buffer.Length);

            a.Decryptor.Decrypt(buffer);
            message.Decode(buffer, 0, buffer.Length);
            Assert.AreEqual(VersionInfo.ProtocolStringV100, message.ProtocolString);
            if (encryption == EncryptionTypes.RC4Full)
            {
                Assert.IsTrue(a.Encryptor is RC4);
            }
            else if (encryption == EncryptionTypes.RC4Header)
            {
                Assert.IsTrue(a.Encryptor is RC4Header);
            }
            else if (encryption == EncryptionTypes.PlainText)
            {
                Assert.IsTrue(a.Encryptor is RC4Header);
            }
        }
Exemple #7
0
        private void Handshake(EncryptionTypes encryptionA, EncryptionTypes encryptionB, bool addInitial)
        {
            bool doneA = false;
            bool doneB = false;

            HandshakeMessage m = new HandshakeMessage(rig.Torrent.InfoHash, "12345123451234512345", VersionInfo.ProtocolStringV100);

            byte[] handshake = m.Encode();

            PeerAEncryption a = new PeerAEncryption(rig.Torrent.InfoHash, encryptionA);

            if (addInitial)
            {
                a.AddPayload(handshake);
            }

            PeerBEncryption b = new PeerBEncryption(new InfoHash[] { rig.Torrent.InfoHash }, encryptionB);

            IAsyncResult resultA = a.BeginHandshake(conn.Outgoing, null, null);
            IAsyncResult resultB = b.BeginHandshake(conn.Incoming, null, null);

            WaitHandle[] handles = new WaitHandle[] { resultA.AsyncWaitHandle, resultB.AsyncWaitHandle };
            int          count   = 1000;

            while (!WaitHandle.WaitAll(handles, 5, true))
            {
                if (!doneA && (doneA = resultA.IsCompleted))
                {
                    a.EndHandshake(resultA);
                }
                if (!doneB && (doneB = resultB.IsCompleted))
                {
                    b.EndHandshake(resultB);
                }

                if (count-- == 0)
                {
                    Assert.Fail("Could not handshake");
                }
            }
            if (!doneA)
            {
                a.EndHandshake(resultA);
            }
            if (!doneB)
            {
                b.EndHandshake(resultB);
            }

            HandshakeMessage d = new HandshakeMessage();

            if (!addInitial)
            {
                a.Encrypt(handshake, 0, handshake.Length);
                b.Decrypt(handshake, 0, handshake.Length);
                d.Decode(handshake, 0, handshake.Length);
            }
            else
            {
                d.Decode(b.InitialData, 0, b.InitialData.Length);
            }
            Assert.AreEqual(m, d);


            if (encryptionA == EncryptionTypes.RC4Full || encryptionB == EncryptionTypes.RC4Full)
            {
                Assert.IsTrue(a.Encryptor is RC4);
                Assert.IsTrue(b.Encryptor is RC4);
            }
            else if (encryptionA == EncryptionTypes.RC4Header || encryptionB == EncryptionTypes.RC4Header)
            {
                Assert.IsTrue(a.Encryptor is RC4Header);
                Assert.IsTrue(b.Encryptor is RC4Header);
            }
            else if (encryptionA == EncryptionTypes.PlainText || encryptionB == EncryptionTypes.PlainText)
            {
                Assert.IsTrue(a.Encryptor is PlainTextEncryption);
                Assert.IsTrue(b.Encryptor is PlainTextEncryption);
            }
        }
Exemple #8
0
        static async Task <byte[]> CheckEncryptionAsync(PeerId id, int bytesToReceive, InfoHash[] sKeys, CancellationToken token)
        {
            IConnection connection        = id.Connection;
            var         allowedEncryption = (id.Engine?.Settings.AllowedEncryption ?? EncryptionTypes.All) & id.Peer.Encryption;
            var         supportsRC4Header = allowedEncryption.HasFlag(EncryptionTypes.RC4Header);
            var         supportsRC4Full   = allowedEncryption.HasFlag(EncryptionTypes.RC4Full);
            var         supportsPlainText = allowedEncryption.HasFlag(EncryptionTypes.PlainText);

            // If the connection is incoming, receive the handshake before
            // trying to decide what encryption to use
            if (connection.IsIncoming)
            {
                var buffer = new byte[bytesToReceive];
                await NetworkIO.ReceiveAsync(connection, buffer, 0, bytesToReceive, null, null, null).ConfigureAwait(false);

                HandshakeMessage message = new HandshakeMessage();
                message.Decode(buffer, 0, buffer.Length);

                if (message.ProtocolString == VersionInfo.ProtocolStringV100)
                {
                    if (supportsPlainText)
                    {
                        id.Encryptor = id.Decryptor = PlainTextEncryption.Instance;
                        return(buffer);
                    }
                }
                else if (supportsRC4Header || supportsRC4Full)
                {
                    // The data we just received was part of an encrypted handshake and was *not* the BitTorrent handshake
                    var encSocket = new PeerBEncryption(sKeys, EncryptionTypes.All);
                    await encSocket.HandshakeAsync(connection, buffer, 0, buffer.Length);

                    if (encSocket.Decryptor is RC4Header && !supportsRC4Header)
                    {
                        throw new EncryptionException("Decryptor was RC4Header but that is not allowed");
                    }
                    if (encSocket.Decryptor is RC4 && !supportsRC4Full)
                    {
                        throw new EncryptionException("Decryptor was RC4Full but that is not allowed");
                    }

                    id.Decryptor = encSocket.Decryptor;
                    id.Encryptor = encSocket.Encryptor;
                    return(encSocket.InitialData?.Length > 0 ? encSocket.InitialData : null);
                }
            }
            else
            {
                if ((id.Engine.Settings.PreferEncryption || !supportsPlainText) && (supportsRC4Header || supportsRC4Full))
                {
                    var encSocket = new PeerAEncryption(id.TorrentManager.InfoHash, allowedEncryption);
                    await encSocket.HandshakeAsync(connection);

                    if (encSocket.Decryptor is RC4Header && !supportsRC4Header)
                    {
                        throw new EncryptionException("Decryptor was RC4Header but that is not allowed");
                    }
                    if (encSocket.Decryptor is RC4 && !supportsRC4Full)
                    {
                        throw new EncryptionException("Decryptor was RC4Full but that is not allowed");
                    }

                    id.Decryptor = encSocket.Decryptor;
                    id.Encryptor = encSocket.Encryptor;
                    return(encSocket.InitialData?.Length > 0 ? encSocket.InitialData : null);
                }
                else if (supportsPlainText)
                {
                    id.Encryptor = id.Decryptor = PlainTextEncryption.Instance;
                    return(null);
                }
            }

            throw new EncryptionException("Invalid handshake received and no decryption works");
        }
        static async ReusableTask <EncryptorResult> DoCheckIncomingConnectionAsync(IConnection connection, IList <EncryptionType> preferredEncryption, InfoHash[] sKeys)
        {
            bool supportsRC4Header = preferredEncryption.Contains(EncryptionType.RC4Header);
            bool supportsRC4Full   = preferredEncryption.Contains(EncryptionType.RC4Full);
            bool supportsPlainText = preferredEncryption.Contains(EncryptionType.PlainText);

            // If the connection is incoming, receive the handshake before
            // trying to decide what encryption to use

            var message = new HandshakeMessage();

            using (NetworkIO.BufferPool.Rent(HandshakeMessage.HandshakeLength, out ByteBuffer buffer)) {
                await NetworkIO.ReceiveAsync(connection, buffer, 0, HandshakeMessage.HandshakeLength, null, null, null).ConfigureAwait(false);

                message.Decode(buffer.Data, 0, HandshakeMessage.HandshakeLength);

                if (message.ProtocolString == VersionInfo.ProtocolStringV100)
                {
                    if (supportsPlainText)
                    {
                        return(new EncryptorResult(PlainTextEncryption.Instance, PlainTextEncryption.Instance, message));
                    }
                }
                else if (supportsRC4Header || supportsRC4Full)
                {
                    // The data we just received was part of an encrypted handshake and was *not* the BitTorrent handshake
                    // First switch to the threadpool as creating encrypted sockets runs expensive computations in the ctor
                    await MainLoop.SwitchToThreadpool();

                    var encSocket = new PeerBEncryption(sKeys, preferredEncryption);
                    await encSocket.HandshakeAsync(connection, buffer.Data, 0, HandshakeMessage.HandshakeLength).ConfigureAwait(false);

                    if (encSocket.Decryptor is RC4Header && !supportsRC4Header)
                    {
                        throw new EncryptionException("Decryptor was RC4Header but that is not allowed");
                    }
                    if (encSocket.Decryptor is RC4 && !supportsRC4Full)
                    {
                        throw new EncryptionException("Decryptor was RC4Full but that is not allowed");
                    }

                    // As the connection was encrypted, the data we got from the initial Receive call will have
                    // been consumed during the crypto handshake process. Now that the encrypted handshake has
                    // been established, we should ensure we read the data again.
                    byte[] data = encSocket.InitialData?.Length > 0 ? encSocket.InitialData : null;
                    if (data == null)
                    {
                        await NetworkIO.ReceiveAsync(connection, buffer, 0, HandshakeMessage.HandshakeLength, null, null, null).ConfigureAwait(false);

                        data = buffer.Data;
                        encSocket.Decryptor.Decrypt(data, 0, HandshakeMessage.HandshakeLength);
                    }
                    message.Decode(data, 0, HandshakeMessage.HandshakeLength);
                    if (message.ProtocolString == VersionInfo.ProtocolStringV100)
                    {
                        return(new EncryptorResult(encSocket.Decryptor, encSocket.Encryptor, message));
                    }
                }
            }

            connection.Dispose();
            throw new EncryptionException("Invalid handshake received and no decryption works");
        }
Exemple #10
0
        private void EndCheckEncryption(IAsyncResult result)
        {
            var id = (PeerId) result.AsyncState;
            try
            {
                byte[] initialData;
                EncryptorFactory.EndCheckEncryption(result, out initialData);

                if (initialData != null && initialData.Length == HandshakeMessage.HandshakeLength)
                {
                    var message = new HandshakeMessage();
                    message.Decode(initialData, 0, initialData.Length);
                    HandleHandshake(id, message);
                }
                else if (initialData.Length > 0)
                {
                    throw new Exception("Argh. I can't handle this scenario. It also shouldn't happen. Ever.");
                }
                else
                {
                    PeerIO.EnqueueReceiveHandshake(id.Connection, id.Decryptor, _handshakeReceivedCallback, id);
                }
            }
            catch
            {
                id.Connection.Dispose();
            }
        }
        static async ReusableTask <EncryptorResult> DoCheckIncomingConnectionAsync(IConnection2 connection, EncryptionTypes encryption, EngineSettings settings, InfoHash[] sKeys)
        {
            var allowedEncryption = (settings?.AllowedEncryption ?? EncryptionTypes.All) & encryption;
            var supportsRC4Header = allowedEncryption.HasFlag(EncryptionTypes.RC4Header);
            var supportsRC4Full   = allowedEncryption.HasFlag(EncryptionTypes.RC4Full);
            var supportsPlainText = allowedEncryption.HasFlag(EncryptionTypes.PlainText);

            // If the connection is incoming, receive the handshake before
            // trying to decide what encryption to use

            var buffer  = ClientEngine.BufferPool.Rent(HandshakeMessage.HandshakeLength);
            var message = new HandshakeMessage();

            try
            {
                await NetworkIO.ReceiveAsync(connection, buffer, 0, HandshakeMessage.HandshakeLength, null, null, null).ConfigureAwait(false);

                message.Decode(buffer, 0, HandshakeMessage.HandshakeLength);


                if (message.ProtocolString == VersionInfo.ProtocolStringV100)
                {
                    if (supportsPlainText)
                    {
                        return(new EncryptorResult(PlainTextEncryption.Instance, PlainTextEncryption.Instance, message));
                    }
                }
                else if (supportsRC4Header || supportsRC4Full)
                {
                    // The data we just received was part of an encrypted handshake and was *not* the BitTorrent handshake
                    var encSocket = new PeerBEncryption(sKeys, EncryptionTypes.All);
                    await encSocket.HandshakeAsync(connection, buffer, 0, HandshakeMessage.HandshakeLength);

                    if (encSocket.Decryptor is RC4Header && !supportsRC4Header)
                    {
                        throw new EncryptionException("Decryptor was RC4Header but that is not allowed");
                    }
                    if (encSocket.Decryptor is RC4 && !supportsRC4Full)
                    {
                        throw new EncryptionException("Decryptor was RC4Full but that is not allowed");
                    }

                    // As the connection was encrypted, the data we got from the initial Receive call will have
                    // been consumed during the crypto handshake process. Now that the encrypted handshake has
                    // been established, we should ensure we read the data again.
                    var data = encSocket.InitialData?.Length > 0 ? encSocket.InitialData : null;
                    if (data == null)
                    {
                        data = buffer;
                        await NetworkIO.ReceiveAsync(connection, data, 0, HandshakeMessage.HandshakeLength, null, null, null);

                        encSocket.Decryptor.Decrypt(data, 0, HandshakeMessage.HandshakeLength);
                    }
                    message.Decode(data, 0, HandshakeMessage.HandshakeLength);
                    if (message.ProtocolString == VersionInfo.ProtocolStringV100)
                    {
                        return(new EncryptorResult(encSocket.Decryptor, encSocket.Encryptor, message));
                    }
                }
            } finally {
                ClientEngine.BufferPool.Return(buffer);
            }

            connection.Dispose();
            throw new EncryptionException("Invalid handshake received and no decryption works");
        }