From 3c4956f0c3f8b5ddf853b6fbde62b0d2c3dd513d Mon Sep 17 00:00:00 2001 From: zzz <zzz@i2pmail.org> Date: Sun, 19 Jun 2022 12:04:10 -0400 Subject: [PATCH] SSU2: Add delayed lookup of RI for relay and peer test Prefer SSU2 introducers for slots 1 and 2 log tweaks --- .../transport/udp/IntroductionManager.java | 85 +++++++++++++++++-- .../router/transport/udp/PeerTestManager.java | 65 +++++++++++++- 2 files changed, 142 insertions(+), 8 deletions(-) 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 66a7d3e407..082b90b398 100644 --- a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java +++ b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java @@ -27,6 +27,7 @@ import net.i2p.router.RouterContext; import net.i2p.router.transport.TransportUtil; import net.i2p.util.Addresses; import net.i2p.util.Log; +import net.i2p.util.SimpleTimer2; import net.i2p.util.VersionComparator; /** @@ -235,11 +236,18 @@ class IntroductionManager { Introducer intro; byte[] key = ua.getIntroducerKey(i); if (key != null) { + // SSU 1 + //// debug, replace SSU 1 with SSU 2 if available for slots 1-2 + //// leave slot 0 for SSU 1 + //// this will churn the SSU 1 introducers, oh well + if (preferV2 && i > 0) + continue; intro = new Introducer(ua.getIntroducerHost(i).getAddress(), ua.getIntroducerPort(i), key, tag, sexp); if (_log.shouldInfo()) _log.info("Reusing introducer: " + ua.getIntroducerHost(i)); } else { + // SSU 2 intro = new Introducer(ua.getIntroducerHash(i), tag, sexp); if (_log.shouldInfo()) _log.info("Reusing introducer: " + ua.getIntroducerHash(i)); @@ -879,6 +887,73 @@ class IntroductionManager { * @since 0.9.55 */ void receiveRelayIntro(PeerState2 bob, Hash alice, byte[] data) { + receiveRelayIntro(bob, alice, data, 0); + } + + /** + * We are Charlie and we got this from Bob. + * Bob should have sent us the RI, but maybe it's in the block + * after this, or maybe it's in a different packet. + * Check for RI, if not found, return true to retry, unless retryCount is at the limit. + * Creates the timer if retryCount == 0. + * + * SSU 2 only. + * + * @return true if RI found, false to delay and retry. + * @since 0.9.55 + */ + private boolean receiveRelayIntro(PeerState2 bob, Hash alice, byte[] data, int retryCount) { + RouterInfo aliceRI = null; + if (retryCount < 5 && !_context.banlist().isBanlisted(alice)) { + aliceRI = _context.netDb().lookupRouterInfoLocally(alice); + if (aliceRI == null) { + if (_log.shouldInfo()) + _log.info("Delay after " + retryCount + " retries, no RI for " + alice.toBase64()); + if (retryCount == 0) + new DelayIntro(bob, alice, data); + return false; + } + } + receiveRelayIntro(bob, alice, data, aliceRI); + return true; + } + + /** + * Wait for RI. + * @since 0.9.55 + */ + private class DelayIntro extends SimpleTimer2.TimedEvent { + private final PeerState2 bob; + private final Hash alice; + private final byte[] data; + private volatile int count; + private static final long DELAY = 50; + + public DelayIntro(PeerState2 b, Hash a, byte[] d) { + super(_context.simpleTimer2()); + bob = b; + alice = a; + data = d; + schedule(DELAY); + } + + public void timeReached() { + boolean ok = receiveRelayIntro(bob, alice, data, ++count); + if (!ok) + reschedule(DELAY << count); + } + } + + /** + * We are Charlie and we got this from Bob. + * Send a HolePunch to Alice, who will soon be sending us a SessionRequest. + * And send a RelayResponse to bob. + * + * SSU 2 only. + * + * @since 0.9.55 + */ + private void receiveRelayIntro(PeerState2 bob, Hash alice, byte[] data, RouterInfo aliceRI) { long nonce = DataHelper.fromLong(data, 0, 4); long tag = DataHelper.fromLong(data, 4, 4); long time = DataHelper.fromLong(data, 8, 4) * 1000; @@ -912,7 +987,6 @@ class IntroductionManager { return; } - RouterInfo aliceRI = null; SessionKey aliceIntroKey = null; int rcode; PeerState aps = _transport.getPeerState(alice); @@ -927,8 +1001,7 @@ class IntroductionManager { rcode = SSU2Util.RELAY_REJECT_CHARLIE_ADDRESS; } else { // bob should have sent it to us. Don't bother to lookup - // remotely if he didn't, or it was out-of-order or lost. - aliceRI = _context.netDb().lookupRouterInfoLocally(alice); + // remotely if he didn't, or it was lost. if (aliceRI != null) { // validate signed data SigningPublicKey spk = aliceRI.getIdentity().getSigningPublicKey(); @@ -946,7 +1019,7 @@ class IntroductionManager { } } else { if (_log.shouldWarn()) - _log.warn("Alice RI not found " + alice); + _log.warn("Alice RI not found " + alice + " for relay intro from " + bob); rcode = SSU2Util.RELAY_REJECT_CHARLIE_UNKNOWN_ALICE; } } @@ -985,8 +1058,8 @@ class IntroductionManager { return; } UDPPacket packet = _builder2.buildRelayResponse(data, bob); - if (_log.shouldDebug()) - _log.debug("Send relay response " + rcode + " as charlie " + " nonce " + nonce + " to bob " + bob); + if (_log.shouldInfo()) + _log.info("Send relay response " + rcode + " as charlie " + " nonce " + nonce + " to bob " + bob); _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/PeerTestManager.java b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java index d7d9e1c1a9..c03982cd4b 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java @@ -32,6 +32,7 @@ import net.i2p.util.Addresses; import net.i2p.util.Log; import net.i2p.util.HexDump; import net.i2p.util.SimpleTimer; +import net.i2p.util.SimpleTimer2; import net.i2p.util.VersionComparator; /** @@ -838,7 +839,67 @@ class PeerTestManager { * @since 0.9.54 */ public void receiveTest(RemoteHostId from, PeerState2 fromPeer, int msg, int status, Hash h, byte[] data) { - receiveTest(from, fromPeer, msg, status, h, data, null, 0); + if (status == 0 && (msg == 2 || msg == 4) && !_context.banlist().isBanlisted(h)) + receiveTest(from, fromPeer, msg, h, data, 0); + else + receiveTest(from, fromPeer, msg, status, h, data, null, 0); + } + + /** + * Status 0 only, Msg 2 and 4 only, SSU 2 only. + * Bob should have sent us the RI, but maybe it's in the block + * after this, or maybe it's in a different packet. + * Check for RI, if not found, return true to retry, unless retryCount is at the limit. + * Creates the timer if retryCount == 0. + * + * We are Alice for msg 4, Charlie for msg 2. + * + * @return true if RI found, false to delay and retry. + * @since 0.9.55 + */ + private boolean receiveTest(RemoteHostId from, PeerState2 fromPeer, int msg, Hash h, byte[] data, int retryCount) { + if (retryCount < 5) { + RouterInfo ri = _context.netDb().lookupRouterInfoLocally(h); + if (ri == null) { + if (_log.shouldInfo()) + _log.info("Delay after " + retryCount + " retries, no RI for " + h.toBase64()); + if (retryCount == 0) + new DelayTest(from, fromPeer, msg, h, data); + return false; + } + } + receiveTest(from, fromPeer, msg, 0, h, data, null, 0); + return true; + } + + /** + * Wait for RI. + * @since 0.9.55 + */ + private class DelayTest extends SimpleTimer2.TimedEvent { + private final RemoteHostId from; + private final PeerState2 fromPeer; + private final int msg; + private final Hash hash; + private final byte[] data; + private volatile int count; + private static final long DELAY = 50; + + public DelayTest(RemoteHostId f, PeerState2 fp, int m, Hash h, byte[] d) { + super(_context.simpleTimer2()); + from = f; + fromPeer = fp; + msg = m; + hash = h; + data = d; + schedule(DELAY); + } + + public void timeReached() { + boolean ok = receiveTest(from, fromPeer, msg, hash, data, ++count); + if (!ok) + reschedule(DELAY << count); + } } /** @@ -1102,7 +1163,7 @@ class PeerTestManager { } } else { if (_log.shouldWarn()) - _log.warn("Alice RI not found " + h); + _log.warn("Alice RI not found " + h + " for peer test from " + fromPeer); rcode = SSU2Util.TEST_REJECT_CHARLIE_UNKNOWN_ALICE; } } -- GitLab