]> git.deb.at Git - rhonda/impostor.hazel.git/commitdiff
Added Handshaking
authorJamJar00 <jamster.30@btinternet.com>
Mon, 14 Nov 2016 23:08:17 +0000 (23:08 +0000)
committerJamJar00 <jamster.30@btinternet.com>
Mon, 14 Nov 2016 23:08:17 +0000 (23:08 +0000)
13 files changed:
Hazel.UnitTests/TcpConnectionTests.cs
Hazel.UnitTests/TestHelper.cs
Hazel.UnitTests/UdpConnectionTests.cs
Hazel/Connection.cs
Hazel/ConnectionListener.cs
Hazel/NewConnectionEventArgs.cs
Hazel/Tcp/TcpConnection.cs
Hazel/Tcp/TcpConnectionListener.cs
Hazel/Udp/UdpClientConnection.cs
Hazel/Udp/UdpConnection.KeepAlive.cs
Hazel/Udp/UdpConnection.cs
Hazel/Udp/UdpConnectionListener.cs
Hazel/Udp/UdpServerConnection.cs

index b092b5ec031651964843dc026880c9b2199bb04d..fc61c9ffe8ed322d20674f08e8afefc0c731d8c4 100644 (file)
@@ -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 });
+            }
+        }
+
         /// <summary>
         ///     Tests IPv4 connectivity.
         /// </summary>
@@ -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);
             }
         }
 
index 838a41ae8ca024f36baad949f2ffcc1cefda268c..87091fcca6c2268211479c0ec2879db87b5011f9 100644 (file)
@@ -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);
index 003cc22e49d943124c132f518aaf1b85e1ee6c7d..7d7b0a1110c70474014946cecc9a3448e1fc1826 100644 (file)
@@ -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 });
+            }
+        }
+
         /// <summary>
         ///     Tests IPv4 connectivity.
         /// </summary>
@@ -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();
index c235f7e38a0d20a9ccba60d8149b72c94108c1b6..3f12dc0236a632b564373b13876a72165f1cd626 100644 (file)
@@ -161,12 +161,13 @@ namespace Hazel
         /// <summary>
         ///     Connects the connection to a server and begins listening.
         /// </summary>
+        /// <param name="bytes">The bytes of data to send in the handshake.</param>
         /// <remarks>
         ///     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 
         ///     <see cref="HazelException"/> if there is a problem connecting.
         /// </remarks>
-        public abstract void Connect();
+        public abstract void Connect(byte[] bytes = null);
 
         /// <summary>
         ///     Invokes the DataReceived event.
index e24962aa8dae6aa119186fe3a6dec290dba0b4a5..88d061af966255aa884a52b515e1740120e0d0ce 100644 (file)
@@ -68,16 +68,17 @@ namespace Hazel
         /// <summary>
         ///     Invokes the NewConnection event with the supplied connection.
         /// </summary>
+        /// <param name="bytes">The user sent bytes that were received as part of the handshake.</param>
         /// <param name="connection">The connection to pass in the arguments.</param>
         /// <remarks>
         ///     Implementers should call this to invoke the <see cref="NewConnection"/> event before data is received so that
         ///     subscribers do not miss any data that may have been sent immediately after connecting.
         /// </remarks>
-        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<NewConnectionEventArgs> handler = NewConnection;
index 30ef5bedfbf383c59213fedba900323c74d1c35e..f6e911db837b4520f586233a8001be9cb74b33a8 100644 (file)
@@ -32,6 +32,11 @@ namespace Hazel
             return objectPool.GetObject();
         }
 
+        /// <summary>
+        ///     The data received from the client in the handshake.
+        /// </summary>
+        public byte[] HandshakeData { get; private set; }
+
         /// <summary>
         ///     The <see cref="Connection"/> to the new client.
         /// </summary>
@@ -48,10 +53,12 @@ namespace Hazel
         /// <summary>
         ///     Sets the members of the arguments.
         /// </summary>
-        /// <param name="Connection">The new connection</param>
-        internal void Set(Connection Connection)
+        /// <param name="bytes">The bytes that were received in the handshake.</param>
+        /// <param name="connection">The new connection</param>
+        internal void Set(byte[] bytes, Connection connection)
         {
-            this.Connection = Connection;
+            this.HandshakeData = bytes;
+            this.Connection = connection;
         }
 
         /// <inheritdoc />
index 6ce67f3c9f3f3c496962e6abe30c3022cc628410..408130e412771487c9e9ecd1b08e4b26521fff75 100644 (file)
@@ -77,7 +77,7 @@ namespace Hazel.Tcp
         }
 
         /// <inheritdoc />
-        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.
         /// </summary>
         /// <param name="bytes">The 4 header bytes read.</param>
-        void HeaderReadCallback(byte[] bytes)
+        /// <param name="callback">The callback to invoke when the body has been received.</param>
+        void HeaderReadCallback(byte[] bytes, Action<byte[]> 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));
+            }
+        }
+
+        /// <summary>
+        ///     Starts waiting for a first handshake packet to be received.
+        /// </summary>
+        /// <param name="callback">The callback to invoke when the handshake has been received.</param>
+        internal void StartWaitingForHandshake(Action<byte[]> 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
         /// <summary>
         ///     Starts this connections waiting for the header.
         /// </summary>
-        void StartWaitingForHeader()
+        /// <param name="callback">The callback to invoke when the body has been read.</param>
+        void StartWaitingForHeader(Action<byte[]> callback)
         {
-            StartWaitingForBytes(4, HeaderReadCallback);
+            StartWaitingForBytes(4, (bytes) => HeaderReadCallback(bytes, callback));
         }
 
         /// <summary>
index 2419b1be25e2e5ed501d45c7ae18b8f851da0325..8374d20e303aa1700e83e3347e1311174689edfc 100644 (file)
@@ -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();
+                    }
+                );
             }
         }
 
index f8836fbaa0e950d3ecbf606b64d0d4c4838e4bbc..04ff3a0192539ddfab5b1c29f2a75e8a15c6af81 100644 (file)
@@ -88,7 +88,7 @@ namespace Hazel.Udp
         }
 
         /// <inheritdoc />
-        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();
index d789a48800faeb93ed2683cb16adeb5eefe8e357..f1d6f3c68d88f0da0957769cbd4a07e2d9ce9ce1 100644 (file)
@@ -66,7 +66,7 @@ namespace Hazel.Udp
                     (o) =>
                     {
                         Trace.WriteLine("Keepalive packet sent.");
-                        SendHello(null);
+                        SendHello(null, null);
                     },
                     null,
                     keepAliveInterval,
index 98afd27c02a6f4d3f5a5c14409c29641312ea9e1..ed21ccd20e6ae7a8f106305c97334b826632c4bd 100644 (file)
@@ -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.
         /// </summary>
         /// <param name="acknowledgeCallback">The callback to invoke when the hello packet is acknowledged.</param>
-        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);
         }
 
         /// <summary>
index 066975dd792c320382ac00a84cdb36b42775a3dc..f999202f784e0790a4e7fd5393d11b7e13cd0752 100644 (file)
@@ -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);
+            }
         }
 
         /// <summary>
index 4d3419aaaa7da8e1b771b51f7c55c43bcab4c086..98a204776e0c3e1378126e4db661c3ef109be661 100644 (file)
@@ -60,7 +60,7 @@ namespace Hazel.Udp
         /// <remarks>
         ///     This will always throw a HazelException.
         /// </remarks>
-        public override void Connect()
+        public override void Connect(byte[] bytes)
         {
             throw new HazelException("Cannot manually connect a UdpServerConnection, did you mean to use UdpClientConnection?");
         }