|
@@ -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);
|
|
|
}
|
|
|
}
|
|
|
|