Example #1
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="manager">The torrent which the peer is associated with.</param>
        /// <param name="id">The peer whose message queue you want to start processing</param>
        internal async void ProcessQueue(TorrentManager manager, PeerId id)
        {
            while (id.QueueLength > 0)
            {
                var msg = id.Dequeue();
                var pm  = msg as PieceMessage;

                try {
                    if (pm != null)
                    {
                        pm.Data = ClientEngine.BufferPool.Rent(pm.ByteLength);
                        try {
                            await DiskManager.ReadAsync(manager.Torrent, pm.StartOffset + ((long)pm.PieceIndex * manager.Torrent.PieceLength), pm.Data, pm.RequestLength);
                        } catch (Exception ex) {
                            manager.TrySetError(Reason.ReadFailure, ex);
                            return;
                        }
                        id.PiecesSent++;
                    }

                    await PeerIO.SendMessageAsync(id.Connection, id.Encryptor, msg, manager.UploadLimiters, id.Monitor, manager.Monitor);

                    if (msg is PieceMessage)
                    {
                        id.IsRequestingPiecesCount--;
                    }

                    id.LastMessageSent.Restart();
                } catch {
                    CleanupSocket(manager, id);
                    break;
                } finally {
                    if (pm?.Data != null)
                    {
                        ClientEngine.BufferPool.Return(pm.Data);
                    }
                }
            }

            id.ProcessingQueue = false;
        }
Example #2
0
        public void ReceiveTwoKeepAlives()
        {
            var message = new KeepAliveMessage();
            var buffer  = message.Encode();
            var handle  = new AutoResetEvent(false);

            NetworkIO.EnqueueSend(Outgoing, buffer, 0, buffer.Length, null, null, null, delegate { }, null);
            NetworkIO.EnqueueSend(Outgoing, buffer, 0, buffer.Length, null, null, null, delegate { }, null);

            AsyncMessageReceivedCallback callback = (s, m, state) => {
                if (s && m is KeepAliveMessage)
                {
                    handle.Set();
                }
            };

            PeerIO.EnqueueReceiveMessage(Incoming, new PlainTextEncryption(), null, null, null, callback, null);
            Assert.IsTrue(handle.WaitOne(TimeSpan.FromSeconds(2)), "#Should receive first message");


            PeerIO.EnqueueReceiveMessage(Incoming, new PlainTextEncryption(), null, null, null, callback, null);
            Assert.IsTrue(handle.WaitOne(TimeSpan.FromSeconds(2)), "#Should receive second message");
        }
Example #3
0
        /// <param name="id">The peer whose message queue you want to start processing</param>
        internal async void ProcessQueue(PeerId id)
        {
            while (id.QueueLength > 0)
            {
                var msg = id.Dequeue();
                var pm  = msg as PieceMessage;

                try {
                    if (pm != null)
                    {
                        pm.Data = ClientEngine.BufferManager.GetBuffer(pm.ByteLength);
                        await engine.DiskManager.ReadAsync(id.TorrentManager, pm.StartOffset + ((long)pm.PieceIndex *id.TorrentManager.Torrent.PieceLength), pm.Data, pm.RequestLength);

                        id.PiecesSent++;
                    }

                    await PeerIO.SendMessageAsync(id.Connection, id.Encryptor, msg, id.TorrentManager.UploadLimiter, id.Monitor, id.TorrentManager.Monitor);

                    if (msg is PieceMessage)
                    {
                        id.IsRequestingPiecesCount--;
                    }

                    id.LastMessageSent.Restart();
                } catch (Exception e) {
                    CleanupSocket(id, "Exception calling SendMessage: " + e.Message);
                    break;
                } finally {
                    if (pm?.Data != null)
                    {
                        ClientEngine.BufferManager.FreeBuffer(pm.Data);
                    }
                }
            }

            id.ProcessingQueue = false;
        }
Example #4
0
        async void ReceiveMessagesAsync(IConnection 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);
            }
        }
