예제 #1
0
        async void ConnectToPeer(TorrentManager manager, Peer peer)
        {
            // Connect to the peer.
            IConnection2 connection = ConnectionConverter.Convert(ConnectionFactory.Create(peer.ConnectionUri));

            if (connection == null)
            {
                return;
            }

            var state = new AsyncConnectState(manager, connection, ValueStopwatch.StartNew());

            PendingConnects.Add(state);
            manager.Peers.ConnectingToPeers.Add(peer);

            bool succeeded;

            try {
                await NetworkIO.ConnectAsync(connection);

                succeeded = true;
            } catch {
                succeeded = false;
            }

            PendingConnects.Remove(state);
            manager.Peers.ConnectingToPeers.Remove(peer);
            if (manager.Engine == null || !manager.Mode.CanAcceptConnections)
            {
                manager.Peers.AvailablePeers.Add(peer);
                connection.Dispose();
                return;
            }

            try {
                if (!succeeded)
                {
                    peer.FailedConnectionAttempts++;
                    connection.Dispose();
                    manager.Peers.BusyPeers.Add(peer);
                    manager.RaiseConnectionAttemptFailed(new ConnectionAttemptFailedEventArgs(peer, ConnectionFailureReason.Unreachable, manager));
                }
                else
                {
                    PeerId id = new PeerId(peer, connection, manager.Bitfield?.Clone().SetAll(false));
                    id.LastMessageReceived.Restart();
                    id.LastMessageSent.Restart();

                    Logger.Log(id.Connection, "ConnectionManager - Connection opened");

                    ProcessNewOutgoingConnection(manager, id);
                }
            } catch {
                // FIXME: Do nothing now?
            } finally {
                // Try to connect to another peer
                TryConnect();
            }
        }
예제 #2
0
        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.Connection, e.TorrentManager.Bitfield?.Clone().SetAll(false));
                    id.LastMessageSent.Restart();
                    id.LastMessageReceived.Restart();

                    Engine.ConnectionManager.ProcessNewOutgoingConnection(e.TorrentManager, id);
                    return;
                }

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

                IConnection2 connection = ConnectionConverter.Convert(e.Connection);
                EncryptorFactory.EncryptorResult result = await EncryptorFactory.CheckIncomingConnectionAsync(connection, e.Peer.AllowedEncryption, Engine.Settings, SKeys);

                if (!await HandleHandshake(e.Peer, connection, result.Handshake, result.Decryptor, result.Encryptor))
                {
                    connection.Dispose();
                }
            } catch {
                e.Connection.Dispose();
            }
        }
예제 #3
0
        public static async ReusableTask <PeerMessage> ReceiveMessageAsync(IConnection2 connection, IEncryption decryptor, IRateLimiter rateLimiter, ConnectionMonitor peerMonitor, ConnectionMonitor managerMonitor, ITorrentData torrentData)
        {
            byte[] messageLengthBuffer = null;
            byte[] messageBuffer       = null;

            int messageLength = 4;
            int messageBody;

            try {
                messageLengthBuffer = ClientEngine.BufferPool.Rent(messageLength);
                await NetworkIO.ReceiveAsync(connection, messageLengthBuffer, 0, messageLength, rateLimiter, peerMonitor?.ProtocolDown, managerMonitor?.ProtocolDown).ConfigureAwait(false);

                decryptor.Decrypt(messageLengthBuffer, 0, messageLength);

                messageBody = IPAddress.HostToNetworkOrder(BitConverter.ToInt32(messageLengthBuffer, 0));
                if (messageBody < 0 || messageBody > MaxMessageLength)
                {
                    connection.Dispose();
                    throw new ProtocolException($"Invalid message length received. Value was '{messageBody}'");
                }

                if (messageBody == 0)
                {
                    return(new KeepAliveMessage());
                }

                messageBuffer = ClientEngine.BufferPool.Rent(messageBody + messageLength);
                Buffer.BlockCopy(messageLengthBuffer, 0, messageBuffer, 0, messageLength);
            } finally {
                ClientEngine.BufferPool.Return(messageLengthBuffer);
            }

            try {
                // Always assume protocol first, then convert to data when we what message it is!
                await NetworkIO.ReceiveAsync(connection, messageBuffer, messageLength, messageBody, rateLimiter, peerMonitor?.ProtocolDown, managerMonitor?.ProtocolDown).ConfigureAwait(false);

                decryptor.Decrypt(messageBuffer, messageLength, messageBody);
                // FIXME: manager should never be null, except some of the unit tests do that.
                var data = PeerMessage.DecodeMessage(messageBuffer, 0, messageLength + messageBody, torrentData);
                if (data is PieceMessage msg)
                {
                    peerMonitor?.ProtocolDown.AddDelta(-msg.RequestLength);
                    managerMonitor?.ProtocolDown.AddDelta(-msg.RequestLength);

                    peerMonitor?.DataDown.AddDelta(msg.RequestLength);
                    managerMonitor?.DataDown.AddDelta(msg.RequestLength);
                }
                return(data);
            } finally {
                ClientEngine.BufferPool.Return(messageBuffer);
            }
        }
