Skip to content

Commit fdf890c

Browse files
committed
docs: add a few standards
1 parent d949fdb commit fdf890c

10 files changed

Lines changed: 2150 additions & 175 deletions

File tree

Cargo.lock

Lines changed: 166 additions & 175 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

specs/anytls.md

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
RFC: The Anytls Protocol Version 2
2+
Category: Informational
3+
Date: April 2026
4+
5+
# The Anytls Protocol Version 2
6+
7+
## Abstract
8+
9+
This document describes the Anytls protocol, a secure, multiplexed proxy protocol that operates over Transport Layer Security (TLS). The protocol is designed to provide robust obfuscation against traffic analysis through dynamic padding schemes, multiplex multiple logical streams over a single session, and protect against active probing by falling back to standard protocols.
10+
11+
## 1. Introduction
12+
13+
Anytls is a proxy protocol that establishes a secure session over a standard TLS connection. It consists of an initial authentication phase followed by a session layer that multiplexes multiple logical streams. To counter advanced Deep Packet Inspection (DPI) heuristics, Anytls utilizes a dynamic `paddingScheme` to alter traffic fingerprints actively.
14+
15+
## 2. Conventions and Terminology
16+
17+
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
18+
19+
- **Session**: A single established TLS connection running the Anytls session layer.
20+
- **Stream**: A multiplexed logical channel within a Session used to proxy a specific connection.
21+
- **Client**: The software initiating the Anytls connection.
22+
- **Server**: The Anytls endpoint receiving the connection and performing the proxying.
23+
24+
## 3. Protocol Architecture
25+
26+
The overall protocol stack is layered as follows:
27+
28+
```text
29+
+-------------------------------------------------+
30+
| User TCP/UDP Proxy |
31+
+-------------------------------------------------+
32+
| Anytls Stream Layer |
33+
+-------------------------------------------------+
34+
| Anytls Session Layer |
35+
+-------------------------------------------------+
36+
| Transport Layer Security (TLS) |
37+
+-------------------------------------------------+
38+
| Transmission Control Protocol (TCP) |
39+
+-------------------------------------------------+
40+
```
41+
42+
## 4. Authentication Phase
43+
44+
Immediately after the TLS handshake completes, the Client MUST send an authentication request.
45+
46+
### 4.1. Client Authentication Request
47+
48+
The authentication payload is structured as follows:
49+
50+
```text
51+
0 1 2 3
52+
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
53+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
54+
| |
55+
+ +
56+
| |
57+
+ +
58+
| |
59+
+ +
60+
| |
61+
+ +
62+
| |
63+
+ +
64+
| |
65+
+ +
66+
| |
67+
+ +
68+
| sha256(password) (32 Bytes) |
69+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
70+
| padding0 length | padding0 (Variable) ... |
71+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72+
```
73+
74+
- **sha256(password)**: 32 bytes. The SHA-256 hash of the pre-shared protocol password.
75+
- **padding0 length**: 2 bytes (Big-Endian uint16). The length of the subsequent padding.
76+
- **padding0**: Variable length. Random padding data.
77+
78+
*Note: The overhead for the authentication portion is 34 bytes (excluding the variable padding).*
79+
80+
### 4.2. Server Authentication Response
81+
82+
The Server MUST read the first packet and verify the authentication request (including fully reading `padding0`).
83+
- If authentication is successful, the Server enters the Session loop.
84+
- If authentication fails, the Server MUST immediately close the connection OR fallback to a standard HTTP/L7 service to defend against active probing.
85+
86+
## 5. Session Layer
87+
88+
Once authenticated, both endpoints enter a session event loop.
89+
90+
### 5.1. Frame Format
91+
92+
The Session layer communicates using the following frame format:
93+
94+
```text
95+
0 1 2 3
96+
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
97+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
98+
| command | streamId |
99+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
100+
| | data length | |
101+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
102+
| data (Variable) |
103+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
104+
```
105+
106+
- **command**: 1 byte (uint8). Identifies the frame type.
107+
- **streamId**: 4 bytes (Big-Endian uint32). Identifies the logical stream.
108+
- **data length**: 2 bytes (Big-Endian uint16). The length of the payload.
109+
- **data**: Variable length payload.
110+
111+
### 5.2. Commands
112+
113+
The following commands are defined. Unless explicitly specified below, commands MUST NOT carry a data payload.
114+
115+
| Command Name | Value | Introduced | Direction | Description |
116+
|---|---|---|---|---|
117+
| `cmdWaste` | 0 | v1 | Both | Padding. Data MUST be read and silently discarded. |
118+
| `cmdSYN` | 1 | v1 | Client -> Server | Open a new Stream. |
119+
| `cmdPSH` | 2 | v1 | Both | Push data to a Stream. |
120+
| `cmdFIN` | 3 | v1 | Both | Close a Stream (EOF). |
121+
| `cmdSettings` | 4 | v1 | Client -> Server | Client Settings negotiation. |
122+
| `cmdAlert` | 5 | v1 | Server -> Client | Fatal error alert from Server. |
123+
| `cmdUpdatePaddingScheme` | 6 | v1 | Server -> Client | Dynamic padding scheme update. |
124+
| `cmdSYNACK` | 7 | v2 | Server -> Client | Stream open acknowledgment. |
125+
| `cmdHeartRequest` | 8 | v2 | Both | Keep-alive request. |
126+
| `cmdHeartResponse` | 9 | v2 | Both | Keep-alive response. |
127+
| `cmdServerSettings` | 10 | v2 | Server -> Client | Server Settings negotiation. |
128+
129+
#### 5.2.1. cmdSettings (4)
130+
The Client MUST send a `cmdSettings` frame immediately upon opening a new Session. If a Server receives a `cmdSYN` before `cmdSettings`, it MUST reject the Session.
131+
132+
The payload is a newline (`\n`) separated list of key-value pairs separated by `=`. Encoded in UTF-8.
133+
Example:
134+
```text
135+
v=2
136+
client=anytls/0.0.1
137+
padding-md5=(lowercase hex encoded md5 of current paddingScheme)
138+
```
139+
140+
#### 5.2.2. cmdServerSettings (10)
141+
If the Client reports version `v >= 2`, the Server MUST reply with `cmdServerSettings` immediately after receiving `cmdSettings`.
142+
Example:
143+
```text
144+
v=2
145+
```
146+
147+
#### 5.2.3. cmdAlert (5)
148+
The payload contains a warning text string sent by the Server. The Client MUST read and log this message, after which both endpoints MUST close the Session. A Server MAY send this to reject outdated or non-compliant Clients.
149+
150+
#### 5.2.4. cmdUpdatePaddingScheme (6)
151+
If the Server detects that the Client's `padding-md5` (from `cmdSettings`) differs from the Server's current scheme, the Server MUST send this command. The payload format is described in Section 6.
152+
153+
#### 5.2.5. cmdHeartRequest (8) and cmdHeartResponse (9)
154+
When an endpoint receives a `cmdHeartRequest`, it MUST respond with a `cmdHeartResponse`. These are used to detect and recover from stuck tunnels.
155+
156+
### 5.3. Stream Lifecycle Commands
157+
158+
#### 5.3.1. cmdSYN (1)
159+
Notifies the Server to open a new Stream. The Client MUST generate a monotonically increasing `streamId` within the Session.
160+
161+
#### 5.3.2. cmdSYNACK (7)
162+
For Client version `v >= 2`, the Server SHOULD send a `cmdSYNACK` with the corresponding `streamId` after the outbound proxy TCP handshake completes.
163+
- A payload-less `cmdSYNACK` indicates a successful proxy stream connection.
164+
- If data is present, it represents an error message. The Client MUST close the corresponding Stream upon receiving an error payload.
165+
166+
#### 5.3.3. cmdPSH (2)
167+
The data payload carries the actual proxied traffic for the Stream.
168+
169+
#### 5.3.4. cmdFIN (3)
170+
Notifies the peer to close the specified Stream.
171+
- If the Session is healthy, the receiving endpoint closes the local Stream but DOES NOT need to reply with its own `cmdFIN`.
172+
- If the Session is already closing, `cmdFIN` does not need to be sent.
173+
174+
## 6. Dynamic Padding Scheme
175+
176+
The Padding Scheme dictates how packets are fragmented and padded to obfuscate traffic patterns.
177+
178+
### 6.1. Padding Scheme Format
179+
180+
The scheme is sent as a newline-separated string. Example:
181+
```text
182+
stop=8
183+
0=30-30
184+
1=100-400
185+
2=400-500,c,500-1000,c,500-1000,c,500-1000,c,500-1000
186+
3=9-9,500-1000
187+
```
188+
189+
### 6.2. Scheme Directives
190+
191+
- `stop`: E.g., `stop=8` dictates that only the first 8 packets (indexes 0 to 7) are subjected to the padding scheme.
192+
- `0=X-Y`: Rules for the 0th packet (`padding0` during Authentication). This cannot be fragmented. The Client sends padding between X and Y bytes.
193+
- `1` and above: Rules for Session phase packets.
194+
195+
**Packet Counting:**
196+
The packet index corresponds to the number of times `Write()` is called on the underlying TLS connection.
197+
- **Packet 1** typically includes: `cmdSettings` + first Stream's `cmdSYN` + `cmdPSH` (containing proxy target address).
198+
- **Packet 2** typically contains the first chunk of user proxy data (e.g., TLS ClientHello of the proxied connection).
199+
200+
**Fragmentation and Padding Logic:**
201+
If a packet is ruled by `A-B,C-D`:
202+
1. The user data is fragmented. The first chunk's size is randomly chosen between A and B. The second between C and D, etc. (Size refers to TLS PlainText length, excluding TLS encryption overhead).
203+
2. If user data remains after all specified fragments, the remainder is sent natively.
204+
3. If user data is exhausted before all fragments are fulfilled, the endpoint MUST send `cmdWaste` filled with padding (preferably 0s) to fulfill the size requirement.
205+
4. **Check Symbol (`c`)**: If a `,c,` separator is present and the user data is exhausted, the implementation MUST return from the Write operation immediately and MUST NOT send subsequent padding packets defined after the `c`.
206+
207+
### 6.3. Scheme Lifecycle
208+
- A Client MUST store the `paddingScheme` specific to the Server it connects to.
209+
- A Client MUST use the default `paddingScheme` on its first connection.
210+
- Upon receiving a `cmdUpdatePaddingScheme`, the Client MUST use the newly provided scheme for all subsequent Sessions created with that Server. This ensures that any discovered fingerprints only affect a minimal subset of initial connections before the scheme is dynamically rolled.
211+
212+
## 7. Connection Multiplexing
213+
214+
Clients MUST implement session multiplexing using a connection pool. The architecture is:
215+
`TCP Proxy -> Stream -> Session -> TLS -> TCP`
216+
217+
### 7.1. Multiplexing Strategy
218+
- Before creating a new Session, the Client MUST check the pool for "idle" Sessions.
219+
- If available, the Client MUST pick the Session with the highest sequence number (`Seq`) to open the new Stream.
220+
- If no idle Sessions exist, a new Session is created. The `Seq` MUST monotonically increase within the Client instance.
221+
- When a Stream closes normally (and the Session has no errors), the Session is returned to the "idle session pool" and its idle start time is updated to `now`.
222+
- The Client SHOULD periodically (e.g., every 30s) reap Sessions that have been idle for a designated duration (e.g., 60s).
223+
- Servers MAY also periodically reap Sessions that lack uplink/downlink activity for extended periods.
224+
225+
## 8. Proxy Protocol Interaction
226+
227+
### 8.1. TCP Proxy
228+
After opening a Stream (`cmdSYN`), the Client MUST send the target destination address in [RFC 1928 (SOCKS5) Address](https://tools.ietf.org/html/rfc1928#section-5) format inside a `cmdPSH`. Following this, bidirectional proxying commences.
229+
230+
### 8.2. UDP Proxy
231+
UDP proxying relies on the `sing-box udp-over-tcp v2` protocol. The Client acts as if it is making a TCP proxy request to the special domain `sp.v2.udp-over-tcp.arpa`.
232+
233+
## 9. Protocol Parameters
234+
235+
The Anytls protocol assumes TLS configuration is handled externally. Specific Anytls parameters include:
236+
237+
### 9.1. Client Parameters
238+
- `password` (String, REQUIRED): Protocol authentication password.
239+
- `idleSessionCheckInterval` (Duration, OPTIONAL): Interval for checking the idle pool.
240+
- `idleSessionTimeout` (Duration, OPTIONAL): Max duration a Session can remain idle before closure.
241+
- `minIdleSession` (Integer, OPTIONAL): The number of fresh idle sessions to maintain as warm reserves.
242+
243+
### 9.2. Server Parameters
244+
- `paddingScheme` (String, OPTIONAL): The master padding scheme to enforce on Clients.
245+
246+
## 10. Version History
247+
248+
- **v1**: Initial implementation.
249+
- **v2 (v0.0.8 - April 2025)**: Added `cmdSYNACK` to report outbound connection state and handle stuck tunnels. Added `cmdHeartRequest`/`cmdHeartResponse` keep-alives. Added `cmdServerSettings` for negotiation.
250+
- **v2 (v0.0.10 - September 2025)**: Clarified `cmdFIN` semantics regarding Session and Stream closure.

0 commit comments

Comments
 (0)