Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package net.i2p.router.transport.udp;
import net.i2p.data.ByteArray;
import net.i2p.data.DataFormatException;
import net.i2p.data.Hash;
import net.i2p.router.RouterContext;
import net.i2p.router.util.CDQEntry;
import net.i2p.util.ByteCache;
import net.i2p.util.Log;
/**
* Hold the raw data fragments of an inbound message.
*
* Warning - there is no synchronization in this class, take care in
* InboundMessageFragments to avoid use-after-release, etc.
*/
class InboundMessageState implements CDQEntry {
private final RouterContext _context;
private final Log _log;
private final long _messageId;
private final Hash _from;
/**
* indexed array of fragments for the message, where not yet
* received fragments are null.
*/
private final ByteArray _fragments[];
/**
* what is the last fragment in the message (or -1 if not yet known)
* Fragment count is _lastFragment + 1
*/
private int _lastFragment;
private final long _receiveBegin;
private long _enqueueTime;
private int _completeSize;
private boolean _released;
/** expire after 10s */
private static final long MAX_RECEIVE_TIME = 10*1000;
public static final int MAX_FRAGMENTS = 64;
/** 10 */
public static final int MAX_PARTIAL_BITFIELD_BYTES = (MAX_FRAGMENTS / 7) + 1;
private static final int MAX_FRAGMENT_SIZE = UDPPacket.MAX_PACKET_SIZE;
private static final ByteCache _fragmentCache = ByteCache.getInstance(64, MAX_FRAGMENT_SIZE);
public InboundMessageState(RouterContext ctx, long messageId, Hash from) {
_context = ctx;
_log = ctx.logManager().getLog(InboundMessageState.class);
_messageId = messageId;
_from = from;
_fragments = new ByteArray[MAX_FRAGMENTS];
_lastFragment = -1;
_completeSize = -1;
_receiveBegin = ctx.clock().now();
}
/**
* Create a new IMS and read in the data from the fragment.
* Do NOT call receiveFragment for the same fragment afterwards.
* This is more efficient if the fragment is the last (and probably only) fragment.
* The main savings is not allocating ByteArray[64].
*
* @throws DataFormatException if the fragment was corrupt
* @since 0.9.9
*/
public InboundMessageState(RouterContext ctx, long messageId, Hash from,
UDPPacketReader.DataReader data, int dataFragment)
throws DataFormatException {
_context = ctx;
_log = ctx.logManager().getLog(InboundMessageState.class);
_messageId = messageId;
_from = from;
if (data.readMessageIsLast(dataFragment)) {
int num = 1 + data.readMessageFragmentNum(dataFragment);
if (num > MAX_FRAGMENTS)
throw new DataFormatException("corrupt - too many fragments: " + num);
_fragments = new ByteArray[num];
} else {
_fragments = new ByteArray[MAX_FRAGMENTS];
}
_lastFragment = -1;
_completeSize = -1;
_receiveBegin = ctx.clock().now();
if (!receiveFragment(data, dataFragment))
throw new DataFormatException("corrupt");
}
/**
* Read in the data from the fragment.
* Caller should synchronize.
*
* @return true if the data was ok, false if it was corrupt
*/
public boolean receiveFragment(UDPPacketReader.DataReader data, int dataFragment) throws DataFormatException {
int fragmentNum = data.readMessageFragmentNum(dataFragment);
if ( (fragmentNum < 0) || (fragmentNum >= _fragments.length)) {
if (_log.shouldLog(Log.WARN))
_log.warn("Invalid fragment " + fragmentNum + '/' + _fragments.length);
return false;
}
if (_fragments[fragmentNum] == null) {
// new fragment, read it
ByteArray message = _fragmentCache.acquire();
try {
data.readMessageFragment(dataFragment, message.getData(), 0);
int size = data.readMessageFragmentSize(dataFragment);
if (size <= 0) {
// Bug in routers prior to 0.8.12
// If the msg size was an exact multiple of the fragment size,
// it would send a zero-length last fragment.
// This message is almost certainly doomed.
// We might as well ack it, keep going, and pass it along to I2NP where it
// will get dropped as corrupted.
// If we don't ack the fragment he will just send a zero-length fragment again.
if (_log.shouldLog(Log.WARN))
_log.warn("Zero-length fragment " + fragmentNum + " for message " + _messageId + " from " + _from);
}
message.setValid(size);
_fragments[fragmentNum] = message;
boolean isLast = data.readMessageIsLast(dataFragment);
if (isLast) {
// don't allow _lastFragment to be set twice
if (_lastFragment >= 0) {
if (_log.shouldLog(Log.ERROR))
_log.error("Multiple last fragments for message " + _messageId + " from " + _from);
return false;
}
// TODO - check for non-last fragments after this one?
_lastFragment = fragmentNum;
} else if (_lastFragment >= 0 && fragmentNum >= _lastFragment) {
// don't allow non-last after last
if (_log.shouldLog(Log.ERROR))
_log.error("Non-last fragment " + fragmentNum + " when last is " + _lastFragment + " for message " + _messageId + " from " + _from);
return false;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("New fragment " + fragmentNum + " for message " + _messageId
+ ", size=" + size
+ ", isLast=" + isLast
/* + ", data=" + Base64.encode(message.getData(), 0, size) */ );
} catch (ArrayIndexOutOfBoundsException aioobe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Corrupt SSU fragment " + fragmentNum, aioobe);
return false;
}
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received fragment " + fragmentNum + " for message " + _messageId
+ " again, old size=" + _fragments[fragmentNum].getValid()
+ " and new size=" + data.readMessageFragmentSize(dataFragment));
}
return true;
}
/**
* May not be valid after released.
* Probably doesn't need to be synced by caller, given the order of
* events in receiveFragment() above, but you might want to anyway
* to be safe.
*/
public boolean isComplete() {
int last = _lastFragment;
if (last < 0) return false;
for (int i = 0; i <= last; i++)
if (_fragments[i] == null)
return false;
return true;
}
public boolean isExpired() {
return _context.clock().now() > _receiveBegin + MAX_RECEIVE_TIME;
}
public long getLifetime() {
return _context.clock().now() - _receiveBegin;
}
/**
* For CDQ
* @since 0.9.3
*/
public void setEnqueueTime(long now) {
_enqueueTime = now;
}
/**
* For CDQ
* @since 0.9.3
*/
public long getEnqueueTime() {
return _enqueueTime;
}
/**
* For CDQ
* @since 0.9.3
*/
public void drop() {
releaseResources();
}
public Hash getFrom() { return _from; }
public long getMessageId() { return _messageId; }
/**
* @throws IllegalStateException if released or not isComplete()
*/
public int getCompleteSize() {
if (_completeSize < 0) {
if (_lastFragment < 0)
throw new IllegalStateException("last fragment not set");
if (_released)
throw new IllegalStateException("SSU IMS 2 Use after free");
int size = 0;
for (int i = 0; i <= _lastFragment; i++) {
ByteArray frag = _fragments[i];
if (frag == null)
throw new IllegalStateException("null fragment " + i + '/' + _lastFragment);
size += frag.getValid();
}
_completeSize = size;
}
return _completeSize;
}
/** FIXME synch here or PeerState.fetchPartialACKs() */
public ACKBitfield createACKBitfield() {
int last = _lastFragment;
int sz = (last >= 0) ? last + 1 : _fragments.length;
return new PartialBitfield(_messageId, _fragments, sz);
}
/**
* A true partial bitfield that is probably not complete.
* fragmentCount() will return 64 if unknown.
*/
private static final class PartialBitfield implements ACKBitfield {
private final long _bitfieldMessageId;
private final int _ackCount;
private final int _highestReceived;
// bitfield, 1 for acked
private final long _fragmentAcks;
/**
* @param data each element is non-null or null for received or not
* @param size size of data to use
*/
public PartialBitfield(long messageId, Object data[], int size) {
if (size > MAX_FRAGMENTS)
throw new IllegalArgumentException();
_bitfieldMessageId = messageId;
int ackCount = 0;
int highestReceived = -1;
long acks = 0;
for (int i = 0; i < size; i++) {
if (data[i] != null) {
acks |= mask(i);
ackCount++;
highestReceived = i;
}
}
_fragmentAcks = acks;
_ackCount = ackCount;
_highestReceived = highestReceived;
}
/**
* @param fragment 0-63
*/
private static long mask(int fragment) {
return 1L << fragment;
}
public int fragmentCount() { return _highestReceived + 1; }
public int ackCount() { return _ackCount; }
public int highestReceived() { return _highestReceived; }
public long getMessageId() { return _bitfieldMessageId; }
public boolean received(int fragmentNum) {
if (fragmentNum < 0 || fragmentNum > _highestReceived)
return false;
return (_fragmentAcks & mask(fragmentNum)) != 0;
}
public boolean receivedComplete() { return _ackCount == _highestReceived + 1; }
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append("OB Partial ACK of ");
buf.append(_bitfieldMessageId);
buf.append(" highest: ").append(_highestReceived);
buf.append(" with ").append(_ackCount).append(" ACKs for: [");
for (int i = 0; i <= _highestReceived; i++) {
if (received(i))
buf.append(i).append(' ');
}
buf.append("] / ").append(_highestReceived + 1);
return buf.toString();
}
}
public void releaseResources() {
_released = true;
for (int i = 0; i < _fragments.length; i++) {
if (_fragments[i] != null) {
_fragmentCache.release(_fragments[i]);
_fragments[i] = null;
}
}
}
/**
* @throws IllegalStateException if released
*/
public ByteArray[] getFragments() {
if (_released) {
RuntimeException e = new IllegalStateException("Use after free: " + _messageId);
_log.error("SSU IMS", e);
throw e;
}
return _fragments;
}
public int getFragmentCount() { return _lastFragment+1; }
/**
* May not be valid if released, or may NPE on race with release, use with care in exception text
*/
@Override
public String toString() {
StringBuilder buf = new StringBuilder(256);
buf.append("IB Message: ").append(_messageId);
buf.append(" from ").append(_from.toString());
if (isComplete()) {
buf.append(" completely received with ");
//buf.append(getCompleteSize()).append(" bytes");
// may display -1 but avoid cascaded exceptions after release
buf.append(_completeSize).append(" bytes in ");
buf.append(_lastFragment + 1).append(" fragments");
} else {
for (int i = 0; i <= _lastFragment; i++) {
buf.append(" fragment ").append(i);
if (_fragments[i] != null)
buf.append(": known at size ").append(_fragments[i].getValid());
else
buf.append(": unknown");
}
}
buf.append(" lifetime: ").append(getLifetime());
return buf.toString();
}
}