Commit 0f04ea54 authored by akwizgran's avatar akwizgran

UPnP port mapper using the Weupnp library (untested).

parent ba07c009
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() {
......
......@@ -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");
......
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;
}
}
package net.sf.briar.plugins.tcp;
interface PortMapper {
void start();
void stop();
MappingResult map(int port);
}
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);
}
}
......@@ -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);
}
......
......@@ -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;
}
......
......@@ -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());
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment