use of net.i2p.data.ByteArray in project i2p.i2p by i2p.
the class EventPumper method processRead.
/**
* OP_READ will always be set before this is called.
* This method will disable the interest if no more reads remain because of inbound bandwidth throttling.
* High-frequency path in thread.
*/
private void processRead(SelectionKey key) {
NTCPConnection con = (NTCPConnection) key.attachment();
ByteBuffer buf = acquireBuf();
try {
int read = con.getChannel().read(buf);
if (read < 0) {
// _context.statManager().addRateData("ntcp.readEOF", 1);
if (con.isInbound() && con.getMessagesReceived() <= 0) {
InetAddress addr = con.getChannel().socket().getInetAddress();
int count;
if (addr != null) {
byte[] ip = addr.getAddress();
ByteArray ba = new ByteArray(ip);
count = _blockedIPs.increment(ba);
if (_log.shouldLog(Log.WARN))
_log.warn("Blocking IP " + Addresses.toString(ip) + " with count " + count + ": " + con);
} else {
count = 1;
if (_log.shouldLog(Log.WARN))
_log.warn("EOF on inbound before receiving any: " + con);
}
_context.statManager().addRateData("ntcp.dropInboundNoMessage", count);
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("EOF on " + con);
}
con.close();
releaseBuf(buf);
} else if (read == 0) {
// if (_log.shouldLog(Log.DEBUG))
// _log.debug("nothing to read for " + con + ", but stay interested");
// stay interested
// key.interestOps(key.interestOps() | SelectionKey.OP_READ);
releaseBuf(buf);
// workaround for channel stuck returning 0 all the time, causing 100% CPU
int consec = con.gotZeroRead();
if (consec >= 5) {
_context.statManager().addRateData("ntcp.zeroReadDrop", 1);
if (_log.shouldLog(Log.WARN))
_log.warn("Fail safe zero read close " + con);
con.close();
} else {
_context.statManager().addRateData("ntcp.zeroRead", consec);
if (_log.shouldLog(Log.INFO))
_log.info("nothing to read for " + con + ", but stay interested");
}
} else {
// clear counter for workaround above
con.clearZeroRead();
// ZERO COPY. The buffer will be returned in Reader.processRead()
buf.flip();
// con, buf);
FIFOBandwidthLimiter.Request req = _context.bandwidthLimiter().requestInbound(read, "NTCP read");
if (req.getPendingRequested() > 0) {
// rare since we generally don't throttle inbound
key.interestOps(key.interestOps() & ~SelectionKey.OP_READ);
// if (_log.shouldLog(Log.DEBUG))
// _log.debug("bw throttled reading for " + con + ", so we don't want to read anymore");
_context.statManager().addRateData("ntcp.queuedRecv", read);
con.queuedRecv(buf, req);
} else {
// fully allocated
// if (_log.shouldLog(Log.DEBUG))
// _log.debug("not bw throttled reading for " + con);
// stay interested
// key.interestOps(key.interestOps() | SelectionKey.OP_READ);
con.recv(buf);
_context.statManager().addRateData("ntcp.read", read);
}
}
} catch (CancelledKeyException cke) {
releaseBuf(buf);
if (_log.shouldLog(Log.WARN))
_log.warn("error reading on " + con, cke);
con.close();
_context.statManager().addRateData("ntcp.readError", 1);
} catch (IOException ioe) {
// common, esp. at outbound connect time
releaseBuf(buf);
if (con.isInbound() && con.getMessagesReceived() <= 0) {
InetAddress addr = con.getChannel().socket().getInetAddress();
int count;
if (addr != null) {
byte[] ip = addr.getAddress();
ByteArray ba = new ByteArray(ip);
count = _blockedIPs.increment(ba);
if (_log.shouldLog(Log.WARN))
_log.warn("Blocking IP " + Addresses.toString(ip) + " with count " + count + ": " + con);
} else {
count = 1;
if (_log.shouldLog(Log.WARN))
_log.warn("IOE on inbound before receiving any: " + con);
}
_context.statManager().addRateData("ntcp.dropInboundNoMessage", count);
} else {
if (_log.shouldLog(Log.INFO))
_log.info("error reading on " + con, ioe);
}
if (con.isEstablished()) {
_context.statManager().addRateData("ntcp.readError", 1);
} else {
// Usually "connection reset by peer", probably a conn limit rejection?
// although it could be a read failure during the DH handshake
// Same stat as in processConnect()
_context.statManager().addRateData("ntcp.connectFailedTimeoutIOE", 1);
RouterIdentity rem = con.getRemotePeer();
if (rem != null && !con.isInbound())
_transport.markUnreachable(rem.calculateHash());
}
con.close();
} catch (NotYetConnectedException nyce) {
releaseBuf(buf);
// ???
key.interestOps(key.interestOps() & ~SelectionKey.OP_READ);
if (_log.shouldLog(Log.WARN))
_log.warn("error reading on " + con, nyce);
}
}
use of net.i2p.data.ByteArray in project i2p.i2p by i2p.
the class EventPumper method processAccept.
private void processAccept(SelectionKey key) {
// if (_log.shouldLog(Log.DEBUG))
// _log.debug("processing accept");
ServerSocketChannel servChan = (ServerSocketChannel) key.attachment();
try {
SocketChannel chan = servChan.accept();
// don't throw an NPE if the connect is gone again
if (chan == null)
return;
chan.configureBlocking(false);
if (!_transport.allowConnection()) {
if (_log.shouldLog(Log.WARN))
_log.warn("Receive session request but at connection limit: " + chan.socket().getInetAddress());
try {
chan.close();
} catch (IOException ioe) {
}
return;
}
byte[] ip = chan.socket().getInetAddress().getAddress();
if (_context.blocklist().isBlocklisted(ip)) {
if (_log.shouldLog(Log.WARN))
_log.warn("Receive session request from blocklisted IP: " + chan.socket().getInetAddress());
// _context.statManager().addRateData("ntcp.connectBlocklisted", 1, 0);
try {
chan.close();
} catch (IOException ioe) {
}
return;
}
ByteArray ba = new ByteArray(ip);
int count = _blockedIPs.count(ba);
if (count > 0) {
count = _blockedIPs.increment(ba);
if (_log.shouldLog(Log.WARN))
_log.warn("Blocking accept of IP with count " + count + ": " + Addresses.toString(ip));
_context.statManager().addRateData("ntcp.dropInboundNoMessage", count);
try {
chan.close();
} catch (IOException ioe) {
}
return;
}
// BUGFIX for firewalls. --Sponge
if (shouldSetKeepAlive(chan))
chan.socket().setKeepAlive(true);
SelectionKey ckey = chan.register(_selector, SelectionKey.OP_READ);
new NTCPConnection(_context, _transport, chan, ckey);
// if (_log.shouldLog(Log.DEBUG))
// _log.debug("new NTCP connection established: " +con);
} catch (IOException ioe) {
_log.error("Error accepting", ioe);
}
}
use of net.i2p.data.ByteArray in project i2p.i2p by i2p.
the class FragmentHandler method verifyPreprocessed.
/**
* Verify that the preprocessed data hasn't been modified by checking the
* H(payload+IV)[0:3] vs preprocessed[16:19], where payload is the data
* after the padding. Remember, the preprocessed data is formatted as
* { IV + H[0:3] + padding + {instructions, fragment}* }. This function is
* very wasteful of memory usage as it doesn't operate inline (since IV and
* payload are mixed up). Later it may be worthwhile to explore optimizing
* this.
*/
private boolean verifyPreprocessed(byte[] preprocessed, int offset, int length) {
// ByteCache/ByteArray corruption detection
// byte[] orig = new byte[length];
// System.arraycopy(preprocessed, 0, orig, 0, length);
// try {
// Thread.sleep(75);
// } catch (InterruptedException ie) {}
// now we need to verify that the message was received correctly
int paddingEnd = HopProcessor.IV_LENGTH + 4;
while (preprocessed[offset + paddingEnd] != (byte) 0x00) {
paddingEnd++;
if (offset + paddingEnd >= length) {
if (_log.shouldLog(Log.WARN))
_log.warn("cannot verify, going past the end [off=" + offset + " len=" + length + " paddingEnd=" + paddingEnd + " data: " + Base64.encode(preprocessed, offset, length));
return false;
}
}
// skip the last
paddingEnd++;
// larger than necessary, but always sufficient
ByteArray ba = _validateCache.acquire();
byte[] preV = ba.getData();
int validLength = length - offset - paddingEnd + HopProcessor.IV_LENGTH;
System.arraycopy(preprocessed, offset + paddingEnd, preV, 0, validLength - HopProcessor.IV_LENGTH);
System.arraycopy(preprocessed, 0, preV, validLength - HopProcessor.IV_LENGTH, HopProcessor.IV_LENGTH);
if (_log.shouldLog(Log.DEBUG))
_log.debug("endpoint IV: " + Base64.encode(preV, validLength - HopProcessor.IV_LENGTH, HopProcessor.IV_LENGTH));
byte[] v = SimpleByteCache.acquire(Hash.HASH_LENGTH);
_context.sha().calculateHash(preV, 0, validLength, v, 0);
_validateCache.release(ba);
boolean eq = DataHelper.eq(v, 0, preprocessed, offset + HopProcessor.IV_LENGTH, 4);
if (!eq) {
if (_log.shouldLog(Log.WARN)) {
_log.warn("Corrupt tunnel message - verification fails: " + Base64.encode(preprocessed, offset + HopProcessor.IV_LENGTH, 4) + " != " + Base64.encode(v, 0, 4));
_log.warn("No matching endpoint: # pad bytes: " + (paddingEnd - (HopProcessor.IV_LENGTH + 4) - 1) + " offset=" + offset + " length=" + length + " paddingEnd=" + paddingEnd + ' ' + Base64.encode(preprocessed, offset, length), new Exception("trace"));
}
}
SimpleByteCache.release(v);
if (eq) {
int excessPadding = paddingEnd - (HopProcessor.IV_LENGTH + 4 + 1);
if (// suboptimal fragmentation
excessPadding > 0)
_context.statManager().addRateData("tunnel.smallFragments", excessPadding);
else
_context.statManager().addRateData("tunnel.fullFragments", 1);
}
return eq;
}
use of net.i2p.data.ByteArray in project i2p.i2p by i2p.
the class FragmentHandler method receiveTunnelMessage.
/**
* Receive the raw preprocessed message at the endpoint, parsing out each
* of the fragments, using those to fill various FragmentedMessages, and
* sending the resulting I2NPMessages where necessary. The received
* fragments are all verified.
*/
public void receiveTunnelMessage(byte[] preprocessed, int offset, int length) {
boolean ok = verifyPreprocessed(preprocessed, offset, length);
if (!ok) {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to verify preprocessed data (pre.length=" + preprocessed.length + " off=" + offset + " len=" + length);
_cache.release(new ByteArray(preprocessed));
_context.statManager().addRateData("tunnel.corruptMessage", 1, 1);
return;
}
// skip the IV
offset += HopProcessor.IV_LENGTH;
// skip the hash segment
offset += 4;
int padding = 0;
while (preprocessed[offset] != (byte) 0x00) {
// skip the padding
offset++;
// AIOOBE http://forum.i2p/viewtopic.php?t=3187
if (offset >= TrivialPreprocessor.PREPROCESSED_SIZE) {
_cache.release(new ByteArray(preprocessed));
_context.statManager().addRateData("tunnel.corruptMessage", 1, 1);
return;
}
padding++;
}
// skip the final 0x00, terminating the padding
offset++;
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Fragments begin at offset=" + offset + " padding=" + padding);
// _log.debug("fragments: " + Base64.encode(preprocessed, offset, preprocessed.length-offset));
}
try {
while (offset < length) {
int off = receiveFragment(preprocessed, offset, length);
if (off < 0) {
_context.statManager().addRateData("tunnel.corruptMessage", 1, 1);
return;
}
offset = off;
}
} catch (ArrayIndexOutOfBoundsException aioobe) {
_context.statManager().addRateData("tunnel.corruptMessage", 1, 1);
} catch (NullPointerException npe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Corrupt fragment received: offset = " + offset, npe);
_context.statManager().addRateData("tunnel.corruptMessage", 1, 1);
} catch (RuntimeException e) {
if (_log.shouldLog(Log.ERROR))
_log.error("Corrupt fragment received: offset = " + offset, e);
_context.statManager().addRateData("tunnel.corruptMessage", 1, 1);
// java.lang.IllegalStateException: don't get the completed size when we're not complete - null fragment i=0 of 1
// at net.i2p.router.tunnel.FragmentedMessage.getCompleteSize(FragmentedMessage.java:194)
// at net.i2p.router.tunnel.FragmentedMessage.toByteArray(FragmentedMessage.java:223)
// at net.i2p.router.tunnel.FragmentHandler.receiveComplete(FragmentHandler.java:380)
// at net.i2p.router.tunnel.FragmentHandler.receiveSubsequentFragment(FragmentHandler.java:353)
// at net.i2p.router.tunnel.FragmentHandler.receiveFragment(FragmentHandler.java:208)
// at net.i2p.router.tunnel.FragmentHandler.receiveTunnelMessage(FragmentHandler.java:92)
// ...
// still trying to find root cause
// let's limit the damage here and skip the:
// .transport.udp.MessageReceiver: b0rked receiving a message.. wazza huzza hmm?
// throw e;
} finally {
// each of the FragmentedMessages populated make a copy out of the
// payload, which they release separately, so we can release
// immediately
//
// This is certainly interesting, to wrap the 1024-byte array in a new ByteArray
// in order to put it in the pool, but it shouldn't cause any harm.
_cache.release(new ByteArray(preprocessed));
}
}
use of net.i2p.data.ByteArray in project i2p.i2p by i2p.
the class FragmentedMessage method getCompleteSize.
public int getCompleteSize() {
if (!_lastReceived)
throw new IllegalStateException("don't get the completed size when we're not complete!");
if (_releasedAfter > 0) {
RuntimeException e = new RuntimeException("use after free in FragmentedMessage");
_log.error("FM completeSize()", e);
throw e;
}
int size = 0;
for (int i = 0; i <= _highFragmentNum; i++) {
ByteArray ba = _fragments[i];
// NPE seen here, root cause unknown
if (ba == null)
throw new IllegalStateException("don't get the completed size when we're not complete! - null fragment i=" + i + " of " + _highFragmentNum);
size += ba.getValid();
}
return size;
}
Aggregations