Example #5
0
        public async Task CountPieceMessageBlockLengthAsData()
        {
            var blockSize = Piece.BlockSize - 1234;
            var msg       = new PieceMessage(0, 0, blockSize)
            {
                DataReleaser = new ByteBufferPool.Releaser(null, new ByteBuffer(blockSize))
            };

            var protocolSize = msg.ByteLength - blockSize;
            await Task.WhenAll(
                PeerIO.SendMessageAsync (pair.Incoming, PlainTextEncryption.Instance, msg, null, pair.Incoming.Monitor, pair.Incoming.ManagerMonitor).AsTask(),
                PeerIO.ReceiveMessageAsync (pair.Outgoing, PlainTextEncryption.Instance, null, pair.Outgoing.Monitor, pair.Outgoing.ManagerMonitor, null).AsTask()
                );

            // incoming connection sends 1 message so should receive nothing.
            Assert.AreEqual(0, pair.Incoming.Monitor.DataBytesDownloaded);
            Assert.AreEqual(0, pair.Incoming.ManagerMonitor.DataBytesDownloaded);
            Assert.AreEqual(0, pair.Incoming.Monitor.ProtocolBytesDownloaded);
            Assert.AreEqual(0, pair.Incoming.ManagerMonitor.ProtocolBytesDownloaded);

            Assert.AreEqual(blockSize, pair.Incoming.Monitor.DataBytesUploaded);
            Assert.AreEqual(blockSize, pair.Incoming.ManagerMonitor.DataBytesUploaded);
            Assert.AreEqual(protocolSize, pair.Incoming.Monitor.ProtocolBytesUploaded);
            Assert.AreEqual(protocolSize, pair.Incoming.ManagerMonitor.ProtocolBytesUploaded);

            // outgoing connection receives 1 message, so should send nothing.
            Assert.AreEqual(0, pair.Outgoing.Monitor.DataBytesUploaded);
            Assert.AreEqual(0, pair.Outgoing.ManagerMonitor.DataBytesUploaded);
            Assert.AreEqual(0, pair.Outgoing.Monitor.ProtocolBytesUploaded);
            Assert.AreEqual(0, pair.Outgoing.ManagerMonitor.ProtocolBytesUploaded);

            Assert.AreEqual(blockSize, pair.Outgoing.Monitor.DataBytesDownloaded);
            Assert.AreEqual(blockSize, pair.Outgoing.ManagerMonitor.DataBytesDownloaded);
            Assert.AreEqual(protocolSize, pair.Outgoing.Monitor.ProtocolBytesDownloaded);
            Assert.AreEqual(protocolSize, pair.Outgoing.ManagerMonitor.ProtocolBytesDownloaded);
        }
        public async Task CountPieceMessageBlockLengthAsData()
        {
            var blockSize = Constants.BlockSize - 1234;
            var msg       = new PieceMessage(0, 0, blockSize);
            var releaser  = new MemoryPool().Rent(blockSize, out Memory <byte> buffer);

            msg.SetData((releaser, buffer));

            var protocolSize = msg.ByteLength - blockSize;
            await Task.WhenAll(
                PeerIO.SendMessageAsync (pair.Incoming, PlainTextEncryption.Instance, msg, null, pair.Incoming.Monitor, pair.Incoming.ManagerMonitor).AsTask(),
                PeerIO.ReceiveMessageAsync (pair.Outgoing, PlainTextEncryption.Instance, null, pair.Outgoing.Monitor, pair.Outgoing.ManagerMonitor, null).AsTask()
                );

            // incoming connection sends 1 message so should receive nothing.
            Assert.AreEqual(0, pair.Incoming.Monitor.DataBytesReceived);
            Assert.AreEqual(0, pair.Incoming.ManagerMonitor.DataBytesReceived);
            Assert.AreEqual(0, pair.Incoming.Monitor.ProtocolBytesReceived);
            Assert.AreEqual(0, pair.Incoming.ManagerMonitor.ProtocolBytesReceived);

            Assert.AreEqual(blockSize, pair.Incoming.Monitor.DataBytesSent);
            Assert.AreEqual(blockSize, pair.Incoming.ManagerMonitor.DataBytesSent);
            Assert.AreEqual(protocolSize, pair.Incoming.Monitor.ProtocolBytesSent);
            Assert.AreEqual(protocolSize, pair.Incoming.ManagerMonitor.ProtocolBytesSent);

            // outgoing connection receives 1 message, so should send nothing.
            Assert.AreEqual(0, pair.Outgoing.Monitor.DataBytesSent);
            Assert.AreEqual(0, pair.Outgoing.ManagerMonitor.DataBytesSent);
            Assert.AreEqual(0, pair.Outgoing.Monitor.ProtocolBytesSent);
            Assert.AreEqual(0, pair.Outgoing.ManagerMonitor.ProtocolBytesSent);

            Assert.AreEqual(blockSize, pair.Outgoing.Monitor.DataBytesReceived);
            Assert.AreEqual(blockSize, pair.Outgoing.ManagerMonitor.DataBytesReceived);
            Assert.AreEqual(protocolSize, pair.Outgoing.Monitor.ProtocolBytesReceived);
            Assert.AreEqual(protocolSize, pair.Outgoing.ManagerMonitor.ProtocolBytesReceived);
        }
