Prechádzať zdrojové kódy

comit: ui local poc success, next: public network test, and cleaning codes

Local 5 rokov pred
rodič
commit
ba03365a01

+ 16 - 9
UdPunching.Common/ExchangeMessage.cs

@@ -7,7 +7,7 @@ namespace UdPunching
 {
     public class ExchangeMessage
     {
-        // [0....] Flags
+        // [0....] Id
         // [1....] Section count
         // [2....] Section Type
         // [3...N] Section Content
@@ -29,6 +29,13 @@ namespace UdPunching
         {
         }
 
+        public ExchangeMessage(ExchangeMessageId id)
+        {
+            Id = id;
+            TimeStamp = DateTime.Now;
+        }
+
+
         public ExchangeMessage(byte[] data)
         {
             var ptr = -1;
@@ -56,7 +63,7 @@ namespace UdPunching
                         break;
 
                     case ExchangeMessageSection.PayloadData:
-                        var len = data[++ptr];
+                        ptr += data.ReadLeInt16(out var len, ++ptr);
                         var payload = new byte[len];
                         Buffer.BlockCopy(data, ++ptr, payload, 0, len);
                         ptr += len;
@@ -76,14 +83,14 @@ namespace UdPunching
 
             if (TimeStamp.HasValue)
             {
-                dic[ExchangeMessageSection.TimeStamp] = TimeStamp.Value.ToUnixTimeStamp().ToLeBytes();
+                dic[ExchangeMessageSection.TimeStamp] = TimeStamp.Value.ToUnixTimeStamp().ToLeInt32Bytes();
             }
 
             if (null != PeerEndPoint)
             {
                 var buf = new byte[17];
-                PeerEndPoint.Address.ToPaddingString().ToAsciiBytes(buf);
-                PeerEndPoint.Port.ToLe16Bytes(buf, 15);
+                PeerEndPoint.Address.ToPaddingString().WriteAsciiBytesTo(buf);
+                PeerEndPoint.Port.WriteLeInt16To(buf, 15);
                 dic[ExchangeMessageSection.PeerEndPoint] = buf;
             }
 
@@ -94,10 +101,10 @@ namespace UdPunching
 
             if (null != PayloadBytes)
             {
-                var buf = new byte[PayloadBytes.Length + 1];
-                buf[0] = (byte)PayloadBytes.Length;
-                PayloadBytes.CopyTo(buf, 1);
-                dic[ExchangeMessageSection.PeerId] = buf;
+                var buf = new byte[PayloadBytes.Length + 2];
+                PayloadBytes.Length.WriteLeInt16To(buf);
+                PayloadBytes.CopyTo(buf, 2);
+                dic[ExchangeMessageSection.PayloadData] = buf;
             }
 
             //TODO: add more sections from properties

+ 9 - 5
UdPunching.Common/ExchangeMessageId.cs

@@ -9,12 +9,15 @@
         KeepAliveAckNoChg = 12,
 
         PeerKnockReq = 60,
-        PeerKnockReqErrPeerNoReady = 61,
+        PeerKnockReqErrPeerNoAvailable = 61,
         PeerKnockReqRelay = 62,
-        PeerKnockReqAck = 63,
-        PeerKnockReqAckRelay = 64,
-        PeerKnockReqDenied = 65,
-        PeerKnockReqDeniedRelay = 66,
+        PeerKnockReqRelayed = 63,
+        PeerKnockAck = 64,
+        PeerKnockAckRelay = 65,
+        PeerKnockAckRelayed = 66,
+        PeerKnockDenied = 67,
+        PeerKnockDeniedRelay = 68,
+        PeerKnockDeniedRelayed = 69,
 
         PeerKnockConnectionReq = 100,
         PeerKnockConnectionAck = 110,
@@ -23,5 +26,6 @@
         ErrTimeStamp = 210,
 
         DataTransfer = 233,
+        DataTransferAck = 234,
     }
 }

+ 65 - 15
UdPunching.Common/TransferCodec.cs

@@ -1,5 +1,6 @@
 using System;
-using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
 using System.Linq;
 using System.Security.Cryptography;
 
@@ -7,32 +8,81 @@ namespace UdPunching
 {
     public static class TransferCodec
     {
-        // [0...15] GUID PeerId
-        // [16..19] Encrypted data length prefix (LeInt32)
-        // [20..END] Encrypted data content
+        // [0.....15] GUID PeerId
+        // [16....17] Encrypted data length prefix (LeInt16)
+        // [18...END] Encrypted data content
 
         public static Guid ReadId(byte[] recvData)
         {
             return new Guid(recvData.Take(16).ToArray());
         }
 
-        public static byte[] DecodeData(RSACryptoServiceProvider cryptoProvider, byte[] recvData)
+        public static byte[] DecodeData(RSACryptoServiceProvider localKey, byte[] recvData)
         {
-            var len = recvData.ToLeInt32(16);
-            return cryptoProvider.Decrypt(recvData.Skip(20).Take(len).ToArray(), false);
+            recvData.ReadLeInt16(out var len, 16);
+            var raw = recvData.Skip(18).Take(len).ToArray();
+            var decompressed = Decompress(raw);
+            var decodeData = localKey.Decrypt(decompressed, false);
+            return decodeData;
         }
 
-        public static byte[] Encode(RSACryptoServiceProvider cryptoProvider, Guid id, byte[] data)
+        public static byte[] Encode(RSACryptoServiceProvider receiverKey, Guid id, byte[] data)
         {
-            var idBytes = id.ToByteArray(); // 16
-            var encryptedBytes = cryptoProvider.Encrypt(data, false);
+            var encryptedBytes = receiverKey.Encrypt(data, false);
+            var compressedData = Compress(encryptedBytes);
 
-            var lst = new List<byte>(idBytes.Length + encryptedBytes.Length);
-            lst.AddRange(idBytes);
-            lst.AddRange(encryptedBytes.Length.ToLeBytes()); //4
-            lst.AddRange(encryptedBytes);
+            var buf = new byte[16 + 2 + compressedData.Length];
 
-            return lst.ToArray();
+            var ptr = -1;
+            ptr += id.WriteTo(buf, ++ptr); // 16
+            ptr += compressedData.Length.WriteLeInt16To(buf, ++ptr); // 2
+            compressedData.WriteTo(buf, ++ptr);
+
+            return buf;
+        }
+
+        private static byte[] Compress(byte[] data)
+        {
+            using (var rawIn = new MemoryStream(data))
+            using (var gzOut = new MemoryStream())
+            using (var gZipStream = new GZipStream(gzOut, CompressionLevel.Optimal))
+            using (var finalOut = new MemoryStream())
+            {
+                rawIn.CopyTo(gZipStream);
+                gZipStream.Close();
+                var compressed = gzOut.ToArray();
+                if (compressed.Length >= data.Length)
+                {
+                    finalOut.WriteByte(0);
+                    finalOut.Write(data, 0, data.Length);
+                }
+                else
+                {
+                    finalOut.WriteByte(1);
+                    finalOut.Write(compressed, 0, compressed.Length);
+                }
+                return finalOut.ToArray();
+            }
+        }
+
+        private static byte[] Decompress(byte[] data)
+        {
+            using (var rawIn = new MemoryStream(data))
+            using (var finalOut = new MemoryStream())
+            {
+                if (rawIn.ReadByte() == 1)
+                {
+                    using (var gZipStream = new GZipStream(rawIn, CompressionMode.Decompress))
+                    {
+                        gZipStream.CopyTo(finalOut);
+                    }
+                }
+                else
+                {
+                    rawIn.CopyTo(finalOut);
+                }
+                return finalOut.ToArray();
+            }
         }
     }
 }

+ 1 - 1
UdPunching.Common/UdPunching.Common.csproj

@@ -46,7 +46,7 @@
     <Compile Include="ExchangeMessage.cs" />
     <Compile Include="ExchangeMessageSection.cs" />
     <Compile Include="TransferCodec.cs" />
-    <Compile Include="ExtensionMethods.cs" />
+    <Compile Include="UtilMethods.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

+ 65 - 29
UdPunching.Common/ExtensionMethods.cs

@@ -6,7 +6,7 @@ using System.Text;
 
 namespace UdPunching
 {
-    public static class ExtensionMethods
+    public static class UtilMethods
     {
         private static readonly DateTime UnixTimeStampOrig = new DateTime(1970, 1, 1);
 
@@ -24,15 +24,16 @@ namespace UdPunching
 
         public static int ReadUnixTimeStamp(this byte[] buf, int index, out DateTime timeStamp)
         {
-            var i32 = buf.ToLeInt32(index);
+            var i32 = buf.GetLeInt32(index);
             timeStamp = UnixTimeStampOrig.AddSeconds(i32).ToLocalTime();
             return 4;
         }
 
         public static int ReadIpEndPoint(this byte[] buf, int index, out IPEndPoint ipEndPoint)
         {
-            var strAddress = buf.GetAsciiString(15, index);
-            var port = buf.GetLeInt16(index + 15);
+            index += buf.ReadAsciiString(15, out var strAddress, index);
+            buf.ReadLeInt16(out var port, index);
+
             ipEndPoint = new IPEndPoint(IPAddress.Parse(strAddress.Trim()), port);
             return 17;
         }
@@ -45,20 +46,9 @@ namespace UdPunching
             throw new FormatException("parse fail");
         }
 
-        //--------- underlying -------------
-
-        public static byte[] ToLeBytes(this int value)
-        {
-            return new[]
-            {
-                (byte)(value & 0xFF),
-                (byte)((value>>8 ) & 0xFF),
-                (byte)((value>>16) & 0xFF),
-                (byte)((value>>24) & 0xFF),
-            };
-        }
+        //========= underlying =============
 
-        public static int ToLeInt32(this byte[] buf, int index)
+        public static int GetLeInt32(this byte[] buf, int index)
         {
             return buf[index]
                 | (buf[index + 1] << 8)
@@ -67,7 +57,15 @@ namespace UdPunching
             ;
         }
 
-        public static byte[] ToLe16Bytes(this int value)
+        public static string ToPaddingString(this IPAddress ipAddress)
+        {
+            if (ipAddress.AddressFamily != AddressFamily.InterNetwork) throw new NotSupportedException("Only ipv4");
+            return ipAddress.ToString().PadRight(15, ' ');
+        }
+
+        //--------- ToBytes Series -------------
+
+        public static byte[] ToLeInt16Bytes(this int value)
         {
             return new[]
             {
@@ -76,31 +74,69 @@ namespace UdPunching
             };
         }
 
-        public static void ToLe16Bytes(this int value, byte[] buf, int index = 0)
+        public static byte[] ToLeInt32Bytes(this int value)
         {
-            buf[index] = (byte)(value & 0xFF);
-            buf[index + 1] = (byte)((value >> 8) & 0xFF);
+            return new[]
+            {
+                (byte)(value & 0xFF),
+                (byte)((value>>8 ) & 0xFF),
+                (byte)((value>>16 ) & 0xFF),
+                (byte)((value>>24 ) & 0xFF),
+            };
         }
 
-        public static int GetLeInt16(this byte[] buf, int index)
+        public static byte[] ToAsciiBytes(this string str) => Encoding.ASCII.GetBytes(str);
+
+        //--------- Read Series -------------
+
+        public static int ReadLeInt16(this byte[] buf, out int value, int index = 0)
+        {
+            value = buf[index]
+                 | buf[index + 1] << 8
+            ;
+
+            return 2;
+        }
+
+        public static int ReadLeInt16(this byte[] buf, int index = 0)
         {
             return buf[index]
-                   | buf[index + 1] << 8
+                    | buf[index + 1] << 8
                 ;
         }
 
-        public static string ToPaddingString(this IPAddress ipAddress)
+        public static int ReadAsciiString(this byte[] buf, int count, out string ascii, int index = 0)
         {
-            if (ipAddress.AddressFamily != AddressFamily.InterNetwork) throw new NotSupportedException("Only ipv4");
-            return ipAddress.ToString().PadRight(15, ' ');
+            ascii = Encoding.ASCII.GetString(buf.Skip(index).Take(count).ToArray());
+            return count;
         }
 
-        public static byte[] ToAsciiBytes(this string str) => Encoding.ASCII.GetBytes(str);
+        //--------- Write...To Series -------------
 
-        public static void ToAsciiBytes(this string str, byte[] buf, int index = 0) => Encoding.ASCII.GetBytes(str, 0, str.Length, buf, index);
+        public static int WriteLeInt16To(this int value, byte[] buf, int index = 0)
+        {
+            buf[index] = (byte)(value & 0xFF);
+            buf[index + 1] = (byte)((value >> 8) & 0xFF);
+            return 2;
+        }
 
-        public static string GetAsciiString(this byte[] buf, int count, int index = 0) => Encoding.ASCII.GetString(buf.Skip(index).Take(count).ToArray());
+        public static int WriteTo(this Guid value, byte[] buf, int index = 0)
+        {
+            var byteArray = value.ToByteArray();
+            byteArray.CopyTo(buf, index);
+            return byteArray.Length;
+        }
 
+        public static int WriteTo(this byte[] bytes, byte[] buf, int index = 0)
+        {
+            bytes.CopyTo(buf, index);
+            return bytes.Length;
+        }
 
+        public static int WriteAsciiBytesTo(this string str, byte[] buf, int index = 0)
+        {
+            Encoding.ASCII.GetBytes(str, 0, str.Length, buf, index);
+            return Encoding.ASCII.GetByteCount(str);
+        }
     }
 }

