remove username sniffer
This commit is contained in:
+2
-4
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -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 "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user