diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index f2522cd4ed540000a936a603d4922f4a287ece1f..7ca1a3ec2c4e6b0a58a1f89cf73e6cb3036dcf84 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -18,7 +18,7 @@ public class RouterVersion {
     /** deprecated */
     public final static String ID = "Monotone";
     public final static String VERSION = CoreVersion.VERSION;
-    public final static long BUILD = 9;
+    public final static long BUILD = 10;
 
     /** for example "-test" */
     public final static String EXTRA = "";
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 5a43bbb8896fd9d45967db10ef26b10cf176038f..1b7b5a2f91f805fc9b15ed762fc7d76e5ae249e3 100644
--- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
@@ -146,6 +146,7 @@ class EstablishmentManager {
         _context.statManager().createRateStat("udp.establishDropped", "Dropped an inbound establish message", "udp", UDPTransport.RATES);
         _context.statManager().createRateStat("udp.establishRejected", "How many pending outbound connections are there when we refuse to add any more?", "udp", UDPTransport.RATES);
         _context.statManager().createRateStat("udp.establishOverflow", "How many messages were queued up on a pending connection when it was too much?", "udp", UDPTransport.RATES);
+        _context.statManager().createRateStat("udp.establishBadIP", "Received IP or port was bad", "udp", UDPTransport.RATES);
         // following are for PeerState
         _context.statManager().createRateStat("udp.congestionOccurred", "How large the cwin was when congestion occurred (duration == sendBps)", "udp", UDPTransport.RATES);
         _context.statManager().createRateStat("udp.congestedRTO", "retransmission timeout after congestion (duration == rtt dev)", "udp", UDPTransport.RATES);
@@ -251,6 +252,7 @@ class EstablishmentManager {
                 _transport.failed(msg, "Remote peer's IP isn't valid");
                 _transport.markUnreachable(toHash);
                 //_context.shitlist().shitlistRouter(msg.getTarget().getIdentity().calculateHash(), "Invalid SSU address", UDPTransport.STYLE);
+                _context.statManager().addRateData("udp.establishBadIP", 1);
                 return;
             }
 
@@ -439,6 +441,7 @@ class EstablishmentManager {
                 if (_context.blocklist().isBlocklisted(from.getIP())) {
                     if (_log.shouldLog(Log.WARN))
                         _log.warn("Receive session request from blocklisted IP: " + from);
+                    _context.statManager().addRateData("udp.establishBadIP", 1);
                     return; // drop the packet
                 }
                 if (!_transport.allowConnection())
@@ -889,15 +892,14 @@ class EstablishmentManager {
         byte ip[] = new byte[sz];
         reader.getRelayResponseReader().readCharlieIP(ip, 0);
         int port = reader.getRelayResponseReader().readCharliePort();
+        if ((!isValid(ip, port)) || (!isValid(bob.getIP(), bob.getPort()))) {
+            if (_log.shouldLog(Log.WARN))
+                _log.warn("Bad relay resp from " + bob + " for " + Addresses.toString(ip, port));
+            _context.statManager().addRateData("udp.relayBadIP", 1);
+            return;
+        }
         InetAddress addr = null;
         try {
-            if (!_transport.isValid(ip))
-                throw new UnknownHostException("non-public IP");
-            // let's not relay to a privileged port, sounds like trouble
-            if (port < 1024 || port > 65535)
-                throw new UnknownHostException("bad port " + port);
-            if (Arrays.equals(ip, _transport.getExternalIP()))
-                throw new UnknownHostException("relay myself");
             addr = InetAddress.getByAddress(ip);
         } catch (UnknownHostException uhe) {
             if (_log.shouldLog(Log.WARN))
@@ -931,7 +933,19 @@ class EstablishmentManager {
         }
         notifyActivity();
     }
-    
+
+    /**
+     *  Are IP and port valid?
+     *  @since 0.9.3
+     */
+    private boolean isValid(byte[] ip, int port) {
+        return port >= 1024 &&
+               port <= 65535 &&
+               _transport.isValid(ip) &&
+               (!Arrays.equals(ip, _transport.getExternalIP())) &&
+               (!_context.blocklist().isBlocklisted(ip));
+    }
+
     /**
      *  Note that while a SessionConfirmed could in theory be fragmented,
      *  in practice a RouterIdentity is 387 bytes and a single fragment is 512 bytes max,
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 dba4cb32d8be7237b0895f61a28a717fe373c34c..2b71deb2cb1440c9f28ae0a8f582fdbfa4f19fbd 100644
--- a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
@@ -64,6 +64,7 @@ class IntroductionManager {
         ctx.statManager().createRateStat("udp.receiveRelayIntro", "How often we get a relayed request for us to talk to someone?", "udp", UDPTransport.RATES);
         ctx.statManager().createRateStat("udp.receiveRelayRequest", "How often we receive a good request to relay to someone else?", "udp", UDPTransport.RATES);
         ctx.statManager().createRateStat("udp.receiveRelayRequestBadTag", "Received relay requests with bad/expired tag", "udp", UDPTransport.RATES);
+        ctx.statManager().createRateStat("udp.relayBadIP", "Received IP or port was bad", "udp", UDPTransport.RATES);
     }
     
     public void reset() {
@@ -160,7 +161,7 @@ class IntroductionManager {
             }
             byte[] ip = cur.getRemoteIP();
             int port = cur.getRemotePort();
-            if (ip == null || !TransportImpl.isPubliclyRoutable(ip) || port < 1024 || port > 65535)
+            if (!isValid(ip, port))
                 continue;
             if (_log.shouldLog(Log.INFO))
                 _log.info("Picking introducer: " + cur);
@@ -227,7 +228,7 @@ class IntroductionManager {
     void receiveRelayIntro(RemoteHostId bob, UDPPacketReader reader) {
         if (_context.router().isHidden())
             return;
-        _context.statManager().addRateData("udp.receiveRelayIntro", 1, 0);
+        _context.statManager().addRateData("udp.receiveRelayIntro", 1);
 
         if (!_transport.allowConnection()) {
             if (_log.shouldLog(Log.WARN))
@@ -239,23 +240,25 @@ class IntroductionManager {
         byte ip[] = new byte[ipSize];
         reader.getRelayIntroReader().readIP(ip, 0);
         int port = reader.getRelayIntroReader().readPort();
+
+        if ((!isValid(ip, port)) || (!isValid(bob.getIP(), bob.getPort()))) {
+            if (_log.shouldLog(Log.WARN))
+                _log.warn("Bad relay intro from " + bob + " for " + Addresses.toString(ip, port));
+            _context.statManager().addRateData("udp.relayBadIP", 1);
+            return;
+        }
+
         if (_log.shouldLog(Log.INFO))
             _log.info("Receive relay intro from " + bob + " for " + Addresses.toString(ip, port));
         
         InetAddress to = null;
         try {
-            if (!_transport.isValid(ip))
-                throw new UnknownHostException("non-public IP");
-            // let's not punch to a privileged port, sounds like trouble
-            if (port < 1024 || port > 65535)
-                throw new UnknownHostException("bad port " + port);
-            if (Arrays.equals(ip, _transport.getExternalIP()))
-                throw new UnknownHostException("punch myself");
             to = InetAddress.getByAddress(ip);
         } catch (UnknownHostException uhe) {
             // shitlist Bob?
             if (_log.shouldLog(Log.WARN))
                 _log.warn("IP for alice to hole punch to is invalid", uhe);
+            _context.statManager().addRateData("udp.relayBadIP", 1);
             return;
         }
         
@@ -321,7 +324,20 @@ class IntroductionManager {
     void receiveRelayRequest(RemoteHostId alice, UDPPacketReader reader) {
         if (_context.router().isHidden())
             return;
-        long tag = reader.getRelayRequestReader().readTag();
+        UDPPacketReader.RelayRequestReader rrReader = reader.getRelayRequestReader();
+        long tag = rrReader.readTag();
+        int ipSize = rrReader.readIPSize();
+        byte ip[] = new byte[ipSize];
+        rrReader.readIP(ip, 0);
+        int port = rrReader.readPort();
+
+        if ((!isValid(ip, port)) || (!isValid(alice.getIP(), alice.getPort()))) {
+            if (_log.shouldLog(Log.WARN))
+                _log.warn("Bad relay req from " + alice + " for " + Addresses.toString(ip, port));
+            _context.statManager().addRateData("udp.relayBadIP", 1);
+            return;
+        }
+
         PeerState charlie = get(tag);
         if (charlie == null) {
             if (_log.shouldLog(Log.INFO))
@@ -346,4 +362,16 @@ class IntroductionManager {
         // send alice back charlie's info
         _transport.send(_builder.buildRelayResponse(alice, charlie, reader.getRelayRequestReader().readNonce(), aliceIntroKey));
     }
+
+    /**
+     *  Are IP and port valid?
+     *  @since 0.9.3
+     */
+    private boolean isValid(byte[] ip, int port) {
+        return port >= 1024 &&
+               port <= 65535 &&
+               _transport.isValid(ip) &&
+               (!Arrays.equals(ip, _transport.getExternalIP())) &&
+               (!_context.blocklist().isBlocklisted(ip));
+    }
 }
diff --git a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
index 4ea51142c48089292ef8dd7fed2719f3759c2659..8d502d86280ef140df44d7376fb8dcd31448d312 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
@@ -2,6 +2,7 @@ package net.i2p.router.transport.udp;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.Arrays;
 import java.util.Map;
 import java.util.Queue;
 import java.util.concurrent.ConcurrentHashMap;
@@ -123,6 +124,7 @@ class PeerTestManager {
         _context.statManager().createRateStat("udp.statusKnownCharlie", "How often the bob we pick passes us to a charlie we already have a session with?", "udp", UDPTransport.RATES);
         _context.statManager().createRateStat("udp.receiveTestReply", "How often we get a reply to our peer test?", "udp", UDPTransport.RATES);
         _context.statManager().createRateStat("udp.receiveTest", "How often we get a packet requesting us to participate in a peer test?", "udp", UDPTransport.RATES);
+        _context.statManager().createRateStat("udp.testBadIP", "Received IP or port was bad", "udp", UDPTransport.RATES);
     }
     
     private static final int RESEND_TIMEOUT = 5*1000;
@@ -240,7 +242,7 @@ class PeerTestManager {
      * test. We are Alice.
      */
     private synchronized void receiveTestReply(RemoteHostId from, UDPPacketReader.PeerTestReader testInfo) {
-        _context.statManager().addRateData("udp.receiveTestReply", 1, 0);
+        _context.statManager().addRateData("udp.receiveTestReply", 1);
         PeerTestState test = _currentTest;
         if (expired())
             return;
@@ -270,7 +272,10 @@ class PeerTestManager {
                 InetAddress addr = InetAddress.getByAddress(ip);
                 test.setAliceIP(addr);
                 test.setReceiveBobTime(_context.clock().now());
-                test.setAlicePort(testInfo.readPort());
+                int testPort = testInfo.readPort();
+                if (testPort == 0)
+                    throw new UnknownHostException("port 0");
+                test.setAlicePort(testPort);
 
                 if (_log.shouldLog(Log.DEBUG))
                     _log.debug("Receive test reply from bob @ " + from + " via our " + test.getAlicePort() + "/" + test.getAlicePortFromCharlie());
@@ -280,6 +285,7 @@ class PeerTestManager {
                 if (_log.shouldLog(Log.ERROR))
                     _log.error("Unable to get our IP (length " + ipSize +
                                ") from bob's reply: " + from + ", " + testInfo, uhe);
+                _context.statManager().addRateData("udp.testBadIP", 1);
             }
         } else {
             // The reply is from Charlie
@@ -294,7 +300,7 @@ class PeerTestManager {
                               + _currentTest + ", charlie: " + from + ")");
                 // why are we doing this instead of calling testComplete() ?
                 _currentTestComplete = true;
-                _context.statManager().addRateData("udp.statusKnownCharlie", 1, 0);
+                _context.statManager().addRateData("udp.statusKnownCharlie", 1);
                 honorStatus(CommSystemFacade.STATUS_UNKNOWN);
                 _currentTest = null;
                 return;
@@ -302,10 +308,13 @@ class PeerTestManager {
     
             if (test.getReceiveCharlieTime() > 0) {
                 // this is our second charlie, yay!
-                test.setAlicePortFromCharlie(testInfo.readPort());
-                byte ip[] = new byte[testInfo.readIPSize()];
-                testInfo.readIP(ip, 0);
                 try {
+                    int testPort = testInfo.readPort();
+                    if (testPort == 0)
+                        throw new UnknownHostException("port 0");
+                    test.setAlicePortFromCharlie(testPort);
+                    byte ip[] = new byte[testInfo.readIPSize()];
+                    testInfo.readIP(ip, 0);
                     InetAddress addr = InetAddress.getByAddress(ip);
                     test.setAliceIPFromCharlie(addr);
                     if (_log.shouldLog(Log.DEBUG))
@@ -316,6 +325,7 @@ class PeerTestManager {
                 } catch (UnknownHostException uhe) {
                     if (_log.shouldLog(Log.ERROR))
                         _log.error("Charlie @ " + from + " said we were an invalid IP address: " + uhe.getMessage(), uhe);
+                    _context.statManager().addRateData("udp.testBadIP", 1);
                 }
             } else {
                 if (test.getPacketsRelayed() > MAX_RELAYED_PER_TEST) {
@@ -343,6 +353,7 @@ class PeerTestManager {
                 } catch (UnknownHostException uhe) {
                     if (_log.shouldLog(Log.WARN))
                         _log.warn("Charlie's IP is b0rked: " + from + ": " + testInfo);
+                    _context.statManager().addRateData("udp.testBadIP", 1);
                 }
             }
         }
@@ -415,10 +426,42 @@ class PeerTestManager {
      * We could be Alice, Bob, or Charlie.
      */
     public void receiveTest(RemoteHostId from, UDPPacketReader reader) {
-        _context.statManager().addRateData("udp.receiveTest", 1, 0);
+        _context.statManager().addRateData("udp.receiveTest", 1);
+        byte[] fromIP = from.getIP();
+        int fromPort = from.getPort();
+        if (fromPort < 1024 || fromPort > 65535 ||
+            (!_transport.isValid(fromIP)) ||
+            _context.blocklist().isBlocklisted(fromIP)) {
+            // spoof check, and don't respond to privileged ports
+            if (_log.shouldLog(Log.WARN))
+                _log.warn("Invalid PeerTest address: " + Addresses.toString(fromIP, fromPort));
+            _context.statManager().addRateData("udp.testBadIP", 1);
+            return;
+        }
         UDPPacketReader.PeerTestReader testInfo = reader.getPeerTestReader();
         byte testIP[] = null;
         int testPort = testInfo.readPort();
+
+        if (testInfo.readIPSize() > 0) {
+            testIP = new byte[testInfo.readIPSize()];
+            testInfo.readIP(testIP, 0);
+        }
+
+        if ((testPort > 0 && (testPort < 1024 || testPort > 65535)) ||
+            (testIP != null && (Arrays.equals(testIP, _transport.getExternalIP()) ||
+                                (!_transport.isValid(testIP)) ||
+                                _context.blocklist().isBlocklisted(testIP)))) {
+            // spoof check, and don't respond to privileged ports
+            if (_log.shouldLog(Log.WARN))
+                _log.warn("Invalid address in PeerTest: " + Addresses.toString(testIP, testPort));
+            _context.statManager().addRateData("udp.testBadIP", 1);
+            return;
+        }
+
+        // The from IP/port and message's IP/port are now validated.
+        // EXCEPT that either the message's IP could be empty or the message's port could be 0.
+        // Both of those cases should be checked in receiveXfromY() as appropriate.
+
         long nonce = testInfo.readNonce();
         PeerTestState test = _currentTest;
         if ( (test != null) && (test.getNonce() == nonce) ) {
@@ -429,22 +472,11 @@ class PeerTestManager {
 
         // we are Bob or Charlie
 
-        if ( (testInfo.readIPSize() > 0) && (testPort > 0) ) {
-            testIP = new byte[testInfo.readIPSize()];
-            testInfo.readIP(testIP, 0);
-        }
-
         PeerTestState state = _activeTests.get(Long.valueOf(nonce));
         
         if (state == null) {
             // NEW TEST
-            if ((testPort > 0 && (testPort < 1024 || testPort > 65535)) ||
-                (testIP != null && !_transport.isValid(testIP))) {
-                // spoof check, and don't respond to privileged ports
-                if (_log.shouldLog(Log.WARN))
-                    _log.warn("Invalid IP/Port rcvd in PeerTest: " + Addresses.toString(testIP, testPort));
-                return;
-            } else if ( (testIP == null) || (testPort <= 0) ) {
+            if ( (testIP == null) || (testPort <= 0) ) {
                 // we are bob, since we haven't seen this nonce before AND its coming from alice
                 if (_log.shouldLog(Log.DEBUG))
                     _log.debug("test IP/port are blank coming from " + from + ", assuming we are Bob and they are alice");
@@ -464,11 +496,11 @@ class PeerTestManager {
         } else {
             // EXISTING TEST
             if (state.getOurRole() == PeerTestState.BOB) {
-                if (DataHelper.eq(from.getIP(), state.getAliceIP().getAddress()) && 
-                    (from.getPort() == state.getAlicePort()) ) {
+                if (DataHelper.eq(fromIP, state.getAliceIP().getAddress()) && 
+                    (fromPort == state.getAlicePort()) ) {
                     receiveFromAliceAsBob(from, testInfo, nonce, state);
-                } else if (DataHelper.eq(from.getIP(), state.getCharlieIP().getAddress()) && 
-                           (from.getPort() == state.getCharliePort()) ) {
+                } else if (DataHelper.eq(fromIP, state.getCharlieIP().getAddress()) && 
+                           (fromPort == state.getCharliePort()) ) {
                     receiveFromCharlieAsBob(from, state);
                 } else {
                     if (_log.shouldLog(Log.WARN))
@@ -509,6 +541,8 @@ class PeerTestManager {
         try {
             testInfo.readIP(aliceIPData, 0);
             int alicePort = testInfo.readPort();
+            if (alicePort == 0)
+                throw new UnknownHostException("port 0");
             InetAddress aliceIP = InetAddress.getByAddress(aliceIPData);
             InetAddress bobIP = InetAddress.getByAddress(from.getIP());
             SessionKey aliceIntroKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
@@ -558,6 +592,7 @@ class PeerTestManager {
         } catch (UnknownHostException uhe) {
             if (_log.shouldLog(Log.WARN))
                 _log.warn("Unable to build the aliceIP from " + from + ", ip size: " + sz + " ip val: " + Base64.encode(aliceIPData), uhe);
+            _context.statManager().addRateData("udp.testBadIP", 1);
         }
     }
 
@@ -649,6 +684,7 @@ class PeerTestManager {
         } catch (UnknownHostException uhe) {
             if (_log.shouldLog(Log.WARN))
                 _log.warn("Unable to build the aliceIP from " + from, uhe);
+            _context.statManager().addRateData("udp.testBadIP", 1);
         }
     }
     
@@ -695,6 +731,7 @@ class PeerTestManager {
         } catch (UnknownHostException uhe) {
             if (_log.shouldLog(Log.WARN))
                 _log.warn("Unable to build the aliceIP from " + from, uhe);
+            _context.statManager().addRateData("udp.testBadIP", 1);
         }
     }