Decoupled ProtocolReader (which belongs in the protocol component)

from PacketReader (which belongs in the transport component).
This commit is contained in:
akwizgran
2011-08-13 14:18:16 +02:00
parent 5b6fecfb43
commit 9d25a819d1
17 changed files with 591 additions and 221 deletions

View File

@@ -12,6 +12,7 @@ import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageEncoder;
import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.TransportUpdate;
@@ -28,11 +29,12 @@ public class ProtocolModule extends AbstractModule {
bind(AuthorFactory.class).to(AuthorFactoryImpl.class);
bind(BatchFactory.class).to(BatchFactoryImpl.class);
bind(GroupFactory.class).to(GroupFactoryImpl.class);
bind(MessageEncoder.class).to(MessageEncoderImpl.class);
bind(OfferFactory.class).to(OfferFactoryImpl.class);
bind(ProtocolReaderFactory.class).to(ProtocolReaderFactoryImpl.class);
bind(RequestFactory.class).to(RequestFactoryImpl.class);
bind(SubscriptionFactory.class).to(SubscriptionFactoryImpl.class);
bind(TransportFactory.class).to(TransportFactoryImpl.class);
bind(MessageEncoder.class).to(MessageEncoderImpl.class);
}
@Provides

View File

@@ -0,0 +1,51 @@
package net.sf.briar.protocol;
import java.io.InputStream;
import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.ProtocolReader;
import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.TransportUpdate;
import net.sf.briar.api.serial.ObjectReader;
import net.sf.briar.api.serial.ReaderFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
class ProtocolReaderFactoryImpl implements ProtocolReaderFactory {
private final ReaderFactory readerFactory;
private final Provider<ObjectReader<Ack>> ackProvider;
private final Provider<ObjectReader<Batch>> batchProvider;
private final Provider<ObjectReader<Offer>> offerProvider;
private final Provider<ObjectReader<Request>> requestProvider;
private final Provider<ObjectReader<SubscriptionUpdate>> subscriptionProvider;
private final Provider<ObjectReader<TransportUpdate>> transportProvider;
@Inject
ProtocolReaderFactoryImpl(ReaderFactory readerFactory,
Provider<ObjectReader<Ack>> ackProvider,
Provider<ObjectReader<Batch>> batchProvider,
Provider<ObjectReader<Offer>> offerProvider,
Provider<ObjectReader<Request>> requestProvider,
Provider<ObjectReader<SubscriptionUpdate>> subscriptionProvider,
Provider<ObjectReader<TransportUpdate>> transportProvider) {
this.readerFactory = readerFactory;
this.ackProvider = ackProvider;
this.batchProvider = batchProvider;
this.offerProvider = offerProvider;
this.requestProvider = requestProvider;
this.subscriptionProvider = subscriptionProvider;
this.transportProvider = transportProvider;
}
public ProtocolReader createProtocolReader(InputStream in) {
return new ProtocolReaderImpl(in, readerFactory, ackProvider.get(),
batchProvider.get(), offerProvider.get(), requestProvider.get(),
subscriptionProvider.get(), transportProvider.get());
}
}

View File