+ 10 - 10
UdPunching.ExampleW/ExampleForm.Designer.cs

@@ -38,7 +38,7 @@
             this.SendContentTextBox = new System.Windows.Forms.TextBox();
             this.RecvTextBox = new System.Windows.Forms.TextBox();
             this.PeerKetyDropDown = new System.Windows.Forms.ComboBox();
-            this.PeerToKonckDropDown = new System.Windows.Forms.ComboBox();
+            this.PeerToKnockDropDown = new System.Windows.Forms.ComboBox();
             this.KnockButton = new System.Windows.Forms.Button();
             this.SendToEndPointTextBox = new System.Windows.Forms.TextBox();
             this.SuspendLayout();
@@ -128,14 +128,14 @@
             this.PeerKetyDropDown.Size = new System.Drawing.Size(248, 20);
             this.PeerKetyDropDown.TabIndex = 5;
             // 
-            // PeerToKonckDropDown
+            // PeerToKnockDropDown
             // 
-            this.PeerToKonckDropDown.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
-            this.PeerToKonckDropDown.FormattingEnabled = true;
-            this.PeerToKonckDropDown.Location = new System.Drawing.Point(12, 70);
-            this.PeerToKonckDropDown.Name = "PeerToKonckDropDown";
-            this.PeerToKonckDropDown.Size = new System.Drawing.Size(248, 20);
-            this.PeerToKonckDropDown.TabIndex = 5;
+            this.PeerToKnockDropDown.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.PeerToKnockDropDown.FormattingEnabled = true;
+            this.PeerToKnockDropDown.Location = new System.Drawing.Point(12, 70);
+            this.PeerToKnockDropDown.Name = "PeerToKnockDropDown";
+            this.PeerToKnockDropDown.Size = new System.Drawing.Size(248, 20);
+            this.PeerToKnockDropDown.TabIndex = 5;
             // 
             // KnockButton
             // 
