mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-21 07:09:56 +01:00
Removed frame padding code (soon to be obsolete).
This commit is contained in:
@@ -1,44 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
|
||||||
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A thread that calls the writeFullFrame() method of a PaddedConnectionWriter
|
|
||||||
* at regular intervals. The interval between calls is determined by a target
|
|
||||||
* output rate. If the underlying output stream cannot accept data at the
|
|
||||||
* target rate, calls will be made as frequently as the output stream allows.
|
|
||||||
*/
|
|
||||||
class FrameScheduler extends Thread {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(FrameScheduler.class.getName());
|
|
||||||
|
|
||||||
private final PaddedConnectionWriter writer;
|
|
||||||
private final int millisPerFrame;
|
|
||||||
|
|
||||||
FrameScheduler(PaddedConnectionWriter writer, int bytesPerSecond) {
|
|
||||||
this.writer = writer;
|
|
||||||
millisPerFrame = bytesPerSecond * 1000 / MAX_FRAME_LENGTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
long lastCall = System.currentTimeMillis();
|
|
||||||
try {
|
|
||||||
while(true) {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
long nextCall = lastCall + millisPerFrame;
|
|
||||||
if(nextCall > now) Thread.sleep(nextCall - now);
|
|
||||||
lastCall = System.currentTimeMillis();
|
|
||||||
if(!writer.writeFullFrame()) return;
|
|
||||||
}
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
if(LOG.isLoggable(Level.INFO))
|
|
||||||
LOG.info("Interrupted while waiting to write frame");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.crypto.Mac;
|
|
||||||
|
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
|
||||||
import net.sf.briar.util.ByteUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A ConnectionWriter that uses padding to hinder traffic analysis. A full-size
|
|
||||||
* frame is written each time the writeFullFrame() method is called, with
|
|
||||||
* padding inserted if necessary. Calls to the writer's write() methods will
|
|
||||||
* block until there is space to buffer the data.
|
|
||||||
*/
|
|
||||||
class PaddedConnectionWriter extends ConnectionWriterImpl {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(PaddedConnectionWriter.class.getName());
|
|
||||||
|
|
||||||
private final byte[] padding;
|
|
||||||
|
|
||||||
private boolean closed = false;
|
|
||||||
private IOException exception = null;
|
|
||||||
|
|
||||||
PaddedConnectionWriter(ConnectionEncrypter encrypter, Mac mac,
|
|
||||||
ErasableKey macKey) {
|
|
||||||
super(encrypter, mac, macKey);
|
|
||||||
padding = new byte[maxPayloadLength];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void close() throws IOException {
|
|
||||||
if(exception != null) throw exception;
|
|
||||||
if(buf.size() > 0) writeFrame(false);
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
closed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void flush() throws IOException {
|
|
||||||
// Na na na, I can't hear you
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void write(int b) throws IOException {
|
|
||||||
if(exception != null) throw exception;
|
|
||||||
if(buf.size() == maxPayloadLength) waitForSpace();
|
|
||||||
buf.write(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(byte[] b) throws IOException {
|
|
||||||
write(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void write(byte[] b, int off, int len)
|
|
||||||
throws IOException {
|
|
||||||
if(exception != null) throw exception;
|
|
||||||
int available = maxPayloadLength - buf.size();
|
|
||||||
while(available < len) {
|
|
||||||
buf.write(b, off, available);
|
|
||||||
off += available;
|
|
||||||
len -= available;
|
|
||||||
waitForSpace();
|
|
||||||
available = maxPayloadLength;
|
|
||||||
}
|
|
||||||
buf.write(b, off, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to write a full-size frame, inserting padding if necessary, and
|
|
||||||
* returns true if the frame was written. If this method returns false it
|
|
||||||
* should not be called again.
|
|
||||||
*/
|
|
||||||
synchronized boolean writeFullFrame() {
|
|
||||||
if(closed) return false;
|
|
||||||
try {
|
|
||||||
writeFrame(true);
|
|
||||||
notify();
|
|
||||||
return true;
|
|
||||||
} catch(IOException e) {
|
|
||||||
exception = e;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void writeFrame(boolean pad) throws IOException {
|
|
||||||
if(frame > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
|
|
||||||
byte[] payload = buf.toByteArray();
|
|
||||||
if(payload.length > maxPayloadLength) throw new IllegalStateException();
|
|
||||||
int paddingLength = pad ? maxPayloadLength - payload.length : 0;
|
|
||||||
ByteUtils.writeUint16(payload.length, header, 0);
|
|
||||||
ByteUtils.writeUint16(paddingLength, header, 2);
|
|
||||||
out.write(header);
|
|
||||||
mac.update(header);
|
|
||||||
out.write(payload);
|
|
||||||
mac.update(payload);
|
|
||||||
out.write(padding, 0, paddingLength);
|
|
||||||
mac.update(padding, 0, paddingLength);
|
|
||||||
encrypter.writeFinal(mac.doFinal());
|
|
||||||
frame++;
|
|
||||||
buf.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void waitForSpace() throws IOException {
|
|
||||||
try {
|
|
||||||
wait();
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
if(LOG.isLoggable(Level.INFO))
|
|
||||||
LOG.info("Interrupted while waiting for space");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
if(exception != null) throw exception;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -55,7 +55,6 @@
|
|||||||
<test name='net.sf.briar.transport.ConnectionWriterImplTest'/>
|
<test name='net.sf.briar.transport.ConnectionWriterImplTest'/>
|
||||||
<test name='net.sf.briar.transport.ConnectionWriterTest'/>
|
<test name='net.sf.briar.transport.ConnectionWriterTest'/>
|
||||||
<test name='net.sf.briar.transport.FrameReadWriteTest'/>
|
<test name='net.sf.briar.transport.FrameReadWriteTest'/>
|
||||||
<test name='net.sf.briar.transport.PaddedConnectionWriterTest'/>
|
|
||||||
<test name='net.sf.briar.transport.batch.BatchConnectionReadWriteTest'/>
|
<test name='net.sf.briar.transport.batch.BatchConnectionReadWriteTest'/>
|
||||||
<test name='net.sf.briar.util.ByteUtilsTest'/>
|
<test name='net.sf.briar.util.ByteUtilsTest'/>
|
||||||
<test name='net.sf.briar.util.FileUtilsTest'/>
|
<test name='net.sf.briar.util.FileUtilsTest'/>
|
||||||
|
|||||||
@@ -1,163 +0,0 @@
|
|||||||
package net.sf.briar.transport;
|
|
||||||
|
|
||||||
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
import net.sf.briar.api.transport.ConnectionWriter;
|
|
||||||
import net.sf.briar.util.ByteUtils;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class PaddedConnectionWriterTest extends TransportTest {
|
|
||||||
|
|
||||||
public PaddedConnectionWriterTest() throws Exception {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWriteByteDoesNotBlockUntilBufferIsFull() throws Exception {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
ConnectionEncrypter e = new NullConnectionEncrypter(out, Long.MAX_VALUE);
|
|
||||||
ConnectionWriter w = new PaddedConnectionWriter(e, mac, macKey);
|
|
||||||
final OutputStream out1 = w.getOutputStream();
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
final AtomicBoolean finished = new AtomicBoolean(false);
|
|
||||||
final AtomicBoolean failed = new AtomicBoolean(false);
|
|
||||||
new Thread() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
for(int i = 0; i < maxPayloadLength; i++) out1.write(0);
|
|
||||||
finished.set(true);
|
|
||||||
} catch(IOException e) {
|
|
||||||
failed.set(true);
|
|
||||||
}
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
// The wait should not time out
|
|
||||||
assertTrue(latch.await(1, TimeUnit.SECONDS));
|
|
||||||
assertTrue(finished.get());
|
|
||||||
assertFalse(failed.get());
|
|
||||||
// Nothing should have been written
|
|
||||||
assertEquals(0, out.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWriteByteBlocksWhenBufferIsFull() throws Exception {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
ConnectionEncrypter e = new NullConnectionEncrypter(out, Long.MAX_VALUE);
|
|
||||||
PaddedConnectionWriter w = new PaddedConnectionWriter(e, mac, macKey);
|
|
||||||
final OutputStream out1 = w.getOutputStream();
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
final AtomicBoolean finished = new AtomicBoolean(false);
|
|
||||||
final AtomicBoolean failed = new AtomicBoolean(false);
|
|
||||||
new Thread() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
for(int i = 0; i < maxPayloadLength + 1; i++) out1.write(0);
|
|
||||||
finished.set(true);
|
|
||||||
} catch(IOException e) {
|
|
||||||
failed.set(true);
|
|
||||||
}
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
// The wait should time out
|
|
||||||
assertFalse(latch.await(1, TimeUnit.SECONDS));
|
|
||||||
assertFalse(finished.get());
|
|
||||||
assertFalse(failed.get());
|
|
||||||
// Calling writeFullFrame() should allow the writer to proceed
|
|
||||||
w.writeFullFrame();
|
|
||||||
assertTrue(latch.await(1, TimeUnit.SECONDS));
|
|
||||||
assertTrue(finished.get());
|
|
||||||
assertFalse(failed.get());
|
|
||||||
// A full frame should have been written
|
|
||||||
assertEquals(MAX_FRAME_LENGTH, out.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWriteArrayDoesNotBlockUntilBufferIsFull() throws Exception {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
ConnectionEncrypter e = new NullConnectionEncrypter(out, Long.MAX_VALUE);
|
|
||||||
ConnectionWriter w = new PaddedConnectionWriter(e, mac, macKey);
|
|
||||||
final OutputStream out1 = w.getOutputStream();
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
final AtomicBoolean finished = new AtomicBoolean(false);
|
|
||||||
final AtomicBoolean failed = new AtomicBoolean(false);
|
|
||||||
new Thread() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
out1.write(new byte[maxPayloadLength]);
|
|
||||||
finished.set(true);
|
|
||||||
} catch(IOException e) {
|
|
||||||
failed.set(true);
|
|
||||||
}
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
// The wait should not time out
|
|
||||||
assertTrue(latch.await(1, TimeUnit.SECONDS));
|
|
||||||
assertTrue(finished.get());
|
|
||||||
assertFalse(failed.get());
|
|
||||||
// Nothing should have been written
|
|
||||||
assertEquals(0, out.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWriteArrayBlocksWhenBufferIsFull() throws Exception {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
ConnectionEncrypter e = new NullConnectionEncrypter(out, Long.MAX_VALUE);
|
|
||||||
PaddedConnectionWriter w = new PaddedConnectionWriter(e, mac, macKey);
|
|
||||||
final OutputStream out1 = w.getOutputStream();
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
final AtomicBoolean finished = new AtomicBoolean(false);
|
|
||||||
final AtomicBoolean failed = new AtomicBoolean(false);
|
|
||||||
new Thread() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
out1.write(new byte[maxPayloadLength + 1]);
|
|
||||||
finished.set(true);
|
|
||||||
} catch(IOException e) {
|
|
||||||
failed.set(true);
|
|
||||||
}
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
// The wait should time out
|
|
||||||
assertFalse(latch.await(1, TimeUnit.SECONDS));
|
|
||||||
assertFalse(finished.get());
|
|
||||||
assertFalse(failed.get());
|
|
||||||
// Calling writeFullFrame() should allow the writer to proceed
|
|
||||||
w.writeFullFrame();
|
|
||||||
assertTrue(latch.await(1, TimeUnit.SECONDS));
|
|
||||||
assertTrue(finished.get());
|
|
||||||
assertFalse(failed.get());
|
|
||||||
// A full frame should have been written
|
|
||||||
assertEquals(MAX_FRAME_LENGTH, out.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWriteFullFrameInsertsPadding() throws Exception {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
ConnectionEncrypter e = new NullConnectionEncrypter(out, Long.MAX_VALUE);
|
|
||||||
PaddedConnectionWriter w = new PaddedConnectionWriter(e, mac, macKey);
|
|
||||||
w.getOutputStream().write(0);
|
|
||||||
w.writeFullFrame();
|
|
||||||
// A full frame should have been written
|
|
||||||
assertEquals(MAX_FRAME_LENGTH, out.size());
|
|
||||||
// The frame should have a payload length of 1 and padding for the rest
|
|
||||||
byte[] frame = out.toByteArray();
|
|
||||||
assertEquals(1, ByteUtils.readUint16(frame, 0)); // Payload length
|
|
||||||
assertEquals(maxPayloadLength - 1, ByteUtils.readUint16(frame, 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user