Update hytale protocol to current latest - #5
Conversation
…ces deployed engine
…ocol from server jar
…y from JWT; gated byte logging
…l-safe language) and clientType decode
…ction, Collection players)
…token; defer player registration
…forward undecodable packets raw
xyzeva
left a comment
There was a problem hiding this comment.
thanks for trying to update hyproxy to latest, while i really appreciate this pr, its riddled with issues and excessive ai usage and unneeded changes
if you are willing to fix all of my review comments, i can take another look at it. but at its current state it is heavily blocked from being merged and i would say you shouldn't use this in production
if you don't want to fix the issues, i plan to update to latest sometime soon myself anyway.
| Packet packet = packetInfo.deserializeFunction().apply(payload); | ||
| out.add(packet); | ||
| } catch (Exception e) { | ||
| log.warn("failed to decode packet id {} - dropping packet", packetId, e); |
There was a problem hiding this comment.
this is a security issue, we want these to be captured by the netty pipeline which will properly close the connection after logging, or not log at all if the player hasn't authenticated yet to prevent console spam
| import java.util.Arrays; | ||
| import java.util.Map; | ||
|
|
||
| /** |
There was a problem hiding this comment.
we dont need this large javadoc for a completely internal class describing all of what it does, it simply isn't needed or wanted
| for (int i = 0; i < length; i++) { | ||
| int oldParamOffset = buf.readerIndex(); | ||
| String key = ProtocolUtil.readVarString(buf, 128); | ||
| String key = ProtocolUtil.readVarString(buf, 4096000); |
There was a problem hiding this comment.
do not use string sizes as large as this, even if the original server uses them. use a more appropriate amount for the situation
| for (int i = 0; i < length; i++) { | ||
| int oldParamOffset = buf.readerIndex(); | ||
| String key = ProtocolUtil.readVarString(buf, 128); | ||
| String key = ProtocolUtil.readVarString(buf, 4096000); |
There was a problem hiding this comment.
do not use string sizes as large as this, even if the original server uses them. use a more appropriate amount for the situation
| if ((nullBits & 0x2) != 0) { | ||
| int offset = varsOffset + motdOffset; | ||
| Pair<String, Integer> varString = ProtocolUtil.readVarString(buf, offset, 500); | ||
| Pair<String, Integer> varString = ProtocolUtil.readVarString(buf, offset, 4096000); |
There was a problem hiding this comment.
do not use string sizes as large as this, even if the original server uses them. use a more appropriate amount for the situation
|
|
||
| JWTVerifier.IdentityTokenClaims claims = connection.getProxy().getJwtVerifier().validateIdentityToken(identityToken); | ||
| if (claims == null) { | ||
| log.warn("rejecting Connect: identity token failed validation (validateIdentityToken returned null)"); |
| return; | ||
| } | ||
|
|
||
| connection.getProxy().registerPlayer(player); |
There was a problem hiding this comment.
why was this moved? very intentional decision to not do this
| return true; | ||
| } | ||
|
|
||
| player.setUsername(tokenUsername); |
There was a problem hiding this comment.
this is a huge security issue, why was this check removed?
| public void unregisterPlayer(HyProxyPlayer player) { | ||
| if (this.getPlayerByProfileId(player.getProfileId(), true) == null) { | ||
| throw new IllegalArgumentException("player profile id " + player.getProfileId() + " not registered"); | ||
| return; |
There was a problem hiding this comment.
this is an intentional decision to return an error here
|
|
||
| this.playersByProfileId.remove(player.getProfileId()); | ||
| this.playersByUsername.remove(player.getUsername().toLowerCase(Locale.ROOT)); | ||
| if (player.getUsername() != null) { |
There was a problem hiding this comment.
i dont get why this check is needed, but i guess it might be good to have it?
Players intermittently disconnect when the Hytale proxy runs behind Cloudflare Spectrum. Spectrum does not fragment UDP and drops any datagram too large to forward. The client-facing QUIC codec advertised the quiche default max_udp_payload_size (65527) and ran DPLPMTUD (discoverPmtu=true), so datagrams grew past the Spectrum-forwardable size and were blackholed, stalling sessions until the 60s idle timeout dropped the player. Cap the datagram size in both directions and disable PMTU probing, both driven by new config keys so the value can be tuned per deployment without a rebuild: - max-udp-payload-size (default 1200, QUIC's universal floor) sets both maxRecvUdpPayloadSize (advertised to the client, caps client->proxy, the direction Spectrum blackholes) and maxSendUdpPayloadSize (caps proxy->client) - discover-pmtu (default false) drives discoverPmtu(...) Behind Spectrum start at 1200 and raise toward 1350 once stability is confirmed. For direct (non-Spectrum) UDP exposure set discover-pmtu=true.
Addresses the review on xyzeva#5. Security: - PacketDecoder: stop swallowing deserialization failures. Let them propagate so the netty pipeline closes the connection instead of letting a client spam malformed packets while staying connected. Protocol string limits (were 4096000): - FormattedMessage: rawText 4096, messageId 256, param keys 256, color 256, link 4096 - ServerInfo: serverName 256, motd 4096 Applied to all 8 sites, not only the 4 flagged, to stay consistent. Noise removal: - Drop javadoc from internal classes (PlayerSkin, InsecurePlayerOptions, RequestInsecurePlayerOptions) - Drop `final` on locals; it is not used elsewhere in the codebase - Drop verbose explainer comments in Connect and InboundInitialPacketHandler - Drop log.warn on rejected pre-auth connections (console spam vector) - Lowercase internal disconnect messages, matching the existing "invalid referral data" convention Intentionally unchanged: the username now comes from the verified access token rather than a Connect field, because the 0.5.5 Connect packet no longer carries a username. registerPlayer moving after auth, the unregisterPlayer early return and the username null guard all follow from that. Restoring the old equality check would compare against null and reject every login; the UUID cross-check against the token is retained.
Based on internal pr: Minehut#1