@@ -162,7 +162,7 @@
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
             this.ClientSize = new System.Drawing.Size(779, 331);
-            this.Controls.Add(this.PeerToKonckDropDown);
+            this.Controls.Add(this.PeerToKnockDropDown);
             this.Controls.Add(this.PeerKetyDropDown);
             this.Controls.Add(this.RecvTextBox);
             this.Controls.Add(this.SendContentTextBox);
@@ -192,7 +192,7 @@
         private System.Windows.Forms.TextBox SendContentTextBox;
         private System.Windows.Forms.TextBox RecvTextBox;
         private System.Windows.Forms.ComboBox PeerKetyDropDown;
-        private System.Windows.Forms.ComboBox PeerToKonckDropDown;
+        private System.Windows.Forms.ComboBox PeerToKnockDropDown;
         private System.Windows.Forms.Button KnockButton;
         private System.Windows.Forms.TextBox SendToEndPointTextBox;
     }

+ 167 - 69
UdPunching.ExampleW/ExampleForm.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Net;
@@ -15,16 +16,17 @@ namespace UdPunching.ExampleW
 
         private static readonly IPEndPoint AnyEndPoint = new IPEndPoint(IPAddress.Any, 0);
 
+        private readonly IReadOnlyDictionary<Guid, RSACryptoServiceProvider> _peerKeyRegister;
+
         private IPEndPoint _serverEndPoint;
