From 49f06402781e44997360e34cb00803efbf35f981 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 16 Jul 2020 16:56:14 +0100 Subject: [PATCH] Use reflected Bluetooth address if we don't know our own address. --- .../api/plugin/BluetoothConstants.java | 3 + .../plugin/bluetooth/BluetoothPlugin.java | 61 +++++++++++++++++-- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java index 6b8a654bd..ce8930f38 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/BluetoothConstants.java @@ -10,6 +10,9 @@ public interface BluetoothConstants { String PROP_ADDRESS = "address"; String PROP_UUID = "uuid"; + // Local settings (not shared with contacts) + String PREF_ADDRESS_IS_REFLECTED = "addressIsReflected"; + // Default value for PREF_PLUGIN_ENABLE boolean DEFAULT_PREF_PLUGIN_ENABLE = false; } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java index 643af4608..2902d3751 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java @@ -1,6 +1,7 @@ package org.briarproject.bramble.plugin.bluetooth; import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.Multiset; import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.event.Event; @@ -25,6 +26,7 @@ import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent; import org.briarproject.bramble.api.plugin.event.DisableBluetoothEvent; import org.briarproject.bramble.api.plugin.event.EnableBluetoothEvent; import org.briarproject.bramble.api.properties.TransportProperties; +import org.briarproject.bramble.api.properties.event.RemoteTransportPropertiesUpdatedEvent; import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint; import org.briarproject.bramble.api.settings.Settings; @@ -48,6 +50,7 @@ import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH; import static org.briarproject.bramble.api.plugin.BluetoothConstants.DEFAULT_PREF_PLUGIN_ENABLE; import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID; +import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_ADDRESS_IS_REFLECTED; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID; import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES; @@ -55,6 +58,7 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; +import static org.briarproject.bramble.api.properties.TransportPropertyConstants.REFLECTED_PROPERTY_PREFIX; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; @@ -205,25 +209,64 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { TransportProperties p = callback.getLocalProperties(); String address = p.get(PROP_ADDRESS); String uuid = p.get(PROP_UUID); + Settings s = callback.getSettings(); + boolean isReflected = s.getBoolean(PREF_ADDRESS_IS_REFLECTED, false); boolean changed = false; - if (address == null) { + if (address == null || isReflected) { address = getBluetoothAddress(); - if (LOG.isLoggable(INFO)) + if (LOG.isLoggable(INFO)) { LOG.info("Local address " + scrubMacAddress(address)); - if (!isNullOrEmpty(address)) { - p.put(PROP_ADDRESS, address); + } + if (address == null) { + address = getReflectedAddress(); + if (LOG.isLoggable(INFO)) { + LOG.info("Reflected address " + scrubMacAddress(address)); + } + if (address != null) { + changed = true; + isReflected = true; + } + } else { changed = true; + isReflected = false; } } if (uuid == null) { byte[] random = new byte[UUID_BYTES]; secureRandom.nextBytes(random); uuid = UUID.nameUUIDFromBytes(random).toString(); - p.put(PROP_UUID, uuid); changed = true; } contactConnectionsUuid = uuid; - if (changed) callback.mergeLocalProperties(p); + if (changed) { + p = new TransportProperties(); + // If we previously used a reflected address and there's no longer + // a reflected address with enough votes to be used, we'll continue + // to use the old reflected address until there's a new winner + if (address != null) p.put(PROP_ADDRESS, address); + p.put(PROP_UUID, uuid); + callback.mergeLocalProperties(p); + s = new Settings(); + s.putBoolean(PREF_ADDRESS_IS_REFLECTED, isReflected); + callback.mergeSettings(s); + } + } + + @Nullable + private String getReflectedAddress() { + // Count the number of votes for each reflected address + String key = REFLECTED_PROPERTY_PREFIX + PROP_ADDRESS; + Multiset votes = new Multiset<>(); + for (TransportProperties p : callback.getRemoteProperties()) { + String address = p.get(key); + if (address != null && isValidAddress(address)) votes.add(address); + } + // If an address gets more than half of the votes, accept it + int total = votes.getTotal(); + for (String address : votes.keySet()) { + if (votes.getCount(address) * 2 > total) return address; + } + return null; } private void acceptContactConnections(SS ss) { @@ -429,6 +472,12 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { ioExecutor.execute(connectionLimiter::keyAgreementStarted); } else if (e instanceof KeyAgreementStoppedListeningEvent) { ioExecutor.execute(connectionLimiter::keyAgreementEnded); + } else if (e instanceof RemoteTransportPropertiesUpdatedEvent) { + RemoteTransportPropertiesUpdatedEvent r = + (RemoteTransportPropertiesUpdatedEvent) e; + if (r.getTransportId().equals(ID)) { + ioExecutor.execute(this::updateProperties); + } } }