SSU2: Peer test improvements

- Return OK as Alice when only receiving msg 5, unless snatted (SSU 1 also)
- Allow and handle firewalled Charlie as Alice
- Only create peer test data once as Alice
- Store test status and send-to-Alice time in test state to support retransmission
- Don't delete state after sending msg 4 as Bob, for possible retransmission
- Check for IP mismatch from Charlie as Alice, abort test and assume good
- Check for port mismatch from Charlie as Alice and validate
- Skip unroutable addresses when searching through RI for the right one
- Respond to retransmitted msg 1 as Bob
- Respond to retransmitted msg 2 as Charlie
- Update Charlie timer to retransmit msg 5
- Only send Charlie RI to Alice (as Bob) if test was accepted
- Convert timers to SimpleTimer2 (SSU 1 also)
- Log tweaks
This commit is contained in:
zzz
2022-12-20 06:12:38 -05:00
parent 72e2161924
commit 4d65024977
3 changed files with 338 additions and 54 deletions

View File

@@ -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() {

View File

@@ -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.

View File

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