예제 #1
0
        public async Task CountPieceMessageBlockLengthAsData()
        {
            var blockSize = Constants.BlockSize - 1234;
            var msg       = new PieceMessage(0, 0, blockSize);

            msg.SetData(new MemoryPool().Rent(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);
        }
예제 #2
0
        async Task <bool> HandleHandshake(Peer peer, IConnection connection, HandshakeMessage message, IEncryption decryptor, IEncryption encryptor)
        {
            TorrentManager man = null;

            if (message.ProtocolString != VersionInfo.ProtocolStringV100)
            {
                return(false);
            }

            // If we're forcing encrypted connections and this is in plain-text, close it!
            if (encryptor is PlainTextEncryption && !Engine.Settings.AllowedEncryption.HasFlag(EncryptionTypes.PlainText))
            {
                return(false);
            }

            for (int i = 0; i < Engine.Torrents.Count; i++)
            {
                if (message.InfoHash == Engine.Torrents[i].InfoHash)
                {
                    man = Engine.Torrents[i];
                }
            }

            // We're not hosting that torrent
            if (man == null)
            {
                return(false);
            }

            if (man.State == TorrentState.Stopped)
            {
                return(false);
            }

            if (!man.Mode.CanAcceptConnections)
            {
                return(false);
            }

            peer.PeerId = message.PeerId;
            var id = new PeerId(peer, connection, man.Bitfield?.Clone().SetAll(false))
            {
                Decryptor = decryptor,
                Encryptor = encryptor
            };

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

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

            message = new HandshakeMessage(man.InfoHash, Engine.PeerId, VersionInfo.ProtocolStringV100);
            await PeerIO.SendMessageAsync(id.Connection, id.Encryptor, message, man.UploadLimiters, id.Monitor, man.Monitor);

            Engine.ConnectionManager.IncomingConnectionAccepted(man, id);
            return(true);
        }
예제 #3
0
        /// <summary>
        /// This method is called when the ClientEngine recieves a valid incoming connection
        /// </summary>
        /// <param name="manager">The torrent which the peer is associated with.</param>
        /// <param name="id">The peer who just conencted</param>
        internal async ReusableTask <bool> IncomingConnectionAcceptedAsync(TorrentManager manager, PeerId id)
        {
            try {
                bool maxAlreadyOpen = OpenConnections >= Settings.MaximumConnections ||
                                      manager.OpenConnections >= manager.Settings.MaximumConnections;

                if (LocalPeerId.Equals(id.Peer.PeerId))
                {
                    logger.Info("Connected to self - disconnecting");
                    CleanupSocket(manager, id);
                    return(false);
                }
                if (manager.Peers.ActivePeers.Contains(id.Peer))
                {
                    logger.Info(id.Connection, "Already connected to peer");
                    id.Connection.Dispose();
                    return(false);
                }
                if (maxAlreadyOpen)
                {
                    logger.Info("Connected to too many peers - disconnecting");
                    CleanupSocket(manager, id);
                    return(false);
                }

                // Add the PeerId to the lists *before* doing anything asynchronous. This ensures that
                // all PeerIds are tracked in 'ConnectedPeers' as soon as they're created.
                logger.Info(id.Connection, "Incoming connection fully accepted");
                manager.Peers.AvailablePeers.Remove(id.Peer);
                manager.Peers.ActivePeers.Add(id.Peer);
                manager.Peers.ConnectedPeers.Add(id);
                Interlocked.Increment(ref openConnections);

                id.WhenConnected.Restart();
                // Baseline the time the last block was received
                id.LastBlockReceived.Restart();

                // Send our handshake now that we've decided to keep the connection
                var handshake = new HandshakeMessage(manager.InfoHashes.V1OrV2, manager.Engine !.PeerId, Constants.ProtocolStringV100);
                await PeerIO.SendMessageAsync(id.Connection, id.Encryptor, handshake, manager.UploadLimiters, id.Monitor, manager.Monitor);

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

                // We've sent our handshake so begin our looping to receive incoming message
                ReceiveMessagesAsync(id.Connection, id.Decryptor, manager.DownloadLimiters, id.Monitor, manager, id);
                logger.InfoFormatted("Incoming connection fully accepted", id.Uri);
                return(true);
            } catch (Exception ex) {
                logger.Exception(ex, "Error handling incoming connection");
                CleanupSocket(manager, id);
                return(false);
            }
        }
예제 #4
0
        private async Task <bool> HandleHandshake(PeerId id, HandshakeMessage message)
        {
            TorrentManager man = null;

            if (message.ProtocolString != VersionInfo.ProtocolStringV100)
            {
                return(false);
            }

            // If we're forcing encrypted connections and this is in plain-text, close it!
            if (id.Encryptor is PlainTextEncryption && !engine.Settings.AllowedEncryption.HasFlag(EncryptionTypes.PlainText))
            {
                return(false);
            }

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

            // We're not hosting that torrent
            if (man == null)
            {
                return(false);
            }

            if (man.State == TorrentState.Stopped)
            {
                return(false);
            }

            if (!man.Mode.CanAcceptConnections)
            {
                return(false);
            }

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

            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);
            await PeerIO.SendMessageAsync(id.Connection, id.Encryptor, message, id.TorrentManager.UploadLimiter, id.Monitor, id.TorrentManager.Monitor);

            engine.ConnectionManager.IncomingConnectionAccepted(id);
            return(true);
        }
