Merge branch 'bluetooth-race' into 'master'

Fix race condition when closing redundant Bluetooth sockets

The Bluetooth invitation code has a race condition: if Alice and Bob connect to each other at roughly the same time, they each consider their outgoing socket to be redundant and close it, resulting in both sockets being closed. This can be triggered pretty reliably by using two phones of the same model and pressing 'Continue' at the same time on both phones.

When more than one invitation socket is opened, Alice should pick which one to use and Bob should use whichever one Alice picks, which Bob can detect by trying to read from both sockets.

Hopefully the Bluetooth invitation code will be retired when #117 is merged, but I'm putting this up for review in case we need to keep Bluetooth as a fallback method.

See merge request !120
This commit is contained in:
akwizgran
2016-03-09 10:12:05 +00:00
15 changed files with 230 additions and 532 deletions

View File

@@ -51,7 +51,7 @@ class AliceConnector extends Connector {
@Override
public void run() {
// Create an incoming or outgoing connection
DuplexTransportConnection conn = createInvitationConnection();
DuplexTransportConnection conn = createInvitationConnection(true);
if (conn == null) return;
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " connected");
// Don't proceed with more than one connection

View File

@@ -51,7 +51,7 @@ class BobConnector extends Connector {
@Override
public void run() {
// Create an incoming or outgoing connection
DuplexTransportConnection conn = createInvitationConnection();
DuplexTransportConnection conn = createInvitationConnection(false);
if (conn == null) return;
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " connected");
// Carry out the key agreement protocol

View File

@@ -93,10 +93,12 @@ abstract class Connector extends Thread {
messageDigest = crypto.getMessageDigest();
}
protected DuplexTransportConnection createInvitationConnection() {
protected DuplexTransportConnection createInvitationConnection(
boolean alice) {
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " creating invitation connection");
return plugin.createInvitationConnection(random, CONNECTION_TIMEOUT);
return plugin.createInvitationConnection(random, CONNECTION_TIMEOUT,
alice);
}
protected void sendPublicKeyHash(BdfWriter w) throws IOException {

View File

@@ -246,7 +246,7 @@ abstract class TcpPlugin implements DuplexPlugin {
}
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout) {
long timeout, boolean alice) {
throw new UnsupportedOperationException();
}

View File

@@ -1,30 +0,0 @@
package org.briarproject.util;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
public class LatchedReference<T> {
private final CountDownLatch latch = new CountDownLatch(1);
private final AtomicReference<T> reference = new AtomicReference<T>();
public boolean isSet() {
return reference.get() != null;
}
public boolean set(T t) {
if (t == null) throw new IllegalArgumentException();
if (reference.compareAndSet(null, t)) {
latch.countDown();
return true;
}
return false;
}
public T waitForReference(long timeout) throws InterruptedException {
latch.await(timeout, MILLISECONDS);
return reference.get();
}
}