diff --git a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java index 50cd9db82cc1ff059c6e5e05e867a8c4923a53a5..d349650735cbd6540378c97777b9e28e013eb8b2 100644 --- a/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java +++ b/briar-android/src/org/briarproject/plugins/droidtooth/DroidtoothTransportConnection.java @@ -3,8 +3,11 @@ package org.briarproject.plugins.droidtooth; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.concurrent.atomic.AtomicBoolean; import org.briarproject.api.plugins.Plugin; +import org.briarproject.api.plugins.TransportConnectionReader; +import org.briarproject.api.plugins.TransportConnectionWriter; import org.briarproject.api.plugins.duplex.DuplexTransportConnection; import android.bluetooth.BluetoothSocket; @@ -13,30 +16,69 @@ class DroidtoothTransportConnection implements DuplexTransportConnection { private final Plugin plugin; private final BluetoothSocket socket; + private final Reader reader; + private final Writer writer; + private final AtomicBoolean halfClosed, closed; DroidtoothTransportConnection(Plugin plugin, BluetoothSocket socket) { this.plugin = plugin; this.socket = socket; + reader = new Reader(); + writer = new Writer(); + halfClosed = new AtomicBoolean(false); + closed = new AtomicBoolean(false); } - public int getMaxFrameLength() { - return plugin.getMaxFrameLength(); + public TransportConnectionReader getReader() { + return reader; } - public long getMaxLatency() { - return plugin.getMaxLatency(); + public TransportConnectionWriter getWriter() { + return writer; } - public InputStream getInputStream() throws IOException { - return socket.getInputStream(); - } + private class Reader implements TransportConnectionReader { + + public int getMaxFrameLength() { + return plugin.getMaxFrameLength(); + } + + public long getMaxLatency() { + return plugin.getMaxLatency(); + } - public OutputStream getOutputStream() throws IOException { - return socket.getOutputStream(); + public InputStream getInputStream() throws IOException { + return socket.getInputStream(); + } + + public void dispose(boolean exception, boolean recognised) + throws IOException { + if(halfClosed.getAndSet(true) || exception) + if(!closed.getAndSet(true)) socket.close(); + } } - public void dispose(boolean exception, boolean recognised) - throws IOException { - socket.close(); + private class Writer implements TransportConnectionWriter { + + public int getMaxFrameLength() { + return plugin.getMaxFrameLength(); + } + + public long getMaxLatency() { + return plugin.getMaxLatency(); + } + + public long getCapacity() { + return Long.MAX_VALUE; + } + + public OutputStream getOutputStream() throws IOException { + return socket.getOutputStream(); + } + + public void dispose(boolean exception) throws IOException { + if(halfClosed.getAndSet(true) || exception) + if(!closed.getAndSet(true)) socket.close(); + } } } diff --git a/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java b/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java index 14c490d1877f20fda3954724e9630998bee9494d..ab0969eae633fd8270cc35bcb33bc8e361884ebf 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorTransportConnection.java @@ -4,38 +4,80 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; +import java.util.concurrent.atomic.AtomicBoolean; import org.briarproject.api.plugins.Plugin; +import org.briarproject.api.plugins.TransportConnectionReader; +import org.briarproject.api.plugins.TransportConnectionWriter; import org.briarproject.api.plugins.duplex.DuplexTransportConnection; class TorTransportConnection implements DuplexTransportConnection { private final Plugin plugin; private final Socket socket; + private final Reader reader; + private final Writer writer; + private final AtomicBoolean halfClosed, closed; TorTransportConnection(Plugin plugin, Socket socket) { this.plugin = plugin; this.socket = socket; + reader = new Reader(); + writer = new Writer(); + halfClosed = new AtomicBoolean(false); + closed = new AtomicBoolean(false); } - public int getMaxFrameLength() { - return plugin.getMaxFrameLength(); + public TransportConnectionReader getReader() { + return reader; } - public long getMaxLatency() { - return plugin.getMaxLatency(); + public TransportConnectionWriter getWriter() { + return writer; } - public InputStream getInputStream() throws IOException { - return socket.getInputStream(); - } + private class Reader implements TransportConnectionReader { + + public int getMaxFrameLength() { + return plugin.getMaxFrameLength(); + } + + public long getMaxLatency() { + return plugin.getMaxLatency(); + } - public OutputStream getOutputStream() throws IOException { - return socket.getOutputStream(); + public InputStream getInputStream() throws IOException { + return socket.getInputStream(); + } + + public void dispose(boolean exception, boolean recognised) + throws IOException { + if(halfClosed.getAndSet(true) || exception) + if(!closed.getAndSet(true)) socket.close(); + } } - public void dispose(boolean exception, boolean recognised) - throws IOException { - socket.close(); + private class Writer implements TransportConnectionWriter { + + public int getMaxFrameLength() { + return plugin.getMaxFrameLength(); + } + + public long getMaxLatency() { + return plugin.getMaxLatency(); + } + + public long getCapacity() { + return Long.MAX_VALUE; + } + + public OutputStream getOutputStream() throws IOException { + return socket.getOutputStream(); + } + + public void dispose(boolean exception) throws IOException { + if(halfClosed.getAndSet(true) || exception) + if(!closed.getAndSet(true)) socket.close(); + } } } diff --git a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java index b946f57606f956cc90abfab69169fdaa27a41877..ba9c86224b04d83b6ae67d1da2ed50b7f9f90b83 100644 --- a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java +++ b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java @@ -72,13 +72,10 @@ public interface CryptoComponent { /** * Derives a frame key from the given temporary secret and stream number. - * @param alice indicates whether the key is for a connection initiated by + * @param alice indicates whether the key is for a stream initiated by * Alice or Bob. - * @param initiator indicates whether the key is for the initiator's or the - * responder's side of the connection. */ - SecretKey deriveFrameKey(byte[] secret, long streamNumber, boolean alice, - boolean initiator); + SecretKey deriveFrameKey(byte[] secret, long streamNumber, boolean alice); /** Returns a cipher for encrypting and authenticating frames. */ AuthenticatedCipher getFrameCipher(); diff --git a/briar-api/src/org/briarproject/api/messaging/MessagingSession.java b/briar-api/src/org/briarproject/api/messaging/MessagingSession.java new file mode 100644 index 0000000000000000000000000000000000000000..786922c8165b25d5196e6e0ae571104bf3437a19 --- /dev/null +++ b/briar-api/src/org/briarproject/api/messaging/MessagingSession.java @@ -0,0 +1,19 @@ +package org.briarproject.api.messaging; + +import java.io.IOException; + +public interface MessagingSession { + + /** + * Runs the session. This method returns when there are no more packets to + * send or when the {@link #interrupt()} method has been called. + */ + void run() throws IOException; + + /** + * Interrupts the session, causing the {@link #run()} method to return at + * the next opportunity or throw an {@link java.io.IOException IOException} + * if it cannot return cleanly. + */ + void interrupt(); +} diff --git a/briar-api/src/org/briarproject/api/messaging/MessagingSessionFactory.java b/briar-api/src/org/briarproject/api/messaging/MessagingSessionFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..a014fe92f9d6d34149dbac84504ef31c7ad46248 --- /dev/null +++ b/briar-api/src/org/briarproject/api/messaging/MessagingSessionFactory.java @@ -0,0 +1,14 @@ +package org.briarproject.api.messaging; + +import org.briarproject.api.plugins.TransportConnectionReader; +import org.briarproject.api.plugins.TransportConnectionWriter; +import org.briarproject.api.transport.StreamContext; + +public interface MessagingSessionFactory { + + MessagingSession createIncomingSession(StreamContext ctx, + TransportConnectionReader r); + + MessagingSession createOutgoingSession(StreamContext ctx, + TransportConnectionWriter w, boolean duplex); +} diff --git a/briar-api/src/org/briarproject/api/messaging/duplex/DuplexConnectionFactory.java b/briar-api/src/org/briarproject/api/messaging/duplex/DuplexConnectionFactory.java deleted file mode 100644 index 977df2176ffef7733c817167ef8d10e57b9e0d39..0000000000000000000000000000000000000000 --- a/briar-api/src/org/briarproject/api/messaging/duplex/DuplexConnectionFactory.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.briarproject.api.messaging.duplex; - -import org.briarproject.api.ContactId; -import org.briarproject.api.TransportId; -import org.briarproject.api.plugins.duplex.DuplexTransportConnection; -import org.briarproject.api.transport.StreamContext; - -public interface DuplexConnectionFactory { - - void createIncomingConnection(StreamContext ctx, - DuplexTransportConnection d); - - void createOutgoingConnection(ContactId c, TransportId t, - DuplexTransportConnection d); -} diff --git a/briar-api/src/org/briarproject/api/messaging/simplex/SimplexConnectionFactory.java b/briar-api/src/org/briarproject/api/messaging/simplex/SimplexConnectionFactory.java deleted file mode 100644 index 61f75a084a3300bce0584f252cddb08c8d31f354..0000000000000000000000000000000000000000 --- a/briar-api/src/org/briarproject/api/messaging/simplex/SimplexConnectionFactory.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.briarproject.api.messaging.simplex; - -import org.briarproject.api.ContactId; -import org.briarproject.api.TransportId; -import org.briarproject.api.plugins.simplex.SimplexTransportReader; -import org.briarproject.api.plugins.simplex.SimplexTransportWriter; -import org.briarproject.api.transport.StreamContext; - -public interface SimplexConnectionFactory { - - void createIncomingConnection(StreamContext ctx, - SimplexTransportReader r); - - void createOutgoingConnection(ContactId c, TransportId t, - SimplexTransportWriter w); -} diff --git a/briar-api/src/org/briarproject/api/plugins/TransportConnectionReader.java b/briar-api/src/org/briarproject/api/plugins/TransportConnectionReader.java new file mode 100644 index 0000000000000000000000000000000000000000..71bf0cc03f6efaa44967211cde8be39fc4e91778 --- /dev/null +++ b/briar-api/src/org/briarproject/api/plugins/TransportConnectionReader.java @@ -0,0 +1,32 @@ +package org.briarproject.api.plugins; + +import java.io.IOException; +import java.io.InputStream; + +/** + * An interface for reading data from a transport connection. The reader is not + * responsible for decrypting or authenticating the data. + */ +public interface TransportConnectionReader { + + /** Returns the maximum frame length of the transport in bytes. */ + int getMaxFrameLength(); + + /** Returns the maximum latency of the transport in milliseconds. */ + long getMaxLatency(); + + /** Returns an input stream for reading from the transport connection. */ + InputStream getInputStream() throws IOException; + + /** + * Marks this side of the transport connection closed. If the transport is + * simplex, the connection is closed. If the transport is duplex, the + * connection is closed if <tt>exception</tt> is true or the other side of + * the connection has been marked as closed. + * @param exception true if the connection is being closed because of an + * exception. This may affect how resources are disposed of. + * @param recognised true if the pseudo-random tag was recognised. This may + * affect how resources are disposed of. + */ + void dispose(boolean exception, boolean recognised) throws IOException; +} diff --git a/briar-api/src/org/briarproject/api/plugins/TransportConnectionWriter.java b/briar-api/src/org/briarproject/api/plugins/TransportConnectionWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..554e5de8a110e3d24c249cf896287bdd1e5336d4 --- /dev/null +++ b/briar-api/src/org/briarproject/api/plugins/TransportConnectionWriter.java @@ -0,0 +1,33 @@ +package org.briarproject.api.plugins; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * An interface for writing data to a transport connection. The writer is not + * responsible for authenticating or encrypting the data. + */ +public interface TransportConnectionWriter { + + /** Returns the maximum frame length of the transport in bytes. */ + int getMaxFrameLength(); + + /** Returns the maximum latency of the transport in milliseconds. */ + long getMaxLatency(); + + /** Returns the capacity of the transport connection in bytes. */ + long getCapacity(); + + /** Returns an output stream for writing to the transport connection. */ + OutputStream getOutputStream() throws IOException; + + /** + * Marks this side of the transport connection closed. If the transport is + * simplex, the connection is closed. If the transport is duplex, the + * connection is closed if <tt>exception</tt> is true or the other side of + * the connection has been marked as closed. + * @param exception true if the connection is being closed because of an + * exception. This may affect how resources are disposed of. + */ + void dispose(boolean exception) throws IOException; +} diff --git a/briar-api/src/org/briarproject/api/plugins/duplex/DuplexTransportConnection.java b/briar-api/src/org/briarproject/api/plugins/duplex/DuplexTransportConnection.java index 7193c0849ae8f57a2fe04b701bc4d2c567e0b35d..8440fe4add593c36820d53262e8aa712f8260ce7 100644 --- a/briar-api/src/org/briarproject/api/plugins/duplex/DuplexTransportConnection.java +++ b/briar-api/src/org/briarproject/api/plugins/duplex/DuplexTransportConnection.java @@ -1,8 +1,7 @@ package org.briarproject.api.plugins.duplex; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import org.briarproject.api.plugins.TransportConnectionReader; +import org.briarproject.api.plugins.TransportConnectionWriter; /** * An interface for reading and writing data over a duplex transport. The @@ -11,23 +10,11 @@ import java.io.OutputStream; */ public interface DuplexTransportConnection { - /** Returns the maximum frame length of the transport in bytes. */ - int getMaxFrameLength(); + /** Returns a {@link org.briarproject.api.plugins.TransportConnectionReader + * TransportConnectionReader} for reading from the connection. */ + TransportConnectionReader getReader(); - /** Returns the maximum latency of the transport in milliseconds. */ - long getMaxLatency(); - - /** Returns an input stream for reading from the connection. */ - InputStream getInputStream() throws IOException; - - /** Returns an output stream for writing to the connection. */ - OutputStream getOutputStream() throws IOException; - - /** - * Closes the connection and disposes of any associated resources. The - * first argument indicates whether the connection is being closed because - * of an exception and the second argument indicates whether the connection - * was recognised, which may affect how resources are disposed of. - */ - void dispose(boolean exception, boolean recognised) throws IOException; + /** Returns a {@link org.briarproject.api.plugins.TransportConnectionWriter + * TransportConnectionWriter} for writing to the connection. */ + TransportConnectionWriter getWriter(); } diff --git a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPlugin.java b/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPlugin.java index c6334becc9765c7b11ba0affcf730c659e79a066..9e4a1defd8e64fe5a46b550f1d39f3676f30e64d 100644 --- a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPlugin.java +++ b/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPlugin.java @@ -2,6 +2,8 @@ package org.briarproject.api.plugins.simplex; import org.briarproject.api.ContactId; import org.briarproject.api.plugins.Plugin; +import org.briarproject.api.plugins.TransportConnectionReader; +import org.briarproject.api.plugins.TransportConnectionWriter; /** An interface for transport plugins that support simplex communication. */ public interface SimplexPlugin extends Plugin { @@ -11,12 +13,12 @@ public interface SimplexPlugin extends Plugin { * current transport and configuration properties. Returns null if a reader * could not be created. */ - SimplexTransportReader createReader(ContactId c); + TransportConnectionReader createReader(ContactId c); /** * Attempts to create and return a writer for the given contact using the * current transport and configuration properties. Returns null if a writer * could not be created. */ - SimplexTransportWriter createWriter(ContactId c); + TransportConnectionWriter createWriter(ContactId c); } diff --git a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPluginCallback.java b/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPluginCallback.java index d4cf816118e5e038c3dfdb71ddc4b8d4136ee228..1f597c70d21cf9f6d4f7f10790c9e2065786c569 100644 --- a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPluginCallback.java +++ b/briar-api/src/org/briarproject/api/plugins/simplex/SimplexPluginCallback.java @@ -2,6 +2,8 @@ package org.briarproject.api.plugins.simplex; import org.briarproject.api.ContactId; import org.briarproject.api.plugins.PluginCallback; +import org.briarproject.api.plugins.TransportConnectionReader; +import org.briarproject.api.plugins.TransportConnectionWriter; /** * An interface for handling readers and writers created by a simplex transport @@ -9,7 +11,7 @@ import org.briarproject.api.plugins.PluginCallback; */ public interface SimplexPluginCallback extends PluginCallback { - void readerCreated(SimplexTransportReader r); + void readerCreated(TransportConnectionReader r); - void writerCreated(ContactId c, SimplexTransportWriter w); + void writerCreated(ContactId c, TransportConnectionWriter w); } diff --git a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexTransportReader.java b/briar-api/src/org/briarproject/api/plugins/simplex/SimplexTransportReader.java deleted file mode 100644 index 387a7a03bf69cc48b7109afe9a450881695f01f5..0000000000000000000000000000000000000000 --- a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexTransportReader.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.briarproject.api.plugins.simplex; - -import java.io.IOException; -import java.io.InputStream; - -/** - * An interface for reading data from a simplex transport. The reader is not - * responsible for decrypting or authenticating the data before returning it. - */ -public interface SimplexTransportReader { - - /** Returns the maximum frame length of the transport in bytes. */ - int getMaxFrameLength(); - - /** Returns an input stream for reading from the transport. */ - InputStream getInputStream() throws IOException; - - /** - * Closes the reader and disposes of any associated resources. The first - * argument indicates whether the reader is being closed because of an - * exception and the second argument indicates whether the connection was - * recognised, which may affect how resources are disposed of. - */ - void dispose(boolean exception, boolean recognised) throws IOException; -} diff --git a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexTransportWriter.java b/briar-api/src/org/briarproject/api/plugins/simplex/SimplexTransportWriter.java deleted file mode 100644 index 0b50a7898126c235ed784e7499a83705dd0ce73d..0000000000000000000000000000000000000000 --- a/briar-api/src/org/briarproject/api/plugins/simplex/SimplexTransportWriter.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.briarproject.api.plugins.simplex; - -import java.io.IOException; -import java.io.OutputStream; - -/** - * An interface for writing data to a simplex transport. The writer is not - * responsible for authenticating or encrypting the data before writing it. - */ -public interface SimplexTransportWriter { - - /** Returns the capacity of the transport in bytes. */ - long getCapacity(); - - /** Returns the maximum frame length of the transport in bytes. */ - int getMaxFrameLength(); - - /** Returns the maximum latency of the transport in milliseconds. */ - long getMaxLatency(); - - /** Returns an output stream for writing to the transport. */ - OutputStream getOutputStream() throws IOException; - - /** - * Closes the writer and disposes of any associated resources. The - * argument indicates whether the writer is being closed because of an - * exception, which may affect how resources are disposed of. - */ - void dispose(boolean exception) throws IOException; -} diff --git a/briar-api/src/org/briarproject/api/transport/ConnectionDispatcher.java b/briar-api/src/org/briarproject/api/transport/ConnectionDispatcher.java index 27f41ce3fad0057d2dd13c09f149bfa2ce34d2de..6b2058c38f459394f434876e72605154300c26cb 100644 --- a/briar-api/src/org/briarproject/api/transport/ConnectionDispatcher.java +++ b/briar-api/src/org/briarproject/api/transport/ConnectionDispatcher.java @@ -2,18 +2,18 @@ package org.briarproject.api.transport; import org.briarproject.api.ContactId; import org.briarproject.api.TransportId; +import org.briarproject.api.plugins.TransportConnectionReader; +import org.briarproject.api.plugins.TransportConnectionWriter; import org.briarproject.api.plugins.duplex.DuplexTransportConnection; -import org.briarproject.api.plugins.simplex.SimplexTransportReader; -import org.briarproject.api.plugins.simplex.SimplexTransportWriter; public interface ConnectionDispatcher { - void dispatchIncomingConnection(TransportId t, SimplexTransportReader r); + void dispatchIncomingConnection(TransportId t, TransportConnectionReader r); void dispatchIncomingConnection(TransportId t, DuplexTransportConnection d); void dispatchOutgoingConnection(ContactId c, TransportId t, - SimplexTransportWriter w); + TransportConnectionWriter w); void dispatchOutgoingConnection(ContactId c, TransportId t, DuplexTransportConnection d); diff --git a/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java b/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java index 8c43c30eb539c8c90c769feb05b6a17e808f8f61..14459ec60a0d7e1c2e1f3e286fdaea0dd8554ea9 100644 --- a/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java +++ b/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java @@ -6,7 +6,7 @@ public interface StreamReaderFactory { /** Creates a {@link StreamReader} for a transport connection. */ StreamReader createStreamReader(InputStream in, int maxFrameLength, - StreamContext ctx, boolean incoming, boolean initiator); + StreamContext ctx); /** Creates a {@link StreamReader} for an invitation connection. */ StreamReader createInvitationStreamReader(InputStream in, diff --git a/briar-api/src/org/briarproject/api/transport/StreamWriter.java b/briar-api/src/org/briarproject/api/transport/StreamWriter.java index e5f50ecce8bbc229470a11367c7e7aef0052863b..2684742dbdf5889fcb382c8cadbaade65b2eb7f0 100644 --- a/briar-api/src/org/briarproject/api/transport/StreamWriter.java +++ b/briar-api/src/org/briarproject/api/transport/StreamWriter.java @@ -10,10 +10,4 @@ public interface StreamWriter { * be written. */ OutputStream getOutputStream(); - - /** - * Returns the maximum number of bytes that can be written to the output - * stream. - */ - long getRemainingCapacity(); } diff --git a/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java b/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java index f3b8e1c2f86e5afdb364c42168797c4d6cce2b6d..f038a75221d52f0798ab9176ed353868d281d624 100644 --- a/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java +++ b/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java @@ -6,8 +6,7 @@ public interface StreamWriterFactory { /** Creates a {@link StreamWriter} for a transport connection. */ StreamWriter createStreamWriter(OutputStream out, int maxFrameLength, - long capacity, StreamContext ctx, boolean incoming, - boolean initiator); + StreamContext ctx); /** Creates a {@link StreamWriter} for an invitation connection. */ StreamWriter createInvitationStreamWriter(OutputStream out, diff --git a/briar-api/src/org/briarproject/api/transport/TagRecogniser.java b/briar-api/src/org/briarproject/api/transport/TagRecogniser.java index a6823a2e932a85f45bd6a79e1a32b4e8a9b22f2d..b73d096979d6f5a153e66c6a205c0355cdfec81f 100644 --- a/briar-api/src/org/briarproject/api/transport/TagRecogniser.java +++ b/briar-api/src/org/briarproject/api/transport/TagRecogniser.java @@ -4,12 +4,13 @@ import org.briarproject.api.ContactId; import org.briarproject.api.TransportId; import org.briarproject.api.db.DbException; -/** Maintains the table of expected tags for recognising incoming streams. */ +/** Keeps track of expected tags and uses them to recognise incoming streams. */ public interface TagRecogniser { /** - * Returns a {@link StreamContext} for reading from the stream with the - * given tag if the tag was expected, or null if the tag was unexpected. + * Looks up the given tag and returns a {@link StreamContext} for reading + * from the stream if the tag was expected, or null if the tag was + * unexpected. */ StreamContext recogniseTag(TransportId t, byte[] tag) throws DbException; diff --git a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java index dc4dbba95cef4dfd8486d6c24ed70a59d049620b..d616a50b72854df08239c071de91ed33687005be 100644 --- a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java +++ b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java @@ -76,14 +76,10 @@ class CryptoComponentImpl implements CryptoComponent { // Labels for key derivation private static final byte[] A_TAG = { 'A', '_', 'T', 'A', 'G', '\0' }; private static final byte[] B_TAG = { 'B', '_', 'T', 'A', 'G', '\0' }; - private static final byte[] A_FRAME_A = - { 'A', '_', 'F', 'R', 'A', 'M', 'E', '_', 'A', '\0' }; - private static final byte[] A_FRAME_B = - { 'A', '_', 'F', 'R', 'A', 'M', 'E', '_', 'B', '\0' }; - private static final byte[] B_FRAME_A = - { 'B', '_', 'F', 'R', 'A', 'M', 'E', '_', 'A', '\0' }; - private static final byte[] B_FRAME_B = - { 'B', '_', 'F', 'R', 'A', 'M', 'E', '_', 'B', '\0' }; + private static final byte[] A_FRAME = + { 'A', '_', 'F', 'R', 'A', 'M', 'E', '\0' }; + private static final byte[] B_FRAME = + { 'B', '_', 'F', 'R', 'A', 'M', 'E', '\0' }; // Blank secret for argument validation private static final byte[] BLANK_SECRET = new byte[CIPHER_KEY_BYTES]; @@ -288,20 +284,15 @@ class CryptoComponentImpl implements CryptoComponent { } public SecretKey deriveFrameKey(byte[] secret, long streamNumber, - boolean alice, boolean initiator) { + boolean alice) { if(secret.length != CIPHER_KEY_BYTES) throw new IllegalArgumentException(); if(Arrays.equals(secret, BLANK_SECRET)) throw new IllegalArgumentException(); if(streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED) throw new IllegalArgumentException(); - if(alice) { - if(initiator) return deriveKey(secret, A_FRAME_A, streamNumber); - else return deriveKey(secret, A_FRAME_B, streamNumber); - } else { - if(initiator) return deriveKey(secret, B_FRAME_A, streamNumber); - else return deriveKey(secret, B_FRAME_B, streamNumber); - } + if(alice) return deriveKey(secret, A_FRAME, streamNumber); + else return deriveKey(secret, B_FRAME, streamNumber); } private SecretKey deriveKey(byte[] secret, byte[] label, long context) { diff --git a/briar-core/src/org/briarproject/invitation/AliceConnector.java b/briar-core/src/org/briarproject/invitation/AliceConnector.java index 455e1a6bda1dfd6bc09bb67a0836fb19f390f75d..955dc158872d56f80bcfa806f6dc674678342087 100644 --- a/briar-core/src/org/briarproject/invitation/AliceConnector.java +++ b/briar-core/src/org/briarproject/invitation/AliceConnector.java @@ -75,8 +75,8 @@ class AliceConnector extends Connector { Writer w; byte[] secret; try { - in = conn.getInputStream(); - out = conn.getOutputStream(); + in = conn.getReader().getInputStream(); + out = conn.getWriter().getOutputStream(); r = readerFactory.createReader(in); w = writerFactory.createWriter(out); // Alice goes first @@ -130,7 +130,7 @@ class AliceConnector extends Connector { // Confirmation succeeded - upgrade to a secure connection if(LOG.isLoggable(INFO)) LOG.info(pluginName + " confirmation succeeded"); - int maxFrameLength = conn.getMaxFrameLength(); + int maxFrameLength = conn.getReader().getMaxFrameLength(); StreamReader streamReader = streamReaderFactory.createInvitationStreamReader(in, maxFrameLength, secret, false); diff --git a/briar-core/src/org/briarproject/invitation/BobConnector.java b/briar-core/src/org/briarproject/invitation/BobConnector.java index 584f5b81f972282a6f62c5304425eb11b26fb24b..4da2407eb3fce591147c8da2910dfd03ef497e08 100644 --- a/briar-core/src/org/briarproject/invitation/BobConnector.java +++ b/briar-core/src/org/briarproject/invitation/BobConnector.java @@ -69,8 +69,8 @@ class BobConnector extends Connector { Writer w; byte[] secret; try { - in = conn.getInputStream(); - out = conn.getOutputStream(); + in = conn.getReader().getInputStream(); + out = conn.getWriter().getOutputStream(); r = readerFactory.createReader(in); w = writerFactory.createWriter(out); // Alice goes first @@ -130,7 +130,7 @@ class BobConnector extends Connector { // Confirmation succeeded - upgrade to a secure connection if(LOG.isLoggable(INFO)) LOG.info(pluginName + " confirmation succeeded"); - int maxFrameLength = conn.getMaxFrameLength(); + int maxFrameLength = conn.getReader().getMaxFrameLength(); StreamReader streamReader = streamReaderFactory.createInvitationStreamReader(in, maxFrameLength, secret, true); diff --git a/briar-core/src/org/briarproject/invitation/Connector.java b/briar-core/src/org/briarproject/invitation/Connector.java index 36b84860f532b7a30a68bd07a97425006468cc07..32347a130a0173f238c835ba5ed55836d1bdad8c 100644 --- a/briar-core/src/org/briarproject/invitation/Connector.java +++ b/briar-core/src/org/briarproject/invitation/Connector.java @@ -311,7 +311,8 @@ abstract class Connector extends Thread { boolean exception) { try { LOG.info("Closing connection"); - conn.dispose(exception, true); + conn.getReader().dispose(exception, true); + conn.getWriter().dispose(exception); } catch(IOException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } diff --git a/briar-core/src/org/briarproject/messaging/simplex/IncomingSimplexConnection.java b/briar-core/src/org/briarproject/messaging/IncomingSession.java similarity index 62% rename from briar-core/src/org/briarproject/messaging/simplex/IncomingSimplexConnection.java rename to briar-core/src/org/briarproject/messaging/IncomingSession.java index 72162747c2c5ac167a27d0f107d78dc290d174c1..37547d3cd9ce774fa16b0dd19a0dba4f26b1fd70 100644 --- a/briar-core/src/org/briarproject/messaging/simplex/IncomingSimplexConnection.java +++ b/briar-core/src/org/briarproject/messaging/IncomingSession.java @@ -1,4 +1,4 @@ -package org.briarproject.messaging.simplex; +package org.briarproject.messaging; import static java.util.logging.Level.WARNING; @@ -10,12 +10,12 @@ import java.util.logging.Logger; import org.briarproject.api.ContactId; import org.briarproject.api.FormatException; -import org.briarproject.api.TransportId; import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DbException; import org.briarproject.api.messaging.Ack; import org.briarproject.api.messaging.Message; import org.briarproject.api.messaging.MessageVerifier; +import org.briarproject.api.messaging.MessagingSession; import org.briarproject.api.messaging.PacketReader; import org.briarproject.api.messaging.PacketReaderFactory; import org.briarproject.api.messaging.RetentionAck; @@ -25,103 +25,91 @@ import org.briarproject.api.messaging.SubscriptionUpdate; import org.briarproject.api.messaging.TransportAck; import org.briarproject.api.messaging.TransportUpdate; import org.briarproject.api.messaging.UnverifiedMessage; -import org.briarproject.api.plugins.simplex.SimplexTransportReader; -import org.briarproject.api.transport.ConnectionRegistry; +import org.briarproject.api.plugins.TransportConnectionReader; import org.briarproject.api.transport.StreamContext; import org.briarproject.api.transport.StreamReader; import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.util.ByteUtils; -class IncomingSimplexConnection { +/** + * An incoming {@link org.briarproject.api.messaging.MessagingSession + * MessagingSession}. + */ +class IncomingSession implements MessagingSession { private static final Logger LOG = - Logger.getLogger(IncomingSimplexConnection.class.getName()); + Logger.getLogger(IncomingSession.class.getName()); + private final DatabaseComponent db; private final Executor dbExecutor, cryptoExecutor; private final MessageVerifier messageVerifier; - private final DatabaseComponent db; - private final ConnectionRegistry connRegistry; - private final StreamReaderFactory connReaderFactory; + private final StreamReaderFactory streamReaderFactory; private final PacketReaderFactory packetReaderFactory; private final StreamContext ctx; - private final SimplexTransportReader transport; + private final TransportConnectionReader transportReader; private final ContactId contactId; - private final TransportId transportId; - IncomingSimplexConnection(Executor dbExecutor, Executor cryptoExecutor, - MessageVerifier messageVerifier, DatabaseComponent db, - ConnectionRegistry connRegistry, - StreamReaderFactory connReaderFactory, + private volatile boolean interrupted = false; + + IncomingSession(DatabaseComponent db, Executor dbExecutor, + Executor cryptoExecutor, MessageVerifier messageVerifier, + StreamReaderFactory streamReaderFactory, PacketReaderFactory packetReaderFactory, StreamContext ctx, - SimplexTransportReader transport) { + TransportConnectionReader transportReader) { + this.db = db; this.dbExecutor = dbExecutor; this.cryptoExecutor = cryptoExecutor; this.messageVerifier = messageVerifier; - this.db = db; - this.connRegistry = connRegistry; - this.connReaderFactory = connReaderFactory; + this.streamReaderFactory = streamReaderFactory; this.packetReaderFactory = packetReaderFactory; this.ctx = ctx; - this.transport = transport; + this.transportReader = transportReader; contactId = ctx.getContactId(); - transportId = ctx.getTransportId(); } - void read() { - connRegistry.registerConnection(contactId, transportId); - try { - InputStream in = transport.getInputStream(); - int maxFrameLength = transport.getMaxFrameLength(); - StreamReader conn = connReaderFactory.createStreamReader(in, - maxFrameLength, ctx, true, true); - in = conn.getInputStream(); - PacketReader reader = packetReaderFactory.createPacketReader(in); - // Read packets until EOF - while(!reader.eof()) { - if(reader.hasAck()) { - Ack a = reader.readAck(); - dbExecutor.execute(new ReceiveAck(a)); - } else if(reader.hasMessage()) { - UnverifiedMessage m = reader.readMessage(); - cryptoExecutor.execute(new VerifyMessage(m)); - } else if(reader.hasRetentionAck()) { - RetentionAck a = reader.readRetentionAck(); - dbExecutor.execute(new ReceiveRetentionAck(a)); - } else if(reader.hasRetentionUpdate()) { - RetentionUpdate u = reader.readRetentionUpdate(); - dbExecutor.execute(new ReceiveRetentionUpdate(u)); - } else if(reader.hasSubscriptionAck()) { - SubscriptionAck a = reader.readSubscriptionAck(); - dbExecutor.execute(new ReceiveSubscriptionAck(a)); - } else if(reader.hasSubscriptionUpdate()) { - SubscriptionUpdate u = reader.readSubscriptionUpdate(); - dbExecutor.execute(new ReceiveSubscriptionUpdate(u)); - } else if(reader.hasTransportAck()) { - TransportAck a = reader.readTransportAck(); - dbExecutor.execute(new ReceiveTransportAck(a)); - } else if(reader.hasTransportUpdate()) { - TransportUpdate u = reader.readTransportUpdate(); - dbExecutor.execute(new ReceiveTransportUpdate(u)); - } else { - throw new FormatException(); - } + public void run() throws IOException { + InputStream in = transportReader.getInputStream(); + int maxFrameLength = transportReader.getMaxFrameLength(); + StreamReader streamReader = streamReaderFactory.createStreamReader(in, + maxFrameLength, ctx); + in = streamReader.getInputStream(); + PacketReader packetReader = packetReaderFactory.createPacketReader(in); + // Read packets until interrupted or EOF + while(!interrupted && !packetReader.eof()) { + if(packetReader.hasAck()) { + Ack a = packetReader.readAck(); + dbExecutor.execute(new ReceiveAck(a)); + } else if(packetReader.hasMessage()) { + UnverifiedMessage m = packetReader.readMessage(); + cryptoExecutor.execute(new VerifyMessage(m)); + } else if(packetReader.hasRetentionAck()) { + RetentionAck a = packetReader.readRetentionAck(); + dbExecutor.execute(new ReceiveRetentionAck(a)); + } else if(packetReader.hasRetentionUpdate()) { + RetentionUpdate u = packetReader.readRetentionUpdate(); + dbExecutor.execute(new ReceiveRetentionUpdate(u)); + } else if(packetReader.hasSubscriptionAck()) { + SubscriptionAck a = packetReader.readSubscriptionAck(); + dbExecutor.execute(new ReceiveSubscriptionAck(a)); + } else if(packetReader.hasSubscriptionUpdate()) { + SubscriptionUpdate u = packetReader.readSubscriptionUpdate(); + dbExecutor.execute(new ReceiveSubscriptionUpdate(u)); + } else if(packetReader.hasTransportAck()) { + TransportAck a = packetReader.readTransportAck(); + dbExecutor.execute(new ReceiveTransportAck(a)); + } else if(packetReader.hasTransportUpdate()) { + TransportUpdate u = packetReader.readTransportUpdate(); + dbExecutor.execute(new ReceiveTransportUpdate(u)); + } else { + throw new FormatException(); } - dispose(false, true); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true, true); - } finally { - connRegistry.unregisterConnection(contactId, transportId); } + in.close(); } - private void dispose(boolean exception, boolean recognised) { - ByteUtils.erase(ctx.getSecret()); - try { - transport.dispose(exception, recognised); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } + public void interrupt() { + // This won't interrupt a blocking read, but the read will throw an + // exception when the transport connection is closed + interrupted = true; } private class ReceiveAck implements Runnable { diff --git a/briar-core/src/org/briarproject/messaging/MessagingModule.java b/briar-core/src/org/briarproject/messaging/MessagingModule.java index c2dde22025cbce2e348f5a84d1e0725ef581d51c..5986266b8dd50b2062b0499e2b31f18d04833926 100644 --- a/briar-core/src/org/briarproject/messaging/MessagingModule.java +++ b/briar-core/src/org/briarproject/messaging/MessagingModule.java @@ -1,5 +1,7 @@ package org.briarproject.messaging; +import javax.inject.Singleton; + import org.briarproject.api.Author; import org.briarproject.api.AuthorFactory; import org.briarproject.api.crypto.CryptoComponent; @@ -9,6 +11,7 @@ import org.briarproject.api.messaging.MessageFactory; import org.briarproject.api.messaging.MessageVerifier; import org.briarproject.api.messaging.PacketReaderFactory; import org.briarproject.api.messaging.PacketWriterFactory; +import org.briarproject.api.messaging.MessagingSessionFactory; import org.briarproject.api.messaging.SubscriptionUpdate; import org.briarproject.api.messaging.UnverifiedMessage; import org.briarproject.api.serial.StructReader; @@ -18,6 +21,7 @@ import com.google.inject.Provides; public class MessagingModule extends AbstractModule { + @Override protected void configure() { bind(AuthorFactory.class).to(AuthorFactoryImpl.class); bind(GroupFactory.class).to(GroupFactoryImpl.class); @@ -25,6 +29,8 @@ public class MessagingModule extends AbstractModule { bind(MessageVerifier.class).to(MessageVerifierImpl.class); bind(PacketReaderFactory.class).to(PacketReaderFactoryImpl.class); bind(PacketWriterFactory.class).to(PacketWriterFactoryImpl.class); + bind(MessagingSessionFactory.class).to( + MessagingSessionFactoryImpl.class).in(Singleton.class); } @Provides diff --git a/briar-core/src/org/briarproject/messaging/MessagingSessionFactoryImpl.java b/briar-core/src/org/briarproject/messaging/MessagingSessionFactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..f8b3792a41af7bd8dae2b530a15cd832090146b4 --- /dev/null +++ b/briar-core/src/org/briarproject/messaging/MessagingSessionFactoryImpl.java @@ -0,0 +1,67 @@ +package org.briarproject.messaging; + +import java.util.concurrent.Executor; + +import javax.inject.Inject; + +import org.briarproject.api.crypto.CryptoExecutor; +import org.briarproject.api.db.DatabaseComponent; +import org.briarproject.api.db.DatabaseExecutor; +import org.briarproject.api.event.EventBus; +import org.briarproject.api.messaging.MessageVerifier; +import org.briarproject.api.messaging.MessagingSession; +import org.briarproject.api.messaging.PacketReaderFactory; +import org.briarproject.api.messaging.PacketWriterFactory; +import org.briarproject.api.messaging.MessagingSessionFactory; +import org.briarproject.api.plugins.TransportConnectionReader; +import org.briarproject.api.plugins.TransportConnectionWriter; +import org.briarproject.api.transport.StreamContext; +import org.briarproject.api.transport.StreamReaderFactory; +import org.briarproject.api.transport.StreamWriterFactory; + +class MessagingSessionFactoryImpl implements MessagingSessionFactory { + + private final DatabaseComponent db; + private final Executor dbExecutor, cryptoExecutor; + private final MessageVerifier messageVerifier; + private final EventBus eventBus; + private final StreamReaderFactory streamReaderFactory; + private final StreamWriterFactory streamWriterFactory; + private final PacketReaderFactory packetReaderFactory; + private final PacketWriterFactory packetWriterFactory; + + @Inject + MessagingSessionFactoryImpl(DatabaseComponent db, + @DatabaseExecutor Executor dbExecutor, + @CryptoExecutor Executor cryptoExecutor, + MessageVerifier messageVerifier, EventBus eventBus, + StreamReaderFactory streamReaderFactory, + StreamWriterFactory streamWriterFactory, + PacketReaderFactory packetReaderFactory, + PacketWriterFactory packetWriterFactory) { + this.db = db; + this.dbExecutor = dbExecutor; + this.cryptoExecutor = cryptoExecutor; + this.messageVerifier = messageVerifier; + this.eventBus = eventBus; + this.streamReaderFactory = streamReaderFactory; + this.streamWriterFactory = streamWriterFactory; + this.packetReaderFactory = packetReaderFactory; + this.packetWriterFactory = packetWriterFactory; + } + + public MessagingSession createIncomingSession(StreamContext ctx, + TransportConnectionReader r) { + return new IncomingSession(db, dbExecutor, cryptoExecutor, + messageVerifier, streamReaderFactory, packetReaderFactory, + ctx, r); + } + + public MessagingSession createOutgoingSession(StreamContext ctx, + TransportConnectionWriter w, boolean duplex) { + if(duplex) return new ReactiveOutgoingSession(db, dbExecutor, eventBus, + streamWriterFactory, packetWriterFactory, ctx, w); + else return new SinglePassOutgoingSession(db, dbExecutor, + streamWriterFactory, packetWriterFactory, ctx, w); + } +} diff --git a/briar-core/src/org/briarproject/messaging/ReactiveOutgoingSession.java b/briar-core/src/org/briarproject/messaging/ReactiveOutgoingSession.java new file mode 100644 index 0000000000000000000000000000000000000000..c3eae51056073fa352e5d6df0b37c4cdd5fb20ff --- /dev/null +++ b/briar-core/src/org/briarproject/messaging/ReactiveOutgoingSession.java @@ -0,0 +1,518 @@ +package org.briarproject.messaging; + +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; +import static org.briarproject.api.messaging.MessagingConstants.MAX_PACKET_LENGTH; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Collection; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Logger; + +import org.briarproject.api.ContactId; +import org.briarproject.api.db.DatabaseComponent; +import org.briarproject.api.db.DbException; +import org.briarproject.api.event.ContactRemovedEvent; +import org.briarproject.api.event.Event; +import org.briarproject.api.event.EventBus; +import org.briarproject.api.event.EventListener; +import org.briarproject.api.event.LocalSubscriptionsUpdatedEvent; +import org.briarproject.api.event.LocalTransportsUpdatedEvent; +import org.briarproject.api.event.MessageAddedEvent; +import org.briarproject.api.event.MessageExpiredEvent; +import org.briarproject.api.event.MessageRequestedEvent; +import org.briarproject.api.event.MessageToAckEvent; +import org.briarproject.api.event.MessageToRequestEvent; +import org.briarproject.api.event.RemoteRetentionTimeUpdatedEvent; +import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent; +import org.briarproject.api.event.RemoteTransportsUpdatedEvent; +import org.briarproject.api.event.TransportRemovedEvent; +import org.briarproject.api.messaging.Ack; +import org.briarproject.api.messaging.MessagingSession; +import org.briarproject.api.messaging.Offer; +import org.briarproject.api.messaging.PacketWriter; +import org.briarproject.api.messaging.PacketWriterFactory; +import org.briarproject.api.messaging.Request; +import org.briarproject.api.messaging.RetentionAck; +import org.briarproject.api.messaging.RetentionUpdate; +import org.briarproject.api.messaging.SubscriptionAck; +import org.briarproject.api.messaging.SubscriptionUpdate; +import org.briarproject.api.messaging.TransportAck; +import org.briarproject.api.messaging.TransportUpdate; +import org.briarproject.api.plugins.TransportConnectionWriter; +import org.briarproject.api.transport.StreamContext; +import org.briarproject.api.transport.StreamWriter; +import org.briarproject.api.transport.StreamWriterFactory; + +/** + * An outgoing {@link org.briarproject.api.messaging.MessagingSession + * MessagingSession} that keeps its output stream open and reacts to events + * that make packets available to send. + */ +class ReactiveOutgoingSession implements MessagingSession, EventListener { + + private static final Logger LOG = + Logger.getLogger(ReactiveOutgoingSession.class.getName()); + + private static final ThrowingRunnable<IOException> CLOSE = + new ThrowingRunnable<IOException>() { + public void run() {} + }; + + private final DatabaseComponent db; + private final Executor dbExecutor; + private final EventBus eventBus; + private final StreamWriterFactory streamWriterFactory; + private final PacketWriterFactory packetWriterFactory; + private final StreamContext ctx; + private final TransportConnectionWriter transportWriter; + private final ContactId contactId; + private final long maxLatency; + private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks; + + private volatile PacketWriter packetWriter = null; + private volatile boolean interrupted = false; + + ReactiveOutgoingSession(DatabaseComponent db, Executor dbExecutor, + EventBus eventBus, StreamWriterFactory streamWriterFactory, + PacketWriterFactory packetWriterFactory, StreamContext ctx, + TransportConnectionWriter transportWriter) { + this.db = db; + this.dbExecutor = dbExecutor; + this.eventBus = eventBus; + this.streamWriterFactory = streamWriterFactory; + this.packetWriterFactory = packetWriterFactory; + this.ctx = ctx; + this.transportWriter = transportWriter; + contactId = ctx.getContactId(); + maxLatency = transportWriter.getMaxLatency(); + writerTasks = new LinkedBlockingQueue<ThrowingRunnable<IOException>>(); + } + + public void run() throws IOException { + eventBus.addListener(this); + try { + OutputStream out = transportWriter.getOutputStream(); + int maxFrameLength = transportWriter.getMaxFrameLength(); + StreamWriter streamWriter = streamWriterFactory.createStreamWriter( + out, maxFrameLength, ctx); + out = streamWriter.getOutputStream(); + packetWriter = packetWriterFactory.createPacketWriter(out, true); + // Start a query for each type of packet, in order of urgency + dbExecutor.execute(new GenerateTransportAcks()); + dbExecutor.execute(new GenerateTransportUpdates()); + dbExecutor.execute(new GenerateSubscriptionAck()); + dbExecutor.execute(new GenerateSubscriptionUpdate()); + dbExecutor.execute(new GenerateRetentionAck()); + dbExecutor.execute(new GenerateRetentionUpdate()); + dbExecutor.execute(new GenerateAck()); + dbExecutor.execute(new GenerateBatch()); + dbExecutor.execute(new GenerateOffer()); + dbExecutor.execute(new GenerateRequest()); + // Write packets until interrupted + try { + while(!interrupted) { + ThrowingRunnable<IOException> task = writerTasks.take(); + if(task == CLOSE) break; + task.run(); + } + out.flush(); + out.close(); + } catch(InterruptedException e) { + LOG.info("Interrupted while waiting for a packet to write"); + Thread.currentThread().interrupt(); + } + } finally { + eventBus.removeListener(this); + } + } + + public void interrupt() { + interrupted = true; + writerTasks.add(CLOSE); + } + + public void eventOccurred(Event e) { + if(e instanceof ContactRemovedEvent) { + ContactRemovedEvent c = (ContactRemovedEvent) e; + if(contactId.equals(c.getContactId())) { + LOG.info("Contact removed, closing"); + interrupt(); + } + } else if(e instanceof MessageAddedEvent) { + dbExecutor.execute(new GenerateOffer()); + } else if(e instanceof MessageExpiredEvent) { + dbExecutor.execute(new GenerateRetentionUpdate()); + } else if(e instanceof LocalSubscriptionsUpdatedEvent) { + LocalSubscriptionsUpdatedEvent l = + (LocalSubscriptionsUpdatedEvent) e; + if(l.getAffectedContacts().contains(contactId)) { + dbExecutor.execute(new GenerateSubscriptionUpdate()); + dbExecutor.execute(new GenerateOffer()); + } + } else if(e instanceof LocalTransportsUpdatedEvent) { + dbExecutor.execute(new GenerateTransportUpdates()); + } else if(e instanceof MessageRequestedEvent) { + if(((MessageRequestedEvent) e).getContactId().equals(contactId)) + dbExecutor.execute(new GenerateBatch()); + } else if(e instanceof MessageToAckEvent) { + if(((MessageToAckEvent) e).getContactId().equals(contactId)) + dbExecutor.execute(new GenerateAck()); + } else if(e instanceof MessageToRequestEvent) { + if(((MessageToRequestEvent) e).getContactId().equals(contactId)) + dbExecutor.execute(new GenerateRequest()); + } else if(e instanceof RemoteRetentionTimeUpdatedEvent) { + dbExecutor.execute(new GenerateRetentionAck()); + } else if(e instanceof RemoteSubscriptionsUpdatedEvent) { + dbExecutor.execute(new GenerateSubscriptionAck()); + dbExecutor.execute(new GenerateOffer()); + } else if(e instanceof RemoteTransportsUpdatedEvent) { + dbExecutor.execute(new GenerateTransportAcks()); + } else if(e instanceof TransportRemovedEvent) { + TransportRemovedEvent t = (TransportRemovedEvent) e; + if(ctx.getTransportId().equals(t.getTransportId())) { + LOG.info("Transport removed, closing"); + interrupt(); + } + } + } + + // This task runs on the database thread + private class GenerateAck implements Runnable { + + public void run() { + int maxMessages = packetWriter.getMaxMessagesForAck(Long.MAX_VALUE); + try { + Ack a = db.generateAck(contactId, maxMessages); + if(LOG.isLoggable(INFO)) + LOG.info("Generated ack: " + (a != null)); + if(a != null) writerTasks.add(new WriteAck(a)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This task runs on the writer thread + private class WriteAck implements ThrowingRunnable<IOException> { + + private final Ack ack; + + private WriteAck(Ack ack) { + this.ack = ack; + } + + public void run() throws IOException { + packetWriter.writeAck(ack); + LOG.info("Sent ack"); + dbExecutor.execute(new GenerateAck()); + } + } + + // This task runs on the database thread + private class GenerateBatch implements Runnable { + + public void run() { + try { + Collection<byte[]> b = db.generateRequestedBatch(contactId, + MAX_PACKET_LENGTH, maxLatency); + if(LOG.isLoggable(INFO)) + LOG.info("Generated batch: " + (b != null)); + if(b != null) writerTasks.add(new WriteBatch(b)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This task runs on the writer thread + private class WriteBatch implements ThrowingRunnable<IOException> { + + private final Collection<byte[]> batch; + + private WriteBatch(Collection<byte[]> batch) { + this.batch = batch; + } + + public void run() throws IOException { + for(byte[] raw : batch) packetWriter.writeMessage(raw); + LOG.info("Sent batch"); + dbExecutor.execute(new GenerateBatch()); + } + } + + // This task runs on the database thread + private class GenerateOffer implements Runnable { + + public void run() { + int maxMessages = packetWriter.getMaxMessagesForOffer( + Long.MAX_VALUE); + try { + Offer o = db.generateOffer(contactId, maxMessages, maxLatency); + if(LOG.isLoggable(INFO)) + LOG.info("Generated offer: " + (o != null)); + if(o != null) writerTasks.add(new WriteOffer(o)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This task runs on the writer thread + private class WriteOffer implements ThrowingRunnable<IOException> { + + private final Offer offer; + + private WriteOffer(Offer offer) { + this.offer = offer; + } + + public void run() throws IOException { + packetWriter.writeOffer(offer); + LOG.info("Sent offer"); + dbExecutor.execute(new GenerateOffer()); + } + } + + // This task runs on the database thread + private class GenerateRequest implements Runnable { + + public void run() { + int maxMessages = packetWriter.getMaxMessagesForRequest( + Long.MAX_VALUE); + try { + Request r = db.generateRequest(contactId, maxMessages); + if(LOG.isLoggable(INFO)) + LOG.info("Generated request: " + (r != null)); + if(r != null) writerTasks.add(new WriteRequest(r)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This task runs on the writer thread + private class WriteRequest implements ThrowingRunnable<IOException> { + + private final Request request; + + private WriteRequest(Request request) { + this.request = request; + } + + public void run() throws IOException { + packetWriter.writeRequest(request); + LOG.info("Sent request"); + dbExecutor.execute(new GenerateRequest()); + } + } + + // This task runs on the database thread + private class GenerateRetentionAck implements Runnable { + + public void run() { + try { + RetentionAck a = db.generateRetentionAck(contactId); + if(LOG.isLoggable(INFO)) + LOG.info("Generated retention ack: " + (a != null)); + if(a != null) writerTasks.add(new WriteRetentionAck(a)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This tasks runs on the writer thread + private class WriteRetentionAck implements ThrowingRunnable<IOException> { + + private final RetentionAck ack; + + private WriteRetentionAck(RetentionAck ack) { + this.ack = ack; + } + + + public void run() throws IOException { + packetWriter.writeRetentionAck(ack); + LOG.info("Sent retention ack"); + dbExecutor.execute(new GenerateRetentionAck()); + } + } + + // This task runs on the database thread + private class GenerateRetentionUpdate implements Runnable { + + public void run() { + try { + RetentionUpdate u = + db.generateRetentionUpdate(contactId, maxLatency); + if(LOG.isLoggable(INFO)) + LOG.info("Generated retention update: " + (u != null)); + if(u != null) writerTasks.add(new WriteRetentionUpdate(u)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This task runs on the writer thread + private class WriteRetentionUpdate + implements ThrowingRunnable<IOException> { + + private final RetentionUpdate update; + + private WriteRetentionUpdate(RetentionUpdate update) { + this.update = update; + } + + public void run() throws IOException { + packetWriter.writeRetentionUpdate(update); + LOG.info("Sent retention update"); + dbExecutor.execute(new GenerateRetentionUpdate()); + } + } + + // This task runs on the database thread + private class GenerateSubscriptionAck implements Runnable { + + public void run() { + try { + SubscriptionAck a = db.generateSubscriptionAck(contactId); + if(LOG.isLoggable(INFO)) + LOG.info("Generated subscription ack: " + (a != null)); + if(a != null) writerTasks.add(new WriteSubscriptionAck(a)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This tasks runs on the writer thread + private class WriteSubscriptionAck + implements ThrowingRunnable<IOException> { + + private final SubscriptionAck ack; + + private WriteSubscriptionAck(SubscriptionAck ack) { + this.ack = ack; + } + + public void run() throws IOException { + packetWriter.writeSubscriptionAck(ack); + LOG.info("Sent subscription ack"); + dbExecutor.execute(new GenerateSubscriptionAck()); + } + } + + // This task runs on the database thread + private class GenerateSubscriptionUpdate implements Runnable { + + public void run() { + try { + SubscriptionUpdate u = + db.generateSubscriptionUpdate(contactId, maxLatency); + if(LOG.isLoggable(INFO)) + LOG.info("Generated subscription update: " + (u != null)); + if(u != null) writerTasks.add(new WriteSubscriptionUpdate(u)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This task runs on the writer thread + private class WriteSubscriptionUpdate + implements ThrowingRunnable<IOException> { + + private final SubscriptionUpdate update; + + private WriteSubscriptionUpdate(SubscriptionUpdate update) { + this.update = update; + } + + public void run() throws IOException { + packetWriter.writeSubscriptionUpdate(update); + LOG.info("Sent subscription update"); + dbExecutor.execute(new GenerateSubscriptionUpdate()); + } + } + + // This task runs on the database thread + private class GenerateTransportAcks implements Runnable { + + public void run() { + try { + Collection<TransportAck> acks = + db.generateTransportAcks(contactId); + if(LOG.isLoggable(INFO)) + LOG.info("Generated transport acks: " + (acks != null)); + if(acks != null) writerTasks.add(new WriteTransportAcks(acks)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This tasks runs on the writer thread + private class WriteTransportAcks implements ThrowingRunnable<IOException> { + + private final Collection<TransportAck> acks; + + private WriteTransportAcks(Collection<TransportAck> acks) { + this.acks = acks; + } + + public void run() throws IOException { + for(TransportAck a : acks) packetWriter.writeTransportAck(a); + LOG.info("Sent transport acks"); + dbExecutor.execute(new GenerateTransportAcks()); + } + } + + // This task runs on the database thread + private class GenerateTransportUpdates implements Runnable { + + public void run() { + try { + Collection<TransportUpdate> t = + db.generateTransportUpdates(contactId, maxLatency); + if(LOG.isLoggable(INFO)) + LOG.info("Generated transport updates: " + (t != null)); + if(t != null) writerTasks.add(new WriteTransportUpdates(t)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This task runs on the writer thread + private class WriteTransportUpdates + implements ThrowingRunnable<IOException> { + + private final Collection<TransportUpdate> updates; + + private WriteTransportUpdates(Collection<TransportUpdate> updates) { + this.updates = updates; + } + + public void run() throws IOException { + for(TransportUpdate u : updates) + packetWriter.writeTransportUpdate(u); + LOG.info("Sent transport updates"); + dbExecutor.execute(new GenerateTransportUpdates()); + } + } +} diff --git a/briar-core/src/org/briarproject/messaging/SinglePassOutgoingSession.java b/briar-core/src/org/briarproject/messaging/SinglePassOutgoingSession.java new file mode 100644 index 0000000000000000000000000000000000000000..c09829a92044d11ca1b3254a5fcdb513f55f187c --- /dev/null +++ b/briar-core/src/org/briarproject/messaging/SinglePassOutgoingSession.java @@ -0,0 +1,395 @@ +package org.briarproject.messaging; + +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; +import static org.briarproject.api.messaging.MessagingConstants.MAX_PACKET_LENGTH; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Collection; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Logger; + +import org.briarproject.api.ContactId; +import org.briarproject.api.db.DatabaseComponent; +import org.briarproject.api.db.DbException; +import org.briarproject.api.messaging.Ack; +import org.briarproject.api.messaging.MessagingSession; +import org.briarproject.api.messaging.PacketWriter; +import org.briarproject.api.messaging.PacketWriterFactory; +import org.briarproject.api.messaging.RetentionAck; +import org.briarproject.api.messaging.RetentionUpdate; +import org.briarproject.api.messaging.SubscriptionAck; +import org.briarproject.api.messaging.SubscriptionUpdate; +import org.briarproject.api.messaging.TransportAck; +import org.briarproject.api.messaging.TransportUpdate; +import org.briarproject.api.plugins.TransportConnectionWriter; +import org.briarproject.api.transport.StreamContext; +import org.briarproject.api.transport.StreamWriter; +import org.briarproject.api.transport.StreamWriterFactory; + +/** + * An outgoing {@link org.briarproject.api.messaging.MessagingSession + * MessagingSession} that closes its output stream when no more packets are + * available to send. + */ +class SinglePassOutgoingSession implements MessagingSession { + + private static final Logger LOG = + Logger.getLogger(SinglePassOutgoingSession.class.getName()); + + private static final ThrowingRunnable<IOException> CLOSE = + new ThrowingRunnable<IOException>() { + public void run() {} + }; + + private final DatabaseComponent db; + private final Executor dbExecutor; + private final StreamWriterFactory streamWriterFactory; + private final PacketWriterFactory packetWriterFactory; + private final StreamContext ctx; + private final TransportConnectionWriter transportWriter; + private final ContactId contactId; + private final long maxLatency; + private final AtomicInteger outstandingQueries; + private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks; + + private volatile StreamWriter streamWriter = null; + private volatile PacketWriter packetWriter = null; + private volatile boolean interrupted = false; + + SinglePassOutgoingSession(DatabaseComponent db, Executor dbExecutor, + StreamWriterFactory streamWriterFactory, + PacketWriterFactory packetWriterFactory, StreamContext ctx, + TransportConnectionWriter transportWriter) { + this.db = db; + this.dbExecutor = dbExecutor; + this.streamWriterFactory = streamWriterFactory; + this.packetWriterFactory = packetWriterFactory; + this.ctx = ctx; + this.transportWriter = transportWriter; + contactId = ctx.getContactId(); + maxLatency = transportWriter.getMaxLatency(); + outstandingQueries = new AtomicInteger(8); // One per type of packet + writerTasks = new LinkedBlockingQueue<ThrowingRunnable<IOException>>(); + } + + public void run() throws IOException { + OutputStream out = transportWriter.getOutputStream(); + int maxFrameLength = transportWriter.getMaxFrameLength(); + streamWriter = streamWriterFactory.createStreamWriter(out, + maxFrameLength, ctx); + out = streamWriter.getOutputStream(); + packetWriter = packetWriterFactory.createPacketWriter(out, false); + // Start a query for each type of packet, in order of urgency + dbExecutor.execute(new GenerateTransportAcks()); + dbExecutor.execute(new GenerateTransportUpdates()); + dbExecutor.execute(new GenerateSubscriptionAck()); + dbExecutor.execute(new GenerateSubscriptionUpdate()); + dbExecutor.execute(new GenerateRetentionAck()); + dbExecutor.execute(new GenerateRetentionUpdate()); + dbExecutor.execute(new GenerateAck()); + dbExecutor.execute(new GenerateBatch()); + // Write packets until interrupted or there are no more packets to write + try { + while(!interrupted) { + ThrowingRunnable<IOException> task = writerTasks.take(); + if(task == CLOSE) break; + task.run(); + } + out.flush(); + out.close(); + } catch(InterruptedException e) { + LOG.info("Interrupted while waiting for a packet to write"); + Thread.currentThread().interrupt(); + } + } + + public void interrupt() { + interrupted = true; + writerTasks.add(CLOSE); + } + + private void decrementOutstandingQueries() { + if(outstandingQueries.decrementAndGet() == 0) writerTasks.add(CLOSE); + } + + // This task runs on the database thread + private class GenerateAck implements Runnable { + + public void run() { + int maxMessages = packetWriter.getMaxMessagesForAck(Long.MAX_VALUE); + try { + Ack a = db.generateAck(contactId, maxMessages); + if(LOG.isLoggable(INFO)) + LOG.info("Generated ack: " + (a != null)); + if(a == null) decrementOutstandingQueries(); + else writerTasks.add(new WriteAck(a)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This task runs on the writer thread + private class WriteAck implements ThrowingRunnable<IOException> { + + private final Ack ack; + + private WriteAck(Ack ack) { + this.ack = ack; + } + + public void run() throws IOException { + packetWriter.writeAck(ack); + LOG.info("Sent ack"); + dbExecutor.execute(new GenerateAck()); + } + } + + // This task runs on the database thread + private class GenerateBatch implements Runnable { + + public void run() { + try { + Collection<byte[]> b = db.generateBatch(contactId, + MAX_PACKET_LENGTH, maxLatency); + if(LOG.isLoggable(INFO)) + LOG.info("Generated batch: " + (b != null)); + if(b == null) decrementOutstandingQueries(); + else writerTasks.add(new WriteBatch(b)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This task runs on the writer thread + private class WriteBatch implements ThrowingRunnable<IOException> { + + private final Collection<byte[]> batch; + + private WriteBatch(Collection<byte[]> batch) { + this.batch = batch; + } + + public void run() throws IOException { + for(byte[] raw : batch) packetWriter.writeMessage(raw); + LOG.info("Sent batch"); + dbExecutor.execute(new GenerateBatch()); + } + } + + // This task runs on the database thread + private class GenerateRetentionAck implements Runnable { + + public void run() { + try { + RetentionAck a = db.generateRetentionAck(contactId); + if(LOG.isLoggable(INFO)) + LOG.info("Generated retention ack: " + (a != null)); + if(a == null) decrementOutstandingQueries(); + else writerTasks.add(new WriteRetentionAck(a)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This tasks runs on the writer thread + private class WriteRetentionAck implements ThrowingRunnable<IOException> { + + private final RetentionAck ack; + + private WriteRetentionAck(RetentionAck ack) { + this.ack = ack; + } + + + public void run() throws IOException { + packetWriter.writeRetentionAck(ack); + LOG.info("Sent retention ack"); + dbExecutor.execute(new GenerateRetentionAck()); + } + } + + // This task runs on the database thread + private class GenerateRetentionUpdate implements Runnable { + + public void run() { + try { + RetentionUpdate u = + db.generateRetentionUpdate(contactId, maxLatency); + if(LOG.isLoggable(INFO)) + LOG.info("Generated retention update: " + (u != null)); + if(u == null) decrementOutstandingQueries(); + else writerTasks.add(new WriteRetentionUpdate(u)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This task runs on the writer thread + private class WriteRetentionUpdate + implements ThrowingRunnable<IOException> { + + private final RetentionUpdate update; + + private WriteRetentionUpdate(RetentionUpdate update) { + this.update = update; + } + + public void run() throws IOException { + packetWriter.writeRetentionUpdate(update); + LOG.info("Sent retention update"); + dbExecutor.execute(new GenerateRetentionUpdate()); + } + } + + // This task runs on the database thread + private class GenerateSubscriptionAck implements Runnable { + + public void run() { + try { + SubscriptionAck a = db.generateSubscriptionAck(contactId); + if(LOG.isLoggable(INFO)) + LOG.info("Generated subscription ack: " + (a != null)); + if(a == null) decrementOutstandingQueries(); + else writerTasks.add(new WriteSubscriptionAck(a)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This tasks runs on the writer thread + private class WriteSubscriptionAck + implements ThrowingRunnable<IOException> { + + private final SubscriptionAck ack; + + private WriteSubscriptionAck(SubscriptionAck ack) { + this.ack = ack; + } + + public void run() throws IOException { + packetWriter.writeSubscriptionAck(ack); + LOG.info("Sent subscription ack"); + dbExecutor.execute(new GenerateSubscriptionAck()); + } + } + + // This task runs on the database thread + private class GenerateSubscriptionUpdate implements Runnable { + + public void run() { + try { + SubscriptionUpdate u = + db.generateSubscriptionUpdate(contactId, maxLatency); + if(LOG.isLoggable(INFO)) + LOG.info("Generated subscription update: " + (u != null)); + if(u == null) decrementOutstandingQueries(); + else writerTasks.add(new WriteSubscriptionUpdate(u)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This task runs on the writer thread + private class WriteSubscriptionUpdate + implements ThrowingRunnable<IOException> { + + private final SubscriptionUpdate update; + + private WriteSubscriptionUpdate(SubscriptionUpdate update) { + this.update = update; + } + + public void run() throws IOException { + packetWriter.writeSubscriptionUpdate(update); + LOG.info("Sent subscription update"); + dbExecutor.execute(new GenerateSubscriptionUpdate()); + } + } + + // This task runs on the database thread + private class GenerateTransportAcks implements Runnable { + + public void run() { + try { + Collection<TransportAck> acks = + db.generateTransportAcks(contactId); + if(LOG.isLoggable(INFO)) + LOG.info("Generated transport acks: " + (acks != null)); + if(acks == null) decrementOutstandingQueries(); + else writerTasks.add(new WriteTransportAcks(acks)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This tasks runs on the writer thread + private class WriteTransportAcks implements ThrowingRunnable<IOException> { + + private final Collection<TransportAck> acks; + + private WriteTransportAcks(Collection<TransportAck> acks) { + this.acks = acks; + } + + public void run() throws IOException { + for(TransportAck a : acks) packetWriter.writeTransportAck(a); + LOG.info("Sent transport acks"); + dbExecutor.execute(new GenerateTransportAcks()); + } + } + + // This task runs on the database thread + private class GenerateTransportUpdates implements Runnable { + + public void run() { + try { + Collection<TransportUpdate> t = + db.generateTransportUpdates(contactId, maxLatency); + if(LOG.isLoggable(INFO)) + LOG.info("Generated transport updates: " + (t != null)); + if(t == null) decrementOutstandingQueries(); + else writerTasks.add(new WriteTransportUpdates(t)); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + interrupt(); + } + } + } + + // This task runs on the writer thread + private class WriteTransportUpdates + implements ThrowingRunnable<IOException> { + + private final Collection<TransportUpdate> updates; + + private WriteTransportUpdates(Collection<TransportUpdate> updates) { + this.updates = updates; + } + + public void run() throws IOException { + for(TransportUpdate u : updates) + packetWriter.writeTransportUpdate(u); + LOG.info("Sent transport updates"); + dbExecutor.execute(new GenerateTransportUpdates()); + } + } +} diff --git a/briar-core/src/org/briarproject/messaging/ThrowingRunnable.java b/briar-core/src/org/briarproject/messaging/ThrowingRunnable.java new file mode 100644 index 0000000000000000000000000000000000000000..334581daac6631e0e3245299d2762cf359fecf60 --- /dev/null +++ b/briar-core/src/org/briarproject/messaging/ThrowingRunnable.java @@ -0,0 +1,6 @@ +package org.briarproject.messaging; + +interface ThrowingRunnable<T extends Throwable> { + + public void run() throws T; +} diff --git a/briar-core/src/org/briarproject/messaging/duplex/DuplexConnection.java b/briar-core/src/org/briarproject/messaging/duplex/DuplexConnection.java deleted file mode 100644 index 0ba89f29fd93a0b649a9586b507c10bbe71af0da..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/messaging/duplex/DuplexConnection.java +++ /dev/null @@ -1,871 +0,0 @@ -package org.briarproject.messaging.duplex; - -import static java.util.logging.Level.INFO; -import static java.util.logging.Level.WARNING; -import static org.briarproject.api.messaging.MessagingConstants.MAX_PACKET_LENGTH; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.GeneralSecurityException; -import java.util.Collection; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Executor; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.logging.Logger; - -import org.briarproject.api.ContactId; -import org.briarproject.api.FormatException; -import org.briarproject.api.TransportId; -import org.briarproject.api.db.DatabaseComponent; -import org.briarproject.api.db.DbException; -import org.briarproject.api.event.ContactRemovedEvent; -import org.briarproject.api.event.Event; -import org.briarproject.api.event.EventBus; -import org.briarproject.api.event.EventListener; -import org.briarproject.api.event.LocalSubscriptionsUpdatedEvent; -import org.briarproject.api.event.LocalTransportsUpdatedEvent; -import org.briarproject.api.event.MessageAddedEvent; -import org.briarproject.api.event.MessageExpiredEvent; -import org.briarproject.api.event.MessageRequestedEvent; -import org.briarproject.api.event.MessageToAckEvent; -import org.briarproject.api.event.MessageToRequestEvent; -import org.briarproject.api.event.RemoteRetentionTimeUpdatedEvent; -import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent; -import org.briarproject.api.event.RemoteTransportsUpdatedEvent; -import org.briarproject.api.messaging.Ack; -import org.briarproject.api.messaging.Message; -import org.briarproject.api.messaging.MessageVerifier; -import org.briarproject.api.messaging.Offer; -import org.briarproject.api.messaging.PacketReader; -import org.briarproject.api.messaging.PacketReaderFactory; -import org.briarproject.api.messaging.PacketWriter; -import org.briarproject.api.messaging.PacketWriterFactory; -import org.briarproject.api.messaging.Request; -import org.briarproject.api.messaging.RetentionAck; -import org.briarproject.api.messaging.RetentionUpdate; -import org.briarproject.api.messaging.SubscriptionAck; -import org.briarproject.api.messaging.SubscriptionUpdate; -import org.briarproject.api.messaging.TransportAck; -import org.briarproject.api.messaging.TransportUpdate; -import org.briarproject.api.messaging.UnverifiedMessage; -import org.briarproject.api.plugins.duplex.DuplexTransportConnection; -import org.briarproject.api.transport.ConnectionRegistry; -import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamReader; -import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.api.transport.StreamWriter; -import org.briarproject.api.transport.StreamWriterFactory; -import org.briarproject.util.ByteUtils; - -abstract class DuplexConnection implements EventListener { - - private static final Logger LOG = - Logger.getLogger(DuplexConnection.class.getName()); - - private static final Runnable CLOSE = new Runnable() { - public void run() {} - }; - - private static final Runnable DIE = new Runnable() { - public void run() {} - }; - - protected final DatabaseComponent db; - protected final EventBus eventBus; - protected final ConnectionRegistry connRegistry; - protected final StreamReaderFactory connReaderFactory; - protected final StreamWriterFactory connWriterFactory; - protected final PacketReaderFactory packetReaderFactory; - protected final PacketWriterFactory packetWriterFactory; - protected final StreamContext ctx; - protected final DuplexTransportConnection transport; - protected final ContactId contactId; - protected final TransportId transportId; - - private final Executor dbExecutor, cryptoExecutor; - private final MessageVerifier messageVerifier; - private final long maxLatency; - private final AtomicBoolean disposed; - private final BlockingQueue<Runnable> writerTasks; - - private volatile PacketWriter writer = null; - - DuplexConnection(Executor dbExecutor, Executor cryptoExecutor, - MessageVerifier messageVerifier, DatabaseComponent db, - EventBus eventBus, ConnectionRegistry connRegistry, - StreamReaderFactory connReaderFactory, - StreamWriterFactory connWriterFactory, - PacketReaderFactory packetReaderFactory, - PacketWriterFactory packetWriterFactory, StreamContext ctx, - DuplexTransportConnection transport) { - this.dbExecutor = dbExecutor; - this.cryptoExecutor = cryptoExecutor; - this.messageVerifier = messageVerifier; - this.db = db; - this.eventBus = eventBus; - this.connRegistry = connRegistry; - this.connReaderFactory = connReaderFactory; - this.connWriterFactory = connWriterFactory; - this.packetReaderFactory = packetReaderFactory; - this.packetWriterFactory = packetWriterFactory; - this.ctx = ctx; - this.transport = transport; - contactId = ctx.getContactId(); - transportId = ctx.getTransportId(); - maxLatency = transport.getMaxLatency(); - disposed = new AtomicBoolean(false); - writerTasks = new LinkedBlockingQueue<Runnable>(); - } - - protected abstract StreamReader createStreamReader() throws IOException; - - protected abstract StreamWriter createStreamWriter() throws IOException; - - public void eventOccurred(Event e) { - if(e instanceof ContactRemovedEvent) { - ContactRemovedEvent c = (ContactRemovedEvent) e; - if(contactId.equals(c.getContactId())) writerTasks.add(CLOSE); - } else if(e instanceof MessageAddedEvent) { - dbExecutor.execute(new GenerateOffer()); - } else if(e instanceof MessageExpiredEvent) { - dbExecutor.execute(new GenerateRetentionUpdate()); - } else if(e instanceof LocalSubscriptionsUpdatedEvent) { - LocalSubscriptionsUpdatedEvent l = - (LocalSubscriptionsUpdatedEvent) e; - if(l.getAffectedContacts().contains(contactId)) { - dbExecutor.execute(new GenerateSubscriptionUpdate()); - dbExecutor.execute(new GenerateOffer()); - } - } else if(e instanceof LocalTransportsUpdatedEvent) { - dbExecutor.execute(new GenerateTransportUpdates()); - } else if(e instanceof MessageRequestedEvent) { - if(((MessageRequestedEvent) e).getContactId().equals(contactId)) - dbExecutor.execute(new GenerateBatch()); - } else if(e instanceof MessageToAckEvent) { - if(((MessageToAckEvent) e).getContactId().equals(contactId)) - dbExecutor.execute(new GenerateAck()); - } else if(e instanceof MessageToRequestEvent) { - if(((MessageToRequestEvent) e).getContactId().equals(contactId)) - dbExecutor.execute(new GenerateRequest()); - } else if(e instanceof RemoteRetentionTimeUpdatedEvent) { - dbExecutor.execute(new GenerateRetentionAck()); - } else if(e instanceof RemoteSubscriptionsUpdatedEvent) { - dbExecutor.execute(new GenerateSubscriptionAck()); - dbExecutor.execute(new GenerateOffer()); - } else if(e instanceof RemoteTransportsUpdatedEvent) { - dbExecutor.execute(new GenerateTransportAcks()); - } - } - - void read() { - try { - InputStream in = createStreamReader().getInputStream(); - PacketReader reader = packetReaderFactory.createPacketReader(in); - LOG.info("Starting to read"); - while(!reader.eof()) { - if(reader.hasAck()) { - Ack a = reader.readAck(); - LOG.info("Received ack"); - dbExecutor.execute(new ReceiveAck(a)); - } else if(reader.hasMessage()) { - UnverifiedMessage m = reader.readMessage(); - LOG.info("Received message"); - cryptoExecutor.execute(new VerifyMessage(m)); - } else if(reader.hasOffer()) { - Offer o = reader.readOffer(); - LOG.info("Received offer"); - dbExecutor.execute(new ReceiveOffer(o)); - } else if(reader.hasRequest()) { - Request r = reader.readRequest(); - LOG.info("Received request"); - dbExecutor.execute(new ReceiveRequest(r)); - } else if(reader.hasRetentionAck()) { - RetentionAck a = reader.readRetentionAck(); - LOG.info("Received retention ack"); - dbExecutor.execute(new ReceiveRetentionAck(a)); - } else if(reader.hasRetentionUpdate()) { - RetentionUpdate u = reader.readRetentionUpdate(); - LOG.info("Received retention update"); - dbExecutor.execute(new ReceiveRetentionUpdate(u)); - } else if(reader.hasSubscriptionAck()) { - SubscriptionAck a = reader.readSubscriptionAck(); - LOG.info("Received subscription ack"); - dbExecutor.execute(new ReceiveSubscriptionAck(a)); - } else if(reader.hasSubscriptionUpdate()) { - SubscriptionUpdate u = reader.readSubscriptionUpdate(); - LOG.info("Received subscription update"); - dbExecutor.execute(new ReceiveSubscriptionUpdate(u)); - } else if(reader.hasTransportAck()) { - TransportAck a = reader.readTransportAck(); - LOG.info("Received transport ack"); - dbExecutor.execute(new ReceiveTransportAck(a)); - } else if(reader.hasTransportUpdate()) { - TransportUpdate u = reader.readTransportUpdate(); - LOG.info("Received transport update"); - dbExecutor.execute(new ReceiveTransportUpdate(u)); - } else { - throw new FormatException(); - } - } - LOG.info("Finished reading"); - writerTasks.add(CLOSE); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - writerTasks.add(DIE); - } - } - - void write() { - connRegistry.registerConnection(contactId, transportId); - eventBus.addListener(this); - try { - OutputStream out = createStreamWriter().getOutputStream(); - writer = packetWriterFactory.createPacketWriter(out, true); - LOG.info("Starting to write"); - // Ensure the tag is sent - out.flush(); - // Send the initial packets - dbExecutor.execute(new GenerateTransportAcks()); - dbExecutor.execute(new GenerateTransportUpdates()); - dbExecutor.execute(new GenerateSubscriptionAck()); - dbExecutor.execute(new GenerateSubscriptionUpdate()); - dbExecutor.execute(new GenerateRetentionAck()); - dbExecutor.execute(new GenerateRetentionUpdate()); - dbExecutor.execute(new GenerateAck()); - dbExecutor.execute(new GenerateBatch()); - dbExecutor.execute(new GenerateOffer()); - dbExecutor.execute(new GenerateRequest()); - // Main loop - Runnable task = null; - while(true) { - LOG.info("Waiting for something to write"); - task = writerTasks.take(); - if(task == CLOSE || task == DIE) break; - task.run(); - } - LOG.info("Finished writing"); - if(task == CLOSE) { - writer.flush(); - writer.close(); - dispose(false, true); - } else { - dispose(true, true); - } - } catch(InterruptedException e) { - LOG.warning("Interrupted while waiting for task"); - Thread.currentThread().interrupt(); - dispose(true, true); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true, true); - } - eventBus.removeListener(this); - connRegistry.unregisterConnection(contactId, transportId); - } - - private void dispose(boolean exception, boolean recognised) { - if(disposed.getAndSet(true)) return; - if(LOG.isLoggable(INFO)) - LOG.info("Disposing: " + exception + ", " + recognised); - ByteUtils.erase(ctx.getSecret()); - try { - transport.dispose(exception, recognised); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - - // This task runs on the database thread - private class ReceiveAck implements Runnable { - - private final Ack ack; - - private ReceiveAck(Ack ack) { - this.ack = ack; - } - - public void run() { - try { - db.receiveAck(contactId, ack); - LOG.info("DB received ack"); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on a crypto thread - private class VerifyMessage implements Runnable { - - private final UnverifiedMessage message; - - private VerifyMessage(UnverifiedMessage message) { - this.message = message; - } - - public void run() { - try { - Message m = messageVerifier.verifyMessage(message); - LOG.info("Verified message"); - dbExecutor.execute(new ReceiveMessage(m)); - } catch(GeneralSecurityException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the database thread - private class ReceiveMessage implements Runnable { - - private final Message message; - - private ReceiveMessage(Message message) { - this.message = message; - } - - public void run() { - try { - db.receiveMessage(contactId, message); - LOG.info("DB received message"); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the database thread - private class ReceiveOffer implements Runnable { - - private final Offer offer; - - private ReceiveOffer(Offer offer) { - this.offer = offer; - } - - public void run() { - try { - db.receiveOffer(contactId, offer); - LOG.info("DB received offer"); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the database thread - private class ReceiveRequest implements Runnable { - - private final Request request; - - private ReceiveRequest(Request request) { - this.request = request; - } - - public void run() { - try { - db.receiveRequest(contactId, request); - LOG.info("DB received request"); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the database thread - private class ReceiveRetentionAck implements Runnable { - - private final RetentionAck ack; - - private ReceiveRetentionAck(RetentionAck ack) { - this.ack = ack; - } - - public void run() { - try { - db.receiveRetentionAck(contactId, ack); - LOG.info("DB received retention ack"); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the database thread - private class ReceiveRetentionUpdate implements Runnable { - - private final RetentionUpdate update; - - private ReceiveRetentionUpdate(RetentionUpdate update) { - this.update = update; - } - - public void run() { - try { - db.receiveRetentionUpdate(contactId, update); - LOG.info("DB received retention update"); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the database thread - private class ReceiveSubscriptionAck implements Runnable { - - private final SubscriptionAck ack; - - private ReceiveSubscriptionAck(SubscriptionAck ack) { - this.ack = ack; - } - - public void run() { - try { - db.receiveSubscriptionAck(contactId, ack); - LOG.info("DB received subscription ack"); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the database thread - private class ReceiveSubscriptionUpdate implements Runnable { - - private final SubscriptionUpdate update; - - private ReceiveSubscriptionUpdate(SubscriptionUpdate update) { - this.update = update; - } - - public void run() { - try { - db.receiveSubscriptionUpdate(contactId, update); - LOG.info("DB received subscription update"); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the database thread - private class ReceiveTransportAck implements Runnable { - - private final TransportAck ack; - - private ReceiveTransportAck(TransportAck ack) { - this.ack = ack; - } - - public void run() { - try { - db.receiveTransportAck(contactId, ack); - LOG.info("DB received transport ack"); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the database thread - private class ReceiveTransportUpdate implements Runnable { - - private final TransportUpdate update; - - private ReceiveTransportUpdate(TransportUpdate update) { - this.update = update; - } - - public void run() { - try { - db.receiveTransportUpdate(contactId, update); - LOG.info("DB received transport update"); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the database thread - private class GenerateAck implements Runnable { - - public void run() { - assert writer != null; - int maxMessages = writer.getMaxMessagesForAck(Long.MAX_VALUE); - try { - Ack a = db.generateAck(contactId, maxMessages); - if(LOG.isLoggable(INFO)) - LOG.info("Generated ack: " + (a != null)); - if(a != null) writerTasks.add(new WriteAck(a)); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the writer thread - private class WriteAck implements Runnable { - - private final Ack ack; - - private WriteAck(Ack ack) { - this.ack = ack; - } - - public void run() { - assert writer != null; - try { - writer.writeAck(ack); - LOG.info("Sent ack"); - dbExecutor.execute(new GenerateAck()); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true, true); - } - } - } - - // This task runs on the database thread - private class GenerateBatch implements Runnable { - - public void run() { - assert writer != null; - try { - Collection<byte[]> b = db.generateRequestedBatch(contactId, - MAX_PACKET_LENGTH, maxLatency); - if(LOG.isLoggable(INFO)) - LOG.info("Generated batch: " + (b != null)); - if(b != null) writerTasks.add(new WriteBatch(b)); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the writer thread - private class WriteBatch implements Runnable { - - private final Collection<byte[]> batch; - - private WriteBatch(Collection<byte[]> batch) { - this.batch = batch; - } - - public void run() { - assert writer != null; - try { - for(byte[] raw : batch) writer.writeMessage(raw); - LOG.info("Sent batch"); - dbExecutor.execute(new GenerateBatch()); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true, true); - } - } - } - - // This task runs on the database thread - private class GenerateOffer implements Runnable { - - public void run() { - assert writer != null; - int maxMessages = writer.getMaxMessagesForOffer(Long.MAX_VALUE); - try { - Offer o = db.generateOffer(contactId, maxMessages, maxLatency); - if(LOG.isLoggable(INFO)) - LOG.info("Generated offer: " + (o != null)); - if(o != null) writerTasks.add(new WriteOffer(o)); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the writer thread - private class WriteOffer implements Runnable { - - private final Offer offer; - - private WriteOffer(Offer offer) { - this.offer = offer; - } - - public void run() { - assert writer != null; - try { - writer.writeOffer(offer); - LOG.info("Sent offer"); - dbExecutor.execute(new GenerateOffer()); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true, true); - } - } - } - - // This task runs on the database thread - private class GenerateRequest implements Runnable { - - public void run() { - assert writer != null; - int maxMessages = writer.getMaxMessagesForRequest(Long.MAX_VALUE); - try { - Request r = db.generateRequest(contactId, maxMessages); - if(LOG.isLoggable(INFO)) - LOG.info("Generated request: " + (r != null)); - if(r != null) writerTasks.add(new WriteRequest(r)); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the writer thread - private class WriteRequest implements Runnable { - - private final Request request; - - private WriteRequest(Request request) { - this.request = request; - } - - public void run() { - assert writer != null; - try { - writer.writeRequest(request); - LOG.info("Sent request"); - dbExecutor.execute(new GenerateRequest()); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true, true); - } - } - } - - // This task runs on the database thread - private class GenerateRetentionAck implements Runnable { - - public void run() { - try { - RetentionAck a = db.generateRetentionAck(contactId); - if(LOG.isLoggable(INFO)) - LOG.info("Generated retention ack: " + (a != null)); - if(a != null) writerTasks.add(new WriteRetentionAck(a)); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This tasks runs on the writer thread - private class WriteRetentionAck implements Runnable { - - private final RetentionAck ack; - - private WriteRetentionAck(RetentionAck ack) { - this.ack = ack; - } - - public void run() { - assert writer != null; - try { - writer.writeRetentionAck(ack); - LOG.info("Sent retention ack"); - dbExecutor.execute(new GenerateRetentionAck()); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true, true); - } - } - } - - // This task runs on the database thread - private class GenerateRetentionUpdate implements Runnable { - - public void run() { - try { - RetentionUpdate u = - db.generateRetentionUpdate(contactId, maxLatency); - if(LOG.isLoggable(INFO)) - LOG.info("Generated retention update: " + (u != null)); - if(u != null) writerTasks.add(new WriteRetentionUpdate(u)); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the writer thread - private class WriteRetentionUpdate implements Runnable { - - private final RetentionUpdate update; - - private WriteRetentionUpdate(RetentionUpdate update) { - this.update = update; - } - - public void run() { - assert writer != null; - try { - writer.writeRetentionUpdate(update); - LOG.info("Sent retention update"); - dbExecutor.execute(new GenerateRetentionUpdate()); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true, true); - } - } - } - - // This task runs on the database thread - private class GenerateSubscriptionAck implements Runnable { - - public void run() { - try { - SubscriptionAck a = db.generateSubscriptionAck(contactId); - if(LOG.isLoggable(INFO)) - LOG.info("Generated subscription ack: " + (a != null)); - if(a != null) writerTasks.add(new WriteSubscriptionAck(a)); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This tasks runs on the writer thread - private class WriteSubscriptionAck implements Runnable { - - private final SubscriptionAck ack; - - private WriteSubscriptionAck(SubscriptionAck ack) { - this.ack = ack; - } - - public void run() { - assert writer != null; - try { - writer.writeSubscriptionAck(ack); - LOG.info("Sent subscription ack"); - dbExecutor.execute(new GenerateSubscriptionAck()); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true, true); - } - } - } - - // This task runs on the database thread - private class GenerateSubscriptionUpdate implements Runnable { - - public void run() { - try { - SubscriptionUpdate u = - db.generateSubscriptionUpdate(contactId, maxLatency); - if(LOG.isLoggable(INFO)) - LOG.info("Generated subscription update: " + (u != null)); - if(u != null) writerTasks.add(new WriteSubscriptionUpdate(u)); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the writer thread - private class WriteSubscriptionUpdate implements Runnable { - - private final SubscriptionUpdate update; - - private WriteSubscriptionUpdate(SubscriptionUpdate update) { - this.update = update; - } - - public void run() { - assert writer != null; - try { - writer.writeSubscriptionUpdate(update); - LOG.info("Sent subscription update"); - dbExecutor.execute(new GenerateSubscriptionUpdate()); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true, true); - } - } - } - - // This task runs on the database thread - private class GenerateTransportAcks implements Runnable { - - public void run() { - try { - Collection<TransportAck> acks = - db.generateTransportAcks(contactId); - if(LOG.isLoggable(INFO)) - LOG.info("Generated transport acks: " + (acks != null)); - if(acks != null) writerTasks.add(new WriteTransportAcks(acks)); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This tasks runs on the writer thread - private class WriteTransportAcks implements Runnable { - - private final Collection<TransportAck> acks; - - private WriteTransportAcks(Collection<TransportAck> acks) { - this.acks = acks; - } - - public void run() { - assert writer != null; - try { - for(TransportAck a : acks) writer.writeTransportAck(a); - LOG.info("Sent transport acks"); - dbExecutor.execute(new GenerateTransportAcks()); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true, true); - } - } - } - - // This task runs on the database thread - private class GenerateTransportUpdates implements Runnable { - - public void run() { - try { - Collection<TransportUpdate> t = - db.generateTransportUpdates(contactId, maxLatency); - if(LOG.isLoggable(INFO)) - LOG.info("Generated transport updates: " + (t != null)); - if(t != null) writerTasks.add(new WriteTransportUpdates(t)); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } - } - - // This task runs on the writer thread - private class WriteTransportUpdates implements Runnable { - - private final Collection<TransportUpdate> updates; - - private WriteTransportUpdates(Collection<TransportUpdate> updates) { - this.updates = updates; - } - - public void run() { - assert writer != null; - try { - for(TransportUpdate u : updates) writer.writeTransportUpdate(u); - LOG.info("Sent transport updates"); - dbExecutor.execute(new GenerateTransportUpdates()); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true, true); - } - } - } -} diff --git a/briar-core/src/org/briarproject/messaging/duplex/DuplexConnectionFactoryImpl.java b/briar-core/src/org/briarproject/messaging/duplex/DuplexConnectionFactoryImpl.java deleted file mode 100644 index f8affa8dde6ddb479ac883c32e65488dc3797952..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/messaging/duplex/DuplexConnectionFactoryImpl.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.briarproject.messaging.duplex; - -import java.util.concurrent.Executor; -import java.util.logging.Logger; - -import javax.inject.Inject; - -import org.briarproject.api.ContactId; -import org.briarproject.api.TransportId; -import org.briarproject.api.crypto.CryptoExecutor; -import org.briarproject.api.crypto.KeyManager; -import org.briarproject.api.db.DatabaseComponent; -import org.briarproject.api.db.DatabaseExecutor; -import org.briarproject.api.event.EventBus; -import org.briarproject.api.messaging.MessageVerifier; -import org.briarproject.api.messaging.PacketReaderFactory; -import org.briarproject.api.messaging.PacketWriterFactory; -import org.briarproject.api.messaging.duplex.DuplexConnectionFactory; -import org.briarproject.api.plugins.duplex.DuplexTransportConnection; -import org.briarproject.api.transport.ConnectionRegistry; -import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.api.transport.StreamWriterFactory; - -class DuplexConnectionFactoryImpl implements DuplexConnectionFactory { - - private static final Logger LOG = - Logger.getLogger(DuplexConnectionFactoryImpl.class.getName()); - - private final Executor dbExecutor, cryptoExecutor; - private final MessageVerifier messageVerifier; - private final DatabaseComponent db; - private final EventBus eventBus; - private final KeyManager keyManager; - private final ConnectionRegistry connRegistry; - private final StreamReaderFactory connReaderFactory; - private final StreamWriterFactory connWriterFactory; - private final PacketReaderFactory packetReaderFactory; - private final PacketWriterFactory packetWriterFactory; - - @Inject - DuplexConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor, - @CryptoExecutor Executor cryptoExecutor, - MessageVerifier messageVerifier, DatabaseComponent db, - EventBus eventBus, KeyManager keyManager, - ConnectionRegistry connRegistry, - StreamReaderFactory connReaderFactory, - StreamWriterFactory connWriterFactory, - PacketReaderFactory packetReaderFactory, - PacketWriterFactory packetWriterFactory) { - this.dbExecutor = dbExecutor; - this.cryptoExecutor = cryptoExecutor; - this.messageVerifier = messageVerifier; - this.db = db; - this.eventBus = eventBus; - this.keyManager = keyManager; - this.connRegistry = connRegistry; - this.connReaderFactory = connReaderFactory; - this.connWriterFactory = connWriterFactory; - this.packetReaderFactory = packetReaderFactory; - this.packetWriterFactory = packetWriterFactory; - } - - public void createIncomingConnection(StreamContext ctx, - DuplexTransportConnection transport) { - final DuplexConnection conn = new IncomingDuplexConnection(dbExecutor, - cryptoExecutor, messageVerifier, db, eventBus, connRegistry, - connReaderFactory, connWriterFactory, packetReaderFactory, - packetWriterFactory, ctx, transport); - Runnable write = new Runnable() { - public void run() { - conn.write(); - } - }; - new Thread(write, "DuplexConnectionWriter").start(); - Runnable read = new Runnable() { - public void run() { - conn.read(); - } - }; - new Thread(read, "DuplexConnectionReader").start(); - } - - public void createOutgoingConnection(ContactId c, TransportId t, - DuplexTransportConnection transport) { - StreamContext ctx = keyManager.getStreamContext(c, t); - if(ctx == null) { - LOG.warning("Could not create outgoing stream context"); - return; - } - final DuplexConnection conn = new OutgoingDuplexConnection(dbExecutor, - cryptoExecutor, messageVerifier, db, eventBus, connRegistry, - connReaderFactory, connWriterFactory, packetReaderFactory, - packetWriterFactory, ctx, transport); - Runnable write = new Runnable() { - public void run() { - conn.write(); - } - }; - new Thread(write, "DuplexConnectionWriter").start(); - Runnable read = new Runnable() { - public void run() { - conn.read(); - } - }; - new Thread(read, "DuplexConnectionReader").start(); - } -} diff --git a/briar-core/src/org/briarproject/messaging/duplex/DuplexMessagingModule.java b/briar-core/src/org/briarproject/messaging/duplex/DuplexMessagingModule.java deleted file mode 100644 index 7f3996f346a5c6cfbcf6a3a1c41a9c410eb81aed..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/messaging/duplex/DuplexMessagingModule.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.briarproject.messaging.duplex; - -import javax.inject.Singleton; - -import org.briarproject.api.messaging.duplex.DuplexConnectionFactory; - -import com.google.inject.AbstractModule; - -public class DuplexMessagingModule extends AbstractModule { - - protected void configure() { - bind(DuplexConnectionFactory.class).to( - DuplexConnectionFactoryImpl.class).in(Singleton.class); - } -} diff --git a/briar-core/src/org/briarproject/messaging/duplex/IncomingDuplexConnection.java b/briar-core/src/org/briarproject/messaging/duplex/IncomingDuplexConnection.java deleted file mode 100644 index 52e4e931810f29948de4e5eb495660b75e28c6c4..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/messaging/duplex/IncomingDuplexConnection.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.briarproject.messaging.duplex; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.concurrent.Executor; - -import org.briarproject.api.db.DatabaseComponent; -import org.briarproject.api.event.EventBus; -import org.briarproject.api.messaging.MessageVerifier; -import org.briarproject.api.messaging.PacketReaderFactory; -import org.briarproject.api.messaging.PacketWriterFactory; -import org.briarproject.api.plugins.duplex.DuplexTransportConnection; -import org.briarproject.api.transport.ConnectionRegistry; -import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamReader; -import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.api.transport.StreamWriter; -import org.briarproject.api.transport.StreamWriterFactory; - -class IncomingDuplexConnection extends DuplexConnection { - - IncomingDuplexConnection(Executor dbExecutor, Executor cryptoExecutor, - MessageVerifier messageVerifier, DatabaseComponent db, - EventBus eventBus, ConnectionRegistry connRegistry, - StreamReaderFactory connReaderFactory, - StreamWriterFactory connWriterFactory, - PacketReaderFactory packetReaderFactory, - PacketWriterFactory packetWriterFactory, - StreamContext ctx, DuplexTransportConnection transport) { - super(dbExecutor, cryptoExecutor, messageVerifier, db, eventBus, - connRegistry, connReaderFactory, connWriterFactory, - packetReaderFactory, packetWriterFactory, ctx, transport); - } - - @Override - protected StreamReader createStreamReader() throws IOException { - InputStream in = transport.getInputStream(); - int maxFrameLength = transport.getMaxFrameLength(); - return connReaderFactory.createStreamReader(in, maxFrameLength, - ctx, true, true); - } - - @Override - protected StreamWriter createStreamWriter() throws IOException { - OutputStream out = transport.getOutputStream(); - int maxFrameLength = transport.getMaxFrameLength(); - return connWriterFactory.createStreamWriter(out, maxFrameLength, - Long.MAX_VALUE, ctx, true, false); - } -} diff --git a/briar-core/src/org/briarproject/messaging/duplex/OutgoingDuplexConnection.java b/briar-core/src/org/briarproject/messaging/duplex/OutgoingDuplexConnection.java deleted file mode 100644 index 7eab0226d9e8cabeccc18752f156f72190ff9a97..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/messaging/duplex/OutgoingDuplexConnection.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.briarproject.messaging.duplex; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.concurrent.Executor; - -import org.briarproject.api.db.DatabaseComponent; -import org.briarproject.api.event.EventBus; -import org.briarproject.api.messaging.MessageVerifier; -import org.briarproject.api.messaging.PacketReaderFactory; -import org.briarproject.api.messaging.PacketWriterFactory; -import org.briarproject.api.plugins.duplex.DuplexTransportConnection; -import org.briarproject.api.transport.ConnectionRegistry; -import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamReader; -import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.api.transport.StreamWriter; -import org.briarproject.api.transport.StreamWriterFactory; - -class OutgoingDuplexConnection extends DuplexConnection { - - OutgoingDuplexConnection(Executor dbExecutor, Executor cryptoExecutor, - MessageVerifier messageVerifier, DatabaseComponent db, - EventBus eventBus, ConnectionRegistry connRegistry, - StreamReaderFactory connReaderFactory, - StreamWriterFactory connWriterFactory, - PacketReaderFactory packetReaderFactory, - PacketWriterFactory packetWriterFactory, StreamContext ctx, - DuplexTransportConnection transport) { - super(dbExecutor, cryptoExecutor, messageVerifier, db, eventBus, - connRegistry, connReaderFactory, connWriterFactory, - packetReaderFactory, packetWriterFactory, ctx, transport); - } - - @Override - protected StreamReader createStreamReader() throws IOException { - InputStream in = transport.getInputStream(); - int maxFrameLength = transport.getMaxFrameLength(); - return connReaderFactory.createStreamReader(in, maxFrameLength, - ctx, false, false); - } - - @Override - protected StreamWriter createStreamWriter() throws IOException { - OutputStream out = transport.getOutputStream(); - int maxFrameLength = transport.getMaxFrameLength(); - return connWriterFactory.createStreamWriter(out, maxFrameLength, - Long.MAX_VALUE, ctx, false, true); - } -} diff --git a/briar-core/src/org/briarproject/messaging/simplex/OutgoingSimplexConnection.java b/briar-core/src/org/briarproject/messaging/simplex/OutgoingSimplexConnection.java deleted file mode 100644 index 94fd128ff7aab12e6409709a87d77b0ef8e7f5ed..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/messaging/simplex/OutgoingSimplexConnection.java +++ /dev/null @@ -1,187 +0,0 @@ -package org.briarproject.messaging.simplex; - -import static java.util.logging.Level.WARNING; -import static org.briarproject.api.messaging.MessagingConstants.MAX_PACKET_LENGTH; - -import java.io.EOFException; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Collection; -import java.util.logging.Logger; - -import org.briarproject.api.ContactId; -import org.briarproject.api.TransportId; -import org.briarproject.api.db.DatabaseComponent; -import org.briarproject.api.db.DbException; -import org.briarproject.api.messaging.Ack; -import org.briarproject.api.messaging.PacketWriter; -import org.briarproject.api.messaging.PacketWriterFactory; -import org.briarproject.api.messaging.RetentionAck; -import org.briarproject.api.messaging.RetentionUpdate; -import org.briarproject.api.messaging.SubscriptionAck; -import org.briarproject.api.messaging.SubscriptionUpdate; -import org.briarproject.api.messaging.TransportAck; -import org.briarproject.api.messaging.TransportUpdate; -import org.briarproject.api.plugins.simplex.SimplexTransportWriter; -import org.briarproject.api.transport.ConnectionRegistry; -import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamWriter; -import org.briarproject.api.transport.StreamWriterFactory; -import org.briarproject.util.ByteUtils; - -class OutgoingSimplexConnection { - - private static final Logger LOG = - Logger.getLogger(OutgoingSimplexConnection.class.getName()); - - private final DatabaseComponent db; - private final ConnectionRegistry connRegistry; - private final StreamWriterFactory connWriterFactory; - private final PacketWriterFactory packetWriterFactory; - private final StreamContext ctx; - private final SimplexTransportWriter transport; - private final ContactId contactId; - private final TransportId transportId; - private final long maxLatency; - - OutgoingSimplexConnection(DatabaseComponent db, - ConnectionRegistry connRegistry, - StreamWriterFactory connWriterFactory, - PacketWriterFactory packetWriterFactory, StreamContext ctx, - SimplexTransportWriter transport) { - this.db = db; - this.connRegistry = connRegistry; - this.connWriterFactory = connWriterFactory; - this.packetWriterFactory = packetWriterFactory; - this.ctx = ctx; - this.transport = transport; - contactId = ctx.getContactId(); - transportId = ctx.getTransportId(); - maxLatency = transport.getMaxLatency(); - } - - void write() { - connRegistry.registerConnection(contactId, transportId); - try { - OutputStream out = transport.getOutputStream(); - long capacity = transport.getCapacity(); - int maxFrameLength = transport.getMaxFrameLength(); - StreamWriter conn = connWriterFactory.createStreamWriter( - out, maxFrameLength, capacity, ctx, false, true); - out = conn.getOutputStream(); - if(conn.getRemainingCapacity() < MAX_PACKET_LENGTH) - throw new EOFException(); - PacketWriter writer = packetWriterFactory.createPacketWriter(out, - false); - // Send the initial packets: updates and acks - boolean hasSpace = writeTransportAcks(conn, writer); - if(hasSpace) hasSpace = writeTransportUpdates(conn, writer); - if(hasSpace) hasSpace = writeSubscriptionAck(conn, writer); - if(hasSpace) hasSpace = writeSubscriptionUpdate(conn, writer); - if(hasSpace) hasSpace = writeRetentionAck(conn, writer); - if(hasSpace) hasSpace = writeRetentionUpdate(conn, writer); - // Write acks until you can't write acks no more - capacity = conn.getRemainingCapacity(); - int maxMessages = writer.getMaxMessagesForAck(capacity); - Ack a = db.generateAck(contactId, maxMessages); - while(a != null) { - writer.writeAck(a); - capacity = conn.getRemainingCapacity(); - maxMessages = writer.getMaxMessagesForAck(capacity); - a = db.generateAck(contactId, maxMessages); - } - // Write messages until you can't write messages no more - capacity = conn.getRemainingCapacity(); - int maxLength = (int) Math.min(capacity, MAX_PACKET_LENGTH); - Collection<byte[]> batch = db.generateBatch(contactId, maxLength, - maxLatency); - while(batch != null) { - for(byte[] raw : batch) writer.writeMessage(raw); - capacity = conn.getRemainingCapacity(); - maxLength = (int) Math.min(capacity, MAX_PACKET_LENGTH); - batch = db.generateBatch(contactId, maxLength, maxLatency); - } - writer.flush(); - writer.close(); - dispose(false); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true); - } - connRegistry.unregisterConnection(contactId, transportId); - } - - private boolean writeTransportAcks(StreamWriter conn, - PacketWriter writer) throws DbException, IOException { - assert conn.getRemainingCapacity() >= MAX_PACKET_LENGTH; - Collection<TransportAck> acks = db.generateTransportAcks(contactId); - if(acks == null) return true; - for(TransportAck a : acks) { - writer.writeTransportAck(a); - if(conn.getRemainingCapacity() < MAX_PACKET_LENGTH) return false; - } - return true; - } - - private boolean writeTransportUpdates(StreamWriter conn, - PacketWriter writer) throws DbException, IOException { - assert conn.getRemainingCapacity() >= MAX_PACKET_LENGTH; - Collection<TransportUpdate> updates = - db.generateTransportUpdates(contactId, maxLatency); - if(updates == null) return true; - for(TransportUpdate u : updates) { - writer.writeTransportUpdate(u); - if(conn.getRemainingCapacity() < MAX_PACKET_LENGTH) return false; - } - return true; - } - - private boolean writeSubscriptionAck(StreamWriter conn, - PacketWriter writer) throws DbException, IOException { - assert conn.getRemainingCapacity() >= MAX_PACKET_LENGTH; - SubscriptionAck a = db.generateSubscriptionAck(contactId); - if(a == null) return true; - writer.writeSubscriptionAck(a); - return conn.getRemainingCapacity() >= MAX_PACKET_LENGTH; - } - - private boolean writeSubscriptionUpdate(StreamWriter conn, - PacketWriter writer) throws DbException, IOException { - assert conn.getRemainingCapacity() >= MAX_PACKET_LENGTH; - SubscriptionUpdate u = - db.generateSubscriptionUpdate(contactId, maxLatency); - if(u == null) return true; - writer.writeSubscriptionUpdate(u); - return conn.getRemainingCapacity() >= MAX_PACKET_LENGTH; - } - - private boolean writeRetentionAck(StreamWriter conn, - PacketWriter writer) throws DbException, IOException { - assert conn.getRemainingCapacity() >= MAX_PACKET_LENGTH; - RetentionAck a = db.generateRetentionAck(contactId); - if(a == null) return true; - writer.writeRetentionAck(a); - return conn.getRemainingCapacity() >= MAX_PACKET_LENGTH; - } - - private boolean writeRetentionUpdate(StreamWriter conn, - PacketWriter writer) throws DbException, IOException { - assert conn.getRemainingCapacity() >= MAX_PACKET_LENGTH; - RetentionUpdate u = db.generateRetentionUpdate(contactId, maxLatency); - if(u == null) return true; - writer.writeRetentionUpdate(u); - return conn.getRemainingCapacity() >= MAX_PACKET_LENGTH; - } - - private void dispose(boolean exception) { - ByteUtils.erase(ctx.getSecret()); - try { - transport.dispose(exception); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } - } -} diff --git a/briar-core/src/org/briarproject/messaging/simplex/SimplexConnectionFactoryImpl.java b/briar-core/src/org/briarproject/messaging/simplex/SimplexConnectionFactoryImpl.java deleted file mode 100644 index f202d061eba2b9715943a3cb8c6fd251617fe49b..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/messaging/simplex/SimplexConnectionFactoryImpl.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.briarproject.messaging.simplex; - -import java.util.concurrent.Executor; -import java.util.logging.Logger; - -import javax.inject.Inject; - -import org.briarproject.api.ContactId; -import org.briarproject.api.TransportId; -import org.briarproject.api.crypto.CryptoExecutor; -import org.briarproject.api.crypto.KeyManager; -import org.briarproject.api.db.DatabaseComponent; -import org.briarproject.api.db.DatabaseExecutor; -import org.briarproject.api.messaging.MessageVerifier; -import org.briarproject.api.messaging.PacketReaderFactory; -import org.briarproject.api.messaging.PacketWriterFactory; -import org.briarproject.api.messaging.simplex.SimplexConnectionFactory; -import org.briarproject.api.plugins.simplex.SimplexTransportReader; -import org.briarproject.api.plugins.simplex.SimplexTransportWriter; -import org.briarproject.api.transport.ConnectionRegistry; -import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.api.transport.StreamWriterFactory; - -class SimplexConnectionFactoryImpl implements SimplexConnectionFactory { - - private static final Logger LOG = - Logger.getLogger(SimplexConnectionFactoryImpl.class.getName()); - - private final Executor dbExecutor, cryptoExecutor; - private final MessageVerifier messageVerifier; - private final DatabaseComponent db; - private final KeyManager keyManager; - private final ConnectionRegistry connRegistry; - private final StreamReaderFactory connReaderFactory; - private final StreamWriterFactory connWriterFactory; - private final PacketReaderFactory packetReaderFactory; - private final PacketWriterFactory packetWriterFactory; - - @Inject - SimplexConnectionFactoryImpl(@DatabaseExecutor Executor dbExecutor, - @CryptoExecutor Executor cryptoExecutor, - MessageVerifier messageVerifier, DatabaseComponent db, - KeyManager keyManager, ConnectionRegistry connRegistry, - StreamReaderFactory connReaderFactory, - StreamWriterFactory connWriterFactory, - PacketReaderFactory packetReaderFactory, - PacketWriterFactory packetWriterFactory) { - this.dbExecutor = dbExecutor; - this.cryptoExecutor = cryptoExecutor; - this.messageVerifier = messageVerifier; - this.db = db; - this.keyManager = keyManager; - this.connRegistry = connRegistry; - this.connReaderFactory = connReaderFactory; - this.connWriterFactory = connWriterFactory; - this.packetReaderFactory = packetReaderFactory; - this.packetWriterFactory = packetWriterFactory; - } - - public void createIncomingConnection(StreamContext ctx, - SimplexTransportReader r) { - final IncomingSimplexConnection conn = new IncomingSimplexConnection( - dbExecutor, cryptoExecutor, messageVerifier, db, connRegistry, - connReaderFactory, packetReaderFactory, ctx, r); - Runnable read = new Runnable() { - public void run() { - conn.read(); - } - }; - new Thread(read, "SimplexConnectionReader").start(); - } - - public void createOutgoingConnection(ContactId c, TransportId t, - SimplexTransportWriter w) { - StreamContext ctx = keyManager.getStreamContext(c, t); - if(ctx == null) { - LOG.warning("Could not create outgoing connection context"); - return; - } - final OutgoingSimplexConnection conn = new OutgoingSimplexConnection(db, - connRegistry, connWriterFactory, packetWriterFactory, ctx, w); - Runnable write = new Runnable() { - public void run() { - conn.write(); - } - }; - new Thread(write, "SimplexConnectionWriter").start(); - } -} diff --git a/briar-core/src/org/briarproject/messaging/simplex/SimplexMessagingModule.java b/briar-core/src/org/briarproject/messaging/simplex/SimplexMessagingModule.java deleted file mode 100644 index 3bf9ffe85ef8441dca6e1456b8cea6571081bdaa..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/messaging/simplex/SimplexMessagingModule.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.briarproject.messaging.simplex; - -import javax.inject.Singleton; - -import org.briarproject.api.messaging.simplex.SimplexConnectionFactory; - -import com.google.inject.AbstractModule; - -public class SimplexMessagingModule extends AbstractModule { - - protected void configure() { - bind(SimplexConnectionFactory.class).to( - SimplexConnectionFactoryImpl.class).in(Singleton.class); - } -} diff --git a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java index 1e14f9c72c0cbd00ecf6c283ee3af63df753d33c..745d5df05348ff8bac1827366b3f7e5141de5bf7 100644 --- a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java +++ b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java @@ -27,6 +27,8 @@ import org.briarproject.api.lifecycle.IoExecutor; import org.briarproject.api.plugins.Plugin; import org.briarproject.api.plugins.PluginCallback; import org.briarproject.api.plugins.PluginManager; +import org.briarproject.api.plugins.TransportConnectionReader; +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.DuplexPluginConfig; @@ -36,8 +38,6 @@ import org.briarproject.api.plugins.simplex.SimplexPlugin; import org.briarproject.api.plugins.simplex.SimplexPluginCallback; import org.briarproject.api.plugins.simplex.SimplexPluginConfig; import org.briarproject.api.plugins.simplex.SimplexPluginFactory; -import org.briarproject.api.plugins.simplex.SimplexTransportReader; -import org.briarproject.api.plugins.simplex.SimplexTransportWriter; import org.briarproject.api.system.Clock; import org.briarproject.api.transport.ConnectionDispatcher; import org.briarproject.api.ui.UiCallback; @@ -377,11 +377,11 @@ class PluginManagerImpl implements PluginManager { super(id); } - public void readerCreated(SimplexTransportReader r) { + public void readerCreated(TransportConnectionReader r) { dispatcher.dispatchIncomingConnection(id, r); } - public void writerCreated(ContactId c, SimplexTransportWriter w) { + public void writerCreated(ContactId c, TransportConnectionWriter w) { dispatcher.dispatchOutgoingConnection(c, id, w); } } diff --git a/briar-core/src/org/briarproject/plugins/PollerImpl.java b/briar-core/src/org/briarproject/plugins/PollerImpl.java index a92cdf4cd85c57ede04f0b9ed3dd0c998382d2fb..b560bfdd51bb7d0de4823b1bc78a83f06053fb5e 100644 --- a/briar-core/src/org/briarproject/plugins/PollerImpl.java +++ b/briar-core/src/org/briarproject/plugins/PollerImpl.java @@ -20,14 +20,14 @@ class PollerImpl implements Poller { Logger.getLogger(PollerImpl.class.getName()); private final Executor ioExecutor; - private final ConnectionRegistry connRegistry; + private final ConnectionRegistry connectionRegistry; private final Timer timer; @Inject - PollerImpl(@IoExecutor Executor ioExecutor, ConnectionRegistry connRegistry, - Timer timer) { + PollerImpl(@IoExecutor Executor ioExecutor, + ConnectionRegistry connectionRegistry, Timer timer) { this.ioExecutor = ioExecutor; - this.connRegistry = connRegistry; + this.connectionRegistry = connectionRegistry; this.timer = timer; } @@ -53,7 +53,7 @@ class PollerImpl implements Poller { public void run() { if(LOG.isLoggable(INFO)) LOG.info("Polling " + p.getClass().getSimpleName()); - p.poll(connRegistry.getConnectedContacts(p.getId())); + p.poll(connectionRegistry.getConnectedContacts(p.getId())); } }); } diff --git a/briar-core/src/org/briarproject/plugins/file/FilePlugin.java b/briar-core/src/org/briarproject/plugins/file/FilePlugin.java index 03020373660ff631dd65106b376e82428b6d2be6..1af4215ba6afb3fdf547cebc033cb07dde57e157 100644 --- a/briar-core/src/org/briarproject/plugins/file/FilePlugin.java +++ b/briar-core/src/org/briarproject/plugins/file/FilePlugin.java @@ -14,10 +14,10 @@ import java.util.concurrent.Executor; import java.util.logging.Logger; import org.briarproject.api.ContactId; +import org.briarproject.api.plugins.TransportConnectionReader; +import org.briarproject.api.plugins.TransportConnectionWriter; import org.briarproject.api.plugins.simplex.SimplexPlugin; import org.briarproject.api.plugins.simplex.SimplexPluginCallback; -import org.briarproject.api.plugins.simplex.SimplexTransportReader; -import org.briarproject.api.plugins.simplex.SimplexTransportWriter; import org.briarproject.api.system.FileUtils; public abstract class FilePlugin implements SimplexPlugin { @@ -60,11 +60,11 @@ public abstract class FilePlugin implements SimplexPlugin { return running; } - public SimplexTransportReader createReader(ContactId c) { + public TransportConnectionReader createReader(ContactId c) { return null; } - public SimplexTransportWriter createWriter(ContactId c) { + public TransportConnectionWriter createWriter(ContactId c) { if(!running) return null; return createWriter(createConnectionFilename()); } @@ -81,7 +81,7 @@ public abstract class FilePlugin implements SimplexPlugin { return filename.toLowerCase(Locale.US).matches("[a-z]{8}\\.dat"); } - private SimplexTransportWriter createWriter(String filename) { + private TransportConnectionWriter createWriter(String filename) { if(!running) return null; File dir = chooseOutputDirectory(); if(dir == null || !dir.exists() || !dir.isDirectory()) return null; diff --git a/briar-core/src/org/briarproject/plugins/file/FileTransportReader.java b/briar-core/src/org/briarproject/plugins/file/FileTransportReader.java index 0a97e56732473129dc43f427f6927b3f83c67e7d..316773ec01270fc0b45282edd62a6b40785985e5 100644 --- a/briar-core/src/org/briarproject/plugins/file/FileTransportReader.java +++ b/briar-core/src/org/briarproject/plugins/file/FileTransportReader.java @@ -7,9 +7,9 @@ import java.io.IOException; import java.io.InputStream; import java.util.logging.Logger; -import org.briarproject.api.plugins.simplex.SimplexTransportReader; +import org.briarproject.api.plugins.TransportConnectionReader; -class FileTransportReader implements SimplexTransportReader { +class FileTransportReader implements TransportConnectionReader { private static final Logger LOG = Logger.getLogger(FileTransportReader.class.getName()); @@ -28,6 +28,10 @@ class FileTransportReader implements SimplexTransportReader { return plugin.getMaxFrameLength(); } + public long getMaxLatency() { + return plugin.getMaxLatency(); + } + public InputStream getInputStream() { return in; } diff --git a/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java b/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java index d56dfb72691387680510a56ab929dd23fe020271..2ca55593fc6cdb63b0789a4e9dac14a233b335d4 100644 --- a/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java +++ b/briar-core/src/org/briarproject/plugins/file/FileTransportWriter.java @@ -7,9 +7,9 @@ import java.io.IOException; import java.io.OutputStream; import java.util.logging.Logger; -import org.briarproject.api.plugins.simplex.SimplexTransportWriter; +import org.briarproject.api.plugins.TransportConnectionWriter; -class FileTransportWriter implements SimplexTransportWriter { +class FileTransportWriter implements TransportConnectionWriter { private static final Logger LOG = Logger.getLogger(FileTransportWriter.class.getName()); @@ -27,10 +27,6 @@ class FileTransportWriter implements SimplexTransportWriter { this.plugin = plugin; } - public long getCapacity() { - return capacity; - } - public int getMaxFrameLength() { return plugin.getMaxFrameLength(); } @@ -39,6 +35,10 @@ class FileTransportWriter implements SimplexTransportWriter { return plugin.getMaxLatency(); } + public long getCapacity() { + return capacity; + } + public OutputStream getOutputStream() { return out; } diff --git a/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java b/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java index 6612f95fe593ce8e6446575eabbf922363a9e048..83506a211682a611ddf4a6918079336e4530eaa4 100644 --- a/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java +++ b/briar-core/src/org/briarproject/plugins/tcp/TcpTransportConnection.java @@ -4,38 +4,80 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; +import java.util.concurrent.atomic.AtomicBoolean; import org.briarproject.api.plugins.Plugin; +import org.briarproject.api.plugins.TransportConnectionReader; +import org.briarproject.api.plugins.TransportConnectionWriter; import org.briarproject.api.plugins.duplex.DuplexTransportConnection; class TcpTransportConnection implements DuplexTransportConnection { private final Plugin plugin; private final Socket socket; + private final Reader reader; + private final Writer writer; + private final AtomicBoolean halfClosed, closed; TcpTransportConnection(Plugin plugin, Socket socket) { this.plugin = plugin; this.socket = socket; + reader = new Reader(); + writer = new Writer(); + halfClosed = new AtomicBoolean(false); + closed = new AtomicBoolean(false); } - public int getMaxFrameLength() { - return plugin.getMaxFrameLength(); + public TransportConnectionReader getReader() { + return reader; } - public long getMaxLatency() { - return plugin.getMaxLatency(); + public TransportConnectionWriter getWriter() { + return writer; } - public InputStream getInputStream() throws IOException { - return socket.getInputStream(); - } + private class Reader implements TransportConnectionReader { + + public int getMaxFrameLength() { + return plugin.getMaxFrameLength(); + } + + public long getMaxLatency() { + return plugin.getMaxLatency(); + } - public OutputStream getOutputStream() throws IOException { - return socket.getOutputStream(); + public InputStream getInputStream() throws IOException { + return socket.getInputStream(); + } + + public void dispose(boolean exception, boolean recognised) + throws IOException { + if(halfClosed.getAndSet(true) || exception) + if(!closed.getAndSet(true)) socket.close(); + } } - public void dispose(boolean exception, boolean recognised) - throws IOException { - socket.close(); + private class Writer implements TransportConnectionWriter { + + public int getMaxFrameLength() { + return plugin.getMaxFrameLength(); + } + + public long getMaxLatency() { + return plugin.getMaxLatency(); + } + + public long getCapacity() { + return Long.MAX_VALUE; + } + + public OutputStream getOutputStream() throws IOException { + return socket.getOutputStream(); + } + + public void dispose(boolean exception) throws IOException { + if(halfClosed.getAndSet(true) || exception) + if(!closed.getAndSet(true)) socket.close(); + } } } diff --git a/briar-core/src/org/briarproject/transport/ConnectionDispatcherImpl.java b/briar-core/src/org/briarproject/transport/ConnectionDispatcherImpl.java index adda06a5602036dd9483de27d06d7ca9fa8061eb..abae50bff7017eca7e6b81494855b4ddc9c2bddf 100644 --- a/briar-core/src/org/briarproject/transport/ConnectionDispatcherImpl.java +++ b/briar-core/src/org/briarproject/transport/ConnectionDispatcherImpl.java @@ -13,14 +13,16 @@ import javax.inject.Inject; import org.briarproject.api.ContactId; import org.briarproject.api.TransportId; +import org.briarproject.api.crypto.KeyManager; import org.briarproject.api.db.DbException; import org.briarproject.api.lifecycle.IoExecutor; -import org.briarproject.api.messaging.duplex.DuplexConnectionFactory; -import org.briarproject.api.messaging.simplex.SimplexConnectionFactory; +import org.briarproject.api.messaging.MessagingSession; +import org.briarproject.api.messaging.MessagingSessionFactory; +import org.briarproject.api.plugins.TransportConnectionReader; +import org.briarproject.api.plugins.TransportConnectionWriter; import org.briarproject.api.plugins.duplex.DuplexTransportConnection; -import org.briarproject.api.plugins.simplex.SimplexTransportReader; -import org.briarproject.api.plugins.simplex.SimplexTransportWriter; import org.briarproject.api.transport.ConnectionDispatcher; +import org.briarproject.api.transport.ConnectionRegistry; import org.briarproject.api.transport.StreamContext; import org.briarproject.api.transport.TagRecogniser; @@ -30,132 +32,280 @@ class ConnectionDispatcherImpl implements ConnectionDispatcher { Logger.getLogger(ConnectionDispatcherImpl.class.getName()); private final Executor ioExecutor; + private final KeyManager keyManager; private final TagRecogniser tagRecogniser; - private final SimplexConnectionFactory simplexConnFactory; - private final DuplexConnectionFactory duplexConnFactory; + private final MessagingSessionFactory messagingSessionFactory; + private final ConnectionRegistry connectionRegistry; @Inject ConnectionDispatcherImpl(@IoExecutor Executor ioExecutor, - TagRecogniser tagRecogniser, - SimplexConnectionFactory simplexConnFactory, - DuplexConnectionFactory duplexConnFactory) { + KeyManager keyManager, TagRecogniser tagRecogniser, + MessagingSessionFactory messagingSessionFactory, + ConnectionRegistry connectionRegistry) { this.ioExecutor = ioExecutor; + this.keyManager = keyManager; this.tagRecogniser = tagRecogniser; - this.simplexConnFactory = simplexConnFactory; - this.duplexConnFactory = duplexConnFactory; + this.messagingSessionFactory = messagingSessionFactory; + this.connectionRegistry = connectionRegistry; } public void dispatchIncomingConnection(TransportId t, - SimplexTransportReader r) { - ioExecutor.execute(new DispatchSimplexConnection(t, r)); + TransportConnectionReader r) { + ioExecutor.execute(new DispatchIncomingSimplexConnection(t, r)); } public void dispatchIncomingConnection(TransportId t, DuplexTransportConnection d) { - ioExecutor.execute(new DispatchDuplexConnection(t, d)); + ioExecutor.execute(new DispatchIncomingDuplexConnection(t, d)); } public void dispatchOutgoingConnection(ContactId c, TransportId t, - SimplexTransportWriter w) { - simplexConnFactory.createOutgoingConnection(c, t, w); + TransportConnectionWriter w) { + ioExecutor.execute(new DispatchOutgoingSimplexConnection(c, t, w)); } public void dispatchOutgoingConnection(ContactId c, TransportId t, DuplexTransportConnection d) { - duplexConnFactory.createOutgoingConnection(c, t, d); + ioExecutor.execute(new DispatchOutgoingDuplexConnection(c, t, d)); } - private byte[] readTag(InputStream in) throws IOException { - byte[] b = new byte[TAG_LENGTH]; - int offset = 0; - while(offset < b.length) { - int read = in.read(b, offset, b.length - offset); - if(read == -1) throw new EOFException(); - offset += read; + private StreamContext readAndRecogniseTag(TransportId t, + TransportConnectionReader r) { + // Read the tag + byte[] tag = new byte[TAG_LENGTH]; + try { + InputStream in = r.getInputStream(); + int offset = 0; + while(offset < tag.length) { + int read = in.read(tag, offset, tag.length - offset); + if(read == -1) throw new EOFException(); + offset += read; + } + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + dispose(r, true, false); + return null; + } + // Recognise the tag + StreamContext ctx = null; + try { + ctx = tagRecogniser.recogniseTag(t, tag); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + dispose(r, true, false); + return null; + } + if(ctx == null) dispose(r, false, false); + return ctx; + } + + private void runAndDispose(StreamContext ctx, TransportConnectionReader r) { + MessagingSession in = + messagingSessionFactory.createIncomingSession(ctx, r); + ContactId contactId = ctx.getContactId(); + TransportId transportId = ctx.getTransportId(); + connectionRegistry.registerConnection(contactId, transportId); + try { + in.run(); + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + dispose(r, true, true); + return; + } finally { + connectionRegistry.unregisterConnection(contactId, transportId); + } + dispose(r, false, true); + } + + private void dispose(TransportConnectionReader r, boolean exception, + boolean recognised) { + try { + r.dispose(exception, recognised); + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } + } + + private void runAndDispose(StreamContext ctx, TransportConnectionWriter w, + boolean duplex) { + MessagingSession out = + messagingSessionFactory.createOutgoingSession(ctx, w, duplex); + ContactId contactId = ctx.getContactId(); + TransportId transportId = ctx.getTransportId(); + connectionRegistry.registerConnection(contactId, transportId); + try { + out.run(); + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + dispose(w, true); + return; + } finally { + connectionRegistry.unregisterConnection(contactId, transportId); + } + dispose(w, false); + } + + private void dispose(TransportConnectionWriter w, boolean exception) { + try { + w.dispose(exception); + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } - return b; } - private class DispatchSimplexConnection implements Runnable { + private class DispatchIncomingSimplexConnection implements Runnable { private final TransportId transportId; - private final SimplexTransportReader transport; + private final TransportConnectionReader reader; - private DispatchSimplexConnection(TransportId transportId, - SimplexTransportReader transport) { + private DispatchIncomingSimplexConnection(TransportId transportId, + TransportConnectionReader reader) { this.transportId = transportId; - this.transport = transport; + this.reader = reader; } public void run() { - try { - byte[] tag = readTag(transport.getInputStream()); - StreamContext ctx = tagRecogniser.recogniseTag(transportId, - tag); - if(ctx == null) { - transport.dispose(false, false); - } else { - simplexConnFactory.createIncomingConnection(ctx, - transport); - } - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - try { - transport.dispose(true, false); - } catch(IOException e1) { - if(LOG.isLoggable(WARNING)) - LOG.log(WARNING, e1.toString(), e1); - } - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - try { - transport.dispose(true, false); - } catch(IOException e1) { - if(LOG.isLoggable(WARNING)) - LOG.log(WARNING, e1.toString(), e1); - } + // Read and recognise the tag + StreamContext ctx = readAndRecogniseTag(transportId, reader); + if(ctx == null) return; + // Run the incoming session + runAndDispose(ctx, reader); + } + } + + private class DispatchOutgoingSimplexConnection implements Runnable { + + private final ContactId contactId; + private final TransportId transportId; + private final TransportConnectionWriter writer; + + private DispatchOutgoingSimplexConnection(ContactId contactId, + TransportId transportId, TransportConnectionWriter writer) { + this.contactId = contactId; + this.transportId = transportId; + this.writer = writer; + } + + public void run() { + // Allocate a stream context + StreamContext ctx = keyManager.getStreamContext(contactId, + transportId); + if(ctx == null) { + dispose(writer, false); + return; } + // Run the outgoing session + runAndDispose(ctx, writer, false); } } - private class DispatchDuplexConnection implements Runnable { + private class DispatchIncomingDuplexConnection implements Runnable { private final TransportId transportId; - private final DuplexTransportConnection transport; + private final TransportConnectionReader reader; + private final TransportConnectionWriter writer; - private DispatchDuplexConnection(TransportId transportId, + private DispatchIncomingDuplexConnection(TransportId transportId, DuplexTransportConnection transport) { this.transportId = transportId; - this.transport = transport; + reader = transport.getReader(); + writer = transport.getWriter(); + } + + public void run() { + // Read and recognise the tag + StreamContext ctx = readAndRecogniseTag(transportId, reader); + if(ctx == null) return; + // Start the outgoing session on another thread + ioExecutor.execute(new DispatchIncomingDuplexConnectionSide2( + ctx.getContactId(), transportId, writer)); + // Run the incoming session + runAndDispose(ctx, reader); + } + } + + private class DispatchIncomingDuplexConnectionSide2 implements Runnable { + + private final ContactId contactId; + private final TransportId transportId; + private final TransportConnectionWriter writer; + + private DispatchIncomingDuplexConnectionSide2(ContactId contactId, + TransportId transportId, TransportConnectionWriter writer) { + this.contactId = contactId; + this.transportId = transportId; + this.writer = writer; } public void run() { - byte[] tag; - try { - tag = readTag(transport.getInputStream()); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true, false); + // Allocate a stream context + StreamContext ctx = keyManager.getStreamContext(contactId, + transportId); + if(ctx == null) { + dispose(writer, false); return; } - StreamContext ctx = null; - try { - ctx = tagRecogniser.recogniseTag(transportId, tag); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - dispose(true, false); + // Run the outgoing session + runAndDispose(ctx, writer, true); + } + } + + private class DispatchOutgoingDuplexConnection implements Runnable { + + private final ContactId contactId; + private final TransportId transportId; + private final TransportConnectionReader reader; + private final TransportConnectionWriter writer; + + private DispatchOutgoingDuplexConnection(ContactId contactId, + TransportId transportId, DuplexTransportConnection transport) { + this.contactId = contactId; + this.transportId = transportId; + reader = transport.getReader(); + writer = transport.getWriter(); + } + + public void run() { + // Allocate a stream context + StreamContext ctx = keyManager.getStreamContext(contactId, + transportId); + if(ctx == null) { + dispose(writer, false); return; } - if(ctx == null) dispose(false, false); - else duplexConnFactory.createIncomingConnection(ctx, transport); + // Start the incoming session on another thread + ioExecutor.execute(new DispatchOutgoingDuplexConnectionSide2( + contactId, transportId, reader)); + // Run the outgoing session + runAndDispose(ctx, writer, true); } + } + + private class DispatchOutgoingDuplexConnectionSide2 implements Runnable { - private void dispose(boolean exception, boolean recognised) { - try { - transport.dispose(exception, recognised); - } catch(IOException e) { - if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + private final ContactId contactId; + private final TransportId transportId; + private final TransportConnectionReader reader; + + private DispatchOutgoingDuplexConnectionSide2(ContactId contactId, + TransportId transportId, TransportConnectionReader reader) { + this.contactId = contactId; + this.transportId = transportId; + this.reader = reader; + } + + public void run() { + // Read and recognise the tag + StreamContext ctx = readAndRecogniseTag(transportId, reader); + if(ctx == null) return; + // Check that the stream comes from the expected contact + if(!ctx.getContactId().equals(contactId)) { + LOG.warning("Wrong contact ID for duplex connection"); + dispose(reader, true, true); + return; } + // Run the incoming session + runAndDispose(ctx, reader); } } } \ No newline at end of file diff --git a/briar-core/src/org/briarproject/transport/FrameWriter.java b/briar-core/src/org/briarproject/transport/FrameWriter.java index 167ab3430bfc0943f146c928a97cb9edc2c26196..4f29b7999a08fa1c7964fc65140124232f0b9028 100644 --- a/briar-core/src/org/briarproject/transport/FrameWriter.java +++ b/briar-core/src/org/briarproject/transport/FrameWriter.java @@ -8,9 +8,6 @@ interface FrameWriter { void writeFrame(byte[] frame, int payloadLength, boolean finalFrame) throws IOException; - /** Flushes the stack. */ + /** Flushes the stream. */ void flush() throws IOException; - - /** Returns the maximum number of bytes that can be written. */ - long getRemainingCapacity(); } diff --git a/briar-core/src/org/briarproject/transport/OutgoingEncryptionLayer.java b/briar-core/src/org/briarproject/transport/OutgoingEncryptionLayer.java index a2c0036772af4452243119cea77dc78a1fba6f5a..0d6d6ace977950a38d774f6652d005b282d16eef 100644 --- a/briar-core/src/org/briarproject/transport/OutgoingEncryptionLayer.java +++ b/briar-core/src/org/briarproject/transport/OutgoingEncryptionLayer.java @@ -19,22 +19,18 @@ class OutgoingEncryptionLayer implements FrameWriter { private final AuthenticatedCipher frameCipher; private final SecretKey frameKey; private final byte[] tag, iv, aad, ciphertext; - private final int frameLength, maxPayloadLength; + private final int frameLength; - private long capacity, frameNumber; + private long frameNumber; private boolean writeTag; - /** Constructor for the initiator's side of a connection. */ - OutgoingEncryptionLayer(OutputStream out, long capacity, - AuthenticatedCipher frameCipher, SecretKey frameKey, - int frameLength, byte[] tag) { + OutgoingEncryptionLayer(OutputStream out, AuthenticatedCipher frameCipher, + SecretKey frameKey, int frameLength, byte[] tag) { this.out = out; - this.capacity = capacity; this.frameCipher = frameCipher; this.frameKey = frameKey; this.frameLength = frameLength; this.tag = tag; - maxPayloadLength = frameLength - HEADER_LENGTH - MAC_LENGTH; iv = new byte[IV_LENGTH]; aad = new byte[AAD_LENGTH]; ciphertext = new byte[frameLength]; @@ -42,24 +38,6 @@ class OutgoingEncryptionLayer implements FrameWriter { writeTag = true; } - /** Constructor for the responder's side of a connection. */ - OutgoingEncryptionLayer(OutputStream out, long capacity, - AuthenticatedCipher frameCipher, SecretKey frameKey, - int frameLength) { - this.out = out; - this.capacity = capacity; - this.frameCipher = frameCipher; - this.frameKey = frameKey; - this.frameLength = frameLength; - tag = null; - maxPayloadLength = frameLength - HEADER_LENGTH - MAC_LENGTH; - iv = new byte[IV_LENGTH]; - aad = new byte[AAD_LENGTH]; - ciphertext = new byte[frameLength]; - frameNumber = 0; - writeTag = false; - } - public void writeFrame(byte[] frame, int payloadLength, boolean finalFrame) throws IOException { if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException(); @@ -71,7 +49,6 @@ class OutgoingEncryptionLayer implements FrameWriter { frameKey.erase(); throw e; } - capacity -= tag.length; writeTag = false; } // Encode the header @@ -107,7 +84,6 @@ class OutgoingEncryptionLayer implements FrameWriter { frameKey.erase(); throw e; } - capacity -= ciphertextLength; frameNumber++; } @@ -120,33 +96,8 @@ class OutgoingEncryptionLayer implements FrameWriter { frameKey.erase(); throw e; } - capacity -= tag.length; writeTag = false; } out.flush(); } - - public long getRemainingCapacity() { - // How many frame numbers can we use? - long frameNumbers = MAX_32_BIT_UNSIGNED - frameNumber + 1; - // How many full frames do we have space for? - long bytes = writeTag ? capacity - tag.length : capacity; - long fullFrames = bytes / frameLength; - // Are we limited by frame numbers or space? - if(frameNumbers > fullFrames) { - // Can we send a partial frame after the full frames? - int partialFrame = (int) (bytes - fullFrames * frameLength); - if(partialFrame > HEADER_LENGTH + MAC_LENGTH) { - // Send full frames and a partial frame, limited by space - int partialPayload = partialFrame - HEADER_LENGTH - MAC_LENGTH; - return maxPayloadLength * fullFrames + partialPayload; - } else { - // Send full frames only, limited by space - return maxPayloadLength * fullFrames; - } - } else { - // Send full frames only, limited by frame numbers - return maxPayloadLength * frameNumbers; - } - } } \ No newline at end of file diff --git a/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java b/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java index d2997b36635c4548306c0e80a95e45bd27cc92de..d71bfc50ac24c8cc72009a9df6689abee11ddfaa 100644 --- a/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java +++ b/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java @@ -20,24 +20,21 @@ class StreamReaderFactoryImpl implements StreamReaderFactory { } public StreamReader createStreamReader(InputStream in, - int maxFrameLength, StreamContext ctx, boolean incoming, - boolean initiator) { + int maxFrameLength, StreamContext ctx) { byte[] secret = ctx.getSecret(); long streamNumber = ctx.getStreamNumber(); - boolean weAreAlice = ctx.getAlice(); - boolean initiatorIsAlice = incoming ? !weAreAlice : weAreAlice; - SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, - initiatorIsAlice, initiator); - FrameReader encryption = new IncomingEncryptionLayer(in, + boolean alice = !ctx.getAlice(); + SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice); + FrameReader frameReader = new IncomingEncryptionLayer(in, crypto.getFrameCipher(), frameKey, maxFrameLength); - return new StreamReaderImpl(encryption, maxFrameLength); + return new StreamReaderImpl(frameReader, maxFrameLength); } public StreamReader createInvitationStreamReader(InputStream in, int maxFrameLength, byte[] secret, boolean alice) { - SecretKey frameKey = crypto.deriveFrameKey(secret, 0, true, alice); - FrameReader encryption = new IncomingEncryptionLayer(in, + SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice); + FrameReader frameReader = new IncomingEncryptionLayer(in, crypto.getFrameCipher(), frameKey, maxFrameLength); - return new StreamReaderImpl(encryption, maxFrameLength); + return new StreamReaderImpl(frameReader, maxFrameLength); } } diff --git a/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java b/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java index 1935fea1be241f80218264eb3ae7bcbc2fe92fe8..80185d74b04e9db0a85f4ed6340d8877e0a90b9e 100644 --- a/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java +++ b/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java @@ -22,35 +22,29 @@ class StreamWriterFactoryImpl implements StreamWriterFactory { } public StreamWriter createStreamWriter(OutputStream out, - int maxFrameLength, long capacity, StreamContext ctx, - boolean incoming, boolean initiator) { + int maxFrameLength, StreamContext ctx) { byte[] secret = ctx.getSecret(); long streamNumber = ctx.getStreamNumber(); - boolean weAreAlice = ctx.getAlice(); - boolean initiatorIsAlice = incoming ? !weAreAlice : weAreAlice; - SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, - initiatorIsAlice, initiator); - FrameWriter encryption; - if(initiator) { - byte[] tag = new byte[TAG_LENGTH]; - SecretKey tagKey = crypto.deriveTagKey(secret, initiatorIsAlice); - crypto.encodeTag(tag, tagKey, streamNumber); - tagKey.erase(); - encryption = new OutgoingEncryptionLayer(out, capacity, - crypto.getFrameCipher(), frameKey, maxFrameLength, tag); - } else { - encryption = new OutgoingEncryptionLayer(out, capacity, - crypto.getFrameCipher(), frameKey, maxFrameLength); - } - return new StreamWriterImpl(encryption, maxFrameLength); + boolean alice = ctx.getAlice(); + byte[] tag = new byte[TAG_LENGTH]; + SecretKey tagKey = crypto.deriveTagKey(secret, alice); + crypto.encodeTag(tag, tagKey, streamNumber); + tagKey.erase(); + SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice); + FrameWriter frameWriter = new OutgoingEncryptionLayer(out, + crypto.getFrameCipher(), frameKey, maxFrameLength, tag); + return new StreamWriterImpl(frameWriter, maxFrameLength); } public StreamWriter createInvitationStreamWriter(OutputStream out, int maxFrameLength, byte[] secret, boolean alice) { - SecretKey frameKey = crypto.deriveFrameKey(secret, 0, true, alice); - FrameWriter encryption = new OutgoingEncryptionLayer(out, - Long.MAX_VALUE, crypto.getFrameCipher(), frameKey, - maxFrameLength); - return new StreamWriterImpl(encryption, maxFrameLength); + byte[] tag = new byte[TAG_LENGTH]; + SecretKey tagKey = crypto.deriveTagKey(secret, alice); + crypto.encodeTag(tag, tagKey, 0); + tagKey.erase(); + SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice); + FrameWriter frameWriter = new OutgoingEncryptionLayer(out, + crypto.getFrameCipher(), frameKey, maxFrameLength, tag); + return new StreamWriterImpl(frameWriter, maxFrameLength); } } \ No newline at end of file diff --git a/briar-core/src/org/briarproject/transport/StreamWriterImpl.java b/briar-core/src/org/briarproject/transport/StreamWriterImpl.java index 82de19e1e0012fc6ad0ae396c21e19986f34bee3..7a65a12199a7133a79928c768d38f0d3d7be0e68 100644 --- a/briar-core/src/org/briarproject/transport/StreamWriterImpl.java +++ b/briar-core/src/org/briarproject/transport/StreamWriterImpl.java @@ -33,10 +33,6 @@ class StreamWriterImpl extends OutputStream implements StreamWriter { return this; } - public long getRemainingCapacity() { - return out.getRemainingCapacity(); - } - @Override public void close() throws IOException { writeFrame(true); diff --git a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java index 4e99e439c56dde2036ec979e345533e387a0be83..8a2c7967257c223d5e34c08444eab33663ab3afd 100644 --- a/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java +++ b/briar-desktop/src/org/briarproject/plugins/bluetooth/BluetoothTransportConnection.java @@ -3,40 +3,82 @@ package org.briarproject.plugins.bluetooth; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.concurrent.atomic.AtomicBoolean; import javax.microedition.io.StreamConnection; import org.briarproject.api.plugins.Plugin; +import org.briarproject.api.plugins.TransportConnectionReader; +import org.briarproject.api.plugins.TransportConnectionWriter; import org.briarproject.api.plugins.duplex.DuplexTransportConnection; class BluetoothTransportConnection implements DuplexTransportConnection { private final Plugin plugin; private final StreamConnection stream; + private final Reader reader; + private final Writer writer; + private final AtomicBoolean halfClosed, closed; BluetoothTransportConnection(Plugin plugin, StreamConnection stream) { this.plugin = plugin; this.stream = stream; + reader = new Reader(); + writer = new Writer(); + halfClosed = new AtomicBoolean(false); + closed = new AtomicBoolean(false); } - public int getMaxFrameLength() { - return plugin.getMaxFrameLength(); + public TransportConnectionReader getReader() { + return reader; } - public long getMaxLatency() { - return plugin.getMaxLatency(); + public TransportConnectionWriter getWriter() { + return writer; } - public InputStream getInputStream() throws IOException { - return stream.openInputStream(); - } + private class Reader implements TransportConnectionReader { + + public int getMaxFrameLength() { + return plugin.getMaxFrameLength(); + } + + public long getMaxLatency() { + return plugin.getMaxLatency(); + } - public OutputStream getOutputStream() throws IOException { - return stream.openOutputStream(); + public InputStream getInputStream() throws IOException { + return stream.openInputStream(); + } + + public void dispose(boolean exception, boolean recognised) + throws IOException { + if(halfClosed.getAndSet(true) || exception) + if(!closed.getAndSet(true)) stream.close(); + } } - public void dispose(boolean exception, boolean recognised) - throws IOException { - stream.close(); + private class Writer implements TransportConnectionWriter { + + public int getMaxFrameLength() { + return plugin.getMaxFrameLength(); + } + + public long getMaxLatency() { + return plugin.getMaxLatency(); + } + + public long getCapacity() { + return Long.MAX_VALUE; + } + + public OutputStream getOutputStream() throws IOException { + return stream.openOutputStream(); + } + + public void dispose(boolean exception) throws IOException { + if(halfClosed.getAndSet(true) || exception) + if(!closed.getAndSet(true)) stream.close(); + } } } diff --git a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java index 22d45eb0254f99298522c1c702c17f47bf49f446..873fd5e395ccd905b67370534c441db13489a316 100644 --- a/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java +++ b/briar-desktop/src/org/briarproject/plugins/modem/ModemPlugin.java @@ -14,12 +14,15 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import org.briarproject.api.ContactId; import org.briarproject.api.TransportId; import org.briarproject.api.TransportProperties; import org.briarproject.api.crypto.PseudoRandom; +import org.briarproject.api.plugins.TransportConnectionReader; +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.DuplexTransportConnection; @@ -102,6 +105,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { return running; } + // FIXME: Don't poll this plugin public boolean shouldPoll() { return true; } @@ -164,7 +168,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { } } - private boolean resetModem() { + boolean resetModem() { if(!running) return false; for(String portName : serialPortList.getPortNames()) { if(LOG.isLoggable(INFO)) @@ -227,25 +231,21 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { private class ModemTransportConnection implements DuplexTransportConnection { - private final CountDownLatch finished = new CountDownLatch(1); + private final AtomicBoolean halfClosed = new AtomicBoolean(false); + private final AtomicBoolean closed = new AtomicBoolean(false); + private final CountDownLatch disposalFinished = new CountDownLatch(1); + private final Reader reader = new Reader(); + private final Writer writer = new Writer(); - public int getMaxFrameLength() { - return maxFrameLength; + public TransportConnectionReader getReader() { + return reader; } - public long getMaxLatency() { - return maxLatency; + public TransportConnectionWriter getWriter() { + return writer; } - public InputStream getInputStream() throws IOException { - return modem.getInputStream(); - } - - public OutputStream getOutputStream() throws IOException { - return modem.getOutputStream(); - } - - public void dispose(boolean exception, boolean recognised) { + private void hangUp(boolean exception) { LOG.info("Call disconnected"); try { modem.hangUp(); @@ -254,11 +254,55 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { exception = true; } if(exception) resetModem(); - finished.countDown(); + disposalFinished.countDown(); } private void waitForDisposal() throws InterruptedException { - finished.await(); + disposalFinished.await(); + } + + private class Reader implements TransportConnectionReader { + + public int getMaxFrameLength() { + return maxFrameLength; + } + + public long getMaxLatency() { + return maxLatency; + } + + public InputStream getInputStream() throws IOException { + return modem.getInputStream(); + } + + public void dispose(boolean exception, boolean recognised) { + if(halfClosed.getAndSet(true) || exception) + if(!closed.getAndSet(true)) hangUp(exception); + } + } + + private class Writer implements TransportConnectionWriter { + + public int getMaxFrameLength() { + return maxFrameLength; + } + + public long getMaxLatency() { + return maxLatency; + } + + public long getCapacity() { + return Long.MAX_VALUE; + } + + public OutputStream getOutputStream() throws IOException { + return modem.getOutputStream(); + } + + public void dispose(boolean exception) { + if(halfClosed.getAndSet(true) || exception) + if(!closed.getAndSet(true)) hangUp(exception); + } } } } diff --git a/briar-tests/build.xml b/briar-tests/build.xml index e126d2dceb96441e923f8ad36bfa1af844c0db75..88c573a8b1c528153ebfb572afb7d03fc257bcef 100644 --- a/briar-tests/build.xml +++ b/briar-tests/build.xml @@ -111,8 +111,8 @@ <test name='org.briarproject.messaging.ConstantsTest'/> <test name='org.briarproject.messaging.ConsumersTest'/> <test name='org.briarproject.messaging.PacketReaderImplTest'/> - <test name='org.briarproject.messaging.simplex.OutgoingSimplexConnectionTest'/> - <test name='org.briarproject.messaging.simplex.SimplexMessagingIntegrationTest'/> + <test name='org.briarproject.messaging.SimplexMessagingIntegrationTest'/> + <test name='org.briarproject.messaging.SinglePassOutgoingSessionTest'/> <test name='org.briarproject.plugins.PluginManagerImplTest'/> <test name='org.briarproject.plugins.file.LinuxRemovableDriveFinderTest'/> <test name='org.briarproject.plugins.file.MacRemovableDriveFinderTest'/> diff --git a/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java b/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java index 9e1b190adfb1baba0d647bf762ee4dfb3d8e9b43..8cbc75befd6f6970ffc181ea48a36c46aabdd8e7 100644 --- a/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java +++ b/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java @@ -45,8 +45,6 @@ import org.briarproject.crypto.CryptoModule; import org.briarproject.db.DatabaseModule; import org.briarproject.event.EventModule; import org.briarproject.messaging.MessagingModule; -import org.briarproject.messaging.duplex.DuplexMessagingModule; -import org.briarproject.messaging.simplex.SimplexMessagingModule; import org.briarproject.reliability.ReliabilityModule; import org.briarproject.serial.SerialModule; import org.briarproject.transport.TransportModule; @@ -81,7 +79,6 @@ public class ProtocolIntegrationTest extends BriarTestCase { new TestLifecycleModule(), new TestSystemModule(), new TestUiModule(), new CryptoModule(), new DatabaseModule(), new EventModule(), new MessagingModule(), - new DuplexMessagingModule(), new SimplexMessagingModule(), new ReliabilityModule(), new SerialModule(), new TransportModule()); streamReaderFactory = i.getInstance(StreamReaderFactory.class); @@ -125,29 +122,29 @@ public class ProtocolIntegrationTest extends BriarTestCase { ByteArrayOutputStream out = new ByteArrayOutputStream(); StreamContext ctx = new StreamContext(contactId, transportId, secret.clone(), 0, true); - StreamWriter conn = streamWriterFactory.createStreamWriter( - out, MAX_FRAME_LENGTH, Long.MAX_VALUE, ctx, false, true); - OutputStream out1 = conn.getOutputStream(); - PacketWriter writer = packetWriterFactory.createPacketWriter(out1, + StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out, + MAX_FRAME_LENGTH, ctx); + OutputStream out1 = streamWriter.getOutputStream(); + PacketWriter packetWriter = packetWriterFactory.createPacketWriter(out1, false); - writer.writeAck(new Ack(messageIds)); + packetWriter.writeAck(new Ack(messageIds)); - writer.writeMessage(message.getSerialised()); - writer.writeMessage(message1.getSerialised()); + packetWriter.writeMessage(message.getSerialised()); + packetWriter.writeMessage(message1.getSerialised()); - writer.writeOffer(new Offer(messageIds)); + packetWriter.writeOffer(new Offer(messageIds)); - writer.writeRequest(new Request(messageIds)); + packetWriter.writeRequest(new Request(messageIds)); SubscriptionUpdate su = new SubscriptionUpdate(Arrays.asList(group), 1); - writer.writeSubscriptionUpdate(su); + packetWriter.writeSubscriptionUpdate(su); TransportUpdate tu = new TransportUpdate(transportId, transportProperties, 1); - writer.writeTransportUpdate(tu); + packetWriter.writeTransportUpdate(tu); - writer.flush(); + packetWriter.flush(); return out.toByteArray(); } @@ -158,44 +155,44 @@ public class ProtocolIntegrationTest extends BriarTestCase { // FIXME: Check that the expected tag was received StreamContext ctx = new StreamContext(contactId, transportId, secret.clone(), 0, false); - StreamReader conn = streamReaderFactory.createStreamReader( - in, MAX_FRAME_LENGTH, ctx, true, true); - InputStream in1 = conn.getInputStream(); - PacketReader reader = packetReaderFactory.createPacketReader(in1); + StreamReader streamReader = streamReaderFactory.createStreamReader(in, + MAX_FRAME_LENGTH, ctx); + InputStream in1 = streamReader.getInputStream(); + PacketReader packetReader = packetReaderFactory.createPacketReader(in1); // Read the ack - assertTrue(reader.hasAck()); - Ack a = reader.readAck(); + assertTrue(packetReader.hasAck()); + Ack a = packetReader.readAck(); assertEquals(messageIds, a.getMessageIds()); // Read and verify the messages - assertTrue(reader.hasMessage()); - UnverifiedMessage m = reader.readMessage(); + assertTrue(packetReader.hasMessage()); + UnverifiedMessage m = packetReader.readMessage(); checkMessageEquality(message, messageVerifier.verifyMessage(m)); - assertTrue(reader.hasMessage()); - m = reader.readMessage(); + assertTrue(packetReader.hasMessage()); + m = packetReader.readMessage(); checkMessageEquality(message1, messageVerifier.verifyMessage(m)); - assertFalse(reader.hasMessage()); + assertFalse(packetReader.hasMessage()); // Read the offer - assertTrue(reader.hasOffer()); - Offer o = reader.readOffer(); + assertTrue(packetReader.hasOffer()); + Offer o = packetReader.readOffer(); assertEquals(messageIds, o.getMessageIds()); // Read the request - assertTrue(reader.hasRequest()); - Request req = reader.readRequest(); + assertTrue(packetReader.hasRequest()); + Request req = packetReader.readRequest(); assertEquals(messageIds, req.getMessageIds()); // Read the subscription update - assertTrue(reader.hasSubscriptionUpdate()); - SubscriptionUpdate su = reader.readSubscriptionUpdate(); + assertTrue(packetReader.hasSubscriptionUpdate()); + SubscriptionUpdate su = packetReader.readSubscriptionUpdate(); assertEquals(Arrays.asList(group), su.getGroups()); assertEquals(1, su.getVersion()); // Read the transport update - assertTrue(reader.hasTransportUpdate()); - TransportUpdate tu = reader.readTransportUpdate(); + assertTrue(packetReader.hasTransportUpdate()); + TransportUpdate tu = packetReader.readTransportUpdate(); assertEquals(transportId, tu.getId()); assertEquals(transportProperties, tu.getProperties()); assertEquals(1, tu.getVersion()); diff --git a/briar-tests/src/org/briarproject/crypto/KeyDerivationTest.java b/briar-tests/src/org/briarproject/crypto/KeyDerivationTest.java index 4b27522e84c17a756ce52e1a6e0f261f277758ac..54c4a3c1560d6363bbe277a740a358ae811aff30 100644 --- a/briar-tests/src/org/briarproject/crypto/KeyDerivationTest.java +++ b/briar-tests/src/org/briarproject/crypto/KeyDerivationTest.java @@ -25,10 +25,8 @@ public class KeyDerivationTest extends BriarTestCase { @Test public void testKeysAreDistinct() { List<SecretKey> keys = new ArrayList<SecretKey>(); - keys.add(crypto.deriveFrameKey(secret, 0, false, false)); - keys.add(crypto.deriveFrameKey(secret, 0, false, true)); - keys.add(crypto.deriveFrameKey(secret, 0, true, false)); - keys.add(crypto.deriveFrameKey(secret, 0, true, true)); + keys.add(crypto.deriveFrameKey(secret, 0, true)); + keys.add(crypto.deriveFrameKey(secret, 0, false)); keys.add(crypto.deriveTagKey(secret, true)); keys.add(crypto.deriveTagKey(secret, false)); for(int i = 0; i < 4; i++) { diff --git a/briar-tests/src/org/briarproject/messaging/ConstantsTest.java b/briar-tests/src/org/briarproject/messaging/ConstantsTest.java index 75e36a80fd08134fcc3000a5eb7d583911488591..1aee065e1f325972074c82e0915b4a8e2a899d20 100644 --- a/briar-tests/src/org/briarproject/messaging/ConstantsTest.java +++ b/briar-tests/src/org/briarproject/messaging/ConstantsTest.java @@ -46,8 +46,6 @@ import org.briarproject.api.messaging.TransportUpdate; import org.briarproject.crypto.CryptoModule; import org.briarproject.db.DatabaseModule; import org.briarproject.event.EventModule; -import org.briarproject.messaging.duplex.DuplexMessagingModule; -import org.briarproject.messaging.simplex.SimplexMessagingModule; import org.briarproject.serial.SerialModule; import org.briarproject.transport.TransportModule; import org.junit.Test; @@ -67,8 +65,7 @@ public class ConstantsTest extends BriarTestCase { Injector i = Guice.createInjector(new TestDatabaseModule(), new TestLifecycleModule(), new TestSystemModule(), new CryptoModule(), new DatabaseModule(), new EventModule(), - new MessagingModule(), new DuplexMessagingModule(), - new SimplexMessagingModule(), new SerialModule(), + new MessagingModule(), new SerialModule(), new TransportModule()); crypto = i.getInstance(CryptoComponent.class); groupFactory = i.getInstance(GroupFactory.class); diff --git a/briar-tests/src/org/briarproject/messaging/simplex/SimplexMessagingIntegrationTest.java b/briar-tests/src/org/briarproject/messaging/SimplexMessagingIntegrationTest.java similarity index 84% rename from briar-tests/src/org/briarproject/messaging/simplex/SimplexMessagingIntegrationTest.java rename to briar-tests/src/org/briarproject/messaging/SimplexMessagingIntegrationTest.java index e26b4be88458e1ed464fc54d3090c5dddfab28c7..834eba129f1fab9435a762b584749e9652ebc475 100644 --- a/briar-tests/src/org/briarproject/messaging/simplex/SimplexMessagingIntegrationTest.java +++ b/briar-tests/src/org/briarproject/messaging/SimplexMessagingIntegrationTest.java @@ -1,4 +1,4 @@ -package org.briarproject.messaging.simplex; +package org.briarproject.messaging; import static org.briarproject.api.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.api.messaging.MessagingConstants.GROUP_SALT_LENGTH; @@ -30,9 +30,9 @@ import org.briarproject.api.messaging.GroupFactory; import org.briarproject.api.messaging.Message; import org.briarproject.api.messaging.MessageFactory; import org.briarproject.api.messaging.MessageVerifier; +import org.briarproject.api.messaging.MessagingSession; import org.briarproject.api.messaging.PacketReaderFactory; import org.briarproject.api.messaging.PacketWriterFactory; -import org.briarproject.api.transport.ConnectionRegistry; import org.briarproject.api.transport.Endpoint; import org.briarproject.api.transport.StreamContext; import org.briarproject.api.transport.StreamReaderFactory; @@ -41,8 +41,6 @@ import org.briarproject.api.transport.TagRecogniser; import org.briarproject.crypto.CryptoModule; import org.briarproject.db.DatabaseModule; import org.briarproject.event.EventModule; -import org.briarproject.messaging.MessagingModule; -import org.briarproject.messaging.duplex.DuplexMessagingModule; import org.briarproject.plugins.ImmediateExecutor; import org.briarproject.serial.SerialModule; import org.briarproject.transport.TransportModule; @@ -88,8 +86,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { return Guice.createInjector(new TestDatabaseModule(dir), new TestLifecycleModule(), new TestSystemModule(), new CryptoModule(), new DatabaseModule(), new EventModule(), - new MessagingModule(), new DuplexMessagingModule(), - new SimplexMessagingModule(), new SerialModule(), + new MessagingModule(), new SerialModule(), new TransportModule()); } @@ -140,29 +137,26 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { Message message = messageFactory.createAnonymousMessage(null, group, contentType, timestamp, body); db.addLocalMessage(message); - // Create an outgoing simplex connection + // Create an outgoing messaging session ByteArrayOutputStream out = new ByteArrayOutputStream(); - ConnectionRegistry connRegistry = - alice.getInstance(ConnectionRegistry.class); - StreamWriterFactory connWriterFactory = + StreamWriterFactory streamWriterFactory = alice.getInstance(StreamWriterFactory.class); PacketWriterFactory packetWriterFactory = alice.getInstance(PacketWriterFactory.class); - TestSimplexTransportWriter transport = new TestSimplexTransportWriter( - out, Long.MAX_VALUE, Long.MAX_VALUE); + TestTransportConnectionWriter transport = + new TestTransportConnectionWriter(out); StreamContext ctx = km.getStreamContext(contactId, transportId); assertNotNull(ctx); - OutgoingSimplexConnection simplex = new OutgoingSimplexConnection(db, - connRegistry, connWriterFactory, packetWriterFactory, ctx, - transport); + MessagingSession session = new SinglePassOutgoingSession(db, + new ImmediateExecutor(), streamWriterFactory, + packetWriterFactory, ctx, transport); // Write whatever needs to be written - simplex.write(); - assertTrue(transport.getDisposed()); - assertFalse(transport.getException()); + session.run(); + transport.dispose(false); // Clean up km.stop(); db.close(); - // Return the contents of the simplex connection + // Return the contents of the stream return out.toByteArray(); } @@ -204,28 +198,24 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { assertEquals(tag.length, read); StreamContext ctx = rec.recogniseTag(transportId, tag); assertNotNull(ctx); - // Create an incoming simplex connection + // Create an incoming messaging session MessageVerifier messageVerifier = bob.getInstance(MessageVerifier.class); - ConnectionRegistry connRegistry = - bob.getInstance(ConnectionRegistry.class); - StreamReaderFactory connWriterFactory = + StreamReaderFactory streamReaderFactory = bob.getInstance(StreamReaderFactory.class); - PacketReaderFactory packetWriterFactory = + PacketReaderFactory packetReaderFactory = bob.getInstance(PacketReaderFactory.class); - TestSimplexTransportReader transport = - new TestSimplexTransportReader(in); - IncomingSimplexConnection simplex = new IncomingSimplexConnection( + TestTransportConnectionReader transport = + new TestTransportConnectionReader(in); + MessagingSession session = new IncomingSession(db, new ImmediateExecutor(), new ImmediateExecutor(), - messageVerifier, db, connRegistry, connWriterFactory, - packetWriterFactory, ctx, transport); + messageVerifier, streamReaderFactory, packetReaderFactory, + ctx, transport); // No messages should have been added yet assertFalse(listener.messageAdded); // Read whatever needs to be read - simplex.read(); - assertTrue(transport.getDisposed()); - assertFalse(transport.getException()); - assertTrue(transport.getRecognised()); + session.run(); + transport.dispose(false, true); // The private message from Alice should have been added assertTrue(listener.messageAdded); // Clean up diff --git a/briar-tests/src/org/briarproject/messaging/simplex/OutgoingSimplexConnectionTest.java b/briar-tests/src/org/briarproject/messaging/SinglePassOutgoingSessionTest.java similarity index 70% rename from briar-tests/src/org/briarproject/messaging/simplex/OutgoingSimplexConnectionTest.java rename to briar-tests/src/org/briarproject/messaging/SinglePassOutgoingSessionTest.java index 3de41423c4f04d3135a61882f8b42ff1abc2e759..27f03296f1439519023432c34bbaa5de448aeae3 100644 --- a/briar-tests/src/org/briarproject/messaging/simplex/OutgoingSimplexConnectionTest.java +++ b/briar-tests/src/org/briarproject/messaging/SinglePassOutgoingSessionTest.java @@ -1,9 +1,7 @@ -package org.briarproject.messaging.simplex; +package org.briarproject.messaging; -import static org.briarproject.api.messaging.MessagingConstants.MAX_PACKET_LENGTH; import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; -import static org.briarproject.api.transport.TransportConstants.MIN_STREAM_LENGTH; import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; import java.io.ByteArrayOutputStream; @@ -23,14 +21,12 @@ import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DatabaseExecutor; import org.briarproject.api.messaging.Ack; import org.briarproject.api.messaging.MessageId; +import org.briarproject.api.messaging.MessagingSession; import org.briarproject.api.messaging.PacketWriterFactory; -import org.briarproject.api.transport.ConnectionRegistry; import org.briarproject.api.transport.StreamContext; import org.briarproject.api.transport.StreamWriterFactory; import org.briarproject.crypto.CryptoModule; import org.briarproject.event.EventModule; -import org.briarproject.messaging.MessagingModule; -import org.briarproject.messaging.duplex.DuplexMessagingModule; import org.briarproject.serial.SerialModule; import org.briarproject.transport.TransportModule; import org.jmock.Expectations; @@ -42,39 +38,37 @@ import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; -public class OutgoingSimplexConnectionTest extends BriarTestCase { +public class SinglePassOutgoingSessionTest extends BriarTestCase { // FIXME: This is an integration test, not a unit test private final Mockery context; private final DatabaseComponent db; - private final ConnectionRegistry connRegistry; - private final StreamWriterFactory connWriterFactory; + private final Executor dbExecutor; + private final StreamWriterFactory streamWriterFactory; private final PacketWriterFactory packetWriterFactory; private final ContactId contactId; private final MessageId messageId; private final TransportId transportId; private final byte[] secret; - public OutgoingSimplexConnectionTest() { + public SinglePassOutgoingSessionTest() { context = new Mockery(); db = context.mock(DatabaseComponent.class); + dbExecutor = Executors.newSingleThreadExecutor(); Module testModule = new AbstractModule() { @Override public void configure() { bind(DatabaseComponent.class).toInstance(db); bind(Executor.class).annotatedWith( - DatabaseExecutor.class).toInstance( - Executors.newCachedThreadPool()); + DatabaseExecutor.class).toInstance(dbExecutor); } }; Injector i = Guice.createInjector(testModule, new TestLifecycleModule(), new TestSystemModule(), new CryptoModule(), new EventModule(), new MessagingModule(), - new DuplexMessagingModule(), new SimplexMessagingModule(), new SerialModule(), new TransportModule()); - connRegistry = i.getInstance(ConnectionRegistry.class); - connWriterFactory = i.getInstance(StreamWriterFactory.class); + streamWriterFactory = i.getInstance(StreamWriterFactory.class); packetWriterFactory = i.getInstance(PacketWriterFactory.class); contactId = new ContactId(234); messageId = new MessageId(TestUtils.getRandomId()); @@ -83,34 +77,15 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase { new Random().nextBytes(secret); } - @Test - public void testConnectionTooShort() throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - TestSimplexTransportWriter transport = new TestSimplexTransportWriter( - out, MAX_PACKET_LENGTH, Long.MAX_VALUE); - StreamContext ctx = new StreamContext(contactId, transportId, - secret, 0, true); - OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db, - connRegistry, connWriterFactory, packetWriterFactory, ctx, - transport); - connection.write(); - // Nothing should have been written - assertEquals(0, out.size()); - // The transport should have been disposed with exception == true - assertTrue(transport.getDisposed()); - assertTrue(transport.getException()); - } - @Test public void testNothingToSend() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - TestSimplexTransportWriter transport = new TestSimplexTransportWriter( - out, MIN_STREAM_LENGTH, Long.MAX_VALUE); + TestTransportConnectionWriter writer = + new TestTransportConnectionWriter(out); StreamContext ctx = new StreamContext(contactId, transportId, secret, 0, true); - OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db, - connRegistry, connWriterFactory, packetWriterFactory, ctx, - transport); + MessagingSession session = new SinglePassOutgoingSession(db, dbExecutor, + streamWriterFactory, packetWriterFactory, ctx, writer); context.checking(new Expectations() {{ // No transport acks to send oneOf(db).generateTransportAcks(contactId); @@ -141,25 +116,22 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase { with(any(long.class))); will(returnValue(null)); }}); - connection.write(); + session.run(); // Only the tag and an empty final frame should have been written assertEquals(TAG_LENGTH + HEADER_LENGTH + MAC_LENGTH, out.size()); - // The transport should have been disposed with exception == false - assertTrue(transport.getDisposed()); - assertFalse(transport.getException()); context.assertIsSatisfied(); } @Test public void testSomethingToSend() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - TestSimplexTransportWriter transport = new TestSimplexTransportWriter( - out, MIN_STREAM_LENGTH, Long.MAX_VALUE); + TestTransportConnectionWriter writer = + new TestTransportConnectionWriter(out); StreamContext ctx = new StreamContext(contactId, transportId, secret, 0, true); - OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db, - connRegistry, connWriterFactory, packetWriterFactory, ctx, - transport); + MessagingSession session = new SinglePassOutgoingSession(db, dbExecutor, + streamWriterFactory, packetWriterFactory, + ctx, writer); final byte[] raw = new byte[1234]; context.checking(new Expectations() {{ // No transport acks to send @@ -198,13 +170,10 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase { with(any(long.class))); will(returnValue(null)); }}); - connection.write(); + session.run(); // Something should have been written int overhead = TAG_LENGTH + HEADER_LENGTH + MAC_LENGTH; assertTrue(out.size() > overhead + UniqueId.LENGTH + raw.length); - // The transport should have been disposed with exception == false - assertTrue(transport.getDisposed()); - assertFalse(transport.getException()); context.assertIsSatisfied(); } } diff --git a/briar-tests/src/org/briarproject/messaging/TestTransportConnectionReader.java b/briar-tests/src/org/briarproject/messaging/TestTransportConnectionReader.java new file mode 100644 index 0000000000000000000000000000000000000000..246c0920d0989eea47159c666e5fd897f2640bd3 --- /dev/null +++ b/briar-tests/src/org/briarproject/messaging/TestTransportConnectionReader.java @@ -0,0 +1,35 @@ +package org.briarproject.messaging; + +import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; + +import java.io.InputStream; + +import org.briarproject.api.plugins.TransportConnectionReader; + +class TestTransportConnectionReader implements TransportConnectionReader { + + private final InputStream in; + + private boolean disposed = false; + + TestTransportConnectionReader(InputStream in) { + this.in = in; + } + + public int getMaxFrameLength() { + return MAX_FRAME_LENGTH; + } + + public long getMaxLatency() { + return Long.MAX_VALUE; + } + + public InputStream getInputStream() { + return in; + } + + public void dispose(boolean exception, boolean recognised) { + assert !disposed; + disposed = true; + } +} \ No newline at end of file diff --git a/briar-tests/src/org/briarproject/messaging/TestTransportConnectionWriter.java b/briar-tests/src/org/briarproject/messaging/TestTransportConnectionWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..ab4e4077ecee1e2a4c77dc5cb5ccfa6f91315d20 --- /dev/null +++ b/briar-tests/src/org/briarproject/messaging/TestTransportConnectionWriter.java @@ -0,0 +1,40 @@ +package org.briarproject.messaging; + +import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; + +import org.briarproject.api.plugins.TransportConnectionWriter; + +class TestTransportConnectionWriter implements TransportConnectionWriter { + + private final ByteArrayOutputStream out; + + private boolean disposed = false; + + TestTransportConnectionWriter(ByteArrayOutputStream out) { + this.out = out; + } + + public long getCapacity() { + return Long.MAX_VALUE; + } + + public int getMaxFrameLength() { + return MAX_FRAME_LENGTH; + } + + public long getMaxLatency() { + return Long.MAX_VALUE; + } + + public OutputStream getOutputStream() { + return out; + } + + public void dispose(boolean exception) { + assert !disposed; + disposed = true; + } +} \ No newline at end of file diff --git a/briar-tests/src/org/briarproject/messaging/simplex/TestSimplexTransportReader.java b/briar-tests/src/org/briarproject/messaging/simplex/TestSimplexTransportReader.java deleted file mode 100644 index 22829b8b85cb728ebbc1b8cddc9612a83436af63..0000000000000000000000000000000000000000 --- a/briar-tests/src/org/briarproject/messaging/simplex/TestSimplexTransportReader.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.briarproject.messaging.simplex; - -import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; - -import java.io.InputStream; - -import org.briarproject.api.plugins.simplex.SimplexTransportReader; - -class TestSimplexTransportReader implements SimplexTransportReader { - - private final InputStream in; - - private boolean disposed = false, exception = false, recognised = false; - - TestSimplexTransportReader(InputStream in) { - this.in = in; - } - - public int getMaxFrameLength() { - return MAX_FRAME_LENGTH; - } - - public InputStream getInputStream() { - return in; - } - - public void dispose(boolean exception, boolean recognised) { - assert !disposed; - disposed = true; - this.exception = exception; - this.recognised = recognised; - } - - boolean getDisposed() { - return disposed; - } - - boolean getException() { - return exception; - } - - boolean getRecognised() { - return recognised; - } -} \ No newline at end of file diff --git a/briar-tests/src/org/briarproject/messaging/simplex/TestSimplexTransportWriter.java b/briar-tests/src/org/briarproject/messaging/simplex/TestSimplexTransportWriter.java deleted file mode 100644 index 39e4a76810813326d5b53886012619688376dfe7..0000000000000000000000000000000000000000 --- a/briar-tests/src/org/briarproject/messaging/simplex/TestSimplexTransportWriter.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.briarproject.messaging.simplex; - -import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; - -import java.io.ByteArrayOutputStream; -import java.io.OutputStream; - -import org.briarproject.api.plugins.simplex.SimplexTransportWriter; - -class TestSimplexTransportWriter implements SimplexTransportWriter { - - private final ByteArrayOutputStream out; - private final long capacity, maxLatency; - - private boolean disposed = false, exception = false; - - TestSimplexTransportWriter(ByteArrayOutputStream out, long capacity, - long maxLatency) { - this.out = out; - this.capacity = capacity; - this.maxLatency = maxLatency; - } - - public long getCapacity() { - return capacity; - } - - public int getMaxFrameLength() { - return MAX_FRAME_LENGTH; - } - - public long getMaxLatency() { - return maxLatency; - } - - public OutputStream getOutputStream() { - return out; - } - - public void dispose(boolean exception) { - assert !disposed; - disposed = true; - this.exception = exception; - } - - boolean getDisposed() { - return disposed; - } - - boolean getException() { - return exception; - } -} \ No newline at end of file diff --git a/briar-tests/src/org/briarproject/plugins/DuplexTest.java b/briar-tests/src/org/briarproject/plugins/DuplexTest.java index 20de33cbe6baf2e7af9f255e0aa2da5cc55b580b..e818e3bb25092fcc56d4d863e45e046e5a9eabe5 100644 --- a/briar-tests/src/org/briarproject/plugins/DuplexTest.java +++ b/briar-tests/src/org/briarproject/plugins/DuplexTest.java @@ -7,6 +7,8 @@ import java.util.Scanner; import org.briarproject.api.ContactId; import org.briarproject.api.crypto.PseudoRandom; +import org.briarproject.api.plugins.TransportConnectionReader; +import org.briarproject.api.plugins.TransportConnectionWriter; import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexTransportConnection; @@ -21,12 +23,14 @@ abstract class DuplexTest { protected void sendChallengeReceiveResponse(DuplexTransportConnection d) { assert plugin != null; + TransportConnectionReader r = d.getReader(); + TransportConnectionWriter w = d.getWriter(); try { - PrintStream out = new PrintStream(d.getOutputStream()); + PrintStream out = new PrintStream(w.getOutputStream()); out.println(CHALLENGE); out.flush(); System.out.println("Sent challenge: " + CHALLENGE); - Scanner in = new Scanner(d.getInputStream()); + Scanner in = new Scanner(r.getInputStream()); if(in.hasNextLine()) { String response = in.nextLine(); System.out.println("Received response: " + response); @@ -38,11 +42,13 @@ abstract class DuplexTest { } else { System.out.println("No response"); } - d.dispose(false, true); + r.dispose(false, true); + w.dispose(false); } catch(IOException e) { e.printStackTrace(); try { - d.dispose(true, true); + r.dispose(true, true); + w.dispose(true); } catch(IOException e1) { e1.printStackTrace(); } @@ -51,13 +57,16 @@ abstract class DuplexTest { protected void receiveChallengeSendResponse(DuplexTransportConnection d) { assert plugin != null; + TransportConnectionReader r = d.getReader(); + TransportConnectionWriter w = d.getWriter(); try { - Scanner in = new Scanner(d.getInputStream()); + Scanner in = new Scanner(r.getInputStream()); if(in.hasNextLine()) { String challenge = in.nextLine(); System.out.println("Received challenge: " + challenge); if(CHALLENGE.equals(challenge)) { - PrintStream out = new PrintStream(d.getOutputStream()); + + PrintStream out = new PrintStream(w.getOutputStream()); out.println(RESPONSE); out.flush(); System.out.println("Sent response: " + RESPONSE); @@ -67,11 +76,13 @@ abstract class DuplexTest { } else { System.out.println("No challenge"); } - d.dispose(false, true); + r.dispose(false, true); + w.dispose(false); } catch(IOException e) { e.printStackTrace(); try { - d.dispose(true, true); + r.dispose(true, true); + w.dispose(true); } catch(IOException e1) { e1.printStackTrace(); } diff --git a/briar-tests/src/org/briarproject/plugins/file/RemovableDrivePluginTest.java b/briar-tests/src/org/briarproject/plugins/file/RemovableDrivePluginTest.java index b799262185d751e8cc068677e41ce8d6244d2a94..b8e4ee88b24ac4fe00e2a7babdb3c973d32f465e 100644 --- a/briar-tests/src/org/briarproject/plugins/file/RemovableDrivePluginTest.java +++ b/briar-tests/src/org/briarproject/plugins/file/RemovableDrivePluginTest.java @@ -15,8 +15,8 @@ import org.briarproject.BriarTestCase; import org.briarproject.TestFileUtils; import org.briarproject.TestUtils; import org.briarproject.api.ContactId; +import org.briarproject.api.plugins.TransportConnectionWriter; import org.briarproject.api.plugins.simplex.SimplexPluginCallback; -import org.briarproject.api.plugins.simplex.SimplexTransportWriter; import org.briarproject.api.system.FileUtils; import org.briarproject.plugins.ImmediateExecutor; import org.briarproject.plugins.file.RemovableDriveMonitor.Callback; @@ -32,6 +32,7 @@ public class RemovableDrivePluginTest extends BriarTestCase { private final ContactId contactId = new ContactId(234); private final FileUtils fileUtils = new TestFileUtils(); + @Override @Before public void setUp() { testDir.mkdirs(); @@ -253,7 +254,7 @@ public class RemovableDrivePluginTest extends BriarTestCase { fileUtils, callback, finder, monitor, MAX_FRAME_LENGTH, 0); plugin.start(); - SimplexTransportWriter writer = plugin.createWriter(contactId); + TransportConnectionWriter writer = plugin.createWriter(contactId); assertNotNull(writer); // The output file should exist and should be empty File[] files = drive1.listFiles(); @@ -352,6 +353,7 @@ public class RemovableDrivePluginTest extends BriarTestCase { context.assertIsSatisfied(); } + @Override @After public void tearDown() { TestUtils.deleteTestDirectory(testDir); diff --git a/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java b/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java index d008330a28e3e77cd04c19099b521fea5975c35c..1abf9d24402d61938abb3b3df38420968679fafd 100644 --- a/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java +++ b/briar-tests/src/org/briarproject/plugins/modem/ModemPluginTest.java @@ -278,7 +278,8 @@ public class ModemPluginTest extends BriarTestCase { public Object invoke(Invocation invocation) throws Throwable { DuplexTransportConnection conn = (DuplexTransportConnection) invocation.getParameter(1); - conn.dispose(false, true); + conn.getReader().dispose(false, true); + conn.getWriter().dispose(false); invoked.countDown(); return null; } diff --git a/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java b/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java index fcbb30c52bbcc228481eb7d9d2ea1049f6c51f46..f3a321bbcc0242b84c137c9e2cce7512c795c9a8 100644 --- a/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java +++ b/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java @@ -148,7 +148,8 @@ public class LanTcpPluginTest extends BriarTestCase { assertTrue(latch.await(5, SECONDS)); assertFalse(error.get()); // Clean up - d.dispose(false, true); + d.getReader().dispose(false, true); + d.getWriter().dispose(false); ss.close(); plugin.stop(); } diff --git a/briar-tests/src/org/briarproject/transport/OutgoingEncryptionLayerTest.java b/briar-tests/src/org/briarproject/transport/OutgoingEncryptionLayerTest.java index d3a7746a1fa14370e42581ce8249d9d891a09344..93f0a53f9b83e4b81676dbf76a878e6ae5d0bdc0 100644 --- a/briar-tests/src/org/briarproject/transport/OutgoingEncryptionLayerTest.java +++ b/briar-tests/src/org/briarproject/transport/OutgoingEncryptionLayerTest.java @@ -25,8 +25,6 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase { // FIXME: This is an integration test, not a unit test private static final int FRAME_LENGTH = 1024; - private static final int MAX_PAYLOAD_LENGTH = - FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH; private final CryptoComponent crypto; private final AuthenticatedCipher frameCipher; @@ -56,7 +54,7 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase { // Check that the actual tag and ciphertext match what's expected ByteArrayOutputStream out = new ByteArrayOutputStream(); OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, - 10 * FRAME_LENGTH, frameCipher, frameKey, FRAME_LENGTH, tag); + frameCipher, frameKey, FRAME_LENGTH, tag); o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], payloadLength, false); byte[] actual = out.toByteArray(); assertEquals(TAG_LENGTH + FRAME_LENGTH, actual.length); @@ -67,93 +65,14 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase { } @Test - public void testInitiatorClosesConnectionWithoutWriting() throws Exception { + public void testCloseConnectionWithoutWriting() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); // Initiator's constructor OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, - 10 * FRAME_LENGTH, frameCipher, crypto.generateSecretKey(), - FRAME_LENGTH, tag); + frameCipher, crypto.generateSecretKey(), FRAME_LENGTH, tag); // Write an empty final frame without having written any other frames o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], 0, true); // The tag and the empty frame should be written to the output stream assertEquals(TAG_LENGTH + HEADER_LENGTH + MAC_LENGTH, out.size()); } - - @Test - public void testResponderClosesConnectionWithoutWriting() throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - // Responder's constructor - OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, - 10 * FRAME_LENGTH, frameCipher, crypto.generateSecretKey(), - FRAME_LENGTH); - // Write an empty final frame without having written any other frames - o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], 0, true); - // An empty final frame should be written to the output stream - assertEquals(HEADER_LENGTH + MAC_LENGTH, out.size()); - } - - @Test - public void testRemainingCapacityWithTag() throws Exception { - int MAX_PAYLOAD_LENGTH = FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - // Initiator's constructor - OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, - 10 * FRAME_LENGTH, frameCipher, crypto.generateSecretKey(), - FRAME_LENGTH, tag); - // There should be space for nine full frames and one partial frame - byte[] frame = new byte[FRAME_LENGTH - MAC_LENGTH]; - assertEquals(10 * MAX_PAYLOAD_LENGTH - TAG_LENGTH, - o.getRemainingCapacity()); - // Write nine frames, each containing a partial payload - for(int i = 0; i < 9; i++) { - o.writeFrame(frame, 123, false); - assertEquals((9 - i) * MAX_PAYLOAD_LENGTH - TAG_LENGTH, - o.getRemainingCapacity()); - } - // Write the final frame, which will not be padded - o.writeFrame(frame, 123, true); - int finalFrameLength = HEADER_LENGTH + 123 + MAC_LENGTH; - assertEquals(MAX_PAYLOAD_LENGTH - TAG_LENGTH - finalFrameLength, - o.getRemainingCapacity()); - } - - @Test - public void testRemainingCapacityWithoutTag() throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - // Responder's constructor - OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, - 10 * FRAME_LENGTH, frameCipher, crypto.generateSecretKey(), - FRAME_LENGTH); - // There should be space for ten full frames - assertEquals(10 * MAX_PAYLOAD_LENGTH, o.getRemainingCapacity()); - // Write nine frames, each containing a partial payload - byte[] frame = new byte[FRAME_LENGTH - MAC_LENGTH]; - for(int i = 0; i < 9; i++) { - o.writeFrame(frame, 123, false); - assertEquals((9 - i) * MAX_PAYLOAD_LENGTH, - o.getRemainingCapacity()); - } - // Write the final frame, which will not be padded - o.writeFrame(frame, 123, true); - int finalFrameLength = HEADER_LENGTH + 123 + MAC_LENGTH; - assertEquals(MAX_PAYLOAD_LENGTH - finalFrameLength, - o.getRemainingCapacity()); - } - - @Test - public void testRemainingCapacityLimitedByFrameNumbers() throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - // The connection has plenty of space so we're limited by frame numbers - OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, - Long.MAX_VALUE, frameCipher, crypto.generateSecretKey(), - FRAME_LENGTH); - // There should be enough frame numbers for 2^32 frames - assertEquals((1L << 32) * MAX_PAYLOAD_LENGTH, o.getRemainingCapacity()); - // Write a frame containing a partial payload - byte[] frame = new byte[FRAME_LENGTH - MAC_LENGTH]; - o.writeFrame(frame, 123, false); - // There should be enough frame numbers for 2^32 - 1 frames - assertEquals(((1L << 32) - 1) * MAX_PAYLOAD_LENGTH, - o.getRemainingCapacity()); - } } diff --git a/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java b/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java index 742e2074128fd4602ecb54fbf03ffa7b9b25363e..19153db75a48546db0f4f12a6c827568ffd7511c 100644 --- a/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java +++ b/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java @@ -1,12 +1,11 @@ package org.briarproject.transport; -import static org.briarproject.api.messaging.MessagingConstants.MAX_PACKET_LENGTH; -import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH; -import static org.briarproject.api.transport.TransportConstants.MIN_STREAM_LENGTH; +import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; import static org.junit.Assert.assertArrayEquals; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Random; @@ -14,13 +13,9 @@ import java.util.Random; import org.briarproject.BriarTestCase; import org.briarproject.TestLifecycleModule; import org.briarproject.TestSystemModule; -import org.briarproject.api.ContactId; -import org.briarproject.api.TransportId; import org.briarproject.api.crypto.AuthenticatedCipher; import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.SecretKey; -import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamWriter; import org.briarproject.api.transport.StreamWriterFactory; import org.briarproject.crypto.CryptoModule; import org.junit.Test; @@ -35,13 +30,10 @@ public class TransportIntegrationTest extends BriarTestCase { private final int FRAME_LENGTH = 2048; private final CryptoComponent crypto; - private final StreamWriterFactory streamWriterFactory; - private final ContactId contactId; - private final TransportId transportId; private final AuthenticatedCipher frameCipher; private final Random random; private final byte[] secret; - private final SecretKey frameKey; + private final SecretKey tagKey, frameKey; public TransportIntegrationTest() { Module testModule = new AbstractModule() { @@ -54,15 +46,13 @@ public class TransportIntegrationTest extends BriarTestCase { Injector i = Guice.createInjector(testModule, new CryptoModule(), new TestLifecycleModule(), new TestSystemModule()); crypto = i.getInstance(CryptoComponent.class); - streamWriterFactory = i.getInstance(StreamWriterFactory.class); - contactId = new ContactId(234); - transportId = new TransportId("id"); frameCipher = crypto.getFrameCipher(); random = new Random(); // Since we're sending frames to ourselves, we only need outgoing keys secret = new byte[32]; random.nextBytes(secret); - frameKey = crypto.deriveFrameKey(secret, 0, true, true); + tagKey = crypto.deriveTagKey(secret, true); + frameKey = crypto.deriveFrameKey(secret, 0, true); } @Test @@ -76,6 +66,9 @@ public class TransportIntegrationTest extends BriarTestCase { } private void testWriteAndRead(boolean initiator) throws Exception { + // Encode the tag + byte[] tag = new byte[TAG_LENGTH]; + crypto.encodeTag(tag, tagKey, 0); // Generate two random frames byte[] frame = new byte[1234]; random.nextBytes(frame); @@ -83,87 +76,46 @@ public class TransportIntegrationTest extends BriarTestCase { random.nextBytes(frame1); // Copy the frame key - the copy will be erased SecretKey frameCopy = frameKey.copy(); - // Write the frames + // Write the tag and the frames ByteArrayOutputStream out = new ByteArrayOutputStream(); - FrameWriter encryptionOut = new OutgoingEncryptionLayer(out, - Long.MAX_VALUE, frameCipher, frameCopy, FRAME_LENGTH); - StreamWriterImpl writer = new StreamWriterImpl(encryptionOut, + FrameWriter frameWriter = new OutgoingEncryptionLayer(out, + frameCipher, frameCopy, FRAME_LENGTH, tag); + StreamWriterImpl streamWriter = new StreamWriterImpl(frameWriter, FRAME_LENGTH); - OutputStream out1 = writer.getOutputStream(); + OutputStream out1 = streamWriter.getOutputStream(); out1.write(frame); out1.flush(); out1.write(frame1); out1.flush(); byte[] output = out.toByteArray(); - assertEquals(FRAME_LENGTH * 2, output.length); - // Read the tag and the frames back + assertEquals(TAG_LENGTH + FRAME_LENGTH * 2, output.length); + // Read the tag back ByteArrayInputStream in = new ByteArrayInputStream(output); - FrameReader encryptionIn = new IncomingEncryptionLayer(in, frameCipher, + byte[] recoveredTag = new byte[tag.length]; + read(in, recoveredTag); + assertArrayEquals(tag, recoveredTag); + // Read the frames back + FrameReader frameReader = new IncomingEncryptionLayer(in, frameCipher, frameKey, FRAME_LENGTH); - StreamReaderImpl reader = new StreamReaderImpl(encryptionIn, + StreamReaderImpl streamReader = new StreamReaderImpl(frameReader, FRAME_LENGTH); - InputStream in1 = reader.getInputStream(); - byte[] recovered = new byte[frame.length]; + InputStream in1 = streamReader.getInputStream(); + byte[] recoveredFrame = new byte[frame.length]; + read(in1, recoveredFrame); + assertArrayEquals(frame, recoveredFrame); + byte[] recoveredFrame1 = new byte[frame1.length]; + read(in1, recoveredFrame1); + assertArrayEquals(frame1, recoveredFrame1); + streamWriter.close(); + streamReader.close(); + } + + private void read(InputStream in, byte[] dest) throws IOException { int offset = 0; - while(offset < recovered.length) { - int read = in1.read(recovered, offset, recovered.length - offset); - if(read == -1) break; - offset += read; - } - assertEquals(recovered.length, offset); - assertArrayEquals(frame, recovered); - byte[] recovered1 = new byte[frame1.length]; - offset = 0; - while(offset < recovered1.length) { - int read = in1.read(recovered1, offset, recovered1.length - offset); + while(offset < dest.length) { + int read = in.read(dest, offset, dest.length - offset); if(read == -1) break; offset += read; } - assertEquals(recovered1.length, offset); - assertArrayEquals(frame1, recovered1); - writer.close(); - reader.close(); - } - - @Test - public void testOverheadWithTag() throws Exception { - ByteArrayOutputStream out = - new ByteArrayOutputStream(MIN_STREAM_LENGTH); - StreamContext ctx = new StreamContext(contactId, transportId, - secret, 0, true); - StreamWriter w = streamWriterFactory.createStreamWriter(out, - MAX_FRAME_LENGTH, MIN_STREAM_LENGTH, ctx, false, true); - // Check that the connection writer thinks there's room for a packet - long capacity = w.getRemainingCapacity(); - assertTrue(capacity > MAX_PACKET_LENGTH); - assertTrue(capacity < MIN_STREAM_LENGTH); - // Check that there really is room for a packet - byte[] payload = new byte[MAX_PACKET_LENGTH]; - w.getOutputStream().write(payload); - w.getOutputStream().close(); - long used = out.size(); - assertTrue(used > MAX_PACKET_LENGTH); - assertTrue(used <= MIN_STREAM_LENGTH); - } - - @Test - public void testOverheadWithoutTag() throws Exception { - ByteArrayOutputStream out = - new ByteArrayOutputStream(MIN_STREAM_LENGTH); - StreamContext ctx = new StreamContext(contactId, transportId, - secret, 0, true); - StreamWriter w = streamWriterFactory.createStreamWriter(out, - MAX_FRAME_LENGTH, MIN_STREAM_LENGTH, ctx, false, false); - // Check that the connection writer thinks there's room for a packet - long capacity = w.getRemainingCapacity(); - assertTrue(capacity > MAX_PACKET_LENGTH); - assertTrue(capacity < MIN_STREAM_LENGTH); - // Check that there really is room for a packet - byte[] payload = new byte[MAX_PACKET_LENGTH]; - w.getOutputStream().write(payload); - w.getOutputStream().close(); - long used = out.size(); - assertTrue(used > MAX_PACKET_LENGTH); - assertTrue(used <= MIN_STREAM_LENGTH); } }