예제 #5
0
        public void IgnoreNullMonitors()
        {
            var blockSize = Constants.BlockSize - 1234;
            var msg       = new PieceMessage(0, 0, blockSize);

            msg.SetData(new MemoryPool().Rent(blockSize));

            Assert.DoesNotThrowAsync(() => {
                return(Task.WhenAll(
                           PeerIO.SendMessageAsync(pair.Incoming, PlainTextEncryption.Instance, msg).AsTask(),
                           PeerIO.ReceiveMessageAsync(pair.Outgoing, PlainTextEncryption.Instance).AsTask()
                           ));
            });
        }
예제 #6
0
        private async void EndCheckEncryption(PeerId id, byte[] initialData)
        {
            try
            {
                if (initialData != null && initialData.Length > 0)
                {
                    throw new EncryptionException("unhandled initial data");
                }

                EncryptionTypes e = engine.Settings.AllowedEncryption;
                if (id.Encryptor is RC4 && !Toolbox.HasEncryption(e, EncryptionTypes.RC4Full) ||
                    id.Encryptor is RC4Header && !Toolbox.HasEncryption(e, EncryptionTypes.RC4Header) ||
                    id.Encryptor is PlainTextEncryption && !Toolbox.HasEncryption(e, EncryptionTypes.PlainText))
                {
                    CleanupSocket(id, id.Encryptor.GetType().Name + " encryption is not enabled");
                }
                else
                {
                    // Create a handshake message to send to the peer
                    var handshake = new HandshakeMessage(id.TorrentManager.InfoHash, engine.PeerId, VersionInfo.ProtocolStringV100);
                    await PeerIO.SendMessageAsync(id.Connection, id.Encryptor, handshake, id.TorrentManager.UploadLimiter, id.Monitor, id.TorrentManager.Monitor);

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

                    handshake.Handle(id);

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

                    ReceiveMessagesAsync(id.Connection, id.Decryptor, id.TorrentManager.DownloadLimiter, id.Monitor, id.TorrentManager, id);
                    // Alert the engine that there is a new usable connection
                    id.TorrentManager.HandlePeerConnected(id, Direction.Outgoing);
                }
            }
            catch
            {
                id.Peer.Encryption &= ~EncryptionTypes.RC4Full;
                id.Peer.Encryption &= ~EncryptionTypes.RC4Header;
                CleanupSocket(id, "Failed encryptor check");
            }
        }
예제 #7
0
        public void IgnoreNullMonitors()
        {
            var blockSize = Piece.BlockSize - 1234;
            var msg       = new PieceMessage(0, 0, blockSize)
            {
                DataReleaser = new ByteBufferPool.Releaser(null, new ByteBuffer(blockSize))
            };

            Assert.DoesNotThrowAsync(() => {
                return(Task.WhenAll(
                           PeerIO.SendMessageAsync(pair.Incoming, PlainTextEncryption.Instance, msg).AsTask(),
                           PeerIO.ReceiveMessageAsync(pair.Outgoing, PlainTextEncryption.Instance).AsTask()
                           ));
            });
        }
예제 #8
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)
            {
                try {
                    PeerMessage msg = id.Dequeue();
                    var         pm  = msg as PieceMessage;

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


                    if (pm != null)
                    {
                        ClientEngine.BufferManager.FreeBuffer(pm.Data);
                    }

                    // Fire the event to let the user know a message was sent
                    if (PeerMessageTransferred != null)
                    {
                        RaisePeerMessageTransferred(new PeerMessageEventArgs(id.TorrentManager, msg, Direction.Outgoing, id));
                    }

                    id.LastMessageSent.Restart();
                } catch (Exception e) {
                    CleanupSocket(id, "Exception calling SendMessage: " + e.Message);
                    break;
                }
            }

            id.ProcessingQueue = false;
        }
