SSU2: Hook in new classes to EstablishmentManager

Implement handshake retransmissions
Fix up calls to IES2/OES2
split() TODO
not hooked in to PacketHandler yet
WIP, untested
This commit is contained in:
zzz
2022-02-27 12:03:28 -05:00
parent 7eda9c77af
commit 0c08a05bce
7 changed files with 533 additions and 84 deletions

View File

@@ -2,6 +2,7 @@ package net.i2p.router.transport.udp;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
@@ -402,10 +403,20 @@ class EstablishmentManager {
// don't ask if they are indirect
boolean requestIntroduction = allowExtendedOptions && !isIndirect &&
_transport.introducersMaybeRequired(TransportUtil.isIPv6(ra));
state = new OutboundEstablishState(_context, maybeTo, to,
int version = _transport.getSSUVersion(ra);
if (version == 1) {
state = new OutboundEstablishState(_context, maybeTo, to,
toIdentity, allowExtendedOptions,
requestIntroduction,
sessionKey, addr, _transport.getDHFactory());
} else if (version == 2) {
state = new OutboundEstablishState2(_context, _transport, maybeTo, to,
toIdentity, requestIntroduction, sessionKey, ra, addr);
} else {
// shouldn't happen
_transport.failed(msg, "OB to bad addr? " + ra);
return;
}
OutboundEstablishState oldState = _outboundStates.putIfAbsent(to, state);
boolean isNew = oldState == null;
if (isNew) {
@@ -471,6 +482,7 @@ class EstablishmentManager {
/**
* Got a SessionRequest (initiates an inbound establishment)
*
* SSU 1 only.
*/
void receiveSessionRequest(RemoteHostId from, UDPPacketReader reader) {
if (!TransportUtil.isValidPort(from.getPort()) || !_transport.isValid(from.getIP())) {
@@ -544,9 +556,92 @@ class EstablishmentManager {
notifyActivity();
}
/**
* Got a SessionRequest OR a TokenRequest (initiates an inbound establishment)
*
* SSU 2 only.
* @since 0.9.54
*/
void receiveSessionRequest(RemoteHostId from, UDPPacket packet) {
if (!TransportUtil.isValidPort(from.getPort()) || !_transport.isValid(from.getIP())) {
if (_log.shouldWarn())
_log.warn("Receive session request from invalid: " + from);
return;
}
boolean isNew = false;
InboundEstablishState state = _inboundStates.get(from);
if (state == null) {
// TODO this is insufficient to prevent DoSing, especially if
// IP spoofing is used. For further study.
if (!shouldAllowInboundEstablishment()) {
if (_log.shouldWarn())
_log.warn("Dropping inbound establish, increase " + PROP_MAX_CONCURRENT_ESTABLISH);
_context.statManager().addRateData("udp.establishDropped", 1);
return; // drop the packet
}
if (_context.blocklist().isBlocklisted(from.getIP())) {
if (_log.shouldWarn())
_log.warn("Receive session request from blocklisted IP: " + from);
_context.statManager().addRateData("udp.establishBadIP", 1);
return; // drop the packet
}
if (!_transport.allowConnection())
return; // drop the packet
try {
state = new InboundEstablishState2(_context, _transport, packet);
} catch (GeneralSecurityException gse) {
if (_log.shouldWarn())
_log.warn("Corrupt Session/Token Request from: " + from, gse);
_context.statManager().addRateData("udp.establishDropped", 1);
return;
}
/**** TODO
if (_replayFilter.add(state.getReceivedX(), 0, 8)) {
if (_log.shouldLog(Log.WARN))
_log.warn("Duplicate X in session request from: " + from);
_context.statManager().addRateData("udp.dupDHX", 1);
return; // drop the packet
}
****/
InboundEstablishState oldState = _inboundStates.putIfAbsent(from, state);
isNew = oldState == null;
if (!isNew)
// whoops, somebody beat us to it, throw out the state we just created
state = oldState;
}
if (isNew) {
/**** TODO
// Don't offer to relay to privileged ports.
// Only offer for an IPv4 session.
// TODO if already we have their RI, only offer if they need it (no 'C' cap)
// if extended options, only if they asked for it
if (state.isIntroductionRequested() &&
state.getSentPort() >= 1024 &&
_transport.canIntroduce(state.getSentIP().length == 16)) {
// ensure > 0
long tag = 1 + _context.random().nextLong(MAX_TAG_VALUE);
state.setSentRelayTag(tag);
} else {
// we got an IB even though we were firewalled, hidden, not high cap, etc.
}
****/
if (_log.shouldInfo())
_log.info("Received NEW session request " + state);
} else {
if (_log.shouldDebug())
_log.debug("Receive DUP session request from: " + state);
}
notifyActivity();
}
/**
* got a SessionConfirmed (should only happen as part of an inbound
* establishment)
*
* SSU 1 only.
*/
void receiveSessionConfirmed(RemoteHostId from, UDPPacketReader reader) {
InboundEstablishState state = _inboundStates.get(from);
@@ -561,9 +656,42 @@ class EstablishmentManager {
}
}
/**
* got a SessionConfirmed (should only happen as part of an inbound
* establishment)
*
* SSU 2 only.
* @since 0.9.54
*/
void receiveSessionConfirmed(RemoteHostId from, UDPPacket packet) {
InboundEstablishState state = _inboundStates.get(from);
if (state != null) {
if (state.getVersion() != 2)
return;
InboundEstablishState2 state2 = (InboundEstablishState2) state;
try {
state2.receiveSessionConfirmed(packet);
} catch (GeneralSecurityException gse) {
if (_log.shouldWarn())
_log.warn("Corrupt Session Confirmed from: " + from, gse);
state.fail();
return;
}
// we are done, go right to ps2
handleCompletelyEstablished(state2);
notifyActivity();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive session confirmed from: " + state);
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Receive (DUP?) session confirmed from: " + from);
}
}
/**
* Got a SessionCreated (in response to our outbound SessionRequest)
*
* SSU 1 only.
*/
void receiveSessionCreated(RemoteHostId from, UDPPacketReader reader) {
OutboundEstablishState state = _outboundStates.get(from);
@@ -577,6 +705,64 @@ class EstablishmentManager {
_log.warn("Receive (DUP?) session created from: " + from);
}
}
/**
* Got a SessionCreated (in response to our outbound SessionRequest)
*
* SSU 2 only.
* @since 0.9.54
*/
void receiveSessionCreated(RemoteHostId from, UDPPacket packet) {
OutboundEstablishState state = _outboundStates.get(from);
if (state != null) {
if (state.getVersion() != 2)
return;
OutboundEstablishState2 state2 = (OutboundEstablishState2) state;
try {
state2.receiveSessionCreated(packet);
} catch (GeneralSecurityException gse) {
if (_log.shouldWarn())
_log.warn("Corrupt Session Created from: " + from, gse);
state.fail();
return;
}
notifyActivity();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive session created from: " + state);
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Receive (DUP?) session created from: " + from);
}
}
/**
* Got a Retry (in response to our outbound SessionRequest or TokenRequest)
*
* SSU 2 only.
* @since 0.9.54
*/
void receiveRetry(RemoteHostId from, UDPPacket packet) {
OutboundEstablishState state = _outboundStates.get(from);
if (state != null) {
if (state.getVersion() != 2)
return;
OutboundEstablishState2 state2 = (OutboundEstablishState2) state;
try {
state2.receiveSessionCreated(packet);
} catch (GeneralSecurityException gse) {
if (_log.shouldWarn())
_log.warn("Corrupt Retry from: " + from, gse);
state.fail();
return;
}
notifyActivity();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive retry from: " + state);
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Receive (DUP?) retry from: " + from);
}
}
/**
* Got a SessionDestroy on an established conn
@@ -719,25 +905,35 @@ class EstablishmentManager {
if (state.isComplete()) return;
RouterIdentity remote = state.getConfirmedIdentity();
PeerState peer = new PeerState(_context, _transport,
state.getSentIP(), state.getSentPort(), remote.calculateHash(), true, state.getRTT());
peer.setCurrentCipherKey(state.getCipherKey());
peer.setCurrentMACKey(state.getMACKey());
peer.setWeRelayToThemAs(state.getSentRelayTag());
// Lookup the peer's MTU from the netdb, since it isn't included in the protocol setup (yet)
// TODO if we don't have RI then we will get it shortly, but too late.
// Perhaps netdb should notify transport when it gets a new RI...
RouterInfo info = _context.netDb().lookupRouterInfoLocally(remote.calculateHash());
if (info != null) {
RouterAddress addr = _transport.getTargetAddress(info);
if (addr != null) {
String smtu = addr.getOption(UDPAddress.PROP_MTU);
if (smtu != null) {
try {
boolean isIPv6 = state.getSentIP().length == 16;
int mtu = MTU.rectify(isIPv6, Integer.parseInt(smtu));
peer.setHisMTU(mtu);
} catch (NumberFormatException nfe) {}
PeerState peer;
int version = state.getVersion();
if (version == 1) {
peer = new PeerState(_context, _transport,
state.getSentIP(), state.getSentPort(), remote.calculateHash(), true, state.getRTT());
peer.setCurrentCipherKey(state.getCipherKey());
peer.setCurrentMACKey(state.getMACKey());
peer.setWeRelayToThemAs(state.getSentRelayTag());
} else {
InboundEstablishState2 state2 = (InboundEstablishState2) state;
peer = state2.getPeerState();
}
if (version == 1) {
// Lookup the peer's MTU from the netdb, since it isn't included in the protocol setup (yet)
// TODO if we don't have RI then we will get it shortly, but too late.
// Perhaps netdb should notify transport when it gets a new RI...
RouterInfo info = _context.netDb().lookupRouterInfoLocally(remote.calculateHash());
if (info != null) {
RouterAddress addr = _transport.getTargetAddress(info);
if (addr != null) {
String smtu = addr.getOption(UDPAddress.PROP_MTU);
if (smtu != null) {
try {
boolean isIPv6 = state.getSentIP().length == 16;
int mtu = MTU.rectify(isIPv6, Integer.parseInt(smtu));
peer.setHisMTU(mtu);
} catch (NumberFormatException nfe) {}
}
}
}
}
@@ -838,11 +1034,18 @@ class EstablishmentManager {
if (claimed != null)
_outboundByClaimedAddress.remove(claimed, state);
_outboundByHash.remove(remote.calculateHash(), state);
PeerState peer = new PeerState(_context, _transport,
state.getSentIP(), state.getSentPort(), remote.calculateHash(), false, state.getRTT());
peer.setCurrentCipherKey(state.getCipherKey());
peer.setCurrentMACKey(state.getMACKey());
peer.setTheyRelayToUsAs(state.getReceivedRelayTag());
int version = state.getVersion();
PeerState peer;
if (version == 1) {
peer = new PeerState(_context, _transport,
state.getSentIP(), state.getSentPort(), remote.calculateHash(), false, state.getRTT());
peer.setCurrentCipherKey(state.getCipherKey());
peer.setCurrentMACKey(state.getMACKey());
peer.setTheyRelayToUsAs(state.getReceivedRelayTag());
} else {
OutboundEstablishState2 state2 = (OutboundEstablishState2) state;
peer = state2.getPeerState();
}
int mtu = state.getRemoteAddress().getMTU();
if (mtu > 0)
peer.setHisMTU(mtu);
@@ -859,10 +1062,10 @@ class EstablishmentManager {
_context.statManager().addRateData("udp.outboundEstablishTime", state.getLifetime());
DatabaseStoreMessage dbsm = null;
if (!state.isFirstMessageOurDSM()) {
dbsm = getOurInfo();
} else if (_log.shouldLog(Log.INFO)) {
_log.info("Skipping publish: " + state);
if (version == 1) {
if (!state.isFirstMessageOurDSM()) {
dbsm = getOurInfo();
}
}
List<OutNetMessage> msgs = new ArrayList<OutNetMessage>(8);
@@ -912,18 +1115,30 @@ class EstablishmentManager {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Send created to: " + state);
try {
state.generateSessionKey();
} catch (DHSessionKeyBuilder.InvalidPublicParameterException ippe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Peer " + state + " sent us an invalid DH parameter", ippe);
_inboundStates.remove(state.getRemoteHostId());
state.fail();
return;
int version = state.getVersion();
UDPPacket pkt;
if (version == 1) {
try {
state.generateSessionKey();
} catch (DHSessionKeyBuilder.InvalidPublicParameterException ippe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Peer " + state + " sent us an invalid DH parameter", ippe);
_inboundStates.remove(state.getRemoteHostId());
state.fail();
return;
}
pkt = _builder.buildSessionCreatedPacket(state,
_transport.getExternalPort(state.getSentIP().length == 16),
_transport.getIntroKey());
} else {
// if already sent, get from the state to retx
InboundEstablishState2 state2 = (InboundEstablishState2) state;
InboundEstablishState.InboundState istate = state2.getState();
if (istate == IB_STATE_CREATED_SENT)
pkt = state2.getRetransmitSessionCreatedPacket();
else
pkt = _builder2.buildSessionCreatedPacket((InboundEstablishState2) state);
}
UDPPacket pkt = _builder.buildSessionCreatedPacket(state,
_transport.getExternalPort(state.getSentIP().length == 16),
_transport.getIntroKey());
if (pkt == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("Peer " + state + " sent us an invalid IP?");
@@ -932,7 +1147,9 @@ class EstablishmentManager {
return;
}
_transport.send(pkt);
state.createdPacketSent();
if (version == 1)
state.createdPacketSent();
// else PacketBuilder2 told the state
}
/**
@@ -941,14 +1158,28 @@ class EstablishmentManager {
private void sendRequest(OutboundEstablishState state) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Send SessionRequest to: " + state);
UDPPacket packet = _builder.buildSessionRequestPacket(state);
int version = state.getVersion();
UDPPacket packet;
if (version == 1) {
packet = _builder.buildSessionRequestPacket(state);
} else {
// if already sent, get from the state to retx
OutboundEstablishState2 state2 = (OutboundEstablishState2) state;
OutboundEstablishState.OutboundState ostate = state2.getState();
if (ostate == OB_STATE_REQUEST_SENT)
packet = state2.getRetransmitSessionRequestPacket();
else
packet = _builder2.buildSessionRequestPacket(state2);
}
if (packet != null) {
_transport.send(packet);
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to build a session request packet for " + state);
}
state.requestSent();
if (version == 1)
state.requestSent();
// else PacketBuilder2 told the state
}
/**
@@ -1105,19 +1336,41 @@ class EstablishmentManager {
// gives us the opportunity to "detect" our external addr
_transport.externalAddressReceived(state.getRemoteIdentity().calculateHash(), state.getReceivedIP(), state.getReceivedPort());
// signs if we havent signed yet
state.prepareSessionConfirmed();
// BUG - handle null return
UDPPacket packets[] = _builder.buildSessionConfirmedPackets(state, _context.router().getRouterInfo().getIdentity());
int version = state.getVersion();
UDPPacket packets[];
if (version == 1) {
// signs if we havent signed yet
state.prepareSessionConfirmed();
packets = _builder.buildSessionConfirmedPackets(state, _context.router().getRouterInfo().getIdentity());
} else {
OutboundEstablishState2 state2 = (OutboundEstablishState2) state;
OutboundEstablishState.OutboundState ostate = state2.getState();
// shouldn't happen, we go straight to confirmed after sending
if (ostate == OB_STATE_CONFIRMED_COMPLETELY)
return;
packets = _builder2.buildSessionConfirmedPackets(state2, _context.router().getRouterInfo());
}
if (packets == null) {
state.fail();
return;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Send confirm to: " + state);
for (int i = 0; i < packets.length; i++)
for (int i = 0; i < packets.length; i++) {
_transport.send(packets[i]);
}
state.confirmedPacketsSent();
if (version == 1) {
state.confirmedPacketsSent();
} else {
// save for retx
OutboundEstablishState2 state2 = (OutboundEstablishState2) state;
state2.confirmedPacketsSent(packets);
// we are done, go right to ps2
handleCompletelyEstablished(state2);
}
}
/**

View File

@@ -2,8 +2,10 @@ package net.i2p.router.transport.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.util.List;
@@ -44,6 +46,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
private final byte[] _rcvHeaderEncryptKey1;
private byte[] _sendHeaderEncryptKey2;
private byte[] _rcvHeaderEncryptKey2;
private byte[] _sessCrForReTX;
// testing
private static final boolean ENFORCE_TOKEN = false;
@@ -52,7 +55,8 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
/**
* @param localPort Must be our external port, otherwise the signature of the
* SessionCreated message will be bad if the external port != the internal port.
* @param packet with all header encryption removed
* @param packet with all header encryption removed,
* either a SessionRequest OR a TokenRequest.
*/
public InboundEstablishState2(RouterContext ctx, UDPTransport transport,
UDPPacket packet) throws GeneralSecurityException {
@@ -279,6 +283,13 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
// end payload callbacks
/////////////////////////////////////////////////////////
// SSU 1 unsupported things
@Override
public void generateSessionKey() { throw new UnsupportedOperationException(); }
// SSU 2 things
public long getSendConnID() { return _sendConnID; }
public long getRcvConnID() { return _rcvConnID; }
public long getToken() { return _token; }
@@ -406,6 +417,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
if (_log.shouldDebug())
_log.debug("State after sess conf: " + _handshakeState);
processPayload(payload, payload.length, false);
_sessCrForReTX = null;
// TODO split, calculate keys
@@ -426,6 +438,58 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
packetReceived();
}
/**
* note that we just sent the SessionCreated packet
* and save it for retransmission
*/
public synchronized void createdPacketSent(DatagramPacket pkt) {
if (_sessCrForReTX == null) {
// store pkt for retx
byte data[] = pkt.getData();
int off = pkt.getOffset();
int len = pkt.getLength();
_sessCrForReTX = new byte[len];
System.arraycopy(data, off, _sessCrForReTX, 0, len);
}
createdPacketSent();
}
/**
* @return null if not sent or already got the session created
*/
public synchronized UDPPacket getRetransmitSessionCreatedPacket() {
if (_sessCrForReTX == null)
return null;
UDPPacket packet = UDPPacket.acquire(_context, false);
DatagramPacket pkt = packet.getPacket();
byte data[] = pkt.getData();
int off = pkt.getOffset();
System.arraycopy(_sessCrForReTX, 0, data, off, _sessCrForReTX.length);
InetAddress to;
try {
to = InetAddress.getByAddress(_aliceIP);
} catch (UnknownHostException uhe) {
if (_log.shouldLog(Log.ERROR))
_log.error("How did we think this was a valid IP? " + _remoteHostId);
packet.release();
return null;
}
pkt.setAddress(to);
pkt.setPort(_alicePort);
packet.setMessageType(PacketBuilder2.TYPE_CONF);
packet.setPriority(PacketBuilder2.PRIORITY_HIGH);
createdPacketSent();
return packet;
}
/**
* @return null we have not received the session confirmed
*/
public synchronized PeerState2 getPeerState() {
// TODO
return null;
}
@Override
public String toString() {

View File

@@ -16,6 +16,7 @@ import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.SessionKey;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.router.RouterAddress;
import net.i2p.data.router.RouterIdentity;
import net.i2p.data.router.RouterInfo;
import net.i2p.router.RouterContext;
@@ -36,6 +37,7 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
private final UDPTransport _transport;
private final long _sendConnID;
private final long _rcvConnID;
private final RouterAddress _routerAddress;
private long _token;
private HandshakeState _handshakeState;
private final byte[] _sendHeaderEncryptKey1;
@@ -44,6 +46,8 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
private byte[] _rcvHeaderEncryptKey2;
private final byte[] _rcvRetryHeaderEncryptKey2;
private int _mtu;
private byte[] _sessReqForReTX;
private byte[] _sessConfForReTX;
private static final boolean SET_TOKEN = false;
/**
@@ -56,10 +60,9 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
* @param addr non-null
*/
public OutboundEstablishState2(RouterContext ctx, UDPTransport transport, RemoteHostId claimedAddress,
RemoteHostId remoteHostId, int mtu,
RouterIdentity remotePeer, byte[] publicKey,
boolean needIntroduction,
SessionKey introKey, UDPAddress addr) {
RemoteHostId remoteHostId, RouterIdentity remotePeer,
boolean needIntroduction,
SessionKey introKey, RouterAddress ra, UDPAddress addr) throws IllegalArgumentException {
super(ctx, claimedAddress, remoteHostId, remotePeer, needIntroduction, introKey, addr);
_transport = transport;
if (claimedAddress != null) {
@@ -68,10 +71,8 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
} catch (UnknownHostException uhe) {
throw new IllegalArgumentException("bad IP", uhe);
}
_mtu = mtu;
} else {
_mtu = PeerState.MIN_IPV6_MTU;
}
_mtu = addr.getMTU();
if (addr.getIntroducerCount() > 0) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("new outbound establish to " + remotePeer.calculateHash() + ", with address: " + addr);
@@ -80,20 +81,18 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
_currentState = OutboundState.OB_STATE_UNKNOWN;
}
// SSU2
createNewState(publicKey);
_sendConnID = ctx.random().nextLong();
// rcid == scid is not allowed
long rcid;
do {
rcid = ctx.random().nextLong();
} while (_sendConnID == rcid);
if (SET_TOKEN) {
do {
_token = ctx.random().nextLong();
} while (_token == 0);
}
_token = _transport.getEstablisher().getOutboundToken(_remotePeer.calculateHash());
_routerAddress = ra;
if (_token != 0)
createNewState(ra);
_rcvConnID = rcid;
byte[] ik = introKey.getData();
_sendHeaderEncryptKey1 = ik;
@@ -103,7 +102,15 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
_rcvRetryHeaderEncryptKey2 = ik;
}
private void createNewState(byte[] publicKey) {
private void createNewState(RouterAddress addr) {
String ss = addr.getOption("s");
if (ss == null)
throw new IllegalArgumentException("no SSU2 S");
byte[] publicKey = Base64.decode(ss);
if (publicKey == null)
throw new IllegalArgumentException("bad SSU2 S");
if (publicKey.length != 32)
throw new IllegalArgumentException("bad SSU2 S len");
try {
_handshakeState = new HandshakeState(HandshakeState.PATTERN_ID_XK_SSU2, HandshakeState.INITIATOR, _transport.getXDHFactory());
} catch (GeneralSecurityException gse) {
@@ -119,8 +126,9 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
HandshakeState old = _handshakeState;
byte[] pub = new byte[32];
old.getRemotePublicKey().getPublicKey(pub, 0);
createNewState(pub);
old.destroy();
createNewState(_routerAddress);
if (old != null)
old.destroy();
//_rcvHeaderEncryptKey2 will be set after the Session Request message is created
_rcvHeaderEncryptKey2 = null;
}
@@ -201,6 +209,11 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
// end payload callbacks
/////////////////////////////////////////////////////////
// SSU 1 unsupported things
// SSU 2 things
@Override
public int getVersion() { return 2; }
public long getSendConnID() { return _sendConnID; }
@@ -226,6 +239,12 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
/** what is the largest packet we can send to the peer? */
public int getMTU() { return _mtu; }
public synchronized void receiveRetry(UDPPacket packet) throws GeneralSecurityException {
////// TODO state check
createNewState(_routerAddress);
////// TODO state change
}
public synchronized void receiveSessionCreated(UDPPacket packet) throws GeneralSecurityException {
////// todo fix state check
if (_currentState == OutboundState.OB_STATE_VALIDATION_FAILED) {
@@ -256,6 +275,7 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
if (_log.shouldDebug())
_log.debug("State after sess cr: " + _handshakeState);
processPayload(payload, payload.length, true);
_sessReqForReTX = null;
_sendHeaderEncryptKey2 = SSU2Util.hkdf(_context, _handshakeState.getChainingKey(), "SessionConfirmed");
if (_currentState == OutboundState.OB_STATE_UNKNOWN ||
@@ -271,14 +291,92 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
}
/**
* note that we just sent the SessionRequest packet
* note that we just sent the SessionConfirmed packets
* and save them for retransmission
*/
@Override
public synchronized void requestSent() {
/// TODO store pkt for retx
public synchronized void tokenRequestSent(DatagramPacket packet) {
if (_currentState == OutboundState.OB_STATE_UNKNOWN)
_currentState = OutboundState.OB_STATE_TOKEN_REQUEST_SENT;
else if (_currentState == OutboundState.OB_STATE_RETRY_RECEIVED)
_currentState = OutboundState.OB_STATE_REQUEST_SENT_NEW_TOKEN;
// don't bother saving for retx, just make a new one every time
}
/**
* note that we just sent the SessionRequest packet
* and save it for retransmission
*/
public synchronized void requestSent(DatagramPacket pkt) {
if (_sessReqForReTX == null) {
// store pkt for retx
byte data[] = pkt.getData();
int off = pkt.getOffset();
int len = pkt.getLength();
_sessReqForReTX = new byte[len];
System.arraycopy(data, off, _sessReqForReTX, 0, len);
}
if (_rcvHeaderEncryptKey2 == null)
_rcvHeaderEncryptKey2 = SSU2Util.hkdf(_context, _handshakeState.getChainingKey(), "SessCreateHeader");
super.requestSent();
requestSent();
}
/**
* note that we just sent the SessionConfirmed packets
* and save them for retransmission
*/
public synchronized void confirmedPacketsSent(UDPPacket[] packets) {
if (_sessConfForReTX == null) {
// store pkt for retx
// only one supported right now
DatagramPacket pkt = packets[0].getPacket();
byte data[] = pkt.getData();
int off = pkt.getOffset();
int len = pkt.getLength();
_sessConfForReTX = new byte[len];
System.arraycopy(data, off, _sessConfForReTX, 0, len);
if (_rcvHeaderEncryptKey2 == null)
_rcvHeaderEncryptKey2 = SSU2Util.hkdf(_context, _handshakeState.getChainingKey(), "SessCreateHeader");
// TODO split(), create PeerState2
}
confirmedPacketsSent();
}
/**
* @return null if not sent or already got the session created
*/
public synchronized UDPPacket getRetransmitSessionRequestPacket() {
if (_sessReqForReTX == null)
return null;
UDPPacket packet = UDPPacket.acquire(_context, false);
DatagramPacket pkt = packet.getPacket();
byte data[] = pkt.getData();
int off = pkt.getOffset();
System.arraycopy(_sessReqForReTX, 0, data, off, _sessReqForReTX.length);
InetAddress to;
try {
to = InetAddress.getByAddress(_bobIP);
} catch (UnknownHostException uhe) {
if (_log.shouldLog(Log.ERROR))
_log.error("How did we think this was a valid IP? " + _remoteHostId);
packet.release();
return null;
}
pkt.setAddress(to);
pkt.setPort(_bobPort);
packet.setMessageType(PacketBuilder2.TYPE_SREQ);
packet.setPriority(PacketBuilder2.PRIORITY_HIGH);
requestSent();
return packet;
}
/**
* @return null we have not sent the session confirmed
*/
public synchronized PeerState2 getPeerState() {
// TODO
// set confirmed pkt data
return null;
}
@Override

View File

@@ -292,7 +292,7 @@ class PacketBuilder2 {
packet.release();
return null;
}
InetAddress to = null;
InetAddress to;
try {
to = InetAddress.getByAddress(toIP);
} catch (UnknownHostException uhe) {
@@ -309,6 +309,7 @@ class PacketBuilder2 {
setTo(packet, to, state.getSentPort());
packet.setMessageType(TYPE_SREQ);
packet.setPriority(PRIORITY_HIGH);
state.tokenRequestSent(pkt);
return packet;
}
@@ -328,7 +329,7 @@ class PacketBuilder2 {
packet.release();
return null;
}
InetAddress to = null;
InetAddress to;
try {
to = InetAddress.getByAddress(toIP);
} catch (UnknownHostException uhe) {
@@ -345,6 +346,7 @@ class PacketBuilder2 {
setTo(packet, to, state.getSentPort());
packet.setMessageType(TYPE_SREQ);
packet.setPriority(PRIORITY_HIGH);
state.requestSent(pkt);
return packet;
}
@@ -365,10 +367,10 @@ class PacketBuilder2 {
encryptSessionCreated(packet, state.getHandshakeState(), state.getSendHeaderEncryptKey1(),
state.getSendHeaderEncryptKey2(), state.getSentRelayTag(), state.getNextToken(),
sentIP, port);
state.createdPacketSent();
pkt.setSocketAddress(state.getSentAddress());
packet.setMessageType(TYPE_CREAT);
packet.setPriority(PRIORITY_HIGH);
state.createdPacketSent(pkt);
return packet;
}
@@ -390,10 +392,10 @@ class PacketBuilder2 {
encryptRetry(packet, state.getSendHeaderEncryptKey1(), n, state.getSendHeaderEncryptKey1(),
state.getSendHeaderEncryptKey2(),
sentIP, port);
state.retryPacketSent();
pkt.setSocketAddress(state.getSentAddress());
packet.setMessageType(TYPE_CREAT);
packet.setPriority(PRIORITY_HIGH);
state.retryPacketSent();
return packet;
}
@@ -460,7 +462,7 @@ class PacketBuilder2 {
// TODO numFragments > 1 requires shift to data phase
throw new IllegalArgumentException("TODO");
}
state.confirmedPacketsSent();
state.confirmedPacketsSent(packets);
return packets;
}
@@ -473,7 +475,7 @@ class PacketBuilder2 {
UDPPacket packet = buildShortPacketHeader(state.getSendConnID(), 1, SESSION_CONFIRMED_FLAG_BYTE);
DatagramPacket pkt = packet.getPacket();
InetAddress to = null;
InetAddress to;
try {
to = InetAddress.getByAddress(state.getSentIP());
} catch (UnknownHostException uhe) {

View File

@@ -139,11 +139,11 @@ public class PeerState {
/** what IP is the peer sending and receiving packets on? */
protected final byte[] _remoteIP;
/** cached IP address */
private volatile InetAddress _remoteIPAddress;
protected volatile InetAddress _remoteIPAddress;
/** what port is the peer sending and receiving packets on? */
private volatile int _remotePort;
protected volatile int _remotePort;
/** cached RemoteHostId, used to find the peerState by remote info */
private volatile RemoteHostId _remoteHostId;
protected volatile RemoteHostId _remoteHostId;
/** if we need to contact them, do we need to talk to an introducer? */
//private boolean _remoteRequiresIntroduction;

View File

@@ -1,7 +1,9 @@
package net.i2p.router.transport.udp;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -44,6 +46,7 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
private final byte[] _rcvHeaderEncryptKey2;
private final SSU2Bitfield _receivedMessages;
private final SSU2Bitfield _ackedMessages;
private byte[] _sessConfForReTX;
public static final int MIN_MTU = 1280;
@@ -333,4 +336,33 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
void fragmentsSent(long pktNum, List<PacketBuilder.Fragment> fragments) {
}
/**
* note that we just sent the SessionConfirmed packets
* and save them for retransmission
*/
public synchronized void confirmedPacketsSent(byte[] data) {
if (_sessConfForReTX == null)
_sessConfForReTX = data;
}
/**
* @return null if not sent or already got the ack
*/
public synchronized UDPPacket[] getRetransmitSessionConfirmedPackets() {
if (_sessConfForReTX == null)
return null;
UDPPacket packet = UDPPacket.acquire(_context, false);
UDPPacket[] rv = new UDPPacket[1];
rv[0] = packet;
DatagramPacket pkt = packet.getPacket();
byte data[] = pkt.getData();
int off = pkt.getOffset();
System.arraycopy(_sessConfForReTX, 0, data, off, _sessConfForReTX.length);
pkt.setAddress(_remoteIPAddress);
pkt.setPort(_remotePort);
packet.setMessageType(PacketBuilder2.TYPE_CONF);
packet.setPriority(PacketBuilder2.PRIORITY_HIGH);
return rv;
}
}

View File

@@ -906,7 +906,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* @return the valid version 1 or 2, or 0 if unusable
* @since 0.9.54
*/
private int getSSUVersion(RouterAddress addr) {
int getSSUVersion(RouterAddress addr) {
int rv;
String style = addr.getTransportStyle();
if (style.equals(STYLE)) {