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

Merge branch '385-bqp-lan' into 'master'

Add BQP support to LanTcpPlugin

Closes #385

See merge request !185
parents 5db584bd 02ed01ea
No related branches found
No related tags found
No related merge requests found
......@@ -2,28 +2,44 @@ package org.briarproject.plugins.tcp;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.keyagreement.KeyAgreementConnection;
import org.briarproject.api.keyagreement.KeyAgreementListener;
import org.briarproject.api.keyagreement.TransportDescriptor;
import org.briarproject.api.plugins.Backoff;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import org.briarproject.api.properties.TransportProperties;
import org.briarproject.api.settings.Settings;
import org.briarproject.util.StringUtils;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
class LanTcpPlugin extends TcpPlugin {
static final TransportId ID = new TransportId("lan");
private static final Logger LOG =
Logger.getLogger(LanTcpPlugin.class.getName());
private static final int MAX_ADDRESSES = 5;
private static final String PROP_IP_PORTS = "ipPorts";
private static final String PROP_IP_PORT = "ipPort";
private static final String SEPARATOR = ",";
LanTcpPlugin(Executor ioExecutor, Backoff backoff,
......@@ -143,4 +159,100 @@ class LanTcpPlugin extends TcpPlugin {
// Unrecognised prefix - may be compatible
return true;
}
@Override
public boolean supportsKeyAgreement() {
return true;
}
@Override
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
ServerSocket ss = null;
for (SocketAddress addr : getLocalSocketAddresses()) {
try {
ss = new ServerSocket();
ss.bind(addr);
break;
} catch (IOException e) {
if (LOG.isLoggable(INFO))
LOG.info("Failed to bind " + addr);
tryToClose(ss);
}
}
if (ss == null || !ss.isBound()) {
LOG.info("Could not bind server socket for key agreement");
return null;
}
TransportProperties p = new TransportProperties();
SocketAddress local = ss.getLocalSocketAddress();
p.put(PROP_IP_PORT, getIpPortString((InetSocketAddress) local));
TransportDescriptor d = new TransportDescriptor(ID, p);
return new LanKeyAgreementListener(d, ss);
}
@Override
public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, TransportDescriptor d, long timeout) {
if (!isRunning()) return null;
if (!ID.equals(d.getIdentifier())) return null;
TransportProperties p = d.getProperties();
if (p == null) return null;
String ipPort = p.get(PROP_IP_PORT);
InetSocketAddress remote = parseSocketAddress(ipPort);
if (remote == null) return null;
if (!isConnectable(remote)) {
if (LOG.isLoggable(INFO)) {
SocketAddress local = socket.getLocalSocketAddress();
LOG.info(remote + " is not connectable from " + local);
}
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;
}
}
private class LanKeyAgreementListener extends KeyAgreementListener {
private final ServerSocket ss;
public LanKeyAgreementListener(TransportDescriptor descriptor,
ServerSocket ss) {
super(descriptor);
this.ss = ss;
}
@Override
public Callable<KeyAgreementConnection> listen() {
return new Callable<KeyAgreementConnection>() {
@Override
public KeyAgreementConnection call() throws IOException {
Socket s = ss.accept();
if (LOG.isLoggable(INFO))
LOG.info(ID.getString() + ": Incoming connection");
return new KeyAgreementConnection(
new TcpTransportConnection(LanTcpPlugin.this, s),
ID);
}
};
}
@Override
public void close() {
try {
ss.close();
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
}
}
......@@ -2,6 +2,9 @@ package org.briarproject.plugins.tcp;
import org.briarproject.BriarTestCase;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.keyagreement.KeyAgreementConnection;
import org.briarproject.api.keyagreement.KeyAgreementListener;
import org.briarproject.api.keyagreement.TransportDescriptor;
import org.briarproject.api.plugins.Backoff;
import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
......@@ -20,9 +23,12 @@ import java.net.Socket;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicBoolean;
import static java.util.concurrent.TimeUnit.SECONDS;
......@@ -170,6 +176,102 @@ public class LanTcpPluginTest extends BriarTestCase {
plugin.stop();
}
@Test
public void testIncomingKeyAgreementConnection() throws Exception {
if (!systemHasLocalIpv4Address()) {
System.err.println("WARNING: Skipping test, no local IPv4 address");
return;
}
Callback callback = new Callback();
Executor executor = Executors.newCachedThreadPool();
DuplexPlugin plugin = new LanTcpPlugin(executor, backoff, callback,
0, 0);
plugin.start();
assertTrue(callback.propertiesLatch.await(5, SECONDS));
KeyAgreementListener kal = plugin.createKeyAgreementListener(null);
Callable<KeyAgreementConnection> c = kal.listen();
FutureTask<KeyAgreementConnection> f = new FutureTask<>(c);
new Thread(f).start();
// The plugin should have bound a socket and stored the port number
TransportDescriptor d = kal.getDescriptor();
TransportProperties p = d.getProperties();
String ipPort = p.get("ipPort");
assertNotNull(ipPort);
String[] split = ipPort.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());
int port = Integer.parseInt(portString);
assertTrue(port > 0 && port < 65536);
// The plugin should be listening on the port
InetSocketAddress socketAddr = new InetSocketAddress(addr, port);
Socket s = new Socket();
s.connect(socketAddr, 100);
assertNotNull(f.get(5, SECONDS));
s.close();
kal.close();
// Stop the plugin
plugin.stop();
}
@Test
public void testOutgoingKeyAgreementConnection() throws Exception {
if (!systemHasLocalIpv4Address()) {
System.err.println("WARNING: Skipping test, no local IPv4 address");
return;
}
Callback callback = new Callback();
Executor executor = Executors.newCachedThreadPool();
DuplexPlugin plugin = new LanTcpPlugin(executor, backoff, callback,
0, 0);
plugin.start();
// The plugin should have bound a socket and stored the port number
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(addrString, 0), 10);
int port = ss.getLocalPort();
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean error = new AtomicBoolean(false);
new Thread() {
@Override
public void run() {
try {
ss.accept();
latch.countDown();
} catch (IOException e) {
error.set(true);
}
}
}.start();
// Tell the plugin about the port
TransportProperties p = new TransportProperties();
p.put("ipPort", addrString + ":" + port);
TransportDescriptor desc = new TransportDescriptor(plugin.getId(), p);
// Connect to the port
DuplexTransportConnection d =
plugin.createKeyAgreementConnection(null, desc, 5000);
assertNotNull(d);
// Check that the connection was accepted
assertTrue(latch.await(5, SECONDS));
assertFalse(error.get());
// Clean up
d.getReader().dispose(false, true);
d.getWriter().dispose(false);
ss.close();
plugin.stop();
}
private boolean systemHasLocalIpv4Address() throws Exception {
for (NetworkInterface i : Collections.list(
NetworkInterface.getNetworkInterfaces())) {
......
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