Merge branch 'bluetooth-connection-limiter' into 'master'

Don't make or accept Bluetooth contact connections during key agreement

See merge request akwizgran/briar!770
This commit is contained in:
Torsten Grote
2018-04-29 14:42:00 +00:00
11 changed files with 239 additions and 20 deletions

View File

@@ -14,6 +14,7 @@ import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFailedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStoppedListeningEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
@@ -71,6 +72,7 @@ class KeyAgreementTaskImpl extends Thread implements KeyAgreementTask,
if (localPayload != null) {
if (remotePayload == null) connector.stopListening();
else interrupt();
eventBus.broadcast(new KeyAgreementStoppedListeningEvent());
}
}

View File

@@ -0,0 +1,47 @@
package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
@NotNullByDefault
interface BluetoothConnectionLimiter {
/**
* Informs the limiter that key agreement has started.
*/
void keyAgreementStarted();
/**
* Informs the limiter that key agreement has ended.
*/
void keyAgreementEnded();
/**
* Returns true if a contact connection can be opened. This method does not
* need to be called for key agreement connections.
*/
boolean canOpenContactConnection();
/**
* Informs the limiter that a contact connection has been opened. The
* limiter may close the new connection if key agreement is in progress.
* <p/>
* Returns false if the limiter has closed the new connection.
*/
boolean contactConnectionOpened(DuplexTransportConnection conn);
/**
* Informs the limiter that a key agreement connection has been opened.
*/
void keyAgreementConnectionOpened(DuplexTransportConnection conn);
/**
* Informs the limiter that the given connection has been closed.
*/
void connectionClosed(DuplexTransportConnection conn);
/**
* Informs the limiter that all connections have been closed.
*/
void allConnectionsClosed();
}

View File

@@ -0,0 +1,115 @@
package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
@NotNullByDefault
@ThreadSafe
class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter {
private static final Logger LOG =
Logger.getLogger(BluetoothConnectionLimiterImpl.class.getName());
private final Object lock = new Object();
// The following are locking: lock
private final LinkedList<DuplexTransportConnection> connections =
new LinkedList<>();
private boolean keyAgreementInProgress = false;
@Override
public void keyAgreementStarted() {
List<DuplexTransportConnection> close;
synchronized (lock) {
keyAgreementInProgress = true;
close = new ArrayList<>(connections);
connections.clear();
}
if (LOG.isLoggable(INFO)) {
LOG.info("Key agreement started, closing " + close.size() +
" connections");
}
for (DuplexTransportConnection conn : close) tryToClose(conn);
}
@Override
public void keyAgreementEnded() {
synchronized (lock) {
keyAgreementInProgress = false;
}
LOG.info("Key agreement ended");
}
@Override
public boolean canOpenContactConnection() {
synchronized (lock) {
if (keyAgreementInProgress) {
LOG.info("Can't open contact connection during key agreement");
return false;
} else {
LOG.info("Can open contact connection");
return true;
}
}
}
@Override
public boolean contactConnectionOpened(DuplexTransportConnection conn) {
boolean accept = true;
synchronized (lock) {
if (keyAgreementInProgress) {
LOG.info("Refusing contact connection during key agreement");
accept = false;
} else {
LOG.info("Accepting contact connection");
connections.add(conn);
}
}
if (!accept) tryToClose(conn);
return accept;
}
@Override
public void keyAgreementConnectionOpened(DuplexTransportConnection conn) {
synchronized (lock) {
LOG.info("Accepting key agreement connection");
connections.add(conn);
}
}
private void tryToClose(DuplexTransportConnection conn) {
try {
conn.getWriter().dispose(false);
conn.getReader().dispose(false, false);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
@Override
public void connectionClosed(DuplexTransportConnection conn) {
synchronized (lock) {
connections.remove(conn);
if (LOG.isLoggable(INFO))
LOG.info("Connection closed, " + connections.size() + " open");
}
}
@Override
public void allConnectionsClosed() {
synchronized (lock) {
connections.clear();
LOG.info("All connections closed");
}
}
}

View File

@@ -7,6 +7,8 @@ import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStoppedListeningEvent;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
@@ -51,6 +53,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
private static final Logger LOG =
Logger.getLogger(BluetoothPlugin.class.getName());
final BluetoothConnectionLimiter connectionLimiter;
private final Executor ioExecutor;
private final SecureRandom secureRandom;
private final Backoff backoff;
@@ -91,8 +95,10 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
abstract DuplexTransportConnection connectTo(String address, String uuid)
throws IOException;
BluetoothPlugin(Executor ioExecutor, SecureRandom secureRandom,
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
Executor ioExecutor, SecureRandom secureRandom,
Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
this.connectionLimiter = connectionLimiter;
this.ioExecutor = ioExecutor;
this.secureRandom = secureRandom;
this.backoff = backoff;
@@ -110,6 +116,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
void onAdapterDisabled() {
LOG.info("Bluetooth disabled");
tryToClose(socket);
connectionLimiter.allConnectionsClosed();
callback.transportDisabled();
}
@@ -213,7 +220,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
return;
}
backoff.reset();
callback.incomingConnectionCreated(conn);
if (connectionLimiter.contactConnectionOpened(conn))
callback.incomingConnectionCreated(conn);
if (!running) return;
}
}
@@ -257,10 +265,12 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
if (StringUtils.isNullOrEmpty(uuid)) continue;
ioExecutor.execute(() -> {
if (!isRunning() || !shouldAllowContactConnections()) return;
if (!connectionLimiter.canOpenContactConnection()) return;
DuplexTransportConnection conn = connect(address, uuid);
if (conn != null) {
backoff.reset();
callback.outgoingConnectionCreated(c, conn);
if (connectionLimiter.contactConnectionOpened(conn))
callback.outgoingConnectionCreated(c, conn);
}
});
}
@@ -300,12 +310,16 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
@Override
public DuplexTransportConnection createConnection(ContactId c) {
if (!isRunning() || !shouldAllowContactConnections()) return null;
if (!connectionLimiter.canOpenContactConnection()) return null;
TransportProperties p = callback.getRemoteProperties(c);
String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) return null;
return connect(address, uuid);
DuplexTransportConnection conn = connect(address, uuid);
if (conn == null) return null;
// TODO: Why don't we reset the backoff here?
return connectionLimiter.contactConnectionOpened(conn) ? conn : null;
}
@Override
@@ -355,7 +369,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
if (LOG.isLoggable(INFO))
LOG.info("Connecting to key agreement UUID " + uuid);
return connect(address, uuid);
DuplexTransportConnection conn = connect(address, uuid);
if (conn != null) connectionLimiter.keyAgreementConnectionOpened(conn);
return conn;
}
private String parseAddress(BdfList descriptor) throws FormatException {
@@ -376,6 +392,10 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
if (s.getNamespace().equals(ID.getString()))
ioExecutor.execute(this::onSettingsUpdated);
} else if (e instanceof KeyAgreementListeningEvent) {
ioExecutor.execute(connectionLimiter::keyAgreementStarted);
} else if (e instanceof KeyAgreementStoppedListeningEvent) {
ioExecutor.execute(connectionLimiter::keyAgreementEnded);
}
}
@@ -408,6 +428,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
public KeyAgreementConnection accept() throws IOException {
DuplexTransportConnection conn = acceptConnection(ss);
if (LOG.isLoggable(INFO)) LOG.info(ID + ": Incoming connection");
connectionLimiter.keyAgreementConnectionOpened(conn);
return new KeyAgreementConnection(conn, ID);
}