UPnP port mapper using the Weupnp library (untested).

This commit is contained in:
akwizgran
2012-11-02 19:38:57 +00:00
parent ba07c00907
commit 0f04ea5496
9 changed files with 192 additions and 19 deletions

BIN
libs/weupnp-0.1.1.jar Normal file

Binary file not shown.

View File

@@ -1,13 +1,10 @@
package net.sf.briar; package net.sf.briar;
import static android.content.Context.MODE_PRIVATE;
import java.io.File; import java.io.File;
import net.sf.briar.api.crypto.Password; import net.sf.briar.api.crypto.Password;
import net.sf.briar.api.db.DatabaseConfig; import net.sf.briar.api.db.DatabaseConfig;
import net.sf.briar.api.ui.UiCallback; import net.sf.briar.api.ui.UiCallback;
import android.content.Context;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
@@ -16,7 +13,7 @@ public class HelloWorldModule extends AbstractModule {
private final DatabaseConfig config; private final DatabaseConfig config;
private final UiCallback callback; private final UiCallback callback;
public HelloWorldModule(final Context appContext) { public HelloWorldModule(final File dir) {
final Password password = new Password() { final Password password = new Password() {
public char[] getPassword() { public char[] getPassword() {
@@ -26,7 +23,7 @@ public class HelloWorldModule extends AbstractModule {
config = new DatabaseConfig() { config = new DatabaseConfig() {
public File getDataDirectory() { public File getDataDirectory() {
return appContext.getDir("db", MODE_PRIVATE); return dir;
} }
public Password getPassword() { public Password getPassword() {

View File

@@ -3,6 +3,7 @@ package net.sf.briar;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -55,8 +56,8 @@ public class HelloWorldService extends Service implements Runnable {
} }
public void run() { public void run() {
Injector i = Guice.createInjector( File dir = getApplicationContext().getDir("db", MODE_PRIVATE);
new HelloWorldModule(getApplicationContext()), Injector i = Guice.createInjector(new HelloWorldModule(dir),
new AndroidModule(), new ClockModule(), new CryptoModule(), new AndroidModule(), new ClockModule(), new CryptoModule(),
new DatabaseModule(), new LifecycleModule(), new DatabaseModule(), new LifecycleModule(),
new PluginsModule(), new ProtocolModule(), new PluginsModule(), new ProtocolModule(),
@@ -77,7 +78,7 @@ public class HelloWorldService extends Service implements Runnable {
LOG.info(pluginsStarted + " plugins started"); LOG.info(pluginsStarted + " plugins started");
// ...sleep... // ...sleep...
try { try {
Thread.sleep(1000); Thread.sleep(30 * 1000);
} catch(InterruptedException ignored) {} } catch(InterruptedException ignored) {}
// ...and stop // ...and stop
if(LOG.isLoggable(INFO)) LOG.info("Shutting down"); if(LOG.isLoggable(INFO)) LOG.info("Shutting down");

View File

@@ -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;
}
}

View File

@@ -0,0 +1,10 @@
package net.sf.briar.plugins.tcp;
interface PortMapper {
void start();
void stop();
MappingResult map(int port);
}

View File

@@ -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);
}
}

View File

@@ -91,10 +91,11 @@ abstract class TcpPlugin implements DuplexPlugin {
socket = ss; socket = ss;
} }
if(LOG.isLoggable(INFO)) { if(LOG.isLoggable(INFO)) {
LOG.info("Listening on " + ss.getInetAddress().getHostAddress() String addr = ss.getInetAddress().getHostAddress();
+ ":" + ss.getLocalPort()); int port = ss.getLocalPort();
LOG.info("Listening on " + addr + " " + port);
} }
setLocalSocketAddress(ss.getLocalSocketAddress()); setLocalSocketAddress((InetSocketAddress) ss.getLocalSocketAddress());
acceptContactConnections(ss); acceptContactConnections(ss);
} }
@@ -106,12 +107,11 @@ abstract class TcpPlugin implements DuplexPlugin {
} }
} }
private void setLocalSocketAddress(SocketAddress s) { protected void setLocalSocketAddress(InetSocketAddress a) {
InetSocketAddress i = (InetSocketAddress) s; InetAddress addr = a.getAddress();
InetAddress addr = i.getAddress();
TransportProperties p = new TransportProperties(); TransportProperties p = new TransportProperties();
p.put("address", addr.getHostAddress()); p.put("address", addr.getHostAddress());
p.put("port", String.valueOf(i.getPort())); p.put("port", String.valueOf(a.getPort()));
callback.mergeLocalProperties(p); callback.mergeLocalProperties(p);
} }

View File

@@ -2,6 +2,7 @@ package net.sf.briar.plugins.tcp;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.NetworkInterface; import java.net.NetworkInterface;
@@ -33,27 +34,54 @@ class WanTcpPlugin extends TcpPlugin {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(WanTcpPlugin.class.getName()); Logger.getLogger(WanTcpPlugin.class.getName());
private final PortMapper portMapper;
private volatile MappingResult mappingResult;
WanTcpPlugin(@PluginExecutor Executor pluginExecutor, WanTcpPlugin(@PluginExecutor Executor pluginExecutor,
DuplexPluginCallback callback, long pollingInterval) { DuplexPluginCallback callback, long pollingInterval,
PortMapper portMapper) {
super(pluginExecutor, callback, pollingInterval); super(pluginExecutor, callback, pollingInterval);
this.portMapper = portMapper;
} }
public TransportId getId() { public TransportId getId() {
return ID; 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 @Override
protected List<SocketAddress> getLocalSocketAddresses() { protected List<SocketAddress> getLocalSocketAddresses() {
List<SocketAddress> addrs = new ArrayList<SocketAddress>(); 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(); TransportProperties p = callback.getLocalProperties();
String addrString = p.get("address"); String addrString = p.get("address");
String portString = p.get("port"); String portString = p.get("port");
InetAddress addr = null; InetAddress addr = null;
int port = 0;
if(addrString != null && portString != null) { if(addrString != null && portString != null) {
try { try {
addr = InetAddress.getByName(addrString); addr = InetAddress.getByName(addrString);
int port = Integer.valueOf(portString); port = Integer.valueOf(portString);
addrs.add(new InetSocketAddress(addr, port)); addrs.add(new InetSocketAddress(addr, port));
addrs.add(new InetSocketAddress(addr, 0)); addrs.add(new InetSocketAddress(addr, 0));
} catch(NumberFormatException e) { } catch(NumberFormatException e) {
@@ -79,9 +107,31 @@ class WanTcpPlugin extends TcpPlugin {
if(!link && !site) addrs.add(new InetSocketAddress(a, 0)); 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; 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() { public boolean supportsInvitations() {
return false; return false;
} }

View File

@@ -16,6 +16,7 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(@PluginExecutor Executor pluginExecutor, public DuplexPlugin createPlugin(@PluginExecutor Executor pluginExecutor,
AndroidExecutor androidExecutor, Context appContext, AndroidExecutor androidExecutor, Context appContext,
DuplexPluginCallback callback) { DuplexPluginCallback callback) {
return new WanTcpPlugin(pluginExecutor, callback, POLLING_INTERVAL); return new WanTcpPlugin(pluginExecutor, callback, POLLING_INTERVAL,
new PortMapperImpl());
} }
} }