mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 03:09:04 +01:00
Limit the number of subscriptions per user.
This limit is necessary to limit the size of subscription update packets; it can be lifted when incremental subscription updates are implemented.
This commit is contained in:
@@ -297,8 +297,11 @@ public interface DatabaseComponent {
|
||||
void setVisibility(GroupId g, Collection<ContactId> visible)
|
||||
throws DbException;
|
||||
|
||||
/** Subscribes to the given group. */
|
||||
void subscribe(Group g) throws DbException;
|
||||
/**
|
||||
* Subscribes to the given group, or returns false if the user already has
|
||||
* the maximum number of subscriptions.
|
||||
*/
|
||||
boolean subscribe(Group g) throws DbException;
|
||||
|
||||
/**
|
||||
* Unsubscribes from the given group. Any messages belonging to the group
|
||||
|
||||
@@ -11,6 +11,9 @@ public interface MessagingConstants {
|
||||
*/
|
||||
int MAX_PACKET_LENGTH = MIN_CONNECTION_LENGTH / 2;
|
||||
|
||||
/** The maximum number of groups a user may subscribe to. */
|
||||
int MAX_SUBSCRIPTIONS = 3000;
|
||||
|
||||
/** The maximum number of properties per transport. */
|
||||
int MAX_PROPERTIES_PER_TRANSPORT = 100;
|
||||
|
||||
|
||||
@@ -132,11 +132,12 @@ interface Database<T> {
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Subscribes to the given group.
|
||||
* Subscribes to the given group, or returns false if the user already has
|
||||
* the maximum number of subscriptions.
|
||||
* <p>
|
||||
* Locking: subscription write.
|
||||
*/
|
||||
void addSubscription(T txn, Group g) throws DbException;
|
||||
boolean addSubscription(T txn, Group g) throws DbException;
|
||||
|
||||
/**
|
||||
* Adds a new transport to the database.
|
||||
|
||||
@@ -1715,14 +1715,16 @@ DatabaseCleaner.Callback {
|
||||
callListeners(new LocalSubscriptionsUpdatedEvent(affected));
|
||||
}
|
||||
|
||||
public void subscribe(Group g) throws DbException {
|
||||
public boolean subscribe(Group g) throws DbException {
|
||||
subscriptionLock.writeLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
boolean added = false;
|
||||
if(!db.containsSubscription(txn, g.getId()))
|
||||
db.addSubscription(txn, g);
|
||||
added = db.addSubscription(txn, g);
|
||||
db.commitTransaction(txn);
|
||||
return added;
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
|
||||
@@ -4,6 +4,7 @@ import static java.sql.Types.BINARY;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static net.sf.briar.api.Rating.UNRATED;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_SUBSCRIPTIONS;
|
||||
import static net.sf.briar.db.DatabaseConstants.RETENTION_MODULUS;
|
||||
import static net.sf.briar.db.ExponentialBackoff.calculateExpiry;
|
||||
|
||||
@@ -709,11 +710,21 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
public void addSubscription(Connection txn, Group g) throws DbException {
|
||||
public boolean addSubscription(Connection txn, Group g) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "INSERT INTO groups"
|
||||
+ " (groupId, name, key) VALUES (?, ?, ?)";
|
||||
String sql = "SELECT COUNT (groupId) from GROUPS";
|
||||
ps = txn.prepareStatement(sql);
|
||||
rs = ps.executeQuery();
|
||||
if(!rs.next()) throw new DbStateException();
|
||||
int count = rs.getInt(1);
|
||||
if(rs.next()) throw new DbStateException();
|
||||
rs.close();
|
||||
ps.close();
|
||||
if(count > MAX_SUBSCRIPTIONS) throw new DbStateException();
|
||||
if(count == MAX_SUBSCRIPTIONS) return false;
|
||||
sql = "INSERT INTO groups (groupId, name, key) VALUES (?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, g.getId().getBytes());
|
||||
ps.setString(2, g.getName());
|
||||
@@ -721,6 +732,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
int affected = ps.executeUpdate();
|
||||
if(affected != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
return true;
|
||||
} catch(SQLException e) {
|
||||
tryToClose(ps);
|
||||
throw new DbException(e);
|
||||
|
||||
@@ -221,8 +221,7 @@ class PacketReaderImpl implements PacketReader {
|
||||
r.setMaxStringLength(MAX_PROPERTY_LENGTH);
|
||||
Map<String, String> m = r.readMap(String.class, String.class);
|
||||
r.resetMaxStringLength();
|
||||
if(m.size() > MAX_PROPERTIES_PER_TRANSPORT)
|
||||
throw new FormatException();
|
||||
if(m.size() > MAX_PROPERTIES_PER_TRANSPORT) throw new FormatException();
|
||||
// Read the version number
|
||||
long version = r.readInt64();
|
||||
if(version < 0) throw new FormatException();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.sf.briar.messaging;
|
||||
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_SUBSCRIPTIONS;
|
||||
import static net.sf.briar.api.messaging.Types.SUBSCRIPTION_UPDATE;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -31,7 +32,8 @@ class SubscriptionUpdateReader implements StructReader<SubscriptionUpdate> {
|
||||
// Read the subscriptions
|
||||
List<Group> subs = new ArrayList<Group>();
|
||||
r.readListStart();
|
||||
while(!r.hasListEnd()) subs.add(groupReader.readStruct(r));
|
||||
for(int i = 0; i < MAX_SUBSCRIPTIONS && !r.hasListEnd(); i++)
|
||||
subs.add(groupReader.readStruct(r));
|
||||
r.readListEnd();
|
||||
// Read the version number
|
||||
long version = r.readInt64();
|
||||
|
||||
@@ -4,16 +4,24 @@ import static net.sf.briar.api.messaging.MessagingConstants.MAX_AUTHOR_NAME_LENG
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_BODY_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_GROUP_NAME_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_PROPERTIES_PER_TRANSPORT;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_PROPERTY_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_SIGNATURE_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_SUBJECT_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_SUBSCRIPTIONS;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Signature;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Random;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.TransportProperties;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.messaging.Ack;
|
||||
import net.sf.briar.api.messaging.Author;
|
||||
@@ -26,10 +34,12 @@ import net.sf.briar.api.messaging.MessageId;
|
||||
import net.sf.briar.api.messaging.Offer;
|
||||
import net.sf.briar.api.messaging.PacketWriter;
|
||||
import net.sf.briar.api.messaging.PacketWriterFactory;
|
||||
import net.sf.briar.api.messaging.SubscriptionUpdate;
|
||||
import net.sf.briar.api.messaging.TransportId;
|
||||
import net.sf.briar.api.messaging.TransportUpdate;
|
||||
import net.sf.briar.api.messaging.UniqueId;
|
||||
import net.sf.briar.clock.ClockModule;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.messaging.MessagingModule;
|
||||
import net.sf.briar.serial.SerialModule;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -56,6 +66,37 @@ public class ConstantsTest extends BriarTestCase {
|
||||
packetWriterFactory = i.getInstance(PacketWriterFactory.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAgreementPublicKeys() throws Exception {
|
||||
// Generate 10 agreement key pairs
|
||||
for(int i = 0; i < 10; i++) {
|
||||
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
||||
// Check the length of the public key
|
||||
byte[] publicKey = keyPair.getPublic().getEncoded();
|
||||
assertTrue(publicKey.length <= MAX_PUBLIC_KEY_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignaturePublicKeys() throws Exception {
|
||||
Random random = new Random();
|
||||
Signature sig = crypto.getSignature();
|
||||
// Generate 10 signature key pairs
|
||||
for(int i = 0; i < 10; i++) {
|
||||
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
||||
// Check the length of the public key
|
||||
byte[] publicKey = keyPair.getPublic().getEncoded();
|
||||
assertTrue(publicKey.length <= MAX_PUBLIC_KEY_LENGTH);
|
||||
// Sign some random data and check the length of the signature
|
||||
byte[] toBeSigned = new byte[100];
|
||||
random.nextBytes(toBeSigned);
|
||||
sig.initSign(keyPair.getPrivate());
|
||||
sig.update(toBeSigned);
|
||||
byte[] signature = sig.sign();
|
||||
assertTrue(signature.length <= MAX_SIGNATURE_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageIdsFitIntoLargeAck() throws Exception {
|
||||
testMessageIdsFitIntoAck(MAX_PACKET_LENGTH);
|
||||
@@ -66,21 +107,6 @@ public class ConstantsTest extends BriarTestCase {
|
||||
testMessageIdsFitIntoAck(1000);
|
||||
}
|
||||
|
||||
private void testMessageIdsFitIntoAck(int length) throws Exception {
|
||||
// Create an ack with as many message IDs as possible
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(length);
|
||||
PacketWriter writer = packetWriterFactory.createPacketWriter(out,
|
||||
true);
|
||||
int maxMessages = writer.getMaxMessagesForAck(length);
|
||||
Collection<MessageId> acked = new ArrayList<MessageId>();
|
||||
for(int i = 0; i < maxMessages; i++) {
|
||||
acked.add(new MessageId(TestUtils.getRandomId()));
|
||||
}
|
||||
writer.writeAck(new Ack(acked));
|
||||
// Check the size of the serialised ack
|
||||
assertTrue(out.size() <= length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageFitsIntoPacket() throws Exception {
|
||||
// Create a maximum-length group
|
||||
@@ -92,8 +118,10 @@ public class ConstantsTest extends BriarTestCase {
|
||||
byte[] authorPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
|
||||
Author author = authorFactory.createAuthor(authorName, authorPublic);
|
||||
// Create a maximum-length message
|
||||
PrivateKey groupPrivate = crypto.generateSignatureKeyPair().getPrivate();
|
||||
PrivateKey authorPrivate = crypto.generateSignatureKeyPair().getPrivate();
|
||||
PrivateKey groupPrivate =
|
||||
crypto.generateSignatureKeyPair().getPrivate();
|
||||
PrivateKey authorPrivate =
|
||||
crypto.generateSignatureKeyPair().getPrivate();
|
||||
String subject = createRandomString(MAX_SUBJECT_LENGTH);
|
||||
byte[] body = new byte[MAX_BODY_LENGTH];
|
||||
Message message = messageFactory.createMessage(null, group,
|
||||
@@ -116,16 +144,66 @@ public class ConstantsTest extends BriarTestCase {
|
||||
testMessageIdsFitIntoOffer(1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertiesFitIntoTransportUpdate() throws Exception {
|
||||
// Create the maximum number of properties with the maximum length
|
||||
TransportProperties p = new TransportProperties();
|
||||
for(int i = 0; i < MAX_PROPERTIES_PER_TRANSPORT; i++) {
|
||||
String key = createRandomString(MAX_PROPERTY_LENGTH);
|
||||
String value = createRandomString(MAX_PROPERTY_LENGTH);
|
||||
p.put(key, value);
|
||||
}
|
||||
// Create a maximum-length transport update
|
||||
TransportId id = new TransportId(TestUtils.getRandomId());
|
||||
TransportUpdate u = new TransportUpdate(id, p, Long.MAX_VALUE);
|
||||
// Serialise the update
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
PacketWriter writer = packetWriterFactory.createPacketWriter(out, true);
|
||||
writer.writeTransportUpdate(u);
|
||||
// Check the size of the serialised transport update
|
||||
assertTrue(out.size() <= MAX_PACKET_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroupsFitIntoSubscriptionUpdate() throws Exception {
|
||||
// Create the maximum number of maximum-length groups
|
||||
Collection<Group> subs = new ArrayList<Group>();
|
||||
for(int i = 0; i < MAX_SUBSCRIPTIONS; i++) {
|
||||
String groupName = createRandomString(MAX_GROUP_NAME_LENGTH);
|
||||
byte[] groupPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
|
||||
subs.add(groupFactory.createGroup(groupName, groupPublic));
|
||||
}
|
||||
// Create a maximum-length subscription update
|
||||
SubscriptionUpdate u = new SubscriptionUpdate(subs, Long.MAX_VALUE);
|
||||
// Serialise the update
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
PacketWriter writer = packetWriterFactory.createPacketWriter(out, true);
|
||||
writer.writeSubscriptionUpdate(u);
|
||||
// Check the size of the serialised subscription update
|
||||
assertTrue(out.size() <= MAX_PACKET_LENGTH);
|
||||
}
|
||||
|
||||
private void testMessageIdsFitIntoAck(int length) throws Exception {
|
||||
// Create an ack with as many message IDs as possible
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(length);
|
||||
PacketWriter writer = packetWriterFactory.createPacketWriter(out, true);
|
||||
int maxMessages = writer.getMaxMessagesForAck(length);
|
||||
Collection<MessageId> acked = new ArrayList<MessageId>();
|
||||
for(int i = 0; i < maxMessages; i++)
|
||||
acked.add(new MessageId(TestUtils.getRandomId()));
|
||||
writer.writeAck(new Ack(acked));
|
||||
// Check the size of the serialised ack
|
||||
assertTrue(out.size() <= length);
|
||||
}
|
||||
|
||||
private void testMessageIdsFitIntoOffer(int length) throws Exception {
|
||||
// Create an offer with as many message IDs as possible
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(length);
|
||||
PacketWriter writer = packetWriterFactory.createPacketWriter(out,
|
||||
true);
|
||||
PacketWriter writer = packetWriterFactory.createPacketWriter(out, true);
|
||||
int maxMessages = writer.getMaxMessagesForOffer(length);
|
||||
Collection<MessageId> offered = new ArrayList<MessageId>();
|
||||
for(int i = 0; i < maxMessages; i++) {
|
||||
for(int i = 0; i < maxMessages; i++)
|
||||
offered.add(new MessageId(TestUtils.getRandomId()));
|
||||
}
|
||||
writer.writeOffer(new Offer(offered));
|
||||
// Check the size of the serialised offer
|
||||
assertTrue(out.size() <= length);
|
||||
|
||||
Reference in New Issue
Block a user