C# code to achieve TCP penetration (hole drilling)

Intranet penetration technology is required to realize TCP communication between intranets. The specific principles are available on the Internet. Refer to:

https://blog.csdn.net/leisure512/article/details/4900191

https://blog.csdn.net/aaron133/article/details/79206257

The condition for successful TCP penetration requires that the networks on both sides are cone-shaped nat (or at least one end of the network is cone-shaped NAT). For details, please refer to

https://blog.csdn.net/h_armony/article/details/45167975

There are various NAT descriptions:

Broadband with public IP: for example, ADSL of China Unicom, this kind of broadband will assign a public IP to each user, so its NAT type depends on the router selected by the user. Most home routers are port limited cone NAT;

Broadband without public network IP: for example, broadband access, which allocates LAN IP to users, and NAT connected to public network is owned by operators, which is generally symmetrical NAT;

Mobile Internet: similar to "broadband without public IP", LAN IP is allocated to mobile phones, and symmetrical NAT is basically used for exports;

Large company routers: most of them are configured as symmetrical NAT.

VS2010 C# implementation is used here:

Server code:

static void Main(string[] args)
{
int port = 555;
IPEndPoint ipe = new IPEndPoint(IPAddress.Any, port);
Socket sSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sSocket.Bind(ipe);
sSocket.Listen(100);
Console.WriteLine("listening has been turned on, please wait");

        while (true)
        {
            Socket serverSocket1 = sSocket.Accept();
            Console.WriteLine("The connection has been established");
            string recStr = "";
            byte[] recByte = new byte[4096];
            int bytes = serverSocket1.Receive(recByte);
            IPEndPoint ep1 = (IPEndPoint)serverSocket1.RemoteEndPoint;
            Console.WriteLine(" from {0}", ep1.ToString()); 
            recStr = Encoding.ASCII.GetString(recByte, 0, bytes);
            Console.WriteLine("Client 1:{0}", recStr);

            Socket serverSocket2 = sSocket.Accept();
            bytes = serverSocket2.Receive(recByte);
            IPEndPoint ep2 = (IPEndPoint)serverSocket2.RemoteEndPoint;
            Console.WriteLine(" from {0}", ep2.ToString());
            recStr = Encoding.ASCII.GetString(recByte, 0, bytes);
            Console.WriteLine("Client 2:{0}", recStr);


            byte[] sendByte =Encoding.ASCII.GetBytes(ep1.ToString() + ":" + ep2.ToString());  
            serverSocket1.Send(sendByte, sendByte.Length, 0);

            sendByte = Encoding.ASCII.GetBytes(ep2.ToString() + ":" + ep1.ToString());  
            serverSocket2.Send(sendByte, sendByte.Length, 0);

            serverSocket1.Close();
            serverSocket2.Close();
        } 
          
    } 

Function: after the clients on both sides connect to the server, the mapped external network IP and port number are transmitted to both sides.

Client code

static void Main(string[] args)
{
string host = “115.21.X.X”;// Server IP address
int port = 555;
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//Set port reusability
clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
//Connect server
clientSocket.Connect(host, port);
Console.WriteLine("Connect: " + host + " " + port);

        string data = "hello,Server!";
        clientSocket.Send(Encoding.ASCII.GetBytes(data));
        Console.WriteLine("Send: " + data);
        byte[] recBytes = new byte[100];
        //Obtain the ip and port numbers of both parties
        int bytes = clientSocket.Receive(recBytes, recBytes.Length, 0);
       string result = Encoding.ASCII.GetString(recBytes, 0, bytes);
       Console.WriteLine("Recv: " +result);
       clientSocket.Close();

       string[] ips = result.Split(':'); 
       int myPort = Convert.ToInt32(ips[1]);
       string otherIp = ips[2];
       int otherPort = Convert.ToInt32(ips[3]);


       Socket mySocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
       mySocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        //Bind to the port number of the previous connection
       IPEndPoint ipe = new IPEndPoint(IPAddress.Any, Convert.ToInt32(myPort));
       mySocket.Bind(ipe);
        //Try 5 connections
       for (int j = 0; j < 5; j++)
       {
           try
           {
               mySocket.Connect(otherIp, otherPort);
               Console.WriteLine("Connect: success{0},{1}", otherIp,otherPort);
               break;
           }
           catch (Exception)
           {
               Console.WriteLine("Connect: fail");
                // otherPort++;// If it is a symmetric NAT, the port number of the client may have changed. Normally, it should be added by 1 in order. You can try + 1 and try again (the port number becomes + 1 when I use the mobile phone hotspot connection) unless it encounters a random port.
           }

       }
       while (true)
       {
           mySocket.Send(Encoding.ASCII.GetBytes("hello,the other client!"));

           byte[] recv = new byte[4096];
           int len = mySocket.Receive(recv, recv.Length, 0);
           result = Encoding.ASCII.GetString(recv, 0, len);
           Console.WriteLine("recv :" + result);

           Thread.Sleep(1000); 
       }

}

The client on the other side is the same. After connecting to the server, you can bind the previous port number for reuse, but if one end is a symmetric NAT, the port number will be different each time you use it, so you have to predict the port number that may be used next time to connect. For example, when using the mobile hotspot network to connect to the server, it is obtained that the port number used is 56324, and it will not be connected until the next time the clients connect to each other with 56325.

Keywords: C# TCPIP

Added by iPixel on Sat, 15 Jan 2022 12:22:57 +0200