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); } }