Author: toad
Date: 2007-09-07 20:00:04 +0000 (Fri, 07 Sep 2007)
New Revision: 15013

Modified:
   trunk/plugins/UPnP/UPnP.java
Log:
UP&P: Support forwarding opennet, including enabling and disabling on the fly, 
using the new port forwarding plugin interface.
NOT TESTED!

Modified: trunk/plugins/UPnP/UPnP.java
===================================================================
--- trunk/plugins/UPnP/UPnP.java        2007-09-07 19:59:21 UTC (rev 15012)
+++ trunk/plugins/UPnP/UPnP.java        2007-09-07 20:00:04 UTC (rev 15013)
@@ -5,7 +5,10 @@

 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
+import java.util.Set;

 import plugins.UPnP.org.cybergarage.upnp.Action;
 import plugins.UPnP.org.cybergarage.upnp.ActionList;
@@ -20,9 +23,13 @@
 import plugins.UPnP.org.cybergarage.upnp.StateVariable;
 import plugins.UPnP.org.cybergarage.upnp.device.DeviceChangeListener;
 import freenet.pluginmanager.DetectedIP;
+import freenet.pluginmanager.ForwardPort;
+import freenet.pluginmanager.ForwardPortCallback;
+import freenet.pluginmanager.ForwardPortStatus;
 import freenet.pluginmanager.FredPlugin;
 import freenet.pluginmanager.FredPluginHTTP;
 import freenet.pluginmanager.FredPluginIPDetector;
+import freenet.pluginmanager.FredPluginPortForward;
 import freenet.pluginmanager.FredPluginThreadless;
 import freenet.pluginmanager.PluginHTTPException;
 import freenet.pluginmanager.PluginRespirator;
@@ -45,7 +52,7 @@
  * TODO: Advertise the node like the MDNS plugin does
  * TODO: Implement EventListener and react on ip-change
  */ 
-public class UPnP extends ControlPoint implements FredPluginHTTP, FredPlugin, 
FredPluginThreadless, FredPluginIPDetector, DeviceChangeListener {
+public class UPnP extends ControlPoint implements FredPluginHTTP, FredPlugin, 
FredPluginThreadless, FredPluginIPDetector, FredPluginPortForward, 
DeviceChangeListener {
        private PluginRespirator pr;

        /** some schemas */
@@ -59,8 +66,14 @@
        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;

+       /** List of ports we want to forward */
+       private Set/*<ForwardPort>*/ portsToForward;
+       /** List of ports we have actually forwarded */
+       private Set/*<ForwardPort>*/ portsForwarded;
+       /** Callback to call when a forward fails or succeeds */
+       private ForwardPortCallback forwardCallback;
+       
        public UPnP() {
                super();
                addDeviceChangeListener(this);
@@ -68,12 +81,11 @@

        public void runPlugin(PluginRespirator pr) {
                this.pr = pr;
-               this.fnpPortNumber = pr.getNode().getFNPPort();
                super.start();
        }

        public void terminate() {
-               unregisterPortMapping();
+               unregisterPortMappings();
                super.stop();
        }

@@ -133,12 +145,20 @@
                                isDisabled = true;
                                _router = null;
                        } else
-                               registerPortMapping();
+                               registerPortMappings();
                        // We have found the device we need: stop the listener 
thread
                        stop();
                }
        }

+       private void registerPortMappings() {
+               Set ports;
+               synchronized(this) {
+                       ports = portsToForward;
+               }
+               registerPorts(ports);
+       }
+
        /**
         * Traverses the structure of the router device looking for the port 
mapping service.
         */
@@ -163,16 +183,11 @@
                }
        }

