3

first thing sorry for my bad English but it is not my first language. My question is this: I'm working for realize a simple software for desktop sharing, using Java. The first problem is how can communicate two client that are behind NAT. After some research on Internet, I found that one of the better solution is to use the technique of UDP hole punching. If I correctly understood, this function works in the following way:

A = client1

NA = nat of client 1

B = client 2

NB = nat of client 2

S = public server

1 - A sends a UDP packet to S

2 - S receives the public IP and the public port of NA

3 - B sends a UDP packet to S

4 - S receives the public IP and the public port of NB

5 - S sends a UDP packet to A with the public ip and port of B through the binded socket that received the first packet from A

6 - S sends a UDP packet to B with the public ip and port of A through the binded socket that received the first packet from B

7 - A sends a UDP packet to B with the specifics received from S, NB drop this packet since it doesn't know who is A (it hasn't a route for this packet). But this open a hole in the NA that can accept and inoltrate to A a packet that arrives from B.

8 - B send a UDP packet to A, NA can inoltrate the packet.

I understood that this method will work only with 3 of the 4 NAT types: full cone NAT, restricted cone NAT, restricted port NAT, but not with Symmetric NUT. Now, I realized a little software that should simulate the UDP hole punching, and this is the code (I know it is not elegant, but now I want just see if the procedure works):

Client:

import java.io.IOException;
import java.net.*;

public class JavaClient {
    public static void main(String[] args) throws UnknownHostException, SocketException, IOException {
        new JavaClient().sendHostInfoToS();

    }

