diff --git a/.github/workflows/ant.yml b/.github/workflows/ant.yml index ff65611c7..35b5383bf 100644 --- a/.github/workflows/ant.yml +++ b/.github/workflows/ant.yml @@ -93,22 +93,22 @@ jobs: echo "javac.compilerargs=-bootclasspath $HOME/openjdk-7/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/rt.jar:$HOME/openjdk-7/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/jce.jar -Xlint:all" >> override.properties - name: build with Ant run: ./installer/resources/maven-dev-release.sh 1 - - name: Upload installer.jar + - name: Upload servlet-i2p.jar uses: actions/upload-artifact@v4 with: name: servlet-i2p-${{ github.sha }}.jar path: pkg-mavencentral/servlet-i2p*.jar - - name: Upload installer.jar + - name: Upload streaming.jar uses: actions/upload-artifact@v4 with: name: streaming-${{ github.sha }}.jar path: pkg-mavencentral/streaming*.jar - - name: Upload installer.jar + - name: Upload mstreaming.jar uses: actions/upload-artifact@v4 with: name: mstreaming-${{ github.sha }}.jar path: pkg-mavencentral/mstreaming*.jar - - name: Upload installer.jar + - name: Upload router.jar uses: actions/upload-artifact@v4 with: name: router-${{ github.sha }}.jar 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 168fe5be2..eebc1c97c 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java +++ b/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java @@ -689,6 +689,13 @@ class InboundEstablishState extends EstablishBase implements NTCP2Payload.Payloa throw new DataFormatException(mismatchMessage + ri); } + if (ri.getCapabilities().equals("LU") && ri.getVersion().equals("0.9.56")) { + _context.banlist().banlistRouter(h, "Slow", null, + null, _context.clock().now() + 2*60*60*1000); + _msg3p2FailReason = NTCPConnection.REASON_BANNED; + throw new DataFormatException("Old and slow: " + h); + } + 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 e102c62c6..0b2f58f65 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java @@ -345,6 +345,14 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa if (!"2".equals(ra.getOption("v"))) throw new RIException("bad SSU2 v", REASON_VERSION); + if (ri.getCapabilities().equals("LU") && ri.getVersion().equals("0.9.56")) { + _context.banlist().banlistRouter(h, "Slow", null, + null, _context.clock().now() + 2*60*60*1000); + if (ri.verifySignature()) + _context.blocklist().add(_aliceIP); + throw new RIException("Old and slow: " + h, REASON_BANNED); + } + String smtu = ra.getOption(UDPAddress.PROP_MTU); int mtu = 0; try { diff --git a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java index ca4c31762..7f9c1aff0 100644 --- a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java +++ b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java @@ -262,9 +262,12 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver { case DeliveryInstructions.DELIVERY_MODE_LOCAL: if (_log.shouldLog(Log.DEBUG)) _log.debug("local delivery instructions for clove: " + data.getClass().getSimpleName()); - if (type == GarlicMessage.MESSAGE_TYPE) { + switch (type) { + case GarlicMessage.MESSAGE_TYPE: _receiver.receive((GarlicMessage)data); - } else if (type == DatabaseStoreMessage.MESSAGE_TYPE) { + break; + + case DatabaseStoreMessage.MESSAGE_TYPE: // Treat db store explicitly here (not in HandleFloodfillDatabaseStoreMessageJob), // since we don't want to republish (or flood) // unnecessarily. Reply tokens ignored. @@ -318,7 +321,9 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver { + ") for: " + dsm.getKey()); _context.inNetMessagePool().add(dsm, null, null, _msgIDBloomXor); } - } else if (_client != null && type == DatabaseSearchReplyMessage.MESSAGE_TYPE) { + break; + + case DatabaseSearchReplyMessage.MESSAGE_TYPE: // DSRMs show up here now that replies are encrypted // TODO: Strip in IterativeLookupJob etc. instead, depending on // LS or RI and client or expl., so that we can safely follow references @@ -336,24 +341,37 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver { } ****/ _context.inNetMessagePool().add(orig, null, null, _msgIDBloomXor); - } else if (type == DataMessage.MESSAGE_TYPE) { + break; + + case DataMessage.MESSAGE_TYPE: // a data message targetting the local router is how we send load tests (real // data messages target destinations) _context.statManager().addRateData("tunnel.handleLoadClove", 1); data = null; //_context.inNetMessagePool().add(data, null, null); - } else if (_client != null && type != DeliveryStatusMessage.MESSAGE_TYPE && - type != OutboundTunnelBuildReplyMessage.MESSAGE_TYPE) { - // drop it, since the data we receive shouldn't include other stuff, - // as that might open an attack vector + break; + + case DeliveryStatusMessage.MESSAGE_TYPE: + case OutboundTunnelBuildReplyMessage.MESSAGE_TYPE: + _context.inNetMessagePool().add(data, null, null, _msgIDBloomXor); + break; + + default: + // drop it, since the data we receive shouldn't include other stuff, + // as that might open an attack vector + if (_client != null) { _context.statManager().addRateData("tunnel.dropDangerousClientTunnelMessage", 1, data.getType()); _log.error("Dropped dangerous message received down a tunnel for " + _clientNickname + " (" + _client.toBase32() + ") : " + data, new Exception("cause")); - } else { - _context.inNetMessagePool().add(data, null, null, _msgIDBloomXor); - } + } else { + _log.error("Dropped dangerous message received down an expl. tunnel " + + data, new Exception("cause")); + } + break; + + } // switch (type) return; case DeliveryInstructions.DELIVERY_MODE_DESTINATION: diff --git a/router/java/src/net/i2p/router/tunnel/pool/TestJob.java b/router/java/src/net/i2p/router/tunnel/pool/TestJob.java index aa5ab59b6..b65a2e678 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TestJob.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TestJob.java @@ -76,20 +76,19 @@ class TestJob extends JobImpl { if (ctx.router().gracefulShutdownInProgress()) return; // don't reschedule _found = false; - // note: testing with exploratory tunnels always, even if the tested tunnel - // is a client tunnel (per _cfg.getDestination()) - // should we test with the tunnel that we exposed the creation with? - // (accessible as _cfg.getPairedTunnel()) - _replyTunnel = null; - _outTunnel = null; + boolean isExpl = _pool.getSettings().isExploratory(); if (_cfg.isInbound()) { _replyTunnel = _cfg; - // TODO if testing is re-enabled, pick closest to far end - _outTunnel = ctx.tunnelManager().selectOutboundTunnel(); + if (isExpl) + _outTunnel = ctx.tunnelManager().selectOutboundTunnel(); + else + _outTunnel = ctx.tunnelManager().selectOutboundTunnel(_pool.getSettings().getDestination()); _otherTunnel = (PooledTunnelCreatorConfig) _outTunnel; } else { - // TODO if testing is re-enabled, pick closest to far end - _replyTunnel = ctx.tunnelManager().selectInboundTunnel(); + if (isExpl) + _replyTunnel = ctx.tunnelManager().selectInboundTunnel(); + else + _replyTunnel = ctx.tunnelManager().selectInboundTunnel(_pool.getSettings().getDestination()); _outTunnel = _cfg; _otherTunnel = (PooledTunnelCreatorConfig) _replyTunnel; } @@ -109,7 +108,7 @@ class TestJob extends JobImpl { m.setMessageId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE)); ReplySelector sel = new ReplySelector(m.getMessageId(), testExpiration); OnTestReply onReply = new OnTestReply(); - OnTestTimeout onTimeout = new OnTestTimeout(); + OnTestTimeout onTimeout = new OnTestTimeout(now); OutNetMessage msg = ctx.messageRegistry().registerPending(sel, onReply, onTimeout); onReply.setSentMessage(msg); sendTest(m, testPeriod); @@ -122,37 +121,42 @@ class TestJob extends JobImpl { // remembering that key+tag so that we can decrypt it later. this means we can do the // garlic encryption without any ElGamal (yay) final RouterContext ctx = getContext(); - MessageWrapper.OneTimeSession sess; - if (_cfg.isInbound() && !_pool.getSettings().isExploratory()) { - // to client. false means don't force AES - sess = MessageWrapper.generateSession(ctx, _pool.getSettings().getDestination(), testPeriod, false); + if (ctx.random().nextInt(4) != 0) { + MessageWrapper.OneTimeSession sess; + if (_cfg.isInbound() && !_pool.getSettings().isExploratory()) { + // to client. false means don't force AES + sess = MessageWrapper.generateSession(ctx, _pool.getSettings().getDestination(), testPeriod, false); + } else { + // to router. AES or ChaCha. + sess = MessageWrapper.generateSession(ctx, testPeriod); + } + if (sess == null) { + scheduleRetest(); + return; + } + if (sess.tag != null) { + // AES + _encryptTag = sess.tag; + m = MessageWrapper.wrap(ctx, m, sess.key, sess.tag); + } else { + // ratchet + _ratchetEncryptTag = sess.rtag; + m = MessageWrapper.wrap(ctx, m, sess.key, sess.rtag); + } + if (m == null) { + // overloaded / unknown peers / etc + scheduleRetest(); + return; + } } else { - // to router. AES or ChaCha. - sess = MessageWrapper.generateSession(ctx, testPeriod); - } - if (sess == null) { - scheduleRetest(); - return; - } - GarlicMessage msg; - if (sess.tag != null) { - // AES - _encryptTag = sess.tag; - msg = MessageWrapper.wrap(ctx, m, sess.key, sess.tag); - } else { - // ratchet - _ratchetEncryptTag = sess.rtag; - msg = MessageWrapper.wrap(ctx, m, sess.key, sess.rtag); - } - if (msg == null) { - // overloaded / unknown peers / etc - scheduleRetest(); - return; + // Periodically send unencrypted DSM to provide cover for netdb replies + if (_log.shouldDebug()) + _log.debug("Sending garlic test unencrypted"); } _id = __id.getAndIncrement(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending garlic test #" + _id + " of " + _outTunnel + " / " + _replyTunnel); - ctx.tunnelDispatcher().dispatchOutbound(msg, _outTunnel.getSendTunnelId(0), + ctx.tunnelDispatcher().dispatchOutbound(m, _outTunnel.getSendTunnelId(0), _replyTunnel.getReceiveTunnelId(0), _replyTunnel.getPeer(0)); } @@ -327,17 +331,17 @@ class TestJob extends JobImpl { private class OnTestTimeout extends JobImpl { private final long _started; - public OnTestTimeout() { + public OnTestTimeout(long now) { super(TestJob.this.getContext()); - _started = getContext().clock().now(); + _started = now; } public String getName() { return "Tunnel test timeout"; } public void runJob() { - //if (_log.shouldLog(Log.WARN)) - // _log.warn("Tunnel test #" + _id + " timeout: found? " + _found); - if (!_found) { + if (_log.shouldDebug()) + _log.debug("Tunnel test #" + _id + " timeout: found? " + _found); + if (!_found && (_encryptTag != null || _ratchetEncryptTag != null)) { // don't clog up the SKM with old one-tag tagsets SessionKeyManager skm; if (_cfg.isInbound() && !_pool.getSettings().isExploratory()) { @@ -364,6 +368,8 @@ class TestJob extends JobImpl { rskm.consumeTag(_ratchetEncryptTag); } } + } + if (!_found) { testFailed(getContext().clock().now() - _started); } }