support regex and player name sniff
This commit is contained in:
+48
-10
@@ -72,7 +72,7 @@ The hub reads exactly one Handshake packet and dispatches on `Intent`:
|
||||
|---------------|---------|
|
||||
| `17` | redapricot session establishment (control session *or* worker conn). |
|
||||
| `18` | Reserved for redapricot management/status. Never matched against patterns. The reference hub replies with a status line and closes. |
|
||||
| anything else | **Player** connection. `ServerAddress` is matched (case-insensitively) against registered PATTERNs. |
|
||||
| anything else | **Player** connection. `ServerAddress` is matched against the registered **regex** PATTERNs (§5.1). |
|
||||
|
||||
For `Intent == 17` the hub additionally requires
|
||||
`ServerAddress == lowercase_hex(SHA3-224(PSK))` — a 56-character hex string.
|
||||
@@ -81,7 +81,8 @@ the connection.
|
||||
|
||||
For player connections the hub normalizes `ServerAddress` before matching:
|
||||
lower-cased, and any trailing `.` or Forge/FML `\0`-suffix (`host\0FML\0`)
|
||||
stripped to the bare hostname.
|
||||
stripped to the bare hostname. The resulting hostname is then tested against the
|
||||
registered regex patterns (§5.1).
|
||||
|
||||
## 3. Encryption
|
||||
|
||||
@@ -181,21 +182,50 @@ Type : u8
|
||||
| `0x00` | SessionReady | S → C | *(none)* — the confirmation frame from §4 |
|
||||
| `0x01` | Register | C → S | `Pattern: String` |
|
||||
| `0x02` | Unregister | C → S | `Pattern: String` |
|
||||
| `0x03` | RegisterAck | S → C | `Pattern: String`, `Status: u8` (0 = ok) |
|
||||
| `0x04` | ControlRequest | S → C | `CID: Bytes[16]`, `Pattern: String`, `PlayerIP: String`, `PlayerPort: U16` |
|
||||
| `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` |
|
||||
| `0x05` | Ping | C → S | `Nonce: I64` |
|
||||
| `0x06` | Pong | S → C | `Nonce: I64` |
|
||||
|
||||
* **Register / Unregister**: the client may (un)register a PATTERN at any time.
|
||||
Patterns are stored lower-cased. Re-registering an existing pattern reassigns
|
||||
it to the newest session (last writer wins).
|
||||
A PATTERN is a **regular expression** (§5.1) and is stored **verbatim** — the
|
||||
exact string is the registry key (never normalized, so regex metacharacters are
|
||||
preserved). Re-registering the identical pattern string reassigns it to the
|
||||
newest session (last writer wins); `Unregister` only removes it if the
|
||||
requesting session still owns it.
|
||||
* **RegisterAck**: acknowledges a `Register`. `Status` is `0` on success, or `1`
|
||||
if the pattern is not a valid regular expression (in which case nothing is
|
||||
registered). The `Pattern` echoes the string that was registered.
|
||||
* **ControlRequest**: emitted by the hub when a player Handshake matches a
|
||||
PATTERN this session registered. `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).
|
||||
PATTERN this session registered. `Pattern` is the **registered pattern string
|
||||
that matched** (echoed verbatim), *not* the player's hostname — so the client
|
||||
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.
|
||||
* **Ping/Pong**: optional keepalive so idle control sessions survive NAT
|
||||
timeouts. The client pings periodically; the hub echoes the nonce.
|
||||
|
||||
### 5.1 Pattern matching
|
||||
|
||||
A registered PATTERN is a **regular expression** (the reference hub uses
|
||||
`java.util.regex`). Matching is:
|
||||
|
||||
* **Case-insensitive** — patterns are compiled with a case-insensitive flag, and
|
||||
the player hostname is lower-cased during normalization (§2.1).
|
||||
* **Whole-string (anchored)** — the pattern must match the *entire* normalized
|
||||
hostname, as if wrapped in `^…$`. `mc\.example\.com` matches `mc.example.com`
|
||||
but not `mc.example.com.evil` or `sub.mc.example.com`.
|
||||
* **First match wins** — the hostname is tested against every registered pattern;
|
||||
the first that matches routes the player. If several patterns overlap, which
|
||||
one wins is unspecified.
|
||||
|
||||
Because the pattern is a regex, a literal dot must be escaped (`mc\.example\.com`);
|
||||
an unescaped `.` is the regex "any character" wildcard. A pattern that fails to
|
||||
compile is rejected at `Register` time with `RegisterAck` status `1`.
|
||||
|
||||
## 6. Error frame (any redapricot connection)
|
||||
|
||||
At any time either side may send, then close:
|
||||
@@ -310,11 +340,17 @@ big-endian.
|
||||
"maxConn": 4,
|
||||
"pingIntervalMs": 20000,
|
||||
"mappings": [
|
||||
{ "pattern": "mc.example.com", "destination": "127.0.0.1:25566", "proxyProtocol": true }
|
||||
{ "pattern": "mc\\.example\\.com", "destination": "127.0.0.1:25566", "proxyProtocol": true }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Each `pattern` is a regular expression (§5.1) matched against the whole
|
||||
normalized player hostname, case-insensitively. Escape literal dots (`mc\.example\.com`,
|
||||
which is `mc\\.example\\.com` in JSON); an unescaped `.` matches any character.
|
||||
Use ordinary regex to route wildcards, e.g. `.*\.example\.com` for every
|
||||
subdomain or `(alpha|beta)\.mc\.net` for a fixed set.
|
||||
|
||||
## 10. Constants summary
|
||||
|
||||
| Name | Value |
|
||||
@@ -327,6 +363,8 @@ big-endian.
|
||||
| key derivation | `SHA3-256(PK ‖ 0x01)` c→s, `SHA3-256(PK ‖ 0x02)` s→c |
|
||||
| rekey material | `Rand ‖ Timestamp(I64 BE)` |
|
||||
| Magic: control / worker | `0x01` / `0x02` |
|
||||
| RegisterAck status: ok / invalid pattern | `0x00` / `0x01` |
|
||||
| pattern matching | case-insensitive, whole-string regex; first match wins |
|
||||
| CID length | 16 bytes |
|
||||
| max frame payload | 1 MiB |
|
||||
| saturation threshold | active streams `> 8` |
|
||||
|
||||
Reference in New Issue
Block a user