-       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");
+       public boolean tryAddMapping(String protocol, int port, String 
description, ForwardPort fp) {
+               Logger.normal(this, "Registering a port mapping for " + port + 
"/" + protocol);
                int nbOfTries = 0;
                while(nbOfTries++ < 5) {
-                       isPortForwarded = addMapping("UDP", fnpPortNumber, 
"Freenet 0.7 FNP - " + _router.getInterfaceAddress());
+                       isPortForwarded = addMapping("UDP", port, "Freenet 0.7 
FNP - " + _router.getInterfaceAddress(), fp);
                        if(isPortForwarded)
                                break;
                        try {
@@ -180,16 +195,15 @@
                        } catch (InterruptedException e) {}
                }
                Logger.normal(this, (isPortForwarded ? "Mapping is successful!" 
: "Mapping has failed!") + " ("+ nbOfTries + " tries)");
+               return isPortForwarded;
        }

-       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 unregisterPortMappings() {
+               Set ports;
+               synchronized(this) {
+                       ports = portsForwarded;
+               }
+               this.unregisterPorts(ports);
        }

        public void deviceRemoved(Device dev ){
@@ -369,12 +383,12 @@
                return null;
        }

-       private boolean addMapping(String protocol, int port, String 
description) {
-               if(isDisabled || !isNATPresent())
+       private boolean addMapping(String protocol, int port, String 
description, ForwardPort fp) {
+               if(isDisabled || !isNATPresent() || _router == null)
                        return false;

                // Just in case...
-               removeMapping(protocol, port);
+               removeMapping(protocol, port, fp);

                Action add = _service.getAction("AddPortMapping");
                if(add == null) {
@@ -392,10 +406,15 @@
                add.setArgumentValue("NewEnabled","1");
                add.setArgumentValue("NewLeaseDuration", 0);

-               return add.postControlAction();
+               if(add.postControlAction()) {
+                       synchronized(this) {
+                               portsForwarded.add(fp);
+                       }
+                       return true;
+               } else return false;
        }

-       private boolean removeMapping(String protocol, int port) {
+       private boolean removeMapping(String protocol, int port, ForwardPort 
fp) {
                if(isDisabled || !isNATPresent())
                        return false;

@@ -409,6 +428,104 @@
                remove.setArgumentValue("NewExternalPort", port);
                remove.setArgumentValue("NewProtocol", protocol);

-               return remove.postControlAction();
+               boolean retval = remove.postControlAction();
+               synchronized(this) {
+                       portsForwarded.remove(fp);
+               }
+               return retval;
        }
+
+       public void onChangePublicPorts(Set ports, ForwardPortCallback cb) {
+               Set portsToDumpNow = null;
+               Set portsToForwardNow = null;
+               synchronized(lock) {
+                       if(forwardCallback != null && forwardCallback != cb && 
cb != null) {
+                               Logger.error(this, "ForwardPortCallback changed 
from "+forwardCallback+" to "+cb+" - using new value, but this is very 
strange!");
+                       }
+                       forwardCallback = cb;
+                       if(portsToForward == null || portsToForward.isEmpty()) {
+                               portsToForward = ports;
+                               portsToForwardNow = ports;
+                               portsToDumpNow = null;
+                       } else if(ports == null || ports.isEmpty()) {
+                               portsToDumpNow = portsToForward;
+                               portsToForward = ports;
+                               portsToForwardNow = null;
+                       } else {
+                               // Some ports to keep, some ports to dump
+                               // Ports in ports but not in portsToForwardNow 
we must forward
+                               // Ports in portsToForwardNow but not in ports 
we must dump
+                               for(Iterator i=ports.iterator();i.hasNext();) {
+                                       ForwardPort port = (ForwardPort) 
i.next();
+                                       if(portsToForward.contains(port)) {
+                                               // We have forwarded it, and it 
should be forwarded, cool.
+                                       } else {
+                                               // Needs forwarding
+                                               if(portsToForwardNow == null) 
portsToForwardNow = new HashSet();
+                                               portsToForwardNow.add(port);
+                                       }
+                               }
+                               for(Iterator 
i=portsToForward.iterator();i.hasNext();) {
+                                       ForwardPort port = (ForwardPort) 
i.next();
+                                       if(ports.contains(port)) {
+                                               // Should be forwarded, has 
been forwarded, cool.
+                                       } else {
+                                               // Needs dropping
+                                               if(portsToDumpNow == null) 
portsToDumpNow = new HashSet();
+                                               portsToDumpNow.add(port);
+                                       }
+                               }
+                               portsToForward = ports;
+                       }
+                       if(_router == null) return; // When one is found, we 
will do the forwards
+               }
+               if(portsToDumpNow != null)
+                       unregisterPorts(portsToDumpNow);
+               if(portsToForwardNow != null)
+                       registerPorts(portsToForwardNow);
+       }
+
+       private void registerPorts(Set portsToForwardNow) {
+               for(Iterator i=portsToForwardNow.iterator();i.hasNext();) {
+                       ForwardPort port = (ForwardPort) i.next();
+                       String proto;
+                       if(port.protocol == ForwardPort.PROTOCOL_UDP_IPV4)
+                               proto = "UDP";
+                       else if(port.protocol == ForwardPort.PROTOCOL_TCP_IPV4)
+                               proto = "TCP";
+                       else {
+                               HashMap map = new HashMap();
+                               map.put(port, new 
ForwardPortStatus(ForwardPortStatus.DEFINITE_FAILURE, "Protocol not supported", 
port.portNumber));
+                               forwardCallback.portForwardStatus(map);
+                               continue;
+                       }
+                       if(tryAddMapping(proto, port.portNumber, port.name, 
port)) {
+                               HashMap map = new HashMap();
+                               map.put(port, new 
ForwardPortStatus(ForwardPortStatus.MAYBE_SUCCESS, "Port apparently forwarded 
by UPnP", port.portNumber));
+                               forwardCallback.portForwardStatus(map);
+                               continue;
+                       } else {
+                               HashMap map = new HashMap();
+                               map.put(port, new 
ForwardPortStatus(ForwardPortStatus.PROBABLE_FAILURE, "UPnP port forwarding 
apparently failed", port.portNumber));
+                               forwardCallback.portForwardStatus(map);
+                               continue;
+                       }
+               }
+       }
+
+       private void unregisterPorts(Set portsToForwardNow) {
+               for(Iterator i=portsToForwardNow.iterator();i.hasNext();) {
+                       ForwardPort port = (ForwardPort) i.next();
+                       String proto;
+                       if(port.protocol == ForwardPort.PROTOCOL_UDP_IPV4)
+                               proto = "UDP";
+                       else if(port.protocol == ForwardPort.PROTOCOL_TCP_IPV4)
+                               proto = "TCP";
+                       else {
+                               // Ignore, we've already complained about it
+                               continue;
+                       }
+                       removeMapping(proto, port.portNumber, port);
+               }
+       }
 }
\ No newline at end of file


Reply via email to