![JAR search and dependency download from the Maven repository](/logo.png)
net.i2p.router.tunnel.TrivialPreprocessor Maven / Gradle / Ivy
The newest version!
package net.i2p.router.tunnel;
import java.util.List;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.router.RouterContext;
import net.i2p.util.ByteCache;
import net.i2p.util.Log;
import net.i2p.util.SimpleByteCache;
/**
* Do the simplest thing possible for preprocessing - for each message available,
* turn it into the minimum number of fragmented preprocessed blocks, sending
* each of those out. This does not coallesce message fragments or delay for more
* optimal throughput.
*
* See FragmentHandler Javadoc for tunnel message fragment format
*
* Not instantiated directly except in unit tests; see BatchedPreprocessor
*
*/
class TrivialPreprocessor implements TunnelGateway.QueuePreprocessor {
protected final RouterContext _context;
protected final Log _log;
public static final int PREPROCESSED_SIZE = 1024;
protected static final int IV_SIZE = HopProcessor.IV_LENGTH;
/**
* Here in tunnels, we take from the cache but never add to it.
* In other words, we take advantage of other places in the router also using 1024-byte ByteCaches
* (since ByteCache only maintains once instance for each size)
* Used in BatchedPreprocessor; see add'l comments there
*/
protected static final ByteCache _dataCache = ByteCache.getInstance(512, PREPROCESSED_SIZE);
public TrivialPreprocessor(RouterContext ctx) {
_context = ctx;
_log = ctx.logManager().getLog(getClass());
}
/** how long do we want to wait before flushing */
public long getDelayAmount() { return 0; }
/**
* Return true if there were messages remaining, and we should queue up
* a delayed flush to clear them
*
* NOTE: Unused here, see BatchedPreprocessor override, super is not called.
*/
public boolean preprocessQueue(List pending, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) {
throw new UnsupportedOperationException("unused, right?");
}
protected void notePreprocessing(long messageId, int numFragments, int totalLength, List messageIds, String msg) {}
/**
* Wrap the preprocessed fragments with the necessary padding / checksums
* to act as a tunnel message.
*
* @param fragmentLength fragments[0:fragmentLength] is used
*/
protected void preprocess(byte fragments[], int fragmentLength) {
byte iv[] = SimpleByteCache.acquire(IV_SIZE);
_context.random().nextBytes(iv);
// payload ready, now H(instructions+payload+IV)
System.arraycopy(iv, 0, fragments, fragmentLength, IV_SIZE);
byte[] hashBuf = SimpleByteCache.acquire(Hash.HASH_LENGTH);
//Hash h = _context.sha().calculateHash(fragments, 0, fragmentLength + IV_SIZE);
_context.sha().calculateHash(fragments, 0, fragmentLength + IV_SIZE, hashBuf, 0);
//Hash h = _context.sha().calculateHash(target, 0, offset + IV_SIZE);
//_log.debug("before shift: " + Base64.encode(target));
// now shiiiiiift
int distance = PREPROCESSED_SIZE - fragmentLength;
System.arraycopy(fragments, 0, fragments, distance, fragmentLength);
//if (_log.shouldLog(Log.DEBUG))
// _log.debug(msg.getMessageId() + ": fragments begin at " + distance + " (size="
// + payloadLength + " offset=" + offset +")");
java.util.Arrays.fill(fragments, 0, distance, (byte)0x0);
//_log.debug("after shift: " + Base64.encode(target));
int offset = 0;
System.arraycopy(iv, 0, fragments, offset, IV_SIZE);
offset += IV_SIZE;
//System.arraycopy(h.getData(), 0, fragments, offset, 4);
System.arraycopy(hashBuf, 0, fragments, offset, 4);
offset += 4;
//_log.debug("before pad : " + Base64.encode(target));
SimpleByteCache.release(hashBuf);
SimpleByteCache.release(iv);
// fits in a single message, so may be smaller than the full size
int numPadBytes = PREPROCESSED_SIZE // max
- IV_SIZE // hmm..
- 4 // 4 bytes of the SHA256
- 1 // the 0x00 after the padding
- fragmentLength; // the size of the fragments (instructions+payload)
//_log.debug("# pad bytes: " + numPadBytes + " payloadLength: " + payloadLength + " instructions: " + instructionsLength);
if (numPadBytes > 0) {
fillRandomNonZero(fragments, offset, numPadBytes);
offset += numPadBytes;
}
fragments[offset] = 0x0; // no more padding
offset++;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Preprocessing beginning of the fragment instructions at " + offset);
}
/**
* Efficiently fill with nonzero random data
* Don't waste too much entropy or call random() too often.
* @since 0.8.5
*/
private void fillRandomNonZero(byte[] b, int off, int len) {
// get about as much as we think we will need, overestimate some
final int est = len + (len / 128) + 3;
final byte[] tmp = new byte[est];
_context.random().nextBytes(tmp);
int extra = len;
for (int i = 0; i < len; i++) {
while (tmp[i] == 0) {
if (extra < est)
tmp[i] = tmp[extra++]; // use from the extra we have at the end
else
tmp[i] = (byte)(_context.random().nextInt() & 0xFF); // waste 3/4 of the entropy
}
}
System.arraycopy(tmp, 0, b, off, len);
}
/** is this a follw up byte? */
private static final byte MASK_IS_SUBSEQUENT = FragmentHandler.MASK_IS_SUBSEQUENT;
/** how should this be delivered? shift this 5 the right and get TYPE_* */
//private static final byte MASK_TYPE = FragmentHandler.MASK_TYPE;
/** is this the first of a fragmented message? */
private static final byte MASK_FRAGMENTED = FragmentHandler.MASK_FRAGMENTED;
/**
* are there follow up headers?
* @deprecated unimplemented
*/
@Deprecated
private static final byte MASK_EXTENDED = FragmentHandler.MASK_EXTENDED;
private static final byte MASK_TUNNEL = (byte)(FragmentHandler.TYPE_TUNNEL << 5);
private static final byte MASK_ROUTER = (byte)(FragmentHandler.TYPE_ROUTER << 5);
protected int writeFirstFragment(PendingGatewayMessage msg, byte target[], int offset) {
boolean fragmented = false;
int instructionsLength = getInstructionsSize(msg);
int payloadLength = msg.getData().length - msg.getOffset();
if (offset + payloadLength + instructionsLength + IV_SIZE + 1 + 4 > PREPROCESSED_SIZE) {
fragmented = true;
instructionsLength += 4; // messageId
payloadLength = PREPROCESSED_SIZE - IV_SIZE - 1 - 4 - instructionsLength - offset;
if (payloadLength <= 0)
throw new RuntimeException("Fragment too small! payloadLen=" + payloadLength
+ " target.length=" + target.length + " offset="+offset
+ " msg.length=" + msg.getData().length + " msg.offset=" + msg.getOffset()
+ " instructionsLength=" + instructionsLength + " for " + msg);
}
if (payloadLength <= 0)
throw new RuntimeException("Full size too small! payloadLen=" + payloadLength
+ " target.length=" + target.length + " offset="+offset
+ " msg.length=" + msg.getData().length + " msg.offset=" + msg.getOffset()
+ " instructionsLength=" + instructionsLength + " for " + msg);
// first fragment, or full message
target[offset] = 0x0;
if (msg.getToTunnel() != null)
target[offset] |= MASK_TUNNEL;
else if (msg.getToRouter() != null)
target[offset] |= MASK_ROUTER;
if (fragmented)
target[offset] |= MASK_FRAGMENTED;
if (_log.shouldLog(Log.DEBUG))
_log.debug("CONTROL: " + Integer.toHexString(target[offset]));
offset++;
if (msg.getToTunnel() != null) {
DataHelper.toLong(target, offset, 4, msg.getToTunnel().getTunnelId());
offset += 4;
}
if (msg.getToRouter() != null) {
System.arraycopy(msg.getToRouter().getData(), 0, target, offset, Hash.HASH_LENGTH);
offset += Hash.HASH_LENGTH;
}
if (fragmented) {
DataHelper.toLong(target, offset, 4, msg.getMessageId());
if (_log.shouldLog(Log.DEBUG))
_log.debug("writing messageId= " + msg.getMessageId() + " at offset " + offset);
offset += 4;
}
DataHelper.toLong(target, offset, 2, payloadLength);
offset += 2;
//_log.debug("raw data : " + Base64.encode(msg.getData()));
System.arraycopy(msg.getData(), msg.getOffset(), target, offset, payloadLength);
if (_log.shouldLog(Log.DEBUG))
_log.debug("initial fragment[" + msg.getMessageId() + "/" + msg.getFragmentNumber()+ "/"
+ (PREPROCESSED_SIZE - offset - payloadLength) + "/" + payloadLength + "]: "
);
//+ Base64.encode(target, offset, payloadLength));
offset += payloadLength;
msg.setOffset(msg.getOffset() + payloadLength);
if (fragmented)
msg.incrementFragmentNumber();
return offset;
}
protected int writeSubsequentFragment(PendingGatewayMessage msg, byte target[], int offset) {
boolean isLast = true;
int instructionsLength = getInstructionsSize(msg);
int payloadLength = msg.getData().length - msg.getOffset();
if (payloadLength + instructionsLength + IV_SIZE + 1 + 4 > PREPROCESSED_SIZE) {
isLast = false;
payloadLength = PREPROCESSED_SIZE - IV_SIZE - 1 - 4 - instructionsLength;
}
// first fragment, or full message
target[offset] = 0x0;
target[offset] |= MASK_IS_SUBSEQUENT;
target[offset] |= (byte)(msg.getFragmentNumber() << 1); // max 63 fragments
if (isLast)
target[offset] |= 1;
if (_log.shouldLog(Log.DEBUG))
_log.debug("CONTROL: " + Integer.toHexString(target[offset]) + "/"
+ Base64.encode(target, offset, 1) + " at offset " + offset);
offset++;
DataHelper.toLong(target, offset, 4, msg.getMessageId());
offset += 4;
DataHelper.toLong(target, offset, 2, payloadLength);
offset += 2;
System.arraycopy(msg.getData(), msg.getOffset(), target, offset, payloadLength);
if (_log.shouldLog(Log.DEBUG))
_log.debug("subsequent fragment[" + msg.getMessageId() + "/" + msg.getFragmentNumber()+ "/"
+ offset + "/" + payloadLength + "]: "
);
//+ Base64.encode(target, offset, payloadLength));
offset += payloadLength;
if (!isLast)
msg.incrementFragmentNumber();
msg.setOffset(msg.getOffset() + payloadLength);
return offset;
}
/**
* @return generally 3 or 35 or 39 for first fragment, 7 for subsequent fragments.
*
* Does NOT include 4 for the message ID if the message will be fragmented;
* call getInstructionAugmentationSize() for that.
*/
protected static int getInstructionsSize(PendingGatewayMessage msg) {
if (msg.getFragmentNumber() > 0)
return 7;
// control byte
int header = 1;
// tunnel ID
if (msg.getToTunnel() != null)
header += 4;
// router hash
if (msg.getToRouter() != null)
header += 32;
// size
header += 2;
return header;
}
/** @return 0 or 4 */
protected static int getInstructionAugmentationSize(PendingGatewayMessage msg, int offset, int instructionsSize) {
int payloadLength = msg.getData().length - msg.getOffset();
if (offset + payloadLength + instructionsSize + IV_SIZE + 1 + 4 > PREPROCESSED_SIZE) {
// requires fragmentation, so include the messageId
return 4;
}
return 0;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy