forked from I2P_Developers/i2p.i2p
SSU2: Fixes part 11
Save data messages received before or immediately after session confirmed by queueing them for processing after the PeerState2 is created. The fragments for the first I2NP message from Alice to Bob are frequently lost, this will hopefully fix it. Not fully tested, needs wider network testing.
This commit is contained in:
@@ -7,6 +7,7 @@ import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.southernstorm.noise.protocol.ChaChaPolyCipherState;
|
||||
@@ -56,6 +57,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
|
||||
private long _skew;
|
||||
private int _mtu;
|
||||
private PeerState2 _pstate;
|
||||
private List<UDPPacket> _queuedDataPackets;
|
||||
|
||||
// testing
|
||||
private static final boolean ENFORCE_TOKEN = true;
|
||||
@@ -434,7 +436,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
|
||||
public byte[] getSendHeaderEncryptKey1() { return _sendHeaderEncryptKey1; }
|
||||
public byte[] getRcvHeaderEncryptKey1() { return _rcvHeaderEncryptKey1; }
|
||||
public byte[] getSendHeaderEncryptKey2() { return _sendHeaderEncryptKey2; }
|
||||
public byte[] getRcvHeaderEncryptKey2() { return _rcvHeaderEncryptKey2; }
|
||||
public synchronized byte[] getRcvHeaderEncryptKey2() { return _rcvHeaderEncryptKey2; }
|
||||
public InetSocketAddress getSentAddress() { return _aliceSocketAddress; }
|
||||
|
||||
@Override
|
||||
@@ -661,9 +663,51 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
|
||||
* @return null if we have not received the session confirmed
|
||||
*/
|
||||
public synchronized PeerState2 getPeerState() {
|
||||
_currentState = InboundState.IB_STATE_COMPLETE;
|
||||
if (_pstate != null) {
|
||||
_currentState = InboundState.IB_STATE_COMPLETE;
|
||||
if (_queuedDataPackets != null) {
|
||||
for (UDPPacket packet : _queuedDataPackets) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Passing possible data " + packet + " to PeerState2: " + this);
|
||||
_pstate.receivePacket(packet);
|
||||
packet.release();
|
||||
}
|
||||
_queuedDataPackets.clear();
|
||||
}
|
||||
}
|
||||
return _pstate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param packet with header still encrypted
|
||||
*/
|
||||
public synchronized void queuePossibleDataPacket(UDPPacket packet) {
|
||||
if (_pstate == null) {
|
||||
// case 1, race or out-of-order, queue until we have the peerstate
|
||||
if (_queuedDataPackets == null) {
|
||||
_queuedDataPackets = new ArrayList<UDPPacket>(4);
|
||||
} else if (_queuedDataPackets.size() >= 10) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Not queueing possible data " + packet + ", too many queued on " + this);
|
||||
return;
|
||||
}
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Queueing possible data " + packet + " on " + this);
|
||||
// have to copy it because PacketHandler will release
|
||||
DatagramPacket pkt = packet.getPacket();
|
||||
UDPPacket packet2 = UDPPacket.acquire(_context, true);
|
||||
DatagramPacket pkt2 = packet2.getPacket();
|
||||
System.arraycopy(pkt.getData(), pkt.getOffset(), pkt2.getData(), pkt2.getOffset(), pkt.getLength());
|
||||
pkt2.setLength(pkt.getLength());
|
||||
pkt2.setSocketAddress(pkt.getSocketAddress());
|
||||
_queuedDataPackets.add(packet2);
|
||||
} else {
|
||||
// case 2, race, decrypt header and pass over
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Passing possible data " + packet + " to PeerState2: " + this);
|
||||
_pstate.receivePacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
@@ -820,8 +820,10 @@ class PacketHandler {
|
||||
if (header == null ||
|
||||
header.getVersion() != 2 ||
|
||||
header.getNetID() != _networkID) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Does not decrypt as Session Request, Token Request, or Peer Test: " + header);
|
||||
// typical case, SSU 1 that didn't validate, will be logged at WARN level above
|
||||
// in group 4 receive packet
|
||||
//if (_log.shouldDebug())
|
||||
// _log.debug("Does not decrypt as Session Request, Token Request, or Peer Test: " + header);
|
||||
return false;
|
||||
}
|
||||
type = header.getType();
|
||||
@@ -851,27 +853,43 @@ class PacketHandler {
|
||||
// tell establisher?
|
||||
return false;
|
||||
}
|
||||
if (header.getDestConnID() != state.getRcvConnID()) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Bad Dest Conn id " + header);
|
||||
return false;
|
||||
}
|
||||
type = SSU2Util.SESSION_REQUEST_FLAG_BYTE;
|
||||
} else {
|
||||
// Session Confirmed or retransmitted Session Request or Token Request
|
||||
header = SSU2Header.trialDecryptShortHeader(packet, k1, k2);
|
||||
if (header == null ||
|
||||
if (header == null) {
|
||||
// too short
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Failed decrypt Session Confirmed");
|
||||
return false;
|
||||
}
|
||||
if (header.getDestConnID() != state.getRcvConnID()) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Bad Dest Conn id " + header);
|
||||
return false;
|
||||
}
|
||||
if (header.getPacketNumber() != 0 ||
|
||||
header.getType() != SSU2Util.SESSION_CONFIRMED_FLAG_BYTE) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Failed decrypt Session Confirmed: " + header);
|
||||
_log.warn("Queue possible data packet on: " + state);
|
||||
// TODO either attempt to decrypt as a retransmitted
|
||||
// Session Request or Token Request,
|
||||
// or just tell establisher so it can retransmit Session Created or Retry
|
||||
// Could also be Data messages after (possibly lost or out-of-order) Session Confirmed
|
||||
return false;
|
||||
|
||||
// Possible ordering issues and races:
|
||||
// Case 1: Data packets before (possibly lost or out-of-order) Session Confirmed
|
||||
// Case 2: Data packets after Session Confirmed but it wasn't processed yet
|
||||
// Queue the packet with the state for processing
|
||||
state.queuePossibleDataPacket(packet);
|
||||
return true;
|
||||
}
|
||||
type = SSU2Util.SESSION_CONFIRMED_FLAG_BYTE;
|
||||
}
|
||||
if (header.getDestConnID() != state.getRcvConnID()) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Bad Dest Conn id " + header);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// all good
|
||||
|
||||
Reference in New Issue
Block a user