remove username sniffer

This commit is contained in:
iceBear67
2026-07-15 16:05:34 +00:00
parent bbe4efbe16
commit a41cb7965e
5 changed files with 14 additions and 40 deletions
+2 -4
View File
@@ -195,7 +195,7 @@ Type : u8
| `0x01` | Register | C → S | `Pattern: String` | | `0x01` | Register | C → S | `Pattern: String` |
| `0x02` | Unregister | C → S | `Pattern: String` | | `0x02` | Unregister | C → S | `Pattern: String` |
| `0x03` | RegisterAck | S → C | `Pattern: String`, `Status: u8` (0 = ok, 1 = invalid pattern) | | `0x03` | RegisterAck | S → C | `Pattern: String`, `Status: u8` (0 = ok, 1 = invalid pattern) |
| `0x04` | ControlRequest | S → C | `CID: Bytes[16]`, `Pattern: String`, `PlayerIP: String`, `PlayerPort: U16`, `Username: String` | | `0x04` | ControlRequest | S → C | `CID: Bytes[16]`, `Pattern: String`, `PlayerIP: String`, `PlayerPort: U16` |
| `0x05` | Ping | C → S | `Nonce: I64` | | `0x05` | Ping | C → S | `Nonce: I64` |
| `0x06` | Pong | S → C | `Nonce: I64` | | `0x06` | Pong | S → C | `Nonce: I64` |
@@ -214,9 +214,7 @@ Type : u8
can look the pattern up in its own route table. `CID` is 16 can look the pattern up in its own route table. `CID` is 16
cryptographically-random bytes generated by the hub, unique to that pending cryptographically-random bytes generated by the hub, unique to that pending
player. `PlayerIP`/`PlayerPort` are the player's source address (used for player. `PlayerIP`/`PlayerPort` are the player's source address (used for
HAProxy v2). `Username` is the player's name, read best-effort from the Login HAProxy v2).
Start packet — present when the client pipelined it with the Handshake (the
usual case), otherwise an empty string. It is informational (logging) only.
* **Ping/Pong**: optional keepalive so idle control sessions survive NAT * **Ping/Pong**: optional keepalive so idle control sessions survive NAT
timeouts. The client pings periodically; the hub echoes the nonce. timeouts. The client pings periodically; the hub echoes the nonce.
+3 -4
View File
@@ -221,8 +221,7 @@ func (c *Client) dispatchControl(payload []byte) {
pattern, _ := r.String() pattern, _ := r.String()
ip, _ := r.String() ip, _ := r.String()
port, _ := r.U16() port, _ := r.U16()
username, _ := r.String() go c.handleControlRequest(cid, pattern, ip, int(port))
go c.handleControlRequest(cid, pattern, ip, int(port), username)
case CtlPong: case CtlPong:
// ignore // ignore
default: default:
@@ -248,13 +247,13 @@ func (c *Client) pingLoop(ctx context.Context, fc *wire.FramedConn) {
// handleControlRequest reacts to a matched player: allocate a worker stream, // handleControlRequest reacts to a matched player: allocate a worker stream,
// SYN it, and bridge it to the mapped destination. // SYN it, and bridge it to the mapped destination.
func (c *Client) handleControlRequest(cid []byte, pattern, ip string, port int, username string) { func (c *Client) handleControlRequest(cid []byte, pattern, ip string, port int) {
mapping, ok := c.mappings[NormalizeAddress(pattern)] mapping, ok := c.mappings[NormalizeAddress(pattern)]
if !ok { if !ok {
log.Printf("control-request for unmapped pattern %q; ignoring", pattern) log.Printf("control-request for unmapped pattern %q; ignoring", pattern)
return return
} }
log.Printf("player %s:%d joined as %q via pattern %q -> %s", ip, port, username, pattern, mapping.Destination) log.Printf("player %s:%d joined via pattern %q -> %s", ip, port, pattern, mapping.Destination)
wc, sid, err := c.pool.Allocate() wc, sid, err := c.pool.Allocate()
if err != nil { if err != nil {
log.Printf("worker allocate failed: %v", err) log.Printf("worker allocate failed: %v", err)
+2 -2
View File
@@ -65,9 +65,9 @@ share a keystream beyond that first frame.
``` ```
Player Hub Client Destination Player Hub Client Destination
│─ Handshake(addr="mc.example.com", Intent=2)─▶ normalize + regex-match │─ Handshake(addr="mc.example.com", Intent=2)─▶ normalize + regex-match
(+ maybe pipelined Login Start) │ pause player socket, │ pause player socket,
│ │ buffer bytes, mint CID │ │ buffer bytes, mint CID
│ │─ ControlRequest(CID, pattern, ip:port, user) ─▶ │ │─ ControlRequest(CID, pattern, ip:port) ─▶
│ │ allocate worker+stream │ │ allocate worker+stream
│ │◀──────── SYN(streamId, CID) ────────────│ │ │◀──────── SYN(streamId, CID) ────────────│
│ │ takePending(CID) → bind dial destination, │ │ takePending(CID) → bind dial destination,
@@ -43,17 +43,16 @@ public final class ControlSession {
} }
} }
public void sendControlRequest(byte[] cid, String pattern, String playerIp, int playerPort, String username) { public void sendControlRequest(byte[] cid, String pattern, String playerIp, int playerPort) {
byte[] msg = new ProtoWriter() byte[] msg = new ProtoWriter()
.u8(Protocol.CTL_CONTROL_REQUEST) .u8(Protocol.CTL_CONTROL_REQUEST)
.bytes(cid) .bytes(cid)
.string(pattern) .string(pattern)
.string(playerIp) .string(playerIp)
.u16(playerPort) .u16(playerPort)
.string(username)
.toBytes(); .toBytes();
frames.send(msg); frames.send(msg);
LOG.info("control-request pattern={} player={}:{} user={}", pattern, playerIp, playerPort, username); LOG.info("control-request pattern={} player={}:{}", pattern, playerIp, playerPort);
} }
private void sendRegisterAck(String pattern, int status) { private void sendRegisterAck(String pattern, int status) {
@@ -106,7 +106,7 @@ public final class HubConnection {
LOG.info("{} reserved intent 18; closing", id); LOG.info("{} reserved intent 18; closing", id);
socket.close(); socket.close();
} else { } else {
handlePlayer(address, intent, afterHandshake); handlePlayer(address);
} }
} }
@@ -203,7 +203,7 @@ public final class HubConnection {
// ---- player connection ---- // ---- player connection ----
private void handlePlayer(String address, int intent, Buffer afterHandshake) { private void handlePlayer(String address) {
String host = Hub.normalizeAddress(address); String host = Hub.normalizeAddress(address);
Hub.Match matched = hub.match(address); Hub.Match matched = hub.match(address);
if (matched == null) { if (matched == null) {
@@ -219,9 +219,6 @@ public final class HubConnection {
String cidHex = Hex.encode(cid); String cidHex = Hex.encode(cid);
String ip = socket.remoteAddress() != null ? socket.remoteAddress().host() : "0.0.0.0"; String ip = socket.remoteAddress() != null ? socket.remoteAddress().host() : "0.0.0.0";
int port = socket.remoteAddress() != null ? socket.remoteAddress().port() : 0; int port = socket.remoteAddress() != null ? socket.remoteAddress().port() : 0;
// Best-effort username: only login/transfer intents carry a Login Start, and only
// if the client pipelined it into this same buffer (the usual case).
String username = (intent == 2 || intent == 3) ? parseLoginName(afterHandshake) : "";
socket.pause(); socket.pause();
Buffer buffered = hs.copy(); // handshake + any pipelined bytes, forwarded verbatim Buffer buffered = hs.copy(); // handshake + any pipelined bytes, forwarded verbatim
@@ -230,27 +227,8 @@ public final class HubConnection {
hub.addPending(p); hub.addPending(p);
closeCleanup = () -> hub.removePending(cidHex); closeCleanup = () -> hub.removePending(cidHex);
session.sendControlRequest(cid, pattern, ip, port, username); session.sendControlRequest(cid, pattern, ip, port);
LOG.info("{} player {}:{} host '{}' user '{}' matched pattern '{}' cid={}", LOG.info("{} player {}:{} host '{}' matched pattern '{}' cid={}",
id, ip, port, host, username, pattern, cidHex); id, ip, port, host, pattern, cidHex);
}
/**
* Best-effort read of the player's username from a pipelined Login Start packet
* (Login state, packet id 0x00, first field {@code Name: String}). Returns "" if
* the packet is not (yet) fully present or does not parse as a Login Start; the hub
* never blocks waiting for it.
*/
private static String parseLoginName(Buffer afterHandshake) {
if (afterHandshake == null || afterHandshake.length() == 0) return "";
try {
ProtoReader r = new ProtoReader(afterHandshake);
int pktLen = r.readVarInt();
if (pktLen <= 0 || pktLen > r.remaining()) return ""; // not fully buffered
if (r.readVarInt() != 0x00) return ""; // not a Login Start
return r.readString();
} catch (RuntimeException e) {
return "";
}
} }
} }