Example #7
0
        internal async void ProcessNewOutgoingConnection(TorrentManager manager, PeerId id)
        {
            // If we have too many open connections, close the connection
            if (OpenConnections > MaxOpenConnections)
            {
                CleanupSocket(manager, id);
                return;
            }

            id.ProcessingQueue = true;
            manager.Peers.ActivePeers.Add(id.Peer);
            manager.Peers.HandshakingPeers.Add(id);

            try {
                // Create a handshake message to send to the peer
                var handshake = new HandshakeMessage(manager.InfoHash, LocalPeerId, VersionInfo.ProtocolStringV100);
                var result    = await EncryptorFactory.CheckOutgoingConnectionAsync(id.Connection, id.Peer.AllowedEncryption, Settings, manager.InfoHash, handshake);

                id.Decryptor = result.Decryptor;
                id.Encryptor = result.Encryptor;
            } catch {
                // If an exception is thrown it's because we tried to establish an encrypted connection and something went wrong
                id.Peer.AllowedEncryption &= ~(EncryptionTypes.RC4Full | EncryptionTypes.RC4Header);

                manager.RaiseConnectionAttemptFailed(new ConnectionAttemptFailedEventArgs(id.Peer, ConnectionFailureReason.EncryptionNegiotiationFailed, manager));
                CleanupSocket(manager, id);
                return;
            }

            try {
                // Receive their handshake
                var handshake = await PeerIO.ReceiveHandshakeAsync(id.Connection, id.Decryptor);

                handshake.Handle(manager, id);
            } catch {
                // If we choose plaintext and it resulted in the connection being closed, remove it from the list.
                id.Peer.AllowedEncryption &= ~id.EncryptionType;

                manager.RaiseConnectionAttemptFailed(new ConnectionAttemptFailedEventArgs(id.Peer, ConnectionFailureReason.HandshakeFailed, manager));
                CleanupSocket(manager, id);
                return;
            }

            try {
                manager.Peers.HandshakingPeers.Remove(id);
                manager.HandlePeerConnected(id);

                // If there are any pending messages, send them otherwise set the queue
                // processing as finished.
                if (id.QueueLength > 0)
                {
                    ProcessQueue(manager, id);
                }
                else
                {
                    id.ProcessingQueue = false;
                }

                ReceiveMessagesAsync(id.Connection, id.Decryptor, manager.DownloadLimiters, id.Monitor, manager, id);

                id.WhenConnected.Restart();
                id.LastBlockReceived.Restart();
            } catch {
                manager.RaiseConnectionAttemptFailed(new ConnectionAttemptFailedEventArgs(id.Peer, ConnectionFailureReason.Unknown, manager));
                CleanupSocket(manager, id);
                return;
            }
        }
 private void PeerHandshakeSent(PeerId id)
 {
     PeerIO.EnqueueReceiveHandshake(id.Connection, id.Decryptor, peerHandshakeReceivedCallback, id);
 }
        /// <summary>
        ///
        /// </summary>
        /// <param name="manager">The torrent which the peer is associated with.</param>
        /// <param name="id">The peer whose message queue you want to start processing</param>
        internal async void TryProcessQueue(TorrentManager manager, PeerId id)
        {
            if (!id.MessageQueue.BeginProcessing())
            {
                return;
            }

            await MainLoop.SwitchToThreadpool();

            ByteBufferPool.Releaser messageBuffer = default;
            ByteBufferPool.Releaser pieceBuffer   = default;
            PeerMessage             msg;

            try {
                while ((msg = id.MessageQueue.TryDequeue()) != null)
                {
                    var msgLength = msg.ByteLength;

                    if (msg is PieceMessage pm)
                    {
                        if (pieceBuffer.Buffer == null)
                        {
                            pieceBuffer = DiskManager.BufferPool.Rent(msgLength, out ByteBuffer _);
                        }
                        pm.DataReleaser = pieceBuffer;
                        try {
                            await DiskManager.ReadAsync(manager, pm.StartOffset + ((long)pm.PieceIndex * manager.Torrent.PieceLength), pm.Data, pm.RequestLength).ConfigureAwait(false);
                        } catch (Exception ex) {
                            await ClientEngine.MainLoop;
                            manager.TrySetError(Reason.ReadFailure, ex);
                            return;
                        }
                        System.Threading.Interlocked.Increment(ref id.piecesSent);
                    }
                    else
                    {
                        pieceBuffer.Dispose();
                    }

                    if (messageBuffer.Buffer == null || messageBuffer.Buffer.Data.Length < msg.ByteLength)
                    {
                        messageBuffer.Dispose();
                        messageBuffer = NetworkIO.BufferPool.Rent(msgLength, out ByteBuffer _);
                    }
                    await PeerIO.SendMessageAsync(id.Connection, id.Encryptor, msg, manager.UploadLimiters, id.Monitor, manager.Monitor, messageBuffer.Buffer).ConfigureAwait(false);

                    if (msg is PieceMessage)
                    {
                        System.Threading.Interlocked.Decrement(ref id.isRequestingPiecesCount);
                    }

                    id.LastMessageSent.Restart();
                }
            } catch {
                await ClientEngine.MainLoop;
                CleanupSocket(manager, id);
            } finally {
                messageBuffer.Dispose();
                pieceBuffer.Dispose();
            }
        }
