diff --git a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java index 7fba352139cf5737396ed1dc169ea6e51f38efb4..ae59271efbb074e30fc69a1bc0cffb5b2b6efaa3 100644 --- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java +++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java @@ -775,6 +775,9 @@ class EstablishmentManager { /** * Got a SessionDestroy on an established conn + * + * SSU 1 or 2 + * * @since 0.8.1 */ void receiveSessionDestroy(RemoteHostId from, PeerState state) { @@ -785,6 +788,9 @@ class EstablishmentManager { /** * Got a SessionDestroy during outbound establish + * + * SSU 1 or 2 + * * @since 0.8.1 */ void receiveSessionDestroy(RemoteHostId from, OutboundEstablishState state) { @@ -800,6 +806,9 @@ class EstablishmentManager { * TODO - PacketHandler won't look up inbound establishes * As this packet was essentially unauthenticated (i.e. intro key, not session key) * we just log it as it could be spoofed. + * + * SSU 1 or 2 + * * @since 0.8.1 */ void receiveSessionDestroy(RemoteHostId from) { diff --git a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java index d0687eee6402806a31c2e286e33abca4ce7a1969..746d1e94e10419216e0c5fdee03c965b0ec466a8 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java @@ -144,6 +144,10 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa _sendHeaderEncryptKey2 = SSU2Util.hkdf(_context, _handshakeState.getChainingKey(), "SessCreateHeader"); _currentState = InboundState.IB_STATE_REQUEST_RECEIVED; } + if (_currentState == InboundState.IB_STATE_FAILED) { + // termination block received + throw new GeneralSecurityException("Termination block in Session/Token Request"); + } if (_timeReceived == 0) throw new GeneralSecurityException("No DateTime block in Session/Token Request"); long skew = _establishBegin - _timeReceived; @@ -308,7 +312,11 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa } public void gotTermination(int reason, long count) { - throw new IllegalStateException("Termination in Handshake"); + if (_log.shouldWarn()) + _log.warn("Got TERMINATION block, reason: " + reason + " count: " + count); + // this sets the state to FAILED + fail(); + _transport.getEstablisher().receiveSessionDestroy(_remoteHostId); } public void gotUnknown(int type, int len) { @@ -418,6 +426,11 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa _log.debug("State after sess req: " + _handshakeState); _timeReceived = 0; processPayload(data, off + LONG_HEADER_SIZE, len - (LONG_HEADER_SIZE + KEY_LEN + MAC_LEN), true); + packetReceived(); + if (_currentState == InboundState.IB_STATE_FAILED) { + // termination block received + throw new GeneralSecurityException("Termination block in Session Request"); + } if (_timeReceived == 0) throw new GeneralSecurityException("No DateTime block in Session Request"); long skew = _establishBegin - _timeReceived; @@ -426,8 +439,6 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa _sendHeaderEncryptKey2 = SSU2Util.hkdf(_context, _handshakeState.getChainingKey(), "SessCreateHeader"); _currentState = InboundState.IB_STATE_REQUEST_RECEIVED; _rtt = (int) ( _context.clock().now() - _lastSend ); - - packetReceived(); } /** @@ -463,6 +474,11 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa if (_log.shouldDebug()) _log.debug("State after sess conf: " + _handshakeState); processPayload(data, off + SHORT_HEADER_SIZE, len - (SHORT_HEADER_SIZE + KEY_LEN + MAC_LEN + MAC_LEN), false); + packetReceived(); + if (_currentState == InboundState.IB_STATE_FAILED) { + // termination block received + throw new GeneralSecurityException("Termination block in Session Confirmed"); + } _sessCrForReTX = null; if (_receivedConfirmedIdentity == null) @@ -471,7 +487,6 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa // createPeerState() called from gotRI() _currentState = InboundState.IB_STATE_CONFIRMED_COMPLETELY; - packetReceived(); return _pstate; } diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java index ea2ecaf48a0925fe4768d2927f1c2550dc0cc97d..48e05f34bc9931b62be5a49e156c929a5c44caf8 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java @@ -234,7 +234,11 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl } public void gotTermination(int reason, long count) { - throw new IllegalStateException("Termination in Sess Created"); + if (_log.shouldWarn()) + _log.warn("Got TERMINATION block, reason: " + reason + " count: " + count); + // this sets the state to FAILED + fail(); + _transport.getEstablisher().receiveSessionDestroy(_remoteHostId, this); } public void gotUnknown(int type, int len) { @@ -323,6 +327,11 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl _log.debug("Retry error", gse); throw gse; } + packetReceived(); + if (_currentState == OutboundState.OB_STATE_VALIDATION_FAILED) { + // termination block received + return; + } if (_timeReceived == 0) throw new GeneralSecurityException("No DateTime block in Session/Token Request"); long skew = _establishBegin - _timeReceived; @@ -330,7 +339,6 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl throw new GeneralSecurityException("Skew exceeded in Session/Token Request: " + skew); createNewState(_routerAddress); _currentState = OutboundState.OB_STATE_RETRY_RECEIVED; - packetReceived(); } public synchronized void receiveSessionCreated(UDPPacket packet) throws GeneralSecurityException { @@ -371,6 +379,11 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl _log.debug("State after sess cr: " + _handshakeState); _timeReceived = 0; processPayload(data, off + LONG_HEADER_SIZE, len - (LONG_HEADER_SIZE + KEY_LEN + MAC_LEN), true); + packetReceived(); + if (_currentState == OutboundState.OB_STATE_VALIDATION_FAILED) { + // termination block received + return; + } if (_timeReceived == 0) throw new GeneralSecurityException("No DateTime block in Session/Token Request"); long skew = _establishBegin - _timeReceived; @@ -384,7 +397,6 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl if (_requestSentCount == 1) { _rtt = (int) (_context.clock().now() - _requestSentTime); } - packetReceived(); } /** diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java index 4977c9d7d9917692e9eaea55b0597d5623629ec5..189302bbbbcb2a03491384f9149f53c29dbb07a9 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java @@ -133,6 +133,15 @@ class PacketBuilder2 { * */ public UDPPacket buildPacket(List<Fragment> fragments, PeerState2 peer) { + return buildPacket(fragments, null, peer); + } + + /* + * Multiple fragments and optional other blocks. + * + * @param otherBlocks may be null or empty + */ + public UDPPacket buildPacket(List<Fragment> fragments, List<Block> otherBlocks, PeerState2 peer) { // calculate data size int numFragments = fragments.size(); int dataSize = 0; @@ -172,7 +181,10 @@ class PacketBuilder2 { // ok, now for the body... // +2 for acks and padding - List<Block> blocks = new ArrayList<Block>(fragments.size() + 2); + int bcnt = fragments.size() + 2; + if (otherBlocks != null) + bcnt += otherBlocks.size(); + List<Block> blocks = new ArrayList<Block>(bcnt); int sizeWritten = 0; // add the acks @@ -207,6 +219,17 @@ class PacketBuilder2 { off += sz; sizeWritten += sz; } + + // now the other blocks, if any + if (otherBlocks != null) { + for (Block block : otherBlocks) { + blocks.add(block); + int sz = block.getTotalLength(); + off += sz; + sizeWritten += sz; + } + } + // FIXME Block block = getPadding(sizeWritten, currentMTU); if (block != null) { @@ -280,6 +303,15 @@ class PacketBuilder2 { public UDPPacket buildACK(PeerState2 peer) { return buildPacket(Collections.emptyList(), peer); } + + /** + * Build a data packet with a termination block. + * This will also include acks and padding. + */ + public UDPPacket buildSessionDestroyPacket(int reason, PeerState2 peer) { + Block block = new SSU2Payload.TerminationBlock(reason, peer.getReceivedMessages().getHighestSet()); + return buildPacket(Collections.emptyList(), Collections.singletonList(block), peer); + } /** * Build a new SessionRequest packet for the given peer, encrypting it diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState2.java b/router/java/src/net/i2p/router/transport/udp/PeerState2.java index c863aa7b5511d630cda8004ea06de97253e34549..aba46fd9d902f7622e0b5b40b02c6753b7c70d27 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerState2.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerState2.java @@ -350,8 +350,9 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback } public void gotTermination(int reason, long count) { - if (_log.shouldDebug()) - _log.debug("Got TERMINATION block, reason: " + reason + " count: " + count); + if (_log.shouldWarn()) + _log.warn("Got TERMINATION block, reason: " + reason + " count: " + count); + _transport.getEstablisher().receiveSessionDestroy(_remoteHostId, this); } public void gotUnknown(int type, int len) { diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java index 771c3f85d5a0d14c75c814a1fa86723fdda9b48e..da17423947f9b62e9bead2b31bc7ba43a3f8198a 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -2126,10 +2126,16 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority * @since 0.8.9 */ void sendDestroy(PeerState peer) { - // peer must be fully established - if (peer.getCurrentCipherKey() == null) - return; - UDPPacket pkt = _packetBuilder.buildSessionDestroyPacket(peer); + UDPPacket pkt; + if (peer.getVersion() == 1) { + // peer must be fully established + if (peer.getCurrentCipherKey() == null) + return; + pkt = _packetBuilder.buildSessionDestroyPacket(peer); + } else { + // unspecified reason + pkt = _packetBuilder2.buildSessionDestroyPacket(0, (PeerState2) peer); + } if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending destroy to : " + peer); send(pkt);