gameserver: consolidate netcode R&D — remove diag spam, document DTLS-PSK wall
Build Game Server / build (push) Successful in 21s
Build Game Server / build (push) Successful in 21s
- Keep the R3.5.0 header-bit fix (bunch alignment breakthrough) - Remove per-packet [HS-RAW]/[HS-HDR]/[HS-BUNCH] diagnostics - Document the DTLS-PSK encryption wall + packed-binary finding in NETCODE-RND.md - UWorld NMT_Hello: honest comment on why the connection stops here Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -501,21 +501,6 @@ public abstract class UNetConnection : UPlayer
|
||||
var resetReaderMark = new FBitReaderMark(reader);
|
||||
var channelsToClose = new List<FChannelCloseInfo>();
|
||||
|
||||
// TEMP DIAG: dump the raw packet bit-stream (LSB-first per byte) so we can
|
||||
// hand-decode the header/bunch alignment. Non-destructive (position restored).
|
||||
{
|
||||
var peekMark = new FBitReaderMark(reader);
|
||||
var totalDiag = reader.GetBitsLeft();
|
||||
var takeDiag = Math.Min(totalDiag, 512);
|
||||
var sbDiag = new System.Text.StringBuilder(takeDiag);
|
||||
for (var i = 0; i < takeDiag; i++)
|
||||
{
|
||||
sbDiag.Append(reader.ReadBit() ? '1' : '0');
|
||||
}
|
||||
peekMark.Pop(reader);
|
||||
Logger.Information("[HS-RAW] totalBits={T} bits={B}", totalDiag, sbDiag.ToString());
|
||||
}
|
||||
|
||||
if (_bInternalAck)
|
||||
{
|
||||
++InPacketId;
|
||||
@@ -569,9 +554,6 @@ public abstract class UNetConnection : UPlayer
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Information("[HS-HDR] seq={Seq} ackedSeq={Acked} historyWords={HW} posAfterHeader={Pos} bitsLeft={Left}",
|
||||
header.Seq.Value, header.AckedSeq.Value, header.HistoryWordCount, reader.GetPosBits(), reader.GetBitsLeft());
|
||||
|
||||
var packetSequenceDelta = PacketNotify.GetSequenceDelta(header);
|
||||
|
||||
// The Cycle client doesn't derive its initial packet sequences from the handshake
|
||||
@@ -686,9 +668,6 @@ public abstract class UNetConnection : UPlayer
|
||||
bunch.bIsReplicationPaused = reader.ReadBit();
|
||||
bunch.bReliable = reader.ReadBit();
|
||||
|
||||
Logger.Information("[HS-BUNCH] startBit={Start} posBeforeChIndex={Pos} bitsLeft={Left} bControl={C} bOpen={O} bClose={Cl} bReliable={R} netVer={V}",
|
||||
incomingStartPos, reader.GetPosBits(), reader.GetBitsLeft(), bControl, bunch.bOpen, bunch.bClose, bunch.bReliable, (int)bunch.EngineNetVer());
|
||||
|
||||
if (bunch.EngineNetVer() < EEngineNetworkVersionHistory.HISTORY_MAX_ACTOR_CHANNELS_CUSTOMIZATION)
|
||||
{
|
||||
const int oldMaxActorChannels = 10240;
|
||||
|
||||
@@ -286,15 +286,28 @@ public abstract partial class UWorld : FNetworkNotify, IAsyncDisposable
|
||||
}
|
||||
else
|
||||
{
|
||||
// The Cycle client offers an EncryptionToken in NMT_Hello. A UE
|
||||
// connection only switches to AES once the server replies with
|
||||
// NMT_EncryptionAck — the token is an offer, not a requirement.
|
||||
// Implementing the full AES-GCM packet handler is a large effort,
|
||||
// so first proceed WITHOUT encryption: never send the ack and go
|
||||
// straight to the challenge. If the client keeps talking plaintext
|
||||
// we're done; if it insists on encryption it will drop here and we
|
||||
// know AES is mandatory.
|
||||
Logger.Warning("Client sent EncryptionToken ({Len} chars) — proceeding WITHOUT encryption (no EncryptionAck)", encryptionToken.Length);
|
||||
// R&D WALL — The Cycle mandates encryption on the game connection.
|
||||
// The client's NMT_Hello carries EncryptionToken = its PlayFab
|
||||
// user_id (e.g. "92EBCFE8C3EAF3AC"), and the client PacketHandler
|
||||
// stack is [DTLSHandlerComponent, StatelessConnectHandlerComponent]
|
||||
// (confirmed from the client's own logs). Encryption is therefore
|
||||
// DTLS in PSK mode (the shipping exe bundles ECDHE/DHE-PSK-AES256
|
||||
// cipher suites + a "DTLS.PreSharedKeys" cvar + DTLSPSK*Callback).
|
||||
//
|
||||
// Proceeding to the challenge WITHOUT sending NMT_EncryptionAck does
|
||||
// NOT work: the client requires encryption and closes the control
|
||||
// channel immediately after the plaintext challenge.
|
||||
//
|
||||
// To finish this we'd need (1) a DTLS-PSK server that matches UE's
|
||||
// DTLSHandlerComponent framing, and (2) the 32-byte PSK the client
|
||||
// derives per user_id. The derivation lives in the client's code,
|
||||
// which cannot be recovered statically: the exe is packed/encrypted
|
||||
// (.text entropy = 8.0), so Ghidra/radare2 see only ciphertext. The
|
||||
// only route left is a runtime memory dump of the decrypted image.
|
||||
//
|
||||
// For now: log and proceed to the challenge so the flow is visible
|
||||
// in logs; the client will close afterwards.
|
||||
Logger.Warning("Client requires DTLS-PSK encryption (EncryptionToken={Token}); server-side DTLS not implemented — connection will be dropped by client", encryptionToken);
|
||||
connection.SendChallengeControlMessage();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user