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