From 08e95655c17c77d49fa8cabb7ff81796bc9985f1 Mon Sep 17 00:00:00 2001
From: zzz <zzz@i2pmail.org>
Date: Wed, 22 Jun 2022 11:00:56 -0400
Subject: [PATCH] SSU2: More MTU checks

Add code for bob rejecting alice peer test address
Don't allow MTU to exceed his MTU
Don't use a token about to expire
Send token with termination message
Add internal type to termination message
log tweaks
---
 .../transport/udp/EstablishmentManager.java   | 13 ++++++++++++
 .../transport/udp/InboundEstablishState2.java | 10 ++++++---
 .../transport/udp/IntroductionManager.java    |  3 ++-
 .../udp/OutboundEstablishState2.java          |  8 +++++--
 .../router/transport/udp/PacketBuilder2.java  | 21 ++++++++++++++++---
 .../i2p/router/transport/udp/PeerState.java   |  3 ++-
 .../i2p/router/transport/udp/PeerState2.java  | 12 ++++++-----
 .../i2p/router/transport/udp/SSU2Util.java    |  1 +
 8 files changed, 56 insertions(+), 15 deletions(-)

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 a51a49c239..91758a8ec5 100644
--- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
@@ -415,6 +415,17 @@ class EstablishmentManager {
                                 version = 1;
                         }
                     }
