forked from I2P_Developers/i2p.i2p
SSU2: Relay WIP part 4
Decrypt and handle hole punch as alice
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package net.i2p.router.transport.udp;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.GeneralSecurityException;
|
||||
@@ -10,7 +11,10 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.southernstorm.noise.protocol.ChaChaPolyCipherState;
|
||||
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterIdentity;
|
||||
@@ -27,9 +31,11 @@ import net.i2p.router.transport.TransportUtil;
|
||||
import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
|
||||
import static net.i2p.router.transport.udp.InboundEstablishState.InboundState.*;
|
||||
import static net.i2p.router.transport.udp.OutboundEstablishState.OutboundState.*;
|
||||
import static net.i2p.router.transport.udp.SSU2Util.*;
|
||||
import net.i2p.router.util.DecayingHashSet;
|
||||
import net.i2p.router.util.DecayingBloomFilter;
|
||||
import net.i2p.util.Addresses;
|
||||
import net.i2p.util.HexDump;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.LHMCache;
|
||||
import net.i2p.util.Log;
|
||||
@@ -1371,6 +1377,8 @@ class EstablishmentManager {
|
||||
* Called from UDPReceiver.
|
||||
* Accelerate response to RelayResponse if we haven't sent it yet.
|
||||
*
|
||||
* SSU 1 only.
|
||||
*
|
||||
* @since 0.9.15
|
||||
*/
|
||||
void receiveHolePunch(InetAddress from, int fromPort) {
|
||||
@@ -1393,6 +1401,66 @@ class EstablishmentManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from PacketHandler.
|
||||
* Accelerate response to RelayResponse if we haven't sent it yet.
|
||||
*
|
||||
* SSU 2 only.
|
||||
*
|
||||
* @param id non-null
|
||||
* @param packet header already decrypted
|
||||
* @since 0.9.55
|
||||
*/
|
||||
void receiveHolePunch(RemoteHostId id, UDPPacket packet) {
|
||||
DatagramPacket pkt = packet.getPacket();
|
||||
int off = pkt.getOffset();
|
||||
int len = pkt.getLength();
|
||||
byte data[] = pkt.getData();
|
||||
long rcvConnID = DataHelper.fromLong8(data, off);
|
||||
long sendConnID = DataHelper.fromLong8(data, off + SRC_CONN_ID_OFFSET);
|
||||
int type = data[off + TYPE_OFFSET] & 0xff;
|
||||
if (type != HOLE_PUNCH_FLAG_BYTE)
|
||||
return;
|
||||
byte[] introKey = _transport.getSSU2StaticIntroKey();
|
||||
ChaChaPolyCipherState chacha = new ChaChaPolyCipherState();
|
||||
chacha.initializeKey(introKey, 0);
|
||||
long n = DataHelper.fromLong(data, off + PKT_NUM_OFFSET, 4);
|
||||
chacha.setNonce(n);
|
||||
try {
|
||||
// decrypt in-place
|
||||
chacha.decryptWithAd(data, off, LONG_HEADER_SIZE,
|
||||
data, off + LONG_HEADER_SIZE, data, off + LONG_HEADER_SIZE, len - LONG_HEADER_SIZE);
|
||||
int payloadLen = len - (LONG_HEADER_SIZE + MAC_LEN);
|
||||
SSU2Payload.PayloadCallback cb = new HPCallback(id);
|
||||
SSU2Payload.processPayload(_context, cb, data, off + LONG_HEADER_SIZE, payloadLen, false);
|
||||
// TODO process cb fields
|
||||
} catch (Exception e) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Bad HolePunch packet:\n" + HexDump.dump(data, off, len), e);
|
||||
return;
|
||||
} finally {
|
||||
chacha.destroy();
|
||||
}
|
||||
|
||||
// TODO now we can look up by nonce instead if we want
|
||||
OutboundEstablishState state = _outboundStates.get(id);
|
||||
if (state != null) {
|
||||
boolean sendNow = state.receiveHolePunch();
|
||||
if (sendNow) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Hole punch from " + state + ", sending SessionRequest now");
|
||||
notifyActivity();
|
||||
} else {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Hole punch from " + state + ", already sent SessionRequest");
|
||||
}
|
||||
} else {
|
||||
// HolePunch received before RelayResponse, and we didn't know the IP/port, or it changed
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("No state found for hole punch from " + id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Are IP and port valid? This is only for checking the relay response.
|
||||
* Allow IPv6 as of 0.9.50.
|
||||
@@ -1963,6 +2031,89 @@ class EstablishmentManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process SSU2 hole punch payload
|
||||
*
|
||||
* @since 0.9.55
|
||||
*/
|
||||
private class HPCallback implements SSU2Payload.PayloadCallback {
|
||||
private final RemoteHostId _from;
|
||||
public long _timeReceived;
|
||||
public byte[] _aliceIP;
|
||||
public int _alicePort;
|
||||
public int _respCode;
|
||||
public byte[] _respData;
|
||||
|
||||
public HPCallback(RemoteHostId from) {
|
||||
_from = from;
|
||||
}
|
||||
|
||||
public void gotDateTime(long time) {
|
||||
_timeReceived = time;
|
||||
}
|
||||
|
||||
public void gotOptions(byte[] options, boolean isHandshake) {}
|
||||
|
||||
public void gotRI(RouterInfo ri, boolean isHandshake, boolean flood) {
|
||||
throw new IllegalStateException("Bad block in HP");
|
||||
}
|
||||
|
||||
public void gotRIFragment(byte[] data, boolean isHandshake, boolean flood, boolean isGzipped, int frag, int totalFrags) {
|
||||
throw new IllegalStateException("Bad block in HP");
|
||||
}
|
||||
|
||||
public void gotAddress(byte[] ip, int port) {
|
||||
_aliceIP = ip;
|
||||
_alicePort = port;
|
||||
}
|
||||
|
||||
public void gotRelayTagRequest() {
|
||||
throw new IllegalStateException("Bad block in HP");
|
||||
}
|
||||
|
||||
public void gotRelayTag(long tag) {
|
||||
throw new IllegalStateException("Bad block in HP");
|
||||
}
|
||||
|
||||
public void gotRelayRequest(byte[] data) {
|
||||
throw new IllegalStateException("Bad block in HP");
|
||||
}
|
||||
|
||||
public void gotRelayResponse(int status, byte[] data) {
|
||||
_respCode = status;
|
||||
_respData = data;
|
||||
}
|
||||
|
||||
public void gotRelayIntro(Hash aliceHash, byte[] data) {
|
||||
throw new IllegalStateException("Bad block in HP");
|
||||
}
|
||||
|
||||
public void gotPeerTest(int msg, int status, Hash h, byte[] data) {
|
||||
throw new IllegalStateException("Bad block in HP");
|
||||
}
|
||||
|
||||
public void gotToken(long token, long expires) {
|
||||
throw new IllegalStateException("Bad block in HP");
|
||||
}
|
||||
|
||||
public void gotI2NP(I2NPMessage msg) {
|
||||
throw new IllegalStateException("Bad block in HP");
|
||||
}
|
||||
|
||||
public void gotFragment(byte[] data, int off, int len, long messageId,int frag, boolean isLast) {
|
||||
throw new IllegalStateException("Bad block in HP");
|
||||
}
|
||||
|
||||
public void gotACK(long ackThru, int acks, byte[] ranges) {
|
||||
throw new IllegalStateException("Bad block in HP");
|
||||
}
|
||||
|
||||
public void gotTermination(int reason, long count) {
|
||||
throw new IllegalStateException("Bad block in HP");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// End SSU 2 ////
|
||||
|
||||
|
||||
|
||||
@@ -810,7 +810,7 @@ class PacketHandler {
|
||||
SSU2Header.Header header;
|
||||
int type;
|
||||
if (state == null) {
|
||||
// Session Request, Token Request, or Peer Test
|
||||
// Session Request, Token Request, Peer Test 5-7, or Hole Punch
|
||||
k2 = k1;
|
||||
header = SSU2Header.trialDecryptHandshakeHeader(packet, k1, k2);
|
||||
if (header == null ||
|
||||
@@ -818,7 +818,7 @@ class PacketHandler {
|
||||
header.getVersion() != 2 ||
|
||||
header.getNetID() != _networkID) {
|
||||
if (header != null && _log.shouldInfo())
|
||||
_log.info("Does not decrypt as Session Request, attempt to decrypt as Token Request/Peer Test: " + header + " from " + from);
|
||||
_log.info("Does not decrypt as Session Request, attempt to decrypt as TokenRequest/PeerTest/HolePunch: " + header + " from " + from);
|
||||
// The first 32 bytes were fine, but it corrupted the next 32 bytes
|
||||
// TODO make this more efficient, just take the first 32 bytes
|
||||
header = SSU2Header.trialDecryptLongHeader(packet, k1, k2);
|
||||
@@ -916,6 +916,11 @@ class PacketHandler {
|
||||
_log.debug("Got a Peer Test");
|
||||
if (SSU2Util.ENABLE_PEER_TEST)
|
||||
_testManager.receiveTest(from, packet);
|
||||
} else if (type == SSU2Util.HOLE_PUNCH_FLAG_BYTE) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Got a Hole Punch");
|
||||
if (SSU2Util.ENABLE_RELAY)
|
||||
_establisher.receiveHolePunch(from, packet);
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Got unknown message " + header + " on " + state);
|
||||
|
||||
Reference in New Issue
Block a user