SSU2: Allow termination in retry

Handle skew reason in termination, possibly ban peer
Adjust clock at startup when terminated with skew reason
Don't send immediate termination to a too-close address
Javadoc fixes
Log tweaks
This commit is contained in:
zzz
2022-12-11 11:35:31 -05:00
parent dc400c652a
commit 415e31e560
6 changed files with 60 additions and 13 deletions

View File

@@ -822,6 +822,8 @@ class EstablishmentManager {
}
// very basic validation that this is probably in response to a good packet.
// we don't bother to decrypt the packet, even if it's only a token request
if (_transport.isTooClose(to.getIP()))
return;
DatagramPacket pkt = fromPacket.getPacket();
int off = pkt.getOffset();
int len = pkt.getLength();

View File

@@ -823,8 +823,8 @@ class IntroductionManager {
RouterInfo aliceRI = null;
int rcode;
if (charlie == null) {
if (_log.shouldWarn())
_log.warn("Relay tag not found " + tag + " from " + alice);
if (_log.shouldInfo())
_log.info("Relay tag not found " + tag + " from " + alice);
rcode = SSU2Util.RELAY_REJECT_BOB_NO_TAG;
} else if (charlie.getVersion() != 2) {
if (_log.shouldWarn())

View File

@@ -27,6 +27,7 @@ import net.i2p.data.router.RouterIdentity;
import net.i2p.data.router.RouterInfo;
import net.i2p.router.RouterContext;
import static net.i2p.router.transport.udp.SSU2Util.*;
import net.i2p.time.BuildTime;
import net.i2p.util.Addresses;
import net.i2p.util.Log;
@@ -262,7 +263,7 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
if (_log.shouldDebug())
_log.debug("Processed " + blocks + " blocks on " + this);
} catch (Exception e) {
throw new GeneralSecurityException("Session Created payload error", e);
throw new GeneralSecurityException("Retry or Session Created payload error", e);
}
}
@@ -346,13 +347,39 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
// this sets the state to FAILED
fail();
_transport.getEstablisher().receiveSessionDestroy(_remoteHostId, this);
Hash bob = _remotePeer.calculateHash();
if (reason == REASON_BANNED) {
_context.banlist().banlistRouter(_remotePeer.calculateHash(), "They banned us", null, null, _context.clock().now() + 2*60*60*1000);
_context.banlist().banlistRouter(bob, "They banned us", null, null, _context.clock().now() + 2*60*60*1000);
} else if (reason == REASON_MSG1) {
// this is like a short ban
_context.banlist().banlistRouter(_remotePeer.calculateHash(), "They banned us", null, null, _context.clock().now() + 20*60*1000);
_context.banlist().banlistRouter(bob, "They banned us", null, null, _context.clock().now() + 20*60*1000);
} else if (reason == REASON_SKEW) {
long sendOn = _timeReceived;
long recvOn = _establishBegin;
// Positive when we are ahead of them
long skew = recvOn - sendOn;
String skewString = DataHelper.formatDuration(Math.abs(skew));
if (_log.shouldWarn())
_log.warn("Failed, clock skew " + skewString + " on " + this);
if (sendOn == 0) {
// no datetime block
} else if (sendOn < BuildTime.getEarliestTime() || sendOn > BuildTime.getLatestTime()) {
// his problem
_context.banlist().banlistRouter(skewString, bob, _x("Excessive clock skew: {0}"));
} else {
boolean skewOK = skew < PacketHandler.MAX_SKEW && skew > (0 - PacketHandler.MAX_SKEW);
if (skewOK && !_context.clock().getUpdatedSuccessfully()) {
// adjust the clock one time in desperation
_context.clock().setOffset(0 - skew, true);
if (skew != 0)
_log.logAlways(Log.WARN, "NTP failure, UDP adjusting clock by " + skewString);
} else {
_context.banlist().banlistRouter(skewString, bob, _x("Excessive clock skew: {0}"));
}
}
_context.statManager().addRateData("udp.destroyedInvalidSkew", skew);
}
// TODO handle other cases - skew?
// TODO handle other cases
}
public void gotPathChallenge(RemoteHostId from, byte[] data) {
@@ -782,4 +809,15 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
' ' + _currentState +
(_introducers != null ? (" Introducers: " + _introducers.toString()) : "");
}
/**
* Mark a string for extraction by xgettext and translation.
* Use this only in static initializers.
* It does not translate!
* @return s
* @since 0.9.57
*/
private static final String _x(String s) {
return s;
}
}