+                    if (version == 2) {
+                        int mtu = addr.getMTU();
+                        if (mtu > 0 && mtu < PeerState2.MIN_MTU) {
+                            if (ra.getTransportStyle().equals("SSU2")) {
+                                _transport.markUnreachable(toHash);
+                                _transport.failed(msg, "MTU too small");
+                                return;
+                            }
+                            version = 1;
+                        }
+                    }
                     if (version == 1) {
                         keyBytes = addr.getIntroKey();
                     } else {
@@ -2435,6 +2446,8 @@ class EstablishmentManager {
      *  @since 0.9.54
      */
     public void addOutboundToken(RemoteHostId peer, long token, long expires) {
+        // so we don't use a token about to expire
+        expires -= 2*60*1000;
         if (expires < _context.clock().now())
             return;
         Token tok = new Token(token, expires);
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 ee0c312c24..48b1b1f0dc 100644
--- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java
+++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java
@@ -202,8 +202,8 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
     }
 
     public void gotRI(RouterInfo ri, boolean isHandshake, boolean flood) throws DataFormatException {
-        if (_log.shouldDebug())
-            _log.debug("Got RI block: " + ri);
+        //if (_log.shouldDebug())
+        //    _log.debug("Got RI block: " + ri);
         if (isHandshake)
             throw new DataFormatException("RI in Sess Req");
         if (_receivedUnconfirmedIdentity != null)
@@ -275,7 +275,9 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
                     mtu = PeerState2.DEFAULT_SSU_IPV4_MTU;
             }
         } else {
-            // TODO if too small, give up now
+            // if too small, give up now
+            if (mtu < PeerState2.MIN_MTU)
+                throw new DataFormatException("MTU too small " + mtu);
             if (ra.getTransportStyle().equals(UDPTransport.STYLE2)) {
                 mtu = Math.min(Math.max(mtu, PeerState2.MIN_MTU), PeerState2.MAX_MTU);
             } else {
@@ -369,6 +371,8 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
     }
 
     public void gotToken(long token, long expires) {
+        if (_log.shouldDebug())
+            _log.debug("Got token: " + token + " expires " + DataHelper.formatTime(expires) + " on " + this);
         if (_receivedConfirmedIdentity == null)
             throw new IllegalStateException("RI must be first");
         _transport.getEstablisher().addOutboundToken(_remoteHostId, token, expires);
diff --git a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
index 082b90b398..69b6856266 100644
--- a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
@@ -1059,7 +1059,8 @@ class IntroductionManager {
         }
         UDPPacket packet = _builder2.buildRelayResponse(data, bob);
         if (_log.shouldInfo())
-            _log.info("Send relay response " + rcode + " as charlie " + " nonce " + nonce + " to bob " + bob);
+            _log.info("Send relay response " + rcode + " as charlie " + " nonce " + nonce + " to bob " + bob +
+                      " for alice " + Addresses.toString(testIP, testPort) + ' ' + aliceRI);
         _transport.send(packet);
         if (rcode == SSU2Util.RELAY_ACCEPT) {
             // send hole punch with the same data we sent to Bob
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 5c644cd683..3381b2c80d 100644
--- a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java
+++ b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java
@@ -155,9 +155,11 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
                     mtu = PeerState2.DEFAULT_SSU_IPV4_MTU;
             }
         } else {
-            // TODO if too small, give up now
+            // If too small, give up now
+            if (mtu < PeerState2.MIN_MTU)
+                throw new IllegalArgumentException("MTU " + mtu + " too small for " + remotePeer.getHash());
             if (ra.getTransportStyle().equals(UDPTransport.STYLE2)) {
-                mtu = Math.min(Math.max(mtu, PeerState2.MIN_MTU), PeerState2.MAX_MTU);
+                mtu = Math.min(mtu, PeerState2.MAX_MTU);
             } else {
                 if (_bobIP != null && _bobIP.length == 16)
                     mtu = Math.min(Math.max(mtu, PeerState2.MIN_SSU_IPV6_MTU), PeerState2.MAX_SSU_IPV6_MTU);
@@ -321,6 +323,8 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
     }
 
     public void gotToken(long token, long expires) {
+        if (_log.shouldDebug())
+            _log.debug("Got token: " + token + " expires " + DataHelper.formatTime(expires) + " on " + this);
         _transport.getEstablisher().addOutboundToken(_remoteHostId, token, expires);
     }
 
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 d9eaedf3ea..7c20f84ab4 100644
--- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java
+++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java
@@ -60,6 +60,7 @@ class PacketBuilder2 {
     static final int TYPE_CONF = 71;
     static final int TYPE_SREQ = 72;
     static final int TYPE_CREAT = 73;
+    static final int TYPE_DESTROY = 74;
 
     /** IPv4 only */
     public static final int IP_HEADER_SIZE = PacketBuilder.IP_HEADER_SIZE;
@@ -100,6 +101,7 @@ class PacketBuilder2 {
     /**
      *  Will a packet to 'peer' that already has 'numFragments' fragments
      *  totalling 'curDataSize' bytes fit another fragment?
+     *  This includes the 3 byte block overhead, but NOT the 5 byte followon fragment overhead.
      *
      *  This doesn't leave anything for acks or anything else.
      *
@@ -279,7 +281,7 @@ class PacketBuilder2 {
         if (_log.shouldWarn()) {
             int maxMTU = PeerState2.MAX_MTU;
             off += MAC_LEN;
-            if (off + ipHeaderSize > maxMTU) {
+            if (off + ipHeaderSize > currentMTU) {
                 _log.warn("Size is " + off + " for " + packet +
                        " data size " + dataSize +
                        " pkt size " + (off + ipHeaderSize) +
@@ -340,11 +342,24 @@ class PacketBuilder2 {
 
     /**
      *  Build a data packet with a termination block.
-     *  This will also include acks and padding.
+     *  This will also include acks, a new token block, and padding.
      */
     public UDPPacket buildSessionDestroyPacket(int reason, PeerState2 peer) {
+            if (_log.shouldWarn())
+                _log.warn("Sending termination " + reason + " to : " + peer);
+        List<Block> blocks = new ArrayList<Block>(2);
+        if (peer.getKeyEstablishedTime() - _context.clock().now() > EstablishmentManager.IB_TOKEN_EXPIRATION / 2 &&
+            !_context.router().gracefulShutdownInProgress()) {
+            // update token
+            EstablishmentManager.Token token = _transport.getEstablisher().getInboundToken(peer.getRemoteHostId());
+            Block block = new SSU2Payload.NewTokenBlock(token.token, token.expires);
+            blocks.add(block);
+        }
         Block block = new SSU2Payload.TerminationBlock(reason, peer.getReceivedMessages().getHighestSet());
-        return buildPacket(Collections.emptyList(), Collections.singletonList(block), peer);
+        blocks.add(block);
+        UDPPacket packet = buildPacket(Collections.emptyList(), blocks, peer);
+        packet.setMessageType(TYPE_DESTROY);
+        return packet;
     }
     
     /**
diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState.java b/router/java/src/net/i2p/router/transport/udp/PeerState.java
index e2636ef426..a3bf19991c 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerState.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerState.java
@@ -1267,7 +1267,8 @@ public class PeerState {
     synchronized void setHisMTU(int mtu) {
         if (mtu <= _minMTU || mtu >= _largeMTU)
             return;
-        _largeMTU = mtu;
+        if (mtu < _largeMTU)
+            _largeMTU = mtu;
         if (mtu < _mtu)
             _mtu = mtu;
     }
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 29ee6305ed..097eb1a0ab 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerState2.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerState2.java
@@ -563,14 +563,16 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
     public void gotACK(long ackThru, int acks, byte[] ranges) {
         int hc = (((int) ackThru) << 8) ^ (acks << 24) ^ DataHelper.hashCode(ranges);
         if (_lastAckHashCode.getAndSet(hc) == hc) {
-            if (_log.shouldDebug())
-                _log.debug("Got dup ACK block: " + SSU2Bitfield.toString(ackThru, acks, ranges, (ranges != null ? ranges.length / 2 : 0)));
+            //if (_log.shouldDebug())
+            //    _log.debug("Got dup ACK block: " + SSU2Bitfield.toString(ackThru, acks, ranges, (ranges != null ? ranges.length / 2 : 0)));
             return;
         }
         try {
             SSU2Bitfield ackbf = SSU2Bitfield.fromACKBlock(ackThru, acks, ranges, (ranges != null ? ranges.length / 2 : 0));
             if (_log.shouldDebug())
-                _log.debug("Got new ACK block: " + SSU2Bitfield.toString(ackThru, acks, ranges, (ranges != null ? ranges.length / 2 : 0)));
+                _log.debug("Got new ACK block from " +
+                           _remotePeer.toBase64().substring(0,6) + ' ' +
+                           SSU2Bitfield.toString(ackThru, acks, ranges, (ranges != null ? ranges.length / 2 : 0)));
             // calls bitSet() below
             ackbf.forEachAndNot(_ackedMessages, this);
         } catch (Exception e) {
@@ -583,8 +585,8 @@ 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 + " on " + this);
+        if (_log.shouldInfo())
+            _log.info("Got TERMINATION block, reason: " + reason + " count: " + count + " on " + this);
         _transport.getEstablisher().receiveSessionDestroy(_remoteHostId, this);
     }
 
diff --git a/router/java/src/net/i2p/router/transport/udp/SSU2Util.java b/router/java/src/net/i2p/router/transport/udp/SSU2Util.java
index 46418292a6..63fd04e82d 100644
--- a/router/java/src/net/i2p/router/transport/udp/SSU2Util.java
+++ b/router/java/src/net/i2p/router/transport/udp/SSU2Util.java
@@ -125,6 +125,7 @@ final class SSU2Util {
     public static final int TEST_REJECT_BOB_NO_CHARLIE = 2;
     public static final int TEST_REJECT_BOB_LIMIT = 3;
     public static final int TEST_REJECT_BOB_SIGFAIL = 4;
+    public static final int TEST_REJECT_BOB_ADDRESS = 5;
     public static final int TEST_REJECT_CHARLIE_UNSPEC = 64;
     public static final int TEST_REJECT_CHARLIE_ADDRESS = 65;
     public static final int TEST_REJECT_CHARLIE_LIMIT = 66;
-- 
GitLab