123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Security.Cryptography;
- using System.Text;
- using System.Windows.Forms;
- namespace UdPunching.ExampleW
- {
- public partial class ExampleForm : Form
- {
- private const int ReceiveBufferSize = 1500;
- private static readonly IPEndPoint AnyEndPoint = new IPEndPoint(IPAddress.Any, 0);
- private readonly IReadOnlyDictionary<Guid, RSACng> _peerPublicKeyRegistry;
- private IPEndPoint _serverEndPoint;
- private RSACng _serverPublicKey;
- private SocketAsyncEventArgs _saeReceive;
- private Guid _localId;
- private RSACng _localPrivateKey;
- private Socket _localSocket;
- private IPEndPoint _localPublicEndPoint;
- private readonly byte[] _keepAliveBuf = new byte[7];// 1flag,1count,1section,4timestamp
- private readonly ExchangeMessage _keepAliveMsg = new ExchangeMessage { Id = ExchangeMessageId.KeepAliveReq };
- //------------- ctor -------------
- public ExampleForm()
- {
- InitializeComponent();
- _peerPublicKeyRegistry = Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PeerPublicKey"))
- .ToDictionary(
- s => new Guid(Path.GetFileNameWithoutExtension(s)),
- TransferCodec.LoadKey
- );
- }
- //------------- ui event -------------
- private void ExampleForm_Shown(object sender, EventArgs e)
- {
- _serverPublicKey = new RSACng();
- _serverPublicKey.FromXmlString(File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ServerPublicKey.txt")));
- var privateKeys = Directory
- .GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PrivateKey"))
- .Select(Path.GetFileNameWithoutExtension)
- .ToArray();
- PeerKetyDropDown.DataSource = privateKeys;
- PeerToKnockDropDown.DataSource = _peerPublicKeyRegistry.Keys.ToArray();
- }
- private void StartButton_Click(object sender, EventArgs e)
- {
- Log("Starting...");
- _serverEndPoint = ServerIEndPointTextBox.Text.ParseToIpEndPointV4();
- _localId = new Guid(PeerKetyDropDown.Text);
- var peerPrivateKeyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PrivateKey", PeerKetyDropDown.Text + ".txt");
- _localPrivateKey = TransferCodec.LoadKey(peerPrivateKeyPath);
- _localSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
- _localSocket.Bind(AnyEndPoint);
- _saeReceive = new SocketAsyncEventArgs();
- _saeReceive.SetBuffer(new byte[ReceiveBufferSize], 0, ReceiveBufferSize);
- _saeReceive.Completed += ReceiveCompleted;
- BeginRecv();
- KeepAliveTimer.Start();
- KeepAliveTimer_Tick(null, null);
- PeerKetyDropDown.Enabled = false;
- StartButton.Enabled = false;
- StopButton.Enabled = true;
- KnockButton.Enabled = true;
- Log("Started...");
- }
- private void StopButton_Click(object sender, EventArgs e)
- {
- Log("Stopping...");
- KeepAliveTimer.Stop();
- _localSocket?.Dispose();
- _saeReceive?.Dispose();
- _localPrivateKey?.Dispose();
- _localSocket = null;
- _saeReceive = null;
- _localPrivateKey = null;
- PeerKetyDropDown.Enabled = true;
- StartButton.Enabled = true;
- StopButton.Enabled = false;
- KnockButton.Enabled = false;
- SendButton.Enabled = false;
- Log("Stopped");
- }
- private void KeepAliveTimer_Tick(object sender, EventArgs e)
- {
- _keepAliveMsg.TimeStamp = DateTime.Now;
- _keepAliveMsg.WriteToBuffer(_keepAliveBuf);
- _localSocket.SendExchangeMessageTo(_serverEndPoint, _localPrivateKey, _serverPublicKey, _localId, _keepAliveMsg);
- }
- private void KnockButton_Click(object sender, EventArgs e)
- {
- var idToKnock = new Guid(PeerToKnockDropDown.Text);
- if (idToKnock == _localId)
- {
- Log("KNOCK YOUR SELF ???");
- return;
- }
- var msg = new ExchangeMessage(ExchangeMessageId.PeerKnockReq)
- {
- PeerId = idToKnock
- };
- _localSocket.SendExchangeMessageTo(_serverEndPoint, _localPrivateKey, _serverPublicKey, _localId, msg);
- }
- private void SendButton_Click(object sender, EventArgs e)
- {
- var to = SendToEndPointTextBox.Text.ParseToIpEndPointV4();
- var sendMsg = new ExchangeMessage(ExchangeMessageId.DataTransfer);
- sendMsg.PayloadBytes = Encoding.UTF8.GetBytes(SendContentTextBox.Text);
- var sendBytes = TransferCodec.Encode(_localPrivateKey, _peerPublicKeyRegistry[new Guid(PeerToKnockDropDown.Text)], _localId, sendMsg.ToBytes());
- var sent = _localSocket.SendTo(sendBytes, to);
- }
- //------------- logic -------------
- private void ProcessPacket()
- {
- var peerId = TransferCodec.ReadId(_saeReceive.Buffer);
- if (_saeReceive.RemoteEndPoint.IpEndPointEqualsTo(_serverEndPoint))
- {
- 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(_localPrivateKey, _serverPublicKey, _saeReceive.Buffer);
- var msg = new ExchangeMessage(msgData);
- switch (msg.Id)
- {
- 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 == _peerPublicKeyRegistry.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);
- _localSocket.SendExchangeMessageTo(msg.PeerEndPoint, _localPrivateKey, peerKey, _localId, connMsg);
- }
- _localSocket.SendExchangeMessageTo(_serverEndPoint, _localPrivateKey, _serverPublicKey, _localId, msgReply);
- break;
- case ExchangeMessageId.PeerKnockAckRelay:
- if (false == msg.PeerId.HasValue)
- {
- Log($"INVALID RESPONSE {msg.Id} was IGNORED: peer id is required");
- break;
- }
- Log($"KNOCK SUCCESS by {msg.PeerId} peer endpont is {msg.PeerEndPoint}");
- Invoke(new Action(() =>
- {
- SendToEndPointTextBox.Text = msg.PeerEndPoint.ToString();
- SendButton.Enabled = true;
- }));
- {
- Log($"SENDING CONNECTION REQ to {msg.PeerId} @ {msg.PeerEndPoint}");
- var connMsg = new ExchangeMessage(ExchangeMessageId.PeerKnockConnectionReq);
- _localSocket.SendExchangeMessageTo(msg.PeerEndPoint, _localPrivateKey, _peerPublicKeyRegistry[msg.PeerId.Value], _localId, connMsg);
- }
- break;
- case ExchangeMessageId.PeerKnockAckRelayed:
- Log($"ACCEPT SENT {msg.PeerId}");
- break;
- case ExchangeMessageId.PeerKnockReqErrPeerNoAvailable:
- Log($"KNOCK FAIL: {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 (BuildInPeerId.Invalid == peerId || BuildInPeerId.Server == peerId || false == _peerPublicKeyRegistry.TryGetValue(peerId, out var peerPublicKey))
- {
- throw new InvalidDataException("PEER ERROR: INVALID PEER ID");
- }
- var msgData = TransferCodec.DecodeData(_localPrivateKey, peerPublicKey, _saeReceive.Buffer);
- 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.PeerKnockConnectionReq:
- reply.Id = ExchangeMessageId.PeerKnockConnectionAck;
- break;
- 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:
- Log($"RECV {msg.Id} FROM {peerId} @ {_saeReceive.RemoteEndPoint}");
- return;
- }
- }
- _localSocket.SendExchangeMessageTo(_saeReceive.RemoteEndPoint, _localPrivateKey, peerPublicKey, _localId, reply);
- }
- }
- private void ReceiveCompleted(object sender, SocketAsyncEventArgs e)
- {
- if (null == _saeReceive) return;
- if (_saeReceive.SocketError == SocketError.Success)
- {
- try
- {
- ProcessPacket();
- }
- catch (InvalidDataException exception)
- {
- Log($"ERROR ProcessPacket:{exception.Message}");
- }
- catch (Exception exception)
- {
- Log($"ERROR ProcessPacket:{exception}");
- }
- }
- else
- {
- Log($"ERROR SOCKET:{_saeReceive.SocketError}");
- }
- if (null == _localSocket) return;
- if (false == _localSocket.ReceiveFromAsync(_saeReceive)) ReceiveCompleted(null, null);
- }
- private void BeginRecv()
- {
- _saeReceive.RemoteEndPoint = AnyEndPoint;
- if (false == _localSocket.ReceiveFromAsync(_saeReceive))
- {
- ReceiveCompleted(null, _saeReceive);
- }
- }
- //------------- util -------------
- private void Log(string content)
- {
- if (InvokeRequired)
- {
- Invoke(new Action<string>(Log), content);
- return;
- }
- RecvTextBox.Text =
- $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} {content}"
- + $"{Environment.NewLine}"
- + RecvTextBox.Text;
- RecvTextBox.Refresh();
- }
- }
- }
|