Example #10
0
        internal async void ProcessNewOutgoingConnection(TorrentManager manager, PeerId id)
        {
            // If we have too many open connections, close the connection
            if (OpenConnections > MaxOpenConnections)
            {
                CleanupSocket(manager, id);
                return;
            }

            manager.Peers.ActivePeers.Add(id.Peer);
            manager.Peers.ConnectedPeers.Add(id);
            Interlocked.Increment(ref openConnections);

            try {
                // Create a handshake message to send to the peer
                var handshake           = new HandshakeMessage(manager.InfoHash, LocalPeerId, VersionInfo.ProtocolStringV100);
                var preferredEncryption = EncryptionTypes.GetPreferredEncryption(id.Peer.AllowedEncryption, Settings.AllowedEncryption);
                EncryptorFactory.EncryptorResult result = await EncryptorFactory.CheckOutgoingConnectionAsync(id.Connection, preferredEncryption, manager.InfoHash, handshake);

                id.Decryptor = result.Decryptor;
                id.Encryptor = result.Encryptor;
            } catch {
                // If an exception is thrown it's because we tried to establish an encrypted connection and something went wrong
                if (id.Peer.AllowedEncryption.Contains(EncryptionType.PlainText))
                {
                    id.Peer.AllowedEncryption = EncryptionTypes.PlainText;
                }
                else
                {
                    id.Peer.AllowedEncryption = EncryptionTypes.None;
                }

                manager.RaiseConnectionAttemptFailed(new ConnectionAttemptFailedEventArgs(id.Peer, ConnectionFailureReason.EncryptionNegiotiationFailed, manager));
                CleanupSocket(manager, id);

                // CleanupSocket will contain the peer only if AllowedEncryption is not set to None. If
                // the peer was re-added, then we should try to reconnect to it immediately to try an
                // unencrypted connection.
                if (manager.Peers.AvailablePeers.Remove(id.Peer))
                {
                    ConnectToPeer(manager, id.Peer);
                }
                return;
            }

            try {
                // Receive their handshake
                HandshakeMessage handshake = await PeerIO.ReceiveHandshakeAsync(id.Connection, id.Decryptor);

                manager.Mode.HandleMessage(id, handshake);
            } catch {
                // If we choose plaintext and it resulted in the connection being closed, remove it from the list.
                id.Peer.AllowedEncryption = EncryptionTypes.Remove(id.Peer.AllowedEncryption, id.EncryptionType);

                manager.RaiseConnectionAttemptFailed(new ConnectionAttemptFailedEventArgs(id.Peer, ConnectionFailureReason.HandshakeFailed, manager));
                CleanupSocket(manager, id);

                // CleanupSocket will contain the peer only if AllowedEncryption is not set to None. If
                // the peer was re-added, then we should try to reconnect to it immediately to try an
                // encrypted connection, assuming the previous connection was unencrypted and it failed.
                if (manager.Peers.AvailablePeers.Remove(id.Peer))
                {
                    ConnectToPeer(manager, id.Peer);
                }

                return;
            }

            try {
                if (id.BitField.Length != manager.Bitfield.Length)
                {
                    throw new TorrentException($"The peer's bitfield was of length {id.BitField.Length} but the TorrentManager's bitfield was of length {manager.Bitfield.Length}.");
                }

                manager.HandlePeerConnected(id);
                id.MessageQueue.SetReady();
                TryProcessQueue(manager, id);

                ReceiveMessagesAsync(id.Connection, id.Decryptor, manager.DownloadLimiters, id.Monitor, manager, id);

                id.WhenConnected.Restart();
                id.LastBlockReceived.Restart();
            } catch {
                manager.RaiseConnectionAttemptFailed(new ConnectionAttemptFailedEventArgs(id.Peer, ConnectionFailureReason.Unknown, manager));
                CleanupSocket(manager, id);
                return;
            }
        }
