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; } }
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(); } }
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); } }
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); } }
/// <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); }
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(); } }
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; }
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); } }
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)) {
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); } }
/// <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); } }
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"); }
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"); }
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); } }
public static async ReusableTask <HandshakeMessage> ReceiveHandshakeAsync(IConnection2 connection, IEncryption decryptor) { using (ClientEngine.BufferPool.Rent(HandshakeMessage.HandshakeLength, out byte[] buffer)) {
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)); }
public static ReusableTask SendAsync(IConnection2 connection, byte [] buffer, int offset, int count) => SendAsync(connection, buffer, offset, count, null, null, null);
public static async ReusableTask ConnectAsync(IConnection2 connection) { await IOLoop; await connection.ConnectAsync(); }
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"); }
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)); }
public static ReusableTask ReceiveAsync(IConnection2 connection, byte[] buffer, int offset, int count) { return(ReceiveAsync(connection, buffer, offset, count, null, null, null)); }
public static ReusableTask <PeerMessage> ReceiveMessageAsync(IConnection2 connection, IEncryption decryptor) { return(ReceiveMessageAsync(connection, decryptor, null, null, null, null)); }
public static ReusableTask SendMessageAsync(IConnection2 connection, IEncryption encryptor, PeerMessage message) { return(SendMessageAsync(connection, encryptor, message, null, null, null)); }