    private void sendHostInfoToS() {
        // TODO code application logic here
        byte[] buffer = {
            99,99,99,99,99,99,99
        } ;

        InetAddress address;
        try {
            //Client contacts server
            address = InetAddress.getByName("X.XXX.XX.XXX"); //SERVER

            DatagramPacket packet = new DatagramPacket(
            buffer, buffer.length, address, 9999
            );
            DatagramSocket datagramSocket = new DatagramSocket();


            datagramSocket.send(packet);
            System.out.println(InetAddress.getLocalHost().getHostAddress());
            /////////////////////////////////////



            //Client waits that server send back a UDP packet containing the information of the second client
            DatagramPacket receivePacket = new DatagramPacket(new byte[40], 40);
            System.out.println("WAIT RESPONSE...");
            datagramSocket.receive(receivePacket);
            System.out.println("RESPONSE RECEIVED...");
            String sentence = new String( receivePacket.getData());
            System.out.println("RECEIVED: " + sentence);

            System.out.println("CODING IP...");
            Thread.sleep(3000);

            String[] p = sentence.split(":");
            p[0] = p[0].replace("/","");
            System.out.println("END CODING...");
            /////////////////////////////////



            //Client send to the other client a UDP packet using the information received from the server
            byte[] sendData = new byte[8];
            String sendString ="test";
            sendData = sendString.getBytes();
            DatagramPacket sPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName(p[0]), Integer.parseInt(p[1].substring(0,5)));
            System.out.println("SENDING DATA TO CLIENT " + sPacket.getSocketAddress() + " on  port " + Integer.toString(sPacket.getPort()));
            DatagramSocket sDatagramSocket = new DatagramSocket();
           //datagramSocket = new DatagramSocket();
            sDatagramSocket.send(sPacket);
            Thread.sleep(500);
            System.out.println("DATA SENT...");
            /////////////////////////////////////////////


            //Client wait a response from the other client
            receivePacket = new DatagramPacket(new byte[128], 128);
            System.out.println("WAIT RESPONSE...");
            //datagramSocket = new DatagramSocket();
            sDatagramSocket.receive(receivePacket);
            System.out.println("RESPONSE RECEIVED...");
            sentence = new String( receivePacket.getData());
            System.out.println("RECEIVED: "+ sentence);
            /////////////////////////////////
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Server:

import java.io.IOException;
import java.net.*;

public class JavaServer {
    DatagramPacket [] hosts = new DatagramPacket[2];

    public static void main(String[] args) throws UnknownHostException, SocketException, IOException {
        new JavaServer().waitInUDPConn();

    }


    DatagramSocket serverSocket;
    private void waitInUDPConn() {
        try {
            serverSocket = new DatagramSocket(9999);
            byte[] receiveData = new byte[8];


            int indHosts = 0;

            while(true) {
                DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
                System.out.println("WAITING DATA...");
                serverSocket.receive(receivePacket);
                String sentence = new String( receivePacket.getData());
                System.out.println("RECEIVED: "+ sentence);


                hosts[indHosts] = receivePacket;

                indHosts++;
                if (indHosts == 2) break;
            }
        } catch (Exception e) {
        }
        System.out.println("Hosts 1: "+ hosts[0].getAddress().toString() +", on port "+ Integer.toString(hosts[0].getPort()));
        System.out.println("Hosts 2: "+ hosts[1].getAddress().toString() +", on port "+ Integer.toString(hosts[1].getPort()));

        sendInfoToC(hosts[0], hosts[1]);
        sendInfoToC(hosts[1], hosts[0]);
    }

    private void sendInfoToC(DatagramPacket dpDest, DatagramPacket dpInfoB) {
       try {
       byte[] sendData = new byte[8];
        System.out.println("START WAIT...");
        Thread.sleep(3000);
        System.out.println("STOP WAIT...");

        InetAddress IPAddress = dpDest.getAddress();
        String sendString =dpInfoB.getAddress().toString() +":"+ Integer.toString(dpInfoB.getPort());
        sendData = sendString.getBytes();
        DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, dpDest.getPort());

        System.out.println("SENDING PACKET...");
        serverSocket.send(sendPacket);
        System.out.println("PACKET SENT...");
        }
        catch (Exception e) {
        }
    }
}

With this code I can contact the server with both clients, server receives the packets from both clients, server send to A the information of B and to B the information of A, both clients receive the information, but they can't communicate together. A doesn't receive the packet from B and B doesn't receive the packet from A. So now I need to understand if I'm behind symmetric nat with both clients. If I understood the other types of NAT (full cone, restricted cone, and port restricted cone), they use always the same port for each external connection. So if for example I have and internal pc with the IP: 192.168.0.50 and this machine wants reach a public ip (for example 54.66.18.22) NAT will assign at the packet the public IP and an external port (for example the port 58000). Later if the same pc (192.168.0.50) wants to connect with another external host, NAT always use the same external port 58000. Instead with a symmetric NAT every connection will have a different external port (first connection port 58000 and the second connection with different port). Now if this is correct I think that the both clients are behind two Symmetric NAT. I can determinate this because from client A when I connect to the server I see that server receive the public IP of NAT A and a port for example 55000. If I close the client and run it again, this time I see that the server receives always the same IP (obviously) but the port is not 55000 but 55001 and if I try again the port is 55002 and so on. And the same thing is with the second client, the port is increment by 1 from a connection to the other. Now, always if I'm understanding the whole scenario, this should confirm me that the both clients are behind symmetric NAT. But the interesting thing is that it seems that the both NATs use an increment of one unit so I thought that I could use a port prediction when I send a packet from A to B and from B to A, so I simply tried this:

DatagramPacket sPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName(p[0]), Integer.parseInt(p[1].substring(0,5))+1);

I added 1 to the port that I receive from the server because if is true that I'm behind two symmetric NAT, is also true the the increment is of 1 port. Check with an example:

A connect to the server and NAT A send to S this a packet containing: 45.89.66.125:58000

B connect to the server and NAT B send to S this a packet containing: 144.85.1.18:45000

S send the information of B to A and the information of A to B

Now if A send to B this information, the NAT A will create this map:

INTERNAL_IP_A:58001-144.85.1.18:45001

For this connection NAT A should use the port 58001 (last port + 1 it is a symmetric NAT)

NAT B receive the packet but discard it.

Now if B send a packet to A with the information received the NAT B will create this map:

INTERNAL_IP_B:45001-45.89.66.125:58001

Now NAT should accept this packet since in its table there is the information to accept it.

But also in this case none of the client receive the information from the other. What am I doing wrong? Or what I haven't understood?

4

0 回答 0