View File

@@ -54,7 +54,7 @@ class PacketHandler {
* There's no use making it any larger, as messages will just be thrown out there.
*/
private static final long GRACE_PERIOD = Router.CLOCK_FUDGE_FACTOR + 30*1000;
private static final long MAX_SKEW = 90*24*60*60*1000L;
static final long MAX_SKEW = 90*24*60*60*1000L;
private enum AuthType { NONE, INTRO, BOBINTRO, SESSION }
@@ -897,8 +897,13 @@ class PacketHandler {
header.getType() != SSU2Util.SESSION_REQUEST_FLAG_BYTE ||
header.getVersion() != 2 ||
header.getNetID() != _networkID) {
if (_log.shouldWarn())
if (_log.shouldWarn()) {
if (header == null) {
// packet too short, possibly token-request-after-retry? let's see...
header = SSU2Header.trialDecryptLongHeader(packet, k1, k2);
}
_log.warn("Failed decrypt Session Request after Retry: " + header + " on " + state);
}
return false;
}
if (header.getSrcConnID() != state.getSendConnID()) {
@@ -1025,8 +1030,8 @@ class PacketHandler {
header.getType() != SSU2Util.RETRY_FLAG_BYTE ||
header.getVersion() != 2 ||
header.getNetID() != _networkID) {
if (_log.shouldWarn())
_log.warn("Does not decrypt as Session Created or Retry: " + header + " on " + state);
if (_log.shouldInfo())
_log.info("Does not decrypt as Session Created or Retry: " + header + " on " + state);
return false;
}
type = SSU2Util.RETRY_FLAG_BYTE;

View File

@@ -355,8 +355,6 @@ class SSU2Payload {
break;
case BLOCK_TERMINATION:
if (isHandshake)
throw new IOException("Illegal block in handshake: " + type);
if (len < 9)
throw new IOException("Bad length for TERMINATION: " + len);
long last = DataHelper.fromLong8(payload, i);

View File

@@ -99,7 +99,7 @@ final class SSU2Util {
public static final int DATA_HEADER_SIZE = SHORT_HEADER_SIZE;
/**
* The message types, 0-10, as bytes
* The message types, 0-11, as bytes
*/
public static final byte SESSION_REQUEST_FLAG_BYTE = UDPPacket.PAYLOAD_TYPE_SESSION_REQUEST;
public static final byte SESSION_CREATED_FLAG_BYTE = UDPPacket.PAYLOAD_TYPE_SESSION_CREATED;
@@ -110,6 +110,7 @@ final class SSU2Util {
public static final byte TOKEN_REQUEST_FLAG_BYTE = 10;
public static final byte HOLE_PUNCH_FLAG_BYTE = 11;
// HKDF infos
public static final String INFO_CREATED = "SessCreateHeader";
public static final String INFO_CONFIRMED = "SessionConfirmed";
public static final String INFO_DATA = "HKDFSSU2DataKeys";
@@ -118,10 +119,12 @@ final class SSU2Util {
public static final byte[] ZEROKEY = new byte[KEY_LEN];
// relay and peer test
// Signature prologues
public static final byte[] RELAY_REQUEST_PROLOGUE = DataHelper.getASCII("RelayRequestData");
public static final byte[] RELAY_RESPONSE_PROLOGUE = DataHelper.getASCII("RelayAgreementOK");
public static final byte[] PEER_TEST_PROLOGUE = DataHelper.getASCII("PeerTestValidate");
// test status codes
public static final int TEST_ACCEPT = 0;
public static final int TEST_REJECT_BOB_UNSPEC = 1;
public static final int TEST_REJECT_BOB_NO_CHARLIE = 2;
@@ -136,6 +139,7 @@ final class SSU2Util {
public static final int TEST_REJECT_CHARLIE_BANNED = 69;
public static final int TEST_REJECT_CHARLIE_UNKNOWN_ALICE = 70;
// relay status codes
public static final int RELAY_ACCEPT = 0;
public static final int RELAY_REJECT_BOB_UNSPEC = 1;
public static final int RELAY_REJECT_BOB_BANNED_CHARLIE = 2;