forked from I2P_Developers/i2p.i2p
SSU2: Relay WIP part 10
Fix OES2 initial state when pending intro Handle relay response as Alice Check relay request and relay response sigs as Bob Remove unused method OES2.reset() Fix javadoc for one OES state
This commit is contained in:
@@ -20,6 +20,7 @@ import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterIdentity;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.data.i2np.DatabaseLookupMessage;
|
||||
import net.i2p.data.i2np.DatabaseStoreMessage;
|
||||
import net.i2p.data.i2np.DeliveryStatusMessage;
|
||||
@@ -1514,14 +1515,130 @@ class EstablishmentManager {
|
||||
|
||||
/**
|
||||
* We are Alice, we sent a RelayRequest to Bob and got a RelayResponse back.
|
||||
* Time and version already checked by caller.
|
||||
*
|
||||
* SSU 2 only.
|
||||
*
|
||||
* @param data including token if code == 0
|
||||
* @param data including nonce, including token if code == 0
|
||||
* @since 0.9.55
|
||||
*/
|
||||
void receiveRelayResponse(PeerState2 bob, long nonce, int code, byte[] data) {
|
||||
// lookup nonce, determine who signed, validate sig, send SessionRequest if code == 0
|
||||
// don't remove unless accepted or rejected by charlie
|
||||
OutboundEstablishState charlie;
|
||||
Long lnonce = Long.valueOf(nonce);
|
||||
if (code > 0 && code < 64)
|
||||
charlie = _liveIntroductions.get(lnonce);
|
||||
else
|
||||
charlie = _liveIntroductions.remove(lnonce);
|
||||
if (charlie == null) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Dup or unknown RelayResponse: " + nonce);
|
||||
return; // already established
|
||||
}
|
||||
long token;
|
||||
if (code == 0) {
|
||||
token = DataHelper.fromLong8(data, data.length - 8);
|
||||
data = Arrays.copyOfRange(data, 0, data.length - 8);
|
||||
} else {
|
||||
token = 0;
|
||||
}
|
||||
Hash bobHash = bob.getRemotePeer();
|
||||
Hash charlieHash = charlie.getRemoteHostId().getPeerHash();
|
||||
RouterInfo bobRI = _context.netDb().lookupRouterInfoLocally(bobHash);
|
||||
RouterInfo charlieRI = _context.netDb().lookupRouterInfoLocally(charlieHash);
|
||||
Hash signer;
|
||||
if (code > 0 && code < 64)
|
||||
signer = bobHash;
|
||||
else
|
||||
signer = charlieHash;
|
||||
RouterInfo signerRI = _context.netDb().lookupRouterInfoLocally(signer);
|
||||
if (signerRI != null) {
|
||||
// validate signed data
|
||||
SigningPublicKey spk = signerRI.getIdentity().getSigningPublicKey();
|
||||
if (SSU2Util.validateSig(_context, SSU2Util.RELAY_REQUEST_PROLOGUE,
|
||||
bobHash, null, data, spk)) {
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Signature failed relay response\n" + signerRI);
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Signer RI not found " + signer);
|
||||
}
|
||||
if (code == 0) {
|
||||
int iplen = data[9] & 0xff;
|
||||
if (iplen != 6 && iplen != 18) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Bad IP length " + iplen + " from " + charlie);
|
||||
charlie.fail();
|
||||
return;
|
||||
}
|
||||
boolean isIPv6 = iplen == 18;
|
||||
int port = (int) DataHelper.fromLong(data, 10, 2);
|
||||
byte[] ip = new byte[iplen - 2];
|
||||
System.arraycopy(data, 12, ip, 0, iplen - 2);
|
||||
// validate
|
||||
if (!TransportUtil.isValidPort(port) ||
|
||||
!_transport.isValid(ip) ||
|
||||
_transport.isTooClose(ip) ||
|
||||
_context.blocklist().isBlocklisted(ip)) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Bad relay resp from " + charlie + " for " + Addresses.toString(ip, port));
|
||||
_context.statManager().addRateData("udp.relayBadIP", 1);
|
||||
charlie.fail();
|
||||
return;
|
||||
}
|
||||
InetAddress charlieIP;
|
||||
try {
|
||||
charlieIP = InetAddress.getByAddress(ip);
|
||||
} catch (UnknownHostException uhe) {
|
||||
charlie.fail();
|
||||
return;
|
||||
}
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Received RelayResponse from " + charlie + " - they are on " +
|
||||
Addresses.toString(ip, port));
|
||||
if (charlieRI == null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Charlie RI not found " + charlie);
|
||||
// maybe it will show up later
|
||||
return;
|
||||
}
|
||||
synchronized (charlie) {
|
||||
RemoteHostId oldId = charlie.getRemoteHostId();
|
||||
((OutboundEstablishState2) charlie).introduced(ip, port, token);
|
||||
RemoteHostId newId = charlie.getRemoteHostId();
|
||||
addOutboundToken(newId, token, _context.clock().now() + 10*1000);
|
||||
// Swap out the RemoteHostId the state is indexed under.
|
||||
// It was a Hash, change it to a IP/port.
|
||||
// Remove the entry in the byClaimedAddress map as it's now in main map.
|
||||
// Add an entry in the byHash map so additional OB pkts can find it.
|
||||
_outboundByHash.put(charlieHash, charlie);
|
||||
RemoteHostId claimed = charlie.getClaimedAddress();
|
||||
if (!oldId.equals(newId)) {
|
||||
_outboundStates.remove(oldId);
|
||||
_outboundStates.put(newId, charlie);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("RR replaced " + oldId + " with " + newId + ", claimed address was " + claimed);
|
||||
}
|
||||
//
|
||||
if (claimed != null)
|
||||
_outboundByClaimedAddress.remove(oldId, charlie); // only if == state
|
||||
}
|
||||
notifyActivity();
|
||||
} else if (code >= 64) {
|
||||
// that's it
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Received RelayResponse rejection " + code + " from charlie " + charlie);
|
||||
charlie.fail();
|
||||
_liveIntroductions.remove(lnonce);
|
||||
} else {
|
||||
// don't give up, maybe more bobs out there
|
||||
// TODO keep track
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Received RelayResponse rejection " + code + " from bob " + bob);
|
||||
notifyActivity();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1010,7 +1010,27 @@ class IntroductionManager {
|
||||
PeerState2 alice = _nonceToAlice.remove(Long.valueOf(nonce));
|
||||
if (alice != null) {
|
||||
// We are Bob, send to Alice
|
||||
// We don't check the signature here
|
||||
// Debug, check the signature, but send it along even if failed
|
||||
if (true) {
|
||||
RouterInfo charlie = _context.netDb().lookupRouterInfoLocally(peer.getRemotePeer());
|
||||
if (charlie != null) {
|
||||
byte[] signedData;
|
||||
if (status == 0)
|
||||
signedData = Arrays.copyOfRange(data, 0, data.length - 8); // token
|
||||
else
|
||||
signedData = data;
|
||||
SigningPublicKey spk = charlie.getIdentity().getSigningPublicKey();
|
||||
if (SSU2Util.validateSig(_context, SSU2Util.RELAY_REQUEST_PROLOGUE,
|
||||
_context.routerHash(), null, data, spk)) {
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Signature failed relay response\n" + charlie);
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Signer RI not found " + peer);
|
||||
}
|
||||
}
|
||||
byte[] idata = new byte[2 + data.length];
|
||||
//idata[0] = 0; // flag
|
||||
idata[1] = (byte) status;
|
||||
|
||||
@@ -110,7 +110,7 @@ class OutboundEstablishState {
|
||||
*/
|
||||
OB_STATE_RETRY_RECEIVED,
|
||||
/**
|
||||
* SSU2: We have sent a second token request with a new token
|
||||
* SSU2: We have sent a session request after receiving a retry
|
||||
* @since 0.9.54
|
||||
*/
|
||||
OB_STATE_REQUEST_SENT_NEW_TOKEN
|
||||
|
||||
@@ -111,12 +111,18 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
|
||||
}
|
||||
}
|
||||
_mtu = mtu;
|
||||
_routerAddress = ra;
|
||||
if (addr.getIntroducerCount() > 0) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("new outbound establish to " + remotePeer.calculateHash() + ", with address: " + addr);
|
||||
_currentState = OutboundState.OB_STATE_PENDING_INTRO;
|
||||
// we will get a token in the relay response or hole punch
|
||||
} else {
|
||||
_currentState = OutboundState.OB_STATE_UNKNOWN;
|
||||
_token = _transport.getEstablisher().getOutboundToken(_remoteHostId);
|
||||
if (_token != 0) {
|
||||
_currentState = OutboundState.OB_STATE_UNKNOWN;
|
||||
createNewState(ra);
|
||||
} else {
|
||||
_currentState = OutboundState.OB_STATE_NEEDS_TOKEN;
|
||||
}
|
||||
}
|
||||
|
||||
_sendConnID = ctx.random().nextLong();
|
||||
@@ -127,13 +133,6 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
|
||||
} while (_sendConnID == rcid);
|
||||
_rcvConnID = rcid;
|
||||
|
||||
_token = _transport.getEstablisher().getOutboundToken(_remoteHostId);
|
||||
_routerAddress = ra;
|
||||
if (_token != 0)
|
||||
createNewState(ra);
|
||||
else
|
||||
_currentState = OutboundState.OB_STATE_NEEDS_TOKEN;
|
||||
|
||||
byte[] ik = introKey.getData();
|
||||
_sendHeaderEncryptKey1 = ik;
|
||||
_rcvHeaderEncryptKey1 = ik;
|
||||
@@ -144,6 +143,19 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
|
||||
_log.debug("New " + this);
|
||||
}
|
||||
|
||||
/**
|
||||
* After introduction
|
||||
*
|
||||
* @since 0.9.55
|
||||
*/
|
||||
public synchronized void introduced(byte[] ip, int port, long token) {
|
||||
if (_currentState != OutboundState.OB_STATE_PENDING_INTRO)
|
||||
return;
|
||||
introduced(ip, port);
|
||||
_token = token;
|
||||
createNewState(_routerAddress);
|
||||
}
|
||||
|
||||
private void createNewState(RouterAddress addr) {
|
||||
String ss = addr.getOption("s");
|
||||
if (ss == null)
|
||||
@@ -163,18 +175,6 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
|
||||
_transport.getSSU2StaticPubKey(), 0);
|
||||
}
|
||||
|
||||
public synchronized void restart(long token) {
|
||||
_token = token;
|
||||
HandshakeState old = _handshakeState;
|
||||
if (old != null) {
|
||||
// TODO pass the old keys over to createNewState()
|
||||
old.destroy();
|
||||
}
|
||||
createNewState(_routerAddress);
|
||||
//_rcvHeaderEncryptKey2 will be set after the Session Request message is created
|
||||
_rcvHeaderEncryptKey2 = null;
|
||||
}
|
||||
|
||||
private void processPayload(byte[] payload, int offset, int length, boolean isHandshake) throws GeneralSecurityException {
|
||||
try {
|
||||
int blocks = SSU2Payload.processPayload(_context, this, payload, offset, length, isHandshake);
|
||||
|
||||
Reference in New Issue
Block a user