Пример #1
0
        public static async ReusableTask SendAsync(IConnection2 connection, byte [] buffer, int offset, int count, IRateLimiter rateLimiter, SpeedMonitor peerMonitor, SpeedMonitor managerMonitor)
        {
            await IOLoop;

            while (count > 0)
            {
                int  transferred;
                bool unlimited  = rateLimiter?.Unlimited ?? true;
                var  shouldRead = unlimited ? count : Math.Min(ChunkLength, count);

                if (rateLimiter != null && !unlimited && !rateLimiter.TryProcess(shouldRead))
                {
                    var   tcs = new ReusableTaskCompletionSource <int> ();
                    await IOLoop;
                    sendQueue.Enqueue(new QueuedIO(connection, buffer, offset, shouldRead, rateLimiter, tcs));
                    transferred = await tcs.Task.ConfigureAwait(false);
                }
                else
                {
                    transferred = await connection.SendAsync(buffer, offset, shouldRead).ConfigureAwait(false);
                }

                if (transferred == 0)
                {
                    throw new Exception("Socket is dead");
                }

                peerMonitor?.AddDelta(transferred);
                managerMonitor?.AddDelta(transferred);

                offset += transferred;
                count  -= transferred;
            }
        }
Пример #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
        internal async void ReceiveMessagesAsync(IConnection2 connection, IEncryption decryptor, RateLimiterGroup downloadLimiter, ConnectionMonitor monitor, TorrentManager torrentManager, PeerId id)
        {
            try {
                while (true)
                {
                    Messages.PeerMessage message = await PeerIO.ReceiveMessageAsync(connection, decryptor, downloadLimiter, monitor, torrentManager.Monitor, torrentManager.Torrent);

                    if (id.Disposed)
                    {
                        if (message is PieceMessage msg)
                        {
                            msg.DataReleaser.Dispose();
                        }
                        break;
                    }
                    else
                    {
                        id.LastMessageReceived.Restart();
                        torrentManager.Mode.HandleMessage(id, message);
                    }
                }
            } catch {
                CleanupSocket(torrentManager, id);
            }
        }
Пример #4
0
        public static async ReusableTask SendMessageAsync(IConnection2 connection, IEncryption encryptor, PeerMessage message, IRateLimiter rateLimiter, ConnectionMonitor peerMonitor, ConnectionMonitor managerMonitor)
        {
            int count = message.ByteLength;

            byte[] buffer = ClientEngine.BufferPool.Rent(count);

            try {
                var pieceMessage = message as PieceMessage;
                message.Encode(buffer, 0);
                encryptor.Encrypt(buffer, 0, count);

                // Assume protocol first, then swap it to data once we successfully send the data bytes.
                await NetworkIO.SendAsync(connection, buffer, 0, count, pieceMessage == null?null : rateLimiter, peerMonitor?.ProtocolUp, managerMonitor?.ProtocolUp).ConfigureAwait(false);

                if (pieceMessage != null)
                {
                    peerMonitor?.ProtocolUp.AddDelta(-pieceMessage.RequestLength);
                    managerMonitor?.ProtocolUp.AddDelta(-pieceMessage.RequestLength);

                    peerMonitor?.DataUp.AddDelta(pieceMessage.RequestLength);
                    managerMonitor?.DataUp.AddDelta(pieceMessage.RequestLength);
                }
            } finally {
                ClientEngine.BufferPool.Return(buffer);
            }
        }
Пример #5
0
 /// <summary>
 /// Begins the message stream encryption handshaking process, beginning with some data
 /// already received from the socket.
 /// </summary>
 /// <param name="socket">The socket to perform handshaking with</param>
 /// <param name="initialBuffer">Buffer containing soome data already received from the socket</param>
 /// <param name="offset">Offset to begin reading in initialBuffer</param>
 /// <param name="count">Number of bytes to read from initialBuffer</param>
 public virtual async ReusableTask HandshakeAsync(IConnection2 socket, byte[] initialBuffer, int offset, int count)
 {
     this.initialBuffer  = initialBuffer;
     initialBufferOffset = offset;
     initialBufferCount  = count;
     await HandshakeAsync(socket).ConfigureAwait(false);
 }
Пример #6
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();
            }
        }
Пример #7
0
 public QueuedIO(IConnection2 connection, byte [] buffer, int offset, int count, IRateLimiter rateLimiter, ReusableTaskCompletionSource <int> tcs)
 {
     this.connection  = connection;
     this.buffer      = buffer;
     this.offset      = offset;
     this.count       = count;
     this.rateLimiter = rateLimiter;
     this.tcs         = tcs;
 }
