diff --git a/router/java/src/net/i2p/router/transport/udp/PeerTestEvent.java b/router/java/src/net/i2p/router/transport/udp/PeerTestEvent.java index de866d4df70ec350100187ba379ac852f0710d7c..6d48cf6906d68fc9bb516a953287ce5610dbe9c3 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerTestEvent.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerTestEvent.java @@ -83,8 +83,9 @@ class PeerTestEvent extends SimpleTimer2.TimedEvent { if (bob != null) { if (_log.shouldLog(Log.INFO)) _log.info("Running periodic test with bob = " + bob); - _testManager.runTest(bob); - setLastTested(isIPv6); + boolean started = _testManager.runTest(bob); + if (started) + setLastTested(isIPv6); } else { if (_log.shouldLog(Log.WARN)) _log.warn("Unable to run peer test, no peers available - v6? " + isIPv6); @@ -107,15 +108,15 @@ class PeerTestEvent extends SimpleTimer2.TimedEvent { * @since 0.9.39 */ public synchronized void forceRunSoon(boolean isIPv6, long delay) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("PTE.forceRunSoon() - v6? " + isIPv6, new Exception()); + //if (_log.shouldLog(Log.DEBUG)) + // _log.debug("PTE.forceRunSoon() - v6? " + isIPv6, new Exception()); if (!isIPv6 && _transport.isIPv4Firewalled()) return; if (isIPv6 && _transport.isIPv6Firewalled()) return; _forceRun = isIPv6 ? FORCE_IPV6 : FORCE_IPV4; if (_log.shouldDebug()) - _log.debug("reschedule for " + net.i2p.data.DataHelper.formatDuration(delay)); + _log.debug("PTE.forceRunSoon(), v6? " + isIPv6 + ", reschedule for " + net.i2p.data.DataHelper.formatDuration(delay)); reschedule(delay); } @@ -153,8 +154,8 @@ class PeerTestEvent extends SimpleTimer2.TimedEvent { _lastTestedV6.set(now); else _lastTested.set(now); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("PTE.setLastTested() - v6? " + isIPv6, new Exception()); + //if (_log.shouldLog(Log.DEBUG)) + // _log.debug("PTE.setLastTested() - v6? " + isIPv6, new Exception()); } private boolean shouldTest() { 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 6fa8779ff2fc96a2bbceb477bcc3a87fb3ccd6a0..4d5acfb95534c90c44340b05a17bbca4b3444326 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java @@ -171,6 +171,15 @@ class PeerTestManager { private static final long MAX_SKEW = 2*60*1000; private static final long MAX_NONCE = (1l << 32) - 1l; + // special markers for SSU2 when Charlie is firewalled + private static final InetAddress PENDING_IP; + static { + InetAddress p = null; + try { p = InetAddress.getByName("0.0.0.1"); } catch (UnknownHostException uhe) {} + PENDING_IP = p; + } + private static final int PENDING_PORT = 99999; + /** * Have seen peer tests (as Alice) get stuck (_currentTest != null) * so I've thrown some synchronizization on the methods; @@ -195,18 +204,19 @@ class PeerTestManager { * The next few methods are for when we are Alice * * @param bob IPv4 only + * @return true if we successfully started a test */ - public synchronized void runTest(PeerState bob) { + public synchronized boolean runTest(PeerState bob) { if (_currentTest != null) { if (_log.shouldLog(Log.WARN)) _log.warn("We are already running a test: " + _currentTest + ", aborting test with bob = " + bob); - return; + return false; } InetAddress bobIP = bob.getRemoteIPAddress(); if (_transport.isTooClose(bobIP.getAddress())) { if (_log.shouldLog(Log.WARN)) _log.warn("Not running test with Bob too close to us " + bobIP); - return; + return false; } PeerTestState test = new PeerTestState(ALICE, bob, bobIP instanceof Inet6Address, _context.random().nextLong(MAX_NONCE), @@ -219,7 +229,7 @@ class PeerTestManager { } catch (UnknownHostException uhe) { if (_log.shouldWarn()) _log.warn("Unable to get our IP", uhe); - return; + return false; } } _currentTest = test; @@ -234,14 +244,21 @@ class PeerTestManager { test.incrementPacketsRelayed(); sendTestToBob(); - _context.simpleTimer2().addEvent(new ContinueTest(test.getNonce()), RESEND_TIMEOUT); + new ContinueTest(test.getNonce()); + return true; } - private class ContinueTest implements SimpleTimer.TimedEvent { + /** + * SSU 1 or 2. We are Alice. + */ + private class ContinueTest extends SimpleTimer2.TimedEvent { private final long _nonce; + /** schedules itself */ public ContinueTest(long nonce) { + super(_context.simpleTimer2()); _nonce = nonce; + schedule(RESEND_TIMEOUT); } public void timeReached() { @@ -265,24 +282,35 @@ class PeerTestManager { testComplete(); return; } - if (state.getReceiveBobTime() <= 0) { - // no message from Bob yet, send it again + long bobTime = state.getReceiveBobTime(); + long charlieTime = state.getReceiveCharlieTime(); + if (bobTime <= 0 && charlieTime <= 0) { + // no message from Bob or Charlie yet, send it again sendTestToBob(); - } else if (state.getReceiveCharlieTime() <= 0) { + } else if (charlieTime <= 0) { // received from Bob, but no reply from Charlie. send it to // Bob again so he pokes Charlie - // This is only useful for SSU 1; SSU 2 discards dups + // We don't resend to Bob for SSU2; Charlie will retransmit. if (state.getBob().getVersion() == 1) sendTestToBob(); + // TODO if version 2 and long enough, send msg 6 anyway + } else if (bobTime <= 0) { + // received from Charlie, but no reply from Bob. Send it to + // Bob again so he retransmits his reply. + // Bob handles dups / retx as of 0.9.57 + //if (state.getBob().getVersion() == 1) + sendTestToBob(); + // TODO if version 1 and long enough, send msg 6 anyway + // For version 2, we can't send msg 6 without knowing charlie's intro key } else { // received from both Bob and Charlie, but we haven't received a // second message from Charlie yet sendTestToCharlie(); } // retx at 4, 10, 17, 25 elapsed time - _context.simpleTimer2().addEvent(ContinueTest.this, RESEND_TIMEOUT + (sent*1000)); + reschedule(RESEND_TIMEOUT + (sent*1000)); } else { - _context.simpleTimer2().addEvent(ContinueTest.this, RESEND_TIMEOUT - timeSinceSend); + reschedule(RESEND_TIMEOUT - timeSinceSend); } } } @@ -307,28 +335,33 @@ class PeerTestManager { if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending test to Bob: " + test); UDPPacket packet; - if (test.getBob().getVersion() == 1) { + PeerState bob = test.getBob(); + if (bob.getVersion() == 1) { packet = _packetBuilder.buildPeerTestFromAlice(test.getBobIP(), test.getBobPort(), test.getBobCipherKey(), test.getBobMACKey(), test.getNonce(), _transport.getIntroKey()); } else { - SigningPrivateKey spk = _context.keyManager().getSigningPrivateKey(); - PeerState2 bob = (PeerState2) test.getBob(); - // TODO only create this once - byte[] data = SSU2Util.createPeerTestData(_context, bob.getRemotePeer(), null, - ALICE, test.getNonce(), bob.getOurIP(), bob.getOurPort(), spk); + PeerState2 bob2 = (PeerState2) bob; + // only create this once + byte[] data = test.getTestData(); if (data == null) { - if (_log.shouldWarn()) - _log.warn("sig fail"); - testComplete(); - return; + SigningPrivateKey spk = _context.keyManager().getSigningPrivateKey(); + data = SSU2Util.createPeerTestData(_context, bob2.getRemotePeer(), null, + ALICE, test.getNonce(), bob2.getOurIP(), bob2.getOurPort(), spk); + if (data == null) { + if (_log.shouldWarn()) + _log.warn("sig fail"); + testComplete(); + return; + } + test.setTestData(data); } - packet = _packetBuilder2.buildPeerTestFromAlice(data, bob); + packet = _packetBuilder2.buildPeerTestFromAlice(data, bob2); } _transport.send(packet); long now = _context.clock().now(); test.setLastSendTime(now); - test.getBob().setLastSendTime(now); + bob.setLastSendTime(now); } else { _currentTest = null; } @@ -580,12 +613,18 @@ class PeerTestManager { } } else if (test.getReceiveCharlieTime() > 0) { // we received only one message (5) from charlie - status = Status.UNKNOWN; + // change in 0.9.57; previously returned UNKNOWN always + if (_transport.isSnatted()) { + status = Status.UNKNOWN; + } else { + // assume good + status = isIPv6 ? Status.IPV4_UNKNOWN_IPV6_OK : Status.IPV4_OK_IPV6_UNKNOWN; + } } else if (test.getReceiveBobTime() > 0) { // we received a message from bob (4) but no messages from charlie status = isIPv6 ? Status.IPV4_UNKNOWN_IPV6_FIREWALLED : Status.IPV4_FIREWALLED_IPV6_UNKNOWN; } else { - // we never received anything from bob - he is either down, + // we never received anything from bob or charlie, // ignoring us, or unable to get a Charlie to respond status = Status.UNKNOWN; // TODO disconnect from Bob if version 2? @@ -890,6 +929,7 @@ class PeerTestManager { private volatile int count; private static final long DELAY = 50; + /** schedules itself */ public DelayTest(RemoteHostId f, PeerState2 fp, int m, Hash h, byte[] d) { super(_context.simpleTimer2()); from = f; @@ -966,7 +1006,7 @@ class PeerTestManager { if (_log.shouldDebug()) _log.debug("Got peer test msg " + msg + " status: " + status + - " hash: " + h + + " hash: " + (h != null ? h.toBase64() : "null") + " nonce: " + nonce + " time: " + DataHelper.formatTime(time) + " ip/port: " + Addresses.toString(testIP, testPort) + @@ -1008,8 +1048,34 @@ class PeerTestManager { } if (msg < 3) { if (state != null) { + byte[] retx = state.getTestData(); + if (retx != null) { + if (msg == 1 && state.getSendAliceTime() > 0) { + if (_log.shouldDebug()) + _log.debug("Retx msg 4 to alice on " + state); + // we already sent to alice, send it again + PeerState2 alice = state.getAlice(); + UDPPacket packet = _packetBuilder2.buildPeerTestToAlice(state.getStatus(), state.getCharlieHash(), data, alice); + _transport.send(packet); + alice.setLastSendTime(now); + state.setSendAliceTime(now); + return; + } else if (msg == 2) { + if (_log.shouldDebug()) + _log.debug("Retx msg 3 to bob on " + state); + PeerState2 bob = (PeerState2) state.getBob(); + UDPPacket packet = _packetBuilder2.buildPeerTestToBob(state.getStatus(), data, bob); + _transport.send(packet); + bob.setLastSendTime(now); + // should we retx msg 5 also? + return; + } else { + // msg 1 but haven't heard from a good charlie yet + // TODO pick a new charlie + } + } if (_log.shouldWarn()) - _log.warn("Dup msg " + msg + " from " + fromPeer); + _log.warn("Dup msg " + msg + " from " + fromPeer + " on " + state); return; } if (_activeTests.size() >= MAX_ACTIVE_TESTS) { @@ -1041,6 +1107,8 @@ class PeerTestManager { switch (msg) { // alice to bob, in-session + // If we immediately reject with a TEST_REJECT_BOB code, we do not + // save the test state; so if Alice retransmits, we'll do it all again. case 1: { if (status != 0) { if (_log.shouldWarn()) @@ -1120,23 +1188,27 @@ class PeerTestManager { // save alice-signed test data in case we need to send to another charlie state.setTestData(data); _activeTests.put(lNonce, state); - _context.simpleTimer2().addEvent(new RemoveTest(lNonce), MAX_BOB_LIFETIME); + // TODO we need a retx or pick-new-charlie timer + new RemoveTest(lNonce, MAX_BOB_LIFETIME); // send alice RI to charlie if (_log.shouldDebug()) _log.debug("Send Alice RI and msg 2 to charlie on " + state); + // TODO see if Alide RI will compress enough to fit in the peer test packet DatabaseStoreMessage dbsm = new DatabaseStoreMessage(_context); dbsm.setEntry(aliceRI); dbsm.setMessageExpiration(now + 10*1000); _transport.send(dbsm, charlie); // forward to charlie, don't bother to validate signed data - // FIXME this will probably get there before the RI UDPPacket packet = _packetBuilder2.buildPeerTestToCharlie(alice, data, (PeerState2) charlie); - _transport.send(packet); + // delay because dbsm is queued, we want it to get there first + new DelaySend(packet, 100); charlie.setLastSendTime(now); break; } // bob to charlie, in-session + // If we immediately reject with a TEST_REJECT_CHARLIE code, we do not + // save the test state; so if Alice or Bob retransmits, we'll do it all again. case 2: { if (status != 0) { if (_log.shouldWarn()) @@ -1202,7 +1274,7 @@ class PeerTestManager { state.setReceiveBobTime(now); state.setLastSendTime(now); _activeTests.put(lNonce, state); - _context.simpleTimer2().addEvent(new RemoveTest(lNonce), MAX_CHARLIE_LIFETIME); + new CharlieTimer(lNonce); } // generate our signed data // we sign it even if rejecting, not required though @@ -1232,6 +1304,10 @@ class PeerTestManager { aliceIntroKey, true, sendId, rcvId, data); _transport.send(packet); + state.incrementPacketsRelayed(); + // save charlie-signed test data in case we need to retransmit to alice or bob + state.setStatus(rcode); + state.setTestData(data); } break; } @@ -1252,12 +1328,14 @@ class PeerTestManager { _log.info("Charlie response " + status + " picked a new one " + charlie + " on " + state); state.setCharlie(charlie.getRemoteIPAddress(), charlie.getRemotePort(), charlie.getRemotePeer()); state.setLastSendTime(now); + // TODO see if Alice RI will compress enough to fit in the peer test packet DatabaseStoreMessage dbsm = new DatabaseStoreMessage(_context); dbsm.setEntry(aliceRI); dbsm.setMessageExpiration(now + 10*1000); _transport.send(dbsm, charlie); UDPPacket packet = _packetBuilder2.buildPeerTestToCharlie(alice, state.getTestData(), (PeerState2) charlie); - _transport.send(packet); + // delay because dbsm is queued, we want it to get there first + new DelaySend(packet, 100); charlie.setLastSendTime(now); break; } @@ -1269,11 +1347,13 @@ class PeerTestManager { state.setLastSendTime(now); PeerState2 alice = state.getAlice(); Hash charlie = fromPeer.getRemotePeer(); - RouterInfo charlieRI = _context.netDb().lookupRouterInfoLocally(charlie); + RouterInfo charlieRI = (status == SSU2Util.TEST_ACCEPT) ? _context.netDb().lookupRouterInfoLocally(charlie) : null; if (charlieRI != null) { - // send charlie RI to alice + // send charlie RI to alice, only if ACCEPT. + // Alice would need it to verify sig, but not worth the bandwidth if (_log.shouldDebug()) _log.debug("Send Charlie RI to alice on " + state); + // TODO see if Charlie RI will compress enough to fit in the peer test packet DatabaseStoreMessage dbsm = new DatabaseStoreMessage(_context); dbsm.setEntry(charlieRI); dbsm.setMessageExpiration(now + 10*1000); @@ -1290,7 +1370,7 @@ class PeerTestManager { } } else { // oh well, maybe alice has it - if (_log.shouldLog(Log.WARN)) + if (status == SSU2Util.TEST_ACCEPT && _log.shouldWarn()) _log.warn("No charlie RI"); } // forward to alice, don't bother to validate signed data @@ -1298,10 +1378,18 @@ class PeerTestManager { if (_log.shouldDebug()) _log.debug("Send msg 4 status " + status + " to alice on " + state); UDPPacket packet = _packetBuilder2.buildPeerTestToAlice(status, charlie, data, alice); - _transport.send(packet); + // delay because dbsm is queued, we want it to get there first + if (charlieRI != null) + new DelaySend(packet, 100); + else + _transport.send(packet); alice.setLastSendTime(now); - // we are done - _activeTests.remove(lNonce); + // overwrite alice-signed test data with charlie-signed data in case we need to retransmit + state.setStatus(status); + state.setSendAliceTime(now); + state.setTestData(data); + // we should be done, but stick around for possible retx to alice + //_activeTests.remove(lNonce); break; } @@ -1374,6 +1462,8 @@ class PeerTestManager { // i2pd Bob picks firewalled Charlie if (_log.shouldWarn()) _log.warn("Charlie IP not found: " + test + '\n' + ra); + charlieIP = PENDING_IP; + charliePort = PENDING_PORT; } } else { if (_log.shouldWarn()) @@ -1397,7 +1487,44 @@ class PeerTestManager { testComplete(); return; } - test.setCharlie(charlieIP, charliePort, h); + InetAddress oldIP = test.getCharlieIP(); + if (oldIP == null) { + // msg 4 before msg 5 + test.setCharlie(charlieIP, charliePort, h); + } else if (charlieIP == PENDING_IP) { + // dup msg 4 ?? + } else { + // msg 4 after msg 5, charlie is not firewalled + int oldPort = test.getCharliePort(); + if (!charlieIP.equals(oldIP)) { + if (_log.shouldWarn()) + _log.warn("Charlie IP mismatch, msg 4: " + Addresses.toString(charlieIP.getAddress(), charliePort) + + ", msg 5: " + Addresses.toString(oldIP.getAddress(), oldPort) + " on " + test); + // stop here, assume good unless snatted + if (!_transport.isSnatted()) { + test.setAliceIPFromCharlie(test.getAliceIP()); + test.setAlicePortFromCharlie(test.getAlicePort()); + } + testComplete(); + return; + } else if (charliePort != oldPort) { + if (_log.shouldWarn()) + _log.warn("Charlie port mismatch, msg 4: " + Addresses.toString(charlieIP.getAddress(), charliePort) + + ", msg 5: " + Addresses.toString(oldIP.getAddress(), oldPort) + " on " + test); + if (TransportUtil.isValidPort(charliePort)) { + // Charlie is snatted or confused about his port, update port and keep going + test.setCharlie(charlieIP, charliePort, h); + } else { + // Don't like charlie's port, stop here, assume good unless snatted + if (!_transport.isSnatted()) { + test.setAliceIPFromCharlie(test.getAliceIP()); + test.setAlicePortFromCharlie(test.getAlicePort()); + } + testComplete(); + return; + } + } + } test.setCharlieIntroKey(charlieIntroKey); if (test.getReceiveCharlieTime() > 0) { // send msg 6 @@ -1421,6 +1548,51 @@ class PeerTestManager { _log.warn("Test nonce mismatch? " + nonce); return; } + InetAddress charlieIP = test.getCharlieIP(); + if (charlieIP == null) { + // msg 5 before msg 4 + try { + test.setCharlie(InetAddress.getByAddress(fromIP), fromPort, null); + } catch (UnknownHostException uhe) {} + } else if (charlieIP == PENDING_IP) { + // msg 5 after msg 4, charlie is firewalled + // set charlie's real IP/port + try { + test.setCharlie(InetAddress.getByAddress(fromIP), fromPort, test.getCharlieHash()); + } catch (UnknownHostException uhe) {} + } else { + // msg 5 after msg 4, charlie is not firewalled + byte[] oldIP = charlieIP.getAddress(); + int oldPort = test.getCharliePort(); + if (!DataHelper.eq(fromIP, oldIP)) { + if (_log.shouldWarn()) + _log.warn("Charlie IP mismatch, msg 4: " + Addresses.toString(oldIP, oldPort) + + ", msg 5: " + Addresses.toString(fromIP, fromPort) + " on " + test); + // stop here, assume good unless snatted + if (!_transport.isSnatted()) { + test.setAliceIPFromCharlie(test.getAliceIP()); + test.setAlicePortFromCharlie(test.getAlicePort()); + } + testComplete(); + return; + } else if (fromPort != oldPort) { + if (_log.shouldWarn()) + _log.warn("Charlie port mismatch, msg 4: " + Addresses.toString(oldIP, oldPort) + + ", msg 5: " + Addresses.toString(fromIP, fromPort) + " on " + test); + if (TransportUtil.isValidPort(fromPort)) { + // Charlie is snatted or confused about his port, update port and keep going + test.setCharlie(charlieIP, fromPort, h); + } else { + // Don't like charlie's port, stop here, assume good unless snatted + if (!_transport.isSnatted()) { + test.setAliceIPFromCharlie(test.getAliceIP()); + test.setAlicePortFromCharlie(test.getAlicePort()); + } + testComplete(); + return; + } + } + } test.setReceiveCharlieTime(now); // Do NOT set this here, only for msg 7, this is how testComplete() knows we got msg 7 //test.setAlicePortFromCharlie(testPort); @@ -1440,6 +1612,7 @@ class PeerTestManager { } } else { // we haven't gotten message 4 yet + // We don't know Charlie's hash or intro key, we can't send msg 6 until we do if (_log.shouldDebug()) _log.debug("Got msg 5 before msg 4 on " + test); } @@ -1478,7 +1651,10 @@ class PeerTestManager { state.getAliceIntroKey(), false, sendId, rcvId, data); _transport.send(packet); + state.incrementPacketsRelayed(); // for now, ignore address block, we could pass it to externalAddressReceived() + // we should be done, but stick around in case we get a retransmitted msg 6 + //_activeTests.remove(lNonce); break; } @@ -1562,6 +1738,10 @@ class PeerTestManager { if (!host.contains(".") && !caps.contains(TransportImpl.CAP_IPV4)) continue; } + // skip bogus addresses + byte[] ip = addr.getIP(); + if (ip != null && !TransportUtil.isPubliclyRoutable(ip, true)) + continue; ra = addr; break; } @@ -1652,7 +1832,7 @@ class PeerTestManager { if (isNew) { Long lnonce = Long.valueOf(nonce); _activeTests.put(lnonce, state); - _context.simpleTimer2().addEvent(new RemoveTest(lnonce), MAX_CHARLIE_LIFETIME); + new RemoveTest(lnonce, MAX_CHARLIE_LIFETIME); } state.setLastSendTime(now); @@ -1766,7 +1946,7 @@ class PeerTestManager { if (isNew) { Long lnonce = Long.valueOf(nonce); _activeTests.put(lnonce, state); - _context.simpleTimer2().addEvent(new RemoveTest(lnonce), MAX_BOB_LIFETIME); + new RemoveTest(lnonce, MAX_BOB_LIFETIME); } state.setLastSendTime(now); @@ -1873,15 +2053,19 @@ class PeerTestManager { _context.statManager().addRateData("udp.testBadIP", 1); } } - + /** + * SSU 1 Bob/Charlie and SSU 2 Bob * forget about charlie's nonce after a short while. */ - private class RemoveTest implements SimpleTimer.TimedEvent { + private class RemoveTest extends SimpleTimer2.TimedEvent { private final Long _nonce; - public RemoveTest(Long nonce) { + /** schedules itself */ + public RemoveTest(Long nonce, long delay) { + super(_context.simpleTimer2()); _nonce = nonce; + schedule(delay); } public void timeReached() { @@ -1890,6 +2074,77 @@ class PeerTestManager { } } + /** + * SSU 2 Charlie only. + * Retransmit msg 5 if necessary, and then + * forget about charlie's nonce after a short while. + * + * @since 0.9.57 + */ + private class CharlieTimer extends SimpleTimer2.TimedEvent { + private final Long _nonce; + + /** schedules itself */ + public CharlieTimer(Long nonce) { + super(_context.simpleTimer2()); + _nonce = nonce; + schedule(RESEND_TIMEOUT); + } + + public void timeReached() { + PeerTestState state = _activeTests.get(_nonce); + if (state == null) + return; + long now = _context.clock().now(); + long remaining = state.getBeginTime() + MAX_CHARLIE_LIFETIME - now; + if (remaining <= 0) { + if (_log.shouldDebug()) + _log.debug("Expired as charlie on " + state); + _activeTests.remove(_nonce); + return; + } + if (state.getReceiveAliceTime() > 0) { + // got msg 6, no more need to retx msg 5 + reschedule(remaining); + return; + } + + // retransmit at 4/8/12 sec, no backoff + if (_log.shouldDebug()) + _log.debug("Retx msg 5 to alice on " + state); + long nonce = _nonce.longValue(); + long sendId = (nonce << 32) | nonce; + long rcvId = ~sendId; + // send the same data we sent to Bob + UDPPacket packet = _packetBuilder2.buildPeerTestToAlice(state.getAliceIP(), state.getAlicePort(), + state.getAliceIntroKey(), true, + sendId, rcvId, state.getTestData()); + _transport.send(packet); + state.incrementPacketsRelayed(); + state.setLastSendTime(now); + reschedule(Math.min(RESEND_TIMEOUT, remaining)); + } + } + + /** + * Simple fix for RI getting there before PeerTest. + * SSU2 only. We are Bob, for delaying msg sent after RI to Alice or Charlie. + * @since 0.9.57 + */ + private class DelaySend extends SimpleTimer2.TimedEvent { + private final UDPPacket pkt; + + public DelaySend(UDPPacket packet, long delay) { + super(_context.simpleTimer2()); + pkt = packet; + schedule(delay); + } + + public void timeReached() { + _transport.send(pkt); + } + } + /** * This is only for out-of-session messages 5-7, * where most blocks are not allowed. diff --git a/router/java/src/net/i2p/router/transport/udp/PeerTestState.java b/router/java/src/net/i2p/router/transport/udp/PeerTestState.java index 24376003d2e5cd32831307a67f99a1a7f38c907b..a2b82cbac5d45f0510fbca8c216532d89cfc8ead 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerTestState.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerTestState.java @@ -42,6 +42,8 @@ class PeerTestState { private long _receiveAliceTime; private long _receiveBobTime; private long _receiveCharlieTime; + private long _sendAliceTime; + private int _status; private final AtomicInteger _packetsRelayed = new AtomicInteger(); public enum Role {ALICE, BOB, CHARLIE}; @@ -198,13 +200,39 @@ class PeerTestState { public void setReceiveCharlieTime(long when) { _receiveCharlieTime = when; } /** - * SSU2 only, we are Bob + * when did we send to alice, SSU2 Bob only + * @since 0.9.57 + */ + public long getSendAliceTime() { return _sendAliceTime; } + + /** + * when did we send to alice, SSU2 Bob only + * @since 0.9.57 + */ + public void setSendAliceTime(long when) { _sendAliceTime = when; } + + /** + * what code did we send to alice, SSU2 Bob only + * @since 0.9.57 + */ + public int getStatus() { return _status; } + + /** + * what code did we send to alice, SSU2 Bob only + * @since 0.9.57 + */ + public void setStatus(int status) { _status = status; } + + /** + * Get for retransmission. + * SSU2 only, we are Alice, Bob or Charlie * @since 0.9.57 */ public byte[] getTestData() { return _testData; } /** - * SSU2 only, we are Bob + * Save for retransmission. + * SSU2 only, we are Alice, Bob or Charlie * @since 0.9.57 */ public void setTestData(byte[] data) { _testData = data; }