/*
 * STUNUtility.java
 *
 * Created on December 30, 2007, 8:19 PM
 */

import java.util.logging.Logger;
import net.java.stun4j.client.StunDiscoveryReport;
import net.java.stun4j.StunAddress;
import net.java.stun4j.client.NetworkConfigurationDiscoveryProcess;

/**
 * A wrapper for the Stun4J library. This makes NAT traversal really easy.
 * @author Jason Ederle
 */
public class STUNUtility {
    //variables used to perform the stun lookup
    private int localPort; //port to bind and send/recv
    private int stunServerPort; //port of the stun service on server
    private String stunServer;  //address of the stun server
    private StunAddress stunServerAddress;   //stun server 
    private StunAddress stunLocalAddress;    //set after stun lookup completes
    //nat information after stun lookup
    private int publicPort;   //port that is public via NAT
    private String publicIP;  //IP address that is public via NAT
    private String natType;   //Type of NAT you are behind
    public static Logger logger = Logger.getLogger(STUNUtility.class.getName());

    /** 
     * Creates a new instance of STUNUtility 
     */
    public STUNUtility(String stunServer, int stunServerPort, int localPort) {

        //copy over the connection info to use later
        this.stunServerPort = stunServerPort;
        this.stunServer = stunServer;
        this.localPort = localPort;

        //build server address object used by stun4j
        stunServerAddress = new StunAddress(stunServer, stunServerPort);
    }

    /**
     * This is a blocking method that will try to discover
     * the type of NAT (if any) a user is behind. After you call this,
     * you can use the getPublicIP() and getPublicPort() methods.
     * @throws java.lang.Exception
     */
    public void performSTUNLookup() throws Exception {


        logger.info("Starting STUN NAT Discovery.");

        //do the actual stun lookup
        NetworkConfigurationDiscoveryProcess stunDiscovery = new NetworkConfigurationDiscoveryProcess(new StunAddress(localPort), stunServerAddress);
        stunDiscovery.start();
        StunDiscoveryReport report = stunDiscovery.determineAddress();

        //now unbind the socket, so the application can re-bind it to use with stun info
        stunDiscovery.shutDown();


        //get the IP address object
        stunLocalAddress = null;
        stunLocalAddress = report.getPublicAddress();


        //see if the IP address is non-null (it return null if it failes)
        if (stunLocalAddress == null ||
                stunLocalAddress.getSocketAddress() == null ||
                stunLocalAddress.getSocketAddress().getAddress() == null) {

            //the lookup had an error.. throw an exception
            throw new Exception("Stun lookup failed! No IP Address found, it's NULL.");

        }



        //get the nat type name
        natType = report.getNatType();


        //just get the IP address.. the other info is excessive
        publicIP = stunLocalAddress.getSocketAddress().getAddress().getHostAddress();





        publicPort = stunLocalAddress.getPort();


    /*
    //Print if you want to see the results.
    logger.info("NAT Port Mapping Info:");
    logger.info("NAT type: " + getNatType());
    logger.info("Public IP address = " + getPublicIP());
    logger.info("Public Port number = " + getPublicPort());
     */




    }

    /**
     * Returns the name of the NAT type as the.
     * @return 
     */
    public String getNatType() {
        return natType;
    }

    /**
     * Get public ip address.
     */
    public String getPublicIP() {
        return publicIP;
    }

    /**
     * get public port info.
     */
    public int getPublicPort() {
        return publicPort;
    }

    /**
     * Demo class to find your NAT routing information.
     */
    public static void main(String[] args) {


        try {

            //this is apublic stun server. It might go down after posting this, so you should really setup your own. 
            String stunServerAddress = new String("stun.xten.net");

            //this is the port that most stun services run on
            int stunServerPort = 3478;

            //this is the port your program wants to be able to use.
            //The stun program will find the NAT mapping of this port,
            //and find the public IP address and port number that internet users
            //can connect to you with.
            int aLocalPortYouWantToUse = 2525;

            //create a stun utility tool (makes stun4j a little easier)
            STUNUtility stun = new STUNUtility(stunServerAddress, stunServerPort, aLocalPortYouWantToUse);

            //talk to the stun server and figure out the NAT information
            stun.performSTUNLookup();

            //now print out the info that users outside the internet can use to connect to you.
            logger.info("Internet users can connect to my IP address " + stun.getPublicIP() + " and port " + stun.getPublicPort());

        } catch (Exception e) {
            logger.severe("Failed to lookup NAT information via STUN: " + e.getMessage());
        }

    }
}
