Example #1
0
 public void Teardown()
 {
     Incoming?.Dispose();
     Outgoing?.Dispose();
 }
        static async ReusableTask <EncryptorResult> DoCheckIncomingConnectionAsync(IPeerConnection connection, IList <EncryptionType> preferredEncryption, InfoHash[] sKeys, Factories factories)
        {
            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

            using (NetworkIO.BufferPool.Rent(HandshakeMessage.HandshakeLength, out Memory <byte> buffer)) {
                await NetworkIO.ReceiveAsync(connection, buffer, null, null, null).ConfigureAwait(false);

                var message = new HandshakeMessage(buffer.Span);

                if (message.ProtocolString == Constants.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();

                    using var encSocket = new PeerBEncryption(factories, sKeys, preferredEncryption);
                    await encSocket.HandshakeAsync(connection, buffer).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.
                    Memory <byte> data = encSocket.InitialData?.Length > 0 ? encSocket.InitialData : default;
                    if (data.IsEmpty)
                    {
                        await NetworkIO.ReceiveAsync(connection, buffer, null, null, null).ConfigureAwait(false);

                        encSocket.Decryptor !.Decrypt(buffer.Span);
                        data = buffer;
                    }
                    message.Decode(data.Span);
                    if (message.ProtocolString == Constants.ProtocolStringV100)
                    {
                        return(new EncryptorResult(encSocket.Decryptor !, encSocket.Encryptor !, message));
                    }
                }
            }

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