@@ -0,0 +1,85 @@
package net.sf.briar.protocol;
import java.io.IOException;
import java.io.InputStream;
import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.ProtocolReader;
import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.Tags;
import net.sf.briar.api.protocol.TransportUpdate;
import net.sf.briar.api.serial.ObjectReader;
import net.sf.briar.api.serial.Reader;
import net.sf.briar.api.serial.ReaderFactory;
class ProtocolReaderImpl implements ProtocolReader {
private final Reader reader;
ProtocolReaderImpl(InputStream in, ReaderFactory readerFactory,
ObjectReader<Ack> ackReader, ObjectReader<Batch> batchReader,
ObjectReader<Offer> offerReader,
ObjectReader<Request> requestReader,
ObjectReader<SubscriptionUpdate> subscriptionReader,
ObjectReader<TransportUpdate> transportReader) {
reader = readerFactory.createReader(in);
reader.addObjectReader(Tags.ACK, ackReader);
reader.addObjectReader(Tags.BATCH, batchReader);
reader.addObjectReader(Tags.OFFER, offerReader);
reader.addObjectReader(Tags.REQUEST, requestReader);
reader.addObjectReader(Tags.SUBSCRIPTIONS, subscriptionReader);
reader.addObjectReader(Tags.TRANSPORTS, transportReader);
}
public boolean hasAck() throws IOException {
return reader.hasUserDefined(Tags.ACK);
}
public Ack readAck() throws IOException {
return reader.readUserDefined(Tags.ACK, Ack.class);
}
public boolean hasBatch() throws IOException {
return reader.hasUserDefined(Tags.BATCH);
}
public Batch readBatch() throws IOException {
return reader.readUserDefined(Tags.BATCH, Batch.class);
}
public boolean hasOffer() throws IOException {
return reader.hasUserDefined(Tags.OFFER);
}
public Offer readOffer() throws IOException {
return reader.readUserDefined(Tags.OFFER, Offer.class);
}
public boolean hasRequest() throws IOException {
return reader.hasUserDefined(Tags.REQUEST);
}
public Request readRequest() throws IOException {
return reader.readUserDefined(Tags.REQUEST, Request.class);
}
public boolean hasSubscriptionUpdate() throws IOException {
return reader.hasUserDefined(Tags.SUBSCRIPTIONS);
}
public SubscriptionUpdate readSubscriptionUpdate() throws IOException {
return reader.readUserDefined(Tags.SUBSCRIPTIONS,
SubscriptionUpdate.class);
}
public boolean hasTransportUpdate() throws IOException {
return reader.hasUserDefined(Tags.TRANSPORTS);
}
public TransportUpdate readTransportUpdate() throws IOException {
return reader.readUserDefined(Tags.TRANSPORTS, TransportUpdate.class);
}
}

View File

@@ -8,6 +8,10 @@ interface PacketDecrypter {
/** Returns the input stream from which packets should be read. */
InputStream getInputStream();
/** Reads, decrypts and returns a tag from the underlying input stream. */
/**
* Reads, decrypts and returns a tag from the underlying input stream.
* Returns null if the end of the input stream is reached before any bytes
* are read.
*/
byte[] readTag() throws IOException;
}

View File

@@ -54,9 +54,11 @@ class PacketDecrypterImpl extends FilterInputStream implements PacketDecrypter {
bufOff = bufLen = 0;
while(offset < tag.length) {
int read = in.read(tag, offset, tag.length - offset);
if(read == -1) throw new EOFException();
if(read == -1) break;
offset += read;
}
if(offset == 0) return null; // EOF between packets is acceptable
if(offset < tag.length) throw new EOFException();
betweenPackets = false;
try {
byte[] decryptedTag = tagCipher.doFinal(tag);

View File

@@ -8,47 +8,18 @@ import javax.crypto.Mac;
import javax.crypto.SecretKey;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.TransportUpdate;
import net.sf.briar.api.serial.ObjectReader;
import net.sf.briar.api.serial.ReaderFactory;
import net.sf.briar.api.transport.PacketReader;
import net.sf.briar.api.transport.PacketReaderFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
class PacketReaderFactoryImpl implements PacketReaderFactory {
private final CryptoComponent crypto;
private final ReaderFactory readerFactory;
private final Provider<ObjectReader<Ack>> ackProvider;
private final Provider<ObjectReader<Batch>> batchProvider;
private final Provider<ObjectReader<Offer>> offerProvider;
private final Provider<ObjectReader<Request>> requestProvider;
private final Provider<ObjectReader<SubscriptionUpdate>> subscriptionProvider;
private final Provider<ObjectReader<TransportUpdate>> transportProvider;
@Inject
PacketReaderFactoryImpl(CryptoComponent crypto, ReaderFactory readerFactory,
Provider<ObjectReader<Ack>> ackProvider,
Provider<ObjectReader<Batch>> batchProvider,
Provider<ObjectReader<Offer>> offerProvider,
Provider<ObjectReader<Request>> requestProvider,
Provider<ObjectReader<SubscriptionUpdate>> subscriptionProvider,
Provider<ObjectReader<TransportUpdate>> transportProvider) {
PacketReaderFactoryImpl(CryptoComponent crypto) {
this.crypto = crypto;
this.readerFactory = readerFactory;
this.ackProvider = ackProvider;
this.batchProvider = batchProvider;
this.offerProvider = offerProvider;
this.requestProvider = requestProvider;
this.subscriptionProvider = subscriptionProvider;
this.transportProvider = transportProvider;
}
public PacketReader createPacketReader(byte[] firstTag, InputStream in,
@@ -66,9 +37,6 @@ class PacketReaderFactoryImpl implements PacketReaderFactory {
}
PacketDecrypter decrypter = new PacketDecrypterImpl(firstTag, in,
tagCipher, packetCipher, tagKey, packetKey);
return new PacketReaderImpl(firstTag, readerFactory, ackProvider.get(),
batchProvider.get(), offerProvider.get(), requestProvider.get(),
subscriptionProvider.get(), transportProvider.get(),
decrypter, mac, transportId, connection);
return new PacketReaderImpl(decrypter, mac, transportId, connection);
}
}

View File

@@ -1,27 +1,18 @@
package net.sf.briar.transport;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.crypto.Mac;
import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.Tags;
import net.sf.briar.api.protocol.TransportUpdate;
import net.sf.briar.api.serial.FormatException;
import net.sf.briar.api.serial.ObjectReader;
import net.sf.briar.api.serial.Reader;
import net.sf.briar.api.serial.ReaderFactory;
import net.sf.briar.api.transport.PacketReader;
class PacketReaderImpl implements PacketReader {
class PacketReaderImpl extends FilterInputStream implements PacketReader {
private final Reader reader;
private final PacketDecrypter decrypter;
private final Mac mac;
private final int macLength, transportId;
@@ -30,23 +21,9 @@ class PacketReaderImpl implements PacketReader {
private long packet = 0L;
private boolean betweenPackets = true;
PacketReaderImpl(byte[] firstTag, ReaderFactory readerFactory,
ObjectReader<Ack> ackReader, ObjectReader<Batch> batchReader,
ObjectReader<Offer> offerReader,
ObjectReader<Request> requestReader,
ObjectReader<SubscriptionUpdate> subscriptionReader,
ObjectReader<TransportUpdate> transportReader,
PacketDecrypter decrypter, Mac mac, int transportId,
PacketReaderImpl(PacketDecrypter decrypter, Mac mac, int transportId,
long connection) {
InputStream in = decrypter.getInputStream();
reader = readerFactory.createReader(in);
reader.addObjectReader(Tags.ACK, ackReader);
reader.addObjectReader(Tags.BATCH, batchReader);
reader.addObjectReader(Tags.OFFER, offerReader);
reader.addObjectReader(Tags.REQUEST, requestReader);
reader.addObjectReader(Tags.SUBSCRIPTIONS, subscriptionReader);
reader.addObjectReader(Tags.TRANSPORTS, transportReader);
reader.addConsumer(new MacConsumer(mac));
super(decrypter.getInputStream());
this.decrypter = decrypter;
this.mac = mac;
macLength = mac.getMacLength();
@@ -54,32 +31,36 @@ class PacketReaderImpl implements PacketReader {
this.connection = connection;
}
public boolean hasAck() throws IOException {
public InputStream getInputStream() {
return this;
}
public void finishPacket() throws IOException, GeneralSecurityException {
if(!betweenPackets) readMac();
}
@Override
public int read() throws IOException {
if(betweenPackets) readTag();
return reader.hasUserDefined(Tags.ACK);
int i = in.read();
if(i != -1) mac.update((byte) i);
return i;
}
private void readTag() throws IOException {
assert betweenPackets;
if(packet > Constants.MAX_32_BIT_UNSIGNED)
throw new IllegalStateException();
byte[] tag = decrypter.readTag();
if(!TagDecoder.decodeTag(tag, transportId, connection, packet))
throw new FormatException();
mac.update(tag);
packet++;
betweenPackets = false;
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
public Ack readAck() throws IOException {
@Override
public int read(byte[] b, int off, int len) throws IOException {
if(betweenPackets) readTag();
Ack a = reader.readUserDefined(Tags.ACK, Ack.class);
readMac();
betweenPackets = true;
return a;
int i = in.read(b, off, len);
if(i != -1) mac.update(b, off, i);
return i;
}
private void readMac() throws IOException {
private void readMac() throws IOException, GeneralSecurityException {
byte[] expectedMac = mac.doFinal();
byte[] actualMac = new byte[macLength];
InputStream in = decrypter.getInputStream();
@@ -89,74 +70,22 @@ class PacketReaderImpl implements PacketReader {
if(read == -1) break;
offset += read;
}
if(offset < macLength) throw new FormatException();
if(!Arrays.equals(expectedMac, actualMac)) throw new FormatException();
}
public boolean hasBatch() throws IOException {
if(betweenPackets) readTag();
return reader.hasUserDefined(Tags.BATCH);
}
public Batch readBatch() throws IOException {
if(betweenPackets) readTag();
Batch b = reader.readUserDefined(Tags.BATCH, Batch.class);
readMac();
if(offset < macLength) throw new GeneralSecurityException();
if(!Arrays.equals(expectedMac, actualMac))
throw new GeneralSecurityException();
betweenPackets = true;
return b;
}
public boolean hasOffer() throws IOException {
if(betweenPackets) readTag();
return reader.hasUserDefined(Tags.OFFER);
}
public Offer readOffer() throws IOException {
if(betweenPackets) readTag();
Offer o = reader.readUserDefined(Tags.OFFER, Offer.class);
readMac();
betweenPackets = true;
return o;
}
public boolean hasRequest() throws IOException {
if(betweenPackets) readTag();
return reader.hasUserDefined(Tags.REQUEST);
}
public Request readRequest() throws IOException {
if(betweenPackets) readTag();
Request r = reader.readUserDefined(Tags.REQUEST, Request.class);
readMac();
betweenPackets = true;
return r;
}
public boolean hasSubscriptionUpdate() throws IOException {
if(betweenPackets) readTag();
return reader.hasUserDefined(Tags.SUBSCRIPTIONS);
}
public SubscriptionUpdate readSubscriptionUpdate() throws IOException {
if(betweenPackets) readTag();
SubscriptionUpdate s = reader.readUserDefined(Tags.SUBSCRIPTIONS,
SubscriptionUpdate.class);
readMac();
betweenPackets = true;
return s;
}
public boolean hasTransportUpdate() throws IOException {
if(betweenPackets) readTag();
return reader.hasUserDefined(Tags.TRANSPORTS);
}
public TransportUpdate readTransportUpdate() throws IOException {
if(betweenPackets) readTag();
TransportUpdate t = reader.readUserDefined(Tags.TRANSPORTS,
TransportUpdate.class);
readMac();
betweenPackets = true;
return t;
private void readTag() throws IOException {
assert betweenPackets;
if(packet > Constants.MAX_32_BIT_UNSIGNED)
throw new IllegalStateException();
byte[] tag = decrypter.readTag();
if(tag == null) return; // EOF
if(!TagDecoder.decodeTag(tag, transportId, connection, packet))
throw new FormatException();
mac.update(tag);
packet++;
betweenPackets = false;
}
}

View File

@@ -37,7 +37,7 @@ class PacketWriterImpl extends FilterOutputStream implements PacketWriter {
return this;
}
public void nextPacket() throws IOException {
public void finishPacket() throws IOException {
if(!betweenPackets) writeMac();
}
@@ -50,9 +50,7 @@ class PacketWriterImpl extends FilterOutputStream implements PacketWriter {
@Override
public void write(byte[] b) throws IOException {
if(betweenPackets) writeTag();
out.write(b);
mac.update(b);
write(b, 0, b.length);
}
@Override