Don't try to connect to unreachable IP addresses.

This commit is contained in:
akwizgran
2014-04-04 23:49:53 +01:00
parent e3a8db6b7a
commit e74465dd41
4 changed files with 117 additions and 33 deletions

View File

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

View File

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

View File

@@ -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()) {

View File

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