mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Merge branch '302-lan-plugin-privacy' into 'master'
Store a fixed number of recent IP addresses, padded with fakes Closes #302 See merge request !159
This commit is contained in:
@@ -1,14 +1,19 @@
|
||||
package org.briarproject.plugins.tcp;
|
||||
|
||||
import org.briarproject.api.TransportId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.plugins.Backoff;
|
||||
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
|
||||
import org.briarproject.api.properties.TransportProperties;
|
||||
import org.briarproject.api.settings.Settings;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -17,6 +22,10 @@ class LanTcpPlugin extends TcpPlugin {
|
||||
|
||||
static final TransportId ID = new TransportId("lan");
|
||||
|
||||
private static final int MAX_ADDRESSES = 5;
|
||||
private static final String PROP_IP_PORTS = "ipPorts";
|
||||
private static final String SEPARATOR = ",";
|
||||
|
||||
LanTcpPlugin(Executor ioExecutor, Backoff backoff,
|
||||
DuplexPluginCallback callback, int maxLatency, int maxIdleTime) {
|
||||
super(ioExecutor, backoff, callback, maxLatency, maxIdleTime);
|
||||
@@ -30,18 +39,74 @@ class LanTcpPlugin extends TcpPlugin {
|
||||
protected List<SocketAddress> getLocalSocketAddresses() {
|
||||
// Use the same address and port as last time if available
|
||||
TransportProperties p = callback.getLocalProperties();
|
||||
String oldAddress = p.get("address"), oldPort = p.get("port");
|
||||
InetSocketAddress old = parseSocketAddress(oldAddress, oldPort);
|
||||
List<SocketAddress> addrs = new LinkedList<SocketAddress>();
|
||||
for (InetAddress a : getLocalIpAddresses()) {
|
||||
if (isAcceptableAddress(a)) {
|
||||
String oldIpPorts = p.get(PROP_IP_PORTS);
|
||||
List<InetSocketAddress> olds = parseSocketAddresses(oldIpPorts);
|
||||
List<SocketAddress> locals = new LinkedList<SocketAddress>();
|
||||
for (InetAddress local : getLocalIpAddresses()) {
|
||||
if (isAcceptableAddress(local)) {
|
||||
// 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()));
|
||||
addrs.add(new InetSocketAddress(a, 0));
|
||||
for (InetSocketAddress old : olds) {
|
||||
if (old.getAddress().equals(local)) {
|
||||
int port = old.getPort();
|
||||
locals.add(0, new InetSocketAddress(local, port));
|
||||
}
|
||||
}
|
||||
locals.add(new InetSocketAddress(local, 0));
|
||||
}
|
||||
}
|
||||
return addrs;
|
||||
return locals;
|
||||
}
|
||||
|
||||
private List<InetSocketAddress> parseSocketAddresses(String ipPorts) {
|
||||
if (StringUtils.isNullOrEmpty(ipPorts)) return Collections.emptyList();
|
||||
String[] split = ipPorts.split(SEPARATOR);
|
||||
List<InetSocketAddress> remotes = new ArrayList<InetSocketAddress>();
|
||||
for (String ipPort : split) {
|
||||
InetSocketAddress a = parseSocketAddress(ipPort);
|
||||
if (a != null) remotes.add(a);
|
||||
}
|
||||
return remotes;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setLocalSocketAddress(InetSocketAddress a) {
|
||||
String ipPort = getIpPortString(a);
|
||||
// Get the list of recently used addresses
|
||||
String setting = callback.getSettings().get(PROP_IP_PORTS);
|
||||
List<String> recent = new ArrayList<String>();
|
||||
if (!StringUtils.isNullOrEmpty(setting))
|
||||
Collections.addAll(recent, setting.split(SEPARATOR));
|
||||
// Is the address already in the list?
|
||||
if (recent.remove(ipPort)) {
|
||||
// Move the address to the start of the list
|
||||
recent.add(0, ipPort);
|
||||
setting = StringUtils.join(recent, SEPARATOR);
|
||||
} else {
|
||||
// Add the address to the start of the list
|
||||
recent.add(0, ipPort);
|
||||
// Drop the least recently used address if the list is full
|
||||
if (recent.size() > MAX_ADDRESSES)
|
||||
recent = recent.subList(0, MAX_ADDRESSES);
|
||||
setting = StringUtils.join(recent, SEPARATOR);
|
||||
// Update the list of addresses shared with contacts
|
||||
List<String> shared = new ArrayList<String>(recent);
|
||||
Collections.sort(shared);
|
||||
String property = StringUtils.join(shared, SEPARATOR);
|
||||
TransportProperties properties = new TransportProperties();
|
||||
properties.put(PROP_IP_PORTS, property);
|
||||
callback.mergeLocalProperties(properties);
|
||||
}
|
||||
// Save the setting
|
||||
Settings settings = new Settings();
|
||||
settings.put(PROP_IP_PORTS, setting);
|
||||
callback.mergeSettings(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<InetSocketAddress> getRemoteSocketAddresses(ContactId c) {
|
||||
TransportProperties p = callback.getRemoteProperties().get(c);
|
||||
if (p == null) return Collections.emptyList();
|
||||
return parseSocketAddresses(p.get(PROP_IP_PORTS));
|
||||
}
|
||||
|
||||
private boolean isAcceptableAddress(InetAddress a) {
|
||||
|
||||
@@ -8,7 +8,6 @@ import org.briarproject.api.plugins.Backoff;
|
||||
import org.briarproject.api.plugins.duplex.DuplexPlugin;
|
||||
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
|
||||
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
|
||||
import org.briarproject.api.properties.TransportProperties;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -52,7 +51,22 @@ abstract class TcpPlugin implements DuplexPlugin {
|
||||
*/
|
||||
protected abstract List<SocketAddress> getLocalSocketAddresses();
|
||||
|
||||
/** Returns true if connections to the given address can be attempted. */
|
||||
/**
|
||||
* Adds the address on which the plugin is listening to the transport
|
||||
* properties.
|
||||
*/
|
||||
protected abstract void setLocalSocketAddress(InetSocketAddress a);
|
||||
|
||||
/**
|
||||
* Returns zero or more socket addresses for connecting to the given
|
||||
* contact.
|
||||
*/
|
||||
protected abstract List<InetSocketAddress> getRemoteSocketAddresses(
|
||||
ContactId c);
|
||||
|
||||
/**
|
||||
* Returns true if connections to the given address can be attempted.
|
||||
*/
|
||||
protected abstract boolean isConnectable(InetSocketAddress remote);
|
||||
|
||||
protected TcpPlugin(Executor ioExecutor, Backoff backoff,
|
||||
@@ -126,17 +140,11 @@ abstract class TcpPlugin implements DuplexPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
protected String getHostAddress(InetAddress a) {
|
||||
String addr = a.getHostAddress();
|
||||
protected String getIpPortString(InetSocketAddress a) {
|
||||
String addr = a.getAddress().getHostAddress();
|
||||
int percent = addr.indexOf('%');
|
||||
return percent == -1 ? addr : addr.substring(0, percent);
|
||||
}
|
||||
|
||||
protected void setLocalSocketAddress(InetSocketAddress a) {
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put("address", getHostAddress(a.getAddress()));
|
||||
p.put("port", String.valueOf(a.getPort()));
|
||||
callback.mergeLocalProperties(p);
|
||||
if (percent != -1) addr = addr.substring(0, percent);
|
||||
return addr + ":" + a.getPort();
|
||||
}
|
||||
|
||||
private void acceptContactConnections() {
|
||||
@@ -197,37 +205,34 @@ abstract class TcpPlugin implements DuplexPlugin {
|
||||
|
||||
public DuplexTransportConnection createConnection(ContactId c) {
|
||||
if (!isRunning()) return null;
|
||||
InetSocketAddress remote = getRemoteSocketAddress(c);
|
||||
if (remote == null) return null;
|
||||
if (!isConnectable(remote)) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
SocketAddress local = socket.getLocalSocketAddress();
|
||||
LOG.info(remote + " is not connectable from " + local);
|
||||
for (InetSocketAddress remote : getRemoteSocketAddresses(c)) {
|
||||
if (!isConnectable(remote)) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
SocketAddress local = socket.getLocalSocketAddress();
|
||||
LOG.info(remote + " is not connectable from " + local);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Socket s = new Socket();
|
||||
try {
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Connecting to " + remote);
|
||||
s.connect(remote);
|
||||
s.setSoTimeout(socketTimeout);
|
||||
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 " + remote);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Socket s = new Socket();
|
||||
try {
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Connecting to " + remote);
|
||||
s.connect(remote);
|
||||
s.setSoTimeout(socketTimeout);
|
||||
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 " + remote);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private InetSocketAddress getRemoteSocketAddress(ContactId c) {
|
||||
TransportProperties p = callback.getRemoteProperties().get(c);
|
||||
if (p == null) return null;
|
||||
return parseSocketAddress(p.get("address"), p.get("port"));
|
||||
}
|
||||
|
||||
protected InetSocketAddress parseSocketAddress(String addr, String port) {
|
||||
if (StringUtils.isNullOrEmpty(addr)) return null;
|
||||
if (StringUtils.isNullOrEmpty(port)) return null;
|
||||
protected InetSocketAddress parseSocketAddress(String ipPort) {
|
||||
if (StringUtils.isNullOrEmpty(ipPort)) return null;
|
||||
String[] split = ipPort.split(":");
|
||||
if (split.length != 2) return null;
|
||||
String addr = split[0], port = split[1];
|
||||
// Ensure getByName() won't perform a DNS lookup
|
||||
if (!DOTTED_QUAD.matcher(addr).matches()) return null;
|
||||
try {
|
||||
@@ -235,10 +240,12 @@ abstract class TcpPlugin implements DuplexPlugin {
|
||||
int p = Integer.parseInt(port);
|
||||
return new InetSocketAddress(a, p);
|
||||
} catch (UnknownHostException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.warning("Invalid address: " + addr);
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.warning("Invalid address: " + addr);
|
||||
return null;
|
||||
} catch (NumberFormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.warning("Invalid port: " + port);
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.warning("Invalid port: " + port);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.plugins.tcp;
|
||||
|
||||
import org.briarproject.api.TransportId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.plugins.Backoff;
|
||||
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
|
||||
import org.briarproject.api.properties.TransportProperties;
|
||||
@@ -9,6 +10,7 @@ import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -17,6 +19,8 @@ class WanTcpPlugin extends TcpPlugin {
|
||||
|
||||
static final TransportId ID = new TransportId("wan");
|
||||
|
||||
private static final String PROP_IP_PORT = "ipPort";
|
||||
|
||||
private final PortMapper portMapper;
|
||||
|
||||
private volatile MappingResult mappingResult;
|
||||
@@ -35,8 +39,7 @@ class WanTcpPlugin extends TcpPlugin {
|
||||
protected List<SocketAddress> getLocalSocketAddresses() {
|
||||
// Use the same address and port as last time if available
|
||||
TransportProperties p = callback.getLocalProperties();
|
||||
String oldAddress = p.get("address"), oldPort = p.get("port");
|
||||
InetSocketAddress old = parseSocketAddress(oldAddress, oldPort);
|
||||
InetSocketAddress old = parseSocketAddress(p.get(PROP_IP_PORT));
|
||||
List<SocketAddress> addrs = new LinkedList<SocketAddress>();
|
||||
for (InetAddress a : getLocalIpAddresses()) {
|
||||
if (isAcceptableAddress(a)) {
|
||||
@@ -69,6 +72,15 @@ class WanTcpPlugin extends TcpPlugin {
|
||||
return 32768 + (int) (Math.random() * 32768);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<InetSocketAddress> getRemoteSocketAddresses(ContactId c) {
|
||||
TransportProperties p = callback.getRemoteProperties().get(c);
|
||||
if (p == null) return Collections.emptyList();
|
||||
InetSocketAddress parsed = parseSocketAddress(p.get(PROP_IP_PORT));
|
||||
if (parsed == null) return Collections.emptyList();
|
||||
return Collections.singletonList(parsed);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isConnectable(InetSocketAddress remote) {
|
||||
if (remote.getPort() == 0) return false;
|
||||
@@ -83,8 +95,7 @@ class WanTcpPlugin extends TcpPlugin {
|
||||
a = mappingResult.getExternal();
|
||||
}
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put("address", getHostAddress(a.getAddress()));
|
||||
p.put("port", String.valueOf(a.getPort()));
|
||||
p.put(PROP_IP_PORT, getIpPortString(a));
|
||||
callback.mergeLocalProperties(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -91,14 +92,17 @@ public class LanTcpPluginTest extends BriarTestCase {
|
||||
plugin.start();
|
||||
// The plugin should have bound a socket and stored the port number
|
||||
assertTrue(callback.propertiesLatch.await(5, SECONDS));
|
||||
String addrString = callback.local.get("address");
|
||||
assertNotNull(addrString);
|
||||
String ipPorts = callback.local.get("ipPorts");
|
||||
assertNotNull(ipPorts);
|
||||
String[] split = ipPorts.split(",");
|
||||
assertEquals(1, split.length);
|
||||
split = split[0].split(":");
|
||||
assertEquals(2, split.length);
|
||||
String addrString = split[0], portString = split[1];
|
||||
InetAddress addr = InetAddress.getByName(addrString);
|
||||
assertTrue(addr instanceof Inet4Address);
|
||||
assertFalse(addr.isLoopbackAddress());
|
||||
assertTrue(addr.isLinkLocalAddress() || addr.isSiteLocalAddress());
|
||||
String portString = callback.local.get("port");
|
||||
assertNotNull(portString);
|
||||
int port = Integer.parseInt(portString);
|
||||
assertTrue(port > 0 && port < 65536);
|
||||
// The plugin should be listening on the port
|
||||
@@ -124,11 +128,17 @@ public class LanTcpPluginTest extends BriarTestCase {
|
||||
plugin.start();
|
||||
// The plugin should have bound a socket and stored the port number
|
||||
assertTrue(callback.propertiesLatch.await(5, SECONDS));
|
||||
String addr = callback.local.get("address");
|
||||
assertNotNull(addr);
|
||||
assertTrue(callback.propertiesLatch.await(5, SECONDS));
|
||||
String ipPorts = callback.local.get("ipPorts");
|
||||
assertNotNull(ipPorts);
|
||||
String[] split = ipPorts.split(",");
|
||||
assertEquals(1, split.length);
|
||||
split = split[0].split(":");
|
||||
assertEquals(2, split.length);
|
||||
String addrString = split[0];
|
||||
// Listen on the same interface as the plugin
|
||||
final ServerSocket ss = new ServerSocket();
|
||||
ss.bind(new InetSocketAddress(addr, 0), 10);
|
||||
ss.bind(new InetSocketAddress(addrString, 0), 10);
|
||||
int port = ss.getLocalPort();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicBoolean error = new AtomicBoolean(false);
|
||||
@@ -145,8 +155,7 @@ public class LanTcpPluginTest extends BriarTestCase {
|
||||
}.start();
|
||||
// Tell the plugin about the port
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put("address", addr);
|
||||
p.put("port", String.valueOf(port));
|
||||
p.put("ipPorts", addrString + ":" + port);
|
||||
callback.remote.put(contactId, p);
|
||||
// Connect to the port
|
||||
DuplexTransportConnection d = plugin.createConnection(contactId);
|
||||
@@ -175,7 +184,7 @@ public class LanTcpPluginTest extends BriarTestCase {
|
||||
private static class Callback implements DuplexPluginCallback {
|
||||
|
||||
private final Map<ContactId, TransportProperties> remote =
|
||||
new Hashtable<ContactId, TransportProperties>();
|
||||
new Hashtable<>();
|
||||
private final CountDownLatch propertiesLatch = new CountDownLatch(1);
|
||||
private final CountDownLatch connectionsLatch = new CountDownLatch(1);
|
||||
private final TransportProperties local = new TransportProperties();
|
||||
@@ -192,7 +201,8 @@ public class LanTcpPluginTest extends BriarTestCase {
|
||||
return remote;
|
||||
}
|
||||
|
||||
public void mergeSettings(Settings s) {}
|
||||
public void mergeSettings(Settings s) {
|
||||
}
|
||||
|
||||
public void mergeLocalProperties(TransportProperties p) {
|
||||
local.putAll(p);
|
||||
@@ -207,18 +217,22 @@ public class LanTcpPluginTest extends BriarTestCase {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void showMessage(String... message) {}
|
||||
public void showMessage(String... message) {
|
||||
}
|
||||
|
||||
public void incomingConnectionCreated(DuplexTransportConnection d) {
|
||||
connectionsLatch.countDown();
|
||||
}
|
||||
|
||||
public void outgoingConnectionCreated(ContactId c,
|
||||
DuplexTransportConnection d) {}
|
||||
DuplexTransportConnection d) {
|
||||
}
|
||||
|
||||
public void transportEnabled() {}
|
||||
public void transportEnabled() {
|
||||
}
|
||||
|
||||
public void transportDisabled() {}
|
||||
public void transportDisabled() {
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestBackoff implements Backoff {
|
||||
@@ -227,8 +241,10 @@ public class LanTcpPluginTest extends BriarTestCase {
|
||||
return 60 * 1000;
|
||||
}
|
||||
|
||||
public void increment() {}
|
||||
public void increment() {
|
||||
}
|
||||
|
||||
public void reset() {}
|
||||
public void reset() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user