Example #11
0
        private void handleHandshake(PeerId id, HandshakeMessage message)
        {
            TorrentManager man = null;

            try
            {
                if (message.ProtocolString != VersionInfo.ProtocolStringV100)
                {
                    throw new ProtocolException("Invalid protocol string in handshake");
                }
            }
            catch (Exception ex)
            {
                Logger.Log(id.Connection, ex.Message);
                id.Connection.Dispose();
                return;
            }

            ClientEngine.MainLoop.QueueWait((MainLoopTask) delegate {
                for (int i = 0; i < engine.Torrents.Count; i++)
                {
                    if (message.infoHash == engine.Torrents[i].InfoHash)
                    {
                        man = engine.Torrents[i];
                    }
                }
            });

            //FIXME: #warning FIXME: Don't stop the message loop until Dispose() and track all incoming connections
            if (man == null)        // We're not hosting that torrent
            {
                Logger.Log(id.Connection, "ListenManager - Handshake requested nonexistant torrent");
                id.Connection.Dispose();
                return;
            }
            if (man.State == TorrentState.Stopped)
            {
                Logger.Log(id.Connection, "ListenManager - Handshake requested for torrent which is not running");
                id.Connection.Dispose();
                return;
            }
            if (!man.Mode.CanAcceptConnections)
            {
                Logger.Log(id.Connection, "ListenManager - Current mode does not support connections");
                id.Connection.Dispose();
                return;
            }

            id.Peer.PeerId    = message.PeerId;
            id.TorrentManager = man;

            // If the handshake was parsed properly without encryption, then it definitely was not encrypted. If this is not allowed, abort
            if ((id.Encryptor is PlainTextEncryption && !Toolbox.HasEncryption(engine.Settings.AllowedEncryption, EncryptionTypes.PlainText)) && ClientEngine.SupportsEncryption)
            {
                Logger.Log(id.Connection, "ListenManager - Encryption is required but was not active");
                id.Connection.Dispose();
                return;
            }

            message.Handle(id);
            Logger.Log(id.Connection, "ListenManager - Handshake successful handled");

            id.ClientApp = new Software(message.PeerId);

            message = new HandshakeMessage(id.TorrentManager.InfoHash, engine.PeerId, VersionInfo.ProtocolStringV100);
            var callback = engine.ConnectionManager.incomingConnectionAcceptedCallback;

            PeerIO.EnqueueSendMessage(id.Connection, id.Encryptor, message, id.TorrentManager.UploadLimiter,
                                      id.Monitor, id.TorrentManager.Monitor, callback, id);
        }