From: JamJar00 Date: Mon, 14 Nov 2016 23:08:17 +0000 (+0000) Subject: Added Handshaking X-Git-Tag: 1.0.0~131 X-Git-Url: https://git.deb.at/?a=commitdiff_plain;h=e0015195c4a08353e2c48ec2bb4152ac0db61236;p=rhonda%2Fimpostor.hazel.git Added Handshaking --- diff --git a/Hazel.UnitTests/TcpConnectionTests.cs b/Hazel.UnitTests/TcpConnectionTests.cs index b092b5e..fc61c9f 100644 --- a/Hazel.UnitTests/TcpConnectionTests.cs +++ b/Hazel.UnitTests/TcpConnectionTests.cs @@ -4,6 +4,7 @@ using System.Net; using System.Threading; using Hazel.Tcp; +using System.Linq; namespace Hazel.UnitTests { @@ -30,11 +31,28 @@ namespace Hazel.UnitTests //TcpConnection fields Assert.AreEqual(new IPEndPoint(IPAddress.Loopback, 4296), connection.RemoteEndPoint); - Assert.AreEqual(0, connection.Statistics.DataBytesSent); + Assert.AreEqual(1, connection.Statistics.DataBytesSent); Assert.AreEqual(0, connection.Statistics.DataBytesReceived); } } + [TestMethod] + public void TcpHandshakeTest() + { + using (TcpConnectionListener listener = new TcpConnectionListener(IPAddress.Any, 4296, IPMode.IPv4)) + using (TcpConnection connection = new TcpConnection(new NetworkEndPoint(IPAddress.Loopback, 4296, IPMode.IPv4))) + { + listener.Start(); + + listener.NewConnection += delegate (object sender, NewConnectionEventArgs e) + { + Assert.IsTrue(Enumerable.SequenceEqual(e.HandshakeData, new byte[] { 1, 2, 3, 4, 5, 6 })); + }; + + connection.Connect(new byte[] { 1, 2, 3, 4, 5, 6 }); + } + } + /// /// Tests IPv4 connectivity. /// @@ -76,7 +94,7 @@ namespace Hazel.UnitTests using (TcpConnectionListener listener = new TcpConnectionListener(IPAddress.Any, 4296)) using (TcpConnection connection = new TcpConnection(new NetworkEndPoint(IPAddress.Loopback, 4296))) { - TestHelper.RunServerToClientTest(listener, connection, 4, 0, SendOption.FragmentedReliable); + TestHelper.RunServerToClientTest(listener, connection, 4, 5, SendOption.FragmentedReliable); } } @@ -89,7 +107,7 @@ namespace Hazel.UnitTests using (TcpConnectionListener listener = new TcpConnectionListener(IPAddress.Any, 4296)) using (TcpConnection connection = new TcpConnection(new NetworkEndPoint(IPAddress.Loopback, 4296))) { - TestHelper.RunClientToServerTest(listener, connection, 4, 0, SendOption.FragmentedReliable); + TestHelper.RunClientToServerTest(listener, connection, 4, 5, SendOption.FragmentedReliable); } } diff --git a/Hazel.UnitTests/TestHelper.cs b/Hazel.UnitTests/TestHelper.cs index 838a41a..87091fc 100644 --- a/Hazel.UnitTests/TestHelper.cs +++ b/Hazel.UnitTests/TestHelper.cs @@ -56,7 +56,7 @@ namespace Hazel.UnitTests //Wait until data is received mutex.WaitOne(); - Assert.AreEqual(0, connection.Statistics.DataBytesSent); + Assert.AreEqual(1, connection.Statistics.DataBytesSent); Assert.AreEqual(data.Length, connection.Statistics.DataBytesReceived); Assert.AreEqual(totalHandshakeSize, connection.Statistics.TotalBytesSent); Assert.AreEqual(data.Length + headerSize, connection.Statistics.TotalBytesReceived); @@ -111,7 +111,7 @@ namespace Hazel.UnitTests //Wait until data is received mutex2.WaitOne(); - Assert.AreEqual(data.Length, connection.Statistics.DataBytesSent); + Assert.AreEqual(data.Length + 1, connection.Statistics.DataBytesSent); Assert.AreEqual(0, connection.Statistics.DataBytesReceived); Assert.AreEqual(totalHandshakeSize + data.Length + headerSize, connection.Statistics.TotalBytesSent); Assert.AreEqual(0, connection.Statistics.TotalBytesReceived); diff --git a/Hazel.UnitTests/UdpConnectionTests.cs b/Hazel.UnitTests/UdpConnectionTests.cs index 003cc22..7d7b0a1 100644 --- a/Hazel.UnitTests/UdpConnectionTests.cs +++ b/Hazel.UnitTests/UdpConnectionTests.cs @@ -4,6 +4,7 @@ using System.Net; using System.Threading; using Hazel.Udp; +using System.Linq; namespace Hazel.UnitTests { @@ -30,11 +31,28 @@ namespace Hazel.UnitTests //UdpConnection fields Assert.AreEqual(new IPEndPoint(IPAddress.Loopback, 4296), connection.RemoteEndPoint); - Assert.AreEqual(0, connection.Statistics.DataBytesSent); + Assert.AreEqual(1, connection.Statistics.DataBytesSent); Assert.AreEqual(0, connection.Statistics.DataBytesReceived); } } + [TestMethod] + public void UdpHandshakeTest() + { + using (UdpConnectionListener listener = new UdpConnectionListener(IPAddress.Any, 4296, IPMode.IPv4)) + using (UdpConnection connection = new UdpClientConnection(new NetworkEndPoint(IPAddress.Loopback, 4296, IPMode.IPv4))) + { + listener.Start(); + + listener.NewConnection += delegate (object sender, NewConnectionEventArgs e) + { + Assert.IsTrue(Enumerable.SequenceEqual(e.HandshakeData, new byte[] { 1, 2, 3, 4, 5, 6 })); + }; + + connection.Connect(new byte[] { 1, 2, 3, 4, 5, 6 }); + } + } + /// /// Tests IPv4 connectivity. /// @@ -76,7 +94,7 @@ namespace Hazel.UnitTests using (UdpConnectionListener listener = new UdpConnectionListener(new NetworkEndPoint(IPAddress.Any, 4296))) using (UdpConnection connection = new UdpClientConnection(new NetworkEndPoint(IPAddress.Loopback, 4296))) { - TestHelper.RunServerToClientTest(listener, connection, 1, 3, SendOption.None); + TestHelper.RunServerToClientTest(listener, connection, 1, 4, SendOption.None); } } @@ -89,7 +107,7 @@ namespace Hazel.UnitTests using (UdpConnectionListener listener = new UdpConnectionListener(new NetworkEndPoint(IPAddress.Any, 4296))) using (UdpConnection connection = new UdpClientConnection(new NetworkEndPoint(IPAddress.Loopback, 4296))) { - TestHelper.RunServerToClientTest(listener, connection, 3, 3, SendOption.Reliable); + TestHelper.RunServerToClientTest(listener, connection, 3, 4, SendOption.Reliable); } } @@ -102,7 +120,7 @@ namespace Hazel.UnitTests using (UdpConnectionListener listener = new UdpConnectionListener(new NetworkEndPoint(IPAddress.Any, 4296))) using (UdpConnection connection = new UdpClientConnection(new NetworkEndPoint(IPAddress.Loopback, 4296))) { - TestHelper.RunClientToServerTest(listener, connection, 1, 3, SendOption.None); + TestHelper.RunClientToServerTest(listener, connection, 1, 4, SendOption.None); } } @@ -115,7 +133,7 @@ namespace Hazel.UnitTests using (UdpConnectionListener listener = new UdpConnectionListener(new NetworkEndPoint(IPAddress.Any, 4296))) using (UdpConnection connection = new UdpClientConnection(new NetworkEndPoint(IPAddress.Loopback, 4296))) { - TestHelper.RunClientToServerTest(listener, connection, 3, 3, SendOption.Reliable); + TestHelper.RunClientToServerTest(listener, connection, 3, 4, SendOption.Reliable); } } @@ -133,11 +151,11 @@ namespace Hazel.UnitTests connection.Connect(); connection.KeepAliveInterval = 100; - System.Threading.Thread.Sleep(1100); //Enough time for ~10 keep alive packets + System.Threading.Thread.Sleep(1050); //Enough time for ~10 keep alive packets Assert.IsTrue( - connection.Statistics.TotalBytesSent >= 27 && - connection.Statistics.TotalBytesSent <= 33, + connection.Statistics.TotalBytesSent >= 30 && + connection.Statistics.TotalBytesSent <= 50, "Sent: " + connection.Statistics.TotalBytesSent ); } @@ -158,12 +176,12 @@ namespace Hazel.UnitTests { ((UdpConnection)args.Connection).KeepAliveInterval = 100; - Thread.Sleep(1100); //Enough time for ~10 keep alive packets + Thread.Sleep(1050); //Enough time for ~10 keep alive packets Assert.IsTrue( - args.Connection.Statistics.TotalBytesSent >= 27 && - args.Connection.Statistics.TotalBytesSent <= 33, - "Sent: " + connection.Statistics.TotalBytesSent + args.Connection.Statistics.TotalBytesSent >= 30 && + args.Connection.Statistics.TotalBytesSent <= 50, + "Sent: " + args.Connection.Statistics.TotalBytesSent ); mutex.Set(); diff --git a/Hazel/Connection.cs b/Hazel/Connection.cs index c235f7e..3f12dc0 100644 --- a/Hazel/Connection.cs +++ b/Hazel/Connection.cs @@ -161,12 +161,13 @@ namespace Hazel /// /// Connects the connection to a server and begins listening. /// + /// The bytes of data to send in the handshake. /// /// Calling Connect makes the connection attempt to connect to the end point that's specified in the /// constructor. This method will block until the connection attempt completes and will throw a /// if there is a problem connecting. /// - public abstract void Connect(); + public abstract void Connect(byte[] bytes = null); /// /// Invokes the DataReceived event. diff --git a/Hazel/ConnectionListener.cs b/Hazel/ConnectionListener.cs index e24962a..88d061a 100644 --- a/Hazel/ConnectionListener.cs +++ b/Hazel/ConnectionListener.cs @@ -68,16 +68,17 @@ namespace Hazel /// /// Invokes the NewConnection event with the supplied connection. /// + /// The user sent bytes that were received as part of the handshake. /// The connection to pass in the arguments. /// /// Implementers should call this to invoke the event before data is received so that /// subscribers do not miss any data that may have been sent immediately after connecting. /// - protected void InvokeNewConnection(Connection connection) + protected void InvokeNewConnection(byte[] bytes, Connection connection) { //Get new args NewConnectionEventArgs args = NewConnectionEventArgs.GetObject(); - args.Set(connection); + args.Set(bytes, connection); //Make a copy to avoid race condition between null check and invocation EventHandler handler = NewConnection; diff --git a/Hazel/NewConnectionEventArgs.cs b/Hazel/NewConnectionEventArgs.cs index 30ef5be..f6e911d 100644 --- a/Hazel/NewConnectionEventArgs.cs +++ b/Hazel/NewConnectionEventArgs.cs @@ -32,6 +32,11 @@ namespace Hazel return objectPool.GetObject(); } + /// + /// The data received from the client in the handshake. + /// + public byte[] HandshakeData { get; private set; } + /// /// The to the new client. /// @@ -48,10 +53,12 @@ namespace Hazel /// /// Sets the members of the arguments. /// - /// The new connection - internal void Set(Connection Connection) + /// The bytes that were received in the handshake. + /// The new connection + internal void Set(byte[] bytes, Connection connection) { - this.Connection = Connection; + this.HandshakeData = bytes; + this.Connection = connection; } /// diff --git a/Hazel/Tcp/TcpConnection.cs b/Hazel/Tcp/TcpConnection.cs index 6ce67f3..408130e 100644 --- a/Hazel/Tcp/TcpConnection.cs +++ b/Hazel/Tcp/TcpConnection.cs @@ -77,7 +77,7 @@ namespace Hazel.Tcp } /// - public override void Connect() + public override void Connect(byte[] bytes = null) { lock(socketLock) { @@ -96,15 +96,29 @@ namespace Hazel.Tcp //Start receiving data try { - StartWaitingForHeader(); + StartWaitingForHeader(BodyReadCallback); } - catch (SocketException e) + catch (SocketException e) //TODO change these to catch exception, security risk { throw new HazelException("A Socket exception occured while initiating the first receive operation.", e); } + //Send handshake + byte[] actualBytes; + if (bytes == null) + { + actualBytes = new byte[1]; + } + else + { + actualBytes = new byte[bytes.Length + 1]; + Buffer.BlockCopy(bytes, 0, actualBytes, 1, bytes.Length); + } + //Set connected State = ConnectionState.Connected; + + SendBytes(actualBytes); } } @@ -146,7 +160,8 @@ namespace Hazel.Tcp /// Called when a 4 byte header has been received. /// /// The 4 header bytes read. - void HeaderReadCallback(byte[] bytes) + /// The callback to invoke when the body has been received. + void HeaderReadCallback(byte[] bytes, Action callback) { //Get length int length = GetLengthFromBytes(bytes); @@ -154,7 +169,7 @@ namespace Hazel.Tcp //Begin receiving the body try { - StartWaitingForBytes(length, BodyReadCallback); + StartWaitingForBytes(length, callback); } catch (SocketException e) { @@ -171,7 +186,7 @@ namespace Hazel.Tcp //Begin receiving from the start try { - StartWaitingForHeader(); + StartWaitingForHeader(BodyReadCallback); } catch (SocketException e) { @@ -191,7 +206,32 @@ namespace Hazel.Tcp { try { - StartWaitingForHeader(); + StartWaitingForHeader(BodyReadCallback); + } + catch (SocketException e) + { + HandleDisconnect(new HazelException("A Socket exception occured while initiating the first receive operation.", e)); + } + } + + /// + /// Starts waiting for a first handshake packet to be received. + /// + /// The callback to invoke when the handshake has been received. + internal void StartWaitingForHandshake(Action callback) + { + try + { + StartWaitingForHeader( + delegate (byte[] bytes) + { + //Remove version byte + byte[] dataBytes = new byte[bytes.Length - 1]; + Buffer.BlockCopy(bytes, 1, dataBytes, 0, bytes.Length - 1); + + callback.Invoke(dataBytes); + } + ); } catch (SocketException e) { @@ -202,9 +242,10 @@ namespace Hazel.Tcp /// /// Starts this connections waiting for the header. /// - void StartWaitingForHeader() + /// The callback to invoke when the body has been read. + void StartWaitingForHeader(Action callback) { - StartWaitingForBytes(4, HeaderReadCallback); + StartWaitingForBytes(4, (bytes) => HeaderReadCallback(bytes, callback)); } /// diff --git a/Hazel/Tcp/TcpConnectionListener.cs b/Hazel/Tcp/TcpConnectionListener.cs index 2419b1b..8374d20 100644 --- a/Hazel/Tcp/TcpConnectionListener.cs +++ b/Hazel/Tcp/TcpConnectionListener.cs @@ -98,10 +98,16 @@ namespace Hazel.Tcp //Sort the event out TcpConnection tcpConnection = new TcpConnection(tcpSocket); - //Invoke - InvokeNewConnection(tcpConnection); - - tcpConnection.StartReceiving(); + //Wait for handshake + tcpConnection.StartWaitingForHandshake( + delegate (byte[] bytes) + { + //Invoke + InvokeNewConnection(bytes, tcpConnection); + + tcpConnection.StartReceiving(); + } + ); } } diff --git a/Hazel/Udp/UdpClientConnection.cs b/Hazel/Udp/UdpClientConnection.cs index f8836fb..04ff3a0 100644 --- a/Hazel/Udp/UdpClientConnection.cs +++ b/Hazel/Udp/UdpClientConnection.cs @@ -88,7 +88,7 @@ namespace Hazel.Udp } /// - public override void Connect() + public override void Connect(byte[] bytes = null) { lock(socketLock) { @@ -129,7 +129,7 @@ namespace Hazel.Udp //Write bytes to the server to tell it hi (and to punch a hole in our NAT, if present) //When acknowledged set the state to connected - SendHello(() => { lock (socketLock) State = ConnectionState.Connected; }); + SendHello(bytes, () => { lock (socketLock) State = ConnectionState.Connected; }); //Wait till hello packet is acknowledged and the state is set to Connected WaitOnConnect(); diff --git a/Hazel/Udp/UdpConnection.KeepAlive.cs b/Hazel/Udp/UdpConnection.KeepAlive.cs index d789a48..f1d6f3c 100644 --- a/Hazel/Udp/UdpConnection.KeepAlive.cs +++ b/Hazel/Udp/UdpConnection.KeepAlive.cs @@ -66,7 +66,7 @@ namespace Hazel.Udp (o) => { Trace.WriteLine("Keepalive packet sent."); - SendHello(null); + SendHello(null, null); }, null, keepAliveInterval, diff --git a/Hazel/Udp/UdpConnection.cs b/Hazel/Udp/UdpConnection.cs index 98afd27..ed21ccd 100644 --- a/Hazel/Udp/UdpConnection.cs +++ b/Hazel/Udp/UdpConnection.cs @@ -59,15 +59,10 @@ namespace Hazel.Udp byte[] bytes; switch (sendOption) { - //Handle reliable header + //Handle reliable header and hellos case (byte)SendOption.Reliable: - bytes = new byte[data.Length + 3]; - WriteReliableSendHeader(bytes, ackCallback); - break; - - //Handle hellos (ignore data) case (byte)SendOptionInternal.Hello: - bytes = new byte[3]; + bytes = new byte[data.Length + 3]; WriteReliableSendHeader(bytes, ackCallback); break; @@ -144,9 +139,21 @@ namespace Hazel.Udp /// Sends a hello packet to the remote endpoint. /// /// The callback to invoke when the hello packet is acknowledged. - protected void SendHello(Action acknowledgeCallback) + protected void SendHello(byte[] bytes, Action acknowledgeCallback) { - HandleSend(new byte[0], (byte)SendOptionInternal.Hello, acknowledgeCallback); + //First byte of handshake is version indicator so add data after + byte[] actualBytes; + if (bytes == null) + { + actualBytes = new byte[1]; + } + else + { + actualBytes = new byte[bytes.Length + 1]; + Buffer.BlockCopy(bytes, 0, actualBytes, 1, bytes.Length); + } + + HandleSend(actualBytes, (byte)SendOptionInternal.Hello, acknowledgeCallback); } /// diff --git a/Hazel/Udp/UdpConnectionListener.cs b/Hazel/Udp/UdpConnectionListener.cs index 066975d..f999202 100644 --- a/Hazel/Udp/UdpConnectionListener.cs +++ b/Hazel/Udp/UdpConnectionListener.cs @@ -149,7 +149,7 @@ namespace Hazel.Udp else { //Check for malformed connection attempts - if (buffer[0] != (byte)SendOptionInternal.Hello || buffer.Length != 3) + if (buffer[0] != (byte)SendOptionInternal.Hello) return; connection = new UdpServerConnection(this, remoteEndPoint, IPMode); @@ -162,9 +162,15 @@ namespace Hazel.Udp //And fire the corresponding event if (aware) + { connection.InvokeDataReceived(buffer); + } else - InvokeNewConnection(connection); + { + byte[] dataBuffer = new byte[buffer.Length - 1]; + Buffer.BlockCopy(buffer, 1, dataBuffer, 0, buffer.Length - 1); + InvokeNewConnection(dataBuffer, connection); + } } /// diff --git a/Hazel/Udp/UdpServerConnection.cs b/Hazel/Udp/UdpServerConnection.cs index 4d3419a..98a2047 100644 --- a/Hazel/Udp/UdpServerConnection.cs +++ b/Hazel/Udp/UdpServerConnection.cs @@ -60,7 +60,7 @@ namespace Hazel.Udp /// /// This will always throw a HazelException. /// - public override void Connect() + public override void Connect(byte[] bytes) { throw new HazelException("Cannot manually connect a UdpServerConnection, did you mean to use UdpClientConnection?"); }