bitletorg / weupnp

A tiny UPnP (Universal Plug and Play) client library written in Java
http://bitletorg.github.io/weupnp
100 stars 36 forks source link

NAT-PMP support #7

Open offbynull opened 10 years ago

offbynull commented 10 years ago

Would it be possible to add NAT-PMP support to this library? The protocol seems stupid easy -- much less convoluted than UPnP IGD. http://miniupnp.free.fr/nat-pmp.html has a good overview of the protocol. Here's an example that I jut wrote that grabs your exposed IP address and opens a port for 60 seconds (tested on my home router):

    public static void main(String[] args) throws Throwable {
        DatagramSocket datagramSocket = new DatagramSocket(10000 + new Random().nextInt(55535));

        DatagramPacket publicAddressRequest = new DatagramPacket(new byte[] { 0, 0 }, 2, InetAddress.getByName("192.168.1.1"), 5351);
        datagramSocket.send(publicAddressRequest);

        ByteBuffer publicAddressResponseBuffer = ByteBuffer.allocate(12);
        DatagramPacket publicAddressResponse = new DatagramPacket(publicAddressResponseBuffer.array(), publicAddressResponseBuffer.capacity());
        datagramSocket.receive(publicAddressResponse);

        System.out.println(Integer.toHexString(publicAddressResponseBuffer.getInt(8))); // my ip

        DatagramPacket newPortMappingRequest = new DatagramPacket(new byte[] { 
            0, // version
            1, // udp (for tcp choose 2)
            0, 0, // reserved
            0x10, 0x00, // private port
            0x10, 0x00, // public port
            0, 0, 0, 60 // time to live for mapping
        }, 12, InetAddress.getByName("192.168.1.1"), 5351);
        datagramSocket.send(newPortMappingRequest);

        ByteBuffer newPortMappingBuffer = ByteBuffer.allocate(12);
        DatagramPacket newPortMappingResponse = new DatagramPacket(newPortMappingBuffer.array(), newPortMappingBuffer.capacity());
        datagramSocket.receive(newPortMappingResponse);

        System.out.println(Integer.toHexString(publicAddressResponseBuffer.getShort(2))); // result code
    }

The only issue with this is that there doesn't seem to be a standard way of getting the default gateway address in Java (192.168.1.1 in the example).

abahgat commented 10 years ago

I would be happy to implement this, but my present router does not seem to support it. I am, however, getting a new device this week that might support this. However, I did some cursory research during the weekend and I stumbled upon the same problem: there does not seem to be any way to get the gateway address without relying on OS-dependent commands. I am not really fond of this approach, as it might not work for some platforms (e.g. Android?)

If we could find a way to get this to work in 100% Java and platform independent code, I would definitely love to have this in weupnp.

offbynull commented 10 years ago

Sorry, the delay in response lead me to believe this project had been abandoned. I ended up writing an implementation under my own p2p project: https://code.google.com/p/peernetic/source/browse/#git%2Frouter-natpmp%02Fsrc%2Fmain%2Fjava%2Fcom%2Foffbynull%2Fpeernetic%2Frouter%2Fnatpmp.

Regarding the gateway address, I don't think there's much that can be done to make this platform independent. I used two approaches to try to get around this problem:

1) Common gateway addresses: I hardcoded a list of common IP router addresses from http://www.techspot.com/guides/287-default-router-ip-addresses/ 2) Run "netstat -rn" and scrap the output for IPv4 addresses: This should work on Linux/OSX/Windows, but not other playforms like Android :(

The third solution I thought about was trying to do an exhaustive scan of internal addresses. This may be do-able for class C range, but class A and B ranges are too large.

My router (ASUS RT-N56U) is NAT-PMP enabled. I think internally it uses miniupnpd. I was able to get everything working except listening for external address change broadcasts. I don't know if this is an issue with my router or with the code I wrote.

Also, the other day I came across some information that says that there's a newer version of NAT-PMP called PCP (http://tools.ietf.org/html/rfc6887). My understanding is that PCP needs a gateway address as well and has a similar request/response structure, but supports IPv6 and large scale NATs as well (and possibly chained NATs?).

If you're still interested in NAT-PMP/PCP support (even thought it won't be a 100% Java solution), we should pool our efforts together under a single banner and make sure that we don't end up duplicating effort/work.