Пример #8
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);
            }
        }
Пример #9
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

            var message = new HandshakeMessage();

            using (ClientEngine.BufferPool.Rent(HandshakeMessage.HandshakeLength, out byte[] buffer)) {
Пример #10
0
        public static async ReusableTask <HandshakeMessage> ReceiveHandshakeAsync(IConnection2 connection, IEncryption decryptor)
        {
            byte[] buffer = ClientEngine.BufferPool.Rent(HandshakeMessage.HandshakeLength);
            try {
                await NetworkIO.ReceiveAsync(connection, buffer, 0, HandshakeMessage.HandshakeLength, null, null, null).ConfigureAwait(false);

                decryptor.Decrypt(buffer, 0, HandshakeMessage.HandshakeLength);

                var message = new HandshakeMessage();
                message.Decode(buffer, 0, HandshakeMessage.HandshakeLength);
                return(message);
            } finally {
                ClientEngine.BufferPool.Return(buffer);
            }
        }
Пример #11
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);
            }
        }
Пример #12
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");
        }
Пример #13
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");
        }
Пример #14
0
        async void ReceiveMessagesAsync(IConnection2 connection, IEncryption decryptor, RateLimiterGroup downloadLimiter, ConnectionMonitor monitor, TorrentManager torrentManager, PeerId id)
        {
            try {
                while (true)
                {
                    var message = await PeerIO.ReceiveMessageAsync(connection, decryptor, downloadLimiter, monitor, torrentManager);

                    if (id.Disposed)
                    {
                        if (message is PieceMessage msg)
                        {
                            ClientEngine.BufferManager.FreeBuffer(msg.Data);
                        }
                    }
                    else
                    {
                        id.LastMessageReceived.Restart();
                        message.Handle(torrentManager, id);
                    }
                }
            } catch {
                CleanupSocket(torrentManager, id);
            }
        }
Пример #15
0
 public static async ReusableTask <HandshakeMessage> ReceiveHandshakeAsync(IConnection2 connection, IEncryption decryptor)
 {
     using (ClientEngine.BufferPool.Rent(HandshakeMessage.HandshakeLength, out byte[] buffer)) {
Пример #16
0
        internal static async ReusableTask <EncryptorResult> CheckOutgoingConnectionAsync(IConnection2 connection, EncryptionTypes encryption, EngineSettings settings, InfoHash infoHash, HandshakeMessage handshake = null)
        {
            if (connection.IsIncoming)
            {
                throw new Exception("oops");
            }

            using (var cts = new CancellationTokenSource(Timeout))
                using (var registration = cts.Token.Register(connection.Dispose))
                    return(await DoCheckOutgoingConnectionAsync(connection, encryption, settings, infoHash, handshake));
        }
Пример #17
0
 public static ReusableTask SendAsync(IConnection2 connection, byte [] buffer, int offset, int count)
 => SendAsync(connection, buffer, offset, count, null, null, null);
Пример #18
0
        public static async ReusableTask ConnectAsync(IConnection2 connection)
        {
            await IOLoop;

            await connection.ConnectAsync();
        }
Пример #19
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");
        }
Пример #20
0
        internal static async ReusableTask <EncryptorResult> CheckIncomingConnectionAsync(IConnection2 connection, EncryptionTypes encryption, EngineSettings settings, InfoHash[] sKeys)
        {
            if (!connection.IsIncoming)
            {
                throw new Exception("oops");
            }

            using var cts = new CancellationTokenSource(Timeout);
            using CancellationTokenRegistration registration = cts.Token.Register(connection.Dispose);
            return(await DoCheckIncomingConnectionAsync(connection, encryption, settings, sKeys).ConfigureAwait(false));
        }
Пример #21
0
 public static ReusableTask ReceiveAsync(IConnection2 connection, byte[] buffer, int offset, int count)
 {
     return(ReceiveAsync(connection, buffer, offset, count, null, null, null));
 }
Пример #22
0
 public static ReusableTask <PeerMessage> ReceiveMessageAsync(IConnection2 connection, IEncryption decryptor)
 {
     return(ReceiveMessageAsync(connection, decryptor, null, null, null, null));
 }
Пример #23
0
 public static ReusableTask SendMessageAsync(IConnection2 connection, IEncryption encryptor, PeerMessage message)
 {
     return(SendMessageAsync(connection, encryptor, message, null, null, null));
 }