mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-18 21:59:54 +01:00
Massive refactoring to merge handling of simplex and duplex connections.
This commit is contained in:
@@ -3,8 +3,11 @@ package org.briarproject.plugins.droidtooth;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import org.briarproject.api.plugins.Plugin;
|
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 org.briarproject.api.plugins.duplex.DuplexTransportConnection;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothSocket;
|
import android.bluetooth.BluetoothSocket;
|
||||||
@@ -13,30 +16,69 @@ class DroidtoothTransportConnection implements DuplexTransportConnection {
|
|||||||
|
|
||||||
private final Plugin plugin;
|
private final Plugin plugin;
|
||||||
private final BluetoothSocket socket;
|
private final BluetoothSocket socket;
|
||||||
|
private final Reader reader;
|
||||||
|
private final Writer writer;
|
||||||
|
private final AtomicBoolean halfClosed, closed;
|
||||||
|
|
||||||
DroidtoothTransportConnection(Plugin plugin, BluetoothSocket socket) {
|
DroidtoothTransportConnection(Plugin plugin, BluetoothSocket socket) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
|
reader = new Reader();
|
||||||
|
writer = new Writer();
|
||||||
|
halfClosed = new AtomicBoolean(false);
|
||||||
|
closed = new AtomicBoolean(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxFrameLength() {
|
public TransportConnectionReader getReader() {
|
||||||
return plugin.getMaxFrameLength();
|
return reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMaxLatency() {
|
public TransportConnectionWriter getWriter() {
|
||||||
return plugin.getMaxLatency();
|
return writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream() throws IOException {
|
private class Reader implements TransportConnectionReader {
|
||||||
return socket.getInputStream();
|
|
||||||
|
public int getMaxFrameLength() {
|
||||||
|
return plugin.getMaxFrameLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMaxLatency() {
|
||||||
|
return plugin.getMaxLatency();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 OutputStream getOutputStream() throws IOException {
|
private class Writer implements TransportConnectionWriter {
|
||||||
return socket.getOutputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispose(boolean exception, boolean recognised)
|
public int getMaxFrameLength() {
|
||||||
throws IOException {
|
return plugin.getMaxFrameLength();
|
||||||
socket.close();
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,38 +4,80 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import org.briarproject.api.plugins.Plugin;
|
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 org.briarproject.api.plugins.duplex.DuplexTransportConnection;
|
||||||
|
|
||||||
class TorTransportConnection implements DuplexTransportConnection {
|
class TorTransportConnection implements DuplexTransportConnection {
|
||||||
|
|
||||||
private final Plugin plugin;
|
private final Plugin plugin;
|
||||||
private final Socket socket;
|
private final Socket socket;
|
||||||
|
private final Reader reader;
|
||||||
|
private final Writer writer;
|
||||||
|
private final AtomicBoolean halfClosed, closed;
|
||||||
|
|
||||||
TorTransportConnection(Plugin plugin, Socket socket) {
|
TorTransportConnection(Plugin plugin, Socket socket) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
|
reader = new Reader();
|
||||||
|
writer = new Writer();
|
||||||
|
halfClosed = new AtomicBoolean(false);
|
||||||
|
closed = new AtomicBoolean(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxFrameLength() {
|
public TransportConnectionReader getReader() {
|
||||||
return plugin.getMaxFrameLength();
|
return reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMaxLatency() {
|
public TransportConnectionWriter getWriter() {
|
||||||
return plugin.getMaxLatency();
|
return writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream() throws IOException {
|
private class Reader implements TransportConnectionReader {
|
||||||
return socket.getInputStream();
|
|
||||||
|
public int getMaxFrameLength() {
|
||||||
|
return plugin.getMaxFrameLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMaxLatency() {
|
||||||
|
return plugin.getMaxLatency();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 OutputStream getOutputStream() throws IOException {
|
private class Writer implements TransportConnectionWriter {
|
||||||
return socket.getOutputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispose(boolean exception, boolean recognised)
|
public int getMaxFrameLength() {
|
||||||
throws IOException {
|
return plugin.getMaxFrameLength();
|
||||||
socket.close();
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,13 +72,10 @@ public interface CryptoComponent {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives a frame key from the given temporary secret and stream number.
|
* 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.
|
* 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,
|
SecretKey deriveFrameKey(byte[] secret, long streamNumber, boolean alice);
|
||||||
boolean initiator);
|
|
||||||
|
|
||||||
/** Returns a cipher for encrypting and authenticating frames. */
|
/** Returns a cipher for encrypting and authenticating frames. */
|
||||||
AuthenticatedCipher getFrameCipher();
|
AuthenticatedCipher getFrameCipher();
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.briarproject.api.plugins.duplex;
|
package org.briarproject.api.plugins.duplex;
|
||||||
|
|
||||||
import java.io.IOException;
|
import org.briarproject.api.plugins.TransportConnectionReader;
|
||||||
import java.io.InputStream;
|
import org.briarproject.api.plugins.TransportConnectionWriter;
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface for reading and writing data over a duplex transport. The
|
* An interface for reading and writing data over a duplex transport. The
|
||||||
@@ -11,23 +10,11 @@ import java.io.OutputStream;
|
|||||||
*/
|
*/
|
||||||
public interface DuplexTransportConnection {
|
public interface DuplexTransportConnection {
|
||||||
|
|
||||||
/** Returns the maximum frame length of the transport in bytes. */
|
/** Returns a {@link org.briarproject.api.plugins.TransportConnectionReader
|
||||||
int getMaxFrameLength();
|
* TransportConnectionReader} for reading from the connection. */
|
||||||
|
TransportConnectionReader getReader();
|
||||||
|
|
||||||
/** Returns the maximum latency of the transport in milliseconds. */
|
/** Returns a {@link org.briarproject.api.plugins.TransportConnectionWriter
|
||||||
long getMaxLatency();
|
* TransportConnectionWriter} for writing to the connection. */
|
||||||
|
TransportConnectionWriter getWriter();
|
||||||
/** 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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package org.briarproject.api.plugins.simplex;
|
|||||||
|
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
import org.briarproject.api.plugins.Plugin;
|
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. */
|
/** An interface for transport plugins that support simplex communication. */
|
||||||
public interface SimplexPlugin extends Plugin {
|
public interface SimplexPlugin extends Plugin {
|
||||||
@@ -11,12 +13,12 @@ public interface SimplexPlugin extends Plugin {
|
|||||||
* current transport and configuration properties. Returns null if a reader
|
* current transport and configuration properties. Returns null if a reader
|
||||||
* could not be created.
|
* 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
|
* Attempts to create and return a writer for the given contact using the
|
||||||
* current transport and configuration properties. Returns null if a writer
|
* current transport and configuration properties. Returns null if a writer
|
||||||
* could not be created.
|
* could not be created.
|
||||||
*/
|
*/
|
||||||
SimplexTransportWriter createWriter(ContactId c);
|
TransportConnectionWriter createWriter(ContactId c);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package org.briarproject.api.plugins.simplex;
|
|||||||
|
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
import org.briarproject.api.plugins.PluginCallback;
|
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
|
* 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 {
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -2,18 +2,18 @@ package org.briarproject.api.transport;
|
|||||||
|
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
import org.briarproject.api.TransportId;
|
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.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.api.plugins.simplex.SimplexTransportReader;
|
|
||||||
import org.briarproject.api.plugins.simplex.SimplexTransportWriter;
|
|
||||||
|
|
||||||
public interface ConnectionDispatcher {
|
public interface ConnectionDispatcher {
|
||||||
|
|
||||||
void dispatchIncomingConnection(TransportId t, SimplexTransportReader r);
|
void dispatchIncomingConnection(TransportId t, TransportConnectionReader r);
|
||||||
|
|
||||||
void dispatchIncomingConnection(TransportId t, DuplexTransportConnection d);
|
void dispatchIncomingConnection(TransportId t, DuplexTransportConnection d);
|
||||||
|
|
||||||
void dispatchOutgoingConnection(ContactId c, TransportId t,
|
void dispatchOutgoingConnection(ContactId c, TransportId t,
|
||||||
SimplexTransportWriter w);
|
TransportConnectionWriter w);
|
||||||
|
|
||||||
void dispatchOutgoingConnection(ContactId c, TransportId t,
|
void dispatchOutgoingConnection(ContactId c, TransportId t,
|
||||||
DuplexTransportConnection d);
|
DuplexTransportConnection d);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ public interface StreamReaderFactory {
|
|||||||
|
|
||||||
/** Creates a {@link StreamReader} for a transport connection. */
|
/** Creates a {@link StreamReader} for a transport connection. */
|
||||||
StreamReader createStreamReader(InputStream in, int maxFrameLength,
|
StreamReader createStreamReader(InputStream in, int maxFrameLength,
|
||||||
StreamContext ctx, boolean incoming, boolean initiator);
|
StreamContext ctx);
|
||||||
|
|
||||||
/** Creates a {@link StreamReader} for an invitation connection. */
|
/** Creates a {@link StreamReader} for an invitation connection. */
|
||||||
StreamReader createInvitationStreamReader(InputStream in,
|
StreamReader createInvitationStreamReader(InputStream in,
|
||||||
|
|||||||
@@ -10,10 +10,4 @@ public interface StreamWriter {
|
|||||||
* be written.
|
* be written.
|
||||||
*/
|
*/
|
||||||
OutputStream getOutputStream();
|
OutputStream getOutputStream();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the maximum number of bytes that can be written to the output
|
|
||||||
* stream.
|
|
||||||
*/
|
|
||||||
long getRemainingCapacity();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ public interface StreamWriterFactory {
|
|||||||
|
|
||||||
/** Creates a {@link StreamWriter} for a transport connection. */
|
/** Creates a {@link StreamWriter} for a transport connection. */
|
||||||
StreamWriter createStreamWriter(OutputStream out, int maxFrameLength,
|
StreamWriter createStreamWriter(OutputStream out, int maxFrameLength,
|
||||||
long capacity, StreamContext ctx, boolean incoming,
|
StreamContext ctx);
|
||||||
boolean initiator);
|
|
||||||
|
|
||||||
/** Creates a {@link StreamWriter} for an invitation connection. */
|
/** Creates a {@link StreamWriter} for an invitation connection. */
|
||||||
StreamWriter createInvitationStreamWriter(OutputStream out,
|
StreamWriter createInvitationStreamWriter(OutputStream out,
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ import org.briarproject.api.ContactId;
|
|||||||
import org.briarproject.api.TransportId;
|
import org.briarproject.api.TransportId;
|
||||||
import org.briarproject.api.db.DbException;
|
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 {
|
public interface TagRecogniser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link StreamContext} for reading from the stream with the
|
* Looks up the given tag and returns a {@link StreamContext} for reading
|
||||||
* given tag if the tag was expected, or null if the tag was unexpected.
|
* from the stream if the tag was expected, or null if the tag was
|
||||||
|
* unexpected.
|
||||||
*/
|
*/
|
||||||
StreamContext recogniseTag(TransportId t, byte[] tag) throws DbException;
|
StreamContext recogniseTag(TransportId t, byte[] tag) throws DbException;
|
||||||
|
|
||||||
|
|||||||
@@ -76,14 +76,10 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
// Labels for key derivation
|
// Labels for key derivation
|
||||||
private static final byte[] A_TAG = { 'A', '_', 'T', 'A', 'G', '\0' };
|
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[] B_TAG = { 'B', '_', 'T', 'A', 'G', '\0' };
|
||||||
private static final byte[] A_FRAME_A =
|
private static final byte[] A_FRAME =
|
||||||
{ 'A', '_', 'F', 'R', 'A', 'M', 'E', '_', 'A', '\0' };
|
{ 'A', '_', 'F', 'R', 'A', 'M', 'E', '\0' };
|
||||||
private static final byte[] A_FRAME_B =
|
private static final byte[] B_FRAME =
|
||||||
{ 'A', '_', 'F', 'R', 'A', 'M', 'E', '_', 'B', '\0' };
|
{ 'B', '_', 'F', 'R', 'A', 'M', 'E', '\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' };
|
|
||||||
// Blank secret for argument validation
|
// Blank secret for argument validation
|
||||||
private static final byte[] BLANK_SECRET = new byte[CIPHER_KEY_BYTES];
|
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,
|
public SecretKey deriveFrameKey(byte[] secret, long streamNumber,
|
||||||
boolean alice, boolean initiator) {
|
boolean alice) {
|
||||||
if(secret.length != CIPHER_KEY_BYTES)
|
if(secret.length != CIPHER_KEY_BYTES)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
if(Arrays.equals(secret, BLANK_SECRET))
|
if(Arrays.equals(secret, BLANK_SECRET))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
if(streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
if(streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
if(alice) {
|
if(alice) return deriveKey(secret, A_FRAME, streamNumber);
|
||||||
if(initiator) return deriveKey(secret, A_FRAME_A, streamNumber);
|
else return deriveKey(secret, B_FRAME, 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SecretKey deriveKey(byte[] secret, byte[] label, long context) {
|
private SecretKey deriveKey(byte[] secret, byte[] label, long context) {
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ class AliceConnector extends Connector {
|
|||||||
Writer w;
|
Writer w;
|
||||||
byte[] secret;
|
byte[] secret;
|
||||||
try {
|
try {
|
||||||
in = conn.getInputStream();
|
in = conn.getReader().getInputStream();
|
||||||
out = conn.getOutputStream();
|
out = conn.getWriter().getOutputStream();
|
||||||
r = readerFactory.createReader(in);
|
r = readerFactory.createReader(in);
|
||||||
w = writerFactory.createWriter(out);
|
w = writerFactory.createWriter(out);
|
||||||
// Alice goes first
|
// Alice goes first
|
||||||
@@ -130,7 +130,7 @@ class AliceConnector extends Connector {
|
|||||||
// Confirmation succeeded - upgrade to a secure connection
|
// Confirmation succeeded - upgrade to a secure connection
|
||||||
if(LOG.isLoggable(INFO))
|
if(LOG.isLoggable(INFO))
|
||||||
LOG.info(pluginName + " confirmation succeeded");
|
LOG.info(pluginName + " confirmation succeeded");
|
||||||
int maxFrameLength = conn.getMaxFrameLength();
|
int maxFrameLength = conn.getReader().getMaxFrameLength();
|
||||||
StreamReader streamReader =
|
StreamReader streamReader =
|
||||||
streamReaderFactory.createInvitationStreamReader(in,
|
streamReaderFactory.createInvitationStreamReader(in,
|
||||||
maxFrameLength, secret, false);
|
maxFrameLength, secret, false);
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ class BobConnector extends Connector {
|
|||||||
Writer w;
|
Writer w;
|
||||||
byte[] secret;
|
byte[] secret;
|
||||||
try {
|
try {
|
||||||
in = conn.getInputStream();
|
in = conn.getReader().getInputStream();
|
||||||
out = conn.getOutputStream();
|
out = conn.getWriter().getOutputStream();
|
||||||
r = readerFactory.createReader(in);
|
r = readerFactory.createReader(in);
|
||||||
w = writerFactory.createWriter(out);
|
w = writerFactory.createWriter(out);
|
||||||
// Alice goes first
|
// Alice goes first
|
||||||
@@ -130,7 +130,7 @@ class BobConnector extends Connector {
|
|||||||
// Confirmation succeeded - upgrade to a secure connection
|
// Confirmation succeeded - upgrade to a secure connection
|
||||||
if(LOG.isLoggable(INFO))
|
if(LOG.isLoggable(INFO))
|
||||||
LOG.info(pluginName + " confirmation succeeded");
|
LOG.info(pluginName + " confirmation succeeded");
|
||||||
int maxFrameLength = conn.getMaxFrameLength();
|
int maxFrameLength = conn.getReader().getMaxFrameLength();
|
||||||
StreamReader streamReader =
|
StreamReader streamReader =
|
||||||
streamReaderFactory.createInvitationStreamReader(in,
|
streamReaderFactory.createInvitationStreamReader(in,
|
||||||
maxFrameLength, secret, true);
|
maxFrameLength, secret, true);
|
||||||
|
|||||||
@@ -311,7 +311,8 @@ abstract class Connector extends Thread {
|
|||||||
boolean exception) {
|
boolean exception) {
|
||||||
try {
|
try {
|
||||||
LOG.info("Closing connection");
|
LOG.info("Closing connection");
|
||||||
conn.dispose(exception, true);
|
conn.getReader().dispose(exception, true);
|
||||||
|
conn.getWriter().dispose(exception);
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.messaging.simplex;
|
package org.briarproject.messaging;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
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.ContactId;
|
||||||
import org.briarproject.api.FormatException;
|
import org.briarproject.api.FormatException;
|
||||||
import org.briarproject.api.TransportId;
|
|
||||||
import org.briarproject.api.db.DatabaseComponent;
|
import org.briarproject.api.db.DatabaseComponent;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.messaging.Ack;
|
import org.briarproject.api.messaging.Ack;
|
||||||
import org.briarproject.api.messaging.Message;
|
import org.briarproject.api.messaging.Message;
|
||||||
import org.briarproject.api.messaging.MessageVerifier;
|
import org.briarproject.api.messaging.MessageVerifier;
|
||||||
|
import org.briarproject.api.messaging.MessagingSession;
|
||||||
import org.briarproject.api.messaging.PacketReader;
|
import org.briarproject.api.messaging.PacketReader;
|
||||||
import org.briarproject.api.messaging.PacketReaderFactory;
|
import org.briarproject.api.messaging.PacketReaderFactory;
|
||||||
import org.briarproject.api.messaging.RetentionAck;
|
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.TransportAck;
|
||||||
import org.briarproject.api.messaging.TransportUpdate;
|
import org.briarproject.api.messaging.TransportUpdate;
|
||||||
import org.briarproject.api.messaging.UnverifiedMessage;
|
import org.briarproject.api.messaging.UnverifiedMessage;
|
||||||
import org.briarproject.api.plugins.simplex.SimplexTransportReader;
|
import org.briarproject.api.plugins.TransportConnectionReader;
|
||||||
import org.briarproject.api.transport.ConnectionRegistry;
|
|
||||||
import org.briarproject.api.transport.StreamContext;
|
import org.briarproject.api.transport.StreamContext;
|
||||||
import org.briarproject.api.transport.StreamReader;
|
import org.briarproject.api.transport.StreamReader;
|
||||||
import org.briarproject.api.transport.StreamReaderFactory;
|
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 =
|
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 Executor dbExecutor, cryptoExecutor;
|
||||||
private final MessageVerifier messageVerifier;
|
private final MessageVerifier messageVerifier;
|
||||||
private final DatabaseComponent db;
|
private final StreamReaderFactory streamReaderFactory;
|
||||||
private final ConnectionRegistry connRegistry;
|
|
||||||
private final StreamReaderFactory connReaderFactory;
|
|
||||||
private final PacketReaderFactory packetReaderFactory;
|
private final PacketReaderFactory packetReaderFactory;
|
||||||
private final StreamContext ctx;
|
private final StreamContext ctx;
|
||||||
private final SimplexTransportReader transport;
|
private final TransportConnectionReader transportReader;
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
private final TransportId transportId;
|
|
||||||
|
|
||||||
IncomingSimplexConnection(Executor dbExecutor, Executor cryptoExecutor,
|
private volatile boolean interrupted = false;
|
||||||
MessageVerifier messageVerifier, DatabaseComponent db,
|
|
||||||
ConnectionRegistry connRegistry,
|
IncomingSession(DatabaseComponent db, Executor dbExecutor,
|
||||||
StreamReaderFactory connReaderFactory,
|
Executor cryptoExecutor, MessageVerifier messageVerifier,
|
||||||
|
StreamReaderFactory streamReaderFactory,
|
||||||
PacketReaderFactory packetReaderFactory, StreamContext ctx,
|
PacketReaderFactory packetReaderFactory, StreamContext ctx,
|
||||||
SimplexTransportReader transport) {
|
TransportConnectionReader transportReader) {
|
||||||
|
this.db = db;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.cryptoExecutor = cryptoExecutor;
|
this.cryptoExecutor = cryptoExecutor;
|
||||||
this.messageVerifier = messageVerifier;
|
this.messageVerifier = messageVerifier;
|
||||||
this.db = db;
|
this.streamReaderFactory = streamReaderFactory;
|
||||||
this.connRegistry = connRegistry;
|
|
||||||
this.connReaderFactory = connReaderFactory;
|
|
||||||
this.packetReaderFactory = packetReaderFactory;
|
this.packetReaderFactory = packetReaderFactory;
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.transport = transport;
|
this.transportReader = transportReader;
|
||||||
contactId = ctx.getContactId();
|
contactId = ctx.getContactId();
|
||||||
transportId = ctx.getTransportId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void read() {
|
public void run() throws IOException {
|
||||||
connRegistry.registerConnection(contactId, transportId);
|
InputStream in = transportReader.getInputStream();
|
||||||
try {
|
int maxFrameLength = transportReader.getMaxFrameLength();
|
||||||
InputStream in = transport.getInputStream();
|
StreamReader streamReader = streamReaderFactory.createStreamReader(in,
|
||||||
int maxFrameLength = transport.getMaxFrameLength();
|
maxFrameLength, ctx);
|
||||||
StreamReader conn = connReaderFactory.createStreamReader(in,
|
in = streamReader.getInputStream();
|
||||||
maxFrameLength, ctx, true, true);
|
PacketReader packetReader = packetReaderFactory.createPacketReader(in);
|
||||||
in = conn.getInputStream();
|
// Read packets until interrupted or EOF
|
||||||
PacketReader reader = packetReaderFactory.createPacketReader(in);
|
while(!interrupted && !packetReader.eof()) {
|
||||||
// Read packets until EOF
|
if(packetReader.hasAck()) {
|
||||||
while(!reader.eof()) {
|
Ack a = packetReader.readAck();
|
||||||
if(reader.hasAck()) {
|
dbExecutor.execute(new ReceiveAck(a));
|
||||||
Ack a = reader.readAck();
|
} else if(packetReader.hasMessage()) {
|
||||||
dbExecutor.execute(new ReceiveAck(a));
|
UnverifiedMessage m = packetReader.readMessage();
|
||||||
} else if(reader.hasMessage()) {
|
cryptoExecutor.execute(new VerifyMessage(m));
|
||||||
UnverifiedMessage m = reader.readMessage();
|
} else if(packetReader.hasRetentionAck()) {
|
||||||
cryptoExecutor.execute(new VerifyMessage(m));
|
RetentionAck a = packetReader.readRetentionAck();
|
||||||
} else if(reader.hasRetentionAck()) {
|
dbExecutor.execute(new ReceiveRetentionAck(a));
|
||||||
RetentionAck a = reader.readRetentionAck();
|
} else if(packetReader.hasRetentionUpdate()) {
|
||||||
dbExecutor.execute(new ReceiveRetentionAck(a));
|
RetentionUpdate u = packetReader.readRetentionUpdate();
|
||||||
} else if(reader.hasRetentionUpdate()) {
|
dbExecutor.execute(new ReceiveRetentionUpdate(u));
|
||||||
RetentionUpdate u = reader.readRetentionUpdate();
|
} else if(packetReader.hasSubscriptionAck()) {
|
||||||
dbExecutor.execute(new ReceiveRetentionUpdate(u));
|
SubscriptionAck a = packetReader.readSubscriptionAck();
|
||||||
} else if(reader.hasSubscriptionAck()) {
|
dbExecutor.execute(new ReceiveSubscriptionAck(a));
|
||||||
SubscriptionAck a = reader.readSubscriptionAck();
|
} else if(packetReader.hasSubscriptionUpdate()) {
|
||||||
dbExecutor.execute(new ReceiveSubscriptionAck(a));
|
SubscriptionUpdate u = packetReader.readSubscriptionUpdate();
|
||||||
} else if(reader.hasSubscriptionUpdate()) {
|
dbExecutor.execute(new ReceiveSubscriptionUpdate(u));
|
||||||
SubscriptionUpdate u = reader.readSubscriptionUpdate();
|
} else if(packetReader.hasTransportAck()) {
|
||||||
dbExecutor.execute(new ReceiveSubscriptionUpdate(u));
|
TransportAck a = packetReader.readTransportAck();
|
||||||
} else if(reader.hasTransportAck()) {
|
dbExecutor.execute(new ReceiveTransportAck(a));
|
||||||
TransportAck a = reader.readTransportAck();
|
} else if(packetReader.hasTransportUpdate()) {
|
||||||
dbExecutor.execute(new ReceiveTransportAck(a));
|
TransportUpdate u = packetReader.readTransportUpdate();
|
||||||
} else if(reader.hasTransportUpdate()) {
|
dbExecutor.execute(new ReceiveTransportUpdate(u));
|
||||||
TransportUpdate u = reader.readTransportUpdate();
|
} else {
|
||||||
dbExecutor.execute(new ReceiveTransportUpdate(u));
|
throw new FormatException();
|
||||||
} 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) {
|
public void interrupt() {
|
||||||
ByteUtils.erase(ctx.getSecret());
|
// This won't interrupt a blocking read, but the read will throw an
|
||||||
try {
|
// exception when the transport connection is closed
|
||||||
transport.dispose(exception, recognised);
|
interrupted = true;
|
||||||
} catch(IOException e) {
|
|
||||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ReceiveAck implements Runnable {
|
private class ReceiveAck implements Runnable {
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.briarproject.messaging;
|
package org.briarproject.messaging;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.briarproject.api.Author;
|
import org.briarproject.api.Author;
|
||||||
import org.briarproject.api.AuthorFactory;
|
import org.briarproject.api.AuthorFactory;
|
||||||
import org.briarproject.api.crypto.CryptoComponent;
|
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.MessageVerifier;
|
||||||
import org.briarproject.api.messaging.PacketReaderFactory;
|
import org.briarproject.api.messaging.PacketReaderFactory;
|
||||||
import org.briarproject.api.messaging.PacketWriterFactory;
|
import org.briarproject.api.messaging.PacketWriterFactory;
|
||||||
|
import org.briarproject.api.messaging.MessagingSessionFactory;
|
||||||
import org.briarproject.api.messaging.SubscriptionUpdate;
|
import org.briarproject.api.messaging.SubscriptionUpdate;
|
||||||
import org.briarproject.api.messaging.UnverifiedMessage;
|
import org.briarproject.api.messaging.UnverifiedMessage;
|
||||||
import org.briarproject.api.serial.StructReader;
|
import org.briarproject.api.serial.StructReader;
|
||||||
@@ -18,6 +21,7 @@ import com.google.inject.Provides;
|
|||||||
|
|
||||||
public class MessagingModule extends AbstractModule {
|
public class MessagingModule extends AbstractModule {
|
||||||
|
|
||||||
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bind(AuthorFactory.class).to(AuthorFactoryImpl.class);
|
bind(AuthorFactory.class).to(AuthorFactoryImpl.class);
|
||||||
bind(GroupFactory.class).to(GroupFactoryImpl.class);
|
bind(GroupFactory.class).to(GroupFactoryImpl.class);
|
||||||
@@ -25,6 +29,8 @@ public class MessagingModule extends AbstractModule {
|
|||||||
bind(MessageVerifier.class).to(MessageVerifierImpl.class);
|
bind(MessageVerifier.class).to(MessageVerifierImpl.class);
|
||||||
bind(PacketReaderFactory.class).to(PacketReaderFactoryImpl.class);
|
bind(PacketReaderFactory.class).to(PacketReaderFactoryImpl.class);
|
||||||
bind(PacketWriterFactory.class).to(PacketWriterFactoryImpl.class);
|
bind(PacketWriterFactory.class).to(PacketWriterFactoryImpl.class);
|
||||||
|
bind(MessagingSessionFactory.class).to(
|
||||||
|
MessagingSessionFactoryImpl.class).in(Singleton.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package org.briarproject.messaging;
|
||||||
|
|
||||||
|
interface ThrowingRunnable<T extends Throwable> {
|
||||||
|
|
||||||
|
public void run() throws T;
|
||||||
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -27,6 +27,8 @@ import org.briarproject.api.lifecycle.IoExecutor;
|
|||||||
import org.briarproject.api.plugins.Plugin;
|
import org.briarproject.api.plugins.Plugin;
|
||||||
import org.briarproject.api.plugins.PluginCallback;
|
import org.briarproject.api.plugins.PluginCallback;
|
||||||
import org.briarproject.api.plugins.PluginManager;
|
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.DuplexPlugin;
|
||||||
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
|
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
|
||||||
import org.briarproject.api.plugins.duplex.DuplexPluginConfig;
|
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.SimplexPluginCallback;
|
||||||
import org.briarproject.api.plugins.simplex.SimplexPluginConfig;
|
import org.briarproject.api.plugins.simplex.SimplexPluginConfig;
|
||||||
import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
|
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.system.Clock;
|
||||||
import org.briarproject.api.transport.ConnectionDispatcher;
|
import org.briarproject.api.transport.ConnectionDispatcher;
|
||||||
import org.briarproject.api.ui.UiCallback;
|
import org.briarproject.api.ui.UiCallback;
|
||||||
@@ -377,11 +377,11 @@ class PluginManagerImpl implements PluginManager {
|
|||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readerCreated(SimplexTransportReader r) {
|
public void readerCreated(TransportConnectionReader r) {
|
||||||
dispatcher.dispatchIncomingConnection(id, r);
|
dispatcher.dispatchIncomingConnection(id, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writerCreated(ContactId c, SimplexTransportWriter w) {
|
public void writerCreated(ContactId c, TransportConnectionWriter w) {
|
||||||
dispatcher.dispatchOutgoingConnection(c, id, w);
|
dispatcher.dispatchOutgoingConnection(c, id, w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ class PollerImpl implements Poller {
|
|||||||
Logger.getLogger(PollerImpl.class.getName());
|
Logger.getLogger(PollerImpl.class.getName());
|
||||||
|
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
private final ConnectionRegistry connRegistry;
|
private final ConnectionRegistry connectionRegistry;
|
||||||
private final Timer timer;
|
private final Timer timer;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
PollerImpl(@IoExecutor Executor ioExecutor, ConnectionRegistry connRegistry,
|
PollerImpl(@IoExecutor Executor ioExecutor,
|
||||||
Timer timer) {
|
ConnectionRegistry connectionRegistry, Timer timer) {
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.connRegistry = connRegistry;
|
this.connectionRegistry = connectionRegistry;
|
||||||
this.timer = timer;
|
this.timer = timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ class PollerImpl implements Poller {
|
|||||||
public void run() {
|
public void run() {
|
||||||
if(LOG.isLoggable(INFO))
|
if(LOG.isLoggable(INFO))
|
||||||
LOG.info("Polling " + p.getClass().getSimpleName());
|
LOG.info("Polling " + p.getClass().getSimpleName());
|
||||||
p.poll(connRegistry.getConnectedContacts(p.getId()));
|
p.poll(connectionRegistry.getConnectedContacts(p.getId()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ import java.util.concurrent.Executor;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.briarproject.api.ContactId;
|
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.SimplexPlugin;
|
||||||
import org.briarproject.api.plugins.simplex.SimplexPluginCallback;
|
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;
|
import org.briarproject.api.system.FileUtils;
|
||||||
|
|
||||||
public abstract class FilePlugin implements SimplexPlugin {
|
public abstract class FilePlugin implements SimplexPlugin {
|
||||||
@@ -60,11 +60,11 @@ public abstract class FilePlugin implements SimplexPlugin {
|
|||||||
return running;
|
return running;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimplexTransportReader createReader(ContactId c) {
|
public TransportConnectionReader createReader(ContactId c) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimplexTransportWriter createWriter(ContactId c) {
|
public TransportConnectionWriter createWriter(ContactId c) {
|
||||||
if(!running) return null;
|
if(!running) return null;
|
||||||
return createWriter(createConnectionFilename());
|
return createWriter(createConnectionFilename());
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ public abstract class FilePlugin implements SimplexPlugin {
|
|||||||
return filename.toLowerCase(Locale.US).matches("[a-z]{8}\\.dat");
|
return filename.toLowerCase(Locale.US).matches("[a-z]{8}\\.dat");
|
||||||
}
|
}
|
||||||
|
|
||||||
private SimplexTransportWriter createWriter(String filename) {
|
private TransportConnectionWriter createWriter(String filename) {
|
||||||
if(!running) return null;
|
if(!running) return null;
|
||||||
File dir = chooseOutputDirectory();
|
File dir = chooseOutputDirectory();
|
||||||
if(dir == null || !dir.exists() || !dir.isDirectory()) return null;
|
if(dir == null || !dir.exists() || !dir.isDirectory()) return null;
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.logging.Logger;
|
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 =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(FileTransportReader.class.getName());
|
Logger.getLogger(FileTransportReader.class.getName());
|
||||||
@@ -28,6 +28,10 @@ class FileTransportReader implements SimplexTransportReader {
|
|||||||
return plugin.getMaxFrameLength();
|
return plugin.getMaxFrameLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getMaxLatency() {
|
||||||
|
return plugin.getMaxLatency();
|
||||||
|
}
|
||||||
|
|
||||||
public InputStream getInputStream() {
|
public InputStream getInputStream() {
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import java.io.IOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.logging.Logger;
|
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 =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(FileTransportWriter.class.getName());
|
Logger.getLogger(FileTransportWriter.class.getName());
|
||||||
@@ -27,10 +27,6 @@ class FileTransportWriter implements SimplexTransportWriter {
|
|||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getCapacity() {
|
|
||||||
return capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxFrameLength() {
|
public int getMaxFrameLength() {
|
||||||
return plugin.getMaxFrameLength();
|
return plugin.getMaxFrameLength();
|
||||||
}
|
}
|
||||||
@@ -39,6 +35,10 @@ class FileTransportWriter implements SimplexTransportWriter {
|
|||||||
return plugin.getMaxLatency();
|
return plugin.getMaxLatency();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getCapacity() {
|
||||||
|
return capacity;
|
||||||
|
}
|
||||||
|
|
||||||
public OutputStream getOutputStream() {
|
public OutputStream getOutputStream() {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,38 +4,80 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import org.briarproject.api.plugins.Plugin;
|
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 org.briarproject.api.plugins.duplex.DuplexTransportConnection;
|
||||||
|
|
||||||
class TcpTransportConnection implements DuplexTransportConnection {
|
class TcpTransportConnection implements DuplexTransportConnection {
|
||||||
|
|
||||||
private final Plugin plugin;
|
private final Plugin plugin;
|
||||||
private final Socket socket;
|
private final Socket socket;
|
||||||
|
private final Reader reader;
|
||||||
|
private final Writer writer;
|
||||||
|
private final AtomicBoolean halfClosed, closed;
|
||||||
|
|
||||||
TcpTransportConnection(Plugin plugin, Socket socket) {
|
TcpTransportConnection(Plugin plugin, Socket socket) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
|
reader = new Reader();
|
||||||
|
writer = new Writer();
|
||||||
|
halfClosed = new AtomicBoolean(false);
|
||||||
|
closed = new AtomicBoolean(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxFrameLength() {
|
public TransportConnectionReader getReader() {
|
||||||
return plugin.getMaxFrameLength();
|
return reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMaxLatency() {
|
public TransportConnectionWriter getWriter() {
|
||||||
return plugin.getMaxLatency();
|
return writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream() throws IOException {
|
private class Reader implements TransportConnectionReader {
|
||||||
return socket.getInputStream();
|
|
||||||
|
public int getMaxFrameLength() {
|
||||||
|
return plugin.getMaxFrameLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMaxLatency() {
|
||||||
|
return plugin.getMaxLatency();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 OutputStream getOutputStream() throws IOException {
|
private class Writer implements TransportConnectionWriter {
|
||||||
return socket.getOutputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispose(boolean exception, boolean recognised)
|
public int getMaxFrameLength() {
|
||||||
throws IOException {
|
return plugin.getMaxFrameLength();
|
||||||
socket.close();
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,14 +13,16 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
import org.briarproject.api.TransportId;
|
import org.briarproject.api.TransportId;
|
||||||
|
import org.briarproject.api.crypto.KeyManager;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.lifecycle.IoExecutor;
|
import org.briarproject.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.api.messaging.duplex.DuplexConnectionFactory;
|
import org.briarproject.api.messaging.MessagingSession;
|
||||||
import org.briarproject.api.messaging.simplex.SimplexConnectionFactory;
|
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.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.ConnectionDispatcher;
|
||||||
|
import org.briarproject.api.transport.ConnectionRegistry;
|
||||||
import org.briarproject.api.transport.StreamContext;
|
import org.briarproject.api.transport.StreamContext;
|
||||||
import org.briarproject.api.transport.TagRecogniser;
|
import org.briarproject.api.transport.TagRecogniser;
|
||||||
|
|
||||||
@@ -30,132 +32,280 @@ class ConnectionDispatcherImpl implements ConnectionDispatcher {
|
|||||||
Logger.getLogger(ConnectionDispatcherImpl.class.getName());
|
Logger.getLogger(ConnectionDispatcherImpl.class.getName());
|
||||||
|
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
|
private final KeyManager keyManager;
|
||||||
private final TagRecogniser tagRecogniser;
|
private final TagRecogniser tagRecogniser;
|
||||||
private final SimplexConnectionFactory simplexConnFactory;
|
private final MessagingSessionFactory messagingSessionFactory;
|
||||||
private final DuplexConnectionFactory duplexConnFactory;
|
private final ConnectionRegistry connectionRegistry;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ConnectionDispatcherImpl(@IoExecutor Executor ioExecutor,
|
ConnectionDispatcherImpl(@IoExecutor Executor ioExecutor,
|
||||||
TagRecogniser tagRecogniser,
|
KeyManager keyManager, TagRecogniser tagRecogniser,
|
||||||
SimplexConnectionFactory simplexConnFactory,
|
MessagingSessionFactory messagingSessionFactory,
|
||||||
DuplexConnectionFactory duplexConnFactory) {
|
ConnectionRegistry connectionRegistry) {
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
|
this.keyManager = keyManager;
|
||||||
this.tagRecogniser = tagRecogniser;
|
this.tagRecogniser = tagRecogniser;
|
||||||
this.simplexConnFactory = simplexConnFactory;
|
this.messagingSessionFactory = messagingSessionFactory;
|
||||||
this.duplexConnFactory = duplexConnFactory;
|
this.connectionRegistry = connectionRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dispatchIncomingConnection(TransportId t,
|
public void dispatchIncomingConnection(TransportId t,
|
||||||
SimplexTransportReader r) {
|
TransportConnectionReader r) {
|
||||||
ioExecutor.execute(new DispatchSimplexConnection(t, r));
|
ioExecutor.execute(new DispatchIncomingSimplexConnection(t, r));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dispatchIncomingConnection(TransportId t,
|
public void dispatchIncomingConnection(TransportId t,
|
||||||
DuplexTransportConnection d) {
|
DuplexTransportConnection d) {
|
||||||
ioExecutor.execute(new DispatchDuplexConnection(t, d));
|
ioExecutor.execute(new DispatchIncomingDuplexConnection(t, d));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dispatchOutgoingConnection(ContactId c, TransportId t,
|
public void dispatchOutgoingConnection(ContactId c, TransportId t,
|
||||||
SimplexTransportWriter w) {
|
TransportConnectionWriter w) {
|
||||||
simplexConnFactory.createOutgoingConnection(c, t, w);
|
ioExecutor.execute(new DispatchOutgoingSimplexConnection(c, t, w));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dispatchOutgoingConnection(ContactId c, TransportId t,
|
public void dispatchOutgoingConnection(ContactId c, TransportId t,
|
||||||
DuplexTransportConnection d) {
|
DuplexTransportConnection d) {
|
||||||
duplexConnFactory.createOutgoingConnection(c, t, d);
|
ioExecutor.execute(new DispatchOutgoingDuplexConnection(c, t, d));
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] readTag(InputStream in) throws IOException {
|
private StreamContext readAndRecogniseTag(TransportId t,
|
||||||
byte[] b = new byte[TAG_LENGTH];
|
TransportConnectionReader r) {
|
||||||
int offset = 0;
|
// Read the tag
|
||||||
while(offset < b.length) {
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
int read = in.read(b, offset, b.length - offset);
|
try {
|
||||||
if(read == -1) throw new EOFException();
|
InputStream in = r.getInputStream();
|
||||||
offset += read;
|
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;
|
||||||
}
|
}
|
||||||
return b;
|
// 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 class DispatchSimplexConnection implements Runnable {
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DispatchIncomingSimplexConnection implements Runnable {
|
||||||
|
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
private final SimplexTransportReader transport;
|
private final TransportConnectionReader reader;
|
||||||
|
|
||||||
private DispatchSimplexConnection(TransportId transportId,
|
private DispatchIncomingSimplexConnection(TransportId transportId,
|
||||||
SimplexTransportReader transport) {
|
TransportConnectionReader reader) {
|
||||||
this.transportId = transportId;
|
this.transportId = transportId;
|
||||||
this.transport = transport;
|
this.reader = reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
// Read and recognise the tag
|
||||||
byte[] tag = readTag(transport.getInputStream());
|
StreamContext ctx = readAndRecogniseTag(transportId, reader);
|
||||||
StreamContext ctx = tagRecogniser.recogniseTag(transportId,
|
if(ctx == null) return;
|
||||||
tag);
|
// Run the incoming session
|
||||||
if(ctx == null) {
|
runAndDispose(ctx, reader);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DispatchDuplexConnection implements Runnable {
|
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 DispatchIncomingDuplexConnection implements Runnable {
|
||||||
|
|
||||||
private final TransportId transportId;
|
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) {
|
DuplexTransportConnection transport) {
|
||||||
this.transportId = transportId;
|
this.transportId = transportId;
|
||||||
this.transport = transport;
|
reader = transport.getReader();
|
||||||
|
writer = transport.getWriter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
byte[] tag;
|
// Read and recognise the tag
|
||||||
try {
|
StreamContext ctx = readAndRecogniseTag(transportId, reader);
|
||||||
tag = readTag(transport.getInputStream());
|
if(ctx == null) return;
|
||||||
} catch(IOException e) {
|
// Start the outgoing session on another thread
|
||||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
ioExecutor.execute(new DispatchIncomingDuplexConnectionSide2(
|
||||||
dispose(true, false);
|
ctx.getContactId(), transportId, writer));
|
||||||
return;
|
// Run the incoming session
|
||||||
}
|
runAndDispose(ctx, reader);
|
||||||
StreamContext ctx = null;
|
}
|
||||||
try {
|
}
|
||||||
ctx = tagRecogniser.recogniseTag(transportId, tag);
|
|
||||||
} catch(DbException e) {
|
private class DispatchIncomingDuplexConnectionSide2 implements Runnable {
|
||||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
dispose(true, false);
|
private final ContactId contactId;
|
||||||
return;
|
private final TransportId transportId;
|
||||||
}
|
private final TransportConnectionWriter writer;
|
||||||
if(ctx == null) dispose(false, false);
|
|
||||||
else duplexConnFactory.createIncomingConnection(ctx, transport);
|
private DispatchIncomingDuplexConnectionSide2(ContactId contactId,
|
||||||
|
TransportId transportId, TransportConnectionWriter writer) {
|
||||||
|
this.contactId = contactId;
|
||||||
|
this.transportId = transportId;
|
||||||
|
this.writer = writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dispose(boolean exception, boolean recognised) {
|
public void run() {
|
||||||
try {
|
// Allocate a stream context
|
||||||
transport.dispose(exception, recognised);
|
StreamContext ctx = keyManager.getStreamContext(contactId,
|
||||||
} catch(IOException e) {
|
transportId);
|
||||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
if(ctx == null) {
|
||||||
|
dispose(writer, false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
// 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 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,9 +8,6 @@ interface FrameWriter {
|
|||||||
void writeFrame(byte[] frame, int payloadLength, boolean finalFrame)
|
void writeFrame(byte[] frame, int payloadLength, boolean finalFrame)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
/** Flushes the stack. */
|
/** Flushes the stream. */
|
||||||
void flush() throws IOException;
|
void flush() throws IOException;
|
||||||
|
|
||||||
/** Returns the maximum number of bytes that can be written. */
|
|
||||||
long getRemainingCapacity();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,22 +19,18 @@ class OutgoingEncryptionLayer implements FrameWriter {
|
|||||||
private final AuthenticatedCipher frameCipher;
|
private final AuthenticatedCipher frameCipher;
|
||||||
private final SecretKey frameKey;
|
private final SecretKey frameKey;
|
||||||
private final byte[] tag, iv, aad, ciphertext;
|
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;
|
private boolean writeTag;
|
||||||
|
|
||||||
/** Constructor for the initiator's side of a connection. */
|
OutgoingEncryptionLayer(OutputStream out, AuthenticatedCipher frameCipher,
|
||||||
OutgoingEncryptionLayer(OutputStream out, long capacity,
|
SecretKey frameKey, int frameLength, byte[] tag) {
|
||||||
AuthenticatedCipher frameCipher, SecretKey frameKey,
|
|
||||||
int frameLength, byte[] tag) {
|
|
||||||
this.out = out;
|
this.out = out;
|
||||||
this.capacity = capacity;
|
|
||||||
this.frameCipher = frameCipher;
|
this.frameCipher = frameCipher;
|
||||||
this.frameKey = frameKey;
|
this.frameKey = frameKey;
|
||||||
this.frameLength = frameLength;
|
this.frameLength = frameLength;
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
maxPayloadLength = frameLength - HEADER_LENGTH - MAC_LENGTH;
|
|
||||||
iv = new byte[IV_LENGTH];
|
iv = new byte[IV_LENGTH];
|
||||||
aad = new byte[AAD_LENGTH];
|
aad = new byte[AAD_LENGTH];
|
||||||
ciphertext = new byte[frameLength];
|
ciphertext = new byte[frameLength];
|
||||||
@@ -42,24 +38,6 @@ class OutgoingEncryptionLayer implements FrameWriter {
|
|||||||
writeTag = true;
|
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)
|
public void writeFrame(byte[] frame, int payloadLength, boolean finalFrame)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
|
if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
|
||||||
@@ -71,7 +49,6 @@ class OutgoingEncryptionLayer implements FrameWriter {
|
|||||||
frameKey.erase();
|
frameKey.erase();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
capacity -= tag.length;
|
|
||||||
writeTag = false;
|
writeTag = false;
|
||||||
}
|
}
|
||||||
// Encode the header
|
// Encode the header
|
||||||
@@ -107,7 +84,6 @@ class OutgoingEncryptionLayer implements FrameWriter {
|
|||||||
frameKey.erase();
|
frameKey.erase();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
capacity -= ciphertextLength;
|
|
||||||
frameNumber++;
|
frameNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,33 +96,8 @@ class OutgoingEncryptionLayer implements FrameWriter {
|
|||||||
frameKey.erase();
|
frameKey.erase();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
capacity -= tag.length;
|
|
||||||
writeTag = false;
|
writeTag = false;
|
||||||
}
|
}
|
||||||
out.flush();
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -20,24 +20,21 @@ class StreamReaderFactoryImpl implements StreamReaderFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public StreamReader createStreamReader(InputStream in,
|
public StreamReader createStreamReader(InputStream in,
|
||||||
int maxFrameLength, StreamContext ctx, boolean incoming,
|
int maxFrameLength, StreamContext ctx) {
|
||||||
boolean initiator) {
|
|
||||||
byte[] secret = ctx.getSecret();
|
byte[] secret = ctx.getSecret();
|
||||||
long streamNumber = ctx.getStreamNumber();
|
long streamNumber = ctx.getStreamNumber();
|
||||||
boolean weAreAlice = ctx.getAlice();
|
boolean alice = !ctx.getAlice();
|
||||||
boolean initiatorIsAlice = incoming ? !weAreAlice : weAreAlice;
|
SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice);
|
||||||
SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber,
|
FrameReader frameReader = new IncomingEncryptionLayer(in,
|
||||||
initiatorIsAlice, initiator);
|
|
||||||
FrameReader encryption = new IncomingEncryptionLayer(in,
|
|
||||||
crypto.getFrameCipher(), frameKey, maxFrameLength);
|
crypto.getFrameCipher(), frameKey, maxFrameLength);
|
||||||
return new StreamReaderImpl(encryption, maxFrameLength);
|
return new StreamReaderImpl(frameReader, maxFrameLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StreamReader createInvitationStreamReader(InputStream in,
|
public StreamReader createInvitationStreamReader(InputStream in,
|
||||||
int maxFrameLength, byte[] secret, boolean alice) {
|
int maxFrameLength, byte[] secret, boolean alice) {
|
||||||
SecretKey frameKey = crypto.deriveFrameKey(secret, 0, true, alice);
|
SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice);
|
||||||
FrameReader encryption = new IncomingEncryptionLayer(in,
|
FrameReader frameReader = new IncomingEncryptionLayer(in,
|
||||||
crypto.getFrameCipher(), frameKey, maxFrameLength);
|
crypto.getFrameCipher(), frameKey, maxFrameLength);
|
||||||
return new StreamReaderImpl(encryption, maxFrameLength);
|
return new StreamReaderImpl(frameReader, maxFrameLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,35 +22,29 @@ class StreamWriterFactoryImpl implements StreamWriterFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public StreamWriter createStreamWriter(OutputStream out,
|
public StreamWriter createStreamWriter(OutputStream out,
|
||||||
int maxFrameLength, long capacity, StreamContext ctx,
|
int maxFrameLength, StreamContext ctx) {
|
||||||
boolean incoming, boolean initiator) {
|
|
||||||
byte[] secret = ctx.getSecret();
|
byte[] secret = ctx.getSecret();
|
||||||
long streamNumber = ctx.getStreamNumber();
|
long streamNumber = ctx.getStreamNumber();
|
||||||
boolean weAreAlice = ctx.getAlice();
|
boolean alice = ctx.getAlice();
|
||||||
boolean initiatorIsAlice = incoming ? !weAreAlice : weAreAlice;
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber,
|
SecretKey tagKey = crypto.deriveTagKey(secret, alice);
|
||||||
initiatorIsAlice, initiator);
|
crypto.encodeTag(tag, tagKey, streamNumber);
|
||||||
FrameWriter encryption;
|
tagKey.erase();
|
||||||
if(initiator) {
|
SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice);
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
FrameWriter frameWriter = new OutgoingEncryptionLayer(out,
|
||||||
SecretKey tagKey = crypto.deriveTagKey(secret, initiatorIsAlice);
|
crypto.getFrameCipher(), frameKey, maxFrameLength, tag);
|
||||||
crypto.encodeTag(tag, tagKey, streamNumber);
|
return new StreamWriterImpl(frameWriter, maxFrameLength);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public StreamWriter createInvitationStreamWriter(OutputStream out,
|
public StreamWriter createInvitationStreamWriter(OutputStream out,
|
||||||
int maxFrameLength, byte[] secret, boolean alice) {
|
int maxFrameLength, byte[] secret, boolean alice) {
|
||||||
SecretKey frameKey = crypto.deriveFrameKey(secret, 0, true, alice);
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
FrameWriter encryption = new OutgoingEncryptionLayer(out,
|
SecretKey tagKey = crypto.deriveTagKey(secret, alice);
|
||||||
Long.MAX_VALUE, crypto.getFrameCipher(), frameKey,
|
crypto.encodeTag(tag, tagKey, 0);
|
||||||
maxFrameLength);
|
tagKey.erase();
|
||||||
return new StreamWriterImpl(encryption, maxFrameLength);
|
SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice);
|
||||||
|
FrameWriter frameWriter = new OutgoingEncryptionLayer(out,
|
||||||
|
crypto.getFrameCipher(), frameKey, maxFrameLength, tag);
|
||||||
|
return new StreamWriterImpl(frameWriter, maxFrameLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,10 +33,6 @@ class StreamWriterImpl extends OutputStream implements StreamWriter {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getRemainingCapacity() {
|
|
||||||
return out.getRemainingCapacity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
writeFrame(true);
|
writeFrame(true);
|
||||||
|
|||||||
@@ -3,40 +3,82 @@ package org.briarproject.plugins.bluetooth;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import javax.microedition.io.StreamConnection;
|
import javax.microedition.io.StreamConnection;
|
||||||
|
|
||||||
import org.briarproject.api.plugins.Plugin;
|
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 org.briarproject.api.plugins.duplex.DuplexTransportConnection;
|
||||||
|
|
||||||
class BluetoothTransportConnection implements DuplexTransportConnection {
|
class BluetoothTransportConnection implements DuplexTransportConnection {
|
||||||
|
|
||||||
private final Plugin plugin;
|
private final Plugin plugin;
|
||||||
private final StreamConnection stream;
|
private final StreamConnection stream;
|
||||||
|
private final Reader reader;
|
||||||
|
private final Writer writer;
|
||||||
|
private final AtomicBoolean halfClosed, closed;
|
||||||
|
|
||||||
BluetoothTransportConnection(Plugin plugin, StreamConnection stream) {
|
BluetoothTransportConnection(Plugin plugin, StreamConnection stream) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
|
reader = new Reader();
|
||||||
|
writer = new Writer();
|
||||||
|
halfClosed = new AtomicBoolean(false);
|
||||||
|
closed = new AtomicBoolean(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxFrameLength() {
|
public TransportConnectionReader getReader() {
|
||||||
return plugin.getMaxFrameLength();
|
return reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMaxLatency() {
|
public TransportConnectionWriter getWriter() {
|
||||||
return plugin.getMaxLatency();
|
return writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream() throws IOException {
|
private class Reader implements TransportConnectionReader {
|
||||||
return stream.openInputStream();
|
|
||||||
|
public int getMaxFrameLength() {
|
||||||
|
return plugin.getMaxFrameLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMaxLatency() {
|
||||||
|
return plugin.getMaxLatency();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 OutputStream getOutputStream() throws IOException {
|
private class Writer implements TransportConnectionWriter {
|
||||||
return stream.openOutputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispose(boolean exception, boolean recognised)
|
public int getMaxFrameLength() {
|
||||||
throws IOException {
|
return plugin.getMaxFrameLength();
|
||||||
stream.close();
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,15 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
import org.briarproject.api.TransportId;
|
import org.briarproject.api.TransportId;
|
||||||
import org.briarproject.api.TransportProperties;
|
import org.briarproject.api.TransportProperties;
|
||||||
import org.briarproject.api.crypto.PseudoRandom;
|
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.DuplexPlugin;
|
||||||
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
|
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
|
||||||
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
|
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
|
||||||
@@ -102,6 +105,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
|
|||||||
return running;
|
return running;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Don't poll this plugin
|
||||||
public boolean shouldPoll() {
|
public boolean shouldPoll() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -164,7 +168,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean resetModem() {
|
boolean resetModem() {
|
||||||
if(!running) return false;
|
if(!running) return false;
|
||||||
for(String portName : serialPortList.getPortNames()) {
|
for(String portName : serialPortList.getPortNames()) {
|
||||||
if(LOG.isLoggable(INFO))
|
if(LOG.isLoggable(INFO))
|
||||||
@@ -227,25 +231,21 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
|
|||||||
private class ModemTransportConnection
|
private class ModemTransportConnection
|
||||||
implements DuplexTransportConnection {
|
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() {
|
public TransportConnectionReader getReader() {
|
||||||
return maxFrameLength;
|
return reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMaxLatency() {
|
public TransportConnectionWriter getWriter() {
|
||||||
return maxLatency;
|
return writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream() throws IOException {
|
private void hangUp(boolean exception) {
|
||||||
return modem.getInputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
public OutputStream getOutputStream() throws IOException {
|
|
||||||
return modem.getOutputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispose(boolean exception, boolean recognised) {
|
|
||||||
LOG.info("Call disconnected");
|
LOG.info("Call disconnected");
|
||||||
try {
|
try {
|
||||||
modem.hangUp();
|
modem.hangUp();
|
||||||
@@ -254,11 +254,55 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
|
|||||||
exception = true;
|
exception = true;
|
||||||
}
|
}
|
||||||
if(exception) resetModem();
|
if(exception) resetModem();
|
||||||
finished.countDown();
|
disposalFinished.countDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForDisposal() throws InterruptedException {
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,8 +111,8 @@
|
|||||||
<test name='org.briarproject.messaging.ConstantsTest'/>
|
<test name='org.briarproject.messaging.ConstantsTest'/>
|
||||||
<test name='org.briarproject.messaging.ConsumersTest'/>
|
<test name='org.briarproject.messaging.ConsumersTest'/>
|
||||||
<test name='org.briarproject.messaging.PacketReaderImplTest'/>
|
<test name='org.briarproject.messaging.PacketReaderImplTest'/>
|
||||||
<test name='org.briarproject.messaging.simplex.OutgoingSimplexConnectionTest'/>
|
<test name='org.briarproject.messaging.SimplexMessagingIntegrationTest'/>
|
||||||
<test name='org.briarproject.messaging.simplex.SimplexMessagingIntegrationTest'/>
|
<test name='org.briarproject.messaging.SinglePassOutgoingSessionTest'/>
|
||||||
<test name='org.briarproject.plugins.PluginManagerImplTest'/>
|
<test name='org.briarproject.plugins.PluginManagerImplTest'/>
|
||||||
<test name='org.briarproject.plugins.file.LinuxRemovableDriveFinderTest'/>
|
<test name='org.briarproject.plugins.file.LinuxRemovableDriveFinderTest'/>
|
||||||
<test name='org.briarproject.plugins.file.MacRemovableDriveFinderTest'/>
|
<test name='org.briarproject.plugins.file.MacRemovableDriveFinderTest'/>
|
||||||
|
|||||||
@@ -45,8 +45,6 @@ import org.briarproject.crypto.CryptoModule;
|
|||||||
import org.briarproject.db.DatabaseModule;
|
import org.briarproject.db.DatabaseModule;
|
||||||
import org.briarproject.event.EventModule;
|
import org.briarproject.event.EventModule;
|
||||||
import org.briarproject.messaging.MessagingModule;
|
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.reliability.ReliabilityModule;
|
||||||
import org.briarproject.serial.SerialModule;
|
import org.briarproject.serial.SerialModule;
|
||||||
import org.briarproject.transport.TransportModule;
|
import org.briarproject.transport.TransportModule;
|
||||||
@@ -81,7 +79,6 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
|||||||
new TestLifecycleModule(), new TestSystemModule(),
|
new TestLifecycleModule(), new TestSystemModule(),
|
||||||
new TestUiModule(), new CryptoModule(), new DatabaseModule(),
|
new TestUiModule(), new CryptoModule(), new DatabaseModule(),
|
||||||
new EventModule(), new MessagingModule(),
|
new EventModule(), new MessagingModule(),
|
||||||
new DuplexMessagingModule(), new SimplexMessagingModule(),
|
|
||||||
new ReliabilityModule(), new SerialModule(),
|
new ReliabilityModule(), new SerialModule(),
|
||||||
new TransportModule());
|
new TransportModule());
|
||||||
streamReaderFactory = i.getInstance(StreamReaderFactory.class);
|
streamReaderFactory = i.getInstance(StreamReaderFactory.class);
|
||||||
@@ -125,29 +122,29 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
|||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamContext ctx = new StreamContext(contactId, transportId,
|
StreamContext ctx = new StreamContext(contactId, transportId,
|
||||||
secret.clone(), 0, true);
|
secret.clone(), 0, true);
|
||||||
StreamWriter conn = streamWriterFactory.createStreamWriter(
|
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out,
|
||||||
out, MAX_FRAME_LENGTH, Long.MAX_VALUE, ctx, false, true);
|
MAX_FRAME_LENGTH, ctx);
|
||||||
OutputStream out1 = conn.getOutputStream();
|
OutputStream out1 = streamWriter.getOutputStream();
|
||||||
PacketWriter writer = packetWriterFactory.createPacketWriter(out1,
|
PacketWriter packetWriter = packetWriterFactory.createPacketWriter(out1,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
writer.writeAck(new Ack(messageIds));
|
packetWriter.writeAck(new Ack(messageIds));
|
||||||
|
|
||||||
writer.writeMessage(message.getSerialised());
|
packetWriter.writeMessage(message.getSerialised());
|
||||||
writer.writeMessage(message1.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);
|
SubscriptionUpdate su = new SubscriptionUpdate(Arrays.asList(group), 1);
|
||||||
writer.writeSubscriptionUpdate(su);
|
packetWriter.writeSubscriptionUpdate(su);
|
||||||
|
|
||||||
TransportUpdate tu = new TransportUpdate(transportId,
|
TransportUpdate tu = new TransportUpdate(transportId,
|
||||||
transportProperties, 1);
|
transportProperties, 1);
|
||||||
writer.writeTransportUpdate(tu);
|
packetWriter.writeTransportUpdate(tu);
|
||||||
|
|
||||||
writer.flush();
|
packetWriter.flush();
|
||||||
return out.toByteArray();
|
return out.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,44 +155,44 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
|||||||
// FIXME: Check that the expected tag was received
|
// FIXME: Check that the expected tag was received
|
||||||
StreamContext ctx = new StreamContext(contactId, transportId,
|
StreamContext ctx = new StreamContext(contactId, transportId,
|
||||||
secret.clone(), 0, false);
|
secret.clone(), 0, false);
|
||||||
StreamReader conn = streamReaderFactory.createStreamReader(
|
StreamReader streamReader = streamReaderFactory.createStreamReader(in,
|
||||||
in, MAX_FRAME_LENGTH, ctx, true, true);
|
MAX_FRAME_LENGTH, ctx);
|
||||||
InputStream in1 = conn.getInputStream();
|
InputStream in1 = streamReader.getInputStream();
|
||||||
PacketReader reader = packetReaderFactory.createPacketReader(in1);
|
PacketReader packetReader = packetReaderFactory.createPacketReader(in1);
|
||||||
|
|
||||||
// Read the ack
|
// Read the ack
|
||||||
assertTrue(reader.hasAck());
|
assertTrue(packetReader.hasAck());
|
||||||
Ack a = reader.readAck();
|
Ack a = packetReader.readAck();
|
||||||
assertEquals(messageIds, a.getMessageIds());
|
assertEquals(messageIds, a.getMessageIds());
|
||||||
|
|
||||||
// Read and verify the messages
|
// Read and verify the messages
|
||||||
assertTrue(reader.hasMessage());
|
assertTrue(packetReader.hasMessage());
|
||||||
UnverifiedMessage m = reader.readMessage();
|
UnverifiedMessage m = packetReader.readMessage();
|
||||||
checkMessageEquality(message, messageVerifier.verifyMessage(m));
|
checkMessageEquality(message, messageVerifier.verifyMessage(m));
|
||||||
assertTrue(reader.hasMessage());
|
assertTrue(packetReader.hasMessage());
|
||||||
m = reader.readMessage();
|
m = packetReader.readMessage();
|
||||||
checkMessageEquality(message1, messageVerifier.verifyMessage(m));
|
checkMessageEquality(message1, messageVerifier.verifyMessage(m));
|
||||||
assertFalse(reader.hasMessage());
|
assertFalse(packetReader.hasMessage());
|
||||||
|
|
||||||
// Read the offer
|
// Read the offer
|
||||||
assertTrue(reader.hasOffer());
|
assertTrue(packetReader.hasOffer());
|
||||||
Offer o = reader.readOffer();
|
Offer o = packetReader.readOffer();
|
||||||
assertEquals(messageIds, o.getMessageIds());
|
assertEquals(messageIds, o.getMessageIds());
|
||||||
|
|
||||||
// Read the request
|
// Read the request
|
||||||
assertTrue(reader.hasRequest());
|
assertTrue(packetReader.hasRequest());
|
||||||
Request req = reader.readRequest();
|
Request req = packetReader.readRequest();
|
||||||
assertEquals(messageIds, req.getMessageIds());
|
assertEquals(messageIds, req.getMessageIds());
|
||||||
|
|
||||||
// Read the subscription update
|
// Read the subscription update
|
||||||
assertTrue(reader.hasSubscriptionUpdate());
|
assertTrue(packetReader.hasSubscriptionUpdate());
|
||||||
SubscriptionUpdate su = reader.readSubscriptionUpdate();
|
SubscriptionUpdate su = packetReader.readSubscriptionUpdate();
|
||||||
assertEquals(Arrays.asList(group), su.getGroups());
|
assertEquals(Arrays.asList(group), su.getGroups());
|
||||||
assertEquals(1, su.getVersion());
|
assertEquals(1, su.getVersion());
|
||||||
|
|
||||||
// Read the transport update
|
// Read the transport update
|
||||||
assertTrue(reader.hasTransportUpdate());
|
assertTrue(packetReader.hasTransportUpdate());
|
||||||
TransportUpdate tu = reader.readTransportUpdate();
|
TransportUpdate tu = packetReader.readTransportUpdate();
|
||||||
assertEquals(transportId, tu.getId());
|
assertEquals(transportId, tu.getId());
|
||||||
assertEquals(transportProperties, tu.getProperties());
|
assertEquals(transportProperties, tu.getProperties());
|
||||||
assertEquals(1, tu.getVersion());
|
assertEquals(1, tu.getVersion());
|
||||||
|
|||||||
@@ -25,10 +25,8 @@ public class KeyDerivationTest extends BriarTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testKeysAreDistinct() {
|
public void testKeysAreDistinct() {
|
||||||
List<SecretKey> keys = new ArrayList<SecretKey>();
|
List<SecretKey> keys = new ArrayList<SecretKey>();
|
||||||
keys.add(crypto.deriveFrameKey(secret, 0, false, false));
|
keys.add(crypto.deriveFrameKey(secret, 0, true));
|
||||||
keys.add(crypto.deriveFrameKey(secret, 0, false, true));
|
keys.add(crypto.deriveFrameKey(secret, 0, false));
|
||||||
keys.add(crypto.deriveFrameKey(secret, 0, true, false));
|
|
||||||
keys.add(crypto.deriveFrameKey(secret, 0, true, true));
|
|
||||||
keys.add(crypto.deriveTagKey(secret, true));
|
keys.add(crypto.deriveTagKey(secret, true));
|
||||||
keys.add(crypto.deriveTagKey(secret, false));
|
keys.add(crypto.deriveTagKey(secret, false));
|
||||||
for(int i = 0; i < 4; i++) {
|
for(int i = 0; i < 4; i++) {
|
||||||
|
|||||||
@@ -46,8 +46,6 @@ import org.briarproject.api.messaging.TransportUpdate;
|
|||||||
import org.briarproject.crypto.CryptoModule;
|
import org.briarproject.crypto.CryptoModule;
|
||||||
import org.briarproject.db.DatabaseModule;
|
import org.briarproject.db.DatabaseModule;
|
||||||
import org.briarproject.event.EventModule;
|
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.serial.SerialModule;
|
||||||
import org.briarproject.transport.TransportModule;
|
import org.briarproject.transport.TransportModule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -67,8 +65,7 @@ public class ConstantsTest extends BriarTestCase {
|
|||||||
Injector i = Guice.createInjector(new TestDatabaseModule(),
|
Injector i = Guice.createInjector(new TestDatabaseModule(),
|
||||||
new TestLifecycleModule(), new TestSystemModule(),
|
new TestLifecycleModule(), new TestSystemModule(),
|
||||||
new CryptoModule(), new DatabaseModule(), new EventModule(),
|
new CryptoModule(), new DatabaseModule(), new EventModule(),
|
||||||
new MessagingModule(), new DuplexMessagingModule(),
|
new MessagingModule(), new SerialModule(),
|
||||||
new SimplexMessagingModule(), new SerialModule(),
|
|
||||||
new TransportModule());
|
new TransportModule());
|
||||||
crypto = i.getInstance(CryptoComponent.class);
|
crypto = i.getInstance(CryptoComponent.class);
|
||||||
groupFactory = i.getInstance(GroupFactory.class);
|
groupFactory = i.getInstance(GroupFactory.class);
|
||||||
|
|||||||
@@ -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.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||||
import static org.briarproject.api.messaging.MessagingConstants.GROUP_SALT_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.Message;
|
||||||
import org.briarproject.api.messaging.MessageFactory;
|
import org.briarproject.api.messaging.MessageFactory;
|
||||||
import org.briarproject.api.messaging.MessageVerifier;
|
import org.briarproject.api.messaging.MessageVerifier;
|
||||||
|
import org.briarproject.api.messaging.MessagingSession;
|
||||||
import org.briarproject.api.messaging.PacketReaderFactory;
|
import org.briarproject.api.messaging.PacketReaderFactory;
|
||||||
import org.briarproject.api.messaging.PacketWriterFactory;
|
import org.briarproject.api.messaging.PacketWriterFactory;
|
||||||
import org.briarproject.api.transport.ConnectionRegistry;
|
|
||||||
import org.briarproject.api.transport.Endpoint;
|
import org.briarproject.api.transport.Endpoint;
|
||||||
import org.briarproject.api.transport.StreamContext;
|
import org.briarproject.api.transport.StreamContext;
|
||||||
import org.briarproject.api.transport.StreamReaderFactory;
|
import org.briarproject.api.transport.StreamReaderFactory;
|
||||||
@@ -41,8 +41,6 @@ import org.briarproject.api.transport.TagRecogniser;
|
|||||||
import org.briarproject.crypto.CryptoModule;
|
import org.briarproject.crypto.CryptoModule;
|
||||||
import org.briarproject.db.DatabaseModule;
|
import org.briarproject.db.DatabaseModule;
|
||||||
import org.briarproject.event.EventModule;
|
import org.briarproject.event.EventModule;
|
||||||
import org.briarproject.messaging.MessagingModule;
|
|
||||||
import org.briarproject.messaging.duplex.DuplexMessagingModule;
|
|
||||||
import org.briarproject.plugins.ImmediateExecutor;
|
import org.briarproject.plugins.ImmediateExecutor;
|
||||||
import org.briarproject.serial.SerialModule;
|
import org.briarproject.serial.SerialModule;
|
||||||
import org.briarproject.transport.TransportModule;
|
import org.briarproject.transport.TransportModule;
|
||||||
@@ -88,8 +86,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
|||||||
return Guice.createInjector(new TestDatabaseModule(dir),
|
return Guice.createInjector(new TestDatabaseModule(dir),
|
||||||
new TestLifecycleModule(), new TestSystemModule(),
|
new TestLifecycleModule(), new TestSystemModule(),
|
||||||
new CryptoModule(), new DatabaseModule(), new EventModule(),
|
new CryptoModule(), new DatabaseModule(), new EventModule(),
|
||||||
new MessagingModule(), new DuplexMessagingModule(),
|
new MessagingModule(), new SerialModule(),
|
||||||
new SimplexMessagingModule(), new SerialModule(),
|
|
||||||
new TransportModule());
|
new TransportModule());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,29 +137,26 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
|||||||
Message message = messageFactory.createAnonymousMessage(null, group,
|
Message message = messageFactory.createAnonymousMessage(null, group,
|
||||||
contentType, timestamp, body);
|
contentType, timestamp, body);
|
||||||
db.addLocalMessage(message);
|
db.addLocalMessage(message);
|
||||||
// Create an outgoing simplex connection
|
// Create an outgoing messaging session
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
ConnectionRegistry connRegistry =
|
StreamWriterFactory streamWriterFactory =
|
||||||
alice.getInstance(ConnectionRegistry.class);
|
|
||||||
StreamWriterFactory connWriterFactory =
|
|
||||||
alice.getInstance(StreamWriterFactory.class);
|
alice.getInstance(StreamWriterFactory.class);
|
||||||
PacketWriterFactory packetWriterFactory =
|
PacketWriterFactory packetWriterFactory =
|
||||||
alice.getInstance(PacketWriterFactory.class);
|
alice.getInstance(PacketWriterFactory.class);
|
||||||
TestSimplexTransportWriter transport = new TestSimplexTransportWriter(
|
TestTransportConnectionWriter transport =
|
||||||
out, Long.MAX_VALUE, Long.MAX_VALUE);
|
new TestTransportConnectionWriter(out);
|
||||||
StreamContext ctx = km.getStreamContext(contactId, transportId);
|
StreamContext ctx = km.getStreamContext(contactId, transportId);
|
||||||
assertNotNull(ctx);
|
assertNotNull(ctx);
|
||||||
OutgoingSimplexConnection simplex = new OutgoingSimplexConnection(db,
|
MessagingSession session = new SinglePassOutgoingSession(db,
|
||||||
connRegistry, connWriterFactory, packetWriterFactory, ctx,
|
new ImmediateExecutor(), streamWriterFactory,
|
||||||
transport);
|
packetWriterFactory, ctx, transport);
|
||||||
// Write whatever needs to be written
|
// Write whatever needs to be written
|
||||||
simplex.write();
|
session.run();
|
||||||
assertTrue(transport.getDisposed());
|
transport.dispose(false);
|
||||||
assertFalse(transport.getException());
|
|
||||||
// Clean up
|
// Clean up
|
||||||
km.stop();
|
km.stop();
|
||||||
db.close();
|
db.close();
|
||||||
// Return the contents of the simplex connection
|
// Return the contents of the stream
|
||||||
return out.toByteArray();
|
return out.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,28 +198,24 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
|||||||
assertEquals(tag.length, read);
|
assertEquals(tag.length, read);
|
||||||
StreamContext ctx = rec.recogniseTag(transportId, tag);
|
StreamContext ctx = rec.recogniseTag(transportId, tag);
|
||||||
assertNotNull(ctx);
|
assertNotNull(ctx);
|
||||||
// Create an incoming simplex connection
|
// Create an incoming messaging session
|
||||||
MessageVerifier messageVerifier =
|
MessageVerifier messageVerifier =
|
||||||
bob.getInstance(MessageVerifier.class);
|
bob.getInstance(MessageVerifier.class);
|
||||||
ConnectionRegistry connRegistry =
|
StreamReaderFactory streamReaderFactory =
|
||||||
bob.getInstance(ConnectionRegistry.class);
|
|
||||||
StreamReaderFactory connWriterFactory =
|
|
||||||
bob.getInstance(StreamReaderFactory.class);
|
bob.getInstance(StreamReaderFactory.class);
|
||||||
PacketReaderFactory packetWriterFactory =
|
PacketReaderFactory packetReaderFactory =
|
||||||
bob.getInstance(PacketReaderFactory.class);
|
bob.getInstance(PacketReaderFactory.class);
|
||||||
TestSimplexTransportReader transport =
|
TestTransportConnectionReader transport =
|
||||||
new TestSimplexTransportReader(in);
|
new TestTransportConnectionReader(in);
|
||||||
IncomingSimplexConnection simplex = new IncomingSimplexConnection(
|
MessagingSession session = new IncomingSession(db,
|
||||||
new ImmediateExecutor(), new ImmediateExecutor(),
|
new ImmediateExecutor(), new ImmediateExecutor(),
|
||||||
messageVerifier, db, connRegistry, connWriterFactory,
|
messageVerifier, streamReaderFactory, packetReaderFactory,
|
||||||
packetWriterFactory, ctx, transport);
|
ctx, transport);
|
||||||
// No messages should have been added yet
|
// No messages should have been added yet
|
||||||
assertFalse(listener.messageAdded);
|
assertFalse(listener.messageAdded);
|
||||||
// Read whatever needs to be read
|
// Read whatever needs to be read
|
||||||
simplex.read();
|
session.run();
|
||||||
assertTrue(transport.getDisposed());
|
transport.dispose(false, true);
|
||||||
assertFalse(transport.getException());
|
|
||||||
assertTrue(transport.getRecognised());
|
|
||||||
// The private message from Alice should have been added
|
// The private message from Alice should have been added
|
||||||
assertTrue(listener.messageAdded);
|
assertTrue(listener.messageAdded);
|
||||||
// Clean up
|
// Clean up
|
||||||
@@ -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.HEADER_LENGTH;
|
||||||
import static org.briarproject.api.transport.TransportConstants.MAC_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 static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -23,14 +21,12 @@ import org.briarproject.api.db.DatabaseComponent;
|
|||||||
import org.briarproject.api.db.DatabaseExecutor;
|
import org.briarproject.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.api.messaging.Ack;
|
import org.briarproject.api.messaging.Ack;
|
||||||
import org.briarproject.api.messaging.MessageId;
|
import org.briarproject.api.messaging.MessageId;
|
||||||
|
import org.briarproject.api.messaging.MessagingSession;
|
||||||
import org.briarproject.api.messaging.PacketWriterFactory;
|
import org.briarproject.api.messaging.PacketWriterFactory;
|
||||||
import org.briarproject.api.transport.ConnectionRegistry;
|
|
||||||
import org.briarproject.api.transport.StreamContext;
|
import org.briarproject.api.transport.StreamContext;
|
||||||
import org.briarproject.api.transport.StreamWriterFactory;
|
import org.briarproject.api.transport.StreamWriterFactory;
|
||||||
import org.briarproject.crypto.CryptoModule;
|
import org.briarproject.crypto.CryptoModule;
|
||||||
import org.briarproject.event.EventModule;
|
import org.briarproject.event.EventModule;
|
||||||
import org.briarproject.messaging.MessagingModule;
|
|
||||||
import org.briarproject.messaging.duplex.DuplexMessagingModule;
|
|
||||||
import org.briarproject.serial.SerialModule;
|
import org.briarproject.serial.SerialModule;
|
||||||
import org.briarproject.transport.TransportModule;
|
import org.briarproject.transport.TransportModule;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
@@ -42,39 +38,37 @@ import com.google.inject.Guice;
|
|||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
import com.google.inject.Module;
|
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
|
// FIXME: This is an integration test, not a unit test
|
||||||
|
|
||||||
private final Mockery context;
|
private final Mockery context;
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final ConnectionRegistry connRegistry;
|
private final Executor dbExecutor;
|
||||||
private final StreamWriterFactory connWriterFactory;
|
private final StreamWriterFactory streamWriterFactory;
|
||||||
private final PacketWriterFactory packetWriterFactory;
|
private final PacketWriterFactory packetWriterFactory;
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
private final MessageId messageId;
|
private final MessageId messageId;
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
private final byte[] secret;
|
private final byte[] secret;
|
||||||
|
|
||||||
public OutgoingSimplexConnectionTest() {
|
public SinglePassOutgoingSessionTest() {
|
||||||
context = new Mockery();
|
context = new Mockery();
|
||||||
db = context.mock(DatabaseComponent.class);
|
db = context.mock(DatabaseComponent.class);
|
||||||
|
dbExecutor = Executors.newSingleThreadExecutor();
|
||||||
Module testModule = new AbstractModule() {
|
Module testModule = new AbstractModule() {
|
||||||
@Override
|
@Override
|
||||||
public void configure() {
|
public void configure() {
|
||||||
bind(DatabaseComponent.class).toInstance(db);
|
bind(DatabaseComponent.class).toInstance(db);
|
||||||
bind(Executor.class).annotatedWith(
|
bind(Executor.class).annotatedWith(
|
||||||
DatabaseExecutor.class).toInstance(
|
DatabaseExecutor.class).toInstance(dbExecutor);
|
||||||
Executors.newCachedThreadPool());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Injector i = Guice.createInjector(testModule,
|
Injector i = Guice.createInjector(testModule,
|
||||||
new TestLifecycleModule(), new TestSystemModule(),
|
new TestLifecycleModule(), new TestSystemModule(),
|
||||||
new CryptoModule(), new EventModule(), new MessagingModule(),
|
new CryptoModule(), new EventModule(), new MessagingModule(),
|
||||||
new DuplexMessagingModule(), new SimplexMessagingModule(),
|
|
||||||
new SerialModule(), new TransportModule());
|
new SerialModule(), new TransportModule());
|
||||||
connRegistry = i.getInstance(ConnectionRegistry.class);
|
streamWriterFactory = i.getInstance(StreamWriterFactory.class);
|
||||||
connWriterFactory = i.getInstance(StreamWriterFactory.class);
|
|
||||||
packetWriterFactory = i.getInstance(PacketWriterFactory.class);
|
packetWriterFactory = i.getInstance(PacketWriterFactory.class);
|
||||||
contactId = new ContactId(234);
|
contactId = new ContactId(234);
|
||||||
messageId = new MessageId(TestUtils.getRandomId());
|
messageId = new MessageId(TestUtils.getRandomId());
|
||||||
@@ -83,34 +77,15 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
|
|||||||
new Random().nextBytes(secret);
|
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
|
@Test
|
||||||
public void testNothingToSend() throws Exception {
|
public void testNothingToSend() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
TestSimplexTransportWriter transport = new TestSimplexTransportWriter(
|
TestTransportConnectionWriter writer =
|
||||||
out, MIN_STREAM_LENGTH, Long.MAX_VALUE);
|
new TestTransportConnectionWriter(out);
|
||||||
StreamContext ctx = new StreamContext(contactId, transportId,
|
StreamContext ctx = new StreamContext(contactId, transportId,
|
||||||
secret, 0, true);
|
secret, 0, true);
|
||||||
OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db,
|
MessagingSession session = new SinglePassOutgoingSession(db, dbExecutor,
|
||||||
connRegistry, connWriterFactory, packetWriterFactory, ctx,
|
streamWriterFactory, packetWriterFactory, ctx, writer);
|
||||||
transport);
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// No transport acks to send
|
// No transport acks to send
|
||||||
oneOf(db).generateTransportAcks(contactId);
|
oneOf(db).generateTransportAcks(contactId);
|
||||||
@@ -141,25 +116,22 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
|
|||||||
with(any(long.class)));
|
with(any(long.class)));
|
||||||
will(returnValue(null));
|
will(returnValue(null));
|
||||||
}});
|
}});
|
||||||
connection.write();
|
session.run();
|
||||||
// Only the tag and an empty final frame should have been written
|
// Only the tag and an empty final frame should have been written
|
||||||
assertEquals(TAG_LENGTH + HEADER_LENGTH + MAC_LENGTH, out.size());
|
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();
|
context.assertIsSatisfied();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSomethingToSend() throws Exception {
|
public void testSomethingToSend() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
TestSimplexTransportWriter transport = new TestSimplexTransportWriter(
|
TestTransportConnectionWriter writer =
|
||||||
out, MIN_STREAM_LENGTH, Long.MAX_VALUE);
|
new TestTransportConnectionWriter(out);
|
||||||
StreamContext ctx = new StreamContext(contactId, transportId,
|
StreamContext ctx = new StreamContext(contactId, transportId,
|
||||||
secret, 0, true);
|
secret, 0, true);
|
||||||
OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db,
|
MessagingSession session = new SinglePassOutgoingSession(db, dbExecutor,
|
||||||
connRegistry, connWriterFactory, packetWriterFactory, ctx,
|
streamWriterFactory, packetWriterFactory,
|
||||||
transport);
|
ctx, writer);
|
||||||
final byte[] raw = new byte[1234];
|
final byte[] raw = new byte[1234];
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// No transport acks to send
|
// No transport acks to send
|
||||||
@@ -198,13 +170,10 @@ public class OutgoingSimplexConnectionTest extends BriarTestCase {
|
|||||||
with(any(long.class)));
|
with(any(long.class)));
|
||||||
will(returnValue(null));
|
will(returnValue(null));
|
||||||
}});
|
}});
|
||||||
connection.write();
|
session.run();
|
||||||
// Something should have been written
|
// Something should have been written
|
||||||
int overhead = TAG_LENGTH + HEADER_LENGTH + MAC_LENGTH;
|
int overhead = TAG_LENGTH + HEADER_LENGTH + MAC_LENGTH;
|
||||||
assertTrue(out.size() > overhead + UniqueId.LENGTH + raw.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();
|
context.assertIsSatisfied();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,8 @@ import java.util.Scanner;
|
|||||||
|
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
import org.briarproject.api.crypto.PseudoRandom;
|
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.DuplexPlugin;
|
||||||
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
|
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
|
||||||
|
|
||||||
@@ -21,12 +23,14 @@ abstract class DuplexTest {
|
|||||||
|
|
||||||
protected void sendChallengeReceiveResponse(DuplexTransportConnection d) {
|
protected void sendChallengeReceiveResponse(DuplexTransportConnection d) {
|
||||||
assert plugin != null;
|
assert plugin != null;
|
||||||
|
TransportConnectionReader r = d.getReader();
|
||||||
|
TransportConnectionWriter w = d.getWriter();
|
||||||
try {
|
try {
|
||||||
PrintStream out = new PrintStream(d.getOutputStream());
|
PrintStream out = new PrintStream(w.getOutputStream());
|
||||||
out.println(CHALLENGE);
|
out.println(CHALLENGE);
|
||||||
out.flush();
|
out.flush();
|
||||||
System.out.println("Sent challenge: " + CHALLENGE);
|
System.out.println("Sent challenge: " + CHALLENGE);
|
||||||
Scanner in = new Scanner(d.getInputStream());
|
Scanner in = new Scanner(r.getInputStream());
|
||||||
if(in.hasNextLine()) {
|
if(in.hasNextLine()) {
|
||||||
String response = in.nextLine();
|
String response = in.nextLine();
|
||||||
System.out.println("Received response: " + response);
|
System.out.println("Received response: " + response);
|
||||||
@@ -38,11 +42,13 @@ abstract class DuplexTest {
|
|||||||
} else {
|
} else {
|
||||||
System.out.println("No response");
|
System.out.println("No response");
|
||||||
}
|
}
|
||||||
d.dispose(false, true);
|
r.dispose(false, true);
|
||||||
|
w.dispose(false);
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
try {
|
try {
|
||||||
d.dispose(true, true);
|
r.dispose(true, true);
|
||||||
|
w.dispose(true);
|
||||||
} catch(IOException e1) {
|
} catch(IOException e1) {
|
||||||
e1.printStackTrace();
|
e1.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -51,13 +57,16 @@ abstract class DuplexTest {
|
|||||||
|
|
||||||
protected void receiveChallengeSendResponse(DuplexTransportConnection d) {
|
protected void receiveChallengeSendResponse(DuplexTransportConnection d) {
|
||||||
assert plugin != null;
|
assert plugin != null;
|
||||||
|
TransportConnectionReader r = d.getReader();
|
||||||
|
TransportConnectionWriter w = d.getWriter();
|
||||||
try {
|
try {
|
||||||
Scanner in = new Scanner(d.getInputStream());
|
Scanner in = new Scanner(r.getInputStream());
|
||||||
if(in.hasNextLine()) {
|
if(in.hasNextLine()) {
|
||||||
String challenge = in.nextLine();
|
String challenge = in.nextLine();
|
||||||
System.out.println("Received challenge: " + challenge);
|
System.out.println("Received challenge: " + challenge);
|
||||||
if(CHALLENGE.equals(challenge)) {
|
if(CHALLENGE.equals(challenge)) {
|
||||||
PrintStream out = new PrintStream(d.getOutputStream());
|
|
||||||
|
PrintStream out = new PrintStream(w.getOutputStream());
|
||||||
out.println(RESPONSE);
|
out.println(RESPONSE);
|
||||||
out.flush();
|
out.flush();
|
||||||
System.out.println("Sent response: " + RESPONSE);
|
System.out.println("Sent response: " + RESPONSE);
|
||||||
@@ -67,11 +76,13 @@ abstract class DuplexTest {
|
|||||||
} else {
|
} else {
|
||||||
System.out.println("No challenge");
|
System.out.println("No challenge");
|
||||||
}
|
}
|
||||||
d.dispose(false, true);
|
r.dispose(false, true);
|
||||||
|
w.dispose(false);
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
try {
|
try {
|
||||||
d.dispose(true, true);
|
r.dispose(true, true);
|
||||||
|
w.dispose(true);
|
||||||
} catch(IOException e1) {
|
} catch(IOException e1) {
|
||||||
e1.printStackTrace();
|
e1.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import org.briarproject.BriarTestCase;
|
|||||||
import org.briarproject.TestFileUtils;
|
import org.briarproject.TestFileUtils;
|
||||||
import org.briarproject.TestUtils;
|
import org.briarproject.TestUtils;
|
||||||
import org.briarproject.api.ContactId;
|
import org.briarproject.api.ContactId;
|
||||||
|
import org.briarproject.api.plugins.TransportConnectionWriter;
|
||||||
import org.briarproject.api.plugins.simplex.SimplexPluginCallback;
|
import org.briarproject.api.plugins.simplex.SimplexPluginCallback;
|
||||||
import org.briarproject.api.plugins.simplex.SimplexTransportWriter;
|
|
||||||
import org.briarproject.api.system.FileUtils;
|
import org.briarproject.api.system.FileUtils;
|
||||||
import org.briarproject.plugins.ImmediateExecutor;
|
import org.briarproject.plugins.ImmediateExecutor;
|
||||||
import org.briarproject.plugins.file.RemovableDriveMonitor.Callback;
|
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 ContactId contactId = new ContactId(234);
|
||||||
private final FileUtils fileUtils = new TestFileUtils();
|
private final FileUtils fileUtils = new TestFileUtils();
|
||||||
|
|
||||||
|
@Override
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
testDir.mkdirs();
|
testDir.mkdirs();
|
||||||
@@ -253,7 +254,7 @@ public class RemovableDrivePluginTest extends BriarTestCase {
|
|||||||
fileUtils, callback, finder, monitor, MAX_FRAME_LENGTH, 0);
|
fileUtils, callback, finder, monitor, MAX_FRAME_LENGTH, 0);
|
||||||
plugin.start();
|
plugin.start();
|
||||||
|
|
||||||
SimplexTransportWriter writer = plugin.createWriter(contactId);
|
TransportConnectionWriter writer = plugin.createWriter(contactId);
|
||||||
assertNotNull(writer);
|
assertNotNull(writer);
|
||||||
// The output file should exist and should be empty
|
// The output file should exist and should be empty
|
||||||
File[] files = drive1.listFiles();
|
File[] files = drive1.listFiles();
|
||||||
@@ -352,6 +353,7 @@ public class RemovableDrivePluginTest extends BriarTestCase {
|
|||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
TestUtils.deleteTestDirectory(testDir);
|
TestUtils.deleteTestDirectory(testDir);
|
||||||
|
|||||||
@@ -278,7 +278,8 @@ public class ModemPluginTest extends BriarTestCase {
|
|||||||
public Object invoke(Invocation invocation) throws Throwable {
|
public Object invoke(Invocation invocation) throws Throwable {
|
||||||
DuplexTransportConnection conn =
|
DuplexTransportConnection conn =
|
||||||
(DuplexTransportConnection) invocation.getParameter(1);
|
(DuplexTransportConnection) invocation.getParameter(1);
|
||||||
conn.dispose(false, true);
|
conn.getReader().dispose(false, true);
|
||||||
|
conn.getWriter().dispose(false);
|
||||||
invoked.countDown();
|
invoked.countDown();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,8 @@ public class LanTcpPluginTest extends BriarTestCase {
|
|||||||
assertTrue(latch.await(5, SECONDS));
|
assertTrue(latch.await(5, SECONDS));
|
||||||
assertFalse(error.get());
|
assertFalse(error.get());
|
||||||
// Clean up
|
// Clean up
|
||||||
d.dispose(false, true);
|
d.getReader().dispose(false, true);
|
||||||
|
d.getWriter().dispose(false);
|
||||||
ss.close();
|
ss.close();
|
||||||
plugin.stop();
|
plugin.stop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase {
|
|||||||
// FIXME: This is an integration test, not a unit test
|
// FIXME: This is an integration test, not a unit test
|
||||||
|
|
||||||
private static final int FRAME_LENGTH = 1024;
|
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 CryptoComponent crypto;
|
||||||
private final AuthenticatedCipher frameCipher;
|
private final AuthenticatedCipher frameCipher;
|
||||||
@@ -56,7 +54,7 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase {
|
|||||||
// Check that the actual tag and ciphertext match what's expected
|
// Check that the actual tag and ciphertext match what's expected
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out,
|
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);
|
o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], payloadLength, false);
|
||||||
byte[] actual = out.toByteArray();
|
byte[] actual = out.toByteArray();
|
||||||
assertEquals(TAG_LENGTH + FRAME_LENGTH, actual.length);
|
assertEquals(TAG_LENGTH + FRAME_LENGTH, actual.length);
|
||||||
@@ -67,93 +65,14 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInitiatorClosesConnectionWithoutWriting() throws Exception {
|
public void testCloseConnectionWithoutWriting() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
// Initiator's constructor
|
// Initiator's constructor
|
||||||
OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out,
|
OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out,
|
||||||
10 * FRAME_LENGTH, frameCipher, crypto.generateSecretKey(),
|
frameCipher, crypto.generateSecretKey(), FRAME_LENGTH, tag);
|
||||||
FRAME_LENGTH, tag);
|
|
||||||
// Write an empty final frame without having written any other frames
|
// Write an empty final frame without having written any other frames
|
||||||
o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], 0, true);
|
o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], 0, true);
|
||||||
// The tag and the empty frame should be written to the output stream
|
// The tag and the empty frame should be written to the output stream
|
||||||
assertEquals(TAG_LENGTH + HEADER_LENGTH + MAC_LENGTH, out.size());
|
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
package org.briarproject.transport;
|
package org.briarproject.transport;
|
||||||
|
|
||||||
import static org.briarproject.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
|
import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
|
||||||
import static org.briarproject.api.transport.TransportConstants.MIN_STREAM_LENGTH;
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
@@ -14,13 +13,9 @@ import java.util.Random;
|
|||||||
import org.briarproject.BriarTestCase;
|
import org.briarproject.BriarTestCase;
|
||||||
import org.briarproject.TestLifecycleModule;
|
import org.briarproject.TestLifecycleModule;
|
||||||
import org.briarproject.TestSystemModule;
|
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.AuthenticatedCipher;
|
||||||
import org.briarproject.api.crypto.CryptoComponent;
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.api.crypto.SecretKey;
|
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.api.transport.StreamWriterFactory;
|
||||||
import org.briarproject.crypto.CryptoModule;
|
import org.briarproject.crypto.CryptoModule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -35,13 +30,10 @@ public class TransportIntegrationTest extends BriarTestCase {
|
|||||||
private final int FRAME_LENGTH = 2048;
|
private final int FRAME_LENGTH = 2048;
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto;
|
||||||
private final StreamWriterFactory streamWriterFactory;
|
|
||||||
private final ContactId contactId;
|
|
||||||
private final TransportId transportId;
|
|
||||||
private final AuthenticatedCipher frameCipher;
|
private final AuthenticatedCipher frameCipher;
|
||||||
private final Random random;
|
private final Random random;
|
||||||
private final byte[] secret;
|
private final byte[] secret;
|
||||||
private final SecretKey frameKey;
|
private final SecretKey tagKey, frameKey;
|
||||||
|
|
||||||
public TransportIntegrationTest() {
|
public TransportIntegrationTest() {
|
||||||
Module testModule = new AbstractModule() {
|
Module testModule = new AbstractModule() {
|
||||||
@@ -54,15 +46,13 @@ public class TransportIntegrationTest extends BriarTestCase {
|
|||||||
Injector i = Guice.createInjector(testModule, new CryptoModule(),
|
Injector i = Guice.createInjector(testModule, new CryptoModule(),
|
||||||
new TestLifecycleModule(), new TestSystemModule());
|
new TestLifecycleModule(), new TestSystemModule());
|
||||||
crypto = i.getInstance(CryptoComponent.class);
|
crypto = i.getInstance(CryptoComponent.class);
|
||||||
streamWriterFactory = i.getInstance(StreamWriterFactory.class);
|
|
||||||
contactId = new ContactId(234);
|
|
||||||
transportId = new TransportId("id");
|
|
||||||
frameCipher = crypto.getFrameCipher();
|
frameCipher = crypto.getFrameCipher();
|
||||||
random = new Random();
|
random = new Random();
|
||||||
// Since we're sending frames to ourselves, we only need outgoing keys
|
// Since we're sending frames to ourselves, we only need outgoing keys
|
||||||
secret = new byte[32];
|
secret = new byte[32];
|
||||||
random.nextBytes(secret);
|
random.nextBytes(secret);
|
||||||
frameKey = crypto.deriveFrameKey(secret, 0, true, true);
|
tagKey = crypto.deriveTagKey(secret, true);
|
||||||
|
frameKey = crypto.deriveFrameKey(secret, 0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -76,6 +66,9 @@ public class TransportIntegrationTest extends BriarTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void testWriteAndRead(boolean initiator) throws Exception {
|
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
|
// Generate two random frames
|
||||||
byte[] frame = new byte[1234];
|
byte[] frame = new byte[1234];
|
||||||
random.nextBytes(frame);
|
random.nextBytes(frame);
|
||||||
@@ -83,87 +76,46 @@ public class TransportIntegrationTest extends BriarTestCase {
|
|||||||
random.nextBytes(frame1);
|
random.nextBytes(frame1);
|
||||||
// Copy the frame key - the copy will be erased
|
// Copy the frame key - the copy will be erased
|
||||||
SecretKey frameCopy = frameKey.copy();
|
SecretKey frameCopy = frameKey.copy();
|
||||||
// Write the frames
|
// Write the tag and the frames
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
FrameWriter encryptionOut = new OutgoingEncryptionLayer(out,
|
FrameWriter frameWriter = new OutgoingEncryptionLayer(out,
|
||||||
Long.MAX_VALUE, frameCipher, frameCopy, FRAME_LENGTH);
|
frameCipher, frameCopy, FRAME_LENGTH, tag);
|
||||||
StreamWriterImpl writer = new StreamWriterImpl(encryptionOut,
|
StreamWriterImpl streamWriter = new StreamWriterImpl(frameWriter,
|
||||||
FRAME_LENGTH);
|
FRAME_LENGTH);
|
||||||
OutputStream out1 = writer.getOutputStream();
|
OutputStream out1 = streamWriter.getOutputStream();
|
||||||
out1.write(frame);
|
out1.write(frame);
|
||||||
out1.flush();
|
out1.flush();
|
||||||
out1.write(frame1);
|
out1.write(frame1);
|
||||||
out1.flush();
|
out1.flush();
|
||||||
byte[] output = out.toByteArray();
|
byte[] output = out.toByteArray();
|
||||||
assertEquals(FRAME_LENGTH * 2, output.length);
|
assertEquals(TAG_LENGTH + FRAME_LENGTH * 2, output.length);
|
||||||
// Read the tag and the frames back
|
// Read the tag back
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(output);
|
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);
|
frameKey, FRAME_LENGTH);
|
||||||
StreamReaderImpl reader = new StreamReaderImpl(encryptionIn,
|
StreamReaderImpl streamReader = new StreamReaderImpl(frameReader,
|
||||||
FRAME_LENGTH);
|
FRAME_LENGTH);
|
||||||
InputStream in1 = reader.getInputStream();
|
InputStream in1 = streamReader.getInputStream();
|
||||||
byte[] recovered = new byte[frame.length];
|
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;
|
int offset = 0;
|
||||||
while(offset < recovered.length) {
|
while(offset < dest.length) {
|
||||||
int read = in1.read(recovered, offset, recovered.length - offset);
|
int read = in.read(dest, offset, dest.length - offset);
|
||||||
if(read == -1) break;
|
if(read == -1) break;
|
||||||
offset += read;
|
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);
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user