UPNP library

Computers connected to the internet through a NAT (basically anyone using an ADSL modem and the like) are generally unable to accept connections from the internet. This is rubbish if you want them to be able to host games and so on.
Happily, most home routers are UPNP devices, and so can have their NAT tables manipulated to allow incoming connections to be routed to the correct address on the local network.
Here’s a little lib that’ll make that process easy. Jar here, javadoc zip here, svn repo here.

Using it to add a port mapping looks like this:


InternetGatewayDevice dev = InternetGatewayDevice.getDevices( 1000 )[ 0 ];
String thisWillBeHandy = dev.getExternalIPAddress();
String connType = "TCP"; // or "UDP"
String remoteHost = null; // allow any remote host to connect
int internetSidePort = 1337;
String localHost = "192.168.1.whatever";
int localPort = 7331;
dev.addPortMapping( "A mapping for my game", connType, remoteHost, internetSidePort, localHost, localPort, 0 );

after this, any TCP connections made to the external IP and port will be forwarded to the local IP and port. Remember to clean up after yourself:


dev.deletePortMapping( null, internetSidePort, connType );

Please note that this is just the library found over here, but brutally cut down so it’s 60KB rather than 2MB. If it works, gratitude should be directed towards SuperBonBon, if it doesn’t work, it’s probably because I broke it :persecutioncomplex:

very nice & usefull, couldn’t you cut it a little more :slight_smile:

Yeah I could, but I’d have to use tiny electrons, and have you priced those things recently? I’m not made of money!

Neat - I’ll have a play with this…
60K though!? I don’t need it to make the tea as well!

Hah! Turns out genjar was pulling in some xml classes already included in rt.jar.
53K. Happy now? :wink:
edit: Also added a main method to the jar - it’ll print information on any NAT devices on your local network

Cool! I need to try this out.

Very cool! I definitely need to check this out ;D

OMG i Got it To Compile it works ;D

Ive been trying for ages to get this to work !

now its time to experiment ;D Thanks a million !

Will try to see if it works with my router, … brb

EDIT: Okay its not working for me for some reason. It can see the device fine through the java but when I use your example to add a port forward, first I can’t see it in my router config and 2nd is that I tried testing them (two ports one on 1337 and another on 7331) and neither of them works (I used http://www.seemyport.com/ to test).

Running this

	
public static void main( String[] args )
{
	try
	{
		System.out.println( "looking for device" );
		InternetGatewayDevice dev = getDevices( 1000 )[ 0 ];
		System.out.println( "External IP = " + dev.getExternalIPAddress() );
		System.out.print( "adding mapping : " );
		System.out.println( dev.addPortMapping( "foo", "TCP", null, 1337, "my lan address", 7331, 0 ) );

		System.out.println( "waiting for connection" );
		ServerSocket ss = new ServerSocket( 7331 );
		ss.setSoTimeout( 10000 );
		try
		{
			Socket s = ss.accept();
			InputStream in = s.getInputStream();
			while( in.available() > 0 )
			{
				System.out.println( "byte : " + in.read() );
			}
		}
		catch( SocketTimeoutException stoe )
		{
			System.out.println( "Timed out" );
		}
		System.out.print( "removing mapping : " );
		System.out.println( dev.deletePortMapping( null, 1337, "TCP" ) );
	}
	catch( IOException e )
	{
		e.printStackTrace();
	}
	catch( UPNPResponseException e )
	{
		e.printStackTrace();
	}
}

Works for me when testing port 1337 on the seemyport website. They don’t seem to send any data, but they do need a serversocket on your end to accept a connection. I’ll add this to the jar’s main method.

As to the port mapping not showing up in your router config: does the addPortMapping method return true? Are there any port mappings there already? What is your router make/model?

edit: that’s the jar updated now. Run "java -jar upnp.jar ", wait for the mapping to be made, and then test with seemyport.com

it successfully added a port forward to my router config (belkin 54g cable modem router).

it failed for me :P.

Ill try again later, and will mess around with it, but I got to go right now.

[quote]As to the port mapping not showing up in your router config: does the addPortMapping method return true? Are there any port mappings there already? What is your router make/model?

edit: that’s the jar updated now. Run "java -jar upnp.jar ", wait for the mapping to be made, and then test with seemyport.com
[/quote]
Okay I tried running the jar, here’s the output:

looking for device
adding mapping from 67.80.51.61:6115
        to 192.168.1.102:6114
waiting for connection
        Timed out
removing mapping : true

I tried using seemyport.com after “waiting for connection” on the external port and I get:

Could not connect to 6115 on 67.80.51.61 (Connection timed out). Bummer.

I check my router port forwarding list (its was empty) and the port forward is not there.

My router is Linksys WRT160N firmware v3.0.02.

Okay I tried some other things, I tried using port 1337 and 1338, also tried adding the port forward manually to the router and disabling windows firewall… a few things happen:

  1. disabling firewall causes seemyport.com to respond much faster (no more wait of 3-5 seconds, its instant)
  2. after disabling firewall, and when I try adding the port forward manually in the router config I get an exception when trying to upnp the same port:
looking for device
adding mapping from 67.80.51.61:1337
        to 192.168.1.102:1337
net.sbbi.upnp.messages.UPNPResponseException: Detailed error code :501, Detailed
 error description :Action Failed
        at net.sbbi.upnp.messages.ActionMessageResponseParser.startElement(Actio
nMessageResponseParser.java:189)
        at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startEle
ment(Unknown Source)
        at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scan
StartElement(Unknown Source)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImp
l$FragmentContentDriver.next(Unknown Source)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(U
nknown Source)
        at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next
(Unknown Source)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImp
l.scanDocument(Unknown Source)
        at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(U
nknown Source)
        at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(U
nknown Source)
        at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown So
urce)
        at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(Un
known Source)
        at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.p
arse(Unknown Source)
        at javax.xml.parsers.SAXParser.parse(Unknown Source)
        at net.sbbi.upnp.messages.ActionMessage.service(ActionMessage.java:207)
        at net.sbbi.upnp.impls.InternetGatewayDevice.addPortMapping(InternetGate
wayDevice.java:621)
        at net.sbbi.upnp.impls.InternetGatewayDevice.main(InternetGatewayDevice.
java:131)

You haven’t specifically said so, hence I have to ask so we’re not chasing our tails on this: You’re checking the port mapping table on the router’s config pages in the ten seconds between the mapping being made and being deleted right?

Assuming so:
Hrm, from your output it seems that your router is reporting success when you add the mapping, but not actually doing it. Not very helpful :-\ I’ve had a good trawl through the code and can’t see any way for addPortMapping() to return true without the device giving a 200 (HTTP_OK) response.
I guess the thing to do is try another UPNP control point and see if that works, looks like there are windows tools over here that’ll let you query your network devices and call actions manually. If that tool works for you, it might be time to bust out wireshark and see what’s going over the wire.

The exception you are seeing when you try and add an already-existing mapping is to be expected. The description according to the spec is

[quote]501: Action Failed - May be returned in[sic] current state of service prevents invoking that action.
[/quote]
Which is fair enough under the circumstances - and at least it shows that the router is doing something with the request.

There are a few “tables” on my router. There’s “single port forwarding”, “port range forwarding” and “port range triggering”. Also there’s the “routing table”. I checked all of these during the 10 seconds and all don’t have the port forward I set up. I also tried the “logging” feature of the router but there’s nothing of interest there either.

Okay thanks I’ll try the tools…brb.

EDIT: Okay I am not sure what am I supposed to be doing… I can see my device and the values and stuff but how am I supposed to test if port forwarding works? Which tool am I supposed to use?

EDIT2: I think I found the problem… I don’t know how or why, but things started working, but incorrectly, it’s really strange.
What I did was try uTorrent, I enabled UPnP there, and then tested using seemyport.com, everything worked. Then I disabled UPnP in uTorrent, chose a different port and set the port forward using the jar, and it worked. wtf? Okay so I close uTorrent down and try doing it with just the jar, I chose yet a different port, then run upnp.jar, try seemyport.com and it tells me this:

Could not connect to 3131 on 67.80.51.61 (Connection refused). Bummer.
It appears you have the port forwarding set up, but nothing is answering on port 3131.

So it seems to me that somehow maybe the jar is incorrectly setting up the listening socket? Why did it start working all of the sudden (or does it even work correctly?). Also, if I close all programs and try seemyport.com with a port that I tried setting up with the jar, it tells me connection refused, but if I use a port I didn’t setup with the jar, I get the usual connection timed out.
So, conclusions:

  1. somehow something started working after I tried uTorrent with UPnP
  2. now the upnp.jar works, but the socket it sets up refuses connections (?)
  3. If I use uTorrent with UPnP disabled but use the jar’s UPnP for the same port, the connection works fine.

Disaster strikes when i try to run the program on my xp machines on the same network

this is my test code…
I imported all the classes but it didnt make a difference




import net.sbbi.upnp.*;
import net.sbbi.upnp.impls.*;
import net.sbbi.upnp.devices.*;
import net.sbbi.upnp.services.*;
import net.sbbi.upnp.messages.*;

public class test{

	public static void main(String [] args){
	
		try{
		
			InternetGatewayDevice dev = InternetGatewayDevice.getDevices( 1000 )[ 0 ];
			String thisWillBeHandy = dev.getExternalIPAddress();
			System.out.println(thisWillBeHandy);
		}
		
		catch(Exception a){
		
			System.out.println("oh oh");					
		}	
	}
}



java.net.SocketException: Connection reset
        at java.net.SocketInputStream.read(Unknown Source)
        at java.io.BufferedInputStream.fill(Unknown Source)
        at java.io.BufferedInputStream.read1(Unknown Source)
        at java.io.BufferedInputStream.read(Unknown Source)
        at sun.net.www.http.HttpClient.parseHTTPHeader(Unknown Source)
        at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
        at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
        at java.net.URL.openStream(Unknown Source)
        at net.sbbi.upnp.XMLUtil.getXMLString(XMLUtil.java:97)
        at net.sbbi.upnp.XMLUtil.getXML(XMLUtil.java:134)
        at net.sbbi.upnp.devices.RootDevice.build(RootDevice.java:119)
        at net.sbbi.upnp.Discovery$1.discoveredDevice(Discovery.java:243)
        at net.sbbi.upnp.DiscoveryListener.listenBroadCast(DiscoveryListener.java:369)
        at net.sbbi.upnp.DiscoveryListener.run(DiscoveryListener.java:241)
        at java.lang.Thread.run(Unknown Source)
java.lang.NullPointerException
        at net.sbbi.upnp.XMLUtil.getXML(XMLUtil.java:135)
        at net.sbbi.upnp.devices.RootDevice.build(RootDevice.java:119)
        at net.sbbi.upnp.Discovery$1.discoveredDevice(Discovery.java:243)
        at net.sbbi.upnp.DiscoveryListener.listenBroadCast(DiscoveryListener.java:369)
        at net.sbbi.upnp.DiscoveryListener.run(DiscoveryListener.java:241)
        at java.lang.Thread.run(Unknown Source)


i am using the jre 6 update 15 i will try again after i have updated to the latest

I’ve only used tools in linux that have been “inspired by” the ones you got from that site, but I think the one you’re after is the “Device Spy” program. It should present you with a tree of UPNP devices - each of which contains Services and other Devices. Somewhere in this tree there will be a “addPortMapping” entry (find attached an image of what it looks like on my system). Double-clicking on it should let you set the appropriate arguments and submit the request.

I have got no clue what’s going on here. If you’ve got access to a remote system, try using that to test - then you control how the connection is made. e.g.: Before you mentioned seemyport.com, I was running a connection test from a remote server controlled through ssh.

First off: import statements only affect compilation. If your code compiles without errors, adding import statements will not make any difference to that code’s behaviour.

From the stacktrace you’ve posted, what seems to have happened is

There seems to be some fundamental problem with getting the router’s upnp definition. As with MarkMistry, try using another UPNP control point.

Okay I found the problem. The jar opens the port forward on the port you ask it in the command line but listens on 7331… yeah.

Connect to port 7331 on 67.80.51.61 OK!

So it was a bug in the program. I tried Device Spy also and it lets you lookup other mappings that are already there so it’s pretty cool. Will play around with it and see what other things I can do.

My bad :persecutioncomplex: Fixed now.