forked from I2P_Developers/i2p.i2p
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:
@@ -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() {
|
||||
|
@@ -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.
|
||||
|
@@ -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; }
|
||||
|
Reference in New Issue
Block a user