Compare commits

..

97 Commits

Author SHA1 Message Date
patterniha
c56e1650a1 remove unused 2025-08-21 12:57:52 +03:30
patterniha
6be9c4a429 remove UDP cache 2025-08-21 11:39:15 +03:30
dependabot[bot]
87b643a388 Bump google.golang.org/grpc from 1.74.2 to 1.75.0 (#5039)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.74.2 to 1.75.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.74.2...v1.75.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.75.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-20 14:15:01 +00:00
patterniha
6d1695a686 Some refines related to direct/freedom and targetStrategy; More intelligent "useIP"/"ForceIP", enhance "origin" functionality (#5030)
https://github.com/XTLS/Xray-core/pull/5009#issuecomment-3194264277
2025-08-20 01:19:13 +03:30
Skh-web6982
3a54924045 Chore: Optimize .gitignore (#5029) 2025-08-19 13:58:06 +00:00
风扇滑翔翼
573300bc22 Issues template: Refine requirements 2025-08-19 09:27:25 +00:00
RPRX
7f300dbf0c VLESS practice: Use user-sent VLESS UUID's 7th<<8 | 8th bytes as vlessRoute instead
https://github.com/XTLS/Xray-core/pull/5009#issuecomment-3195718690

Replaces 105b306d07
2025-08-18 08:50:43 +00:00
RPRX
5464862ee6 XTLS Vision inbound: Use user-sent VLESS UUID for NewTrafficState()
Fixes https://github.com/XTLS/Xray-core/pull/5009#issuecomment-3194523018
2025-08-17 18:13:56 +00:00
yuhan6665
337b4b814e Wireguard inbound: Fix context sharing problem (#4988)
* Try fix Wireguard inbound context sharing problem

* Shallow copy inbound and content

* Fix context passing

* Add notes for source address
2025-08-17 10:56:48 -04:00
RPRX
105b306d07 VLESS practice: Use user-sent VLESS UUID's last byte as vlessRoute for routing rules
https://github.com/XTLS/Xray-core/pull/5009#issuecomment-3194262087
2025-08-17 13:55:18 +00:00
RPRX
de23e51077 DNS outbound: Set "reject" as the default value for nonIPQuery
https://github.com/XTLS/Xray-core/pull/4903#issuecomment-3091367130
2025-08-16 19:12:59 +00:00
patterniha
40ce850bd9 Direct/Freedom config: Add targetStrategy as an alias of domainStrategy; Routing config: Remove domainMatcher, "linear" and type (#5027)
https://github.com/XTLS/Xray-core/pull/5006#issuecomment-3193547307
2025-08-16 14:54:10 +00:00
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇
2485f4831f Build: Use more aggressive inlining for higher efficiency (#5026)
https://github.com/XTLS/Xray-core/pull/4952#issuecomment-3189847357
2025-08-16 11:11:00 +00:00
Jesus
aac0d6a6a5 VLESS inbound: Add option to set default flow (#5023)
Closes https://github.com/XTLS/Xray-core/issues/4994
2025-08-15 23:07:33 +00:00
patterniha
f557bf7da4 Outbound: Add targetStrategy; Fix mux does not close link.Reader; Fix origin does not work on UDP; Add logs (#5006) 2025-08-15 20:51:36 +00:00
风扇滑翔翼
6fc0a40c2a XHTTP client: Fix edge-case issue for packet-up mode (#5020)
https://github.com/XTLS/Xray-core/pull/4952#issuecomment-3184080580
2025-08-15 18:01:15 +00:00
风扇滑翔翼
f3cdcad541 common/buf/multi_buffer.go: Fix Compact() (#5015)
Fixes https://github.com/XTLS/Xray-core/issues/5012

Co-authored-by: patterniha <71074308+patterniha@users.noreply.github.com>
2025-08-15 15:27:12 +00:00
风扇滑翔翼
5a8e9c25a4 Chore: Migrate to Go 1.25 (#5024)
* Try to update to go1.25

* Remove unsafe usage
2025-08-15 14:50:35 +00:00
dependabot[bot]
836b6487e4 Bump actions/checkout from 4 to 5 (#5016)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-13 21:46:42 +00:00
风扇滑翔翼
b1107b9810 Refine must2 and apply NewAesGcm() to all usage (#5011)
* Refine must2 and apply NewAesGcm() to all usage

* Remove unused package

* Fix test
2025-08-10 21:37:46 -04:00
dependabot[bot]
0cceea75da Bump golang.org/x/net from 0.42.0 to 0.43.0 (#5002)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.42.0 to 0.43.0.
- [Commits](https://github.com/golang/net/compare/v0.42.0...v0.43.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.43.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-08 00:37:51 +00:00
dependabot[bot]
4b21c9aed3 Bump google.golang.org/protobuf from 1.36.6 to 1.36.7 (#5001)
Bumps google.golang.org/protobuf from 1.36.6 to 1.36.7.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-08 00:37:37 +00:00
patterniha
cde6e33ec9 Freedom: Add maxSplit fragment option; Add applyTo noises option (#4998) 2025-08-07 23:56:09 +00:00
patterniha
5dce7e4e25 Router: Add localIP and localPort; Add sourceIP as an alias of source (#4992)
https://github.com/XTLS/Xray-core/discussions/4984#discussioncomment-13990687

Closes https://github.com/XTLS/Xray-core/issues/4993
2025-08-07 23:50:39 +00:00
LjhAUMEM
9359844149 feat(api): update timestamp for existing IPs in AddIP instead of skipping (#4989)
Co-authored-by: null <null>
2025-08-05 14:02:51 -04:00
LjhAUMEM
8222f43eea API: Fix user online map remain 1 after connection dropped (#4982)
* c.value

* remove value

* c.access.RLock()

* remove local var "list"

---------

Co-authored-by: null <null>
2025-08-04 23:21:40 -04:00
dependabot[bot]
04e6439b51 Bump github.com/miekg/dns from 1.1.67 to 1.1.68 (#4980)
Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.67 to 1.1.68.
- [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release)
- [Commits](https://github.com/miekg/dns/compare/v1.1.67...v1.1.68)

---
updated-dependencies:
- dependency-name: github.com/miekg/dns
  dependency-version: 1.1.68
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-04 15:42:05 -04:00
RPRX
bd86732f68 v25.8.3
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-08-03 11:09:00 +00:00
风扇滑翔翼
d4f11e6d68 UDS: Check address before listen (#4945)
Fixes https://github.com/XTLS/Xray-core/issues/4944
2025-08-03 11:02:55 +00:00
风扇滑翔翼
00f3147242 app/proxyman/inbound/inbound.go: Fix ListHandlers() (#4976)
Fixes https://github.com/XTLS/Xray-core/issues/4956
2025-08-03 11:02:08 +00:00
风扇滑翔翼
7cbf5b004c TLS ECH client: echForceQuery "full" / "half" / "none" (default) (#4973)
https://github.com/XTLS/Xray-core/pull/4971#issuecomment-3148113203
2025-08-03 10:15:42 +00:00
patterniha
87fff12fd9 Root config: Add version config (min and max) (#4970)
https://github.com/XTLS/Xray-core/pull/4968#issuecomment-3146592323
2025-08-03 01:06:47 +00:00
patterniha
a02723e63f TLS ECH client: Use chrome-fingerprint and add padding; Add "h2c" and echSockopt; Fix some issues (#4949)
Completes https://github.com/XTLS/Xray-core/pull/3813
2025-08-02 16:05:00 +00:00
RPRX
146b14ab55 Tunnel inbound: Add portMap config (local listening port -> remote specified address/port)
Completes https://github.com/XTLS/Xray-core/pull/4968
2025-08-02 14:58:56 +00:00
风扇滑翔翼
b2829219a0 TLS ECH client: Add echForceQuery config (#4947)
https://github.com/XTLS/Xray-core/pull/4947#issuecomment-3124359776
2025-08-01 11:25:15 +00:00
RPRX
116cd70a3a Dokodemo-door: Add simple tunnel config (alias and default values) (#4968)
dokodemo-door -> tunnel
freedom -> direct
blackhole -> block

https://github.com/XTLS/Xray-core/discussions/4966#discussioncomment-13948546
https://github.com/XTLS/Xray-core/pull/4967#issuecomment-3143951306
2025-08-01 11:20:53 +00:00
yuhan6665
c569f478af Update readme 2025-07-27 19:02:27 -04:00
RPRX
b6b51c51c8 v25.7.26
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-07-26 09:07:56 +00:00
风扇滑翔翼
fb7a9d8d61 TLS client & server: Support Encrypted Client Hello (ECH) (#3813)
b9a72a4a26

---------

Co-authored-by: yuhan6665 <1588741+yuhan6665@users.noreply.github.com>
2025-07-26 08:47:27 +00:00
nobody
3fe02a658a Commands: Add adu/rmu inbound user management to API (#4943)
Co-authored-by: nobody <nobody@nowhere.mars>
2025-07-26 08:40:04 +00:00
RPRX
5f93ff6c3a REALITY config: mldsa65Seed and privateKey can not be the same value
Otherwise the point of using ML-DSA-65 is lost.
2025-07-26 08:36:58 +00:00
patterniha
10376f5b4d Freedom UDP: Fix some cone uses like STUN,... when address is domain (#4942)
https://github.com/XTLS/Xray-core/issues/2962#issuecomment-3120472154
2025-07-26 01:59:15 +00:00
风扇滑翔翼
1ea00fad81 UDP listener: Allow listening on "localhost" (#4940)
Fixes https://github.com/XTLS/Xray-core/issues/4939
2025-07-26 01:27:35 +00:00
RPRX
cfcf2a63d1 v25.7.25
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-07-25 14:59:43 +00:00
Meow
66025f2889 Workflows: Fix github.ref_name sometimes is empty when building Docker images (#4937)
From https://github.com/XTLS/Xray-core/pull/4809
2025-07-25 14:47:12 +00:00
风扇滑翔翼
c9cd26d6d3 UDP: Remove removeRay()'s error log (#4936)
From https://github.com/XTLS/Xray-core/pull/4899
2025-07-25 14:40:26 +00:00
RPRX
caee152adf Update github.com/xtls/reality to 20250725142056
5b52a03d4f
2025-07-25 14:28:47 +00:00
风扇滑翔翼
eb433d9462 Inbounds & Outbounds: TCP KeepAlive better default value (#4931)
From https://github.com/XTLS/Xray-core/pull/4927
2025-07-25 12:06:05 +00:00
风扇滑翔翼
87d8b97d9a Commands: Output certificate chain's total length in tls ping (#4933)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-07-25 10:18:26 +00:00
风扇滑翔翼
9d15ecf1f9 REALITY client: Fix log when printing "is using X25519MLKEM768..." (#4929) 2025-07-25 07:11:35 +00:00
RPRX
4f45c5faa5 v25.7.24
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-07-24 02:19:08 +00:00
RPRX
26de58933f README.md: Update Donation & NFTs
https://opensea.io/collection/xtls
2025-07-24 02:08:03 +00:00
RPRX
31b508d372 REALITY config: Convert mldsa65Seed to its private key later 2025-07-24 01:31:07 +00:00
风扇滑翔翼
955a569181 REALITY config: Allow mldsa65 fields to be empty (#4924) 2025-07-24 01:15:48 +00:00
RPRX
d141d01d0c v25.7.23
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-07-23 12:55:05 +00:00
patterniha
4e826abebf Chore: Three small fixes (#4922) 2025-07-23 12:53:37 +00:00
RPRX
4433641e30 Update github.com/xtls/reality to 20250723121014
c6320729d9
2025-07-23 12:39:10 +00:00
RPRX
a196a16c55 README.md: Add Project X NFT's image & link
https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1
2025-07-23 12:21:40 +00:00
Stan B.
8c0bf15901 README.md: Add GoXRay to macOS & Linux Clients (#4260)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-07-23 12:17:18 +00:00
Happ-dev
dbd9125686 README.md: Add Happ to macOS x64 & tvOS Clients (#4921)
Co-authored-by: y.sivushkin <y.sivushkin@corp.101xp.com>
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-07-23 10:52:59 +00:00
j2rong4cn
923b5d7229 DNS hosts: Support returning RCode (#4681) 2025-07-23 10:11:43 +00:00
风扇滑翔翼
f90fae22aa UDP: Fix removeRay will close a connEntry that not belongs to it (#4899) 2025-07-23 10:10:12 +00:00
patterniha
b065595f58 Reverse: portal-worker should not be closed before making sure there is at least one other active worker (#4869) 2025-07-23 10:09:17 +00:00
patterniha
308f8a7459 MUX: Refine and Fix some occasional problems (#4861)
Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2025-07-23 10:09:04 +00:00
patterniha
050f596e8f Freedom: Fix UDP reply mismatch-address (#4816) 2025-07-23 10:07:24 +00:00
风扇滑翔翼
3b47d0846e Freedom: Cache UDP resolve result (#4804) 2025-07-23 10:02:27 +00:00
dependabot[bot]
7f23a1cb65 Bump google.golang.org/grpc from 1.73.0 to 1.74.2 (#4919)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.73.0 to 1.74.2.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.73.0...v1.74.2)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.74.2
  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-07-23 02:37:58 +00:00
RPRX
446315cf1f REALITY protocol: Add optional Post-Quantum ML-DSA-65 verification for cert's ExtraExtensions (#4915)
00881f6740
2025-07-23 02:29:11 +00:00
RPRX
eed05549fc Revert "Bump google.golang.org/grpc from 1.73.0 to 1.74.0 (#4905)" (#4914)
This reverts commit 6afd721ced.
2025-07-21 04:55:43 +00:00
dependabot[bot]
2b4a8d235b Bump github.com/quic-go/quic-go from 0.53.0 to 0.54.0 (#4913)
---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-version: 0.54.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-07-21 02:19:47 +00:00
风扇滑翔翼
83686ebfaa DNS outbound: Prevent panic from rejecting invalid domain (#4903)
Fixes https://github.com/XTLS/Xray-core/pull/4824#issuecomment-3078811352
2025-07-19 01:52:26 +00:00
Meow
79c6f99384 Workflows: Cleaner Docker builds, support for manual exec and pre-release (#4809) 2025-07-19 01:29:25 +00:00
Random Guy
ca8ef209a7 Stats API: Return status "not found" instead of "unknown" (#4860) 2025-07-19 01:21:18 +00:00
风扇滑翔翼
cbcab89c7e Commands: Display Post-Quantum key exchange in tls ping (#4857)
https://github.com/XTLS/Xray-core/pull/4857#issuecomment-3064964301
2025-07-19 01:14:56 +00:00
xqzr
abd551e9f7 VLESS fallbacks: dest defaults to "127.0.0.1" -> "localhost" (#4840)
https://github.com/XTLS/Xray-examples/issues/234#issuecomment-3091319391
2025-07-19 00:47:43 +00:00
o_O
10dbeb4335 README.md: Add AnyPortal to GUI Clients (#4902) 2025-07-19 00:33:46 +00:00
dependabot[bot]
6afd721ced Bump google.golang.org/grpc from 1.73.0 to 1.74.0 (#4905)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.73.0 to 1.74.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.73.0...v1.74.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.74.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-07-19 00:27:18 +00:00
dependabot[bot]
5c0bc361d3 Bump golang.org/x/net from 0.41.0 to 0.42.0 (#4892)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.41.0 to 0.42.0.
- [Commits](https://github.com/golang/net/compare/v0.41.0...v0.42.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.42.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-07-19 00:27:00 +00:00
xDragonZ
3a2ac9d0bf Bump quic-go to v0.53.0 & update codes (#4906) 2025-07-19 00:21:18 +00:00
RPRX
1785178762 REALITY server: Three types of ALPN for post-handshake records detection & imitation; Two fixes
https://github.com/XTLS/Xray-core/issues/4778#issuecomment-3072047745

Closes https://github.com/XTLS/Xray-core/issues/4788

---------

Fixes https://github.com/XTLS/Xray-core/issues/4843

Fixes https://github.com/XTLS/Xray-core/issues/4845
2025-07-19 00:06:59 +00:00
dependabot[bot]
1976d02ec9 Bump golang.org/x/sys from 0.33.0 to 0.34.0 (#4882)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.33.0 to 0.34.0.
- [Commits](https://github.com/golang/sys/compare/v0.33.0...v0.34.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-version: 0.34.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-07-11 13:01:47 -04:00
dependabot[bot]
3ba733079e Bump golang.org/x/crypto from 0.39.0 to 0.40.0 (#4885)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.39.0 to 0.40.0.
- [Commits](https://github.com/golang/crypto/compare/v0.39.0...v0.40.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.40.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-07-11 13:01:32 -04:00
dependabot[bot]
6a8a85f83a Bump golang.org/x/sync from 0.15.0 to 0.16.0 (#4881)
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.15.0 to 0.16.0.
- [Commits](https://github.com/golang/sync/compare/v0.15.0...v0.16.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-version: 0.16.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-07-11 12:55:05 -04:00
dependabot[bot]
409e4e8f12 Bump github.com/miekg/dns from 1.1.66 to 1.1.67 (#4880)
Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.66 to 1.1.67.
- [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release)
- [Commits](https://github.com/miekg/dns/compare/v1.1.66...v1.1.67)

---
updated-dependencies:
- dependency-name: github.com/miekg/dns
  dependency-version: 1.1.67
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-11 12:54:33 -04:00
Jesus
486d005986 API: add option to fetch only tags from ListInbounds (#4870)
* feat: add new method for get only inbound tags from core. ListTags.

* refactor: simplify creating response.

* refactor: move getting tags in already exist method via option.
2025-07-11 12:52:51 -04:00
isluckys
cb1afb33e6 common: fix task leak in timer (#4831)
signal包里面SetTimeout方法并发时可能会出现task close以后执行start导致泄露
2025-06-23 08:47:27 -04:00
风扇滑翔翼
38ed2cc387 DNS: Add new nonIPQuery "reject" (#4824) 2025-06-22 22:48:24 -04:00
fL1pSt3r
b043db8260 API: Fix issue with inbounduser not finding emails with uppercase letters (#4818) 2025-06-22 21:57:02 -04:00
Jesus
27742da2c6 BurstObservatory: add option to set http method for burst check (#4835)
* feat: add options to set method for burst check.

* chore: gen proto.

* chore: change protoc-gen-go to latest.

* revert

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2025-06-22 21:48:49 -04:00
RPRX
fbae89d017 v25.6.8
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-06-08 13:53:03 +00:00
ketetefid
58c28b4aeb README.md: Add GorzRay to Linux Clients (#4767) 2025-06-08 13:51:39 +00:00
Yury Kastov
ca1c4b63f6 README.md: Add xtls-sdk to Xray Wrapper (#4793)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-06-08 13:47:23 +00:00
Meow
18ab291e0c REALITY: Add rate limiting to fallback handling via token bucket (#4553)
https://github.com/XTLS/REALITY/pull/12
2025-06-08 13:43:55 +00:00
RPRX
e011b746dc RAW transport hub.go: Call REALITY's DetectPostHandshakeRecordsLens() in advance
https://github.com/XTLS/Xray-core/issues/4778#issuecomment-2953455751
2025-06-08 05:01:31 +00:00
TonyMa1
f4b23c6565 README.md: Add DeepWiki badge to Contributing (#4777)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-06-08 04:51:10 +00:00
Meow
7d36cad3e0 Docker: Fix geodata directory permissions issue (#4790)
Fixes https://github.com/XTLS/Xray-core/issues/4747#issuecomment-2953519012
2025-06-08 04:49:26 +00:00
Meow
a576a4b183 Tests: Real fix for TestCommanderListHandlers (#4792)
Completes 402067d281
2025-06-08 04:47:04 +00:00
136 changed files with 3595 additions and 1547 deletions

View File

@@ -7,6 +7,8 @@ body:
description: |- description: |-
Please check all of the following options to prove that you have read and understood the requirements, otherwise this issue will be closed. Please check all of the following options to prove that you have read and understood the requirements, otherwise this issue will be closed.
options: options:
- label: I have read all the comments in the issue template and ensured that this issue meet the requirements.
required: true
- label: I confirm that I have read the documentation, understand the meaning of all the configuration items I wrote, and did not pile up seemingly useful options or default values. - label: I confirm that I have read the documentation, understand the meaning of all the configuration items I wrote, and did not pile up seemingly useful options or default values.
required: true required: true
- label: I provided the complete config and logs, rather than just providing the truncated parts based on my own judgment. - label: I provided the complete config and logs, rather than just providing the truncated parts based on my own judgment.
@@ -38,6 +40,8 @@ body:
### For config ### For config
Please provide the configuration files that can reproduce the problem, including the server and client. Please provide the configuration files that can reproduce the problem, including the server and client.
Don't just paste a big exported config file here. Eliminate useless inbound/outbound, rules, options, this can help determine the problem, if you really want to get help. Don't just paste a big exported config file here. Eliminate useless inbound/outbound, rules, options, this can help determine the problem, if you really want to get help.
After removing parts that do not affect reproduction, provide the actual running **complete** file.
meaning of complete: This config can be directly used to start the core, **not a truncated part of the config**. For fields like keys, use newly generated valid parameters that have not been actually used to fill in.
### For logs ### For logs
Please set the log level to debug and dnsLog to true first. Please set the log level to debug and dnsLog to true first.
@@ -46,42 +50,29 @@ body:
Provide the log of Xray-core, not the log output by the panel or other things. Provide the log of Xray-core, not the log output by the panel or other things.
### Finally ### Finally
After removing parts that do not affect reproduction, provide the actual running **complete** file, do not only provide inbound or outbound or a few lines of logs based on your own judgment. The specific content to be filled in each of the following text boxes needs to be placed between ```<details><pre><code>``` and ```</code></pre></details>```, like this
Put the content between the preset ```<details><pre><code>``` ```</code></pre></details>``` in the text box. ```
If the problem is very clear that only related to one end (such as core startup failure/crash after correctly writing the config according to the documents), N/A can be filled in for unnecessary areas below. <details><pre><code>
(config)
</code></pre></details>
```
- type: textarea - type: textarea
attributes: attributes:
label: Client config label: Client config
value: |-
<details><pre><code>
</code></pre></details>
validations: validations:
required: true required: true
- type: textarea - type: textarea
attributes: attributes:
label: Server config label: Server config
value: |-
<details><pre><code>
</code></pre></details>
validations: validations:
required: true required: true
- type: textarea - type: textarea
attributes: attributes:
label: Client log label: Client log
value: |-
<details><pre><code>
</code></pre></details>
validations: validations:
required: true required: true
- type: textarea - type: textarea
attributes: attributes:
label: Server log label: Server log
value: |-
<details><pre><code>
</code></pre></details>
validations: validations:
required: true required: true

View File

@@ -7,6 +7,8 @@ body:
description: |- description: |-
请勾选以下所有选项以证明您已经阅读并理解了以下要求,否则该 issue 将被关闭。 请勾选以下所有选项以证明您已经阅读并理解了以下要求,否则该 issue 将被关闭。
options: options:
- label: 我读完了 issue 模板中的所有注释,确保填写符合要求。
required: true
- label: 我保证阅读了文档,了解所有我编写的配置文件项的含义,而不是大量堆砌看似有用的选项或默认值。 - label: 我保证阅读了文档,了解所有我编写的配置文件项的含义,而不是大量堆砌看似有用的选项或默认值。
required: true required: true
- label: 我提供了完整的配置文件和日志,而不是出于自己的判断只给出截取的部分。 - label: 我提供了完整的配置文件和日志,而不是出于自己的判断只给出截取的部分。
@@ -38,6 +40,8 @@ body:
### 对于配置文件 ### 对于配置文件
请提供可以重现问题的配置文件,包括服务端和客户端。 请提供可以重现问题的配置文件,包括服务端和客户端。
不要直接在这里黏贴一大段导出的 config 文件。去掉无用的出入站、规则、选项,这可以帮助确定问题,如果你真的想得到帮助。 不要直接在这里黏贴一大段导出的 config 文件。去掉无用的出入站、规则、选项,这可以帮助确定问题,如果你真的想得到帮助。
在去掉不影响复现的部分后,提供实际运行的**完整**文件。
完整的含义:可以直接使用这个配置启动核心,**不是截取的部分配置**。对于密钥等参数使用重新生成未实际使用的有效参数填充。
### 对于日志 ### 对于日志
请先将日志等级设置为 debug, dnsLog 设置为true. 请先将日志等级设置为 debug, dnsLog 设置为true.
@@ -46,42 +50,29 @@ body:
提供 Xray-core 的日志,而不是面板或者别的东西输出的日志。 提供 Xray-core 的日志,而不是面板或者别的东西输出的日志。
### 最后 ### 最后
在去掉不影响复现的部分后,提供实际运行的**完整**文件,不要出于自己的判断只提供入站出站或者几行日志。 把下面的每格具体内容需要放在 ```<details><pre><code>``` 和 ```</code></pre></details>``` 中间,如
把内容放在文本框预置的 ```<details><pre><code>``` 和 ```</code></pre></details>``` 中间。 ```
如果问题十分明确只出现在某一端(如按文档正确编写配置后核心启动失败/崩溃)可以在下面不需要的项目填入N/A. <details><pre><code>
(config)
</code></pre></details>
```
- type: textarea - type: textarea
attributes: attributes:
label: 客户端配置 label: 客户端配置
value: |-
<details><pre><code>
</code></pre></details>
validations: validations:
required: true required: true
- type: textarea - type: textarea
attributes: attributes:
label: 服务端配置 label: 服务端配置
value: |-
<details><pre><code>
</code></pre></details>
validations: validations:
required: true required: true
- type: textarea - type: textarea
attributes: attributes:
label: 客户端日志 label: 客户端日志
value: |-
<details><pre><code>
</code></pre></details>
validations: validations:
required: true required: true
- type: textarea - type: textarea
attributes: attributes:
label: 服务端日志 label: 服务端日志
value: |-
<details><pre><code>
</code></pre></details>
validations: validations:
required: true required: true

View File

@@ -45,6 +45,7 @@ RUN mkdir -p /tmp/var/log/xray && touch \
FROM gcr.io/distroless/static:nonroot FROM gcr.io/distroless/static:nonroot
COPY --from=build --chown=0:0 --chmod=755 /src/xray /usr/local/bin/xray COPY --from=build --chown=0:0 --chmod=755 /src/xray /usr/local/bin/xray
COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /usr/local/share/xray
COPY --from=build --chown=0:0 --chmod=644 /tmp/geodat/*.dat /usr/local/share/xray/ COPY --from=build --chown=0:0 --chmod=644 /tmp/geodat/*.dat /usr/local/share/xray/
COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /usr/local/etc/xray COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /usr/local/etc/xray
COPY --from=build --chown=0:0 --chmod=644 /tmp/usr/local/etc/xray/*.json /usr/local/etc/xray/ COPY --from=build --chown=0:0 --chmod=644 /tmp/usr/local/etc/xray/*.json /usr/local/etc/xray/

View File

@@ -54,6 +54,7 @@ RUN mkdir -p /tmp/var/log/xray && touch \
FROM --platform=linux/amd64 gcr.io/distroless/static:nonroot FROM --platform=linux/amd64 gcr.io/distroless/static:nonroot
COPY --from=build --chown=0:0 --chmod=755 /src/xray /usr/local/bin/xray COPY --from=build --chown=0:0 --chmod=755 /src/xray /usr/local/bin/xray
COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /usr/local/share/xray
COPY --from=build --chown=0:0 --chmod=644 /tmp/geodat/*.dat /usr/local/share/xray/ COPY --from=build --chown=0:0 --chmod=644 /tmp/geodat/*.dat /usr/local/share/xray/
COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /usr/local/etc/xray COPY --from=build --chown=0:0 --chmod=755 /tmp/empty /usr/local/etc/xray
COPY --from=build --chown=0:0 --chmod=644 /tmp/usr/local/etc/xray/*.json /usr/local/etc/xray/ COPY --from=build --chown=0:0 --chmod=644 /tmp/usr/local/etc/xray/*.json /usr/local/etc/xray/

View File

@@ -1,36 +1,77 @@
name: Build docker image name: Build and Push Docker Image
on: on:
release: release:
types: [published] types:
- published
- released
workflow_dispatch:
inputs:
tag:
description: "Docker image tag:"
required: true
latest:
description: "Set to latest"
type: boolean
default: false
jobs: jobs:
build-image: build-and-push:
if: (github.event.action != 'published') || (github.event.action == 'published' && github.event.release.prerelease == true)
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: read
packages: write packages: write
steps: steps:
- uses: actions/checkout@v4 - name: Set repository and image name to lowercase
env:
IMAGE_NAME: "${{ github.repository }}"
run: |
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
- name: Docker metadata - name: Validate and extract tag
id: meta run: |
uses: docker/metadata-action@v5 SOURCE_TAG="${{ github.event.inputs.tag }}"
with: if [[ -z "$SOURCE_TAG" ]]; then
images: ghcr.io/${{ github.repository_owner }}/xray-core SOURCE_TAG="${{ github.ref_name }}"
flavor: latest=auto fi
tags: | if [[ -z "$SOURCE_TAG" ]]; then
type=semver,pattern={{version}} SOURCE_TAG="${{ github.event.release.tag_name }}"
fi
- name: Docker metadata (unsupported architectures) if [[ -z "$SOURCE_TAG" ]]; then
id: metausa echo "Error: Could not determine a valid tag source. Input tag and context tag (github.ref_name) are both empty."
uses: docker/metadata-action@v5 exit 1
with: fi
images: ghcr.io/${{ github.repository_owner }}/xray-core
flavor: | if [[ "$SOURCE_TAG" =~ ^v[0-9]+\.[0-9] ]]; then
latest=auto IMAGE_TAG="${SOURCE_TAG#v}"
suffix=-usa,onlatest=true else
tags: | IMAGE_TAG="$SOURCE_TAG"
type=semver,pattern={{version}} fi
echo "Docker image tag: '$IMAGE_TAG'."
echo "IMAGE_TAG=$IMAGE_TAG" >>${GITHUB_ENV}
LATEST=false
if [[ "${{ github.event_name }}" == "release" && "${{ github.event.release.prerelease }}" == "false" ]] || [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.latest }}" == "true" ]]; then
LATEST=true
fi
echo "Latest: '$LATEST'."
echo "LATEST=$LATEST" >>${GITHUB_ENV}
- name: Checkout code
uses: actions/checkout@v5
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v3
@@ -39,13 +80,12 @@ jobs:
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx - name: Build Docker image (main architectures)
uses: docker/setup-buildx-action@v3 id: build_main_arches
- name: Build and push
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
context: . context: .
file: .github/docker/Dockerfile
platforms: | platforms: |
linux/amd64 linux/amd64
linux/arm/v7 linux/arm/v7
@@ -53,39 +93,41 @@ jobs:
linux/ppc64le linux/ppc64le
linux/s390x linux/s390x
provenance: false provenance: false
file: .github/docker/Dockerfile outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
push: true
tags: ${{ steps.meta.outputs.tags }}
- name: Build and push (unsupported architectures) - name: Build Docker image (additional architectures)
id: build_additional_arches
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
context: . context: .
file: .github/docker/Dockerfile.usa
platforms: | platforms: |
linux/386 linux/386
linux/arm/v6 linux/arm/v6
linux/riscv64 linux/riscv64
linux/loong64 linux/loong64
provenance: false provenance: false
file: .github/docker/Dockerfile.usa outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
push: true
tags: ${{ steps.metausa.outputs.tags }}
- name: Merge Multi-Arch Manifests - name: Create manifest list and push
run: | run: |
echo "Starting to merge multi-architecture manifests..." echo "Creating multi-arch manifest with tag: '${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}'."
docker buildx imagetools create \
--tag ${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }} \
${{ env.FULL_IMAGE_NAME }}@${{ steps.build_main_arches.outputs.digest }} \
${{ env.FULL_IMAGE_NAME }}@${{ steps.build_additional_arches.outputs.digest }}
# Convert newlines to spaces and split into array if [[ "${{ env.LATEST }}" == "true" ]]; then
TAGS=($(echo "${{ steps.meta.outputs.tags }}" | tr '\n' ' ')) echo "Adding 'latest' tag to manifest: '${{ env.FULL_IMAGE_NAME }}:latest'."
docker buildx imagetools create \
--tag ${{ env.FULL_IMAGE_NAME }}:latest \
${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}
fi
echo "Total tags to process: ${#TAGS[@]}" - name: Inspect image
for tag in "${TAGS[@]}"; do run: |
echo "Merging tag: $tag with unsupported architectures ($tag-usa)" docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}
docker buildx imagetools create --append --tag "$tag" "$tag-usa"
if [ $? -ne 0 ]; then
echo "Error: Failed to merge $tag-usa into $tag"
exit 1
fi
done
echo "Multi-architecture manifest merge completed successfully." if [[ "${{ env.LATEST }}" == "true" ]]; then
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:latest
fi

View File

@@ -63,7 +63,7 @@ jobs:
CGO_ENABLED: 0 CGO_ENABLED: 0
steps: steps:
- name: Checkout codebase - name: Checkout codebase
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Show workflow information - name: Show workflow information
run: | run: |
@@ -94,11 +94,11 @@ jobs:
mkdir -p build_assets mkdir -p build_assets
COMMID=$(git describe --always --dirty) COMMID=$(git describe --always --dirty)
echo 'Building Xray for Windows 7...' echo 'Building Xray for Windows 7...'
go build -o build_assets/xray.exe -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main go build -o build_assets/xray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
echo 'CreateObject("Wscript.Shell").Run "xray.exe -config config.json",0' > build_assets/xray_no_window.vbs echo 'CreateObject("Wscript.Shell").Run "xray.exe -config config.json",0' > build_assets/xray_no_window.vbs
echo 'Start-Process -FilePath ".\xray.exe" -ArgumentList "-config .\config.json" -WindowStyle Hidden' > build_assets/xray_no_window.ps1 echo 'Start-Process -FilePath ".\xray.exe" -ArgumentList "-config .\config.json" -WindowStyle Hidden' > build_assets/xray_no_window.ps1
# The line below is for without running conhost.exe version. Commented for not being used. Provided for reference. # The line below is for without running conhost.exe version. Commented for not being used. Provided for reference.
# go build -o build_assets/wxray.exe -trimpath -buildvcs=false -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main # go build -o build_assets/wxray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
- name: Restore Geodat Cache - name: Restore Geodat Cache
uses: actions/cache/restore@v4 uses: actions/cache/restore@v4

View File

@@ -153,7 +153,7 @@ jobs:
CGO_ENABLED: 0 CGO_ENABLED: 0
steps: steps:
- name: Checkout codebase - name: Checkout codebase
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Set up NDK - name: Set up NDK
if: matrix.goos == 'android' if: matrix.goos == 'android'
@@ -190,17 +190,19 @@ jobs:
COMMID=$(git describe --always --dirty) COMMID=$(git describe --always --dirty)
if [[ ${GOOS} == 'windows' ]]; then if [[ ${GOOS} == 'windows' ]]; then
echo 'Building Xray for Windows...' echo 'Building Xray for Windows...'
go build -o build_assets/xray.exe -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main go build -o build_assets/xray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
echo 'CreateObject("Wscript.Shell").Run "xray.exe -config config.json",0' > build_assets/xray_no_window.vbs echo 'CreateObject("Wscript.Shell").Run "xray.exe -config config.json",0' > build_assets/xray_no_window.vbs
echo 'Start-Process -FilePath ".\xray.exe" -ArgumentList "-config .\config.json" -WindowStyle Hidden' > build_assets/xray_no_window.ps1 echo 'Start-Process -FilePath ".\xray.exe" -ArgumentList "-config .\config.json" -WindowStyle Hidden' > build_assets/xray_no_window.ps1
# The line below is for without running conhost.exe version. Commented for not being used. Provided for reference. # The line below is for without running conhost.exe version. Commented for not being used. Provided for reference.
# go build -o build_assets/wxray.exe -trimpath -buildvcs=false -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main # go build -o build_assets/wxray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
else else
echo 'Building Xray...' echo 'Building Xray...'
go build -o build_assets/xray -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
if [[ ${GOARCH} == 'mips' || ${GOARCH} == 'mipsle' ]]; then if [[ ${GOARCH} == 'mips' || ${GOARCH} == 'mipsle' ]]; then
go build -o build_assets/xray -trimpath -buildvcs=false -gcflags="-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
echo 'Building soft-float Xray for MIPS/MIPSLE 32-bit...' echo 'Building soft-float Xray for MIPS/MIPSLE 32-bit...'
GOMIPS=softfloat go build -o build_assets/xray_softfloat -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main GOMIPS=softfloat go build -o build_assets/xray_softfloat -trimpath -buildvcs=false -gcflags="-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
else
go build -o build_assets/xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
fi fi
fi fi

View File

@@ -45,7 +45,7 @@ jobs:
os: [windows-latest, ubuntu-latest, macos-latest] os: [windows-latest, ubuntu-latest, macos-latest]
steps: steps:
- name: Checkout codebase - name: Checkout codebase
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:

40
.gitignore vendored
View File

@@ -11,21 +11,23 @@
# Output of the go coverage tool, specifically when used with LiteIDE # Output of the go coverage tool, specifically when used with LiteIDE
*.out *.out
# Dependency directories (remove the comment below to include it)
# vendor/
# macOS specific files # macOS specific files
*.DS_Store .DS_Store
# IDE specific files # IDE/editor specific files
.idea/ .idea/
.vscode/ .vscode/
*.swp
*.swo
# Archive files # Archives and compressed files
*.zip *.zip
*.tar.gz *.tar.gz
*.tar
*.gz
*.bz2
# Binaries # Go build binaries
xray xray
xray_softfloat xray_softfloat
mockgen mockgen
@@ -36,11 +38,31 @@ errorgen
*.dat *.dat
# Build assets # Build assets
/build_assets /build_assets/
# Output from dlv test # Output from dlv test
**/debug.* **/debug.*
# Certificates # Certificates and keys
*.crt *.crt
*.key *.key
# Dependency directories (uncomment if needed)
# vendor/
# Logs
*.log
# Coverage reports
coverage.*
# Node modules (in case of frontend assets)
node_modules/
# System files
Thumbs.db
ehthumbs.db
# Other common ignores
*.bak
*.tmp

View File

@@ -6,9 +6,13 @@
## Donation & NFTs ## Donation & NFTs
### [Collect a Project X NFT to support the development of Project X!](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1)
[<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`** - **ETH/USDT/USDC: `0xDc3Fe44F0f25D13CACb1C4896CD0D321df3146Ee`**
- **Project X NFT: [Announcement of NFTs by Project X](https://github.com/XTLS/Xray-core/discussions/3633)** - **REALITY NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2**
- **REALITY NFT: [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113)** - **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)**
## License ## License
@@ -81,27 +85,35 @@
- [v2rayN](https://github.com/2dust/v2rayN) - [v2rayN](https://github.com/2dust/v2rayN)
- [Furious](https://github.com/LorenEteval/Furious) - [Furious](https://github.com/LorenEteval/Furious)
- [Invisible Man - Xray](https://github.com/InvisibleManVPN/InvisibleMan-XRayClient) - [Invisible Man - Xray](https://github.com/InvisibleManVPN/InvisibleMan-XRayClient)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- Android - Android
- [v2rayNG](https://github.com/2dust/v2rayNG) - [v2rayNG](https://github.com/2dust/v2rayNG)
- [X-flutter](https://github.com/XTLS/X-flutter) - [X-flutter](https://github.com/XTLS/X-flutter)
- [SaeedDev94/Xray](https://github.com/SaeedDev94/Xray) - [SaeedDev94/Xray](https://github.com/SaeedDev94/Xray)
- [SimpleXray](https://github.com/lhear/SimpleXray) - [SimpleXray](https://github.com/lhear/SimpleXray)
- iOS & macOS arm64 - [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) - iOS & macOS arm64 & tvOS
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) ([tvOS](https://apps.apple.com/us/app/happ-proxy-utility-for-tv/id6748297274))
- [Streisand](https://apps.apple.com/app/streisand/id6450534064) - [Streisand](https://apps.apple.com/app/streisand/id6450534064)
- [OneXray](https://github.com/OneXray/OneXray) - [OneXray](https://github.com/OneXray/OneXray)
- macOS arm64 & x64 - macOS arm64 & x64
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215)
- [V2rayU](https://github.com/yanue/V2rayU) - [V2rayU](https://github.com/yanue/V2rayU)
- [V2RayXS](https://github.com/tzmax/V2RayXS) - [V2RayXS](https://github.com/tzmax/V2RayXS)
- [Furious](https://github.com/LorenEteval/Furious) - [Furious](https://github.com/LorenEteval/Furious)
- [OneXray](https://github.com/OneXray/OneXray) - [OneXray](https://github.com/OneXray/OneXray)
- [GoXRay](https://github.com/goxray/desktop)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- Linux - Linux
- [v2rayA](https://github.com/v2rayA/v2rayA) - [v2rayA](https://github.com/v2rayA/v2rayA)
- [Furious](https://github.com/LorenEteval/Furious) - [Furious](https://github.com/LorenEteval/Furious)
- [GorzRay](https://github.com/ketetefid/GorzRay)
- [GoXRay](https://github.com/goxray/desktop)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
## Others that support VLESS, XTLS, REALITY, XUDP, PLUX... ## Others that support VLESS, XTLS, REALITY, XUDP, PLUX...
- iOS & macOS arm64 - iOS & macOS arm64 & tvOS
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118) - [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
- [Loon](https://apps.apple.com/us/app/loon/id1373567447) - [Loon](https://apps.apple.com/us/app/loon/id1373567447)
- Xray Tools - Xray Tools
@@ -109,6 +121,7 @@
- [xray-checker](https://github.com/kutovoys/xray-checker) - [xray-checker](https://github.com/kutovoys/xray-checker)
- Xray Wrapper - Xray Wrapper
- [XTLS/libXray](https://github.com/XTLS/libXray) - [XTLS/libXray](https://github.com/XTLS/libXray)
- [xtls-sdk](https://github.com/remnawave/xtls-sdk)
- [xtlsapi](https://github.com/hiddify/xtlsapi) - [xtlsapi](https://github.com/hiddify/xtlsapi)
- [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite) - [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite)
- [Xray-core-python](https://github.com/LorenEteval/Xray-core-python) - [Xray-core-python](https://github.com/LorenEteval/Xray-core-python)
@@ -125,6 +138,8 @@
[Code of Conduct](https://github.com/XTLS/Xray-core/blob/main/CODE_OF_CONDUCT.md) [Code of Conduct](https://github.com/XTLS/Xray-core/blob/main/CODE_OF_CONDUCT.md)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/XTLS/Xray-core)
## Credits ## Credits
- [Xray-core v1.0.0](https://github.com/XTLS/Xray-core/releases/tag/v1.0.0) was forked from [v2fly-core 9a03cc5](https://github.com/v2fly/v2ray-core/commit/9a03cc5c98d04cc28320fcee26dbc236b3291256), and we have made & accumulated a huge number of enhancements over time, check [the release notes for each version](https://github.com/XTLS/Xray-core/releases). - [Xray-core v1.0.0](https://github.com/XTLS/Xray-core/releases/tag/v1.0.0) was forked from [v2fly-core 9a03cc5](https://github.com/v2fly/v2ray-core/commit/9a03cc5c98d04cc28320fcee26dbc236b3291256), and we have made & accumulated a huge number of enhancements over time, check [the release notes for each version](https://github.com/XTLS/Xray-core/releases).
@@ -150,7 +165,13 @@ CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -ldflags="-s -w -buildi
Make sure that you are using the same Go version, and remember to set the git commit id (7 bytes): Make sure that you are using the same Go version, and remember to set the git commit id (7 bytes):
```bash ```bash
CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid=" -v ./main CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid=" -v ./main
```
If you are compiling a 32-bit MIPS/MIPSLE target, use this command instead:
```bash
CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid=" -v ./main
``` ```
## Stargazers over time ## Stargazers over time

View File

@@ -204,7 +204,12 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er
} }
// Static host lookup // Static host lookup
switch addrs := s.hosts.Lookup(domain, option); { switch addrs, err := s.hosts.Lookup(domain, option); {
case err != nil:
if go_errors.Is(err, dns.ErrEmptyResponse) {
return nil, 0, dns.ErrEmptyResponse
}
return nil, 0, errors.New("returning nil for domain ", domain).Base(err)
case addrs == nil: // Domain not recorded in static host case addrs == nil: // Domain not recorded in static host
break break
case len(addrs) == 0: // Domain recorded, but no valid IP returned (e.g. IPv4 address with only IPv6 enabled) case len(addrs) == 0: // Domain recorded, but no valid IP returned (e.g. IPv4 address with only IPv6 enabled)

View File

@@ -42,12 +42,15 @@ func (r *IPRecord) getIPs() ([]net.IP, uint32, error) {
if r == nil { if r == nil {
return nil, 0, errRecordNotFound return nil, 0, errRecordNotFound
} }
untilExpire := time.Until(r.Expire) untilExpire := time.Until(r.Expire).Seconds()
if untilExpire <= 0 { if untilExpire <= 0 {
return nil, 0, errRecordNotFound return nil, 0, errRecordNotFound
} }
ttl := uint32(untilExpire/time.Second) + uint32(1) ttl := uint32(untilExpire) + 1
if ttl == 1 {
r.Expire = time.Now().Add(time.Second) // To ensure that two consecutive requests get the same result
}
if r.RCode != dnsmessage.RCodeSuccess { if r.RCode != dnsmessage.RCodeSuccess {
return nil, ttl, dns_feature.RCodeError(r.RCode) return nil, ttl, dns_feature.RCodeError(r.RCode)
} }

View File

@@ -18,31 +18,31 @@ func Test_parseResponse(t *testing.T) {
ans := new(dns.Msg) ans := new(dns.Msg)
ans.Id = 0 ans.Id = 0
p = append(p, common.Must2(ans.Pack()).([]byte)) p = append(p, common.Must2(ans.Pack()))
p = append(p, []byte{}) p = append(p, []byte{})
ans = new(dns.Msg) ans = new(dns.Msg)
ans.Id = 1 ans.Id = 1
ans.Answer = append(ans.Answer, ans.Answer = append(ans.Answer,
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")).(dns.RR), common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")).(dns.RR), common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")),
common.Must2(dns.NewRR("google.com. IN A 8.8.8.8")).(dns.RR), common.Must2(dns.NewRR("google.com. IN A 8.8.8.8")),
common.Must2(dns.NewRR("google.com. IN A 8.8.4.4")).(dns.RR), common.Must2(dns.NewRR("google.com. IN A 8.8.4.4")),
) )
p = append(p, common.Must2(ans.Pack()).([]byte)) p = append(p, common.Must2(ans.Pack()))
ans = new(dns.Msg) ans = new(dns.Msg)
ans.Id = 2 ans.Id = 2
ans.Answer = append(ans.Answer, ans.Answer = append(ans.Answer,
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")).(dns.RR), common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")).(dns.RR), common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")),
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")).(dns.RR), common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
common.Must2(dns.NewRR("google.com. IN CNAME test.google.com")).(dns.RR), common.Must2(dns.NewRR("google.com. IN CNAME test.google.com")),
common.Must2(dns.NewRR("google.com. IN AAAA 2001::123:8888")).(dns.RR), common.Must2(dns.NewRR("google.com. IN AAAA 2001::123:8888")),
common.Must2(dns.NewRR("google.com. IN AAAA 2001::123:8844")).(dns.RR), common.Must2(dns.NewRR("google.com. IN AAAA 2001::123:8844")),
) )
p = append(p, common.Must2(ans.Pack()).([]byte)) p = append(p, common.Must2(ans.Pack()))
tests := []struct { tests := []struct {
name string name string

View File

@@ -2,6 +2,8 @@ package dns
import ( import (
"context" "context"
"strconv"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/strmatcher" "github.com/xtls/xray-core/common/strmatcher"
@@ -31,7 +33,15 @@ func NewStaticHosts(hosts []*Config_HostMapping) (*StaticHosts, error) {
ips := make([]net.Address, 0, len(mapping.Ip)+1) ips := make([]net.Address, 0, len(mapping.Ip)+1)
switch { switch {
case len(mapping.ProxiedDomain) > 0: case len(mapping.ProxiedDomain) > 0:
ips = append(ips, net.DomainAddress(mapping.ProxiedDomain)) if mapping.ProxiedDomain[0] == '#' {
rcode, err := strconv.Atoi(mapping.ProxiedDomain[1:])
if err != nil {
return nil, err
}
ips = append(ips, dns.RCodeError(rcode))
} else {
ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
}
case len(mapping.Ip) > 0: case len(mapping.Ip) > 0:
for _, ip := range mapping.Ip { for _, ip := range mapping.Ip {
addr := net.IPAddress(ip) addr := net.IPAddress(ip)
@@ -58,38 +68,51 @@ func filterIP(ips []net.Address, option dns.IPOption) []net.Address {
return filtered return filtered
} }
func (h *StaticHosts) lookupInternal(domain string) []net.Address { func (h *StaticHosts) lookupInternal(domain string) ([]net.Address, error) {
ips := make([]net.Address, 0) ips := make([]net.Address, 0)
found := false found := false
for _, id := range h.matchers.Match(domain) { for _, id := range h.matchers.Match(domain) {
for _, v := range h.ips[id] {
if err, ok := v.(dns.RCodeError); ok {
if uint16(err) == 0 {
return nil, dns.ErrEmptyResponse
}
return nil, err
}
}
ips = append(ips, h.ips[id]...) ips = append(ips, h.ips[id]...)
found = true found = true
} }
if !found { if !found {
return nil return nil, nil
} }
return ips return ips, nil
} }
func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) []net.Address { func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) ([]net.Address, error) {
switch addrs := h.lookupInternal(domain); { switch addrs, err := h.lookupInternal(domain); {
case err != nil:
return nil, err
case len(addrs) == 0: // Not recorded in static hosts, return nil case len(addrs) == 0: // Not recorded in static hosts, return nil
return addrs return addrs, nil
case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Try to unwrap domain case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Try to unwrap domain
errors.LogDebug(context.Background(), "found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it") errors.LogDebug(context.Background(), "found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it")
if maxDepth > 0 { if maxDepth > 0 {
unwrapped := h.lookup(addrs[0].Domain(), option, maxDepth-1) unwrapped, err := h.lookup(addrs[0].Domain(), option, maxDepth-1)
if err != nil {
return nil, err
}
if unwrapped != nil { if unwrapped != nil {
return unwrapped return unwrapped, nil
} }
} }
return addrs return addrs, nil
default: // IP record found, return a non-nil IP array default: // IP record found, return a non-nil IP array
return filterIP(addrs, option) return filterIP(addrs, option), nil
} }
} }
// Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts. // Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts.
func (h *StaticHosts) Lookup(domain string, option dns.IPOption) []net.Address { func (h *StaticHosts) Lookup(domain string, option dns.IPOption) ([]net.Address, error) {
return h.lookup(domain, option, 5) return h.lookup(domain, option, 5)
} }

View File

@@ -12,6 +12,11 @@ import (
func TestStaticHosts(t *testing.T) { func TestStaticHosts(t *testing.T) {
pb := []*Config_HostMapping{ pb := []*Config_HostMapping{
{
Type: DomainMatchingType_Subdomain,
Domain: "lan",
ProxiedDomain: "#3",
},
{ {
Type: DomainMatchingType_Full, Type: DomainMatchingType_Full,
Domain: "example.com", Domain: "example.com",
@@ -54,7 +59,14 @@ func TestStaticHosts(t *testing.T) {
common.Must(err) common.Must(err)
{ {
ips := hosts.Lookup("example.com", dns.IPOption{ _, err := hosts.Lookup("example.com.lan", dns.IPOption{})
if dns.RCodeFromError(err) != 3 {
t.Error(err)
}
}
{
ips, _ := hosts.Lookup("example.com", dns.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: true, IPv6Enable: true,
}) })
@@ -67,7 +79,7 @@ func TestStaticHosts(t *testing.T) {
} }
{ {
domain := hosts.Lookup("proxy.xray.com", dns.IPOption{ domain, _ := hosts.Lookup("proxy.xray.com", dns.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: false, IPv6Enable: false,
}) })
@@ -80,7 +92,7 @@ func TestStaticHosts(t *testing.T) {
} }
{ {
domain := hosts.Lookup("proxy2.xray.com", dns.IPOption{ domain, _ := hosts.Lookup("proxy2.xray.com", dns.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: false, IPv6Enable: false,
}) })
@@ -93,7 +105,7 @@ func TestStaticHosts(t *testing.T) {
} }
{ {
ips := hosts.Lookup("www.example.cn", dns.IPOption{ ips, _ := hosts.Lookup("www.example.cn", dns.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: true, IPv6Enable: true,
}) })
@@ -106,7 +118,7 @@ func TestStaticHosts(t *testing.T) {
} }
{ {
ips := hosts.Lookup("baidu.com", dns.IPOption{ ips, _ := hosts.Lookup("baidu.com", dns.IPOption{
IPv4Enable: false, IPv4Enable: false,
IPv6Enable: true, IPv6Enable: true,
}) })

View File

@@ -32,7 +32,7 @@ type QUICNameServer struct {
sync.RWMutex sync.RWMutex
cacheController *CacheController cacheController *CacheController
destination *net.Destination destination *net.Destination
connection quic.Connection connection *quic.Conn
clientIP net.IP clientIP net.IP
} }
@@ -220,7 +220,7 @@ func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, option dns_
} }
func isActive(s quic.Connection) bool { func isActive(s *quic.Conn) bool {
select { select {
case <-s.Context().Done(): case <-s.Context().Done():
return false return false
@@ -229,8 +229,8 @@ func isActive(s quic.Connection) bool {
} }
} }
func (s *QUICNameServer) getConnection() (quic.Connection, error) { func (s *QUICNameServer) getConnection() (*quic.Conn, error) {
var conn quic.Connection var conn *quic.Conn
s.RLock() s.RLock()
conn = s.connection conn = s.connection
if conn != nil && isActive(conn) { if conn != nil && isActive(conn) {
@@ -263,7 +263,7 @@ func (s *QUICNameServer) getConnection() (quic.Connection, error) {
return conn, nil return conn, nil
} }
func (s *QUICNameServer) openConnection() (quic.Connection, error) { func (s *QUICNameServer) openConnection() (*quic.Conn, error) {
tlsConfig := tls.Config{} tlsConfig := tls.Config{}
quicConfig := &quic.Config{ quicConfig := &quic.Config{
HandshakeIdleTimeout: handshakeTimeout, HandshakeIdleTimeout: handshakeTimeout,
@@ -283,7 +283,7 @@ func (s *QUICNameServer) openConnection() (quic.Connection, error) {
return conn, nil return conn, nil
} }
func (s *QUICNameServer) openStream(ctx context.Context) (quic.Stream, error) { func (s *QUICNameServer) openStream(ctx context.Context) (*quic.Stream, error) {
conn, err := s.getConnection() conn, err := s.getConnection()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -90,6 +90,8 @@ type HealthPingConfig struct {
SamplingCount int32 `protobuf:"varint,4,opt,name=samplingCount,proto3" json:"samplingCount,omitempty"` SamplingCount int32 `protobuf:"varint,4,opt,name=samplingCount,proto3" json:"samplingCount,omitempty"`
// ping timeout, int64 values of time.Duration // ping timeout, int64 values of time.Duration
Timeout int64 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"` Timeout int64 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"`
// http method to make request
HttpMethod string `protobuf:"bytes,6,opt,name=httpMethod,proto3" json:"httpMethod,omitempty"`
} }
func (x *HealthPingConfig) Reset() { func (x *HealthPingConfig) Reset() {
@@ -157,6 +159,13 @@ func (x *HealthPingConfig) GetTimeout() int64 {
return 0 return 0
} }
func (x *HealthPingConfig) GetHttpMethod() string {
if x != nil {
return x.HttpMethod
}
return ""
}
var File_app_observatory_burst_config_proto protoreflect.FileDescriptor var File_app_observatory_burst_config_proto protoreflect.FileDescriptor
var file_app_observatory_burst_config_proto_rawDesc = []byte{ var file_app_observatory_burst_config_proto_rawDesc = []byte{
@@ -173,7 +182,7 @@ var file_app_observatory_burst_config_proto_rawDesc = []byte{
0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x62, 0x75, 0x72, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x62, 0x75, 0x72,
0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22,
0xb4, 0x01, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0xd4, 0x01, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69,
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
@@ -184,7 +193,9 @@ var file_app_observatory_burst_config_proto_rawDesc = []byte{
0x6e, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x73, 0x6e, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x73,
0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07,
0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74,
0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x42, 0x70, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x4d, 0x65,
0x74, 0x68, 0x6f, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x68, 0x74, 0x74, 0x70,
0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x42, 0x70, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f,
0x72, 0x79, 0x2e, 0x62, 0x75, 0x72, 0x73, 0x74, 0x50, 0x01, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x72, 0x79, 0x2e, 0x62, 0x75, 0x72, 0x73, 0x74, 0x50, 0x01, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,

View File

@@ -26,4 +26,7 @@ message HealthPingConfig {
int32 samplingCount = 4; int32 samplingCount = 4;
// ping timeout, int64 values of time.Duration // ping timeout, int64 values of time.Duration
int64 timeout = 5; int64 timeout = 5;
// http method to make request
string httpMethod = 6;
} }

View File

@@ -19,6 +19,7 @@ type HealthPingSettings struct {
Interval time.Duration `json:"interval"` Interval time.Duration `json:"interval"`
SamplingCount int `json:"sampling"` SamplingCount int `json:"sampling"`
Timeout time.Duration `json:"timeout"` Timeout time.Duration `json:"timeout"`
HttpMethod string `json:"httpMethod"`
} }
// HealthPing is the health checker for balancers // HealthPing is the health checker for balancers
@@ -37,12 +38,21 @@ type HealthPing struct {
func NewHealthPing(ctx context.Context, dispatcher routing.Dispatcher, config *HealthPingConfig) *HealthPing { func NewHealthPing(ctx context.Context, dispatcher routing.Dispatcher, config *HealthPingConfig) *HealthPing {
settings := &HealthPingSettings{} settings := &HealthPingSettings{}
if config != nil { if config != nil {
var httpMethod string
if config.HttpMethod == "" {
httpMethod = "HEAD"
} else {
httpMethod = strings.TrimSpace(config.HttpMethod)
}
settings = &HealthPingSettings{ settings = &HealthPingSettings{
Connectivity: strings.TrimSpace(config.Connectivity), Connectivity: strings.TrimSpace(config.Connectivity),
Destination: strings.TrimSpace(config.Destination), Destination: strings.TrimSpace(config.Destination),
Interval: time.Duration(config.Interval), Interval: time.Duration(config.Interval),
SamplingCount: int(config.SamplingCount), SamplingCount: int(config.SamplingCount),
Timeout: time.Duration(config.Timeout), Timeout: time.Duration(config.Timeout),
HttpMethod: httpMethod,
} }
} }
if settings.Destination == "" { if settings.Destination == "" {
@@ -164,7 +174,7 @@ func (h *HealthPing) doCheck(tags []string, duration time.Duration, rounds int)
} }
time.AfterFunc(delay, func() { time.AfterFunc(delay, func() {
errors.LogDebug(h.ctx, "checking ", handler) errors.LogDebug(h.ctx, "checking ", handler)
delay, err := client.MeasureDelay() delay, err := client.MeasureDelay(h.Settings.HttpMethod)
if err == nil { if err == nil {
ch <- &rtt{ ch <- &rtt{
handler: handler, handler: handler,
@@ -251,7 +261,7 @@ func (h *HealthPing) checkConnectivity() bool {
h.Settings.Connectivity, h.Settings.Connectivity,
h.Settings.Timeout, h.Settings.Timeout,
) )
if _, err := tester.MeasureDelay(); err != nil { if _, err := tester.MeasureDelay(h.Settings.HttpMethod); err != nil {
return false return false
} }
return true return true

View File

@@ -2,6 +2,7 @@ package burst
import ( import (
"context" "context"
"io"
"net/http" "net/http"
"time" "time"
@@ -51,20 +52,28 @@ func newHTTPClient(ctxv context.Context, dispatcher routing.Dispatcher, handler
} }
// MeasureDelay returns the delay time of the request to dest // MeasureDelay returns the delay time of the request to dest
func (s *pingClient) MeasureDelay() (time.Duration, error) { func (s *pingClient) MeasureDelay(httpMethod string) (time.Duration, error) {
if s.httpClient == nil { if s.httpClient == nil {
panic("pingClient not initialized") panic("pingClient not initialized")
} }
req, err := http.NewRequest(http.MethodHead, s.destination, nil)
req, err := http.NewRequest(httpMethod, s.destination, nil)
if err != nil { if err != nil {
return rttFailed, err return rttFailed, err
} }
start := time.Now() start := time.Now()
resp, err := s.httpClient.Do(req) resp, err := s.httpClient.Do(req)
if err != nil { if err != nil {
return rttFailed, err return rttFailed, err
} }
// don't wait for body if httpMethod == http.MethodGet {
_, err = io.Copy(io.Discard, resp.Body)
if err != nil {
return rttFailed, err
}
}
resp.Body.Close() resp.Body.Close()
return time.Since(start), nil return time.Since(start), nil
} }

View File

@@ -103,13 +103,22 @@ func (s *handlerServer) AlterInbound(ctx context.Context, request *AlterInboundR
func (s *handlerServer) ListInbounds(ctx context.Context, request *ListInboundsRequest) (*ListInboundsResponse, error) { func (s *handlerServer) ListInbounds(ctx context.Context, request *ListInboundsRequest) (*ListInboundsResponse, error) {
handlers := s.ihm.ListHandlers(ctx) handlers := s.ihm.ListHandlers(ctx)
response := &ListInboundsResponse{} response := &ListInboundsResponse{}
for _, handler := range handlers { if request.GetIsOnlyTags() {
response.Inbounds = append(response.Inbounds, &core.InboundHandlerConfig{ for _, handler := range handlers {
Tag: handler.Tag(), response.Inbounds = append(response.Inbounds, &core.InboundHandlerConfig{
ReceiverSettings: handler.ReceiverSettings(), Tag: handler.Tag(),
ProxySettings: handler.ProxySettings(), })
}) }
} else {
for _, handler := range handlers {
response.Inbounds = append(response.Inbounds, &core.InboundHandlerConfig{
Tag: handler.Tag(),
ReceiverSettings: handler.ReceiverSettings(),
ProxySettings: handler.ProxySettings(),
})
}
} }
return response, nil return response, nil
} }

View File

@@ -368,6 +368,8 @@ type ListInboundsRequest struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
IsOnlyTags bool `protobuf:"varint,1,opt,name=isOnlyTags,proto3" json:"isOnlyTags,omitempty"`
} }
func (x *ListInboundsRequest) Reset() { func (x *ListInboundsRequest) Reset() {
@@ -400,6 +402,13 @@ func (*ListInboundsRequest) Descriptor() ([]byte, []int) {
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{8} return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{8}
} }
func (x *ListInboundsRequest) GetIsOnlyTags() bool {
if x != nil {
return x.IsOnlyTags
}
return false
}
type ListInboundsResponse struct { type ListInboundsResponse struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -993,9 +1002,11 @@ var file_app_proxyman_command_command_proto_rawDesc = []byte{
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70,
0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x22, 0x16, 0x0a, 0x14, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x62, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x16, 0x0a, 0x14, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x0a, 0x13,
0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x22, 0x53, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x54, 0x61, 0x67,
0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x54,
0x61, 0x67, 0x73, 0x22, 0x53, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75,
0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x69, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x69,
0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e,

View File

@@ -37,7 +37,9 @@ message AlterInboundRequest {
message AlterInboundResponse {} message AlterInboundResponse {}
message ListInboundsRequest {} message ListInboundsRequest {
bool isOnlyTags = 1;
}
message ListInboundsResponse { message ListInboundsResponse {
repeated core.InboundHandlerConfig inbounds = 1; repeated core.InboundHandlerConfig inbounds = 1;

View File

@@ -449,11 +449,12 @@ type SenderConfig struct {
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
// Send traffic through the given IP. Only IP is allowed. // Send traffic through the given IP. Only IP is allowed.
Via *net.IPOrDomain `protobuf:"bytes,1,opt,name=via,proto3" json:"via,omitempty"` Via *net.IPOrDomain `protobuf:"bytes,1,opt,name=via,proto3" json:"via,omitempty"`
StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"` StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"`
ProxySettings *internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"` ProxySettings *internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"`
MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"` MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"`
ViaCidr string `protobuf:"bytes,5,opt,name=via_cidr,json=viaCidr,proto3" json:"via_cidr,omitempty"` ViaCidr string `protobuf:"bytes,5,opt,name=via_cidr,json=viaCidr,proto3" json:"via_cidr,omitempty"`
TargetStrategy internet.DomainStrategy `protobuf:"varint,6,opt,name=target_strategy,json=targetStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"target_strategy,omitempty"`
} }
func (x *SenderConfig) Reset() { func (x *SenderConfig) Reset() {
@@ -521,6 +522,13 @@ func (x *SenderConfig) GetViaCidr() string {
return "" return ""
} }
func (x *SenderConfig) GetTargetStrategy() internet.DomainStrategy {
if x != nil {
return x.TargetStrategy
}
return internet.DomainStrategy(0)
}
type MultiplexingConfig struct { type MultiplexingConfig struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -779,7 +787,7 @@ var file_app_proxyman_config_proto_rawDesc = []byte{
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65,
0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53,
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xcb, 0x02, 0x0a, 0x0c, 0x53, 0x65, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x9d, 0x03, 0x0a, 0x0c, 0x53, 0x65,
0x6e, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x0a, 0x03, 0x76, 0x69, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x0a, 0x03, 0x76, 0x69,
0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63,
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f,
@@ -800,23 +808,28 @@ var file_app_proxyman_config_proto_rawDesc = []byte{
0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69,
0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19, 0x0a, 0x08,
0x76, 0x69, 0x61, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x69, 0x61, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
0x76, 0x69, 0x61, 0x43, 0x69, 0x64, 0x72, 0x22, 0xa4, 0x01, 0x0a, 0x12, 0x4d, 0x75, 0x6c, 0x74, 0x76, 0x69, 0x61, 0x43, 0x69, 0x64, 0x72, 0x12, 0x50, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65,
0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e,
0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69,
0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x63, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65,
0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x22, 0xa4, 0x01, 0x0a, 0x12, 0x4d, 0x75,
0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x01, 0x28, 0x05, 0x52, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f,
0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x78, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52,
0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x42, 0x55, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f,
0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18,
0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50, 0x01, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72,
0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x72, 0x6f, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33,
0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50, 0x01, 0x5a, 0x26, 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, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79,
0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50,
0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@@ -850,6 +863,7 @@ var file_app_proxyman_config_proto_goTypes = []any{
(*internet.StreamConfig)(nil), // 13: xray.transport.internet.StreamConfig (*internet.StreamConfig)(nil), // 13: xray.transport.internet.StreamConfig
(*serial.TypedMessage)(nil), // 14: xray.common.serial.TypedMessage (*serial.TypedMessage)(nil), // 14: xray.common.serial.TypedMessage
(*internet.ProxyConfig)(nil), // 15: xray.transport.internet.ProxyConfig (*internet.ProxyConfig)(nil), // 15: xray.transport.internet.ProxyConfig
(internet.DomainStrategy)(0), // 16: xray.transport.internet.DomainStrategy
} }
var file_app_proxyman_config_proto_depIdxs = []int32{ var file_app_proxyman_config_proto_depIdxs = []int32{
0, // 0: xray.app.proxyman.AllocationStrategy.type:type_name -> xray.app.proxyman.AllocationStrategy.Type 0, // 0: xray.app.proxyman.AllocationStrategy.type:type_name -> xray.app.proxyman.AllocationStrategy.Type
@@ -866,11 +880,12 @@ var file_app_proxyman_config_proto_depIdxs = []int32{
13, // 11: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig 13, // 11: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
15, // 12: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig 15, // 12: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig
8, // 13: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig 8, // 13: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig
14, // [14:14] is the sub-list for method output_type 16, // 14: xray.app.proxyman.SenderConfig.target_strategy:type_name -> xray.transport.internet.DomainStrategy
14, // [14:14] is the sub-list for method input_type 15, // [15:15] is the sub-list for method output_type
14, // [14:14] is the sub-list for extension type_name 15, // [15:15] is the sub-list for method input_type
14, // [14:14] is the sub-list for extension extendee 15, // [15:15] is the sub-list for extension type_name
0, // [0:14] is the sub-list for field type_name 15, // [15:15] is the sub-list for extension extendee
0, // [0:15] is the sub-list for field type_name
} }
func init() { file_app_proxyman_config_proto_init() } func init() { file_app_proxyman_config_proto_init() }

View File

@@ -84,6 +84,7 @@ message SenderConfig {
xray.transport.internet.ProxyConfig proxy_settings = 3; xray.transport.internet.ProxyConfig proxy_settings = 3;
MultiplexingConfig multiplex_settings = 4; MultiplexingConfig multiplex_settings = 4;
string via_cidr = 5; string via_cidr = 5;
xray.transport.internet.DomainStrategy target_strategy = 6;
} }
message MultiplexingConfig { message MultiplexingConfig {

View File

@@ -17,7 +17,7 @@ import (
// Manager manages all inbound handlers. // Manager manages all inbound handlers.
type Manager struct { type Manager struct {
access sync.RWMutex access sync.RWMutex
untaggedHandler []inbound.Handler untaggedHandlers []inbound.Handler
taggedHandlers map[string]inbound.Handler taggedHandlers map[string]inbound.Handler
running bool running bool
} }
@@ -47,7 +47,7 @@ func (m *Manager) AddHandler(ctx context.Context, handler inbound.Handler) error
} }
m.taggedHandlers[tag] = handler m.taggedHandlers[tag] = handler
} else { } else {
m.untaggedHandler = append(m.untaggedHandler, handler) m.untaggedHandlers = append(m.untaggedHandlers, handler)
} }
if m.running { if m.running {
@@ -94,8 +94,8 @@ func (m *Manager) ListHandlers(ctx context.Context) []inbound.Handler {
m.access.RLock() m.access.RLock()
defer m.access.RUnlock() defer m.access.RUnlock()
var response []inbound.Handler response := make([]inbound.Handler, len(m.untaggedHandlers))
copy(m.untaggedHandler, response) copy(response, m.untaggedHandlers)
for _, v := range m.taggedHandlers { for _, v := range m.taggedHandlers {
response = append(response, v) response = append(response, v)
@@ -117,7 +117,7 @@ func (m *Manager) Start() error {
} }
} }
for _, handler := range m.untaggedHandler { for _, handler := range m.untaggedHandlers {
if err := handler.Start(); err != nil { if err := handler.Start(); err != nil {
return err return err
} }
@@ -138,7 +138,7 @@ func (m *Manager) Close() error {
errs = append(errs, err) errs = append(errs, err)
} }
} }
for _, handler := range m.untaggedHandler { for _, handler := range m.untaggedHandlers {
if err := handler.Close(); err != nil { if err := handler.Close(); err != nil {
errs = append(errs, err) errs = append(errs, err)
} }

View File

@@ -91,6 +91,7 @@ func (w *tcpWorker) callback(conn stat.Connection) {
} }
ctx = session.ContextWithInbound(ctx, &session.Inbound{ ctx = session.ContextWithInbound(ctx, &session.Inbound{
Source: net.DestinationFromAddr(conn.RemoteAddr()), Source: net.DestinationFromAddr(conn.RemoteAddr()),
Local: net.DestinationFromAddr(conn.LocalAddr()),
Gateway: net.TCPDestination(w.address, w.port), Gateway: net.TCPDestination(w.address, w.port),
Tag: w.tag, Tag: w.tag,
Conn: conn, Conn: conn,
@@ -161,6 +162,7 @@ type udpConn struct {
uplink stats.Counter uplink stats.Counter
downlink stats.Counter downlink stats.Counter
inactive bool inactive bool
cancel context.CancelFunc
} }
func (c *udpConn) setInactive() { func (c *udpConn) setInactive() {
@@ -203,6 +205,9 @@ func (c *udpConn) Write(buf []byte) (int, error) {
} }
func (c *udpConn) Close() error { func (c *udpConn) Close() error {
if c.cancel != nil {
c.cancel()
}
common.Must(c.done.Close()) common.Must(c.done.Close())
common.Must(common.Close(c.writer)) common.Must(common.Close(c.writer))
return nil return nil
@@ -259,6 +264,7 @@ func (w *udpWorker) getConnection(id connID) (*udpConn, bool) {
defer w.Unlock() defer w.Unlock()
if conn, found := w.activeConn[id]; found && !conn.done.Done() { if conn, found := w.activeConn[id]; found && !conn.done.Done() {
conn.updateActivity()
return conn, true return conn, true
} }
@@ -306,7 +312,8 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
common.Must(w.checker.Start()) common.Must(w.checker.Start())
go func() { go func() {
ctx := w.ctx ctx, cancel := context.WithCancel(w.ctx)
conn.cancel = cancel
sid := session.NewID() sid := session.NewID()
ctx = c.ContextWithID(ctx, sid) ctx = c.ContextWithID(ctx, sid)
@@ -315,8 +322,18 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
outbounds[0].Target = originalDest outbounds[0].Target = originalDest
} }
ctx = session.ContextWithOutbounds(ctx, outbounds) ctx = session.ContextWithOutbounds(ctx, outbounds)
local := net.DestinationFromAddr(w.hub.Addr())
if local.Address == net.AnyIP || local.Address == net.AnyIPv6 {
if source.Address.Family().IsIPv4() {
local.Address = net.AnyIP
} else if source.Address.Family().IsIPv6() {
local.Address = net.AnyIPv6
}
}
ctx = session.ContextWithInbound(ctx, &session.Inbound{ ctx = session.ContextWithInbound(ctx, &session.Inbound{
Source: source, Source: source,
Local: local, // Due to some limitations, in UDP connections, localIP is always equal to listen interface IP
Gateway: net.UDPDestination(w.address, w.port), Gateway: net.UDPDestination(w.address, w.port),
Tag: w.tag, Tag: w.tag,
}) })
@@ -466,6 +483,7 @@ func (w *dsWorker) callback(conn stat.Connection) {
} }
ctx = session.ContextWithInbound(ctx, &session.Inbound{ ctx = session.ContextWithInbound(ctx, &session.Inbound{
Source: net.DestinationFromAddr(conn.RemoteAddr()), Source: net.DestinationFromAddr(conn.RemoteAddr()),
Local: net.DestinationFromAddr(conn.LocalAddr()),
Gateway: net.UnixDestination(w.address), Gateway: net.UnixDestination(w.address),
Tag: w.tag, Tag: w.tag,
Conn: conn, Conn: conn,

View File

@@ -9,6 +9,8 @@ import (
gonet "net" gonet "net"
"os" "os"
"github.com/xtls/xray-core/common/dice"
"github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/app/proxyman"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
@@ -177,6 +179,29 @@ func (h *Handler) Tag() string {
func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) { func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
outbounds := session.OutboundsFromContext(ctx) outbounds := session.OutboundsFromContext(ctx)
ob := outbounds[len(outbounds)-1] ob := outbounds[len(outbounds)-1]
content := session.ContentFromContext(ctx)
if h.senderSettings != nil && h.senderSettings.TargetStrategy.HasStrategy() && ob.Target.Address.Family().IsDomain() && (content == nil || !content.SkipDNSResolve) {
strategy := h.senderSettings.TargetStrategy
if ob.Target.Network == net.Network_UDP && ob.OriginalTarget.Address != nil {
strategy = strategy.GetDynamicStrategy(ob.OriginalTarget.Address.Family())
}
ips, err := internet.LookupForIP(ob.Target.Address.Domain(), strategy, nil)
if err != nil {
errors.LogInfoInner(ctx, err, "failed to resolve ip for target ", ob.Target.Address.Domain())
if h.senderSettings.TargetStrategy.ForceIP() {
err := errors.New("failed to resolve ip for target ", ob.Target.Address.Domain()).Base(err)
session.SubmitOutboundErrorToOriginator(ctx, err)
common.Interrupt(link.Writer)
common.Interrupt(link.Reader)
return
}
} else {
unchangedDomain := ob.Target.Address.Domain()
ob.Target.Address = net.IPAddress(ips[dice.Roll(len(ips))])
errors.LogInfo(ctx, "target: ", unchangedDomain, " resolved to: ", ob.Target.Address.String())
}
}
if ob.Target.Network == net.Network_UDP && ob.OriginalTarget.Address != nil && ob.OriginalTarget.Address != ob.Target.Address { if ob.Target.Network == net.Network_UDP && ob.OriginalTarget.Address != nil && ob.OriginalTarget.Address != ob.Target.Address {
link.Reader = &buf.EndpointOverrideReader{Reader: link.Reader, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address} link.Reader = &buf.EndpointOverrideReader{Reader: link.Reader, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
link.Writer = &buf.EndpointOverrideWriter{Writer: link.Writer, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address} link.Writer = &buf.EndpointOverrideWriter{Writer: link.Writer, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
@@ -188,6 +213,7 @@ func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
session.SubmitOutboundErrorToOriginator(ctx, err) session.SubmitOutboundErrorToOriginator(ctx, err)
errors.LogInfo(ctx, err.Error()) errors.LogInfo(ctx, err.Error())
common.Interrupt(link.Writer) common.Interrupt(link.Writer)
common.Interrupt(link.Reader)
} }
} }
if ob.Target.Network == net.Network_UDP && ob.Target.Port == 443 { if ob.Target.Network == net.Network_UDP && ob.Target.Port == 443 {
@@ -230,14 +256,6 @@ out:
common.Interrupt(link.Reader) common.Interrupt(link.Reader)
} }
// Address implements internet.Dialer.
func (h *Handler) Address() net.Address {
if h.senderSettings == nil || h.senderSettings.Via == nil {
return nil
}
return h.senderSettings.Via.AsAddress()
}
func (h *Handler) DestIpAddress() net.IP { func (h *Handler) DestIpAddress() net.IP {
return internet.DestIpAddress() return internet.DestIpAddress()
} }
@@ -272,49 +290,16 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
return h.getStatCouterConnection(conn), nil return h.getStatCouterConnection(conn), nil
} }
errors.LogWarning(ctx, "failed to get outbound handler with tag: ", tag) errors.LogError(ctx, "failed to get outbound handler with tag: ", tag)
return nil, errors.New("failed to get outbound handler with tag: " + tag)
} }
if h.senderSettings.Via != nil { if h.senderSettings.Via != nil {
outbounds := session.OutboundsFromContext(ctx) outbounds := session.OutboundsFromContext(ctx)
ob := outbounds[len(outbounds)-1] ob := outbounds[len(outbounds)-1]
var domain string h.SetOutboundGateway(ctx, ob)
addr := h.senderSettings.Via.AsAddress()
domain = h.senderSettings.Via.GetDomain()
switch {
case h.senderSettings.ViaCidr != "":
ob.Gateway = ParseRandomIP(addr, h.senderSettings.ViaCidr)
case domain == "origin":
if inbound := session.InboundFromContext(ctx); inbound != nil {
if inbound.Conn != nil {
origin, _, err := net.SplitHostPort(inbound.Conn.LocalAddr().String())
if err == nil {
ob.Gateway = net.ParseAddress(origin)
errors.LogDebug(ctx, "use receive package ip as snedthrough: ", origin)
}
}
}
case domain == "srcip":
if inbound := session.InboundFromContext(ctx); inbound != nil {
if inbound.Conn != nil {
clientaddr, _, err := net.SplitHostPort(inbound.Conn.RemoteAddr().String())
if err == nil {
ob.Gateway = net.ParseAddress(clientaddr)
errors.LogDebug(ctx, "use client src ip as snedthrough: ", clientaddr)
}
}
}
//case addr.Family().IsDomain():
default:
ob.Gateway = addr
}
} }
} }
if conn, err := h.getUoTConnection(ctx, dest); err != os.ErrInvalid { if conn, err := h.getUoTConnection(ctx, dest); err != os.ErrInvalid {
@@ -329,6 +314,38 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
return conn, err return conn, err
} }
func (h *Handler) SetOutboundGateway(ctx context.Context, ob *session.Outbound) {
if ob.Gateway == nil && h.senderSettings != nil && h.senderSettings.Via != nil && !h.senderSettings.ProxySettings.HasTag() && (h.streamSettings.SocketSettings == nil || len(h.streamSettings.SocketSettings.DialerProxy) == 0) {
var domain string
addr := h.senderSettings.Via.AsAddress()
domain = h.senderSettings.Via.GetDomain()
switch {
case h.senderSettings.ViaCidr != "":
ob.Gateway = ParseRandomIP(addr, h.senderSettings.ViaCidr)
case domain == "origin":
if inbound := session.InboundFromContext(ctx); inbound != nil {
if inbound.Local.IsValid() && inbound.Local.Address.Family().IsIP() {
ob.Gateway = inbound.Local.Address
errors.LogDebug(ctx, "use inbound local ip as sendthrough: ", inbound.Local.Address.String())
}
}
case domain == "srcip":
if inbound := session.InboundFromContext(ctx); inbound != nil {
if inbound.Source.IsValid() && inbound.Source.Address.Family().IsIP() {
ob.Gateway = inbound.Source.Address
errors.LogDebug(ctx, "use inbound source ip as sendthrough: ", inbound.Source.Address.String())
}
}
//case addr.Family().IsDomain():
default:
ob.Gateway = addr
}
}
}
func (h *Handler) getStatCouterConnection(conn stat.Connection) stat.Connection { func (h *Handler) getStatCouterConnection(conn stat.Connection) stat.Connection {
if h.uplinkCounter != nil || h.downlinkCounter != nil { if h.uplinkCounter != nil || h.downlinkCounter != nil {
return &stat.CounterConnection{ return &stat.CounterConnection{

View File

@@ -150,8 +150,8 @@ func (m *Manager) ListHandlers(ctx context.Context) []outbound.Handler {
m.access.RLock() m.access.RLock()
defer m.access.RUnlock() defer m.access.RUnlock()
var response []outbound.Handler response := make([]outbound.Handler, len(m.untaggedHandlers))
copy(m.untaggedHandlers, response) copy(response, m.untaggedHandlers)
for _, v := range m.taggedHandler { for _, v := range m.taggedHandler {
response = append(response, v) response = append(response, v)

View File

@@ -9,6 +9,7 @@ import (
func (c *Control) FillInRandom() { func (c *Control) FillInRandom() {
randomLength := dice.Roll(64) randomLength := dice.Roll(64)
randomLength++
c.Random = make([]byte, randomLength) c.Random = make([]byte, randomLength)
io.ReadFull(rand.Reader, c.Random) io.ReadFull(rand.Reader, c.Random)
} }

View File

@@ -170,7 +170,7 @@ func (p *StaticMuxPicker) PickAvailable() (*mux.ClientWorker, error) {
if w.draining { if w.draining {
continue continue
} }
if w.client.Closed() { if w.IsFull() {
continue continue
} }
if w.client.ActiveConnections() < minConn { if w.client.ActiveConnections() < minConn {
@@ -211,6 +211,7 @@ type PortalWorker struct {
writer buf.Writer writer buf.Writer
reader buf.Reader reader buf.Reader
draining bool draining bool
counter uint32
} }
func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) { func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) {
@@ -244,7 +245,7 @@ func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) {
} }
func (w *PortalWorker) heartbeat() error { func (w *PortalWorker) heartbeat() error {
if w.client.Closed() { if w.Closed() {
return errors.New("client worker stopped") return errors.New("client worker stopped")
} }
@@ -260,16 +261,21 @@ func (w *PortalWorker) heartbeat() error {
msg.State = Control_DRAIN msg.State = Control_DRAIN
defer func() { defer func() {
w.client.GetTimer().Reset(time.Second * 16)
common.Close(w.writer) common.Close(w.writer)
common.Interrupt(w.reader) common.Interrupt(w.reader)
w.writer = nil w.writer = nil
}() }()
} }
b, err := proto.Marshal(msg) w.counter = (w.counter + 1) % 5
common.Must(err) if w.draining || w.counter == 1 {
mb := buf.MergeBytes(nil, b) b, err := proto.Marshal(msg)
return w.writer.WriteMultiBuffer(mb) common.Must(err)
mb := buf.MergeBytes(nil, b)
return w.writer.WriteMultiBuffer(mb)
}
return nil
} }
func (w *PortalWorker) IsFull() bool { func (w *PortalWorker) IsFull() bool {

View File

@@ -42,6 +42,9 @@ type RoutingContext struct {
Attributes map[string]string `protobuf:"bytes,10,rep,name=Attributes,proto3" json:"Attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Attributes map[string]string `protobuf:"bytes,10,rep,name=Attributes,proto3" json:"Attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
OutboundGroupTags []string `protobuf:"bytes,11,rep,name=OutboundGroupTags,proto3" json:"OutboundGroupTags,omitempty"` OutboundGroupTags []string `protobuf:"bytes,11,rep,name=OutboundGroupTags,proto3" json:"OutboundGroupTags,omitempty"`
OutboundTag string `protobuf:"bytes,12,opt,name=OutboundTag,proto3" json:"OutboundTag,omitempty"` OutboundTag string `protobuf:"bytes,12,opt,name=OutboundTag,proto3" json:"OutboundTag,omitempty"`
LocalIPs [][]byte `protobuf:"bytes,13,rep,name=LocalIPs,proto3" json:"LocalIPs,omitempty"`
LocalPort uint32 `protobuf:"varint,14,opt,name=LocalPort,proto3" json:"LocalPort,omitempty"`
VlessRoute uint32 `protobuf:"varint,15,opt,name=VlessRoute,proto3" json:"VlessRoute,omitempty"`
} }
func (x *RoutingContext) Reset() { func (x *RoutingContext) Reset() {
@@ -158,6 +161,27 @@ func (x *RoutingContext) GetOutboundTag() string {
return "" return ""
} }
func (x *RoutingContext) GetLocalIPs() [][]byte {
if x != nil {
return x.LocalIPs
}
return nil
}
func (x *RoutingContext) GetLocalPort() uint32 {
if x != nil {
return x.LocalPort
}
return 0
}
func (x *RoutingContext) GetVlessRoute() uint32 {
if x != nil {
return x.VlessRoute
}
return 0
}
// SubscribeRoutingStatsRequest subscribes to routing statistics channel if // SubscribeRoutingStatsRequest subscribes to routing statistics channel if
// opened by xray-core. // opened by xray-core.
// * FieldSelectors selects a subset of fields in routing statistics to return. // * FieldSelectors selects a subset of fields in routing statistics to return.
@@ -827,7 +851,7 @@ var file_app_router_command_command_proto_rawDesc = []byte{
0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65,
0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9c, 0x04, 0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf6, 0x04, 0x0a, 0x0e, 0x52, 0x6f, 0x75,
0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x49, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x49,
0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0a, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x32, 0x0a, 0x07, 0x4e, 0x0a, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x32, 0x0a, 0x07, 0x4e,
@@ -857,123 +881,129 @@ var file_app_router_command_command_proto_rawDesc = []byte{
0x03, 0x28, 0x09, 0x52, 0x11, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x03, 0x28, 0x09, 0x52, 0x11, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x47, 0x72, 0x6f,
0x75, 0x70, 0x54, 0x61, 0x67, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x75, 0x70, 0x54, 0x61, 0x67, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75,
0x6e, 0x64, 0x54, 0x61, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4f, 0x75, 0x74, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4f, 0x75, 0x74,
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61,
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x6c, 0x49, 0x50, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x4c, 0x6f, 0x63, 0x61,
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x6c, 0x49, 0x50, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, 0x72,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f,
0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x46, 0x0a, 0x1c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x6f, 0x75, 0x74, 0x65,
0x72, 0x69, 0x62, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x6f, 0x75,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73,
0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x22, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0xb1, 0x01, 0x0a, 0x10, 0x54, 0x65, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x01, 0x22, 0x46, 0x0a, 0x1c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x6f,
0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64,
0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x22, 0xb1, 0x01, 0x0a, 0x10, 0x54, 0x65,
0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f,
0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x46, 0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x24, 0x0a, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x0d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03,
0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73,
0x75, 0x6c, 0x74, 0x22, 0x27, 0x0a, 0x13, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65,
0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61,
0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x26, 0x0a, 0x0c,
0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06,
0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61,
0x72, 0x67, 0x65, 0x74, 0x22, 0xa9, 0x01, 0x0a, 0x0b, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65,
0x72, 0x4d, 0x73, 0x67, 0x12, 0x41, 0x0a, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65,
0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6f, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52,
0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x57, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12,
0x69, 0x70, 0x6c, 0x65, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72,
0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65,
0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x50, 0x75, 0x62, 0x6c, 0x69,
0x63, 0x69, 0x70, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d,
0x0f, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x27, 0x0a,
0x22, 0x2a, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x13, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74,
0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28,
0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x5b, 0x0a, 0x17, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x26, 0x0a, 0x0c, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69,
0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xa9,
0x63, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x01, 0x0a, 0x0b, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x4d, 0x73, 0x67, 0x12, 0x41,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x0a, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
0x61, 0x6e, 0x64, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x4d, 0x73, 0x67, 0x52, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x22, 0x59, 0x0a, 0x1d, 0x4f, 0x76, 0x65, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72,
0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x69, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x61, 0x65, 0x12, 0x57, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x74,
0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x72,
0x0b, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x54, 0x61, 0x67, 0x12, 0x16, 0x0a, 0x06,
0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61,
0x72, 0x67, 0x65, 0x74, 0x22, 0x20, 0x0a, 0x1e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65,
0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6e, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79,
0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x41, 0x70, 0x70, 0x65,
0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64,
0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x22, 0x11, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c,
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x0a, 0x11, 0x52, 0x65, 0x6d,
0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18,
0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x07, 0x72, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x22, 0x14, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f,
0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x08,
0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0xbf, 0x05, 0x0a, 0x0e, 0x52, 0x6f, 0x75,
0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7b, 0x0a, 0x15, 0x53,
0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53,
0x74, 0x61, 0x74, 0x73, 0x12, 0x35, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53,
0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53,
0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x54,
0x74, 0x65, 0x78, 0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x61, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x6e, 0x63,
0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x69, 0x70, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x2a, 0x0a, 0x16, 0x47, 0x65,
0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71,
0x54, 0x65, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28,
0x1a, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x5b, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c,
0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69,
0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x00, 0x12, 0x76, 0x0a, 0x0f, 0x47,
0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2f,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61,
0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x30, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,
0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c,
0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x00, 0x12, 0x8b, 0x01, 0x0a, 0x16, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x65, 0x12, 0x40, 0x0a, 0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x18, 0x01, 0x20,
0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x36, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72,
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x42, 0x61,
0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x4d, 0x73, 0x67, 0x52, 0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e,
0x63, 0x65, 0x72, 0x22, 0x59, 0x0a, 0x1d, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42,
0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72,
0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x61, 0x6c, 0x61, 0x6e,
0x63, 0x65, 0x72, 0x54, 0x61, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x20,
0x0a, 0x1e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x6e, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x22, 0x0a, 0x0c,
0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01,
0x28, 0x08, 0x52, 0x0c, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64,
0x22, 0x11, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x2d, 0x0a, 0x11, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65,
0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x54,
0x61, 0x67, 0x22, 0x14, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x32, 0xbf, 0x05, 0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7b, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
0x62, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x35,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x62, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65,
0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x12, 0x5e, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x27, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63,
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e,
0x41, 0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x00,
0x00, 0x12, 0x67, 0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x30, 0x01, 0x12, 0x61, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12,
0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,
0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x52, 0x6f,
0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x78, 0x72, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x78, 0x72, 0x61,
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x65, 0x78, 0x74, 0x22, 0x00, 0x12, 0x76, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61,
0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e,
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x72, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x2e, 0x41, 0x70, 0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49,
0x61, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x8b, 0x01,
0x0a, 0x16, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x36, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
0x6e, 0x64, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e,
0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x37, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72,
0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65,
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x07, 0x41,
0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
0x2e, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,
0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c,
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x67, 0x0a, 0x0a, 0x52,
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e,
0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x00, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2c, 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, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x52,
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

View File

@@ -25,6 +25,9 @@ message RoutingContext {
map<string, string> Attributes = 10; map<string, string> Attributes = 10;
repeated string OutboundGroupTags = 11; repeated string OutboundGroupTags = 11;
string OutboundTag = 12; string OutboundTag = 12;
repeated bytes LocalIPs = 13;
uint32 LocalPort = 14;
uint32 VlessRoute = 15;
} }
// SubscribeRoutingStatsRequest subscribes to routing statistics channel if // SubscribeRoutingStatsRequest subscribes to routing statistics channel if

View File

@@ -28,6 +28,18 @@ func (c routingContext) GetTargetPort() net.Port {
return net.Port(c.RoutingContext.GetTargetPort()) return net.Port(c.RoutingContext.GetTargetPort())
} }
func (c routingContext) GetLocalIPs() []net.IP {
return mapBytesToIPs(c.RoutingContext.GetLocalIPs())
}
func (c routingContext) GetLocalPort() net.Port {
return net.Port(c.RoutingContext.GetLocalPort())
}
func (c routingContext) GetVlessRoute() net.Port {
return net.Port(c.RoutingContext.GetVlessRoute())
}
func (c routingContext) GetRuleTag() string { func (c routingContext) GetRuleTag() string {
return "" return ""
} }
@@ -54,8 +66,10 @@ var fieldMap = map[string]func(*RoutingContext, routing.Route){
"network": func(s *RoutingContext, r routing.Route) { s.Network = r.GetNetwork() }, "network": func(s *RoutingContext, r routing.Route) { s.Network = r.GetNetwork() },
"ip_source": func(s *RoutingContext, r routing.Route) { s.SourceIPs = mapIPsToBytes(r.GetSourceIPs()) }, "ip_source": func(s *RoutingContext, r routing.Route) { s.SourceIPs = mapIPsToBytes(r.GetSourceIPs()) },
"ip_target": func(s *RoutingContext, r routing.Route) { s.TargetIPs = mapIPsToBytes(r.GetTargetIPs()) }, "ip_target": func(s *RoutingContext, r routing.Route) { s.TargetIPs = mapIPsToBytes(r.GetTargetIPs()) },
"ip_local": func(s *RoutingContext, r routing.Route) { s.LocalIPs = mapIPsToBytes(r.GetLocalIPs()) },
"port_source": func(s *RoutingContext, r routing.Route) { s.SourcePort = uint32(r.GetSourcePort()) }, "port_source": func(s *RoutingContext, r routing.Route) { s.SourcePort = uint32(r.GetSourcePort()) },
"port_target": func(s *RoutingContext, r routing.Route) { s.TargetPort = uint32(r.GetTargetPort()) }, "port_target": func(s *RoutingContext, r routing.Route) { s.TargetPort = uint32(r.GetTargetPort()) },
"port_local": func(s *RoutingContext, r routing.Route) { s.LocalPort = uint32(r.GetLocalPort()) },
"domain": func(s *RoutingContext, r routing.Route) { s.TargetDomain = r.GetTargetDomain() }, "domain": func(s *RoutingContext, r routing.Route) { s.TargetDomain = r.GetTargetDomain() },
"protocol": func(s *RoutingContext, r routing.Route) { s.Protocol = r.GetProtocol() }, "protocol": func(s *RoutingContext, r routing.Route) { s.Protocol = r.GetProtocol() },
"user": func(s *RoutingContext, r routing.Route) { s.User = r.GetUser() }, "user": func(s *RoutingContext, r routing.Route) { s.User = r.GetUser() },

View File

@@ -83,21 +83,6 @@ func NewMphMatcherGroup(domains []*Domain) (*DomainMatcher, error) {
}, nil }, nil
} }
func NewDomainMatcher(domains []*Domain) (*DomainMatcher, error) {
g := new(strmatcher.MatcherGroup)
for _, d := range domains {
m, err := domainToMatcher(d)
if err != nil {
return nil, err
}
g.Add(m)
}
return &DomainMatcher{
matchers: g,
}, nil
}
func (m *DomainMatcher) ApplyDomain(domain string) bool { func (m *DomainMatcher) ApplyDomain(domain string) bool {
return len(m.matchers.Match(strings.ToLower(domain))) > 0 return len(m.matchers.Match(strings.ToLower(domain))) > 0
} }
@@ -113,10 +98,10 @@ func (m *DomainMatcher) Apply(ctx routing.Context) bool {
type MultiGeoIPMatcher struct { type MultiGeoIPMatcher struct {
matchers []*GeoIPMatcher matchers []*GeoIPMatcher
onSource bool asType string // local, source, target
} }
func NewMultiGeoIPMatcher(geoips []*GeoIP, onSource bool) (*MultiGeoIPMatcher, error) { func NewMultiGeoIPMatcher(geoips []*GeoIP, asType string) (*MultiGeoIPMatcher, error) {
var matchers []*GeoIPMatcher var matchers []*GeoIPMatcher
for _, geoip := range geoips { for _, geoip := range geoips {
matcher, err := GlobalGeoIPContainer.Add(geoip) matcher, err := GlobalGeoIPContainer.Add(geoip)
@@ -128,7 +113,7 @@ func NewMultiGeoIPMatcher(geoips []*GeoIP, onSource bool) (*MultiGeoIPMatcher, e
matcher := &MultiGeoIPMatcher{ matcher := &MultiGeoIPMatcher{
matchers: matchers, matchers: matchers,
onSource: onSource, asType: asType,
} }
return matcher, nil return matcher, nil
@@ -137,11 +122,18 @@ func NewMultiGeoIPMatcher(geoips []*GeoIP, onSource bool) (*MultiGeoIPMatcher, e
// Apply implements Condition. // Apply implements Condition.
func (m *MultiGeoIPMatcher) Apply(ctx routing.Context) bool { func (m *MultiGeoIPMatcher) Apply(ctx routing.Context) bool {
var ips []net.IP var ips []net.IP
if m.onSource {
switch m.asType {
case "local":
ips = ctx.GetLocalIPs()
case "source":
ips = ctx.GetSourceIPs() ips = ctx.GetSourceIPs()
} else { case "target":
ips = ctx.GetTargetIPs() ips = ctx.GetTargetIPs()
default:
panic("unreachable, asType should be local or source or target")
} }
for _, ip := range ips { for _, ip := range ips {
for _, matcher := range m.matchers { for _, matcher := range m.matchers {
if matcher.Match(ip) { if matcher.Match(ip) {
@@ -153,25 +145,33 @@ func (m *MultiGeoIPMatcher) Apply(ctx routing.Context) bool {
} }
type PortMatcher struct { type PortMatcher struct {
port net.MemoryPortList port net.MemoryPortList
onSource bool asType string // local, source, target
} }
// NewPortMatcher create a new port matcher that can match source or destination port // NewPortMatcher create a new port matcher that can match source or local or destination port
func NewPortMatcher(list *net.PortList, onSource bool) *PortMatcher { func NewPortMatcher(list *net.PortList, asType string) *PortMatcher {
return &PortMatcher{ return &PortMatcher{
port: net.PortListFromProto(list), port: net.PortListFromProto(list),
onSource: onSource, asType: asType,
} }
} }
// Apply implements Condition. // Apply implements Condition.
func (v *PortMatcher) Apply(ctx routing.Context) bool { func (v *PortMatcher) Apply(ctx routing.Context) bool {
if v.onSource { switch v.asType {
case "local":
return v.port.Contains(ctx.GetLocalPort())
case "source":
return v.port.Contains(ctx.GetSourcePort()) return v.port.Contains(ctx.GetSourcePort())
} else { case "target":
return v.port.Contains(ctx.GetTargetPort()) return v.port.Contains(ctx.GetTargetPort())
case "vlessRoute":
return v.port.Contains(ctx.GetVlessRoute())
default:
panic("unreachable, asType should be local or source or target")
} }
} }
type NetworkMatcher struct { type NetworkMatcher struct {

View File

@@ -328,9 +328,6 @@ func TestChinaSites(t *testing.T) {
domains, err := loadGeoSite("CN") domains, err := loadGeoSite("CN")
common.Must(err) common.Must(err)
matcher, err := NewDomainMatcher(domains)
common.Must(err)
acMatcher, err := NewMphMatcherGroup(domains) acMatcher, err := NewMphMatcherGroup(domains)
common.Must(err) common.Must(err)
@@ -362,12 +359,9 @@ func TestChinaSites(t *testing.T) {
} }
for _, testCase := range testCases { for _, testCase := range testCases {
r1 := matcher.ApplyDomain(testCase.Domain) r := acMatcher.ApplyDomain(testCase.Domain)
r2 := acMatcher.ApplyDomain(testCase.Domain) if r != testCase.Output {
if r1 != testCase.Output { t.Error("ACDomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r)
t.Error("DomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r1)
} else if r2 != testCase.Output {
t.Error("ACDomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r2)
} }
} }
} }
@@ -414,48 +408,6 @@ func BenchmarkMphDomainMatcher(b *testing.B) {
} }
} }
func BenchmarkDomainMatcher(b *testing.B) {
domains, err := loadGeoSite("CN")
common.Must(err)
matcher, err := NewDomainMatcher(domains)
common.Must(err)
type TestCase struct {
Domain string
Output bool
}
testCases := []TestCase{
{
Domain: "163.com",
Output: true,
},
{
Domain: "163.com",
Output: true,
},
{
Domain: "164.com",
Output: false,
},
{
Domain: "164.com",
Output: false,
},
}
for i := 0; i < 1024; i++ {
testCases = append(testCases, TestCase{Domain: strconv.Itoa(i) + ".not-exists.com", Output: false})
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, testCase := range testCases {
_ = matcher.ApplyDomain(testCase.Domain)
}
}
}
func BenchmarkMultiGeoIPMatcher(b *testing.B) { func BenchmarkMultiGeoIPMatcher(b *testing.B) {
var geoips []*GeoIP var geoips []*GeoIP
@@ -495,7 +447,7 @@ func BenchmarkMultiGeoIPMatcher(b *testing.B) {
}) })
} }
matcher, err := NewMultiGeoIPMatcher(geoips, false) matcher, err := NewMultiGeoIPMatcher(geoips, "target")
common.Must(err) common.Must(err)
ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)}) ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)})

View File

@@ -33,39 +33,36 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
conds := NewConditionChan() conds := NewConditionChan()
if len(rr.Domain) > 0 { if len(rr.Domain) > 0 {
switch rr.DomainMatcher { matcher, err := NewMphMatcherGroup(rr.Domain)
case "linear": if err != nil {
matcher, err := NewDomainMatcher(rr.Domain) return nil, errors.New("failed to build domain condition with MphDomainMatcher").Base(err)
if err != nil {
return nil, errors.New("failed to build domain condition").Base(err)
}
conds.Add(matcher)
case "mph", "hybrid":
fallthrough
default:
matcher, err := NewMphMatcherGroup(rr.Domain)
if err != nil {
return nil, errors.New("failed to build domain condition with MphDomainMatcher").Base(err)
}
errors.LogDebug(context.Background(), "MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)")
conds.Add(matcher)
} }
errors.LogDebug(context.Background(), "MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)")
conds.Add(matcher)
} }
if len(rr.UserEmail) > 0 { if len(rr.UserEmail) > 0 {
conds.Add(NewUserMatcher(rr.UserEmail)) conds.Add(NewUserMatcher(rr.UserEmail))
} }
if rr.VlessRouteList != nil {
conds.Add(NewPortMatcher(rr.VlessRouteList, "vlessRoute"))
}
if len(rr.InboundTag) > 0 { if len(rr.InboundTag) > 0 {
conds.Add(NewInboundTagMatcher(rr.InboundTag)) conds.Add(NewInboundTagMatcher(rr.InboundTag))
} }
if rr.PortList != nil { if rr.PortList != nil {
conds.Add(NewPortMatcher(rr.PortList, false)) conds.Add(NewPortMatcher(rr.PortList, "target"))
} }
if rr.SourcePortList != nil { if rr.SourcePortList != nil {
conds.Add(NewPortMatcher(rr.SourcePortList, true)) conds.Add(NewPortMatcher(rr.SourcePortList, "source"))
}
if rr.LocalPortList != nil {
conds.Add(NewPortMatcher(rr.LocalPortList, "local"))
} }
if len(rr.Networks) > 0 { if len(rr.Networks) > 0 {
@@ -73,7 +70,7 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
} }
if len(rr.Geoip) > 0 { if len(rr.Geoip) > 0 {
cond, err := NewMultiGeoIPMatcher(rr.Geoip, false) cond, err := NewMultiGeoIPMatcher(rr.Geoip, "target")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -81,13 +78,22 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
} }
if len(rr.SourceGeoip) > 0 { if len(rr.SourceGeoip) > 0 {
cond, err := NewMultiGeoIPMatcher(rr.SourceGeoip, true) cond, err := NewMultiGeoIPMatcher(rr.SourceGeoip, "source")
if err != nil { if err != nil {
return nil, err return nil, err
} }
conds.Add(cond) conds.Add(cond)
} }
if len(rr.LocalGeoip) > 0 {
cond, err := NewMultiGeoIPMatcher(rr.LocalGeoip, "local")
if err != nil {
return nil, err
}
conds.Add(cond)
errors.LogWarning(context.Background(), "Due to some limitations, in UDP connections, localIP is always equal to listen interface IP, so \"localIP\" rule condition does not work properly on UDP inbound connections that listen on all interfaces")
}
if len(rr.Protocol) > 0 { if len(rr.Protocol) > 0 {
conds.Add(NewProtocolMatcher(rr.Protocol)) conds.Add(NewProtocolMatcher(rr.Protocol))
} }

View File

@@ -470,7 +470,7 @@ type RoutingRule struct {
// *RoutingRule_Tag // *RoutingRule_Tag
// *RoutingRule_BalancingTag // *RoutingRule_BalancingTag
TargetTag isRoutingRule_TargetTag `protobuf_oneof:"target_tag"` TargetTag isRoutingRule_TargetTag `protobuf_oneof:"target_tag"`
RuleTag string `protobuf:"bytes,18,opt,name=rule_tag,json=ruleTag,proto3" json:"rule_tag,omitempty"` RuleTag string `protobuf:"bytes,19,opt,name=rule_tag,json=ruleTag,proto3" json:"rule_tag,omitempty"`
// List of domains for target domain matching. // List of domains for target domain matching.
Domain []*Domain `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"` Domain []*Domain `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"`
// List of GeoIPs for target IP address matching. If this entry exists, the // List of GeoIPs for target IP address matching. If this entry exists, the
@@ -491,7 +491,9 @@ type RoutingRule struct {
InboundTag []string `protobuf:"bytes,8,rep,name=inbound_tag,json=inboundTag,proto3" json:"inbound_tag,omitempty"` InboundTag []string `protobuf:"bytes,8,rep,name=inbound_tag,json=inboundTag,proto3" json:"inbound_tag,omitempty"`
Protocol []string `protobuf:"bytes,9,rep,name=protocol,proto3" json:"protocol,omitempty"` Protocol []string `protobuf:"bytes,9,rep,name=protocol,proto3" json:"protocol,omitempty"`
Attributes map[string]string `protobuf:"bytes,15,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Attributes map[string]string `protobuf:"bytes,15,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
DomainMatcher string `protobuf:"bytes,17,opt,name=domain_matcher,json=domainMatcher,proto3" json:"domain_matcher,omitempty"` LocalGeoip []*GeoIP `protobuf:"bytes,17,rep,name=local_geoip,json=localGeoip,proto3" json:"local_geoip,omitempty"`
LocalPortList *net.PortList `protobuf:"bytes,18,opt,name=local_port_list,json=localPortList,proto3" json:"local_port_list,omitempty"`
VlessRouteList *net.PortList `protobuf:"bytes,20,opt,name=vless_route_list,json=vlessRouteList,proto3" json:"vless_route_list,omitempty"`
} }
func (x *RoutingRule) Reset() { func (x *RoutingRule) Reset() {
@@ -622,11 +624,25 @@ func (x *RoutingRule) GetAttributes() map[string]string {
return nil return nil
} }
func (x *RoutingRule) GetDomainMatcher() string { func (x *RoutingRule) GetLocalGeoip() []*GeoIP {
if x != nil { if x != nil {
return x.DomainMatcher return x.LocalGeoip
} }
return "" return nil
}
func (x *RoutingRule) GetLocalPortList() *net.PortList {
if x != nil {
return x.LocalPortList
}
return nil
}
func (x *RoutingRule) GetVlessRouteList() *net.PortList {
if x != nil {
return x.VlessRouteList
}
return nil
} }
type isRoutingRule_TargetTag interface { type isRoutingRule_TargetTag interface {
@@ -1069,13 +1085,13 @@ var file_app_router_config_proto_rawDesc = []byte{
0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6e, 0x74,
0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x53, 0x69,
0x74, 0x65, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0xce, 0x05, 0x0a, 0x0b, 0x52, 0x6f, 0x74, 0x65, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0xe8, 0x06, 0x0a, 0x0b, 0x52, 0x6f,
0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x74, 0x61, 0x67,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x25, 0x0a, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x25, 0x0a,
0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x0c, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x0c,
0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e,
0x67, 0x54, 0x61, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x61, 0x67, 0x67, 0x54, 0x61, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x61, 0x67,
0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x12, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x12,
0x2f, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,
0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
@@ -1107,69 +1123,79 @@ var file_app_router_config_proto_rawDesc = []byte{
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x52, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x52,
0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69,
0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72,
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f,
0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72,
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x1a, 0x3d, 0x0a, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65,
0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x6f, 0x49, 0x50, 0x52, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12,
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x41, 0x0a, 0x0f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c, 0x69,
0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x73, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x4c,
0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x22, 0xdc, 0x01, 0x0a, 0x0d, 0x42, 0x69, 0x73, 0x74, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, 0x72, 0x74, 0x4c, 0x69,
0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x10, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x72, 0x6f, 0x75, 0x74,
0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x2b, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78,
0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50,
0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x6f,
0x75, 0x6e, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x75, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69,
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x4d, 0x0a, 0x11, 0x73, 0x74, 0x72, 0x61, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05,
0x65, 0x67, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c,
0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x5f, 0x74, 0x61, 0x67, 0x22, 0xdc, 0x01, 0x0a, 0x0d, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69,
0x73, 0x61, 0x67, 0x65, 0x52, 0x10, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x53, 0x65, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62,
0x63, 0x6b, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x61, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20,
0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x54, 0x61, 0x67, 0x22, 0x54, 0x0a, 0x0e, 0x53, 0x74, 0x72, 0x03, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x65, 0x6c,
0x61, 0x74, 0x65, 0x67, 0x79, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67,
0x65, 0x67, 0x65, 0x78, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67,
0x65, 0x78, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x79, 0x12, 0x4d, 0x0a, 0x11, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x5f, 0x73, 0x65,
0x28, 0x09, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78,
0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61,
0xc0, 0x01, 0x0a, 0x17, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x4c, 0x65, 0x61, 0x73, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x10,
0x74, 0x4c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x05, 0x63, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x74, 0x61, 0x67,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b,
0x61, 0x74, 0x65, 0x67, 0x79, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x05, 0x63, 0x6f, 0x73, 0x54, 0x61, 0x67, 0x22, 0x54, 0x0a, 0x0e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x57,
0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x65, 0x78, 0x70, 0x18,
0x03, 0x20, 0x03, 0x28, 0x03, 0x52, 0x09, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x67, 0x65, 0x78, 0x70, 0x12, 0x14, 0x0a,
0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x61,
0x28, 0x05, 0x52, 0x08, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x63, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01,
0x6d, 0x61, 0x78, 0x52, 0x54, 0x54, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, 0x28, 0x02, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xc0, 0x01, 0x0a, 0x17, 0x53, 0x74,
0x78, 0x52, 0x54, 0x54, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x4c, 0x65, 0x61, 0x73, 0x74, 0x4c, 0x6f, 0x61, 0x64, 0x43,
0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x05, 0x63, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02,
0x63, 0x65, 0x22, 0x9b, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x57,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x05, 0x63, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09,
0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x03, 0x52,
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x09, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78,
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x30, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x65, 0x78,
0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x52, 0x54, 0x54,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x52, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x52, 0x54, 0x54, 0x12, 0x1c,
0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x0a, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28,
0x12, 0x45, 0x0a, 0x0e, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x02, 0x52, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x9b, 0x02, 0x0a,
0x6c, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x22, 0x47, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x73, 0x49, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x73, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x73, 0x65, 0x49, 0x70, 0x10, 0x01, 0x12, 0x10, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x30, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65,
0x0a, 0x0c, 0x49, 0x70, 0x49, 0x66, 0x4e, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x10, 0x02, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x70, 0x4f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x10, 0x03, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67,
0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x45, 0x0a, 0x0e, 0x62, 0x61,
0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x03,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f,
0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0xaa, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75,
0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x6c, 0x65, 0x52, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c,
0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x65, 0x22, 0x47, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74,
0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x73, 0x49, 0x73, 0x10, 0x00, 0x12, 0x09, 0x0a,
0x05, 0x55, 0x73, 0x65, 0x49, 0x70, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x70, 0x49, 0x66,
0x4e, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x70,
0x4f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x10, 0x03, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f,
0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,
0x72, 0x50, 0x01, 0x5a, 0x24, 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, 0x61,
0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0xaa, 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79,
0x2e, 0x41, 0x70, 0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
} }
var ( var (
@@ -1220,16 +1246,19 @@ var file_app_router_config_proto_depIdxs = []int32{
4, // 10: xray.app.router.RoutingRule.source_geoip:type_name -> xray.app.router.GeoIP 4, // 10: xray.app.router.RoutingRule.source_geoip:type_name -> xray.app.router.GeoIP
15, // 11: xray.app.router.RoutingRule.source_port_list:type_name -> xray.common.net.PortList 15, // 11: xray.app.router.RoutingRule.source_port_list:type_name -> xray.common.net.PortList
14, // 12: xray.app.router.RoutingRule.attributes:type_name -> xray.app.router.RoutingRule.AttributesEntry 14, // 12: xray.app.router.RoutingRule.attributes:type_name -> xray.app.router.RoutingRule.AttributesEntry
17, // 13: xray.app.router.BalancingRule.strategy_settings:type_name -> xray.common.serial.TypedMessage 4, // 13: xray.app.router.RoutingRule.local_geoip:type_name -> xray.app.router.GeoIP
10, // 14: xray.app.router.StrategyLeastLoadConfig.costs:type_name -> xray.app.router.StrategyWeight 15, // 14: xray.app.router.RoutingRule.local_port_list:type_name -> xray.common.net.PortList
1, // 15: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy 15, // 15: xray.app.router.RoutingRule.vless_route_list:type_name -> xray.common.net.PortList
8, // 16: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule 17, // 16: xray.app.router.BalancingRule.strategy_settings:type_name -> xray.common.serial.TypedMessage
9, // 17: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule 10, // 17: xray.app.router.StrategyLeastLoadConfig.costs:type_name -> xray.app.router.StrategyWeight
18, // [18:18] is the sub-list for method output_type 1, // 18: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy
18, // [18:18] is the sub-list for method input_type 8, // 19: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule
18, // [18:18] is the sub-list for extension type_name 9, // 20: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule
18, // [18:18] is the sub-list for extension extendee 21, // [21:21] is the sub-list for method output_type
0, // [0:18] is the sub-list for field type_name 21, // [21:21] is the sub-list for method input_type
21, // [21:21] is the sub-list for extension type_name
21, // [21:21] is the sub-list for extension extendee
0, // [0:21] is the sub-list for field type_name
} }
func init() { file_app_router_config_proto_init() } func init() { file_app_router_config_proto_init() }

View File

@@ -79,7 +79,7 @@ message RoutingRule {
// Tag of routing balancer. // Tag of routing balancer.
string balancing_tag = 12; string balancing_tag = 12;
} }
string rule_tag = 18; string rule_tag = 19;
// List of domains for target domain matching. // List of domains for target domain matching.
repeated Domain domain = 2; repeated Domain domain = 2;
@@ -109,7 +109,10 @@ message RoutingRule {
map<string, string> attributes = 15; map<string, string> attributes = 15;
string domain_matcher = 17; repeated GeoIP local_geoip = 17;
xray.common.net.PortList local_port_list = 18;
xray.common.net.PortList vless_route_list = 20;
} }
message BalancingRule { message BalancingRule {

View File

@@ -12,6 +12,8 @@ import (
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
feature_stats "github.com/xtls/xray-core/features/stats" feature_stats "github.com/xtls/xray-core/features/stats"
grpc "google.golang.org/grpc" grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
) )
// statsServer is an implementation of StatsService. // statsServer is an implementation of StatsService.
@@ -30,7 +32,7 @@ func NewStatsServer(manager feature_stats.Manager) StatsServiceServer {
func (s *statsServer) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) { func (s *statsServer) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
c := s.stats.GetCounter(request.Name) c := s.stats.GetCounter(request.Name)
if c == nil { if c == nil {
return nil, errors.New(request.Name, " not found.") return nil, status.Error(codes.NotFound, request.Name+" not found.")
} }
var value int64 var value int64
if request.Reset_ { if request.Reset_ {
@@ -49,7 +51,7 @@ func (s *statsServer) GetStats(ctx context.Context, request *GetStatsRequest) (*
func (s *statsServer) GetStatsOnline(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) { func (s *statsServer) GetStatsOnline(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
c := s.stats.GetOnlineMap(request.Name) c := s.stats.GetOnlineMap(request.Name)
if c == nil { if c == nil {
return nil, errors.New(request.Name, " not found.") return nil, status.Error(codes.NotFound, request.Name+" not found.")
} }
value := int64(c.Count()) value := int64(c.Count())
return &GetStatsResponse{ return &GetStatsResponse{
@@ -64,7 +66,7 @@ func (s *statsServer) GetStatsOnlineIpList(ctx context.Context, request *GetStat
c := s.stats.GetOnlineMap(request.Name) c := s.stats.GetOnlineMap(request.Name)
if c == nil { if c == nil {
return nil, errors.New(request.Name, " not found.") return nil, status.Error(codes.NotFound, request.Name+" not found.")
} }
ips := make(map[string]int64) ips := make(map[string]int64)

View File

@@ -7,7 +7,6 @@ import (
// OnlineMap is an implementation of stats.OnlineMap. // OnlineMap is an implementation of stats.OnlineMap.
type OnlineMap struct { type OnlineMap struct {
value int
ipList map[string]time.Time ipList map[string]time.Time
access sync.RWMutex access sync.RWMutex
lastCleanup time.Time lastCleanup time.Time
@@ -25,7 +24,10 @@ func NewOnlineMap() *OnlineMap {
// Count implements stats.OnlineMap. // Count implements stats.OnlineMap.
func (c *OnlineMap) Count() int { func (c *OnlineMap) Count() int {
return c.value c.access.RLock()
defer c.access.RUnlock()
return len(c.ipList)
} }
// List implements stats.OnlineMap. // List implements stats.OnlineMap.
@@ -35,23 +37,18 @@ func (c *OnlineMap) List() []string {
// AddIP implements stats.OnlineMap. // AddIP implements stats.OnlineMap.
func (c *OnlineMap) AddIP(ip string) { func (c *OnlineMap) AddIP(ip string) {
list := c.ipList
if ip == "127.0.0.1" { if ip == "127.0.0.1" {
return return
} }
c.access.Lock() c.access.Lock()
if _, ok := list[ip]; !ok { c.ipList[ip] = time.Now()
list[ip] = time.Now()
}
c.access.Unlock() c.access.Unlock()
if time.Since(c.lastCleanup) > c.cleanupPeriod { if time.Since(c.lastCleanup) > c.cleanupPeriod {
list = c.RemoveExpiredIPs(list) c.RemoveExpiredIPs()
c.lastCleanup = time.Now() c.lastCleanup = time.Now()
} }
c.value = len(list)
c.ipList = list
} }
func (c *OnlineMap) GetKeys() []string { func (c *OnlineMap) GetKeys() []string {
@@ -65,24 +62,22 @@ func (c *OnlineMap) GetKeys() []string {
return keys return keys
} }
func (c *OnlineMap) RemoveExpiredIPs(list map[string]time.Time) map[string]time.Time { func (c *OnlineMap) RemoveExpiredIPs() {
c.access.Lock() c.access.Lock()
defer c.access.Unlock() defer c.access.Unlock()
now := time.Now() now := time.Now()
for k, t := range list { for k, t := range c.ipList {
diff := now.Sub(t) diff := now.Sub(t)
if diff.Seconds() > 20 { if diff.Seconds() > 20 {
delete(list, k) delete(c.ipList, k)
} }
} }
return list
} }
func (c *OnlineMap) IpTimeMap() map[string]time.Time { func (c *OnlineMap) IpTimeMap() map[string]time.Time {
list := c.ipList
if time.Since(c.lastCleanup) > c.cleanupPeriod { if time.Since(c.lastCleanup) > c.cleanupPeriod {
list = c.RemoveExpiredIPs(list) c.RemoveExpiredIPs()
c.lastCleanup = time.Now() c.lastCleanup = time.Now()
} }

152
app/version/config.pb.go Normal file
View File

@@ -0,0 +1,152 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// source: app/version/config.proto
package version
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 Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
CoreVersion string `protobuf:"bytes,1,opt,name=core_version,json=coreVersion,proto3" json:"core_version,omitempty"`
MinVersion string `protobuf:"bytes,2,opt,name=min_version,json=minVersion,proto3" json:"min_version,omitempty"`
MaxVersion string `protobuf:"bytes,3,opt,name=max_version,json=maxVersion,proto3" json:"max_version,omitempty"`
}
func (x *Config) Reset() {
*x = Config{}
mi := &file_app_version_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Config) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_version_config_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
return file_app_version_config_proto_rawDescGZIP(), []int{0}
}
func (x *Config) GetCoreVersion() string {
if x != nil {
return x.CoreVersion
}
return ""
}
func (x *Config) GetMinVersion() string {
if x != nil {
return x.MinVersion
}
return ""
}
func (x *Config) GetMaxVersion() string {
if x != nil {
return x.MaxVersion
}
return ""
}
var File_app_version_config_proto protoreflect.FileDescriptor
var file_app_version_config_proto_rawDesc = []byte{
0x0a, 0x18, 0x61, 0x70, 0x70, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2f, 0x63, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x6d, 0x0a, 0x06,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f,
0x72, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e,
0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
0x6d, 0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61,
0x78, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0a, 0x6d, 0x61, 0x78, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x52, 0x0a, 0x14, 0x63,
0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 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, 0x61, 0x70, 0x70, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0xaa, 0x02, 0x10, 0x58,
0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_app_version_config_proto_rawDescOnce sync.Once
file_app_version_config_proto_rawDescData = file_app_version_config_proto_rawDesc
)
func file_app_version_config_proto_rawDescGZIP() []byte {
file_app_version_config_proto_rawDescOnce.Do(func() {
file_app_version_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_version_config_proto_rawDescData)
})
return file_app_version_config_proto_rawDescData
}
var file_app_version_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_app_version_config_proto_goTypes = []any{
(*Config)(nil), // 0: xray.app.version.Config
}
var file_app_version_config_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
}
func init() { file_app_version_config_proto_init() }
func file_app_version_config_proto_init() {
if File_app_version_config_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_version_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_app_version_config_proto_goTypes,
DependencyIndexes: file_app_version_config_proto_depIdxs,
MessageInfos: file_app_version_config_proto_msgTypes,
}.Build()
File_app_version_config_proto = out.File
file_app_version_config_proto_rawDesc = nil
file_app_version_config_proto_goTypes = nil
file_app_version_config_proto_depIdxs = nil
}

14
app/version/config.proto Normal file
View File

@@ -0,0 +1,14 @@
syntax = "proto3";
package xray.app.version;
option csharp_namespace = "Xray.App.Version";
option go_package = "github.com/xtls/xray-core/app/version";
option java_package = "com.xray.app.version";
option java_multiple_files = true;
message Config {
string core_version = 1;
string min_version = 2;
string max_version = 3;
}

77
app/version/version.go Normal file
View File

@@ -0,0 +1,77 @@
package version
import (
"context"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"strconv"
"strings"
)
type Version struct {
config *Config
ctx context.Context
}
func New(ctx context.Context, config *Config) (*Version, error) {
if config.MinVersion != "" {
result, err := compareVersions(config.MinVersion, config.CoreVersion)
if err != nil {
return nil, err
}
if result > 0 {
return nil, errors.New("this config must be run on version ", config.MinVersion, " or higher")
}
}
if config.MaxVersion != "" {
result, err := compareVersions(config.MaxVersion, config.CoreVersion)
if err != nil {
return nil, err
}
if result < 0 {
return nil, errors.New("this config should be run on version ", config.MaxVersion, " or lower")
}
}
return &Version{config: config, ctx: ctx}, nil
}
func compareVersions(v1, v2 string) (int, error) {
// Split version strings into components
v1Parts := strings.Split(v1, ".")
v2Parts := strings.Split(v2, ".")
// Pad shorter versions with zeros
for len(v1Parts) < len(v2Parts) {
v1Parts = append(v1Parts, "0")
}
for len(v2Parts) < len(v1Parts) {
v2Parts = append(v2Parts, "0")
}
// Compare each part
for i := 0; i < len(v1Parts); i++ {
// Convert parts to integers
n1, err := strconv.Atoi(v1Parts[i])
if err != nil {
return 0, errors.New("invalid version component ", v1Parts[i], " in ", v1)
}
n2, err := strconv.Atoi(v2Parts[i])
if err != nil {
return 0, errors.New("invalid version component ", v2Parts[i], " in ", v2)
}
if n1 < n2 {
return -1, nil // v1 < v2
}
if n1 > n2 {
return 1, nil // v1 > v2
}
}
return 0, nil // v1 == v2
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return New(ctx, config.(*Config))
}))
}

View File

@@ -13,6 +13,8 @@ const (
Size = 8192 Size = 8192
) )
var ErrBufferFull = errors.New("buffer is full")
var zero = [Size * 10]byte{0} var zero = [Size * 10]byte{0}
var pool = bytespool.GetPool(Size) var pool = bytespool.GetPool(Size)
@@ -244,6 +246,14 @@ func (b *Buffer) Cap() int32 {
return int32(len(b.v)) return int32(len(b.v))
} }
// Available returns the available capacity of the buffer content.
func (b *Buffer) Available() int32 {
if b == nil {
return 0
}
return int32(len(b.v)) - b.end
}
// IsEmpty returns true if the buffer is empty. // IsEmpty returns true if the buffer is empty.
func (b *Buffer) IsEmpty() bool { func (b *Buffer) IsEmpty() bool {
return b.Len() == 0 return b.Len() == 0
@@ -258,13 +268,16 @@ func (b *Buffer) IsFull() bool {
func (b *Buffer) Write(data []byte) (int, error) { func (b *Buffer) Write(data []byte) (int, error) {
nBytes := copy(b.v[b.end:], data) nBytes := copy(b.v[b.end:], data)
b.end += int32(nBytes) b.end += int32(nBytes)
if nBytes < len(data) {
return nBytes, ErrBufferFull
}
return nBytes, nil return nBytes, nil
} }
// WriteByte writes a single byte into the buffer. // WriteByte writes a single byte into the buffer.
func (b *Buffer) WriteByte(v byte) error { func (b *Buffer) WriteByte(v byte) error {
if b.IsFull() { if b.IsFull() {
return errors.New("buffer full") return ErrBufferFull
} }
b.v[b.end] = v b.v[b.end] = v
b.end++ b.end++

View File

@@ -144,7 +144,7 @@ func Compact(mb MultiBuffer) MultiBuffer {
for i := 1; i < len(mb); i++ { for i := 1; i < len(mb); i++ {
curr := mb[i] curr := mb[i]
if last.Len()+curr.Len() > Size { if curr.Len() > last.Available() {
mb2 = append(mb2, last) mb2 = append(mb2, last)
last = curr last = curr
} else { } else {

View File

@@ -175,6 +175,29 @@ func TestCompact(t *testing.T) {
} }
} }
func TestCompactWithConsumed(t *testing.T) {
// make a consumed buffer (a.Start != 0)
a := New()
for range 8192 {
common.Must2(a.WriteString("a"))
}
a.Read(make([]byte, 2))
b := New()
for range 2 {
common.Must2(b.WriteString("b"))
}
mb := MultiBuffer{a, b}
cmb := Compact(mb)
mbc := &MultiBufferContainer{mb}
mbc.Read(make([]byte, 8190))
if w := cmb.String(); w != "bb" {
t.Error("unexpected Compact result ", w)
}
}
func BenchmarkSplitBytes(b *testing.B) { func BenchmarkSplitBytes(b *testing.B) {
var mb MultiBuffer var mb MultiBuffer
raw := make([]byte, Size) raw := make([]byte, Size)

View File

@@ -23,7 +23,9 @@ func Must(err error) {
} }
// Must2 panics if the second parameter is not nil, otherwise returns the first parameter. // Must2 panics if the second parameter is not nil, otherwise returns the first parameter.
func Must2(v interface{}, err error) interface{} { // This is useful when function returned "sth, err" and avoid many "if err != nil"
// Internal usage only, if user input can cause err, it must be handled
func Must2[T any](v T, err error) T {
Must(err) Must(err)
return v return v
} }

View File

@@ -32,9 +32,7 @@ func NewAesCTRStream(key []byte, iv []byte) cipher.Stream {
// NewAesGcm creates a AEAD cipher based on AES-GCM. // NewAesGcm creates a AEAD cipher based on AES-GCM.
func NewAesGcm(key []byte) cipher.AEAD { func NewAesGcm(key []byte) cipher.AEAD {
block, err := aes.NewCipher(key) block := common.Must2(aes.NewCipher(key))
common.Must(err) aead := common.Must2(cipher.NewGCM(block))
aead, err := cipher.NewGCM(block)
common.Must(err)
return aead return aead
} }

View File

@@ -2,8 +2,6 @@ package crypto_test
import ( import (
"bytes" "bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand" "crypto/rand"
"io" "io"
"testing" "testing"
@@ -18,11 +16,8 @@ import (
func TestAuthenticationReaderWriter(t *testing.T) { func TestAuthenticationReaderWriter(t *testing.T) {
key := make([]byte, 16) key := make([]byte, 16)
rand.Read(key) rand.Read(key)
block, err := aes.NewCipher(key)
common.Must(err)
aead, err := cipher.NewGCM(block) aead := NewAesGcm(key)
common.Must(err)
const payloadSize = 1024 * 80 const payloadSize = 1024 * 80
rawPayload := make([]byte, payloadSize) rawPayload := make([]byte, payloadSize)
@@ -71,7 +66,7 @@ func TestAuthenticationReaderWriter(t *testing.T) {
t.Error(r) t.Error(r)
} }
_, err = reader.ReadMultiBuffer() _, err := reader.ReadMultiBuffer()
if err != io.EOF { if err != io.EOF {
t.Error("error: ", err) t.Error("error: ", err)
} }
@@ -80,11 +75,8 @@ func TestAuthenticationReaderWriter(t *testing.T) {
func TestAuthenticationReaderWriterPacket(t *testing.T) { func TestAuthenticationReaderWriterPacket(t *testing.T) {
key := make([]byte, 16) key := make([]byte, 16)
common.Must2(rand.Read(key)) common.Must2(rand.Read(key))
block, err := aes.NewCipher(key)
common.Must(err)
aead, err := cipher.NewGCM(block) aead := NewAesGcm(key)
common.Must(err)
cache := buf.New() cache := buf.New()
iv := make([]byte, 12) iv := make([]byte, 12)

View File

@@ -173,6 +173,7 @@ type ClientWorker struct {
sessionManager *SessionManager sessionManager *SessionManager
link transport.Link link transport.Link
done *done.Instance done *done.Instance
timer *time.Ticker
strategy ClientStrategy strategy ClientStrategy
} }
@@ -187,6 +188,7 @@ func NewClientWorker(stream transport.Link, s ClientStrategy) (*ClientWorker, er
sessionManager: NewSessionManager(), sessionManager: NewSessionManager(),
link: stream, link: stream,
done: done.New(), done: done.New(),
timer: time.NewTicker(time.Second * 16),
strategy: s, strategy: s,
} }
@@ -209,9 +211,12 @@ func (m *ClientWorker) Closed() bool {
return m.done.Done() return m.done.Done()
} }
func (m *ClientWorker) GetTimer() *time.Ticker {
return m.timer
}
func (m *ClientWorker) monitor() { func (m *ClientWorker) monitor() {
timer := time.NewTicker(time.Second * 16) defer m.timer.Stop()
defer timer.Stop()
for { for {
select { select {
@@ -220,7 +225,7 @@ func (m *ClientWorker) monitor() {
common.Close(m.link.Writer) common.Close(m.link.Writer)
common.Interrupt(m.link.Reader) common.Interrupt(m.link.Reader)
return return
case <-timer.C: case <-m.timer.C:
size := m.sessionManager.Size() size := m.sessionManager.Size()
if size == 0 && m.sessionManager.CloseIfNoSession() { if size == 0 && m.sessionManager.CloseIfNoSession() {
common.Must(m.done.Close()) common.Must(m.done.Close())
@@ -276,6 +281,8 @@ func (m *ClientWorker) IsClosing() bool {
return false return false
} }
// IsFull returns true if this ClientWorker is unable to accept more connections.
// it might be because it is closing, or the number of connections has reached the limit.
func (m *ClientWorker) IsFull() bool { func (m *ClientWorker) IsFull() bool {
if m.IsClosing() || m.Closed() { if m.IsClosing() || m.Closed() {
return true return true
@@ -289,12 +296,12 @@ func (m *ClientWorker) IsFull() bool {
} }
func (m *ClientWorker) Dispatch(ctx context.Context, link *transport.Link) bool { func (m *ClientWorker) Dispatch(ctx context.Context, link *transport.Link) bool {
if m.IsFull() || m.Closed() { if m.IsFull() {
return false return false
} }
sm := m.sessionManager sm := m.sessionManager
s := sm.Allocate() s := sm.Allocate(&m.strategy)
if s == nil { if s == nil {
return false return false
} }

View File

@@ -118,9 +118,7 @@ func (w *ServerWorker) handleStatusKeepAlive(meta *FrameMetadata, reader *buf.Bu
} }
func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, reader *buf.BufferedReader) error { func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, reader *buf.BufferedReader) error {
// deep-clone outbounds because it is going to be mutated concurrently ctx = session.SubContextFromMuxInbound(ctx)
// (Target and OriginalTarget)
ctx = session.ContextCloneOutboundsAndContent(ctx)
errors.LogInfo(ctx, "received request for ", meta.Target) errors.LogInfo(ctx, "received request for ", meta.Target)
{ {
msg := &log.AccessMessage{ msg := &log.AccessMessage{
@@ -201,11 +199,12 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
transferType: protocol.TransferTypePacket, transferType: protocol.TransferTypePacket,
XUDP: x, XUDP: x,
} }
go handle(ctx, x.Mux, w.link.Writer)
x.Status = Active x.Status = Active
if !w.sessionManager.Add(x.Mux) { if !w.sessionManager.Add(x.Mux) {
x.Mux.Close(false) x.Mux.Close(false)
return errors.New("failed to add new session")
} }
go handle(ctx, x.Mux, w.link.Writer)
return nil return nil
} }
@@ -226,18 +225,23 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
if meta.Target.Network == net.Network_UDP { if meta.Target.Network == net.Network_UDP {
s.transferType = protocol.TransferTypePacket s.transferType = protocol.TransferTypePacket
} }
w.sessionManager.Add(s) if !w.sessionManager.Add(s) {
s.Close(false)
return errors.New("failed to add new session")
}
go handle(ctx, s, w.link.Writer) go handle(ctx, s, w.link.Writer)
if !meta.Option.Has(OptionData) { if !meta.Option.Has(OptionData) {
return nil return nil
} }
rr := s.NewReader(reader, &meta.Target) rr := s.NewReader(reader, &meta.Target)
if err := buf.Copy(rr, s.output); err != nil { err = buf.Copy(rr, s.output)
buf.Copy(rr, buf.Discard)
return s.Close(false) if err != nil && buf.IsWriteError(err) {
s.Close(false)
return buf.Copy(rr, buf.Discard)
} }
return nil return err
} }
func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.BufferedReader) error { func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.BufferedReader) error {
@@ -304,10 +308,11 @@ func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedRead
} }
func (w *ServerWorker) run(ctx context.Context) { func (w *ServerWorker) run(ctx context.Context) {
input := w.link.Reader reader := &buf.BufferedReader{Reader: w.link.Reader}
reader := &buf.BufferedReader{Reader: input}
defer w.sessionManager.Close() defer w.sessionManager.Close()
defer common.Close(w.link.Writer)
defer common.Interrupt(w.link.Reader)
for { for {
select { select {
@@ -318,7 +323,6 @@ func (w *ServerWorker) run(ctx context.Context) {
if err != nil { if err != nil {
if errors.Cause(err) != io.EOF { if errors.Cause(err) != io.EOF {
errors.LogInfoInner(ctx, err, "unexpected EOF") errors.LogInfoInner(ctx, err, "unexpected EOF")
common.Interrupt(input)
} }
return return
} }

View File

@@ -50,11 +50,14 @@ func (m *SessionManager) Count() int {
return int(m.count) return int(m.count)
} }
func (m *SessionManager) Allocate() *Session { func (m *SessionManager) Allocate(Strategy *ClientStrategy) *Session {
m.Lock() m.Lock()
defer m.Unlock() defer m.Unlock()
MaxConcurrency := int(Strategy.MaxConcurrency)
MaxConnection := uint16(Strategy.MaxConnection)
if m.closed { if m.closed || (MaxConcurrency > 0 && len(m.sessions) >= MaxConcurrency) || (MaxConnection > 0 && m.count >= MaxConnection) {
return nil return nil
} }

View File

@@ -9,7 +9,7 @@ import (
func TestSessionManagerAdd(t *testing.T) { func TestSessionManagerAdd(t *testing.T) {
m := NewSessionManager() m := NewSessionManager()
s := m.Allocate() s := m.Allocate(&ClientStrategy{})
if s.ID != 1 { if s.ID != 1 {
t.Error("id: ", s.ID) t.Error("id: ", s.ID)
} }
@@ -17,7 +17,7 @@ func TestSessionManagerAdd(t *testing.T) {
t.Error("size: ", m.Size()) t.Error("size: ", m.Size())
} }
s = m.Allocate() s = m.Allocate(&ClientStrategy{})
if s.ID != 2 { if s.ID != 2 {
t.Error("id: ", s.ID) t.Error("id: ", s.ID)
} }
@@ -39,7 +39,7 @@ func TestSessionManagerAdd(t *testing.T) {
func TestSessionManagerClose(t *testing.T) { func TestSessionManagerClose(t *testing.T) {
m := NewSessionManager() m := NewSessionManager()
s := m.Allocate() s := m.Allocate(&ClientStrategy{})
if m.CloseIfNoSession() { if m.CloseIfNoSession() {
t.Error("able to close") t.Error("able to close")

View File

@@ -184,8 +184,7 @@ func getConfig() string {
"inboundTag": [ "inboundTag": [
"api-in" "api-in"
], ],
"outboundTag": "api", "outboundTag": "api"
"type": "field"
} }
], ],
"domainStrategy": "AsIs" "domainStrategy": "AsIs"

View File

@@ -16,15 +16,15 @@ const (
inboundSessionKey ctx.SessionKey = 1 inboundSessionKey ctx.SessionKey = 1
outboundSessionKey ctx.SessionKey = 2 outboundSessionKey ctx.SessionKey = 2
contentSessionKey ctx.SessionKey = 3 contentSessionKey ctx.SessionKey = 3
muxPreferredSessionKey ctx.SessionKey = 4 muxPreferredSessionKey ctx.SessionKey = 4 // unused
sockoptSessionKey ctx.SessionKey = 5 sockoptSessionKey ctx.SessionKey = 5 // used by dokodemo to only receive sockopt.Mark
trackedConnectionErrorKey ctx.SessionKey = 6 trackedConnectionErrorKey ctx.SessionKey = 6 // used by observer to get outbound error
dispatcherKey ctx.SessionKey = 7 dispatcherKey ctx.SessionKey = 7 // used by ss2022 inbounds to get dispatcher
timeoutOnlyKey ctx.SessionKey = 8 timeoutOnlyKey ctx.SessionKey = 8 // mux context's child contexts to only cancel when its own traffic times out
allowedNetworkKey ctx.SessionKey = 9 allowedNetworkKey ctx.SessionKey = 9 // muxcool server control incoming request tcp/udp
handlerSessionKey ctx.SessionKey = 10 handlerSessionKey ctx.SessionKey = 10 // unused
mitmAlpn11Key ctx.SessionKey = 11 mitmAlpn11Key ctx.SessionKey = 11 // used by TLS dialer
mitmServerNameKey ctx.SessionKey = 12 mitmServerNameKey ctx.SessionKey = 12 // used by TLS dialer
) )
func ContextWithInbound(ctx context.Context, inbound *Inbound) context.Context { func ContextWithInbound(ctx context.Context, inbound *Inbound) context.Context {
@@ -42,18 +42,8 @@ func ContextWithOutbounds(ctx context.Context, outbounds []*Outbound) context.Co
return context.WithValue(ctx, outboundSessionKey, outbounds) return context.WithValue(ctx, outboundSessionKey, outbounds)
} }
func ContextCloneOutboundsAndContent(ctx context.Context) context.Context { func SubContextFromMuxInbound(ctx context.Context) context.Context {
outbounds := OutboundsFromContext(ctx) newOutbounds := []*Outbound{{}}
newOutbounds := make([]*Outbound, len(outbounds))
for i, ob := range outbounds {
if ob == nil {
continue
}
// copy outbound by value
v := *ob
newOutbounds[i] = &v
}
content := ContentFromContext(ctx) content := ContentFromContext(ctx)
newContent := Content{} newContent := Content{}

View File

@@ -36,6 +36,8 @@ func ExportIDToError(ctx context.Context) errors.ExportOption {
type Inbound struct { type Inbound struct {
// Source address of the inbound connection. // Source address of the inbound connection.
Source net.Destination Source net.Destination
// Local address of the inbound connection.
Local net.Destination
// Gateway address. // Gateway address.
Gateway net.Destination Gateway net.Destination
// Tag of the inbound proxy that handles the connection. // Tag of the inbound proxy that handles the connection.
@@ -44,9 +46,11 @@ type Inbound struct {
Name string Name string
// User is the user that authenticates for the inbound. May be nil if the protocol allows anonymous traffic. // User is the user that authenticates for the inbound. May be nil if the protocol allows anonymous traffic.
User *protocol.MemoryUser User *protocol.MemoryUser
// Conn is actually internet.Connection. May be nil. // VlessRoute is the user-sent VLESS UUID's 7th<<8 | 8th bytes.
VlessRoute net.Port
// Used by splice copy. Conn is actually internet.Connection. May be nil.
Conn net.Conn Conn net.Conn
// Timer of the inbound buf copier. May be nil. // Used by splice copy. Timer of the inbound buf copier. May be nil.
Timer *signal.ActivityTimer Timer *signal.ActivityTimer
// CanSpliceCopy is a property for this connection // CanSpliceCopy is a property for this connection
// 1 = can, 2 = after processing protocol info should be able to, 3 = cannot // 1 = can, 2 = after processing protocol info should be able to, 3 = cannot
@@ -65,31 +69,33 @@ type Outbound struct {
Tag string Tag string
// Name of the outbound proxy that handles the connection. // Name of the outbound proxy that handles the connection.
Name string Name string
// Conn is actually internet.Connection. May be nil. It is currently nil for outbound with proxySettings // Unused. Conn is actually internet.Connection. May be nil. It is currently nil for outbound with proxySettings
Conn net.Conn Conn net.Conn
// CanSpliceCopy is a property for this connection // CanSpliceCopy is a property for this connection
// 1 = can, 2 = after processing protocol info should be able to, 3 = cannot // 1 = can, 2 = after processing protocol info should be able to, 3 = cannot
CanSpliceCopy int CanSpliceCopy int
} }
// SniffingRequest controls the behavior of content sniffing. // SniffingRequest controls the behavior of content sniffing. They are from inbound config. Read-only
type SniffingRequest struct { type SniffingRequest struct {
ExcludeForDomain []string // read-only once set ExcludeForDomain []string
OverrideDestinationForProtocol []string // read-only once set OverrideDestinationForProtocol []string
Enabled bool Enabled bool
MetadataOnly bool MetadataOnly bool
RouteOnly bool RouteOnly bool
} }
// Content is the metadata of the connection content. // Content is the metadata of the connection content. Mainly used for routing.
type Content struct { type Content struct {
// Protocol of current content. // Protocol of current content.
Protocol string Protocol string
SniffingRequest SniffingRequest SniffingRequest SniffingRequest
// HTTP traffic sniffed headers
Attributes map[string]string Attributes map[string]string
// SkipDNSResolve is set from DNS module. the DOH remote server maybe a domain name, this prevents cycle resolving dead loop
SkipDNSResolve bool SkipDNSResolve bool
} }

View File

@@ -67,9 +67,9 @@ func (t *ActivityTimer) SetTimeout(timeout time.Duration) {
t.checkTask.Close() t.checkTask.Close()
} }
t.checkTask = checkTask t.checkTask = checkTask
t.Unlock()
t.Update() t.Update()
common.Must(checkTask.Start()) common.Must(checkTask.Start())
t.Unlock()
} }
func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer { func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer {

View File

@@ -0,0 +1,112 @@
package utils
import (
"sync"
)
// TypedSyncMap is a wrapper of sync.Map that provides type-safe for keys and values.
// No need to use type assertions every time, so you can have more time to enjoy other things like GochiUsa
// If sync.Map methods returned nil, it will return the zero value of the type V.
type TypedSyncMap[K, V any] struct {
syncMap *sync.Map
}
// NewTypedSyncMap creates a new TypedSyncMap
// K is key type, V is value type
// It is recommended to use pointer types for V because sync.Map might return nil
// If sync.Map methods really returned nil, it will return the zero value of the type V
func NewTypedSyncMap[K any, V any]() *TypedSyncMap[K, V] {
return &TypedSyncMap[K, V]{
syncMap: &sync.Map{},
}
}
// Clear deletes all the entries, resulting in an empty Map.
func (m *TypedSyncMap[K, V]) Clear() {
m.syncMap.Clear()
}
// CompareAndDelete deletes the entry for key if its value is equal to old.
// The old value must be of a comparable type.
//
// If there is no current value for key in the map, CompareAndDelete
// returns false (even if the old value is the nil interface value).
func (m *TypedSyncMap[K, V]) CompareAndDelete(key K, old V) (deleted bool) {
return m.syncMap.CompareAndDelete(key, old)
}
// CompareAndSwap swaps the old and new values for key
// if the value stored in the map is equal to old.
// The old value must be of a comparable type.
func (m *TypedSyncMap[K, V]) CompareAndSwap(key K, old V, new V) (swapped bool) {
return m.syncMap.CompareAndSwap(key, old, new)
}
// Delete deletes the value for a key.
func (m *TypedSyncMap[K, V]) Delete(key K) {
m.syncMap.Delete(key)
}
// Load returns the value stored in the map for a key, or nil if no
// value is present.
// The ok result indicates whether value was found in the map.
func (m *TypedSyncMap[K, V]) Load(key K) (value V, ok bool) {
anyValue, ok := m.syncMap.Load(key)
// anyValue might be nil
if anyValue != nil {
value = anyValue.(V)
}
return value, ok
}
// LoadAndDelete deletes the value for a key, returning the previous value if any.
// The loaded result reports whether the key was present.
func (m *TypedSyncMap[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
anyValue, loaded := m.syncMap.LoadAndDelete(key)
if anyValue != nil {
value = anyValue.(V)
}
return value, loaded
}
// LoadOrStore returns the existing value for the key if present.
// Otherwise, it stores and returns the given value.
// The loaded result is true if the value was loaded, false if stored.
func (m *TypedSyncMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
anyActual, loaded := m.syncMap.LoadOrStore(key, value)
if anyActual != nil {
actual = anyActual.(V)
}
return actual, loaded
}
// Range calls f sequentially for each key and value present in the map.
// If f returns false, range stops the iteration.
//
// Range does not necessarily correspond to any consistent snapshot of the Map's
// contents: no key will be visited more than once, but if the value for any key
// is stored or deleted concurrently (including by f), Range may reflect any
// mapping for that key from any point during the Range call. Range does not
// block other methods on the receiver; even f itself may call any method on m.
//
// Range may be O(N) with the number of elements in the map even if f returns
// false after a constant number of calls.
func (m *TypedSyncMap[K, V]) Range(f func(key K, value V) bool) {
m.syncMap.Range(func(key, value any) bool {
return f(key.(K), value.(V))
})
}
// Store sets the value for a key.
func (m *TypedSyncMap[K, V]) Store(key K, value V) {
m.syncMap.Store(key, value)
}
// Swap swaps the value for a key and returns the previous value if any. The loaded result reports whether the key was present.
func (m *TypedSyncMap[K, V]) Swap(key K, value V) (previous V, loaded bool) {
anyPrevious, loaded := m.syncMap.Swap(key, value)
if anyPrevious != nil {
previous = anyPrevious.(V)
}
return previous, loaded
}

View File

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

View File

@@ -42,6 +42,24 @@ func (e RCodeError) Error() string {
return serial.Concat("rcode: ", uint16(e)) return serial.Concat("rcode: ", uint16(e))
} }
func (RCodeError) IP() net.IP {
panic("Calling IP() on a RCodeError.")
}
func (RCodeError) Domain() string {
panic("Calling Domain() on a RCodeError.")
}
func (RCodeError) Family() net.AddressFamily {
panic("Calling Family() on a RCodeError.")
}
func (e RCodeError) String() string {
return e.Error()
}
var _ net.Address = (*RCodeError)(nil)
func RCodeFromError(err error) uint16 { func RCodeFromError(err error) uint16 {
if err == nil { if err == nil {
return 0 return 0

View File

@@ -23,6 +23,12 @@ type Context interface {
// GetTargetPort returns the target port of the connection. // GetTargetPort returns the target port of the connection.
GetTargetPort() net.Port GetTargetPort() net.Port
// GetLocalIPs returns the local IPs bound to the connection.
GetLocalIPs() []net.IP
// GetLocalPort returns the local port of the connection.
GetLocalPort() net.Port
// GetTargetDomain returns the target domain of the connection, if exists. // GetTargetDomain returns the target domain of the connection, if exists.
GetTargetDomain() string GetTargetDomain() string
@@ -35,6 +41,9 @@ type Context interface {
// GetUser returns the user email from the connection content, if exists. // GetUser returns the user email from the connection content, if exists.
GetUser() string GetUser() string
// GetVlessRoute returns the user-sent VLESS UUID's 7th<<8 | 8th bytes, if exists.
GetVlessRoute() net.Port
// GetAttributes returns extra attributes from the conneciont content. // GetAttributes returns extra attributes from the conneciont content.
GetAttributes() map[string]string GetAttributes() map[string]string

View File

@@ -28,12 +28,13 @@ func (ctx *Context) GetSourceIPs() []net.IP {
if ctx.Inbound == nil || !ctx.Inbound.Source.IsValid() { if ctx.Inbound == nil || !ctx.Inbound.Source.IsValid() {
return nil return nil
} }
dest := ctx.Inbound.Source
if dest.Address.Family().IsDomain() { if ctx.Inbound.Source.Address.Family().IsIP() {
return nil return []net.IP{ctx.Inbound.Source.Address.IP()}
} }
return []net.IP{dest.Address.IP()} return nil
} }
// GetSourcePort implements routing.Context. // GetSourcePort implements routing.Context.
@@ -65,6 +66,27 @@ func (ctx *Context) GetTargetPort() net.Port {
return ctx.Outbound.Target.Port return ctx.Outbound.Target.Port
} }
// GetLocalIPs implements routing.Context.
func (ctx *Context) GetLocalIPs() []net.IP {
if ctx.Inbound == nil || !ctx.Inbound.Local.IsValid() {
return nil
}
if ctx.Inbound.Local.Address.Family().IsIP() {
return []net.IP{ctx.Inbound.Local.Address.IP()}
}
return nil
}
// GetLocalPort implements routing.Context.
func (ctx *Context) GetLocalPort() net.Port {
if ctx.Inbound == nil || !ctx.Inbound.Local.IsValid() {
return 0
}
return ctx.Inbound.Local.Port
}
// GetTargetDomain implements routing.Context. // GetTargetDomain implements routing.Context.
func (ctx *Context) GetTargetDomain() string { func (ctx *Context) GetTargetDomain() string {
if ctx.Outbound == nil || !ctx.Outbound.Target.IsValid() { if ctx.Outbound == nil || !ctx.Outbound.Target.IsValid() {
@@ -106,6 +128,14 @@ func (ctx *Context) GetUser() string {
return ctx.Inbound.User.Email return ctx.Inbound.User.Email
} }
// GetVlessRoute implements routing.Context.
func (ctx *Context) GetVlessRoute() net.Port {
if ctx.Inbound == nil {
return 0
}
return ctx.Inbound.VlessRoute
}
// GetAttributes implements routing.Context. // GetAttributes implements routing.Context.
func (ctx *Context) GetAttributes() map[string]string { func (ctx *Context) GetAttributes() map[string]string {
if ctx.Content == nil { if ctx.Content == nil {

44
go.mod
View File

@@ -1,60 +1,58 @@
module github.com/xtls/xray-core module github.com/xtls/xray-core
go 1.24 go 1.25
require ( require (
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0
github.com/cloudflare/circl v1.6.1 github.com/cloudflare/circl v1.6.1
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344
github.com/golang/mock v1.7.0-rc.1 github.com/golang/mock v1.7.0-rc.1
github.com/google/go-cmp v0.7.0 github.com/google/go-cmp v0.7.0
github.com/gorilla/websocket v1.5.3 github.com/gorilla/websocket v1.5.3
github.com/miekg/dns v1.1.66 github.com/miekg/dns v1.1.68
github.com/pelletier/go-toml v1.9.5 github.com/pelletier/go-toml v1.9.5
github.com/pires/go-proxyproto v0.8.1 github.com/pires/go-proxyproto v0.8.1
github.com/quic-go/quic-go v0.52.0 github.com/quic-go/quic-go v0.54.0
github.com/refraction-networking/utls v1.7.3 github.com/refraction-networking/utls v1.8.0
github.com/sagernet/sing v0.5.1 github.com/sagernet/sing v0.5.1
github.com/sagernet/sing-shadowsocks v0.2.7 github.com/sagernet/sing-shadowsocks v0.2.7
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
github.com/vishvananda/netlink v1.3.1 github.com/vishvananda/netlink v1.3.1
github.com/xtls/reality v0.0.0-20250607105625-90e738a94c8c github.com/xtls/reality v0.0.0-20250725142056-5b52a03d4fb7
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/crypto v0.39.0 golang.org/x/crypto v0.41.0
golang.org/x/net v0.41.0 golang.org/x/net v0.43.0
golang.org/x/sync v0.15.0 golang.org/x/sync v0.16.0
golang.org/x/sys v0.33.0 golang.org/x/sys v0.35.0
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
google.golang.org/grpc v1.73.0 google.golang.org/grpc v1.75.0
google.golang.org/protobuf v1.36.6 google.golang.org/protobuf v1.36.7
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5 gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5
h12.io/socks v1.0.3 h12.io/socks v1.0.3
lukechampine.com/blake3 v1.4.1 lukechampine.com/blake3 v1.4.1
) )
require ( require (
github.com/andybalholm/brotli v1.1.0 // indirect github.com/andybalholm/brotli v1.0.6 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/btree v1.1.2 // indirect github.com/google/btree v1.1.2 // indirect
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba // indirect github.com/juju/ratelimit v1.0.2 // indirect
github.com/klauspost/compress v1.17.8 // indirect github.com/klauspost/compress v1.17.4 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/klauspost/cpuid/v2 v2.0.12 // indirect
github.com/onsi/ginkgo/v2 v2.19.0 // indirect github.com/kr/pretty v0.3.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/vishvananda/netns v0.0.5 // indirect github.com/vishvananda/netns v0.0.5 // indirect
go.uber.org/mock v0.5.0 // indirect go.uber.org/mock v0.5.0 // indirect
golang.org/x/mod v0.25.0 // indirect golang.org/x/mod v0.26.0 // indirect
golang.org/x/text v0.26.0 // indirect golang.org/x/text v0.28.0 // indirect
golang.org/x/time v0.7.0 // indirect golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.33.0 // indirect golang.org/x/tools v0.35.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

121
go.sum
View File

@@ -1,23 +1,19 @@
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 h1:Wo41lDOevRJSGpevP+8Pk5bANX7fJacO2w04aqLiC5I= github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM= github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165 h1:BS21ZUJ/B5X2UVUbczfmdWH7GapPWAhxcMsDnjJTU1E=
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U= github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs= github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
@@ -26,40 +22,43 @@ github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JTNM+zJrgIbnyCKX/wdlyPufP5g=
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364 h1:5XxdakFhqd9dnXoAZy1Mb2R/DZ6D1e+0bGC/JhucGYI= github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364 h1:5XxdakFhqd9dnXoAZy1Mb2R/DZ6D1e+0bGC/JhucGYI=
github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c= github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0= github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA= github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ= github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
github.com/refraction-networking/utls v1.7.3 h1:L0WRhHY7Oq1T0zkdzVZMR6zWZv+sXbHB9zcuvsAEqCo= github.com/refraction-networking/utls v1.8.0 h1:L38krhiTAyj9EeiQQa2sg+hYb4qwLCqdMcpZrRfbONE=
github.com/refraction-networking/utls v1.7.3/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ= github.com/refraction-networking/utls v1.8.0/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sagernet/sing v0.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y= github.com/sagernet/sing v0.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y=
github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8= github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
@@ -76,64 +75,63 @@ github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= 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 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/xtls/reality v0.0.0-20250607105625-90e738a94c8c h1:GiY3/SynO0ujSH3rQDEIrE4MTTZM9KHufR3zx3JLD3c= github.com/xtls/reality v0.0.0-20250725142056-5b52a03d4fb7 h1:Ript0vN+nSO33+Vj4n0mgNY5M+oOxFQJdrJ1VnwTBO0=
github.com/xtls/reality v0.0.0-20250607105625-90e738a94c8c/go.mod h1:Rkdcxe9Yd8SWQRRP+LSvX6wxk1m4lmNkyUZEHzbPDZw= github.com/xtls/reality v0.0.0-20250725142056-5b52a03d4fb7/go.mod h1:XxvnCCgBee4WWE0bc4E+a7wbk8gkJ/rS0vNVNtC5qp0=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 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 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -141,14 +139,17 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

View File

@@ -28,9 +28,7 @@ func (c *DNSOutboundConfig) Build() (proto.Message, error) {
config.Server.Address = c.Address.Build() config.Server.Address = c.Address.Build()
} }
switch c.NonIPQuery { switch c.NonIPQuery {
case "": case "", "reject", "drop", "skip":
c.NonIPQuery = "drop"
case "drop", "skip":
default: default:
return nil, errors.New(`unknown "nonIPQuery": `, c.NonIPQuery) return nil, errors.New(`unknown "nonIPQuery": `, c.NonIPQuery)
} }

View File

@@ -27,7 +27,6 @@ func TestDnsProxyConfig(t *testing.T) {
Address: net.NewIPOrDomain(net.IPAddress([]byte{8, 8, 8, 8})), Address: net.NewIPOrDomain(net.IPAddress([]byte{8, 8, 8, 8})),
Port: 53, Port: 53,
}, },
Non_IPQuery: "drop",
}, },
}, },
}) })

View File

@@ -1,26 +1,35 @@
package conf package conf
import ( import (
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/proxy/dokodemo" "github.com/xtls/xray-core/proxy/dokodemo"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
type DokodemoConfig struct { type DokodemoConfig struct {
Host *Address `json:"address"` Address *Address `json:"address"`
PortValue uint16 `json:"port"` Port uint16 `json:"port"`
NetworkList *NetworkList `json:"network"` PortMap map[string]string `json:"portMap"`
Redirect bool `json:"followRedirect"` Network *NetworkList `json:"network"`
UserLevel uint32 `json:"userLevel"` FollowRedirect bool `json:"followRedirect"`
UserLevel uint32 `json:"userLevel"`
} }
func (v *DokodemoConfig) Build() (proto.Message, error) { func (v *DokodemoConfig) Build() (proto.Message, error) {
config := new(dokodemo.Config) config := new(dokodemo.Config)
if v.Host != nil { if v.Address != nil {
config.Address = v.Host.Build() config.Address = v.Address.Build()
} }
config.Port = uint32(v.PortValue) config.Port = uint32(v.Port)
config.Networks = v.NetworkList.Build() config.PortMap = v.PortMap
config.FollowRedirect = v.Redirect for _, v := range config.PortMap {
if _, _, err := net.SplitHostPort(v); err != nil {
return nil, errors.New("invalid portMap: ", v).Base(err)
}
}
config.Networks = v.Network.Build()
config.FollowRedirect = v.FollowRedirect
config.UserLevel = v.UserLevel config.UserLevel = v.UserLevel
return config, nil return config, nil
} }

View File

@@ -11,9 +11,11 @@ import (
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/proxy/freedom" "github.com/xtls/xray-core/proxy/freedom"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"github.com/xtls/xray-core/transport/internet"
) )
type FreedomConfig struct { type FreedomConfig struct {
TargetStrategy string `json:"targetStrategy"`
DomainStrategy string `json:"domainStrategy"` DomainStrategy string `json:"domainStrategy"`
Redirect string `json:"redirect"` Redirect string `json:"redirect"`
UserLevel uint32 `json:"userLevel"` UserLevel uint32 `json:"userLevel"`
@@ -27,42 +29,48 @@ type Fragment struct {
Packets string `json:"packets"` Packets string `json:"packets"`
Length *Int32Range `json:"length"` Length *Int32Range `json:"length"`
Interval *Int32Range `json:"interval"` Interval *Int32Range `json:"interval"`
MaxSplit *Int32Range `json:"maxSplit"`
} }
type Noise struct { type Noise struct {
Type string `json:"type"` Type string `json:"type"`
Packet string `json:"packet"` Packet string `json:"packet"`
Delay *Int32Range `json:"delay"` Delay *Int32Range `json:"delay"`
ApplyTo string `json:"applyTo"`
} }
// Build implements Buildable // Build implements Buildable
func (c *FreedomConfig) Build() (proto.Message, error) { func (c *FreedomConfig) Build() (proto.Message, error) {
config := new(freedom.Config) config := new(freedom.Config)
switch strings.ToLower(c.DomainStrategy) { targetStrategy := c.TargetStrategy
if targetStrategy == "" {
targetStrategy = c.DomainStrategy
}
switch strings.ToLower(targetStrategy) {
case "asis", "": case "asis", "":
config.DomainStrategy = freedom.Config_AS_IS config.DomainStrategy = internet.DomainStrategy_AS_IS
case "useip": case "useip":
config.DomainStrategy = freedom.Config_USE_IP config.DomainStrategy = internet.DomainStrategy_USE_IP
case "useipv4": case "useipv4":
config.DomainStrategy = freedom.Config_USE_IP4 config.DomainStrategy = internet.DomainStrategy_USE_IP4
case "useipv6": case "useipv6":
config.DomainStrategy = freedom.Config_USE_IP6 config.DomainStrategy = internet.DomainStrategy_USE_IP6
case "useipv4v6": case "useipv4v6":
config.DomainStrategy = freedom.Config_USE_IP46 config.DomainStrategy = internet.DomainStrategy_USE_IP46
case "useipv6v4": case "useipv6v4":
config.DomainStrategy = freedom.Config_USE_IP64 config.DomainStrategy = internet.DomainStrategy_USE_IP64
case "forceip": case "forceip":
config.DomainStrategy = freedom.Config_FORCE_IP config.DomainStrategy = internet.DomainStrategy_FORCE_IP
case "forceipv4": case "forceipv4":
config.DomainStrategy = freedom.Config_FORCE_IP4 config.DomainStrategy = internet.DomainStrategy_FORCE_IP4
case "forceipv6": case "forceipv6":
config.DomainStrategy = freedom.Config_FORCE_IP6 config.DomainStrategy = internet.DomainStrategy_FORCE_IP6
case "forceipv4v6": case "forceipv4v6":
config.DomainStrategy = freedom.Config_FORCE_IP46 config.DomainStrategy = internet.DomainStrategy_FORCE_IP46
case "forceipv6v4": case "forceipv6v4":
config.DomainStrategy = freedom.Config_FORCE_IP64 config.DomainStrategy = internet.DomainStrategy_FORCE_IP64
default: default:
return nil, errors.New("unsupported domain strategy: ", c.DomainStrategy) return nil, errors.New("unsupported domain strategy: ", targetStrategy)
} }
if c.Fragment != nil { if c.Fragment != nil {
@@ -108,6 +116,13 @@ func (c *FreedomConfig) Build() (proto.Message, error) {
config.Fragment.IntervalMin = uint64(c.Fragment.Interval.From) config.Fragment.IntervalMin = uint64(c.Fragment.Interval.From)
config.Fragment.IntervalMax = uint64(c.Fragment.Interval.To) config.Fragment.IntervalMax = uint64(c.Fragment.Interval.To)
} }
{
if c.Fragment.MaxSplit != nil {
config.Fragment.MaxSplitMin = uint64(c.Fragment.MaxSplit.From)
config.Fragment.MaxSplitMax = uint64(c.Fragment.MaxSplit.To)
}
}
} }
if c.Noise != nil { if c.Noise != nil {
@@ -193,5 +208,15 @@ func ParseNoise(noise *Noise) (*freedom.Noise, error) {
NConfig.DelayMin = uint64(noise.Delay.From) NConfig.DelayMin = uint64(noise.Delay.From)
NConfig.DelayMax = uint64(noise.Delay.To) NConfig.DelayMax = uint64(noise.Delay.To)
} }
switch strings.ToLower(noise.ApplyTo) {
case "", "ip", "all":
NConfig.ApplyTo = "ip"
case "ipv4":
NConfig.ApplyTo = "ipv4"
case "ipv6":
NConfig.ApplyTo = "ipv6"
default:
return nil, errors.New("Invalid applyTo, only ip/ipv4/ipv6 are supported")
}
return NConfig, nil return NConfig, nil
} }

View File

@@ -7,6 +7,7 @@ import (
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
. "github.com/xtls/xray-core/infra/conf" . "github.com/xtls/xray-core/infra/conf"
"github.com/xtls/xray-core/proxy/freedom" "github.com/xtls/xray-core/proxy/freedom"
"github.com/xtls/xray-core/transport/internet"
) )
func TestFreedomConfig(t *testing.T) { func TestFreedomConfig(t *testing.T) {
@@ -23,7 +24,7 @@ func TestFreedomConfig(t *testing.T) {
}`, }`,
Parser: loadJSON(creator), Parser: loadJSON(creator),
Output: &freedom.Config{ Output: &freedom.Config{
DomainStrategy: freedom.Config_AS_IS, DomainStrategy: internet.DomainStrategy_AS_IS,
DestinationOverride: &freedom.DestinationOverride{ DestinationOverride: &freedom.DestinationOverride{
Server: &protocol.ServerEndpoint{ Server: &protocol.ServerEndpoint{
Address: &net.IPOrDomain{ Address: &net.IPOrDomain{

View File

@@ -74,8 +74,6 @@ type RouterConfig struct {
RuleList []json.RawMessage `json:"rules"` RuleList []json.RawMessage `json:"rules"`
DomainStrategy *string `json:"domainStrategy"` DomainStrategy *string `json:"domainStrategy"`
Balancers []*BalancingRule `json:"balancers"` Balancers []*BalancingRule `json:"balancers"`
DomainMatcher string `json:"domainMatcher"`
} }
func (c *RouterConfig) getDomainStrategy() router.Config_DomainStrategy { func (c *RouterConfig) getDomainStrategy() router.Config_DomainStrategy {
@@ -111,10 +109,6 @@ func (c *RouterConfig) Build() (*router.Config, error) {
return nil, err return nil, err
} }
if rule.DomainMatcher == "" {
rule.DomainMatcher = c.DomainMatcher
}
config.Rule = append(config.Rule, rule) config.Rule = append(config.Rule, rule)
} }
for _, rawBalancer := range c.Balancers { for _, rawBalancer := range c.Balancers {
@@ -129,11 +123,8 @@ func (c *RouterConfig) Build() (*router.Config, error) {
type RouterRule struct { type RouterRule struct {
RuleTag string `json:"ruleTag"` RuleTag string `json:"ruleTag"`
Type string `json:"type"`
OutboundTag string `json:"outboundTag"` OutboundTag string `json:"outboundTag"`
BalancerTag string `json:"balancerTag"` BalancerTag string `json:"balancerTag"`
DomainMatcher string `json:"domainMatcher"`
} }
func ParseIP(s string) (*router.CIDR, error) { func ParseIP(s string) (*router.CIDR, error) {
@@ -536,12 +527,16 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
IP *StringList `json:"ip"` IP *StringList `json:"ip"`
Port *PortList `json:"port"` Port *PortList `json:"port"`
Network *NetworkList `json:"network"` Network *NetworkList `json:"network"`
SourceIP *StringList `json:"source"` SourceIP *StringList `json:"sourceIP"`
Source *StringList `json:"source"`
SourcePort *PortList `json:"sourcePort"` SourcePort *PortList `json:"sourcePort"`
User *StringList `json:"user"` User *StringList `json:"user"`
VlessRoute *PortList `json:"vlessRoute"`
InboundTag *StringList `json:"inboundTag"` InboundTag *StringList `json:"inboundTag"`
Protocols *StringList `json:"protocol"` Protocols *StringList `json:"protocol"`
Attributes map[string]string `json:"attrs"` Attributes map[string]string `json:"attrs"`
LocalIP *StringList `json:"localIP"`
LocalPort *PortList `json:"localPort"`
} }
rawFieldRule := new(RawFieldRule) rawFieldRule := new(RawFieldRule)
err := json.Unmarshal(msg, rawFieldRule) err := json.Unmarshal(msg, rawFieldRule)
@@ -564,10 +559,6 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
return nil, errors.New("neither outboundTag nor balancerTag is specified in routing rule") return nil, errors.New("neither outboundTag nor balancerTag is specified in routing rule")
} }
if rawFieldRule.DomainMatcher != "" {
rule.DomainMatcher = rawFieldRule.DomainMatcher
}
if rawFieldRule.Domain != nil { if rawFieldRule.Domain != nil {
for _, domain := range *rawFieldRule.Domain { for _, domain := range *rawFieldRule.Domain {
rules, err := parseDomainRule(domain) rules, err := parseDomainRule(domain)
@@ -604,6 +595,10 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
rule.Networks = rawFieldRule.Network.Build() rule.Networks = rawFieldRule.Network.Build()
} }
if rawFieldRule.SourceIP == nil {
rawFieldRule.SourceIP = rawFieldRule.Source
}
if rawFieldRule.SourceIP != nil { if rawFieldRule.SourceIP != nil {
geoipList, err := ToCidrList(*rawFieldRule.SourceIP) geoipList, err := ToCidrList(*rawFieldRule.SourceIP)
if err != nil { if err != nil {
@@ -616,12 +611,28 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
rule.SourcePortList = rawFieldRule.SourcePort.Build() rule.SourcePortList = rawFieldRule.SourcePort.Build()
} }
if rawFieldRule.LocalIP != nil {
geoipList, err := ToCidrList(*rawFieldRule.LocalIP)
if err != nil {
return nil, err
}
rule.LocalGeoip = geoipList
}
if rawFieldRule.LocalPort != nil {
rule.LocalPortList = rawFieldRule.LocalPort.Build()
}
if rawFieldRule.User != nil { if rawFieldRule.User != nil {
for _, s := range *rawFieldRule.User { for _, s := range *rawFieldRule.User {
rule.UserEmail = append(rule.UserEmail, s) rule.UserEmail = append(rule.UserEmail, s)
} }
} }
if rawFieldRule.VlessRoute != nil {
rule.VlessRouteList = rawFieldRule.VlessRoute.Build()
}
if rawFieldRule.InboundTag != nil { if rawFieldRule.InboundTag != nil {
for _, s := range *rawFieldRule.InboundTag { for _, s := range *rawFieldRule.InboundTag {
rule.InboundTag = append(rule.InboundTag, s) rule.InboundTag = append(rule.InboundTag, s)
@@ -647,12 +658,10 @@ func ParseRule(msg json.RawMessage) (*router.RoutingRule, error) {
if err != nil { if err != nil {
return nil, errors.New("invalid router rule").Base(err) return nil, errors.New("invalid router rule").Base(err)
} }
if rawRule.Type == "" || strings.EqualFold(rawRule.Type, "field") {
fieldrule, err := parseFieldRule(msg) fieldrule, err := parseFieldRule(msg)
if err != nil { if err != nil {
return nil, errors.New("invalid field rule").Base(err) return nil, errors.New("invalid field rule").Base(err)
}
return fieldrule, nil
} }
return nil, errors.New("unknown router rule type: ", rawRule.Type) return fieldrule, nil
} }

View File

@@ -2,6 +2,7 @@ package conf
import ( import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"strings"
"github.com/xtls/xray-core/app/observatory/burst" "github.com/xtls/xray-core/app/observatory/burst"
"github.com/xtls/xray-core/app/router" "github.com/xtls/xray-core/app/router"
@@ -51,15 +52,23 @@ type healthCheckSettings struct {
Interval duration.Duration `json:"interval"` Interval duration.Duration `json:"interval"`
SamplingCount int `json:"sampling"` SamplingCount int `json:"sampling"`
Timeout duration.Duration `json:"timeout"` Timeout duration.Duration `json:"timeout"`
HttpMethod string `json:"httpMethod"`
} }
func (h healthCheckSettings) Build() (proto.Message, error) { func (h healthCheckSettings) Build() (proto.Message, error) {
var httpMethod string
if h.HttpMethod == "" {
httpMethod = "HEAD"
} else {
httpMethod = strings.TrimSpace(h.HttpMethod)
}
return &burst.HealthPingConfig{ return &burst.HealthPingConfig{
Destination: h.Destination, Destination: h.Destination,
Connectivity: h.Connectivity, Connectivity: h.Connectivity,
Interval: int64(h.Interval), Interval: int64(h.Interval),
Timeout: int64(h.Timeout), Timeout: int64(h.Timeout),
SamplingCount: int32(h.SamplingCount), SamplingCount: int32(h.SamplingCount),
HttpMethod: httpMethod,
}, nil }, nil
} }

View File

@@ -91,7 +91,6 @@ func TestRouterConfig(t *testing.T) {
"domainStrategy": "AsIs", "domainStrategy": "AsIs",
"rules": [ "rules": [
{ {
"type": "field",
"domain": [ "domain": [
"baidu.com", "baidu.com",
"qq.com" "qq.com"
@@ -99,18 +98,15 @@ func TestRouterConfig(t *testing.T) {
"outboundTag": "direct" "outboundTag": "direct"
}, },
{ {
"type": "field",
"ip": [ "ip": [
"10.0.0.0/8", "10.0.0.0/8",
"::1/128" "::1/128"
], ],
"outboundTag": "test" "outboundTag": "test"
},{ },{
"type": "field",
"port": "53, 443, 1000-2000", "port": "53, 443, 1000-2000",
"outboundTag": "test" "outboundTag": "test"
},{ },{
"type": "field",
"port": 123, "port": 123,
"outboundTag": "test" "outboundTag": "test"
} }
@@ -249,7 +245,6 @@ func TestRouterConfig(t *testing.T) {
"domainStrategy": "IPIfNonMatch", "domainStrategy": "IPIfNonMatch",
"rules": [ "rules": [
{ {
"type": "field",
"domain": [ "domain": [
"baidu.com", "baidu.com",
"qq.com" "qq.com"
@@ -257,7 +252,6 @@ func TestRouterConfig(t *testing.T) {
"outboundTag": "direct" "outboundTag": "direct"
}, },
{ {
"type": "field",
"ip": [ "ip": [
"10.0.0.0/8", "10.0.0.0/8",
"::1/128" "::1/128"

View File

@@ -412,6 +412,10 @@ type TLSConfig struct {
MasterKeyLog string `json:"masterKeyLog"` MasterKeyLog string `json:"masterKeyLog"`
ServerNameToVerify string `json:"serverNameToVerify"` ServerNameToVerify string `json:"serverNameToVerify"`
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"` VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
ECHServerKeys string `json:"echServerKeys"`
ECHConfigList string `json:"echConfigList"`
ECHForceQuery string `json:"echForceQuery"`
ECHSocketSettings *SocketConfig `json:"echSockopt"`
} }
// Build implements Buildable. // Build implements Buildable.
@@ -435,7 +439,7 @@ func (c *TLSConfig) Build() (proto.Message, error) {
} }
if len(config.NextProtocol) > 1 { if len(config.NextProtocol) > 1 {
for _, p := range config.NextProtocol { for _, p := range config.NextProtocol {
if tcp.IsFromMitm(p) { if tls.IsFromMitm(p) {
return nil, errors.New(`only one element is allowed in "alpn" when using "fromMitm" in it`) return nil, errors.New(`only one element is allowed in "alpn" when using "fromMitm" in it`)
} }
} }
@@ -483,9 +487,38 @@ func (c *TLSConfig) Build() (proto.Message, error) {
} }
config.VerifyPeerCertInNames = c.VerifyPeerCertInNames config.VerifyPeerCertInNames = c.VerifyPeerCertInNames
if c.ECHServerKeys != "" {
EchPrivateKey, err := base64.StdEncoding.DecodeString(c.ECHServerKeys)
if err != nil {
return nil, errors.New("invalid ECH Config", c.ECHServerKeys)
}
config.EchServerKeys = EchPrivateKey
}
switch c.ECHForceQuery {
case "none", "half", "full", "":
config.EchForceQuery = c.ECHForceQuery
default:
return nil, errors.New(`invalid "echForceQuery": `, c.ECHForceQuery)
}
config.EchForceQuery = c.ECHForceQuery
config.EchConfigList = c.ECHConfigList
if c.ECHSocketSettings != nil {
ss, err := c.ECHSocketSettings.Build()
if err != nil {
return nil, errors.New("Failed to build ech sockopt.").Base(err)
}
config.EchSocketSettings = ss
}
return config, nil return config, nil
} }
type LimitFallback struct {
AfterBytes uint64
BytesPerSec uint64
BurstBytesPerSec uint64
}
type REALITYConfig struct { type REALITYConfig struct {
MasterKeyLog string `json:"masterKeyLog"` MasterKeyLog string `json:"masterKeyLog"`
Show bool `json:"show"` Show bool `json:"show"`
@@ -499,13 +532,18 @@ type REALITYConfig struct {
MaxClientVer string `json:"maxClientVer"` MaxClientVer string `json:"maxClientVer"`
MaxTimeDiff uint64 `json:"maxTimeDiff"` MaxTimeDiff uint64 `json:"maxTimeDiff"`
ShortIds []string `json:"shortIds"` ShortIds []string `json:"shortIds"`
Mldsa65Seed string `json:"mldsa65Seed"`
Fingerprint string `json:"fingerprint"` LimitFallbackUpload LimitFallback `json:"limitFallbackUpload"`
ServerName string `json:"serverName"` LimitFallbackDownload LimitFallback `json:"limitFallbackDownload"`
Password string `json:"password"`
PublicKey string `json:"publicKey"` Fingerprint string `json:"fingerprint"`
ShortId string `json:"shortId"` ServerName string `json:"serverName"`
SpiderX string `json:"spiderX"` Password string `json:"password"`
PublicKey string `json:"publicKey"`
ShortId string `json:"shortId"`
Mldsa65Verify string `json:"mldsa65Verify"`
SpiderX string `json:"spiderX"`
} }
func (c *REALITYConfig) Build() (proto.Message, error) { func (c *REALITYConfig) Build() (proto.Message, error) {
@@ -535,7 +573,7 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
} }
default: default:
if _, err = strconv.Atoi(s); err == nil { if _, err = strconv.Atoi(s); err == nil {
s = "127.0.0.1:" + s s = "localhost:" + s
} }
if _, _, err = net.SplitHostPort(s); err == nil { if _, _, err = net.SplitHostPort(s); err == nil {
c.Type = "tcp" c.Type = "tcp"
@@ -600,6 +638,24 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
config.Xver = c.Xver config.Xver = c.Xver
config.ServerNames = c.ServerNames config.ServerNames = c.ServerNames
config.MaxTimeDiff = c.MaxTimeDiff config.MaxTimeDiff = c.MaxTimeDiff
if c.Mldsa65Seed != "" {
if c.Mldsa65Seed == c.PrivateKey {
return nil, errors.New(`"mldsa65Seed" and "privateKey" can not be the same value: `, c.Mldsa65Seed)
}
if config.Mldsa65Seed, err = base64.RawURLEncoding.DecodeString(c.Mldsa65Seed); err != nil || len(config.Mldsa65Seed) != 32 {
return nil, errors.New(`invalid "mldsa65Seed": `, c.Mldsa65Seed)
}
}
config.LimitFallbackUpload = new(reality.LimitFallback)
config.LimitFallbackUpload.AfterBytes = c.LimitFallbackUpload.AfterBytes
config.LimitFallbackUpload.BytesPerSec = c.LimitFallbackUpload.BytesPerSec
config.LimitFallbackUpload.BurstBytesPerSec = c.LimitFallbackUpload.BurstBytesPerSec
config.LimitFallbackDownload = new(reality.LimitFallback)
config.LimitFallbackDownload.AfterBytes = c.LimitFallbackDownload.AfterBytes
config.LimitFallbackDownload.BytesPerSec = c.LimitFallbackDownload.BytesPerSec
config.LimitFallbackDownload.BurstBytesPerSec = c.LimitFallbackDownload.BurstBytesPerSec
} else { } else {
config.Fingerprint = strings.ToLower(c.Fingerprint) config.Fingerprint = strings.ToLower(c.Fingerprint)
if config.Fingerprint == "unsafe" || config.Fingerprint == "hellogolang" { if config.Fingerprint == "unsafe" || config.Fingerprint == "hellogolang" {
@@ -627,6 +683,11 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
if _, err = hex.Decode(config.ShortId, []byte(c.ShortId)); err != nil { if _, err = hex.Decode(config.ShortId, []byte(c.ShortId)); err != nil {
return nil, errors.New(`invalid "shortId": `, c.ShortId) return nil, errors.New(`invalid "shortId": `, c.ShortId)
} }
if c.Mldsa65Verify != "" {
if config.Mldsa65Verify, err = base64.RawURLEncoding.DecodeString(c.Mldsa65Verify); err != nil || len(config.Mldsa65Verify) != 1952 {
return nil, errors.New(`invalid "mldsa65Verify": `, c.Mldsa65Verify)
}
}
if c.SpiderX == "" { if c.SpiderX == "" {
c.SpiderX = "/" c.SpiderX = "/"
} }

View File

@@ -155,7 +155,7 @@ func (c *TrojanServerConfig) Build() (proto.Message, error) {
} }
} else { } else {
if _, err := strconv.Atoi(fb.Dest); err == nil { if _, err := strconv.Atoi(fb.Dest); err == nil {
fb.Dest = "127.0.0.1:" + fb.Dest fb.Dest = "localhost:" + fb.Dest
} }
if _, _, err := net.SplitHostPort(fb.Dest); err == nil { if _, _, err := net.SplitHostPort(fb.Dest); err == nil {
fb.Type = "tcp" fb.Type = "tcp"

22
infra/conf/version.go Normal file
View File

@@ -0,0 +1,22 @@
package conf
import (
"github.com/xtls/xray-core/app/version"
"github.com/xtls/xray-core/core"
"strconv"
)
type VersionConfig struct {
MinVersion string `json:"min"`
MaxVersion string `json:"max"`
}
func (c *VersionConfig) Build() (*version.Config, error) {
coreVersion := strconv.Itoa(int(core.Version_x)) + "." + strconv.Itoa(int(core.Version_y)) + "." + strconv.Itoa(int(core.Version_z))
return &version.Config{
CoreVersion: coreVersion,
MinVersion: c.MinVersion,
MaxVersion: c.MaxVersion,
}, nil
}

View File

@@ -32,12 +32,20 @@ type VLessInboundConfig struct {
Clients []json.RawMessage `json:"clients"` Clients []json.RawMessage `json:"clients"`
Decryption string `json:"decryption"` Decryption string `json:"decryption"`
Fallbacks []*VLessInboundFallback `json:"fallbacks"` Fallbacks []*VLessInboundFallback `json:"fallbacks"`
Flow string `json:"flow"`
} }
// Build implements Buildable // Build implements Buildable
func (c *VLessInboundConfig) Build() (proto.Message, error) { func (c *VLessInboundConfig) Build() (proto.Message, error) {
config := new(inbound.Config) config := new(inbound.Config)
config.Clients = make([]*protocol.User, len(c.Clients)) config.Clients = make([]*protocol.User, len(c.Clients))
switch c.Flow {
case vless.None:
c.Flow = ""
case "", vless.XRV:
default:
return nil, errors.New(`VLESS "settings.flow" doesn't support "` + c.Flow + `" in this version`)
}
for idx, rawUser := range c.Clients { for idx, rawUser := range c.Clients {
user := new(protocol.User) user := new(protocol.User)
if err := json.Unmarshal(rawUser, user); err != nil { if err := json.Unmarshal(rawUser, user); err != nil {
@@ -55,7 +63,11 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
account.Id = u.String() account.Id = u.String()
switch account.Flow { switch account.Flow {
case "", vless.XRV: case "":
account.Flow = c.Flow
case vless.None:
account.Flow = ""
case vless.XRV:
default: default:
return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`) return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`)
} }
@@ -111,7 +123,7 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
} }
} else { } else {
if _, err := strconv.Atoi(fb.Dest); err == nil { if _, err := strconv.Atoi(fb.Dest); err == nil {
fb.Dest = "127.0.0.1:" + fb.Dest fb.Dest = "localhost:" + fb.Dest
} }
if _, _, err := net.SplitHostPort(fb.Dest); err == nil { if _, _, err := net.SplitHostPort(fb.Dest); err == nil {
fb.Type = "tcp" fb.Type = "tcp"

View File

@@ -110,7 +110,7 @@ func TestVLessInbound(t *testing.T) {
Alpn: "", Alpn: "",
Path: "", Path: "",
Type: "tcp", Type: "tcp",
Dest: "127.0.0.1:80", Dest: "localhost:80",
Xver: 0, Xver: 0,
}, },
{ {

View File

@@ -21,6 +21,7 @@ import (
var ( var (
inboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{ inboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
"tunnel": func() interface{} { return new(DokodemoConfig) },
"dokodemo-door": func() interface{} { return new(DokodemoConfig) }, "dokodemo-door": func() interface{} { return new(DokodemoConfig) },
"http": func() interface{} { return new(HTTPServerConfig) }, "http": func() interface{} { return new(HTTPServerConfig) },
"shadowsocks": func() interface{} { return new(ShadowsocksServerConfig) }, "shadowsocks": func() interface{} { return new(ShadowsocksServerConfig) },
@@ -33,8 +34,10 @@ var (
}, "protocol", "settings") }, "protocol", "settings")
outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{ outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
"block": func() interface{} { return new(BlackholeConfig) },
"blackhole": func() interface{} { return new(BlackholeConfig) }, "blackhole": func() interface{} { return new(BlackholeConfig) },
"loopback": func() interface{} { return new(LoopbackConfig) }, "loopback": func() interface{} { return new(LoopbackConfig) },
"direct": func() interface{} { return new(FreedomConfig) },
"freedom": func() interface{} { return new(FreedomConfig) }, "freedom": func() interface{} { return new(FreedomConfig) },
"http": func() interface{} { return new(HTTPClientConfig) }, "http": func() interface{} { return new(HTTPClientConfig) },
"shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) }, "shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) },
@@ -242,7 +245,7 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
return nil, errors.New("failed to load inbound detour config for protocol ", c.Protocol).Base(err) return nil, errors.New("failed to load inbound detour config for protocol ", c.Protocol).Base(err)
} }
if dokodemoConfig, ok := rawConfig.(*DokodemoConfig); ok { if dokodemoConfig, ok := rawConfig.(*DokodemoConfig); ok {
receiverSettings.ReceiveOriginalDestination = dokodemoConfig.Redirect receiverSettings.ReceiveOriginalDestination = dokodemoConfig.FollowRedirect
} }
ts, err := rawConfig.(Buildable).Build() ts, err := rawConfig.(Buildable).Build()
if err != nil { if err != nil {
@@ -257,13 +260,14 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
} }
type OutboundDetourConfig struct { type OutboundDetourConfig struct {
Protocol string `json:"protocol"` Protocol string `json:"protocol"`
SendThrough *string `json:"sendThrough"` SendThrough *string `json:"sendThrough"`
Tag string `json:"tag"` Tag string `json:"tag"`
Settings *json.RawMessage `json:"settings"` Settings *json.RawMessage `json:"settings"`
StreamSetting *StreamConfig `json:"streamSettings"` StreamSetting *StreamConfig `json:"streamSettings"`
ProxySettings *ProxyConfig `json:"proxySettings"` ProxySettings *ProxyConfig `json:"proxySettings"`
MuxSettings *MuxConfig `json:"mux"` MuxSettings *MuxConfig `json:"mux"`
TargetStrategy string `json:"targetStrategy"`
} }
func (c *OutboundDetourConfig) checkChainProxyConfig() error { func (c *OutboundDetourConfig) checkChainProxyConfig() error {
@@ -279,6 +283,32 @@ func (c *OutboundDetourConfig) checkChainProxyConfig() error {
// Build implements Buildable. // Build implements Buildable.
func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) { func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
senderSettings := &proxyman.SenderConfig{} senderSettings := &proxyman.SenderConfig{}
switch strings.ToLower(c.TargetStrategy) {
case "asis", "":
senderSettings.TargetStrategy = internet.DomainStrategy_AS_IS
case "useip":
senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP
case "useipv4":
senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP4
case "useipv6":
senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP6
case "useipv4v6":
senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP46
case "useipv6v4":
senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP64
case "forceip":
senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP
case "forceipv4":
senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP4
case "forceipv6":
senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP6
case "forceipv4v6":
senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP46
case "forceipv6v4":
senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP64
default:
return nil, errors.New("unsupported target domain strategy: ", c.TargetStrategy)
}
if err := c.checkChainProxyConfig(); err != nil { if err := c.checkChainProxyConfig(); err != nil {
return nil, err return nil, err
} }
@@ -380,6 +410,7 @@ type Config struct {
FakeDNS *FakeDNSConfig `json:"fakeDns"` FakeDNS *FakeDNSConfig `json:"fakeDns"`
Observatory *ObservatoryConfig `json:"observatory"` Observatory *ObservatoryConfig `json:"observatory"`
BurstObservatory *BurstObservatoryConfig `json:"burstObservatory"` BurstObservatory *BurstObservatoryConfig `json:"burstObservatory"`
Version *VersionConfig `json:"version"`
} }
func (c *Config) findInboundTag(tag string) int { func (c *Config) findInboundTag(tag string) int {
@@ -448,6 +479,10 @@ func (c *Config) Override(o *Config, fn string) {
c.BurstObservatory = o.BurstObservatory c.BurstObservatory = o.BurstObservatory
} }
if o.Version != nil {
c.Version = o.Version
}
// update the Inbound in slice if the only one in override config has same tag // update the Inbound in slice if the only one in override config has same tag
if len(o.InboundConfigs) > 0 { if len(o.InboundConfigs) > 0 {
for i := range o.InboundConfigs { for i := range o.InboundConfigs {
@@ -588,6 +623,14 @@ func (c *Config) Build() (*core.Config, error) {
config.App = append(config.App, serial.ToTypedMessage(r)) config.App = append(config.App, serial.ToTypedMessage(r))
} }
if c.Version != nil {
r, err := c.Version.Build()
if err != nil {
return nil, errors.New("failed to build version configuration").Base(err)
}
config.App = append(config.App, serial.ToTypedMessage(r))
}
var inbounds []InboundDetourConfig var inbounds []InboundDetourConfig
if len(c.InboundConfigs) > 0 { if len(c.InboundConfigs) > 0 {

View File

@@ -77,7 +77,6 @@ func TestXrayConfig(t *testing.T) {
"ip": [ "ip": [
"10.0.0.0/8" "10.0.0.0/8"
], ],
"type": "field",
"outboundTag": "blocked" "outboundTag": "blocked"
} }
] ]

View File

@@ -23,6 +23,8 @@ var CmdAPI = &base.Command{
cmdRemoveOutbounds, cmdRemoveOutbounds,
cmdListInbounds, cmdListInbounds,
cmdListOutbounds, cmdListOutbounds,
cmdAddInboundUsers,
cmdRemoveInboundUsers,
cmdInboundUser, cmdInboundUser,
cmdInboundUserCount, cmdInboundUserCount,
cmdAddRules, cmdAddRules,

View File

@@ -0,0 +1,144 @@
package api
import (
"context"
"fmt"
"github.com/xtls/xray-core/common/protocol"
handlerService "github.com/xtls/xray-core/app/proxyman/command"
cserial "github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/infra/conf"
"github.com/xtls/xray-core/infra/conf/serial"
"github.com/xtls/xray-core/proxy/shadowsocks"
"github.com/xtls/xray-core/proxy/shadowsocks_2022"
"github.com/xtls/xray-core/proxy/trojan"
vlessin "github.com/xtls/xray-core/proxy/vless/inbound"
vmessin "github.com/xtls/xray-core/proxy/vmess/inbound"
"github.com/xtls/xray-core/main/commands/base"
)
var cmdAddInboundUsers = &base.Command{
CustomFlags: true,
UsageLine: "{{.Exec}} api adu [--server=127.0.0.1:8080] <c1.json> [c2.json]...",
Short: "Add users to inbounds",
Long: `
Add users to inbounds.
Arguments:
-s, -server
The API server address. Default 127.0.0.1:8080
-t, -timeout
Timeout seconds to call API. Default 3
Example:
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json
`,
Run: executeAddInboundUsers,
}
func executeAddInboundUsers(cmd *base.Command, args []string) {
setSharedFlags(cmd)
cmd.Flag.Parse(args)
unnamedArgs := cmd.Flag.Args()
inbs := extractInboundsConfig(unnamedArgs)
conn, ctx, close := dialAPIServer()
defer close()
client := handlerService.NewHandlerServiceClient(conn)
success := 0
for _, inb := range inbs {
success += executeInboundUserAction(ctx, client, inb, addInboundUserAction)
}
fmt.Println("Added", success, "user(s) in total.")
}
func addInboundUserAction(ctx context.Context, client handlerService.HandlerServiceClient, tag string, user *protocol.User) error {
fmt.Println("add user:", user.Email)
_, err := client.AlterInbound(ctx, &handlerService.AlterInboundRequest{
Tag: tag,
Operation: cserial.ToTypedMessage(
&handlerService.AddUserOperation{
User: user,
}),
})
return err
}
func extractInboundUsers(inb *core.InboundHandlerConfig) []*protocol.User {
if inb == nil {
return nil
}
inst, err := inb.ProxySettings.GetInstance()
if err != nil || inst == nil {
fmt.Println("failed to get inbound instance:", err)
return nil
}
switch ty := inst.(type) {
case *vmessin.Config:
return ty.User
case *vlessin.Config:
return ty.Clients
case *trojan.ServerConfig:
return ty.Users
case *shadowsocks.ServerConfig:
return ty.Users
case *shadowsocks_2022.MultiUserServerConfig:
return ty.Users
default:
fmt.Println("unsupported inbound type")
}
return nil
}
func extractInboundsConfig(unnamedArgs []string) []conf.InboundDetourConfig {
ins := make([]conf.InboundDetourConfig, 0)
for _, arg := range unnamedArgs {
r, err := loadArg(arg)
if err != nil {
base.Fatalf("failed to load %s: %s", arg, err)
}
conf, err := serial.DecodeJSONConfig(r)
if err != nil {
base.Fatalf("failed to decode %s: %s", arg, err)
}
ins = append(ins, conf.InboundConfigs...)
}
return ins
}
func executeInboundUserAction(ctx context.Context, client handlerService.HandlerServiceClient, inb conf.InboundDetourConfig, action func(ctx context.Context, client handlerService.HandlerServiceClient, tag string, user *protocol.User) error) int {
success := 0
tag := inb.Tag
if len(tag) < 1 {
return success
}
fmt.Println("processing inbound:", tag)
built, err := inb.Build()
if err != nil {
fmt.Println("failed to build config:", err)
return success
}
users := extractInboundUsers(built)
if users == nil {
return success
}
for _, user := range users {
if len(user.Email) < 1 {
continue
}
if err := action(ctx, client, inb.Tag, user); err == nil {
fmt.Println("result: ok")
success += 1
} else {
fmt.Println(err)
}
}
return success
}

View File

@@ -0,0 +1,62 @@
package api
import (
"fmt"
handlerService "github.com/xtls/xray-core/app/proxyman/command"
cserial "github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/main/commands/base"
)
var cmdRemoveInboundUsers = &base.Command{
CustomFlags: true,
UsageLine: "{{.Exec}} api rmu [--server=127.0.0.1:8080] -tag=tag <email1> [email2]...",
Short: "Remove users from inbounds",
Long: `
Remove users from inbounds.
Arguments:
-s, -server
The API server address. Default 127.0.0.1:8080
-t, -timeout
Timeout seconds to call API. Default 3
-tag
Inbound tag
Example:
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -tag="vless-in" "xray@love.com" ...
`,
Run: executeRemoveUsers,
}
func executeRemoveUsers(cmd *base.Command, args []string) {
setSharedFlags(cmd)
var tag string
cmd.Flag.StringVar(&tag, "tag", "", "")
cmd.Flag.Parse(args)
emails := cmd.Flag.Args()
if len(tag) < 1 {
base.Fatalf("inbound tag not specified")
}
conn, ctx, close := dialAPIServer()
defer close()
client := handlerService.NewHandlerServiceClient(conn)
success := 0
for _, email := range emails {
fmt.Println("remove user:", email)
_, err := client.AlterInbound(ctx, &handlerService.AlterInboundRequest{
Tag: tag,
Operation: cserial.ToTypedMessage(
&handlerService.RemoveUserOperation{
Email: email,
}),
})
if err == nil {
success += 1
} else {
fmt.Println(err)
}
}
fmt.Println("Removed", success, "user(s) in total.")
}

View File

@@ -7,7 +7,7 @@ import (
var cmdListInbounds = &base.Command{ var cmdListInbounds = &base.Command{
CustomFlags: true, CustomFlags: true,
UsageLine: "{{.Exec}} api lsi [--server=127.0.0.1:8080]", UsageLine: "{{.Exec}} api lsi [--server=127.0.0.1:8080] [--isOnlyTags=true]",
Short: "List inbounds", Short: "List inbounds",
Long: ` Long: `
List inbounds in Xray. List inbounds in Xray.
@@ -29,14 +29,17 @@ Example:
func executeListInbounds(cmd *base.Command, args []string) { func executeListInbounds(cmd *base.Command, args []string) {
setSharedFlags(cmd) setSharedFlags(cmd)
var isOnlyTagsStr string
cmd.Flag.StringVar(&isOnlyTagsStr, "isOnlyTags", "", "")
cmd.Flag.Parse(args) cmd.Flag.Parse(args)
isOnlyTags := isOnlyTagsStr == "true"
conn, ctx, close := dialAPIServer() conn, ctx, close := dialAPIServer()
defer close() defer close()
client := handlerService.NewHandlerServiceClient(conn) client := handlerService.NewHandlerServiceClient(conn)
resp, err := client.ListInbounds(ctx, &handlerService.ListInboundsRequest{}) resp, err := client.ListInbounds(ctx, &handlerService.ListInboundsRequest{IsOnlyTags: isOnlyTags})
if err != nil { if err != nil {
base.Fatalf("failed to list inbounds: %s", err) base.Fatalf("failed to list inbounds: %s", err)
} }

View File

@@ -93,7 +93,6 @@ func executeSourceIpBlock(cmd *base.Command, args []string) {
"ruleTag" : "%s", "ruleTag" : "%s",
"inboundTag": %s, "inboundTag": %s,
"outboundTag": "%s", "outboundTag": "%s",
"type": "field",
"source": %s "source": %s
} }
] ]

View File

@@ -16,5 +16,6 @@ func init() {
cmdUUID, cmdUUID,
cmdX25519, cmdX25519,
cmdWG, cmdWG,
cmdMLDSA65,
) )
} }

View File

@@ -0,0 +1,42 @@
package all
import (
"crypto/rand"
"encoding/base64"
"fmt"
"github.com/cloudflare/circl/sign/mldsa/mldsa65"
"github.com/xtls/xray-core/main/commands/base"
)
var cmdMLDSA65 = &base.Command{
UsageLine: `{{.Exec}} mldsa65 [-i "seed (base64.RawURLEncoding)"]`,
Short: `Generate key pair for ML-DSA-65 post-quantum signature`,
Long: `
Generate key pair for ML-DSA-65 post-quantum signature.
Random: {{.Exec}} mldsa65
From seed: {{.Exec}} mldsa65 -i "seed (base64.RawURLEncoding)"
`,
}
func init() {
cmdMLDSA65.Run = executeMLDSA65 // break init loop
}
var input_seed = 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)
seed = [32]byte(s)
} else {
rand.Read(seed[:])
}
pub, _ := mldsa65.NewKeyFromSeed(&seed)
fmt.Printf("Seed: %v\nVerify: %v",
base64.RawURLEncoding.EncodeToString(seed[:]),
base64.RawURLEncoding.EncodeToString(pub.Bytes()))
}

View File

@@ -1,25 +1,26 @@
package tls package tls
import ( import (
"encoding/json" "encoding/base64"
"encoding/pem" "encoding/pem"
"os" "os"
"strings"
"github.com/OmarTariq612/goech" "github.com/xtls/reality/hpke"
"github.com/cloudflare/circl/hpke"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/main/commands/base" "github.com/xtls/xray-core/main/commands/base"
"github.com/xtls/xray-core/transport/internet/tls"
"golang.org/x/crypto/cryptobyte"
) )
var cmdECH = &base.Command{ var cmdECH = &base.Command{
UsageLine: `{{.Exec}} tls ech [--serverName (string)] [--json]`, UsageLine: `{{.Exec}} tls ech [--serverName (string)] [--pem] [-i "ECHServerKeys (base64.StdEncoding)"]`,
Short: `Generate TLS-ECH certificates`, Short: `Generate TLS-ECH certificates`,
Long: ` Long: `
Generate TLS-ECH certificates. Generate TLS-ECH certificates.
Set serverName to your custom string: {{.Exec}} tls ech --serverName (string) Set serverName to your custom string: {{.Exec}} tls ech --serverName (string)
Generate into json format: {{.Exec}} tls ech --json Generate into pem format: {{.Exec}} tls ech --pem
Restore ECHConfigs from ECHServerKeys: {{.Exec}} tls ech -i "ECHServerKeys (base64.StdEncoding)"
`, // Enable PQ signature schemes: {{.Exec}} tls ech --pq-signature-schemes-enabled `, // Enable PQ signature schemes: {{.Exec}} tls ech --pq-signature-schemes-enabled
} }
@@ -27,43 +28,66 @@ func init() {
cmdECH.Run = executeECH cmdECH.Run = executeECH
} }
var input_pqSignatureSchemesEnabled = cmdECH.Flag.Bool("pqSignatureSchemesEnabled", false, "") var input_echServerKeys = cmdECH.Flag.String("i", "", "ECHServerKeys (base64.StdEncoding)")
// var input_pqSignatureSchemesEnabled = cmdECH.Flag.Bool("pqSignatureSchemesEnabled", false, "")
var input_serverName = cmdECH.Flag.String("serverName", "cloudflare-ech.com", "") var input_serverName = cmdECH.Flag.String("serverName", "cloudflare-ech.com", "")
var input_json = cmdECH.Flag.Bool("json", false, "True == turn on json output") var input_pem = cmdECH.Flag.Bool("pem", false, "True == turn on pem output")
func executeECH(cmd *base.Command, args []string) { func executeECH(cmd *base.Command, args []string) {
var kem hpke.KEM var kem uint16
if *input_pqSignatureSchemesEnabled { // if *input_pqSignatureSchemesEnabled {
kem = hpke.KEM_X25519_KYBER768_DRAFT00 // kem = 0x30 // hpke.KEM_X25519_KYBER768_DRAFT00
} else { // } else {
kem = hpke.KEM_X25519_HKDF_SHA256 kem = hpke.DHKEM_X25519_HKDF_SHA256
} // }
echKeySet, err := goech.GenerateECHKeySet(0, *input_serverName, kem) echConfig, priv, err := tls.GenerateECHKeySet(0, *input_serverName, kem)
common.Must(err) common.Must(err)
configBuffer, _ := echKeySet.ECHConfig.MarshalBinary() var configBuffer, keyBuffer []byte
keyBuffer, _ := echKeySet.MarshalBinary() if *input_echServerKeys == "" {
configBytes, _ := tls.MarshalBinary(echConfig)
configPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH CONFIGS", Bytes: configBuffer})) var b cryptobyte.Builder
keyPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH KEYS", Bytes: keyBuffer})) b.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) {
if *input_json { child.AddBytes(configBytes)
jECHConfigs := map[string]interface{}{ })
"configs": strings.Split(strings.TrimSpace(string(configPEM)), "\n"), configBuffer, _ = b.Bytes()
} var b2 cryptobyte.Builder
jECHKey := map[string]interface{}{ b2.AddUint16(uint16(len(priv)))
"key": strings.Split(strings.TrimSpace(string(keyPEM)), "\n"), b2.AddBytes(priv)
} b2.AddUint16(uint16(len(configBytes)))
b2.AddBytes(configBytes)
for _, i := range []map[string]interface{}{jECHConfigs, jECHKey} { keyBuffer, _ = b2.Bytes()
content, err := json.MarshalIndent(i, "", " ")
common.Must(err)
os.Stdout.Write(content)
os.Stdout.WriteString("\n")
}
} else { } else {
keySetsByte, err := base64.StdEncoding.DecodeString(*input_echServerKeys)
if err != nil {
os.Stdout.WriteString("Failed to decode ECHServerKeys: " + err.Error() + "\n")
return
}
keyBuffer = keySetsByte
KeySets, err := tls.ConvertToGoECHKeys(keySetsByte)
if err != nil {
os.Stdout.WriteString("Failed to decode ECHServerKeys: " + err.Error() + "\n")
return
}
var b cryptobyte.Builder
for _, keySet := range KeySets {
b.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) {
child.AddBytes(keySet.Config)
})
}
configBuffer, _ = b.Bytes()
}
if *input_pem {
configPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH CONFIGS", Bytes: configBuffer}))
keyPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH KEYS", Bytes: keyBuffer}))
os.Stdout.WriteString(configPEM) os.Stdout.WriteString(configPEM)
os.Stdout.WriteString(keyPEM) os.Stdout.WriteString(keyPEM)
} else {
os.Stdout.WriteString("ECH config list: \n" + base64.StdEncoding.EncodeToString(configBuffer) + "\n")
os.Stdout.WriteString("ECH server keys: \n" + base64.StdEncoding.EncodeToString(keyBuffer) + "\n")
} }
} }

View File

@@ -6,6 +6,7 @@ import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"net" "net"
"strconv"
"github.com/xtls/xray-core/main/commands/base" "github.com/xtls/xray-core/main/commands/base"
. "github.com/xtls/xray-core/transport/internet/tls" . "github.com/xtls/xray-core/transport/internet/tls"
@@ -36,8 +37,15 @@ func executePing(cmd *base.Command, args []string) {
base.Fatalf("domain not specified") base.Fatalf("domain not specified")
} }
domain := cmdPing.Flag.Arg(0) domainWithPort := cmdPing.Flag.Arg(0)
fmt.Println("Tls ping: ", domain) fmt.Println("TLS ping: ", domainWithPort)
TargetPort := 443
domain, port, err := net.SplitHostPort(domainWithPort)
if err != nil {
domain = domainWithPort
} else {
TargetPort, _ = strconv.Atoi(port)
}
var ip net.IP var ip net.IP
if len(*pingIPStr) > 0 { if len(*pingIPStr) > 0 {
@@ -53,19 +61,19 @@ func executePing(cmd *base.Command, args []string) {
} }
ip = v.IP ip = v.IP
} }
fmt.Println("Using IP: ", ip.String()) fmt.Println("Using IP: ", ip.String()+":"+strconv.Itoa(TargetPort))
fmt.Println("-------------------") fmt.Println("-------------------")
fmt.Println("Pinging without SNI") fmt.Println("Pinging without SNI")
{ {
tcpConn, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: ip, Port: 443}) tcpConn, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: ip, Port: TargetPort})
if err != nil { if err != nil {
base.Fatalf("Failed to dial tcp: %s", err) base.Fatalf("Failed to dial tcp: %s", err)
} }
tlsConn := gotls.Client(tcpConn, &gotls.Config{ tlsConn := gotls.Client(tcpConn, &gotls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
NextProtos: []string{"http/1.1"}, NextProtos: []string{"h2", "http/1.1"},
MaxVersion: gotls.VersionTLS12, MaxVersion: gotls.VersionTLS13,
MinVersion: gotls.VersionTLS12, MinVersion: gotls.VersionTLS12,
// Do not release tool before v5's refactor // Do not release tool before v5's refactor
// VerifyPeerCertificate: showCert(), // VerifyPeerCertificate: showCert(),
@@ -75,6 +83,7 @@ func executePing(cmd *base.Command, args []string) {
fmt.Println("Handshake failure: ", err) fmt.Println("Handshake failure: ", err)
} else { } else {
fmt.Println("Handshake succeeded") fmt.Println("Handshake succeeded")
printTLSConnDetail(tlsConn)
printCertificates(tlsConn.ConnectionState().PeerCertificates) printCertificates(tlsConn.ConnectionState().PeerCertificates)
} }
tlsConn.Close() tlsConn.Close()
@@ -89,31 +98,59 @@ func executePing(cmd *base.Command, args []string) {
} }
tlsConn := gotls.Client(tcpConn, &gotls.Config{ tlsConn := gotls.Client(tcpConn, &gotls.Config{
ServerName: domain, ServerName: domain,
NextProtos: []string{"http/1.1"}, NextProtos: []string{"h2", "http/1.1"},
MaxVersion: gotls.VersionTLS12, MaxVersion: gotls.VersionTLS13,
MinVersion: gotls.VersionTLS12, MinVersion: gotls.VersionTLS12,
// Do not release tool before v5's refactor // Do not release tool before v5's refactor
// VerifyPeerCertificate: showCert(), // VerifyPeerCertificate: showCert(),
}) })
err = tlsConn.Handshake() err = tlsConn.Handshake()
if err != nil { if err != nil {
fmt.Println("handshake failure: ", err) fmt.Println("Handshake failure: ", err)
} else { } else {
fmt.Println("handshake succeeded") fmt.Println("Handshake succeeded")
printTLSConnDetail(tlsConn)
printCertificates(tlsConn.ConnectionState().PeerCertificates) printCertificates(tlsConn.ConnectionState().PeerCertificates)
} }
tlsConn.Close() tlsConn.Close()
} }
fmt.Println("Tls ping finished") fmt.Println("-------------------")
fmt.Println("TLS ping finished")
} }
func printCertificates(certs []*x509.Certificate) { func printCertificates(certs []*x509.Certificate) {
var leaf *x509.Certificate
var length int
for _, cert := range certs { for _, cert := range certs {
if len(cert.DNSNames) == 0 { length += len(cert.Raw)
continue if len(cert.DNSNames) != 0 {
leaf = cert
} }
fmt.Println("Allowed domains: ", cert.DNSNames) }
fmt.Println("Certificate chain's total length: ", length, "(certs count: "+strconv.Itoa(len(certs))+")")
if leaf != nil {
fmt.Println("Cert's signature algorithm: ", leaf.SignatureAlgorithm.String())
fmt.Println("Cert's publicKey algorithm: ", leaf.PublicKeyAlgorithm.String())
fmt.Println("Cert's allowed domains: ", leaf.DNSNames)
}
}
func printTLSConnDetail(tlsConn *gotls.Conn) {
connectionState := tlsConn.ConnectionState()
var tlsVersion string
if connectionState.Version == gotls.VersionTLS13 {
tlsVersion = "TLS 1.3"
} else if connectionState.Version == gotls.VersionTLS12 {
tlsVersion = "TLS 1.2"
}
fmt.Println("TLS Version: ", tlsVersion)
curveID := connectionState.CurveID
if curveID != 0 {
PostQuantum := (curveID == gotls.X25519MLKEM768)
fmt.Println("TLS Post-Quantum key exchange: ", PostQuantum, "("+curveID.String()+")")
} else {
fmt.Println("TLS Post-Quantum key exchange: false (RSA Exchange)")
} }
} }

View File

@@ -65,6 +65,9 @@ func (h *Handler) Init(config *Config, dnsClient dns.Client, policyManager polic
h.server = config.Server.AsDestination() h.server = config.Server.AsDestination()
} }
h.nonIPQuery = config.Non_IPQuery h.nonIPQuery = config.Non_IPQuery
if h.nonIPQuery == "" {
h.nonIPQuery = "reject"
}
h.blockTypes = config.BlockTypes h.blockTypes = config.BlockTypes
return nil return nil
} }
@@ -187,6 +190,9 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
if len(h.blockTypes) > 0 { if len(h.blockTypes) > 0 {
for _, blocktype := range h.blockTypes { for _, blocktype := range h.blockTypes {
if blocktype == int32(qType) { if blocktype == int32(qType) {
if h.nonIPQuery == "reject" {
go h.rejectNonIPQuery(id, qType, domain, writer)
}
errors.LogInfo(ctx, "blocked type ", qType, " query for domain ", domain) errors.LogInfo(ctx, "blocked type ", qType, " query for domain ", domain)
return nil return nil
} }
@@ -199,6 +205,11 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
b.Release() b.Release()
continue continue
} }
if h.nonIPQuery == "reject" {
go h.rejectNonIPQuery(id, qType, domain, writer)
b.Release()
continue
}
} }
if err := connWriter.WriteMessage(b); err != nil { if err := connWriter.WriteMessage(b); err != nil {
@@ -317,6 +328,43 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string,
} }
} }
func (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter) {
b := buf.New()
rawBytes := b.Extend(buf.Size)
builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{
ID: id,
RCode: dnsmessage.RCodeRefused,
RecursionAvailable: true,
RecursionDesired: true,
Response: true,
Authoritative: true,
})
builder.EnableCompression()
common.Must(builder.StartQuestions())
err := builder.Question(dnsmessage.Question{
Name: dnsmessage.MustNewName(domain),
Class: dnsmessage.ClassINET,
Type: qType,
})
if err != nil {
errors.LogInfo(context.Background(), "unexpected domain ", domain, " when building reject message: ", err)
b.Release()
return
}
msgBytes, err := builder.Finish()
if err != nil {
errors.LogInfoInner(context.Background(), err, "pack reject message")
b.Release()
return
}
b.Resize(0, int32(len(msgBytes)))
if err := writer.WriteMessage(b); err != nil {
errors.LogInfoInner(context.Background(), err, "write reject answer")
}
}
type outboundConn struct { type outboundConn struct {
access sync.Mutex access sync.Mutex
dialer func() (stat.Connection, error) dialer func() (stat.Connection, error)

View File

@@ -26,8 +26,9 @@ type Config struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"`
PortMap map[string]string `protobuf:"bytes,3,rep,name=port_map,json=portMap,proto3" json:"port_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
// List of networks that the Dokodemo accepts. // List of networks that the Dokodemo accepts.
Networks []net.Network `protobuf:"varint,7,rep,packed,name=networks,proto3,enum=xray.common.net.Network" json:"networks,omitempty"` Networks []net.Network `protobuf:"varint,7,rep,packed,name=networks,proto3,enum=xray.common.net.Network" json:"networks,omitempty"`
FollowRedirect bool `protobuf:"varint,5,opt,name=follow_redirect,json=followRedirect,proto3" json:"follow_redirect,omitempty"` FollowRedirect bool `protobuf:"varint,5,opt,name=follow_redirect,json=followRedirect,proto3" json:"follow_redirect,omitempty"`
@@ -78,6 +79,13 @@ func (x *Config) GetPort() uint32 {
return 0 return 0
} }
func (x *Config) GetPortMap() map[string]string {
if x != nil {
return x.PortMap
}
return nil
}
func (x *Config) GetNetworks() []net.Network { func (x *Config) GetNetworks() []net.Network {
if x != nil { if x != nil {
return x.Networks return x.Networks
@@ -108,26 +116,34 @@ var file_proxy_dokodemo_config_proto_rawDesc = []byte{
0x6d, 0x6f, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x6d, 0x6f, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x61,
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x63, 0x6f, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x63, 0x6f,
0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd1, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd2, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x12, 0x35, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x67, 0x12, 0x35, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52,
0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x34, 0x0a, 0x08, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x43, 0x0a, 0x08,
0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6f, 0x6b, 0x6f,
0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x6f, 0x72, 0x74,
0x6b, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x72, 0x65, 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x61,
0x69, 0x72, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x66, 0x6f, 0x6c, 0x70, 0x12, 0x34, 0x0a, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, 0x07, 0x20,
0x6c, 0x6f, 0x77, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x08, 0x6e,
0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x42, 0x5b, 0x0a, 0x17, 0x63, 0x6f, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x6f, 0x6c, 0x6c, 0x6f,
0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6f, 0x6b, 0x77, 0x5f, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08,
0x6f, 0x64, 0x65, 0x6d, 0x6f, 0x50, 0x01, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x52, 0x0e, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x06,
0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x64, 0x6f, 0x6b, 0x6f, 0x64, 0x65, 0x6d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x1a,
0x6f, 0xaa, 0x02, 0x13, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x44, 0x3a, 0x0a, 0x0c, 0x50, 0x6f, 0x72, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
0x6f, 0x6b, 0x6f, 0x64, 0x65, 0x6d, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x5b, 0x0a, 0x17, 0x63,
0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6f,
0x6b, 0x6f, 0x64, 0x65, 0x6d, 0x6f, 0x50, 0x01, 0x5a, 0x28, 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, 0x64, 0x6f, 0x6b, 0x6f, 0x64, 0x65,
0x6d, 0x6f, 0xaa, 0x02, 0x13, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e,
0x44, 0x6f, 0x6b, 0x6f, 0x64, 0x65, 0x6d, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@@ -142,20 +158,22 @@ func file_proxy_dokodemo_config_proto_rawDescGZIP() []byte {
return file_proxy_dokodemo_config_proto_rawDescData return file_proxy_dokodemo_config_proto_rawDescData
} }
var file_proxy_dokodemo_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_proxy_dokodemo_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_proxy_dokodemo_config_proto_goTypes = []any{ var file_proxy_dokodemo_config_proto_goTypes = []any{
(*Config)(nil), // 0: xray.proxy.dokodemo.Config (*Config)(nil), // 0: xray.proxy.dokodemo.Config
(*net.IPOrDomain)(nil), // 1: xray.common.net.IPOrDomain nil, // 1: xray.proxy.dokodemo.Config.PortMapEntry
(net.Network)(0), // 2: xray.common.net.Network (*net.IPOrDomain)(nil), // 2: xray.common.net.IPOrDomain
(net.Network)(0), // 3: xray.common.net.Network
} }
var file_proxy_dokodemo_config_proto_depIdxs = []int32{ var file_proxy_dokodemo_config_proto_depIdxs = []int32{
1, // 0: xray.proxy.dokodemo.Config.address:type_name -> xray.common.net.IPOrDomain 2, // 0: xray.proxy.dokodemo.Config.address:type_name -> xray.common.net.IPOrDomain
2, // 1: xray.proxy.dokodemo.Config.networks:type_name -> xray.common.net.Network 1, // 1: xray.proxy.dokodemo.Config.port_map:type_name -> xray.proxy.dokodemo.Config.PortMapEntry
2, // [2:2] is the sub-list for method output_type 3, // 2: xray.proxy.dokodemo.Config.networks:type_name -> xray.common.net.Network
2, // [2:2] is the sub-list for method input_type 3, // [3:3] is the sub-list for method output_type
2, // [2:2] is the sub-list for extension type_name 3, // [3:3] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension extendee 3, // [3:3] is the sub-list for extension type_name
0, // [0:2] is the sub-list for field type_name 3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
} }
func init() { file_proxy_dokodemo_config_proto_init() } func init() { file_proxy_dokodemo_config_proto_init() }
@@ -169,7 +187,7 @@ func file_proxy_dokodemo_config_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proxy_dokodemo_config_proto_rawDesc, RawDescriptor: file_proxy_dokodemo_config_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 1, NumMessages: 2,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },

View File

@@ -13,6 +13,8 @@ message Config {
xray.common.net.IPOrDomain address = 1; xray.common.net.IPOrDomain address = 1;
uint32 port = 2; uint32 port = 2;
map<string, string> port_map = 3;
// List of networks that the Dokodemo accepts. // List of networks that the Dokodemo accepts.
repeated xray.common.net.Network networks = 7; repeated xray.common.net.Network networks = 7;

View File

@@ -3,6 +3,8 @@ package dokodemo
import ( import (
"context" "context"
"runtime" "runtime"
"strconv"
"strings"
"sync/atomic" "sync/atomic"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
@@ -36,6 +38,7 @@ type DokodemoDoor struct {
config *Config config *Config
address net.Address address net.Address
port net.Port port net.Port
portMap map[string]string
sockopt *session.Sockopt sockopt *session.Sockopt
} }
@@ -47,6 +50,7 @@ func (d *DokodemoDoor) Init(config *Config, pm policy.Manager, sockopt *session.
d.config = config d.config = config
d.address = config.GetPredefinedAddress() d.address = config.GetPredefinedAddress()
d.port = net.Port(config.Port) d.port = net.Port(config.Port)
d.portMap = config.PortMap
d.policyManager = pm d.policyManager = pm
d.sockopt = sockopt d.sockopt = sockopt
@@ -73,6 +77,33 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
Port: d.port, Port: d.port,
} }
if !d.config.FollowRedirect {
host, port, err := net.SplitHostPort(conn.LocalAddr().String())
if dest.Address == nil {
if err != nil {
dest.Address = net.DomainAddress("localhost")
} else {
if strings.Contains(host, ".") {
dest.Address = net.LocalHostIP
} else {
dest.Address = net.LocalHostIPv6
}
}
}
if dest.Port == 0 {
dest.Port = net.Port(common.Must2(strconv.Atoi(port)))
}
if d.portMap != nil && d.portMap[port] != "" {
h, p, _ := net.SplitHostPort(d.portMap[port])
if len(h) > 0 {
dest.Address = net.ParseAddress(h)
}
if len(p) > 0 {
dest.Port = net.Port(common.Must2(strconv.Atoi(p)))
}
}
}
destinationOverridden := false destinationOverridden := false
if d.config.FollowRedirect { if d.config.FollowRedirect {
outbounds := session.OutboundsFromContext(ctx) outbounds := session.OutboundsFromContext(ctx)

View File

@@ -1,44 +1 @@
package freedom package freedom
var strategy = [][]byte{
// name strategy, prefer, fallback
{0, 0, 0}, // AsIs none, /, /
{1, 0, 0}, // UseIP use, both, none
{1, 4, 0}, // UseIPv4 use, 4, none
{1, 6, 0}, // UseIPv6 use, 6, none
{1, 4, 6}, // UseIPv4v6 use, 4, 6
{1, 6, 4}, // UseIPv6v4 use, 6, 4
{2, 0, 0}, // ForceIP force, both, none
{2, 4, 0}, // ForceIPv4 force, 4, none
{2, 6, 0}, // ForceIPv6 force, 6, none
{2, 4, 6}, // ForceIPv4v6 force, 4, 6
{2, 6, 4}, // ForceIPv6v4 force, 6, 4
}
func (c *Config) hasStrategy() bool {
return strategy[c.DomainStrategy][0] != 0
}
func (c *Config) forceIP() bool {
return strategy[c.DomainStrategy][0] == 2
}
func (c *Config) preferIP4() bool {
return strategy[c.DomainStrategy][1] == 4 || strategy[c.DomainStrategy][1] == 0
}
func (c *Config) preferIP6() bool {
return strategy[c.DomainStrategy][1] == 6 || strategy[c.DomainStrategy][1] == 0
}
func (c *Config) hasFallback() bool {
return strategy[c.DomainStrategy][2] != 0
}
func (c *Config) fallbackIP4() bool {
return strategy[c.DomainStrategy][2] == 4
}
func (c *Config) fallbackIP6() bool {
return strategy[c.DomainStrategy][2] == 6
}

View File

@@ -8,6 +8,7 @@ package freedom
import ( import (
protocol "github.com/xtls/xray-core/common/protocol" protocol "github.com/xtls/xray-core/common/protocol"
internet "github.com/xtls/xray-core/transport/internet"
protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
@@ -21,79 +22,6 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
type Config_DomainStrategy int32
const (
Config_AS_IS Config_DomainStrategy = 0
Config_USE_IP Config_DomainStrategy = 1
Config_USE_IP4 Config_DomainStrategy = 2
Config_USE_IP6 Config_DomainStrategy = 3
Config_USE_IP46 Config_DomainStrategy = 4
Config_USE_IP64 Config_DomainStrategy = 5
Config_FORCE_IP Config_DomainStrategy = 6
Config_FORCE_IP4 Config_DomainStrategy = 7
Config_FORCE_IP6 Config_DomainStrategy = 8
Config_FORCE_IP46 Config_DomainStrategy = 9
Config_FORCE_IP64 Config_DomainStrategy = 10
)
// Enum value maps for Config_DomainStrategy.
var (
Config_DomainStrategy_name = map[int32]string{
0: "AS_IS",
1: "USE_IP",
2: "USE_IP4",
3: "USE_IP6",
4: "USE_IP46",
5: "USE_IP64",
6: "FORCE_IP",
7: "FORCE_IP4",
8: "FORCE_IP6",
9: "FORCE_IP46",
10: "FORCE_IP64",
}
Config_DomainStrategy_value = map[string]int32{
"AS_IS": 0,
"USE_IP": 1,
"USE_IP4": 2,
"USE_IP6": 3,
"USE_IP46": 4,
"USE_IP64": 5,
"FORCE_IP": 6,
"FORCE_IP4": 7,
"FORCE_IP6": 8,
"FORCE_IP46": 9,
"FORCE_IP64": 10,
}
)
func (x Config_DomainStrategy) Enum() *Config_DomainStrategy {
p := new(Config_DomainStrategy)
*p = x
return p
}
func (x Config_DomainStrategy) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Config_DomainStrategy) Descriptor() protoreflect.EnumDescriptor {
return file_proxy_freedom_config_proto_enumTypes[0].Descriptor()
}
func (Config_DomainStrategy) Type() protoreflect.EnumType {
return &file_proxy_freedom_config_proto_enumTypes[0]
}
func (x Config_DomainStrategy) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Config_DomainStrategy.Descriptor instead.
func (Config_DomainStrategy) EnumDescriptor() ([]byte, []int) {
return file_proxy_freedom_config_proto_rawDescGZIP(), []int{3, 0}
}
type DestinationOverride struct { type DestinationOverride struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -150,6 +78,8 @@ type Fragment struct {
LengthMax uint64 `protobuf:"varint,4,opt,name=length_max,json=lengthMax,proto3" json:"length_max,omitempty"` LengthMax uint64 `protobuf:"varint,4,opt,name=length_max,json=lengthMax,proto3" json:"length_max,omitempty"`
IntervalMin uint64 `protobuf:"varint,5,opt,name=interval_min,json=intervalMin,proto3" json:"interval_min,omitempty"` IntervalMin uint64 `protobuf:"varint,5,opt,name=interval_min,json=intervalMin,proto3" json:"interval_min,omitempty"`
IntervalMax uint64 `protobuf:"varint,6,opt,name=interval_max,json=intervalMax,proto3" json:"interval_max,omitempty"` IntervalMax uint64 `protobuf:"varint,6,opt,name=interval_max,json=intervalMax,proto3" json:"interval_max,omitempty"`
MaxSplitMin uint64 `protobuf:"varint,7,opt,name=max_split_min,json=maxSplitMin,proto3" json:"max_split_min,omitempty"`
MaxSplitMax uint64 `protobuf:"varint,8,opt,name=max_split_max,json=maxSplitMax,proto3" json:"max_split_max,omitempty"`
} }
func (x *Fragment) Reset() { func (x *Fragment) Reset() {
@@ -224,6 +154,20 @@ func (x *Fragment) GetIntervalMax() uint64 {
return 0 return 0
} }
func (x *Fragment) GetMaxSplitMin() uint64 {
if x != nil {
return x.MaxSplitMin
}
return 0
}
func (x *Fragment) GetMaxSplitMax() uint64 {
if x != nil {
return x.MaxSplitMax
}
return 0
}
type Noise struct { type Noise struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -234,6 +178,7 @@ type Noise struct {
DelayMin uint64 `protobuf:"varint,3,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"` DelayMin uint64 `protobuf:"varint,3,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"`
DelayMax uint64 `protobuf:"varint,4,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"` DelayMax uint64 `protobuf:"varint,4,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"`
Packet []byte `protobuf:"bytes,5,opt,name=packet,proto3" json:"packet,omitempty"` Packet []byte `protobuf:"bytes,5,opt,name=packet,proto3" json:"packet,omitempty"`
ApplyTo string `protobuf:"bytes,6,opt,name=apply_to,json=applyTo,proto3" json:"apply_to,omitempty"`
} }
func (x *Noise) Reset() { func (x *Noise) Reset() {
@@ -301,17 +246,24 @@ func (x *Noise) GetPacket() []byte {
return nil return nil
} }
func (x *Noise) GetApplyTo() string {
if x != nil {
return x.ApplyTo
}
return ""
}
type Config struct { type Config struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
DomainStrategy Config_DomainStrategy `protobuf:"varint,1,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.proxy.freedom.Config_DomainStrategy" json:"domain_strategy,omitempty"` DomainStrategy internet.DomainStrategy `protobuf:"varint,1,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"domain_strategy,omitempty"`
DestinationOverride *DestinationOverride `protobuf:"bytes,3,opt,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"` DestinationOverride *DestinationOverride `protobuf:"bytes,3,opt,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"`
UserLevel uint32 `protobuf:"varint,4,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"` UserLevel uint32 `protobuf:"varint,4,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"`
Fragment *Fragment `protobuf:"bytes,5,opt,name=fragment,proto3" json:"fragment,omitempty"` Fragment *Fragment `protobuf:"bytes,5,opt,name=fragment,proto3" json:"fragment,omitempty"`
ProxyProtocol uint32 `protobuf:"varint,6,opt,name=proxy_protocol,json=proxyProtocol,proto3" json:"proxy_protocol,omitempty"` ProxyProtocol uint32 `protobuf:"varint,6,opt,name=proxy_protocol,json=proxyProtocol,proto3" json:"proxy_protocol,omitempty"`
Noises []*Noise `protobuf:"bytes,7,rep,name=noises,proto3" json:"noises,omitempty"` Noises []*Noise `protobuf:"bytes,7,rep,name=noises,proto3" json:"noises,omitempty"`
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@@ -344,11 +296,11 @@ func (*Config) Descriptor() ([]byte, []int) {
return file_proxy_freedom_config_proto_rawDescGZIP(), []int{3} return file_proxy_freedom_config_proto_rawDescGZIP(), []int{3}
} }
func (x *Config) GetDomainStrategy() Config_DomainStrategy { func (x *Config) GetDomainStrategy() internet.DomainStrategy {
if x != nil { if x != nil {
return x.DomainStrategy return x.DomainStrategy
} }
return Config_AS_IS return internet.DomainStrategy(0)
} }
func (x *Config) GetDestinationOverride() *DestinationOverride { func (x *Config) GetDestinationOverride() *DestinationOverride {
@@ -394,75 +346,72 @@ var file_proxy_freedom_config_proto_rawDesc = []byte{
0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d,
0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,
0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x2e, 0x70, 0x72, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x22, 0x53, 0x0a, 0x13, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69,
0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70,
0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x53, 0x0a, 0x13, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x3c, 0x0a, 0x06, 0x73,
0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72,
0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0xd0, 0x01, 0x0a, 0x08, 0x46, 0x72, 0x61, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e,
0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x70, 0x61, 0x63, 0x74, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x98, 0x02, 0x0a, 0x08, 0x46, 0x72,
0x6b, 0x65, 0x74, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74,
0x65, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x70, 0x61, 0x73, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x70, 0x61,
0x63, 0x6b, 0x65, 0x74, 0x73, 0x54, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x63,
0x68, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6c, 0x65, 0x6e, 0x6b, 0x65, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x70,
0x67, 0x74, 0x68, 0x4d, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x54, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x6e, 0x67,
0x5f, 0x6d, 0x61, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6c, 0x65,
0x74, 0x68, 0x4d, 0x61, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6e, 0x67, 0x74, 0x68, 0x4d, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x6e, 0x67, 0x74,
0x6c, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x68, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6c, 0x65, 0x6e,
0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x74, 0x68, 0x4d, 0x61, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76,
0x72, 0x76, 0x61, 0x6c, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x61, 0x6c, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x6e,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x61, 0x78, 0x22, 0x97, 0x01, 0x0a, 0x05, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x74,
0x4e, 0x6f, 0x69, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5f, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52,
0x6d, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x61, 0x78, 0x12, 0x22, 0x0a, 0x0d,
0x68, 0x4d, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5f, 0x6d, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x07, 0x20,
0x61, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x4d, 0x69, 0x6e,
0x4d, 0x61, 0x78, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6e, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x6d, 0x61,
0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6e, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x53, 0x70, 0x6c, 0x69,
0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x04, 0x20, 0x74, 0x4d, 0x61, 0x78, 0x22, 0xb2, 0x01, 0x0a, 0x05, 0x4e, 0x6f, 0x69, 0x73, 0x65, 0x12, 0x1d,
0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x61, 0x78, 0x12, 0x16, 0x0a, 0x0a, 0x0a, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01,
0x06, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x28, 0x04, 0x52, 0x09, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x4d, 0x69, 0x6e, 0x12, 0x1d, 0x0a,
0x61, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x97, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28,
0x12, 0x52, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x04, 0x52, 0x09, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x4d, 0x61, 0x78, 0x12, 0x1b, 0x0a, 0x09,
0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52,
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x43, 0x08, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x69, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x6c,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x61, 0x79, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x65,
0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x6c, 0x61, 0x79, 0x4d, 0x61, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74,
0x74, 0x65, 0x67, 0x79, 0x12, 0x5a, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x19,
0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x0a, 0x08, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x5f, 0x74, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x52, 0x07, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x54, 0x6f, 0x22, 0xe9, 0x02, 0x0a, 0x06, 0x43, 0x6f,
0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x50, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73,
0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e,
0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74,
0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74,
0x38, 0x0a, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x5a, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e,
0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x03,
0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78,
0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e,
0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x52, 0x13, 0x64,
0x0d, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69,
0x12, 0x31, 0x0a, 0x06, 0x6e, 0x6f, 0x69, 0x73, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c,
0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65,
0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x4e, 0x6f, 0x69, 0x73, 0x65, 0x52, 0x06, 0x6e, 0x6f, 0x69, 0x6c, 0x12, 0x38, 0x0a, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20,
0x73, 0x65, 0x73, 0x22, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79,
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e,
0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x74, 0x52, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70,
0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x06, 0x20,
0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
0x50, 0x34, 0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x6f, 0x6c, 0x12, 0x31, 0x0a, 0x06, 0x6e, 0x6f, 0x69, 0x73, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03,
0x34, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e,
0x06, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x4e, 0x6f, 0x69, 0x73, 0x65, 0x52, 0x06, 0x6e,
0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x6f, 0x69, 0x73, 0x65, 0x73, 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61,
0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x50,
0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x42, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74,
0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f,
0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x78, 0x79, 0x2f, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x46, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x62,
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x66, 0x72, 0x65, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78,
0x79, 0x2e, 0x46, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
} }
var ( var (
@@ -477,22 +426,21 @@ func file_proxy_freedom_config_proto_rawDescGZIP() []byte {
return file_proxy_freedom_config_proto_rawDescData return file_proxy_freedom_config_proto_rawDescData
} }
var file_proxy_freedom_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_proxy_freedom_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_proxy_freedom_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_proxy_freedom_config_proto_goTypes = []any{ var file_proxy_freedom_config_proto_goTypes = []any{
(Config_DomainStrategy)(0), // 0: xray.proxy.freedom.Config.DomainStrategy (*DestinationOverride)(nil), // 0: xray.proxy.freedom.DestinationOverride
(*DestinationOverride)(nil), // 1: xray.proxy.freedom.DestinationOverride (*Fragment)(nil), // 1: xray.proxy.freedom.Fragment
(*Fragment)(nil), // 2: xray.proxy.freedom.Fragment (*Noise)(nil), // 2: xray.proxy.freedom.Noise
(*Noise)(nil), // 3: xray.proxy.freedom.Noise (*Config)(nil), // 3: xray.proxy.freedom.Config
(*Config)(nil), // 4: xray.proxy.freedom.Config (*protocol.ServerEndpoint)(nil), // 4: xray.common.protocol.ServerEndpoint
(*protocol.ServerEndpoint)(nil), // 5: xray.common.protocol.ServerEndpoint (internet.DomainStrategy)(0), // 5: xray.transport.internet.DomainStrategy
} }
var file_proxy_freedom_config_proto_depIdxs = []int32{ var file_proxy_freedom_config_proto_depIdxs = []int32{
5, // 0: xray.proxy.freedom.DestinationOverride.server:type_name -> xray.common.protocol.ServerEndpoint 4, // 0: xray.proxy.freedom.DestinationOverride.server:type_name -> xray.common.protocol.ServerEndpoint
0, // 1: xray.proxy.freedom.Config.domain_strategy:type_name -> xray.proxy.freedom.Config.DomainStrategy 5, // 1: xray.proxy.freedom.Config.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
1, // 2: xray.proxy.freedom.Config.destination_override:type_name -> xray.proxy.freedom.DestinationOverride 0, // 2: xray.proxy.freedom.Config.destination_override:type_name -> xray.proxy.freedom.DestinationOverride
2, // 3: xray.proxy.freedom.Config.fragment:type_name -> xray.proxy.freedom.Fragment 1, // 3: xray.proxy.freedom.Config.fragment:type_name -> xray.proxy.freedom.Fragment
3, // 4: xray.proxy.freedom.Config.noises:type_name -> xray.proxy.freedom.Noise 2, // 4: xray.proxy.freedom.Config.noises:type_name -> xray.proxy.freedom.Noise
5, // [5:5] is the sub-list for method output_type 5, // [5:5] is the sub-list for method output_type
5, // [5:5] is the sub-list for method input_type 5, // [5:5] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension type_name
@@ -510,14 +458,13 @@ func file_proxy_freedom_config_proto_init() {
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proxy_freedom_config_proto_rawDesc, RawDescriptor: file_proxy_freedom_config_proto_rawDesc,
NumEnums: 1, NumEnums: 0,
NumMessages: 4, NumMessages: 4,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },
GoTypes: file_proxy_freedom_config_proto_goTypes, GoTypes: file_proxy_freedom_config_proto_goTypes,
DependencyIndexes: file_proxy_freedom_config_proto_depIdxs, DependencyIndexes: file_proxy_freedom_config_proto_depIdxs,
EnumInfos: file_proxy_freedom_config_proto_enumTypes,
MessageInfos: file_proxy_freedom_config_proto_msgTypes, MessageInfos: file_proxy_freedom_config_proto_msgTypes,
}.Build() }.Build()
File_proxy_freedom_config_proto = out.File File_proxy_freedom_config_proto = out.File

View File

@@ -7,6 +7,7 @@ option java_package = "com.xray.proxy.freedom";
option java_multiple_files = true; option java_multiple_files = true;
import "common/protocol/server_spec.proto"; import "common/protocol/server_spec.proto";
import "transport/internet/config.proto";
message DestinationOverride { message DestinationOverride {
xray.common.protocol.ServerEndpoint server = 1; xray.common.protocol.ServerEndpoint server = 1;
@@ -19,6 +20,8 @@ message Fragment {
uint64 length_max = 4; uint64 length_max = 4;
uint64 interval_min = 5; uint64 interval_min = 5;
uint64 interval_max = 6; uint64 interval_max = 6;
uint64 max_split_min = 7;
uint64 max_split_max = 8;
} }
message Noise { message Noise {
uint64 length_min = 1; uint64 length_min = 1;
@@ -26,23 +29,11 @@ message Noise {
uint64 delay_min = 3; uint64 delay_min = 3;
uint64 delay_max = 4; uint64 delay_max = 4;
bytes packet = 5; bytes packet = 5;
string apply_to = 6;
} }
message Config { message Config {
enum DomainStrategy { xray.transport.internet.DomainStrategy domain_strategy = 1;
AS_IS = 0;
USE_IP = 1;
USE_IP4 = 2;
USE_IP6 = 3;
USE_IP46 = 4;
USE_IP64 = 5;
FORCE_IP = 6;
FORCE_IP4 = 7;
FORCE_IP6 = 8;
FORCE_IP46 = 9;
FORCE_IP64 = 10;
}
DomainStrategy domain_strategy = 1;
DestinationOverride destination_override = 3; DestinationOverride destination_override = 3;
uint32 user_level = 4; uint32 user_level = 4;
Fragment fragment = 5; Fragment fragment = 5;

View File

@@ -19,7 +19,6 @@ import (
"github.com/xtls/xray-core/common/signal" "github.com/xtls/xray-core/common/signal"
"github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/policy" "github.com/xtls/xray-core/features/policy"
"github.com/xtls/xray-core/features/stats" "github.com/xtls/xray-core/features/stats"
"github.com/xtls/xray-core/proxy" "github.com/xtls/xray-core/proxy"
@@ -34,8 +33,8 @@ var useSplice bool
func init() { func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
h := new(Handler) h := new(Handler)
if err := core.RequireFeatures(ctx, func(pm policy.Manager, d dns.Client) error { if err := core.RequireFeatures(ctx, func(pm policy.Manager) error {
return h.Init(config.(*Config), pm, d) return h.Init(config.(*Config), pm)
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
@@ -52,16 +51,13 @@ func init() {
// Handler handles Freedom connections. // Handler handles Freedom connections.
type Handler struct { type Handler struct {
policyManager policy.Manager policyManager policy.Manager
dns dns.Client
config *Config config *Config
} }
// Init initializes the Handler with necessary parameters. // Init initializes the Handler with necessary parameters.
func (h *Handler) Init(config *Config, pm policy.Manager, d dns.Client) error { func (h *Handler) Init(config *Config, pm policy.Manager) error {
h.config = config h.config = config
h.policyManager = pm h.policyManager = pm
h.dns = d
return nil return nil
} }
@@ -70,35 +66,13 @@ func (h *Handler) policy() policy.Session {
return p return p
} }
func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address {
ips, _, err := h.dns.LookupIP(domain, dns.IPOption{
IPv4Enable: (localAddr == nil || localAddr.Family().IsIPv4()) && h.config.preferIP4(),
IPv6Enable: (localAddr == nil || localAddr.Family().IsIPv6()) && h.config.preferIP6(),
})
{ // Resolve fallback
if (len(ips) == 0 || err != nil) && h.config.hasFallback() && localAddr == nil {
ips, _, err = h.dns.LookupIP(domain, dns.IPOption{
IPv4Enable: h.config.fallbackIP4(),
IPv6Enable: h.config.fallbackIP6(),
})
}
}
if err != nil {
errors.LogInfoInner(ctx, err, "failed to get IP address for domain ", domain)
}
if len(ips) == 0 {
return nil
}
return net.IPAddress(ips[dice.Roll(len(ips))])
}
func isValidAddress(addr *net.IPOrDomain) bool { func isValidAddress(addr *net.IPOrDomain) bool {
if addr == nil { if addr == nil {
return false return false
} }
a := addr.AsAddress() a := addr.AsAddress()
return a != net.AnyIP return a != net.AnyIP && a != net.AnyIPv6
} }
// Process implements proxy.Outbound. // Process implements proxy.Outbound.
@@ -113,6 +87,12 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
inbound := session.InboundFromContext(ctx) inbound := session.InboundFromContext(ctx)
destination := ob.Target destination := ob.Target
origTargetAddr := ob.OriginalTarget.Address
if origTargetAddr == nil {
origTargetAddr = ob.Target.Address
}
dialer.SetOutboundGateway(ctx, ob)
outGateway := ob.Gateway
UDPOverride := net.UDPDestination(nil, 0) UDPOverride := net.UDPDestination(nil, 0)
if h.config.DestinationOverride != nil { if h.config.DestinationOverride != nil {
server := h.config.DestinationOverride.Server server := h.config.DestinationOverride.Server
@@ -132,17 +112,24 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
var conn stat.Connection var conn stat.Connection
err := retry.ExponentialBackoff(5, 100).On(func() error { err := retry.ExponentialBackoff(5, 100).On(func() error {
dialDest := destination dialDest := destination
if h.config.hasStrategy() && dialDest.Address.Family().IsDomain() { if h.config.DomainStrategy.HasStrategy() && dialDest.Address.Family().IsDomain() {
ip := h.resolveIP(ctx, dialDest.Address.Domain(), dialer.Address()) strategy := h.config.DomainStrategy
if ip != nil { if destination.Network == net.Network_UDP && origTargetAddr != nil && outGateway == nil {
strategy = strategy.GetDynamicStrategy(origTargetAddr.Family())
}
ips, err := internet.LookupForIP(dialDest.Address.Domain(), strategy, outGateway)
if err != nil {
errors.LogInfoInner(ctx, err, "failed to get IP address for domain ", dialDest.Address.Domain())
if h.config.DomainStrategy.ForceIP() {
return err
}
} else {
dialDest = net.Destination{ dialDest = net.Destination{
Network: dialDest.Network, Network: dialDest.Network,
Address: ip, Address: net.IPAddress(ips[dice.Roll(len(ips))]),
Port: dialDest.Port, Port: dialDest.Port,
} }
errors.LogInfo(ctx, "dialing to ", dialDest) errors.LogInfo(ctx, "dialing to ", dialDest)
} else if h.config.forceIP() {
return dns.ErrEmptyResponse
} }
} }
@@ -193,7 +180,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
if destination.Network == net.Network_TCP { if destination.Network == net.Network_TCP {
if h.config.Fragment != nil { if h.config.Fragment != nil {
errors.LogDebug(ctx, "FRAGMENT", h.config.Fragment.PacketsFrom, h.config.Fragment.PacketsTo, h.config.Fragment.LengthMin, h.config.Fragment.LengthMax, errors.LogDebug(ctx, "FRAGMENT", h.config.Fragment.PacketsFrom, h.config.Fragment.PacketsTo, h.config.Fragment.LengthMin, h.config.Fragment.LengthMax,
h.config.Fragment.IntervalMin, h.config.Fragment.IntervalMax) h.config.Fragment.IntervalMin, h.config.Fragment.IntervalMax, h.config.Fragment.MaxSplitMin, h.config.Fragment.MaxSplitMax)
writer = buf.NewWriter(&FragmentWriter{ writer = buf.NewWriter(&FragmentWriter{
fragment: h.config.Fragment, fragment: h.config.Fragment,
writer: conn, writer: conn,
@@ -202,7 +189,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
writer = buf.NewWriter(conn) writer = buf.NewWriter(conn)
} }
} else { } else {
writer = NewPacketWriter(conn, h, ctx, UDPOverride) writer = NewPacketWriter(conn, UDPOverride, destination)
if h.config.Noises != nil { if h.config.Noises != nil {
errors.LogDebug(ctx, "NOISE", h.config.Noises) errors.LogDebug(ctx, "NOISE", h.config.Noises)
writer = &NoisePacketWriter{ writer = &NoisePacketWriter{
@@ -210,6 +197,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
noises: h.config.Noises, noises: h.config.Noises,
firstWrite: true, firstWrite: true,
UDPOverride: UDPOverride, UDPOverride: UDPOverride,
remoteAddr: net.DestinationFromAddr(conn.RemoteAddr()).Address,
} }
} }
} }
@@ -238,7 +226,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
if destination.Network == net.Network_TCP { if destination.Network == net.Network_TCP {
reader = buf.NewReader(conn) reader = buf.NewReader(conn)
} else { } else {
reader = NewPacketReader(conn, UDPOverride) reader = NewPacketReader(conn, UDPOverride, destination)
} }
if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil { if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil {
return errors.New("failed to process response").Base(err) return errors.New("failed to process response").Base(err)
@@ -273,7 +261,7 @@ func isTLSConn(conn stat.Connection) bool {
return false return false
} }
func NewPacketReader(conn net.Conn, UDPOverride net.Destination) buf.Reader { func NewPacketReader(conn net.Conn, UDPOverride net.Destination, destination net.Destination) buf.Reader {
iConn := conn iConn := conn
statConn, ok := iConn.(*stat.CounterConnection) statConn, ok := iConn.(*stat.CounterConnection)
if ok { if ok {
@@ -283,10 +271,18 @@ func NewPacketReader(conn net.Conn, UDPOverride net.Destination) buf.Reader {
if statConn != nil { if statConn != nil {
counter = statConn.ReadCounter counter = statConn.ReadCounter
} }
if c, ok := iConn.(*internet.PacketConnWrapper); ok && UDPOverride.Address == nil && UDPOverride.Port == 0 { if c, ok := iConn.(*internet.PacketConnWrapper); ok {
isOverridden := false
if UDPOverride.Address != nil || UDPOverride.Port != 0 {
isOverridden = true
}
return &PacketReader{ return &PacketReader{
PacketConnWrapper: c, PacketConnWrapper: c,
Counter: counter, Counter: counter,
IsOverridden: isOverridden,
InitUnchangedAddr: destination.Address,
InitChangedAddr: net.DestinationFromAddr(conn.RemoteAddr()).Address,
} }
} }
return &buf.PacketReader{Reader: conn} return &buf.PacketReader{Reader: conn}
@@ -295,6 +291,9 @@ func NewPacketReader(conn net.Conn, UDPOverride net.Destination) buf.Reader {
type PacketReader struct { type PacketReader struct {
*internet.PacketConnWrapper *internet.PacketConnWrapper
stats.Counter stats.Counter
IsOverridden bool
InitUnchangedAddr net.Address
InitChangedAddr net.Address
} }
func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
@@ -306,10 +305,18 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
return nil, err return nil, err
} }
b.Resize(0, int32(n)) b.Resize(0, int32(n))
b.UDP = &net.Destination{ // if udp dest addr is changed, we are unable to get the correct src addr
Address: net.IPAddress(d.(*net.UDPAddr).IP), // so we don't attach src info to udp packet, break cone behavior, assuming the dial dest is the expected scr addr
Port: net.Port(d.(*net.UDPAddr).Port), if !r.IsOverridden {
Network: net.Network_UDP, address := net.IPAddress(d.(*net.UDPAddr).IP)
if r.InitChangedAddr == address {
address = r.InitUnchangedAddr
}
b.UDP = &net.Destination{
Address: address,
Port: net.Port(d.(*net.UDPAddr).Port),
Network: net.Network_UDP,
}
} }
if r.Counter != nil { if r.Counter != nil {
r.Counter.Add(int64(n)) r.Counter.Add(int64(n))
@@ -317,7 +324,8 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
return buf.MultiBuffer{b}, nil return buf.MultiBuffer{b}, nil
} }
func NewPacketWriter(conn net.Conn, h *Handler, ctx context.Context, UDPOverride net.Destination) buf.Writer { // DialDest means the dial target used in the dialer when creating conn
func NewPacketWriter(conn net.Conn, UDPOverride net.Destination, destination net.Destination) buf.Writer {
iConn := conn iConn := conn
statConn, ok := iConn.(*stat.CounterConnection) statConn, ok := iConn.(*stat.CounterConnection)
if ok { if ok {
@@ -331,9 +339,9 @@ func NewPacketWriter(conn net.Conn, h *Handler, ctx context.Context, UDPOverride
return &PacketWriter{ return &PacketWriter{
PacketConnWrapper: c, PacketConnWrapper: c,
Counter: counter, Counter: counter,
Handler: h,
Context: ctx,
UDPOverride: UDPOverride, UDPOverride: UDPOverride,
InitUnchangedAddr: destination.Address,
InitChangedAddr: net.DestinationFromAddr(conn.RemoteAddr()).Address,
} }
} }
@@ -343,9 +351,10 @@ func NewPacketWriter(conn net.Conn, h *Handler, ctx context.Context, UDPOverride
type PacketWriter struct { type PacketWriter struct {
*internet.PacketConnWrapper *internet.PacketConnWrapper
stats.Counter stats.Counter
*Handler
context.Context
UDPOverride net.Destination UDPOverride net.Destination
InitUnchangedAddr net.Address
InitChangedAddr net.Address
} }
func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
@@ -364,18 +373,15 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
if w.UDPOverride.Port != 0 { if w.UDPOverride.Port != 0 {
b.UDP.Port = w.UDPOverride.Port b.UDP.Port = w.UDPOverride.Port
} }
if w.Handler.config.hasStrategy() && b.UDP.Address.Family().IsDomain() { if b.UDP.Address == w.InitUnchangedAddr {
ip := w.Handler.resolveIP(w.Context, b.UDP.Address.Domain(), nil) b.UDP.Address = w.InitChangedAddr
if ip != nil {
b.UDP.Address = ip
}
} }
destAddr, _ := net.ResolveUDPAddr("udp", b.UDP.NetAddr()) if b.UDP.Address.Family().IsDomain() {
if destAddr == nil {
b.Release() b.Release()
continue buf.ReleaseMulti(mb)
return errors.New("multiple domains cone is not supported")
} }
n, err = w.PacketConnWrapper.WriteTo(b.Bytes(), destAddr) n, err = w.PacketConnWrapper.WriteTo(b.Bytes(), b.UDP.RawNetAddr())
} else { } else {
n, err = w.PacketConnWrapper.Write(b.Bytes()) n, err = w.PacketConnWrapper.Write(b.Bytes())
} }
@@ -396,6 +402,7 @@ type NoisePacketWriter struct {
noises []*Noise noises []*Noise
firstWrite bool firstWrite bool
UDPOverride net.Destination UDPOverride net.Destination
remoteAddr net.Address
} }
// MultiBuffer writer with Noise before first packet // MultiBuffer writer with Noise before first packet
@@ -408,8 +415,24 @@ func (w *NoisePacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
} }
var noise []byte var noise []byte
var err error var err error
if w.remoteAddr.Family().IsDomain() {
panic("impossible, remoteAddr is always IP")
}
for _, n := range w.noises { for _, n := range w.noises {
//User input string or base64 encoded string switch n.ApplyTo {
case "ipv4":
if w.remoteAddr.Family().IsIPv6() {
continue
}
case "ipv6":
if w.remoteAddr.Family().IsIPv4() {
continue
}
case "ip":
default:
panic("unreachable, applyTo is ip/ipv4/ipv6")
}
//User input string or base64 encoded string or hex string
if n.Packet != nil { if n.Packet != nil {
noise = n.Packet noise = n.Packet
} else { } else {
@@ -420,7 +443,10 @@ func (w *NoisePacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
if err != nil { if err != nil {
return err return err
} }
w.Writer.WriteMultiBuffer(buf.MultiBuffer{buf.FromBytes(noise)}) err = w.Writer.WriteMultiBuffer(buf.MultiBuffer{buf.FromBytes(noise)})
if err != nil {
return err
}
if n.DelayMin != 0 || n.DelayMax != 0 { if n.DelayMin != 0 || n.DelayMax != 0 {
time.Sleep(time.Duration(crypto.RandBetween(int64(n.DelayMin), int64(n.DelayMax))) * time.Millisecond) time.Sleep(time.Duration(crypto.RandBetween(int64(n.DelayMin), int64(n.DelayMax))) * time.Millisecond)
@@ -449,23 +475,29 @@ func (f *FragmentWriter) Write(b []byte) (int, error) {
return f.writer.Write(b) return f.writer.Write(b)
} }
data := b[5:recordLen] data := b[5:recordLen]
buf := make([]byte, 1024) buff := make([]byte, 2048)
var hello []byte var hello []byte
maxSplit := crypto.RandBetween(int64(f.fragment.MaxSplitMin), int64(f.fragment.MaxSplitMax))
var splitNum int64
for from := 0; ; { for from := 0; ; {
to := from + int(crypto.RandBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax))) to := from + int(crypto.RandBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax)))
if to > len(data) { splitNum++
if to > len(data) || (maxSplit > 0 && splitNum >= maxSplit) {
to = len(data) to = len(data)
} }
copy(buf[:3], b)
copy(buf[5:], data[from:to])
l := to - from l := to - from
if 5+l > len(buff) {
buff = make([]byte, 5+l)
}
copy(buff[:3], b)
copy(buff[5:], data[from:to])
from = to from = to
buf[3] = byte(l >> 8) buff[3] = byte(l >> 8)
buf[4] = byte(l) buff[4] = byte(l)
if f.fragment.IntervalMax == 0 { // combine fragmented tlshello if interval is 0 if f.fragment.IntervalMax == 0 { // combine fragmented tlshello if interval is 0
hello = append(hello, buf[:5+l]...) hello = append(hello, buff[:5+l]...)
} else { } else {
_, err := f.writer.Write(buf[:5+l]) _, err := f.writer.Write(buff[:5+l])
time.Sleep(time.Duration(crypto.RandBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond) time.Sleep(time.Duration(crypto.RandBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond)
if err != nil { if err != nil {
return 0, err return 0, err
@@ -492,17 +524,20 @@ func (f *FragmentWriter) Write(b []byte) (int, error) {
if f.fragment.PacketsFrom != 0 && (f.count < f.fragment.PacketsFrom || f.count > f.fragment.PacketsTo) { if f.fragment.PacketsFrom != 0 && (f.count < f.fragment.PacketsFrom || f.count > f.fragment.PacketsTo) {
return f.writer.Write(b) return f.writer.Write(b)
} }
maxSplit := crypto.RandBetween(int64(f.fragment.MaxSplitMin), int64(f.fragment.MaxSplitMax))
var splitNum int64
for from := 0; ; { for from := 0; ; {
to := from + int(crypto.RandBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax))) to := from + int(crypto.RandBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax)))
if to > len(b) { splitNum++
if to > len(b) || (maxSplit > 0 && splitNum >= maxSplit) {
to = len(b) to = len(b)
} }
n, err := f.writer.Write(b[from:to]) n, err := f.writer.Write(b[from:to])
from += n from += n
time.Sleep(time.Duration(crypto.RandBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond)
if err != nil { if err != nil {
return from, err return from, err
} }
time.Sleep(time.Duration(crypto.RandBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond)
if from >= len(b) { if from >= len(b) {
return from, nil return from, nil
} }

View File

@@ -2,7 +2,6 @@ package shadowsocks
import ( import (
"bytes" "bytes"
"crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/md5" "crypto/md5"
"crypto/sha1" "crypto/sha1"
@@ -58,11 +57,7 @@ func (a *MemoryAccount) CheckIV(iv []byte) error {
} }
func createAesGcm(key []byte) cipher.AEAD { func createAesGcm(key []byte) cipher.AEAD {
block, err := aes.NewCipher(key) return crypto.NewAesGcm(key)
common.Must(err)
gcm, err := cipher.NewGCM(block)
common.Must(err)
return gcm
} }
func createChaCha20Poly1305(key []byte) cipher.AEAD { func createChaCha20Poly1305(key []byte) cipher.AEAD {

View File

@@ -128,6 +128,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis
conn.Write(data.Bytes()) conn.Write(data.Bytes())
}) })
defer udpServer.RemoveRay()
inbound := session.InboundFromContext(ctx) inbound := session.InboundFromContext(ctx)
var dest *net.Destination var dest *net.Destination

View File

@@ -245,13 +245,15 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis
udpMessage, err := EncodeUDPPacket(request, payload.Bytes()) udpMessage, err := EncodeUDPPacket(request, payload.Bytes())
payload.Release() payload.Release()
defer udpMessage.Release()
if err != nil { if err != nil {
errors.LogWarningInner(ctx, err, "failed to write UDP response") errors.LogWarningInner(ctx, err, "failed to write UDP response")
return
} }
defer udpMessage.Release()
conn.Write(udpMessage.Bytes()) conn.Write(udpMessage.Bytes())
}) })
defer udpServer.RemoveRay()
inbound := session.InboundFromContext(ctx) inbound := session.InboundFromContext(ctx)
if inbound != nil && inbound.Source.IsValid() { if inbound != nil && inbound.Source.IsValid() {

Some files were not shown because too many files have changed in this diff Show More