diff --git a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java index a03bd6155def5185110a1ca45bc705b012ec1f50..d67a6d22e2115ac19e6d60323380d6a3a89cd314 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java +++ b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java @@ -50,14 +50,9 @@ class LanTcpPlugin extends TcpPlugin { return Collections.emptyList(); } List<SocketAddress> addrs = new LinkedList<SocketAddress>(); - // Accept interfaces with local IPv4 addresses for(NetworkInterface iface : ifaces) { for(InetAddress a : Collections.list(iface.getInetAddresses())) { - boolean ipv4 = a instanceof Inet4Address; - boolean loop = a.isLoopbackAddress(); - boolean link = a.isLinkLocalAddress(); - boolean site = a.isSiteLocalAddress(); - if(ipv4 && !loop && (link || site)) { + if(isAcceptableAddress(a)) { // If this is the old address, try to use the same port if(old != null && old.getAddress().equals(a)) addrs.add(0, new InetSocketAddress(a, old.getPort())); @@ -67,4 +62,38 @@ class LanTcpPlugin extends TcpPlugin { } return addrs; } + + private boolean isAcceptableAddress(InetAddress a) { + // Accept link-local and site-local IPv4 addresses + boolean ipv4 = a instanceof Inet4Address; + boolean loop = a.isLoopbackAddress(); + boolean link = a.isLinkLocalAddress(); + boolean site = a.isSiteLocalAddress(); + return ipv4 && !loop && (link || site); + } + + @Override + protected boolean isConnectable(InetSocketAddress remote) { + if(remote.getPort() == 0) return false; + if(!isAcceptableAddress(remote.getAddress())) return false; + // Try to determine whether the address is on the same LAN as us + if(socket == null) return true; + byte[] localIp = socket.getInetAddress().getAddress(); + byte[] remoteIp = remote.getAddress().getAddress(); + return addressesAreOnSameLan(localIp, remoteIp); + } + + // Package access for testing + boolean addressesAreOnSameLan(byte[] localIp, byte[] remoteIp) { + // 10.0.0.0/8 + if(localIp[0] == 10) return remoteIp[0] == 10; + // 172.16.0.0/12 + if(localIp[0] == (byte) 172 && (localIp[1] & 0xF0) == 16) + return remoteIp[0] == (byte) 172 && (remoteIp[1] & 0xF0) == 16; + // 192.168.0.0/16 + if(localIp[0] == (byte) 192 && localIp[1] == (byte) 168) + return remoteIp[0] == (byte) 192 && remoteIp[1] == (byte) 168; + // Unrecognised prefix - may be compatible + return true; + } } \ No newline at end of file diff --git a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java index ce47e0c70640e27989c58bd2e16a4cbdc8462df5..827a5098868b6ab8bed17f6e4cb89f2dce209d79 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java +++ b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java @@ -12,7 +12,6 @@ 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; import java.util.regex.Pattern; @@ -46,6 +45,9 @@ abstract class TcpPlugin implements DuplexPlugin { */ protected abstract List<SocketAddress> getLocalSocketAddresses(); + /** Returns true if connections to the given address can be attempted. */ + protected abstract boolean isConnectable(InetSocketAddress remote); + protected TcpPlugin(Executor pluginExecutor, DuplexPluginCallback callback, int maxFrameLength, long maxLatency, long pollingInterval) { this.pluginExecutor = pluginExecutor; @@ -162,40 +164,40 @@ abstract class TcpPlugin implements DuplexPlugin { public void poll(Collection<ContactId> connected) { if(!isRunning()) return; - Map<ContactId, TransportProperties> remote = - callback.getRemoteProperties(); - for(final ContactId c : remote.keySet()) { - if(connected.contains(c)) continue; - pluginExecutor.execute(new Runnable() { - public void run() { - connectAndCallBack(c); - } - }); - } + for(ContactId c : callback.getRemoteProperties().keySet()) + if(!connected.contains(c)) connectAndCallBack(c); } - private void connectAndCallBack(ContactId c) { - DuplexTransportConnection d = createConnection(c); - if(d != null) callback.outgoingConnectionCreated(c, d); + private void connectAndCallBack(final ContactId c) { + pluginExecutor.execute(new Runnable() { + public void run() { + DuplexTransportConnection d = createConnection(c); + if(d != null) callback.outgoingConnectionCreated(c, d); + } + }); } public DuplexTransportConnection createConnection(ContactId c) { if(!isRunning()) return null; - SocketAddress addr = getRemoteSocketAddress(c); - if(addr == null) return null; + InetSocketAddress remote = getRemoteSocketAddress(c); + if(remote == null) return null; + if(!isConnectable(remote)) { + if(LOG.isLoggable(INFO)) LOG.info(remote + " is not connectable"); + return null; + } Socket s = new Socket(); try { - if(LOG.isLoggable(INFO)) LOG.info("Connecting to " + addr); - s.connect(addr); - if(LOG.isLoggable(INFO)) LOG.info("Connected to " + addr); + if(LOG.isLoggable(INFO)) LOG.info("Connecting to " + remote); + s.connect(remote); + if(LOG.isLoggable(INFO)) LOG.info("Connected to " + remote); return new TcpTransportConnection(this, s); } catch(IOException e) { - if(LOG.isLoggable(INFO)) LOG.info("Could not connect to " + addr); + if(LOG.isLoggable(INFO)) LOG.info("Could not connect to " + remote); return null; } } - private SocketAddress getRemoteSocketAddress(ContactId c) { + private InetSocketAddress getRemoteSocketAddress(ContactId c) { TransportProperties p = callback.getRemoteProperties().get(c); if(p == null) return null; return parseSocketAddress(p.get("address"), p.get("port")); diff --git a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java index 56f61ec239b0d88a2e6db33a5a6686994d5149f7..16c168c2e487c2e136a631dd20d58069da4fd7aa 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java +++ b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java @@ -56,14 +56,9 @@ class WanTcpPlugin extends TcpPlugin { return Collections.emptyList(); } List<SocketAddress> addrs = new LinkedList<SocketAddress>(); - // Accept interfaces without global IPv4 addresses for(NetworkInterface iface : ifaces) { for(InetAddress a : Collections.list(iface.getInetAddresses())) { - boolean ipv4 = a instanceof Inet4Address; - boolean loop = a.isLoopbackAddress(); - boolean link = a.isLinkLocalAddress(); - boolean site = a.isSiteLocalAddress(); - if(ipv4 && !loop && !link && !site) { + if(isAcceptableAddress(a)) { // If this is the old address, try to use the same port if(old != null && old.getAddress().equals(a)) addrs.add(0, new InetSocketAddress(a, old.getPort())); @@ -81,10 +76,25 @@ class WanTcpPlugin extends TcpPlugin { return addrs; } + private boolean isAcceptableAddress(InetAddress a) { + // Accept global IPv4 addresses + boolean ipv4 = a instanceof Inet4Address; + boolean loop = a.isLoopbackAddress(); + boolean link = a.isLinkLocalAddress(); + boolean site = a.isSiteLocalAddress(); + return ipv4 && !loop && !link && !site; + } + private int chooseEphemeralPort() { return 32768 + (int) (Math.random() * 32768); } + @Override + protected boolean isConnectable(InetSocketAddress remote) { + if(remote.getPort() == 0) return false; + return isAcceptableAddress(remote.getAddress()); + } + @Override protected void setLocalSocketAddress(InetSocketAddress a) { if(mappingResult != null && mappingResult.isUsable()) { diff --git a/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java b/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java index 268de984412548a014e810723772e7152750e4cd..03c2236035dfd55482077900787bd56c7c09a174 100644 --- a/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java +++ b/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java @@ -28,6 +28,49 @@ public class LanTcpPluginTest extends BriarTestCase { private final ContactId contactId = new ContactId(234); + @Test + public void testAddressesAreOnSameLan() { + LanTcpPlugin plugin = new LanTcpPlugin(null, null, 0, 0, 0); + // Local and remote in 10.0.0.0/8 should return true + assertTrue(plugin.addressesAreOnSameLan(makeAddress(10, 0, 0, 0), + makeAddress(10, 255, 255, 255))); + // Local and remote in 172.16.0.0/12 should return true + assertTrue(plugin.addressesAreOnSameLan(makeAddress(172, 16, 0, 0), + makeAddress(172, 31, 255, 255))); + // Local and remote in 192.168.0.0/16 should return true + assertTrue(plugin.addressesAreOnSameLan(makeAddress(192, 168, 0, 0), + makeAddress(192, 168, 255, 255))); + // Local and remote in different recognised prefixes should return false + assertFalse(plugin.addressesAreOnSameLan(makeAddress(10, 0, 0, 0), + makeAddress(172, 31, 255, 255))); + assertFalse(plugin.addressesAreOnSameLan(makeAddress(10, 0, 0, 0), + makeAddress(192, 168, 255, 255))); + assertFalse(plugin.addressesAreOnSameLan(makeAddress(172, 16, 0, 0), + makeAddress(10, 255, 255, 255))); + assertFalse(plugin.addressesAreOnSameLan(makeAddress(172, 16, 0, 0), + makeAddress(192, 168, 255, 255))); + assertFalse(plugin.addressesAreOnSameLan(makeAddress(192, 168, 0, 0), + makeAddress(10, 255, 255, 255))); + assertFalse(plugin.addressesAreOnSameLan(makeAddress(192, 168, 0, 0), + makeAddress(172, 31, 255, 255))); + // Remote prefix unrecognised should return false + assertFalse(plugin.addressesAreOnSameLan(makeAddress(10, 0, 0, 0), + makeAddress(1, 2, 3, 4))); + assertFalse(plugin.addressesAreOnSameLan(makeAddress(172, 16, 0, 0), + makeAddress(1, 2, 3, 4))); + assertFalse(plugin.addressesAreOnSameLan(makeAddress(192, 168, 0, 0), + makeAddress(1, 2, 3, 4))); + // Both prefixes unrecognised should return true (could be link-local) + assertTrue(plugin.addressesAreOnSameLan(makeAddress(1, 2, 3, 4), + makeAddress(5, 6, 7, 8))); + } + + private byte[] makeAddress(int... parts) { + byte[] b = new byte[parts.length]; + for(int i = 0; i < parts.length; i++) b[i] = (byte) parts[i]; + return b; + } + @Test public void testIncomingConnection() throws Exception { Callback callback = new Callback();