remove username sniffer
This commit is contained in:
+2
-4
@@ -195,7 +195,7 @@ Type : u8
|
||||
| `0x01` | Register | C → S | `Pattern: String` |
|
||||
| `0x02` | Unregister | C → S | `Pattern: String` |
|
||||
| `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` |
|
||||
| `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
|
||||
cryptographically-random bytes generated by the hub, unique to that pending
|
||||
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
|
||||
Start packet — present when the client pipelined it with the Handshake (the
|
||||
usual case), otherwise an empty string. It is informational (logging) only.
|
||||
HAProxy v2).
|
||||
* **Ping/Pong**: optional keepalive so idle control sessions survive NAT
|
||||
timeouts. The client pings periodically; the hub echoes the nonce.
|
||||
|
||||
|
||||
+3
-4
@@ -221,8 +221,7 @@ func (c *Client) dispatchControl(payload []byte) {
|
||||
pattern, _ := r.String()
|
||||
ip, _ := r.String()
|
||||
port, _ := r.U16()
|
||||
username, _ := r.String()
|
||||
go c.handleControlRequest(cid, pattern, ip, int(port), username)
|
||||
go c.handleControlRequest(cid, pattern, ip, int(port))
|
||||
case CtlPong:
|
||||
// ignore
|
||||
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,
|
||||
// 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)]
|
||||
if !ok {
|
||||
log.Printf("control-request for unmapped pattern %q; ignoring", pattern)
|
||||
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()
|
||||
if err != nil {
|
||||
log.Printf("worker allocate failed: %v", err)
|
||||
|
||||
@@ -65,9 +65,9 @@ share a keystream beyond that first frame.
|
||||
```
|
||||
Player Hub Client Destination
|
||||
│─ Handshake(addr="mc.example.com", Intent=2)─▶ normalize + regex-match
|
||||
│ (+ maybe pipelined Login Start) │ pause player socket,
|
||||
│ │ pause player socket,
|
||||
│ │ buffer bytes, mint CID
|
||||
│ │─ ControlRequest(CID, pattern, ip:port, user) ─▶
|
||||
│ │─ ControlRequest(CID, pattern, ip:port) ─▶
|
||||
│ │ allocate worker+stream
|
||||
│ │◀──────── SYN(streamId, CID) ────────────│
|
||||
│ │ 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()
|
||||
.u8(Protocol.CTL_CONTROL_REQUEST)
|
||||
.bytes(cid)
|
||||
.string(pattern)
|
||||
.string(playerIp)
|
||||
.u16(playerPort)
|
||||
.string(username)
|
||||
.toBytes();
|
||||
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) {
|
||||
|
||||
@@ -106,7 +106,7 @@ public final class HubConnection {
|
||||
LOG.info("{} reserved intent 18; closing", id);
|
||||
socket.close();
|
||||
} else {
|
||||
handlePlayer(address, intent, afterHandshake);
|
||||
handlePlayer(address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ public final class HubConnection {
|
||||
|
||||
// ---- player connection ----
|
||||
|
||||
private void handlePlayer(String address, int intent, Buffer afterHandshake) {
|
||||
private void handlePlayer(String address) {
|
||||
String host = Hub.normalizeAddress(address);
|
||||
Hub.Match matched = hub.match(address);
|
||||
if (matched == null) {
|
||||
@@ -219,9 +219,6 @@ public final class HubConnection {
|
||||
String cidHex = Hex.encode(cid);
|
||||
String ip = socket.remoteAddress() != null ? socket.remoteAddress().host() : "0.0.0.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();
|
||||
Buffer buffered = hs.copy(); // handshake + any pipelined bytes, forwarded verbatim
|
||||
@@ -230,27 +227,8 @@ public final class HubConnection {
|
||||
hub.addPending(p);
|
||||
closeCleanup = () -> hub.removePending(cidHex);
|
||||
|
||||
session.sendControlRequest(cid, pattern, ip, port, username);
|
||||
LOG.info("{} player {}:{} host '{}' user '{}' matched pattern '{}' cid={}",
|
||||
id, ip, port, host, username, 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 "";
|
||||
}
|
||||
session.sendControlRequest(cid, pattern, ip, port);
|
||||
LOG.info("{} player {}:{} host '{}' matched pattern '{}' cid={}",
|
||||
id, ip, port, host, pattern, cidHex);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user