-        private RSACryptoServiceProvider _serverRsa;
-        private SocketAsyncEventArgs _saeRecv;
+        private RSACryptoServiceProvider _serverKey;
+        private SocketAsyncEventArgs _saeReceive;
 
         private Guid _localId;
-        private RSACryptoServiceProvider _localRsa;
+        private RSACryptoServiceProvider _localKey;
 
         private Socket _localSocket;
-        private IPEndPoint _publicEndPoint;
-        private IPEndPoint _peerEndPoint;
+        private IPEndPoint _localPublicEndPoint;
 
         private readonly byte[] _keepAliveBuf = new byte[7];// 1flag,1count,1section,4timestamp
         private readonly ExchangeMessage _keepAliveMsg = new ExchangeMessage { Id = ExchangeMessageId.KeepAliveReq };
@@ -34,15 +36,25 @@ namespace UdPunching.ExampleW
         public ExampleForm()
         {
             InitializeComponent();
-            //------------- ui event -------------
+
+            _peerKeyRegister = Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PeerPublicKey"))
+                .ToDictionary(
+                    s => new Guid(Path.GetFileNameWithoutExtension(s)),
+                    p =>
+                    {
+                        var rsa = new RSACryptoServiceProvider();
+                        rsa.FromXmlString(File.ReadAllText(p));
+                        return rsa;
+                    }
+                );
         }
 
         //------------- ui event -------------
 
         private void ExampleForm_Shown(object sender, EventArgs e)
         {
-            _serverRsa = new RSACryptoServiceProvider();
-            _serverRsa.FromXmlString(File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ServerPublicKey.txt")));
+            _serverKey = new RSACryptoServiceProvider();
+            _serverKey.FromXmlString(File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ServerPublicKey.txt")));
 
             var privateKeys = Directory
                 .GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PrivateKey"))
@@ -51,12 +63,7 @@ namespace UdPunching.ExampleW
 
             PeerKetyDropDown.DataSource = privateKeys;
 
-            var peerPubKeys = Directory
-                .GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PeerPublicKey"))
-                .Select(Path.GetFileNameWithoutExtension)
-                .ToArray();
-
-            PeerToKonckDropDown.DataSource = peerPubKeys;
+            PeerToKnockDropDown.DataSource = _peerKeyRegister.Keys.ToArray();
         }
 
         private void StartButton_Click(object sender, EventArgs e)
@@ -66,9 +73,9 @@ namespace UdPunching.ExampleW
             _serverEndPoint = ServerIEndPointTextBox.Text.ParseToIpEndPointV4();
 
             _localId = new Guid(PeerKetyDropDown.Text);
-            _localRsa = new RSACryptoServiceProvider();
+            _localKey = new RSACryptoServiceProvider();
             var peerPrivateKeyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PrivateKey", PeerKetyDropDown.Text + ".txt");
-            _localRsa.FromXmlString(
+            _localKey.FromXmlString(
                 File.ReadAllText(
                     peerPrivateKeyPath
                 )
@@ -77,9 +84,9 @@ namespace UdPunching.ExampleW
             _localSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
             _localSocket.Bind(AnyEndPoint);
 
-            _saeRecv = new SocketAsyncEventArgs();
-            _saeRecv.SetBuffer(new byte[ReceiveBufferSize], 0, ReceiveBufferSize);
-            _saeRecv.Completed += RecvCompleted;
+            _saeReceive = new SocketAsyncEventArgs();
+            _saeReceive.SetBuffer(new byte[ReceiveBufferSize], 0, ReceiveBufferSize);
+            _saeReceive.Completed += ReceiveCompleted;
             BeginRecv();
 
             KeepAliveTimer.Start();
@@ -100,12 +107,12 @@ namespace UdPunching.ExampleW
             KeepAliveTimer.Stop();
 
             _localSocket?.Dispose();
-            _saeRecv?.Dispose();
-            _localRsa?.Dispose();
+            _saeReceive?.Dispose();
+            _localKey?.Dispose();
 
             _localSocket = null;
-            _saeRecv = null;
-            _localRsa = null;
+            _saeReceive = null;
+            _localKey = null;
 
             PeerKetyDropDown.Enabled = true;
             StartButton.Enabled = true;
@@ -120,88 +127,179 @@ namespace UdPunching.ExampleW
         {
             _keepAliveMsg.TimeStamp = DateTime.Now;
             _keepAliveMsg.WriteToBuffer(_keepAliveBuf);
-            var encode = TransferCodec.Encode(_serverRsa, _localId, _keepAliveBuf);
+            var encode = TransferCodec.Encode(_serverKey, _localId, _keepAliveBuf);
             _localSocket.SendTo(encode, _serverEndPoint);
         }
 
         private void KnockButton_Click(object sender, EventArgs e)
         {
-            //TODO:
-            SendButton.Enabled = true;
+            var idToKnock = new Guid(PeerToKnockDropDown.Text);
+            if (idToKnock == _localId)
+            {
+                Log("KNOCK YOUR SELF ???");
+                return;
+            }
+            var msg = new ExchangeMessage(ExchangeMessageId.PeerKnockReq)
+            {
+                PeerId = idToKnock
+            };
+            var encode = TransferCodec.Encode(_serverKey, _localId, msg.ToBytes());
+            _localSocket.SendTo(encode, _serverEndPoint);
         }
 
         private void SendButton_Click(object sender, EventArgs e)
         {
-            //TODO: use datatransfer message
             var to = SendToEndPointTextBox.Text.ParseToIpEndPointV4();
-            var sent = _localSocket.SendTo(Encoding.UTF8.GetBytes(SendContentTextBox.Text), to);
+            var sendMsg = new ExchangeMessage(ExchangeMessageId.DataTransfer);
+            sendMsg.PayloadBytes = Encoding.UTF8.GetBytes(SendContentTextBox.Text);
+            var sendBytes = TransferCodec.Encode(_peerKeyRegister[new Guid(PeerToKnockDropDown.Text)], _localId, sendMsg.ToBytes());
+            var sent = _localSocket.SendTo(sendBytes, to);
         }
 
         //------------- logic -------------
 
         private void ProcessPacket()
         {
-            var peerId = TransferCodec.ReadId(_saeRecv.Buffer);
-            if (_saeRecv.RemoteEndPoint.IpEndPointEqualsTo(_serverEndPoint))
+            var peerId = TransferCodec.ReadId(_saeReceive.Buffer);
+            if (_saeReceive.RemoteEndPoint.IpEndPointEqualsTo(_serverEndPoint))
             {
-                if (BuildInPeerId.Invalid == peerId)
+                if (BuildInPeerId.Invalid == peerId) throw new InvalidDataException("SERVER ERROR: FAILURE");
+                if (Guid.Empty != peerId) throw new InvalidDataException("SERVER ERROR: INVALID SERVER PEER ID");
+
+                var msgData = TransferCodec.DecodeData(_localKey, _saeReceive.Buffer);
+                var msg = new ExchangeMessage(msgData);
+
+                switch (msg.Id)
                 {
-                    Log("ERROR SERVER FAILURE");
+                    case ExchangeMessageId.KeepAliveAckSessionCreated:
+                        _localPublicEndPoint = msg.PeerEndPoint;
+                        Log($"Session Created, public endpoint {_localPublicEndPoint}");
+                        Invoke(new Action(() =>
+                        {
+                            PublicEndPointTextBox.Text = msg.PeerEndPoint.ToString();
+                        }));
+                        break;
+
+                    case ExchangeMessageId.KeepAliveAckNoChg:
+                        break;
+
+                    case ExchangeMessageId.PeerKnockReqRelay:
+                        if (false == msg.PeerId.HasValue)
+                        {
+                            Log($"INVALID {msg.Id} was IGNORED: peer id is required");
+                            break;
+                        }
+
+                        ExchangeMessage msgReply;
+
+                        if (false == _peerKeyRegister.TryGetValue(msg.PeerId.Value, out var peerKey))
+                        {
+                            Log($"DENIED {msg.Id}: peer id {msg.PeerId.Value}");
+                            msgReply = new ExchangeMessage(ExchangeMessageId.PeerKnockDenied);
+                        }
+                        else
+                        {
+                            Log($"ACCEPT {msg.Id}: peer id {msg.PeerId.Value} @ {msg.PeerEndPoint}");
+                            msgReply = new ExchangeMessage(ExchangeMessageId.PeerKnockAck) { PeerId = msg.PeerId };
+
+                            Log($"SENDING CONNECTION REQ to {msg.PeerId} @ {msg.PeerEndPoint}");
+                            var connMsg = new ExchangeMessage(ExchangeMessageId.PeerKnockConnectionReq);
+                            var connMsgBytes = TransferCodec.Encode(peerKey, _localId, connMsg.ToBytes());
+                            _localSocket.SendTo(connMsgBytes, msg.PeerEndPoint);
+                        }
+
+                        var bytes = TransferCodec.Encode(_serverKey, _localId, msgReply.ToBytes());
+                        _localSocket.SendTo(bytes, _serverEndPoint);
+
+                        break;
+
+                    case ExchangeMessageId.PeerKnockAckRelay:
+                        Log($"KNOCK SUCCESS by {msg.PeerId} peer endpont is {msg.PeerEndPoint}");
+                        Invoke(new Action(() =>
+                        {
+                            SendToEndPointTextBox.Text = msg.PeerEndPoint.ToString();
+                            SendButton.Enabled = true;
+                        }));
+                        break;
+
+                    case ExchangeMessageId.PeerKnockAckRelayed:
+                        Log($"ACCEPT SENT {msg.PeerId}");
+                        break;
+
+                    case ExchangeMessageId.PeerKnockReqErrPeerNoAvailable:
+                        Log($"KNOCK FAIL:{msg.Id} {msg.PeerId}");
+                        break;
+
+                    case ExchangeMessageId.PeerKnockReqRelayed:
+                        Log($"KNOCK SENT {msg.PeerId}");
+                        break;
+
+                    case ExchangeMessageId.PeerKnockDeniedRelay:
+                        Log($"KNOCK DENIED by {msg.PeerId}");
+                        break;
+
+                    default:
+                        throw new ArgumentOutOfRangeException("msg.Id", "SERVER ERROR: NO EXCEPTED MESSAGE FROM SERVER," + msg.Id);
                 }
-                else if (Guid.Empty == peerId)
+            }
+            else
+            {
+                if (BuildInPeerId.Invalid == peerId || BuildInPeerId.Server == peerId || false == _peerKeyRegister.TryGetValue(peerId, out var peerKey))
                 {
-                    var msgData = TransferCodec.DecodeData(_localRsa, _saeRecv.Buffer);
+                    throw new InvalidDataException("PEER ERROR: INVALID PEER ID");
+                }
+
+                var msgData = TransferCodec.DecodeData(_localKey, _saeReceive.Buffer);
+                var msg = new ExchangeMessage(msgData);
 
-                    var msg = new ExchangeMessage(msgData);
+                var reply = new ExchangeMessage { TimeStamp = DateTime.Now };
 
+                if (false == msg.TimeStamp.HasValue || Math.Abs((DateTime.Now - msg.TimeStamp.Value).TotalSeconds) > 10)
+                {
+                    Log($"TIMESTAMP ERROR from peer {peerId}");
+                    reply.Id = ExchangeMessageId.ErrTimeStamp;
+                }
+                else
+                {
                     switch (msg.Id)
                     {
-                        case ExchangeMessageId.KeepAliveAckSessionCreated:
-                            _publicEndPoint = msg.PeerEndPoint;
-                            Log($"Session Created, public endpoint {_publicEndPoint}");
-                            Invoke(new Action(() =>
-                            {
-                                PublicEndPointTextBox.Text = msg.PeerEndPoint.ToString();
-                            }));
-                            break;
-
-                        case ExchangeMessageId.KeepAliveAckNoChg:
-                            break;
-
-                        case ExchangeMessageId.PeerKnockReqAckRelay:
+                        case ExchangeMessageId.PeerKnockConnectionReq:
+                            reply.Id = ExchangeMessageId.PeerKnockConnectionAck;
                             break;
 
-                        case ExchangeMessageId.PeerKnockReqDeniedRelay:
+                        case ExchangeMessageId.DataTransfer:
+                            reply.Id = ExchangeMessageId.DataTransferAck;
+                            var payloadString = Encoding.UTF8.GetString(msg.PayloadBytes);
+                            Log($"DATA FROM {peerId}:{payloadString}");
+                            reply.PayloadBytes = payloadString.Length.ToLeInt16Bytes();
                             break;
+                        case ExchangeMessageId.DataTransferAck:
+                            Log($"DATA ACK LEN:{msg.PayloadBytes.ReadLeInt16()} FROM {peerId}");
 
+                            return;
                         default:
-                            throw new ArgumentOutOfRangeException("msg.Id", "no excepted message from server:" + msg.Id);
+                            Log($"RECV {msg.Id} FROM {peerId} @ {_saeReceive.RemoteEndPoint}");
+                            return;
                     }
                 }
-            }
-            else
-            {
-                if (BuildInPeerId.Invalid == peerId || BuildInPeerId.Server == peerId)
-                {
-                    return; //Ignore invalid message
-                }
 
-                Invoke(new Action(() =>
-                {
-                    //TODO: decode message by peer public key
-                    //Log($"RECV FROM {_saeRecv.RemoteEndPoint},{Encoding.UTF8.GetString(_saeRecv.Buffer, 0, _saeRecv.BytesTransferred)}");
-                }));
+                var bytes = TransferCodec.Encode(peerKey, _localId, reply.ToBytes());
+                _localSocket.SendTo(bytes, _saeReceive.RemoteEndPoint);
             }
         }
 
-        private void RecvCompleted(object sender, SocketAsyncEventArgs e)
+        private void ReceiveCompleted(object sender, SocketAsyncEventArgs e)
         {
-            if (e.SocketError == SocketError.Success)
+            if (_saeReceive.SocketError == SocketError.Success)
             {
                 try
                 {
                     ProcessPacket();
                 }
+                catch (InvalidDataException exception)
+                {
+                    Log($"ERROR ProcessPacket:{exception.Message}");
+                }
                 catch (Exception exception)
                 {
                     Log($"ERROR ProcessPacket:{exception}");
@@ -209,19 +307,19 @@ namespace UdPunching.ExampleW
             }
             else
             {
-                Log($"ERROR SOCKET:{e.SocketError}");
+                Log($"ERROR SOCKET:{_saeReceive.SocketError}");
             }
 
             if (null == _localSocket) return;
-            if (false == _localSocket.ReceiveFromAsync(_saeRecv)) RecvCompleted(null, null);
+            if (false == _localSocket.ReceiveFromAsync(_saeReceive)) ReceiveCompleted(null, null);
         }
 
         private void BeginRecv()
         {
-            _saeRecv.RemoteEndPoint = AnyEndPoint;
-            if (false == _localSocket.ReceiveFromAsync(_saeRecv))
+            _saeReceive.RemoteEndPoint = AnyEndPoint;
+            if (false == _localSocket.ReceiveFromAsync(_saeReceive))
             {
-                RecvCompleted(null, _saeRecv);
+                ReceiveCompleted(null, _saeReceive);
             }
         }
 

UdPunching.ExampleW/PeerPublicKey/ab9ae069-1bed-4fce-8e6a-feabb8c519a6.txt → UdPunching.ExampleW/PeerPublicKey/ab9ae069-1bed-4fce-8e6a-feabb8c519a7.txt


UdPunching.ExampleW/PrivateKey/ab9ae069-1bed-4fce-8e6a-feabb8c519a6.txt → UdPunching.ExampleW/PrivateKey/ab9ae069-1bed-4fce-8e6a-feabb8c519a7.txt


+ 2 - 2
UdPunching.ExampleW/UdPunching.ExampleW.csproj

@@ -92,13 +92,13 @@
       <Link>ServerPublicKey.txt</Link>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="PrivateKey\ab9ae069-1bed-4fce-8e6a-feabb8c519a6.txt">
+    <Content Include="PrivateKey\ab9ae069-1bed-4fce-8e6a-feabb8c519a7.txt">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="PrivateKey\26531b6b-1fa1-4c70-8a1e-7c1b579742a5.txt">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="PeerPublicKey\ab9ae069-1bed-4fce-8e6a-feabb8c519a6.txt">
+    <Content Include="PeerPublicKey\ab9ae069-1bed-4fce-8e6a-feabb8c519a7.txt">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="PeerPublicKey\26531b6b-1fa1-4c70-8a1e-7c1b579742a5.txt">

+ 68 - 17
UdPunching.Serv/ServProgram.cs

@@ -15,8 +15,8 @@ namespace UdPunching.Serv
     {
         private static readonly ConcurrentDictionary<Guid, OnlineSession> OnlineSessions = new ConcurrentDictionary<Guid, OnlineSession>();
 
-        private static RSACryptoServiceProvider _serverRsaCryptoServiceProvider;
-        private static IReadOnlyDictionary<Guid, RSACryptoServiceProvider> _peerRsaDict;
+        private static RSACryptoServiceProvider _serverKey;
+        private static IReadOnlyDictionary<Guid, RSACryptoServiceProvider> _peerKeyRegister;
 
         private static volatile int _packetSeq;
         private static Socket _svrSocket;
@@ -30,9 +30,9 @@ namespace UdPunching.Serv
             ++_packetSeq;
 
             var peerId = TransferCodec.ReadId(sae.Buffer);
-            _peerRsaDict.TryGetValue(peerId, out var peerRsa);
+            _peerKeyRegister.TryGetValue(peerId, out var peerKey);
 
-            if (null == peerRsa)
+            if (null == peerKey)
             {
                 Console.WriteLine($"pacet #{_packetSeq} peer id {peerId} no registered");
                 _svrSocket.SendTo(BuildInPeerId.Invalid.ToByteArray(), sae.RemoteEndPoint);
@@ -43,7 +43,7 @@ namespace UdPunching.Serv
 
             try
             {
-                var msgData = TransferCodec.DecodeData(_serverRsaCryptoServiceProvider, sae.Buffer);
+                var msgData = TransferCodec.DecodeData(_serverKey, sae.Buffer);
                 requestMessage = new ExchangeMessage(msgData);
             }
             catch (Exception exception)
@@ -53,7 +53,7 @@ namespace UdPunching.Serv
                 return;
             }
 
-            var responseMessage = new ExchangeMessage();
+            var responseMessage = new ExchangeMessage { TimeStamp = DateTime.Now };
 
             if (false == requestMessage.TimeStamp.HasValue || Math.Abs((DateTime.Now - requestMessage.TimeStamp.Value).TotalSeconds) > 10)
             {
@@ -99,14 +99,66 @@ namespace UdPunching.Serv
                             }
                             break;
 
-                        //case ExchangeMessageId.PeerKnockReq:
-                        //    break;
+                        case ExchangeMessageId.PeerKnockReq:
 
-                        //case ExchangeMessageId.PeerKnockReqAck:
-                        //    break;
+                            if (false == requestMessage.PeerId.HasValue
+                                || false == _peerKeyRegister.TryGetValue(requestMessage.PeerId.Value, out var knockKey)
+                                || false == OnlineSessions.TryGetValue(requestMessage.PeerId.Value, out var knockSession)
+                            )
+                            {
+                                Console.WriteLine($"pacet #{_packetSeq} bad knock request: peer id no available {requestMessage.PeerId}");
+                                responseMessage.Id = ExchangeMessageId.PeerKnockReqErrPeerNoAvailable;
+                                responseMessage.PeerId = requestMessage.PeerId;
+                                break;
+                            }
 
-                        //case ExchangeMessageId.PeerKnockReqDenied:
-                        //    break;
+                            Console.WriteLine($"pacet #{_packetSeq} knock to {requestMessage.PeerId} by {peerId} @ {sae.RemoteEndPoint}");
+
+                            var reqRelayMessage = new ExchangeMessage(ExchangeMessageId.PeerKnockReqRelay)
+                            {
+                                PeerId = peerId,
+                                PeerEndPoint = (IPEndPoint)sae.RemoteEndPoint,
+                            };
+
+                            var reqRelayBytes = TransferCodec.Encode(knockKey, BuildInPeerId.Server, reqRelayMessage.ToBytes());
+                            _svrSocket.SendTo(reqRelayBytes, knockSession.RemoteEndPoint);
+
+                            responseMessage.Id = ExchangeMessageId.PeerKnockReqRelayed;
+                            responseMessage.PeerId = requestMessage.PeerId;
+                            break;
+
+                        case ExchangeMessageId.PeerKnockAck:
+
+                            if (false == requestMessage.PeerId.HasValue
+                                || false == _peerKeyRegister.TryGetValue(requestMessage.PeerId.Value, out var knockAckKey)
+                                || false == OnlineSessions.TryGetValue(requestMessage.PeerId.Value, out var knockAckSession)
+                            )
+                            {
+                                Console.WriteLine($"pacet #{_packetSeq} bad knock ack: peer id no available {requestMessage.PeerId}");
+                                responseMessage.Id = ExchangeMessageId.PeerKnockReqErrPeerNoAvailable;
+                                responseMessage.PeerId = requestMessage.PeerId;
+                                break;
+                            }
+
+                            Console.WriteLine($"pacet #{_packetSeq} knock ack {requestMessage.PeerId} by {peerId}");
+
+                            var ackRelayMessage = new ExchangeMessage(ExchangeMessageId.PeerKnockAckRelay)
+                            {
+                                PeerId = peerId,
+                                PeerEndPoint = (IPEndPoint)sae.RemoteEndPoint,
+                            };
+
+                            var ackRelayBytes = TransferCodec.Encode(knockAckKey, BuildInPeerId.Server, ackRelayMessage.ToBytes());
+                            _svrSocket.SendTo(ackRelayBytes, knockAckSession.RemoteEndPoint);
+
+                            responseMessage.Id = ExchangeMessageId.PeerKnockAckRelayed;
+                            responseMessage.PeerId = requestMessage.PeerId;
+
+                            break;
+
+                        case ExchangeMessageId.PeerKnockDenied:
+
+                            break;
 
                         //case ExchangeMessageId.PeerKnockConnectionReq:
                         //    break;
@@ -123,8 +175,7 @@ namespace UdPunching.Serv
                 }
             }
 
-            responseMessage.TimeStamp = DateTime.Now;
-            var replyBytes = TransferCodec.Encode(peerRsa, BuildInPeerId.Server, responseMessage.ToBytes());
+            var replyBytes = TransferCodec.Encode(peerKey, BuildInPeerId.Server, responseMessage.ToBytes());
             _svrSocket.SendTo(replyBytes, sae.RemoteEndPoint);
         }
 
@@ -169,10 +220,10 @@ namespace UdPunching.Serv
         {
             Console.WriteLine("Init...");
 
-            _serverRsaCryptoServiceProvider = new RSACryptoServiceProvider();
-            _serverRsaCryptoServiceProvider.FromXmlString(File.ReadAllText("ServerPrivateKey.txt"));
+            _serverKey = new RSACryptoServiceProvider();
+            _serverKey.FromXmlString(File.ReadAllText("ServerPrivateKey.txt"));
 
-            _peerRsaDict = Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PeerPublicKey"))
+            _peerKeyRegister = Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PeerPublicKey"))
                 .ToDictionary(
                     s => new Guid(Path.GetFileNameWithoutExtension(s)),
                     p =>