예제 #9
0
        /// <summary>
        /// This method is called when the ClientEngine recieves a valid incoming connection
        /// </summary>
        /// <param name="manager">The torrent which the peer is associated with.</param>
        /// <param name="id">The peer who just conencted</param>
        internal async ReusableTask <bool> IncomingConnectionAcceptedAsync(TorrentManager manager, PeerId id)
        {
            try {
                bool maxAlreadyOpen = OpenConnections >= Math.Min(MaxOpenConnections, manager.Settings.MaximumConnections);
                if (LocalPeerId.Equals(id.Peer.PeerId) || maxAlreadyOpen)
                {
                    CleanupSocket(manager, id);
                    return(false);
                }

                if (manager.Peers.ActivePeers.Contains(id.Peer))
                {
                    Logger.Log(id.Connection, "ConnectionManager - Already connected to peer");
                    id.Connection.Dispose();
                    return(false);
                }

                // Add the PeerId to the lists *before* doing anything asynchronous. This ensures that
                // all PeerIds are tracked in 'ConnectedPeers' as soon as they're created.
                Logger.Log(id.Connection, "ConnectionManager - Incoming connection fully accepted");
                manager.Peers.AvailablePeers.Remove(id.Peer);
                manager.Peers.ActivePeers.Add(id.Peer);
                manager.Peers.ConnectedPeers.Add(id);

                id.WhenConnected.Restart();
                // Baseline the time the last block was received
                id.LastBlockReceived.Restart();

                // Send our handshake now that we've decided to keep the connection
                var handshake = new HandshakeMessage(manager.InfoHash, manager.Engine.PeerId, VersionInfo.ProtocolStringV100);
                await PeerIO.SendMessageAsync(id.Connection, id.Encryptor, handshake, manager.UploadLimiters, id.Monitor, manager.Monitor);

                manager.HandlePeerConnected(id);

                // We've sent our handshake so begin our looping to receive incoming message
                ReceiveMessagesAsync(id.Connection, id.Decryptor, manager.DownloadLimiters, id.Monitor, manager, id);
                return(true);
            } catch {
                CleanupSocket(manager, id);
                return(false);
            }
        }
예제 #10
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.BufferManager.GetBuffer(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.BufferManager.FreeBuffer(pm.Data);
                    }
                }
            }

            id.ProcessingQueue = false;
        }
예제 #11
0
        internal async void ProcessNewOutgoingConnection(PeerId id)
        {
            // If we have too many open connections, close the connection
            if (OpenConnections > MaxOpenConnections)
            {
                CleanupSocket(id);
                return;
            }

            // The peer is no longer in the 'ConnectingToPeers' list, so we should
            // add it immediately to the 'Connected' list so it is always in one of
            // the lists.
            id.ProcessingQueue = true;
            id.TorrentManager.Peers.ActivePeers.Add(id.Peer);
            id.TorrentManager.Peers.ConnectedPeers.Add(id);

            try {
                // Increase the count of the "open" connections
                var result = await EncryptorFactory.CheckOutgoingConnectionAsync(id.Connection, id.Peer.AllowedEncryption, engine.Settings, id.TorrentManager.InfoHash);

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

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

            try {
                // Create a handshake message to send to the peer
                var handshake = new HandshakeMessage(id.TorrentManager.InfoHash, engine.PeerId, VersionInfo.ProtocolStringV100);
                await PeerIO.SendMessageAsync(id.Connection, id.Encryptor, handshake, id.TorrentManager.UploadLimiter, id.Monitor, id.TorrentManager.Monitor);

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

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

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

            try {
                id.TorrentManager.HandlePeerConnected(id);

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

                ReceiveMessagesAsync(id.Connection, id.Decryptor, id.TorrentManager.DownloadLimiter, id.Monitor, id.TorrentManager, id);

                id.WhenConnected.Restart();
                id.LastBlockReceived.Restart();
            } catch {
                id.TorrentManager.RaiseConnectionAttemptFailed(new ConnectionAttemptFailedEventArgs(id.Peer, ConnectionFailureReason.Unknown, id.TorrentManager));
                CleanupSocket(id);
                return;
            }
        }
예제 #12
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 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();
            }
        }