diff --git a/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java b/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java index 78e41112f27d07e52edc950f4bbd501d06fd8ad1..f69ecb0c521deba9965bce5e290a6a69a8d1c983 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java @@ -74,6 +74,21 @@ class InboundMessageFragments /*implements UDPTransport.PartialACKSource */{ * Pull the fragments and ACKs out of the authenticated data packet */ public void receiveData(PeerState from, UDPPacketReader.DataReader data) { + try { + rcvData(from, data); + } catch (DataFormatException dfe) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Bad pkt from: " + from, dfe); + } catch (IndexOutOfBoundsException ioobe) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Bad pkt from: " + from, ioobe); + } + } + + /** + * Pull the fragments and ACKs out of the authenticated data packet + */ + private void rcvData(PeerState from, UDPPacketReader.DataReader data) throws DataFormatException { //long beforeMsgs = _context.clock().now(); int fragmentsIncluded = receiveMessages(from, data); //long afterMsgs = _context.clock().now(); @@ -95,7 +110,7 @@ class InboundMessageFragments /*implements UDPTransport.PartialACKSource */{ * * @return number of data fragments included */ - private int receiveMessages(PeerState from, UDPPacketReader.DataReader data) { + private int receiveMessages(PeerState from, UDPPacketReader.DataReader data) throws DataFormatException { int fragments = data.readFragmentCount(); if (fragments <= 0) return fragments; Hash fromPeer = from.getRemotePeer(); @@ -132,11 +147,7 @@ class InboundMessageFragments /*implements UDPTransport.PartialACKSource */{ boolean isNew = false; state = messages.get(messageId); if (state == null) { - try { - state = new InboundMessageState(_context, mid, fromPeer, data, i); - } catch (DataFormatException dfe) { - break; - } + state = new InboundMessageState(_context, mid, fromPeer, data, i); isNew = true; fragmentOK = true; // we will add to messages shortly if it isn't complete @@ -199,7 +210,7 @@ class InboundMessageFragments /*implements UDPTransport.PartialACKSource */{ /** * @return the number of bitfields in the ack? why? */ - private int receiveACKs(PeerState from, UDPPacketReader.DataReader data) { + private int receiveACKs(PeerState from, UDPPacketReader.DataReader data) throws DataFormatException { int rv = 0; boolean newAck = false; if (data.readACKsIncluded()) { diff --git a/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java b/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java index 2e4f73f440b83434fc2d8e4686a5d4591512aa94..fcaae36dd05f10a1ac301e6466f42f358e3f4da9 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java @@ -89,7 +89,7 @@ class InboundMessageState implements CDQEntry { * * @return true if the data was ok, false if it was corrupt */ - public boolean receiveFragment(UDPPacketReader.DataReader data, int dataFragment) { + 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)) @@ -233,7 +233,6 @@ class InboundMessageState implements CDQEntry { */ private static final class PartialBitfield implements ACKBitfield { private final long _bitfieldMessageId; - private final int _fragmentCount; private final int _ackCount; private final int _highestReceived; // bitfield, 1 for acked @@ -241,7 +240,7 @@ class InboundMessageState implements CDQEntry { /** * @param data each element is non-null or null for received or not - * @param lastFragment size of data to use + * @param size size of data to use */ public PartialBitfield(long messageId, Object data[], int size) { if (size > MAX_FRAGMENTS) @@ -258,7 +257,6 @@ class InboundMessageState implements CDQEntry { } } _fragmentAcks = acks; - _fragmentCount = size; _ackCount = ackCount; _highestReceived = highestReceived; } @@ -270,7 +268,7 @@ class InboundMessageState implements CDQEntry { return 1L << fragment; } - public int fragmentCount() { return _fragmentCount; } + public int fragmentCount() { return _highestReceived + 1; } public int ackCount() { return _ackCount; } @@ -279,12 +277,12 @@ class InboundMessageState implements CDQEntry { public long getMessageId() { return _bitfieldMessageId; } public boolean received(int fragmentNum) { - if (fragmentNum < 0 || fragmentNum >= _fragmentCount) + if (fragmentNum < 0 || fragmentNum > _highestReceived) return false; return (_fragmentAcks & mask(fragmentNum)) != 0; } - public boolean receivedComplete() { return _ackCount == _fragmentCount; } + public boolean receivedComplete() { return _ackCount == _highestReceived + 1; } @Override public String toString() { @@ -293,11 +291,11 @@ class InboundMessageState implements CDQEntry { buf.append(_bitfieldMessageId); buf.append(" highest: ").append(_highestReceived); buf.append(" with ").append(_ackCount).append(" ACKs for: ["); - for (int i = 0; i < _fragmentCount; i++) { + for (int i = 0; i <= _highestReceived; i++) { if (received(i)) buf.append(i).append(' '); } - buf.append("] / ").append(_fragmentCount); + buf.append("] / ").append(_highestReceived + 1); return buf.toString(); } } diff --git a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java index 8ad73330e47858314452db2ee4e5eaccec407729..206e9c44f7aea9964f813d7fcd9b7ba0c8ae73d4 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java @@ -8,6 +8,7 @@ import java.util.concurrent.BlockingQueue; import net.i2p.router.Router; import net.i2p.router.RouterContext; import net.i2p.router.util.CoDelBlockingQueue; +import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.util.I2PThread; import net.i2p.util.LHMCache; @@ -691,33 +692,35 @@ class PacketHandler { state = _establisher.receiveData(outState); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received new DATA packet from " + state + ": " + packet); + UDPPacketReader.DataReader dr = reader.getDataReader(); if (state != null) { - UDPPacketReader.DataReader dr = reader.getDataReader(); if (_log.shouldLog(Log.DEBUG)) { StringBuilder msg = new StringBuilder(512); msg.append("Receive ").append(System.identityHashCode(packet)); msg.append(" from ").append(state.getRemotePeer().toBase64()).append(" ").append(state.getRemoteHostId()); - for (int i = 0; i < dr.readFragmentCount(); i++) { - msg.append(" msg ").append(dr.readMessageId(i)); - msg.append(":").append(dr.readMessageFragmentNum(i)); - if (dr.readMessageIsLast(i)) - msg.append("*"); - } + try { + int count = dr.readFragmentCount(); + for (int i = 0; i < count; i++) { + msg.append(" msg ").append(dr.readMessageId(i)); + msg.append(":").append(dr.readMessageFragmentNum(i)); + if (dr.readMessageIsLast(i)) + msg.append("*"); + } + } catch (DataFormatException dfe) {} msg.append(": ").append(dr.toString()); _log.debug(msg.toString()); } //packet.beforeReceiveFragments(); _inbound.receiveData(state, dr); _context.statManager().addRateData("udp.receivePacketSize.dataKnown", packet.getPacket().getLength(), packet.getLifetime()); - if (dr.readFragmentCount() <= 0) - _context.statManager().addRateData("udp.receivePacketSize.dataKnownAck", packet.getPacket().getLength(), packet.getLifetime()); } else { // doesn't happen _context.statManager().addRateData("udp.receivePacketSize.dataUnknown", packet.getPacket().getLength(), packet.getLifetime()); - UDPPacketReader.DataReader dr = reader.getDataReader(); + } + try { if (dr.readFragmentCount() <= 0) _context.statManager().addRateData("udp.receivePacketSize.dataUnknownAck", packet.getPacket().getLength(), packet.getLifetime()); - } + } catch (DataFormatException dfe) {} break; case UDPPacket.PAYLOAD_TYPE_TEST: _state = 51; diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java index d956642b55179ea40a31341e2a5d74c5140462df..1f61cb197a27824fd62ceb32989c03c1de230ed0 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java @@ -2,6 +2,7 @@ package net.i2p.router.transport.udp; import net.i2p.I2PAppContext; import net.i2p.data.Base64; +import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.SessionKey; import net.i2p.data.Signature; @@ -12,6 +13,9 @@ import net.i2p.util.Log; * the appropriate fields. If the interesting bits are in message specific * elements, grab the appropriate subreader. * + * Many of the methods here and in the subclasses will throw AIOOBE on + * malformed packets, that should be caught also. + * */ class UDPPacketReader { private final I2PAppContext _context; @@ -314,7 +318,7 @@ class UDPPacketReader { return DataHelper.fromLong(_message, off + (4 * index), 4); } - public ACKBitfield[] readACKBitfields() { + public ACKBitfield[] readACKBitfields() throws DataFormatException { if (!readACKBitfieldsIncluded()) return null; int off = readBodyOffset() + 1; if (readACKsIncluded()) { @@ -334,7 +338,7 @@ class UDPPacketReader { return rv; } - public int readFragmentCount() { + public int readFragmentCount() throws DataFormatException { int off = readBodyOffset() + 1; if (readACKsIncluded()) { int numACKs = (int)DataHelper.fromLong(_message, off, 1); @@ -358,31 +362,31 @@ class UDPPacketReader { return _message[off]; } - public long readMessageId(int fragmentNum) { + public long readMessageId(int fragmentNum) throws DataFormatException { int fragmentBegin = getFragmentBegin(fragmentNum); return DataHelper.fromLong(_message, fragmentBegin, 4); } - public int readMessageFragmentNum(int fragmentNum) { + public int readMessageFragmentNum(int fragmentNum) throws DataFormatException { int off = getFragmentBegin(fragmentNum); off += 4; // messageId return (_message[off] & 0xFF) >>> 1; } - public boolean readMessageIsLast(int fragmentNum) { + public boolean readMessageIsLast(int fragmentNum) throws DataFormatException { int off = getFragmentBegin(fragmentNum); off += 4; // messageId return ((_message[off] & 1) != 0); } - public int readMessageFragmentSize(int fragmentNum) { + public int readMessageFragmentSize(int fragmentNum) throws DataFormatException { int off = getFragmentBegin(fragmentNum); off += 5; // messageId + fragment info return ((int)DataHelper.fromLong(_message, off, 2)) & 0x3FFF; } public void readMessageFragment(int fragmentNum, byte target[], int targetOffset) - throws ArrayIndexOutOfBoundsException { + throws DataFormatException { int off = getFragmentBegin(fragmentNum); off += 5; // messageId + fragment info int size = ((int)DataHelper.fromLong(_message, off, 2)) & 0x3FFF; @@ -390,7 +394,7 @@ class UDPPacketReader { System.arraycopy(_message, off, target, targetOffset, size); } - private int getFragmentBegin(int fragmentNum) { + private int getFragmentBegin(int fragmentNum) throws DataFormatException { int off = readBodyOffset() + 1; if (readACKsIncluded()) { int numACKs = (int)DataHelper.fromLong(_message, off, 1); @@ -452,10 +456,15 @@ class UDPPacketReader { off++; buf.append("with partial ACKs for "); - for (int i = 0; i < numBitfields; i++) { - PacketACKBitfield bf = new PacketACKBitfield(off); - buf.append(bf.getMessageId()).append(' '); - off += bf.getByteLength(); + try { + for (int i = 0; i < numBitfields; i++) { + PacketACKBitfield bf = new PacketACKBitfield(off); + buf.append(bf.getMessageId()).append(' '); + off += bf.getByteLength(); + } + } catch (DataFormatException dfe) { + buf.append("CORRUPT"); + return buf.toString(); } } if (readExtendedDataIncluded()) { @@ -492,7 +501,7 @@ class UDPPacketReader { return buf.toString(); } - public void toRawString(StringBuilder buf) { + public void toRawString(StringBuilder buf) throws DataFormatException { UDPPacketReader.this.toRawString(buf); buf.append(" payload: "); @@ -513,16 +522,19 @@ class UDPPacketReader { private final int _bitfieldStart; private final int _bitfieldSize; - public PacketACKBitfield(int start) { + public PacketACKBitfield(int start) throws DataFormatException { _start = start; _bitfieldStart = start + 4; int bfsz = 1; // bitfield is an array of bytes where the high bit is 1 if // further bytes in the bitfield follow while ((_message[_bitfieldStart + bfsz - 1] & UDPPacket.BITFIELD_CONTINUATION) != 0x0) { - if (++bfsz > InboundMessageState.MAX_PARTIAL_BITFIELD_BYTES) - throw new IllegalArgumentException(); + bfsz++; + //if (bfsz > InboundMessageState.MAX_PARTIAL_BITFIELD_BYTES) + // throw new DataFormatException(); } + if (bfsz > InboundMessageState.MAX_PARTIAL_BITFIELD_BYTES) + throw new DataFormatException("bitfield size: " + bfsz); _bitfieldSize = bfsz; }