diff --git a/api/net/sf/briar/api/protocol/Tags.java b/api/net/sf/briar/api/protocol/Tags.java index 101b1e6577017ecb940230243c266cc99edc5feb..7013d9c665f1314527687494cb7eccdcf3d5743b 100644 --- a/api/net/sf/briar/api/protocol/Tags.java +++ b/api/net/sf/briar/api/protocol/Tags.java @@ -19,6 +19,7 @@ public interface Tags { static final int OFFER = 9; static final int OFFER_ID = 10; static final int REQUEST = 11; - static final int SUBSCRIPTIONS = 12; - static final int TRANSPORTS = 13; + static final int SUBSCRIPTION_UPDATE = 12; + static final int TRANSPORT_PROPERTIES = 13; + static final int TRANSPORT_UPDATE = 14; } diff --git a/api/net/sf/briar/api/serial/Reader.java b/api/net/sf/briar/api/serial/Reader.java index 9b8960229ce5b12ec23696ae473e2f4e1f106663..c00acfe934d1276a982d7187668287258759dffc 100644 --- a/api/net/sf/briar/api/serial/Reader.java +++ b/api/net/sf/briar/api/serial/Reader.java @@ -9,6 +9,12 @@ public interface Reader { boolean eof() throws IOException; void close() throws IOException; + void setMaxStringLength(int length); + void resetMaxStringLength(); + + void setMaxBytesLength(int length); + void resetMaxBytesLength(); + void addConsumer(Consumer c); void removeConsumer(Consumer c); diff --git a/components/net/sf/briar/protocol/ProtocolReaderImpl.java b/components/net/sf/briar/protocol/ProtocolReaderImpl.java index 5797d89b3d08b287340120fb6cb261fd0c98af05..d79d8a4e40cbf2980507a78fdecd29ebc9cf41f5 100644 --- a/components/net/sf/briar/protocol/ProtocolReaderImpl.java +++ b/components/net/sf/briar/protocol/ProtocolReaderImpl.java @@ -30,8 +30,8 @@ class ProtocolReaderImpl implements ProtocolReader { 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.addObjectReader(Tags.SUBSCRIPTION_UPDATE, subscriptionReader); + reader.addObjectReader(Tags.TRANSPORT_UPDATE, transportReader); } public boolean hasAck() throws IOException { @@ -67,19 +67,19 @@ class ProtocolReaderImpl implements ProtocolReader { } public boolean hasSubscriptionUpdate() throws IOException { - return reader.hasUserDefined(Tags.SUBSCRIPTIONS); + return reader.hasUserDefined(Tags.SUBSCRIPTION_UPDATE); } public SubscriptionUpdate readSubscriptionUpdate() throws IOException { - return reader.readUserDefined(Tags.SUBSCRIPTIONS, + return reader.readUserDefined(Tags.SUBSCRIPTION_UPDATE, SubscriptionUpdate.class); } public boolean hasTransportUpdate() throws IOException { - return reader.hasUserDefined(Tags.TRANSPORTS); + return reader.hasUserDefined(Tags.TRANSPORT_UPDATE); } public TransportUpdate readTransportUpdate() throws IOException { - return reader.readUserDefined(Tags.TRANSPORTS, TransportUpdate.class); + return reader.readUserDefined(Tags.TRANSPORT_UPDATE, TransportUpdate.class); } } diff --git a/components/net/sf/briar/protocol/SubscriptionReader.java b/components/net/sf/briar/protocol/SubscriptionReader.java index 8faaf647e1b9283f7f2f791575e26f51a0295c45..7ea4cacb356e1ecb3c398f128054c89fec22c99a 100644 --- a/components/net/sf/briar/protocol/SubscriptionReader.java +++ b/components/net/sf/briar/protocol/SubscriptionReader.java @@ -26,7 +26,7 @@ class SubscriptionReader implements ObjectReader<SubscriptionUpdate> { Consumer counting = new CountingConsumer(SubscriptionUpdate.MAX_SIZE); // Read the data r.addConsumer(counting); - r.readUserDefinedTag(Tags.SUBSCRIPTIONS); + r.readUserDefinedTag(Tags.SUBSCRIPTION_UPDATE); r.addObjectReader(Tags.GROUP, groupReader); Map<Group, Long> subs = r.readMap(Group.class, Long.class); r.removeObjectReader(Tags.GROUP); diff --git a/components/net/sf/briar/protocol/TransportReader.java b/components/net/sf/briar/protocol/TransportReader.java index 2ea5e61b3bc548e8615af0ad3ae9a23d186c9986..fe729f3f0ddcefbe24422fe9f93c7baf8df0bea1 100644 --- a/components/net/sf/briar/protocol/TransportReader.java +++ b/components/net/sf/briar/protocol/TransportReader.java @@ -1,6 +1,7 @@ package net.sf.briar.protocol; import java.io.IOException; +import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -13,9 +14,11 @@ import net.sf.briar.api.serial.Reader; class TransportReader implements ObjectReader<TransportUpdate> { private final TransportFactory transportFactory; + private final ObjectReader<TransportProperties> propertiesReader; TransportReader(TransportFactory transportFactory) { this.transportFactory = transportFactory; + propertiesReader = new TransportPropertiesReader(); } public TransportUpdate readObject(Reader r) throws IOException { @@ -23,27 +26,41 @@ class TransportReader implements ObjectReader<TransportUpdate> { Consumer counting = new CountingConsumer(TransportUpdate.MAX_SIZE); // Read the data r.addConsumer(counting); - r.readUserDefinedTag(Tags.TRANSPORTS); - // Transport maps are always written in delimited form + r.readUserDefinedTag(Tags.TRANSPORT_UPDATE); + r.addObjectReader(Tags.TRANSPORT_PROPERTIES, propertiesReader); + r.setMaxStringLength(TransportUpdate.MAX_SIZE); + List<TransportProperties> l = r.readList(TransportProperties.class); + r.resetMaxStringLength(); + r.removeObjectReader(Tags.TRANSPORT_PROPERTIES); Map<String, Map<String, String>> transports = new TreeMap<String, Map<String, String>>(); - r.readMapStart(); - while(!r.hasMapEnd()) { - String name = r.readString(TransportUpdate.MAX_SIZE); - Map<String, String> properties = new TreeMap<String, String>(); - r.readMapStart(); - while(!r.hasMapEnd()) { - String key = r.readString(TransportUpdate.MAX_SIZE); - String value = r.readString(TransportUpdate.MAX_SIZE); - properties.put(key, value); - } - r.readMapEnd(); - transports.put(name, properties); - } - r.readMapEnd(); + for(TransportProperties t : l) transports.put(t.name, t.properties); long timestamp = r.readInt64(); r.removeConsumer(counting); // Build and return the transport update return transportFactory.createTransports(transports, timestamp); } + + private static class TransportProperties { + + private final String name; + private final Map<String, String> properties; + + TransportProperties(String name, Map<String, String> properties) { + this.name = name; + this.properties = properties; + } + } + + private static class TransportPropertiesReader + implements ObjectReader<TransportProperties> { + + public TransportProperties readObject(Reader r) throws IOException { + r.readUserDefinedTag(Tags.TRANSPORT_PROPERTIES); + String name = r.readString(); + Map<String, String> properties = + r.readMap(String.class, String.class); + return new TransportProperties(name, properties); + } + } } diff --git a/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java b/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java index 3b591f408bb72e7f632fb3605d700b6d52a3e155..b8f080e27ba640e46ace381ba2d2796ecc8f24e1 100644 --- a/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java +++ b/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java @@ -21,7 +21,7 @@ class SubscriptionWriterImpl implements SubscriptionWriter { } public void writeSubscriptions(Map<Group, Long> subs) throws IOException { - w.writeUserDefinedTag(Tags.SUBSCRIPTIONS); + w.writeUserDefinedTag(Tags.SUBSCRIPTION_UPDATE); w.writeMap(subs); w.writeInt64(System.currentTimeMillis()); out.flush(); diff --git a/components/net/sf/briar/protocol/writers/TransportWriterImpl.java b/components/net/sf/briar/protocol/writers/TransportWriterImpl.java index 89dc7ac0d346dc5968f98ccbcc927daa4755b6db..8381f4d0648ce52f10ecc69bf2d11e8633cd7b4a 100644 --- a/components/net/sf/briar/protocol/writers/TransportWriterImpl.java +++ b/components/net/sf/briar/protocol/writers/TransportWriterImpl.java @@ -22,19 +22,14 @@ class TransportWriterImpl implements TransportWriter { public void writeTransports(Map<String, Map<String, String>> transports) throws IOException { - w.writeUserDefinedTag(Tags.TRANSPORTS); - // Transport maps are always written in delimited form - w.writeMapStart(); + w.writeUserDefinedTag(Tags.TRANSPORT_UPDATE); + w.writeListStart(); for(Entry<String, Map<String, String>> e : transports.entrySet()) { + w.writeUserDefinedTag(Tags.TRANSPORT_PROPERTIES); w.writeString(e.getKey()); - w.writeMapStart(); - for(Entry<String, String> e1 : e.getValue().entrySet()) { - w.writeString(e1.getKey()); - w.writeString(e1.getValue()); - } - w.writeMapEnd(); + w.writeMap(e.getValue()); } - w.writeMapEnd(); + w.writeListEnd(); w.writeInt64(System.currentTimeMillis()); out.flush(); } diff --git a/components/net/sf/briar/serial/ReaderImpl.java b/components/net/sf/briar/serial/ReaderImpl.java index 92cb7a8652fc0b8d3afe3926cc77652126b4df43..63b9e4ef0c9796ae8567edd859a596e03d5dd081 100644 --- a/components/net/sf/briar/serial/ReaderImpl.java +++ b/components/net/sf/briar/serial/ReaderImpl.java @@ -26,6 +26,8 @@ class ReaderImpl implements Reader { private boolean hasLookahead = false, eof = false; private byte next, nextNext; private byte[] buf = null; + private int maxStringLength = Integer.MAX_VALUE; + private int maxBytesLength = Integer.MAX_VALUE; ReaderImpl(InputStream in) { this.in = in; @@ -71,6 +73,22 @@ class ReaderImpl implements Reader { in.close(); } + public void setMaxStringLength(int length) { + maxStringLength = length; + } + + public void resetMaxStringLength() { + maxStringLength = Integer.MAX_VALUE; + } + + public void setMaxBytesLength(int length) { + maxBytesLength = length; + } + + public void resetMaxBytesLength() { + maxBytesLength = Integer.MAX_VALUE; + } + public void addConsumer(Consumer c) { Consumer[] newConsumers = new Consumer[consumers.length + 1]; System.arraycopy(consumers, 0, newConsumers, 0, consumers.length); @@ -268,21 +286,26 @@ class ReaderImpl implements Reader { } public String readString() throws IOException { - return readString(Integer.MAX_VALUE); - } - - public String readString(int maxLength) throws IOException { if(!hasString()) throw new FormatException(); consumeLookahead(); int length; if(next == Tag.STRING) length = readLength(); else length = 0xFF & next ^ Tag.SHORT_STRING; - if(length > maxLength) throw new FormatException(); + if(length > maxStringLength) throw new FormatException(); if(length == 0) return ""; readIntoBuffer(length); return new String(buf, 0, length, "UTF-8"); } + public String readString(int maxLength) throws IOException { + setMaxStringLength(maxLength); + try { + return readString(); + } finally { + resetMaxStringLength(); + } + } + private int readLength() throws IOException { if(!hasLength()) throw new FormatException(); if(next >= 0) return readUint7(); @@ -306,22 +329,27 @@ class ReaderImpl implements Reader { } public byte[] readBytes() throws IOException { - return readBytes(Integer.MAX_VALUE); - } - - public byte[] readBytes(int maxLength) throws IOException { if(!hasBytes()) throw new FormatException(); consumeLookahead(); int length; if(next == Tag.BYTES) length = readLength(); else length = 0xFF & next ^ Tag.SHORT_BYTES; - if(length > maxLength) throw new FormatException(); + if(length > maxBytesLength) throw new FormatException(); if(length == 0) return EMPTY_BUFFER; byte[] b = new byte[length]; readIntoBuffer(b, length); return b; } + public byte[] readBytes(int maxLength) throws IOException { + setMaxBytesLength(maxLength); + try { + return readBytes(); + } finally { + resetMaxBytesLength(); + } + } + public boolean hasList() throws IOException { if(!hasLookahead) readLookahead(true); if(eof) return false; diff --git a/test/net/sf/briar/serial/ReaderImplTest.java b/test/net/sf/briar/serial/ReaderImplTest.java index 2187e67bf74764ac9ae6e83a8e9975899ab12e71..28670b8b91751fb013d46a5a7da4ce5166757488 100644 --- a/test/net/sf/briar/serial/ReaderImplTest.java +++ b/test/net/sf/briar/serial/ReaderImplTest.java @@ -4,6 +4,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -485,6 +486,31 @@ public class ReaderImplTest extends TestCase { assertEquals("bar", e.getValue().s); } + @Test + public void testMaxLengthAppliesInsideMap() throws Exception { + setContents("B" + "1" + "83666F6F" + "93010203"); + r.setMaxStringLength(3); + r.setMaxBytesLength(3); + Map<String, Bytes> m = r.readMap(String.class, Bytes.class); + String key = "foo"; + Bytes value = new Bytes(new byte[] {1, 2, 3}); + assertEquals(Collections.singletonMap(key, value), m); + // The max string length should be applied inside the map + setContents("B" + "1" + "83666F6F" + "93010203"); + r.setMaxStringLength(2); + try { + r.readMap(String.class, Bytes.class); + fail(); + } catch(FormatException expected) {} + // The max bytes length should be applied inside the map + setContents("B" + "1" + "83666F6F" + "93010203"); + r.setMaxBytesLength(2); + try { + r.readMap(String.class, Bytes.class); + fail(); + } catch(FormatException expected) {} + } + @Test public void testReadEmptyInput() throws Exception { setContents("");