diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxConstants.java index 753e8c324f901eef4c03fe51ba6f51bb8f044311..5fd186d81edf8baa4678a1250b2ed1afdf0046a6 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxConstants.java @@ -6,7 +6,7 @@ public interface MailboxConstants { TransportId ID = new TransportId("org.briarproject.bramble.mailbox"); - int POLLING_INTERVALL = 30 * 1000 ; // Scientifically chosen + int MIN_POLLING_INTERVALL = 30 * 1000 ; // Scientifically chosen int MAX_LATENCY = 30 * 1000; // 30 seconds int MAX_IDLE_TIME = 30 * 1000; // 30 seconds int MAX_MAILBOX_LATENCY = 604800000; // 1 Week diff --git a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxServiceImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxServiceImpl.java index 7c9604cf85fba476f9bd5018ff7db8f35c6d8c5e..e6b408e53c07049cba07ac9a452a61eadeef32d8 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxServiceImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxServiceImpl.java @@ -1,6 +1,5 @@ package org.briarproject.bramble.mailbox; -import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactType; @@ -13,7 +12,6 @@ import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.lifecycle.IoExecutor; -import org.briarproject.bramble.api.lifecycle.ServiceException; import org.briarproject.bramble.api.mailbox.MailboxConstants; import org.briarproject.bramble.api.mailbox.MailboxInfo; import org.briarproject.bramble.api.mailbox.MailboxManager; @@ -21,6 +19,7 @@ import org.briarproject.bramble.api.mailbox.MailboxService; import org.briarproject.bramble.api.plugin.ConnectionRegistry; import org.briarproject.bramble.api.plugin.LanTcpConstants; import org.briarproject.bramble.api.plugin.PluginManager; +import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; @@ -31,8 +30,11 @@ import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.system.Scheduler; import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; @@ -44,8 +46,7 @@ import javax.inject.Inject; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; -import static org.briarproject.bramble.api.contact.ContactType.PRIVATE_MAILBOX; -import static org.briarproject.bramble.api.mailbox.MailboxConstants.POLLING_INTERVALL; +import static org.briarproject.bramble.api.mailbox.MailboxConstants.MIN_POLLING_INTERVALL; import static org.briarproject.bramble.util.LogUtils.logException; /** @@ -64,12 +65,16 @@ public class MailboxServiceImpl implements MailboxService, EventListener { private final PluginManager pluginManager; private final MailboxManager mailboxManager; private final TransportPropertyManager transportPropertyManager; + private SecureRandom secureRandom; - private volatile Future mailboxLanFuture; + private volatile Future mailboxFuture; private volatile ContactId privateMailboxId = null; private volatile AtomicBoolean hasPrivateMailbox = new AtomicBoolean(false); + private ConcurrentHashMap<TransportId, DuplexPlugin> activePlugins = + new ConcurrentHashMap<>(); + @Inject MailboxServiceImpl(@IoExecutor Executor ioExecutor, @Scheduler ScheduledExecutorService scheduler, EventBus eventBus, @@ -77,7 +82,8 @@ public class MailboxServiceImpl implements MailboxService, EventListener { DatabaseComponent db, ConnectionRegistry connectionRegistry, PluginManager pluginManager, MailboxManager mailboxManager, - TransportPropertyManager transportPropertyManager) { + TransportPropertyManager transportPropertyManager, + SecureRandom secureRandom) { this.ioExecutor = ioExecutor; this.scheduler = scheduler; this.eventBus = eventBus; @@ -87,7 +93,7 @@ public class MailboxServiceImpl implements MailboxService, EventListener { this.pluginManager = pluginManager; this.mailboxManager = mailboxManager; this.transportPropertyManager = transportPropertyManager; - + this.secureRandom = secureRandom; } @Override @@ -97,7 +103,7 @@ public class MailboxServiceImpl implements MailboxService, EventListener { try { PrivateMailbox mb = contactManager.getPrivateMailbox(); - if (mb!=null) + if (mb != null) privateMailboxId = mb.getId(); } catch (DbException e1) { if (LOG.isLoggable(WARNING)) @@ -105,35 +111,61 @@ public class MailboxServiceImpl implements MailboxService, EventListener { } if (privateMailboxId != null) hasPrivateMailbox.set(true); - - tryToRunLanMailboxFuture(); + + // Get Lan plugin if available + DuplexPlugin plugin = + (DuplexPlugin) pluginManager.getPlugin(LanTcpConstants.ID); + + if (plugin != null && plugin.isRunning()) + activePlugins.put(LanTcpConstants.ID, plugin); + + // Get Tor plugin if available + plugin = (DuplexPlugin) pluginManager.getPlugin(TorConstants.ID); + + if (plugin != null && plugin.isRunning()) + activePlugins.put(TorConstants.ID, plugin); + + + mailboxFuture = + schedule(new ConnectMailboxesTask(MIN_POLLING_INTERVALL), + MIN_POLLING_INTERVALL); + this.eventBus.addListener(this); } + private Future schedule(Runnable task, int delay) { + return scheduler + .schedule(() -> ioExecutor.execute(task), delay, MILLISECONDS); + } + @Override public void eventOccurred(Event e) { if (e instanceof TransportEnabledEvent) { - if (((TransportEnabledEvent) e).getTransportId() - .equals(LanTcpConstants.ID)) { - if (mailboxLanFuture == null || mailboxLanFuture.isCancelled()) - tryToRunLanMailboxFuture(); + TransportEnabledEvent tee = (TransportEnabledEvent) e; + if (tee.getTransportId().equals(LanTcpConstants.ID) || + tee.getTransportId().equals(TorConstants.ID)) { + DuplexPlugin p = + (DuplexPlugin) pluginManager + .getPlugin(tee.getTransportId()); + activePlugins.put(p.getId(), p); } } if (e instanceof TransportDisabledEvent) { - if (((TransportDisabledEvent) e).getTransportId() - .equals(LanTcpConstants.ID)) { - if (mailboxLanFuture != null) - mailboxLanFuture.cancel(false); + if (activePlugins + .containsKey( + ((TransportDisabledEvent) e).getTransportId())) { + activePlugins + .remove(((TransportDisabledEvent) e).getTransportId()); } } - if (e instanceof ContactAddedEvent){ + if (e instanceof ContactAddedEvent) { if (hasPrivateMailbox.get()) return; try { PrivateMailbox mb = contactManager.getPrivateMailbox(); - if (mb!=null) + if (mb != null) privateMailboxId = mb.getId(); } catch (DbException e1) { if (LOG.isLoggable(WARNING)) @@ -144,51 +176,36 @@ public class MailboxServiceImpl implements MailboxService, EventListener { } } - private void tryToRunLanMailboxFuture() { - //NOTE/TODO: Only using LAN connections for now - DuplexPlugin plugin = - (DuplexPlugin) pluginManager.getPlugin(LanTcpConstants.ID); - // TODO: Find a useful polling interval and randomize (should have - // a minimum polling interval) - if (plugin != null && plugin.isRunning()) - mailboxLanFuture = schedule( - new ConnectMailboxesTask(LanTcpConstants.ID, - POLLING_INTERVALL, plugin), POLLING_INTERVALL); - } - - private Future schedule(Runnable task, int delay) { - return scheduler - .schedule(() -> ioExecutor.execute(task), delay, MILLISECONDS); - } - private class ConnectMailboxesTask implements Runnable { - private TransportId transportId; + // TODO: Try to connect via Lan then Tor (this is debatable but + // surely useful for testing) + ArrayList<TransportId> transportOrder = new ArrayList<>( + Arrays.asList(LanTcpConstants.ID, TorConstants.ID)); + private int delay; - private DuplexPlugin plugin; - private TransportProperties privateMailboxProperties = null; - - private ConnectMailboxesTask(TransportId transportId, - int delay, DuplexPlugin plugin) { - this.transportId = transportId; - this.delay = delay; - this.plugin = plugin; + + private ConnectMailboxesTask(int min_interval) { + this.delay = (int) (min_interval + + secureRandom.nextFloat() * min_interval); } private void reschedule() { - mailboxLanFuture = schedule(this, delay); + mailboxFuture = schedule(this, delay); } @Override public void run() { - if (hasPrivateMailbox.get() && - !connectionRegistry.isConnected(privateMailboxId)) { - try { - connectPrivateMailbox(); - } catch (DbException e) { - logException(LOG, WARNING, e); - return; - } + // Wait for plugins to become available + if (activePlugins.isEmpty()) { + reschedule(); + } + + try { + connectPrivateMailbox(); + } catch (DbException e) { + logException(LOG, WARNING, e); + return; } // Poll Contact mailboxes @@ -211,64 +228,95 @@ public class MailboxServiceImpl implements MailboxService, EventListener { } for (MailboxInfo mailboxInfo : contactMailboxes) { - if (mailboxInfo.getMailboxId() == null) + if (connectionRegistry + .isConnected(mailboxInfo.getContactId())) + continue; + + // If mailboxId == null the contact does not have their own mailbox + // but has been introduced to our mailbox + if (mailboxInfo.getMailboxId() == null) { + mailboxManager + .handleOwnerContactWithoutMailbox(mailboxInfo); continue; - if (!connectionRegistry - .isConnected(mailboxInfo.getContactId())) { - TransportProperties mailboxTP = - contacts.get(mailboxInfo.getMailboxId()); - if (mailboxTP == null) - throw new AssertionError(); - ioExecutor.execute( - () -> connectContactMailbox(plugin, - mailboxInfo.getContactId(), - mailboxTP)); } + + TransportProperties properties = + contacts.get(mailboxInfo.getMailboxId()); + + if (properties == null) + throw new AssertionError(); + + ioExecutor.execute( + () -> connectContactMailbox(mailboxInfo.getContactId(), + properties)); } reschedule(); - } - private void connectContactMailbox(DuplexPlugin plugin, - ContactId contactId, - TransportProperties transportProperties) { - DuplexTransportConnection mailBoxConn = - plugin.createConnection(transportProperties); - if (mailBoxConn != null) - mailboxManager.handleOutgoingContactMailboxConnection( - contactId, mailBoxConn, - transportId); + private void connectContactMailbox(ContactId contactId, + TransportProperties properties) { + for (TransportId transportId : transportOrder) { + if (!activePlugins.containsKey(transportId)) + continue; + + DuplexPlugin plugin = activePlugins.get(transportId); + + DuplexTransportConnection conn = + plugin.createConnection(properties); + + if (conn != null) { + if (LOG.isLoggable(INFO)) + LOG.info("Contact mailbox connected"); + + mailboxManager.handleOutgoingContactMailboxConnection( + contactId, conn, transportId); + } + } } private void connectPrivateMailbox() throws DbException { - if (privateMailboxProperties == null) { - privateMailboxProperties = transportPropertyManager - .getRemoteProperties(privateMailboxId, transportId); - } + // Check if mailbox is available and if it is connected + if (!hasPrivateMailbox.get() || + connectionRegistry.isConnected(privateMailboxId)) + return; - if (LOG.isLoggable(INFO)) - LOG.info("Connecting to private mailbox"); + for (TransportId transportId : transportOrder) { + if (!activePlugins.containsKey(transportId)) + continue; + + DuplexPlugin plugin = activePlugins.get(transportId); - DuplexTransportConnection mailBoxConn = - plugin.createConnection(privateMailboxProperties); + TransportProperties properties = transportPropertyManager + .getRemoteProperties(privateMailboxId, transportId); - if (mailBoxConn != null) { - mailboxManager - .handleOutgoingPrivateMailboxConnection( - privateMailboxId, mailBoxConn, - transportId); - } else { if (LOG.isLoggable(INFO)) - LOG.info("Unable to connect to private mailbox"); + LOG.info("Connecting to private mailbox"); + + DuplexTransportConnection conn = + plugin.createConnection(properties); + + + if (conn != null) { + if (LOG.isLoggable(INFO)) + LOG.info("Connected to private mailbox"); + mailboxManager + .handleOutgoingPrivateMailboxConnection( + privateMailboxId, conn, + transportId); + return; + } } + + if (LOG.isLoggable(INFO)) + LOG.info("Unable to connect to private mailbox"); } } @Override - public void stopService() throws ServiceException { - if (mailboxLanFuture != null && !mailboxLanFuture.isCancelled()) - mailboxLanFuture.cancel(true); + public void stopService() { + if (mailboxFuture != null && !mailboxFuture.isCancelled()) + mailboxFuture.cancel(true); eventBus.removeListener(this); } }