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