Compare commits

...

44 Commits

Author SHA1 Message Date
yuhan6665
43779f379f Experiment: seed ignore buffer and send 1st fake packet instantly 2025-09-07 09:48:21 -04:00
yuhan6665
082fecf334 Add quick logic for scheduler to send fake packet when no buffer is pending 2025-09-07 09:37:44 -04:00
yuhan6665
7299cfc56f Add VLESS seed tests 2025-09-07 09:36:46 -04:00
yuhan6665
0e206b99bd Add proxy Scheduler 2025-09-07 09:33:58 -04:00
yuhan6665
45f677a538 Fill vless response addon 2025-09-07 09:31:14 -04:00
yuhan6665
b6afe68d84 Add seed padding logic
- Seed is decoupled with XTLS Vision, which means Seed can turn on without flow
- XTLS Vision now use Seed config to configure its padding only mode
2025-09-07 09:28:24 -04:00
yuhan6665
51234fbe53 Collect stats and possible padding (previously Vision Reader Writer) to all traffic 2025-09-07 09:12:48 -04:00
yuhan6665
cba71f8cdc Add more traffic info for future use 2025-09-05 21:49:55 -04:00
yuhan6665
279abd4fc8 Populate Seed (more TBD) and checks 2025-09-05 21:33:31 -04:00
yuhan6665
b1f4d32ef0 Add VLESS seed configurations 2025-09-05 21:29:11 -04:00
RPRX
6ec0291d4e app/reverse/bridge.go: Fix DispatchLink() returns immediately
Some checks failed
Build and Release / build (amd64, android, android-amd64) (push) Has been cancelled
Build and Release / build (amd64, darwin, ) (push) Has been cancelled
Build and Release / build (amd64, freebsd, ) (push) Has been cancelled
Build and Release / build (amd64, linux, ) (push) Has been cancelled
Build and Release / build (amd64, openbsd, ) (push) Has been cancelled
Build and Release / build (amd64, windows, ) (push) Has been cancelled
Build and Release / build (arm, 5, linux) (push) Has been cancelled
Build and Release / build (arm, 6, linux) (push) Has been cancelled
Build and Release / build (arm, 7, freebsd) (push) Has been cancelled
Build and Release / build (arm, 7, linux) (push) Has been cancelled
Build and Release / build (arm, 7, openbsd) (push) Has been cancelled
Build and Release / build (arm, 7, windows) (push) Has been cancelled
Build and Release / build (arm64, android) (push) Has been cancelled
Build and Release / build (arm64, darwin) (push) Has been cancelled
Build and Release / build (arm64, freebsd) (push) Has been cancelled
Build and Release / build (arm64, linux) (push) Has been cancelled
Build and Release / build (arm64, openbsd) (push) Has been cancelled
Build and Release / build (arm64, windows) (push) Has been cancelled
Build and Release / build (loong64, linux) (push) Has been cancelled
Build and Release / build (mips, linux) (push) Has been cancelled
Build and Release / build (mips64, linux) (push) Has been cancelled
Build and Release / build (mips64le, linux) (push) Has been cancelled
Build and Release / build (mipsle, linux) (push) Has been cancelled
Build and Release / build (ppc64, linux) (push) Has been cancelled
Build and Release / build (ppc64le, linux) (push) Has been cancelled
Build and Release / build (riscv64, linux) (push) Has been cancelled
Build and Release / build (s390x, linux) (push) Has been cancelled
Test / test (macos-latest) (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
Test / test (windows-latest) (push) Has been cancelled
Fixes https://github.com/XTLS/Xray-core/issues/5088
2025-09-05 15:58:49 +00:00
RPRX
118131fcaf v25.9.5
Some checks failed
Build and Release / build (amd64, darwin, ) (push) Has been cancelled
Build and Release / build (amd64, freebsd, ) (push) Has been cancelled
Build and Release / build (amd64, linux, ) (push) Has been cancelled
Build and Release / build (amd64, openbsd, ) (push) Has been cancelled
Build and Release / build (amd64, windows, ) (push) Has been cancelled
Build and Release / build (arm, 5, linux) (push) Has been cancelled
Build and Release / build (arm, 6, linux) (push) Has been cancelled
Build and Release / build (arm, 7, freebsd) (push) Has been cancelled
Build and Release / build (arm, 7, linux) (push) Has been cancelled
Build and Release / build (arm, 7, openbsd) (push) Has been cancelled
Build and Release / build (arm, 7, windows) (push) Has been cancelled
Build and Release / build (arm64, android) (push) Has been cancelled
Build and Release / build (arm64, darwin) (push) Has been cancelled
Build and Release / build (arm64, freebsd) (push) Has been cancelled
Build and Release / build (arm64, linux) (push) Has been cancelled
Build and Release / build (arm64, openbsd) (push) Has been cancelled
Build and Release / build (arm64, windows) (push) Has been cancelled
Build and Release / build (loong64, linux) (push) Has been cancelled
Build and Release / build (mips, linux) (push) Has been cancelled
Build and Release / build (mips64, linux) (push) Has been cancelled
Build and Release / build (mips64le, linux) (push) Has been cancelled
Build and Release / build (mipsle, linux) (push) Has been cancelled
Build and Release / build (ppc64, linux) (push) Has been cancelled
Build and Release / build (ppc64le, linux) (push) Has been cancelled
Build and Release / build (riscv64, linux) (push) Has been cancelled
Build and Release / build (s390x, linux) (push) Has been cancelled
Test / check-assets (push) Has been cancelled
Test / test (macos-latest) (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
Test / test (windows-latest) (push) Has been cancelled
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

VLESS Post-Quantum Encryption: https://github.com/XTLS/Xray-core/pull/5067
VLESS NFT: https://opensea.io/collection/vless

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-09-05 09:02:36 +00:00
patterniha
197b319f9a DNS outbound: Fix some issues (#5081) 2025-09-05 08:15:16 +00:00
风扇滑翔翼
8b579bf3ec Commands: Add vlessenc (generate complete json pair directly) (#5078)
https://github.com/XTLS/Xray-core/pull/5078#issuecomment-3254161589

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-09-05 08:14:48 +00:00
RPRX
cbade89ab1 VLESS Encryption: Improve server-side tickets' expiration mechanism
https://github.com/XTLS/Xray-core/pull/5067#issuecomment-3253717319
2025-09-04 14:03:55 +00:00
RPRX
d20397c15d DispatchLink(): Fix user stats
Some checks failed
Build and Release / build (arm64, android) (push) Has been cancelled
Build and Release / build (arm64, darwin) (push) Has been cancelled
Test / check-assets (push) Has been cancelled
Test / test (macos-latest) (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
Test / test (windows-latest) (push) Has been cancelled
Build and Release / build (amd64, darwin, ) (push) Has been cancelled
Build and Release / build (amd64, freebsd, ) (push) Has been cancelled
Build and Release / build (amd64, linux, ) (push) Has been cancelled
Build and Release / build (amd64, openbsd, ) (push) Has been cancelled
Build and Release / build (amd64, windows, ) (push) Has been cancelled
Build and Release / build (arm, 5, linux) (push) Has been cancelled
Build and Release / build (arm, 6, linux) (push) Has been cancelled
Build and Release / build (arm, 7, freebsd) (push) Has been cancelled
Build and Release / build (arm, 7, linux) (push) Has been cancelled
Build and Release / build (arm, 7, openbsd) (push) Has been cancelled
Build and Release / build (arm, 7, windows) (push) Has been cancelled
Build and Release / build (arm64, freebsd) (push) Has been cancelled
Build and Release / build (arm64, linux) (push) Has been cancelled
Build and Release / build (arm64, openbsd) (push) Has been cancelled
Build and Release / build (arm64, windows) (push) Has been cancelled
Build and Release / build (loong64, linux) (push) Has been cancelled
Build and Release / build (mips, linux) (push) Has been cancelled
Build and Release / build (mips64, linux) (push) Has been cancelled
Build and Release / build (mips64le, linux) (push) Has been cancelled
Build and Release / build (mipsle, linux) (push) Has been cancelled
Build and Release / build (ppc64, linux) (push) Has been cancelled
Build and Release / build (ppc64le, linux) (push) Has been cancelled
Build and Release / build (riscv64, linux) (push) Has been cancelled
Build and Release / build (s390x, linux) (push) Has been cancelled
Fixes https://github.com/XTLS/Xray-core/pull/5076#issuecomment-3243431593
2025-09-03 23:25:17 +00:00
RPRX
19f8907296 VLESS Encryption: Randomize seconds in ticket and simplify expiration mechanism
Some checks failed
Build and Release / build (amd64, freebsd, ) (push) Has been cancelled
Build and Release / build (amd64, linux, ) (push) Has been cancelled
Build and Release / build (amd64, openbsd, ) (push) Has been cancelled
Build and Release / build (amd64, windows, ) (push) Has been cancelled
Build and Release / build (arm, 5, linux) (push) Has been cancelled
Build and Release / build (arm, 6, linux) (push) Has been cancelled
Build and Release / build (arm, 7, freebsd) (push) Has been cancelled
Build and Release / build (arm, 7, linux) (push) Has been cancelled
Build and Release / build (arm, 7, openbsd) (push) Has been cancelled
Build and Release / build (arm, 7, windows) (push) Has been cancelled
Build and Release / build (arm64, android) (push) Has been cancelled
Build and Release / build (arm64, darwin) (push) Has been cancelled
Build and Release / build (arm64, freebsd) (push) Has been cancelled
Build and Release / build (arm64, linux) (push) Has been cancelled
Build and Release / build (arm64, openbsd) (push) Has been cancelled
Build and Release / build (arm64, windows) (push) Has been cancelled
Build and Release / build (loong64, linux) (push) Has been cancelled
Build and Release / build (mips, linux) (push) Has been cancelled
Build and Release / build (mips64, linux) (push) Has been cancelled
Build and Release / build (mips64le, linux) (push) Has been cancelled
Build and Release / build (mipsle, linux) (push) Has been cancelled
Build and Release / build (ppc64, linux) (push) Has been cancelled
Build and Release / build (ppc64le, linux) (push) Has been cancelled
Build and Release / build (riscv64, linux) (push) Has been cancelled
Build and Release / build (s390x, linux) (push) Has been cancelled
Test / check-assets (push) Has been cancelled
Test / test (macos-latest) (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
Test / test (windows-latest) (push) Has been cancelled
Scheduled assets update / geodat (push) Has been cancelled
https://github.com/XTLS/Xray-core/pull/5067#issuecomment-3246925902
2025-09-02 23:37:14 +00:00
RPRX
e943de5300 proxy/proxy.go: IsRAWTransport() -> IsRAWTransportWithoutSecurity() 2025-09-02 18:15:08 +00:00
yuhan6665
4064f8dd80 XTLS Vision: Refactor code to use DispatchLink() in VLESS inbound (#5076)
Some checks failed
Build and Release / build (amd64, freebsd, ) (push) Has been cancelled
Build and Release / build (amd64, linux, ) (push) Has been cancelled
Build and Release / build (amd64, openbsd, ) (push) Has been cancelled
Build and Release / build (amd64, windows, ) (push) Has been cancelled
Build and Release / build (arm, 5, linux) (push) Has been cancelled
Build and Release / build (arm, 6, linux) (push) Has been cancelled
Build and Release / build (arm, 7, freebsd) (push) Has been cancelled
Build and Release / build (arm, 7, linux) (push) Has been cancelled
Build and Release / build (arm, 7, openbsd) (push) Has been cancelled
Build and Release / build (arm, 7, windows) (push) Has been cancelled
Build and Release / build (arm64, android) (push) Has been cancelled
Build and Release / build (arm64, darwin) (push) Has been cancelled
Build and Release / build (arm64, freebsd) (push) Has been cancelled
Build and Release / build (arm64, linux) (push) Has been cancelled
Build and Release / build (arm64, openbsd) (push) Has been cancelled
Build and Release / build (arm64, windows) (push) Has been cancelled
Build and Release / build (loong64, linux) (push) Has been cancelled
Build and Release / build (mips, linux) (push) Has been cancelled
Build and Release / build (mips64, linux) (push) Has been cancelled
Build and Release / build (mips64le, linux) (push) Has been cancelled
Build and Release / build (mipsle, linux) (push) Has been cancelled
Build and Release / build (ppc64, linux) (push) Has been cancelled
Build and Release / build (ppc64le, linux) (push) Has been cancelled
Build and Release / build (riscv64, linux) (push) Has been cancelled
Build and Release / build (s390x, linux) (push) Has been cancelled
Test / check-assets (push) Has been cancelled
Test / test (macos-latest) (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
Test / test (windows-latest) (push) Has been cancelled
Scheduled assets update / geodat (push) Has been cancelled
* Xtls: code refactor

- Move more logic to VisionReader/Writer
- Remove XtlsWrite()
- XtlsRead now only handle splice at the outbound
- This helps VLESS inbound to have simple buf.copy() so that we can remove pipe next

* Add bufferFlushNext; Use DispatchLink() in VLESS inbound

* Use TimeoutWrapperReader; clean up timer/buffer
2025-09-01 15:15:32 +00:00
yuhan6665
2acd206821 Direct/Freedom outbound: Use proxy.IsRAWTransport(conn) (#5074) 2025-09-01 15:03:01 +00:00
RPRX
4c6fd94d97 VLESS Encryption: Server checks one specific zero-bit in the peer-sent X25519 public key in relays
https://github.com/XTLS/Xray-core/pull/5067#issuecomment-3240198336
2025-09-01 15:01:54 +00:00
RPRX
fd54b10d97 TimeoutWrapperReader: Fix latency issue
Pre-released for 2 days and no one had ever noticed this issue until today : (
2025-09-01 15:00:59 +00:00
RPRX
6830089d3c v25.8.31
Some checks failed
Build and Release / build (amd64, freebsd, ) (push) Has been cancelled
Build and Release / build (amd64, linux, ) (push) Has been cancelled
Build and Release / build (amd64, openbsd, ) (push) Has been cancelled
Build and Release / build (amd64, windows, ) (push) Has been cancelled
Build and Release / build (arm, 5, linux) (push) Has been cancelled
Build and Release / build (arm, 6, linux) (push) Has been cancelled
Build and Release / build (arm, 7, freebsd) (push) Has been cancelled
Build and Release / build (arm, 7, linux) (push) Has been cancelled
Build and Release / build (arm, 7, openbsd) (push) Has been cancelled
Build and Release / build (arm, 7, windows) (push) Has been cancelled
Build and Release / build (arm64, android) (push) Has been cancelled
Build and Release / build (arm64, darwin) (push) Has been cancelled
Build and Release / build (arm64, freebsd) (push) Has been cancelled
Build and Release / build (arm64, linux) (push) Has been cancelled
Build and Release / build (arm64, openbsd) (push) Has been cancelled
Build and Release / build (arm64, windows) (push) Has been cancelled
Build and Release / build (loong64, linux) (push) Has been cancelled
Build and Release / build (mips, linux) (push) Has been cancelled
Build and Release / build (mips64, linux) (push) Has been cancelled
Build and Release / build (mips64le, linux) (push) Has been cancelled
Build and Release / build (mipsle, linux) (push) Has been cancelled
Build and Release / build (ppc64, linux) (push) Has been cancelled
Build and Release / build (ppc64le, linux) (push) Has been cancelled
Build and Release / build (riscv64, linux) (push) Has been cancelled
Build and Release / build (s390x, linux) (push) Has been cancelled
Test / check-assets (push) Has been cancelled
Test / test (macos-latest) (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
Test / test (windows-latest) (push) Has been cancelled
Scheduled assets update / geodat (push) Has been cancelled
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

VLESS Post-Quantum Encryption: https://github.com/XTLS/Xray-core/pull/5067
VLESS NFT: https://opensea.io/collection/vless

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-08-31 13:30:42 +00:00
RPRX
6768a22f67 VLESS Encryption: Switch to "probability-from-to" format for customizable 1-RTT padding parameters
See https://github.com/XTLS/Xray-core/pull/5067#issue-3361308276 for details
2025-08-31 11:35:38 +00:00
RPRX
e8b02cd664 VLESS Encryption: Add customizable 1-RTT padding parameters; Decrease memory using; Chores
Some checks failed
Build and Release / build (amd64, darwin, ) (push) Has been cancelled
Build and Release / build (amd64, freebsd, ) (push) Has been cancelled
Build and Release / build (amd64, linux, ) (push) Has been cancelled
Build and Release / build (amd64, openbsd, ) (push) Has been cancelled
Build and Release / build (amd64, windows, ) (push) Has been cancelled
Build and Release / build (arm, 5, linux) (push) Has been cancelled
Build and Release / build (arm, 6, linux) (push) Has been cancelled
Build and Release / build (arm, 7, freebsd) (push) Has been cancelled
Build and Release / build (arm, 7, linux) (push) Has been cancelled
Build and Release / build (arm, 7, openbsd) (push) Has been cancelled
Build and Release / build (arm, 7, windows) (push) Has been cancelled
Build and Release / build (arm64, android) (push) Has been cancelled
Build and Release / build (arm64, darwin) (push) Has been cancelled
Build and Release / build (arm64, freebsd) (push) Has been cancelled
Build and Release / build (arm64, linux) (push) Has been cancelled
Build and Release / build (arm64, openbsd) (push) Has been cancelled
Build and Release / build (arm64, windows) (push) Has been cancelled
Build and Release / build (loong64, linux) (push) Has been cancelled
Build and Release / build (mips, linux) (push) Has been cancelled
Build and Release / build (mips64, linux) (push) Has been cancelled
Build and Release / build (mips64le, linux) (push) Has been cancelled
Build and Release / build (mipsle, linux) (push) Has been cancelled
Build and Release / build (ppc64, linux) (push) Has been cancelled
Build and Release / build (ppc64le, linux) (push) Has been cancelled
Build and Release / build (riscv64, linux) (push) Has been cancelled
Build and Release / build (s390x, linux) (push) Has been cancelled
Test / check-assets (push) Has been cancelled
Test / test (macos-latest) (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
Test / test (windows-latest) (push) Has been cancelled
Completes https://github.com/XTLS/Xray-core/pull/5067

---------

Co-authored-by: wwqgtxx <wwqgtxx@gmail.com>
2025-08-31 04:09:28 +00:00
RPRX
fbb0ecfb83 Chore: Fix tests
Some checks failed
Build and Release / build (amd64, freebsd, ) (push) Has been cancelled
Build and Release / build (amd64, linux, ) (push) Has been cancelled
Build and Release / build (amd64, openbsd, ) (push) Has been cancelled
Build and Release / build (amd64, windows, ) (push) Has been cancelled
Build and Release / build (arm, 5, linux) (push) Has been cancelled
Build and Release / build (arm, 6, linux) (push) Has been cancelled
Build and Release / build (arm, 7, freebsd) (push) Has been cancelled
Build and Release / build (arm, 7, linux) (push) Has been cancelled
Build and Release / build (arm, 7, openbsd) (push) Has been cancelled
Build and Release / build (arm, 7, windows) (push) Has been cancelled
Build and Release / build (arm64, android) (push) Has been cancelled
Build and Release / build (arm64, darwin) (push) Has been cancelled
Build and Release / build (arm64, freebsd) (push) Has been cancelled
Build and Release / build (arm64, linux) (push) Has been cancelled
Build and Release / build (arm64, openbsd) (push) Has been cancelled
Build and Release / build (arm64, windows) (push) Has been cancelled
Build and Release / build (loong64, linux) (push) Has been cancelled
Build and Release / build (mips, linux) (push) Has been cancelled
Build and Release / build (mips64, linux) (push) Has been cancelled
Build and Release / build (mips64le, linux) (push) Has been cancelled
Build and Release / build (mipsle, linux) (push) Has been cancelled
Build and Release / build (ppc64, linux) (push) Has been cancelled
Build and Release / build (ppc64le, linux) (push) Has been cancelled
Build and Release / build (riscv64, linux) (push) Has been cancelled
Build and Release / build (s390x, linux) (push) Has been cancelled
Test / check-assets (push) Has been cancelled
Test / test (macos-latest) (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
Test / test (windows-latest) (push) Has been cancelled
Scheduled assets update / geodat (push) Has been cancelled
https://github.com/XTLS/Xray-core/pull/5067#issuecomment-3239405569
2025-08-30 17:35:21 +00:00
RPRX
a31842feaa Commands/run: Try all suffixes for default config 2025-08-30 14:17:08 +00:00
风扇滑翔翼
79325ead2e common/buf/buffer.go: Replace copy zero with clear() (#5071)
Co-authored-by: скриде с Тигром (0iq) <42325154+SkrideOne@users.noreply.github.com>
2025-08-30 13:13:40 +00:00
RPRX
81b7cd718a v25.8.29
Some checks failed
Build and Release / build (amd64, freebsd, ) (push) Has been cancelled
Build and Release / build (amd64, linux, ) (push) Has been cancelled
Build and Release / build (amd64, openbsd, ) (push) Has been cancelled
Build and Release / build (amd64, windows, ) (push) Has been cancelled
Build and Release / build (arm, 5, linux) (push) Has been cancelled
Build and Release / build (arm, 6, linux) (push) Has been cancelled
Build and Release / build (arm, 7, freebsd) (push) Has been cancelled
Build and Release / build (arm, 7, linux) (push) Has been cancelled
Build and Release / build (arm, 7, openbsd) (push) Has been cancelled
Build and Release / build (arm, 7, windows) (push) Has been cancelled
Build and Release / build (arm64, android) (push) Has been cancelled
Build and Release / build (arm64, darwin) (push) Has been cancelled
Build and Release / build (arm64, freebsd) (push) Has been cancelled
Build and Release / build (arm64, linux) (push) Has been cancelled
Build and Release / build (arm64, openbsd) (push) Has been cancelled
Build and Release / build (arm64, windows) (push) Has been cancelled
Build and Release / build (loong64, linux) (push) Has been cancelled
Build and Release / build (mips, linux) (push) Has been cancelled
Build and Release / build (mips64, linux) (push) Has been cancelled
Build and Release / build (mips64le, linux) (push) Has been cancelled
Build and Release / build (mipsle, linux) (push) Has been cancelled
Build and Release / build (ppc64, linux) (push) Has been cancelled
Build and Release / build (ppc64le, linux) (push) Has been cancelled
Build and Release / build (riscv64, linux) (push) Has been cancelled
Build and Release / build (s390x, linux) (push) Has been cancelled
Test / check-assets (push) Has been cancelled
Test / test (macos-latest) (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
Test / test (windows-latest) (push) Has been cancelled
Scheduled assets update / geodat (push) Has been cancelled
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

VLESS Post-Quantum Encryption: https://github.com/XTLS/Xray-core/pull/5067
VLESS NFT: https://opensea.io/collection/vless

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-08-29 14:33:22 +00:00
patterniha
ea1a3ae8f1 Trojan UoT: Fix memory/goroutine leak (#5064) 2025-08-29 14:32:13 +00:00
patterniha
593ededd3e Trojan-UoT & UDP-nameserver: Fix forgotten release buffer; UDP dispatcher: Simplified and optimized (#5050) 2025-08-29 14:31:46 +00:00
RPRX
82ea7a3cc5 VLESS Encryption: Re-add automatically ChaCha20-Poly1305
https://github.com/XTLS/Xray-core/pull/5067#issuecomment-3234892060

Fixes https://github.com/XTLS/Xray-core/pull/4952#issuecomment-3234083367 for cheap routers
2025-08-29 14:05:39 +00:00
RPRX
56a45ad578 First step of upcoming refactor for Xray-core: Add TimeoutWrapperReader; Use DispatchLink() in Tunnel/Socks/HTTP inbounds
https://github.com/XTLS/Xray-core/pull/5067#issuecomment-3236833240

Fixes https://github.com/XTLS/Xray-core/pull/4952#issuecomment-3229878125 for client's Xray-core
2025-08-29 12:35:56 +00:00
风扇滑翔翼
4976085ddb Socks/HTTP inbound: Fix unexpected rawConn copy (#5041)
Fixes https://github.com/XTLS/Xray-core/issues/5040
2025-08-28 13:41:44 +00:00
dependabot[bot]
fcdd4df446 Bump github.com/stretchr/testify from 1.11.0 to 1.11.1 (#5068)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.11.0 to 1.11.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.11.0...v1.11.1)
2025-08-28 10:42:02 +00:00
RPRX
12b077f33b Update github.com/xtls/reality to 20250828044527
046fad5ab6
2025-08-28 10:41:39 +00:00
RPRX
702d2c06ca README.md: Update Donation & NFTs
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

VLESS Post-Quantum Encryption: https://github.com/XTLS/Xray-core/pull/5067
VLESS NFT: https://opensea.io/collection/vless

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-08-28 10:41:10 +00:00
RPRX
7951a5c4bf VLESS protocol: Add lightweight, Post-Quantum ML-KEM-768-based PFS 1-RTT / anti-replay 0-RTT AEAD Encryption (#5067)
https://opensea.io/collection/vless
2025-08-28 10:40:48 +00:00
xqzr
c2141f09e7 Test_parseResponse(t *testing.T): Use dns.google for IPv6 (#5060)
Some checks failed
Build and Release / build (amd64, freebsd, ) (push) Has been cancelled
Build and Release / build (amd64, linux, ) (push) Has been cancelled
Build and Release / build (amd64, openbsd, ) (push) Has been cancelled
Build and Release / build (amd64, windows, ) (push) Has been cancelled
Build and Release / build (arm, 5, linux) (push) Has been cancelled
Build and Release / build (arm, 6, linux) (push) Has been cancelled
Build and Release / build (arm, 7, freebsd) (push) Has been cancelled
Build and Release / build (arm, 7, linux) (push) Has been cancelled
Build and Release / build (arm, 7, openbsd) (push) Has been cancelled
Build and Release / build (arm, 7, windows) (push) Has been cancelled
Build and Release / build (arm64, android) (push) Has been cancelled
Build and Release / build (arm64, darwin) (push) Has been cancelled
Build and Release / build (arm64, freebsd) (push) Has been cancelled
Build and Release / build (arm64, linux) (push) Has been cancelled
Build and Release / build (arm64, openbsd) (push) Has been cancelled
Build and Release / build (arm64, windows) (push) Has been cancelled
Build and Release / build (loong64, linux) (push) Has been cancelled
Build and Release / build (mips, linux) (push) Has been cancelled
Build and Release / build (mips64, linux) (push) Has been cancelled
Build and Release / build (mips64le, linux) (push) Has been cancelled
Build and Release / build (mipsle, linux) (push) Has been cancelled
Build and Release / build (ppc64, linux) (push) Has been cancelled
Build and Release / build (ppc64le, linux) (push) Has been cancelled
Build and Release / build (riscv64, linux) (push) Has been cancelled
Build and Release / build (s390x, linux) (push) Has been cancelled
Test / check-assets (push) Has been cancelled
Test / test (macos-latest) (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
Test / test (windows-latest) (push) Has been cancelled
Scheduled assets update / geodat (push) Has been cancelled
2025-08-27 09:37:13 +00:00
xqzr
ef640ed309 checkSystemNetwork(): Use c.root-servers.net (#5059) 2025-08-27 09:36:33 +00:00
风扇滑翔翼
5fa5f3fbb9 WireGuard outbound: Fix close closed (#5054)
Fixes https://github.com/XTLS/Xray-core/issues/5053
2025-08-27 09:33:09 +00:00
风扇滑翔翼
2ee372e758 common/signal/timer.go: Refator to use sync.Once (#5052)
Fixes https://github.com/XTLS/Xray-core/issues/5051
2025-08-27 09:28:53 +00:00
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇
11f0513bce Commands: Add -outpbfile for convert pb (#5048) 2025-08-27 09:24:54 +00:00
dependabot[bot]
b65da77267 Bump github.com/stretchr/testify from 1.10.0 to 1.11.0 (#5061)
Some checks failed
Build and Release / build (amd64, darwin, ) (push) Has been cancelled
Build and Release / build (amd64, freebsd, ) (push) Has been cancelled
Build and Release / build (amd64, linux, ) (push) Has been cancelled
Build and Release / build (amd64, openbsd, ) (push) Has been cancelled
Build and Release / build (amd64, windows, ) (push) Has been cancelled
Build and Release / build (arm, 5, linux) (push) Has been cancelled
Build and Release / build (arm, 6, linux) (push) Has been cancelled
Build and Release / build (arm, 7, freebsd) (push) Has been cancelled
Build and Release / build (arm, 7, linux) (push) Has been cancelled
Build and Release / build (arm, 7, openbsd) (push) Has been cancelled
Build and Release / build (arm, 7, windows) (push) Has been cancelled
Build and Release / build (arm64, android) (push) Has been cancelled
Build and Release / build (arm64, darwin) (push) Has been cancelled
Build and Release / build (arm64, freebsd) (push) Has been cancelled
Build and Release / build (arm64, linux) (push) Has been cancelled
Build and Release / build (arm64, openbsd) (push) Has been cancelled
Build and Release / build (arm64, windows) (push) Has been cancelled
Build and Release / build (loong64, linux) (push) Has been cancelled
Build and Release / build (mips, linux) (push) Has been cancelled
Build and Release / build (mips64, linux) (push) Has been cancelled
Build and Release / build (mips64le, linux) (push) Has been cancelled
Build and Release / build (mipsle, linux) (push) Has been cancelled
Build and Release / build (ppc64, linux) (push) Has been cancelled
Build and Release / build (ppc64le, linux) (push) Has been cancelled
Build and Release / build (riscv64, linux) (push) Has been cancelled
Build and Release / build (s390x, linux) (push) Has been cancelled
Test / test (macos-latest) (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
Test / test (windows-latest) (push) Has been cancelled
Scheduled assets update / geodat (push) Has been cancelled
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.10.0 to 1.11.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.10.0...v1.11.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-version: 1.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-25 12:37:44 +00:00
68 changed files with 3596 additions and 885 deletions

View File

@@ -11,8 +11,10 @@
[<img alt="Project X NFT" width="150px" src="https://raw2.seadn.io/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/7fa9ce900fb39b44226348db330e32/8b7fa9ce900fb39b44226348db330e32.svg" />](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1)
- **ETH/USDT/USDC: `0xDc3Fe44F0f25D13CACb1C4896CD0D321df3146Ee`**
- **Project X NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1**
- **VLESS NFT: https://opensea.io/collection/vless**
- **REALITY NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2**
- **Related links: https://opensea.io/collection/xtls, [Announcement of NFTs by Project X](https://github.com/XTLS/Xray-core/discussions/3633), [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113)**
- **Related links: [VLESS Post-Quantum Encryption](https://github.com/XTLS/Xray-core/pull/5067), [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113), [Announcement of NFTs by Project X](https://github.com/XTLS/Xray-core/discussions/3633)**
## License

View File

@@ -29,7 +29,7 @@ var errSniffingTimeout = errors.New("timeout on sniffing")
type cachedReader struct {
sync.Mutex
reader *pipe.Reader
reader buf.TimeoutReader // *pipe.Reader or *buf.TimeoutWrapperReader
cache buf.MultiBuffer
}
@@ -87,7 +87,9 @@ func (r *cachedReader) Interrupt() {
r.cache = buf.ReleaseMulti(r.cache)
}
r.Unlock()
r.reader.Interrupt()
if p, ok := r.reader.(*pipe.Reader); ok {
p.Interrupt()
}
}
// DefaultDispatcher is a default implementation of Dispatcher.
@@ -194,6 +196,47 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
return inboundLink, outboundLink
}
func (d *DefaultDispatcher) WrapLink(ctx context.Context, link *transport.Link) *transport.Link {
sessionInbound := session.InboundFromContext(ctx)
var user *protocol.MemoryUser
if sessionInbound != nil {
user = sessionInbound.User
}
link.Reader = &buf.TimeoutWrapperReader{Reader: link.Reader}
if user != nil && len(user.Email) > 0 {
p := d.policy.ForLevel(user.Level)
if p.Stats.UserUplink {
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
link.Reader.(*buf.TimeoutWrapperReader).Counter = c
}
}
if p.Stats.UserDownlink {
name := "user>>>" + user.Email + ">>>traffic>>>downlink"
if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
link.Writer = &SizeStatWriter{
Counter: c,
Writer: link.Writer,
}
}
}
if p.Stats.UserOnline {
name := "user>>>" + user.Email + ">>>online"
if om, _ := stats.GetOrRegisterOnlineMap(d.stats, name); om != nil {
sessionInbounds := session.InboundFromContext(ctx)
userIP := sessionInbounds.Source.Address.String()
om.AddIP(userIP)
// log Online user with ips
// errors.LogDebug(ctx, "user>>>" + user.Email + ">>>online", om.Count(), om.List())
}
}
}
return link
}
func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool {
domain := result.Domain()
if domain == "" {
@@ -314,12 +357,13 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
content = new(session.Content)
ctx = session.ContextWithContent(ctx, content)
}
outbound = d.WrapLink(ctx, outbound)
sniffingRequest := content.SniffingRequest
if !sniffingRequest.Enabled {
d.routedDispatch(ctx, outbound, destination)
} else {
cReader := &cachedReader{
reader: outbound.Reader.(*pipe.Reader),
reader: outbound.Reader.(buf.TimeoutReader),
}
outbound.Reader = cReader
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network)

View File

@@ -329,7 +329,7 @@ func init() {
}
func checkSystemNetwork() (supportIPv4 bool, supportIPv6 bool) {
conn4, err4 := net.Dial("udp4", "8.8.8.8:53")
conn4, err4 := net.Dial("udp4", "192.33.4.12:53")
if err4 != nil {
supportIPv4 = false
} else {
@@ -337,7 +337,7 @@ func checkSystemNetwork() (supportIPv4 bool, supportIPv6 bool) {
conn4.Close()
}
conn6, err6 := net.Dial("udp6", "[2001:4860:4860::8888]:53")
conn6, err6 := net.Dial("udp6", "[2001:500:2::c]:53")
if err6 != nil {
supportIPv6 = false
} else {

View File

@@ -39,8 +39,8 @@ func Test_parseResponse(t *testing.T) {
common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")),
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
common.Must2(dns.NewRR("google.com. IN CNAME test.google.com")),
common.Must2(dns.NewRR("google.com. IN AAAA 2001::123:8888")),
common.Must2(dns.NewRR("google.com. IN AAAA 2001::123:8844")),
common.Must2(dns.NewRR("google.com. IN AAAA 2001:4860:4860::8888")),
common.Must2(dns.NewRR("google.com. IN AAAA 2001:4860:4860::8844")),
)
p = append(p, common.Must2(ans.Pack()))
@@ -72,7 +72,7 @@ func Test_parseResponse(t *testing.T) {
},
{
"aaaa record",
&IPRecord{2, []net.IP{net.ParseIP("2001::123:8888"), net.ParseIP("2001::123:8844")}, time.Time{}, dnsmessage.RCodeSuccess, nil},
&IPRecord{2, []net.IP{net.ParseIP("2001:4860:4860::8888"), net.ParseIP("2001:4860:4860::8844")}, time.Time{}, dnsmessage.RCodeSuccess, nil},
false,
},
}

View File

@@ -90,7 +90,9 @@ func (s *ClassicNameServer) RequestsCleanup() error {
// HandleResponse handles udp response packet from remote DNS server.
func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_proto.Packet) {
ipRec, err := parseResponse(packet.Payload.Bytes())
payload := packet.Payload
ipRec, err := parseResponse(payload.Bytes())
payload.Release()
if err != nil {
errors.LogError(ctx, s.Name(), " fail to parse responded DNS udp")
return
@@ -125,6 +127,8 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
newReq.msg = &newMsg
s.addPendingRequest(&newReq)
b, _ := dns.PackMessage(newReq.msg)
copyDest := net.UDPDestination(s.address.Address, s.address.Port)
b.UDP = &copyDest
s.udpServer.Dispatch(toDnsContext(newReq.ctx, s.address.String()), *s.address, b)
return
}
@@ -158,6 +162,8 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, domai
}
s.addPendingRequest(udpReq)
b, _ := dns.PackMessage(req.msg)
copyDest := net.UDPDestination(s.address.Address, s.address.Port)
b.UDP = &copyDest
s.udpServer.Dispatch(toDnsContext(ctx, s.address.String()), *s.address, b)
}
}

View File

@@ -239,8 +239,10 @@ func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
}
out:
err := h.proxy.Process(ctx, link, h)
var errC error
if err != nil {
if goerrors.Is(err, io.EOF) || goerrors.Is(err, io.ErrClosedPipe) || goerrors.Is(err, context.Canceled) {
errC = errors.Cause(err)
if goerrors.Is(errC, io.EOF) || goerrors.Is(errC, io.ErrClosedPipe) || goerrors.Is(errC, context.Canceled) {
err = nil
}
}
@@ -250,9 +252,13 @@ out:
session.SubmitOutboundErrorToOriginator(ctx, err)
errors.LogInfo(ctx, err.Error())
common.Interrupt(link.Writer)
} else {
if errC != nil && goerrors.Is(errC, io.ErrClosedPipe) {
common.Interrupt(link.Writer)
} else {
common.Close(link.Writer)
}
}
common.Interrupt(link.Reader)
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"time"
"github.com/xtls/xray-core/app/dispatcher"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/mux"
"github.com/xtls/xray-core/common/net"
@@ -148,7 +149,6 @@ func (w *BridgeWorker) Connections() uint32 {
}
func (w *BridgeWorker) handleInternalConn(link *transport.Link) {
go func() {
reader := link.Reader
for {
mb, err := reader.ReadMultiBuffer()
@@ -166,7 +166,6 @@ func (w *BridgeWorker) handleInternalConn(link *transport.Link) {
}
}
}
}()
}
func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) {
@@ -181,7 +180,7 @@ func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*tra
uplinkReader, uplinkWriter := pipe.New(opt...)
downlinkReader, downlinkWriter := pipe.New(opt...)
w.handleInternalConn(&transport.Link{
go w.handleInternalConn(&transport.Link{
Reader: downlinkReader,
Writer: uplinkWriter,
})
@@ -200,6 +199,7 @@ func (w *BridgeWorker) DispatchLink(ctx context.Context, dest net.Destination, l
return w.dispatcher.DispatchLink(ctx, dest, link)
}
link = w.dispatcher.(*dispatcher.DefaultDispatcher).WrapLink(ctx, link)
w.handleInternalConn(link)
return nil

View File

@@ -15,8 +15,6 @@ const (
var ErrBufferFull = errors.New("buffer is full")
var zero = [Size * 10]byte{0}
var pool = bytespool.GetPool(Size)
// ownership represents the data owner of the buffer.
@@ -146,7 +144,7 @@ func (b *Buffer) Bytes() []byte {
}
// Extend increases the buffer size by n bytes, and returns the extended part.
// It panics if result size is larger than buf.Size.
// It panics if result size is larger than size of this buffer.
func (b *Buffer) Extend(n int32) []byte {
end := b.end + n
if end > int32(len(b.v)) {
@@ -154,7 +152,7 @@ func (b *Buffer) Extend(n int32) []byte {
}
ext := b.v[b.end:end]
b.end = end
copy(ext, zero[:])
clear(ext)
return ext
}
@@ -217,7 +215,7 @@ func (b *Buffer) Resize(from, to int32) {
b.start += from
b.Check()
if b.end > oldEnd {
copy(b.v[oldEnd:b.end], zero[:])
clear(b.v[oldEnd:b.end])
}
}

View File

@@ -24,9 +24,59 @@ var ErrReadTimeout = errors.New("IO timeout")
// TimeoutReader is a reader that returns error if Read() operation takes longer than the given timeout.
type TimeoutReader interface {
Reader
ReadMultiBufferTimeout(time.Duration) (MultiBuffer, error)
}
type TimeoutWrapperReader struct {
Reader
stats.Counter
mb MultiBuffer
err error
done chan struct{}
}
func (r *TimeoutWrapperReader) ReadMultiBuffer() (MultiBuffer, error) {
if r.done != nil {
<-r.done
r.done = nil
if r.Counter != nil {
r.Counter.Add(int64(r.mb.Len()))
}
return r.mb, r.err
}
r.mb, r.err = r.Reader.ReadMultiBuffer()
if r.Counter != nil {
r.Counter.Add(int64(r.mb.Len()))
}
return r.mb, r.err
}
func (r *TimeoutWrapperReader) ReadMultiBufferTimeout(duration time.Duration) (MultiBuffer, error) {
if r.done == nil {
r.done = make(chan struct{})
go func() {
r.mb, r.err = r.Reader.ReadMultiBuffer()
close(r.done)
}()
}
timeout := make(chan struct{})
go func() {
time.Sleep(duration)
close(timeout)
}()
select {
case <-r.done:
r.done = nil
if r.Counter != nil {
r.Counter.Add(int64(r.mb.Len()))
}
return r.mb, r.err
case <-timeout:
return nil, nil
}
}
// Writer extends io.Writer with MultiBuffer.
type Writer interface {
// WriteMultiBuffer writes a MultiBuffer into underlying writer.

View File

@@ -78,6 +78,7 @@ type BufferedWriter struct {
writer Writer
buffer *Buffer
buffered bool
flushNext bool
}
// NewBufferedWriter creates a new BufferedWriter.
@@ -161,6 +162,12 @@ func (w *BufferedWriter) WriteMultiBuffer(b MultiBuffer) error {
}
}
if w.flushNext {
w.buffered = false
w.flushNext = false
return w.flushInternal()
}
return nil
}
@@ -201,6 +208,13 @@ func (w *BufferedWriter) SetBuffered(f bool) error {
return nil
}
// SetFlushNext will wait the next WriteMultiBuffer to flush and set buffered = false
func (w *BufferedWriter) SetFlushNext() {
w.Lock()
defer w.Unlock()
w.flushNext = true
}
// ReadFrom implements io.ReaderFrom.
func (w *BufferedWriter) ReadFrom(reader io.Reader) (int64, error) {
if err := w.SetBuffered(false); err != nil {

View File

@@ -7,6 +7,7 @@ import (
"go/build"
"os"
"path/filepath"
"reflect"
"strings"
"github.com/xtls/xray-core/common/errors"
@@ -153,3 +154,14 @@ func GetModuleName(pathToProjectRoot string) (string, error) {
}
return moduleName, fmt.Errorf("no `go.mod` file in every parent directory of `%s`", pathToProjectRoot)
}
// CloseIfExists call obj.Close() if obj is not nil.
func CloseIfExists(obj any) error {
if obj != nil {
v := reflect.ValueOf(obj)
if !v.IsNil() {
return Close(obj)
}
}
return nil
}

View File

@@ -10,6 +10,9 @@ func RandBetween(from int64, to int64) int64 {
if from == to {
return from
}
if from > to {
from, to = to, from
}
bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from))
return from + bigInt.Int64()
}

View File

@@ -2,6 +2,7 @@ package mux
import (
"context"
goerrors "errors"
"io"
"sync"
"time"
@@ -154,8 +155,11 @@ func (f *DialingWorkerFactory) Create() (*ClientWorker, error) {
ctx := session.ContextWithOutbounds(context.Background(), outbounds)
ctx, cancel := context.WithCancel(ctx)
if err := p.Process(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter}, d); err != nil {
errors.LogInfoInner(ctx, err, "failed to handler mux client connection")
if errP := p.Process(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter}, d); errP != nil {
errC := errors.Cause(errP)
if !(goerrors.Is(errC, io.EOF) || goerrors.Is(errC, io.ErrClosedPipe) || goerrors.Is(errC, context.Canceled)) {
errors.LogInfoInner(ctx, errP, "failed to handler mux client connection")
}
}
common.Must(c.Close())
cancel()
@@ -222,7 +226,7 @@ func (m *ClientWorker) monitor() {
select {
case <-m.done.Wait():
m.sessionManager.Close()
common.Close(m.link.Writer)
common.Interrupt(m.link.Writer)
common.Interrupt(m.link.Reader)
return
case <-m.timer.C:
@@ -247,7 +251,7 @@ func writeFirstPayload(reader buf.Reader, writer *Writer) error {
return nil
}
func fetchInput(ctx context.Context, s *Session, output buf.Writer) {
func fetchInput(ctx context.Context, s *Session, output buf.Writer, timer *time.Ticker) {
outbounds := session.OutboundsFromContext(ctx)
ob := outbounds[len(outbounds)-1]
transferType := protocol.TransferTypeStream
@@ -258,6 +262,7 @@ func fetchInput(ctx context.Context, s *Session, output buf.Writer) {
writer := NewWriter(s.ID, ob.Target, output, transferType, xudp.GetGlobalID(ctx))
defer s.Close(false)
defer writer.Close()
defer timer.Reset(time.Second * 16)
errors.LogInfo(ctx, "dispatching request to ", ob.Target)
if err := writeFirstPayload(s.input, writer); err != nil {
@@ -307,7 +312,11 @@ func (m *ClientWorker) Dispatch(ctx context.Context, link *transport.Link) bool
}
s.input = link.Reader
s.output = link.Writer
go fetchInput(ctx, s, m.link.Writer)
if _, ok := link.Reader.(*pipe.Reader); ok {
go fetchInput(ctx, s, m.link.Writer, m.timer)
} else {
fetchInput(ctx, s, m.link.Writer, m.timer)
}
return true
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"io"
"github.com/xtls/xray-core/app/dispatcher"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
@@ -61,6 +62,7 @@ func (s *Server) DispatchLink(ctx context.Context, dest net.Destination, link *t
if dest.Address != muxCoolAddress {
return s.dispatcher.DispatchLink(ctx, dest, link)
}
link = s.dispatcher.(*dispatcher.DefaultDispatcher).WrapLink(ctx, link)
_, err := NewServerWorker(ctx, s.dispatcher, link)
return err
}
@@ -87,7 +89,14 @@ func NewServerWorker(ctx context.Context, d routing.Dispatcher, link *transport.
link: link,
sessionManager: NewSessionManager(),
}
if inbound := session.InboundFromContext(ctx); inbound != nil {
inbound.CanSpliceCopy = 3
}
if _, ok := link.Reader.(*pipe.Reader); ok {
go worker.run(ctx)
} else {
worker.run(ctx)
}
return worker, nil
}
@@ -311,8 +320,8 @@ func (w *ServerWorker) run(ctx context.Context) {
reader := &buf.BufferedReader{Reader: w.link.Reader}
defer w.sessionManager.Close()
defer common.Close(w.link.Writer)
defer common.Interrupt(w.link.Reader)
defer common.Interrupt(w.link.Writer)
for {
select {

View File

@@ -79,20 +79,18 @@ type CommandSwitchAccount struct {
}
var (
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
// Keep in sync with crypto/tls/cipher_suites.go.
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ && cpu.X86.HasSSE41 && cpu.X86.HasSSSE3
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
// Keep in sync with crypto/aes/cipher_s390x.go.
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
(cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCTR && cpu.S390X.HasGHASH
hasGCMAsmPPC64 = runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le"
hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
runtime.GOARCH == "s390x" && hasGCMAsmS390X
HasAESGCMHardwareSupport = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X || hasGCMAsmPPC64
)
func (sc *SecurityConfig) GetSecurityType() SecurityType {
if sc == nil || sc.Type == SecurityType_AUTO {
if hasAESGCMHardwareSupport {
if HasAESGCMHardwareSupport {
return SecurityType_AES128_GCM
}
return SecurityType_CHACHA20_POLY1305

View File

@@ -3,6 +3,7 @@ package signal
import (
"context"
"sync"
"sync/atomic"
"time"
"github.com/xtls/xray-core/common"
@@ -14,10 +15,12 @@ type ActivityUpdater interface {
}
type ActivityTimer struct {
sync.RWMutex
mu sync.RWMutex
updated chan struct{}
checkTask *task.Periodic
onTimeout func()
consumed atomic.Bool
once sync.Once
}
func (t *ActivityTimer) Update() {
@@ -37,39 +40,39 @@ func (t *ActivityTimer) check() error {
}
func (t *ActivityTimer) finish() {
t.Lock()
defer t.Unlock()
t.once.Do(func() {
t.consumed.Store(true)
t.mu.Lock()
defer t.mu.Unlock()
if t.onTimeout != nil {
common.CloseIfExists(t.checkTask)
t.onTimeout()
t.onTimeout = nil
}
if t.checkTask != nil {
t.checkTask.Close()
t.checkTask = nil
}
})
}
func (t *ActivityTimer) SetTimeout(timeout time.Duration) {
if t.consumed.Load() {
return
}
if timeout == 0 {
t.finish()
return
}
checkTask := &task.Periodic{
t.mu.Lock()
defer t.mu.Unlock()
// double check, just in case
if t.consumed.Load() {
return
}
newCheckTask := &task.Periodic{
Interval: timeout,
Execute: t.check,
}
t.Lock()
if t.checkTask != nil {
t.checkTask.Close()
}
t.checkTask = checkTask
common.CloseIfExists(t.checkTask)
t.checkTask = newCheckTask
t.Update()
common.Must(checkTask.Start())
t.Unlock()
common.Must(newCheckTask.Start())
}
func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer {

View File

@@ -18,8 +18,8 @@ import (
var (
Version_x byte = 25
Version_y byte = 8
Version_z byte = 3
Version_y byte = 9
Version_z byte = 5
)
var (

4
go.mod
View File

@@ -16,10 +16,10 @@ require (
github.com/sagernet/sing v0.5.1
github.com/sagernet/sing-shadowsocks v0.2.7
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771
github.com/stretchr/testify v1.10.0
github.com/stretchr/testify v1.11.1
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
github.com/vishvananda/netlink v1.3.1
github.com/xtls/reality v0.0.0-20250725142056-5b52a03d4fb7
github.com/xtls/reality v0.0.0-20250828044527-046fad5ab64f
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/crypto v0.41.0
golang.org/x/net v0.43.0

8
go.sum
View File

@@ -67,16 +67,16 @@ github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0=
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/xtls/reality v0.0.0-20250725142056-5b52a03d4fb7 h1:Ript0vN+nSO33+Vj4n0mgNY5M+oOxFQJdrJ1VnwTBO0=
github.com/xtls/reality v0.0.0-20250725142056-5b52a03d4fb7/go.mod h1:XxvnCCgBee4WWE0bc4E+a7wbk8gkJ/rS0vNVNtC5qp0=
github.com/xtls/reality v0.0.0-20250828044527-046fad5ab64f h1:o1Kryl9qEYYzNep9RId9DM1kBn8tBrcK5UJnti/l0NI=
github.com/xtls/reality v0.0.0-20250828044527-046fad5ab64f/go.mod h1:XxvnCCgBee4WWE0bc4E+a7wbk8gkJ/rS0vNVNtC5qp0=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=

View File

@@ -1,6 +1,7 @@
package conf
import (
"encoding/base64"
"encoding/json"
"path/filepath"
"runtime"
@@ -73,17 +74,67 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
}
if account.Encryption != "" {
return nil, errors.New(`VLESS clients: "encryption" should not in inbound settings`)
return nil, errors.New(`VLESS clients: "encryption" should not be in inbound settings`)
}
user.Account = serial.ToTypedMessage(account)
config.Clients[idx] = user
}
if c.Decryption != "none" {
config.Decryption = c.Decryption
if !func() bool {
s := strings.Split(config.Decryption, ".")
if len(s) < 4 || s[0] != "mlkem768x25519plus" {
return false
}
switch s[1] {
case "native":
case "xorpub":
config.XorMode = 1
case "random":
config.XorMode = 2
default:
return false
}
t := strings.SplitN(strings.TrimSuffix(s[2], "s"), "-", 2)
i, err := strconv.Atoi(t[0])
if err != nil {
return false
}
config.SecondsFrom = int64(i)
if len(t) == 2 {
i, err := strconv.Atoi(t[1])
if err != nil {
return false
}
config.SecondsTo = int64(i)
}
padding := 0
for _, r := range s[3:] {
if len(r) < 20 {
padding += len(r) + 1
continue
}
if b, _ := base64.RawURLEncoding.DecodeString(r); len(b) != 32 && len(b) != 64 {
return false
}
}
config.Decryption = config.Decryption[27+len(s[2]):]
if padding > 0 {
config.Padding = config.Decryption[:padding-1]
config.Decryption = config.Decryption[padding:]
}
return true
}() && config.Decryption != "none" {
if config.Decryption == "" {
return nil, errors.New(`VLESS settings: please add/set "decryption":"none" to every settings`)
}
config.Decryption = c.Decryption
return nil, errors.New(`VLESS settings: unsupported "decryption": ` + config.Decryption)
}
if config.Decryption != "none" && c.Fallbacks != nil {
return nil, errors.New(`VLESS settings: "fallbacks" can not be used together with "decryption"`)
}
for _, fb := range c.Fallbacks {
var i uint16
@@ -155,16 +206,16 @@ type VLessOutboundConfig struct {
func (c *VLessOutboundConfig) Build() (proto.Message, error) {
config := new(outbound.Config)
if len(c.Vnext) == 0 {
return nil, errors.New(`VLESS settings: "vnext" is empty`)
if len(c.Vnext) != 1 {
return nil, errors.New(`VLESS settings: "vnext" should have one and only one member`)
}
config.Vnext = make([]*protocol.ServerEndpoint, len(c.Vnext))
for idx, rec := range c.Vnext {
if rec.Address == nil {
return nil, errors.New(`VLESS vnext: "address" is not set`)
}
if len(rec.Users) == 0 {
return nil, errors.New(`VLESS vnext: "users" is empty`)
if len(rec.Users) != 1 {
return nil, errors.New(`VLESS vnext: "users" should have one and only one member`)
}
spec := &protocol.ServerEndpoint{
Address: rec.Address.Build(),
@@ -193,9 +244,49 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
return nil, errors.New(`VLESS users: "flow" doesn't support "` + account.Flow + `" in this version`)
}
if account.Encryption != "none" {
if !func() bool {
s := strings.Split(account.Encryption, ".")
if len(s) < 4 || s[0] != "mlkem768x25519plus" {
return false
}
switch s[1] {
case "native":
case "xorpub":
account.XorMode = 1
case "random":
account.XorMode = 2
default:
return false
}
switch s[2] {
case "1rtt":
case "0rtt":
account.Seconds = 1
default:
return false
}
padding := 0
for _, r := range s[3:] {
if len(r) < 20 {
padding += len(r) + 1
continue
}
if b, _ := base64.RawURLEncoding.DecodeString(r); len(b) != 32 && len(b) != 1184 {
return false
}
}
account.Encryption = account.Encryption[27+len(s[2]):]
if padding > 0 {
account.Padding = account.Encryption[:padding-1]
account.Encryption = account.Encryption[padding:]
}
return true
}() && account.Encryption != "none" {
if account.Encryption == "" {
return nil, errors.New(`VLESS users: please add/set "encryption":"none" for every user`)
}
return nil, errors.New(`VLESS users: unsupported "encryption": ` + account.Encryption)
}
user.Account = serial.ToTypedMessage(account)
spec.User[idx] = user

View File

@@ -17,5 +17,7 @@ func init() {
cmdX25519,
cmdWG,
cmdMLDSA65,
cmdMLKEM768,
cmdVLESSEnc,
)
}

View File

@@ -3,6 +3,7 @@ package convert
import (
"fmt"
"os"
"strings"
"github.com/xtls/xray-core/common/cmdarg"
creflect "github.com/xtls/xray-core/common/reflect"
@@ -14,15 +15,18 @@ import (
var cmdProtobuf = &base.Command{
CustomFlags: true,
UsageLine: "{{.Exec}} convert pb [-debug] [-type] [json file] [json file] ...",
UsageLine: "{{.Exec}} convert pb [-outpbfile file] [-debug] [-type] [json file] [json file] ...",
Short: "Convert multiple json configs to protobuf",
Long: `
Convert multiple json configs to protobuf.
Convert multiple configs to ProtoBuf. JSON, YAML and TOML can be used.
Arguments:
-o file, -outpbfile file
Write the ProtoBuf output (eg. mix.pb) to specified file location.
-d, -debug
Show mix.pb as json.
Show mix.pb as JSON format.
FOR DEBUGGING ONLY!
DO NOT PASS THIS OUTPUT TO XRAY-CORE!
@@ -31,16 +35,20 @@ Arguments:
Examples:
{{.Exec}} convert pb config.json c1.json c2.json c3.json > mix.pb
{{.Exec}} convert pb -outpbfile output.pb config.json c1.json c2.json c3.json
{{.Exec}} convert pb -debug mix.pb
`,
Run: executeConvertConfigsToProtobuf,
}
func executeConvertConfigsToProtobuf(cmd *base.Command, args []string) {
var optFile string
var optDump bool
var optType bool
cmd.Flag.StringVar(&optFile, "o", "", "")
cmd.Flag.StringVar(&optFile, "outpbfile", "", "")
cmd.Flag.BoolVar(&optDump, "d", false, "")
cmd.Flag.BoolVar(&optDump, "debug", false, "")
cmd.Flag.BoolVar(&optType, "t", false, "")
@@ -52,6 +60,17 @@ func executeConvertConfigsToProtobuf(cmd *base.Command, args []string) {
unnamedArgs.Set(v)
}
if len(optFile) > 0 {
switch core.GetFormatByExtension(getFileExtension(optFile)){
case "protobuf", "":
fmt.Println("Output ProtoBuf file is ", optFile)
default:
base.Fatalf("-outpbfile followed by a possible original config.")
}
} else if !optDump {
base.Fatalf("-outpbfile not specified")
}
if len(unnamedArgs) < 1 {
base.Fatalf("invalid config list length: %d", len(unnamedArgs))
}
@@ -70,12 +89,28 @@ func executeConvertConfigsToProtobuf(cmd *base.Command, args []string) {
}
}
if len(optFile) > 0 {
bytesConfig, err := proto.Marshal(pbConfig)
if err != nil {
base.Fatalf("failed to marshal proto config: %s", err)
}
if _, err := os.Stdout.Write(bytesConfig); err != nil {
base.Fatalf("failed to write proto config: %s", err)
f, err := os.Create(optFile)
if err != nil {
base.Fatalf("failed to create proto file: %s", err)
}
defer f.Close()
if _, err := f.Write(bytesConfig); err != nil {
base.Fatalf("failed to write proto file: %s", err)
}
}
}
func getFileExtension(filename string) string {
idx := strings.LastIndexByte(filename, '.')
if idx == -1 {
return ""
}
return filename[idx+1:]
}

View File

@@ -1,17 +1,15 @@
package all
import (
"crypto/ecdh"
"crypto/rand"
"encoding/base64"
"fmt"
"golang.org/x/crypto/curve25519"
"lukechampine.com/blake3"
)
func Curve25519Genkey(StdEncoding bool, input_base64 string) {
var output string
var err error
var privateKey, publicKey []byte
var encoding *base64.Encoding
if *input_stdEncoding || StdEncoding {
encoding = base64.StdEncoding
@@ -19,40 +17,47 @@ func Curve25519Genkey(StdEncoding bool, input_base64 string) {
encoding = base64.RawURLEncoding
}
var privateKey []byte
if len(input_base64) > 0 {
privateKey, err = encoding.DecodeString(input_base64)
privateKey, _ = encoding.DecodeString(input_base64)
if len(privateKey) != 32 {
fmt.Println("Invalid length of X25519 private key.")
return
}
}
privateKey, password, hash32, err := genCurve25519(privateKey)
if err != nil {
output = err.Error()
goto out
}
if len(privateKey) != curve25519.ScalarSize {
output = "Invalid length of private key."
goto out
}
fmt.Println(err)
return
}
fmt.Printf("PrivateKey: %v\nPassword: %v\nHash32: %v\n",
encoding.EncodeToString(privateKey),
encoding.EncodeToString(password),
encoding.EncodeToString(hash32[:]))
}
if privateKey == nil {
privateKey = make([]byte, curve25519.ScalarSize)
if _, err = rand.Read(privateKey); err != nil {
output = err.Error()
goto out
func genCurve25519(inputPrivateKey []byte) (privateKey []byte, password []byte, hash32 [32]byte, returnErr error) {
if len(inputPrivateKey) > 0 {
privateKey = inputPrivateKey
}
if privateKey == nil {
privateKey = make([]byte, 32)
rand.Read(privateKey)
}
// Modify random bytes using algorithm described at:
// https://cr.yp.to/ecdh.html.
// https://cr.yp.to/ecdh.html
// (Just to make sure printing the real private key)
privateKey[0] &= 248
privateKey[31] &= 127
privateKey[31] |= 64
if publicKey, err = curve25519.X25519(privateKey, curve25519.Basepoint); err != nil {
output = err.Error()
goto out
key, err := ecdh.X25519().NewPrivateKey(privateKey)
if err != nil {
returnErr = err
return
}
output = fmt.Sprintf("Private key: %v\nPublic key: %v",
encoding.EncodeToString(privateKey),
encoding.EncodeToString(publicKey))
out:
fmt.Println(output)
password = key.PublicKey().Bytes()
hash32 = blake3.Sum256(password)
return
}

View File

@@ -11,9 +11,9 @@ import (
var cmdMLDSA65 = &base.Command{
UsageLine: `{{.Exec}} mldsa65 [-i "seed (base64.RawURLEncoding)"]`,
Short: `Generate key pair for ML-DSA-65 post-quantum signature`,
Short: `Generate key pair for ML-DSA-65 post-quantum signature (REALITY)`,
Long: `
Generate key pair for ML-DSA-65 post-quantum signature.
Generate key pair for ML-DSA-65 post-quantum signature (REALITY).
Random: {{.Exec}} mldsa65
@@ -25,18 +25,22 @@ func init() {
cmdMLDSA65.Run = executeMLDSA65 // break init loop
}
var input_seed = cmdMLDSA65.Flag.String("i", "", "")
var input_mldsa65 = cmdMLDSA65.Flag.String("i", "", "")
func executeMLDSA65(cmd *base.Command, args []string) {
var seed [32]byte
if len(*input_seed) > 0 {
s, _ := base64.RawURLEncoding.DecodeString(*input_seed)
if len(*input_mldsa65) > 0 {
s, _ := base64.RawURLEncoding.DecodeString(*input_mldsa65)
if len(s) != 32 {
fmt.Println("Invalid length of ML-DSA-65 seed.")
return
}
seed = [32]byte(s)
} else {
rand.Read(seed[:])
}
pub, _ := mldsa65.NewKeyFromSeed(&seed)
fmt.Printf("Seed: %v\nVerify: %v",
fmt.Printf("Seed: %v\nVerify: %v\n",
base64.RawURLEncoding.EncodeToString(seed[:]),
base64.RawURLEncoding.EncodeToString(pub.Bytes()))
}

View File

@@ -0,0 +1,60 @@
package all
import (
"crypto/mlkem"
"crypto/rand"
"encoding/base64"
"fmt"
"github.com/xtls/xray-core/main/commands/base"
"lukechampine.com/blake3"
)
var cmdMLKEM768 = &base.Command{
UsageLine: `{{.Exec}} mlkem768 [-i "seed (base64.RawURLEncoding)"]`,
Short: `Generate key pair for ML-KEM-768 post-quantum key exchange (VLESS Encryption)`,
Long: `
Generate key pair for ML-KEM-768 post-quantum key exchange (VLESS Encryption).
Random: {{.Exec}} mlkem768
From seed: {{.Exec}} mlkem768 -i "seed (base64.RawURLEncoding)"
`,
}
func init() {
cmdMLKEM768.Run = executeMLKEM768 // break init loop
}
var input_mlkem768 = cmdMLKEM768.Flag.String("i", "", "")
func executeMLKEM768(cmd *base.Command, args []string) {
var seed [64]byte
if len(*input_mlkem768) > 0 {
s, _ := base64.RawURLEncoding.DecodeString(*input_mlkem768)
if len(s) != 64 {
fmt.Println("Invalid length of ML-KEM-768 seed.")
return
}
seed = [64]byte(s)
} else {
rand.Read(seed[:])
}
seed, client, hash32 := genMLKEM768(&seed)
fmt.Printf("Seed: %v\nClient: %v\nHash32: %v\n",
base64.RawURLEncoding.EncodeToString(seed[:]),
base64.RawURLEncoding.EncodeToString(client),
base64.RawURLEncoding.EncodeToString(hash32[:]))
}
func genMLKEM768(inputSeed *[64]byte) (seed [64]byte, client []byte, hash32 [32]byte) {
if inputSeed == nil {
rand.Read(seed[:])
} else {
seed = *inputSeed
}
key, _ := mlkem.NewDecapsulationKey768(seed[:])
client = key.EncapsulationKey().Bytes()
hash32 = blake3.Sum256(client)
return
}

View File

@@ -9,9 +9,9 @@ import (
var cmdUUID = &base.Command{
UsageLine: `{{.Exec}} uuid [-i "example"]`,
Short: `Generate UUIDv4 or UUIDv5`,
Short: `Generate UUIDv4 or UUIDv5 (VLESS)`,
Long: `
Generate UUIDv4 or UUIDv5.
Generate UUIDv4 or UUIDv5 (VLESS).
UUIDv4 (random): {{.Exec}} uuid

View File

@@ -0,0 +1,41 @@
package all
import (
"encoding/base64"
"fmt"
"strings"
"github.com/xtls/xray-core/main/commands/base"
)
var cmdVLESSEnc = &base.Command{
UsageLine: `{{.Exec}} vlessenc`,
Short: `Generate decryption/encryption json pair (VLESS Encryption)`,
Long: `
Generate decryption/encryption json pair (VLESS Encryption).
`,
}
func init() {
cmdVLESSEnc.Run = executeVLESSEnc // break init loop
}
func executeVLESSEnc(cmd *base.Command, args []string) {
privateKey, password, _, _ := genCurve25519(nil)
serverKey := base64.RawURLEncoding.EncodeToString(privateKey)
clientKey := base64.RawURLEncoding.EncodeToString(password)
decryption := generateDotConfig("mlkem768x25519plus", "native", "600s", serverKey)
encryption := generateDotConfig("mlkem768x25519plus", "native", "0rtt", clientKey)
seed, client, _ := genMLKEM768(nil)
serverKeyPQ := base64.RawURLEncoding.EncodeToString(seed[:])
clientKeyPQ := base64.RawURLEncoding.EncodeToString(client)
decryptionPQ := generateDotConfig("mlkem768x25519plus", "native", "600s", serverKeyPQ)
encryptionPQ := generateDotConfig("mlkem768x25519plus", "native", "0rtt", clientKeyPQ)
fmt.Printf("Choose one Authentication to use, do not mix them. Ephemeral key exchange is Post-Quantum safe anyway.\n\n")
fmt.Printf("Authentication: X25519, not Post-Quantum\n\"decryption\": \"%v\"\n\"encryption\": \"%v\"\n\n", decryption, encryption)
fmt.Printf("Authentication: ML-KEM-768, Post-Quantum\n\"decryption\": \"%v\"\n\"encryption\": \"%v\"\n", decryptionPQ, encryptionPQ)
}
func generateDotConfig(fields ...string) string {
return strings.Join(fields, ".")
}

View File

@@ -6,9 +6,9 @@ import (
var cmdWG = &base.Command{
UsageLine: `{{.Exec}} wg [-i "private key (base64.StdEncoding)"]`,
Short: `Generate key pair for wireguard key exchange`,
Short: `Generate key pair for X25519 key exchange (WireGuard)`,
Long: `
Generate key pair for wireguard key exchange.
Generate key pair for X25519 key exchange (WireGuard).
Random: {{.Exec}} wg

View File

@@ -6,9 +6,9 @@ import (
var cmdX25519 = &base.Command{
UsageLine: `{{.Exec}} x25519 [-i "private key (base64.RawURLEncoding)"] [--std-encoding]`,
Short: `Generate key pair for x25519 key exchange`,
Short: `Generate key pair for X25519 key exchange (REALITY, VLESS Encryption)`,
Long: `
Generate key pair for x25519 key exchange.
Generate key pair for X25519 key exchange (REALITY, VLESS Encryption).
Random: {{.Exec}} x25519

View File

@@ -182,7 +182,9 @@ func getConfigFilePath(verbose bool) cmdarg.Arg {
}
if workingDir, err := os.Getwd(); err == nil {
configFile := filepath.Join(workingDir, "config.json")
suffixes := []string{".json", ".jsonc", ".toml", ".yaml", ".yml"}
for _, suffix := range suffixes {
configFile := filepath.Join(workingDir, "config"+suffix)
if fileExists(configFile) {
if verbose {
log.Println("Using default config: ", configFile)
@@ -190,6 +192,7 @@ func getConfigFilePath(verbose bool) cmdarg.Arg {
return cmdarg.Arg{configFile}
}
}
}
if configFile := platform.GetConfigurationPath(); fileExists(configFile) {
if verbose {

512
proxy/addons.pb.go Normal file
View File

@@ -0,0 +1,512 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc v4.23.1
// source: proxy/addons.proto
package proxy
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type SeedMode int32
const (
SeedMode_Unknown SeedMode = 0
SeedMode_PaddingOnly SeedMode = 1
SeedMode_PaddingPlusDelay SeedMode = 2
SeedMode_IndependentScheduler SeedMode = 3
)
// Enum value maps for SeedMode.
var (
SeedMode_name = map[int32]string{
0: "Unknown",
1: "PaddingOnly",
2: "PaddingPlusDelay",
3: "IndependentScheduler",
}
SeedMode_value = map[string]int32{
"Unknown": 0,
"PaddingOnly": 1,
"PaddingPlusDelay": 2,
"IndependentScheduler": 3,
}
)
func (x SeedMode) Enum() *SeedMode {
p := new(SeedMode)
*p = x
return p
}
func (x SeedMode) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (SeedMode) Descriptor() protoreflect.EnumDescriptor {
return file_proxy_addons_proto_enumTypes[0].Descriptor()
}
func (SeedMode) Type() protoreflect.EnumType {
return &file_proxy_addons_proto_enumTypes[0]
}
func (x SeedMode) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use SeedMode.Descriptor instead.
func (SeedMode) EnumDescriptor() ([]byte, []int) {
return file_proxy_addons_proto_rawDescGZIP(), []int{0}
}
type Addons struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Flow string `protobuf:"bytes,1,opt,name=Flow,proto3" json:"Flow,omitempty"`
Seed []byte `protobuf:"bytes,2,opt,name=Seed,proto3" json:"Seed,omitempty"`
Mode SeedMode `protobuf:"varint,3,opt,name=Mode,proto3,enum=xray.proxy.SeedMode" json:"Mode,omitempty"`
Duration string `protobuf:"bytes,4,opt,name=Duration,proto3" json:"Duration,omitempty"` // "0-8" means apply to number of packets, "1000b-" means start applying once both side exchange 1kb data, counting two-ways
Padding *PaddingConfig `protobuf:"bytes,5,opt,name=Padding,proto3" json:"Padding,omitempty"`
Delay *DelayConfig `protobuf:"bytes,6,opt,name=Delay,proto3" json:"Delay,omitempty"`
Scheduler *SchedulerConfig `protobuf:"bytes,7,opt,name=Scheduler,proto3" json:"Scheduler,omitempty"`
}
func (x *Addons) Reset() {
*x = Addons{}
if protoimpl.UnsafeEnabled {
mi := &file_proxy_addons_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Addons) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Addons) ProtoMessage() {}
func (x *Addons) ProtoReflect() protoreflect.Message {
mi := &file_proxy_addons_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Addons.ProtoReflect.Descriptor instead.
func (*Addons) Descriptor() ([]byte, []int) {
return file_proxy_addons_proto_rawDescGZIP(), []int{0}
}
func (x *Addons) GetFlow() string {
if x != nil {
return x.Flow
}
return ""
}
func (x *Addons) GetSeed() []byte {
if x != nil {
return x.Seed
}
return nil
}
func (x *Addons) GetMode() SeedMode {
if x != nil {
return x.Mode
}
return SeedMode_Unknown
}
func (x *Addons) GetDuration() string {
if x != nil {
return x.Duration
}
return ""
}
func (x *Addons) GetPadding() *PaddingConfig {
if x != nil {
return x.Padding
}
return nil
}
func (x *Addons) GetDelay() *DelayConfig {
if x != nil {
return x.Delay
}
return nil
}
func (x *Addons) GetScheduler() *SchedulerConfig {
if x != nil {
return x.Scheduler
}
return nil
}
type PaddingConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
RegularMin uint32 `protobuf:"varint,1,opt,name=RegularMin,proto3" json:"RegularMin,omitempty"`
RegularMax uint32 `protobuf:"varint,2,opt,name=RegularMax,proto3" json:"RegularMax,omitempty"`
LongMin uint32 `protobuf:"varint,3,opt,name=LongMin,proto3" json:"LongMin,omitempty"`
LongMax uint32 `protobuf:"varint,4,opt,name=LongMax,proto3" json:"LongMax,omitempty"`
}
func (x *PaddingConfig) Reset() {
*x = PaddingConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_proxy_addons_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PaddingConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PaddingConfig) ProtoMessage() {}
func (x *PaddingConfig) ProtoReflect() protoreflect.Message {
mi := &file_proxy_addons_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PaddingConfig.ProtoReflect.Descriptor instead.
func (*PaddingConfig) Descriptor() ([]byte, []int) {
return file_proxy_addons_proto_rawDescGZIP(), []int{1}
}
func (x *PaddingConfig) GetRegularMin() uint32 {
if x != nil {
return x.RegularMin
}
return 0
}
func (x *PaddingConfig) GetRegularMax() uint32 {
if x != nil {
return x.RegularMax
}
return 0
}
func (x *PaddingConfig) GetLongMin() uint32 {
if x != nil {
return x.LongMin
}
return 0
}
func (x *PaddingConfig) GetLongMax() uint32 {
if x != nil {
return x.LongMax
}
return 0
}
type DelayConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
IsRandom bool `protobuf:"varint,1,opt,name=IsRandom,proto3" json:"IsRandom,omitempty"`
MinMillis uint32 `protobuf:"varint,2,opt,name=MinMillis,proto3" json:"MinMillis,omitempty"`
MaxMillis uint32 `protobuf:"varint,3,opt,name=MaxMillis,proto3" json:"MaxMillis,omitempty"`
}
func (x *DelayConfig) Reset() {
*x = DelayConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_proxy_addons_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DelayConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DelayConfig) ProtoMessage() {}
func (x *DelayConfig) ProtoReflect() protoreflect.Message {
mi := &file_proxy_addons_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DelayConfig.ProtoReflect.Descriptor instead.
func (*DelayConfig) Descriptor() ([]byte, []int) {
return file_proxy_addons_proto_rawDescGZIP(), []int{2}
}
func (x *DelayConfig) GetIsRandom() bool {
if x != nil {
return x.IsRandom
}
return false
}
func (x *DelayConfig) GetMinMillis() uint32 {
if x != nil {
return x.MinMillis
}
return 0
}
func (x *DelayConfig) GetMaxMillis() uint32 {
if x != nil {
return x.MaxMillis
}
return 0
}
type SchedulerConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TimeoutMillis uint32 `protobuf:"varint,1,opt,name=TimeoutMillis,proto3" json:"TimeoutMillis,omitempty"` // original traffic will not be sent right away but when scheduler want to send or pending buffer times out
}
func (x *SchedulerConfig) Reset() {
*x = SchedulerConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_proxy_addons_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SchedulerConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SchedulerConfig) ProtoMessage() {}
func (x *SchedulerConfig) ProtoReflect() protoreflect.Message {
mi := &file_proxy_addons_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SchedulerConfig.ProtoReflect.Descriptor instead.
func (*SchedulerConfig) Descriptor() ([]byte, []int) {
return file_proxy_addons_proto_rawDescGZIP(), []int{3}
}
func (x *SchedulerConfig) GetTimeoutMillis() uint32 {
if x != nil {
return x.TimeoutMillis
}
return 0
}
var File_proxy_addons_proto protoreflect.FileDescriptor
var file_proxy_addons_proto_rawDesc = []byte{
0x0a, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x61, 0x64, 0x64, 0x6f, 0x6e, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79,
0x22, 0x95, 0x02, 0x0a, 0x06, 0x41, 0x64, 0x64, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x46,
0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x46, 0x6c, 0x6f, 0x77, 0x12,
0x12, 0x0a, 0x04, 0x53, 0x65, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x53,
0x65, 0x65, 0x64, 0x12, 0x28, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0e, 0x32, 0x14, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x53,
0x65, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x0a,
0x08, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x07, 0x50, 0x61, 0x64,
0x64, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x50, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x50, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x2d,
0x0a, 0x05, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x44, 0x65, 0x6c, 0x61, 0x79,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x39, 0x0a,
0x09, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x53, 0x63,
0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x53,
0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x22, 0x83, 0x01, 0x0a, 0x0d, 0x50, 0x61, 0x64,
0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x52, 0x65,
0x67, 0x75, 0x6c, 0x61, 0x72, 0x4d, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a,
0x52, 0x65, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x4d, 0x69, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x52, 0x65,
0x67, 0x75, 0x6c, 0x61, 0x72, 0x4d, 0x61, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a,
0x52, 0x65, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x4d, 0x61, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x4c, 0x6f,
0x6e, 0x67, 0x4d, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x4c, 0x6f, 0x6e,
0x67, 0x4d, 0x69, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x4c, 0x6f, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x18,
0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x4c, 0x6f, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x22, 0x65,
0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a,
0x08, 0x49, 0x73, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
0x08, 0x49, 0x73, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x12, 0x1c, 0x0a, 0x09, 0x4d, 0x69, 0x6e,
0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x4d, 0x69,
0x6e, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x4d, 0x61, 0x78, 0x4d, 0x69,
0x6c, 0x6c, 0x69, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x4d, 0x61, 0x78, 0x4d,
0x69, 0x6c, 0x6c, 0x69, 0x73, 0x22, 0x37, 0x0a, 0x0f, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c,
0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x69, 0x6d, 0x65,
0x6f, 0x75, 0x74, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x0d, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x2a, 0x58,
0x0a, 0x08, 0x53, 0x65, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e,
0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x61, 0x64, 0x64, 0x69,
0x6e, 0x67, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x61, 0x64, 0x64,
0x69, 0x6e, 0x67, 0x50, 0x6c, 0x75, 0x73, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x10, 0x02, 0x12, 0x18,
0x0a, 0x14, 0x49, 0x6e, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x68,
0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x10, 0x03, 0x42, 0x40, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x01, 0x5a, 0x1f, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72,
0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0xaa, 0x02, 0x0a,
0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (
file_proxy_addons_proto_rawDescOnce sync.Once
file_proxy_addons_proto_rawDescData = file_proxy_addons_proto_rawDesc
)
func file_proxy_addons_proto_rawDescGZIP() []byte {
file_proxy_addons_proto_rawDescOnce.Do(func() {
file_proxy_addons_proto_rawDescData = protoimpl.X.CompressGZIP(file_proxy_addons_proto_rawDescData)
})
return file_proxy_addons_proto_rawDescData
}
var file_proxy_addons_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_proxy_addons_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_proxy_addons_proto_goTypes = []interface{}{
(SeedMode)(0), // 0: xray.proxy.SeedMode
(*Addons)(nil), // 1: xray.proxy.Addons
(*PaddingConfig)(nil), // 2: xray.proxy.PaddingConfig
(*DelayConfig)(nil), // 3: xray.proxy.DelayConfig
(*SchedulerConfig)(nil), // 4: xray.proxy.SchedulerConfig
}
var file_proxy_addons_proto_depIdxs = []int32{
0, // 0: xray.proxy.Addons.Mode:type_name -> xray.proxy.SeedMode
2, // 1: xray.proxy.Addons.Padding:type_name -> xray.proxy.PaddingConfig
3, // 2: xray.proxy.Addons.Delay:type_name -> xray.proxy.DelayConfig
4, // 3: xray.proxy.Addons.Scheduler:type_name -> xray.proxy.SchedulerConfig
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_proxy_addons_proto_init() }
func file_proxy_addons_proto_init() {
if File_proxy_addons_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_proxy_addons_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Addons); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proxy_addons_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PaddingConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proxy_addons_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DelayConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proxy_addons_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SchedulerConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proxy_addons_proto_rawDesc,
NumEnums: 1,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_proxy_addons_proto_goTypes,
DependencyIndexes: file_proxy_addons_proto_depIdxs,
EnumInfos: file_proxy_addons_proto_enumTypes,
MessageInfos: file_proxy_addons_proto_msgTypes,
}.Build()
File_proxy_addons_proto = out.File
file_proxy_addons_proto_rawDesc = nil
file_proxy_addons_proto_goTypes = nil
file_proxy_addons_proto_depIdxs = nil
}

42
proxy/addons.proto Normal file
View File

@@ -0,0 +1,42 @@
syntax = "proto3";
package xray.proxy;
option csharp_namespace = "Xray.Proxy";
option go_package = "github.com/xtls/xray-core/proxy";
option java_package = "com.xray.proxy";
option java_multiple_files = true;
message Addons {
string Flow = 1;
bytes Seed = 2;
SeedMode Mode = 3;
string Duration = 4; // "0-8" means apply to number of packets, "1000b-" means start applying once both side exchange 1kb data, counting two-ways
PaddingConfig Padding = 5;
DelayConfig Delay = 6;
SchedulerConfig Scheduler = 7;
}
enum SeedMode {
Unknown = 0;
PaddingOnly = 1;
PaddingPlusDelay = 2;
IndependentScheduler = 3;
}
message PaddingConfig {
uint32 RegularMin = 1;
uint32 RegularMax = 2;
uint32 LongMin = 3;
uint32 LongMax = 4;
}
message DelayConfig {
bool IsRandom = 1;
uint32 MinMillis = 2;
uint32 MaxMillis = 3;
}
message SchedulerConfig {
uint32 TimeoutMillis = 1; // original traffic will not be sent right away but when scheduler want to send or pending buffer times out
// Other TBD
}

View File

@@ -4,6 +4,7 @@ import (
"context"
go_errors "errors"
"io"
"strings"
"sync"
"time"
@@ -168,11 +169,15 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
}
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, h.timeout)
terminate := func() {
cancel()
conn.Close()
}
timer := signal.CancelAfterInactivity(ctx, terminate, h.timeout)
defer timer.SetTimeout(0)
request := func() error {
defer conn.Close()
defer timer.SetTimeout(0)
for {
b, err := reader.ReadMessage()
if err == io.EOF {
@@ -190,24 +195,33 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
if len(h.blockTypes) > 0 {
for _, blocktype := range h.blockTypes {
if blocktype == int32(qType) {
if h.nonIPQuery == "reject" {
go h.rejectNonIPQuery(id, qType, domain, writer)
}
b.Release()
errors.LogInfo(ctx, "blocked type ", qType, " query for domain ", domain)
if h.nonIPQuery == "reject" {
err := h.rejectNonIPQuery(id, qType, domain, writer)
if err != nil {
return err
}
}
return nil
}
}
}
if isIPQuery {
go h.handleIPQuery(id, qType, domain, writer)
b.Release()
go h.handleIPQuery(id, qType, domain, writer, timer)
continue
}
if isIPQuery || h.nonIPQuery == "drop" {
if h.nonIPQuery == "drop" {
b.Release()
continue
}
if h.nonIPQuery == "reject" {
go h.rejectNonIPQuery(id, qType, domain, writer)
b.Release()
err := h.rejectNonIPQuery(id, qType, domain, writer)
if err != nil {
return err
}
continue
}
}
@@ -219,6 +233,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
}
response := func() error {
defer timer.SetTimeout(0)
for {
b, err := connReader.ReadMessage()
if err == io.EOF {
@@ -244,7 +259,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
return nil
}
func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter) {
func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter, timer *signal.ActivityTimer) {
var ips []net.IP
var err error
@@ -319,16 +334,21 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string,
if err != nil {
errors.LogInfoInner(context.Background(), err, "pack message")
b.Release()
return
timer.SetTimeout(0)
}
b.Resize(0, int32(len(msgBytes)))
if err := writer.WriteMessage(b); err != nil {
errors.LogInfoInner(context.Background(), err, "write IP answer")
timer.SetTimeout(0)
}
}
func (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter) {
func (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter) error {
domainT := strings.TrimSuffix(domain, ".")
if domainT == "" {
return errors.New("empty domain name")
}
b := buf.New()
rawBytes := b.Extend(buf.Size)
builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{
@@ -349,20 +369,22 @@ func (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain stri
if err != nil {
errors.LogInfo(context.Background(), "unexpected domain ", domain, " when building reject message: ", err)
b.Release()
return
return err
}
msgBytes, err := builder.Finish()
if err != nil {
errors.LogInfoInner(context.Background(), err, "pack reject message")
b.Release()
return
return err
}
b.Resize(0, int32(len(msgBytes)))
if err := writer.WriteMessage(b); err != nil {
errors.LogInfoInner(context.Background(), err, "write reject answer")
return err
}
return nil
}
type outboundConn struct {
@@ -371,6 +393,7 @@ type outboundConn struct {
conn net.Conn
connReady chan struct{}
closed bool
}
func (c *outboundConn) dial() error {
@@ -385,12 +408,16 @@ func (c *outboundConn) dial() error {
func (c *outboundConn) Write(b []byte) (int, error) {
c.access.Lock()
if c.closed {
c.access.Unlock()
return 0, errors.New("outbound connection closed")
}
if c.conn == nil {
if err := c.dial(); err != nil {
c.access.Unlock()
errors.LogWarningInner(context.Background(), err, "failed to dial outbound connection")
return len(b), nil
return 0, err
}
}
@@ -400,24 +427,27 @@ func (c *outboundConn) Write(b []byte) (int, error) {
}
func (c *outboundConn) Read(b []byte) (int, error) {
var conn net.Conn
c.access.Lock()
conn = c.conn
if c.closed {
c.access.Unlock()
return 0, io.EOF
}
if conn == nil {
if c.conn == nil {
c.access.Unlock()
_, open := <-c.connReady
if !open {
return 0, io.EOF
}
conn = c.conn
return c.conn.Read(b)
}
return conn.Read(b)
c.access.Unlock()
return c.conn.Read(b)
}
func (c *outboundConn) Close() error {
c.access.Lock()
c.closed = true
close(c.connReady)
if c.conn != nil {
c.conn.Close()

View File

@@ -2,10 +2,8 @@ package dokodemo
import (
"context"
"runtime"
"strconv"
"strings"
"sync/atomic"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
@@ -14,11 +12,10 @@ import (
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/policy"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet/stat"
"github.com/xtls/xray-core/transport/internet/tls"
)
@@ -144,39 +141,11 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
})
errors.LogInfo(ctx, "received request for ", conn.RemoteAddr())
plcy := d.policy()
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, plcy.Timeouts.ConnectionIdle)
if inbound != nil {
inbound.Timer = timer
}
ctx = policy.ContextWithBufferPolicy(ctx, plcy.Buffer)
link, err := dispatcher.Dispatch(ctx, dest)
if err != nil {
return errors.New("failed to dispatch request").Base(err)
}
requestCount := int32(1)
requestDone := func() error {
defer func() {
if atomic.AddInt32(&requestCount, -1) == 0 {
timer.SetTimeout(plcy.Timeouts.DownlinkOnly)
}
}()
var reader buf.Reader
if dest.Network == net.Network_UDP {
reader = buf.NewPacketReader(conn)
} else {
if dest.Network == net.Network_TCP {
reader = buf.NewReader(conn)
}
if err := buf.Copy(reader, link.Writer, buf.UpdateActivity(timer)); err != nil {
return errors.New("failed to transport request").Base(err)
}
return nil
} else {
reader = buf.NewPacketReader(conn)
}
var writer buf.Writer
@@ -208,72 +177,17 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
return err
}
writer = NewPacketWriter(pConn, &dest, mark, back)
defer func() {
runtime.Gosched()
common.Interrupt(link.Reader) // maybe duplicated
runtime.Gosched()
writer.(*PacketWriter).Close() // close fake UDP conns
}()
/*
sockopt := &internet.SocketConfig{
Tproxy: internet.SocketConfig_TProxy,
}
if dest.Address.Family().IsIP() {
sockopt.BindAddress = dest.Address.IP()
sockopt.BindPort = uint32(dest.Port)
}
if d.sockopt != nil {
sockopt.Mark = d.sockopt.Mark
}
tConn, err := internet.DialSystem(ctx, net.DestinationFromAddr(conn.RemoteAddr()), sockopt)
if err != nil {
return err
}
defer tConn.Close()
writer = &buf.SequentialWriter{Writer: tConn}
tReader := buf.NewPacketReader(tConn)
requestCount++
tproxyRequest = func() error {
defer func() {
if atomic.AddInt32(&requestCount, -1) == 0 {
timer.SetTimeout(plcy.Timeouts.DownlinkOnly)
}
}()
if err := buf.Copy(tReader, link.Writer, buf.UpdateActivity(timer)); err != nil {
return errors.New("failed to transport request (TPROXY conn)").Base(err)
}
return nil
}
*/
defer writer.(*PacketWriter).Close() // close fake UDP conns
}
}
responseDone := func() error {
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
if network == net.Network_UDP && destinationOverridden {
buf.Copy(link.Reader, writer) // respect upload's timeout
return nil
if err := dispatcher.DispatchLink(ctx, dest, &transport.Link{
Reader: reader,
Writer: writer},
); err != nil {
return errors.New("failed to dispatch request").Base(err)
}
if err := buf.Copy(link.Reader, writer, buf.UpdateActivity(timer)); err != nil {
return errors.New("failed to transport response").Base(err)
}
return nil
}
if err := task.Run(ctx,
task.OnSuccess(func() error { return task.Run(ctx, requestDone) }, task.Close(link.Writer)),
responseDone); err != nil {
runtime.Gosched()
common.Interrupt(link.Writer)
runtime.Gosched()
common.Interrupt(link.Reader)
return errors.New("connection ends").Base(err)
}
return nil
return nil // Unlike Dispatch(), DispatchLink() will not return until the outbound finishes Process()
}
func NewPacketWriter(conn net.PacketConn, d *net.Destination, mark int, back *net.UDPAddr) buf.Writer {

View File

@@ -26,7 +26,6 @@ import (
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/stat"
"github.com/xtls/xray-core/transport/internet/tls"
)
var useSplice bool
@@ -73,7 +72,7 @@ func isValidAddress(addr *net.IPOrDomain) bool {
}
a := addr.AsAddress()
return a != net.AnyIP
return a != net.AnyIP && a != net.AnyIPv6
}
// Process implements proxy.Outbound.
@@ -212,17 +211,15 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
responseDone := func() error {
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
if destination.Network == net.Network_TCP {
if destination.Network == net.Network_TCP && useSplice && proxy.IsRAWTransportWithoutSecurity(conn) { // it would be tls conn in special use case of MITM, we need to let link handle traffic
var writeConn net.Conn
var inTimer *signal.ActivityTimer
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil && useSplice {
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil {
writeConn = inbound.Conn
inTimer = inbound.Timer
}
if !isTLSConn(conn) { // it would be tls conn in special use case of MITM, we need to let link handle traffic
return proxy.CopyRawConnIfExist(ctx, conn, writeConn, link.Writer, timer, inTimer)
}
}
var reader buf.Reader
if destination.Network == net.Network_TCP {
reader = buf.NewReader(conn)
@@ -246,22 +243,6 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
return nil
}
func isTLSConn(conn stat.Connection) bool {
if conn != nil {
statConn, ok := conn.(*stat.CounterConnection)
if ok {
conn = statConn.Connection
}
if _, ok := conn.(*tls.Conn); ok {
return true
}
if _, ok := conn.(*tls.UConn); ok {
return true
}
}
return false
}
func NewPacketReader(conn net.Conn, UDPOverride net.Destination, DialDest net.Destination) buf.Reader {
iConn := conn
statConn, ok := iConn.(*stat.CounterConnection)
@@ -418,7 +399,7 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
}
}
}
destAddr, _ := net.ResolveUDPAddr("udp", b.UDP.NetAddr())
destAddr := b.UDP.RawNetAddr()
if destAddr == nil {
b.Release()
continue

View File

@@ -18,11 +18,12 @@ import (
"github.com/xtls/xray-core/common/protocol"
http_proto "github.com/xtls/xray-core/common/protocol/http"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/policy"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/proxy"
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet/stat"
)
@@ -95,6 +96,9 @@ func (s *Server) ProcessWithFirstbyte(ctx context.Context, network net.Network,
inbound.User = &protocol.MemoryUser{
Level: s.config.UserLevel,
}
if !proxy.IsRAWTransportWithoutSecurity(conn) {
inbound.CanSpliceCopy = 3
}
var reader *bufio.Reader
if len(firstbyte) > 0 {
readerWithoutFirstbyte := bufio.NewReaderSize(readerOnly{conn}, buf.Size)
@@ -169,62 +173,31 @@ Start:
return err
}
func (s *Server) handleConnect(ctx context.Context, _ *http.Request, reader *bufio.Reader, conn stat.Connection, dest net.Destination, dispatcher routing.Dispatcher, inbound *session.Inbound) error {
func (s *Server) handleConnect(ctx context.Context, _ *http.Request, buffer *bufio.Reader, conn stat.Connection, dest net.Destination, dispatcher routing.Dispatcher, inbound *session.Inbound) error {
_, err := conn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n"))
if err != nil {
return errors.New("failed to write back OK response").Base(err)
}
plcy := s.policy()
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, plcy.Timeouts.ConnectionIdle)
if inbound != nil {
inbound.Timer = timer
}
ctx = policy.ContextWithBufferPolicy(ctx, plcy.Buffer)
link, err := dispatcher.Dispatch(ctx, dest)
reader := buf.NewReader(conn)
if buffer.Buffered() > 0 {
payload, err := buf.ReadFrom(io.LimitReader(buffer, int64(buffer.Buffered())))
if err != nil {
return err
}
if reader.Buffered() > 0 {
payload, err := buf.ReadFrom(io.LimitReader(reader, int64(reader.Buffered())))
if err != nil {
return err
}
if err := link.Writer.WriteMultiBuffer(payload); err != nil {
return err
}
reader = nil
reader = &buf.BufferedReader{Reader: reader, Buffer: payload}
buffer = nil
}
requestDone := func() error {
defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly)
return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer))
}
responseDone := func() error {
if inbound.CanSpliceCopy == 2 {
inbound.CanSpliceCopy = 1
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
v2writer := buf.NewWriter(conn)
if err := buf.Copy(link.Reader, v2writer, buf.UpdateActivity(timer)); err != nil {
return err
}
return nil
if err := dispatcher.DispatchLink(ctx, dest, &transport.Link{
Reader: reader,
Writer: buf.NewWriter(conn)},
); err != nil {
return errors.New("failed to dispatch request").Base(err)
}
closeWriter := task.OnSuccess(requestDone, task.Close(link.Writer))
if err := task.Run(ctx, closeWriter, responseDone); err != nil {
common.Interrupt(link.Reader)
common.Interrupt(link.Writer)
return errors.New("connection ends").Base(err)
}
return nil
}

View File

@@ -13,6 +13,7 @@ import (
"math/big"
"runtime"
"strconv"
"strings"
"time"
"github.com/pires/go-proxyproto"
@@ -25,6 +26,7 @@ import (
"github.com/xtls/xray-core/common/signal"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/features/stats"
"github.com/xtls/xray-core/proxy/vless/encryption"
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/reality"
@@ -101,6 +103,11 @@ type GetOutbound interface {
// It is used by XTLS to determine if switch to raw copy mode, It is used by Vision to calculate padding
type TrafficState struct {
UserUUID []byte
StartTime time.Time
ByteSent int64
ByteReceived int64
NumberOfPacketSent int
NumberOfPacketReceived int
NumberOfPacketToFilter int
EnableXtls bool
IsTLS12orAbove bool
@@ -137,9 +144,14 @@ type OutboundState struct {
UplinkWriterDirectCopy bool
}
func NewTrafficState(userUUID []byte) *TrafficState {
return &TrafficState{
func NewTrafficState(userUUID []byte, flow string) *TrafficState {
var state = TrafficState{
UserUUID: userUUID,
StartTime: time.Time{},
ByteSent: 0,
ByteReceived: 0,
NumberOfPacketSent: 0,
NumberOfPacketReceived: 0,
NumberOfPacketToFilter: 8,
EnableXtls: false,
IsTLS12orAbove: false,
@@ -147,49 +159,72 @@ func NewTrafficState(userUUID []byte) *TrafficState {
Cipher: 0,
RemainingServerHello: -1,
Inbound: InboundState{
WithinPaddingBuffers: true,
UplinkReaderDirectCopy: false,
RemainingCommand: -1,
RemainingContent: -1,
RemainingPadding: -1,
CurrentCommand: 0,
IsPadding: true,
DownlinkWriterDirectCopy: false,
IsPadding: true,
},
Outbound: OutboundState{
WithinPaddingBuffers: true,
DownlinkReaderDirectCopy: false,
RemainingCommand: -1,
RemainingContent: -1,
RemainingPadding: -1,
CurrentCommand: 0,
IsPadding: true,
UplinkWriterDirectCopy: false,
IsPadding: true,
},
}
if len(flow) > 0 {
state.Inbound.WithinPaddingBuffers = true;
state.Outbound.WithinPaddingBuffers = true;
}
return &state
}
// VisionReader is used to read xtls vision protocol
// VisionReader is used to read seed protocol
// Note Vision probably only make sense as the inner most layer of reader, since it need assess traffic state from origin proxy traffic
type VisionReader struct {
buf.Reader
addons *Addons
trafficState *TrafficState
ctx context.Context
isUplink bool
conn net.Conn
input *bytes.Reader
rawInput *bytes.Buffer
ob *session.Outbound
// internal
directReadCounter stats.Counter
}
func NewVisionReader(reader buf.Reader, state *TrafficState, isUplink bool, context context.Context) *VisionReader {
func NewVisionReader(reader buf.Reader, addon *Addons, trafficState *TrafficState, isUplink bool, ctx context.Context, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, ob *session.Outbound) *VisionReader {
return &VisionReader{
Reader: reader,
trafficState: state,
ctx: context,
addons: addon,
trafficState: trafficState,
ctx: ctx,
isUplink: isUplink,
conn: conn,
input: input,
rawInput: rawInput,
ob: ob,
}
}
func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
buffer, err := w.Reader.ReadMultiBuffer()
if !buffer.IsEmpty() {
if buffer.IsEmpty() {
return buffer, err
}
if w.trafficState.StartTime.IsZero() {
w.trafficState.StartTime = time.Now()
}
w.trafficState.ByteReceived += int64(buffer.Len())
var withinPaddingBuffers *bool
var remainingContent *int32
var remainingPadding *int32
@@ -209,7 +244,14 @@ func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
switchToDirectCopy = &w.trafficState.Outbound.DownlinkReaderDirectCopy
}
if *withinPaddingBuffers || w.trafficState.NumberOfPacketToFilter > 0 {
if *switchToDirectCopy {
if w.directReadCounter != nil {
w.directReadCounter.Add(int64(buffer.Len()))
}
return buffer, err
}
if *withinPaddingBuffers || w.trafficState.NumberOfPacketReceived <= 8 || !ShouldStopSeed(w.addons, w.trafficState) {
mb2 := make(buf.MultiBuffer, 0, len(buffer))
for _, b := range buffer {
newbuffer := XtlsUnpadding(b, w.trafficState, w.isUplink, w.ctx)
@@ -229,39 +271,73 @@ func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
errors.LogInfo(w.ctx, "XtlsRead unknown command ", *currentCommand, buffer.Len())
}
}
w.trafficState.NumberOfPacketReceived += len(buffer)
if w.trafficState.NumberOfPacketToFilter > 0 {
XtlsFilterTls(buffer, w.trafficState, w.ctx)
}
if *switchToDirectCopy {
// XTLS Vision processes TLS-like conn's input and rawInput
if inputBuffer, err := buf.ReadFrom(w.input); err == nil && !inputBuffer.IsEmpty() {
buffer, _ = buf.MergeMulti(buffer, inputBuffer)
}
if rawInputBuffer, err := buf.ReadFrom(w.rawInput); err == nil && !rawInputBuffer.IsEmpty() {
buffer, _ = buf.MergeMulti(buffer, rawInputBuffer)
}
*w.input = bytes.Reader{} // release memory
w.input = nil
*w.rawInput = bytes.Buffer{} // release memory
w.rawInput = nil
if inbound := session.InboundFromContext(w.ctx); inbound != nil && inbound.Conn != nil {
if w.isUplink && inbound.CanSpliceCopy == 2 {
inbound.CanSpliceCopy = 1
}
if !w.isUplink && w.ob != nil && w.ob.CanSpliceCopy == 2 { // ob need to be passed in due to context can have more than one ob
w.ob.CanSpliceCopy = 1
}
}
readerConn, readCounter, _ := UnwrapRawConn(w.conn)
w.directReadCounter = readCounter
w.Reader = buf.NewReader(readerConn)
}
return buffer, err
}
// VisionWriter is used to write xtls vision protocol
// VisionWriter is used to write seed protocol
// Note Vision probably only make sense as the inner most layer of writer, since it need assess traffic state from origin proxy traffic
type VisionWriter struct {
buf.Writer
addons *Addons
trafficState *TrafficState
ctx context.Context
writeOnceUserUUID []byte
isUplink bool
conn net.Conn
ob *session.Outbound
// internal
writeOnceUserUUID *[]byte
directWriteCounter stats.Counter
scheduler *Scheduler
}
func NewVisionWriter(writer buf.Writer, state *TrafficState, isUplink bool, context context.Context) *VisionWriter {
w := make([]byte, len(state.UserUUID))
copy(w, state.UserUUID)
func NewVisionWriter(writer buf.Writer, addon *Addons, trafficState *TrafficState, isUplink bool, ctx context.Context, conn net.Conn, ob *session.Outbound) *VisionWriter {
w := make([]byte, len(trafficState.UserUUID))
copy(w, trafficState.UserUUID)
return &VisionWriter{
Writer: writer,
trafficState: state,
ctx: context,
writeOnceUserUUID: w,
addons: addon,
trafficState: trafficState,
ctx: ctx,
writeOnceUserUUID: &w,
isUplink: isUplink,
conn: conn,
ob: ob,
scheduler: NewScheduler(writer, addon, trafficState, &w, ctx),
}
}
func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
if w.trafficState.NumberOfPacketToFilter > 0 {
XtlsFilterTls(mb, w.trafficState, w.ctx)
}
var isPadding *bool
var switchToDirectCopy *bool
if w.isUplink {
@@ -271,11 +347,34 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
isPadding = &w.trafficState.Inbound.IsPadding
switchToDirectCopy = &w.trafficState.Inbound.DownlinkWriterDirectCopy
}
if *isPadding {
if len(mb) == 1 && mb[0] == nil {
mb[0] = XtlsPadding(nil, CommandPaddingContinue, &w.writeOnceUserUUID, true, w.ctx) // we do a long padding to hide vless header
return w.Writer.WriteMultiBuffer(mb)
if *switchToDirectCopy {
if inbound := session.InboundFromContext(w.ctx); inbound != nil {
if !w.isUplink && inbound.CanSpliceCopy == 2 {
inbound.CanSpliceCopy = 1
}
if w.isUplink && w.ob != nil && w.ob.CanSpliceCopy == 2 {
w.ob.CanSpliceCopy = 1
}
}
rawConn, _, writerCounter := UnwrapRawConn(w.conn)
w.Writer = buf.NewWriter(rawConn)
w.directWriteCounter = writerCounter
*switchToDirectCopy = false
}
if !mb.IsEmpty() && w.directWriteCounter != nil {
w.directWriteCounter.Add(int64(mb.Len()))
}
w.trafficState.NumberOfPacketSent += len(mb)
if w.trafficState.NumberOfPacketToFilter > 0 {
XtlsFilterTls(mb, w.trafficState, w.ctx)
}
if *isPadding && ShouldStartSeed(w.addons, w.trafficState) {
if len(mb) == 1 && mb[0] == nil {
mb[0] = XtlsPadding(nil, CommandPaddingContinue, w.writeOnceUserUUID, true, w.addons, w.ctx) // we do a long padding to hide vless header
} else {
mb = ReshapeMultiBuffer(w.ctx, mb)
longPadding := w.trafficState.IsTLS
for i, b := range mb {
@@ -284,19 +383,21 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
*switchToDirectCopy = true
}
var command byte = CommandPaddingContinue
if i == len(mb)-1 {
command = CommandPaddingEnd
if i == len(mb) - 1 {
if w.trafficState.EnableXtls {
command = CommandPaddingDirect
*isPadding = false
} else if ShouldStopSeed(w.addons, w.trafficState) {
command = CommandPaddingEnd
*isPadding = false
}
}
mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, true, w.ctx)
*isPadding = false // padding going to end
mb[i] = XtlsPadding(b, command, w.writeOnceUserUUID, true, w.addons, w.ctx)
longPadding = false
continue
} else if !w.trafficState.IsTLS12orAbove && w.trafficState.NumberOfPacketToFilter <= 1 { // For compatibility with earlier vision receiver, we finish padding 1 packet early
} else if !w.trafficState.IsTLS12orAbove && ShouldStopSeed(w.addons, w.trafficState) {
*isPadding = false
mb[i] = XtlsPadding(b, CommandPaddingEnd, &w.writeOnceUserUUID, longPadding, w.ctx)
mb[i] = XtlsPadding(b, CommandPaddingEnd, w.writeOnceUserUUID, longPadding, w.addons, w.ctx)
break
}
var command byte = CommandPaddingContinue
@@ -306,10 +407,22 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
command = CommandPaddingDirect
}
}
mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, longPadding, w.ctx)
mb[i] = XtlsPadding(b, command, w.writeOnceUserUUID, longPadding, w.addons, w.ctx)
}
}
return w.Writer.WriteMultiBuffer(mb)
}
w.trafficState.ByteSent += int64(mb.Len())
if w.trafficState.StartTime.IsZero() {
w.trafficState.StartTime = time.Now()
}
w.scheduler.Buffer <- mb
if w.addons.Scheduler == nil {
w.scheduler.Trigger <- -1 // send all buffers
}
if len(w.scheduler.Error) > 0 {
return <-w.scheduler.Error
}
return nil
}
// ReshapeMultiBuffer prepare multi buffer for padding structure (max 21 bytes)
@@ -348,24 +461,24 @@ func ReshapeMultiBuffer(ctx context.Context, buffer buf.MultiBuffer) buf.MultiBu
}
// XtlsPadding add padding to eliminate length signature during tls handshake
func XtlsPadding(b *buf.Buffer, command byte, userUUID *[]byte, longPadding bool, ctx context.Context) *buf.Buffer {
func XtlsPadding(b *buf.Buffer, command byte, userUUID *[]byte, longPadding bool, addons *Addons, ctx context.Context) *buf.Buffer {
var contentLen int32 = 0
var paddingLen int32 = 0
if b != nil {
contentLen = b.Len()
}
if contentLen < 900 && longPadding {
l, err := rand.Int(rand.Reader, big.NewInt(500))
if contentLen < int32(addons.Padding.LongMin) && longPadding {
l, err := rand.Int(rand.Reader, big.NewInt(int64(addons.Padding.LongMax - addons.Padding.LongMin)))
if err != nil {
errors.LogDebugInner(ctx, err, "failed to generate padding")
}
paddingLen = int32(l.Int64()) + 900 - contentLen
paddingLen = int32(l.Int64()) + int32(addons.Padding.LongMin) - contentLen
} else {
l, err := rand.Int(rand.Reader, big.NewInt(256))
l, err := rand.Int(rand.Reader, big.NewInt(int64(addons.Padding.RegularMax - addons.Padding.RegularMin)))
if err != nil {
errors.LogDebugInner(ctx, err, "failed to generate padding")
}
paddingLen = int32(l.Int64())
paddingLen = int32(l.Int64()) + int32(addons.Padding.RegularMin)
}
if paddingLen > buf.Size-21-contentLen {
paddingLen = buf.Size - 21 - contentLen
@@ -524,16 +637,24 @@ func XtlsFilterTls(buffer buf.MultiBuffer, trafficState *TrafficState, ctx conte
}
}
// UnwrapRawConn support unwrap stats, tls, utls, reality, proxyproto, uds-wrapper conn and get raw tcp/uds conn from it
// UnwrapRawConn support unwrap encryption, stats, tls, utls, reality, proxyproto, uds-wrapper conn and get raw tcp/uds conn from it
func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
var readCounter, writerCounter stats.Counter
if conn != nil {
statConn, ok := conn.(*stat.CounterConnection)
if ok {
isEncryption := false
if commonConn, ok := conn.(*encryption.CommonConn); ok {
conn = commonConn.Conn
isEncryption = true
}
if xorConn, ok := conn.(*encryption.XorConn); ok {
return xorConn, nil, nil // full-random xorConn should not be penetrated
}
if statConn, ok := conn.(*stat.CounterConnection); ok {
conn = statConn.Connection
readCounter = statConn.ReadCounter
writerCounter = statConn.WriteCounter
}
if !isEncryption { // avoids double penetration
if xc, ok := conn.(*tls.Conn); ok {
conn = xc.NetConn()
} else if utlsConn, ok := conn.(*tls.UConn); ok {
@@ -543,6 +664,7 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
} else if realityUConn, ok := conn.(*reality.UConn); ok {
conn = realityUConn.NetConn()
}
}
if pc, ok := conn.(*proxyproto.Conn); ok {
conn = pc.Raw()
// 8192 > 4096, there is no need to process pc's bufReader
@@ -626,15 +748,76 @@ func CopyRawConnIfExist(ctx context.Context, readerConn net.Conn, writerConn net
}
}
if err != nil {
if errors.Cause(err) == io.EOF {
return nil
}
return err
}
}
}
func readV(ctx context.Context, reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, readCounter stats.Counter) error {
errors.LogInfo(ctx, "CopyRawConn readv")
errors.LogInfo(ctx, "CopyRawConn (maybe) readv")
if err := buf.Copy(reader, writer, buf.UpdateActivity(timer), buf.AddToStatCounter(readCounter)); err != nil {
return errors.New("failed to process response").Base(err)
}
return nil
}
func IsRAWTransportWithoutSecurity(conn stat.Connection) bool {
iConn := conn
if statConn, ok := iConn.(*stat.CounterConnection); ok {
iConn = statConn.Connection
}
_, ok1 := iConn.(*proxyproto.Conn)
_, ok2 := iConn.(*net.TCPConn)
_, ok3 := iConn.(*internet.UnixConnWrapper)
return ok1 || ok2 || ok3
}
func ShouldStartSeed(addons *Addons, trafficState *TrafficState) bool {
if len(addons.Duration) == 0 || len(strings.Split(addons.Duration, "-")) < 2 {
return false
}
start := strings.ToLower(strings.Split(addons.Duration, "-")[0])
if len(start) == 0 {
return true
}
if strings.Contains(start, "b") {
start = strings.TrimRight(start, "b")
i, err := strconv.Atoi(start)
if err == nil && i <= int(trafficState.ByteSent + trafficState.ByteSent) {
return true
}
} else {
i, err := strconv.Atoi(start)
if err == nil && i <= trafficState.NumberOfPacketSent + trafficState.NumberOfPacketReceived {
return true
}
}
return false
}
func ShouldStopSeed(addons *Addons, trafficState *TrafficState) bool {
if len(addons.Duration) == 0 || len(strings.Split(addons.Duration, "-")) < 2 {
return true
}
start := strings.ToLower(strings.Split(addons.Duration, "-")[1])
if len(start) == 0 { // infinite
return false
}
if strings.Contains(start, "b") {
start = strings.TrimRight(start, "b")
i, err := strconv.Atoi(start)
if err == nil && i > int(trafficState.ByteSent + trafficState.ByteSent) {
return false
}
} else {
i, err := strconv.Atoi(start)
if err == nil && i > trafficState.NumberOfPacketSent + trafficState.NumberOfPacketReceived {
return false
}
}
return true
}

107
proxy/scheduler.go Normal file
View File

@@ -0,0 +1,107 @@
package proxy
import (
"context"
"crypto/rand"
"math/big"
"sync"
"time"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
)
type Scheduler struct {
Buffer chan buf.MultiBuffer
Trigger chan int
Error chan error
closed chan int
bufferReadLock *sync.Mutex
writer buf.Writer
addons *Addons
trafficState *TrafficState
writeOnceUserUUID *[]byte
ctx context.Context
}
func NewScheduler(w buf.Writer, addon *Addons, state *TrafficState, userUUID *[]byte, context context.Context) *Scheduler {
var s = Scheduler{
Buffer: make(chan buf.MultiBuffer, 100),
Trigger: make(chan int),
Error: make(chan error, 100),
closed: make(chan int),
bufferReadLock: new(sync.Mutex),
writer: w,
addons: addon,
trafficState: state,
writeOnceUserUUID: userUUID,
ctx: context,
}
go s.mainLoop()
if s.addons.Scheduler != nil {
go s.exampleIndependentScheduler()
}
return &s
}
func(s *Scheduler) mainLoop() {
for trigger := range s.Trigger {
if len(s.closed) > 0 {
return
}
go func() { // each trigger has independent delay, trigger does not block
var d = 0 * time.Millisecond
if s.addons.Delay != nil {
l, err := rand.Int(rand.Reader, big.NewInt(int64(s.addons.Delay.MaxMillis - s.addons.Delay.MinMillis)))
if err != nil {
errors.LogWarningInner(s.ctx, err, "failed to generate delay", trigger)
}
d = time.Duration(uint32(l.Int64()) + s.addons.Delay.MinMillis) * time.Millisecond
time.Sleep(d)
}
s.bufferReadLock.Lock() // guard against multiple trigger threads
var sending = len(s.Buffer)
if sending > 0 {
errors.LogDebug(s.ctx, "Scheduler Trigger for ", sending, " buffer(s) with ", d, " ", trigger)
for i := 0; i<sending; i++ {
err := s.writer.WriteMultiBuffer(<-s.Buffer)
if err != nil {
s.Error <- err
s.closed <- 1
return
}
}
} else if trigger > 0 && (s.trafficState.Inbound.IsPadding || s.trafficState.Outbound.IsPadding) && ShouldStartSeed(s.addons, s.trafficState) && !ShouldStopSeed(s.addons, s.trafficState) {
errors.LogDebug(s.ctx, "Scheduler Trigger for fake buffer with ", d, " ", trigger)
s.trafficState.NumberOfPacketSent += 1
mb := make(buf.MultiBuffer, 1)
mb[0] = XtlsPadding(nil, CommandPaddingContinue, s.writeOnceUserUUID, true, s.addons, s.ctx)
s.trafficState.ByteSent += int64(mb.Len())
if s.trafficState.StartTime.IsZero() {
s.trafficState.StartTime = time.Now()
}
err := s.writer.WriteMultiBuffer(mb)
if err != nil {
s.Error <- err
s.closed <- 1
return
}
if buffered, ok := s.writer.(*buf.BufferedWriter); ok {
buffered.SetBuffered(false)
}
}
s.bufferReadLock.Unlock()
}()
}
}
func(s *Scheduler) exampleIndependentScheduler() {
for {
if len(s.closed) > 0 {
return
}
s.Trigger <- 1 // send fake buffer if no pending
time.Sleep(500 * time.Millisecond)
}
}

View File

@@ -104,12 +104,12 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con
func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dispatcher routing.Dispatcher) error {
udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
request := protocol.RequestHeaderFromContext(ctx)
payload := packet.Payload
if request == nil {
payload.Release()
return
}
payload := packet.Payload
if payload.UDP != nil {
request = &protocol.RequestHeader{
User: request.User,
@@ -124,9 +124,9 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis
errors.LogWarningInner(ctx, err, "failed to encode UDP packet")
return
}
defer data.Release()
conn.Write(data.Bytes())
data.Release()
})
defer udpServer.RemoveRay()

View File

@@ -14,12 +14,12 @@ import (
"github.com/xtls/xray-core/common/protocol"
udp_proto "github.com/xtls/xray-core/common/protocol/udp"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/policy"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/proxy"
"github.com/xtls/xray-core/proxy/http"
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet/stat"
"github.com/xtls/xray-core/transport/internet/udp"
)
@@ -75,6 +75,9 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con
inbound.User = &protocol.MemoryUser{
Level: s.config.UserLevel,
}
if !proxy.IsRAWTransportWithoutSecurity(conn) {
inbound.CanSpliceCopy = 3
}
switch network {
case net.Network_TCP:
@@ -154,8 +157,16 @@ func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatche
Reason: "",
})
}
return s.transport(ctx, reader, conn, dest, dispatcher, inbound)
if inbound.CanSpliceCopy == 2 {
inbound.CanSpliceCopy = 1
}
if err := dispatcher.DispatchLink(ctx, dest, &transport.Link{
Reader: reader,
Writer: buf.NewWriter(conn)},
); err != nil {
return errors.New("failed to dispatch request").Base(err)
}
return nil
}
if request.Command == protocol.RequestCommandUDP {
@@ -174,52 +185,6 @@ func (*Server) handleUDP(c io.Reader) error {
return common.Error2(io.Copy(buf.DiscardBytes, c))
}
func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writer, dest net.Destination, dispatcher routing.Dispatcher, inbound *session.Inbound) error {
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, s.policy().Timeouts.ConnectionIdle)
if inbound != nil {
inbound.Timer = timer
}
plcy := s.policy()
ctx = policy.ContextWithBufferPolicy(ctx, plcy.Buffer)
link, err := dispatcher.Dispatch(ctx, dest)
if err != nil {
return err
}
requestDone := func() error {
defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly)
if err := buf.Copy(buf.NewReader(reader), link.Writer, buf.UpdateActivity(timer)); err != nil {
return errors.New("failed to transport all TCP request").Base(err)
}
return nil
}
responseDone := func() error {
inbound.CanSpliceCopy = 1
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
v2writer := buf.NewWriter(writer)
if err := buf.Copy(link.Reader, v2writer, buf.UpdateActivity(timer)); err != nil {
return errors.New("failed to transport all TCP response").Base(err)
}
return nil
}
requestDonePost := task.OnSuccess(requestDone, task.Close(link.Writer))
if err := task.Run(ctx, requestDonePost, responseDone); err != nil {
common.Interrupt(link.Reader)
common.Interrupt(link.Writer)
return errors.New("connection ends").Base(err)
}
return nil
}
func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dispatcher routing.Dispatcher) error {
if s.udpFilter != nil && !s.udpFilter.Check(conn.RemoteAddr()) {
errors.LogDebug(ctx, "Unauthorized UDP access from ", conn.RemoteAddr().String())
@@ -231,6 +196,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis
request := protocol.RequestHeaderFromContext(ctx)
if request == nil {
payload.Release()
return
}
@@ -249,9 +215,9 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis
errors.LogWarningInner(ctx, err, "failed to write UDP response")
return
}
defer udpMessage.Release()
conn.Write(udpMessage.Bytes())
udpMessage.Release()
})
defer udpServer.RemoveRay()
@@ -259,7 +225,6 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis
if inbound != nil && inbound.Source.IsValid() {
errors.LogInfo(ctx, "client UDP connection from ", inbound.Source)
}
inbound.CanSpliceCopy = 1
var dest *net.Destination

View File

@@ -113,9 +113,11 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
target = b.UDP
}
if _, err := w.writePacket(b.Bytes(), *target); err != nil {
b.Release()
buf.ReleaseMulti(mb)
return err
}
b.Release()
}
return nil
}

View File

@@ -233,7 +233,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con
sessionPolicy = s.policyManager.ForLevel(user.Level)
if destination.Network == net.Network_UDP { // handle udp request
return s.handleUDPPayload(ctx, &PacketReader{Reader: clientReader}, &PacketWriter{Writer: conn}, dispatcher)
return s.handleUDPPayload(ctx, sessionPolicy, &PacketReader{Reader: clientReader}, &PacketWriter{Writer: conn}, dispatcher)
}
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
@@ -248,7 +248,11 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con
return s.handleConnection(ctx, sessionPolicy, destination, clientReader, buf.NewWriter(conn), dispatcher)
}
func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReader, clientWriter *PacketWriter, dispatcher routing.Dispatcher) error {
func (s *Server) handleUDPPayload(ctx context.Context, sessionPolicy policy.Session, clientReader *PacketReader, clientWriter *PacketWriter, dispatcher routing.Dispatcher) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
defer timer.SetTimeout(0)
udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
udpPayload := packet.Payload
if udpPayload.UDP == nil {
@@ -257,6 +261,9 @@ func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReade
if err := clientWriter.WriteMultiBuffer(buf.MultiBuffer{udpPayload}); err != nil {
errors.LogWarningInner(ctx, err, "failed to write response")
cancel()
} else {
timer.Update()
}
})
defer udpServer.RemoveRay()
@@ -266,6 +273,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReade
var dest *net.Destination
requestDone := func() error {
for {
select {
case <-ctx.Done():
@@ -283,6 +291,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReade
if b == nil {
continue
}
timer.Update()
destination := *b.UDP
currentPacketCtx := ctx
@@ -307,6 +316,13 @@ func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReade
}
}
}
}
if err := task.Run(ctx, requestDone); err != nil {
return err
}
return nil
}
func (s *Server) handleConnection(ctx context.Context, sessionPolicy policy.Session,

View File

@@ -18,6 +18,10 @@ func (a *Account) AsAccount() (protocol.Account, error) {
ID: protocol.NewID(id),
Flow: a.Flow, // needs parser here?
Encryption: a.Encryption, // needs parser here?
XorMode: a.XorMode,
Seconds: a.Seconds,
Padding: a.Padding,
Seed: a.Seed,
}, nil
}
@@ -27,8 +31,13 @@ type MemoryAccount struct {
ID *protocol.ID
// Flow of the account. May be "xtls-rprx-vision".
Flow string
// Encryption of the account. Used for client connections, and only accepts "none" for now.
Encryption string
XorMode uint32
Seconds uint32
Padding string
// Seed. Details TBD
Seed string
}
// Equals implements protocol.Account.Equals().
@@ -45,5 +54,8 @@ func (a *MemoryAccount) ToProto() proto.Message {
Id: a.ID.String(),
Flow: a.Flow,
Encryption: a.Encryption,
XorMode: a.XorMode,
Seconds: a.Seconds,
Padding: a.Padding,
}
}

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc-gen-go v1.36.8
// protoc v5.28.2
// source: proxy/vless/account.proto
@@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -21,16 +22,19 @@ const (
)
type Account struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState `protogen:"open.v1"`
// ID of the account, in the form of a UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57".
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
// Flow settings. May be "xtls-rprx-vision".
Flow string `protobuf:"bytes,2,opt,name=flow,proto3" json:"flow,omitempty"`
// Encryption settings. Only applies to client side, and only accepts "none" for now.
Encryption string `protobuf:"bytes,3,opt,name=encryption,proto3" json:"encryption,omitempty"`
XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"`
Seconds uint32 `protobuf:"varint,5,opt,name=seconds,proto3" json:"seconds,omitempty"`
Padding string `protobuf:"bytes,6,opt,name=padding,proto3" json:"padding,omitempty"`
// Seed settings. Details TBD
Seed string `protobuf:"bytes,7,opt,name=seed,proto3" json:"seed,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Account) Reset() {
@@ -84,33 +88,59 @@ func (x *Account) GetEncryption() string {
return ""
}
func (x *Account) GetXorMode() uint32 {
if x != nil {
return x.XorMode
}
return 0
}
func (x *Account) GetSeconds() uint32 {
if x != nil {
return x.Seconds
}
return 0
}
func (x *Account) GetPadding() string {
if x != nil {
return x.Padding
}
return ""
}
func (x *Account) GetSeed() string {
if x != nil {
return x.Seed
}
return ""
}
var File_proxy_vless_account_proto protoreflect.FileDescriptor
var file_proxy_vless_account_proto_rawDesc = []byte{
0x0a, 0x19, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x63,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x22, 0x4d, 0x0a,
0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f, 0x77,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1e, 0x0a, 0x0a,
0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0a, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x52, 0x0a, 0x14,
0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76,
0x6c, 0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72,
0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0xaa, 0x02, 0x10,
0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
const file_proxy_vless_account_proto_rawDesc = "" +
"\n" +
"\x19proxy/vless/account.proto\x12\x10xray.proxy.vless\"\xaf\x01\n" +
"\aAccount\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
"\x04flow\x18\x02 \x01(\tR\x04flow\x12\x1e\n" +
"\n" +
"encryption\x18\x03 \x01(\tR\n" +
"encryption\x12\x18\n" +
"\axorMode\x18\x04 \x01(\rR\axorMode\x12\x18\n" +
"\aseconds\x18\x05 \x01(\rR\aseconds\x12\x18\n" +
"\apadding\x18\x06 \x01(\tR\apadding\x12\x12\n" +
"\x04seed\x18\a \x01(\tR\x04seedBR\n" +
"\x14com.xray.proxy.vlessP\x01Z%github.com/xtls/xray-core/proxy/vless\xaa\x02\x10Xray.Proxy.Vlessb\x06proto3"
var (
file_proxy_vless_account_proto_rawDescOnce sync.Once
file_proxy_vless_account_proto_rawDescData = file_proxy_vless_account_proto_rawDesc
file_proxy_vless_account_proto_rawDescData []byte
)
func file_proxy_vless_account_proto_rawDescGZIP() []byte {
file_proxy_vless_account_proto_rawDescOnce.Do(func() {
file_proxy_vless_account_proto_rawDescData = protoimpl.X.CompressGZIP(file_proxy_vless_account_proto_rawDescData)
file_proxy_vless_account_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proxy_vless_account_proto_rawDesc), len(file_proxy_vless_account_proto_rawDesc)))
})
return file_proxy_vless_account_proto_rawDescData
}
@@ -136,7 +166,7 @@ func file_proxy_vless_account_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proxy_vless_account_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proxy_vless_account_proto_rawDesc), len(file_proxy_vless_account_proto_rawDesc)),
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
@@ -147,7 +177,6 @@ func file_proxy_vless_account_proto_init() {
MessageInfos: file_proxy_vless_account_proto_msgTypes,
}.Build()
File_proxy_vless_account_proto = out.File
file_proxy_vless_account_proto_rawDesc = nil
file_proxy_vless_account_proto_goTypes = nil
file_proxy_vless_account_proto_depIdxs = nil
}

View File

@@ -11,6 +11,11 @@ message Account {
string id = 1;
// Flow settings. May be "xtls-rprx-vision".
string flow = 2;
// Encryption settings. Only applies to client side, and only accepts "none" for now.
string encryption = 3;
uint32 xorMode = 4;
uint32 seconds = 5;
string padding = 6;
// Seed settings. Details TBD
string seed = 7;
}

View File

@@ -1,20 +1,22 @@
package encoding
import (
"bytes"
"context"
"io"
"net"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/proxy"
"github.com/xtls/xray-core/proxy/vless"
"google.golang.org/protobuf/proto"
)
func EncodeHeaderAddons(buffer *buf.Buffer, addons *Addons) error {
switch addons.Flow {
case vless.XRV:
func EncodeHeaderAddons(buffer *buf.Buffer, addons *proxy.Addons) error {
if addons.Flow == vless.XRV || len(addons.Seed) > 0 {
bytes, err := proto.Marshal(addons)
if err != nil {
return errors.New("failed to marshal addons protobuf value").Base(err)
@@ -25,17 +27,16 @@ func EncodeHeaderAddons(buffer *buf.Buffer, addons *Addons) error {
if _, err := buffer.Write(bytes); err != nil {
return errors.New("failed to write addons protobuf value").Base(err)
}
default:
} else {
if err := buffer.WriteByte(0); err != nil {
return errors.New("failed to write addons protobuf length").Base(err)
}
}
return nil
}
func DecodeHeaderAddons(buffer *buf.Buffer, reader io.Reader) (*Addons, error) {
addons := new(Addons)
func DecodeHeaderAddons(buffer *buf.Buffer, reader io.Reader) (*proxy.Addons, error) {
addons := new(proxy.Addons)
buffer.Clear()
if _, err := buffer.ReadFullFrom(reader, 1); err != nil {
return nil, errors.New("failed to read addons protobuf length").Base(err)
@@ -50,37 +51,27 @@ func DecodeHeaderAddons(buffer *buf.Buffer, reader io.Reader) (*Addons, error) {
if err := proto.Unmarshal(buffer.Bytes(), addons); err != nil {
return nil, errors.New("failed to unmarshal addons protobuf value").Base(err)
}
// Verification.
switch addons.Flow {
default:
}
}
return addons, nil
}
// EncodeBodyAddons returns a Writer that auto-encrypt content written by caller.
func EncodeBodyAddons(writer io.Writer, request *protocol.RequestHeader, requestAddons *Addons, state *proxy.TrafficState, isUplink bool, context context.Context) buf.Writer {
func EncodeBodyAddons(writer buf.Writer, request *protocol.RequestHeader, addons *proxy.Addons, state *proxy.TrafficState, isUplink bool, context context.Context, conn net.Conn, ob *session.Outbound) buf.Writer {
w := proxy.NewVisionWriter(writer, addons, state, isUplink, context, conn, ob)
if request.Command == protocol.RequestCommandUDP {
return NewMultiLengthPacketWriter(writer.(buf.Writer))
return NewMultiLengthPacketWriter(w)
}
w := buf.NewWriter(writer)
if requestAddons.Flow == vless.XRV {
w = proxy.NewVisionWriter(w, state, isUplink, context)
}
return w
return writer
}
// DecodeBodyAddons returns a Reader from which caller can fetch decrypted body.
func DecodeBodyAddons(reader io.Reader, request *protocol.RequestHeader, addons *Addons) buf.Reader {
switch addons.Flow {
default:
func DecodeBodyAddons(reader io.Reader, request *protocol.RequestHeader, addons *proxy.Addons, state *proxy.TrafficState, isUplink bool, context context.Context, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, ob *session.Outbound) buf.Reader {
r := proxy.NewVisionReader(buf.NewReader(reader), addons, state, isUplink, context, conn, input, rawInput, ob)
if request.Command == protocol.RequestCommandUDP {
return NewLengthPacketReader(reader)
return NewLengthPacketReader(&buf.BufferedReader{Reader: r})
}
}
return buf.NewReader(reader)
return r
}
func NewMultiLengthPacketWriter(writer buf.Writer) *MultiLengthPacketWriter {
@@ -188,3 +179,78 @@ func (r *LengthPacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
}
return mb, nil
}
func PopulateSeed(seed string, addons *proxy.Addons) {
if len(seed) > 0 {
addons.Seed = []byte {1} // only turn on, more TBD
addons.Mode = proxy.SeedMode_PaddingPlusDelay
addons.Duration = "0-8"
addons.Padding = &proxy.PaddingConfig{
RegularMin: 0,
RegularMax: 256,
LongMin: 900,
LongMax: 1400,
}
// addons.Delay = &proxy.DelayConfig{
// IsRandom: true,
// MinMillis: 100,
// MaxMillis: 500,
// }
addons.Scheduler = &proxy.SchedulerConfig{
TimeoutMillis: 600,
}
} else if addons.Flow == vless.XRV {
addons.Seed = []byte {1} // only turn on, more TBD
addons.Mode = proxy.SeedMode_PaddingOnly
addons.Duration = "0-8"
addons.Padding = &proxy.PaddingConfig{
RegularMin: 0,
RegularMax: 256,
LongMin: 900,
LongMax: 1400,
}
}
}
func CheckSeed(requestAddons *proxy.Addons, responseAddons *proxy.Addons) error {
if !bytes.Equal(requestAddons.Seed, responseAddons.Seed) {
return errors.New("Seed bytes not match", requestAddons.Seed, responseAddons.Seed)
}
if responseAddons.Flow == vless.XRV && len(responseAddons.Seed) == 0 && requestAddons.Mode == proxy.SeedMode_Unknown {
// old vision server config allow empty seed from clients for backwards compatibility
return nil
}
if requestAddons.Mode != responseAddons.Mode {
return errors.New("Mode not match", requestAddons.Mode, responseAddons.Mode)
}
if requestAddons.Duration != responseAddons.Duration {
return errors.New("Duration not match", requestAddons.Duration, responseAddons.Duration)
}
if requestAddons.Padding != nil && responseAddons.Padding != nil {
if requestAddons.Padding.RegularMin != responseAddons.Padding.RegularMin ||
requestAddons.Padding.RegularMax != responseAddons.Padding.RegularMax ||
requestAddons.Padding.LongMin != responseAddons.Padding.LongMin ||
requestAddons.Padding.LongMax != responseAddons.Padding.LongMax {
return errors.New("Padding not match")
}
} else if requestAddons.Padding != nil || responseAddons.Padding != nil {
return errors.New("Padding of one is nil but the other is not nil")
}
if requestAddons.Delay != nil && responseAddons.Delay != nil {
if requestAddons.Delay.IsRandom != responseAddons.Delay.IsRandom ||
requestAddons.Delay.MinMillis != responseAddons.Delay.MinMillis ||
requestAddons.Delay.MaxMillis != responseAddons.Delay.MaxMillis {
return errors.New("Delay not match")
}
} else if requestAddons.Delay != nil || responseAddons.Delay != nil {
return errors.New("Delay of one is nil but the other is not nil")
}
if requestAddons.Scheduler != nil && responseAddons.Scheduler != nil {
if requestAddons.Scheduler.TimeoutMillis != responseAddons.Scheduler.TimeoutMillis {
return errors.New("Scheduler not match")
}
} else if requestAddons.Scheduler != nil || responseAddons.Scheduler != nil {
return errors.New("Scheduler of one is nil but the other is not nil")
}
return nil
}

View File

@@ -20,6 +20,58 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type SeedMode int32
const (
SeedMode_Unknown SeedMode = 0
SeedMode_PaddingOnly SeedMode = 1
SeedMode_PaddingPlusDelay SeedMode = 2
SeedMode_IndependentScheduler SeedMode = 3
)
// Enum value maps for SeedMode.
var (
SeedMode_name = map[int32]string{
0: "Unknown",
1: "PaddingOnly",
2: "PaddingPlusDelay",
3: "IndependentScheduler",
}
SeedMode_value = map[string]int32{
"Unknown": 0,
"PaddingOnly": 1,
"PaddingPlusDelay": 2,
"IndependentScheduler": 3,
}
)
func (x SeedMode) Enum() *SeedMode {
p := new(SeedMode)
*p = x
return p
}
func (x SeedMode) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (SeedMode) Descriptor() protoreflect.EnumDescriptor {
return file_proxy_vless_encoding_addons_proto_enumTypes[0].Descriptor()
}
func (SeedMode) Type() protoreflect.EnumType {
return &file_proxy_vless_encoding_addons_proto_enumTypes[0]
}
func (x SeedMode) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use SeedMode.Descriptor instead.
func (SeedMode) EnumDescriptor() ([]byte, []int) {
return file_proxy_vless_encoding_addons_proto_rawDescGZIP(), []int{0}
}
type Addons struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -27,6 +79,11 @@ type Addons struct {
Flow string `protobuf:"bytes,1,opt,name=Flow,proto3" json:"Flow,omitempty"`
Seed []byte `protobuf:"bytes,2,opt,name=Seed,proto3" json:"Seed,omitempty"`
Mode SeedMode `protobuf:"varint,3,opt,name=Mode,proto3,enum=xray.proxy.vless.encoding.SeedMode" json:"Mode,omitempty"`
Duration string `protobuf:"bytes,4,opt,name=Duration,proto3" json:"Duration,omitempty"` // "0-8" means apply to number of packets, "1kb-" means start applying once both side exchange 1kb data, counting two-ways
Padding *PaddingConfig `protobuf:"bytes,5,opt,name=Padding,proto3" json:"Padding,omitempty"`
Delay *DelayConfig `protobuf:"bytes,6,opt,name=Delay,proto3" json:"Delay,omitempty"`
Scheduler *SchedulerConfig `protobuf:"bytes,7,opt,name=Scheduler,proto3" json:"Scheduler,omitempty"`
}
func (x *Addons) Reset() {
@@ -73,24 +130,282 @@ func (x *Addons) GetSeed() []byte {
return nil
}
func (x *Addons) GetMode() SeedMode {
if x != nil {
return x.Mode
}
return SeedMode_Unknown
}
func (x *Addons) GetDuration() string {
if x != nil {
return x.Duration
}
return ""
}
func (x *Addons) GetPadding() *PaddingConfig {
if x != nil {
return x.Padding
}
return nil
}
func (x *Addons) GetDelay() *DelayConfig {
if x != nil {
return x.Delay
}
return nil
}
func (x *Addons) GetScheduler() *SchedulerConfig {
if x != nil {
return x.Scheduler
}
return nil
}
type PaddingConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
RegularMin uint32 `protobuf:"varint,1,opt,name=RegularMin,proto3" json:"RegularMin,omitempty"`
RegularMax uint32 `protobuf:"varint,2,opt,name=RegularMax,proto3" json:"RegularMax,omitempty"`
LongMin uint32 `protobuf:"varint,3,opt,name=LongMin,proto3" json:"LongMin,omitempty"`
LongMax uint32 `protobuf:"varint,4,opt,name=LongMax,proto3" json:"LongMax,omitempty"`
}
func (x *PaddingConfig) Reset() {
*x = PaddingConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_proxy_vless_encoding_addons_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PaddingConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PaddingConfig) ProtoMessage() {}
func (x *PaddingConfig) ProtoReflect() protoreflect.Message {
mi := &file_proxy_vless_encoding_addons_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PaddingConfig.ProtoReflect.Descriptor instead.
func (*PaddingConfig) Descriptor() ([]byte, []int) {
return file_proxy_vless_encoding_addons_proto_rawDescGZIP(), []int{1}
}
func (x *PaddingConfig) GetRegularMin() uint32 {
if x != nil {
return x.RegularMin
}
return 0
}
func (x *PaddingConfig) GetRegularMax() uint32 {
if x != nil {
return x.RegularMax
}
return 0
}
func (x *PaddingConfig) GetLongMin() uint32 {
if x != nil {
return x.LongMin
}
return 0
}
func (x *PaddingConfig) GetLongMax() uint32 {
if x != nil {
return x.LongMax
}
return 0
}
type DelayConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
IsRandom bool `protobuf:"varint,1,opt,name=IsRandom,proto3" json:"IsRandom,omitempty"`
MinMillis uint32 `protobuf:"varint,2,opt,name=MinMillis,proto3" json:"MinMillis,omitempty"`
MaxMillis uint32 `protobuf:"varint,3,opt,name=MaxMillis,proto3" json:"MaxMillis,omitempty"`
}
func (x *DelayConfig) Reset() {
*x = DelayConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_proxy_vless_encoding_addons_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DelayConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DelayConfig) ProtoMessage() {}
func (x *DelayConfig) ProtoReflect() protoreflect.Message {
mi := &file_proxy_vless_encoding_addons_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DelayConfig.ProtoReflect.Descriptor instead.
func (*DelayConfig) Descriptor() ([]byte, []int) {
return file_proxy_vless_encoding_addons_proto_rawDescGZIP(), []int{2}
}
func (x *DelayConfig) GetIsRandom() bool {
if x != nil {
return x.IsRandom
}
return false
}
func (x *DelayConfig) GetMinMillis() uint32 {
if x != nil {
return x.MinMillis
}
return 0
}
func (x *DelayConfig) GetMaxMillis() uint32 {
if x != nil {
return x.MaxMillis
}
return 0
}
type SchedulerConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TimeoutMillis uint32 `protobuf:"varint,1,opt,name=TimeoutMillis,proto3" json:"TimeoutMillis,omitempty"` // original traffic will not be sent right away but when scheduler want to send or pending buffer times out
}
func (x *SchedulerConfig) Reset() {
*x = SchedulerConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_proxy_vless_encoding_addons_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SchedulerConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SchedulerConfig) ProtoMessage() {}
func (x *SchedulerConfig) ProtoReflect() protoreflect.Message {
mi := &file_proxy_vless_encoding_addons_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SchedulerConfig.ProtoReflect.Descriptor instead.
func (*SchedulerConfig) Descriptor() ([]byte, []int) {
return file_proxy_vless_encoding_addons_proto_rawDescGZIP(), []int{3}
}
func (x *SchedulerConfig) GetTimeoutMillis() uint32 {
if x != nil {
return x.TimeoutMillis
}
return 0
}
var File_proxy_vless_encoding_addons_proto protoreflect.FileDescriptor
var file_proxy_vless_encoding_addons_proto_rawDesc = []byte{
0x0a, 0x21, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x65, 0x6e,
0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x2f, 0x61, 0x64, 0x64, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x12, 0x19, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e,
0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22, 0x30,
0x0a, 0x06, 0x41, 0x64, 0x64, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x6c, 0x6f, 0x77,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04,
0x53, 0x65, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x53, 0x65, 0x65, 0x64,
0x42, 0x6d, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f,
0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e,
0x67, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70,
0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x64,
0x69, 0x6e, 0x67, 0xaa, 0x02, 0x19, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79,
0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22, 0xd1,
0x02, 0x0a, 0x06, 0x41, 0x64, 0x64, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x6c, 0x6f,
0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x12, 0x0a,
0x04, 0x53, 0x65, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x53, 0x65, 0x65,
0x64, 0x12, 0x37, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32,
0x23, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65,
0x73, 0x73, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x65, 0x65, 0x64,
0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x44, 0x75,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x44, 0x75,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x07, 0x50, 0x61, 0x64, 0x64, 0x69, 0x6e,
0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70,
0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64,
0x69, 0x6e, 0x67, 0x2e, 0x50, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x52, 0x07, 0x50, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x3c, 0x0a, 0x05, 0x44, 0x65,
0x6c, 0x61, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x65, 0x6e, 0x63,
0x6f, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x52, 0x05, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x48, 0x0a, 0x09, 0x53, 0x63, 0x68, 0x65,
0x64, 0x75, 0x6c, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x65,
0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65,
0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c,
0x65, 0x72, 0x22, 0x83, 0x01, 0x0a, 0x0d, 0x50, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x52, 0x65, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x4d,
0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x52, 0x65, 0x67, 0x75, 0x6c, 0x61,
0x72, 0x4d, 0x69, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x52, 0x65, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x4d,
0x61, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x52, 0x65, 0x67, 0x75, 0x6c, 0x61,
0x72, 0x4d, 0x61, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x4c, 0x6f, 0x6e, 0x67, 0x4d, 0x69, 0x6e, 0x18,
0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x4c, 0x6f, 0x6e, 0x67, 0x4d, 0x69, 0x6e, 0x12, 0x18,
0x0a, 0x07, 0x4c, 0x6f, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x07, 0x4c, 0x6f, 0x6e, 0x67, 0x4d, 0x61, 0x78, 0x22, 0x65, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x61,
0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x73, 0x52, 0x61, 0x6e,
0x64, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x49, 0x73, 0x52, 0x61, 0x6e,
0x64, 0x6f, 0x6d, 0x12, 0x1c, 0x0a, 0x09, 0x4d, 0x69, 0x6e, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x4d, 0x69, 0x6e, 0x4d, 0x69, 0x6c, 0x6c, 0x69,
0x73, 0x12, 0x1c, 0x0a, 0x09, 0x4d, 0x61, 0x78, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x4d, 0x61, 0x78, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x22,
0x37, 0x0a, 0x0f, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x4d, 0x69, 0x6c,
0x6c, 0x69, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x54, 0x69, 0x6d, 0x65, 0x6f,
0x75, 0x74, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x2a, 0x58, 0x0a, 0x08, 0x53, 0x65, 0x65, 0x64,
0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10,
0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x6e, 0x6c, 0x79,
0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x6c, 0x75,
0x73, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x49, 0x6e, 0x64, 0x65,
0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72,
0x10, 0x03, 0x42, 0x6d, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70,
0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64,
0x69, 0x6e, 0x67, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x65, 0x6e, 0x63,
0x6f, 0x64, 0x69, 0x6e, 0x67, 0xaa, 0x02, 0x19, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f,
0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e,
0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -105,16 +420,25 @@ func file_proxy_vless_encoding_addons_proto_rawDescGZIP() []byte {
return file_proxy_vless_encoding_addons_proto_rawDescData
}
var file_proxy_vless_encoding_addons_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_proxy_vless_encoding_addons_proto_goTypes = []any{
(*Addons)(nil), // 0: xray.proxy.vless.encoding.Addons
var file_proxy_vless_encoding_addons_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_proxy_vless_encoding_addons_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_proxy_vless_encoding_addons_proto_goTypes = []interface{}{
(SeedMode)(0), // 0: xray.proxy.vless.encoding.SeedMode
(*Addons)(nil), // 1: xray.proxy.vless.encoding.Addons
(*PaddingConfig)(nil), // 2: xray.proxy.vless.encoding.PaddingConfig
(*DelayConfig)(nil), // 3: xray.proxy.vless.encoding.DelayConfig
(*SchedulerConfig)(nil), // 4: xray.proxy.vless.encoding.SchedulerConfig
}
var file_proxy_vless_encoding_addons_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
0, // 0: xray.proxy.vless.encoding.Addons.Mode:type_name -> xray.proxy.vless.encoding.SeedMode
2, // 1: xray.proxy.vless.encoding.Addons.Padding:type_name -> xray.proxy.vless.encoding.PaddingConfig
3, // 2: xray.proxy.vless.encoding.Addons.Delay:type_name -> xray.proxy.vless.encoding.DelayConfig
4, // 3: xray.proxy.vless.encoding.Addons.Scheduler:type_name -> xray.proxy.vless.encoding.SchedulerConfig
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_proxy_vless_encoding_addons_proto_init() }
@@ -122,18 +446,69 @@ func file_proxy_vless_encoding_addons_proto_init() {
if File_proxy_vless_encoding_addons_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_proxy_vless_encoding_addons_proto_msgTypes[0].Exporter = func(v any, i int) any {
switch v := v.(*Addons); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proxy_vless_encoding_addons_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PaddingConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proxy_vless_encoding_addons_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DelayConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proxy_vless_encoding_addons_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SchedulerConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proxy_vless_encoding_addons_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumEnums: 1,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_proxy_vless_encoding_addons_proto_goTypes,
DependencyIndexes: file_proxy_vless_encoding_addons_proto_depIdxs,
EnumInfos: file_proxy_vless_encoding_addons_proto_enumTypes,
MessageInfos: file_proxy_vless_encoding_addons_proto_msgTypes,
}.Build()
File_proxy_vless_encoding_addons_proto = out.File

View File

@@ -1,12 +0,0 @@
syntax = "proto3";
package xray.proxy.vless.encoding;
option csharp_namespace = "Xray.Proxy.Vless.Encoding";
option go_package = "github.com/xtls/xray-core/proxy/vless/encoding";
option java_package = "com.xray.proxy.vless.encoding";
option java_multiple_files = true;
message Addons {
string Flow = 1;
bytes Seed = 2;
}

View File

@@ -1,7 +1,6 @@
package encoding
import (
"bytes"
"context"
"io"
@@ -11,7 +10,6 @@ import (
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal"
"github.com/xtls/xray-core/features/stats"
"github.com/xtls/xray-core/proxy"
"github.com/xtls/xray-core/proxy/vless"
)
@@ -28,7 +26,7 @@ var addrParser = protocol.NewAddressParser(
)
// EncodeRequestHeader writes encoded request header into the given writer.
func EncodeRequestHeader(writer io.Writer, request *protocol.RequestHeader, requestAddons *Addons) error {
func EncodeRequestHeader(writer io.Writer, request *protocol.RequestHeader, requestAddons *proxy.Addons) error {
buffer := buf.StackNew()
defer buffer.Release()
@@ -62,7 +60,7 @@ func EncodeRequestHeader(writer io.Writer, request *protocol.RequestHeader, requ
}
// DecodeRequestHeader decodes and returns (if successful) a RequestHeader from an input stream.
func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validator vless.Validator) ([]byte, *protocol.RequestHeader, *Addons, bool, error) {
func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validator vless.Validator) ([]byte, *protocol.RequestHeader, *proxy.Addons, bool, error) {
buffer := buf.StackNew()
defer buffer.Release()
@@ -131,7 +129,7 @@ func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validat
}
// EncodeResponseHeader writes encoded response header into the given writer.
func EncodeResponseHeader(writer io.Writer, request *protocol.RequestHeader, responseAddons *Addons) error {
func EncodeResponseHeader(writer io.Writer, request *protocol.RequestHeader, responseAddons *proxy.Addons) error {
buffer := buf.StackNew()
defer buffer.Release()
@@ -151,7 +149,7 @@ func EncodeResponseHeader(writer io.Writer, request *protocol.RequestHeader, res
}
// DecodeResponseHeader decodes and returns (if successful) a ResponseHeader from an input stream.
func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*Addons, error) {
func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*proxy.Addons, error) {
buffer := buf.StackNew()
defer buffer.Release()
@@ -171,8 +169,8 @@ func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*A
return responseAddons, nil
}
// XtlsRead filter and read xtls protocol
func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error {
// XtlsRead can switch to splice copy
func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, trafficState *proxy.TrafficState, isUplink bool, ctx context.Context) error {
err := func() error {
for {
if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy {
@@ -181,74 +179,11 @@ func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer,
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil {
writerConn = inbound.Conn
inTimer = inbound.Timer
if isUplink && inbound.CanSpliceCopy == 2 {
inbound.CanSpliceCopy = 1
}
if !isUplink && ob != nil && ob.CanSpliceCopy == 2 { // ob need to be passed in due to context can change
ob.CanSpliceCopy = 1
}
}
return proxy.CopyRawConnIfExist(ctx, conn, writerConn, writer, timer, inTimer)
}
buffer, err := reader.ReadMultiBuffer()
if !buffer.IsEmpty() {
timer.Update()
if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy {
// XTLS Vision processes struct TLS Conn's input and rawInput
if inputBuffer, err := buf.ReadFrom(input); err == nil {
if !inputBuffer.IsEmpty() {
buffer, _ = buf.MergeMulti(buffer, inputBuffer)
}
}
if rawInputBuffer, err := buf.ReadFrom(rawInput); err == nil {
if !rawInputBuffer.IsEmpty() {
buffer, _ = buf.MergeMulti(buffer, rawInputBuffer)
}
}
}
if werr := writer.WriteMultiBuffer(buffer); werr != nil {
return werr
}
}
if err != nil {
return err
}
}
}()
if err != nil && errors.Cause(err) != io.EOF {
return err
}
return nil
}
// XtlsWrite filter and write xtls protocol
func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, conn net.Conn, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error {
err := func() error {
var ct stats.Counter
for {
buffer, err := reader.ReadMultiBuffer()
if isUplink && trafficState.Outbound.UplinkWriterDirectCopy || !isUplink && trafficState.Inbound.DownlinkWriterDirectCopy {
if inbound := session.InboundFromContext(ctx); inbound != nil {
if !isUplink && inbound.CanSpliceCopy == 2 {
inbound.CanSpliceCopy = 1
}
if isUplink && ob != nil && ob.CanSpliceCopy == 2 {
ob.CanSpliceCopy = 1
}
}
rawConn, _, writerCounter := proxy.UnwrapRawConn(conn)
writer = buf.NewWriter(rawConn)
ct = writerCounter
if isUplink {
trafficState.Outbound.UplinkWriterDirectCopy = false
} else {
trafficState.Inbound.DownlinkWriterDirectCopy = false
}
}
if !buffer.IsEmpty() {
if ct != nil {
ct.Add(int64(buffer.Len()))
}
timer.Update()
if werr := writer.WriteMultiBuffer(buffer); werr != nil {
return werr

View File

@@ -9,6 +9,7 @@ import (
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/uuid"
"github.com/xtls/xray-core/proxy"
"github.com/xtls/xray-core/proxy/vless"
. "github.com/xtls/xray-core/proxy/vless/encoding"
)
@@ -37,7 +38,7 @@ func TestRequestSerialization(t *testing.T) {
Address: net.DomainAddress("www.example.com"),
Port: net.Port(443),
}
expectedAddons := &Addons{}
expectedAddons := &proxy.Addons{}
buffer := buf.StackNew()
common.Must(EncodeRequestHeader(&buffer, expectedRequest, expectedAddons))
@@ -52,7 +53,7 @@ func TestRequestSerialization(t *testing.T) {
t.Error(r)
}
addonsComparer := func(x, y *Addons) bool {
addonsComparer := func(x, y *proxy.Addons) bool {
return (x.Flow == y.Flow) && (cmp.Equal(x.Seed, y.Seed))
}
if r := cmp.Diff(actualAddons, expectedAddons, cmp.Comparer(addonsComparer)); r != "" {
@@ -78,7 +79,7 @@ func TestInvalidRequest(t *testing.T) {
Address: net.DomainAddress("www.example.com"),
Port: net.Port(443),
}
expectedAddons := &Addons{}
expectedAddons := &proxy.Addons{}
buffer := buf.StackNew()
common.Must(EncodeRequestHeader(&buffer, expectedRequest, expectedAddons))
@@ -109,7 +110,7 @@ func TestMuxRequest(t *testing.T) {
Command: protocol.RequestCommandMux,
Address: net.DomainAddress("v1.mux.cool"),
}
expectedAddons := &Addons{}
expectedAddons := &proxy.Addons{}
buffer := buf.StackNew()
common.Must(EncodeRequestHeader(&buffer, expectedRequest, expectedAddons))
@@ -124,7 +125,7 @@ func TestMuxRequest(t *testing.T) {
t.Error(r)
}
addonsComparer := func(x, y *Addons) bool {
addonsComparer := func(x, y *proxy.Addons) bool {
return (x.Flow == y.Flow) && (cmp.Equal(x.Seed, y.Seed))
}
if r := cmp.Diff(actualAddons, expectedAddons, cmp.Comparer(addonsComparer)); r != "" {

View File

@@ -0,0 +1,210 @@
package encryption
import (
"crypto/cipher"
"crypto/ecdh"
"crypto/mlkem"
"crypto/rand"
"io"
"net"
"sync"
"time"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/protocol"
"lukechampine.com/blake3"
)
type ClientInstance struct {
NfsPKeys []any
NfsPKeysBytes [][]byte
Hash32s [][32]byte
RelaysLength int
XorMode uint32
Seconds uint32
PaddingLens [][3]int
PaddingGaps [][3]int
RWLock sync.RWMutex
Expire time.Time
PfsKey []byte
Ticket []byte
}
func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32, padding string) (err error) {
if i.NfsPKeys != nil {
return errors.New("already initialized")
}
l := len(nfsPKeysBytes)
if l == 0 {
return errors.New("empty nfsPKeysBytes")
}
i.NfsPKeys = make([]any, l)
i.NfsPKeysBytes = nfsPKeysBytes
i.Hash32s = make([][32]byte, l)
for j, k := range nfsPKeysBytes {
if len(k) == 32 {
if i.NfsPKeys[j], err = ecdh.X25519().NewPublicKey(k); err != nil {
return
}
i.RelaysLength += 32 + 32
} else {
if i.NfsPKeys[j], err = mlkem.NewEncapsulationKey768(k); err != nil {
return
}
i.RelaysLength += 1088 + 32
}
i.Hash32s[j] = blake3.Sum256(k)
}
i.RelaysLength -= 32
i.XorMode = xorMode
i.Seconds = seconds
return ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)
}
func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
if i.NfsPKeys == nil {
return nil, errors.New("uninitialized")
}
c := NewCommonConn(conn, protocol.HasAESGCMHardwareSupport)
ivAndRealysLength := 16 + i.RelaysLength
pfsKeyExchangeLength := 18 + 1184 + 32 + 16
paddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps)
clientHello := make([]byte, ivAndRealysLength+pfsKeyExchangeLength+paddingLength)
iv := clientHello[:16]
rand.Read(iv)
relays := clientHello[16:ivAndRealysLength]
var nfsKey []byte
var lastCTR cipher.Stream
for j, k := range i.NfsPKeys {
var index = 32
if k, ok := k.(*ecdh.PublicKey); ok {
privateKey, _ := ecdh.X25519().GenerateKey(rand.Reader)
copy(relays, privateKey.PublicKey().Bytes())
var err error
nfsKey, err = privateKey.ECDH(k)
if err != nil {
return nil, err
}
}
if k, ok := k.(*mlkem.EncapsulationKey768); ok {
var ciphertext []byte
nfsKey, ciphertext = k.Encapsulate()
copy(relays, ciphertext)
index = 1088
}
if i.XorMode > 0 { // this xor can (others can't) be recovered by client's config, revealing an X25519 public key / ML-KEM-768 ciphertext, that's why "native" values
NewCTR(i.NfsPKeysBytes[j], iv).XORKeyStream(relays, relays[:index]) // make X25519 public key / ML-KEM-768 ciphertext distinguishable from random bytes
}
if lastCTR != nil {
lastCTR.XORKeyStream(relays, relays[:32]) // make this relay irreplaceable
}
if j == len(i.NfsPKeys)-1 {
break
}
lastCTR = NewCTR(nfsKey, iv)
lastCTR.XORKeyStream(relays[index:], i.Hash32s[j+1][:])
relays = relays[index+32:]
}
nfsAEAD := NewAEAD(iv, nfsKey, c.UseAES)
if i.Seconds > 0 {
i.RWLock.RLock()
if time.Now().Before(i.Expire) {
c.Client = i
c.UnitedKey = append(i.PfsKey, nfsKey...) // different unitedKey for each connection
nfsAEAD.Seal(clientHello[:ivAndRealysLength], nil, EncodeLength(32), nil)
nfsAEAD.Seal(clientHello[:ivAndRealysLength+18], nil, i.Ticket, nil)
i.RWLock.RUnlock()
c.PreWrite = clientHello[:ivAndRealysLength+18+32]
c.AEAD = NewAEAD(clientHello[ivAndRealysLength+18:ivAndRealysLength+18+32], c.UnitedKey, c.UseAES)
if i.XorMode == 2 {
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, iv), nil, len(c.PreWrite), 16)
}
return c, nil
}
i.RWLock.RUnlock()
}
pfsKeyExchange := clientHello[ivAndRealysLength : ivAndRealysLength+pfsKeyExchangeLength]
nfsAEAD.Seal(pfsKeyExchange[:0], nil, EncodeLength(pfsKeyExchangeLength-18), nil)
mlkem768DKey, _ := mlkem.GenerateKey768()
x25519SKey, _ := ecdh.X25519().GenerateKey(rand.Reader)
pfsPublicKey := append(mlkem768DKey.EncapsulationKey().Bytes(), x25519SKey.PublicKey().Bytes()...)
nfsAEAD.Seal(pfsKeyExchange[:18], nil, pfsPublicKey, nil)
padding := clientHello[ivAndRealysLength+pfsKeyExchangeLength:]
nfsAEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
nfsAEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
paddingLens[0] = ivAndRealysLength + pfsKeyExchangeLength + paddingLens[0]
for i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
if l > 0 {
if _, err := conn.Write(clientHello[:l]); err != nil {
return nil, err
}
clientHello = clientHello[l:]
}
if len(paddingGaps) > i {
time.Sleep(paddingGaps[i])
}
}
encryptedPfsPublicKey := make([]byte, 1088+32+16)
if _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {
return nil, err
}
nfsAEAD.Open(encryptedPfsPublicKey[:0], MaxNonce, encryptedPfsPublicKey, nil)
mlkem768Key, err := mlkem768DKey.Decapsulate(encryptedPfsPublicKey[:1088])
if err != nil {
return nil, err
}
peerX25519PKey, err := ecdh.X25519().NewPublicKey(encryptedPfsPublicKey[1088 : 1088+32])
if err != nil {
return nil, err
}
x25519Key, err := x25519SKey.ECDH(peerX25519PKey)
if err != nil {
return nil, err
}
pfsKey := make([]byte, 32+32) // no more capacity
copy(pfsKey, mlkem768Key)
copy(pfsKey[32:], x25519Key)
c.UnitedKey = append(pfsKey, nfsKey...)
c.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES)
c.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1088+32], c.UnitedKey, c.UseAES)
encryptedTicket := make([]byte, 32)
if _, err := io.ReadFull(conn, encryptedTicket); err != nil {
return nil, err
}
if _, err := c.PeerAEAD.Open(encryptedTicket[:0], nil, encryptedTicket, nil); err != nil {
return nil, err
}
seconds := DecodeLength(encryptedTicket)
if i.Seconds > 0 && seconds > 0 {
i.RWLock.Lock()
i.Expire = time.Now().Add(time.Duration(seconds) * time.Second)
i.PfsKey = pfsKey
i.Ticket = encryptedTicket[:16]
i.RWLock.Unlock()
}
encryptedLength := make([]byte, 18)
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
return nil, err
}
if _, err := c.PeerAEAD.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {
return nil, err
}
length := DecodeLength(encryptedLength[:2])
c.PeerPadding = make([]byte, length) // important: allows server sends padding slowly, eliminating 1-RTT's traffic pattern
if i.XorMode == 2 {
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, iv), NewCTR(c.UnitedKey, encryptedTicket[:16]), 0, length)
}
return c, nil
}

View File

@@ -0,0 +1,280 @@
package encryption
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"fmt"
"io"
"net"
"strconv"
"strings"
"sync"
"time"
"github.com/xtls/xray-core/common/crypto"
"github.com/xtls/xray-core/common/errors"
"golang.org/x/crypto/chacha20poly1305"
"lukechampine.com/blake3"
)
var OutBytesPool = sync.Pool{
New: func() any {
return make([]byte, 5+8192+16)
},
}
type CommonConn struct {
net.Conn
UseAES bool
Client *ClientInstance
UnitedKey []byte
PreWrite []byte
AEAD *AEAD
PeerAEAD *AEAD
PeerPadding []byte
rawInput bytes.Buffer
input bytes.Reader
}
func NewCommonConn(conn net.Conn, useAES bool) *CommonConn {
return &CommonConn{
Conn: conn,
UseAES: useAES,
}
}
func (c *CommonConn) Write(b []byte) (int, error) {
if len(b) == 0 {
return 0, nil
}
outBytes := OutBytesPool.Get().([]byte)
defer OutBytesPool.Put(outBytes)
for n := 0; n < len(b); {
b := b[n:]
if len(b) > 8192 {
b = b[:8192] // for avoiding another copy() in peer's Read()
}
n += len(b)
headerAndData := outBytes[:5+len(b)+16]
EncodeHeader(headerAndData, len(b)+16)
max := false
if bytes.Equal(c.AEAD.Nonce[:], MaxNonce) {
max = true
}
c.AEAD.Seal(headerAndData[:5], nil, b, headerAndData[:5])
if max {
c.AEAD = NewAEAD(headerAndData, c.UnitedKey, c.UseAES)
}
if c.PreWrite != nil {
headerAndData = append(c.PreWrite, headerAndData...)
c.PreWrite = nil
}
if _, err := c.Conn.Write(headerAndData); err != nil {
return 0, err
}
}
return len(b), nil
}
func (c *CommonConn) Read(b []byte) (int, error) {
if len(b) == 0 {
return 0, nil
}
if c.PeerAEAD == nil { // client's 0-RTT
serverRandom := make([]byte, 16)
if _, err := io.ReadFull(c.Conn, serverRandom); err != nil {
return 0, err
}
c.PeerAEAD = NewAEAD(serverRandom, c.UnitedKey, c.UseAES)
if xorConn, ok := c.Conn.(*XorConn); ok {
xorConn.PeerCTR = NewCTR(c.UnitedKey, serverRandom)
}
}
if c.PeerPadding != nil { // client's 1-RTT
if _, err := io.ReadFull(c.Conn, c.PeerPadding); err != nil {
return 0, err
}
if _, err := c.PeerAEAD.Open(c.PeerPadding[:0], nil, c.PeerPadding, nil); err != nil {
return 0, err
}
c.PeerPadding = nil
}
if c.input.Len() > 0 {
return c.input.Read(b)
}
peerHeader := [5]byte{}
if _, err := io.ReadFull(c.Conn, peerHeader[:]); err != nil {
return 0, err
}
l, err := DecodeHeader(peerHeader[:]) // l: 17~17000
if err != nil {
if c.Client != nil && strings.Contains(err.Error(), "invalid header: ") { // client's 0-RTT
c.Client.RWLock.Lock()
if bytes.HasPrefix(c.UnitedKey, c.Client.PfsKey) {
c.Client.Expire = time.Now() // expired
}
c.Client.RWLock.Unlock()
return 0, errors.New("new handshake needed")
}
return 0, err
}
c.Client = nil
if c.rawInput.Cap() < l {
c.rawInput.Grow(l) // no need to use sync.Pool, because we are always reading
}
peerData := c.rawInput.Bytes()[:l]
if _, err := io.ReadFull(c.Conn, peerData); err != nil {
return 0, err
}
dst := peerData[:l-16]
if len(dst) <= len(b) {
dst = b[:len(dst)] // avoids another copy()
}
var newAEAD *AEAD
if bytes.Equal(c.PeerAEAD.Nonce[:], MaxNonce) {
newAEAD = NewAEAD(append(peerHeader[:], peerData...), c.UnitedKey, c.UseAES)
}
_, err = c.PeerAEAD.Open(dst[:0], nil, peerData, peerHeader[:])
if newAEAD != nil {
c.PeerAEAD = newAEAD
}
if err != nil {
return 0, err
}
if len(dst) > len(b) {
c.input.Reset(dst[copy(b, dst):])
dst = b // for len(dst)
}
return len(dst), nil
}
type AEAD struct {
cipher.AEAD
Nonce [12]byte
}
func NewAEAD(ctx, key []byte, useAES bool) *AEAD {
k := make([]byte, 32)
blake3.DeriveKey(k, string(ctx), key)
var aead cipher.AEAD
if useAES {
block, _ := aes.NewCipher(k)
aead, _ = cipher.NewGCM(block)
} else {
aead, _ = chacha20poly1305.New(k)
}
return &AEAD{AEAD: aead}
}
func (a *AEAD) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
if nonce == nil {
nonce = IncreaseNonce(a.Nonce[:])
}
return a.AEAD.Seal(dst, nonce, plaintext, additionalData)
}
func (a *AEAD) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
if nonce == nil {
nonce = IncreaseNonce(a.Nonce[:])
}
return a.AEAD.Open(dst, nonce, ciphertext, additionalData)
}
func IncreaseNonce(nonce []byte) []byte {
for i := range 12 {
nonce[11-i]++
if nonce[11-i] != 0 {
break
}
}
return nonce
}
var MaxNonce = bytes.Repeat([]byte{255}, 12)
func EncodeLength(l int) []byte {
return []byte{byte(l >> 8), byte(l)}
}
func DecodeLength(b []byte) int {
return int(b[0])<<8 | int(b[1])
}
func EncodeHeader(h []byte, l int) {
h[0] = 23
h[1] = 3
h[2] = 3
h[3] = byte(l >> 8)
h[4] = byte(l)
}
func DecodeHeader(h []byte) (l int, err error) {
l = int(h[3])<<8 | int(h[4])
if h[0] != 23 || h[1] != 3 || h[2] != 3 {
l = 0
}
if l < 17 || l > 17000 { // TODO: TLSv1.3 max length
err = errors.New("invalid header: " + fmt.Sprintf("%v", h[:5])) // DO NOT CHANGE: relied by client's Read()
}
return
}
func ParsePadding(padding string, paddingLens, paddingGaps *[][3]int) (err error) {
if padding == "" {
return
}
maxLen := 0
for i, s := range strings.Split(padding, ".") {
x := strings.Split(s, "-")
if len(x) < 3 || x[0] == "" || x[1] == "" || x[2] == "" {
return errors.New("invalid padding lenth/gap parameter: " + s)
}
y := [3]int{}
if y[0], err = strconv.Atoi(x[0]); err != nil {
return
}
if y[1], err = strconv.Atoi(x[1]); err != nil {
return
}
if y[2], err = strconv.Atoi(x[2]); err != nil {
return
}
if i == 0 && (y[0] < 100 || y[1] < 18+17 || y[2] < 18+17) {
return errors.New("first padding length must not be smaller than 35")
}
if i%2 == 0 {
*paddingLens = append(*paddingLens, y)
maxLen += max(y[1], y[2])
} else {
*paddingGaps = append(*paddingGaps, y)
}
}
if maxLen > 18+65535 {
return errors.New("total padding length must not be larger than 65553")
}
return
}
func CreatPadding(paddingLens, paddingGaps [][3]int) (length int, lens []int, gaps []time.Duration) {
if len(paddingLens) == 0 {
paddingLens = [][3]int{{100, 111, 1111}, {50, 0, 3333}}
paddingGaps = [][3]int{{75, 0, 111}}
}
for _, y := range paddingLens {
l := 0
if y[0] >= int(crypto.RandBetween(0, 100)) {
l = int(crypto.RandBetween(int64(y[1]), int64(y[2])))
}
lens = append(lens, l)
length += l
}
for _, y := range paddingGaps {
g := 0
if y[0] >= int(crypto.RandBetween(0, 100)) {
g = int(crypto.RandBetween(int64(y[1]), int64(y[2])))
}
gaps = append(gaps, time.Duration(g)*time.Millisecond)
}
return
}

View File

@@ -0,0 +1,328 @@
package encryption
import (
"bytes"
"crypto/cipher"
"crypto/ecdh"
"crypto/mlkem"
"crypto/rand"
"fmt"
"io"
"net"
"sync"
"time"
"github.com/xtls/xray-core/common/crypto"
"github.com/xtls/xray-core/common/errors"
"lukechampine.com/blake3"
)
type ServerSession struct {
PfsKey []byte
NfsKeys sync.Map
}
type ServerInstance struct {
NfsSKeys []any
NfsPKeysBytes [][]byte
Hash32s [][32]byte
RelaysLength int
XorMode uint32
SecondsFrom int64
SecondsTo int64
PaddingLens [][3]int
PaddingGaps [][3]int
RWLock sync.RWMutex
Closed bool
Lasts map[int64][16]byte
Tickets [][16]byte
Sessions map[[16]byte]*ServerSession
}
func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode uint32, secondsFrom, secondsTo int64, padding string) (err error) {
if i.NfsSKeys != nil {
return errors.New("already initialized")
}
l := len(nfsSKeysBytes)
if l == 0 {
return errors.New("empty nfsSKeysBytes")
}
i.NfsSKeys = make([]any, l)
i.NfsPKeysBytes = make([][]byte, l)
i.Hash32s = make([][32]byte, l)
for j, k := range nfsSKeysBytes {
if len(k) == 32 {
if i.NfsSKeys[j], err = ecdh.X25519().NewPrivateKey(k); err != nil {
return
}
i.NfsPKeysBytes[j] = i.NfsSKeys[j].(*ecdh.PrivateKey).PublicKey().Bytes()
i.RelaysLength += 32 + 32
} else {
if i.NfsSKeys[j], err = mlkem.NewDecapsulationKey768(k); err != nil {
return
}
i.NfsPKeysBytes[j] = i.NfsSKeys[j].(*mlkem.DecapsulationKey768).EncapsulationKey().Bytes()
i.RelaysLength += 1088 + 32
}
i.Hash32s[j] = blake3.Sum256(i.NfsPKeysBytes[j])
}
i.RelaysLength -= 32
i.XorMode = xorMode
i.SecondsFrom = secondsFrom
i.SecondsTo = secondsTo
err = ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)
if err != nil {
return
}
if i.SecondsFrom > 0 || i.SecondsTo > 0 {
i.Lasts = make(map[int64][16]byte)
i.Tickets = make([][16]byte, 0, 1024)
i.Sessions = make(map[[16]byte]*ServerSession)
go func() {
for {
time.Sleep(time.Minute)
i.RWLock.Lock()
if i.Closed {
i.RWLock.Unlock()
return
}
minute := time.Now().Unix() / 60
last := i.Lasts[minute]
delete(i.Lasts, minute)
delete(i.Lasts, minute-1) // for insurance
if last != [16]byte{} {
for j, ticket := range i.Tickets {
delete(i.Sessions, ticket)
if ticket == last {
i.Tickets = i.Tickets[j+1:]
break
}
}
}
i.RWLock.Unlock()
}
}()
}
return
}
func (i *ServerInstance) Close() (err error) {
i.RWLock.Lock()
i.Closed = true
i.RWLock.Unlock()
return
}
func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn, error) {
if i.NfsSKeys == nil {
return nil, errors.New("uninitialized")
}
c := NewCommonConn(conn, true)
ivAndRelays := make([]byte, 16+i.RelaysLength)
if _, err := io.ReadFull(conn, ivAndRelays); err != nil {
return nil, err
}
if fallback != nil {
*fallback = append(*fallback, ivAndRelays...)
}
iv := ivAndRelays[:16]
relays := ivAndRelays[16:]
var nfsKey []byte
var lastCTR cipher.Stream
for j, k := range i.NfsSKeys {
if lastCTR != nil {
lastCTR.XORKeyStream(relays, relays[:32]) // recover this relay
}
var index = 32
if _, ok := k.(*mlkem.DecapsulationKey768); ok {
index = 1088
}
if i.XorMode > 0 {
NewCTR(i.NfsPKeysBytes[j], iv).XORKeyStream(relays, relays[:index]) // we don't use buggy elligator2, because we have PSK :)
}
if k, ok := k.(*ecdh.PrivateKey); ok {
publicKey, err := ecdh.X25519().NewPublicKey(relays[:index])
if err != nil {
return nil, err
}
if publicKey.Bytes()[31] > 127 { // we just don't want the observer can change even one bit without breaking the connection, though it has nothing to do with security
return nil, errors.New("the highest bit of the last byte of the peer-sent X25519 public key is not 0")
}
nfsKey, err = k.ECDH(publicKey)
if err != nil {
return nil, err
}
}
if k, ok := k.(*mlkem.DecapsulationKey768); ok {
var err error
nfsKey, err = k.Decapsulate(relays[:index])
if err != nil {
return nil, err
}
}
if j == len(i.NfsSKeys)-1 {
break
}
relays = relays[index:]
lastCTR = NewCTR(nfsKey, iv)
lastCTR.XORKeyStream(relays, relays[:32])
if !bytes.Equal(relays[:32], i.Hash32s[j+1][:]) {
return nil, errors.New("unexpected hash32: ", fmt.Sprintf("%v", relays[:32]))
}
relays = relays[32:]
}
nfsAEAD := NewAEAD(iv, nfsKey, c.UseAES)
encryptedLength := make([]byte, 18)
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
return nil, err
}
if fallback != nil {
*fallback = append(*fallback, encryptedLength...)
}
decryptedLength := make([]byte, 2)
if _, err := nfsAEAD.Open(decryptedLength[:0], nil, encryptedLength, nil); err != nil {
c.UseAES = !c.UseAES
nfsAEAD = NewAEAD(iv, nfsKey, c.UseAES)
if _, err := nfsAEAD.Open(decryptedLength[:0], nil, encryptedLength, nil); err != nil {
return nil, err
}
}
if fallback != nil {
*fallback = nil
}
length := DecodeLength(decryptedLength)
if length == 32 {
if i.SecondsFrom == 0 && i.SecondsTo == 0 {
return nil, errors.New("0-RTT is not allowed")
}
encryptedTicket := make([]byte, 32)
if _, err := io.ReadFull(conn, encryptedTicket); err != nil {
return nil, err
}
ticket, err := nfsAEAD.Open(nil, nil, encryptedTicket, nil)
if err != nil {
return nil, err
}
i.RWLock.RLock()
s := i.Sessions[[16]byte(ticket)]
i.RWLock.RUnlock()
if s == nil {
noises := make([]byte, crypto.RandBetween(1279, 2279)) // matches 1-RTT's server hello length for "random", though it is not important, just for example
var err error
for err == nil {
rand.Read(noises)
_, err = DecodeHeader(noises)
}
conn.Write(noises) // make client do new handshake
return nil, errors.New("expired ticket")
}
if _, loaded := s.NfsKeys.LoadOrStore([32]byte(nfsKey), true); loaded { // prevents bad client also
return nil, errors.New("replay detected")
}
c.UnitedKey = append(s.PfsKey, nfsKey...) // the same nfsKey links the upload & download (prevents server -> client's another request)
c.PreWrite = make([]byte, 16)
rand.Read(c.PreWrite) // always trust yourself, not the client (also prevents being parsed as TLS thus causing false interruption for "native" and "xorpub")
c.AEAD = NewAEAD(c.PreWrite, c.UnitedKey, c.UseAES)
c.PeerAEAD = NewAEAD(encryptedTicket, c.UnitedKey, c.UseAES) // unchangeable ctx (prevents server -> server), and different ctx length for upload / download (prevents client -> client)
if i.XorMode == 2 {
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, c.PreWrite), NewCTR(c.UnitedKey, iv), 16, 0) // it doesn't matter if the attacker sends client's iv back to the client
}
return c, nil
}
if length < 1184+32+16 { // client may send more public keys in the future's version
return nil, errors.New("too short length")
}
encryptedPfsPublicKey := make([]byte, length)
if _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {
return nil, err
}
if _, err := nfsAEAD.Open(encryptedPfsPublicKey[:0], nil, encryptedPfsPublicKey, nil); err != nil {
return nil, err
}
mlkem768EKey, err := mlkem.NewEncapsulationKey768(encryptedPfsPublicKey[:1184])
if err != nil {
return nil, err
}
mlkem768Key, encapsulatedPfsKey := mlkem768EKey.Encapsulate()
peerX25519PKey, err := ecdh.X25519().NewPublicKey(encryptedPfsPublicKey[1184 : 1184+32])
if err != nil {
return nil, err
}
x25519SKey, _ := ecdh.X25519().GenerateKey(rand.Reader)
x25519Key, err := x25519SKey.ECDH(peerX25519PKey)
if err != nil {
return nil, err
}
pfsKey := make([]byte, 32+32) // no more capacity
copy(pfsKey, mlkem768Key)
copy(pfsKey[32:], x25519Key)
pfsPublicKey := append(encapsulatedPfsKey, x25519SKey.PublicKey().Bytes()...)
c.UnitedKey = append(pfsKey, nfsKey...)
c.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES)
c.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1184+32], c.UnitedKey, c.UseAES)
ticket := [16]byte{}
rand.Read(ticket[:])
var seconds int64
if i.SecondsTo == 0 {
seconds = i.SecondsFrom * crypto.RandBetween(50, 100) / 100
} else {
seconds = crypto.RandBetween(i.SecondsFrom, i.SecondsTo)
}
copy(ticket[:], EncodeLength(int(seconds)))
if seconds > 0 {
i.RWLock.Lock()
i.Lasts[(time.Now().Unix()+max(i.SecondsFrom, i.SecondsTo))/60+2] = ticket
i.Tickets = append(i.Tickets, ticket)
i.Sessions[ticket] = &ServerSession{PfsKey: pfsKey}
i.RWLock.Unlock()
}
pfsKeyExchangeLength := 1088 + 32 + 16
encryptedTicketLength := 32
paddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps)
serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)
nfsAEAD.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil)
c.AEAD.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket[:], nil)
padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]
c.AEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
c.AEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
paddingLens[0] = pfsKeyExchangeLength + encryptedTicketLength + paddingLens[0]
for i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
if l > 0 {
if _, err := conn.Write(serverHello[:l]); err != nil {
return nil, err
}
serverHello = serverHello[l:]
}
if len(paddingGaps) > i {
time.Sleep(paddingGaps[i])
}
}
// important: allows client sends padding slowly, eliminating 1-RTT's traffic pattern
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
return nil, err
}
if _, err := nfsAEAD.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {
return nil, err
}
encryptedPadding := make([]byte, DecodeLength(encryptedLength[:2]))
if _, err := io.ReadFull(conn, encryptedPadding); err != nil {
return nil, err
}
if _, err := nfsAEAD.Open(encryptedPadding[:0], nil, encryptedPadding, nil); err != nil {
return nil, err
}
if i.XorMode == 2 {
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, ticket[:]), NewCTR(c.UnitedKey, iv), 0, 0)
}
return c, nil
}

View File

@@ -0,0 +1,93 @@
package encryption
import (
"crypto/aes"
"crypto/cipher"
"net"
"lukechampine.com/blake3"
)
func NewCTR(key, iv []byte) cipher.Stream {
k := make([]byte, 32)
blake3.DeriveKey(k, "VLESS", key) // avoids using key directly
block, _ := aes.NewCipher(k)
return cipher.NewCTR(block, iv)
//chacha20.NewUnauthenticatedCipher()
}
type XorConn struct {
net.Conn
CTR cipher.Stream
PeerCTR cipher.Stream
OutSkip int
OutHeader []byte
InSkip int
InHeader []byte
}
func NewXorConn(conn net.Conn, ctr, peerCTR cipher.Stream, outSkip, inSkip int) *XorConn {
return &XorConn{
Conn: conn,
CTR: ctr,
PeerCTR: peerCTR,
OutSkip: outSkip,
OutHeader: make([]byte, 0, 5), // important
InSkip: inSkip,
InHeader: make([]byte, 0, 5), // important
}
}
func (c *XorConn) Write(b []byte) (int, error) {
if len(b) == 0 {
return 0, nil
}
for p := b; ; {
if len(p) <= c.OutSkip {
c.OutSkip -= len(p)
break
}
p = p[c.OutSkip:]
c.OutSkip = 0
need := 5 - len(c.OutHeader)
if len(p) < need {
c.OutHeader = append(c.OutHeader, p...)
c.CTR.XORKeyStream(p, p)
break
}
c.OutSkip, _ = DecodeHeader(append(c.OutHeader, p[:need]...))
c.OutHeader = c.OutHeader[:0]
c.CTR.XORKeyStream(p[:need], p[:need])
p = p[need:]
}
if _, err := c.Conn.Write(b); err != nil {
return 0, err
}
return len(b), nil
}
func (c *XorConn) Read(b []byte) (int, error) {
if len(b) == 0 {
return 0, nil
}
n, err := c.Conn.Read(b)
for p := b[:n]; ; {
if len(p) <= c.InSkip {
c.InSkip -= len(p)
break
}
p = p[c.InSkip:]
c.InSkip = 0
need := 5 - len(c.InHeader)
if len(p) < need {
c.PeerCTR.XORKeyStream(p, p)
c.InHeader = append(c.InHeader, p...)
break
}
c.PeerCTR.XORKeyStream(p[:need], p[:need])
c.InSkip, _ = DecodeHeader(append(c.InHeader, p[:need]...))
c.InHeader = c.InHeader[:0]
p = p[need:]
}
return n, err
}

View File

@@ -112,10 +112,12 @@ type Config struct {
unknownFields protoimpl.UnknownFields
Clients []*protocol.User `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"`
// Decryption settings. Only applies to server side, and only accepts "none"
// for now.
Decryption string `protobuf:"bytes,2,opt,name=decryption,proto3" json:"decryption,omitempty"`
Fallbacks []*Fallback `protobuf:"bytes,3,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"`
Fallbacks []*Fallback `protobuf:"bytes,2,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"`
Decryption string `protobuf:"bytes,3,opt,name=decryption,proto3" json:"decryption,omitempty"`
XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"`
SecondsFrom int64 `protobuf:"varint,5,opt,name=seconds_from,json=secondsFrom,proto3" json:"seconds_from,omitempty"`
SecondsTo int64 `protobuf:"varint,6,opt,name=seconds_to,json=secondsTo,proto3" json:"seconds_to,omitempty"`
Padding string `protobuf:"bytes,7,opt,name=padding,proto3" json:"padding,omitempty"`
}
func (x *Config) Reset() {
@@ -155,6 +157,13 @@ func (x *Config) GetClients() []*protocol.User {
return nil
}
func (x *Config) GetFallbacks() []*Fallback {
if x != nil {
return x.Fallbacks
}
return nil
}
func (x *Config) GetDecryption() string {
if x != nil {
return x.Decryption
@@ -162,11 +171,32 @@ func (x *Config) GetDecryption() string {
return ""
}
func (x *Config) GetFallbacks() []*Fallback {
func (x *Config) GetXorMode() uint32 {
if x != nil {
return x.Fallbacks
return x.XorMode
}
return nil
return 0
}
func (x *Config) GetSecondsFrom() int64 {
if x != nil {
return x.SecondsFrom
}
return 0
}
func (x *Config) GetSecondsTo() int64 {
if x != nil {
return x.SecondsTo
}
return 0
}
func (x *Config) GetPadding() string {
if x != nil {
return x.Padding
}
return ""
}
var File_proxy_vless_inbound_config_proto protoreflect.FileDescriptor
@@ -185,25 +215,32 @@ var file_proxy_vless_inbound_config_proto_rawDesc = []byte{
0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65,
0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0xa0, 0x01,
0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0x96, 0x02,
0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65,
0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1e,
0x0a, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40,
0x0a, 0x09, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x40,
0x0a, 0x09, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76,
0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x46, 0x61, 0x6c,
0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x09, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73,
0x42, 0x6a, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f,
0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64,
0x50, 0x01, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78,
0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72,
0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e,
0x64, 0xaa, 0x02, 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56,
0x6c, 0x65, 0x73, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e,
0x12, 0x18, 0x0a, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
0x0d, 0x52, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65,
0x63, 0x6f, 0x6e, 0x64, 0x73, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03,
0x52, 0x0b, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x1d, 0x0a,
0x0a, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x5f, 0x74, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28,
0x03, 0x52, 0x09, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x54, 0x6f, 0x12, 0x18, 0x0a, 0x07,
0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70,
0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x6a, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69,
0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63,
0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f,
0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50,
0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75,
0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@@ -19,8 +19,11 @@ message Fallback {
message Config {
repeated xray.common.protocol.User clients = 1;
// Decryption settings. Only applies to server side, and only accepts "none"
// for now.
string decryption = 2;
repeated Fallback fallbacks = 3;
repeated Fallback fallbacks = 2;
string decryption = 3;
uint32 xorMode = 4;
int64 seconds_from = 5;
int64 seconds_to = 6;
string padding = 7;
}

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"context"
gotls "crypto/tls"
"encoding/base64"
"io"
"reflect"
"strconv"
@@ -29,6 +30,8 @@ import (
"github.com/xtls/xray-core/proxy"
"github.com/xtls/xray-core/proxy/vless"
"github.com/xtls/xray-core/proxy/vless/encoding"
"github.com/xtls/xray-core/proxy/vless/encryption"
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet/reality"
"github.com/xtls/xray-core/transport/internet/stat"
"github.com/xtls/xray-core/transport/internet/tls"
@@ -67,6 +70,7 @@ type Handler struct {
policyManager policy.Manager
validator vless.Validator
dns dns.Client
decryption *encryption.ServerInstance
fallbacks map[string]map[string]map[string]*Fallback // or nil
// regexps map[string]*regexp.Regexp // or nil
}
@@ -81,6 +85,19 @@ func New(ctx context.Context, config *Config, dc dns.Client, validator vless.Val
validator: validator,
}
if config.Decryption != "" && config.Decryption != "none" {
s := strings.Split(config.Decryption, ".")
var nfsSKeysBytes [][]byte
for _, r := range s {
b, _ := base64.RawURLEncoding.DecodeString(r)
nfsSKeysBytes = append(nfsSKeysBytes, b)
}
handler.decryption = &encryption.ServerInstance{}
if err := handler.decryption.Init(nfsSKeysBytes, config.XorMode, config.SecondsFrom, config.SecondsTo, config.Padding); err != nil {
return nil, errors.New("failed to use decryption").Base(err).AtError()
}
}
if config.Fallbacks != nil {
handler.fallbacks = make(map[string]map[string]map[string]*Fallback)
// handler.regexps = make(map[string]*regexp.Regexp)
@@ -159,6 +176,9 @@ func isMuxAndNotXUDP(request *protocol.RequestHeader, first *buf.Buffer) bool {
// Close implements common.Closable.Close().
func (h *Handler) Close() error {
if h.decryption != nil {
h.decryption.Close()
}
return errors.Combine(common.Close(h.validator))
}
@@ -199,6 +219,13 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
iConn = statConn.Connection
}
if h.decryption != nil {
var err error
if connection, err = h.decryption.Handshake(connection, nil); err != nil {
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
}
}
sessionPolicy := h.policyManager.ForLevel(0)
if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil {
return errors.New("unable to set read deadline").Base(err).AtWarning()
@@ -219,7 +246,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
var userSentID []byte // not MemoryAccount.ID
var request *protocol.RequestHeader
var requestAddons *encoding.Addons
var requestAddons *proxy.Addons
var err error
napfb := h.fallbacks
@@ -460,8 +487,12 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
account := request.User.Account.(*vless.MemoryAccount)
responseAddons := &encoding.Addons{
// Flow: requestAddons.Flow,
responseAddons := &proxy.Addons{
Flow: account.Flow,
}
encoding.PopulateSeed(account.Seed, responseAddons)
if check := encoding.CheckSeed(requestAddons, responseAddons); check != nil {
return errors.New("Seed configuration mis-match").Base(check).AtWarning()
}
var input *bytes.Reader
@@ -478,7 +509,13 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
case protocol.RequestCommandTCP:
var t reflect.Type
var p uintptr
if tlsConn, ok := iConn.(*tls.Conn); ok {
if commonConn, ok := connection.(*encryption.CommonConn); ok {
if _, ok := commonConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransportWithoutSecurity(iConn) {
inbound.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport / another securityConn should not be penetrated
}
t = reflect.TypeOf(commonConn).Elem()
p = uintptr(unsafe.Pointer(commonConn))
} else if tlsConn, ok := iConn.(*tls.Conn); ok {
if tlsConn.ConnectionState().Version != gotls.VersionTLS13 {
return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, tlsConn.ConnectionState().Version).AtWarning()
}
@@ -519,89 +556,21 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
ctx = session.ContextWithAllowedNetwork(ctx, net.Network_UDP)
}
sessionPolicy = h.policyManager.ForLevel(request.User.Level)
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
inbound.Timer = timer
ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer)
link, err := dispatcher.Dispatch(ctx, request.Destination())
if err != nil {
return errors.New("failed to dispatch request to ", request.Destination()).Base(err).AtWarning()
}
serverReader := link.Reader // .(*pipe.Reader)
serverWriter := link.Writer // .(*pipe.Writer)
trafficState := proxy.NewTrafficState(userSentID)
postRequest := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
// default: clientReader := reader
clientReader := encoding.DecodeBodyAddons(reader, request, requestAddons)
var err error
if requestAddons.Flow == vless.XRV {
ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice
clientReader = proxy.NewVisionReader(clientReader, trafficState, true, ctx1)
err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, input, rawInput, trafficState, nil, true, ctx1)
} else {
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
}
if err != nil {
return errors.New("failed to transfer request payload").Base(err).AtInfo()
}
return nil
}
getResponse := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
trafficState := proxy.NewTrafficState(userSentID, account.Flow)
clientReader := encoding.DecodeBodyAddons(reader, request, responseAddons, trafficState, true, ctx, connection, input, rawInput, nil)
bufferWriter := buf.NewBufferedWriter(buf.NewWriter(connection))
if err := encoding.EncodeResponseHeader(bufferWriter, request, responseAddons); err != nil {
return errors.New("failed to encode response header").Base(err).AtWarning()
}
clientWriter := encoding.EncodeBodyAddons(bufferWriter, request, responseAddons, trafficState, false, ctx, connection, nil)
bufferWriter.SetFlushNext()
// default: clientWriter := bufferWriter
clientWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons, trafficState, false, ctx)
multiBuffer, err1 := serverReader.ReadMultiBuffer()
if err1 != nil {
return err1 // ...
if err := dispatcher.DispatchLink(ctx, request.Destination(), &transport.Link{
Reader: clientReader,
Writer: clientWriter},
); err != nil {
return errors.New("failed to dispatch request").Base(err)
}
if err := clientWriter.WriteMultiBuffer(multiBuffer); err != nil {
return err // ...
}
// Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer
if err := bufferWriter.SetBuffered(false); err != nil {
return errors.New("failed to write A response payload").Base(err).AtWarning()
}
var err error
if requestAddons.Flow == vless.XRV {
err = encoding.XtlsWrite(serverReader, clientWriter, timer, connection, trafficState, nil, false, ctx)
} else {
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
}
if err != nil {
return errors.New("failed to transfer response payload").Base(err).AtInfo()
}
// Indicates the end of response payload.
switch responseAddons.Flow {
default:
}
return nil
}
if err := task.Run(ctx, task.OnSuccess(postRequest, task.Close(serverWriter)), getResponse); err != nil {
common.Interrupt(serverReader)
common.Interrupt(serverWriter)
return errors.New("connection ends").Base(err).AtInfo()
}
return nil
}

View File

@@ -4,7 +4,9 @@ import (
"bytes"
"context"
gotls "crypto/tls"
"encoding/base64"
"reflect"
"strings"
"time"
"unsafe"
@@ -24,6 +26,7 @@ import (
"github.com/xtls/xray-core/proxy"
"github.com/xtls/xray-core/proxy/vless"
"github.com/xtls/xray-core/proxy/vless/encoding"
"github.com/xtls/xray-core/proxy/vless/encryption"
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/reality"
@@ -43,6 +46,7 @@ type Handler struct {
serverPicker protocol.ServerPicker
policyManager policy.Manager
cone bool
encryption *encryption.ClientInstance
}
// New creates a new VLess outbound handler.
@@ -64,6 +68,20 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
cone: ctx.Value("cone").(bool),
}
a := handler.serverPicker.PickServer().PickUser().Account.(*vless.MemoryAccount)
if a.Encryption != "" && a.Encryption != "none" {
s := strings.Split(a.Encryption, ".")
var nfsPKeysBytes [][]byte
for _, r := range s {
b, _ := base64.RawURLEncoding.DecodeString(r)
nfsPKeysBytes = append(nfsPKeysBytes, b)
}
handler.encryption = &encryption.ClientInstance{}
if err := handler.encryption.Init(nfsPKeysBytes, a.XorMode, a.Seconds, a.Padding); err != nil {
return nil, errors.New("failed to use encryption").Base(err).AtError()
}
}
return handler, nil
}
@@ -98,6 +116,13 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
target := ob.Target
errors.LogInfo(ctx, "tunneling request to ", target, " via ", rec.Destination().NetAddr())
if h.encryption != nil {
var err error
if conn, err = h.encryption.Handshake(conn); err != nil {
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
}
}
command := protocol.RequestCommandTCP
if target.Network == net.Network_UDP {
command = protocol.RequestCommandUDP
@@ -116,9 +141,10 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
account := request.User.Account.(*vless.MemoryAccount)
requestAddons := &encoding.Addons{
requestAddons := &proxy.Addons{
Flow: account.Flow,
}
encoding.PopulateSeed(account.Seed, requestAddons)
var input *bytes.Reader
var rawInput *bytes.Buffer
@@ -140,7 +166,13 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
case protocol.RequestCommandTCP:
var t reflect.Type
var p uintptr
if tlsConn, ok := iConn.(*tls.Conn); ok {
if commonConn, ok := conn.(*encryption.CommonConn); ok {
if _, ok := commonConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransportWithoutSecurity(iConn) {
ob.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport / another securityConn should not be penetrated
}
t = reflect.TypeOf(commonConn).Elem()
p = uintptr(unsafe.Pointer(commonConn))
} else if tlsConn, ok := iConn.(*tls.Conn); ok {
t = reflect.TypeOf(tlsConn.Conn).Elem()
p = uintptr(unsafe.Pointer(tlsConn.Conn))
} else if utlsConn, ok := iConn.(*tls.UConn); ok {
@@ -178,7 +210,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
clientReader := link.Reader // .(*pipe.Reader)
clientWriter := link.Writer // .(*pipe.Writer)
trafficState := proxy.NewTrafficState(account.ID.Bytes())
trafficState := proxy.NewTrafficState(account.ID.Bytes(), account.Flow)
if request.Command == protocol.RequestCommandUDP && (requestAddons.Flow == vless.XRV || (h.cone && request.Port != 53 && request.Port != 443)) {
request.Command = protocol.RequestCommandMux
request.Address = net.DomainAddress("v1.mux.cool")
@@ -194,7 +226,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
}
// default: serverWriter := bufferWriter
serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons, trafficState, true, ctx)
serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons, trafficState, true, ctx, conn, ob)
if request.Command == protocol.RequestCommandMux && request.Port == 666 {
serverWriter = xudp.NewPacketWriter(serverWriter, target, xudp.GetGlobalID(ctx))
}
@@ -222,7 +254,6 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
return errors.New("failed to write A request payload").Base(err).AtWarning()
}
var err error
if requestAddons.Flow == vless.XRV {
if tlsConn, ok := iConn.(*tls.Conn); ok {
if tlsConn.ConnectionState().Version != gotls.VersionTLS13 {
@@ -233,12 +264,8 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, utlsConn.ConnectionState().Version).AtWarning()
}
}
ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice
err = encoding.XtlsWrite(clientReader, serverWriter, timer, conn, trafficState, ob, true, ctx1)
} else {
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
}
err := buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
if err != nil {
return errors.New("failed to transfer request payload").Base(err).AtInfo()
}
@@ -259,20 +286,13 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
}
// default: serverReader := buf.NewReader(conn)
serverReader := encoding.DecodeBodyAddons(conn, request, responseAddons)
if requestAddons.Flow == vless.XRV {
serverReader = proxy.NewVisionReader(serverReader, trafficState, false, ctx)
}
serverReader := encoding.DecodeBodyAddons(conn, request, responseAddons, trafficState, false, ctx, conn, input, rawInput, ob)
if request.Command == protocol.RequestCommandMux && request.Port == 666 {
if requestAddons.Flow == vless.XRV {
serverReader = xudp.NewPacketReader(&buf.BufferedReader{Reader: serverReader})
} else {
serverReader = xudp.NewPacketReader(conn)
}
}
if requestAddons.Flow == vless.XRV {
err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, input, rawInput, trafficState, ob, false, ctx)
err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, trafficState, false, ctx)
} else {
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))

View File

@@ -129,7 +129,8 @@ func (h *Handler) processWireGuard(ctx context.Context, dialer internet.Dialer)
}
defer func() {
if err != nil {
_ = h.bind.Close()
h.bind.Close()
h.bind = nil
}
}()

View File

@@ -249,7 +249,7 @@ func TestZeroBuffer(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*20))
}
if err := errg.Wait(); err != nil {

View File

@@ -182,7 +182,7 @@ func TestReverseProxy(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 32; i++ {
for range 32 {
errg.Go(testTCPConn(externalPort, 10240*1024, time.Second*40))
}
@@ -374,7 +374,7 @@ func TestReverseProxyLongRunning(t *testing.T) {
defer CloseAllServers(servers)
for i := 0; i < 4096; i++ {
for range 4096 {
if err := testTCPConn(externalPort, 1024, time.Second*20)(); err != nil {
t.Error(err)
}

View File

@@ -124,7 +124,7 @@ func testShadowsocks2022Tcp(t *testing.T, method string, password string) {
defer CloseAllServers(servers)
var errGroup errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errGroup.Go(testTCPConn(clientPort, 10240*1024, time.Second*20))
}
@@ -207,7 +207,7 @@ func testShadowsocks2022Udp(t *testing.T, method string, password string) {
defer CloseAllServers(servers)
var errGroup errgroup.Group
for i := 0; i < 2; i++ {
for range 3 {
errGroup.Go(testUDPConn(udpClientPort, 1024, time.Second*5))
}

View File

@@ -96,7 +96,7 @@ func TestShadowsocksChaCha20Poly1305TCP(t *testing.T) {
defer CloseAllServers(servers)
var errGroup errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errGroup.Go(testTCPConn(clientPort, 10240*1024, time.Second*20))
}
if err := errGroup.Wait(); err != nil {
@@ -192,7 +192,7 @@ func TestShadowsocksAES256GCMTCP(t *testing.T) {
defer CloseAllServers(servers)
var errGroup errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errGroup.Go(testTCPConn(clientPort, 10240*1024, time.Second*20))
}
@@ -289,7 +289,7 @@ func TestShadowsocksAES128GCMUDP(t *testing.T) {
defer CloseAllServers(servers)
var errGroup errgroup.Group
for i := 0; i < 2; i++ {
for range 3 {
errGroup.Go(testUDPConn(clientPort, 1024, time.Second*5))
}
if err := errGroup.Wait(); err != nil {
@@ -391,7 +391,7 @@ func TestShadowsocksAES128GCMUDPMux(t *testing.T) {
defer CloseAllServers(servers)
var errGroup errgroup.Group
for i := 0; i < 2; i++ {
for range 3 {
errGroup.Go(testUDPConn(clientPort, 1024, time.Second*5))
}
if err := errGroup.Wait(); err != nil {
@@ -477,7 +477,7 @@ func TestShadowsocksNone(t *testing.T) {
defer CloseAllServers(servers)
var errGroup errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errGroup.Go(testTCPConn(clientPort, 10240*1024, time.Second*20))
}

View File

@@ -240,7 +240,7 @@ func TestAutoIssuingCertificate(t *testing.T) {
common.Must(err)
defer CloseAllServers(servers)
for i := 0; i < 10; i++ {
for range 3 {
if err := testTCPConn(clientPort, 1024, time.Second*20)(); err != nil {
t.Error(err)
}
@@ -449,7 +449,7 @@ func TestTLSOverWebSocket(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*20))
}
if err := errg.Wait(); err != nil {
@@ -565,7 +565,7 @@ func TestGRPC(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 1024*10240, time.Second*40))
}
if err := errg.Wait(); err != nil {
@@ -681,7 +681,7 @@ func TestGRPCMultiMode(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 1024*10240, time.Second*40))
}
if err := errg.Wait(); err != nil {

View File

@@ -116,6 +116,104 @@ func TestVless(t *testing.T) {
common.Must(err)
defer CloseAllServers(servers)
var errg errgroup.Group
for range 3 {
errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30))
}
if err := errg.Wait(); err != nil {
t.Error(err)
}
}
func TestVlessSeedWithIndependentScheduler(t *testing.T) {
tcpServer := tcp.Server{
MsgProcessor: xor,
}
dest, err := tcpServer.Start()
common.Must(err)
defer tcpServer.Close()
userID := protocol.NewID(uuid.New())
serverPort := tcp.PickPort()
serverConfig := &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(&log.Config{
ErrorLogLevel: clog.Severity_Debug,
ErrorLogType: log.LogType_Console,
}),
},
Inbound: []*core.InboundHandlerConfig{
{
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
Listen: net.NewIPOrDomain(net.LocalHostIP),
}),
ProxySettings: serial.ToTypedMessage(&inbound.Config{
Clients: []*protocol.User{
{
Account: serial.ToTypedMessage(&vless.Account{
Id: userID.String(),
Seed: "1",
}),
},
},
}),
},
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
},
},
}
clientPort := tcp.PickPort()
clientConfig := &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(&log.Config{
ErrorLogLevel: clog.Severity_Debug,
ErrorLogType: log.LogType_Console,
}),
},
Inbound: []*core.InboundHandlerConfig{
{
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}},
Listen: net.NewIPOrDomain(net.LocalHostIP),
}),
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
Address: net.NewIPOrDomain(dest.Address),
Port: uint32(dest.Port),
Networks: []net.Network{net.Network_TCP},
}),
},
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&outbound.Config{
Vnext: []*protocol.ServerEndpoint{
{
Address: net.NewIPOrDomain(net.LocalHostIP),
Port: uint32(serverPort),
User: []*protocol.User{
{
Account: serial.ToTypedMessage(&vless.Account{
Id: userID.String(),
Seed: "1",
}),
},
},
},
},
}),
},
},
}
servers, err := InitializeServerConfigs(serverConfig, clientConfig)
common.Must(err)
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 10; i++ {
errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30))
@@ -239,7 +337,7 @@ func TestVlessTls(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30))
}
if err := errg.Wait(); err != nil {
@@ -362,6 +460,132 @@ func TestVlessXtlsVision(t *testing.T) {
common.Must(err)
defer CloseAllServers(servers)
var errg errgroup.Group
for range 3 {
errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30))
}
if err := errg.Wait(); err != nil {
t.Error(err)
}
}
func TestVlessXtlsVisionWithSeed(t *testing.T) {
tcpServer := tcp.Server{
MsgProcessor: xor,
}
dest, err := tcpServer.Start()
common.Must(err)
defer tcpServer.Close()
userID := protocol.NewID(uuid.New())
serverPort := tcp.PickPort()
serverConfig := &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(&log.Config{
ErrorLogLevel: clog.Severity_Debug,
ErrorLogType: log.LogType_Console,
}),
},
Inbound: []*core.InboundHandlerConfig{
{
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},
Listen: net.NewIPOrDomain(net.LocalHostIP),
StreamSettings: &internet.StreamConfig{
ProtocolName: "tcp",
SecurityType: serial.GetMessageType(&tls.Config{}),
SecuritySettings: []*serial.TypedMessage{
serial.ToTypedMessage(&tls.Config{
Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil))},
}),
},
},
}),
ProxySettings: serial.ToTypedMessage(&inbound.Config{
Clients: []*protocol.User{
{
Account: serial.ToTypedMessage(&vless.Account{
Id: userID.String(),
Flow: vless.XRV,
Seed: "1",
}),
},
},
}),
},
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
},
},
}
clientPort := tcp.PickPort()
clientConfig := &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(&log.Config{
ErrorLogLevel: clog.Severity_Debug,
ErrorLogType: log.LogType_Console,
}),
},
Inbound: []*core.InboundHandlerConfig{
{
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}},
Listen: net.NewIPOrDomain(net.LocalHostIP),
}),
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
Address: net.NewIPOrDomain(dest.Address),
Port: uint32(dest.Port),
Networks: []net.Network{net.Network_TCP},
}),
},
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&outbound.Config{
Vnext: []*protocol.ServerEndpoint{
{
Address: net.NewIPOrDomain(net.LocalHostIP),
Port: uint32(serverPort),
User: []*protocol.User{
{
Account: serial.ToTypedMessage(&vless.Account{
Id: userID.String(),
Flow: vless.XRV,
Seed: "1",
}),
},
},
},
},
}),
SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
StreamSettings: &internet.StreamConfig{
ProtocolName: "tcp",
TransportSettings: []*internet.TransportConfig{
{
ProtocolName: "tcp",
Settings: serial.ToTypedMessage(&transtcp.Config{}),
},
},
SecurityType: serial.GetMessageType(&tls.Config{}),
SecuritySettings: []*serial.TypedMessage{
serial.ToTypedMessage(&tls.Config{
AllowInsecure: true,
}),
},
},
}),
},
},
}
servers, err := InitializeServerConfigs(serverConfig, clientConfig)
common.Must(err)
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 10; i++ {
errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30))
@@ -502,7 +726,7 @@ func TestVlessXtlsVisionReality(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 1; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30))
}
if err := errg.Wait(); err != nil {

View File

@@ -260,7 +260,7 @@ func TestVMessGCM(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40))
}
@@ -366,7 +366,7 @@ func TestVMessGCMReadv(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40))
}
if err := errg.Wait(); err != nil {
@@ -465,7 +465,7 @@ func TestVMessGCMUDP(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 2; i++ {
for range 3 {
errg.Go(testUDPConn(clientPort, 1024, time.Second*5))
}
if err := errg.Wait(); err != nil {
@@ -564,7 +564,7 @@ func TestVMessChacha20(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*20))
}
@@ -664,7 +664,7 @@ func TestVMessNone(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30))
}
if err := errg.Wait(); err != nil {
@@ -771,7 +771,7 @@ func TestVMessKCP(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 2; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 1024, time.Minute*2))
}
if err := errg.Wait(); err != nil {
@@ -915,7 +915,7 @@ func TestVMessKCPLarge(t *testing.T) {
common.Must(err)
var errg errgroup.Group
for i := 0; i < 2; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 513*1024, time.Minute*5))
}
if err := errg.Wait(); err != nil {
@@ -1026,7 +1026,7 @@ func TestVMessGCMMux(t *testing.T) {
for range "abcd" {
var errg errgroup.Group
for i := 0; i < 16; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 10240, time.Second*20))
}
if err := errg.Wait(); err != nil {
@@ -1152,7 +1152,7 @@ func TestVMessGCMMuxUDP(t *testing.T) {
for range "ab" {
var errg errgroup.Group
for i := 0; i < 2; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 1024, time.Second*10))
errg.Go(testUDPConn(clientUDPPort, 1024, time.Second*10))
}
@@ -1259,7 +1259,7 @@ func TestVMessZero(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*30))
}
if err := errg.Wait(); err != nil {
@@ -1361,7 +1361,7 @@ func TestVMessGCMLengthAuth(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40))
}
@@ -1465,7 +1465,7 @@ func TestVMessGCMLengthAuthPlusNoTerminationSignal(t *testing.T) {
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 10; i++ {
for range 3 {
errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40))
}

View File

@@ -22,8 +22,24 @@ type ResponseCallback func(ctx context.Context, packet *udp.Packet)
type connEntry struct {
link *transport.Link
timer signal.ActivityUpdater
timer *signal.ActivityTimer
cancel context.CancelFunc
closed bool
}
func (c *connEntry) Close() error {
c.timer.SetTimeout(0)
return nil
}
func (c *connEntry) terminate() {
if c.closed {
panic("terminate called more than once")
}
c.closed = true
c.cancel()
common.Interrupt(c.link.Reader)
common.Interrupt(c.link.Writer)
}
type Dispatcher struct {
@@ -32,6 +48,7 @@ type Dispatcher struct {
dispatcher routing.Dispatcher
callback ResponseCallback
callClose func() error
closed bool
}
func NewDispatcher(dispatcher routing.Dispatcher, callback ResponseCallback) *Dispatcher {
@@ -44,13 +61,9 @@ func NewDispatcher(dispatcher routing.Dispatcher, callback ResponseCallback) *Di
func (v *Dispatcher) RemoveRay() {
v.Lock()
defer v.Unlock()
v.removeRay()
}
func (v *Dispatcher) removeRay() {
v.closed = true
if v.conn != nil {
common.Interrupt(v.conn.link.Reader)
common.Close(v.conn.link.Writer)
v.conn.Close()
v.conn = nil
}
}
@@ -59,35 +72,34 @@ func (v *Dispatcher) getInboundRay(ctx context.Context, dest net.Destination) (*
v.Lock()
defer v.Unlock()
if v.closed {
return nil, errors.New("dispatcher is closed")
}
if v.conn != nil {
if v.conn.closed {
v.conn = nil
} else {
return v.conn, nil
}
}
errors.LogInfo(ctx, "establishing new connection for ", dest)
ctx, cancel := context.WithCancel(ctx)
entry := &connEntry{}
removeRay := func() {
v.Lock()
defer v.Unlock()
// sometimes the entry is already removed by others, don't close again
if entry == v.conn {
cancel()
v.removeRay()
}
}
timer := signal.CancelAfterInactivity(ctx, removeRay, time.Minute)
link, err := v.dispatcher.Dispatch(ctx, dest)
if err != nil {
cancel()
return nil, errors.New("failed to dispatch request to ", dest).Base(err)
}
*entry = connEntry{
entry := &connEntry{
link: link,
timer: timer,
cancel: removeRay,
cancel: cancel,
}
entry.timer = signal.CancelAfterInactivity(ctx, entry.terminate, time.Minute)
v.conn = entry
go handleInput(ctx, entry, dest, v.callback, v.callClose)
return entry, nil
@@ -106,7 +118,7 @@ func (v *Dispatcher) Dispatch(ctx context.Context, destination net.Destination,
if outputStream != nil {
if err := outputStream.WriteMultiBuffer(buf.MultiBuffer{payload}); err != nil {
errors.LogInfoInner(ctx, err, "failed to write first UDP payload")
conn.cancel()
conn.Close()
return
}
}
@@ -114,7 +126,7 @@ func (v *Dispatcher) Dispatch(ctx context.Context, destination net.Destination,
func handleInput(ctx context.Context, conn *connEntry, dest net.Destination, callback ResponseCallback, callClose func() error) {
defer func() {
conn.cancel()
conn.Close()
if callClose != nil {
callClose()
}

View File

@@ -200,16 +200,19 @@ func (p *pipe) Interrupt() {
p.Lock()
defer p.Unlock()
if !p.data.IsEmpty() {
buf.ReleaseMulti(p.data)
p.data = nil
if p.state == closed {
p.state = errord
}
}
if p.state == closed || p.state == errord {
return
}
p.state = errord
if !p.data.IsEmpty() {
buf.ReleaseMulti(p.data)
p.data = nil
}
common.Must(p.done.Close())
}