Merge branch 'connect-to-new-contacts' into 'master'

Connect to new contacts

The motivation for this change was realising that when we add a new contact, if the Tor plugin has finished publishing its descriptor and stopped polling, we'll never try to connect to that contact via Tor. I decided that instead of making a special case for Tor, we should try to connect to new contacts via all transports.

Changes:
* Don't register outgoing connections until we've read the incoming tag - this prevents the connection indicator from blinking when connecting to a contact who's removed us
* Connect to newly activated contacts using all available transports, without waiting for the poller
* Removed device IDs from transport properties, we don't know how we're going to handle multi-device support yet


See merge request !135
This commit is contained in:
akwizgran
2016-04-06 16:13:59 +00:00
21 changed files with 454 additions and 204 deletions

View File

@@ -45,11 +45,11 @@ public interface ContactManager {
void setContactActive(ContactId c, boolean active) throws DbException;
/** Return true if a contact with this name and public key already exists */
boolean contactExists(Transaction txn, AuthorId remoteAuthorID,
boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException;
/** Return true if a contact with this name and public key already exists */
boolean contactExists(AuthorId remoteAuthorID, AuthorId localAuthorId)
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
throws DbException;
interface AddContactHook {

View File

@@ -23,7 +23,6 @@ public interface IntroductionConstants {
String MSG = "msg";
String ACCEPT = "accept";
String TIME = "time";
String DEVICE_ID = "deviceId";
String TRANSPORT = "transport";
String MESSAGE_ID = "messageId";
String MESSAGE_TIME = "timestamp";

View File

@@ -16,5 +16,7 @@ public interface ConnectionRegistry {
Collection<ContactId> getConnectedContacts(TransportId t);
boolean isConnected(ContactId c, TransportId t);
boolean isConnected(ContactId c);
}

View File

@@ -1,6 +1,5 @@
package org.briarproject.api.properties;
import org.briarproject.api.DeviceId;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException;
@@ -14,7 +13,7 @@ public interface TransportPropertyManager {
* Stores the given properties received while adding a contact - they will
* be superseded by any properties synced from the contact.
*/
void addRemoteProperties(Transaction txn, ContactId c, DeviceId dev,
void addRemoteProperties(Transaction txn, ContactId c,
Map<TransportId, TransportProperties> props) throws DbException;
/** Returns the local transport properties for all transports. */

View File

@@ -10,17 +10,22 @@ import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyParser;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.crypto.Signature;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.BdfReader;
import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.BdfWriter;
import org.briarproject.api.data.BdfWriterFactory;
import org.briarproject.api.db.ContactExistsException;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import org.briarproject.api.properties.TransportProperties;
import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.system.Clock;
import org.briarproject.api.transport.StreamReaderFactory;
import org.briarproject.api.transport.StreamWriterFactory;
@@ -29,13 +34,19 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.TransportId.MAX_TRANSPORT_ID_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
public class ContactExchangeTaskImpl extends Thread
implements ContactExchangeTask {
@@ -43,35 +54,40 @@ public class ContactExchangeTaskImpl extends Thread
private static final Logger LOG =
Logger.getLogger(ContactExchangeTaskImpl.class.getName());
private final DatabaseComponent db;
private final AuthorFactory authorFactory;
private final BdfReaderFactory bdfReaderFactory;
private final BdfWriterFactory bdfWriterFactory;
private final Clock clock;
private final ConnectionManager connectionManager;
private final ContactManager contactManager;
private final TransportPropertyManager transportPropertyManager;
private final CryptoComponent crypto;
private final StreamReaderFactory streamReaderFactory;
private final StreamWriterFactory streamWriterFactory;
private ContactExchangeListener listener;
private LocalAuthor localAuthor;
private DuplexTransportConnection conn;
private TransportId transportId;
private SecretKey masterSecret;
private boolean alice;
private volatile ContactExchangeListener listener;
private volatile LocalAuthor localAuthor;
private volatile DuplexTransportConnection conn;
private volatile TransportId transportId;
private volatile SecretKey masterSecret;
private volatile boolean alice;
public ContactExchangeTaskImpl(AuthorFactory authorFactory,
BdfReaderFactory bdfReaderFactory,
public ContactExchangeTaskImpl(DatabaseComponent db,
AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory, Clock clock,
ConnectionManager connectionManager, ContactManager contactManager,
TransportPropertyManager transportPropertyManager,
CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory) {
this.db = db;
this.authorFactory = authorFactory;
this.bdfReaderFactory = bdfReaderFactory;
this.bdfWriterFactory = bdfWriterFactory;
this.clock = clock;
this.connectionManager = connectionManager;
this.contactManager = contactManager;
this.transportPropertyManager = transportPropertyManager;
this.crypto = crypto;
this.streamReaderFactory = streamReaderFactory;
this.streamWriterFactory = streamWriterFactory;
@@ -93,24 +109,12 @@ public class ContactExchangeTaskImpl extends Thread
@Override
public void run() {
// Derive the header keys for the transport streams
SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true);
SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false);
BdfReader r;
BdfWriter w;
// Get the transport connection's input and output streams
InputStream in;
OutputStream out;
try {
// Create the readers
InputStream streamReader =
streamReaderFactory.createInvitationStreamReader(
conn.getReader().getInputStream(),
alice ? bobHeaderKey : aliceHeaderKey);
r = bdfReaderFactory.createReader(streamReader);
// Create the writers
OutputStream streamWriter =
streamWriterFactory.createInvitationStreamWriter(
conn.getWriter().getOutputStream(),
alice ? aliceHeaderKey : bobHeaderKey);
w = bdfWriterFactory.createWriter(streamWriter);
in = conn.getReader().getInputStream();
out = conn.getWriter().getOutputStream();
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
listener.contactExchangeFailed();
@@ -118,6 +122,32 @@ public class ContactExchangeTaskImpl extends Thread
return;
}
// Get the local transport properties
Map<TransportId, TransportProperties> localProperties, remoteProperties;
try {
localProperties = transportPropertyManager.getLocalProperties();
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
listener.contactExchangeFailed();
tryToClose(conn, true);
return;
}
// Derive the header keys for the transport streams
SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true);
SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false);
// Create the readers
InputStream streamReader =
streamReaderFactory.createInvitationStreamReader(in,
alice ? bobHeaderKey : aliceHeaderKey);
BdfReader r = bdfReaderFactory.createReader(streamReader);
// Create the writers
OutputStream streamWriter =
streamWriterFactory.createInvitationStreamWriter(out,
alice ? aliceHeaderKey : bobHeaderKey);
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
// Derive the nonces to be signed
byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true);
byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false);
@@ -130,13 +160,19 @@ public class ContactExchangeTaskImpl extends Thread
if (alice) {
sendPseudonym(w, aliceNonce);
sendTimestamp(w, localTimestamp);
sendTransportProperties(w, localProperties);
w.flush();
remoteAuthor = receivePseudonym(r, bobNonce);
remoteTimestamp = receiveTimestamp(r);
remoteProperties = receiveTransportProperties(r);
} else {
remoteAuthor = receivePseudonym(r, aliceNonce);
remoteTimestamp = receiveTimestamp(r);
remoteProperties = receiveTransportProperties(r);
sendPseudonym(w, bobNonce);
sendTimestamp(w, localTimestamp);
sendTransportProperties(w, localProperties);
w.flush();
}
// Close the outgoing stream and expect EOF on the incoming stream
w.close();
@@ -159,7 +195,7 @@ public class ContactExchangeTaskImpl extends Thread
try {
// Add the contact
ContactId contactId = addContact(remoteAuthor, masterSecret,
timestamp, alice);
timestamp, alice, remoteProperties);
// Reuse the connection as a transport connection
connectionManager.manageOutgoingConnection(contactId, transportId,
conn);
@@ -187,19 +223,22 @@ public class ContactExchangeTaskImpl extends Thread
signature.update(nonce);
byte[] sig = signature.sign();
// Write the name, public key and signature
w.writeListStart();
w.writeString(localAuthor.getName());
w.writeRaw(localAuthor.getPublicKey());
w.writeRaw(sig);
w.flush();
w.writeListEnd();
LOG.info("Sent pseudonym");
}
private Author receivePseudonym(BdfReader r, byte[] nonce)
throws GeneralSecurityException, IOException {
// Read the name, public key and signature
r.readListStart();
String name = r.readString(MAX_AUTHOR_NAME_LENGTH);
byte[] publicKey = r.readRaw(MAX_PUBLIC_KEY_LENGTH);
byte[] sig = r.readRaw(MAX_SIGNATURE_LENGTH);
r.readListEnd();
LOG.info("Received pseudonym");
// Verify the signature
Signature signature = crypto.getSignature();
@@ -217,7 +256,6 @@ public class ContactExchangeTaskImpl extends Thread
private void sendTimestamp(BdfWriter w, long timestamp)
throws IOException {
w.writeLong(timestamp);
w.flush();
LOG.info("Sent timestamp");
}
@@ -228,11 +266,56 @@ public class ContactExchangeTaskImpl extends Thread
return timestamp;
}
private void sendTransportProperties(BdfWriter w,
Map<TransportId, TransportProperties> local) throws IOException {
w.writeListStart();
for (Entry<TransportId, TransportProperties> e : local.entrySet())
w.writeList(BdfList.of(e.getKey().getString(), e.getValue()));
w.writeListEnd();
}
private Map<TransportId, TransportProperties> receiveTransportProperties(
BdfReader r) throws IOException {
Map<TransportId, TransportProperties> remote =
new HashMap<TransportId, TransportProperties>();
r.readListStart();
while (!r.hasListEnd()) {
r.readListStart();
String id = r.readString(MAX_TRANSPORT_ID_LENGTH);
if (id.isEmpty()) throw new FormatException();
TransportProperties p = new TransportProperties();
r.readDictionaryStart();
while (!r.hasDictionaryEnd()) {
if (p.size() == MAX_PROPERTIES_PER_TRANSPORT)
throw new FormatException();
String key = r.readString(MAX_PROPERTY_LENGTH);
String value = r.readString(MAX_PROPERTY_LENGTH);
p.put(key, value);
}
r.readDictionaryEnd();
r.readListEnd();
remote.put(new TransportId(id), p);
}
r.readListEnd();
return remote;
}
private ContactId addContact(Author remoteAuthor, SecretKey master,
long timestamp, boolean alice) throws DbException {
// Add the contact to the database
return contactManager.addContact(remoteAuthor, localAuthor.getId(),
master, timestamp, alice, true);
long timestamp, boolean alice,
Map<TransportId, TransportProperties> remoteProperties)
throws DbException {
ContactId contactId;
Transaction txn = db.startTransaction(false);
try {
contactId = contactManager.addContact(txn, remoteAuthor,
localAuthor.getId(), master, timestamp, alice, true);
transportPropertyManager.addRemoteProperties(txn, contactId,
remoteProperties);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
return contactId;
}
private void tryToClose(DuplexTransportConnection conn,

View File

@@ -126,18 +126,18 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
}
@Override
public boolean contactExists(Transaction txn, AuthorId remoteAuthorID,
public boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException {
return db.containsContact(txn, remoteAuthorID, localAuthorId);
return db.containsContact(txn, remoteAuthorId, localAuthorId);
}
@Override
public boolean contactExists(AuthorId remoteAuthorID,
public boolean contactExists(AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException {
boolean exists = false;
Transaction txn = db.startTransaction(true);
try {
exists = contactExists(txn, remoteAuthorID, localAuthorId);
exists = contactExists(txn, remoteAuthorId, localAuthorId);
txn.setComplete();
} finally {
db.endTransaction(txn);

View File

@@ -5,10 +5,11 @@ import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.BdfWriterFactory;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.system.Clock;
import org.briarproject.api.transport.StreamReaderFactory;
import org.briarproject.api.transport.StreamWriterFactory;
@@ -28,22 +29,23 @@ public class ContactModule {
@Provides
@Singleton
ContactManager getContactManager(LifecycleManager lifecycleManager,
IdentityManager identityManager,
ContactManager getContactManager(IdentityManager identityManager,
ContactManagerImpl contactManager) {
identityManager.registerRemoveIdentityHook(contactManager);
return contactManager;
}
@Provides
ContactExchangeTask provideContactExchangeTask(
ContactExchangeTask provideContactExchangeTask(DatabaseComponent db,
AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory, Clock clock,
ConnectionManager connectionManager, ContactManager contactManager,
TransportPropertyManager transportPropertyManager,
CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory) {
return new ContactExchangeTaskImpl(authorFactory, bdfReaderFactory,
return new ContactExchangeTaskImpl(db, authorFactory, bdfReaderFactory,
bdfWriterFactory, clock, connectionManager, contactManager,
crypto, streamReaderFactory, streamWriterFactory);
transportPropertyManager, crypto, streamReaderFactory,
streamWriterFactory);
}
}

View File

@@ -63,8 +63,8 @@ import static org.briarproject.db.ExponentialBackoff.calculateExpiry;
*/
abstract class JdbcDatabase implements Database<Connection> {
private static final int SCHEMA_VERSION = 22;
private static final int MIN_SCHEMA_VERSION = 22;
private static final int SCHEMA_VERSION = 23;
private static final int MIN_SCHEMA_VERSION = 23;
private static final String CREATE_SETTINGS =
"CREATE TABLE settings"

View File

@@ -34,7 +34,6 @@ import static org.briarproject.api.introduction.IntroduceeProtocolState.FINISHED
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
import static org.briarproject.api.introduction.IntroductionConstants.ANSWERED;
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1;
import static org.briarproject.api.introduction.IntroductionConstants.DEVICE_ID;
import static org.briarproject.api.introduction.IntroductionConstants.EXISTS;
import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
@@ -108,7 +107,6 @@ public class IntroduceeEngine
if (localState.getBoolean(ACCEPT)) {
msg.put(TIME, localState.getLong(OUR_TIME));
msg.put(E_PUBLIC_KEY, localState.getRaw(OUR_PUBLIC_KEY));
msg.put(DEVICE_ID, localAction.getRaw(DEVICE_ID));
msg.put(TRANSPORT, localAction.getDictionary(TRANSPORT));
}
messages.add(msg);
@@ -231,7 +229,6 @@ public class IntroduceeEngine
if (msg.getBoolean(ACCEPT)) {
localState.put(TIME, msg.getLong(TIME));
localState.put(E_PUBLIC_KEY, msg.getRaw(E_PUBLIC_KEY));
localState.put(DEVICE_ID, msg.getRaw(DEVICE_ID));
localState.put(TRANSPORT, msg.getDictionary(TRANSPORT));
}
}

View File

@@ -2,7 +2,6 @@ package org.briarproject.introduction;
import org.briarproject.api.Bytes;
import org.briarproject.api.DeviceId;
import org.briarproject.api.FormatException;
import org.briarproject.api.TransportId;
import org.briarproject.api.clients.ClientHelper;
@@ -47,7 +46,6 @@ import static org.briarproject.api.introduction.IntroductionConstants.ADDED_CONT
import static org.briarproject.api.introduction.IntroductionConstants.ANSWERED;
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT;
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1;
import static org.briarproject.api.introduction.IntroductionConstants.DEVICE_ID;
import static org.briarproject.api.introduction.IntroductionConstants.EXISTS;
import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
@@ -167,7 +165,6 @@ class IntroduceeManager {
// get data to connect and derive a shared secret later
long now = clock.currentTimeMillis();
byte[] deviceId = db.getDeviceId(txn).getBytes();
KeyPair keyPair = cryptoComponent.generateAgreementKeyPair();
byte[] publicKey = keyPair.getPublic().getEncoded();
byte[] privateKey = keyPair.getPrivate().getEncoded();
@@ -183,14 +180,12 @@ class IntroduceeManager {
// define action
BdfDictionary localAction = new BdfDictionary();
localAction.put(TYPE, TYPE_RESPONSE);
localAction.put(DEVICE_ID, deviceId);
localAction.put(TRANSPORT,
encodeTransportProperties(transportProperties));
// start engine and process its state update
IntroduceeEngine engine = new IntroduceeEngine();
processStateUpdate(txn,
engine.onLocalAction(state, localAction));
processStateUpdate(txn, engine.onLocalAction(state, localAction));
}
public void declineIntroduction(Transaction txn, final SessionId sessionId)
@@ -313,11 +308,10 @@ class IntroduceeManager {
localState.put(ADDED_CONTACT_ID, contactId.getInt());
// let the transport manager know how to connect to the contact
DeviceId deviceId = new DeviceId(localState.getRaw(DEVICE_ID));
Map<TransportId, TransportProperties> transportProperties =
parseTransportProperties(localState);
transportPropertyManager.addRemoteProperties(txn, contactId,
deviceId, transportProperties);
transportProperties);
// delete the ephemeral private key by overwriting with NULL value
// this ensures future ephemeral keys can not be recovered when

View File

@@ -20,8 +20,6 @@ import org.briarproject.util.StringUtils;
import java.io.IOException;
import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.introduction.IntroducerProtocolState.PREPARE_REQUESTS;
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_1;

View File

@@ -1,8 +1,6 @@
package org.briarproject.introduction;
import org.briarproject.api.DeviceId;
import org.briarproject.api.FormatException;
import org.briarproject.api.TransportId;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList;
@@ -13,10 +11,10 @@ import org.briarproject.api.sync.Message;
import org.briarproject.api.system.Clock;
import org.briarproject.clients.BdfMessageValidator;
import static org.briarproject.api.TransportId.MAX_TRANSPORT_ID_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
import static org.briarproject.api.introduction.IntroductionConstants.DEVICE_ID;
import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_ID;
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME;
@@ -31,6 +29,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ABORT
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
@@ -102,17 +101,16 @@ class IntroductionValidator extends BdfMessageValidator {
private BdfDictionary validateResponse(BdfList message)
throws FormatException {
checkSize(message, 3, 7);
checkSize(message, 3, 6);
// parse accept/decline
boolean accept = message.getBoolean(2);
long time = 0;
byte[] pubkey = null;
byte[] deviceId = null;
BdfDictionary tp = new BdfDictionary();
if (accept) {
checkSize(message, 7);
checkSize(message, 6);
// parse timestamp
time = message.getLong(3);
@@ -121,16 +119,13 @@ class IntroductionValidator extends BdfMessageValidator {
pubkey = message.getRaw(4);
checkLength(pubkey, 0, MAX_PUBLIC_KEY_LENGTH);
// parse device ID
deviceId = message.getRaw(5);
checkLength(deviceId, DeviceId.LENGTH);
// parse transport properties
tp = message.getDictionary(6);
tp = message.getDictionary(5);
if (tp.size() < 1) throw new FormatException();
for (String tId : tp.keySet()) {
checkLength(tId, 1, TransportId.MAX_TRANSPORT_ID_LENGTH);
checkLength(tId, 1, MAX_TRANSPORT_ID_LENGTH);
BdfDictionary tProps = tp.getDictionary(tId);
checkSize(tProps, MAX_PROPERTIES_PER_TRANSPORT);
for (String propId : tProps.keySet()) {
checkLength(propId, 0, MAX_PROPERTY_LENGTH);
String prop = tProps.getString(propId);
@@ -147,7 +142,6 @@ class IntroductionValidator extends BdfMessageValidator {
if (accept) {
d.put(TIME, time);
d.put(E_PUBLIC_KEY, pubkey);
d.put(DEVICE_ID, deviceId);
d.put(TRANSPORT, tp);
}
return d;

View File

@@ -5,7 +5,6 @@ import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList;
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
import static org.briarproject.api.introduction.IntroductionConstants.DEVICE_ID;
import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
import static org.briarproject.api.introduction.IntroductionConstants.MSG;
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
@@ -21,7 +20,8 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPO
public class MessageEncoder {
public static BdfList encodeMessage(BdfDictionary d) throws FormatException {
public static BdfList encodeMessage(BdfDictionary d)
throws FormatException {
BdfList body;
long type = d.getLong(TYPE);
@@ -39,7 +39,8 @@ public class MessageEncoder {
return body;
}
private static BdfList encodeRequest(BdfDictionary d) throws FormatException {
private static BdfList encodeRequest(BdfDictionary d)
throws FormatException {
BdfList list = BdfList.of(TYPE_REQUEST, d.getRaw(SESSION_ID),
d.getString(NAME), d.getRaw(PUBLIC_KEY));
@@ -49,14 +50,14 @@ public class MessageEncoder {
return list;
}
private static BdfList encodeResponse(BdfDictionary d) throws FormatException {
private static BdfList encodeResponse(BdfDictionary d)
throws FormatException {
BdfList list = BdfList.of(TYPE_RESPONSE, d.getRaw(SESSION_ID),
d.getBoolean(ACCEPT));
if (d.getBoolean(ACCEPT)) {
list.add(d.getLong(TIME));
list.add(d.getRaw(E_PUBLIC_KEY));
list.add(d.getRaw(DEVICE_ID));
list.add(d.getDictionary(TRANSPORT));
}
// TODO Sign the response, see #256

View File

@@ -191,7 +191,7 @@ class ConnectionManagerImpl implements ConnectionManager {
}
if (ctx == null) {
LOG.warning("Could not allocate stream context");
disposeWriter(false);
disposeWriter(true);
return;
}
connectionRegistry.registerConnection(contactId, transportId);
@@ -286,7 +286,7 @@ class ConnectionManagerImpl implements ConnectionManager {
}
if (ctx == null) {
LOG.warning("Could not allocate stream context");
disposeWriter(false);
disposeWriter(true);
return;
}
try {
@@ -351,10 +351,9 @@ class ConnectionManagerImpl implements ConnectionManager {
}
if (ctx == null) {
LOG.warning("Could not allocate stream context");
disposeWriter(false);
disposeWriter(true);
return;
}
connectionRegistry.registerConnection(contactId, transportId);
// Start the incoming session on another thread
ioExecutor.execute(new Runnable() {
public void run() {
@@ -369,8 +368,6 @@ class ConnectionManagerImpl implements ConnectionManager {
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeWriter(true);
} finally {
connectionRegistry.unregisterConnection(contactId, transportId);
}
}
@@ -382,17 +379,17 @@ class ConnectionManagerImpl implements ConnectionManager {
ctx = keyManager.getStreamContext(transportId, tag);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, true);
disposeReader(true, false);
return;
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, true);
disposeReader(true, false);
return;
}
// Unrecognised tags are suspicious in this case
if (ctx == null) {
LOG.warning("Unrecognised tag for returning stream");
disposeReader(true, true);
disposeReader(true, false);
return;
}
// Check that the stream comes from the expected contact
@@ -401,6 +398,7 @@ class ConnectionManagerImpl implements ConnectionManager {
disposeReader(true, true);
return;
}
connectionRegistry.registerConnection(contactId, transportId);
try {
// Create and run the incoming session
incomingSession = createIncomingSession(ctx, reader);
@@ -409,6 +407,8 @@ class ConnectionManagerImpl implements ConnectionManager {
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, true);
} finally {
connectionRegistry.unregisterConnection(contactId, transportId);
}
}

View File

@@ -41,7 +41,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
}
public void registerConnection(ContactId c, TransportId t) {
LOG.info("Connection registered");
if (LOG.isLoggable(INFO)) LOG.info("Connection registered: " + t);
boolean firstConnection = false;
lock.lock();
try {
@@ -63,7 +63,6 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
} finally {
lock.unlock();
}
if (firstConnection) {
LOG.info("Contact connected");
eventBus.broadcast(new ContactConnectedEvent(c));
@@ -71,7 +70,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
}
public void unregisterConnection(ContactId c, TransportId t) {
LOG.info("Connection unregistered");
if (LOG.isLoggable(INFO)) LOG.info("Connection unregistered: " + t);
boolean lastConnection = false;
lock.lock();
try {
@@ -95,15 +94,13 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
} finally {
lock.unlock();
}
if (lastConnection) {
LOG.info("Contact disconnected");
eventBus.broadcast(new ContactDisconnectedEvent(c));
}
}
public Collection<ContactId> getConnectedContacts(
TransportId t) {
public Collection<ContactId> getConnectedContacts(TransportId t) {
lock.lock();
try {
Map<ContactId, Integer> m = connections.get(t);
@@ -115,7 +112,16 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
} finally {
lock.unlock();
}
}
public boolean isConnected(ContactId c, TransportId t) {
lock.lock();
try {
Map<ContactId, Integer> m = connections.get(t);
return m != null && m.containsKey(c);
} finally {
lock.unlock();
}
}
public boolean isConnected(ContactId c) {
@@ -125,6 +131,5 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
} finally {
lock.unlock();
}
}
}

View File

@@ -3,13 +3,17 @@ package org.briarproject.plugins;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException;
import org.briarproject.api.event.ContactStatusChangedEvent;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.TransportDisabledEvent;
import org.briarproject.api.event.TransportEnabledEvent;
import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.lifecycle.Service;
import org.briarproject.api.lifecycle.ServiceException;
import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.PluginCallback;
import org.briarproject.api.plugins.PluginConfig;
@@ -46,7 +50,7 @@ import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
class PluginManagerImpl implements PluginManager, Service {
class PluginManagerImpl implements PluginManager, Service, EventListener {
private static final Logger LOG =
Logger.getLogger(PluginManagerImpl.class.getName());
@@ -56,6 +60,7 @@ class PluginManagerImpl implements PluginManager, Service {
private final PluginConfig pluginConfig;
private final Poller poller;
private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry;
private final SettingsManager settingsManager;
private final TransportPropertyManager transportPropertyManager;
private final UiCallback uiCallback;
@@ -67,6 +72,7 @@ class PluginManagerImpl implements PluginManager, Service {
PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus,
PluginConfig pluginConfig, Poller poller,
ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry,
SettingsManager settingsManager,
TransportPropertyManager transportPropertyManager,
UiCallback uiCallback) {
@@ -75,6 +81,7 @@ class PluginManagerImpl implements PluginManager, Service {
this.pluginConfig = pluginConfig;
this.poller = poller;
this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry;
this.settingsManager = settingsManager;
this.transportPropertyManager = transportPropertyManager;
this.uiCallback = uiCallback;
@@ -106,10 +113,14 @@ class PluginManagerImpl implements PluginManager, Service {
} catch (InterruptedException e) {
throw new ServiceException(e);
}
// Listen for events
eventBus.addListener(this);
}
@Override
public void stopService() throws ServiceException {
// Stop listening for events
eventBus.removeListener(this);
// Stop the poller
LOG.info("Stopping poller");
poller.stop();
@@ -122,9 +133,6 @@ class PluginManagerImpl implements PluginManager, Service {
LOG.info("Stopping duplex plugins");
for (DuplexPlugin plugin : duplexPlugins)
ioExecutor.execute(new PluginStopper(plugin, latch));
plugins.clear();
simplexPlugins.clear();
duplexPlugins.clear();
// Wait for all the plugins to stop
try {
latch.await();
@@ -151,6 +159,47 @@ class PluginManagerImpl implements PluginManager, Service {
return Collections.unmodifiableList(supported);
}
@Override
public void eventOccurred(Event e) {
if (e instanceof ContactStatusChangedEvent) {
ContactStatusChangedEvent c = (ContactStatusChangedEvent) e;
if (c.isActive()) connectToContact(c.getContactId());
}
}
private void connectToContact(ContactId c) {
for (SimplexPlugin s : simplexPlugins)
if (s.shouldPoll()) connectToContact(c, s);
for (DuplexPlugin d : duplexPlugins)
if (d.shouldPoll()) connectToContact(c, d);
}
private void connectToContact(final ContactId c, final SimplexPlugin p) {
ioExecutor.execute(new Runnable() {
public void run() {
TransportId t = p.getId();
if (!connectionRegistry.isConnected(c, t)) {
TransportConnectionWriter w = p.createWriter(c);
if (w != null)
connectionManager.manageOutgoingConnection(c, t, w);
}
}
});
}
private void connectToContact(final ContactId c, final DuplexPlugin p) {
ioExecutor.execute(new Runnable() {
public void run() {
TransportId t = p.getId();
if (!connectionRegistry.isConnected(c, t)) {
DuplexTransportConnection d = p.createConnection(c);
if (d != null)
connectionManager.manageOutgoingConnection(c, t, d);
}
}
});
}
private class SimplexPluginStarter implements Runnable {
private final SimplexPluginFactory factory;

View File

@@ -45,12 +45,13 @@ class PollerImpl implements Poller {
public void addPlugin(Plugin p) {
// Randomise first polling interval
schedule(p, randomise(p.getPollingInterval()), false);
if (p.shouldPoll())
schedule(p, randomise(p.getPollingInterval()), false);
}
public void pollNow(Plugin p) {
// Randomise next polling interval
schedule(p, 0, true);
if (p.shouldPoll()) schedule(p, 0, true);
}
private int randomise(int interval) {

View File

@@ -1,6 +1,5 @@
package org.briarproject.properties;
import org.briarproject.api.DeviceId;
import org.briarproject.api.FormatException;
import org.briarproject.api.TransportId;
import org.briarproject.api.clients.Client;
@@ -73,10 +72,9 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
db.addGroup(txn, g);
db.setVisibleToContact(txn, c.getId(), g.getId(), true);
// Copy the latest local properties into the group
DeviceId dev = db.getDeviceId(txn);
Map<TransportId, TransportProperties> local = getLocalProperties(txn);
for (Entry<TransportId, TransportProperties> e : local.entrySet()) {
storeMessage(txn, g.getId(), dev, e.getKey(), e.getValue(), 1,
storeMessage(txn, g.getId(), e.getKey(), e.getValue(), 1,
true, true);
}
}
@@ -87,11 +85,11 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
}
@Override
public void addRemoteProperties(Transaction txn, ContactId c, DeviceId dev,
public void addRemoteProperties(Transaction txn, ContactId c,
Map<TransportId, TransportProperties> props) throws DbException {
Group g = getContactGroup(db.getContact(txn, c));
for (Entry<TransportId, TransportProperties> e : props.entrySet()) {
storeMessage(txn, g.getId(), dev, e.getKey(), e.getValue(), 0,
storeMessage(txn, g.getId(), e.getKey(), e.getValue(), 0,
false, false);
}
}
@@ -189,16 +187,15 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
}
if (changed) {
// Store the merged properties in the local group
DeviceId dev = db.getDeviceId(txn);
long version = latest == null ? 1 : latest.version + 1;
storeMessage(txn, localGroup.getId(), dev, t, merged,
version, true, false);
storeMessage(txn, localGroup.getId(), t, merged, version,
true, false);
// Store the merged properties in each contact's group
for (Contact c : db.getContacts(txn)) {
Group g = getContactGroup(c);
latest = findLatest(txn, g.getId(), t, true);
version = latest == null ? 1 : latest.version + 1;
storeMessage(txn, g.getId(), dev, t, merged, version,
storeMessage(txn, g.getId(), t, merged, version,
true, true);
}
}
@@ -235,11 +232,11 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
}
}
private void storeMessage(Transaction txn, GroupId g, DeviceId dev,
TransportId t, TransportProperties p, long version, boolean local,
boolean shared) throws DbException {
private void storeMessage(Transaction txn, GroupId g, TransportId t,
TransportProperties p, long version, boolean local, boolean shared)
throws DbException {
try {
BdfList body = encodeProperties(dev, t, p, version);
BdfList body = encodeProperties(t, p, version);
long now = clock.currentTimeMillis();
Message m = clientHelper.createMessage(g, now, body);
BdfDictionary meta = new BdfDictionary();
@@ -252,9 +249,9 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
}
}
private BdfList encodeProperties(DeviceId dev, TransportId t,
TransportProperties p, long version) {
return BdfList.of(dev, t.getString(), version, p);
private BdfList encodeProperties(TransportId t, TransportProperties p,
long version) {
return BdfList.of(t.getString(), version, p);
}
private Map<TransportId, LatestUpdate> findLatest(Transaction txn,
@@ -295,8 +292,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
private TransportProperties parseProperties(BdfList message)
throws FormatException {
// Device ID, transport ID, version, properties
BdfDictionary dictionary = message.getDictionary(3);
// Transport ID, version, properties
BdfDictionary dictionary = message.getDictionary(2);
TransportProperties p = new TransportProperties();
for (String key : dictionary.keySet())
p.put(key, dictionary.getString(key));

View File

@@ -1,7 +1,6 @@
package org.briarproject.properties;
import org.briarproject.api.FormatException;
import org.briarproject.api.UniqueId;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList;
@@ -25,19 +24,16 @@ public class TransportPropertyValidator extends BdfMessageValidator {
@Override
protected BdfDictionary validateMessage(Message m, Group g,
BdfList body) throws FormatException {
// Device ID, transport ID, version, properties
checkSize(body, 4);
// Device ID
byte[] deviceId = body.getRaw(0);
checkLength(deviceId, UniqueId.LENGTH);
// Transport ID, version, properties
checkSize(body, 3);
// Transport ID
String transportId = body.getString(1);
String transportId = body.getString(0);
checkLength(transportId, 1, MAX_TRANSPORT_ID_LENGTH);
// Version
long version = body.getLong(2);
long version = body.getLong(1);
if (version < 0) throw new FormatException();
// Properties
BdfDictionary dictionary = body.getDictionary(3);
BdfDictionary dictionary = body.getDictionary(2);
checkSize(dictionary, 0, MAX_PROPERTIES_PER_TRANSPORT);
for (String key : dictionary.keySet()) {
checkLength(key, 0, MAX_PROPERTY_LENGTH);

View File

@@ -1,13 +1,20 @@
package org.briarproject.plugins;
import org.briarproject.BriarTestCase;
import org.briarproject.ImmediateExecutor;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.event.ContactStatusChangedEvent;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.plugins.PluginConfig;
import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import org.briarproject.api.plugins.simplex.SimplexPlugin;
import org.briarproject.api.plugins.simplex.SimplexPluginCallback;
import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
@@ -36,6 +43,8 @@ public class PluginManagerImplTest extends BriarTestCase {
final Poller poller = context.mock(Poller.class);
final ConnectionManager connectionManager =
context.mock(ConnectionManager.class);
final ConnectionRegistry connectionRegistry =
context.mock(ConnectionRegistry.class);
final SettingsManager settingsManager =
context.mock(SettingsManager.class);
final TransportPropertyManager transportPropertyManager =
@@ -63,6 +72,7 @@ public class PluginManagerImplTest extends BriarTestCase {
final TransportId duplexFailId = new TransportId("duplex1");
context.checking(new Expectations() {{
// start()
// First simplex plugin
oneOf(pluginConfig).getSimplexFactories();
will(returnValue(Arrays.asList(simplexFactory,
@@ -103,6 +113,11 @@ public class PluginManagerImplTest extends BriarTestCase {
oneOf(duplexFailFactory).createPlugin(with(any(
DuplexPluginCallback.class)));
will(returnValue(null)); // Failed to create a plugin
// Start listening for events
oneOf(eventBus).addListener(with(any(EventListener.class)));
// stop()
// Stop listening for events
oneOf(eventBus).removeListener(with(any(EventListener.class)));
// Stop the poller
oneOf(poller).stop();
// Stop the plugins
@@ -111,8 +126,8 @@ public class PluginManagerImplTest extends BriarTestCase {
}});
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus,
pluginConfig, poller, connectionManager, settingsManager,
transportPropertyManager, uiCallback);
pluginConfig, poller, connectionManager, connectionRegistry,
settingsManager, transportPropertyManager, uiCallback);
// Two plugins should be started and stopped
p.startService();
@@ -120,4 +135,151 @@ public class PluginManagerImplTest extends BriarTestCase {
context.assertIsSatisfied();
}
@Test
public void testConnectToNewContact() throws Exception {
Mockery context = new Mockery();
final Executor ioExecutor = new ImmediateExecutor();
final EventBus eventBus = context.mock(EventBus.class);
final PluginConfig pluginConfig = context.mock(PluginConfig.class);
final Poller poller = context.mock(Poller.class);
final ConnectionManager connectionManager =
context.mock(ConnectionManager.class);
final ConnectionRegistry connectionRegistry =
context.mock(ConnectionRegistry.class);
final SettingsManager settingsManager =
context.mock(SettingsManager.class);
final TransportPropertyManager transportPropertyManager =
context.mock(TransportPropertyManager.class);
final UiCallback uiCallback = context.mock(UiCallback.class);
final TransportConnectionWriter transportConnectionWriter =
context.mock(TransportConnectionWriter.class);
final DuplexTransportConnection duplexTransportConnection =
context.mock(DuplexTransportConnection.class);
final ContactId contactId = new ContactId(234);
// Two simplex plugins: one supports polling, the other doesn't
final SimplexPluginFactory simplexFactory =
context.mock(SimplexPluginFactory.class);
final SimplexPlugin simplexPlugin = context.mock(SimplexPlugin.class);
final TransportId simplexId = new TransportId("simplex");
final SimplexPluginFactory simplexFactory1 =
context.mock(SimplexPluginFactory.class, "simplexFactory1");
final SimplexPlugin simplexPlugin1 =
context.mock(SimplexPlugin.class, "simplexPlugin1");
final TransportId simplexId1 = new TransportId("simplex1");
// Two duplex plugins: one supports polling, the other doesn't
final DuplexPluginFactory duplexFactory =
context.mock(DuplexPluginFactory.class);
final DuplexPlugin duplexPlugin = context.mock(DuplexPlugin.class);
final TransportId duplexId = new TransportId("duplex");
final DuplexPluginFactory duplexFactory1 =
context.mock(DuplexPluginFactory.class, "duplexFactory1");
final DuplexPlugin duplexPlugin1 =
context.mock(DuplexPlugin.class, "duplexPlugin1");
final TransportId duplexId1 = new TransportId("duplex1");
context.checking(new Expectations() {{
// start()
// First simplex plugin
oneOf(pluginConfig).getSimplexFactories();
will(returnValue(Arrays.asList(simplexFactory, simplexFactory1)));
oneOf(simplexFactory).getId();
will(returnValue(simplexId));
oneOf(simplexFactory).createPlugin(with(any(
SimplexPluginCallback.class)));
will(returnValue(simplexPlugin)); // Created
oneOf(simplexPlugin).start();
will(returnValue(true)); // Started
oneOf(simplexPlugin).shouldPoll();
will(returnValue(true)); // Should poll
oneOf(poller).addPlugin(simplexPlugin);
// Second simplex plugin
oneOf(simplexFactory1).getId();
will(returnValue(simplexId1));
oneOf(simplexFactory1).createPlugin(with(any(
SimplexPluginCallback.class)));
will(returnValue(simplexPlugin1)); // Created
oneOf(simplexPlugin1).start();
will(returnValue(true)); // Started
oneOf(simplexPlugin1).shouldPoll();
will(returnValue(false)); // Should not poll
// First duplex plugin
oneOf(pluginConfig).getDuplexFactories();
will(returnValue(Arrays.asList(duplexFactory, duplexFactory1)));
oneOf(duplexFactory).getId();
will(returnValue(duplexId));
oneOf(duplexFactory).createPlugin(with(any(
DuplexPluginCallback.class)));
will(returnValue(duplexPlugin)); // Created
oneOf(duplexPlugin).start();
will(returnValue(true)); // Started
oneOf(duplexPlugin).shouldPoll();
will(returnValue(true)); // Should poll
oneOf(poller).addPlugin(duplexPlugin);
// Second duplex plugin
oneOf(duplexFactory1).getId();
will(returnValue(duplexId1));
oneOf(duplexFactory1).createPlugin(with(any(
DuplexPluginCallback.class)));
will(returnValue(duplexPlugin1)); // Created
oneOf(duplexPlugin1).start();
will(returnValue(true)); // Started
oneOf(duplexPlugin1).shouldPoll();
will(returnValue(false)); // Should not poll
// Start listening for events
oneOf(eventBus).addListener(with(any(EventListener.class)));
// eventOccurred()
// First simplex plugin
oneOf(simplexPlugin).shouldPoll();
will(returnValue(true));
oneOf(simplexPlugin).getId();
will(returnValue(simplexId));
oneOf(connectionRegistry).isConnected(contactId, simplexId);
will(returnValue(false));
oneOf(simplexPlugin).createWriter(contactId);
will(returnValue(transportConnectionWriter));
oneOf(connectionManager).manageOutgoingConnection(contactId,
simplexId, transportConnectionWriter);
// Second simplex plugin
oneOf(simplexPlugin1).shouldPoll();
will(returnValue(false));
// First duplex plugin
oneOf(duplexPlugin).shouldPoll();
will(returnValue(true));
oneOf(duplexPlugin).getId();
will(returnValue(duplexId));
oneOf(connectionRegistry).isConnected(contactId, duplexId);
will(returnValue(false));
oneOf(duplexPlugin).createConnection(contactId);
will(returnValue(duplexTransportConnection));
oneOf(connectionManager).manageOutgoingConnection(contactId,
duplexId, duplexTransportConnection);
// Second duplex plugin
oneOf(duplexPlugin1).shouldPoll();
will(returnValue(false));
// stop()
// Stop listening for events
oneOf(eventBus).removeListener(with(any(EventListener.class)));
// Stop the poller
oneOf(poller).stop();
// Stop the plugins
oneOf(simplexPlugin).stop();
oneOf(simplexPlugin1).stop();
oneOf(duplexPlugin).stop();
oneOf(duplexPlugin1).stop();
}});
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus,
pluginConfig, poller, connectionManager, connectionRegistry,
settingsManager, transportPropertyManager, uiCallback);
p.startService();
p.eventOccurred(new ContactStatusChangedEvent(contactId, true));
p.stopService();
context.assertIsSatisfied();
}
}

View File

@@ -2,10 +2,8 @@ package org.briarproject.properties;
import org.briarproject.BriarTestCase;
import org.briarproject.TestUtils;
import org.briarproject.api.DeviceId;
import org.briarproject.api.FormatException;
import org.briarproject.api.TransportId;
import org.briarproject.api.UniqueId;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList;
@@ -28,110 +26,83 @@ import static org.junit.Assert.assertEquals;
public class TransportPropertyValidatorTest extends BriarTestCase {
private final TransportId transportId;
private final DeviceId deviceId;
private final BdfDictionary bdfDictionary;
private final Group group;
private final Message message;
private final TransportPropertyValidator tpv;
public TransportPropertyValidatorTest() {
public TransportPropertyValidatorTest() {
transportId = new TransportId("test");
deviceId = new DeviceId(TestUtils.getRandomId());
bdfDictionary = new BdfDictionary();
bdfDictionary = new BdfDictionary();
GroupId groupId = new GroupId(TestUtils.getRandomId());
ClientId clientId = new ClientId(TestUtils.getRandomId());
byte[] descriptor = TestUtils.getRandomBytes(12);
group = new Group(groupId, clientId, descriptor);
GroupId groupId = new GroupId(TestUtils.getRandomId());
ClientId clientId = new ClientId(TestUtils.getRandomId());
byte[] descriptor = TestUtils.getRandomBytes(12);
group = new Group(groupId, clientId, descriptor);
MessageId messageId = new MessageId(TestUtils.getRandomId());
long timestamp = System.currentTimeMillis();
byte[] body = TestUtils.getRandomBytes(123);
message = new Message(messageId, groupId, timestamp, body);
MessageId messageId = new MessageId(TestUtils.getRandomId());
long timestamp = System.currentTimeMillis();
byte[] body = TestUtils.getRandomBytes(123);
message = new Message(messageId, groupId, timestamp, body);
Mockery context = new Mockery();
ClientHelper clientHelper = context.mock(ClientHelper.class);
MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class);
Clock clock = context.mock(Clock.class);
Mockery context = new Mockery();
ClientHelper clientHelper = context.mock(ClientHelper.class);
MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class);
Clock clock = context.mock(Clock.class);
tpv = new TransportPropertyValidator(clientHelper, metadataEncoder,
clock);
}
}
@Test
public void testValidateProperMessage() throws IOException {
@Test
public void testValidateProperMessage() throws IOException {
BdfList body = BdfList.of(deviceId, transportId.getString(), 4,
bdfDictionary);
BdfList body = BdfList.of(transportId.getString(), 4, bdfDictionary);
BdfDictionary result = tpv.validateMessage(message, group, body);
BdfDictionary result = tpv.validateMessage(message, group, body);
assertEquals("test", result.getString("transportId"));
assertEquals(result.getLong("version").longValue(), 4);
assertEquals(4, result.getLong("version").longValue());
}
@Test(expected = FormatException.class)
public void testValidateWrongVersionValue() throws IOException {
@Test(expected = FormatException.class)
public void testValidateWrongVersionValue() throws IOException {
/* Will create a negative version number */
BdfList body = BdfList.of(deviceId, transportId.getString(), -1,
BdfList body = BdfList.of(transportId.getString(), -1, bdfDictionary);
tpv.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateWrongVersionType() throws IOException {
BdfList body = BdfList.of(transportId.getString(), bdfDictionary,
bdfDictionary);
tpv.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateWrongVersionType() throws IOException {
@Test(expected = FormatException.class)
public void testValidateLongTransportId() throws IOException {
/* Instead of sending a version number I'm sending a dict */
BdfList body = BdfList.of(deviceId, transportId.getString(),
bdfDictionary, bdfDictionary);
tpv.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateShortDeviceId() throws IOException {
/* Will create a Device Id with a short length, getRaw should work */
BdfList body = BdfList.of(new byte[UniqueId.LENGTH - 1],
transportId.getString(), 1, bdfDictionary);
tpv.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateLongDeviceId() throws IOException {
BdfList body = BdfList.of(new byte[UniqueId.LENGTH + 1],
transportId.getString(), 1, bdfDictionary);
tpv.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateWrongDeviceId() throws IOException {
BdfList body = BdfList.of(bdfDictionary, transportId.getString(), 1,
bdfDictionary);
tpv.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateLongTransportId() throws IOException {
/* Generate a string or arbitrary length for the transport id*/
String wrongTransportIdString =
TestUtils.getRandomString(MAX_TRANSPORT_ID_LENGTH + 1);
BdfList body = BdfList.of(deviceId, wrongTransportIdString, 4,
bdfDictionary);
BdfList body = BdfList.of(wrongTransportIdString, 4, bdfDictionary);
tpv.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateTooManyProperties() throws IOException {
@Test(expected = FormatException.class)
public void testValidateEmptyTransportId() throws IOException {
/* Generate a big map for the BdfDictionary*/
BdfDictionary d = new BdfDictionary();
BdfList body = BdfList.of("", 4, bdfDictionary);
tpv.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateTooManyProperties() throws IOException {
BdfDictionary d = new BdfDictionary();
for (int i = 0; i < MAX_PROPERTIES_PER_TRANSPORT + 1; i++)
d.put(String.valueOf(i), i);
BdfList body = BdfList.of(deviceId, transportId.getString(), 4, d);
BdfList body = BdfList.of(transportId.getString(), 4, d);
tpv.validateMessage(message, group, body);
}
}