From 9e2bb1ad0ad106f1783884fbe85d617d70fde314 Mon Sep 17 00:00:00 2001
From: zzz <zzz@i2pmail.org>
Date: Wed, 8 Feb 2023 08:25:09 -0500
Subject: [PATCH] Transports: Check RI in handshake for matching IP

---
 .../transport/ntcp/InboundEstablishState.java | 31 +++++++++++++++++--
 .../transport/udp/InboundEstablishState2.java | 22 +++++++++++++
 2 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java b/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java
index 9f608f5901..6f41a5daea 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java
@@ -629,15 +629,32 @@ class InboundEstablishState extends EstablishBase implements NTCP2Payload.Payloa
             throw new DataFormatException("no NTCP in RI: " + ri);
         }
         String s = null;
+        String mismatchMessage = null;
+        byte[] realIP = _con.getRemoteIP();
         for (RouterAddress addr : addrs) {
             String v = addr.getOption("v");
             if (v == null ||
                 (!v.equals(NTCPTransport.NTCP2_VERSION) && !v.startsWith(NTCPTransport.NTCP2_VERSION_ALT))) {
                  continue;
             }
-            s = addr.getOption("s");
-            if (s != null)
-                break;
+            if (s == null)
+                s = addr.getOption("s");
+            if (realIP != null) {
+                byte[] infoIP = addr.getIP();
+                if (infoIP != null && infoIP.length == realIP.length) {
+                    if (infoIP.length == 16) {
+                        if ((((int) infoIP[0]) & 0xfe) == 0x02)
+                            continue; // ygg
+                        if (DataHelper.eq(realIP, 0, infoIP, 0, 8))
+                            continue;
+                    } else {
+                        if (DataHelper.eq(realIP, infoIP))
+                            continue;
+                    }
+                    // We will ban and throw below after checking s
+                    mismatchMessage = "IP mismatch actual IP " + Addresses.toString(realIP) + " in RI: ";
+                }
+            }
         }
         if (s == null) {
             _msg3p2FailReason = NTCPConnection.REASON_S_MISMATCH;
@@ -661,6 +678,14 @@ class InboundEstablishState extends EstablishBase implements NTCP2Payload.Payloa
         boolean ok = verifyInbound(h);
         if (!ok)
             throw new DataFormatException("NTCP2 verifyInbound() fail");
+
+        // s is verified, we may now ban the hash
+        if (mismatchMessage != null) {
+            _context.banlist().banlistRouter(h, "IP mismatch", null, null, _context.clock().now() + 2*60*60*1000);
+            _msg3p2FailReason = NTCPConnection.REASON_BANNED;
+            throw new DataFormatException(mismatchMessage + ri);
+        }
+
         try {
             RouterInfo old = _context.netDb().store(h, ri);
             if (flood && !ri.equals(old)) {
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 c4f8df7e6d..2630be8597 100644
--- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java
+++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java
@@ -248,6 +248,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
         boolean isIPv6 = _aliceIP.length == 16;
         List<RouterAddress> addrs = _transport.getTargetAddresses(ri);
         RouterAddress ra = null;
+        String mismatchMessage = null;
         for (RouterAddress addr : addrs) {
             // skip SSU 1 address w/o "s"
             if (addrs.size() > 1 && addr.getTransportStyle().equals("SSU") && addr.getOption("s") == null)
@@ -266,6 +267,20 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
                     continue;
             }
             ra = addr;
+            byte[] infoIP = ra.getIP();
+            if (infoIP != null && infoIP.length == _aliceIP.length) {
+                if (isIPv6) {
+                    if ((((int) infoIP[0]) & 0xfe) == 0x02)
+                        continue; // ygg
+                    if (DataHelper.eq(_aliceIP, 0, infoIP, 0, 8))
+                        continue;
+                } else {
+                    if (DataHelper.eq(_aliceIP, infoIP))
+                        continue;
+                }
+                // We will ban and throw below after checking signature
+                mismatchMessage = "IP mismatch actual IP " + Addresses.toString(_aliceIP) + " in RI: ";
+            }
             break;
         }
 
@@ -312,6 +327,13 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
             throw new RIException("SSU2 network ID mismatch", REASON_NETID);
         }
 
+        if (mismatchMessage != null) {
+            _context.banlist().banlistRouter(h, "IP mismatch", null, null, _context.clock().now() + 2*60*60*1000);
+            if (ri.verifySignature())
+                _context.blocklist().add(_aliceIP);
+            throw new RIException(mismatchMessage + ri, REASON_BANNED);
+        }
+
         if (!"2".equals(ra.getOption("v")))
             throw new RIException("bad SSU2 v", REASON_VERSION);
 
-- 
GitLab