public void TestHandshake(byte[] buffer, CustomConnection connection) { // 1) Send local handshake SendMessage(new HandshakeMessage(rig.Manager.Torrent.infoHash, new string('g', 20), VersionInfo.ProtocolStringV100, true, false), connection); // 2) Receive remote handshake if (buffer == null || buffer.Length == 0) { buffer = new byte[68]; Receive(connection, buffer, 0, 68); decryptor.Decrypt(buffer); } HandshakeMessage handshake = new HandshakeMessage(); handshake.Decode(buffer, 0, buffer.Length); Assert.Equal(rig.Engine.PeerId, handshake.PeerId); Assert.Equal(VersionInfo.ProtocolStringV100, handshake.ProtocolString); Assert.Equal(ClientEngine.SupportsFastPeer, handshake.SupportsFastPeer); Assert.Equal(ClientEngine.SupportsExtended, handshake.SupportsExtendedMessaging); // 2) Send local bitfield SendMessage(new BitfieldMessage(rig.Manager.Bitfield), connection); // 3) Receive remote bitfield - have none PeerMessage message = ReceiveMessage(connection); Assert.True(message is HaveNoneMessage || message is BitfieldMessage, "HaveNone"); // 4) Send a few allowed fast SendMessage(new AllowedFastMessage(1), connection); SendMessage(new AllowedFastMessage(2), connection); SendMessage(new AllowedFastMessage(3), connection); SendMessage(new AllowedFastMessage(0), connection); // 5) Receive a few allowed fast ReceiveMessage(connection); ReceiveMessage(connection); ReceiveMessage(connection); ReceiveMessage(connection); ReceiveMessage(connection); ReceiveMessage(connection); ReceiveMessage(connection); ReceiveMessage(connection); ReceiveMessage(connection); ReceiveMessage(connection); }
public async Task EncryptorFactoryPeerAPlain() { await rig.Engine.StartAll(); rig.AddConnection(conn.Incoming); HandshakeMessage message = new HandshakeMessage(rig.Manager.InfoHash, "ABC123ABC123ABC123AB", VersionInfo.ProtocolStringV100); byte[] buffer = message.Encode(); await conn.Outgoing.SendAsync(buffer, 0, buffer.Length); await conn.Outgoing.ReceiveAsync(buffer, 0, buffer.Length); message.Decode(buffer, 0, buffer.Length); Assert.AreEqual(VersionInfo.ProtocolStringV100, message.ProtocolString); }
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.TorrentManager, e.Connection); id.LastMessageSent.Restart(); id.LastMessageReceived.Restart(); Engine.ConnectionManager.ProcessNewOutgoingConnection(id); return; } Logger.Log(e.Connection, "ListenManager - ConnectionReceived"); var skeys = new List <InfoHash>(); for (int i = 0; i < Engine.Torrents.Count; i++) { skeys.Add(Engine.Torrents[i].InfoHash); } var result = await EncryptorFactory.CheckIncomingConnectionAsync(e.Connection, e.Peer.AllowedEncryption, Engine.Settings, HandshakeMessage.HandshakeLength, skeys.ToArray()); var handshake = new HandshakeMessage(); handshake.Decode(result.InitialData, 0, result.InitialData.Length); if (!await HandleHandshake(e.Peer, e.Connection, handshake, result.Decryptor, result.Encryptor)) { e.Connection.Dispose(); } } catch { e.Connection.Dispose(); } }
private static void HandshakeReceived(bool succeeded, int count, object state) { var result = (EncryptorAsyncResult)state; var connection = result.Id.Connection; try { if (!succeeded) { throw new EncryptionException("Couldn't receive the handshake"); } result.Available += count; var message = new HandshakeMessage(); message.Decode(result.Buffer, 0, result.Buffer.Length); var valid = message.ProtocolString == VersionInfo.ProtocolStringV100; var usable = CheckRc4(result.Id); var canUseRc4 = Toolbox.HasEncryption(usable, EncryptionTypes.RC4Header) || Toolbox.HasEncryption(usable, EncryptionTypes.RC4Full); // If encryption is disabled and we received an invalid handshake - abort! if (valid) { result.InitialData = result.Buffer; result.Complete(); return; } if (!canUseRc4) { result.Complete(new EncryptionException("Invalid handshake received and no decryption works")); return; } // The data we just received was part of an encrypted handshake and was *not* the BitTorrent handshake result.EncSocket = new PeerBEncryption(result.SKeys, EncryptionTypes.All); result.EncSocket.BeginHandshake(connection, result.Buffer, 0, result.Buffer.Length, CompletedEncryptedHandshakeCallback, result); } catch (Exception ex) { result.Complete(ex); } }
private void PeerBTest(EncryptionTypes encryption) { rig.Engine.Settings.AllowedEncryption = encryption; rig.Engine.StartAll(); rig.AddConnection(conn.Outgoing); PeerBEncryption a = new PeerBEncryption(new InfoHash[] { rig.Manager.InfoHash }, EncryptionTypes.All); IAsyncResult result = a.BeginHandshake(conn.Incoming, null, null); #if NETSTANDARD1_5 if (!result.AsyncWaitHandle.WaitOne(4000)) #else if (!result.AsyncWaitHandle.WaitOne(4000, true)) #endif { Assert.True(false, "Handshake timed out"); } a.EndHandshake(result); HandshakeMessage message = new HandshakeMessage(); byte[] buffer = new byte[68]; conn.Incoming.EndReceive(conn.Incoming.BeginReceive(buffer, 0, buffer.Length, null, null)); a.Decryptor.Decrypt(buffer); message.Decode(buffer, 0, buffer.Length); Assert.Equal(VersionInfo.ProtocolStringV100, message.ProtocolString); if (encryption == EncryptionTypes.RC4Full) { Assert.True(a.Encryptor is RC4); } else if (encryption == EncryptionTypes.RC4Header) { Assert.True(a.Encryptor is RC4Header); } else if (encryption == EncryptionTypes.PlainText) { Assert.True(a.Encryptor is RC4Header); } }
async Task PeerBTest(EncryptionTypes encryption) { rig.Engine.Settings.AllowedEncryption = encryption; await rig.Engine.StartAll(); rig.AddConnection(conn.Outgoing); PeerBEncryption a = new PeerBEncryption(new InfoHash[] { rig.Manager.InfoHash }, EncryptionTypes.All); var result = a.HandshakeAsync(conn.Incoming); if (!result.Wait(4000)) { Assert.Fail("Handshake timed out"); } HandshakeMessage message = new HandshakeMessage(); byte[] buffer = new byte[HandshakeMessage.HandshakeLength]; await conn.Incoming.ReceiveAsync(buffer, 0, buffer.Length); a.Decryptor.Decrypt(buffer); message.Decode(buffer, 0, buffer.Length); Assert.AreEqual(VersionInfo.ProtocolStringV100, message.ProtocolString); if (encryption == EncryptionTypes.RC4Full) { Assert.IsTrue(a.Encryptor is RC4); } else if (encryption == EncryptionTypes.RC4Header) { Assert.IsTrue(a.Encryptor is RC4Header); } else if (encryption == EncryptionTypes.PlainText) { Assert.IsTrue(a.Encryptor is RC4Header); } }
private void Handshake(EncryptionTypes encryptionA, EncryptionTypes encryptionB, bool addInitial) { bool doneA = false; bool doneB = false; HandshakeMessage m = new HandshakeMessage(rig.Torrent.InfoHash, "12345123451234512345", VersionInfo.ProtocolStringV100); byte[] handshake = m.Encode(); PeerAEncryption a = new PeerAEncryption(rig.Torrent.InfoHash, encryptionA); if (addInitial) { a.AddPayload(handshake); } PeerBEncryption b = new PeerBEncryption(new InfoHash[] { rig.Torrent.InfoHash }, encryptionB); IAsyncResult resultA = a.BeginHandshake(conn.Outgoing, null, null); IAsyncResult resultB = b.BeginHandshake(conn.Incoming, null, null); WaitHandle[] handles = new WaitHandle[] { resultA.AsyncWaitHandle, resultB.AsyncWaitHandle }; int count = 1000; while (!WaitHandle.WaitAll(handles, 5, true)) { if (!doneA && (doneA = resultA.IsCompleted)) { a.EndHandshake(resultA); } if (!doneB && (doneB = resultB.IsCompleted)) { b.EndHandshake(resultB); } if (count-- == 0) { Assert.Fail("Could not handshake"); } } if (!doneA) { a.EndHandshake(resultA); } if (!doneB) { b.EndHandshake(resultB); } HandshakeMessage d = new HandshakeMessage(); if (!addInitial) { a.Encrypt(handshake, 0, handshake.Length); b.Decrypt(handshake, 0, handshake.Length); d.Decode(handshake, 0, handshake.Length); } else { d.Decode(b.InitialData, 0, b.InitialData.Length); } Assert.AreEqual(m, d); if (encryptionA == EncryptionTypes.RC4Full || encryptionB == EncryptionTypes.RC4Full) { Assert.IsTrue(a.Encryptor is RC4); Assert.IsTrue(b.Encryptor is RC4); } else if (encryptionA == EncryptionTypes.RC4Header || encryptionB == EncryptionTypes.RC4Header) { Assert.IsTrue(a.Encryptor is RC4Header); Assert.IsTrue(b.Encryptor is RC4Header); } else if (encryptionA == EncryptionTypes.PlainText || encryptionB == EncryptionTypes.PlainText) { Assert.IsTrue(a.Encryptor is PlainTextEncryption); Assert.IsTrue(b.Encryptor is PlainTextEncryption); } }
static async Task <byte[]> CheckEncryptionAsync(PeerId id, int bytesToReceive, InfoHash[] sKeys, CancellationToken token) { IConnection connection = id.Connection; var allowedEncryption = (id.Engine?.Settings.AllowedEncryption ?? EncryptionTypes.All) & id.Peer.Encryption; var supportsRC4Header = allowedEncryption.HasFlag(EncryptionTypes.RC4Header); var supportsRC4Full = allowedEncryption.HasFlag(EncryptionTypes.RC4Full); var supportsPlainText = allowedEncryption.HasFlag(EncryptionTypes.PlainText); // If the connection is incoming, receive the handshake before // trying to decide what encryption to use if (connection.IsIncoming) { var buffer = new byte[bytesToReceive]; await NetworkIO.ReceiveAsync(connection, buffer, 0, bytesToReceive, null, null, null).ConfigureAwait(false); HandshakeMessage message = new HandshakeMessage(); message.Decode(buffer, 0, buffer.Length); if (message.ProtocolString == VersionInfo.ProtocolStringV100) { if (supportsPlainText) { id.Encryptor = id.Decryptor = PlainTextEncryption.Instance; return(buffer); } } else if (supportsRC4Header || supportsRC4Full) { // The data we just received was part of an encrypted handshake and was *not* the BitTorrent handshake var encSocket = new PeerBEncryption(sKeys, EncryptionTypes.All); await encSocket.HandshakeAsync(connection, buffer, 0, buffer.Length); 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"); } id.Decryptor = encSocket.Decryptor; id.Encryptor = encSocket.Encryptor; return(encSocket.InitialData?.Length > 0 ? encSocket.InitialData : null); } } else { if ((id.Engine.Settings.PreferEncryption || !supportsPlainText) && (supportsRC4Header || supportsRC4Full)) { var encSocket = new PeerAEncryption(id.TorrentManager.InfoHash, allowedEncryption); 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"); } id.Decryptor = encSocket.Decryptor; id.Encryptor = encSocket.Encryptor; return(encSocket.InitialData?.Length > 0 ? encSocket.InitialData : null); } else if (supportsPlainText) { id.Encryptor = id.Decryptor = PlainTextEncryption.Instance; return(null); } } throw new EncryptionException("Invalid handshake received and no decryption works"); }
static async ReusableTask <EncryptorResult> DoCheckIncomingConnectionAsync(IConnection connection, IList <EncryptionType> preferredEncryption, InfoHash[] sKeys) { bool supportsRC4Header = preferredEncryption.Contains(EncryptionType.RC4Header); bool supportsRC4Full = preferredEncryption.Contains(EncryptionType.RC4Full); bool supportsPlainText = preferredEncryption.Contains(EncryptionType.PlainText); // If the connection is incoming, receive the handshake before // trying to decide what encryption to use var message = new HandshakeMessage(); using (NetworkIO.BufferPool.Rent(HandshakeMessage.HandshakeLength, out ByteBuffer buffer)) { await NetworkIO.ReceiveAsync(connection, buffer, 0, HandshakeMessage.HandshakeLength, null, null, null).ConfigureAwait(false); message.Decode(buffer.Data, 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, preferredEncryption); await encSocket.HandshakeAsync(connection, buffer.Data, 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) { await NetworkIO.ReceiveAsync(connection, buffer, 0, HandshakeMessage.HandshakeLength, null, null, null).ConfigureAwait(false); data = buffer.Data; 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)); } } } connection.Dispose(); throw new EncryptionException("Invalid handshake received and no decryption works"); }
private void EndCheckEncryption(IAsyncResult result) { var id = (PeerId) result.AsyncState; try { byte[] initialData; EncryptorFactory.EndCheckEncryption(result, out initialData); if (initialData != null && initialData.Length == HandshakeMessage.HandshakeLength) { var message = new HandshakeMessage(); message.Decode(initialData, 0, initialData.Length); HandleHandshake(id, message); } else if (initialData.Length > 0) { throw new Exception("Argh. I can't handle this scenario. It also shouldn't happen. Ever."); } else { PeerIO.EnqueueReceiveHandshake(id.Connection, id.Decryptor, _handshakeReceivedCallback, id); } } catch { id.Connection.Dispose(); } }
static async ReusableTask <EncryptorResult> DoCheckIncomingConnectionAsync(IConnection2 connection, EncryptionTypes encryption, EngineSettings settings, InfoHash[] sKeys) { var allowedEncryption = (settings?.AllowedEncryption ?? EncryptionTypes.All) & encryption; var supportsRC4Header = allowedEncryption.HasFlag(EncryptionTypes.RC4Header); var supportsRC4Full = allowedEncryption.HasFlag(EncryptionTypes.RC4Full); var supportsPlainText = allowedEncryption.HasFlag(EncryptionTypes.PlainText); // If the connection is incoming, receive the handshake before // trying to decide what encryption to use var 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 var encSocket = new PeerBEncryption(sKeys, EncryptionTypes.All); await encSocket.HandshakeAsync(connection, buffer, 0, HandshakeMessage.HandshakeLength); 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. var data = encSocket.InitialData?.Length > 0 ? encSocket.InitialData : null; if (data == null) { data = buffer; await NetworkIO.ReceiveAsync(connection, data, 0, HandshakeMessage.HandshakeLength, null, null, null); 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"); }