diff --git a/libs/weupnp-0.1.1.jar b/libs/weupnp-0.1.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..2626a76c0284e8ca9e42bf5ce111ba1afb7527ab Binary files /dev/null and b/libs/weupnp-0.1.1.jar differ diff --git a/src/net/sf/briar/HelloWorldModule.java b/src/net/sf/briar/HelloWorldModule.java index 64a3d8fcf23f49682445f87c0a3af7ca3a11c18d..a0fa22c7f89a6b65cc1874c32ff2d50b61de3f39 100644 --- a/src/net/sf/briar/HelloWorldModule.java +++ b/src/net/sf/briar/HelloWorldModule.java @@ -1,13 +1,10 @@ package net.sf.briar; -import static android.content.Context.MODE_PRIVATE; - import java.io.File; import net.sf.briar.api.crypto.Password; import net.sf.briar.api.db.DatabaseConfig; import net.sf.briar.api.ui.UiCallback; -import android.content.Context; import com.google.inject.AbstractModule; @@ -16,7 +13,7 @@ public class HelloWorldModule extends AbstractModule { private final DatabaseConfig config; private final UiCallback callback; - public HelloWorldModule(final Context appContext) { + public HelloWorldModule(final File dir) { final Password password = new Password() { public char[] getPassword() { @@ -26,7 +23,7 @@ public class HelloWorldModule extends AbstractModule { config = new DatabaseConfig() { public File getDataDirectory() { - return appContext.getDir("db", MODE_PRIVATE); + return dir; } public Password getPassword() { diff --git a/src/net/sf/briar/HelloWorldService.java b/src/net/sf/briar/HelloWorldService.java index 2eec307890beedfa8c3fc8844a5d883b5bb1ba49..6dbbb30790aa9c003cd122fe9d814384ca4344b9 100644 --- a/src/net/sf/briar/HelloWorldService.java +++ b/src/net/sf/briar/HelloWorldService.java @@ -3,6 +3,7 @@ package net.sf.briar; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; +import java.io.File; import java.io.IOException; import java.util.logging.Logger; @@ -55,8 +56,8 @@ public class HelloWorldService extends Service implements Runnable { } public void run() { - Injector i = Guice.createInjector( - new HelloWorldModule(getApplicationContext()), + File dir = getApplicationContext().getDir("db", MODE_PRIVATE); + Injector i = Guice.createInjector(new HelloWorldModule(dir), new AndroidModule(), new ClockModule(), new CryptoModule(), new DatabaseModule(), new LifecycleModule(), new PluginsModule(), new ProtocolModule(), @@ -77,7 +78,7 @@ public class HelloWorldService extends Service implements Runnable { LOG.info(pluginsStarted + " plugins started"); // ...sleep... try { - Thread.sleep(1000); + Thread.sleep(30 * 1000); } catch(InterruptedException ignored) {} // ...and stop if(LOG.isLoggable(INFO)) LOG.info("Shutting down"); diff --git a/src/net/sf/briar/plugins/tcp/MappingResult.java b/src/net/sf/briar/plugins/tcp/MappingResult.java new file mode 100644 index 0000000000000000000000000000000000000000..f558a8d731f45d162900485e9ba8253a5a896872 --- /dev/null +++ b/src/net/sf/briar/plugins/tcp/MappingResult.java @@ -0,0 +1,32 @@ +package net.sf.briar.plugins.tcp; + +import java.net.InetAddress; + +class MappingResult { + + private final InetAddress internal, external; + private final boolean succeeded; + + MappingResult(InetAddress internal, InetAddress external, + boolean succeeded) { + this.internal = internal; + this.external = external; + this.succeeded = succeeded; + } + + InetAddress getInternal() { + return internal; + } + + InetAddress getExternal() { + return external; + } + + boolean getSucceeded() { + return succeeded; + } + + boolean isUsable() { + return internal != null && external != null && succeeded; + } +} diff --git a/src/net/sf/briar/plugins/tcp/PortMapper.java b/src/net/sf/briar/plugins/tcp/PortMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..5bd226329df1e9496bf87d35e97cfc2a9376a0a6 --- /dev/null +++ b/src/net/sf/briar/plugins/tcp/PortMapper.java @@ -0,0 +1,10 @@ +package net.sf.briar.plugins.tcp; + +interface PortMapper { + + void start(); + + void stop(); + + MappingResult map(int port); +} diff --git a/src/net/sf/briar/plugins/tcp/PortMapperImpl.java b/src/net/sf/briar/plugins/tcp/PortMapperImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..e990cb2e38eb1e90c3301dcac142400821b089ec --- /dev/null +++ b/src/net/sf/briar/plugins/tcp/PortMapperImpl.java @@ -0,0 +1,82 @@ +package net.sf.briar.plugins.tcp; + +import static java.util.logging.Level.WARNING; + +import java.io.IOException; +import java.net.InetAddress; +import java.util.Collection; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.logging.Logger; + +import javax.xml.parsers.ParserConfigurationException; + +import org.wetorrent.upnp.GatewayDevice; +import org.wetorrent.upnp.GatewayDiscover; +import org.xml.sax.SAXException; + +class PortMapperImpl implements PortMapper { + + private static final Logger LOG = + Logger.getLogger(PortMapperImpl.class.getName()); + + private final CountDownLatch started = new CountDownLatch(1); + private final Collection<Integer> ports = + new CopyOnWriteArrayList<Integer>(); + + private volatile GatewayDevice gateway = null; + + public void start() { + GatewayDiscover d = new GatewayDiscover(); + try { + d.discover(); + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) LOG.warning(e.toString()); + } catch(SAXException e) { + if(LOG.isLoggable(WARNING)) LOG.warning(e.toString()); + } catch(ParserConfigurationException e) { + if(LOG.isLoggable(WARNING)) LOG.warning(e.toString()); + } + gateway = d.getValidGateway(); + started.countDown(); + } + + public void stop() { + if(gateway == null) return; + try { + for(Integer port: ports) gateway.deletePortMapping(port, "TCP"); + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) LOG.warning(e.toString()); + } catch(SAXException e) { + if(LOG.isLoggable(WARNING)) LOG.warning(e.toString()); + } + } + + public MappingResult map(int port) { + try { + started.await(); + } catch(InterruptedException e) { + if(LOG.isLoggable(WARNING)) LOG.warning(e.toString()); + Thread.currentThread().interrupt(); + return null; + } + if(gateway == null) return null; + InetAddress internal = gateway.getLocalAddress(); + if(internal == null) return null; + boolean succeeded = false; + InetAddress external = null; + try { + succeeded = gateway.addPortMapping(port, port, + internal.getHostAddress(), "TCP", "TCP"); + String externalString = gateway.getExternalIPAddress(); + if(externalString != null) + external = InetAddress.getByName(externalString); + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) LOG.warning(e.toString()); + } catch(SAXException e) { + if(LOG.isLoggable(WARNING)) LOG.warning(e.toString()); + } + if(succeeded) ports.add(port); + return new MappingResult(internal, external, succeeded); + } +} diff --git a/src/net/sf/briar/plugins/tcp/TcpPlugin.java b/src/net/sf/briar/plugins/tcp/TcpPlugin.java index 603cba44e1a171da32005aad5d89679b79842393..f476aad47b60139d81e53d3453390385f82b523a 100644 --- a/src/net/sf/briar/plugins/tcp/TcpPlugin.java +++ b/src/net/sf/briar/plugins/tcp/TcpPlugin.java @@ -91,10 +91,11 @@ abstract class TcpPlugin implements DuplexPlugin { socket = ss; } if(LOG.isLoggable(INFO)) { - LOG.info("Listening on " + ss.getInetAddress().getHostAddress() - + ":" + ss.getLocalPort()); + String addr = ss.getInetAddress().getHostAddress(); + int port = ss.getLocalPort(); + LOG.info("Listening on " + addr + " " + port); } - setLocalSocketAddress(ss.getLocalSocketAddress()); + setLocalSocketAddress((InetSocketAddress) ss.getLocalSocketAddress()); acceptContactConnections(ss); } @@ -106,12 +107,11 @@ abstract class TcpPlugin implements DuplexPlugin { } } - private void setLocalSocketAddress(SocketAddress s) { - InetSocketAddress i = (InetSocketAddress) s; - InetAddress addr = i.getAddress(); + protected void setLocalSocketAddress(InetSocketAddress a) { + InetAddress addr = a.getAddress(); TransportProperties p = new TransportProperties(); p.put("address", addr.getHostAddress()); - p.put("port", String.valueOf(i.getPort())); + p.put("port", String.valueOf(a.getPort())); callback.mergeLocalProperties(p); } diff --git a/src/net/sf/briar/plugins/tcp/WanTcpPlugin.java b/src/net/sf/briar/plugins/tcp/WanTcpPlugin.java index 69bd3074e0deb8584a71e812f11d8e2b2e926e89..9b2cd234fa938d2ca74e47d024c97dfe30590438 100644 --- a/src/net/sf/briar/plugins/tcp/WanTcpPlugin.java +++ b/src/net/sf/briar/plugins/tcp/WanTcpPlugin.java @@ -2,6 +2,7 @@ package net.sf.briar.plugins.tcp; import static java.util.logging.Level.WARNING; +import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; @@ -33,27 +34,54 @@ class WanTcpPlugin extends TcpPlugin { private static final Logger LOG = Logger.getLogger(WanTcpPlugin.class.getName()); + private final PortMapper portMapper; + + private volatile MappingResult mappingResult; + WanTcpPlugin(@PluginExecutor Executor pluginExecutor, - DuplexPluginCallback callback, long pollingInterval) { + DuplexPluginCallback callback, long pollingInterval, + PortMapper portMapper) { super(pluginExecutor, callback, pollingInterval); + this.portMapper = portMapper; } public TransportId getId() { return ID; } + @Override + public void start() throws IOException { + super.start(); + pluginExecutor.execute(new Runnable() { + public void run() { + portMapper.start(); + } + }); + } + + @Override + public void stop() throws IOException { + super.stop(); + pluginExecutor.execute(new Runnable() { + public void run() { + portMapper.stop(); + } + }); + } + @Override protected List<SocketAddress> getLocalSocketAddresses() { List<SocketAddress> addrs = new ArrayList<SocketAddress>(); - // Prefer a previously used address and port if available + // Prefer a previously used external address and port if available TransportProperties p = callback.getLocalProperties(); String addrString = p.get("address"); String portString = p.get("port"); InetAddress addr = null; + int port = 0; if(addrString != null && portString != null) { try { addr = InetAddress.getByName(addrString); - int port = Integer.valueOf(portString); + port = Integer.valueOf(portString); addrs.add(new InetSocketAddress(addr, port)); addrs.add(new InetSocketAddress(addr, 0)); } catch(NumberFormatException e) { @@ -79,9 +107,31 @@ class WanTcpPlugin extends TcpPlugin { if(!link && !site) addrs.add(new InetSocketAddress(a, 0)); } } + // Accept interfaces that can be port-mapped + if(port == 0) port = chooseEphemeralPort(); + mappingResult = portMapper.map(port); + if(mappingResult != null && mappingResult.isUsable()) + addrs.add(new InetSocketAddress(mappingResult.getInternal(), port)); return addrs; } + private int chooseEphemeralPort() { + return 32768 + (int) (Math.random() * 32768); + } + + @Override + protected void setLocalSocketAddress(InetSocketAddress a) { + InetAddress addr = a.getAddress(); + if(mappingResult != null && mappingResult.isUsable()) { + if(addr.equals(mappingResult.getInternal())) + addr = mappingResult.getExternal(); + } + TransportProperties p = new TransportProperties(); + p.put("address", addr.getHostAddress()); + p.put("port", String.valueOf(a.getPort())); + callback.mergeLocalProperties(p); + } + public boolean supportsInvitations() { return false; } diff --git a/src/net/sf/briar/plugins/tcp/WanTcpPluginFactory.java b/src/net/sf/briar/plugins/tcp/WanTcpPluginFactory.java index e4ed87284fb30e8bed42e7305e44852fae56ff68..f976e22cf531e5a00d370d3227f47d67a0b08857 100644 --- a/src/net/sf/briar/plugins/tcp/WanTcpPluginFactory.java +++ b/src/net/sf/briar/plugins/tcp/WanTcpPluginFactory.java @@ -16,6 +16,7 @@ public class WanTcpPluginFactory implements DuplexPluginFactory { public DuplexPlugin createPlugin(@PluginExecutor Executor pluginExecutor, AndroidExecutor androidExecutor, Context appContext, DuplexPluginCallback callback) { - return new WanTcpPlugin(pluginExecutor, callback, POLLING_INTERVAL); + return new WanTcpPlugin(pluginExecutor, callback, POLLING_INTERVAL, + new PortMapperImpl()); } }