2
votes

I'm trying to to send a udp packet to a client across a NAT, both of us belong to a different NAT, we are familiar with the theory of STUN therefore the way to achieve this is to 'hole punch' our way through via a simple STUN server..

Basically the server just returns external IP address and port of another client that is 'connected' which I can then use it to send the packet to the client across the NAT... however though we managed to get each other's external ip and ports..we are still unable to receive anything from each other after sending... After searching through forums and many hours of head scratching we are still unable to come up with a solution...was wondering if anyone who is familiar with STUN to able to give us some pointers or advice on where we've gone wrong...

Below is our small client we've written...

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

import javax.swing.JOptionPane;


public class Client {

DatagramSocket socket;

public Client(){
    try {
        socket = new DatagramSocket();
        String data = "Initiate Stun Server";
        byte[] receive = data.getBytes();

        InetAddress host = InetAddress.getByName("the.stun.server.ipaddress");
        DatagramPacket pk = new DatagramPacket(receive,receive.length,host,9345);
        socket.send(pk); //send packet to server to initiate udp communication

        //spawn a new Thread to listen for any incoming packets
        new Thread(){
            public void run(){
                byte[] r;
                DatagramPacket rp;
                while(true){
                    System.out.println("Start listening on new socket");
                    r = new byte[1024];
                    rp = new DatagramPacket(r,r.length);
                    try {
                        socket.receive(rp);
                        System.out.println(new String(rp.getData()));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        String m = JOptionPane.showInputDialog(null,"Enter message to send");
        InetAddress connect = InetAddress.getByName(JOptionPane.showInputDialog(null,"Enter address to send message to"));//This is where we input the external ip
        int connectPort = Integer.parseInt(JOptionPane.showInputDialog(null,"Enter port of the addressee"));//input port
        DatagramPacket p = new DatagramPacket(m.getBytes(),m.getBytes().length,connect,connectPort);
        socket.send(p);

    } catch (SocketException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (UnknownHostException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

public static void main(String args[]){
    Client c = new Client();
}

}
1

1 Answers

2
votes

What you implemented is not the true STUN protocol, but will likely suffice. :)

But I think I see two problems in your code.

  1. You aren't saving the local port number. After you get the response back from the stun server, you need to call socket.getLocalPort to find out what your internal port number that corresponds to the "mapped port". The mapped port is the port that your stun server sees you at. Your NAT will continue to map outbound traffic from your PC's IP, to that mapped port, but only if you use the same local port. So in your subsequent connection to your peer, create the datagram socket on that same port (after closing the original socket), or just reuse the same socket for a subsequent communication to a peer, as the socket is already binded.

  2. Just because you know the remote host's external IP address and his port mapping for his local socket, doesn't mean his NAT is going to forward your packets. Most NATs run as "IP and port restricted". This means that it will only allow inbound traffic, including UDP packets, through the NAT if it knows that there was a corresponding outbound UDP packet for the same remote host's IP and port. If it didn't have this rule in place, it wouldn't know which PC behind the NAT to forward the packet to. Typical NAT traversal technique is for both peers to send simple 1-byte datagrams to each other at the same time, and to try repeatedly (more than once). The first packet tries to leave the host and out of its own NAT, but will likely get blocked by the remote NAT (because it doesn't know who you are). But it does cause your NAT to create a mapping and forwarding entry for the other side to successfully send to you. Eventually, both NATs will allow and forward traffic between both peers.

There are also types of NATs with unpredictable port mapping behavior. (Port mapping changes on a per-IP basis). These are difficult to traverse (with with STUN), but usually works out ok if the other side has a well behaved NAT. Fortunately, these types of NATs are rarer than they used to be.

Here's some links:

ICE (standard mechanism on P2P via usage of STUN and TURN): http://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment

My P2P connectivity in a nutshell answer I gave a while back.

Also, a blatant plug to use my STUN server code base. You can use it in conjunction with the JStun client library.