Skip to content
Snippets Groups Projects
Commit 0f04ea54 authored by akwizgran's avatar akwizgran
Browse files

UPnP port mapper using the Weupnp library (untested).

parent ba07c009
No related branches found
No related tags found
No related merge requests found
File added
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());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment