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 11c63d333..fd02817c1 100644 --- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java +++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java @@ -771,10 +771,10 @@ class EstablishmentManager { } } else { try { - state.receiveSessionRequestAfterRetry(packet); + state.receiveSessionOrTokenRequestAfterRetry(packet); } catch (GeneralSecurityException gse) { if (_log.shouldWarn()) - _log.warn("Corrupt Session Request after Retry from: " + state, gse); + _log.warn("Corrupt Session/Token Request after Retry from: " + state, gse); return; } } @@ -1323,7 +1323,7 @@ class EstablishmentManager { /** * This handles both initial send and retransmission of Session Created, * and, for SSU2, send of Retry. - * Retry is never retransmnitted. + * Retry is never retransmitted except in response to a retransmitted Token Request. * * This may be called more than once. * @@ -1366,6 +1366,7 @@ class EstablishmentManager { case IB_STATE_TOKEN_REQUEST_RECEIVED: case IB_STATE_REQUEST_BAD_TOKEN_RECEIVED: + case IB_STATE_RETRY_SENT: // got a retransmitted token request if (_log.shouldDebug()) _log.debug("Send retry to: " + state); pkt = _builder2.buildRetryPacket(state2, 0); @@ -2294,7 +2295,9 @@ class EstablishmentManager { //if (_log.shouldLog(Log.DEBUG)) // _log.debug("Removing completely confirmed inbound state"); break; - } else if (cur.getLifetime() > MAX_IB_ESTABLISH_TIME) { + } else if (cur.getLifetime() > MAX_IB_ESTABLISH_TIME || + (istate == IB_STATE_RETRY_SENT && // limit time to get sess. req after retry + cur.getLifetime() >= 5 * InboundEstablishState.RETRANSMIT_DELAY)) { // took too long iter.remove(); inboundState = cur; @@ -2351,13 +2354,8 @@ class EstablishmentManager { sendDestroy(inboundState); processExpired(inboundState); } else if (inboundState.getNextSendTime() <= now) { - if (istate == IB_STATE_RETRY_SENT) { - // Retry is never retransmitted - inboundState.fail(); - processExpired(inboundState); - } else { - sendCreated(inboundState); - } + // resend created or retry + sendCreated(inboundState); } break; 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 a9e074d53..acc7cb83b 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java @@ -505,30 +505,39 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa /** note that we just sent a Retry packet */ public synchronized void retryPacketSent() { // retry after clock skew - if (_currentState == InboundState.IB_STATE_FAILED || - _currentState == InboundState.IB_STATE_RETRY_SENT) + if (_currentState == InboundState.IB_STATE_FAILED) return; - if (_currentState != InboundState.IB_STATE_REQUEST_BAD_TOKEN_RECEIVED && + if (_currentState != InboundState.IB_STATE_RETRY_SENT && + _currentState != InboundState.IB_STATE_REQUEST_BAD_TOKEN_RECEIVED && _currentState != InboundState.IB_STATE_TOKEN_REQUEST_RECEIVED) throw new IllegalStateException("Bad state for Retry Sent: " + _currentState); - _currentState = InboundState.IB_STATE_RETRY_SENT; _lastSend = _context.clock().now(); - // Won't really be retransmitted, they have 5 sec to respond or - // EstablishmentManager.handleInbound() will fail the connection - // Alice will retransmit at 1 and 3 seconds, so wait 5 - // We're not going to wait for the 3rd retx at 7 seconds. - _nextSend = _lastSend + (5 * RETRANSMIT_DELAY); + if (_currentState == InboundState.IB_STATE_RETRY_SENT) { + // We received a retransmtted token request and resent the retry. + // Won't really be retransmitted, they have 5 sec to respond + // ensure we expire before retransmitting + _nextSend = _establishBegin + (5 * RETRANSMIT_DELAY); + if (_log.shouldWarn()) + _log.warn("Retransmit retry on " + this); + } else { + _currentState = InboundState.IB_STATE_RETRY_SENT; + // Won't really be retransmitted, they have 5 sec to respond or + // EstablishmentManager.handleInbound() will fail the connection + // Alice will retransmit at 1 and 3 seconds, so wait 5 + // We're not going to wait for the 3rd retx at 7 seconds. + _nextSend = _lastSend + (5 * RETRANSMIT_DELAY); + } } /** * All exceptions thrown from here will be fatal. fail() will be called before throwing. */ - public synchronized void receiveSessionRequestAfterRetry(UDPPacket packet) throws GeneralSecurityException { + public synchronized void receiveSessionOrTokenRequestAfterRetry(UDPPacket packet) throws GeneralSecurityException { try { - locked_receiveSessionRequestAfterRetry(packet); + locked_receiveSessionOrTokenRequestAfterRetry(packet); } catch (GeneralSecurityException gse) { if (_log.shouldDebug()) - _log.debug("Session request error after retry", gse); + _log.debug("Session/Token request error after retry", gse); // fail inside synch rather than have Est. Mgr. do it to prevent races fail(); throw gse; @@ -538,11 +547,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa /** * @since 0.9.56 */ - private void locked_receiveSessionRequestAfterRetry(UDPPacket packet) throws GeneralSecurityException { - if (_currentState != InboundState.IB_STATE_RETRY_SENT) - throw new GeneralSecurityException("Bad state for Session Request after Retry: " + _currentState); - if (_log.shouldDebug()) - _log.debug("Got session request after retry from: " + _aliceSocketAddress); + private void locked_receiveSessionOrTokenRequestAfterRetry(UDPPacket packet) throws GeneralSecurityException { DatagramPacket pkt = packet.getPacket(); SocketAddress from = pkt.getSocketAddress(); if (!from.equals(_aliceSocketAddress)) @@ -556,6 +561,26 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa long sid = DataHelper.fromLong8(data, off + 16); if (sid != _sendConnID) throw new GeneralSecurityException("Conn ID mismatch: 1: " + _sendConnID + " 2: " + sid); + + int type = data[off + TYPE_OFFSET] & 0xff; + if (_currentState != InboundState.IB_STATE_RETRY_SENT) { + // not fatal + if (_log.shouldWarn()) + _log.warn("Got out-of-order or retx msg " + type + " on: " + this); + return; + } + if (type == TOKEN_REQUEST_FLAG_BYTE) { + // retransmitted token request + if (_log.shouldWarn()) + _log.warn("Got retx token request on: " + this); + // Est. mgr will resend retry and call retryPacketSent() + long now = _context.clock().now(); + // rate limit + _nextSend = Math.max(now, _lastSend + 750); + return; + } + if (_log.shouldDebug()) + _log.debug("Got session request after retry on: " + this); long token = DataHelper.fromLong8(data, off + 24); if (token != _token) { // most likely a retransmitted session request with the old invalid token 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 09fe84df9..679dc2b25 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java @@ -897,14 +897,19 @@ class PacketHandler { header.getType() != SSU2Util.SESSION_REQUEST_FLAG_BYTE || header.getVersion() != 2 || header.getNetID() != _networkID) { - if (_log.shouldWarn()) { - if (header == null) { - // packet too short, possibly token-request-after-retry? let's see... - header = SSU2Header.trialDecryptLongHeader(packet, k1, k2); - } - _log.warn("Failed decrypt Session Request after Retry: " + header + " on " + state); + // possibly token-request-after-retry? let's see... + header = SSU2Header.trialDecryptLongHeader(packet, k1, k2); + if (header == null || + header.getType() != SSU2Util.TOKEN_REQUEST_FLAG_BYTE || + header.getVersion() != 2 || + header.getNetID() != _networkID) { + // i2pd short session request? TODO + if (_log.shouldWarn()) + _log.warn("Failed decrypt Session/Token Request after Retry: " + header + + " len " + packet.getPacket().getLength() + " on " + state); + return false; } - return false; + //yes, retransmitted token request } if (header.getSrcConnID() != state.getSendConnID()) { if (_log.shouldWarn()) @@ -918,7 +923,7 @@ class PacketHandler { _log.warn("Bad Dest Conn id " + header + " on " + state); return false; } - type = SSU2Util.SESSION_REQUEST_FLAG_BYTE; + type = header.getType(); } else { // Session Confirmed or retransmitted Session Request or Token Request header = SSU2Header.trialDecryptShortHeader(packet, k1, k2);