Separated TCP plugin into LAN and WAN plugins and renamed package.

This commit is contained in:
akwizgran
2012-11-02 15:08:35 +00:00
parent 6515c54238
commit 031bb09f83
8 changed files with 279 additions and 202 deletions

View File

@@ -10,9 +10,9 @@ public interface PluginManager {
/**
* Starts the plugins and returns the number of plugins successfully
* started. This method must not be called until the database has been
* opened.
* opened. The appContext argument is null on non-Android platforms.
*/
int start(Context context);
int start(Context appContext);
/**
* Stops the plugins and returns the number of plugins successfully stopped.

View File

@@ -50,7 +50,8 @@ class PluginManagerImpl implements PluginManager {
private static final String[] ANDROID_DUPLEX_FACTORIES = new String[] {
"net.sf.briar.plugins.droidtooth.DroidtoothPluginFactory",
"net.sf.briar.plugins.socket.SimpleSocketPluginFactory"
"net.sf.briar.plugins.tcp.LanTcpPluginFactory",
"net.sf.briar.plugins.tcp.WanTcpPluginFactory"
};
private static final String[] J2SE_SIMPLEX_FACTORIES = new String[] {
@@ -59,7 +60,8 @@ class PluginManagerImpl implements PluginManager {
private static final String[] J2SE_DUPLEX_FACTORIES = new String[] {
"net.sf.briar.plugins.bluetooth.BluetoothPluginFactory",
"net.sf.briar.plugins.socket.SimpleSocketPluginFactory",
"net.sf.briar.plugins.tcp.LanSocketPluginFactory",
"net.sf.briar.plugins.tcp.WanSocketPluginFactory",
"net.sf.briar.plugins.tor.TorPluginFactory"
};

View File

@@ -1,154 +0,0 @@
package net.sf.briar.plugins.socket;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.crypto.PseudoRandom;
import net.sf.briar.api.plugins.PluginExecutor;
import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.util.StringUtils;
class SimpleSocketPlugin extends SocketPlugin {
public static final byte[] TRANSPORT_ID =
StringUtils.fromHexString("58c66d999e492b85065924acfd739d80"
+ "c65a62f87e5a4fc6c284f95908b9007d"
+ "512a93ebf89bf68f50a29e96eebf97b6");
private static final TransportId ID = new TransportId(TRANSPORT_ID);
private static final Logger LOG =
Logger.getLogger(SimpleSocketPlugin.class.getName());
SimpleSocketPlugin(@PluginExecutor Executor pluginExecutor,
DuplexPluginCallback callback, long pollingInterval) {
super(pluginExecutor, callback, pollingInterval);
}
public TransportId getId() {
return ID;
}
@Override
protected Socket createClientSocket() throws IOException {
assert running;
return new Socket();
}
@Override
protected ServerSocket createServerSocket() throws IOException {
assert running;
return new ServerSocket();
}
@Override
protected SocketAddress getLocalSocketAddress() {
SocketAddress addr = createSocketAddress(callback.getLocalProperties());
if(addr == null) {
try {
return new InetSocketAddress(chooseInterface(false), 0);
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
}
}
return addr;
}
protected InetAddress chooseInterface(boolean lan) throws IOException {
List<NetworkInterface> ifaces =
Collections.list(NetworkInterface.getNetworkInterfaces());
// Try to find an interface of the preferred type (LAN or WAN)
for(NetworkInterface iface : ifaces) {
for(InetAddress addr : Collections.list(iface.getInetAddresses())) {
if(!addr.isLoopbackAddress()) {
boolean link = addr.isLinkLocalAddress();
boolean site = addr.isSiteLocalAddress();
if(lan == (link || site)) {
if(LOG.isLoggable(INFO)) {
LOG.info("Choosing interface "
+ addr.getHostAddress());
}
return addr;
}
}
}
}
// Settle for an interface that's not of the preferred type
for(NetworkInterface iface : ifaces) {
for(InetAddress addr : Collections.list(iface.getInetAddresses())) {
if(!addr.isLoopbackAddress()) {
if(LOG.isLoggable(INFO)) {
LOG.info("Accepting interface "
+ addr.getHostAddress());
}
return addr;
}
}
}
throw new IOException("No suitable interfaces");
}
@Override
protected SocketAddress getRemoteSocketAddress(ContactId c) {
TransportProperties p = callback.getRemoteProperties().get(c);
return p == null ? null : createSocketAddress(p);
}
private SocketAddress createSocketAddress(TransportProperties p) {
assert p != null;
String host = p.get("external");
if(host == null) host = p.get("internal");
String portString = p.get("port");
if(host == null || portString == null) return null;
int port;
try {
port = Integer.valueOf(portString);
} catch(NumberFormatException e) {
return null;
}
return new InetSocketAddress(host, port);
}
@Override
protected void setLocalSocketAddress(SocketAddress s) {
if(!(s instanceof InetSocketAddress))
throw new IllegalArgumentException();
InetSocketAddress i = (InetSocketAddress) s;
InetAddress addr = i.getAddress();
TransportProperties p = new TransportProperties();
if(addr.isLinkLocalAddress() || addr.isSiteLocalAddress())
p.put("internal", addr.getHostAddress());
else p.put("external", addr.getHostAddress());
p.put("port", String.valueOf(i.getPort()));
callback.mergeLocalProperties(p);
}
public boolean supportsInvitations() {
return false;
}
public DuplexTransportConnection sendInvitation(PseudoRandom r,
long timeout) {
throw new UnsupportedOperationException();
}
public DuplexTransportConnection acceptInvitation(PseudoRandom r,
long timeout) {
throw new UnsupportedOperationException();
}
}

View File

@@ -1,4 +1,4 @@
package net.sf.briar.plugins.socket;
package net.sf.briar.plugins.tcp;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
@@ -8,36 +8,103 @@ import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.crypto.PseudoRandom;
import net.sf.briar.api.plugins.PluginExecutor;
import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.util.ByteUtils;
import net.sf.briar.util.StringUtils;
/** A socket plugin that supports exchanging invitations over a LAN. */
class LanSocketPlugin extends SimpleSocketPlugin {
class LanTcpPlugin extends TcpPlugin {
public static final byte[] TRANSPORT_ID =
StringUtils.fromHexString("0d79357fd7f74d66c2f6f6ad0f7fff81"
+ "d21c53a43b90b0507ed0683872d8e2fc"
+ "5a88e8f953638228dc26669639757bbf");
private static final TransportId ID = new TransportId(TRANSPORT_ID);
private static final Logger LOG =
Logger.getLogger(LanSocketPlugin.class.getName());
Logger.getLogger(LanTcpPlugin.class.getName());
LanSocketPlugin(@PluginExecutor Executor pluginExecutor,
LanTcpPlugin(@PluginExecutor Executor pluginExecutor,
DuplexPluginCallback callback, long pollingInterval) {
super(pluginExecutor, callback, pollingInterval);
}
public TransportId getId() {
return ID;
}
@Override
protected List<SocketAddress> getLocalSocketAddresses() {
List<SocketAddress> addrs = new ArrayList<SocketAddress>();
// Prefer a previously used address and port if available
TransportProperties p = callback.getLocalProperties();
String addrString = p.get("address");
String portString = p.get("port");
InetAddress addr = null;
if(addrString != null && portString != null) {
try {
addr = InetAddress.getByName(addrString);
int port = Integer.valueOf(portString);
addrs.add(new InetSocketAddress(addr, port));
addrs.add(new InetSocketAddress(addr, 0));
} catch(NumberFormatException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
} catch(UnknownHostException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
}
}
List<NetworkInterface> ifaces;
try {
ifaces = Collections.list(NetworkInterface.getNetworkInterfaces());
} catch(SocketException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
return addrs;
}
// Prefer interfaces with link-local or site-local addresses
for(NetworkInterface iface : ifaces) {
for(InetAddress a : Collections.list(iface.getInetAddresses())) {
if(addr != null && a.equals(addr)) continue;
if(a.isLoopbackAddress()) continue;
boolean link = a.isLinkLocalAddress();
boolean site = a.isSiteLocalAddress();
if(link || site) addrs.add(new InetSocketAddress(a, 0));
}
}
// Accept interfaces without link-local or site-local addresses
for(NetworkInterface iface : ifaces) {
for(InetAddress a : Collections.list(iface.getInetAddresses())) {
if(addr != null && a.equals(addr)) continue;
if(a.isLoopbackAddress()) continue;
boolean link = a.isLinkLocalAddress();
boolean site = a.isSiteLocalAddress();
if(!link && !site) addrs.add(new InetSocketAddress(a, 0));
}
}
return addrs;
}
public boolean supportsInvitations() {
return true;
}
@Override
public DuplexTransportConnection sendInvitation(PseudoRandom r,
long timeout) {
synchronized(this) {
@@ -48,7 +115,7 @@ class LanSocketPlugin extends SimpleSocketPlugin {
// Bind a multicast socket for receiving packets
MulticastSocket ms = null;
try {
InetAddress iface = chooseInterface(true);
InetAddress iface = chooseInterface();
ms = new MulticastSocket(mcast.getPort());
ms.setInterface(iface);
ms.joinGroup(mcast.getAddress());
@@ -75,7 +142,7 @@ class LanSocketPlugin extends SimpleSocketPlugin {
try {
// Connect back on the advertised TCP port
Socket s = new Socket(packet.getAddress(), port);
return new SocketTransportConnection(s);
return new TcpTransportConnection(s);
} catch(IOException e) {
if(LOG.isLoggable(WARNING))
LOG.warning(e.toString());
@@ -99,6 +166,35 @@ class LanSocketPlugin extends SimpleSocketPlugin {
return null;
}
private InetAddress chooseInterface() throws IOException {
List<NetworkInterface> ifaces =
Collections.list(NetworkInterface.getNetworkInterfaces());
// Prefer an interface with a link-local or site-local address
for(NetworkInterface iface : ifaces) {
for(InetAddress addr : Collections.list(iface.getInetAddresses())) {
if(addr.isLoopbackAddress()) continue;
boolean link = addr.isLinkLocalAddress();
boolean site = addr.isSiteLocalAddress();
if(link || site) {
if(LOG.isLoggable(INFO))
LOG.info("Preferring " + addr.getHostAddress());
return addr;
}
}
}
// Accept an interface without a link-local or site-local address
for(NetworkInterface iface : ifaces) {
for(InetAddress addr : Collections.list(iface.getInetAddresses())) {
if(addr.isLoopbackAddress()) continue;
if(LOG.isLoggable(INFO))
LOG.info("Accepting " + addr.getHostAddress());
return addr;
}
}
// No suitable interfaces
return null;
}
private void tryToClose(MulticastSocket ms, InetAddress addr) {
try {
ms.leaveGroup(addr);
@@ -139,7 +235,6 @@ class LanSocketPlugin extends SimpleSocketPlugin {
return ByteUtils.readUint16(b, off);
}
@Override
public DuplexTransportConnection acceptInvitation(PseudoRandom r,
long timeout) {
synchronized(this) {
@@ -150,7 +245,7 @@ class LanSocketPlugin extends SimpleSocketPlugin {
// Bind a TCP socket for receiving connections
ServerSocket ss = null;
try {
InetAddress iface = chooseInterface(true);
InetAddress iface = chooseInterface();
ss = new ServerSocket();
ss.bind(new InetSocketAddress(iface, 0));
} catch(IOException e) {
@@ -161,7 +256,7 @@ class LanSocketPlugin extends SimpleSocketPlugin {
// Bind a multicast socket for sending packets
MulticastSocket ms = null;
try {
InetAddress iface = chooseInterface(true);
InetAddress iface = chooseInterface();
ms = new MulticastSocket();
ms.setInterface(iface);
} catch(IOException e) {
@@ -185,7 +280,7 @@ class LanSocketPlugin extends SimpleSocketPlugin {
try {
int wait = (int) (Math.min(end, nextPacket) - now);
ss.setSoTimeout(wait < 1 ? 1 : wait);
return new SocketTransportConnection(ss.accept());
return new TcpTransportConnection(ss.accept());
} catch(SocketTimeoutException e) {
now = System.currentTimeMillis();
if(now < end) {

View File

@@ -1,13 +1,17 @@
package net.sf.briar.plugins.socket;
package net.sf.briar.plugins.tcp;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -19,10 +23,10 @@ import net.sf.briar.api.plugins.duplex.DuplexPlugin;
import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
abstract class SocketPlugin implements DuplexPlugin {
abstract class TcpPlugin implements DuplexPlugin {
private static final Logger LOG =
Logger.getLogger(SocketPlugin.class.getName());
Logger.getLogger(TcpPlugin.class.getName());
protected final Executor pluginExecutor;
protected final DuplexPluginCallback callback;
@@ -30,16 +34,15 @@ abstract class SocketPlugin implements DuplexPlugin {
private final long pollingInterval;
protected boolean running = false; // Locking: this
protected ServerSocket socket = null; // Locking: this
private ServerSocket socket = null; // Locking: this
protected abstract void setLocalSocketAddress(SocketAddress s);
/**
* Returns zero or more socket addresses on which the plugin should listen,
* in order of preference. At most one of the addresses will be bound.
*/
protected abstract List<SocketAddress> getLocalSocketAddresses();
protected abstract Socket createClientSocket() throws IOException;
protected abstract ServerSocket createServerSocket() throws IOException;
protected abstract SocketAddress getLocalSocketAddress();
protected abstract SocketAddress getRemoteSocketAddress(ContactId c);
protected SocketPlugin(@PluginExecutor Executor pluginExecutor,
protected TcpPlugin(@PluginExecutor Executor pluginExecutor,
DuplexPluginCallback callback, long pollingInterval) {
this.pluginExecutor = pluginExecutor;
this.callback = callback;
@@ -58,21 +61,27 @@ abstract class SocketPlugin implements DuplexPlugin {
}
private void bind() {
SocketAddress addr;
ServerSocket ss = null;
ServerSocket ss;
try {
addr = getLocalSocketAddress();
ss = createServerSocket();
ss = new ServerSocket();
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
return;
}
if(addr == null || ss == null) return;
try {
ss.bind(addr);
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
tryToClose(ss);
boolean found = false;
for(SocketAddress addr : getLocalSocketAddresses()) {
try {
ss.bind(addr);
found = true;
break;
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
tryToClose(ss);
continue;
}
}
if(!found) {
if(LOG.isLoggable(INFO)) LOG.info("Could not bind server socket");
return;
}
synchronized(this) {
@@ -98,6 +107,15 @@ abstract class SocketPlugin implements DuplexPlugin {
}
}
private void setLocalSocketAddress(SocketAddress s) {
InetSocketAddress i = (InetSocketAddress) s;
InetAddress addr = i.getAddress();
TransportProperties p = new TransportProperties();
p.put("address", addr.getHostAddress());
p.put("port", String.valueOf(i.getPort()));
callback.mergeLocalProperties(p);
}
private void acceptContactConnections(ServerSocket ss) {
while(true) {
Socket s;
@@ -109,7 +127,7 @@ abstract class SocketPlugin implements DuplexPlugin {
tryToClose(ss);
return;
}
SocketTransportConnection conn = new SocketTransportConnection(s);
TcpTransportConnection conn = new TcpTransportConnection(s);
callback.incomingConnectionCreated(conn);
synchronized(this) {
if(!running) return;
@@ -138,7 +156,7 @@ abstract class SocketPlugin implements DuplexPlugin {
if(!running) return;
}
Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties();
callback.getRemoteProperties();
for(final ContactId c : remote.keySet()) {
if(connected.contains(c)) continue;
pluginExecutor.execute(new Runnable() {
@@ -160,13 +178,32 @@ abstract class SocketPlugin implements DuplexPlugin {
}
SocketAddress addr = getRemoteSocketAddress(c);
try {
Socket s = createClientSocket();
Socket s = new Socket();
if(addr == null || s == null) return null;
s.connect(addr);
return new SocketTransportConnection(s);
return new TcpTransportConnection(s);
} catch(IOException e) {
if(LOG.isLoggable(INFO)) LOG.info(e.toString());
return null;
}
}
private SocketAddress getRemoteSocketAddress(ContactId c) {
TransportProperties p = callback.getRemoteProperties().get(c);
if(p == null) return null;
String addrString = p.get("address");
String portString = p.get("port");
if(addrString != null && portString != null) {
try {
InetAddress addr = InetAddress.getByName(addrString);
int port = Integer.valueOf(portString);
return new InetSocketAddress(addr, port);
} catch(NumberFormatException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
} catch(UnknownHostException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
}
}
return null;
}
}

View File

@@ -1,4 +1,4 @@
package net.sf.briar.plugins.socket;
package net.sf.briar.plugins.tcp;
import java.io.IOException;
import java.io.InputStream;
@@ -7,11 +7,11 @@ import java.net.Socket;
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
class SocketTransportConnection implements DuplexTransportConnection {
class TcpTransportConnection implements DuplexTransportConnection {
private final Socket socket;
SocketTransportConnection(Socket socket) {
TcpTransportConnection(Socket socket) {
this.socket = socket;
}

View File

@@ -0,0 +1,98 @@
package net.sf.briar.plugins.tcp;
import static java.util.logging.Level.WARNING;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.crypto.PseudoRandom;
import net.sf.briar.api.plugins.PluginExecutor;
import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.util.StringUtils;
class WanTcpPlugin extends TcpPlugin {
public static final byte[] TRANSPORT_ID =
StringUtils.fromHexString("58c66d999e492b85065924acfd739d80"
+ "c65a62f87e5a4fc6c284f95908b9007d"
+ "512a93ebf89bf68f50a29e96eebf97b6");
private static final TransportId ID = new TransportId(TRANSPORT_ID);
private static final Logger LOG =
Logger.getLogger(WanTcpPlugin.class.getName());
WanTcpPlugin(@PluginExecutor Executor pluginExecutor,
DuplexPluginCallback callback, long pollingInterval) {
super(pluginExecutor, callback, pollingInterval);
}
public TransportId getId() {
return ID;
}
@Override
protected List<SocketAddress> getLocalSocketAddresses() {
List<SocketAddress> addrs = new ArrayList<SocketAddress>();
// Prefer a previously used address and port if available
TransportProperties p = callback.getLocalProperties();
String addrString = p.get("address");
String portString = p.get("port");
InetAddress addr = null;
if(addrString != null && portString != null) {
try {
addr = InetAddress.getByName(addrString);
int port = Integer.valueOf(portString);
addrs.add(new InetSocketAddress(addr, port));
addrs.add(new InetSocketAddress(addr, 0));
} catch(NumberFormatException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
} catch(UnknownHostException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
}
}
List<NetworkInterface> ifaces;
try {
ifaces = Collections.list(NetworkInterface.getNetworkInterfaces());
} catch(SocketException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
return addrs;
}
// Accept interfaces without link-local or site-local addresses
for(NetworkInterface iface : ifaces) {
for(InetAddress a : Collections.list(iface.getInetAddresses())) {
if(addr != null && a.equals(addr)) continue;
if(a.isLoopbackAddress()) continue;
boolean link = a.isLinkLocalAddress();
boolean site = a.isSiteLocalAddress();
if(!link && !site) addrs.add(new InetSocketAddress(a, 0));
}
}
return addrs;
}
public boolean supportsInvitations() {
return false;
}
public DuplexTransportConnection sendInvitation(PseudoRandom r,
long timeout) {
throw new UnsupportedOperationException();
}
public DuplexTransportConnection acceptInvitation(PseudoRandom r,
long timeout) {
throw new UnsupportedOperationException();
}
}

View File

@@ -1,4 +1,4 @@
package net.sf.briar.plugins.socket;
package net.sf.briar.plugins.tcp;
import java.util.concurrent.Executor;
@@ -9,14 +9,13 @@ import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
import net.sf.briar.api.plugins.duplex.DuplexPluginFactory;
import android.content.Context;
public class SimpleSocketPluginFactory implements DuplexPluginFactory {
public class WanTcpPluginFactory implements DuplexPluginFactory {
private static final long POLLING_INTERVAL = 5L * 60L * 1000L; // 5 mins
private static final long POLLING_INTERVAL = 5L * 60L * 1000L; // 5 minutes
public DuplexPlugin createPlugin(@PluginExecutor Executor pluginExecutor,
AndroidExecutor androidExecutor, Context appContext,
DuplexPluginCallback callback) {
return new SimpleSocketPlugin(pluginExecutor, callback,
POLLING_INTERVAL);
return new WanTcpPlugin(pluginExecutor, callback, POLLING_INTERVAL);
}
}