[freenet-cvs] r13344 - trunk/plugins/UPnP
nextgens at freenetproject.org
nextgens at freenetproject.org
Wed May 23 20:24:22 UTC 2007
Author: nextgens
Date: 2007-05-23 20:24:22 +0000 (Wed, 23 May 2007)
New Revision: 13344
Modified:
trunk/plugins/UPnP/UPnP.java
Log:
UPnP: basic support: port forwarding is set as soon as possible, the web interface displays the device capabilities... I'm not sure that ip detection works reliably.
it needs testing.
Modified: trunk/plugins/UPnP/UPnP.java
===================================================================
--- trunk/plugins/UPnP/UPnP.java 2007-05-23 19:55:43 UTC (rev 13343)
+++ trunk/plugins/UPnP/UPnP.java 2007-05-23 20:24:22 UTC (rev 13344)
@@ -19,7 +19,6 @@
import plugins.UPnP.org.cybergarage.upnp.ServiceStateTable;
import plugins.UPnP.org.cybergarage.upnp.StateVariable;
import plugins.UPnP.org.cybergarage.upnp.device.DeviceChangeListener;
-import plugins.UPnP.org.cybergarage.upnp.xml.StateVariableData;
import freenet.pluginmanager.DetectedIP;
import freenet.pluginmanager.FredPlugin;
import freenet.pluginmanager.FredPluginHTTP;
@@ -27,6 +26,8 @@
import freenet.pluginmanager.FredPluginThreadless;
import freenet.pluginmanager.PluginHTTPException;
import freenet.pluginmanager.PluginRespirator;
+import freenet.support.HTMLNode;
+import freenet.support.Logger;
import freenet.support.api.HTTPRequest;
/**
@@ -40,9 +41,12 @@
* @see http://www.upnp.org/
* @see http://en.wikipedia.org/wiki/Universal_Plug_and_Play
*
- * TODO: add logging!
+ * TODO: Support multiple IGDs ?
+ * TODO: Advertise the node like the MDNS plugin does
+ * TODO: Implement EvenListener and react on ip-change
*/
public class UPnP extends ControlPoint implements FredPluginHTTP, FredPlugin, FredPluginThreadless, FredPluginIPDetector, DeviceChangeListener {
+ private PluginRespirator pr;
/** some schemas */
private static final String ROUTER_DEVICE = "urn:schemas-upnp-org:device:InternetGatewayDevice:1";
@@ -50,9 +54,12 @@
private static final String WANCON_DEVICE = "urn:schemas-upnp-org:device:WANConnectionDevice:1";
private static final String WAN_IP_CONNECTION = "urn:schemas-upnp-org:service:WANIPConnection:1";
- private volatile Device _router;
- private volatile Service _service;
+ private Device _router;
+ private Service _service;
+ private boolean isPortForwarded = false;
+ private boolean isDisabled = false; // We disable the plugin if more than one IGD is found
private final Object lock = new Object();
+ private int fnpPortNumber;
public UPnP() {
super();
@@ -60,31 +67,75 @@
}
public void runPlugin(PluginRespirator pr) {
- start();
+ this.pr = pr;
+ this.fnpPortNumber = pr.getNode().getFNPPort();
+ super.start();
}
public void terminate() {
- stop();
+ unregisterPortMapping();
+ super.stop();
}
- // FIXME: we use the first IGD we detect, so we have got only 1 ip to report
public DetectedIP[] getAddress() {
+ Logger.minor(this, "UP&P.getAddress() is called \\o/");
+ if(isDisabled) {
+ Logger.normal(this, "Plugin has been disabled previously, ignoring request.");
+ return null;
+ } else if(!isNATPresent()) {
+ Logger.normal(this, "No UP&P device found, detection of the external ip address using the plugin has failed");
+ return null;
+ }
+
+ DetectedIP result = null;
+ final String natAddress = getNATAddress();
try {
- return new DetectedIP[] { new DetectedIP(InetAddress.getByName(getNATAddress()), DetectedIP.NOT_SUPPORTED) };
+ // TODO: report a different connection type if port forwarding has succeeded?
+ result = new DetectedIP(InetAddress.getByName(natAddress), DetectedIP.NOT_SUPPORTED);
+
+ Logger.normal(this, "Successful UP&P discovery :" + result);
+ System.out.println("Successful UP&P discovery :" + result);
+
+ return new DetectedIP[] { result };
} catch (UnknownHostException e) {
+ Logger.error(this, "Caught an UnknownHostException resolving " + natAddress, e);
+ System.err.println("UP&P discovery has failed: unable to resolve " + result);
return null;
}
}
- public void deviceAdded(Device dev ) {
+ public void deviceAdded(Device dev) {
synchronized (lock) {
- if(isNATPresent())
- return; // We don't handle more than one IGD.
-
- if(!ROUTER_DEVICE.equals(dev.getDeviceType()) || !dev.isRootDevice())
+ if(isDisabled) {
+ Logger.normal(this, "Plugin has been disabled previously, ignoring new device.");
return;
+ } else if(!ROUTER_DEVICE.equals(dev.getDeviceType()) || !dev.isRootDevice())
+ return; // Silently ignore non-IGD devices
+ else if(isNATPresent()) {
+ Logger.error(this, "We got a second IGD on the network! the plugin doesn't handle that: let's disable it.");
+ System.err.println("The UP&P plugin has found more than one IGD on the network, as a result it will be disabled");
+ isDisabled = true;
+
+ _router = null;
+ _service = null;
+
+ stop();
+ return;
+ }
+ Logger.normal(this, "UP&P IGD found : " + dev.getFriendlyName());
+ System.out.println("UP&P IGD found : " + dev.getFriendlyName());
_router = dev;
+
discoverService();
+ if(_service == null) {
+ Logger.error(this, "The IGD device we got isn't suiting our needs, let's disable the plugin");
+ System.err.println("The IGD device we got isn't suiting our needs, let's disable the plugin");
+ isDisabled = true;
+ _router = null;
+ } else
+ registerPortMapping();
+ // We have found the device we need: stop the listener thread
+ stop();
}
}
@@ -104,7 +155,7 @@
if (!current2.getDeviceType().equals(WANCON_DEVICE))
continue;
-
+
_service = current2.getService(WAN_IP_CONNECTION);
return;
}
@@ -112,6 +163,27 @@
}
}
+ public void registerPortMapping() {
+ if(isPortForwarded) {
+ Logger.error(this, "Port mapping already registered! shouldn't happen!");
+ return;
+ }
+
+ Logger.normal(this, "Registering a port mapping for " + fnpPortNumber + "/udp");
+ isPortForwarded = addMapping("UDP", fnpPortNumber, "Freenet 0.7 FNP - " + _router.getInterfaceAddress());
+ Logger.normal(this, isPortForwarded ? "Mapping is successful!" : "Mapping has failed!");
+ }
+
+ public void unregisterPortMapping() {
+ if(!isPortForwarded)
+ return;
+
+ Logger.normal(this, "Unregistering the port mapping for FNP");
+ isPortForwarded = false;
+ boolean result = removeMapping("udp", fnpPortNumber);
+ Logger.normal(this, result ? "Mapping removal is successful" : "Mapping removal has failed!");
+ }
+
public void deviceRemoved(Device dev ){
synchronized (lock) {
if(_router.equals(dev)) {
@@ -173,10 +245,6 @@
}
}
- private String toString(StateVariableData data) {
- return (data == null ? "null" : data.getValue());
- }
-
private String toString(String action, String Argument, Service serv) {
Action getIP = serv.getAction(action);
if(getIP == null || !getIP.postControlAction())
@@ -186,6 +254,7 @@
return ret.getValue();
}
+ // TODO: extend it! RTFM
private void listSubServices(Device dev, StringBuffer sb) {
ServiceList sl = dev.getServiceList();
for(int i=0; i<sl.size(); i++) {
@@ -193,59 +262,29 @@
if(serv == null) continue;
sb.append("<div>service ("+i+") : "+serv.getServiceType()+"<br>");
if("urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1".equals(serv.getServiceType())){
- StateVariable linkStatus = serv.getStateVariable("PhysicalLinkStatus");
- StateVariable wanAccessType = serv.getStateVariable("WANAccessType");
- StateVariable upstreamBW = serv.getStateVariable("Layer1UpstreamMaxBitRate");
- StateVariable downstreamBW = serv.getStateVariable("Layer1DownstreamMaxBitRate");
-
sb.append("WANCommonInterfaceConfig");
- if(linkStatus != null)
- sb.append(" status: " + toString(linkStatus.getStateVariableData()));
- if(wanAccessType != null)
- sb.append(" type: " + toString(wanAccessType.getStateVariableData()));
- if(upstreamBW != null)
- sb.append(" upstream: " + toString(upstreamBW.getStateVariableData()));
- if(downstreamBW != null)
- sb.append(" downstream: " + toString(downstreamBW.getStateVariableData()) + "<br>");
+ sb.append(" status: " + toString("GetCommonLinkProperties", "NewPhysicalLinkStatus", serv));
+ sb.append(" type: " + toString("GetCommonLinkProperties", "NewWANAccessType", serv));
+ sb.append(" upstream: " + toString("GetCommonLinkProperties", "NewLayer1UpstreamMaxBitRate", serv));
+ sb.append(" downstream: " + toString("GetCommonLinkProperties", "NewLayer1DownstreamMaxBitRate", serv) + "<br>");
}else if("urn:schemas-upnp-org:service:WANPPPConnection:1".equals(serv.getServiceType())){
- StateVariable linkStatus = serv.getStateVariable("ConnectionStatus");
- StateVariable uptime = serv.getStateVariable("Uptime");
- StateVariable upstreamBW = serv.getStateVariable("UpstreamMaxBitRate");
- StateVariable downstreamBW = serv.getStateVariable("DownstreamMaxBitRate");
-
sb.append("WANPPPConnection");
- if(linkStatus != null)
- sb.append(" status: " + toString(linkStatus.getStateVariableData()));
- if(uptime != null)
- sb.append(" uptime: " + toString(uptime.getStateVariableData()));
- if(upstreamBW != null)
- sb.append(" upstream: " + toString(upstreamBW.getStateVariableData()));
- if(downstreamBW != null)
- sb.append(" downstream: " + toString(downstreamBW.getStateVariableData()) + "<br>");
+ sb.append(" status: " + toString("GetStatusInfo", "NewConnectionStatus", serv));
+ sb.append(" type: " + toString("GetConnectionTypeInfo", "NewConnectionType", serv));
+ sb.append(" upstream: " + toString("GetLinkLayerMaxBitRates", "NewUpstreamMaxBitRate", serv));
+ sb.append(" downstream: " + toString("GetLinkLayerMaxBitRates", "NewDownstreamMaxBitRate", serv) + "<br>");
+ sb.append(" external IP: " + toString("GetExternalIPAddress", "NewExternalIPAddress", serv) + "<br>");
}else if("urn:schemas-upnp-org:service:Layer3Forwarding:1".equals(serv.getServiceType())){
- StateVariable defaultConnectionService = serv.getStateVariable("DefaultConnectionService");
- if(defaultConnectionService != null)
- sb.append("DefaultConnectionService: " + toString(defaultConnectionService.getStateVariableData()));
+ sb.append("Layer3Forwarding");
+ sb.append("DefaultConnectionService: " + toString("GetDefaultConnectionService", "NewDefaultConnectionService", serv));
}else if(WAN_IP_CONNECTION.equals(serv.getServiceType())){
sb.append("WANIPConnection");
sb.append(" status: " + toString("GetStatusInfo", "NewConnectionStatus", serv));
sb.append(" type: " + toString("GetConnectionTypeInfo", "NewConnectionType", serv));
sb.append(" external IP: " + toString("GetExternalIPAddress", "NewExternalIPAddress", serv) + "<br>");
}else if("urn:schemas-upnp-org:service:WANEthernetLinkConfig:1".equals(serv.getServiceType())){
- StateVariable linkStatus = serv.getStateVariable("EthernetLinkStatus");
-
sb.append("WANEthernetLinkConfig");
- if(linkStatus != null)
- sb.append(" status: " + toString(linkStatus.getStateVariableData()) + "<br>");
- }else if("urn:schemas-upnp-org:service:LANHostConfigManagement:1".equals(serv.getServiceType())){
- StateVariable netmask = serv.getStateVariable("SubnetMask");
- StateVariable dnsServers = serv.getStateVariable("DNSServers");
-
- sb.append("LANHostConfigManagement");
- if(netmask != null)
- sb.append(" subnetMask: " + toString(netmask.getStateVariableData()));
- if(dnsServers != null)
- sb.append(" dnsServers: " + toString(dnsServers.getStateVariableData()) + "<br>");
+ sb.append(" status: " + toString("GetEthernetLinkStatus", "NewEthernetLinkStatus", serv) + "<br>");
}else
sb.append("~~~~~~~ "+serv.getServiceType());
listActions(serv, sb);
@@ -271,28 +310,96 @@
}
public String handleHTTPGet(HTTPRequest request) throws PluginHTTPException {
- StringBuffer sb = new StringBuffer();
- sb.append("<html><body>");
+ if(request.isParameterSet("getDeviceCapabilities")) {
+ final StringBuffer sb = new StringBuffer();
+ sb.append("<html><head><title>UPnP report</title></head><body>");
+ listSubDev("WANDevice", _router, sb);
+ sb.append("</body></html>");
+ return sb.toString();
+ }
- sb.append("<h2>Our current ip address is : " + getNATAddress() + "</h2>");
+ HTMLNode pageNode = pr.getPageMaker().getPageNode("UP&P plugin configuration page", false, null);
+ HTMLNode contentNode = pr.getPageMaker().getContentNode(pageNode);
- if(_router != null)
- listSubDev("WANDevice", _router, sb);
- else
- sb.append("No UPnP aware device has been found!");
+ if(isDisabled) {
+ HTMLNode disabledInfobox = contentNode.addChild("div", "class", "infobox infobox-error");
+ HTMLNode disabledInfoboxHeader = disabledInfobox.addChild("div", "class", "infobox-header");
+ HTMLNode disabledInfoboxContent = disabledInfobox.addChild("div", "class", "infobox-content");
- sb.append("</body></html>");
- return sb.toString();
+ disabledInfoboxHeader.addChild("#", "UP&P plugin report");
+ disabledInfoboxContent.addChild("#", "The plugin has been disabled; Do you have more than one UP&P IGD on your LAN ?");
+ return pageNode.generate();
+ } else if(!isNATPresent()) {
+ HTMLNode notFoundInfobox = contentNode.addChild("div", "class", "infobox infobox-warning");
+ HTMLNode notFoundInfoboxHeader = notFoundInfobox.addChild("div", "class", "infobox-header");
+ HTMLNode notFoundInfoboxContent = notFoundInfobox.addChild("div", "class", "infobox-content");
+
+ notFoundInfoboxHeader.addChild("#", "UP&P plugin report");
+ notFoundInfoboxContent.addChild("#", "The plugin hasn't found any UP&P aware, compatible device on your LAN.");
+ return pageNode.generate();
+ }
+
+ HTMLNode foundInfobox = contentNode.addChild("div", "class", "infobox infobox-normal");
+ HTMLNode foundInfoboxHeader = foundInfobox.addChild("div", "class", "infobox-header");
+ HTMLNode foundInfoboxContent = foundInfobox.addChild("div", "class", "infobox-content");
+
+ foundInfoboxHeader.addChild("#", "UP&P plugin report");
+ foundInfoboxContent.addChild("p", "The following device has been found : ").addChild("a", "href", "?getDeviceCapabilities").addChild("#", _router.getFriendlyName());
+ foundInfoboxContent.addChild("p", "Our current external ip address is : " + getNATAddress());
+ if(isPortForwarded)
+ foundInfoboxContent.addChild("p", "The plugin has managed to configure the port mapping correctly!");
+
+ return pageNode.generate();
}
- public String handleHTTPPost(HTTPRequest request)
- throws PluginHTTPException {
- // TODO Auto-generated method stub
+ public String handleHTTPPost(HTTPRequest request) throws PluginHTTPException {
return null;
}
public String handleHTTPPut(HTTPRequest request) throws PluginHTTPException {
- // TODO Auto-generated method stub
return null;
}
+
+ private boolean addMapping(String protocol, int port, String description) {
+ if(isDisabled || !isNATPresent())
+ return false;
+
+ // Just in case...
+ removeMapping(protocol, port);
+
+ Action add = _service.getAction("AddPortMapping");
+ if(add == null) {
+ Logger.error(this, "Couldn't find AddPortMapping action!");
+ return false;
+ }
+
+
+ add.setArgumentValue("NewRemoteHost", "");
+ add.setArgumentValue("NewExternalPort", port);
+ add.setArgumentValue("NewInternalClient", _router.getInterfaceAddress());
+ add.setArgumentValue("NewInternalPort", port);
+ add.setArgumentValue("NewProtocol", protocol);
+ add.setArgumentValue("NewPortMappingDescription", description);
+ add.setArgumentValue("NewEnabled","1");
+ add.setArgumentValue("NewLeaseDuration", 0);
+
+ return add.postControlAction();
+ }
+
+ private boolean removeMapping(String protocol, int port) {
+ if(isDisabled || !isNATPresent())
+ return false;
+
+ Action remove = _service.getAction("DeletePortMapping");
+ if(remove == null) {
+ Logger.error(this, "Couldn't find DeletePortMapping action!");
+ return false;
+ }
+
+ // remove.setArgumentValue("NewRemoteHost", "");
+ remove.setArgumentValue("NewExternalPort", port);
+ remove.setArgumentValue("NewProtocol", protocol);
+
+ return remove.postControlAction();
+ }
}
\ No newline at end of file
More information about the cvs
mailing list