Reach Server Select: fix C2S observability, sweep ansVerify, build ansSrvList - #11
Merged
Merged
Conversation
… a shell. Added additional features to the database server. Have some of the auth server built out. Still need to work on validating user credentials and handlers for the packets that follow.
Patch#1 (fd4b39e) had all the launcher/auth-server/database-server work but was never pushed/merged to GitHub - the PR that landed on main was cut before that commit existed. Resolves conflicts by keeping the two dependabot version bumps from main (log4j-api, spring-boot-starter-mail) and otherwise taking Patch#1's side (it's a strict superset - HEAD's versions of these files were pre-refactor and missing whole methods). Also fixes two SessionManager implementers (database-server, launcher) that were missing the clearSessions() override added to the shared commons.SessionManager abstract class, which is what actually broke the build after merging.
Fixes the packet length header, which every encoder byte-swapped into big-endian via Utility.flip. Our servers agreed with each other so this stayed hidden, but the real game client reads the header little-endian and timed out on every packet. Removed the swap at all 10 encoder/decoder sites and added PacketFraming (shared little-endian frame decoder), which also gives the pipelines proper frame reassembly - previously each decoder assumed one TCP read equalled one packet. With that fixed the real client (2.6.0.1) completes A2U_ansReady and replies U2A_askVerify. A2U_ansVerify is still a placeholder and is the next blocker; findings and remaining work are written up in CLIENT-PROTOCOL-NOTES.md. Also: - web-server: account registration over REST -> S2S -> CreateAccount - launcher: login against auth-server, signed-in username, START GAME launches the client (with UAC elevation fallback) - database-server: DB failures no longer escape the packet handler and tear down the shared S2S connection - auth-server: fix client packet category read from message[2] to message[0], which crashed on the first packet of every connection - fix ConcurrentModificationException from remove-during-iterate in auth-server/web-server ServerSessionManager - relax registration validation to match the Profile table's nullability Housekeeping: stop tracking out/artifacts jars and ignore Data/, archives, logs and build output. Data/ is ~7.9GB of client assets that should not be in git history, and *.7z archives of Config/ bundle the TLS private keys that the existing ignore rules were meant to keep out. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The game client now completes the A2U_ansReady / U2A_askVerify exchange
against auth-server. Two things were stopping it.
Environment: two outbound firewall rules blocking Sungame.exe/SUN.exe to
125.141.214.0/24 also stopped the client using the local login server.
Removing them restored connectivity with nothing else changed. Recorded in
the notes as resolved; those rules must not be re-added.
Code: VerifyUser.verify() was a port of the old LoginServer check, written
before this packet had ever been captured. It tested the payload for the
configured client protocol ("346") and client IP ("127001"), neither of
which appears in U2A_askVerify - it carries a hostname. So it always failed
and AnsVerify replied 0x01, a hard refusal. Rewritten against the layout
confirmed on a live client: a 3-byte 07 01 01 prefix then a 32-byte
null-padded host field ("connected1.sunc"). It validates the structure and
logs the host, but deliberately does not compare it to config - that value
lives in LOGIN.INI inside the client's System.wpk, which the server cannot
see, and gating a hard refusal on a check we cannot make is what caused
this.
Adds AnsVerifyProbe, off by default behind [PROBE] ANS_VERIFY_PROBE, which
serves a different ansVerify candidate per connection to hunt the still-
unknown response format, and records the close delay for each.
Docs correct two conclusions that turned out to be wrong:
- close-delay does not rank candidates. The same three bytes produced 3.8s,
14.8s and 5.2s across runs. Judge on whether an inbound byte arrives.
- SERVICE_LOGIN_TRY_COUNTS = 10 does not give ten attempts per launch. The
client makes exactly one askVerify then exits, so each candidate costs a
full launch.
CLAUDE.md was stale in four places, all verified against the code: the
spring-boot:run commands fail because that goal uses the module directory
while configs resolve from the repo root; C2S is live, not commented out;
the pipeline omitted PacketFraming; and the S2S handshake documented an
AES/RSA exchange that mutual TLS has replaced.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… protection' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Two conflicts, both places where this branch is strictly newer than main (main still carries the pre-session state via the earlier PR merge #6). ClientPacketHandler.java - three hunks, took this branch's side in each: - the U2A_askVerify dispatch. Main still calls `new AnsVerify().createPacket()`, which no longer compiles: AnsVerify is now static and takes the request payload so it can validate it. This branch's version also carries the AnsVerifyProbe branch. - channelInactive's PROBE result logging, absent on main. - the connection-reset log at DEBUG rather than WARN. Main's other changes to this file merged cleanly and are kept. CLIENT-PROTOCOL-NOTES.md - add/add. Main has the 174-line pre-session version; this branch's rewrite supersedes all eight of its sections, so took ours. Checked main's copy for anything unique first and restored the one detail the rewrite had dropped: the pristine LOGIN.INI MD5. Added a reference-hash table alongside it. Verified the merged tree compiles (commons, auth-server, web-server, launcher). database-server was not rebuilt - it is running and holds a lock on its jar - but nothing in this merge touches it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The A2U_ansVerify probe cursor now survives auth-server restarts, stored in Config/AuthServer/AnsVerifyProbe.state (gitignored). Windows locks a running jar so every rebuild forces a restart, and each replayed candidate costs a full ~45s client launch. A fresh cursor starts at candidate 4 - candidates 1-3 have been served to a live client and failed. CLIENT-PROTOCOL-NOTES records a session of reverse engineering the real client: - LOGIN_SERVER_TYPE = 1 selects a direct LOGIN_SERVER_IP/PORT path with no HEAD+index+TAIL composition, confirmed against a live client which now reports 127.0.0.1 in askVerify. This removes the hosts-file dependency entirely (notes 4, 5). Earlier attempts at this failed only because they set IP_HEAD, which TYPE=1 ignores. - Corrects notes 4: LOGIN.INI is XOR 0x69 inside System.wpk, not plaintext. wpktool de-obfuscates on extract, and its -m refuses unless the replacement's first 4 bytes match the existing entry. - Sungame.exe is MPRESS-packed; notes 9 records how to dump the unpacked image, the recovered VA map, and what full Ghidra analysis ruled out for the still-open ansVerify blocker. - Result-byte polarity is not the problem: 33 02 01 fails exactly like 33 02 00, so neither polarity is accepted. VerifyUser's javadoc no longer claims the askVerify host field always comes from IP_HEAD/IP_TAIL - both TYPE forms have now been observed on the wire. ANS_VERIFY_PROBE stays FALSE, its documented default. Flip it to TRUE to resume the sweep; the persisted cursor picks up at candidate 9. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Found incidentally while searching for the askVerify builder. It calls
CommandLineToArgvW, converts wide->multibyte, and tokenises on 0x7c
('|'), so it is the decoder for the handoff string in Protocol.java's
capture: 42126697|AioHaruka||<token>|2|1|1|2|2
That makes it the place to settle notes 7's open questions - how many
fields the client expects, what it does with each, and whether -User: or
-Username: is correct - by inspection rather than guesswork. Useless
until auth is reachable, so recorded rather than analysed now.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…sSrvList The game client now gets past login and selects a server. Full evidence and per-run logs in CLIENT-PROTOCOL-NOTES.md sections 10-18. Observability - NioServer honours [NETWORK] UNIQUE_IP_FILTER, which was read from the ini but never applied - the filter was added unconditionally. It allows one connection per IP, and the launcher deliberately holds a 127.0.0.1 connection open across the handoff, so it could take the game client's connection down. Set FALSE for local client testing. - ClientPacketHandler tags every line [conn-N] and reports per-connection duration and packet counts. Connects and zero-packet closes drop to DEBUG, silencing the launcher's five-second health check, which was previously indistinguishable from real client traffic. Protocol findings - ansVerify is not the gate it was assumed to be. All 32 opcodes 0x00-0x1F were swept against a live client and none is accepted as a verify reply. - A2U_ansAuthUser = 0x0E with flag 0x00 IS accepted, and takes the client straight to U2A_askSrvList without it ever authenticating. - Re-sending 0x00 (ansReady) makes the client re-verify on the same connection, so a probe rotation now runs inside one client launch rather than costing a launch per candidate. - The EP1 ClientPackets/ServerPackets documentation matches this client for askVerify, askSrvList, askSrvSelect, ansServerList and ansChannelList. Server list - AnsSrvList emits the documented layouts instead of zero-entry placeholders. The client accepted them: it displayed the list and replied with askSrvSelect. - GameServerRegistry keeps the ServerInfo row the S2S handshake already fetched. This is a stopgap on the wrong table - GameServerInfo and ChannelInfo are the player-facing ones, and both are empty. See section 18. - The auth gate on askSrvList/askSrvSelect is relaxed while the probe is on, because 0x0E makes the client skip askAuthUser entirely. VerifyUser now parses its two fields by offset and logs why it rejects anything. The previous hex-string handling broke on any version component of 0 or >= 10 and failed silently, which is indistinguishable from the client ignoring the reply. ANS_VERIFY_PROBE = TRUE is diagnostic state: the client needs it to get past verify today, but it deliberately sends replies known to be wrong. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| UnusedCode | 1 medium |
🟢 Metrics 9 complexity · 3 duplication
Metric Results Complexity 9 Duplication 3
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The game client now gets past login and reaches Server Select. Full evidence, per-run packet logs and a change record are in
CLIENT-PROTOCOL-NOTES.md§10–§18.Observability
NioServerhonours[NETWORK] UNIQUE_IP_FILTER, which was read from the ini but never applied — the filter was added unconditionally. It allows one connection per IP, and the launcher deliberately holds a127.0.0.1connection open across the handoff, so it could take the game client's connection down. SetFALSEfor local client testing.ClientPacketHandlertags every line[conn-N]and reports per-connection duration and packet counts. Connects and zero-packet closes drop to DEBUG, silencing the launcher's five-second health check — previously indistinguishable from real client traffic in the log.Protocol findings
ansVerifyis not the gate it was assumed to be. All 32 opcodes0x00–0x1Fwere swept against a live client; none is accepted as a verify reply.A2U_ansAuthUser = 0x0Ewith flag0x00is accepted, and takes the client straight toU2A_askSrvListwithout it ever authenticating.0x00(ansReady) makes the client re-verify on the same connection, so a probe rotation now runs inside one client launch instead of costing a launch per candidate.ClientPackets/ServerPacketsdocumentation matches this client foraskVerify,askSrvList,askSrvSelect,ansServerListandansChannelList.Server list
AnsSrvListemits the documented layouts instead of zero-entry placeholders. The client accepted them — it displayed the list and replied withaskSrvSelect.GameServerRegistrykeeps theServerInforow the S2S handshake already fetched. This is a stopgap on the wrong table:GameServerInfoandChannelInfoare the player-facing ones and both are empty. §18 is the handoff for that work.askSrvList/askSrvSelectis relaxed while the probe is on, because0x0Emakes the client skipaskAuthUserentirely.VerifyUsernow parses its two fields by offset and logs why it rejects anything. The previous hex-string handling broke on any version component of0or>= 10and failed silently, which is indistinguishable from the client ignoring the reply.Note before merging
ANS_VERIFY_PROBE = TRUEis committed as diagnostic state. The client needs it to get past verify today, but it deliberately sends replies known to be wrong. Worth flipping toFALSEifmainshould stay honest.This PR will show as conflicting — here is why, and the resolution
1b5dd94 Suncl ashime patch#2 (#8)was a squash merge (single parent), somainalready holds this branch's earlier content with no ancestry link back to it. Git therefore treats every later branch change as a conflict. Six files:CLIENT-PROTOCOL-NOTES.md,Config/AuthServer/AuthServer.ini,AnsVerify.java,AnsVerifyProbe.java,VerifyUser.java,ClientPacketHandler.java.maincontributes nothing unique to any of them. Each conflicted file onmainis byte-identical to a version already in this branch's history:mainis identical to branch commitCLIENT-PROTOCOL-NOTES.md1898ffcConfig/AuthServer/AuthServer.ini1898ffcAnsVerifyProbe.java1898ffcVerifyUser.java1898ffcAnsVerify.java42ae1c6ClientPacketHandler.java42ae1c6Both are ancestors of this branch's head, so taking the branch side for all six is lossless.
CLAUDE.mdand.github/workflows/maven.ymlaremain-only and auto-merge cleanly.🤖 Generated with Claude Code