예제 #4
0
        /// <summary>
        /// Begins the message stream encryption handshaking process
        /// </summary>
        /// <param name="socket">The socket to perform handshaking with</param>
        public virtual async ReusableTask HandshakeAsync(IConnection2 socket)
        {
            this.socket = socket ?? throw new ArgumentNullException(nameof(socket));

            // Either "1 A->B: Diffie Hellman Ya, PadA" or "2 B->A: Diffie Hellman Yb, PadB"
            // These two steps will be done simultaneously to save time due to latency
            ReusableTask first  = SendY();
            ReusableTask second = ReceiveY();

            try {
                await first;
                await second;
            } catch (Exception ex) {
                socket.Dispose();
                throw new EncryptionException("Encrypted handshake failed", ex);
            }
        }
예제 #5
0
        static async ReusableTask <EncryptorResult> DoCheckOutgoingConnectionAsync(IConnection2 connection, EncryptionTypes encryption, EngineSettings settings, InfoHash infoHash, HandshakeMessage handshake)
        {
            EncryptionTypes allowedEncryption = settings.AllowedEncryption & encryption;
            bool            supportsRC4Header = allowedEncryption.HasFlag(EncryptionTypes.RC4Header);
            bool            supportsRC4Full   = allowedEncryption.HasFlag(EncryptionTypes.RC4Full);
            bool            supportsPlainText = allowedEncryption.HasFlag(EncryptionTypes.PlainText);

            if ((settings.PreferEncryption || !supportsPlainText) && (supportsRC4Header || supportsRC4Full))
            {
                // First switch to the threadpool as creating encrypted sockets runs expensive computations in the ctor
                await MainLoop.SwitchToThreadpool();

                var encSocket = new PeerAEncryption(infoHash, allowedEncryption, handshake?.Encode());
                await encSocket.HandshakeAsync(connection).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");
                }

                return(new EncryptorResult(encSocket.Decryptor, encSocket.Encryptor, null));
            }
            else if (supportsPlainText)
            {
                if (handshake != null)
                {
                    int    length = handshake.ByteLength;
                    byte[] buffer = ClientEngine.BufferPool.Rent(length);
                    handshake.Encode(buffer, 0);
                    try {
                        await NetworkIO.SendAsync(connection, buffer, 0, length, null, null, null).ConfigureAwait(false);
                    } finally {
                        ClientEngine.BufferPool.Return(buffer);
                    }
                }
                return(new EncryptorResult(PlainTextEncryption.Instance, PlainTextEncryption.Instance, null));
            }

            connection.Dispose();
            throw new EncryptionException("Invalid handshake received and no decryption works");
        }
예제 #6
0
        static async ReusableTask <EncryptorResult> DoCheckOutgoingConnectionAsync(IConnection2 connection, EncryptionTypes encryption, EngineSettings settings, InfoHash infoHash, HandshakeMessage handshake)
        {
            var allowedEncryption = settings.AllowedEncryption & encryption;
            var supportsRC4Header = allowedEncryption.HasFlag(EncryptionTypes.RC4Header);
            var supportsRC4Full   = allowedEncryption.HasFlag(EncryptionTypes.RC4Full);
            var supportsPlainText = allowedEncryption.HasFlag(EncryptionTypes.PlainText);

            if ((settings.PreferEncryption || !supportsPlainText) && (supportsRC4Header || supportsRC4Full))
            {
                var encSocket = new PeerAEncryption(infoHash, allowedEncryption, handshake?.Encode());

                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");
                }

                return(new EncryptorResult(encSocket.Decryptor, encSocket.Encryptor, null));
            }
            else if (supportsPlainText)
            {
                if (handshake != null)
                {
                    var length = handshake.ByteLength;
                    var buffer = ClientEngine.BufferPool.Rent(length);
                    handshake.Encode(buffer, 0);
                    try  {
                        await NetworkIO.SendAsync(connection, buffer, 0, length, null, null, null);
                    } finally  {
                        ClientEngine.BufferPool.Return(buffer);
                    }
                }
                return(new EncryptorResult(PlainTextEncryption.Instance, PlainTextEncryption.Instance, null));
            }

            connection.Dispose();
            throw new EncryptionException("Invalid handshake received and no decryption works");
        }
예제 #7
0
        static async ReusableTask <EncryptorResult> DoCheckIncomingConnectionAsync(IConnection2 connection, EncryptionTypes encryption, EngineSettings settings, InfoHash[] sKeys)
        {
            EncryptionTypes allowedEncryption = (settings?.AllowedEncryption ?? EncryptionTypes.All) & encryption;
            bool            supportsRC4Header = allowedEncryption.HasFlag(EncryptionTypes.RC4Header);
            bool            supportsRC4Full   = allowedEncryption.HasFlag(EncryptionTypes.RC4Full);
            bool            supportsPlainText = allowedEncryption.HasFlag(EncryptionTypes.PlainText);

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

            byte[] 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
                    // First switch to the threadpool as creating encrypted sockets runs expensive computations in the ctor
                    await MainLoop.SwitchToThreadpool();

                    var encSocket = new PeerBEncryption(sKeys, EncryptionTypes.All);
                    await encSocket.HandshakeAsync(connection, buffer, 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)
                    {
                        data = buffer;
                        await NetworkIO.ReceiveAsync(connection, data, 0, HandshakeMessage.HandshakeLength, null, null, null).ConfigureAwait(false);

                        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");
        }