I have implemented TCP hole punching in C#. What I am getting is, the clients are sending the SYN messages to each other but they are dropped at the opposite routers. The scenario is something like this: Client:A--------Router:A--------Server--------Router:B--------Client:B
If client:A sends the SYN packet to Client:B, it is dropped at Router:B, which is obvious NAT behavior. But, according to the hole punch scenarios, now if the client:B sends SYN to client:A, it should pass through the NAT at router:A. Here in my case, router:A ,too, drops this packet. In netstat, it shows the conenction with B as SYN_SENT and after couple of seconds, I get a message like "The client has not been responding. Connection failure"
As per my research in this, reason behind dropping, may be silent dropping or active rejection. If it is silent dropping, then the hole should be punched and SYN from B should be passed through NAT at A. And if it is active rejection, the router:A will get the notification message from router:B about dropping and A will close the hole.
The problem is I am not getting any notification message at A. (checked through router log). I also tried to an already implemented TCP hole pucnhing example taken from https://github.com/jasonpang/tcp-holepunching
But I am getting the similar results. Help me to find out the solution for this. I am also doubtful about the timeout for the holes punched at routers.
namespace HolePunching { public class HolePunchingClient { private Socket _clientSocket;
private Socket _serverSocket;
private Socket _connectSocket;
//private Socket _serverSocket;
private Socket _holePunchedSocket;
public HolePunchingClient (IPEndPoint localEndPoint)
{
/*For the moment, only TCP Hole Punching is supported*/
_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_clientSocket.Bind(localEndPoint);
//_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//_serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
//_serverSocket.Bind(localEndPoint);
}
private void ProcessClientRequest (object sender, DoWorkEventArgs eventArgs)
{
try
{
IPEndPoint iep = (IPEndPoint)eventArgs.Argument;
_connectSocket.Connect(iep);
Console.WriteLine("Connected correctly with: " + iep);
_serverSocket.Close();
_holePunchedSocket = _connectSocket;
Console.WriteLine("\n\n\nException check clientRequest\n\n\n");
}
catch (Exception e)
{
Console.WriteLine("In ProcessClientRequest: " + e.Message);
return;
}
}
private void ProcessServerRequest (object sender, DoWorkEventArgs eventArgs)
{
try
{
_serverSocket.Listen(10);
Socket s = _serverSocket.Accept();
Console.WriteLine("Received connection from: " + s.RemoteEndPoint);
_holePunchedSocket = s;
Console.WriteLine("\n\n\nException check serverRequest\n\n\n");
}
catch (Exception e)
{
Console.WriteLine("In ProcessServerRequest: " + e.Message);
return;
}
}
private void ProcessRequest (object sender, DoWorkEventArgs eventArgs)
{
while (true)
{
byte[] bytes = new byte[2048];
_clientSocket.Receive(bytes);
MessageType messageType = (MessageType) bytes[0];
Console.WriteLine("MessageType received: " + messageType);
switch (messageType)
{
case MessageType.ConnectClient:
byte[] byteAddress = new byte[4];
byte[] bytePort = new byte[2];
Buffer.BlockCopy(bytes, 1, byteAddress, 0, 4);
Buffer.BlockCopy(bytes, 5, bytePort, 0, 2);
IPEndPoint remoteEndPoint = new IPEndPoint(new IPAddress(byteAddress), BitConverter.ToUInt16(bytePort, 0));
Console.WriteLine("HP will be done towards this address: " + remoteEndPoint);
_connectSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_connectSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_connectSocket.Bind(_clientSocket.LocalEndPoint);
_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_serverSocket.Bind(_clientSocket.LocalEndPoint);
BackgroundWorker bwClient = new BackgroundWorker();
bwClient.DoWork += ProcessClientRequest;
BackgroundWorker bwServer = new BackgroundWorker();
bwServer.DoWork += ProcessServerRequest;
bwClient.RunWorkerAsync(remoteEndPoint);
bwServer.RunWorkerAsync();
break;
}
}
}
public void Connect (IPEndPoint serverEndPoint)
{
_clientSocket.Connect(serverEndPoint);
_clientSocket.Send(BitConverter.GetBytes((byte)MessageType.Register));
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += ProcessRequest;
bw.RunWorkerAsync();
}
public Socket HolePunch (IPAddress otherAddress)
{
byte[] bytes = new byte[5];
Buffer.BlockCopy(BitConverter.GetBytes((byte)MessageType.RequestClient), 0, bytes, 0, 1);
Buffer.BlockCopy(otherAddress.GetAddressBytes(), 0, bytes, 1, 4);
_clientSocket.Send(bytes);
while (_holePunchedSocket == null)
{
System.Threading.Thread.Sleep(1500);
}
return _holePunchedSocket;
}
}
}
This is the client code, which I have taken from the link above. I'm getting the exception message "No response from another client. Connection failure" while connecting to the client B/A.
client A and B both run the same copies of this program. Either of them will send a request to server to connect with the other client. Server will send the IP-Port combination to both. and the client code will proceed by sending the connect request to each other.(which eventually gets timed out)