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; void setContactActive(ContactId c, boolean active) throws DbException;
/** Return true if a contact with this name and public key already exists */ /** 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; AuthorId localAuthorId) throws DbException;
/** Return true if a contact with this name and public key already exists */ /** 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; throws DbException;
interface AddContactHook { interface AddContactHook {

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
package org.briarproject.api.properties; package org.briarproject.api.properties;
import org.briarproject.api.DeviceId;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
@@ -14,7 +13,7 @@ public interface TransportPropertyManager {
* Stores the given properties received while adding a contact - they will * Stores the given properties received while adding a contact - they will
* be superseded by any properties synced from the contact. * 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; Map<TransportId, TransportProperties> props) throws DbException;
/** Returns the local transport properties for all transports. */ /** 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.KeyParser;
import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.crypto.Signature; import org.briarproject.api.crypto.Signature;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.BdfReader; import org.briarproject.api.data.BdfReader;
import org.briarproject.api.data.BdfReaderFactory; import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.BdfWriter; import org.briarproject.api.data.BdfWriter;
import org.briarproject.api.data.BdfWriterFactory; import org.briarproject.api.data.BdfWriterFactory;
import org.briarproject.api.db.ContactExistsException; import org.briarproject.api.db.ContactExistsException;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.identity.Author; import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorFactory; import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.plugins.ConnectionManager; import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection; 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.system.Clock;
import org.briarproject.api.transport.StreamReaderFactory; import org.briarproject.api.transport.StreamReaderFactory;
import org.briarproject.api.transport.StreamWriterFactory; import org.briarproject.api.transport.StreamWriterFactory;
@@ -29,13 +34,19 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger; import java.util.logging.Logger;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; 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_AUTHOR_NAME_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_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.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 public class ContactExchangeTaskImpl extends Thread
implements ContactExchangeTask { implements ContactExchangeTask {
@@ -43,35 +54,40 @@ public class ContactExchangeTaskImpl extends Thread
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(ContactExchangeTaskImpl.class.getName()); Logger.getLogger(ContactExchangeTaskImpl.class.getName());
private final DatabaseComponent db;
private final AuthorFactory authorFactory; private final AuthorFactory authorFactory;
private final BdfReaderFactory bdfReaderFactory; private final BdfReaderFactory bdfReaderFactory;
private final BdfWriterFactory bdfWriterFactory; private final BdfWriterFactory bdfWriterFactory;
private final Clock clock; private final Clock clock;
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final ContactManager contactManager; private final ContactManager contactManager;
private final TransportPropertyManager transportPropertyManager;
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final StreamReaderFactory streamReaderFactory; private final StreamReaderFactory streamReaderFactory;
private final StreamWriterFactory streamWriterFactory; private final StreamWriterFactory streamWriterFactory;
private ContactExchangeListener listener; private volatile ContactExchangeListener listener;
private LocalAuthor localAuthor; private volatile LocalAuthor localAuthor;
private DuplexTransportConnection conn; private volatile DuplexTransportConnection conn;
private TransportId transportId; private volatile TransportId transportId;
private SecretKey masterSecret; private volatile SecretKey masterSecret;
private boolean alice; private volatile boolean alice;
public ContactExchangeTaskImpl(AuthorFactory authorFactory, public ContactExchangeTaskImpl(DatabaseComponent db,
BdfReaderFactory bdfReaderFactory, AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory, Clock clock, BdfWriterFactory bdfWriterFactory, Clock clock,
ConnectionManager connectionManager, ContactManager contactManager, ConnectionManager connectionManager, ContactManager contactManager,
TransportPropertyManager transportPropertyManager,
CryptoComponent crypto, StreamReaderFactory streamReaderFactory, CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory) { StreamWriterFactory streamWriterFactory) {
this.db = db;
this.authorFactory = authorFactory; this.authorFactory = authorFactory;
this.bdfReaderFactory = bdfReaderFactory; this.bdfReaderFactory = bdfReaderFactory;
this.bdfWriterFactory = bdfWriterFactory; this.bdfWriterFactory = bdfWriterFactory;
this.clock = clock; this.clock = clock;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.contactManager = contactManager; this.contactManager = contactManager;
this.transportPropertyManager = transportPropertyManager;
this.crypto = crypto; this.crypto = crypto;
this.streamReaderFactory = streamReaderFactory; this.streamReaderFactory = streamReaderFactory;
this.streamWriterFactory = streamWriterFactory; this.streamWriterFactory = streamWriterFactory;
@@ -93,24 +109,12 @@ public class ContactExchangeTaskImpl extends Thread
@Override @Override
public void run() { public void run() {
// Derive the header keys for the transport streams // Get the transport connection's input and output streams
SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true); InputStream in;
SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false); OutputStream out;
BdfReader r;
BdfWriter w;
try { try {
// Create the readers in = conn.getReader().getInputStream();
InputStream streamReader = out = conn.getWriter().getOutputStream();
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);
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
listener.contactExchangeFailed(); listener.contactExchangeFailed();
@@ -118,6 +122,32 @@ public class ContactExchangeTaskImpl extends Thread
return; 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 // Derive the nonces to be signed
byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true); byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true);
byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false); byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false);
@@ -130,13 +160,19 @@ public class ContactExchangeTaskImpl extends Thread
if (alice) { if (alice) {
sendPseudonym(w, aliceNonce); sendPseudonym(w, aliceNonce);
sendTimestamp(w, localTimestamp); sendTimestamp(w, localTimestamp);
sendTransportProperties(w, localProperties);
w.flush();
remoteAuthor = receivePseudonym(r, bobNonce); remoteAuthor = receivePseudonym(r, bobNonce);
remoteTimestamp = receiveTimestamp(r); remoteTimestamp = receiveTimestamp(r);
remoteProperties = receiveTransportProperties(r);
} else { } else {
remoteAuthor = receivePseudonym(r, aliceNonce); remoteAuthor = receivePseudonym(r, aliceNonce);
remoteTimestamp = receiveTimestamp(r); remoteTimestamp = receiveTimestamp(r);
remoteProperties = receiveTransportProperties(r);
sendPseudonym(w, bobNonce); sendPseudonym(w, bobNonce);
sendTimestamp(w, localTimestamp); sendTimestamp(w, localTimestamp);
sendTransportProperties(w, localProperties);
w.flush();
} }
// Close the outgoing stream and expect EOF on the incoming stream // Close the outgoing stream and expect EOF on the incoming stream
w.close(); w.close();
@@ -159,7 +195,7 @@ public class ContactExchangeTaskImpl extends Thread
try { try {
// Add the contact // Add the contact
ContactId contactId = addContact(remoteAuthor, masterSecret, ContactId contactId = addContact(remoteAuthor, masterSecret,
timestamp, alice); timestamp, alice, remoteProperties);
// Reuse the connection as a transport connection // Reuse the connection as a transport connection
connectionManager.manageOutgoingConnection(contactId, transportId, connectionManager.manageOutgoingConnection(contactId, transportId,
conn); conn);
@@ -187,19 +223,22 @@ public class ContactExchangeTaskImpl extends Thread
signature.update(nonce); signature.update(nonce);
byte[] sig = signature.sign(); byte[] sig = signature.sign();
// Write the name, public key and signature // Write the name, public key and signature
w.writeListStart();
w.writeString(localAuthor.getName()); w.writeString(localAuthor.getName());
w.writeRaw(localAuthor.getPublicKey()); w.writeRaw(localAuthor.getPublicKey());
w.writeRaw(sig); w.writeRaw(sig);
w.flush(); w.writeListEnd();
LOG.info("Sent pseudonym"); LOG.info("Sent pseudonym");
} }
private Author receivePseudonym(BdfReader r, byte[] nonce) private Author receivePseudonym(BdfReader r, byte[] nonce)
throws GeneralSecurityException, IOException { throws GeneralSecurityException, IOException {
// Read the name, public key and signature // Read the name, public key and signature
r.readListStart();
String name = r.readString(MAX_AUTHOR_NAME_LENGTH); String name = r.readString(MAX_AUTHOR_NAME_LENGTH);
byte[] publicKey = r.readRaw(MAX_PUBLIC_KEY_LENGTH); byte[] publicKey = r.readRaw(MAX_PUBLIC_KEY_LENGTH);
byte[] sig = r.readRaw(MAX_SIGNATURE_LENGTH); byte[] sig = r.readRaw(MAX_SIGNATURE_LENGTH);
r.readListEnd();
LOG.info("Received pseudonym"); LOG.info("Received pseudonym");
// Verify the signature // Verify the signature
Signature signature = crypto.getSignature(); Signature signature = crypto.getSignature();
@@ -217,7 +256,6 @@ public class ContactExchangeTaskImpl extends Thread
private void sendTimestamp(BdfWriter w, long timestamp) private void sendTimestamp(BdfWriter w, long timestamp)
throws IOException { throws IOException {
w.writeLong(timestamp); w.writeLong(timestamp);
w.flush();
LOG.info("Sent timestamp"); LOG.info("Sent timestamp");
} }
@@ -228,11 +266,56 @@ public class ContactExchangeTaskImpl extends Thread
return timestamp; 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, private ContactId addContact(Author remoteAuthor, SecretKey master,
long timestamp, boolean alice) throws DbException { long timestamp, boolean alice,
// Add the contact to the database Map<TransportId, TransportProperties> remoteProperties)
return contactManager.addContact(remoteAuthor, localAuthor.getId(), throws DbException {
master, timestamp, alice, true); 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, private void tryToClose(DuplexTransportConnection conn,

View File

@@ -126,18 +126,18 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
} }
@Override @Override
public boolean contactExists(Transaction txn, AuthorId remoteAuthorID, public boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException { AuthorId localAuthorId) throws DbException {
return db.containsContact(txn, remoteAuthorID, localAuthorId); return db.containsContact(txn, remoteAuthorId, localAuthorId);
} }
@Override @Override
public boolean contactExists(AuthorId remoteAuthorID, public boolean contactExists(AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException { AuthorId localAuthorId) throws DbException {
boolean exists = false; boolean exists = false;
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
exists = contactExists(txn, remoteAuthorID, localAuthorId); exists = contactExists(txn, remoteAuthorId, localAuthorId);
txn.setComplete(); txn.setComplete();
} finally { } finally {
db.endTransaction(txn); 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.crypto.CryptoComponent;
import org.briarproject.api.data.BdfReaderFactory; import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.BdfWriterFactory; import org.briarproject.api.data.BdfWriterFactory;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.identity.AuthorFactory; import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.plugins.ConnectionManager; import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import org.briarproject.api.transport.StreamReaderFactory; import org.briarproject.api.transport.StreamReaderFactory;
import org.briarproject.api.transport.StreamWriterFactory; import org.briarproject.api.transport.StreamWriterFactory;
@@ -28,22 +29,23 @@ public class ContactModule {
@Provides @Provides
@Singleton @Singleton
ContactManager getContactManager(LifecycleManager lifecycleManager, ContactManager getContactManager(IdentityManager identityManager,
IdentityManager identityManager,
ContactManagerImpl contactManager) { ContactManagerImpl contactManager) {
identityManager.registerRemoveIdentityHook(contactManager); identityManager.registerRemoveIdentityHook(contactManager);
return contactManager; return contactManager;
} }
@Provides @Provides
ContactExchangeTask provideContactExchangeTask( ContactExchangeTask provideContactExchangeTask(DatabaseComponent db,
AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory, AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory, Clock clock, BdfWriterFactory bdfWriterFactory, Clock clock,
ConnectionManager connectionManager, ContactManager contactManager, ConnectionManager connectionManager, ContactManager contactManager,
TransportPropertyManager transportPropertyManager,
CryptoComponent crypto, StreamReaderFactory streamReaderFactory, CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory) { StreamWriterFactory streamWriterFactory) {
return new ContactExchangeTaskImpl(authorFactory, bdfReaderFactory, return new ContactExchangeTaskImpl(db, authorFactory, bdfReaderFactory,
bdfWriterFactory, clock, connectionManager, contactManager, 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> { abstract class JdbcDatabase implements Database<Connection> {
private static final int SCHEMA_VERSION = 22; private static final int SCHEMA_VERSION = 23;
private static final int MIN_SCHEMA_VERSION = 22; private static final int MIN_SCHEMA_VERSION = 23;
private static final String CREATE_SETTINGS = private static final String CREATE_SETTINGS =
"CREATE TABLE 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.ACCEPT;
import static org.briarproject.api.introduction.IntroductionConstants.ANSWERED; 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.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.EXISTS;
import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY; import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID; import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
@@ -108,7 +107,6 @@ public class IntroduceeEngine
if (localState.getBoolean(ACCEPT)) { if (localState.getBoolean(ACCEPT)) {
msg.put(TIME, localState.getLong(OUR_TIME)); msg.put(TIME, localState.getLong(OUR_TIME));
msg.put(E_PUBLIC_KEY, localState.getRaw(OUR_PUBLIC_KEY)); msg.put(E_PUBLIC_KEY, localState.getRaw(OUR_PUBLIC_KEY));
msg.put(DEVICE_ID, localAction.getRaw(DEVICE_ID));
msg.put(TRANSPORT, localAction.getDictionary(TRANSPORT)); msg.put(TRANSPORT, localAction.getDictionary(TRANSPORT));
} }
messages.add(msg); messages.add(msg);
@@ -231,7 +229,6 @@ public class IntroduceeEngine
if (msg.getBoolean(ACCEPT)) { if (msg.getBoolean(ACCEPT)) {
localState.put(TIME, msg.getLong(TIME)); localState.put(TIME, msg.getLong(TIME));
localState.put(E_PUBLIC_KEY, msg.getRaw(E_PUBLIC_KEY)); localState.put(E_PUBLIC_KEY, msg.getRaw(E_PUBLIC_KEY));
localState.put(DEVICE_ID, msg.getRaw(DEVICE_ID));
localState.put(TRANSPORT, msg.getDictionary(TRANSPORT)); 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.Bytes;
import org.briarproject.api.DeviceId;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.clients.ClientHelper; 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.ANSWERED;
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT; 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.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.EXISTS;
import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY; import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID; 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 // get data to connect and derive a shared secret later
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
byte[] deviceId = db.getDeviceId(txn).getBytes();
KeyPair keyPair = cryptoComponent.generateAgreementKeyPair(); KeyPair keyPair = cryptoComponent.generateAgreementKeyPair();
byte[] publicKey = keyPair.getPublic().getEncoded(); byte[] publicKey = keyPair.getPublic().getEncoded();
byte[] privateKey = keyPair.getPrivate().getEncoded(); byte[] privateKey = keyPair.getPrivate().getEncoded();
@@ -183,14 +180,12 @@ class IntroduceeManager {
// define action // define action
BdfDictionary localAction = new BdfDictionary(); BdfDictionary localAction = new BdfDictionary();
localAction.put(TYPE, TYPE_RESPONSE); localAction.put(TYPE, TYPE_RESPONSE);
localAction.put(DEVICE_ID, deviceId);
localAction.put(TRANSPORT, localAction.put(TRANSPORT,
encodeTransportProperties(transportProperties)); encodeTransportProperties(transportProperties));
// start engine and process its state update // start engine and process its state update
IntroduceeEngine engine = new IntroduceeEngine(); IntroduceeEngine engine = new IntroduceeEngine();
processStateUpdate(txn, processStateUpdate(txn, engine.onLocalAction(state, localAction));
engine.onLocalAction(state, localAction));
} }
public void declineIntroduction(Transaction txn, final SessionId sessionId) public void declineIntroduction(Transaction txn, final SessionId sessionId)
@@ -313,11 +308,10 @@ class IntroduceeManager {
localState.put(ADDED_CONTACT_ID, contactId.getInt()); localState.put(ADDED_CONTACT_ID, contactId.getInt());
// let the transport manager know how to connect to the contact // let the transport manager know how to connect to the contact
DeviceId deviceId = new DeviceId(localState.getRaw(DEVICE_ID));
Map<TransportId, TransportProperties> transportProperties = Map<TransportId, TransportProperties> transportProperties =
parseTransportProperties(localState); parseTransportProperties(localState);
transportPropertyManager.addRemoteProperties(txn, contactId, transportPropertyManager.addRemoteProperties(txn, contactId,
deviceId, transportProperties); transportProperties);
// delete the ephemeral private key by overwriting with NULL value // delete the ephemeral private key by overwriting with NULL value
// this ensures future ephemeral keys can not be recovered when // 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.io.IOException;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.api.introduction.IntroducerProtocolState.PREPARE_REQUESTS; import static org.briarproject.api.introduction.IntroducerProtocolState.PREPARE_REQUESTS;
import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_1; import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_1;

View File

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

View File

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

View File

@@ -191,7 +191,7 @@ class ConnectionManagerImpl implements ConnectionManager {
} }
if (ctx == null) { if (ctx == null) {
LOG.warning("Could not allocate stream context"); LOG.warning("Could not allocate stream context");
disposeWriter(false); disposeWriter(true);
return; return;
} }
connectionRegistry.registerConnection(contactId, transportId); connectionRegistry.registerConnection(contactId, transportId);
@@ -286,7 +286,7 @@ class ConnectionManagerImpl implements ConnectionManager {
} }
if (ctx == null) { if (ctx == null) {
LOG.warning("Could not allocate stream context"); LOG.warning("Could not allocate stream context");
disposeWriter(false); disposeWriter(true);
return; return;
} }
try { try {
@@ -351,10 +351,9 @@ class ConnectionManagerImpl implements ConnectionManager {
} }
if (ctx == null) { if (ctx == null) {
LOG.warning("Could not allocate stream context"); LOG.warning("Could not allocate stream context");
disposeWriter(false); disposeWriter(true);
return; return;
} }
connectionRegistry.registerConnection(contactId, transportId);
// Start the incoming session on another thread // Start the incoming session on another thread
ioExecutor.execute(new Runnable() { ioExecutor.execute(new Runnable() {
public void run() { public void run() {
@@ -369,8 +368,6 @@ class ConnectionManagerImpl implements ConnectionManager {
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeWriter(true); disposeWriter(true);
} finally {
connectionRegistry.unregisterConnection(contactId, transportId);
} }
} }
@@ -382,17 +379,17 @@ class ConnectionManagerImpl implements ConnectionManager {
ctx = keyManager.getStreamContext(transportId, tag); ctx = keyManager.getStreamContext(transportId, tag);
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, true); disposeReader(true, false);
return; return;
} catch (DbException e) { } catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, true); disposeReader(true, false);
return; return;
} }
// Unrecognised tags are suspicious in this case // Unrecognised tags are suspicious in this case
if (ctx == null) { if (ctx == null) {
LOG.warning("Unrecognised tag for returning stream"); LOG.warning("Unrecognised tag for returning stream");
disposeReader(true, true); disposeReader(true, false);
return; return;
} }
// Check that the stream comes from the expected contact // Check that the stream comes from the expected contact
@@ -401,6 +398,7 @@ class ConnectionManagerImpl implements ConnectionManager {
disposeReader(true, true); disposeReader(true, true);
return; return;
} }
connectionRegistry.registerConnection(contactId, transportId);
try { try {
// Create and run the incoming session // Create and run the incoming session
incomingSession = createIncomingSession(ctx, reader); incomingSession = createIncomingSession(ctx, reader);
@@ -409,6 +407,8 @@ class ConnectionManagerImpl implements ConnectionManager {
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, true); 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) { public void registerConnection(ContactId c, TransportId t) {
LOG.info("Connection registered"); if (LOG.isLoggable(INFO)) LOG.info("Connection registered: " + t);
boolean firstConnection = false; boolean firstConnection = false;
lock.lock(); lock.lock();
try { try {
@@ -63,7 +63,6 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
} finally { } finally {
lock.unlock(); lock.unlock();
} }
if (firstConnection) { if (firstConnection) {
LOG.info("Contact connected"); LOG.info("Contact connected");
eventBus.broadcast(new ContactConnectedEvent(c)); eventBus.broadcast(new ContactConnectedEvent(c));
@@ -71,7 +70,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
} }
public void unregisterConnection(ContactId c, TransportId t) { public void unregisterConnection(ContactId c, TransportId t) {
LOG.info("Connection unregistered"); if (LOG.isLoggable(INFO)) LOG.info("Connection unregistered: " + t);
boolean lastConnection = false; boolean lastConnection = false;
lock.lock(); lock.lock();
try { try {
@@ -95,15 +94,13 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
} finally { } finally {
lock.unlock(); lock.unlock();
} }
if (lastConnection) { if (lastConnection) {
LOG.info("Contact disconnected"); LOG.info("Contact disconnected");
eventBus.broadcast(new ContactDisconnectedEvent(c)); eventBus.broadcast(new ContactDisconnectedEvent(c));
} }
} }
public Collection<ContactId> getConnectedContacts( public Collection<ContactId> getConnectedContacts(TransportId t) {
TransportId t) {
lock.lock(); lock.lock();
try { try {
Map<ContactId, Integer> m = connections.get(t); Map<ContactId, Integer> m = connections.get(t);
@@ -115,7 +112,16 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
} finally { } finally {
lock.unlock(); 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) { public boolean isConnected(ContactId c) {
@@ -125,6 +131,5 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
} finally { } finally {
lock.unlock(); lock.unlock();
} }
} }
} }

View File

@@ -3,13 +3,17 @@ package org.briarproject.plugins;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException; 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.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.TransportDisabledEvent; import org.briarproject.api.event.TransportDisabledEvent;
import org.briarproject.api.event.TransportEnabledEvent; import org.briarproject.api.event.TransportEnabledEvent;
import org.briarproject.api.lifecycle.IoExecutor; import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.lifecycle.Service; import org.briarproject.api.lifecycle.Service;
import org.briarproject.api.lifecycle.ServiceException; import org.briarproject.api.lifecycle.ServiceException;
import org.briarproject.api.plugins.ConnectionManager; import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.plugins.Plugin; import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.PluginCallback; import org.briarproject.api.plugins.PluginCallback;
import org.briarproject.api.plugins.PluginConfig; 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.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
class PluginManagerImpl implements PluginManager, Service { class PluginManagerImpl implements PluginManager, Service, EventListener {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(PluginManagerImpl.class.getName()); Logger.getLogger(PluginManagerImpl.class.getName());
@@ -56,6 +60,7 @@ class PluginManagerImpl implements PluginManager, Service {
private final PluginConfig pluginConfig; private final PluginConfig pluginConfig;
private final Poller poller; private final Poller poller;
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry;
private final SettingsManager settingsManager; private final SettingsManager settingsManager;
private final TransportPropertyManager transportPropertyManager; private final TransportPropertyManager transportPropertyManager;
private final UiCallback uiCallback; private final UiCallback uiCallback;
@@ -67,6 +72,7 @@ class PluginManagerImpl implements PluginManager, Service {
PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus, PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus,
PluginConfig pluginConfig, Poller poller, PluginConfig pluginConfig, Poller poller,
ConnectionManager connectionManager, ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry,
SettingsManager settingsManager, SettingsManager settingsManager,
TransportPropertyManager transportPropertyManager, TransportPropertyManager transportPropertyManager,
UiCallback uiCallback) { UiCallback uiCallback) {
@@ -75,6 +81,7 @@ class PluginManagerImpl implements PluginManager, Service {
this.pluginConfig = pluginConfig; this.pluginConfig = pluginConfig;
this.poller = poller; this.poller = poller;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry;
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
this.transportPropertyManager = transportPropertyManager; this.transportPropertyManager = transportPropertyManager;
this.uiCallback = uiCallback; this.uiCallback = uiCallback;
@@ -106,10 +113,14 @@ class PluginManagerImpl implements PluginManager, Service {
} catch (InterruptedException e) { } catch (InterruptedException e) {
throw new ServiceException(e); throw new ServiceException(e);
} }
// Listen for events
eventBus.addListener(this);
} }
@Override @Override
public void stopService() throws ServiceException { public void stopService() throws ServiceException {
// Stop listening for events
eventBus.removeListener(this);
// Stop the poller // Stop the poller
LOG.info("Stopping poller"); LOG.info("Stopping poller");
poller.stop(); poller.stop();
@@ -122,9 +133,6 @@ class PluginManagerImpl implements PluginManager, Service {
LOG.info("Stopping duplex plugins"); LOG.info("Stopping duplex plugins");
for (DuplexPlugin plugin : duplexPlugins) for (DuplexPlugin plugin : duplexPlugins)
ioExecutor.execute(new PluginStopper(plugin, latch)); ioExecutor.execute(new PluginStopper(plugin, latch));
plugins.clear();
simplexPlugins.clear();
duplexPlugins.clear();
// Wait for all the plugins to stop // Wait for all the plugins to stop
try { try {
latch.await(); latch.await();
@@ -151,6 +159,47 @@ class PluginManagerImpl implements PluginManager, Service {
return Collections.unmodifiableList(supported); 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 class SimplexPluginStarter implements Runnable {
private final SimplexPluginFactory factory; private final SimplexPluginFactory factory;

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,20 @@
package org.briarproject.plugins; package org.briarproject.plugins;
import org.briarproject.BriarTestCase; import org.briarproject.BriarTestCase;
import org.briarproject.ImmediateExecutor;
import org.briarproject.api.TransportId; 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.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.plugins.ConnectionManager; import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.plugins.PluginConfig; 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.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexPluginFactory; 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.SimplexPlugin;
import org.briarproject.api.plugins.simplex.SimplexPluginCallback; import org.briarproject.api.plugins.simplex.SimplexPluginCallback;
import org.briarproject.api.plugins.simplex.SimplexPluginFactory; import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
@@ -36,6 +43,8 @@ public class PluginManagerImplTest extends BriarTestCase {
final Poller poller = context.mock(Poller.class); final Poller poller = context.mock(Poller.class);
final ConnectionManager connectionManager = final ConnectionManager connectionManager =
context.mock(ConnectionManager.class); context.mock(ConnectionManager.class);
final ConnectionRegistry connectionRegistry =
context.mock(ConnectionRegistry.class);
final SettingsManager settingsManager = final SettingsManager settingsManager =
context.mock(SettingsManager.class); context.mock(SettingsManager.class);
final TransportPropertyManager transportPropertyManager = final TransportPropertyManager transportPropertyManager =
@@ -63,6 +72,7 @@ public class PluginManagerImplTest extends BriarTestCase {
final TransportId duplexFailId = new TransportId("duplex1"); final TransportId duplexFailId = new TransportId("duplex1");
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start()
// First simplex plugin // First simplex plugin
oneOf(pluginConfig).getSimplexFactories(); oneOf(pluginConfig).getSimplexFactories();
will(returnValue(Arrays.asList(simplexFactory, will(returnValue(Arrays.asList(simplexFactory,
@@ -103,6 +113,11 @@ public class PluginManagerImplTest extends BriarTestCase {
oneOf(duplexFailFactory).createPlugin(with(any( oneOf(duplexFailFactory).createPlugin(with(any(
DuplexPluginCallback.class))); DuplexPluginCallback.class)));
will(returnValue(null)); // Failed to create a plugin 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 // Stop the poller
oneOf(poller).stop(); oneOf(poller).stop();
// Stop the plugins // Stop the plugins
@@ -111,8 +126,8 @@ public class PluginManagerImplTest extends BriarTestCase {
}}); }});
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus, PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus,
pluginConfig, poller, connectionManager, settingsManager, pluginConfig, poller, connectionManager, connectionRegistry,
transportPropertyManager, uiCallback); settingsManager, transportPropertyManager, uiCallback);
// Two plugins should be started and stopped // Two plugins should be started and stopped
p.startService(); p.startService();
@@ -120,4 +135,151 @@ public class PluginManagerImplTest extends BriarTestCase {
context.assertIsSatisfied(); 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.BriarTestCase;
import org.briarproject.TestUtils; import org.briarproject.TestUtils;
import org.briarproject.api.DeviceId;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.UniqueId;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList; import org.briarproject.api.data.BdfList;
@@ -28,110 +26,83 @@ import static org.junit.Assert.assertEquals;
public class TransportPropertyValidatorTest extends BriarTestCase { public class TransportPropertyValidatorTest extends BriarTestCase {
private final TransportId transportId; private final TransportId transportId;
private final DeviceId deviceId;
private final BdfDictionary bdfDictionary; private final BdfDictionary bdfDictionary;
private final Group group; private final Group group;
private final Message message; private final Message message;
private final TransportPropertyValidator tpv; private final TransportPropertyValidator tpv;
public TransportPropertyValidatorTest() { public TransportPropertyValidatorTest() {
transportId = new TransportId("test"); transportId = new TransportId("test");
deviceId = new DeviceId(TestUtils.getRandomId()); bdfDictionary = new BdfDictionary();
bdfDictionary = new BdfDictionary();
GroupId groupId = new GroupId(TestUtils.getRandomId()); GroupId groupId = new GroupId(TestUtils.getRandomId());
ClientId clientId = new ClientId(TestUtils.getRandomId()); ClientId clientId = new ClientId(TestUtils.getRandomId());
byte[] descriptor = TestUtils.getRandomBytes(12); byte[] descriptor = TestUtils.getRandomBytes(12);
group = new Group(groupId, clientId, descriptor); group = new Group(groupId, clientId, descriptor);
MessageId messageId = new MessageId(TestUtils.getRandomId()); MessageId messageId = new MessageId(TestUtils.getRandomId());
long timestamp = System.currentTimeMillis(); long timestamp = System.currentTimeMillis();
byte[] body = TestUtils.getRandomBytes(123); byte[] body = TestUtils.getRandomBytes(123);
message = new Message(messageId, groupId, timestamp, body); message = new Message(messageId, groupId, timestamp, body);
Mockery context = new Mockery(); Mockery context = new Mockery();
ClientHelper clientHelper = context.mock(ClientHelper.class); ClientHelper clientHelper = context.mock(ClientHelper.class);
MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class); MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class);
Clock clock = context.mock(Clock.class); Clock clock = context.mock(Clock.class);
tpv = new TransportPropertyValidator(clientHelper, metadataEncoder, tpv = new TransportPropertyValidator(clientHelper, metadataEncoder,
clock); clock);
} }
@Test @Test
public void testValidateProperMessage() throws IOException { public void testValidateProperMessage() throws IOException {
BdfList body = BdfList.of(deviceId, transportId.getString(), 4, BdfList body = BdfList.of(transportId.getString(), 4, bdfDictionary);
bdfDictionary);
BdfDictionary result = tpv.validateMessage(message, group, body); BdfDictionary result = tpv.validateMessage(message, group, body);
assertEquals("test", result.getString("transportId")); assertEquals("test", result.getString("transportId"));
assertEquals(result.getLong("version").longValue(), 4); assertEquals(4, result.getLong("version").longValue());
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testValidateWrongVersionValue() throws IOException { public void testValidateWrongVersionValue() throws IOException {
/* Will create a negative version number */ BdfList body = BdfList.of(transportId.getString(), -1, bdfDictionary);
BdfList body = BdfList.of(deviceId, transportId.getString(), -1, tpv.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testValidateWrongVersionType() throws IOException {
BdfList body = BdfList.of(transportId.getString(), bdfDictionary,
bdfDictionary); bdfDictionary);
tpv.validateMessage(message, group, body); tpv.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testValidateWrongVersionType() throws IOException { 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 = String wrongTransportIdString =
TestUtils.getRandomString(MAX_TRANSPORT_ID_LENGTH + 1); TestUtils.getRandomString(MAX_TRANSPORT_ID_LENGTH + 1);
BdfList body = BdfList.of(deviceId, wrongTransportIdString, 4, BdfList body = BdfList.of(wrongTransportIdString, 4, bdfDictionary);
bdfDictionary);
tpv.validateMessage(message, group, body); tpv.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testValidateTooManyProperties() throws IOException { public void testValidateEmptyTransportId() throws IOException {
/* Generate a big map for the BdfDictionary*/ BdfList body = BdfList.of("", 4, bdfDictionary);
BdfDictionary d = new 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++) for (int i = 0; i < MAX_PROPERTIES_PER_TRANSPORT + 1; i++)
d.put(String.valueOf(i), 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); tpv.validateMessage(message, group, body);
} }
} }