Compare commits

..

290 Commits

Author SHA1 Message Date
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
RPRX
f38d3f786a v25.6.7
Announcement of NFTs by Project X: #3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

XHTTP: Beyond REALITY: #4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-06-07 13:37:24 +00:00
Meow
402067d281 Tests: Fix TestCommanderListHandlers (#4789) 2025-06-07 13:24:39 +00:00
patterniha
97fdcb4228 New feature: Happy Eyeballs (RFC 8305) (#4667)
Closes https://github.com/XTLS/Xray-core/issues/4473
2025-06-07 13:20:06 +00:00
RPRX
bfbccc2721 Update github.com/xtls/reality to 20250607105625
https://github.com/XTLS/Xray-core/issues/4778#issuecomment-2952355740

https://github.com/XTLS/Xray-core/issues/4741#issuecomment-2919838784
2025-06-07 11:22:12 +00:00
lhear
f67c70c4a2 README.md: Add SimpleXray to Android Clients (#4761) 2025-06-07 09:49:13 +00:00
vpainless
83e5fa4f3c README.md: Add VPainLess to One Click (#4782)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-06-07 09:33:20 +00:00
patterniha
4e1a6f0fd1 Sniffer-destOverride: Remove fakedns+others option (#4739)
https://github.com/XTLS/Xray-core/pull/4726#issuecomment-2886494540
2025-06-07 09:01:57 +00:00
RPRX
ab0b9a6220 REALITY practice: Detect & imitate target's post-handshake records
Fixes https://github.com/XTLS/Xray-core/issues/4778
2025-06-07 08:59:39 +00:00
yuhan6665
b80e319655 README.md: Add Amnezia VPN to Others (#4718) 2025-06-06 02:04:17 +00:00
patterniha
6232e230d9 DNS New Features: disableCache, finalQuery, unexpectedIPs, "*", UseSystem-queryStrategy, useSystemHosts (#4666) 2025-06-06 01:59:16 +00:00
Aubrey Yang
028e1114e6 Outbound: Fix sendthrough srcip precheck (#4750)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-06-06 01:54:15 +00:00
Sergey Gorbunov
af7a76da67 API: Add ListInbounds and ListOutbounds (#4723) 2025-06-06 01:51:48 +00:00
Meow
d44c78b819 Workflows: Refactor docker (#4738)
* Workflows: Rewrite Docker image build process

* Docker: Merge Multi-Arch Manifests
2025-06-06 01:50:05 +00:00
Tamim Hossain
d0c80fc80d Core: Export the running bool (#4775) 2025-06-06 01:48:19 +00:00
dependabot[bot]
1f49bfc6a5 Bump golang.org/x/net from 0.40.0 to 0.41.0 (#4786)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.40.0 to 0.41.0.
- [Commits](https://github.com/golang/net/compare/v0.40.0...v0.41.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.41.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-06-06 01:46:30 +00:00
dependabot[bot]
dd52732e1f Bump google.golang.org/grpc from 1.72.2 to 1.73.0 (#4783)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.72.2 to 1.73.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.72.2...v1.73.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.73.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-06-06 01:46:16 +00:00
dependabot[bot]
4af53ab364 Bump google.golang.org/grpc from 1.72.1 to 1.72.2 (#4763)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.72.1 to 1.72.2.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.72.1...v1.72.2)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.72.2
  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-06-04 13:51:39 +00:00
风扇滑翔翼
462bc3cfba BurstObservatory: Fix nil panic when pingConfig is missing (#4757)
Fixes https://github.com/XTLS/Xray-core/issues/4756
2025-06-04 12:37:43 +00:00
yuhan6665
84c8e24a6c Update reality to 20250527 2025-05-26 21:55:30 -04:00
dependabot[bot]
9fe4ee75b7 Bump github.com/quic-go/quic-go from 0.51.0 to 0.52.0 (#4752)
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.51.0 to 0.52.0.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.51.0...v0.52.0)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-version: 0.52.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-05-26 01:33:50 +00:00
yiguodev
f1116cee60 README.md: Rename FoXray to OneXray in GUI Clients (#4754)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-05-26 01:31:19 +00:00
RPRX
800b8b50cc v25.5.16
Announcement of NFTs by Project X: #3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

XHTTP: Beyond REALITY: #4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-05-16 12:18:34 +00:00
风扇滑翔翼
ef1c165cc5 QUIC sniffer: Fix potential slice panic (#4732)
Fixes https://github.com/XTLS/Xray-core/issues/3914#issuecomment-2853392827
2025-05-16 12:08:29 +00:00
patterniha
bb0e561caf Sniffer: Fix potential infinite loop (#4726)
Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2025-05-16 12:04:54 +00:00
RPRX
09d84c42e9 REALITY protocol: Remove ChaCha20-Poly1305 support for REALITY's session id auth
https://github.com/XTLS/Xray-core/pull/3813#issuecomment-2885686468

https://github.com/XTLS/REALITY/pull/4#issuecomment-2885815235

Reverts 65b467e448
2025-05-16 07:29:05 +00:00
RPRX
7ddc4a2525 REALITY practice: Support X25519MLKEM768 for TLS' communication
https://github.com/XTLS/Xray-core/pull/3813#issuecomment-2873889724
2025-05-16 04:08:38 +00:00
patterniha
882975ce5a DNS Host: Removing code that was not being executed and should not be executed. (#4721)
* Removing a piece of code that was not being executed and should not be executed.

* Remove d.dns

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2025-05-15 10:15:03 -04:00
dependabot[bot]
30bf7be429 Bump google.golang.org/grpc from 1.72.0 to 1.72.1 (#4729)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.72.0 to 1.72.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.72.0...v1.72.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.72.1
  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-05-15 10:12:49 -04:00
dependabot[bot]
c53c1bfc51 Bump github.com/refraction-networking/utls from 1.7.2 to 1.7.3 (#4730)
Bumps [github.com/refraction-networking/utls](https://github.com/refraction-networking/utls) from 1.7.2 to 1.7.3.
- [Release notes](https://github.com/refraction-networking/utls/releases)
- [Commits](https://github.com/refraction-networking/utls/compare/v1.7.2...v1.7.3)

---
updated-dependencies:
- dependency-name: github.com/refraction-networking/utls
  dependency-version: 1.7.3
  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-05-15 10:11:39 -04:00
dependabot[bot]
72170d8b6b Bump github.com/vishvananda/netlink from 1.3.0 to 1.3.1 (#4719)
Bumps [github.com/vishvananda/netlink](https://github.com/vishvananda/netlink) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/vishvananda/netlink/releases)
- [Commits](https://github.com/vishvananda/netlink/compare/v1.3.0...v1.3.1)

---
updated-dependencies:
- dependency-name: github.com/vishvananda/netlink
  dependency-version: 1.3.1
  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-05-11 20:55:45 -04:00
dependabot[bot]
e9b3c53a0d Bump github.com/refraction-networking/utls from 1.7.1 to 1.7.2 (#4710)
Bumps [github.com/refraction-networking/utls](https://github.com/refraction-networking/utls) from 1.7.1 to 1.7.2.
- [Release notes](https://github.com/refraction-networking/utls/releases)
- [Commits](https://github.com/refraction-networking/utls/compare/v1.7.1...v1.7.2)

---
updated-dependencies:
- dependency-name: github.com/refraction-networking/utls
  dependency-version: 1.7.2
  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-05-09 10:32:00 -04:00
dependabot[bot]
7afed1e74d Bump lukechampine.com/blake3 from 1.4.0 to 1.4.1 (#4709)
Bumps [lukechampine.com/blake3](https://github.com/lukechampine/blake3) from 1.4.0 to 1.4.1.
- [Commits](https://github.com/lukechampine/blake3/compare/v1.4.0...v1.4.1)

---
updated-dependencies:
- dependency-name: lukechampine.com/blake3
  dependency-version: 1.4.1
  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-05-09 10:31:28 -04:00
dependabot[bot]
6ed636840b Bump actions/github-script from 6 to 7 (#4708)
Bumps [actions/github-script](https://github.com/actions/github-script) from 6 to 7.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '7'
  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-05-09 10:29:52 -04:00
Meow
f0dfbc2e66 Workflows: Ensure Geodat exists (#4680) 2025-05-08 15:44:54 -04:00
dependabot[bot]
0d0fe7ef7a Bump github.com/miekg/dns from 1.1.65 to 1.1.66 (#4707)
Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.65 to 1.1.66.
- [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release)
- [Commits](https://github.com/miekg/dns/compare/v1.1.65...v1.1.66)

---
updated-dependencies:
- dependency-name: github.com/miekg/dns
  dependency-version: 1.1.66
  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-05-08 14:48:12 -04:00
patterniha
59aa5e1b88 DNS: temporary appending hosts results (#4702) 2025-05-06 18:11:08 -04:00
dependabot[bot]
3e52f73e3c Bump golang.org/x/net from 0.39.0 to 0.40.0 (#4698)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.39.0 to 0.40.0.
- [Commits](https://github.com/golang/net/compare/v0.39.0...v0.40.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  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-05-06 17:56:07 -04:00
dependabot[bot]
d4ca42715a Bump github.com/pires/go-proxyproto from 0.8.0 to 0.8.1 (#4695)
Bumps [github.com/pires/go-proxyproto](https://github.com/pires/go-proxyproto) from 0.8.0 to 0.8.1.
- [Release notes](https://github.com/pires/go-proxyproto/releases)
- [Commits](https://github.com/pires/go-proxyproto/compare/v0.8.0...v0.8.1)

---
updated-dependencies:
- dependency-name: github.com/pires/go-proxyproto
  dependency-version: 0.8.1
  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-05-06 17:55:42 -04:00
A1lo
c847c21f3b Workflows: Authenticating the GitHub API call with GitHub token (#4703) 2025-05-06 17:54:14 -04:00
RPRX
87ab8e5128 v25.4.30
Announcement of NFTs by Project X: #3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

XHTTP: Beyond REALITY: #4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-04-30 04:40:58 +00:00
patterniha
54c6513fd4 DNS: Extend hosts Abilities (#4673) 2025-04-30 04:32:52 +00:00
Aubrey Yang
5e6a5ae01d Improve random IP compatibility: support IPv4, add srcip option, and sync client source IP via sendthrough (#4671) 2025-04-30 04:32:02 +00:00
Pk-web6936
19ba9cbe91 Chore: Update gVisor to the latest version; Fmt .go files (#4663) 2025-04-29 11:32:23 +00:00
j2rong4cn
16641fc4b5 Workflows: Build Android(7+) using NDK; Add Android(7+) amd64 build (#4664)
Makes net.LookupIP() work on Android builds
2025-04-29 11:29:52 +00:00
patterniha
aa4134f4a6 DNS: Fix some bugs; Refactors; Optimizations (#4659) 2025-04-29 08:39:57 +00:00
patterniha
1c4e246788 Sockopt: Fix some domainStrategy & dialerProxy bugs (#4661) 2025-04-29 08:33:36 +00:00
j2rong4cn
d9ebb9b2dc QUIC sniffer: Optimize the code (#4655)
Based on 2eed70e17d
2025-04-29 08:04:04 +00:00
RPRX
2eed70e17d buffer.go: Ensure extended part by Extend() & Resize() are all-zero
https://github.com/XTLS/Xray-core/pull/4655#issuecomment-2837693439
2025-04-29 07:33:37 +00:00
j2rong4cn
58c48664e2 QUIC sniffer: Full support for handling multiple initial packets (#4642)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
Co-authored-by: Vigilans <vigilans@foxmail.com>
Co-authored-by: Shelikhoo <xiaokangwang@outlook.com>
Co-authored-by: dyhkwong <50692134+dyhkwong@users.noreply.github.com>
2025-04-28 10:03:03 +00:00
yuhan6665
a608c5a1db uTLS: Add new fingerprints
PSK extension, Post-Quantum Key Agreement, ML-KEM
2025-04-26 12:31:41 -04:00
dependabot[bot]
0dd74cf072 Bump github.com/refraction-networking/utls from 1.6.7 to 1.7.1
Bumps [github.com/refraction-networking/utls](https://github.com/refraction-networking/utls) from 1.6.7 to 1.7.1.
- [Release notes](https://github.com/refraction-networking/utls/releases)
- [Commits](https://github.com/refraction-networking/utls/compare/v1.6.7...v1.7.1)

---
updated-dependencies:
- dependency-name: github.com/refraction-networking/utls
  dependency-version: 1.7.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-25 00:14:52 +00:00
peter zhang
922ae98a4a DNS log: Optimize IP address display (#4630) 2025-04-23 03:10:20 +00:00
dependabot[bot]
800b33c626 Bump github.com/quic-go/quic-go from 0.50.1 to 0.51.0 (#4634)
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.50.1 to 0.51.0.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Changelog](https://github.com/quic-go/quic-go/blob/master/Changelog.md)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.50.1...v0.51.0)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-version: 0.51.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-04-23 02:58:09 +00:00
dependabot[bot]
0563c9750e Bump google.golang.org/grpc from 1.71.1 to 1.72.0 (#4640)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.71.1 to 1.72.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.71.1...v1.72.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.72.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-04-23 02:57:57 +00:00
xqzr
907a182f64 Sockopt: Fix Windows IP_MULTICAST_IF & IPV6_MULTICAST_IF (#4627)
Completes https://github.com/XTLS/Xray-core/pull/4568
2025-04-19 09:30:12 +00:00
RPRX
0995fa41fe XHTTP client: Set packet-up as the default mode (auto) when using TLS
https://t.me/projectXtls/929
2025-04-18 06:18:51 +00:00
RPRX
2916b1b977 README.md: Rename Clash.Meta to mihomo in Others 2025-04-18 03:57:14 +00:00
RPRX
8212325980 README.md: Add Loon to Others
https://t.me/LoonNews/1662
2025-04-18 03:18:38 +00:00
风扇滑翔翼
5f3ae64f0c Sockopt: Allow customSockopt work for Windows & Darwin (#4576)
* Sockopt: Add custom sockopt on Windows & Darwin

* fix windows udp by the way

* use resolved addr

https://github.com/XTLS/Xray-core/pull/4504#issuecomment-2769153797
2025-04-18 02:30:47 +00:00
Cl-He-O
7a2f42f8d5 Fix issues related to android client (#4616)
* WireGuard: Fix tunnel not closed

* Dialer: Apply controllers in lc.Control
2025-04-14 11:08:26 -04:00
dependabot[bot]
53552d73cc Bump github.com/cloudflare/circl from 1.6.0 to 1.6.1 (#4602)
Bumps [github.com/cloudflare/circl](https://github.com/cloudflare/circl) from 1.6.0 to 1.6.1.
- [Release notes](https://github.com/cloudflare/circl/releases)
- [Commits](https://github.com/cloudflare/circl/compare/v1.6.0...v1.6.1)

---
updated-dependencies:
- dependency-name: github.com/cloudflare/circl
  dependency-version: 1.6.1
  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-04-13 00:55:00 -04:00
dependabot[bot]
19e884bebf Bump golang.org/x/net from 0.38.0 to 0.39.0 (#4598)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.38.0 to 0.39.0.
- [Commits](https://github.com/golang/net/compare/v0.38.0...v0.39.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.39.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-04-08 11:17:36 -04:00
xqzr
78a1e37e89 Sockopt: Use Windows syscall (#4581) 2025-04-07 11:50:07 -04:00
Ragavendaran Puliyadi
2d3126b752 Config: Implement missing MarshalJSON for structs having custom UnmarshalJSON (#4585)
* conf: implement MarshalJSON for FakeDNSConfig

* conf: Rewrite MarshalJSON for PortList
decouple PortRange from PortList.

* conf: implement MarshalJSON for HostAddress

* conf: Add MarshalJSON comments and use pointers.
2025-04-07 11:46:49 -04:00
dependabot[bot]
0dbab7bcd7 Bump golang.org/x/crypto from 0.36.0 to 0.37.0 (#4597)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.36.0 to 0.37.0.
- [Commits](https://github.com/golang/crypto/compare/v0.36.0...v0.37.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.37.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-04-07 10:56:44 -04:00
dependabot[bot]
ab15822ee3 Bump golang.org/x/sync from 0.12.0 to 0.13.0 (#4596)
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.12.0 to 0.13.0.
- [Commits](https://github.com/golang/sync/compare/v0.12.0...v0.13.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-version: 0.13.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-04-07 10:56:34 -04:00
dependabot[bot]
8b2fe32a33 Bump golang.org/x/sys from 0.31.0 to 0.32.0 (#4595)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.31.0 to 0.32.0.
- [Commits](https://github.com/golang/sys/compare/v0.31.0...v0.32.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-version: 0.32.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-04-07 10:52:54 -04:00
dependabot[bot]
dd2a40e64d Bump google.golang.org/grpc from 1.71.0 to 1.71.1 (#4577)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.71.0 to 1.71.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.71.0...v1.71.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  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-04-07 10:30:33 -04:00
dependabot[bot]
33a4336b1d Bump github.com/miekg/dns from 1.1.64 to 1.1.65 (#4594)
Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.64 to 1.1.65.
- [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release)
- [Commits](https://github.com/miekg/dns/compare/v1.1.64...v1.1.65)

---
updated-dependencies:
- dependency-name: github.com/miekg/dns
  dependency-version: 1.1.65
  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-04-07 10:29:49 -04:00
RPRX
ab5d7cf3d2 v25.3.31
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-03-31 12:33:38 +00:00
xqzr
2abeda9c42 Sockopt: Allow listen v6only work for Windows & Darwin (#4571)
Completes https://github.com/XTLS/Xray-core/pull/1677

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-03-31 12:22:16 +00:00
92613hjh
63eb0539b3 Sockopt: Fix Darwin (macOS, iOS...) UDP interface bind (#4530)
Fixes https://github.com/XTLS/Xray-core/issues/4007
2025-03-31 11:19:06 +00:00
RPRX
8284a0ef8f Dialer: Do not use ListenSystemPacket() when dialing UDP
https://github.com/XTLS/Xray-core/pull/4530#issuecomment-2765744961
2025-03-31 10:09:33 +00:00
Ilya Gulya
17207fc5e4 WireGuard: Improve config error handling; Prevent panic in case of errors during server initialization (#4566)
https://github.com/XTLS/Xray-core/pull/4566#issuecomment-2764779273
2025-03-30 22:55:21 +00:00
xqzr
52a2c63682 Sockopt: Fix Windows Multicast interface bind (#4568)
https://github.com/XTLS/Xray-core/pull/4568#issuecomment-2763492336
2025-03-30 20:51:36 +00:00
patterniha
1685c61e44 DNS: Use cache for NXDOMAIN (rcode 3 error) (#4560)
https://github.com/XTLS/Xray-core/pull/4558#issuecomment-2759007356
2025-03-29 14:29:48 +00:00
Pk-web6936
681e943848 Chore: Optimize .gitignore (#4564) 2025-03-29 13:46:26 +00:00
dependabot[bot]
8530bdb0de Bump golang.org/x/net from 0.37.0 to 0.38.0 (#4561)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.37.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.37.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  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-03-29 13:42:20 +00:00
风扇滑翔翼
29e37e8a82 HTTP inbound: Directly forward plain HTTP 1xx response header (#4547)
Fixes https://github.com/XTLS/Xray-core/issues/4545
2025-03-26 12:30:48 +00:00
dependabot[bot]
e129b1e90d Bump google.golang.org/protobuf from 1.36.5 to 1.36.6 (#4549)
Bumps google.golang.org/protobuf from 1.36.5 to 1.36.6.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  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-03-26 12:20:17 +00:00
patterniha
5922caff89 DNS: Add expectedIPs as an alias of expectIPs (#4551)
https://github.com/XTLS/Xray-core/discussions/1903#discussioncomment-5543921
2025-03-26 12:18:58 +00:00
Meo597
4afe2d0cff DNS: Support returning upstream TTL to clients (#4526)
Closes https://github.com/XTLS/Xray-core/issues/4527
2025-03-24 13:33:56 +00:00
patterniha
2d3210e4b8 Env: Add XRAY_LOCATION_CERT variable (#4536)
https://github.com/XTLS/Xray-core/issues/4531#issuecomment-2746155941

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

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-03-24 13:02:35 +00:00
dependabot[bot]
673a9ae063 Bump github.com/quic-go/quic-go from 0.50.0 to 0.50.1 (#4539)
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.50.0 to 0.50.1.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Changelog](https://github.com/quic-go/quic-go/blob/master/Changelog.md)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.50.0...v0.50.1)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  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-03-24 09:49:58 +00:00
RPRX
607c2a6d31 DNS DoH: Use EDNS0 with 100-300 padding by default (body padding)
https://github.com/XTLS/Xray-core/pull/4516#issuecomment-2744093003
2025-03-21 20:13:09 +00:00
风扇滑翔翼
b585b26f29 Sockopt: Fix Windows UDP interface bind; Allow customSockopt work for UDP (#4504) 2025-03-21 10:48:46 +00:00
patterniha
1067171e6a DNS: Add timeoutMs for DnsServerObject (#4523) 2025-03-21 10:24:58 +00:00
风扇滑翔翼
86a225cda1 DNS: Retry with EDNS0 when response is truncated (#4516) 2025-03-21 08:58:51 +00:00
风扇滑翔翼
6f8e253dec DNS: Add tag for DnsServerObject (#4515)
Closes https://github.com/XTLS/Xray-core/issues/4505
2025-03-21 08:58:24 +00:00
patterniha
6a211a0bb9 DNS: Add allowUnexpectedIPs for DnsServerObject (#4497)
Closes https://github.com/XTLS/Xray-core/issues/4424
2025-03-20 12:09:02 +00:00
风扇滑翔翼
335845a9b2 DNS: Ensure order for DNS server match (#4510)
Fixes https://github.com/XTLS/Xray-core/issues/4508
2025-03-20 11:21:21 +00:00
风扇滑翔翼
b70d0abebe API: Fix data race in online ipList (#4513)
Fixes https://github.com/XTLS/Xray-core/issues/4512
2025-03-20 11:12:36 +00:00
dependabot[bot]
3d21128159 Bump github.com/miekg/dns from 1.1.63 to 1.1.64 (#4506)
Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.63 to 1.1.64.
- [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release)
- [Commits](https://github.com/miekg/dns/compare/v1.1.63...v1.1.64)

---
updated-dependencies:
- dependency-name: github.com/miekg/dns
  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-03-20 11:06:51 +00:00
Nikita Korotaev
eca99771ca README.md: Add Remnawave to Web Panels (#4498) 2025-03-20 10:50:43 +00:00
RPRX
2cba2c4d59 v25.3.6
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-03-06 13:50:15 +00:00
Happ-dev
306fa51475 README.md: Add Happ to iOS clients (#4465) 2025-03-06 12:27:25 +00:00
dependabot[bot]
6d6f1c6967 Bump golang.org/x/net from 0.36.0 to 0.37.0 (#4469)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.36.0 to 0.37.0.
- [Commits](https://github.com/golang/net/compare/v0.36.0...v0.37.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  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-03-06 10:59:49 +00:00
dependabot[bot]
152959824f Bump google.golang.org/grpc from 1.70.0 to 1.71.0 (#4463)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.70.0 to 1.71.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.70.0...v1.71.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  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-03-05 09:23:38 +00:00
dependabot[bot]
a977b6357e Bump golang.org/x/net from 0.35.0 to 0.36.0 (#4462)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.35.0 to 0.36.0.
- [Commits](https://github.com/golang/net/compare/v0.35.0...v0.36.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  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-03-05 09:23:31 +00:00
RPRX
16eee1b89c Mux server: Clone the session.Content attached on ctx as well
https://github.com/XTLS/Xray-core/pull/3908#issuecomment-2696911994

Fixes https://github.com/XTLS/Xray-core/pull/3904 for good
2025-03-04 11:01:03 +00:00
RPRX
dde0a4f272 REALITY config: Add password as an alias of publicKey
https://github.com/XTLS/Xray-core/issues/4458#issuecomment-2696181682
2025-03-04 05:29:03 +00:00
RPRX
e15dff94b5 v25.3.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-03-03 15:30:22 +00:00
RPRX
e466b0497c DNS DoH: Use Chrome's fingerprint & keepAlivePeriod, Add header padding by default
https://github.com/XTLS/Xray-core/discussions/4430#discussioncomment-12374292
2025-03-03 14:45:12 +00:00
j3l11234
b9cb93d3c2 Sockopt: Add addressPortStrategy (query SRV or TXT) (#4416)
Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2025-03-02 13:07:55 +00:00
RPRX
8d46f7e14c TLS fingerprints: Refine "random" & "randomized", Add "randomizednoalpn"
https://github.com/XTLS/Xray-core/issues/4436#issuecomment-2687801214
2025-03-02 13:02:47 +00:00
A1lo
4b616f5cd0 XHTTP server: Switch to Go 1.24 native h2c support (#4451) 2025-03-02 13:02:40 +00:00
dependabot[bot]
06b4a7ce4d Bump lukechampine.com/blake3 from 1.3.0 to 1.4.0 (#4439)
Bumps [lukechampine.com/blake3](https://github.com/lukechampine/blake3) from 1.3.0 to 1.4.0.
- [Commits](https://github.com/lukechampine/blake3/compare/v1.3.0...v1.4.0)

---
updated-dependencies:
- dependency-name: lukechampine.com/blake3
  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-02-25 13:26:54 +00:00
dependabot[bot]
4c12e1686b Bump golang.org/x/crypto from 0.33.0 to 0.35.0 (#4438)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.33.0 to 0.35.0.
- [Commits](https://github.com/golang/crypto/compare/v0.33.0...v0.35.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  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-02-25 13:26:32 +00:00
dependabot[bot]
225d151cd3 Bump github.com/google/go-cmp from 0.6.0 to 0.7.0 (#4433)
Bumps [github.com/google/go-cmp](https://github.com/google/go-cmp) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/google/go-cmp/releases)
- [Commits](https://github.com/google/go-cmp/compare/v0.6.0...v0.7.0)

---
updated-dependencies:
- dependency-name: github.com/google/go-cmp
  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-02-25 13:26:00 +00:00
风扇滑翔翼
d451078e72 Chore: Fix tests (#4440) 2025-02-25 13:11:37 +00:00
RPRX
ce2384cccc Use Go 1.24 2025-02-25 11:35:25 +00:00
RPRX
be43f66b63 v25.2.21
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-02-21 07:58:31 +00:00
dependabot[bot]
71a6d89c23 Bump github.com/quic-go/quic-go from 0.49.0 to 0.50.0 (#4420)
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.49.0 to 0.50.0.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Changelog](https://github.com/quic-go/quic-go/blob/master/Changelog.md)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.49.0...v0.50.0)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  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-02-21 06:34:33 +00:00
lastrise
89792aee9d Outbound: Add outbound sendThrough origin behavior (#4349)
* added support of sending through origin for outbounds

* added strings package import

* usage of net.SplitHostPort instead of manual splitting

---------

Co-authored-by: poly <poly@>
2025-02-20 15:15:59 -05:00
RPRX
b786a50aee XHTTP server: Fix stream-up "single POST problem", Use united httpServerConn instead of recover()
https://github.com/XTLS/Xray-core/issues/4373#issuecomment-2671795675

https://github.com/XTLS/Xray-core/issues/4406#issuecomment-2668041926
2025-02-20 16:28:06 +00:00
风扇滑翔翼
b38a53e629 UDS: Use UnixListenerWrapper & UnixConnWrapper (#4413)
Fixes https://github.com/XTLS/Xray-core/issues/4411

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-02-19 11:31:29 +00:00
RPRX
52381a3c03 v25.2.18
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-02-18 11:55:07 +00:00
风扇滑翔翼
4b01eb4398 Metrics: Add direct listen (#4409) 2025-02-18 11:32:48 +00:00
RPRX
c5de08bea6 XHTTP client: Revert "Add back minimal path padding for compatibility"
Reverts efdc70fbf7
2025-02-18 11:11:36 +00:00
RPRX
8cb63db6c0 XHTTP server: Set remoteAddr & localAddr correctly
Completes 22c50a70c6
2025-02-18 10:50:50 +00:00
yuhan6665
eef74b2c7d XTLS: More separate uplink/downlink flags for splice copy (#4407)
- In 03131c72db new flags were added for uplink/downlink, but that was not suffcient
- Now that the traffic state contains all possible info
- Each inbound and outbound is responsible to set their own CanSpliceCopy flag. Note that this also open up more splice usage. E.g. socks in -> freedom out
- Fixes https://github.com/XTLS/Xray-core/issues/4033
2025-02-18 08:37:52 +00:00
Bill Zhong
a1714cc4ce API: Improve cli usage descriptions (#4401) 2025-02-18 08:36:39 +00:00
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇
958b13ebb5 Build: End of the easily mistaken 'Makefile' (#4395)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-02-18 08:33:05 +00:00
风扇滑翔翼
22c50a70c6 UDS: Make all remote addr 0.0.0.0 (#4390)
https://github.com/XTLS/Xray-core/pull/4389#issuecomment-2656360673

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-02-13 14:01:33 +00:00
yiguous
94c7970fd6 Config: Correctly marshal PortList and NameServerConfig to JSON (#4386) 2025-02-12 14:55:16 +00:00
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇
a71762b5da Workflows: Fix Actions' manual dispatch for assets update (#4378) 2025-02-11 13:19:03 +00:00
dependabot[bot]
5033cbceea Bump golang.org/x/net from 0.34.0 to 0.35.0 (#4382)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.34.0 to 0.35.0.
- [Commits](https://github.com/golang/net/compare/v0.34.0...v0.35.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  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-02-11 13:14:22 +00:00
RPRX
dcd7e92c45 XHTTP server: Finish stream-up's HTTP POST when its request.Body is closed
https://github.com/XTLS/Xray-core/issues/4373#issuecomment-2647908310

Fixes https://github.com/XTLS/Xray-core/issues/4373
2025-02-10 13:56:13 +00:00
dependabot[bot]
2d7ca4a6a6 Bump golang.org/x/crypto from 0.32.0 to 0.33.0 (#4375)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.32.0 to 0.33.0.
- [Commits](https://github.com/golang/crypto/compare/v0.32.0...v0.33.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  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-02-10 08:30:42 +00:00
RPRX
925a985cc0 Commands: Use ".crt" & ".key" suffixes when generating TLS certificates
https://github.com/XTLS/Xray-core/issues/4313#issuecomment-2645844058
2025-02-08 18:29:54 +00:00
RPRX
613c63b165 DNS DoH h2c Remote: Add verifyPeerCertInNames "fromMitm" support
https://github.com/XTLS/Xray-core/issues/4313#issuecomment-2645838663
2025-02-08 18:05:41 +00:00
RPRX
d4c7cd02fd MITM freedom RAW TLS: Allow "fromMitm" to be written at any position in verifyPeerCertInNames, Add checking for alpn "fromMitm"
https://github.com/XTLS/Xray-core/issues/4348#issuecomment-2643340434
2025-02-08 12:11:25 +00:00
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇
db5f18b98c Workflows: Reduce Geodata update frequency (#4369) 2025-02-08 08:07:46 +00:00
RPRX
c81d8e488a Geofiles: Switch to Loyalsoldier's v2ray-rules-dat
https://github.com/XTLS/Xray-core/issues/4348#issuecomment-2643351198
2025-02-08 04:47:43 +00:00
Daniel Lavrushin
1d9e6bc2f3 README.md: Add XrayUI to Asuswrt-Merlin clients (#4355) 2025-02-07 13:57:52 +00:00
Artur Melanchyk
ae327eb7e6 Chore: Make some Maps into real Sets (#4362) 2025-02-07 13:48:33 +00:00
Maxim Plotnikov
e893fa1828 API: Add user IPs and access times tracking (#4360) 2025-02-07 12:19:47 +00:00
dependabot[bot]
1982c2366e Bump google.golang.org/protobuf from 1.36.4 to 1.36.5 (#4363)
Bumps google.golang.org/protobuf from 1.36.4 to 1.36.5.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  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-02-07 10:09:50 +00:00
RPRX
117de1fd3c MITM freedom RAW TLS: Report website with unexpected Negotiated Protocol / invalid Domain Fronting certificate
https://github.com/XTLS/Xray-core/issues/4348#issuecomment-2639965524

Needs `"alpn": ["fromMitm"]` / `"verifyPeerCertInNames": ["fromMitm", ...]`.
2025-02-07 08:15:40 +00:00
dependabot[bot]
07c35ed52a Bump github.com/cloudflare/circl from 1.5.0 to 1.6.0 (#4352)
Bumps [github.com/cloudflare/circl](https://github.com/cloudflare/circl) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/cloudflare/circl/releases)
- [Commits](https://github.com/cloudflare/circl/compare/v1.5.0...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/cloudflare/circl
  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-02-06 11:31:13 +00:00
dependabot[bot]
e17c068821 Bump golang.org/x/sync from 0.10.0 to 0.11.0 (#4351)
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.10.0 to 0.11.0.
- [Commits](https://github.com/golang/sync/compare/v0.10.0...v0.11.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  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-02-06 11:30:58 +00:00
dependabot[bot]
88d40d6367 Bump golang.org/x/sys from 0.29.0 to 0.30.0 (#4350)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.29.0 to 0.30.0.
- [Commits](https://github.com/golang/sys/compare/v0.29.0...v0.30.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  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-02-06 11:28:02 +00:00
RPRX
527caa3711 Log: Add microseconds for golang's standard logger
Completes 5679d717ee
2025-02-06 07:50:48 +00:00
RPRX
c6a31f457c MITM: Allow using local received SNI in the outgoing serverName & verifyPeerCertInNames
https://github.com/XTLS/Xray-core/issues/4348#issuecomment-2637370175

Local received SNI was sent by browser/app.

In freedom RAW's `tlsSettings`, set `"serverName": "fromMitm"` to forward it to the real website.

In freedom RAW's `tlsSettings`, set `"verifyPeerCertInNames": ["fromMitm"]` to use all possible names to verify the certificate.
2025-02-06 07:37:30 +00:00
RPRX
9b7841178a MITM: Allow forwarding local negotiated ALPN http/1.1 to the real website
https://github.com/XTLS/Xray-core/issues/4348#issuecomment-2633656408

https://github.com/XTLS/Xray-core/issues/4348#issuecomment-2633865039

Local negotiated ALPN http/1.1 was sent by browser/app or is written in dokodemo-door RAW's `tlsSettings`.

Set `"alpn": ["fromMitm"]` in freedom RAW's `tlsSettings` to forward it to the real website.
2025-02-04 15:10:08 +00:00
RPRX
480c7d7db7 README.md: Add Project XHTTP (Persian) to Telegram
https://t.me/projectXhttp
2025-02-01 16:51:28 +00:00
auvred
c2f6c89987 Commands: Fix ambiguous printing of private x25519 key (#4343) 2025-02-01 14:45:34 +00:00
RPRX
0a8470cb14 v25.1.30
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-01-30 10:57:37 +00:00
RPRX
efdc70fbf7 XHTTP client: Add back minimal path padding for compatibility
It should be reverted in the future.
2025-01-30 10:45:12 +00:00
dependabot[bot]
f35fb08aeb Bump google.golang.org/protobuf from 1.36.3 to 1.36.4 (#4331)
Bumps google.golang.org/protobuf from 1.36.3 to 1.36.4.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  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-01-27 20:45:59 +00:00
dependabot[bot]
1bb0beaa43 Bump github.com/miekg/dns from 1.1.62 to 1.1.63 (#4330)
Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.62 to 1.1.63.
- [Changelog](https://github.com/miekg/dns/blob/master/Makefile.release)
- [Commits](https://github.com/miekg/dns/compare/v1.1.62...v1.1.63)

---
updated-dependencies:
- dependency-name: github.com/miekg/dns
  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-01-27 20:45:47 +00:00
yuhan6665
03131c72db XTLS Vision: Use separate uplink/downlink flag for direct copy (#4329)
Fixes https://github.com/XTLS/Xray-core/issues/4033
2025-01-27 20:44:33 +00:00
Sergey Kutovoy
7b59379d73 README.md: Add xray-checker to Xray Tools (#4319)
https://github.com/XTLS/Xray-core/discussions/4316
2025-01-26 15:59:34 +00:00
RPRX
a7a83624c5 Upgrade quic-go to official v0.49.0
https://github.com/quic-go/quic-go/releases/tag/v0.49.0
https://github.com/quic-go/quic-go/pull/4749
https://github.com/quic-go/quic-go/pull/4798
2025-01-26 11:47:30 +00:00
风扇滑翔翼
3a7a78ff3a UDS: Keep valid source addr (#4325)
Fixes https://github.com/XTLS/Xray-core/issues/4324
2025-01-26 11:08:36 +00:00
RPRX
5679d717ee Log: Add microseconds for all kinds of logs
https://github.com/XTLS/Xray-core/issues/4313#issuecomment-2613932895
2025-01-25 11:52:44 +00:00
RPRX
740a6b0dcd RAW: Allow setting ALPN http/1.1 for non-REALITY uTLS
https://github.com/XTLS/Xray-core/issues/4313#issuecomment-2611889517
2025-01-25 11:15:42 +00:00
RPRX
2522cfd7be DNS DoH: Add h2c Remote mode (with TLS serverNameToVerify)
https://github.com/XTLS/Xray-core/issues/4313#issuecomment-2609339864

Applies https://github.com/refraction-networking/utls/pull/161

Closes https://github.com/XTLS/Xray-core/issues/4313
2025-01-25 10:51:44 +00:00
dependabot[bot]
a0822cb440 Bump google.golang.org/grpc from 1.69.4 to 1.70.0 (#4322)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.69.4 to 1.70.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.69.4...v1.70.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  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-01-24 07:24:33 +00:00
RPRX
ca9a902213 XHTTP server: Add scStreamUpServerSecs, enabled by default (#4306)
Fixes https://github.com/XTLS/Xray-core/discussions/4113#discussioncomment-11682833
2025-01-19 13:32:07 +00:00
风扇滑翔翼
f4fd8b8fad DNS: Implement queryStrategy for "localhost" (#4303)
Fixes https://github.com/XTLS/Xray-core/issues/4302
2025-01-19 07:39:54 +00:00
rPDmYQ
14a6636a41 XHTTP client: Move x_padding into Referer header (#4298)
""Breaking"": Update the server side first, then client
2025-01-18 12:05:19 +00:00
rPDmYQ
30cb22afb1 Mixed inbound: Handle immediately closing connection gracefully (#4297)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-01-17 13:37:40 +00:00
nobody
66dd7808b6 Commands: Fix dumping merged config for XHTTP (#4290)
Fixes https://github.com/XTLS/Xray-core/issues/4287
2025-01-17 10:40:36 +00:00
dependabot[bot]
f1ff454e67 Bump google.golang.org/protobuf from 1.36.2 to 1.36.3 (#4295)
Bumps google.golang.org/protobuf from 1.36.2 to 1.36.3.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  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-01-16 13:21:31 +00:00
dependabot[bot]
4576f56ec8 Bump google.golang.org/grpc from 1.69.2 to 1.69.4 (#4288)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.69.2 to 1.69.4.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.69.2...v1.69.4)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  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-01-14 22:48:36 -05:00
dashangcun
9b1855f719 chore: fix struct field name in comment (#4284)
Signed-off-by: dashangcun <907225865@qq.com>
2025-01-14 09:56:22 -05:00
风扇滑翔翼
3e590a4eb1 Freedom: Don't use rawConn copy when using utls (#4272) 2025-01-12 14:10:39 -05:00
dependabot[bot]
ef4a3c1cae Bump google.golang.org/protobuf from 1.36.1 to 1.36.2 (#4268)
Bumps google.golang.org/protobuf from 1.36.1 to 1.36.2.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  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-01-10 02:02:00 +00:00
dependabot[bot]
5635254ebc Bump golang.org/x/net from 0.33.0 to 0.34.0 (#4262)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.33.0 to 0.34.0.
- [Commits](https://github.com/golang/net/compare/v0.33.0...v0.34.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  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-01-10 02:00:22 +00:00
RPRX
ce6c0dc690 XHTTP XMUX: Abandon client if client.Do(req) failed (#4253)
51769fdde1
2025-01-06 14:06:11 +00:00
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇
aeb12d9e3b Build: Update GeoIP/GeoSite Cache per hour (#4247) 2025-01-05 06:37:50 +00:00
Hossin Asaadi
de53a3b94e Upgrade gVisor to a newer version (#3903) 2025-01-03 15:50:23 +00:00
RPRX
2f52aa7ed8 Freedom noises: Support RawURLEncoding for "base64"
In case we want to share `noises` via sharing link.
2025-01-02 09:45:46 +00:00
GFW-knocker
ca50c9cbe6 Freedom noises: Support "hex" as type & packet (#4239)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-01-02 09:17:03 +00:00
风扇滑翔翼
33186ca5e6 Freedom noises: Change legacy variable name (#4238)
Closes https://github.com/XTLS/Xray-core/issues/4237
2025-01-02 08:21:32 +00:00
RPRX
e80ca67fee v25.1.1
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-01-01 12:03:05 +00:00
RPRX
dd4ba823f5 Workflows: Trigger all Build & Test on all branches & files
https://github.com/XTLS/Xray-core/pull/4192#issuecomment-2566960668
2025-01-01 11:51:32 +00:00
GFW-knocker
0658c9545b Freedom config: Fix noises delay (#4233)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-01-01 11:49:08 +00:00
yiguous
480eac7235 Config: Correctly marshal Int32Range to JSON (#4234)
Fixes https://github.com/XTLS/libXray/issues/62
2025-01-01 10:29:34 +00:00
RPRX
8a6a5385ff Upgrade quic-go to patched v0.48.2
* Change module path
* Add traceWroteRequest & traceGotConn
* http3: allow concurrent calls to Body.Close (https://github.com/quic-go/quic-go/pull/4798)
2025-01-01 09:23:14 +00:00
dependabot[bot]
5178dc500a Bump github.com/cloudflare/circl from 1.4.0 to 1.5.0 (#3899)
Bumps [github.com/cloudflare/circl](https://github.com/cloudflare/circl) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/cloudflare/circl/releases)
- [Commits](https://github.com/cloudflare/circl/compare/v1.4.0...v1.5.0)

---
updated-dependencies:
- dependency-name: github.com/cloudflare/circl
  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-01-01 08:22:00 +00:00
dependabot[bot]
1a1c49de36 Bump google.golang.org/grpc from 1.67.1 to 1.69.2 (#4181)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.67.1 to 1.69.2.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.67.1...v1.69.2)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  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-01-01 08:19:48 +00:00
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇
c8b17ad18d Build: Use patched newer Go version to build Windows 7 assets (#4192)
https://github.com/XTLS/go-win7
2025-01-01 08:06:35 +00:00
RPRX
4be32e99b2 v24.12.31
XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
2024-12-31 12:12:03 +00:00
RPRX
5af90684c4 Inbounds config: Add mixed as an alias of socks
https://github.com/XTLS/Xray-core/pull/3682#issuecomment-2292600144
2024-12-31 11:55:16 +00:00
RPRX
369d8944cf Sockopt config: Add penetrate for XHTTP U-D-S, Remove tcpNoDelay
Now `sockopt` can be shared via `extra`, and be replaced with upload's forcibly.

Closes https://github.com/XTLS/Xray-core/issues/4227
2024-12-31 11:10:17 +00:00
RPRX
4ce65fc74c XHTTP XMUX: cMaxLifetimeMs -> hMaxReusableSecs, Refactor default values
"xmux": {
    "maxConcurrency": "16-32",
    "maxConnections": 0,
    "cMaxReuseTimes": 0,
    "hMaxRequestTimes": "600-900",
    "hMaxReusableSecs": "1800-3000",
    "hKeepAlivePeriod": 0
}

Fixes https://github.com/XTLS/Xray-core/discussions/4113#discussioncomment-11685057
2024-12-31 10:00:19 +00:00
RPRX
93f72db9fd v24.12.28
XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
2024-12-28 13:57:23 +00:00
RPRX
ff4331a7a8 XHTTP XMUX: Increase the default value for cMaxReuseTimes
"64-128" -> "256-512"
2024-12-28 08:20:59 +00:00
RPRX
a8559a1b46 Dokodemo TPROXY: Interrupt UDP download if upload timeouts
https://github.com/XTLS/Xray-core/issues/4194#issuecomment-2564156340

Fixes https://github.com/XTLS/Xray-core/issues/4194
2024-12-28 04:06:49 +00:00
yuhan6665
42aea01fb5 Core: Add mutex to injection resolution (#4206)
* Revert "Add RequireFeaturesAsync() that works regardless order of app init"

* Add mutex to injection resolution

- Turns out we already support async DI resolution regardless of feature ordering
Previous code contain a race condition causing some resolution is lost
- Note that the new mutex cover s.pendingResolutions and s.features
but must not cover callbackResolution() due to deadlock
- Refactor some method names and simplify code

* Add OptionalFeatures injection

For example OptionalFeatures() is useful for fakedns module
2024-12-26 12:55:12 +00:00
dependabot[bot]
a7909f8671 Bump google.golang.org/protobuf from 1.36.0 to 1.36.1 (#4203)
Bumps google.golang.org/protobuf from 1.36.0 to 1.36.1.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  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>
2024-12-24 13:03:17 +00:00
leo
b287d6419b chore: use errors.New to replace fmt.Errorf with no parameters (#4204)
Signed-off-by: RiceChuan <lc582041246@gmail.com>
2024-12-24 00:17:00 -05:00
dependabot[bot]
d54d20abea Bump golang.org/x/net from 0.32.0 to 0.33.0 (#4182)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.32.0 to 0.33.0.
- [Commits](https://github.com/golang/net/compare/v0.32.0...v0.33.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  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>
2024-12-22 09:21:06 +00:00
maoxikun
868799ef04 DNS: Always use a DNS Message ID of 0 for DoH and DoQ (#4193)
Co-authored-by: dyhkwong <50692134+dyhkwong@users.noreply.github.com>
2024-12-22 09:19:09 +00:00
RPRX
db934f0832 XHTTP client: Merge Open* into OpenStream(), and more
https://github.com/XTLS/Xray-core/issues/4148#issuecomment-2557066988
2024-12-20 14:35:33 +00:00
RPRX
53b04d560b XHTTP client: Make H3 httptrace work on v2rayNG
Introduced in https://github.com/XTLS/Xray-core/pull/4150
2024-12-20 07:19:41 +00:00
RPRX
1410b6335b XHTTP XMUX: Fix OpenUsage never gets reduced
Introduced in https://github.com/XTLS/Xray-core/pull/4163
2024-12-20 06:19:10 +00:00
257 changed files with 9716 additions and 4566 deletions

View File

@@ -1,28 +1,62 @@
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM golang:alpine AS build
# syntax=docker/dockerfile:latest
FROM --platform=$BUILDPLATFORM golang:latest AS build
# Build xray-core
WORKDIR /src
COPY . .
ARG TARGETOS
ARG TARGETARCH
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
ADD https://github.com/v2fly/geoip/releases/latest/download/geoip.dat /v2fly/geoip.dat
ADD https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat /v2fly/geosite.dat
ADD https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat /loyalsoldier/geoip.dat
ADD https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat /loyalsoldier/geosite.dat
# chainguard/static contains only tzdata and ca-certificates, can be built with multiarch static binaries.
FROM --platform=linux/amd64 chainguard/static:latest
WORKDIR /var/log/xray
COPY .github/docker/files/config.json /etc/xray/config.json
COPY --from=build --chmod=755 /src/xray /usr/bin/xray
# Download geodat into a staging directory
ADD https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geoip.dat /tmp/geodat/geoip.dat
ADD https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geosite.dat /tmp/geodat/geosite.dat
USER root
WORKDIR /root
VOLUME /etc/xray
ARG TZ=Asia/Shanghai
RUN mkdir -p /tmp/empty
# Create config files with empty JSON content
RUN mkdir -p /tmp/usr/local/etc/xray
RUN cat <<EOF >/tmp/usr/local/etc/xray/00_log.json
{
"log": {
"error": "/var/log/xray/error.log",
"loglevel": "warning",
"access": "none",
"dnsLog": false
}
}
EOF
RUN echo '{}' >/tmp/usr/local/etc/xray/01_api.json
RUN echo '{}' >/tmp/usr/local/etc/xray/02_dns.json
RUN echo '{}' >/tmp/usr/local/etc/xray/03_routing.json
RUN echo '{}' >/tmp/usr/local/etc/xray/04_policy.json
RUN echo '{}' >/tmp/usr/local/etc/xray/05_inbounds.json
RUN echo '{}' >/tmp/usr/local/etc/xray/06_outbounds.json
RUN echo '{}' >/tmp/usr/local/etc/xray/07_transport.json
RUN echo '{}' >/tmp/usr/local/etc/xray/08_stats.json
RUN echo '{}' >/tmp/usr/local/etc/xray/09_reverse.json
# Create log files
RUN mkdir -p /tmp/var/log/xray && touch \
/tmp/var/log/xray/access.log \
/tmp/var/log/xray/error.log
# Build finally image
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 /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=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=755 /tmp/empty /var/log/xray
COPY --from=build --chown=65532:65532 --chmod=600 /tmp/var/log/xray/*.log /var/log/xray/
VOLUME /usr/local/etc/xray
VOLUME /var/log/xray
ARG TZ=Etc/UTC
ENV TZ=$TZ
ENTRYPOINT [ "/usr/bin/xray" ]
CMD [ "-confdir", "/etc/xray/" ]
ARG flavor=v2fly
COPY --from=build --chmod=644 /$flavor /usr/share/xray
ENTRYPOINT [ "/usr/local/bin/xray" ]
CMD [ "-confdir", "/usr/local/etc/xray/" ]

71
.github/docker/Dockerfile.usa vendored Normal file
View File

@@ -0,0 +1,71 @@
# syntax=docker/dockerfile:latest
FROM --platform=$BUILDPLATFORM golang:latest AS build
# Build xray-core
WORKDIR /src
COPY . .
ARG TARGETOS
ARG TARGETARCH
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
# Download geodat into a staging directory
ADD https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geoip.dat /tmp/geodat/geoip.dat
ADD https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geosite.dat /tmp/geodat/geosite.dat
RUN mkdir -p /tmp/empty
# Create config files with empty JSON content
RUN mkdir -p /tmp/usr/local/etc/xray
RUN cat <<EOF >/tmp/usr/local/etc/xray/00_log.json
{
"log": {
"error": "/var/log/xray/error.log",
"loglevel": "warning",
"access": "none",
"dnsLog": false
}
}
EOF
RUN echo '{}' >/tmp/usr/local/etc/xray/01_api.json
RUN echo '{}' >/tmp/usr/local/etc/xray/02_dns.json
RUN echo '{}' >/tmp/usr/local/etc/xray/03_routing.json
RUN echo '{}' >/tmp/usr/local/etc/xray/04_policy.json
RUN echo '{}' >/tmp/usr/local/etc/xray/05_inbounds.json
RUN echo '{}' >/tmp/usr/local/etc/xray/06_outbounds.json
RUN echo '{}' >/tmp/usr/local/etc/xray/07_transport.json
RUN echo '{}' >/tmp/usr/local/etc/xray/08_stats.json
RUN echo '{}' >/tmp/usr/local/etc/xray/09_reverse.json
# Create log files
RUN mkdir -p /tmp/var/log/xray && touch \
/tmp/var/log/xray/access.log \
/tmp/var/log/xray/error.log
# Build finally image
# Note on Distroless Base Image and Architecture Support:
# - The official 'gcr.io/distroless/static' image provided by Google only supports a limited set of architectures for Linux:
# - linux/amd64
# - linux/arm/v7
# - linux/arm64/v8
# - linux/ppc64le
# - linux/s390x
# - Upon inspection, the blob contents of the Distroless images across these architectures are nearly identical, with only minor differences in metadata (e.g., 'Architecture' field in the manifest).
# - Due to this similarity in content, it is feasible to forcibly specify a single platform (e.g., '--platform=linux/amd64') for unsupported architectures, as the core image content remains compatible with statically compiled binaries like Go applications.
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 /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=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=755 /tmp/empty /var/log/xray
COPY --from=build --chown=65532:65532 --chmod=600 /tmp/var/log/xray/*.log /var/log/xray/
VOLUME /usr/local/etc/xray
VOLUME /var/log/xray
ARG TZ=Etc/UTC
ENV TZ=$TZ
ENTRYPOINT [ "/usr/local/bin/xray" ]
CMD [ "-confdir", "/usr/local/etc/xray/" ]

View File

@@ -1,18 +0,0 @@
{
"inbounds": [{
"port": 9000,
"protocol": "vmess",
"settings": {
"clients": [
{
"id": "1eb6e917-774b-4a84-aff6-b058577c60a5",
"level": 1
}
]
}
}],
"outbounds": [{
"protocol": "freedom",
"settings": {}
}]
}

View File

@@ -1,76 +1,133 @@
name: Build docker image
name: Build and Push Docker Image
on:
release:
types: [published]
push:
branches:
- main
types:
- published
- released
workflow_dispatch:
inputs:
tag:
description: "Docker image tag:"
required: true
latest:
description: "Set to latest"
type: boolean
default: false
jobs:
build-image:
build-and-push:
if: (github.event.action != 'published') || (github.event.action == 'published' && github.event.release.prerelease == true)
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/xray-core
flavor: latest=auto
tags: |
type=sha
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
- name: Docker metadata Loyalsoldier flavor
id: loyalsoldier
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/xray-core
flavor: |
latest=auto
suffix=-ls,onlatest=true
tags: |
type=sha
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
- 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: Validate and extract tag
run: |
SOURCE_TAG="${{ github.event.inputs.tag }}"
if [[ -z "$SOURCE_TAG" ]]; then
SOURCE_TAG="${{ github.ref_name }}"
fi
if [[ -z "$SOURCE_TAG" ]]; then
SOURCE_TAG="${{ github.event.release.tag_name }}"
fi
if [[ -z "$SOURCE_TAG" ]]; then
echo "Error: Could not determine a valid tag source. Input tag and context tag (github.ref_name) are both empty."
exit 1
fi
if [[ "$SOURCE_TAG" =~ ^v[0-9]+\.[0-9] ]]; then
IMAGE_TAG="${SOURCE_TAG#v}"
else
IMAGE_TAG="$SOURCE_TAG"
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
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
- name: Build Docker image (main architectures)
id: build_main_arches
uses: docker/build-push-action@v6
with:
context: .
file: .github/docker/Dockerfile
platforms: |
linux/amd64
linux/arm64
linux/loong64
linux/riscv64
linux/arm/v7
linux/arm64/v8
linux/ppc64le
linux/s390x
provenance: false
file: .github/docker/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
- name: Build and push Loyalsoldier flavor
outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
- name: Build Docker image (additional architectures)
id: build_additional_arches
uses: docker/build-push-action@v6
with:
context: .
file: .github/docker/Dockerfile.usa
platforms: |
linux/amd64
linux/arm64
linux/loong64
linux/386
linux/arm/v6
linux/riscv64
linux/loong64
provenance: false
file: .github/docker/Dockerfile
build-args: flavor=loyalsoldier
push: true
tags: |
${{ steps.loyalsoldier.outputs.tags }}
outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
- name: Create manifest list and push
run: |
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 }}
if [[ "${{ env.LATEST }}" == "true" ]]; then
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
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}
if [[ "${{ env.LATEST }}" == "true" ]]; then
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:latest
fi

148
.github/workflows/release-win7.yml vendored Normal file
View File

@@ -0,0 +1,148 @@
name: Build and Release for Windows 7
on:
workflow_dispatch:
release:
types: [published]
push:
pull_request:
types: [opened, synchronize, reopened]
jobs:
check-assets:
runs-on: ubuntu-latest
steps:
- name: Restore Geodat Cache
uses: actions/cache/restore@v4
with:
path: resources
key: xray-geodat-
- name: Check Assets Existence
id: check-assets
run: |
[ -d 'resources' ] || mkdir resources
LIST=('geoip.dat' 'geosite.dat')
for FILE_NAME in "${LIST[@]}"
do
echo -e "Checking ${FILE_NAME}..."
if [ -s "./resources/${FILE_NAME}" ]; then
echo -e "${FILE_NAME} exists."
else
echo -e "${FILE_NAME} does not exist."
echo "missing=true" >> $GITHUB_OUTPUT
break
fi
done
- name: Sleep for 90 seconds if Assets Missing
if: steps.check-assets.outputs.missing == 'true'
run: sleep 90
build:
needs: check-assets
permissions:
contents: write
strategy:
matrix:
include:
# BEGIN Windows 7
- goos: windows
goarch: amd64
assetname: win7-64
- goos: windows
goarch: 386
assetname: win7-32
# END Windows 7
fail-fast: false
runs-on: ubuntu-latest
env:
GOOS: ${{ matrix.goos}}
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: 0
steps:
- name: Checkout codebase
uses: actions/checkout@v5
- name: Show workflow information
run: |
_NAME=${{ matrix.assetname }}
echo "GOOS: ${{ matrix.goos }}, GOARCH: ${{ matrix.goarch }}, RELEASE_NAME: $_NAME"
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
- name: Setup patched builder
run: |
GOSDK=$(go env GOROOT)
rm -r $GOSDK/*
cd $GOSDK
curl -O -L -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" https://github.com/XTLS/go-win7/releases/latest/download/go-for-win7-linux-amd64.zip
unzip ./go-for-win7-linux-amd64.zip -d $GOSDK
rm ./go-for-win7-linux-amd64.zip
- name: Get project dependencies
run: go mod download
- name: Build Xray
run: |
mkdir -p build_assets
COMMID=$(git describe --always --dirty)
echo 'Building Xray for Windows 7...'
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 '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.
# 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
uses: actions/cache/restore@v4
with:
path: resources
key: xray-geodat-
- name: Copy README.md & LICENSE
run: |
mv -f resources/* build_assets
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
- name: Create ZIP archive
if: github.event_name == 'release'
shell: bash
run: |
pushd build_assets || exit 1
touch -mt $(date +%Y01010000) *
zip -9vr ../Xray-${{ env.ASSET_NAME }}.zip .
popd || exit 1
FILE=./Xray-${{ env.ASSET_NAME }}.zip
DGST=$FILE.dgst
for METHOD in {"md5","sha1","sha256","sha512"}
do
openssl dgst -$METHOD $FILE | sed 's/([^)]*)//g' >>$DGST
done
- name: Change the name
run: |
mv build_assets Xray-${{ env.ASSET_NAME }}
- name: Upload files to Artifacts
uses: actions/upload-artifact@v4
with:
name: Xray-${{ env.ASSET_NAME }}
path: |
./Xray-${{ env.ASSET_NAME }}/*
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v2
if: github.event_name == 'release'
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./Xray-${{ env.ASSET_NAME }}.zip*
tag: ${{ github.ref }}
file_glob: true

View File

@@ -1,76 +1,61 @@
name: Build and Release
# NOTE: This Github Actions file depends on the Makefile.
# Building the correct package requires the correct binaries generated by the Makefile. To
# ensure the correct output, the Makefile must accept the appropriate input and compile the
# correct file with the correct name. If you need to modify this file, please ensure it won't
# disrupt the Makefile.
on:
workflow_dispatch:
release:
types: [published]
push:
branches:
- main
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/release.yml"
pull_request:
types: [opened, synchronize, reopened]
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/release.yml"
jobs:
prepare:
check-assets:
runs-on: ubuntu-latest
steps:
- name: Restore Cache
- name: Restore Geodat Cache
uses: actions/cache/restore@v4
with:
path: resources
key: xray-geodat-
- name: Update Geodat
id: update
uses: nick-fields/retry@v3
with:
timeout_minutes: 60
retry_wait_seconds: 60
max_attempts: 60
command: |
[ -d 'resources' ] || mkdir resources
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
for i in "${LIST[@]}"
do
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
FILE_NAME="${INFO[2]}.dat"
echo -e "Verifying HASH key..."
HASH="$(curl -sL "https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
if [ -s "./resources/${FILE_NAME}" ] && [ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ]; then
continue
else
echo -e "Downloading https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat..."
curl -L "https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat" -o ./resources/${FILE_NAME}
echo -e "Verifying HASH key..."
[ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
echo "unhit=true" >> $GITHUB_OUTPUT
fi
done
- name: Check Assets Existence
id: check-assets
run: |
[ -d 'resources' ] || mkdir resources
LIST=('geoip.dat' 'geosite.dat')
for FILE_NAME in "${LIST[@]}"
do
echo -e "Checking ${FILE_NAME}..."
if [ -s "./resources/${FILE_NAME}" ]; then
echo -e "${FILE_NAME} exists."
else
echo -e "${FILE_NAME} does not exist."
echo "missing=true" >> $GITHUB_OUTPUT
break
fi
done
- name: Save Cache
uses: actions/cache/save@v4
if: ${{ steps.update.outputs.unhit }}
- name: Trigger Asset Update Workflow if Assets Missing
if: steps.check-assets.outputs.missing == 'true'
uses: actions/github-script@v7
with:
path: resources
key: xray-geodat-${{ github.sha }}-${{ github.run_number }}
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { owner, repo } = context.repo;
await github.rest.actions.createWorkflowDispatch({
owner,
repo,
workflow_id: 'scheduled-assets-update.yml',
ref: context.ref
});
console.log('Triggered scheduled-assets-update.yml due to missing assets on branch:', context.ref);
- name: Sleep for 90 seconds if Assets Missing
if: steps.check-assets.outputs.missing == 'true'
run: sleep 90
build:
needs: prepare
needs: check-assets
permissions:
contents: write
strategy:
@@ -78,9 +63,7 @@ jobs:
# Include amd64 on all platforms.
goos: [windows, freebsd, openbsd, linux, darwin]
goarch: [amd64, 386]
gotoolchain: [""]
patch-assetname: [""]
exclude:
# Exclude i386 on darwin
- goarch: 386
@@ -105,6 +88,11 @@ jobs:
- goos: android
goarch: arm64
# END Android ARM 8
# BEGIN Android AMD64
- goos: android
goarch: amd64
patch-assetname: android-amd64
# END Android AMD64
# Windows ARM
- goos: windows
goarch: arm64
@@ -155,16 +143,6 @@ jobs:
goarch: arm
goarm: 7
# END OPENBSD ARM
# BEGIN Windows 7
- goos: windows
goarch: amd64
gotoolchain: 1.21.4
patch-assetname: win7-64
- goos: windows
goarch: 386
gotoolchain: 1.21.4
patch-assetname: win7-32
# END Windows 7
fail-fast: false
runs-on: ubuntu-latest
@@ -175,7 +153,20 @@ jobs:
CGO_ENABLED: 0
steps:
- name: Checkout codebase
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up NDK
if: matrix.goos == 'android'
run: |
wget -qO android-ndk.zip https://dl.google.com/android/repository/android-ndk-r28b-linux.zip
unzip android-ndk.zip
rm android-ndk.zip
declare -A arches=(
["amd64"]="x86_64-linux-android24-clang"
["arm64"]="aarch64-linux-android24-clang"
)
echo CC="$(realpath android-ndk-*/toolchains/llvm/prebuilt/linux-x86_64/bin)/${arches[${{ matrix.goarch }}]}" >> $GITHUB_ENV
echo CGO_ENABLED=1 >> $GITHUB_ENV
- name: Show workflow information
run: |
@@ -187,19 +178,35 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.gotoolchain || '1.23' }}
go-version-file: go.mod
check-latest: true
- name: Get project dependencies
run: go mod download
- name: Build Xray
run: |
mkdir -p build_assets
make
find . -maxdepth 1 -type f -regex './\(wxray\|xray\|xray_softfloat\)\(\|.exe\)' -exec mv {} ./build_assets/ \;
COMMID=$(git describe --always --dirty)
if [[ ${GOOS} == 'windows' ]]; then
echo 'Building Xray for Windows...'
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 '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.
# 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
echo 'Building Xray...'
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...'
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
- name: Restore Cache
- name: Restore Geodat Cache
uses: actions/cache/restore@v4
with:
path: resources

View File

@@ -0,0 +1,65 @@
name: Scheduled assets update
# NOTE: This Github Actions is required by other actions, for preparing other packaging assets in a
# routine manner, for example: GeoIP/GeoSite.
# Currently updating:
# - Geodat (GeoIP/Geosite)
on:
workflow_dispatch:
schedule:
# Update GeoData on every day (22:30 UTC)
- cron: "30 22 * * *"
push:
# Prevent triggering update request storm
paths:
- ".github/workflows/scheduled-assets-update.yml"
pull_request:
# Prevent triggering update request storm
paths:
- ".github/workflows/scheduled-assets-update.yml"
jobs:
geodat:
if: github.event.schedule == '30 22 * * *' || github.event_name == 'push'|| github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- name: Restore Geodat Cache
uses: actions/cache/restore@v4
with:
path: resources
key: xray-geodat-
- name: Update Geodat
id: update
uses: nick-fields/retry@v3
with:
timeout_minutes: 60
retry_wait_seconds: 60
max_attempts: 60
command: |
[ -d 'resources' ] || mkdir resources
LIST=('Loyalsoldier v2ray-rules-dat geoip geoip' 'Loyalsoldier v2ray-rules-dat geosite geosite')
for i in "${LIST[@]}"
do
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3,$4}'))
FILE_NAME="${INFO[3]}.dat"
echo -e "Verifying HASH key..."
HASH="$(curl -sL -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "https://raw.githubusercontent.com/${INFO[0]}/${INFO[1]}/release/${INFO[2]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
if [ -s "./resources/${FILE_NAME}" ] && [ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ]; then
continue
else
echo -e "Downloading https://raw.githubusercontent.com/${INFO[0]}/${INFO[1]}/release/${INFO[2]}.dat..."
curl -L -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "https://raw.githubusercontent.com/${INFO[0]}/${INFO[1]}/release/${INFO[2]}.dat" -o ./resources/${FILE_NAME}
echo -e "Verifying HASH key..."
[ "$(sha256sum "./resources/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
echo "unhit=true" >> $GITHUB_OUTPUT
fi
done
- name: Save Geodat Cache
uses: actions/cache/save@v4
if: ${{ steps.update.outputs.unhit }}
with:
path: resources
key: xray-geodat-${{ github.sha }}-${{ github.run_number }}

View File

@@ -2,23 +2,40 @@ name: Test
on:
push:
branches:
- main
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/*.yml"
pull_request:
types: [opened, synchronize, reopened]
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/*.yml"
jobs:
check-assets:
runs-on: ubuntu-latest
steps:
- name: Restore Geodat Cache
uses: actions/cache/restore@v4
with:
path: resources
key: xray-geodat-
- name: Check Assets Existence
id: check-assets
run: |
[ -d 'resources' ] || mkdir resources
LIST=('geoip.dat' 'geosite.dat')
for FILE_NAME in "${LIST[@]}"
do
echo -e "Checking ${FILE_NAME}..."
if [ -s "./resources/${FILE_NAME}" ]; then
echo -e "${FILE_NAME} exists."
else
echo -e "${FILE_NAME} does not exist."
echo "missing=true" >> $GITHUB_OUTPUT
break
fi
done
- name: Sleep for 90 seconds if Assets Missing
if: steps.check-assets.outputs.missing == 'true'
run: sleep 90
test:
needs: check-assets
permissions:
contents: read
runs-on: ${{ matrix.os }}
@@ -28,13 +45,13 @@ jobs:
os: [windows-latest, ubuntu-latest, macos-latest]
steps:
- name: Checkout codebase
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version-file: go.mod
check-latest: true
- name: Restore Cache
- name: Restore Geodat Cache
uses: actions/cache/restore@v4
with:
path: resources

17
.gitignore vendored
View File

@@ -14,10 +14,18 @@
# Dependency directories (remove the comment below to include it)
# vendor/
# macOS specific files
*.DS_Store
.idea
# IDE specific files
.idea/
.vscode/
# Archive files
*.zip
*.tar.gz
# Binaries
xray
xray_softfloat
mockgen
@@ -26,8 +34,13 @@ vprotogen
errorgen
!common/errors/errorgen/
*.dat
.vscode
# Build assets
/build_assets
# Output from dlv test
**/debug.*
# Certificates
*.crt
*.key

View File

@@ -1,37 +0,0 @@
NAME = xray
VERSION=$(shell git describe --always --dirty)
# NOTE: This MAKEFILE can be used to build Xray-core locally and in Automatic workflows. It is \
provided for convenience in automatic building and functions as a part of it.
# NOTE: If you need to modify this file, please be aware that:\
- This file is not the main Makefile; it only accepts environment variables and builds the \
binary.\
- Automatic building expects the correct binaries to be built by this Makefile. If you \
intend to propose a change to this Makefile, carefully review the file below and ensure \
that the change will not accidentally break the automatic building:\
.github/workflows/release.yml \
Otherwise it is recommended to contact the project maintainers.
LDFLAGS = -X github.com/xtls/xray-core/core.build=$(VERSION) -s -w -buildid=
PARAMS = -trimpath -ldflags "$(LDFLAGS)" -v
MAIN = ./main
PREFIX ?= $(shell go env GOPATH)
ifeq ($(GOOS),windows)
OUTPUT = $(NAME).exe
ADDITION = go build -o w$(NAME).exe -trimpath -ldflags "-H windowsgui $(LDFLAGS)" -v $(MAIN)
else
OUTPUT = $(NAME)
endif
ifeq ($(shell echo "$(GOARCH)" | grep -Eq "(mips|mipsle)" && echo true),true) #
ADDITION = GOMIPS=softfloat go build -o $(NAME)_softfloat -trimpath -ldflags "$(LDFLAGS)" -v $(MAIN)
endif
.PHONY: clean build
build:
go build -o $(OUTPUT) $(PARAMS) $(MAIN)
$(ADDITION)
clean:
go clean -v -i $(PWD)
rm -f xray xray.exe wxray.exe xray_softfloat

View File

@@ -6,9 +6,13 @@
## 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`**
- **Project X NFT: [Announcement of NFTs by Project X](https://github.com/XTLS/Xray-core/discussions/3633)**
- **REALITY NFT: [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113)**
- **REALITY NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2**
- **Related links: https://opensea.io/collection/xtls, [Announcement of NFTs by Project X](https://github.com/XTLS/Xray-core/discussions/3633), [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113)**
## License
@@ -24,7 +28,9 @@
[Project X Channel](https://t.me/projectXtls)
[Project VLESS](https://t.me/projectVless) (non-Chinese)
[Project VLESS](https://t.me/projectVless) (Русский)
[Project XHTTP](https://t.me/projectXhttp) (Persian)
## Installation
@@ -36,12 +42,13 @@
- [teddysun/xray](https://hub.docker.com/r/teddysun/xray)
- [wulabing/xray_docker](https://github.com/wulabing/xray_docker)
- Web Panel - **WARNING: Please DO NOT USE plain HTTP panels like 3X-UI**, as they are believed to be bribed by Iran GFW for supporting plain HTTP by default and refused to change (https://github.com/XTLS/Xray-core/pull/3884#issuecomment-2439595331), which has already put many users' data security in danger in the past few years. **If you are already using 3X-UI, please switch to the following panels, which are verified to support HTTPS and SSH port forwarding only:**
- [Remnawave](https://github.com/remnawave/panel)
- [Marzban](https://github.com/Gozargah/Marzban)
- [Xray-UI](https://github.com/qist/xray-ui)
- [Hiddify](https://github.com/hiddify/Hiddify-Manager)
- One Click
- [Xray-REALITY](https://github.com/zxcvos/Xray-script), [xray-reality](https://github.com/sajjaddg/xray-reality), [reality-ezpz](https://github.com/aleskxyz/reality-ezpz)
- [Xray_bash_onekey](https://github.com/hello-yunshu/Xray_bash_onekey), [XTool](https://github.com/LordPenguin666/XTool)
- [Xray_bash_onekey](https://github.com/hello-yunshu/Xray_bash_onekey), [XTool](https://github.com/LordPenguin666/XTool), [VPainLess](https://github.com/vpainless/vpainless)
- [v2ray-agent](https://github.com/mack-a/v2ray-agent), [Xray_onekey](https://github.com/wulabing/Xray_onekey), [ProxySU](https://github.com/proxysu/ProxySU)
- Magisk
- [Xray4Magisk](https://github.com/Asterisk4Magisk/Xray4Magisk)
@@ -72,34 +79,49 @@
- [PassWall](https://github.com/xiaorouji/openwrt-passwall), [PassWall 2](https://github.com/xiaorouji/openwrt-passwall2)
- [ShadowSocksR Plus+](https://github.com/fw876/helloworld)
- [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray))
- Asuswrt-Merlin
- [XRAYUI](https://github.com/DanielLavrushin/asuswrt-merlin-xrayui)
- Windows
- [v2rayN](https://github.com/2dust/v2rayN)
- [Furious](https://github.com/LorenEteval/Furious)
- [Invisible Man - Xray](https://github.com/InvisibleManVPN/InvisibleMan-XRayClient)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- Android
- [v2rayNG](https://github.com/2dust/v2rayNG)
- [X-flutter](https://github.com/XTLS/X-flutter)
- [SaeedDev94/Xray](https://github.com/SaeedDev94/Xray)
- iOS & macOS arm64
- [FoXray](https://apps.apple.com/app/foxray/id6448898396)
- [SimpleXray](https://github.com/lhear/SimpleXray)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- 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)
- [OneXray](https://github.com/OneXray/OneXray)
- macOS arm64 & x64
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215)
- [V2rayU](https://github.com/yanue/V2rayU)
- [V2RayXS](https://github.com/tzmax/V2RayXS)
- [Furious](https://github.com/LorenEteval/Furious)
- [FoXray](https://apps.apple.com/app/foxray/id6448898396)
- [OneXray](https://github.com/OneXray/OneXray)
- [GoXRay](https://github.com/goxray/desktop)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- Linux
- [v2rayA](https://github.com/v2rayA/v2rayA)
- [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...
- iOS & macOS arm64
- iOS & macOS arm64 & tvOS
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
- [Loon](https://apps.apple.com/us/app/loon/id1373567447)
- Xray Tools
- [xray-knife](https://github.com/lilendian0x00/xray-knife)
- [xray-checker](https://github.com/kutovoys/xray-checker)
- Xray Wrapper
- [XTLS/libXray](https://github.com/XTLS/libXray)
- [xtls-sdk](https://github.com/remnawave/xtls-sdk)
- [xtlsapi](https://github.com/hiddify/xtlsapi)
- [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite)
- [Xray-core-python](https://github.com/LorenEteval/Xray-core-python)
@@ -107,39 +129,49 @@
- [XrayR](https://github.com/XrayR-project/XrayR)
- [XrayR-release](https://github.com/XrayR-project/XrayR-release)
- [XrayR-V2Board](https://github.com/missuo/XrayR-V2Board)
- [Clash.Meta](https://github.com/MetaCubeX/Clash.Meta)
- [clashN](https://github.com/2dust/clashN)
- [Clash Meta for Android](https://github.com/MetaCubeX/ClashMetaForAndroid)
- [sing-box](https://github.com/SagerNet/sing-box)
- Cores
- [Amnezia VPN](https://github.com/amnezia-vpn)
- [mihomo](https://github.com/MetaCubeX/mihomo)
- [sing-box](https://github.com/SagerNet/sing-box)
## Contributing
[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
- [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).
- For third-party projects used in [Xray-core](https://github.com/XTLS/Xray-core), check your local or [the latest go.mod](https://github.com/XTLS/Xray-core/blob/main/go.mod).
## Compilation
## One-line Compilation
### Windows (PowerShell)
```powershell
$env:CGO_ENABLED=0
go build -o xray.exe -trimpath -ldflags "-s -w -buildid=" ./main
go build -o xray.exe -trimpath -buildvcs=false -ldflags="-s -w -buildid=" -v ./main
```
### Linux / macOS
```bash
CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -ldflags="-s -w -buildid=" -v ./main
```
### Reproducible Releases
Make sure that you are using the same Go version, and remember to set the git commit id (7 bytes):
```bash
make
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

View File

@@ -8,6 +8,7 @@ import (
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/signal/done"
"github.com/xtls/xray-core/transport"
)
@@ -108,3 +109,13 @@ func (co *Outbound) Close() error {
co.closed = true
return co.listener.Close()
}
// SenderSettings implements outbound.Handler.
func (co *Outbound) SenderSettings() *serial.TypedMessage {
return nil
}
// ProxySettings implements outbound.Handler.
func (co *Outbound) ProxySettings() *serial.TypedMessage {
return nil
}

View File

@@ -33,23 +33,21 @@ type cachedReader struct {
cache buf.MultiBuffer
}
func (r *cachedReader) Cache(b *buf.Buffer) {
mb, _ := r.reader.ReadMultiBufferTimeout(time.Millisecond * 100)
func (r *cachedReader) Cache(b *buf.Buffer, deadline time.Duration) error {
mb, err := r.reader.ReadMultiBufferTimeout(deadline)
if err != nil {
return err
}
r.Lock()
if !mb.IsEmpty() {
r.cache, _ = buf.MergeMulti(r.cache, mb)
}
cacheLen := r.cache.Len()
if cacheLen <= b.Cap() {
b.Clear()
} else {
b.Release()
*b = *buf.NewWithSize(cacheLen)
}
rawBytes := b.Extend(cacheLen)
b.Clear()
rawBytes := b.Extend(min(r.cache.Len(), b.Cap()))
n := r.cache.Copy(rawBytes)
b.Resize(0, int32(n))
r.Unlock()
return nil
}
func (r *cachedReader) readInternal() buf.MultiBuffer {
@@ -98,7 +96,6 @@ type DefaultDispatcher struct {
router routing.Router
policy policy.Manager
stats stats.Manager
dns dns.Client
fdns dns.FakeDNSEngine
}
@@ -106,10 +103,10 @@ func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
d := new(DefaultDispatcher)
if err := core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dc dns.Client) error {
core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) { // FakeDNSEngine is optional
core.OptionalFeatures(ctx, func(fdns dns.FakeDNSEngine) {
d.fdns = fdns
})
return d.Init(config.(*Config), om, router, pm, sm, dc)
return d.Init(config.(*Config), om, router, pm, sm)
}); err != nil {
return nil, err
}
@@ -118,12 +115,11 @@ func init() {
}
// Init initializes DefaultDispatcher.
func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dns dns.Client) error {
func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager) error {
d.ohm = om
d.router = router
d.policy = pm
d.stats = sm
d.dns = dns
return nil
}
@@ -355,7 +351,7 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
}
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network) (SniffResult, error) {
payload := buf.New()
payload := buf.NewWithSize(32767)
defer payload.Release()
sniffer := NewSniffer(ctx)
@@ -367,26 +363,36 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
}
contentResult, contentErr := func() (SniffResult, error) {
cacheDeadline := 200 * time.Millisecond
totalAttempt := 0
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
totalAttempt++
if totalAttempt > 2 {
return nil, errSniffingTimeout
cachingStartingTimeStamp := time.Now()
err := cReader.Cache(payload, cacheDeadline)
if err != nil {
return nil, err
}
cachingTimeElapsed := time.Since(cachingStartingTimeStamp)
cacheDeadline -= cachingTimeElapsed
cReader.Cache(payload)
if !payload.IsEmpty() {
result, err := sniffer.Sniff(ctx, payload.Bytes(), network)
if err != common.ErrNoClue {
switch err {
case common.ErrNoClue: // No Clue: protocol not matches, and sniffer cannot determine whether there will be a match or not
totalAttempt++
case protocol.ErrProtoNeedMoreData: // Protocol Need More Data: protocol matches, but need more data to complete sniffing
// in this case, do not add totalAttempt(allow to read until timeout)
default:
return result, err
}
} else {
totalAttempt++
}
if payload.IsFull() {
return nil, errUnknownContent
if totalAttempt >= 2 || cacheDeadline <= 0 {
return nil, errSniffingTimeout
}
}
}
@@ -402,18 +408,6 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
outbounds := session.OutboundsFromContext(ctx)
ob := outbounds[len(outbounds)-1]
if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() {
proxied := hosts.LookupHosts(ob.Target.String())
if proxied != nil {
ro := ob.RouteTarget == destination
destination.Address = *proxied
if ro {
ob.RouteTarget = destination
} else {
ob.Target = destination
}
}
}
var handler outbound.Handler

View File

@@ -6,6 +6,7 @@ import (
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/protocol/bittorrent"
"github.com/xtls/xray-core/common/protocol/http"
"github.com/xtls/xray-core/common/protocol/quic"
@@ -58,14 +59,17 @@ var errUnknownContent = errors.New("unknown content")
func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) {
var pendingSniffer []protocolSnifferWithMetadata
for _, si := range s.sniffer {
s := si.protocolSniffer
protocolSniffer := si.protocolSniffer
if si.metadataSniffer || si.network != network {
continue
}
result, err := s(c, payload)
result, err := protocolSniffer(c, payload)
if err == common.ErrNoClue {
pendingSniffer = append(pendingSniffer, si)
continue
} else if err == protocol.ErrProtoNeedMoreData { // Sniffer protocol matched, but need more data to complete sniffing
s.sniffer = []protocolSnifferWithMetadata{si}
return nil, err
}
if err == nil && result != nil {

188
app/dns/cache_controller.go Normal file
View File

@@ -0,0 +1,188 @@
package dns
import (
"context"
go_errors "errors"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/signal/pubsub"
"github.com/xtls/xray-core/common/task"
dns_feature "github.com/xtls/xray-core/features/dns"
"golang.org/x/net/dns/dnsmessage"
"sync"
"time"
)
type CacheController struct {
sync.RWMutex
ips map[string]*record
pub *pubsub.Service
cacheCleanup *task.Periodic
name string
disableCache bool
}
func NewCacheController(name string, disableCache bool) *CacheController {
c := &CacheController{
name: name,
disableCache: disableCache,
ips: make(map[string]*record),
pub: pubsub.NewService(),
}
c.cacheCleanup = &task.Periodic{
Interval: time.Minute,
Execute: c.CacheCleanup,
}
return c
}
// CacheCleanup clears expired items from cache
func (c *CacheController) CacheCleanup() error {
now := time.Now()
c.Lock()
defer c.Unlock()
if len(c.ips) == 0 {
return errors.New("nothing to do. stopping...")
}
for domain, record := range c.ips {
if record.A != nil && record.A.Expire.Before(now) {
record.A = nil
}
if record.AAAA != nil && record.AAAA.Expire.Before(now) {
record.AAAA = nil
}
if record.A == nil && record.AAAA == nil {
errors.LogDebug(context.Background(), c.name, "cache cleanup ", domain)
delete(c.ips, domain)
} else {
c.ips[domain] = record
}
}
if len(c.ips) == 0 {
c.ips = make(map[string]*record)
}
return nil
}
func (c *CacheController) updateIP(req *dnsRequest, ipRec *IPRecord) {
elapsed := time.Since(req.start)
c.Lock()
rec, found := c.ips[req.domain]
if !found {
rec = &record{}
}
switch req.reqType {
case dnsmessage.TypeA:
rec.A = ipRec
case dnsmessage.TypeAAAA:
rec.AAAA = ipRec
}
errors.LogInfo(context.Background(), c.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed)
c.ips[req.domain] = rec
switch req.reqType {
case dnsmessage.TypeA:
c.pub.Publish(req.domain+"4", nil)
if !c.disableCache {
_, _, err := rec.AAAA.getIPs()
if !go_errors.Is(err, errRecordNotFound) {
c.pub.Publish(req.domain+"6", nil)
}
}
case dnsmessage.TypeAAAA:
c.pub.Publish(req.domain+"6", nil)
if !c.disableCache {
_, _, err := rec.A.getIPs()
if !go_errors.Is(err, errRecordNotFound) {
c.pub.Publish(req.domain+"4", nil)
}
}
}
c.Unlock()
common.Must(c.cacheCleanup.Start())
}
func (c *CacheController) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
c.RLock()
record, found := c.ips[domain]
c.RUnlock()
if !found {
return nil, 0, errRecordNotFound
}
var errs []error
var allIPs []net.IP
var rTTL uint32 = dns_feature.DefaultTTL
mergeReq := option.IPv4Enable && option.IPv6Enable
if option.IPv4Enable {
ips, ttl, err := record.A.getIPs()
if !mergeReq || go_errors.Is(err, errRecordNotFound) {
return ips, ttl, err
}
if ttl < rTTL {
rTTL = ttl
}
if len(ips) > 0 {
allIPs = append(allIPs, ips...)
} else {
errs = append(errs, err)
}
}
if option.IPv6Enable {
ips, ttl, err := record.AAAA.getIPs()
if !mergeReq || go_errors.Is(err, errRecordNotFound) {
return ips, ttl, err
}
if ttl < rTTL {
rTTL = ttl
}
if len(ips) > 0 {
allIPs = append(allIPs, ips...)
} else {
errs = append(errs, err)
}
}
if len(allIPs) > 0 {
return allIPs, rTTL, nil
}
if go_errors.Is(errs[0], errs[1]) {
return nil, rTTL, errs[0]
}
return nil, rTTL, errors.Combine(errs...)
}
func (c *CacheController) registerSubscribers(domain string, option dns_feature.IPOption) (sub4 *pubsub.Subscriber, sub6 *pubsub.Subscriber) {
// ipv4 and ipv6 belong to different subscription groups
if option.IPv4Enable {
sub4 = c.pub.Subscribe(domain + "4")
}
if option.IPv6Enable {
sub6 = c.pub.Subscribe(domain + "6")
}
return
}
func closeSubscribers(sub4 *pubsub.Subscriber, sub6 *pubsub.Subscriber) {
if sub4 != nil {
sub4.Close()
}
if sub6 != nil {
sub6.Close()
}
}

View File

@@ -80,6 +80,7 @@ const (
QueryStrategy_USE_IP QueryStrategy = 0
QueryStrategy_USE_IP4 QueryStrategy = 1
QueryStrategy_USE_IP6 QueryStrategy = 2
QueryStrategy_USE_SYS QueryStrategy = 3
)
// Enum value maps for QueryStrategy.
@@ -88,11 +89,13 @@ var (
0: "USE_IP",
1: "USE_IP4",
2: "USE_IP6",
3: "USE_SYS",
}
QueryStrategy_value = map[string]int32{
"USE_IP": 0,
"USE_IP4": 1,
"USE_IP6": 2,
"USE_SYS": 3,
}
)
@@ -132,9 +135,16 @@ type NameServer struct {
ClientIp []byte `protobuf:"bytes,5,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"`
SkipFallback bool `protobuf:"varint,6,opt,name=skipFallback,proto3" json:"skipFallback,omitempty"`
PrioritizedDomain []*NameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"`
Geoip []*router.GeoIP `protobuf:"bytes,3,rep,name=geoip,proto3" json:"geoip,omitempty"`
ExpectedGeoip []*router.GeoIP `protobuf:"bytes,3,rep,name=expected_geoip,json=expectedGeoip,proto3" json:"expected_geoip,omitempty"`
OriginalRules []*NameServer_OriginalRule `protobuf:"bytes,4,rep,name=original_rules,json=originalRules,proto3" json:"original_rules,omitempty"`
QueryStrategy QueryStrategy `protobuf:"varint,7,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"`
ActPrior bool `protobuf:"varint,8,opt,name=actPrior,proto3" json:"actPrior,omitempty"`
Tag string `protobuf:"bytes,9,opt,name=tag,proto3" json:"tag,omitempty"`
TimeoutMs uint64 `protobuf:"varint,10,opt,name=timeoutMs,proto3" json:"timeoutMs,omitempty"`
DisableCache bool `protobuf:"varint,11,opt,name=disableCache,proto3" json:"disableCache,omitempty"`
FinalQuery bool `protobuf:"varint,12,opt,name=finalQuery,proto3" json:"finalQuery,omitempty"`
UnexpectedGeoip []*router.GeoIP `protobuf:"bytes,13,rep,name=unexpected_geoip,json=unexpectedGeoip,proto3" json:"unexpected_geoip,omitempty"`
ActUnprior bool `protobuf:"varint,14,opt,name=actUnprior,proto3" json:"actUnprior,omitempty"`
}
func (x *NameServer) Reset() {
@@ -195,9 +205,9 @@ func (x *NameServer) GetPrioritizedDomain() []*NameServer_PriorityDomain {
return nil
}
func (x *NameServer) GetGeoip() []*router.GeoIP {
func (x *NameServer) GetExpectedGeoip() []*router.GeoIP {
if x != nil {
return x.Geoip
return x.ExpectedGeoip
}
return nil
}
@@ -216,6 +226,55 @@ func (x *NameServer) GetQueryStrategy() QueryStrategy {
return QueryStrategy_USE_IP
}
func (x *NameServer) GetActPrior() bool {
if x != nil {
return x.ActPrior
}
return false
}
func (x *NameServer) GetTag() string {
if x != nil {
return x.Tag
}
return ""
}
func (x *NameServer) GetTimeoutMs() uint64 {
if x != nil {
return x.TimeoutMs
}
return 0
}
func (x *NameServer) GetDisableCache() bool {
if x != nil {
return x.DisableCache
}
return false
}
func (x *NameServer) GetFinalQuery() bool {
if x != nil {
return x.FinalQuery
}
return false
}
func (x *NameServer) GetUnexpectedGeoip() []*router.GeoIP {
if x != nil {
return x.UnexpectedGeoip
}
return nil
}
func (x *NameServer) GetActUnprior() bool {
if x != nil {
return x.ActUnprior
}
return false
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -508,7 +567,7 @@ var file_app_dns_config_proto_rawDesc = []byte{
0x2e, 0x64, 0x6e, 0x73, 0x1a, 0x1c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74,
0x2f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb2, 0x04, 0x0a, 0x0a,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb6, 0x06, 0x0a, 0x0a,
0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e,
@@ -522,75 +581,92 @@ var file_app_dns_config_proto_rawDesc = []byte{
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65,
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44,
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x11, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a,
0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x05, 0x67, 0x65, 0x6f, 0x69,
0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52,
0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x4c, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e,
0x61, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61,
0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61,
0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52,
0x75, 0x6c, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74,
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72,
0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79,
0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x1a, 0x5e, 0x0a, 0x0e, 0x50, 0x72, 0x69, 0x6f,
0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79,
0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61,
0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x36, 0x0a, 0x0c, 0x4f, 0x72, 0x69, 0x67,
0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04,
0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65,
0x22, 0x9c, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x0b, 0x6e,
0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e,
0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65,
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x68, 0x6f,
0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x73, 0x74, 0x61,
0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18,
0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69,
0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08,
0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x42,
0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74,
0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65,
0x67, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c,
0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x69, 0x73,
0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x36, 0x0a, 0x16,
0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49,
0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, 0x69,
0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d,
0x61, 0x74, 0x63, 0x68, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70,
0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e,
0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67,
0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f,
0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61,
0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x02,
0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x5f, 0x64, 0x6f,
0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78,
0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x2a,
0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e,
0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x00, 0x12,
0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x01, 0x12, 0x0b,
0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x52,
0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a, 0x35, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53,
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49,
0x50, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x01,
0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x02, 0x42, 0x46, 0x0a,
0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e,
0x73, 0x50, 0x01, 0x5a, 0x21, 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, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70,
0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3d, 0x0a, 0x0e, 0x65, 0x78, 0x70, 0x65,
0x63, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74,
0x65, 0x64, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x4c, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69,
0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e,
0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e,
0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c,
0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73,
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65,
0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72,
0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x74,
0x50, 0x72, 0x69, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x63, 0x74,
0x50, 0x72, 0x69, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x09, 0x20, 0x01,
0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x6f,
0x75, 0x74, 0x4d, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65,
0x6f, 0x75, 0x74, 0x4d, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65,
0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73,
0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6e,
0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66,
0x69, 0x6e, 0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x41, 0x0a, 0x10, 0x75, 0x6e, 0x65,
0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0d, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72,
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0f, 0x75, 0x6e, 0x65,
0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x1e, 0x0a, 0x0a,
0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08,
0x52, 0x0a, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x1a, 0x5e, 0x0a, 0x0e,
0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x34,
0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61,
0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04,
0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x36, 0x0a, 0x0c,
0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04,
0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65,
0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04,
0x73, 0x69, 0x7a, 0x65, 0x22, 0x9c, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x05,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0a,
0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c,
0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63,
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69,
0x63, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52,
0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03,
0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x22,
0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x08,
0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63,
0x68, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61,
0x74, 0x65, 0x67, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53,
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74,
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52,
0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b,
0x12, 0x36, 0x0a, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62,
0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08,
0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63,
0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73,
0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63,
0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16,
0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03,
0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65,
0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08,
0x07, 0x10, 0x08, 0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74,
0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c,
0x6c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x02, 0x12,
0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x0d, 0x51, 0x75,
0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0a, 0x0a, 0x06, 0x55,
0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49,
0x50, 0x34, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10,
0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x53, 0x59, 0x53, 0x10, 0x03, 0x42, 0x46,
0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64,
0x6e, 0x73, 0x50, 0x01, 0x5a, 0x21, 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, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41,
0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -621,19 +697,20 @@ var file_app_dns_config_proto_goTypes = []any{
var file_app_dns_config_proto_depIdxs = []int32{
7, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint
4, // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.app.dns.NameServer.PriorityDomain
8, // 2: xray.app.dns.NameServer.geoip:type_name -> xray.app.router.GeoIP
8, // 2: xray.app.dns.NameServer.expected_geoip:type_name -> xray.app.router.GeoIP
5, // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule
1, // 4: xray.app.dns.NameServer.query_strategy:type_name -> xray.app.dns.QueryStrategy
2, // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer
6, // 6: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping
1, // 7: xray.app.dns.Config.query_strategy:type_name -> xray.app.dns.QueryStrategy
0, // 8: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType
0, // 9: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType
10, // [10:10] is the sub-list for method output_type
10, // [10:10] is the sub-list for method input_type
10, // [10:10] is the sub-list for extension type_name
10, // [10:10] is the sub-list for extension extendee
0, // [0:10] is the sub-list for field type_name
8, // 5: xray.app.dns.NameServer.unexpected_geoip:type_name -> xray.app.router.GeoIP
2, // 6: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer
6, // 7: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping
1, // 8: xray.app.dns.Config.query_strategy:type_name -> xray.app.dns.QueryStrategy
0, // 9: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType
0, // 10: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType
11, // [11:11] is the sub-list for method output_type
11, // [11:11] is the sub-list for method input_type
11, // [11:11] is the sub-list for extension type_name
11, // [11:11] is the sub-list for extension extendee
0, // [0:11] is the sub-list for field type_name
}
func init() { file_app_dns_config_proto_init() }

View File

@@ -25,9 +25,16 @@ message NameServer {
}
repeated PriorityDomain prioritized_domain = 2;
repeated xray.app.router.GeoIP geoip = 3;
repeated xray.app.router.GeoIP expected_geoip = 3;
repeated OriginalRule original_rules = 4;
QueryStrategy query_strategy = 7;
bool actPrior = 8;
string tag = 9;
uint64 timeoutMs = 10;
bool disableCache = 11;
bool finalQuery = 12;
repeated xray.app.router.GeoIP unexpected_geoip = 13;
bool actUnprior = 14;
}
enum DomainMatchingType {
@@ -41,6 +48,7 @@ enum QueryStrategy {
USE_IP = 0;
USE_IP4 = 1;
USE_IP6 = 2;
USE_SYS = 3;
}
message Config {

View File

@@ -3,11 +3,12 @@ package dns
import (
"context"
go_errors "errors"
"fmt"
"sort"
"strings"
"sync"
"github.com/xtls/xray-core/app/router"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
@@ -19,8 +20,6 @@ import (
// DNS is a DNS rely server.
type DNS struct {
sync.Mutex
tag string
disableCache bool
disableFallback bool
disableFallbackIfMatch bool
ipOption *dns.IPOption
@@ -29,6 +28,7 @@ type DNS struct {
ctx context.Context
domainMatcher strmatcher.IndexMatcher
matcherInfos []*DomainMatcherInfo
checkSystem bool
}
// DomainMatcherInfo contains information attached to index returned by Server.domainMatcher
@@ -39,13 +39,6 @@ type DomainMatcherInfo struct {
// New creates a new DNS server with given configuration.
func New(ctx context.Context, config *Config) (*DNS, error) {
var tag string
if len(config.Tag) > 0 {
tag = config.Tag
} else {
tag = generateRandomTag()
}
var clientIP net.IP
switch len(config.ClientIp) {
case 0, net.IPv4len, net.IPv6len:
@@ -54,26 +47,36 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
return nil, errors.New("unexpected client IP length ", len(config.ClientIp))
}
var ipOption *dns.IPOption
var ipOption dns.IPOption
checkSystem := false
switch config.QueryStrategy {
case QueryStrategy_USE_IP:
ipOption = &dns.IPOption{
ipOption = dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
}
case QueryStrategy_USE_SYS:
ipOption = dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
}
checkSystem = true
case QueryStrategy_USE_IP4:
ipOption = &dns.IPOption{
ipOption = dns.IPOption{
IPv4Enable: true,
IPv6Enable: false,
FakeEnable: false,
}
case QueryStrategy_USE_IP6:
ipOption = &dns.IPOption{
ipOption = dns.IPOption{
IPv4Enable: false,
IPv6Enable: true,
FakeEnable: false,
}
default:
return nil, errors.New("unexpected query strategy ", config.QueryStrategy)
}
hosts, err := NewStaticHosts(config.StaticHosts)
@@ -81,8 +84,14 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
return nil, errors.New("failed to create hosts").Base(err)
}
clients := []*Client{}
var clients []*Client
domainRuleCount := 0
var defaultTag = config.Tag
if len(config.Tag) == 0 {
defaultTag = generateRandomTag()
}
for _, ns := range config.NameServer {
domainRuleCount += len(ns.PrioritizedDomain)
}
@@ -90,7 +99,6 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
// MatcherInfos is ensured to cover the maximum index domainMatcher could return, where matcher's index starts from 1
matcherInfos := make([]*DomainMatcherInfo, domainRuleCount+1)
domainMatcher := &strmatcher.MatcherGroup{}
geoipContainer := router.GeoIPMatcherContainer{}
for _, ns := range config.NameServer {
clientIdx := len(clients)
@@ -108,7 +116,19 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
case net.IPv4len, net.IPv6len:
myClientIP = net.IP(ns.ClientIp)
}
client, err := NewClient(ctx, ns, myClientIP, geoipContainer, &matcherInfos, updateDomain)
disableCache := config.DisableCache || ns.DisableCache
var tag = defaultTag
if len(ns.Tag) > 0 {
tag = ns.Tag
}
clientIPOption := ResolveIpOptionOverride(ns.QueryStrategy, ipOption)
if !clientIPOption.IPv4Enable && !clientIPOption.IPv6Enable {
return nil, errors.New("no QueryStrategy available for ", ns.Address)
}
client, err := NewClient(ctx, ns, myClientIP, disableCache, tag, clientIPOption, &matcherInfos, updateDomain)
if err != nil {
return nil, errors.New("failed to create client").Base(err)
}
@@ -117,20 +137,19 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
// If there is no DNS client in config, add a `localhost` DNS client
if len(clients) == 0 {
clients = append(clients, NewLocalDNSClient())
clients = append(clients, NewLocalDNSClient(ipOption))
}
return &DNS{
tag: tag,
hosts: hosts,
ipOption: ipOption,
ipOption: &ipOption,
clients: clients,
ctx: ctx,
domainMatcher: domainMatcher,
matcherInfos: matcherInfos,
disableCache: config.DisableCache,
disableFallback: config.DisableFallback,
disableFallbackIfMatch: config.DisableFallbackIfMatch,
checkSystem: checkSystem,
}, nil
}
@@ -152,94 +171,101 @@ func (s *DNS) Close() error {
// IsOwnLink implements proxy.dns.ownLinkVerifier
func (s *DNS) IsOwnLink(ctx context.Context) bool {
inbound := session.InboundFromContext(ctx)
return inbound != nil && inbound.Tag == s.tag
if inbound == nil {
return false
}
for _, client := range s.clients {
if client.tag == inbound.Tag {
return true
}
}
return false
}
// LookupIP implements dns.Client.
func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) {
if domain == "" {
return nil, errors.New("empty domain name")
}
option.IPv4Enable = option.IPv4Enable && s.ipOption.IPv4Enable
option.IPv6Enable = option.IPv6Enable && s.ipOption.IPv6Enable
if !option.IPv4Enable && !option.IPv6Enable {
return nil, dns.ErrEmptyResponse
}
func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, error) {
// Normalize the FQDN form query
domain = strings.TrimSuffix(domain, ".")
if domain == "" {
return nil, 0, errors.New("empty domain name")
}
if s.checkSystem {
supportIPv4, supportIPv6 := checkSystemNetwork()
option.IPv4Enable = option.IPv4Enable && supportIPv4
option.IPv6Enable = option.IPv6Enable && supportIPv6
} else {
option.IPv4Enable = option.IPv4Enable && s.ipOption.IPv4Enable
option.IPv6Enable = option.IPv6Enable && s.ipOption.IPv6Enable
}
if !option.IPv4Enable && !option.IPv6Enable {
return nil, 0, dns.ErrEmptyResponse
}
// 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
break
case len(addrs) == 0: // Domain recorded, but no valid IP returned (e.g. IPv4 address with only IPv6 enabled)
return nil, dns.ErrEmptyResponse
return nil, 0, dns.ErrEmptyResponse
case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Domain replacement
errors.LogInfo(s.ctx, "domain replaced: ", domain, " -> ", addrs[0].Domain())
domain = addrs[0].Domain()
default: // Successfully found ip records in static host
errors.LogInfo(s.ctx, "returning ", len(addrs), " IP(s) for domain ", domain, " -> ", addrs)
return toNetIP(addrs)
ips, err := toNetIP(addrs)
if err != nil {
return nil, 0, err
}
return ips, 10, nil // Hosts ttl is 10
}
// Name servers lookup
errs := []error{}
ctx := session.ContextWithInbound(s.ctx, &session.Inbound{Tag: s.tag})
var errs []error
for _, client := range s.sortClients(domain) {
if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") {
errors.LogDebug(s.ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name())
continue
}
ips, err := client.QueryIP(ctx, domain, option, s.disableCache)
ips, ttl, err := client.QueryIP(s.ctx, domain, option)
if len(ips) > 0 {
return ips, nil
if ttl == 0 {
ttl = 1
}
return ips, ttl, nil
}
if err != nil {
errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name())
errs = append(errs, err)
errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name())
if err == nil {
err = dns.ErrEmptyResponse
}
// 5 for RcodeRefused in miekg/dns, hardcode to reduce binary size
if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch && err != dns.ErrEmptyResponse && dns.RCodeFromError(err) != 5 {
return nil, err
errs = append(errs, err)
if client.IsFinalQuery() {
break
}
}
return nil, errors.New("returning nil for domain ", domain).Base(errors.Combine(errs...))
}
// LookupHosts implements dns.HostsLookup.
func (s *DNS) LookupHosts(domain string) *net.Address {
domain = strings.TrimSuffix(domain, ".")
if domain == "" {
return nil
if len(errs) > 0 {
allErrs := errors.Combine(errs...)
err0 := errs[0]
if errors.AllEqual(err0, allErrs) {
if go_errors.Is(err0, dns.ErrEmptyResponse) {
return nil, 0, dns.ErrEmptyResponse
}
return nil, 0, errors.New("returning nil for domain ", domain).Base(err0)
}
return nil, 0, errors.New("returning nil for domain ", domain).Base(allErrs)
}
// Normalize the FQDN form query
addrs := s.hosts.Lookup(domain, *s.ipOption)
if len(addrs) > 0 {
errors.LogInfo(s.ctx, "domain replaced: ", domain, " -> ", addrs[0].String())
return &addrs[0]
}
return nil
}
// GetIPOption implements ClientWithIPOption.
func (s *DNS) GetIPOption() *dns.IPOption {
return s.ipOption
}
// SetQueryOption implements ClientWithIPOption.
func (s *DNS) SetQueryOption(isIPv4Enable, isIPv6Enable bool) {
s.ipOption.IPv4Enable = isIPv4Enable
s.ipOption.IPv6Enable = isIPv6Enable
}
// SetFakeDNSOption implements ClientWithIPOption.
func (s *DNS) SetFakeDNSOption(isFakeEnable bool) {
s.ipOption.FakeEnable = isFakeEnable
return nil, 0, dns.ErrEmptyResponse
}
func (s *DNS) sortClients(domain string) []*Client {
@@ -250,7 +276,11 @@ func (s *DNS) sortClients(domain string) []*Client {
// Priority domain matching
hasMatch := false
for _, match := range s.domainMatcher.Match(domain) {
MatchSlice := s.domainMatcher.Match(domain)
sort.Slice(MatchSlice, func(i, j int) bool {
return MatchSlice[i] < MatchSlice[j]
})
for _, match := range MatchSlice {
info := s.matcherInfos[match]
client := s.clients[info.clientIdx]
domainRule := client.domains[info.domainRuleIdx]
@@ -297,3 +327,22 @@ func init() {
return New(ctx, config.(*Config))
}))
}
func checkSystemNetwork() (supportIPv4 bool, supportIPv6 bool) {
conn4, err4 := net.Dial("udp4", "8.8.8.8:53")
if err4 != nil {
supportIPv4 = false
} else {
supportIPv4 = true
conn4.Close()
}
conn6, err6 := net.Dial("udp6", "[2001:4860:4860::8888]:53")
if err6 != nil {
supportIPv6 = false
} else {
supportIPv6 = true
conn6.Close()
}
return
}

View File

@@ -76,6 +76,9 @@ func (*staticHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
case q.Name == "notexist.google.com." && q.Qtype == dns.TypeAAAA:
ans.MsgHdr.Rcode = dns.RcodeNameError
case q.Name == "notexist.google.com." && q.Qtype == dns.TypeA:
ans.MsgHdr.Rcode = dns.RcodeNameError
case q.Name == "hostname." && q.Qtype == dns.TypeA:
rr, _ := dns.NewRR("hostname. IN A 127.0.0.1")
ans.Answer = append(ans.Answer, rr)
@@ -117,7 +120,6 @@ func TestUDPServerSubnet(t *testing.T) {
Handler: &staticHandler{},
UDPSize: 1200,
}
go dnsServer.ListenAndServe()
time.Sleep(time.Second)
@@ -155,7 +157,7 @@ func TestUDPServerSubnet(t *testing.T) {
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
ips, err := client.LookupIP("google.com", feature_dns.IPOption{
ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -216,7 +218,7 @@ func TestUDPServer(t *testing.T) {
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
{
ips, err := client.LookupIP("google.com", feature_dns.IPOption{
ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -231,7 +233,7 @@ func TestUDPServer(t *testing.T) {
}
{
ips, err := client.LookupIP("facebook.com", feature_dns.IPOption{
ips, _, err := client.LookupIP("facebook.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -246,7 +248,7 @@ func TestUDPServer(t *testing.T) {
}
{
_, err := client.LookupIP("notexist.google.com", feature_dns.IPOption{
_, _, err := client.LookupIP("notexist.google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -260,7 +262,7 @@ func TestUDPServer(t *testing.T) {
}
{
ips, err := client.LookupIP("ipv4only.google.com", feature_dns.IPOption{
ips, _, err := client.LookupIP("ipv4only.google.com", feature_dns.IPOption{
IPv4Enable: false,
IPv6Enable: true,
FakeEnable: false,
@@ -276,7 +278,7 @@ func TestUDPServer(t *testing.T) {
dnsServer.Shutdown()
{
ips, err := client.LookupIP("google.com", feature_dns.IPOption{
ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -357,7 +359,7 @@ func TestPrioritizedDomain(t *testing.T) {
startTime := time.Now()
{
ips, err := client.LookupIP("google.com", feature_dns.IPOption{
ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -423,7 +425,7 @@ func TestUDPServerIPv6(t *testing.T) {
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
{
ips, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{
ips, _, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{
IPv4Enable: false,
IPv6Enable: true,
FakeEnable: false,
@@ -492,7 +494,7 @@ func TestStaticHostDomain(t *testing.T) {
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
{
ips, err := client.LookupIP("example.com", feature_dns.IPOption{
ips, _, err := client.LookupIP("example.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -537,7 +539,7 @@ func TestIPMatch(t *testing.T) {
},
Port: uint32(port),
},
Geoip: []*router.GeoIP{
ExpectedGeoip: []*router.GeoIP{
{
CountryCode: "local",
Cidr: []*router.CIDR{
@@ -561,7 +563,7 @@ func TestIPMatch(t *testing.T) {
},
Port: uint32(port),
},
Geoip: []*router.GeoIP{
ExpectedGeoip: []*router.GeoIP{
{
CountryCode: "test",
Cidr: []*router.CIDR{
@@ -603,7 +605,7 @@ func TestIPMatch(t *testing.T) {
startTime := time.Now()
{
ips, err := client.LookupIP("google.com", feature_dns.IPOption{
ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -665,7 +667,7 @@ func TestLocalDomain(t *testing.T) {
// Equivalent of dotless:localhost
{Type: DomainMatchingType_Regex, Domain: "^[^.]*localhost[^.]*$"},
},
Geoip: []*router.GeoIP{
ExpectedGeoip: []*router.GeoIP{
{ // Will match localhost, localhost-a and localhost-b,
CountryCode: "local",
Cidr: []*router.CIDR{
@@ -726,7 +728,7 @@ func TestLocalDomain(t *testing.T) {
startTime := time.Now()
{ // Will match dotless:
ips, err := client.LookupIP("hostname", feature_dns.IPOption{
ips, _, err := client.LookupIP("hostname", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -741,7 +743,7 @@ func TestLocalDomain(t *testing.T) {
}
{ // Will match domain:local
ips, err := client.LookupIP("hostname.local", feature_dns.IPOption{
ips, _, err := client.LookupIP("hostname.local", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -756,7 +758,7 @@ func TestLocalDomain(t *testing.T) {
}
{ // Will match static ip
ips, err := client.LookupIP("hostnamestatic", feature_dns.IPOption{
ips, _, err := client.LookupIP("hostnamestatic", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -771,7 +773,7 @@ func TestLocalDomain(t *testing.T) {
}
{ // Will match domain replacing
ips, err := client.LookupIP("hostnamealias", feature_dns.IPOption{
ips, _, err := client.LookupIP("hostnamealias", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -785,8 +787,8 @@ func TestLocalDomain(t *testing.T) {
}
}
{ // Will match dotless:localhost, but not expectIPs: 127.0.0.2, 127.0.0.3, then matches at dotless:
ips, err := client.LookupIP("localhost", feature_dns.IPOption{
{ // Will match dotless:localhost, but not expectedIPs: 127.0.0.2, 127.0.0.3, then matches at dotless:
ips, _, err := client.LookupIP("localhost", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -800,8 +802,8 @@ func TestLocalDomain(t *testing.T) {
}
}
{ // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3
ips, err := client.LookupIP("localhost-a", feature_dns.IPOption{
{ // Will match dotless:localhost, and expectedIPs: 127.0.0.2, 127.0.0.3
ips, _, err := client.LookupIP("localhost-a", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -815,8 +817,8 @@ func TestLocalDomain(t *testing.T) {
}
}
{ // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3
ips, err := client.LookupIP("localhost-b", feature_dns.IPOption{
{ // Will match dotless:localhost, and expectedIPs: 127.0.0.2, 127.0.0.3
ips, _, err := client.LookupIP("localhost-b", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -831,7 +833,7 @@ func TestLocalDomain(t *testing.T) {
}
{ // Will match dotless:
ips, err := client.LookupIP("Mijia Cloud", feature_dns.IPOption{
ips, _, err := client.LookupIP("Mijia Cloud", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -895,7 +897,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
Domain: "google.com",
},
},
Geoip: []*router.GeoIP{
ExpectedGeoip: []*router.GeoIP{
{ // Will only match 8.8.8.8 and 8.8.4.4
Cidr: []*router.CIDR{
{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
@@ -920,7 +922,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
Domain: "google.com",
},
},
Geoip: []*router.GeoIP{
ExpectedGeoip: []*router.GeoIP{
{ // Will match 8.8.8.8 and 8.8.8.7, etc
Cidr: []*router.CIDR{
{Ip: []byte{8, 8, 8, 7}, Prefix: 24},
@@ -944,7 +946,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
Domain: "api.google.com",
},
},
Geoip: []*router.GeoIP{
ExpectedGeoip: []*router.GeoIP{
{ // Will only match 8.8.7.7 (api.google.com)
Cidr: []*router.CIDR{
{Ip: []byte{8, 8, 7, 7}, Prefix: 32},
@@ -968,7 +970,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
Domain: "v2.api.google.com",
},
},
Geoip: []*router.GeoIP{
ExpectedGeoip: []*router.GeoIP{
{ // Will only match 8.8.7.8 (v2.api.google.com)
Cidr: []*router.CIDR{
{Ip: []byte{8, 8, 7, 8}, Prefix: 32},
@@ -997,7 +999,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
startTime := time.Now()
{ // Will match server 1,2 and server 1 returns expected ip
ips, err := client.LookupIP("google.com", feature_dns.IPOption{
ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -1012,7 +1014,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
}
{ // Will match server 1,2 and server 1 returns unexpected ip, then server 2 returns expected one
ips, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{
ips, _, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: false,
FakeEnable: false,
@@ -1027,7 +1029,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
}
{ // Will match server 3,1,2 and server 3 returns expected one
ips, err := client.LookupIP("api.google.com", feature_dns.IPOption{
ips, _, err := client.LookupIP("api.google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
@@ -1042,7 +1044,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
}
{ // Will match server 4,3,1,2 and server 4 returns expected one
ips, err := client.LookupIP("v2.api.google.com", feature_dns.IPOption{
ips, _, err := client.LookupIP("v2.api.google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,

View File

@@ -31,30 +31,34 @@ type record struct {
// IPRecord is a cacheable item for a resolved domain
type IPRecord struct {
ReqID uint16
IP []net.Address
Expire time.Time
RCode dnsmessage.RCode
ReqID uint16
IP []net.IP
Expire time.Time
RCode dnsmessage.RCode
RawHeader *dnsmessage.Header
}
func (r *IPRecord) getIPs() ([]net.Address, error) {
if r == nil || r.Expire.Before(time.Now()) {
return nil, errRecordNotFound
func (r *IPRecord) getIPs() ([]net.IP, uint32, error) {
if r == nil {
return nil, 0, errRecordNotFound
}
untilExpire := time.Until(r.Expire).Seconds()
if untilExpire <= 0 {
return nil, 0, errRecordNotFound
}
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 {
return nil, dns_feature.RCodeError(r.RCode)
return nil, ttl, dns_feature.RCodeError(r.RCode)
}
if len(r.IP) == 0 {
return nil, ttl, dns_feature.ErrEmptyResponse
}
return r.IP, nil
}
func isNewer(baseRec *IPRecord, newRec *IPRecord) bool {
if newRec == nil {
return false
}
if baseRec == nil {
return true
}
return baseRec.Expire.Before(newRec.Expire)
return r.IP, ttl, nil
}
var errRecordNotFound = errors.New("record not found")
@@ -67,49 +71,59 @@ type dnsRequest struct {
msg *dnsmessage.Message
}
func genEDNS0Options(clientIP net.IP) *dnsmessage.Resource {
if len(clientIP) == 0 {
func genEDNS0Options(clientIP net.IP, padding int) *dnsmessage.Resource {
if len(clientIP) == 0 && padding == 0 {
return nil
}
var netmask int
var family uint16
if len(clientIP) == 4 {
family = 1
netmask = 24 // 24 for IPV4, 96 for IPv6
} else {
family = 2
netmask = 96
}
b := make([]byte, 4)
binary.BigEndian.PutUint16(b[0:], family)
b[2] = byte(netmask)
b[3] = 0
switch family {
case 1:
ip := clientIP.To4().Mask(net.CIDRMask(netmask, net.IPv4len*8))
needLength := (netmask + 8 - 1) / 8 // division rounding up
b = append(b, ip[:needLength]...)
case 2:
ip := clientIP.Mask(net.CIDRMask(netmask, net.IPv6len*8))
needLength := (netmask + 8 - 1) / 8 // division rounding up
b = append(b, ip[:needLength]...)
}
const EDNS0SUBNET = 0x08
const EDNS0SUBNET = 0x8
const EDNS0PADDING = 0xc
opt := new(dnsmessage.Resource)
common.Must(opt.Header.SetEDNS0(1350, 0xfe00, true))
body := dnsmessage.OPTResource{}
opt.Body = &body
opt.Body = &dnsmessage.OPTResource{
Options: []dnsmessage.Option{
{
if len(clientIP) != 0 {
var netmask int
var family uint16
if len(clientIP) == 4 {
family = 1
netmask = 24 // 24 for IPV4, 96 for IPv6
} else {
family = 2
netmask = 96
}
b := make([]byte, 4)
binary.BigEndian.PutUint16(b[0:], family)
b[2] = byte(netmask)
b[3] = 0
switch family {
case 1:
ip := clientIP.To4().Mask(net.CIDRMask(netmask, net.IPv4len*8))
needLength := (netmask + 8 - 1) / 8 // division rounding up
b = append(b, ip[:needLength]...)
case 2:
ip := clientIP.Mask(net.CIDRMask(netmask, net.IPv6len*8))
needLength := (netmask + 8 - 1) / 8 // division rounding up
b = append(b, ip[:needLength]...)
}
body.Options = append(body.Options,
dnsmessage.Option{
Code: EDNS0SUBNET,
Data: b,
},
},
})
}
if padding != 0 {
body.Options = append(body.Options,
dnsmessage.Option{
Code: EDNS0PADDING,
Data: make([]byte, padding),
})
}
return opt
@@ -179,9 +193,10 @@ func parseResponse(payload []byte) (*IPRecord, error) {
now := time.Now()
ipRecord := &IPRecord{
ReqID: h.ID,
RCode: h.RCode,
Expire: now.Add(time.Second * 600),
ReqID: h.ID,
RCode: h.RCode,
Expire: now.Add(time.Second * dns_feature.DefaultTTL),
RawHeader: &h,
}
L:
@@ -196,7 +211,7 @@ L:
ttl := ah.TTL
if ttl == 0 {
ttl = 600
ttl = 1
}
expire := now.Add(time.Duration(ttl) * time.Second)
if ipRecord.Expire.After(expire) {
@@ -210,14 +225,17 @@ L:
errors.LogInfoInner(context.Background(), err, "failed to parse A record for domain: ", ah.Name)
break L
}
ipRecord.IP = append(ipRecord.IP, net.IPAddress(ans.A[:]))
ipRecord.IP = append(ipRecord.IP, net.IPAddress(ans.A[:]).IP())
case dnsmessage.TypeAAAA:
ans, err := parser.AAAAResource()
if err != nil {
errors.LogInfoInner(context.Background(), err, "failed to parse AAAA record for domain: ", ah.Name)
break L
}
ipRecord.IP = append(ipRecord.IP, net.IPAddress(ans.AAAA[:]))
newIP := net.IPAddress(ans.AAAA[:]).IP()
if len(newIP) == net.IPv6len {
ipRecord.IP = append(ipRecord.IP, newIP)
}
default:
if err := parser.SkipAnswer(); err != nil {
errors.LogInfoInner(context.Background(), err, "failed to skip answer")

View File

@@ -18,31 +18,31 @@ func Test_parseResponse(t *testing.T) {
ans := new(dns.Msg)
ans.Id = 0
p = append(p, common.Must2(ans.Pack()).([]byte))
p = append(p, common.Must2(ans.Pack()))
p = append(p, []byte{})
ans = new(dns.Msg)
ans.Id = 1
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 fake.google.com")).(dns.RR),
common.Must2(dns.NewRR("google.com. IN A 8.8.8.8")).(dns.RR),
common.Must2(dns.NewRR("google.com. IN A 8.8.4.4")).(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")),
common.Must2(dns.NewRR("google.com. IN A 8.8.8.8")),
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.Id = 2
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 fake.google.com")).(dns.RR),
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")).(dns.RR),
common.Must2(dns.NewRR("google.com. IN CNAME test.google.com")).(dns.RR),
common.Must2(dns.NewRR("google.com. IN AAAA 2001::123:8888")).(dns.RR),
common.Must2(dns.NewRR("google.com. IN AAAA 2001::123:8844")).(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")),
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
common.Must2(dns.NewRR("google.com. IN CNAME test.google.com")),
common.Must2(dns.NewRR("google.com. IN AAAA 2001::123:8888")),
common.Must2(dns.NewRR("google.com. IN AAAA 2001::123:8844")),
)
p = append(p, common.Must2(ans.Pack()).([]byte))
p = append(p, common.Must2(ans.Pack()))
tests := []struct {
name string
@@ -51,7 +51,7 @@ func Test_parseResponse(t *testing.T) {
}{
{
"empty",
&IPRecord{0, []net.Address(nil), time.Time{}, dnsmessage.RCodeSuccess},
&IPRecord{0, []net.IP(nil), time.Time{}, dnsmessage.RCodeSuccess, nil},
false,
},
{
@@ -63,15 +63,16 @@ func Test_parseResponse(t *testing.T) {
"a record",
&IPRecord{
1,
[]net.Address{net.ParseAddress("8.8.8.8"), net.ParseAddress("8.8.4.4")},
[]net.IP{net.ParseIP("8.8.8.8"), net.ParseIP("8.8.4.4")},
time.Time{},
dnsmessage.RCodeSuccess,
nil,
},
false,
},
{
"aaaa record",
&IPRecord{2, []net.Address{net.ParseAddress("2001::123:8888"), net.ParseAddress("2001::123:8844")}, time.Time{}, dnsmessage.RCodeSuccess},
&IPRecord{2, []net.IP{net.ParseIP("2001::123:8888"), net.ParseIP("2001::123:8844")}, time.Time{}, dnsmessage.RCodeSuccess, nil},
false,
},
}
@@ -84,8 +85,9 @@ func Test_parseResponse(t *testing.T) {
}
if got != nil {
// reset the time
// reset the time and RawHeader
got.Expire = time.Time{}
got.RawHeader = nil
}
if cmp.Diff(got, tt.want) != "" {
t.Error(cmp.Diff(got, tt.want))
@@ -154,7 +156,7 @@ func Test_genEDNS0Options(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := genEDNS0Options(tt.args.clientIP); got == nil {
if got := genEDNS0Options(tt.args.clientIP, 0); got == nil {
t.Errorf("genEDNS0Options() = %v, want %v", got, tt.want)
}
})

View File

@@ -2,6 +2,7 @@ package dns
import (
"context"
"strconv"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
@@ -32,7 +33,15 @@ func NewStaticHosts(hosts []*Config_HostMapping) (*StaticHosts, error) {
ips := make([]net.Address, 0, len(mapping.Ip)+1)
switch {
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:
for _, ip := range mapping.Ip {
addr := net.IPAddress(ip)
@@ -41,8 +50,6 @@ func NewStaticHosts(hosts []*Config_HostMapping) (*StaticHosts, error) {
}
ips = append(ips, addr)
}
default:
return nil, errors.New("neither IP address nor proxied domain specified for domain: ", mapping.Domain).AtWarning()
}
sh.ips[id] = ips
@@ -61,33 +68,51 @@ func filterIP(ips []net.Address, option dns.IPOption) []net.Address {
return filtered
}
func (h *StaticHosts) lookupInternal(domain string) []net.Address {
var ips []net.Address
func (h *StaticHosts) lookupInternal(domain string) ([]net.Address, error) {
ips := make([]net.Address, 0)
found := false
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]...)
found = true
}
return ips
if !found {
return nil, nil
}
return ips, nil
}
func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) []net.Address {
switch addrs := h.lookupInternal(domain); {
func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) ([]net.Address, error) {
switch addrs, err := h.lookupInternal(domain); {
case err != nil:
return nil, err
case len(addrs) == 0: // Not recorded in static hosts, return nil
return nil
return addrs, nil
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")
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 {
return unwrapped
return unwrapped, nil
}
}
return addrs
return addrs, nil
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.
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)
}

View File

@@ -12,6 +12,11 @@ import (
func TestStaticHosts(t *testing.T) {
pb := []*Config_HostMapping{
{
Type: DomainMatchingType_Subdomain,
Domain: "lan",
ProxiedDomain: "#3",
},
{
Type: DomainMatchingType_Full,
Domain: "example.com",
@@ -54,7 +59,14 @@ func TestStaticHosts(t *testing.T) {
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,
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,
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,
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,
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,
IPv6Enable: true,
})

View File

@@ -9,6 +9,7 @@ import (
"github.com/xtls/xray-core/app/router"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/strmatcher"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/dns"
@@ -20,22 +21,27 @@ type Server interface {
// Name of the Client.
Name() string
// QueryIP sends IP queries to its configured server.
QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns.IPOption, disableCache bool) ([]net.IP, error)
QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error)
}
// Client is the interface for DNS client.
type Client struct {
server Server
clientIP net.IP
skipFallback bool
domains []string
expectIPs []*router.GeoIPMatcher
server Server
skipFallback bool
domains []string
expectedIPs []*router.GeoIPMatcher
unexpectedIPs []*router.GeoIPMatcher
actPrior bool
actUnprior bool
tag string
timeoutMs time.Duration
finalQuery bool
ipOption *dns.IPOption
checkSystem bool
}
var errExpectedIPNonMatch = errors.New("expectIPs not match")
// NewServer creates a name server object according to the network destination url.
func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dispatcher, queryStrategy QueryStrategy) (Server, error) {
func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dispatcher, disableCache bool, clientIP net.IP) (Server, error) {
if address := dest.Address; address.Family().IsDomain() {
u, err := url.Parse(address.Domain())
if err != nil {
@@ -44,21 +50,28 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
switch {
case strings.EqualFold(u.String(), "localhost"):
return NewLocalNameServer(), nil
case strings.EqualFold(u.Scheme, "https"): // DOH Remote mode
return NewDoHNameServer(u, dispatcher, queryStrategy)
case strings.EqualFold(u.Scheme, "https+local"): // DOH Local mode
return NewDoHLocalNameServer(u, queryStrategy), nil
case strings.EqualFold(u.Scheme, "https"): // DNS-over-HTTPS Remote mode
return NewDoHNameServer(u, dispatcher, false, disableCache, clientIP), nil
case strings.EqualFold(u.Scheme, "h2c"): // DNS-over-HTTPS h2c Remote mode
return NewDoHNameServer(u, dispatcher, true, disableCache, clientIP), nil
case strings.EqualFold(u.Scheme, "https+local"): // DNS-over-HTTPS Local mode
return NewDoHNameServer(u, nil, false, disableCache, clientIP), nil
case strings.EqualFold(u.Scheme, "h2c+local"): // DNS-over-HTTPS h2c Local mode
return NewDoHNameServer(u, nil, true, disableCache, clientIP), nil
case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode
return NewQUICNameServer(u, queryStrategy)
return NewQUICNameServer(u, disableCache, clientIP)
case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode
return NewTCPNameServer(u, dispatcher, queryStrategy)
return NewTCPNameServer(u, dispatcher, disableCache, clientIP)
case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode
return NewTCPLocalNameServer(u, queryStrategy)
return NewTCPLocalNameServer(u, disableCache, clientIP)
case strings.EqualFold(u.String(), "fakedns"):
var fd dns.FakeDNSEngine
core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) { // FakeDNSEngine is optional
err = core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
fd = fdns
})
if err != nil {
return nil, err
}
return NewFakeDNSServer(fd), nil
}
}
@@ -66,7 +79,7 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
dest.Network = net.Network_UDP
}
if dest.Network == net.Network_UDP { // UDP classic DNS mode
return NewClassicNameServer(dest, dispatcher, queryStrategy), nil
return NewClassicNameServer(dest, dispatcher, disableCache, clientIP), nil
}
return nil, errors.New("No available name server could be created from ", dest).AtWarning()
}
@@ -76,7 +89,9 @@ func NewClient(
ctx context.Context,
ns *NameServer,
clientIP net.IP,
container router.GeoIPMatcherContainer,
disableCache bool,
tag string,
ipOption dns.IPOption,
matcherInfos *[]*DomainMatcherInfo,
updateDomainRule func(strmatcher.Matcher, int, []*DomainMatcherInfo) error,
) (*Client, error) {
@@ -84,7 +99,7 @@ func NewClient(
err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
// Create a new server for each client for now
server, err := NewServer(ctx, ns.Address.AsDestination(), dispatcher, ns.GetQueryStrategy())
server, err := NewServer(ctx, ns.Address.AsDestination(), dispatcher, disableCache, clientIP)
if err != nil {
return errors.New("failed to create nameserver").Base(err).AtWarning()
}
@@ -139,13 +154,23 @@ func NewClient(
}
// Establish expected IPs
var matchers []*router.GeoIPMatcher
for _, geoip := range ns.Geoip {
matcher, err := container.Add(geoip)
var expectedMatchers []*router.GeoIPMatcher
for _, geoip := range ns.ExpectedGeoip {
matcher, err := router.GlobalGeoIPContainer.Add(geoip)
if err != nil {
return errors.New("failed to create ip matcher").Base(err).AtWarning()
return errors.New("failed to create expected ip matcher").Base(err).AtWarning()
}
matchers = append(matchers, matcher)
expectedMatchers = append(expectedMatchers, matcher)
}
// Establish unexpected IPs
var unexpectedMatchers []*router.GeoIPMatcher
for _, geoip := range ns.UnexpectedGeoip {
matcher, err := router.GlobalGeoIPContainer.Add(geoip)
if err != nil {
return errors.New("failed to create unexpected ip matcher").Base(err).AtWarning()
}
unexpectedMatchers = append(unexpectedMatchers, matcher)
}
if len(clientIP) > 0 {
@@ -153,15 +178,29 @@ func NewClient(
case *net.IPOrDomain_Domain:
errors.LogInfo(ctx, "DNS: client ", ns.Address.Address.GetDomain(), " uses clientIP ", clientIP.String())
case *net.IPOrDomain_Ip:
errors.LogInfo(ctx, "DNS: client ", ns.Address.Address.GetIp(), " uses clientIP ", clientIP.String())
errors.LogInfo(ctx, "DNS: client ", net.IP(ns.Address.Address.GetIp()), " uses clientIP ", clientIP.String())
}
}
var timeoutMs = 4000 * time.Millisecond
if ns.TimeoutMs > 0 {
timeoutMs = time.Duration(ns.TimeoutMs) * time.Millisecond
}
checkSystem := ns.QueryStrategy == QueryStrategy_USE_SYS
client.server = server
client.clientIP = clientIP
client.skipFallback = ns.SkipFallback
client.domains = rules
client.expectIPs = matchers
client.expectedIPs = expectedMatchers
client.unexpectedIPs = unexpectedMatchers
client.actPrior = ns.ActPrior
client.actUnprior = ns.ActUnprior
client.tag = tag
client.timeoutMs = timeoutMs
client.finalQuery = ns.FinalQuery
client.ipOption = &ipOption
client.checkSystem = checkSystem
return nil
})
return client, err
@@ -172,43 +211,79 @@ func (c *Client) Name() string {
return c.server.Name()
}
func (c *Client) IsFinalQuery() bool {
return c.finalQuery
}
// QueryIP sends DNS query to the name server with the client's IP.
func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption, disableCache bool) ([]net.IP, error) {
ctx, cancel := context.WithTimeout(ctx, 4*time.Second)
ips, err := c.server.QueryIP(ctx, domain, c.clientIP, option, disableCache)
func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error) {
if c.checkSystem {
supportIPv4, supportIPv6 := checkSystemNetwork()
option.IPv4Enable = option.IPv4Enable && supportIPv4
option.IPv6Enable = option.IPv6Enable && supportIPv6
} else {
option.IPv4Enable = option.IPv4Enable && c.ipOption.IPv4Enable
option.IPv6Enable = option.IPv6Enable && c.ipOption.IPv6Enable
}
if !option.IPv4Enable && !option.IPv6Enable {
return nil, 0, dns.ErrEmptyResponse
}
ctx, cancel := context.WithTimeout(ctx, c.timeoutMs)
ctx = session.ContextWithInbound(ctx, &session.Inbound{Tag: c.tag})
ips, ttl, err := c.server.QueryIP(ctx, domain, option)
cancel()
if err != nil {
return ips, err
return nil, 0, err
}
return c.MatchExpectedIPs(domain, ips)
}
// MatchExpectedIPs matches queried domain IPs with expected IPs and returns matched ones.
func (c *Client) MatchExpectedIPs(domain string, ips []net.IP) ([]net.IP, error) {
if len(c.expectIPs) == 0 {
return ips, nil
if len(ips) == 0 {
return nil, 0, dns.ErrEmptyResponse
}
newIps := []net.IP{}
for _, ip := range ips {
for _, matcher := range c.expectIPs {
if matcher.Match(ip) {
newIps = append(newIps, ip)
break
}
if len(c.expectedIPs) > 0 && !c.actPrior {
ips = router.MatchIPs(c.expectedIPs, ips, false)
errors.LogDebug(context.Background(), "domain ", domain, " expectedIPs ", ips, " matched at server ", c.Name())
if len(ips) == 0 {
return nil, 0, dns.ErrEmptyResponse
}
}
if len(newIps) == 0 {
return nil, errExpectedIPNonMatch
if len(c.unexpectedIPs) > 0 && !c.actUnprior {
ips = router.MatchIPs(c.unexpectedIPs, ips, true)
errors.LogDebug(context.Background(), "domain ", domain, " unexpectedIPs ", ips, " matched at server ", c.Name())
if len(ips) == 0 {
return nil, 0, dns.ErrEmptyResponse
}
}
errors.LogDebug(context.Background(), "domain ", domain, " expectIPs ", newIps, " matched at server ", c.Name())
return newIps, nil
if len(c.expectedIPs) > 0 && c.actPrior {
ipsNew := router.MatchIPs(c.expectedIPs, ips, false)
if len(ipsNew) > 0 {
ips = ipsNew
errors.LogDebug(context.Background(), "domain ", domain, " priorIPs ", ips, " matched at server ", c.Name())
}
}
if len(c.unexpectedIPs) > 0 && c.actUnprior {
ipsNew := router.MatchIPs(c.unexpectedIPs, ips, true)
if len(ipsNew) > 0 {
ips = ipsNew
errors.LogDebug(context.Background(), "domain ", domain, " unpriorIPs ", ips, " matched at server ", c.Name())
}
}
return ips, ttl, nil
}
func ResolveIpOptionOverride(queryStrategy QueryStrategy, ipOption dns.IPOption) dns.IPOption {
switch queryStrategy {
case QueryStrategy_USE_IP:
return ipOption
case QueryStrategy_USE_SYS:
return ipOption
case QueryStrategy_USE_IP4:
return dns.IPOption{
IPv4Enable: ipOption.IPv4Enable,

View File

@@ -3,237 +3,136 @@ package dns
import (
"bytes"
"context"
"crypto/tls"
go_errors "errors"
"fmt"
"io"
"net/http"
"net/url"
"sync"
"sync/atomic"
"strings"
"time"
utls "github.com/refraction-networking/utls"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/crypto"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/protocol/dns"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal/pubsub"
"github.com/xtls/xray-core/common/task"
dns_feature "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport/internet"
"golang.org/x/net/dns/dnsmessage"
"golang.org/x/net/http2"
)
// DoHNameServer implemented DNS over HTTPS (RFC8484) Wire Format,
// which is compatible with traditional dns over udp(RFC1035),
// thus most of the DOH implementation is copied from udpns.go
type DoHNameServer struct {
dispatcher routing.Dispatcher
sync.RWMutex
ips map[string]*record
pub *pubsub.Service
cleanup *task.Periodic
reqID uint32
httpClient *http.Client
dohURL string
name string
queryStrategy QueryStrategy
cacheController *CacheController
httpClient *http.Client
dohURL string
clientIP net.IP
}
// NewDoHNameServer creates DOH server object for remote resolving.
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, queryStrategy QueryStrategy) (*DoHNameServer, error) {
errors.LogInfo(context.Background(), "DNS: created Remote DOH client for ", url.String())
s := baseDOHNameServer(url, "DOH", queryStrategy)
s.dispatcher = dispatcher
tr := &http.Transport{
MaxIdleConns: 30,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 30 * time.Second,
ForceAttemptHTTP2: true,
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
dest, err := net.ParseDestination(network + ":" + addr)
if err != nil {
return nil, err
}
link, err := s.dispatcher.Dispatch(toDnsContext(ctx, s.dohURL), dest)
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
if err != nil {
return nil, err
}
cc := common.ChainedClosable{}
if cw, ok := link.Writer.(common.Closable); ok {
cc = append(cc, cw)
}
if cr, ok := link.Reader.(common.Closable); ok {
cc = append(cc, cr)
}
return cnc.NewConnection(
cnc.ConnectionInputMulti(link.Writer),
cnc.ConnectionOutputMulti(link.Reader),
cnc.ConnectionOnClose(cc),
), nil
},
}
s.httpClient = &http.Client{
Timeout: time.Second * 180,
Transport: tr,
}
return s, nil
}
// NewDoHLocalNameServer creates DOH client object for local resolving
func NewDoHLocalNameServer(url *url.URL, queryStrategy QueryStrategy) *DoHNameServer {
// NewDoHNameServer creates DOH/DOHL client object for remote/local resolving.
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, h2c bool, disableCache bool, clientIP net.IP) *DoHNameServer {
url.Scheme = "https"
s := baseDOHNameServer(url, "DOHL", queryStrategy)
tr := &http.Transport{
IdleConnTimeout: 90 * time.Second,
ForceAttemptHTTP2: true,
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
dest, err := net.ParseDestination(network + ":" + addr)
if err != nil {
return nil, err
}
conn, err := internet.DialSystem(ctx, dest, nil)
log.Record(&log.AccessMessage{
From: "DNS",
To: s.dohURL,
Status: log.AccessAccepted,
Detour: "local",
})
if err != nil {
return nil, err
}
return conn, nil
},
mode := "DOH"
if dispatcher == nil {
mode = "DOHL"
}
errors.LogInfo(context.Background(), "DNS: created ", mode, " client for ", url.String(), ", with h2c ", h2c)
s := &DoHNameServer{
cacheController: NewCacheController(mode+"//"+url.Host, disableCache),
dohURL: url.String(),
clientIP: clientIP,
}
s.httpClient = &http.Client{
Timeout: time.Second * 180,
Transport: tr,
}
errors.LogInfo(context.Background(), "DNS: created Local DOH client for ", url.String())
return s
}
func baseDOHNameServer(url *url.URL, prefix string, queryStrategy QueryStrategy) *DoHNameServer {
s := &DoHNameServer{
ips: make(map[string]*record),
pub: pubsub.NewService(),
name: prefix + "//" + url.Host,
dohURL: url.String(),
queryStrategy: queryStrategy,
}
s.cleanup = &task.Periodic{
Interval: time.Minute,
Execute: s.Cleanup,
Transport: &http2.Transport{
IdleConnTimeout: net.ConnIdleTimeout,
ReadIdleTimeout: net.ChromeH2KeepAlivePeriod,
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
dest, err := net.ParseDestination(network + ":" + addr)
if err != nil {
return nil, err
}
var conn net.Conn
if dispatcher != nil {
dnsCtx := toDnsContext(ctx, s.dohURL)
if h2c {
dnsCtx = session.ContextWithMitmAlpn11(dnsCtx, false) // for insurance
dnsCtx = session.ContextWithMitmServerName(dnsCtx, url.Hostname())
}
link, err := dispatcher.Dispatch(dnsCtx, dest)
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
if err != nil {
return nil, err
}
cc := common.ChainedClosable{}
if cw, ok := link.Writer.(common.Closable); ok {
cc = append(cc, cw)
}
if cr, ok := link.Reader.(common.Closable); ok {
cc = append(cc, cr)
}
conn = cnc.NewConnection(
cnc.ConnectionInputMulti(link.Writer),
cnc.ConnectionOutputMulti(link.Reader),
cnc.ConnectionOnClose(cc),
)
} else {
log.Record(&log.AccessMessage{
From: "DNS",
To: s.dohURL,
Status: log.AccessAccepted,
Detour: "local",
})
conn, err = internet.DialSystem(ctx, dest, nil)
if err != nil {
return nil, err
}
}
if !h2c {
conn = utls.UClient(conn, &utls.Config{ServerName: url.Hostname()}, utls.HelloChrome_Auto)
if err := conn.(*utls.UConn).HandshakeContext(ctx); err != nil {
return nil, err
}
}
return conn, nil
},
},
}
return s
}
// Name implements Server.
func (s *DoHNameServer) Name() string {
return s.name
}
// Cleanup clears expired items from cache
func (s *DoHNameServer) Cleanup() error {
now := time.Now()
s.Lock()
defer s.Unlock()
if len(s.ips) == 0 {
return errors.New("nothing to do. stopping...")
}
for domain, record := range s.ips {
if record.A != nil && record.A.Expire.Before(now) {
record.A = nil
}
if record.AAAA != nil && record.AAAA.Expire.Before(now) {
record.AAAA = nil
}
if record.A == nil && record.AAAA == nil {
errors.LogDebug(context.Background(), s.name, " cleanup ", domain)
delete(s.ips, domain)
} else {
s.ips[domain] = record
}
}
if len(s.ips) == 0 {
s.ips = make(map[string]*record)
}
return nil
}
func (s *DoHNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) {
elapsed := time.Since(req.start)
s.Lock()
rec, found := s.ips[req.domain]
if !found {
rec = &record{}
}
updated := false
switch req.reqType {
case dnsmessage.TypeA:
if isNewer(rec.A, ipRec) {
rec.A = ipRec
updated = true
}
case dnsmessage.TypeAAAA:
addr := make([]net.Address, 0, len(ipRec.IP))
for _, ip := range ipRec.IP {
if len(ip.IP()) == net.IPv6len {
addr = append(addr, ip)
}
}
ipRec.IP = addr
if isNewer(rec.AAAA, ipRec) {
rec.AAAA = ipRec
updated = true
}
}
errors.LogInfo(context.Background(), s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed)
if updated {
s.ips[req.domain] = rec
}
switch req.reqType {
case dnsmessage.TypeA:
s.pub.Publish(req.domain+"4", nil)
case dnsmessage.TypeAAAA:
s.pub.Publish(req.domain+"6", nil)
}
s.Unlock()
common.Must(s.cleanup.Start())
return s.cacheController.name
}
func (s *DoHNameServer) newReqID() uint16 {
return uint16(atomic.AddUint32(&s.reqID, 1))
return 0
}
func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.name, " querying: ", domain)
func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying: ", domain)
if s.name+"." == "DOH//"+domain {
errors.LogError(ctx, s.name, " tries to resolve itself! Use IP or set \"hosts\" instead.")
if s.Name()+"." == "DOH//"+domain {
errors.LogError(ctx, s.Name(), " tries to resolve itself! Use IP or set \"hosts\" instead.")
noResponseErrCh <- errors.New("tries to resolve itself!", s.Name())
return
}
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
// As we don't want our traffic pattern looks like DoH, we use Random-Length Padding instead of Block-Length Padding recommended in RFC 8467
// Although DoH server like 1.1.1.1 will pad the response to Block-Length 468, at least it is better than no padding for response at all
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, int(crypto.RandBetween(100, 300))))
var deadline time.Time
if d, ok := ctx.Deadline(); ok {
@@ -268,19 +167,22 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP n
b, err := dns.PackMessage(r.msg)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query for ", domain)
noResponseErrCh <- err
return
}
resp, err := s.dohHTTPSContext(dnsCtx, b.Bytes())
if err != nil {
errors.LogErrorInner(ctx, err, "failed to retrieve response for ", domain)
noResponseErrCh <- err
return
}
rec, err := parseResponse(resp)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to handle DOH response for ", domain)
noResponseErrCh <- err
return
}
s.updateIP(r, rec)
s.cacheController.updateIP(r, rec)
}(req)
}
}
@@ -295,6 +197,8 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
req.Header.Add("Accept", "application/dns-message")
req.Header.Add("Content-Type", "application/dns-message")
req.Header.Set("X-Padding", strings.Repeat("X", int(crypto.RandBetween(100, 1000))))
hc := s.httpClient
resp, err := hc.Do(req.WithContext(ctx))
@@ -311,107 +215,50 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
return io.ReadAll(resp.Body)
}
func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
s.RLock()
record, found := s.ips[domain]
s.RUnlock()
if !found {
return nil, errRecordNotFound
}
var err4 error
var err6 error
var ips []net.Address
var ip6 []net.Address
if option.IPv4Enable {
ips, err4 = record.A.getIPs()
}
if option.IPv6Enable {
ip6, err6 = record.AAAA.getIPs()
ips = append(ips, ip6...)
}
if len(ips) > 0 {
return toNetIP(ips)
}
if err4 != nil {
return nil, err4
}
if err6 != nil {
return nil, err6
}
if (option.IPv4Enable && record.A != nil) || (option.IPv6Enable && record.AAAA != nil) {
return nil, dns_feature.ErrEmptyResponse
}
return nil, errRecordNotFound
}
// QueryIP implements Server.
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { // nolint: dupl
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { // nolint: dupl
fqdn := Fqdn(domain)
option = ResolveIpOptionOverride(s.queryStrategy, option)
if !option.IPv4Enable && !option.IPv6Enable {
return nil, dns_feature.ErrEmptyResponse
}
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
if disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.name)
if s.cacheController.disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
} else {
ips, err := s.findIPsForDomain(fqdn, option)
if err == nil || err == dns_feature.ErrEmptyResponse {
errors.LogDebugInner(ctx, err, s.name, " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, err
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
if !go_errors.Is(err, errRecordNotFound) {
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, ttl, err
}
}
// ipv4 and ipv6 belong to different subscription groups
var sub4, sub6 *pubsub.Subscriber
if option.IPv4Enable {
sub4 = s.pub.Subscribe(fqdn + "4")
defer sub4.Close()
}
if option.IPv6Enable {
sub6 = s.pub.Subscribe(fqdn + "6")
defer sub6.Close()
}
done := make(chan interface{})
go func() {
if sub4 != nil {
select {
case <-sub4.Wait():
case <-ctx.Done():
}
}
if sub6 != nil {
select {
case <-sub6.Wait():
case <-ctx.Done():
}
}
close(done)
}()
s.sendQuery(ctx, fqdn, clientIP, option)
noResponseErrCh := make(chan error, 2)
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
start := time.Now()
for {
ips, err := s.findIPsForDomain(fqdn, option)
if err != errRecordNotFound {
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, err
}
if sub4 != nil {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub4.Wait():
sub4.Close()
}
}
if sub6 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub6.Wait():
sub6.Close()
}
}
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, ttl, err
}

View File

@@ -17,12 +17,12 @@ func TestDOHNameServer(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err)
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP)
s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: true,
IPv6Enable: true,
}, false)
})
cancel()
common.Must(err)
if len(ips) == 0 {
@@ -34,12 +34,12 @@ func TestDOHNameServerWithCache(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err)
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP)
s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: true,
IPv6Enable: true,
}, false)
})
cancel()
common.Must(err)
if len(ips) == 0 {
@@ -47,10 +47,10 @@ func TestDOHNameServerWithCache(t *testing.T) {
}
ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{
ips2, _, err := s.QueryIP(ctx2, "google.com", dns_feature.IPOption{
IPv4Enable: true,
IPv6Enable: true,
}, true)
})
cancel()
common.Must(err)
if r := cmp.Diff(ips2, ips); r != "" {
@@ -62,12 +62,12 @@ func TestDOHNameServerWithIPv4Override(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err)
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP4)
s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: true,
IPv6Enable: true,
}, false)
IPv6Enable: false,
})
cancel()
common.Must(err)
if len(ips) == 0 {
@@ -85,12 +85,12 @@ func TestDOHNameServerWithIPv6Override(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err)
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP6)
s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
IPv4Enable: true,
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: false,
IPv6Enable: true,
}, false)
})
cancel()
common.Must(err)
if len(ips) == 0 {

View File

@@ -20,9 +20,9 @@ func (FakeDNSServer) Name() string {
return "FakeDNS"
}
func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, opt dns.IPOption, _ bool) ([]net.IP, error) {
func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, opt dns.IPOption) ([]net.IP, uint32, error) {
if f.fakeDNSEngine == nil {
return nil, errors.New("Unable to locate a fake DNS Engine").AtError()
return nil, 0, errors.New("Unable to locate a fake DNS Engine").AtError()
}
var ips []net.Address
@@ -34,13 +34,13 @@ func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, op
netIP, err := toNetIP(ips)
if err != nil {
return nil, errors.New("Unable to convert IP to net ip").Base(err).AtError()
return nil, 0, errors.New("Unable to convert IP to net ip").Base(err).AtError()
}
errors.LogInfo(ctx, f.Name(), " got answer: ", domain, " -> ", ips)
if len(netIP) > 0 {
return netIP, nil
return netIP, 1, nil // fakeIP ttl is 1
}
return nil, dns.ErrEmptyResponse
return nil, 0, dns.ErrEmptyResponse
}

View File

@@ -2,7 +2,6 @@ package dns
import (
"context"
"strings"
"time"
"github.com/xtls/xray-core/common/errors"
@@ -17,16 +16,11 @@ type LocalNameServer struct {
client *localdns.Client
}
const errEmptyResponse = "No address associated with hostname"
// QueryIP implements Server.
func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, _ net.IP, option dns.IPOption, _ bool) (ips []net.IP, err error) {
start := time.Now()
ips, err = s.client.LookupIP(domain, option)
func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, option dns.IPOption) (ips []net.IP, ttl uint32, err error) {
if err != nil && strings.HasSuffix(err.Error(), errEmptyResponse) {
err = dns.ErrEmptyResponse
}
start := time.Now()
ips, ttl, err = s.client.LookupIP(domain, option)
if len(ips) > 0 {
errors.LogInfo(ctx, "Localhost got answer: ", domain, " -> ", ips)
@@ -50,6 +44,6 @@ func NewLocalNameServer() *LocalNameServer {
}
// NewLocalDNSClient creates localdns client object for directly lookup in system DNS.
func NewLocalDNSClient() *Client {
return &Client{server: NewLocalNameServer()}
func NewLocalDNSClient(ipOption dns.IPOption) *Client {
return &Client{server: NewLocalNameServer(), ipOption: &ipOption}
}

View File

@@ -7,18 +7,17 @@ import (
. "github.com/xtls/xray-core/app/dns"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/features/dns"
)
func TestLocalNameServer(t *testing.T) {
s := NewLocalNameServer()
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, err := s.QueryIP(ctx, "google.com", net.IP{}, dns.IPOption{
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
}, false)
})
cancel()
common.Must(err)
if len(ips) == 0 {

View File

@@ -4,24 +4,20 @@ import (
"bytes"
"context"
"encoding/binary"
go_errors "errors"
"net/url"
"sync"
"sync/atomic"
"time"
"github.com/quic-go/quic-go"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol/dns"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal/pubsub"
"github.com/xtls/xray-core/common/task"
dns_feature "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/transport/internet/tls"
"golang.org/x/net/dns/dnsmessage"
"golang.org/x/net/http2"
)
@@ -34,18 +30,14 @@ const handshakeTimeout = time.Second * 8
// QUICNameServer implemented DNS over QUIC
type QUICNameServer struct {
sync.RWMutex
ips map[string]*record
pub *pubsub.Service
cleanup *task.Periodic
reqID uint32
name string
destination *net.Destination
connection quic.Connection
queryStrategy QueryStrategy
cacheController *CacheController
destination *net.Destination
connection *quic.Conn
clientIP net.IP
}
// NewQUICNameServer creates DNS-over-QUIC client object for local resolving
func NewQUICNameServer(url *url.URL, queryStrategy QueryStrategy) (*QUICNameServer, error) {
func NewQUICNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*QUICNameServer, error) {
errors.LogInfo(context.Background(), "DNS: created Local DNS-over-QUIC client for ", url.String())
var err error
@@ -59,15 +51,9 @@ func NewQUICNameServer(url *url.URL, queryStrategy QueryStrategy) (*QUICNameServ
dest := net.UDPDestination(net.ParseAddress(url.Hostname()), port)
s := &QUICNameServer{
ips: make(map[string]*record),
pub: pubsub.NewService(),
name: url.String(),
destination: &dest,
queryStrategy: queryStrategy,
}
s.cleanup = &task.Periodic{
Interval: time.Minute,
Execute: s.Cleanup,
cacheController: NewCacheController(url.String(), disableCache),
destination: &dest,
clientIP: clientIP,
}
return s, nil
@@ -75,94 +61,17 @@ func NewQUICNameServer(url *url.URL, queryStrategy QueryStrategy) (*QUICNameServ
// Name returns client name
func (s *QUICNameServer) Name() string {
return s.name
}
// Cleanup clears expired items from cache
func (s *QUICNameServer) Cleanup() error {
now := time.Now()
s.Lock()
defer s.Unlock()
if len(s.ips) == 0 {
return errors.New("nothing to do. stopping...")
}
for domain, record := range s.ips {
if record.A != nil && record.A.Expire.Before(now) {
record.A = nil
}
if record.AAAA != nil && record.AAAA.Expire.Before(now) {
record.AAAA = nil
}
if record.A == nil && record.AAAA == nil {
errors.LogDebug(context.Background(), s.name, " cleanup ", domain)
delete(s.ips, domain)
} else {
s.ips[domain] = record
}
}
if len(s.ips) == 0 {
s.ips = make(map[string]*record)
}
return nil
}
func (s *QUICNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) {
elapsed := time.Since(req.start)
s.Lock()
rec, found := s.ips[req.domain]
if !found {
rec = &record{}
}
updated := false
switch req.reqType {
case dnsmessage.TypeA:
if isNewer(rec.A, ipRec) {
rec.A = ipRec
updated = true
}
case dnsmessage.TypeAAAA:
addr := make([]net.Address, 0)
for _, ip := range ipRec.IP {
if len(ip.IP()) == net.IPv6len {
addr = append(addr, ip)
}
}
ipRec.IP = addr
if isNewer(rec.AAAA, ipRec) {
rec.AAAA = ipRec
updated = true
}
}
errors.LogInfo(context.Background(), s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed)
if updated {
s.ips[req.domain] = rec
}
switch req.reqType {
case dnsmessage.TypeA:
s.pub.Publish(req.domain+"4", nil)
case dnsmessage.TypeAAAA:
s.pub.Publish(req.domain+"6", nil)
}
s.Unlock()
common.Must(s.cleanup.Start())
return s.cacheController.name
}
func (s *QUICNameServer) newReqID() uint16 {
return uint16(atomic.AddUint32(&s.reqID, 1))
return 0
}
func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.name, " querying: ", domain)
func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying: ", domain)
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
var deadline time.Time
if d, ok := ctx.Deadline(); ok {
@@ -194,23 +103,36 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP
b, err := dns.PackMessage(r.msg)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query")
noResponseErrCh <- err
return
}
dnsReqBuf := buf.New()
binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len()))
dnsReqBuf.Write(b.Bytes())
err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len()))
if err != nil {
errors.LogErrorInner(ctx, err, "binary write failed")
noResponseErrCh <- err
return
}
_, err = dnsReqBuf.Write(b.Bytes())
if err != nil {
errors.LogErrorInner(ctx, err, "buffer write failed")
noResponseErrCh <- err
return
}
b.Release()
conn, err := s.openStream(dnsCtx)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to open quic connection")
noResponseErrCh <- err
return
}
_, err = conn.Write(dnsReqBuf.Bytes())
if err != nil {
errors.LogErrorInner(ctx, err, "failed to send query")
noResponseErrCh <- err
return
}
@@ -221,137 +143,84 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP
n, err := respBuf.ReadFullFrom(conn, 2)
if err != nil && n == 0 {
errors.LogErrorInner(ctx, err, "failed to read response length")
noResponseErrCh <- err
return
}
var length int16
err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to parse response length")
noResponseErrCh <- err
return
}
respBuf.Clear()
n, err = respBuf.ReadFullFrom(conn, int32(length))
if err != nil && n == 0 {
errors.LogErrorInner(ctx, err, "failed to read response length")
noResponseErrCh <- err
return
}
rec, err := parseResponse(respBuf.Bytes())
if err != nil {
errors.LogErrorInner(ctx, err, "failed to handle response")
noResponseErrCh <- err
return
}
s.updateIP(r, rec)
s.cacheController.updateIP(r, rec)
}(req)
}
}
func (s *QUICNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
s.RLock()
record, found := s.ips[domain]
s.RUnlock()
if !found {
return nil, errRecordNotFound
}
var err4 error
var err6 error
var ips []net.Address
var ip6 []net.Address
if option.IPv4Enable {
ips, err4 = record.A.getIPs()
}
if option.IPv6Enable {
ip6, err6 = record.AAAA.getIPs()
ips = append(ips, ip6...)
}
if len(ips) > 0 {
return toNetIP(ips)
}
if err4 != nil {
return nil, err4
}
if err6 != nil {
return nil, err6
}
if (option.IPv4Enable && record.A != nil) || (option.IPv6Enable && record.AAAA != nil) {
return nil, dns_feature.ErrEmptyResponse
}
return nil, errRecordNotFound
}
// QueryIP is called from dns.Server->queryIPTimeout
func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) {
func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
fqdn := Fqdn(domain)
option = ResolveIpOptionOverride(s.queryStrategy, option)
if !option.IPv4Enable && !option.IPv6Enable {
return nil, dns_feature.ErrEmptyResponse
}
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
if disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.name)
if s.cacheController.disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
} else {
ips, err := s.findIPsForDomain(fqdn, option)
if err == nil || err == dns_feature.ErrEmptyResponse {
errors.LogDebugInner(ctx, err, s.name, " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, err
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
if !go_errors.Is(err, errRecordNotFound) {
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, ttl, err
}
}
// ipv4 and ipv6 belong to different subscription groups
var sub4, sub6 *pubsub.Subscriber
if option.IPv4Enable {
sub4 = s.pub.Subscribe(fqdn + "4")
defer sub4.Close()
}
if option.IPv6Enable {
sub6 = s.pub.Subscribe(fqdn + "6")
defer sub6.Close()
}
done := make(chan interface{})
go func() {
if sub4 != nil {
select {
case <-sub4.Wait():
case <-ctx.Done():
}
}
if sub6 != nil {
select {
case <-sub6.Wait():
case <-ctx.Done():
}
}
close(done)
}()
s.sendQuery(ctx, fqdn, clientIP, option)
noResponseErrCh := make(chan error, 2)
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
start := time.Now()
for {
ips, err := s.findIPsForDomain(fqdn, option)
if err != errRecordNotFound {
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, err
}
if sub4 != nil {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub4.Wait():
sub4.Close()
}
}
if sub6 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub6.Wait():
sub6.Close()
}
}
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, ttl, err
}
func isActive(s quic.Connection) bool {
func isActive(s *quic.Conn) bool {
select {
case <-s.Context().Done():
return false
@@ -360,8 +229,8 @@ func isActive(s quic.Connection) bool {
}
}
func (s *QUICNameServer) getConnection() (quic.Connection, error) {
var conn quic.Connection
func (s *QUICNameServer) getConnection() (*quic.Conn, error) {
var conn *quic.Conn
s.RLock()
conn = s.connection
if conn != nil && isActive(conn) {
@@ -394,7 +263,7 @@ func (s *QUICNameServer) getConnection() (quic.Connection, error) {
return conn, nil
}
func (s *QUICNameServer) openConnection() (quic.Connection, error) {
func (s *QUICNameServer) openConnection() (*quic.Conn, error) {
tlsConfig := tls.Config{}
quicConfig := &quic.Config{
HandshakeIdleTimeout: handshakeTimeout,
@@ -414,7 +283,7 @@ func (s *QUICNameServer) openConnection() (quic.Connection, error) {
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()
if err != nil {
return nil, err

View File

@@ -16,24 +16,23 @@ import (
func TestQUICNameServer(t *testing.T) {
url, err := url.Parse("quic://dns.adguard-dns.com")
common.Must(err)
s, err := NewQUICNameServer(url, QueryStrategy_USE_IP)
s, err := NewQUICNameServer(url, false, net.IP(nil))
common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns.IPOption{
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
}, false)
})
cancel()
common.Must(err)
if len(ips) == 0 {
t.Error("expect some ips, but got 0")
}
ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns.IPOption{
ips2, _, err := s.QueryIP(ctx2, "google.com", dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
}, true)
})
cancel()
common.Must(err)
if r := cmp.Diff(ips2, ips); r != "" {
@@ -44,13 +43,13 @@ func TestQUICNameServer(t *testing.T) {
func TestQUICNameServerWithIPv4Override(t *testing.T) {
url, err := url.Parse("quic://dns.adguard-dns.com")
common.Must(err)
s, err := NewQUICNameServer(url, QueryStrategy_USE_IP4)
s, err := NewQUICNameServer(url, false, net.IP(nil))
common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns.IPOption{
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
}, false)
IPv6Enable: false,
})
cancel()
common.Must(err)
if len(ips) == 0 {
@@ -67,13 +66,13 @@ func TestQUICNameServerWithIPv4Override(t *testing.T) {
func TestQUICNameServerWithIPv6Override(t *testing.T) {
url, err := url.Parse("quic://dns.adguard-dns.com")
common.Must(err)
s, err := NewQUICNameServer(url, QueryStrategy_USE_IP6)
s, err := NewQUICNameServer(url, false, net.IP(nil))
common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns.IPOption{
IPv4Enable: true,
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
IPv4Enable: false,
IPv6Enable: true,
}, false)
})
cancel()
common.Must(err)
if len(ips) == 0 {

View File

@@ -4,12 +4,11 @@ import (
"bytes"
"context"
"encoding/binary"
go_errors "errors"
"net/url"
"sync"
"sync/atomic"
"time"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log"
@@ -17,34 +16,28 @@ import (
"github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/protocol/dns"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal/pubsub"
"github.com/xtls/xray-core/common/task"
dns_feature "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport/internet"
"golang.org/x/net/dns/dnsmessage"
)
// TCPNameServer implemented DNS over TCP (RFC7766).
type TCPNameServer struct {
sync.RWMutex
name string
destination *net.Destination
ips map[string]*record
pub *pubsub.Service
cleanup *task.Periodic
reqID uint32
dial func(context.Context) (net.Conn, error)
queryStrategy QueryStrategy
cacheController *CacheController
destination *net.Destination
reqID uint32
dial func(context.Context) (net.Conn, error)
clientIP net.IP
}
// NewTCPNameServer creates DNS over TCP server object for remote resolving.
func NewTCPNameServer(
url *url.URL,
dispatcher routing.Dispatcher,
queryStrategy QueryStrategy,
disableCache bool,
clientIP net.IP,
) (*TCPNameServer, error) {
s, err := baseTCPNameServer(url, "TCP", queryStrategy)
s, err := baseTCPNameServer(url, "TCP", disableCache, clientIP)
if err != nil {
return nil, err
}
@@ -65,8 +58,8 @@ func NewTCPNameServer(
}
// NewTCPLocalNameServer creates DNS over TCP client object for local resolving
func NewTCPLocalNameServer(url *url.URL, queryStrategy QueryStrategy) (*TCPNameServer, error) {
s, err := baseTCPNameServer(url, "TCPL", queryStrategy)
func NewTCPLocalNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*TCPNameServer, error) {
s, err := baseTCPNameServer(url, "TCPL", disableCache, clientIP)
if err != nil {
return nil, err
}
@@ -78,7 +71,7 @@ func NewTCPLocalNameServer(url *url.URL, queryStrategy QueryStrategy) (*TCPNameS
return s, nil
}
func baseTCPNameServer(url *url.URL, prefix string, queryStrategy QueryStrategy) (*TCPNameServer, error) {
func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, clientIP net.IP) (*TCPNameServer, error) {
port := net.Port(53)
if url.Port() != "" {
var err error
@@ -89,15 +82,9 @@ func baseTCPNameServer(url *url.URL, prefix string, queryStrategy QueryStrategy)
dest := net.TCPDestination(net.ParseAddress(url.Hostname()), port)
s := &TCPNameServer{
destination: &dest,
ips: make(map[string]*record),
pub: pubsub.NewService(),
name: prefix + "//" + dest.NetAddr(),
queryStrategy: queryStrategy,
}
s.cleanup = &task.Periodic{
Interval: time.Minute,
Execute: s.Cleanup,
cacheController: NewCacheController(prefix+"//"+dest.NetAddr(), disableCache),
destination: &dest,
clientIP: clientIP,
}
return s, nil
@@ -105,94 +92,17 @@ func baseTCPNameServer(url *url.URL, prefix string, queryStrategy QueryStrategy)
// Name implements Server.
func (s *TCPNameServer) Name() string {
return s.name
}
// Cleanup clears expired items from cache
func (s *TCPNameServer) Cleanup() error {
now := time.Now()
s.Lock()
defer s.Unlock()
if len(s.ips) == 0 {
return errors.New("nothing to do. stopping...")
}
for domain, record := range s.ips {
if record.A != nil && record.A.Expire.Before(now) {
record.A = nil
}
if record.AAAA != nil && record.AAAA.Expire.Before(now) {
record.AAAA = nil
}
if record.A == nil && record.AAAA == nil {
errors.LogDebug(context.Background(), s.name, " cleanup ", domain)
delete(s.ips, domain)
} else {
s.ips[domain] = record
}
}
if len(s.ips) == 0 {
s.ips = make(map[string]*record)
}
return nil
}
func (s *TCPNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) {
elapsed := time.Since(req.start)
s.Lock()
rec, found := s.ips[req.domain]
if !found {
rec = &record{}
}
updated := false
switch req.reqType {
case dnsmessage.TypeA:
if isNewer(rec.A, ipRec) {
rec.A = ipRec
updated = true
}
case dnsmessage.TypeAAAA:
addr := make([]net.Address, 0)
for _, ip := range ipRec.IP {
if len(ip.IP()) == net.IPv6len {
addr = append(addr, ip)
}
}
ipRec.IP = addr
if isNewer(rec.AAAA, ipRec) {
rec.AAAA = ipRec
updated = true
}
}
errors.LogInfo(context.Background(), s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed)
if updated {
s.ips[req.domain] = rec
}
switch req.reqType {
case dnsmessage.TypeA:
s.pub.Publish(req.domain+"4", nil)
case dnsmessage.TypeAAAA:
s.pub.Publish(req.domain+"6", nil)
}
s.Unlock()
common.Must(s.cleanup.Start())
return s.cacheController.name
}
func (s *TCPNameServer) newReqID() uint16 {
return uint16(atomic.AddUint32(&s.reqID, 1))
}
func (s *TCPNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
errors.LogDebug(ctx, s.name, " querying DNS for: ", domain)
func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) {
errors.LogDebug(ctx, s.Name(), " querying DNS for: ", domain)
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
var deadline time.Time
if d, ok := ctx.Deadline(); ok {
@@ -221,23 +131,36 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, domain string, clientIP n
b, err := dns.PackMessage(r.msg)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query")
noResponseErrCh <- err
return
}
conn, err := s.dial(dnsCtx)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to dial namesever")
noResponseErrCh <- err
return
}
defer conn.Close()
dnsReqBuf := buf.New()
binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len()))
dnsReqBuf.Write(b.Bytes())
err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len()))
if err != nil {
errors.LogErrorInner(ctx, err, "binary write failed")
noResponseErrCh <- err
return
}
_, err = dnsReqBuf.Write(b.Bytes())
if err != nil {
errors.LogErrorInner(ctx, err, "buffer write failed")
noResponseErrCh <- err
return
}
b.Release()
_, err = conn.Write(dnsReqBuf.Bytes())
if err != nil {
errors.LogErrorInner(ctx, err, "failed to send query")
noResponseErrCh <- err
return
}
dnsReqBuf.Release()
@@ -247,129 +170,80 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, domain string, clientIP n
n, err := respBuf.ReadFullFrom(conn, 2)
if err != nil && n == 0 {
errors.LogErrorInner(ctx, err, "failed to read response length")
noResponseErrCh <- err
return
}
var length int16
err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to parse response length")
noResponseErrCh <- err
return
}
respBuf.Clear()
n, err = respBuf.ReadFullFrom(conn, int32(length))
if err != nil && n == 0 {
errors.LogErrorInner(ctx, err, "failed to read response length")
noResponseErrCh <- err
return
}
rec, err := parseResponse(respBuf.Bytes())
if err != nil {
errors.LogErrorInner(ctx, err, "failed to parse DNS over TCP response")
noResponseErrCh <- err
return
}
s.updateIP(r, rec)
s.cacheController.updateIP(r, rec)
}(req)
}
}
func (s *TCPNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
s.RLock()
record, found := s.ips[domain]
s.RUnlock()
if !found {
return nil, errRecordNotFound
}
var err4 error
var err6 error
var ips []net.Address
var ip6 []net.Address
if option.IPv4Enable {
ips, err4 = record.A.getIPs()
}
if option.IPv6Enable {
ip6, err6 = record.AAAA.getIPs()
ips = append(ips, ip6...)
}
if len(ips) > 0 {
return toNetIP(ips)
}
if err4 != nil {
return nil, err4
}
if err6 != nil {
return nil, err6
}
return nil, dns_feature.ErrEmptyResponse
}
// QueryIP implements Server.
func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) {
func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
fqdn := Fqdn(domain)
option = ResolveIpOptionOverride(s.queryStrategy, option)
if !option.IPv4Enable && !option.IPv6Enable {
return nil, dns_feature.ErrEmptyResponse
}
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
if disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.name)
if s.cacheController.disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
} else {
ips, err := s.findIPsForDomain(fqdn, option)
if err == nil || err == dns_feature.ErrEmptyResponse {
errors.LogDebugInner(ctx, err, s.name, " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, err
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
if !go_errors.Is(err, errRecordNotFound) {
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, ttl, err
}
}
// ipv4 and ipv6 belong to different subscription groups
var sub4, sub6 *pubsub.Subscriber
if option.IPv4Enable {
sub4 = s.pub.Subscribe(fqdn + "4")
defer sub4.Close()
}
if option.IPv6Enable {
sub6 = s.pub.Subscribe(fqdn + "6")
defer sub6.Close()
}
done := make(chan interface{})
go func() {
if sub4 != nil {
select {
case <-sub4.Wait():
case <-ctx.Done():
}
}
if sub6 != nil {
select {
case <-sub6.Wait():
case <-ctx.Done():
}
}
close(done)
}()
s.sendQuery(ctx, fqdn, clientIP, option)
noResponseErrCh := make(chan error, 2)
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
start := time.Now()
for {
ips, err := s.findIPsForDomain(fqdn, option)
if err != errRecordNotFound {
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, err
}
if sub4 != nil {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub4.Wait():
sub4.Close()
}
}
if sub6 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub6.Wait():
sub6.Close()
}
}
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, ttl, err
}

View File

@@ -16,13 +16,13 @@ import (
func TestTCPLocalNameServer(t *testing.T) {
url, err := url.Parse("tcp+local://8.8.8.8")
common.Must(err)
s, err := NewTCPLocalNameServer(url, QueryStrategy_USE_IP)
s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: true,
IPv6Enable: true,
}, false)
})
cancel()
common.Must(err)
if len(ips) == 0 {
@@ -33,13 +33,13 @@ func TestTCPLocalNameServer(t *testing.T) {
func TestTCPLocalNameServerWithCache(t *testing.T) {
url, err := url.Parse("tcp+local://8.8.8.8")
common.Must(err)
s, err := NewTCPLocalNameServer(url, QueryStrategy_USE_IP)
s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: true,
IPv6Enable: true,
}, false)
})
cancel()
common.Must(err)
if len(ips) == 0 {
@@ -47,10 +47,10 @@ func TestTCPLocalNameServerWithCache(t *testing.T) {
}
ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{
ips2, _, err := s.QueryIP(ctx2, "google.com", dns_feature.IPOption{
IPv4Enable: true,
IPv6Enable: true,
}, true)
})
cancel()
common.Must(err)
if r := cmp.Diff(ips2, ips); r != "" {
@@ -61,13 +61,13 @@ func TestTCPLocalNameServerWithCache(t *testing.T) {
func TestTCPLocalNameServerWithIPv4Override(t *testing.T) {
url, err := url.Parse("tcp+local://8.8.8.8")
common.Must(err)
s, err := NewTCPLocalNameServer(url, QueryStrategy_USE_IP4)
s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: true,
IPv6Enable: true,
}, false)
IPv6Enable: false,
})
cancel()
common.Must(err)
@@ -85,13 +85,13 @@ func TestTCPLocalNameServerWithIPv4Override(t *testing.T) {
func TestTCPLocalNameServerWithIPv6Override(t *testing.T) {
url, err := url.Parse("tcp+local://8.8.8.8")
common.Must(err)
s, err := NewTCPLocalNameServer(url, QueryStrategy_USE_IP6)
s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
IPv4Enable: true,
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: false,
IPv6Enable: true,
}, false)
})
cancel()
common.Must(err)

View File

@@ -2,6 +2,7 @@ package dns
import (
"context"
go_errors "errors"
"strings"
"sync"
"sync/atomic"
@@ -13,7 +14,6 @@ import (
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol/dns"
udp_proto "github.com/xtls/xray-core/common/protocol/udp"
"github.com/xtls/xray-core/common/signal/pubsub"
"github.com/xtls/xray-core/common/task"
dns_feature "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/routing"
@@ -24,35 +24,36 @@ import (
// ClassicNameServer implemented traditional UDP DNS.
type ClassicNameServer struct {
sync.RWMutex
name string
address *net.Destination
ips map[string]*record
requests map[uint16]*dnsRequest
pub *pubsub.Service
udpServer *udp.Dispatcher
cleanup *task.Periodic
reqID uint32
queryStrategy QueryStrategy
cacheController *CacheController
address *net.Destination
requests map[uint16]*udpDnsRequest
udpServer *udp.Dispatcher
requestsCleanup *task.Periodic
reqID uint32
clientIP net.IP
}
type udpDnsRequest struct {
dnsRequest
ctx context.Context
}
// NewClassicNameServer creates udp server object for remote resolving.
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, queryStrategy QueryStrategy) *ClassicNameServer {
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, disableCache bool, clientIP net.IP) *ClassicNameServer {
// default to 53 if unspecific
if address.Port == 0 {
address.Port = net.Port(53)
}
s := &ClassicNameServer{
address: &address,
ips: make(map[string]*record),
requests: make(map[uint16]*dnsRequest),
pub: pubsub.NewService(),
name: strings.ToUpper(address.String()),
queryStrategy: queryStrategy,
cacheController: NewCacheController(strings.ToUpper(address.String()), disableCache),
address: &address,
requests: make(map[uint16]*udpDnsRequest),
clientIP: clientIP,
}
s.cleanup = &task.Periodic{
s.requestsCleanup = &task.Periodic{
Interval: time.Minute,
Execute: s.Cleanup,
Execute: s.RequestsCleanup,
}
s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse)
errors.LogInfo(context.Background(), "DNS: created UDP client initialized for ", address.NetAddr())
@@ -61,37 +62,17 @@ func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher
// Name implements Server.
func (s *ClassicNameServer) Name() string {
return s.name
return s.cacheController.name
}
// Cleanup clears expired items from cache
func (s *ClassicNameServer) Cleanup() error {
// RequestsCleanup clears expired items from cache
func (s *ClassicNameServer) RequestsCleanup() error {
now := time.Now()
s.Lock()
defer s.Unlock()
if len(s.ips) == 0 && len(s.requests) == 0 {
return errors.New(s.name, " nothing to do. stopping...")
}
for domain, record := range s.ips {
if record.A != nil && record.A.Expire.Before(now) {
record.A = nil
}
if record.AAAA != nil && record.AAAA.Expire.Before(now) {
record.AAAA = nil
}
if record.A == nil && record.AAAA == nil {
errors.LogDebug(context.Background(), s.name, " cleanup ", domain)
delete(s.ips, domain)
} else {
s.ips[domain] = record
}
}
if len(s.ips) == 0 {
s.ips = make(map[string]*record)
if len(s.requests) == 0 {
return errors.New(s.Name(), " nothing to do. stopping...")
}
for id, req := range s.requests {
@@ -101,7 +82,7 @@ func (s *ClassicNameServer) Cleanup() error {
}
if len(s.requests) == 0 {
s.requests = make(map[uint16]*dnsRequest)
s.requests = make(map[uint16]*udpDnsRequest)
}
return nil
@@ -111,7 +92,7 @@ func (s *ClassicNameServer) Cleanup() error {
func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_proto.Packet) {
ipRec, err := parseResponse(packet.Payload.Bytes())
if err != nil {
errors.LogError(ctx, s.name, " fail to parse responded DNS udp")
errors.LogError(ctx, s.Name(), " fail to parse responded DNS udp")
return
}
@@ -124,179 +105,107 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
}
s.Unlock()
if !ok {
errors.LogError(ctx, s.name, " cannot find the pending request")
errors.LogError(ctx, s.Name(), " cannot find the pending request")
return
}
var rec record
switch req.reqType {
case dnsmessage.TypeA:
rec.A = ipRec
case dnsmessage.TypeAAAA:
rec.AAAA = ipRec
// if truncated, retry with EDNS0 option(udp payload size: 1350)
if ipRec.RawHeader.Truncated {
// if already has EDNS0 option, no need to retry
if len(req.msg.Additionals) == 0 {
// copy necessary meta data from original request
// and add EDNS0 option
opt := new(dnsmessage.Resource)
common.Must(opt.Header.SetEDNS0(1350, 0xfe00, true))
opt.Body = &dnsmessage.OPTResource{}
newMsg := *req.msg
newReq := *req
newMsg.Additionals = append(newMsg.Additionals, *opt)
newMsg.ID = s.newReqID()
newReq.msg = &newMsg
s.addPendingRequest(&newReq)
b, _ := dns.PackMessage(newReq.msg)
s.udpServer.Dispatch(toDnsContext(newReq.ctx, s.address.String()), *s.address, b)
return
}
}
elapsed := time.Since(req.start)
errors.LogInfo(ctx, s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed)
if len(req.domain) > 0 && (rec.A != nil || rec.AAAA != nil) {
s.updateIP(req.domain, &rec)
}
}
func (s *ClassicNameServer) updateIP(domain string, newRec *record) {
s.Lock()
rec, found := s.ips[domain]
if !found {
rec = &record{}
}
updated := false
if isNewer(rec.A, newRec.A) {
rec.A = newRec.A
updated = true
}
if isNewer(rec.AAAA, newRec.AAAA) {
rec.AAAA = newRec.AAAA
updated = true
}
if updated {
errors.LogDebug(context.Background(), s.name, " updating IP records for domain:", domain)
s.ips[domain] = rec
}
if newRec.A != nil {
s.pub.Publish(domain+"4", nil)
}
if newRec.AAAA != nil {
s.pub.Publish(domain+"6", nil)
}
s.Unlock()
common.Must(s.cleanup.Start())
s.cacheController.updateIP(&req.dnsRequest, ipRec)
}
func (s *ClassicNameServer) newReqID() uint16 {
return uint16(atomic.AddUint32(&s.reqID, 1))
}
func (s *ClassicNameServer) addPendingRequest(req *dnsRequest) {
func (s *ClassicNameServer) addPendingRequest(req *udpDnsRequest) {
s.Lock()
defer s.Unlock()
id := req.msg.ID
req.expire = time.Now().Add(time.Second * 8)
s.requests[id] = req
s.Unlock()
common.Must(s.requestsCleanup.Start())
}
func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
errors.LogDebug(ctx, s.name, " querying DNS for: ", domain)
func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, domain string, option dns_feature.IPOption) {
errors.LogDebug(ctx, s.Name(), " querying DNS for: ", domain)
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
for _, req := range reqs {
s.addPendingRequest(req)
udpReq := &udpDnsRequest{
dnsRequest: *req,
ctx: ctx,
}
s.addPendingRequest(udpReq)
b, _ := dns.PackMessage(req.msg)
s.udpServer.Dispatch(toDnsContext(ctx, s.address.String()), *s.address, b)
}
}
func (s *ClassicNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
s.RLock()
record, found := s.ips[domain]
s.RUnlock()
if !found {
return nil, errRecordNotFound
}
var err4 error
var err6 error
var ips []net.Address
var ip6 []net.Address
if option.IPv4Enable {
ips, err4 = record.A.getIPs()
}
if option.IPv6Enable {
ip6, err6 = record.AAAA.getIPs()
ips = append(ips, ip6...)
}
if len(ips) > 0 {
return toNetIP(ips)
}
if err4 != nil {
return nil, err4
}
if err6 != nil {
return nil, err6
}
return nil, dns_feature.ErrEmptyResponse
}
// QueryIP implements Server.
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) {
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
fqdn := Fqdn(domain)
option = ResolveIpOptionOverride(s.queryStrategy, option)
if !option.IPv4Enable && !option.IPv6Enable {
return nil, dns_feature.ErrEmptyResponse
}
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
if disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.name)
if s.cacheController.disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
} else {
ips, err := s.findIPsForDomain(fqdn, option)
if err == nil || err == dns_feature.ErrEmptyResponse {
errors.LogDebugInner(ctx, err, s.name, " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, err
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
if !go_errors.Is(err, errRecordNotFound) {
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, ttl, err
}
}
// ipv4 and ipv6 belong to different subscription groups
var sub4, sub6 *pubsub.Subscriber
if option.IPv4Enable {
sub4 = s.pub.Subscribe(fqdn + "4")
defer sub4.Close()
}
if option.IPv6Enable {
sub6 = s.pub.Subscribe(fqdn + "6")
defer sub6.Close()
}
done := make(chan interface{})
go func() {
if sub4 != nil {
select {
case <-sub4.Wait():
case <-ctx.Done():
}
}
if sub6 != nil {
select {
case <-sub6.Wait():
case <-ctx.Done():
}
}
close(done)
}()
s.sendQuery(ctx, fqdn, clientIP, option)
noResponseErrCh := make(chan error, 2)
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
start := time.Now()
for {
ips, err := s.findIPsForDomain(fqdn, option)
if err != errRecordNotFound {
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, err
}
if sub4 != nil {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub4.Wait():
sub4.Close()
}
}
if sub6 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub6.Wait():
sub6.Close()
}
}
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, ttl, err
}

View File

@@ -27,7 +27,8 @@ type Config struct {
unknownFields protoimpl.UnknownFields
// Tag of the outbound handler that handles metrics http connections.
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Listen string `protobuf:"bytes,2,opt,name=listen,proto3" json:"listen,omitempty"`
}
func (x *Config) Reset() {
@@ -67,20 +68,28 @@ func (x *Config) GetTag() string {
return ""
}
func (x *Config) GetListen() string {
if x != nil {
return x.Listen
}
return ""
}
var File_app_metrics_config_proto protoreflect.FileDescriptor
var file_app_metrics_config_proto_rawDesc = []byte{
0x0a, 0x18, 0x61, 0x70, 0x70, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x63, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x1a, 0x0a, 0x06,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x32, 0x0a, 0x06,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78,
0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70,
0x70, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79,
0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74,
0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e,
0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4d, 0x65, 0x74,
0x72, 0x69, 0x63, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@@ -10,4 +10,5 @@ option java_multiple_files = true;
message Config {
// Tag of the outbound handler that handles metrics http connections.
string tag = 1;
string listen = 2;
}

View File

@@ -24,12 +24,15 @@ type MetricsHandler struct {
statsManager feature_stats.Manager
observatory extension.Observatory
tag string
listen string
tcpListener net.Listener
}
// NewMetricsHandler creates a new MetricsHandler based on the given config.
func NewMetricsHandler(ctx context.Context, config *Config) (*MetricsHandler, error) {
c := &MetricsHandler{
tag: config.Tag,
tag: config.Tag,
listen: config.Listen,
}
common.Must(core.RequireFeatures(ctx, func(om outbound.Manager, sm feature_stats.Manager) {
c.statsManager = sm
@@ -87,6 +90,23 @@ func (p *MetricsHandler) Type() interface{} {
}
func (p *MetricsHandler) Start() error {
// direct listen a port if listen is set
if p.listen != "" {
TCPlistener, err := net.Listen("tcp", p.listen)
if err != nil {
return err
}
p.tcpListener = TCPlistener
errors.LogInfo(context.Background(), "Metrics server listening on ", p.listen)
go func() {
if err := http.Serve(TCPlistener, http.DefaultServeMux); err != nil {
errors.LogErrorInner(context.Background(), err, "failed to start metrics server")
}
}()
}
listener := &OutboundListener{
buffer: make(chan net.Conn, 4),
done: done.New(),

View File

@@ -8,6 +8,7 @@ import (
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/signal/done"
"github.com/xtls/xray-core/transport"
)
@@ -108,3 +109,13 @@ func (co *Outbound) Close() error {
co.closed = true
return co.listener.Close()
}
// SenderSettings implements outbound.Handler.
func (co *Outbound) SenderSettings() *serial.TypedMessage {
return nil
}
// ProxySettings implements outbound.Handler.
func (co *Outbound) ProxySettings() *serial.TypedMessage {
return nil
}

View File

@@ -90,6 +90,8 @@ type HealthPingConfig struct {
SamplingCount int32 `protobuf:"varint,4,opt,name=samplingCount,proto3" json:"samplingCount,omitempty"`
// ping timeout, int64 values of time.Duration
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() {
@@ -157,6 +159,13 @@ func (x *HealthPingConfig) GetTimeout() int64 {
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_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,
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,
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,
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,
@@ -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,
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,
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,
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,

View File

@@ -26,4 +26,7 @@ message HealthPingConfig {
int32 samplingCount = 4;
// ping timeout, int64 values of time.Duration
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"`
SamplingCount int `json:"sampling"`
Timeout time.Duration `json:"timeout"`
HttpMethod string `json:"httpMethod"`
}
// 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 {
settings := &HealthPingSettings{}
if config != nil {
var httpMethod string
if config.HttpMethod == "" {
httpMethod = "HEAD"
} else {
httpMethod = strings.TrimSpace(config.HttpMethod)
}
settings = &HealthPingSettings{
Connectivity: strings.TrimSpace(config.Connectivity),
Destination: strings.TrimSpace(config.Destination),
Interval: time.Duration(config.Interval),
SamplingCount: int(config.SamplingCount),
Timeout: time.Duration(config.Timeout),
HttpMethod: httpMethod,
}
}
if settings.Destination == "" {
@@ -66,10 +76,10 @@ func NewHealthPing(ctx context.Context, dispatcher routing.Dispatcher, config *H
settings.Timeout = time.Duration(5) * time.Second
}
return &HealthPing{
ctx: ctx,
ctx: ctx,
dispatcher: dispatcher,
Settings: settings,
Results: nil,
Settings: settings,
Results: nil,
}
}
@@ -164,7 +174,7 @@ func (h *HealthPing) doCheck(tags []string, duration time.Duration, rounds int)
}
time.AfterFunc(delay, func() {
errors.LogDebug(h.ctx, "checking ", handler)
delay, err := client.MeasureDelay()
delay, err := client.MeasureDelay(h.Settings.HttpMethod)
if err == nil {
ch <- &rtt{
handler: handler,
@@ -251,7 +261,7 @@ func (h *HealthPing) checkConnectivity() bool {
h.Settings.Connectivity,
h.Settings.Timeout,
)
if _, err := tester.MeasureDelay(); err != nil {
if _, err := tester.MeasureDelay(h.Settings.HttpMethod); err != nil {
return false
}
return true

View File

@@ -2,6 +2,7 @@ package burst
import (
"context"
"io"
"net/http"
"time"
@@ -51,20 +52,28 @@ func newHTTPClient(ctxv context.Context, dispatcher routing.Dispatcher, handler
}
// 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 {
panic("pingClient not initialized")
}
req, err := http.NewRequest(http.MethodHead, s.destination, nil)
req, err := http.NewRequest(httpMethod, s.destination, nil)
if err != nil {
return rttFailed, err
}
start := time.Now()
resp, err := s.httpClient.Do(req)
if err != nil {
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()
return time.Since(start), nil
}

View File

@@ -38,7 +38,7 @@ func init() {
sv := &service{v: s}
err := s.RequireFeatures(func(Observatory extension.Observatory) {
sv.observatory = Observatory
})
}, false)
if err != nil {
return nil, err
}

View File

@@ -32,7 +32,7 @@ type Observer struct {
finished *done.Instance
ohm outbound.Manager
ohm outbound.Manager
dispatcher routing.Dispatcher
}
@@ -226,9 +226,9 @@ func New(ctx context.Context, config *Config) (*Observer, error) {
return nil, errors.New("Cannot get depended features").Base(err)
}
return &Observer{
config: config,
ctx: ctx,
ohm: outboundManager,
config: config,
ctx: ctx,
ohm: outboundManager,
dispatcher: dispatcher,
}, nil
}

View File

@@ -3,6 +3,7 @@ package command
import (
"context"
"github.com/xtls/xray-core/app/commander"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/protocol"
@@ -99,6 +100,28 @@ func (s *handlerServer) AlterInbound(ctx context.Context, request *AlterInboundR
return &AlterInboundResponse{}, operation.ApplyInbound(ctx, handler)
}
func (s *handlerServer) ListInbounds(ctx context.Context, request *ListInboundsRequest) (*ListInboundsResponse, error) {
handlers := s.ihm.ListHandlers(ctx)
response := &ListInboundsResponse{}
if request.GetIsOnlyTags() {
for _, handler := range handlers {
response.Inbounds = append(response.Inbounds, &core.InboundHandlerConfig{
Tag: handler.Tag(),
})
}
} else {
for _, handler := range handlers {
response.Inbounds = append(response.Inbounds, &core.InboundHandlerConfig{
Tag: handler.Tag(),
ReceiverSettings: handler.ReceiverSettings(),
ProxySettings: handler.ProxySettings(),
})
}
}
return response, nil
}
func (s *handlerServer) GetInboundUsers(ctx context.Context, request *GetInboundUserRequest) (*GetInboundUserResponse, error) {
handler, err := s.ihm.GetHandler(ctx, request.Tag)
if err != nil {
@@ -164,6 +187,23 @@ func (s *handlerServer) AlterOutbound(ctx context.Context, request *AlterOutboun
return &AlterOutboundResponse{}, operation.ApplyOutbound(ctx, handler)
}
func (s *handlerServer) ListOutbounds(ctx context.Context, request *ListOutboundsRequest) (*ListOutboundsResponse, error) {
handlers := s.ohm.ListHandlers(ctx)
response := &ListOutboundsResponse{}
for _, handler := range handlers {
// Ignore gRPC outbound
if _, ok := handler.(*commander.Outbound); ok {
continue
}
response.Outbounds = append(response.Outbounds, &core.OutboundHandlerConfig{
Tag: handler.Tag(),
SenderSettings: handler.SenderSettings(),
ProxySettings: handler.ProxySettings(),
})
}
return response, nil
}
func (s *handlerServer) mustEmbedUnimplementedHandlerServiceServer() {}
type service struct {
@@ -177,7 +217,7 @@ func (s *service) Register(server *grpc.Server) {
common.Must(s.v.RequireFeatures(func(im inbound.Manager, om outbound.Manager) {
hs.ihm = im
hs.ohm = om
}))
}, false))
RegisterHandlerServiceServer(server, hs)
// For compatibility purposes

View File

@@ -364,6 +364,96 @@ func (*AlterInboundResponse) Descriptor() ([]byte, []int) {
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{7}
}
type ListInboundsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
IsOnlyTags bool `protobuf:"varint,1,opt,name=isOnlyTags,proto3" json:"isOnlyTags,omitempty"`
}
func (x *ListInboundsRequest) Reset() {
*x = ListInboundsRequest{}
mi := &file_app_proxyman_command_command_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListInboundsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListInboundsRequest) ProtoMessage() {}
func (x *ListInboundsRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_command_command_proto_msgTypes[8]
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 ListInboundsRequest.ProtoReflect.Descriptor instead.
func (*ListInboundsRequest) Descriptor() ([]byte, []int) {
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 {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Inbounds []*core.InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbounds,proto3" json:"inbounds,omitempty"`
}
func (x *ListInboundsResponse) Reset() {
*x = ListInboundsResponse{}
mi := &file_app_proxyman_command_command_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListInboundsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListInboundsResponse) ProtoMessage() {}
func (x *ListInboundsResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_command_command_proto_msgTypes[9]
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 ListInboundsResponse.ProtoReflect.Descriptor instead.
func (*ListInboundsResponse) Descriptor() ([]byte, []int) {
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{9}
}
func (x *ListInboundsResponse) GetInbounds() []*core.InboundHandlerConfig {
if x != nil {
return x.Inbounds
}
return nil
}
type GetInboundUserRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -375,7 +465,7 @@ type GetInboundUserRequest struct {
func (x *GetInboundUserRequest) Reset() {
*x = GetInboundUserRequest{}
mi := &file_app_proxyman_command_command_proto_msgTypes[8]
mi := &file_app_proxyman_command_command_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -387,7 +477,7 @@ func (x *GetInboundUserRequest) String() string {
func (*GetInboundUserRequest) ProtoMessage() {}
func (x *GetInboundUserRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_command_command_proto_msgTypes[8]
mi := &file_app_proxyman_command_command_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -400,7 +490,7 @@ func (x *GetInboundUserRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetInboundUserRequest.ProtoReflect.Descriptor instead.
func (*GetInboundUserRequest) Descriptor() ([]byte, []int) {
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{8}
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{10}
}
func (x *GetInboundUserRequest) GetTag() string {
@@ -427,7 +517,7 @@ type GetInboundUserResponse struct {
func (x *GetInboundUserResponse) Reset() {
*x = GetInboundUserResponse{}
mi := &file_app_proxyman_command_command_proto_msgTypes[9]
mi := &file_app_proxyman_command_command_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -439,7 +529,7 @@ func (x *GetInboundUserResponse) String() string {
func (*GetInboundUserResponse) ProtoMessage() {}
func (x *GetInboundUserResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_command_command_proto_msgTypes[9]
mi := &file_app_proxyman_command_command_proto_msgTypes[11]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -452,7 +542,7 @@ func (x *GetInboundUserResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetInboundUserResponse.ProtoReflect.Descriptor instead.
func (*GetInboundUserResponse) Descriptor() ([]byte, []int) {
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{9}
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{11}
}
func (x *GetInboundUserResponse) GetUsers() []*protocol.User {
@@ -472,7 +562,7 @@ type GetInboundUsersCountResponse struct {
func (x *GetInboundUsersCountResponse) Reset() {
*x = GetInboundUsersCountResponse{}
mi := &file_app_proxyman_command_command_proto_msgTypes[10]
mi := &file_app_proxyman_command_command_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -484,7 +574,7 @@ func (x *GetInboundUsersCountResponse) String() string {
func (*GetInboundUsersCountResponse) ProtoMessage() {}
func (x *GetInboundUsersCountResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_command_command_proto_msgTypes[10]
mi := &file_app_proxyman_command_command_proto_msgTypes[12]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -497,7 +587,7 @@ func (x *GetInboundUsersCountResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetInboundUsersCountResponse.ProtoReflect.Descriptor instead.
func (*GetInboundUsersCountResponse) Descriptor() ([]byte, []int) {
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{10}
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{12}
}
func (x *GetInboundUsersCountResponse) GetCount() int64 {
@@ -517,7 +607,7 @@ type AddOutboundRequest struct {
func (x *AddOutboundRequest) Reset() {
*x = AddOutboundRequest{}
mi := &file_app_proxyman_command_command_proto_msgTypes[11]
mi := &file_app_proxyman_command_command_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -529,7 +619,7 @@ func (x *AddOutboundRequest) String() string {
func (*AddOutboundRequest) ProtoMessage() {}
func (x *AddOutboundRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_command_command_proto_msgTypes[11]
mi := &file_app_proxyman_command_command_proto_msgTypes[13]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -542,7 +632,7 @@ func (x *AddOutboundRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use AddOutboundRequest.ProtoReflect.Descriptor instead.
func (*AddOutboundRequest) Descriptor() ([]byte, []int) {
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{11}
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{13}
}
func (x *AddOutboundRequest) GetOutbound() *core.OutboundHandlerConfig {
@@ -560,7 +650,7 @@ type AddOutboundResponse struct {
func (x *AddOutboundResponse) Reset() {
*x = AddOutboundResponse{}
mi := &file_app_proxyman_command_command_proto_msgTypes[12]
mi := &file_app_proxyman_command_command_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -572,7 +662,7 @@ func (x *AddOutboundResponse) String() string {
func (*AddOutboundResponse) ProtoMessage() {}
func (x *AddOutboundResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_command_command_proto_msgTypes[12]
mi := &file_app_proxyman_command_command_proto_msgTypes[14]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -585,7 +675,7 @@ func (x *AddOutboundResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use AddOutboundResponse.ProtoReflect.Descriptor instead.
func (*AddOutboundResponse) Descriptor() ([]byte, []int) {
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{12}
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{14}
}
type RemoveOutboundRequest struct {
@@ -598,7 +688,7 @@ type RemoveOutboundRequest struct {
func (x *RemoveOutboundRequest) Reset() {
*x = RemoveOutboundRequest{}
mi := &file_app_proxyman_command_command_proto_msgTypes[13]
mi := &file_app_proxyman_command_command_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -610,7 +700,7 @@ func (x *RemoveOutboundRequest) String() string {
func (*RemoveOutboundRequest) ProtoMessage() {}
func (x *RemoveOutboundRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_command_command_proto_msgTypes[13]
mi := &file_app_proxyman_command_command_proto_msgTypes[15]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -623,7 +713,7 @@ func (x *RemoveOutboundRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use RemoveOutboundRequest.ProtoReflect.Descriptor instead.
func (*RemoveOutboundRequest) Descriptor() ([]byte, []int) {
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{13}
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{15}
}
func (x *RemoveOutboundRequest) GetTag() string {
@@ -641,7 +731,7 @@ type RemoveOutboundResponse struct {
func (x *RemoveOutboundResponse) Reset() {
*x = RemoveOutboundResponse{}
mi := &file_app_proxyman_command_command_proto_msgTypes[14]
mi := &file_app_proxyman_command_command_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -653,7 +743,7 @@ func (x *RemoveOutboundResponse) String() string {
func (*RemoveOutboundResponse) ProtoMessage() {}
func (x *RemoveOutboundResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_command_command_proto_msgTypes[14]
mi := &file_app_proxyman_command_command_proto_msgTypes[16]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -666,7 +756,7 @@ func (x *RemoveOutboundResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use RemoveOutboundResponse.ProtoReflect.Descriptor instead.
func (*RemoveOutboundResponse) Descriptor() ([]byte, []int) {
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{14}
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{16}
}
type AlterOutboundRequest struct {
@@ -680,7 +770,7 @@ type AlterOutboundRequest struct {
func (x *AlterOutboundRequest) Reset() {
*x = AlterOutboundRequest{}
mi := &file_app_proxyman_command_command_proto_msgTypes[15]
mi := &file_app_proxyman_command_command_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -692,7 +782,7 @@ func (x *AlterOutboundRequest) String() string {
func (*AlterOutboundRequest) ProtoMessage() {}
func (x *AlterOutboundRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_command_command_proto_msgTypes[15]
mi := &file_app_proxyman_command_command_proto_msgTypes[17]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -705,7 +795,7 @@ func (x *AlterOutboundRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use AlterOutboundRequest.ProtoReflect.Descriptor instead.
func (*AlterOutboundRequest) Descriptor() ([]byte, []int) {
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{15}
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{17}
}
func (x *AlterOutboundRequest) GetTag() string {
@@ -730,7 +820,7 @@ type AlterOutboundResponse struct {
func (x *AlterOutboundResponse) Reset() {
*x = AlterOutboundResponse{}
mi := &file_app_proxyman_command_command_proto_msgTypes[16]
mi := &file_app_proxyman_command_command_proto_msgTypes[18]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -742,7 +832,7 @@ func (x *AlterOutboundResponse) String() string {
func (*AlterOutboundResponse) ProtoMessage() {}
func (x *AlterOutboundResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_command_command_proto_msgTypes[16]
mi := &file_app_proxyman_command_command_proto_msgTypes[18]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -755,7 +845,88 @@ func (x *AlterOutboundResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use AlterOutboundResponse.ProtoReflect.Descriptor instead.
func (*AlterOutboundResponse) Descriptor() ([]byte, []int) {
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{16}
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{18}
}
type ListOutboundsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *ListOutboundsRequest) Reset() {
*x = ListOutboundsRequest{}
mi := &file_app_proxyman_command_command_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListOutboundsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListOutboundsRequest) ProtoMessage() {}
func (x *ListOutboundsRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_command_command_proto_msgTypes[19]
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 ListOutboundsRequest.ProtoReflect.Descriptor instead.
func (*ListOutboundsRequest) Descriptor() ([]byte, []int) {
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{19}
}
type ListOutboundsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Outbounds []*core.OutboundHandlerConfig `protobuf:"bytes,1,rep,name=outbounds,proto3" json:"outbounds,omitempty"`
}
func (x *ListOutboundsResponse) Reset() {
*x = ListOutboundsResponse{}
mi := &file_app_proxyman_command_command_proto_msgTypes[20]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListOutboundsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListOutboundsResponse) ProtoMessage() {}
func (x *ListOutboundsResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_command_command_proto_msgTypes[20]
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 ListOutboundsResponse.ProtoReflect.Descriptor instead.
func (*ListOutboundsResponse) Descriptor() ([]byte, []int) {
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{20}
}
func (x *ListOutboundsResponse) GetOutbounds() []*core.OutboundHandlerConfig {
if x != nil {
return x.Outbounds
}
return nil
}
type Config struct {
@@ -766,7 +937,7 @@ type Config struct {
func (x *Config) Reset() {
*x = Config{}
mi := &file_app_proxyman_command_command_proto_msgTypes[17]
mi := &file_app_proxyman_command_command_proto_msgTypes[21]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -778,7 +949,7 @@ func (x *Config) String() string {
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_command_command_proto_msgTypes[17]
mi := &file_app_proxyman_command_command_proto_msgTypes[21]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -791,7 +962,7 @@ func (x *Config) ProtoReflect() protoreflect.Message {
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{17}
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{21}
}
var File_app_proxyman_command_command_proto protoreflect.FileDescriptor
@@ -831,61 +1002,84 @@ 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,
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,
0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3f, 0x0a, 0x15,
0x47, 0x65, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x4a, 0x0a,
0x16, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73,
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73,
0x65, 0x72, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x22, 0x34, 0x0a, 0x1c, 0x47, 0x65, 0x74,
0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x43, 0x6f, 0x75, 0x6e,
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75,
0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22,
0x52, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63,
0x6f, 0x72, 0x65, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64,
0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75,
0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x0a, 0x15, 0x52, 0x65,
0x6d, 0x6f, 0x76, 0x65, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75,
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,
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, 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,
0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08,
0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0x3f, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x49,
0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
0x74, 0x61, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x4a, 0x0a, 0x16, 0x47, 0x65, 0x74,
0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05,
0x75, 0x73, 0x65, 0x72, 0x73, 0x22, 0x34, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01,
0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x52, 0x0a, 0x12, 0x41,
0x64, 0x64, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x3c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x22,
0x15, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x0a, 0x15, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61,
0x67, 0x22, 0x18, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4f, 0x75, 0x74, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x68, 0x0a, 0x14, 0x41,
0x6c, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x18, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4f,
0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x68, 0x0a, 0x14, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x3e, 0x0a, 0x09, 0x6f, 0x70, 0x65,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 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, 0x09,
0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x17, 0x0a, 0x15, 0x41, 0x6c, 0x74,
0x65, 0x72, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0xc5, 0x07, 0x0a,
0x0e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
0x6b, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2c, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61,
0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x78, 0x72,
0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x3e, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x18, 0x02, 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, 0x09, 0x6f, 0x70, 0x65, 0x72,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x17, 0x0a, 0x15, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x4f, 0x75,
0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16,
0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x57, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x75,
0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x3e, 0x0a, 0x09, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x4f,
0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22,
0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0xae, 0x09, 0x0a, 0x0e, 0x48, 0x61,
0x6e, 0x64, 0x6c, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6b, 0x0a, 0x0a,
0x41, 0x64, 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2c, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63,
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e,
0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x0d, 0x52, 0x65, 0x6d,
0x6f, 0x76, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2f, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63,
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x6e, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e,
0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x75,
0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x0d,
0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2f, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61,
0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d,
0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76,
0x65, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x00, 0x12, 0x71, 0x0a, 0x0c, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x62, 0x6f, 0x75,
0x6e, 0x64, 0x12, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72,
0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41,
0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65,
0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x6e,
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
0x71, 0x0a, 0x0c, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12,
0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79,
0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x6c, 0x74, 0x65,
0x72, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x2f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79,
0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x6c, 0x74, 0x65,
0x72, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x00, 0x12, 0x71, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e,
0x64, 0x73, 0x12, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72,
0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x4c,
0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72,
0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41,
0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x4c,
0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x78, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x30, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
@@ -924,14 +1118,22 @@ var file_app_proxyman_command_command_proto_rawDesc = []byte{
0x1a, 0x30, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78,
0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x6c, 0x74,
0x65, 0x72, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x00, 0x42, 0x6d, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f,
0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2f,
0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x19, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41,
0x70, 0x70, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x75, 0x74, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x2f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
0x64, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
0x6e, 0x64, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x6d, 0x0a, 0x1d, 0x63, 0x6f,
0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79,
0x6d, 0x61, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2e, 0x67,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78,
0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f,
0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x19,
0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61,
0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
@@ -946,7 +1148,7 @@ func file_app_proxyman_command_command_proto_rawDescGZIP() []byte {
return file_app_proxyman_command_command_proto_rawDescData
}
var file_app_proxyman_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
var file_app_proxyman_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 22)
var file_app_proxyman_command_command_proto_goTypes = []any{
(*AddUserOperation)(nil), // 0: xray.app.proxyman.command.AddUserOperation
(*RemoveUserOperation)(nil), // 1: xray.app.proxyman.command.RemoveUserOperation
@@ -956,49 +1158,59 @@ var file_app_proxyman_command_command_proto_goTypes = []any{
(*RemoveInboundResponse)(nil), // 5: xray.app.proxyman.command.RemoveInboundResponse
(*AlterInboundRequest)(nil), // 6: xray.app.proxyman.command.AlterInboundRequest
(*AlterInboundResponse)(nil), // 7: xray.app.proxyman.command.AlterInboundResponse
(*GetInboundUserRequest)(nil), // 8: xray.app.proxyman.command.GetInboundUserRequest
(*GetInboundUserResponse)(nil), // 9: xray.app.proxyman.command.GetInboundUserResponse
(*GetInboundUsersCountResponse)(nil), // 10: xray.app.proxyman.command.GetInboundUsersCountResponse
(*AddOutboundRequest)(nil), // 11: xray.app.proxyman.command.AddOutboundRequest
(*AddOutboundResponse)(nil), // 12: xray.app.proxyman.command.AddOutboundResponse
(*RemoveOutboundRequest)(nil), // 13: xray.app.proxyman.command.RemoveOutboundRequest
(*RemoveOutboundResponse)(nil), // 14: xray.app.proxyman.command.RemoveOutboundResponse
(*AlterOutboundRequest)(nil), // 15: xray.app.proxyman.command.AlterOutboundRequest
(*AlterOutboundResponse)(nil), // 16: xray.app.proxyman.command.AlterOutboundResponse
(*Config)(nil), // 17: xray.app.proxyman.command.Config
(*protocol.User)(nil), // 18: xray.common.protocol.User
(*core.InboundHandlerConfig)(nil), // 19: xray.core.InboundHandlerConfig
(*serial.TypedMessage)(nil), // 20: xray.common.serial.TypedMessage
(*core.OutboundHandlerConfig)(nil), // 21: xray.core.OutboundHandlerConfig
(*ListInboundsRequest)(nil), // 8: xray.app.proxyman.command.ListInboundsRequest
(*ListInboundsResponse)(nil), // 9: xray.app.proxyman.command.ListInboundsResponse
(*GetInboundUserRequest)(nil), // 10: xray.app.proxyman.command.GetInboundUserRequest
(*GetInboundUserResponse)(nil), // 11: xray.app.proxyman.command.GetInboundUserResponse
(*GetInboundUsersCountResponse)(nil), // 12: xray.app.proxyman.command.GetInboundUsersCountResponse
(*AddOutboundRequest)(nil), // 13: xray.app.proxyman.command.AddOutboundRequest
(*AddOutboundResponse)(nil), // 14: xray.app.proxyman.command.AddOutboundResponse
(*RemoveOutboundRequest)(nil), // 15: xray.app.proxyman.command.RemoveOutboundRequest
(*RemoveOutboundResponse)(nil), // 16: xray.app.proxyman.command.RemoveOutboundResponse
(*AlterOutboundRequest)(nil), // 17: xray.app.proxyman.command.AlterOutboundRequest
(*AlterOutboundResponse)(nil), // 18: xray.app.proxyman.command.AlterOutboundResponse
(*ListOutboundsRequest)(nil), // 19: xray.app.proxyman.command.ListOutboundsRequest
(*ListOutboundsResponse)(nil), // 20: xray.app.proxyman.command.ListOutboundsResponse
(*Config)(nil), // 21: xray.app.proxyman.command.Config
(*protocol.User)(nil), // 22: xray.common.protocol.User
(*core.InboundHandlerConfig)(nil), // 23: xray.core.InboundHandlerConfig
(*serial.TypedMessage)(nil), // 24: xray.common.serial.TypedMessage
(*core.OutboundHandlerConfig)(nil), // 25: xray.core.OutboundHandlerConfig
}
var file_app_proxyman_command_command_proto_depIdxs = []int32{
18, // 0: xray.app.proxyman.command.AddUserOperation.user:type_name -> xray.common.protocol.User
19, // 1: xray.app.proxyman.command.AddInboundRequest.inbound:type_name -> xray.core.InboundHandlerConfig
20, // 2: xray.app.proxyman.command.AlterInboundRequest.operation:type_name -> xray.common.serial.TypedMessage
18, // 3: xray.app.proxyman.command.GetInboundUserResponse.users:type_name -> xray.common.protocol.User
21, // 4: xray.app.proxyman.command.AddOutboundRequest.outbound:type_name -> xray.core.OutboundHandlerConfig
20, // 5: xray.app.proxyman.command.AlterOutboundRequest.operation:type_name -> xray.common.serial.TypedMessage
2, // 6: xray.app.proxyman.command.HandlerService.AddInbound:input_type -> xray.app.proxyman.command.AddInboundRequest
4, // 7: xray.app.proxyman.command.HandlerService.RemoveInbound:input_type -> xray.app.proxyman.command.RemoveInboundRequest
6, // 8: xray.app.proxyman.command.HandlerService.AlterInbound:input_type -> xray.app.proxyman.command.AlterInboundRequest
8, // 9: xray.app.proxyman.command.HandlerService.GetInboundUsers:input_type -> xray.app.proxyman.command.GetInboundUserRequest
8, // 10: xray.app.proxyman.command.HandlerService.GetInboundUsersCount:input_type -> xray.app.proxyman.command.GetInboundUserRequest
11, // 11: xray.app.proxyman.command.HandlerService.AddOutbound:input_type -> xray.app.proxyman.command.AddOutboundRequest
13, // 12: xray.app.proxyman.command.HandlerService.RemoveOutbound:input_type -> xray.app.proxyman.command.RemoveOutboundRequest
15, // 13: xray.app.proxyman.command.HandlerService.AlterOutbound:input_type -> xray.app.proxyman.command.AlterOutboundRequest
3, // 14: xray.app.proxyman.command.HandlerService.AddInbound:output_type -> xray.app.proxyman.command.AddInboundResponse
5, // 15: xray.app.proxyman.command.HandlerService.RemoveInbound:output_type -> xray.app.proxyman.command.RemoveInboundResponse
7, // 16: xray.app.proxyman.command.HandlerService.AlterInbound:output_type -> xray.app.proxyman.command.AlterInboundResponse
9, // 17: xray.app.proxyman.command.HandlerService.GetInboundUsers:output_type -> xray.app.proxyman.command.GetInboundUserResponse
10, // 18: xray.app.proxyman.command.HandlerService.GetInboundUsersCount:output_type -> xray.app.proxyman.command.GetInboundUsersCountResponse
12, // 19: xray.app.proxyman.command.HandlerService.AddOutbound:output_type -> xray.app.proxyman.command.AddOutboundResponse
14, // 20: xray.app.proxyman.command.HandlerService.RemoveOutbound:output_type -> xray.app.proxyman.command.RemoveOutboundResponse
16, // 21: xray.app.proxyman.command.HandlerService.AlterOutbound:output_type -> xray.app.proxyman.command.AlterOutboundResponse
14, // [14:22] is the sub-list for method output_type
6, // [6:14] is the sub-list for method input_type
6, // [6:6] is the sub-list for extension type_name
6, // [6:6] is the sub-list for extension extendee
0, // [0:6] is the sub-list for field type_name
22, // 0: xray.app.proxyman.command.AddUserOperation.user:type_name -> xray.common.protocol.User
23, // 1: xray.app.proxyman.command.AddInboundRequest.inbound:type_name -> xray.core.InboundHandlerConfig
24, // 2: xray.app.proxyman.command.AlterInboundRequest.operation:type_name -> xray.common.serial.TypedMessage
23, // 3: xray.app.proxyman.command.ListInboundsResponse.inbounds:type_name -> xray.core.InboundHandlerConfig
22, // 4: xray.app.proxyman.command.GetInboundUserResponse.users:type_name -> xray.common.protocol.User
25, // 5: xray.app.proxyman.command.AddOutboundRequest.outbound:type_name -> xray.core.OutboundHandlerConfig
24, // 6: xray.app.proxyman.command.AlterOutboundRequest.operation:type_name -> xray.common.serial.TypedMessage
25, // 7: xray.app.proxyman.command.ListOutboundsResponse.outbounds:type_name -> xray.core.OutboundHandlerConfig
2, // 8: xray.app.proxyman.command.HandlerService.AddInbound:input_type -> xray.app.proxyman.command.AddInboundRequest
4, // 9: xray.app.proxyman.command.HandlerService.RemoveInbound:input_type -> xray.app.proxyman.command.RemoveInboundRequest
6, // 10: xray.app.proxyman.command.HandlerService.AlterInbound:input_type -> xray.app.proxyman.command.AlterInboundRequest
8, // 11: xray.app.proxyman.command.HandlerService.ListInbounds:input_type -> xray.app.proxyman.command.ListInboundsRequest
10, // 12: xray.app.proxyman.command.HandlerService.GetInboundUsers:input_type -> xray.app.proxyman.command.GetInboundUserRequest
10, // 13: xray.app.proxyman.command.HandlerService.GetInboundUsersCount:input_type -> xray.app.proxyman.command.GetInboundUserRequest
13, // 14: xray.app.proxyman.command.HandlerService.AddOutbound:input_type -> xray.app.proxyman.command.AddOutboundRequest
15, // 15: xray.app.proxyman.command.HandlerService.RemoveOutbound:input_type -> xray.app.proxyman.command.RemoveOutboundRequest
17, // 16: xray.app.proxyman.command.HandlerService.AlterOutbound:input_type -> xray.app.proxyman.command.AlterOutboundRequest
19, // 17: xray.app.proxyman.command.HandlerService.ListOutbounds:input_type -> xray.app.proxyman.command.ListOutboundsRequest
3, // 18: xray.app.proxyman.command.HandlerService.AddInbound:output_type -> xray.app.proxyman.command.AddInboundResponse
5, // 19: xray.app.proxyman.command.HandlerService.RemoveInbound:output_type -> xray.app.proxyman.command.RemoveInboundResponse
7, // 20: xray.app.proxyman.command.HandlerService.AlterInbound:output_type -> xray.app.proxyman.command.AlterInboundResponse
9, // 21: xray.app.proxyman.command.HandlerService.ListInbounds:output_type -> xray.app.proxyman.command.ListInboundsResponse
11, // 22: xray.app.proxyman.command.HandlerService.GetInboundUsers:output_type -> xray.app.proxyman.command.GetInboundUserResponse
12, // 23: xray.app.proxyman.command.HandlerService.GetInboundUsersCount:output_type -> xray.app.proxyman.command.GetInboundUsersCountResponse
14, // 24: xray.app.proxyman.command.HandlerService.AddOutbound:output_type -> xray.app.proxyman.command.AddOutboundResponse
16, // 25: xray.app.proxyman.command.HandlerService.RemoveOutbound:output_type -> xray.app.proxyman.command.RemoveOutboundResponse
18, // 26: xray.app.proxyman.command.HandlerService.AlterOutbound:output_type -> xray.app.proxyman.command.AlterOutboundResponse
20, // 27: xray.app.proxyman.command.HandlerService.ListOutbounds:output_type -> xray.app.proxyman.command.ListOutboundsResponse
18, // [18:28] is the sub-list for method output_type
8, // [8:18] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
}
func init() { file_app_proxyman_command_command_proto_init() }
@@ -1012,7 +1224,7 @@ func file_app_proxyman_command_command_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_proxyman_command_command_proto_rawDesc,
NumEnums: 0,
NumMessages: 18,
NumMessages: 22,
NumExtensions: 0,
NumServices: 1,
},

View File

@@ -37,6 +37,14 @@ message AlterInboundRequest {
message AlterInboundResponse {}
message ListInboundsRequest {
bool isOnlyTags = 1;
}
message ListInboundsResponse {
repeated core.InboundHandlerConfig inbounds = 1;
}
message GetInboundUserRequest {
string tag = 1;
string email = 2;
@@ -69,6 +77,12 @@ message AlterOutboundRequest {
message AlterOutboundResponse {}
message ListOutboundsRequest {}
message ListOutboundsResponse {
repeated core.OutboundHandlerConfig outbounds = 1;
}
service HandlerService {
rpc AddInbound(AddInboundRequest) returns (AddInboundResponse) {}
@@ -76,6 +90,8 @@ service HandlerService {
rpc AlterInbound(AlterInboundRequest) returns (AlterInboundResponse) {}
rpc ListInbounds(ListInboundsRequest) returns (ListInboundsResponse) {}
rpc GetInboundUsers(GetInboundUserRequest) returns (GetInboundUserResponse) {}
rpc GetInboundUsersCount(GetInboundUserRequest) returns (GetInboundUsersCountResponse) {}
@@ -85,6 +101,8 @@ service HandlerService {
rpc RemoveOutbound(RemoveOutboundRequest) returns (RemoveOutboundResponse) {}
rpc AlterOutbound(AlterOutboundRequest) returns (AlterOutboundResponse) {}
rpc ListOutbounds(ListOutboundsRequest) returns (ListOutboundsResponse) {}
}
message Config {}

View File

@@ -22,11 +22,13 @@ const (
HandlerService_AddInbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/AddInbound"
HandlerService_RemoveInbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/RemoveInbound"
HandlerService_AlterInbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/AlterInbound"
HandlerService_ListInbounds_FullMethodName = "/xray.app.proxyman.command.HandlerService/ListInbounds"
HandlerService_GetInboundUsers_FullMethodName = "/xray.app.proxyman.command.HandlerService/GetInboundUsers"
HandlerService_GetInboundUsersCount_FullMethodName = "/xray.app.proxyman.command.HandlerService/GetInboundUsersCount"
HandlerService_AddOutbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/AddOutbound"
HandlerService_RemoveOutbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/RemoveOutbound"
HandlerService_AlterOutbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/AlterOutbound"
HandlerService_ListOutbounds_FullMethodName = "/xray.app.proxyman.command.HandlerService/ListOutbounds"
)
// HandlerServiceClient is the client API for HandlerService service.
@@ -36,11 +38,13 @@ type HandlerServiceClient interface {
AddInbound(ctx context.Context, in *AddInboundRequest, opts ...grpc.CallOption) (*AddInboundResponse, error)
RemoveInbound(ctx context.Context, in *RemoveInboundRequest, opts ...grpc.CallOption) (*RemoveInboundResponse, error)
AlterInbound(ctx context.Context, in *AlterInboundRequest, opts ...grpc.CallOption) (*AlterInboundResponse, error)
ListInbounds(ctx context.Context, in *ListInboundsRequest, opts ...grpc.CallOption) (*ListInboundsResponse, error)
GetInboundUsers(ctx context.Context, in *GetInboundUserRequest, opts ...grpc.CallOption) (*GetInboundUserResponse, error)
GetInboundUsersCount(ctx context.Context, in *GetInboundUserRequest, opts ...grpc.CallOption) (*GetInboundUsersCountResponse, error)
AddOutbound(ctx context.Context, in *AddOutboundRequest, opts ...grpc.CallOption) (*AddOutboundResponse, error)
RemoveOutbound(ctx context.Context, in *RemoveOutboundRequest, opts ...grpc.CallOption) (*RemoveOutboundResponse, error)
AlterOutbound(ctx context.Context, in *AlterOutboundRequest, opts ...grpc.CallOption) (*AlterOutboundResponse, error)
ListOutbounds(ctx context.Context, in *ListOutboundsRequest, opts ...grpc.CallOption) (*ListOutboundsResponse, error)
}
type handlerServiceClient struct {
@@ -81,6 +85,16 @@ func (c *handlerServiceClient) AlterInbound(ctx context.Context, in *AlterInboun
return out, nil
}
func (c *handlerServiceClient) ListInbounds(ctx context.Context, in *ListInboundsRequest, opts ...grpc.CallOption) (*ListInboundsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListInboundsResponse)
err := c.cc.Invoke(ctx, HandlerService_ListInbounds_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *handlerServiceClient) GetInboundUsers(ctx context.Context, in *GetInboundUserRequest, opts ...grpc.CallOption) (*GetInboundUserResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetInboundUserResponse)
@@ -131,6 +145,16 @@ func (c *handlerServiceClient) AlterOutbound(ctx context.Context, in *AlterOutbo
return out, nil
}
func (c *handlerServiceClient) ListOutbounds(ctx context.Context, in *ListOutboundsRequest, opts ...grpc.CallOption) (*ListOutboundsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListOutboundsResponse)
err := c.cc.Invoke(ctx, HandlerService_ListOutbounds_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// HandlerServiceServer is the server API for HandlerService service.
// All implementations must embed UnimplementedHandlerServiceServer
// for forward compatibility.
@@ -138,11 +162,13 @@ type HandlerServiceServer interface {
AddInbound(context.Context, *AddInboundRequest) (*AddInboundResponse, error)
RemoveInbound(context.Context, *RemoveInboundRequest) (*RemoveInboundResponse, error)
AlterInbound(context.Context, *AlterInboundRequest) (*AlterInboundResponse, error)
ListInbounds(context.Context, *ListInboundsRequest) (*ListInboundsResponse, error)
GetInboundUsers(context.Context, *GetInboundUserRequest) (*GetInboundUserResponse, error)
GetInboundUsersCount(context.Context, *GetInboundUserRequest) (*GetInboundUsersCountResponse, error)
AddOutbound(context.Context, *AddOutboundRequest) (*AddOutboundResponse, error)
RemoveOutbound(context.Context, *RemoveOutboundRequest) (*RemoveOutboundResponse, error)
AlterOutbound(context.Context, *AlterOutboundRequest) (*AlterOutboundResponse, error)
ListOutbounds(context.Context, *ListOutboundsRequest) (*ListOutboundsResponse, error)
mustEmbedUnimplementedHandlerServiceServer()
}
@@ -162,6 +188,9 @@ func (UnimplementedHandlerServiceServer) RemoveInbound(context.Context, *RemoveI
func (UnimplementedHandlerServiceServer) AlterInbound(context.Context, *AlterInboundRequest) (*AlterInboundResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AlterInbound not implemented")
}
func (UnimplementedHandlerServiceServer) ListInbounds(context.Context, *ListInboundsRequest) (*ListInboundsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListInbounds not implemented")
}
func (UnimplementedHandlerServiceServer) GetInboundUsers(context.Context, *GetInboundUserRequest) (*GetInboundUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetInboundUsers not implemented")
}
@@ -177,6 +206,9 @@ func (UnimplementedHandlerServiceServer) RemoveOutbound(context.Context, *Remove
func (UnimplementedHandlerServiceServer) AlterOutbound(context.Context, *AlterOutboundRequest) (*AlterOutboundResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AlterOutbound not implemented")
}
func (UnimplementedHandlerServiceServer) ListOutbounds(context.Context, *ListOutboundsRequest) (*ListOutboundsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListOutbounds not implemented")
}
func (UnimplementedHandlerServiceServer) mustEmbedUnimplementedHandlerServiceServer() {}
func (UnimplementedHandlerServiceServer) testEmbeddedByValue() {}
@@ -252,6 +284,24 @@ func _HandlerService_AlterInbound_Handler(srv interface{}, ctx context.Context,
return interceptor(ctx, in, info, handler)
}
func _HandlerService_ListInbounds_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListInboundsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HandlerServiceServer).ListInbounds(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: HandlerService_ListInbounds_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HandlerServiceServer).ListInbounds(ctx, req.(*ListInboundsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _HandlerService_GetInboundUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetInboundUserRequest)
if err := dec(in); err != nil {
@@ -342,6 +392,24 @@ func _HandlerService_AlterOutbound_Handler(srv interface{}, ctx context.Context,
return interceptor(ctx, in, info, handler)
}
func _HandlerService_ListOutbounds_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListOutboundsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HandlerServiceServer).ListOutbounds(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: HandlerService_ListOutbounds_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HandlerServiceServer).ListOutbounds(ctx, req.(*ListOutboundsRequest))
}
return interceptor(ctx, in, info, handler)
}
// HandlerService_ServiceDesc is the grpc.ServiceDesc for HandlerService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -361,6 +429,10 @@ var HandlerService_ServiceDesc = grpc.ServiceDesc{
MethodName: "AlterInbound",
Handler: _HandlerService_AlterInbound_Handler,
},
{
MethodName: "ListInbounds",
Handler: _HandlerService_ListInbounds_Handler,
},
{
MethodName: "GetInboundUsers",
Handler: _HandlerService_GetInboundUsers_Handler,
@@ -381,6 +453,10 @@ var HandlerService_ServiceDesc = grpc.ServiceDesc{
MethodName: "AlterOutbound",
Handler: _HandlerService_AlterOutbound_Handler,
},
{
MethodName: "ListOutbounds",
Handler: _HandlerService_ListOutbounds_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "app/proxyman/command/command.proto",

View File

@@ -449,11 +449,12 @@ type SenderConfig struct {
unknownFields protoimpl.UnknownFields
// Send traffic through the given IP. Only IP is allowed.
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"`
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"`
ViaCidr string `protobuf:"bytes,5,opt,name=via_cidr,json=viaCidr,proto3" json:"via_cidr,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"`
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"`
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() {
@@ -521,6 +522,13 @@ func (x *SenderConfig) GetViaCidr() string {
return ""
}
func (x *SenderConfig) GetTargetStrategy() internet.DomainStrategy {
if x != nil {
return x.TargetStrategy
}
return internet.DomainStrategy(0)
}
type MultiplexingConfig struct {
state protoimpl.MessageState
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,
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,
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,
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,
@@ -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,
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, 0x43, 0x69, 0x64, 0x72, 0x22, 0xa4, 0x01, 0x0a, 0x12, 0x4d, 0x75, 0x6c, 0x74,
0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18,
0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63,
0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x63,
0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75,
0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20,
0x01, 0x28, 0x05, 0x52, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72,
0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78,
0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x78,
0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 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,
0x76, 0x69, 0x61, 0x43, 0x69, 0x64, 0x72, 0x12, 0x50, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65,
0x74, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e,
0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69,
0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65,
0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x22, 0xa4, 0x01, 0x0a, 0x12, 0x4d, 0x75,
0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f,
0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52,
0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f,
0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18,
0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75,
0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72,
0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 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 (
@@ -850,6 +863,7 @@ var file_app_proxyman_config_proto_goTypes = []any{
(*internet.StreamConfig)(nil), // 13: xray.transport.internet.StreamConfig
(*serial.TypedMessage)(nil), // 14: xray.common.serial.TypedMessage
(*internet.ProxyConfig)(nil), // 15: xray.transport.internet.ProxyConfig
(internet.DomainStrategy)(0), // 16: xray.transport.internet.DomainStrategy
}
var file_app_proxyman_config_proto_depIdxs = []int32{
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
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
14, // [14:14] is the sub-list for method output_type
14, // [14:14] is the sub-list for method input_type
14, // [14:14] is the sub-list for extension type_name
14, // [14:14] is the sub-list for extension extendee
0, // [0:14] is the sub-list for field type_name
16, // 14: xray.app.proxyman.SenderConfig.target_strategy:type_name -> xray.transport.internet.DomainStrategy
15, // [15:15] is the sub-list for method output_type
15, // [15:15] is the sub-list for method input_type
15, // [15:15] is the sub-list for extension 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() }

View File

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

View File

@@ -9,11 +9,13 @@ import (
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/mux"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/policy"
"github.com/xtls/xray-core/features/stats"
"github.com/xtls/xray-core/proxy"
"github.com/xtls/xray-core/transport/internet"
"google.golang.org/protobuf/proto"
)
func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) {
@@ -42,10 +44,12 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter)
}
type AlwaysOnInboundHandler struct {
proxy proxy.Inbound
workers []worker
mux *mux.Server
tag string
proxyConfig interface{}
receiverConfig *proxyman.ReceiverConfig
proxy proxy.Inbound
workers []worker
mux *mux.Server
tag string
}
func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*AlwaysOnInboundHandler, error) {
@@ -59,9 +63,11 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
}
h := &AlwaysOnInboundHandler{
proxy: p,
mux: mux.NewServer(ctx),
tag: tag,
receiverConfig: receiverConfig,
proxyConfig: proxyConfig,
proxy: p,
mux: mux.NewServer(ctx),
tag: tag,
}
uplinkCounter, downlinkCounter := getStatCounter(core.MustFromContext(ctx), tag)
@@ -187,3 +193,16 @@ func (h *AlwaysOnInboundHandler) Tag() string {
func (h *AlwaysOnInboundHandler) GetInbound() proxy.Inbound {
return h.proxy
}
// ReceiverSettings implements inbound.Handler.
func (h *AlwaysOnInboundHandler) ReceiverSettings() *serial.TypedMessage {
return serial.ToTypedMessage(h.receiverConfig)
}
// ProxySettings implements inbound.Handler.
func (h *AlwaysOnInboundHandler) ProxySettings() *serial.TypedMessage {
if v, ok := h.proxyConfig.(proto.Message); ok {
return serial.ToTypedMessage(v)
}
return nil
}

View File

@@ -10,10 +10,12 @@ import (
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/mux"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/proxy"
"github.com/xtls/xray-core/transport/internet"
"google.golang.org/protobuf/proto"
)
type DynamicInboundHandler struct {
@@ -23,7 +25,7 @@ type DynamicInboundHandler struct {
receiverConfig *proxyman.ReceiverConfig
streamSettings *internet.MemoryStreamConfig
portMutex sync.Mutex
portsInUse map[net.Port]bool
portsInUse map[net.Port]struct{}
workerMutex sync.RWMutex
worker []worker
lastRefresh time.Time
@@ -39,7 +41,7 @@ func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *p
tag: tag,
proxyConfig: proxyConfig,
receiverConfig: receiverConfig,
portsInUse: make(map[net.Port]bool),
portsInUse: make(map[net.Port]struct{}),
mux: mux.NewServer(ctx),
v: v,
ctx: ctx,
@@ -84,7 +86,7 @@ func (h *DynamicInboundHandler) allocatePort() net.Port {
port := net.Port(allPorts[r])
_, used := h.portsInUse[port]
if !used {
h.portsInUse[port] = true
h.portsInUse[port] = struct{}{}
return port
}
}
@@ -205,3 +207,16 @@ func (h *DynamicInboundHandler) GetRandomInboundProxy() (interface{}, net.Port,
func (h *DynamicInboundHandler) Tag() string {
return h.tag
}
// ReceiverSettings implements inbound.Handler.
func (h *DynamicInboundHandler) ReceiverSettings() *serial.TypedMessage {
return serial.ToTypedMessage(h.receiverConfig)
}
// ProxySettings implements inbound.Handler.
func (h *DynamicInboundHandler) ProxySettings() *serial.TypedMessage {
if v, ok := h.proxyConfig.(proto.Message); ok {
return serial.ToTypedMessage(v)
}
return nil
}

View File

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

View File

@@ -91,6 +91,7 @@ func (w *tcpWorker) callback(conn stat.Connection) {
}
ctx = session.ContextWithInbound(ctx, &session.Inbound{
Source: net.DestinationFromAddr(conn.RemoteAddr()),
Local: net.DestinationFromAddr(conn.LocalAddr()),
Gateway: net.TCPDestination(w.address, w.port),
Tag: w.tag,
Conn: conn,
@@ -161,6 +162,7 @@ type udpConn struct {
uplink stats.Counter
downlink stats.Counter
inactive bool
cancel context.CancelFunc
}
func (c *udpConn) setInactive() {
@@ -203,6 +205,9 @@ func (c *udpConn) Write(buf []byte) (int, error) {
}
func (c *udpConn) Close() error {
if c.cancel != nil {
c.cancel()
}
common.Must(c.done.Close())
common.Must(common.Close(c.writer))
return nil
@@ -259,6 +264,7 @@ func (w *udpWorker) getConnection(id connID) (*udpConn, bool) {
defer w.Unlock()
if conn, found := w.activeConn[id]; found && !conn.done.Done() {
conn.updateActivity()
return conn, true
}
@@ -306,7 +312,8 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
common.Must(w.checker.Start())
go func() {
ctx := w.ctx
ctx, cancel := context.WithCancel(w.ctx)
conn.cancel = cancel
sid := session.NewID()
ctx = c.ContextWithID(ctx, sid)
@@ -315,8 +322,10 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
outbounds[0].Target = originalDest
}
ctx = session.ContextWithOutbounds(ctx, outbounds)
ctx = session.ContextWithInbound(ctx, &session.Inbound{
Source: source,
Local: net.DestinationFromAddr(w.hub.Addr()), // Due to some limitations, in UDP connections, localIP is always equal to listen interface IP
Gateway: net.UDPDestination(w.address, w.port),
Tag: w.tag,
})
@@ -324,6 +333,7 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
if w.sniffingConfig != nil {
content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
content.SniffingRequest.RouteOnly = w.sniffingConfig.RouteOnly
}
@@ -464,8 +474,8 @@ func (w *dsWorker) callback(conn stat.Connection) {
}
}
ctx = session.ContextWithInbound(ctx, &session.Inbound{
// Unix have no source addr, so we use gateway as source for log.
Source: net.UnixDestination(w.address),
Source: net.DestinationFromAddr(conn.RemoteAddr()),
Local: net.DestinationFromAddr(conn.LocalAddr()),
Gateway: net.UnixDestination(w.address),
Tag: w.tag,
Conn: conn,

View File

@@ -4,6 +4,7 @@ import (
"context"
"crypto/rand"
goerrors "errors"
"github.com/xtls/xray-core/common/dice"
"io"
"math/big"
gonet "net"
@@ -16,6 +17,7 @@ import (
"github.com/xtls/xray-core/common/mux"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/outbound"
@@ -27,6 +29,7 @@ import (
"github.com/xtls/xray-core/transport/internet/stat"
"github.com/xtls/xray-core/transport/internet/tls"
"github.com/xtls/xray-core/transport/pipe"
"google.golang.org/protobuf/proto"
)
func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) {
@@ -59,6 +62,7 @@ type Handler struct {
tag string
senderSettings *proxyman.SenderConfig
streamSettings *internet.MemoryStreamConfig
proxyConfig proto.Message
proxy proxy.Outbound
outboundManager outbound.Manager
mux *mux.ClientManager
@@ -101,6 +105,7 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou
if err != nil {
return nil, err
}
h.proxyConfig = proxyConfig
rawProxyHandler, err := common.CreateObject(ctx, proxyConfig)
if err != nil {
@@ -173,6 +178,25 @@ func (h *Handler) Tag() string {
func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
outbounds := session.OutboundsFromContext(ctx)
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) {
ips, err := internet.LookupForIP(ob.Target.Address.Domain(), h.senderSettings.TargetStrategy, 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 {
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}
@@ -184,6 +208,7 @@ func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
session.SubmitOutboundErrorToOriginator(ctx, err)
errors.LogInfo(ctx, err.Error())
common.Interrupt(link.Writer)
common.Interrupt(link.Reader)
}
}
if ob.Target.Network == net.Network_UDP && ob.Target.Port == 443 {
@@ -241,7 +266,9 @@ func (h *Handler) DestIpAddress() net.IP {
// Dial implements internet.Dialer.
func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connection, error) {
if h.senderSettings != nil {
if h.senderSettings.ProxySettings.HasTag() {
tag := h.senderSettings.ProxySettings.Tag
handler := h.outboundManager.GetHandler(tag)
if handler != nil {
@@ -270,13 +297,36 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
}
if h.senderSettings.Via != nil {
outbounds := session.OutboundsFromContext(ctx)
ob := outbounds[len(outbounds)-1]
if h.senderSettings.ViaCidr == "" {
ob.Gateway = h.senderSettings.Via.AsAddress()
} else { //Get a random address.
ob.Gateway = ParseRandomIPv6(h.senderSettings.Via.AsAddress(), h.senderSettings.ViaCidr)
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
}
}
}
@@ -316,23 +366,35 @@ func (h *Handler) Start() error {
// Close implements common.Closable.
func (h *Handler) Close() error {
common.Close(h.mux)
common.Close(h.proxy)
return nil
}
func ParseRandomIPv6(address net.Address, prefix string) net.Address {
_, network, _ := gonet.ParseCIDR(address.IP().String() + "/" + prefix)
maskSize, totalBits := network.Mask.Size()
subnetSize := big.NewInt(1).Lsh(big.NewInt(1), uint(totalBits-maskSize))
// random
randomBigInt, _ := rand.Int(rand.Reader, subnetSize)
startIPBigInt := big.NewInt(0).SetBytes(network.IP.To16())
randomIPBigInt := big.NewInt(0).Add(startIPBigInt, randomBigInt)
randomIPBytes := randomIPBigInt.Bytes()
randomIPBytes = append(make([]byte, 16-len(randomIPBytes)), randomIPBytes...)
return net.ParseAddress(gonet.IP(randomIPBytes).String())
// SenderSettings implements outbound.Handler.
func (h *Handler) SenderSettings() *serial.TypedMessage {
return serial.ToTypedMessage(h.senderSettings)
}
// ProxySettings implements outbound.Handler.
func (h *Handler) ProxySettings() *serial.TypedMessage {
return serial.ToTypedMessage(h.proxyConfig)
}
func ParseRandomIP(addr net.Address, prefix string) net.Address {
_, ipnet, _ := gonet.ParseCIDR(addr.IP().String() + "/" + prefix)
ones, bits := ipnet.Mask.Size()
subnetSize := new(big.Int).Lsh(big.NewInt(1), uint(bits-ones))
rnd, _ := rand.Int(rand.Reader, subnetSize)
startInt := new(big.Int).SetBytes(ipnet.IP)
rndInt := new(big.Int).Add(startInt, rnd)
rndBytes := rndInt.Bytes()
padded := make([]byte, len(ipnet.IP))
copy(padded[len(padded)-len(rndBytes):], rndBytes)
return net.ParseAddress(gonet.IP(padded).String())
}

View File

@@ -145,6 +145,21 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error {
return nil
}
// ListHandlers implements outbound.Manager.
func (m *Manager) ListHandlers(ctx context.Context) []outbound.Handler {
m.access.RLock()
defer m.access.RUnlock()
response := make([]outbound.Handler, len(m.untaggedHandlers))
copy(response, m.untaggedHandlers)
for _, v := range m.taggedHandler {
response = append(response, v)
}
return response
}
// Select implements outbound.HandlerSelector.
func (m *Manager) Select(selectors []string) []string {

View File

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

View File

@@ -10,6 +10,7 @@ import (
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/mux"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/features/outbound"
@@ -111,6 +112,16 @@ func (o *Outbound) Close() error {
return nil
}
// SenderSettings implements outbound.Handler.
func (o *Outbound) SenderSettings() *serial.TypedMessage {
return nil
}
// ProxySettings implements outbound.Handler.
func (o *Outbound) ProxySettings() *serial.TypedMessage {
return nil
}
type StaticMuxPicker struct {
access sync.Mutex
workers []*PortalWorker
@@ -159,7 +170,7 @@ func (p *StaticMuxPicker) PickAvailable() (*mux.ClientWorker, error) {
if w.draining {
continue
}
if w.client.Closed() {
if w.IsFull() {
continue
}
if w.client.ActiveConnections() < minConn {
@@ -200,6 +211,7 @@ type PortalWorker struct {
writer buf.Writer
reader buf.Reader
draining bool
counter uint32
}
func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) {
@@ -233,7 +245,7 @@ func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) {
}
func (w *PortalWorker) heartbeat() error {
if w.client.Closed() {
if w.Closed() {
return errors.New("client worker stopped")
}
@@ -249,16 +261,21 @@ func (w *PortalWorker) heartbeat() error {
msg.State = Control_DRAIN
defer func() {
w.client.GetTimer().Reset(time.Second * 16)
common.Close(w.writer)
common.Interrupt(w.reader)
w.writer = nil
}()
}
b, err := proto.Marshal(msg)
common.Must(err)
mb := buf.MergeBytes(nil, b)
return w.writer.WriteMultiBuffer(mb)
w.counter = (w.counter + 1) % 5
if w.draining || w.counter == 1 {
b, err := proto.Marshal(msg)
common.Must(err)
mb := buf.MergeBytes(nil, b)
return w.writer.WriteMultiBuffer(mb)
}
return nil
}
func (w *PortalWorker) IsFull() bool {

View File

@@ -5,6 +5,7 @@ import (
sync "sync"
"github.com/xtls/xray-core/app/observatory"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/extension"
@@ -31,9 +32,10 @@ type RoundRobinStrategy struct {
func (s *RoundRobinStrategy) InjectContext(ctx context.Context) {
s.ctx = ctx
if len(s.FallbackTag) > 0 {
core.RequireFeaturesAsync(s.ctx, func(observatory extension.Observatory) {
common.Must(core.RequireFeatures(s.ctx, func(observatory extension.Observatory) error {
s.observatory = observatory
})
return nil
}))
}
}

View File

@@ -135,7 +135,7 @@ func (s *service) Register(server *grpc.Server) {
vCoreDesc := RoutingService_ServiceDesc
vCoreDesc.ServiceName = "v2ray.core.app.router.command.RoutingService"
server.RegisterService(&vCoreDesc, rs)
}))
}, false))
}
func init() {

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"`
OutboundGroupTags []string `protobuf:"bytes,11,rep,name=OutboundGroupTags,proto3" json:"OutboundGroupTags,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() {
@@ -158,6 +161,27 @@ func (x *RoutingContext) GetOutboundTag() string {
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
// opened by xray-core.
// * 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,
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,
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,
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,
@@ -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,
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,
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72,
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 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, 0x22, 0x46, 0x0a, 0x1c, 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, 0x12, 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64,
0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52,
0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x22,
0xb1, 0x01, 0x0a, 0x10, 0x54, 0x65, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43,
0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 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, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f,
0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f,
0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65,
0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x46,
0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x24, 0x0a,
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,
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61,
0x6c, 0x49, 0x50, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x4c, 0x6f, 0x63, 0x61,
0x6c, 0x49, 0x50, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, 0x72,
0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f,
0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x6f, 0x75, 0x74, 0x65,
0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x6f, 0x75,
0x74, 0x65, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73,
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 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, 0x22, 0x46, 0x0a, 0x1c, 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, 0x12, 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64,
0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x22, 0xb1, 0x01, 0x0a, 0x10, 0x54, 0x65,
0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f,
0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 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, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6f,
0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x57, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x6e, 0x63,
0x69, 0x70, 0x6c, 0x65, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x2c, 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, 0x50, 0x72, 0x69, 0x6e,
0x63, 0x69, 0x70, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52,
0x0f, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74,
0x22, 0x2a, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49,
0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61,
0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x5b, 0x0a, 0x17,
0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e,
0x63, 0x65, 0x72, 0x18, 0x01, 0x20, 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, 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,
0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52,
0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12,
0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72,
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65,
0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x24, 0x0a, 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, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72,
0x69, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
0x65, 0x12, 0x57, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x74,
0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 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, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e,
0x74, 0x65, 0x78, 0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x61, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74,
0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 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,
0x54, 0x65, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 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, 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,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x54,
0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x6e, 0x63,
0x69, 0x70, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x2a, 0x0a, 0x16, 0x47, 0x65,
0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x5b, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c,
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,
0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x36,
0x65, 0x12, 0x40, 0x0a, 0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x18, 0x01, 0x20,
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, 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, 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, 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,
0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x00,
0x30, 0x01, 0x12, 0x61, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12,
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, 0x54, 0x65, 0x73, 0x74, 0x52, 0x6f,
0x75, 0x74, 0x65, 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, 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, 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 (

View File

@@ -25,6 +25,9 @@ message RoutingContext {
map<string, string> Attributes = 10;
repeated string OutboundGroupTags = 11;
string OutboundTag = 12;
repeated bytes LocalIPs = 13;
uint32 LocalPort = 14;
uint32 VlessRoute = 15;
}
// 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())
}
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 {
return ""
}
@@ -54,8 +66,10 @@ var fieldMap = map[string]func(*RoutingContext, routing.Route){
"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_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_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() },
"protocol": func(s *RoutingContext, r routing.Route) { s.Protocol = r.GetProtocol() },
"user": func(s *RoutingContext, r routing.Route) { s.User = r.GetUser() },

View File

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

View File

@@ -115,4 +115,30 @@ func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) {
return m, nil
}
var globalGeoIPContainer GeoIPMatcherContainer
var GlobalGeoIPContainer GeoIPMatcherContainer
func MatchIPs(matchers []*GeoIPMatcher, ips []net.IP, reverse bool) []net.IP {
if len(matchers) == 0 {
panic("GeoIP matchers should not be empty to avoid ambiguity")
}
newIPs := make([]net.IP, 0, len(ips))
var isFound bool
for _, ip := range ips {
isFound = false
for _, matcher := range matchers {
if matcher.Match(ip) {
isFound = true
break
}
}
if isFound && !reverse {
newIPs = append(newIPs, ip)
continue
}
if !isFound && reverse {
newIPs = append(newIPs, ip)
continue
}
}
return newIPs
}

View File

@@ -328,9 +328,6 @@ func TestChinaSites(t *testing.T) {
domains, err := loadGeoSite("CN")
common.Must(err)
matcher, err := NewDomainMatcher(domains)
common.Must(err)
acMatcher, err := NewMphMatcherGroup(domains)
common.Must(err)
@@ -362,12 +359,9 @@ func TestChinaSites(t *testing.T) {
}
for _, testCase := range testCases {
r1 := matcher.ApplyDomain(testCase.Domain)
r2 := acMatcher.ApplyDomain(testCase.Domain)
if r1 != testCase.Output {
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)
r := acMatcher.ApplyDomain(testCase.Domain)
if r != testCase.Output {
t.Error("ACDomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r)
}
}
}
@@ -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) {
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)
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()
if len(rr.Domain) > 0 {
switch rr.DomainMatcher {
case "linear":
matcher, err := NewDomainMatcher(rr.Domain)
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)
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)
}
if len(rr.UserEmail) > 0 {
conds.Add(NewUserMatcher(rr.UserEmail))
}
if rr.VlessRouteList != nil {
conds.Add(NewPortMatcher(rr.VlessRouteList, "vlessRoute"))
}
if len(rr.InboundTag) > 0 {
conds.Add(NewInboundTagMatcher(rr.InboundTag))
}
if rr.PortList != nil {
conds.Add(NewPortMatcher(rr.PortList, false))
conds.Add(NewPortMatcher(rr.PortList, "target"))
}
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 {
@@ -73,7 +70,7 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
}
if len(rr.Geoip) > 0 {
cond, err := NewMultiGeoIPMatcher(rr.Geoip, false)
cond, err := NewMultiGeoIPMatcher(rr.Geoip, "target")
if err != nil {
return nil, err
}
@@ -81,13 +78,22 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
}
if len(rr.SourceGeoip) > 0 {
cond, err := NewMultiGeoIPMatcher(rr.SourceGeoip, true)
cond, err := NewMultiGeoIPMatcher(rr.SourceGeoip, "source")
if err != nil {
return nil, err
}
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 {
conds.Add(NewProtocolMatcher(rr.Protocol))
}

View File

@@ -470,7 +470,7 @@ type RoutingRule struct {
// *RoutingRule_Tag
// *RoutingRule_BalancingTag
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.
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
@@ -491,7 +491,9 @@ type RoutingRule struct {
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"`
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() {
@@ -622,11 +624,25 @@ func (x *RoutingRule) GetAttributes() map[string]string {
return nil
}
func (x *RoutingRule) GetDomainMatcher() string {
func (x *RoutingRule) GetLocalGeoip() []*GeoIP {
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 {
@@ -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,
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,
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,
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,
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,
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,
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,
@@ -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,
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,
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x1a, 0x3d, 0x0a,
0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
0x12, 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, 0x0c, 0x0a, 0x0a,
0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x22, 0xdc, 0x01, 0x0a, 0x0d, 0x42,
0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03,
0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x2b,
0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63,
0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x73,
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73,
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x4d, 0x0a, 0x11, 0x73, 0x74, 0x72, 0x61, 0x74,
0x65, 0x67, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 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, 0x10, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x53, 0x65,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61,
0x63, 0x6b, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x61,
0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x54, 0x61, 0x67, 0x22, 0x54, 0x0a, 0x0e, 0x53, 0x74, 0x72,
0x61, 0x74, 0x65, 0x67, 0x79, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72,
0x65, 0x67, 0x65, 0x78, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x67,
0x65, 0x78, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22,
0xc0, 0x01, 0x0a, 0x17, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x4c, 0x65, 0x61, 0x73,
0x74, 0x4c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x05, 0x63,
0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x72,
0x61, 0x74, 0x65, 0x67, 0x79, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x05, 0x63, 0x6f, 0x73,
0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18,
0x03, 0x20, 0x03, 0x28, 0x03, 0x52, 0x09, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73,
0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01,
0x28, 0x05, 0x52, 0x08, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06,
0x6d, 0x61, 0x78, 0x52, 0x54, 0x54, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61,
0x78, 0x52, 0x54, 0x54, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63,
0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e,
0x63, 0x65, 0x22, 0x9b, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a,
0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e,
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x30,
0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78,
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, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65,
0x12, 0x45, 0x0a, 0x0e, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75,
0x6c, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e,
0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63,
0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 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,
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f,
0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65,
0x6f, 0x49, 0x50, 0x52, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12,
0x41, 0x0a, 0x0f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c, 0x69,
0x73, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x4c,
0x69, 0x73, 0x74, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, 0x72, 0x74, 0x4c, 0x69,
0x73, 0x74, 0x12, 0x43, 0x0a, 0x10, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x72, 0x6f, 0x75, 0x74,
0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50,
0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x6f,
0x75, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69,
0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 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, 0x0c, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
0x5f, 0x74, 0x61, 0x67, 0x22, 0xdc, 0x01, 0x0a, 0x0d, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69,
0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20,
0x03, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x65, 0x6c,
0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67,
0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67,
0x79, 0x12, 0x4d, 0x0a, 0x11, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x5f, 0x73, 0x65,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 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, 0x10,
0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
0x12, 0x21, 0x0a, 0x0c, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x74, 0x61, 0x67,
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b,
0x54, 0x61, 0x67, 0x22, 0x54, 0x0a, 0x0e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x57,
0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x65, 0x78, 0x70, 0x18,
0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x67, 0x65, 0x78, 0x70, 0x12, 0x14, 0x0a,
0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x61,
0x74, 0x63, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01,
0x28, 0x02, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xc0, 0x01, 0x0a, 0x17, 0x53, 0x74,
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x4c, 0x65, 0x61, 0x73, 0x74, 0x4c, 0x6f, 0x61, 0x64, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x05, 0x63, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x57,
0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x05, 0x63, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09,
0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x03, 0x52,
0x09, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78,
0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x65, 0x78,
0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x52, 0x54, 0x54,
0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x52, 0x54, 0x54, 0x12, 0x1c,
0x0a, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28,
0x02, 0x52, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x9b, 0x02, 0x0a,
0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69,
0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x30, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65,
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 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, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x45, 0x0a, 0x0e, 0x62, 0x61,
0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f,
0x75, 0x74, 0x65, 0x72, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75,
0x6c, 0x65, 0x52, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c,
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 (
@@ -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
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
17, // 13: xray.app.router.BalancingRule.strategy_settings:type_name -> xray.common.serial.TypedMessage
10, // 14: xray.app.router.StrategyLeastLoadConfig.costs:type_name -> xray.app.router.StrategyWeight
1, // 15: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy
8, // 16: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule
9, // 17: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule
18, // [18:18] is the sub-list for method output_type
18, // [18:18] is the sub-list for method input_type
18, // [18:18] is the sub-list for extension type_name
18, // [18:18] is the sub-list for extension extendee
0, // [0:18] is the sub-list for field type_name
4, // 13: xray.app.router.RoutingRule.local_geoip:type_name -> xray.app.router.GeoIP
15, // 14: xray.app.router.RoutingRule.local_port_list:type_name -> xray.common.net.PortList
15, // 15: xray.app.router.RoutingRule.vless_route_list:type_name -> xray.common.net.PortList
17, // 16: xray.app.router.BalancingRule.strategy_settings:type_name -> xray.common.serial.TypedMessage
10, // 17: xray.app.router.StrategyLeastLoadConfig.costs:type_name -> xray.app.router.StrategyWeight
1, // 18: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy
8, // 19: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule
9, // 20: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule
21, // [21:21] is the sub-list for method output_type
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() }

View File

@@ -79,7 +79,7 @@ message RoutingRule {
// Tag of routing balancer.
string balancing_tag = 12;
}
string rule_tag = 18;
string rule_tag = 19;
// List of domains for target domain matching.
repeated Domain domain = 2;
@@ -109,7 +109,10 @@ message RoutingRule {
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 {

View File

@@ -177,7 +177,7 @@ func TestIPOnDemand(t *testing.T) {
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
}).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes()
}).Return([]net.IP{{192, 168, 0, 1}}, uint32(600), nil).AnyTimes()
r := new(Router)
common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil))
@@ -222,7 +222,7 @@ func TestIPIfNonMatchDomain(t *testing.T) {
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
}).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes()
}).Return([]net.IP{{192, 168, 0, 1}}, uint32(600), nil).AnyTimes()
r := new(Router)
common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil))

View File

@@ -7,6 +7,7 @@ import (
"time"
"github.com/xtls/xray-core/app/observatory"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/dice"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/core"
@@ -59,9 +60,10 @@ type node struct {
func (s *LeastLoadStrategy) InjectContext(ctx context.Context) {
s.ctx = ctx
core.RequireFeaturesAsync(s.ctx, func(observatory extension.Observatory) {
common.Must(core.RequireFeatures(s.ctx, func(observatory extension.Observatory) error {
s.observer = observatory
})
return nil
}))
}
func (s *LeastLoadStrategy) PickOutbound(candidates []string) string {

View File

@@ -4,6 +4,7 @@ import (
"context"
"github.com/xtls/xray-core/app/observatory"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/extension"
@@ -20,9 +21,10 @@ func (l *LeastPingStrategy) GetPrincipleTarget(strings []string) []string {
func (l *LeastPingStrategy) InjectContext(ctx context.Context) {
l.ctx = ctx
core.RequireFeaturesAsync(l.ctx, func(observatory extension.Observatory) {
common.Must(core.RequireFeatures(l.ctx, func(observatory extension.Observatory) error {
l.observatory = observatory
})
return nil
}))
}
func (l *LeastPingStrategy) PickOutbound(strings []string) string {

View File

@@ -4,6 +4,7 @@ import (
"context"
"github.com/xtls/xray-core/app/observatory"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/dice"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/extension"
@@ -20,9 +21,10 @@ type RandomStrategy struct {
func (s *RandomStrategy) InjectContext(ctx context.Context) {
s.ctx = ctx
if len(s.FallbackTag) > 0 {
core.RequireFeaturesAsync(s.ctx, func(observatory extension.Observatory) {
common.Must(core.RequireFeatures(s.ctx, func(observatory extension.Observatory) error {
s.observatory = observatory
})
return nil
}))
}
}

View File

@@ -12,6 +12,8 @@ import (
"github.com/xtls/xray-core/core"
feature_stats "github.com/xtls/xray-core/features/stats"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// 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) {
c := s.stats.GetCounter(request.Name)
if c == nil {
return nil, errors.New(request.Name, " not found.")
return nil, status.Error(codes.NotFound, request.Name+" not found.")
}
var value int64
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) {
c := s.stats.GetOnlineMap(request.Name)
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())
return &GetStatsResponse{
@@ -60,6 +62,24 @@ func (s *statsServer) GetStatsOnline(ctx context.Context, request *GetStatsReque
}, nil
}
func (s *statsServer) GetStatsOnlineIpList(ctx context.Context, request *GetStatsRequest) (*GetStatsOnlineIpListResponse, error) {
c := s.stats.GetOnlineMap(request.Name)
if c == nil {
return nil, status.Error(codes.NotFound, request.Name+" not found.")
}
ips := make(map[string]int64)
for ip, t := range c.IpTimeMap() {
ips[ip] = t.Unix()
}
return &GetStatsOnlineIpListResponse{
Name: request.Name,
Ips: ips,
}, nil
}
func (s *statsServer) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) {
matcher, err := strmatcher.Substr.New(request.Pattern)
if err != nil {

View File

@@ -424,6 +424,59 @@ func (x *SysStatsResponse) GetUptime() uint32 {
return 0
}
type GetStatsOnlineIpListResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Ips map[string]int64 `protobuf:"bytes,2,rep,name=ips,proto3" json:"ips,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
}
func (x *GetStatsOnlineIpListResponse) Reset() {
*x = GetStatsOnlineIpListResponse{}
mi := &file_app_stats_command_command_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetStatsOnlineIpListResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetStatsOnlineIpListResponse) ProtoMessage() {}
func (x *GetStatsOnlineIpListResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_stats_command_command_proto_msgTypes[7]
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 GetStatsOnlineIpListResponse.ProtoReflect.Descriptor instead.
func (*GetStatsOnlineIpListResponse) Descriptor() ([]byte, []int) {
return file_app_stats_command_command_proto_rawDescGZIP(), []int{7}
}
func (x *GetStatsOnlineIpListResponse) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *GetStatsOnlineIpListResponse) GetIps() map[string]int64 {
if x != nil {
return x.Ips
}
return nil
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -432,7 +485,7 @@ type Config struct {
func (x *Config) Reset() {
*x = Config{}
mi := &file_app_stats_command_command_proto_msgTypes[7]
mi := &file_app_stats_command_command_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -444,7 +497,7 @@ func (x *Config) String() string {
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_stats_command_command_proto_msgTypes[7]
mi := &file_app_stats_command_command_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -457,7 +510,7 @@ func (x *Config) ProtoReflect() protoreflect.Message {
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
return file_app_stats_command_command_proto_rawDescGZIP(), []int{7}
return file_app_stats_command_command_proto_rawDescGZIP(), []int{8}
}
var File_app_stats_command_command_proto protoreflect.FileDescriptor
@@ -506,40 +559,60 @@ var file_app_stats_command_command_proto_rawDesc = []byte{
0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x50,
0x61, 0x75, 0x73, 0x65, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x55,
0x70, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x55, 0x70, 0x74,
0x69, 0x6d, 0x65, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0xa1, 0x03,
0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f,
0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73,
0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74,
0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
0x65, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e,
0x65, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61,
0x69, 0x6d, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73,
0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x4f, 0x0a, 0x03, 0x69, 0x70, 0x73, 0x18,
0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47,
0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x70, 0x4c,
0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x70, 0x73, 0x45,
0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x69, 0x70, 0x73, 0x1a, 0x36, 0x0a, 0x08, 0x49, 0x70, 0x73,
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 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, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
0x01, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0x9a, 0x04, 0x0a, 0x0c,
0x53, 0x74, 0x61, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x08,
0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61,
0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74,
0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61,
0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a,
0x0e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12,
0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61,
0x74, 0x73, 0x12, 0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74,
0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x51, 0x75, 0x65, 0x72,
0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63,
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x0b, 0x47,
0x65, 0x74, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53,
0x74, 0x61, 0x74, 0x73, 0x12, 0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x51, 0x75,
0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74,
0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a,
0x0b, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53,
0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x42, 0x64, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50,
0x01, 0x5a, 0x2b, 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, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02,
0x16, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e,
0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x61, 0x6e, 0x64, 0x2e, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73,
0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53, 0x79, 0x73,
0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
0x77, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e,
0x65, 0x49, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x34, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74,
0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61,
0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x64, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63,
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2b, 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, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2f, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x16, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70,
0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -554,33 +627,38 @@ func file_app_stats_command_command_proto_rawDescGZIP() []byte {
return file_app_stats_command_command_proto_rawDescData
}
var file_app_stats_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
var file_app_stats_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
var file_app_stats_command_command_proto_goTypes = []any{
(*GetStatsRequest)(nil), // 0: xray.app.stats.command.GetStatsRequest
(*Stat)(nil), // 1: xray.app.stats.command.Stat
(*GetStatsResponse)(nil), // 2: xray.app.stats.command.GetStatsResponse
(*QueryStatsRequest)(nil), // 3: xray.app.stats.command.QueryStatsRequest
(*QueryStatsResponse)(nil), // 4: xray.app.stats.command.QueryStatsResponse
(*SysStatsRequest)(nil), // 5: xray.app.stats.command.SysStatsRequest
(*SysStatsResponse)(nil), // 6: xray.app.stats.command.SysStatsResponse
(*Config)(nil), // 7: xray.app.stats.command.Config
(*GetStatsRequest)(nil), // 0: xray.app.stats.command.GetStatsRequest
(*Stat)(nil), // 1: xray.app.stats.command.Stat
(*GetStatsResponse)(nil), // 2: xray.app.stats.command.GetStatsResponse
(*QueryStatsRequest)(nil), // 3: xray.app.stats.command.QueryStatsRequest
(*QueryStatsResponse)(nil), // 4: xray.app.stats.command.QueryStatsResponse
(*SysStatsRequest)(nil), // 5: xray.app.stats.command.SysStatsRequest
(*SysStatsResponse)(nil), // 6: xray.app.stats.command.SysStatsResponse
(*GetStatsOnlineIpListResponse)(nil), // 7: xray.app.stats.command.GetStatsOnlineIpListResponse
(*Config)(nil), // 8: xray.app.stats.command.Config
nil, // 9: xray.app.stats.command.GetStatsOnlineIpListResponse.IpsEntry
}
var file_app_stats_command_command_proto_depIdxs = []int32{
1, // 0: xray.app.stats.command.GetStatsResponse.stat:type_name -> xray.app.stats.command.Stat
1, // 1: xray.app.stats.command.QueryStatsResponse.stat:type_name -> xray.app.stats.command.Stat
0, // 2: xray.app.stats.command.StatsService.GetStats:input_type -> xray.app.stats.command.GetStatsRequest
0, // 3: xray.app.stats.command.StatsService.GetStatsOnline:input_type -> xray.app.stats.command.GetStatsRequest
3, // 4: xray.app.stats.command.StatsService.QueryStats:input_type -> xray.app.stats.command.QueryStatsRequest
5, // 5: xray.app.stats.command.StatsService.GetSysStats:input_type -> xray.app.stats.command.SysStatsRequest
2, // 6: xray.app.stats.command.StatsService.GetStats:output_type -> xray.app.stats.command.GetStatsResponse
2, // 7: xray.app.stats.command.StatsService.GetStatsOnline:output_type -> xray.app.stats.command.GetStatsResponse
4, // 8: xray.app.stats.command.StatsService.QueryStats:output_type -> xray.app.stats.command.QueryStatsResponse
6, // 9: xray.app.stats.command.StatsService.GetSysStats:output_type -> xray.app.stats.command.SysStatsResponse
6, // [6:10] is the sub-list for method output_type
2, // [2:6] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
9, // 2: xray.app.stats.command.GetStatsOnlineIpListResponse.ips:type_name -> xray.app.stats.command.GetStatsOnlineIpListResponse.IpsEntry
0, // 3: xray.app.stats.command.StatsService.GetStats:input_type -> xray.app.stats.command.GetStatsRequest
0, // 4: xray.app.stats.command.StatsService.GetStatsOnline:input_type -> xray.app.stats.command.GetStatsRequest
3, // 5: xray.app.stats.command.StatsService.QueryStats:input_type -> xray.app.stats.command.QueryStatsRequest
5, // 6: xray.app.stats.command.StatsService.GetSysStats:input_type -> xray.app.stats.command.SysStatsRequest
0, // 7: xray.app.stats.command.StatsService.GetStatsOnlineIpList:input_type -> xray.app.stats.command.GetStatsRequest
2, // 8: xray.app.stats.command.StatsService.GetStats:output_type -> xray.app.stats.command.GetStatsResponse
2, // 9: xray.app.stats.command.StatsService.GetStatsOnline:output_type -> xray.app.stats.command.GetStatsResponse
4, // 10: xray.app.stats.command.StatsService.QueryStats:output_type -> xray.app.stats.command.QueryStatsResponse
6, // 11: xray.app.stats.command.StatsService.GetSysStats:output_type -> xray.app.stats.command.SysStatsResponse
7, // 12: xray.app.stats.command.StatsService.GetStatsOnlineIpList:output_type -> xray.app.stats.command.GetStatsOnlineIpListResponse
8, // [8:13] is the sub-list for method output_type
3, // [3:8] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension 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_app_stats_command_command_proto_init() }
@@ -594,7 +672,7 @@ func file_app_stats_command_command_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_stats_command_command_proto_rawDesc,
NumEnums: 0,
NumMessages: 8,
NumMessages: 10,
NumExtensions: 0,
NumServices: 1,
},

View File

@@ -46,11 +46,17 @@ message SysStatsResponse {
uint32 Uptime = 10;
}
message GetStatsOnlineIpListResponse {
string name = 1;
map<string, int64> ips = 2;
}
service StatsService {
rpc GetStats(GetStatsRequest) returns (GetStatsResponse) {}
rpc GetStatsOnline(GetStatsRequest) returns (GetStatsResponse) {}
rpc QueryStats(QueryStatsRequest) returns (QueryStatsResponse) {}
rpc GetSysStats(SysStatsRequest) returns (SysStatsResponse) {}
rpc GetStatsOnlineIpList(GetStatsRequest) returns (GetStatsOnlineIpListResponse) {}
}
message Config {}

View File

@@ -19,10 +19,11 @@ import (
const _ = grpc.SupportPackageIsVersion9
const (
StatsService_GetStats_FullMethodName = "/xray.app.stats.command.StatsService/GetStats"
StatsService_GetStatsOnline_FullMethodName = "/xray.app.stats.command.StatsService/GetStatsOnline"
StatsService_QueryStats_FullMethodName = "/xray.app.stats.command.StatsService/QueryStats"
StatsService_GetSysStats_FullMethodName = "/xray.app.stats.command.StatsService/GetSysStats"
StatsService_GetStats_FullMethodName = "/xray.app.stats.command.StatsService/GetStats"
StatsService_GetStatsOnline_FullMethodName = "/xray.app.stats.command.StatsService/GetStatsOnline"
StatsService_QueryStats_FullMethodName = "/xray.app.stats.command.StatsService/QueryStats"
StatsService_GetSysStats_FullMethodName = "/xray.app.stats.command.StatsService/GetSysStats"
StatsService_GetStatsOnlineIpList_FullMethodName = "/xray.app.stats.command.StatsService/GetStatsOnlineIpList"
)
// StatsServiceClient is the client API for StatsService service.
@@ -33,6 +34,7 @@ type StatsServiceClient interface {
GetStatsOnline(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error)
QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error)
GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error)
GetStatsOnlineIpList(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsOnlineIpListResponse, error)
}
type statsServiceClient struct {
@@ -83,6 +85,16 @@ func (c *statsServiceClient) GetSysStats(ctx context.Context, in *SysStatsReques
return out, nil
}
func (c *statsServiceClient) GetStatsOnlineIpList(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsOnlineIpListResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetStatsOnlineIpListResponse)
err := c.cc.Invoke(ctx, StatsService_GetStatsOnlineIpList_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// StatsServiceServer is the server API for StatsService service.
// All implementations must embed UnimplementedStatsServiceServer
// for forward compatibility.
@@ -91,6 +103,7 @@ type StatsServiceServer interface {
GetStatsOnline(context.Context, *GetStatsRequest) (*GetStatsResponse, error)
QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error)
GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error)
GetStatsOnlineIpList(context.Context, *GetStatsRequest) (*GetStatsOnlineIpListResponse, error)
mustEmbedUnimplementedStatsServiceServer()
}
@@ -113,6 +126,9 @@ func (UnimplementedStatsServiceServer) QueryStats(context.Context, *QueryStatsRe
func (UnimplementedStatsServiceServer) GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetSysStats not implemented")
}
func (UnimplementedStatsServiceServer) GetStatsOnlineIpList(context.Context, *GetStatsRequest) (*GetStatsOnlineIpListResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetStatsOnlineIpList not implemented")
}
func (UnimplementedStatsServiceServer) mustEmbedUnimplementedStatsServiceServer() {}
func (UnimplementedStatsServiceServer) testEmbeddedByValue() {}
@@ -206,6 +222,24 @@ func _StatsService_GetSysStats_Handler(srv interface{}, ctx context.Context, dec
return interceptor(ctx, in, info, handler)
}
func _StatsService_GetStatsOnlineIpList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetStatsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(StatsServiceServer).GetStatsOnlineIpList(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: StatsService_GetStatsOnlineIpList_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(StatsServiceServer).GetStatsOnlineIpList(ctx, req.(*GetStatsRequest))
}
return interceptor(ctx, in, info, handler)
}
// StatsService_ServiceDesc is the grpc.ServiceDesc for StatsService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -229,6 +263,10 @@ var StatsService_ServiceDesc = grpc.ServiceDesc{
MethodName: "GetSysStats",
Handler: _StatsService_GetSysStats_Handler,
},
{
MethodName: "GetStatsOnlineIpList",
Handler: _StatsService_GetStatsOnlineIpList_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "app/stats/command/command.proto",

View File

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

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,8 +13,21 @@ const (
Size = 8192
)
var ErrBufferFull = errors.New("buffer is full")
var zero = [Size * 10]byte{0}
var pool = bytespool.GetPool(Size)
// ownership represents the data owner of the buffer.
type ownership uint8
const (
managed ownership = iota
unmanaged
bytespools
)
// Buffer is a recyclable allocation of a byte array. Buffer.Release() recycles
// the buffer into an internal buffer pool, in order to recreate a buffer more
// quickly.
@@ -22,11 +35,11 @@ type Buffer struct {
v []byte
start int32
end int32
unmanaged bool
ownership ownership
UDP *net.Destination
}
// New creates a Buffer with 0 length and 8K capacity.
// New creates a Buffer with 0 length and 8K capacity, managed.
func New() *Buffer {
buf := pool.Get().([]byte)
if cap(buf) >= Size {
@@ -40,7 +53,7 @@ func New() *Buffer {
}
}
// NewExisted creates a managed, standard size Buffer with an existed bytearray
// NewExisted creates a standard size Buffer with an existed bytearray, managed.
func NewExisted(b []byte) *Buffer {
if cap(b) < Size {
panic("Invalid buffer")
@@ -57,16 +70,16 @@ func NewExisted(b []byte) *Buffer {
}
}
// FromBytes creates a Buffer with an existed bytearray
// FromBytes creates a Buffer with an existed bytearray, unmanaged.
func FromBytes(b []byte) *Buffer {
return &Buffer{
v: b,
end: int32(len(b)),
unmanaged: true,
ownership: unmanaged,
}
}
// StackNew creates a new Buffer object on stack.
// StackNew creates a new Buffer object on stack, managed.
// This method is for buffers that is released in the same function.
func StackNew() Buffer {
buf := pool.Get().([]byte)
@@ -81,9 +94,17 @@ func StackNew() Buffer {
}
}
// NewWithSize creates a Buffer with 0 length and capacity with at least the given size, bytespool's.
func NewWithSize(size int32) *Buffer {
return &Buffer{
v: bytespool.Alloc(size),
ownership: bytespools,
}
}
// Release recycles the buffer into an internal buffer pool.
func (b *Buffer) Release() {
if b == nil || b.v == nil || b.unmanaged {
if b == nil || b.v == nil || b.ownership == unmanaged {
return
}
@@ -91,8 +112,13 @@ func (b *Buffer) Release() {
b.v = nil
b.Clear()
if cap(p) == Size {
pool.Put(p)
switch b.ownership {
case managed:
if cap(p) == Size {
pool.Put(p)
}
case bytespools:
bytespool.Free(p)
}
b.UDP = nil
}
@@ -128,6 +154,7 @@ func (b *Buffer) Extend(n int32) []byte {
}
ext := b.v[b.end:end]
b.end = end
copy(ext, zero[:])
return ext
}
@@ -176,6 +203,7 @@ func (b *Buffer) Check() {
// Resize cuts the buffer at the given position.
func (b *Buffer) Resize(from, to int32) {
oldEnd := b.end
if from < 0 {
from += b.Len()
}
@@ -188,6 +216,9 @@ func (b *Buffer) Resize(from, to int32) {
b.end = b.start + to
b.start += from
b.Check()
if b.end > oldEnd {
copy(b.v[oldEnd:b.end], zero[:])
}
}
// Advance cuts the buffer at the given position.
@@ -215,11 +246,12 @@ func (b *Buffer) Cap() int32 {
return int32(len(b.v))
}
// NewWithSize creates a Buffer with 0 length and capacity with at least the given size.
func NewWithSize(size int32) *Buffer {
return &Buffer{
v: bytespool.Alloc(size),
// 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.
@@ -236,13 +268,16 @@ func (b *Buffer) IsFull() bool {
func (b *Buffer) Write(data []byte) (int, error) {
nBytes := copy(b.v[b.end:], data)
b.end += int32(nBytes)
if nBytes < len(data) {
return nBytes, ErrBufferFull
}
return nBytes, nil
}
// WriteByte writes a single byte into the buffer.
func (b *Buffer) WriteByte(v byte) error {
if b.IsFull() {
return errors.New("buffer full")
return ErrBufferFull
}
b.v[b.end] = v
b.end++

View File

@@ -144,7 +144,7 @@ func Compact(mb MultiBuffer) MultiBuffer {
for i := 1; i < len(mb); i++ {
curr := mb[i]
if last.Len()+curr.Len() > Size {
if curr.Len() > last.Available() {
mb2 = append(mb2, last)
last = curr
} 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) {
var mb MultiBuffer
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.
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)
return v
}
@@ -38,7 +40,7 @@ func Error2(v interface{}, err error) error {
func envFile() (string, error) {
if file := os.Getenv("GOENV"); file != "" {
if file == "off" {
return "", fmt.Errorf("GOENV=off")
return "", errors.New("GOENV=off")
}
return file, nil
}
@@ -47,7 +49,7 @@ func envFile() (string, error) {
return "", err
}
if dir == "" {
return "", fmt.Errorf("missing user-config dir")
return "", errors.New("missing user-config dir")
}
return filepath.Join(dir, "go", "env"), nil
}
@@ -60,7 +62,7 @@ func GetRuntimeEnv(key string) (string, error) {
return "", err
}
if file == "" {
return "", fmt.Errorf("missing runtime env file")
return "", errors.New("missing runtime env file")
}
var data []byte
var runtimeEnv string

View File

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

View File

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

View File

@@ -1,2 +1,15 @@
// Package crypto provides common crypto libraries for Xray.
package crypto // import "github.com/xtls/xray-core/common/crypto"
import (
"crypto/rand"
"math/big"
)
func RandBetween(from int64, to int64) int64 {
if from == to {
return from
}
bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from))
return from + bigInt.Int64()
}

View File

@@ -1,6 +1,7 @@
package errors
import (
"errors"
"strings"
)
@@ -36,12 +37,12 @@ func AllEqual(expected error, actual error) bool {
return false
}
for _, err := range errs {
if err != expected {
if !errors.Is(err, expected) {
return false
}
}
return true
default:
return errs == expected
return errors.Is(errs, expected)
}
}

View File

@@ -146,7 +146,7 @@ func (w *fileLogWriter) Close() error {
func CreateStdoutLogWriter() WriterCreator {
return func() Writer {
return &consoleLogWriter{
logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
logger: log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lmicroseconds),
}
}
}
@@ -155,7 +155,7 @@ func CreateStdoutLogWriter() WriterCreator {
func CreateStderrLogWriter() WriterCreator {
return func() Writer {
return &consoleLogWriter{
logger: log.New(os.Stderr, "", log.Ldate|log.Ltime),
logger: log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lmicroseconds),
}
}
}
@@ -174,7 +174,7 @@ func CreateFileLogWriter(path string) (WriterCreator, error) {
}
return &fileLogWriter{
file: file,
logger: log.New(file, "", log.Ldate|log.Ltime),
logger: log.New(file, "", log.Ldate|log.Ltime|log.Lmicroseconds),
}
}, nil
}

View File

@@ -173,6 +173,7 @@ type ClientWorker struct {
sessionManager *SessionManager
link transport.Link
done *done.Instance
timer *time.Ticker
strategy ClientStrategy
}
@@ -187,6 +188,7 @@ func NewClientWorker(stream transport.Link, s ClientStrategy) (*ClientWorker, er
sessionManager: NewSessionManager(),
link: stream,
done: done.New(),
timer: time.NewTicker(time.Second * 16),
strategy: s,
}
@@ -209,9 +211,12 @@ func (m *ClientWorker) Closed() bool {
return m.done.Done()
}
func (m *ClientWorker) GetTimer() *time.Ticker {
return m.timer
}
func (m *ClientWorker) monitor() {
timer := time.NewTicker(time.Second * 16)
defer timer.Stop()
defer m.timer.Stop()
for {
select {
@@ -220,7 +225,7 @@ func (m *ClientWorker) monitor() {
common.Close(m.link.Writer)
common.Interrupt(m.link.Reader)
return
case <-timer.C:
case <-m.timer.C:
size := m.sessionManager.Size()
if size == 0 && m.sessionManager.CloseIfNoSession() {
common.Must(m.done.Close())
@@ -276,6 +281,8 @@ func (m *ClientWorker) IsClosing() bool {
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 {
if m.IsClosing() || m.Closed() {
return true
@@ -289,12 +296,12 @@ func (m *ClientWorker) IsFull() bool {
}
func (m *ClientWorker) Dispatch(ctx context.Context, link *transport.Link) bool {
if m.IsFull() || m.Closed() {
if m.IsFull() {
return false
}
sm := m.sessionManager
s := sm.Allocate()
s := sm.Allocate(&m.strategy)
if s == nil {
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 {
// deep-clone outbounds because it is going to be mutated concurrently
// (Target and OriginalTarget)
ctx = session.ContextCloneOutbounds(ctx)
ctx = session.SubContextFromMuxInbound(ctx)
errors.LogInfo(ctx, "received request for ", meta.Target)
{
msg := &log.AccessMessage{
@@ -201,11 +199,12 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
transferType: protocol.TransferTypePacket,
XUDP: x,
}
go handle(ctx, x.Mux, w.link.Writer)
x.Status = Active
if !w.sessionManager.Add(x.Mux) {
x.Mux.Close(false)
return errors.New("failed to add new session")
}
go handle(ctx, x.Mux, w.link.Writer)
return nil
}
@@ -226,18 +225,23 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
if meta.Target.Network == net.Network_UDP {
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)
if !meta.Option.Has(OptionData) {
return nil
}
rr := s.NewReader(reader, &meta.Target)
if err := buf.Copy(rr, s.output); err != nil {
buf.Copy(rr, buf.Discard)
return s.Close(false)
err = buf.Copy(rr, s.output)
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 {
@@ -304,10 +308,11 @@ func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedRead
}
func (w *ServerWorker) run(ctx context.Context) {
input := w.link.Reader
reader := &buf.BufferedReader{Reader: input}
reader := &buf.BufferedReader{Reader: w.link.Reader}
defer w.sessionManager.Close()
defer common.Close(w.link.Writer)
defer common.Interrupt(w.link.Reader)
for {
select {
@@ -318,7 +323,6 @@ func (w *ServerWorker) run(ctx context.Context) {
if err != nil {
if errors.Cause(err) != io.EOF {
errors.LogInfoInner(ctx, err, "unexpected EOF")
common.Interrupt(input)
}
return
}

View File

@@ -50,11 +50,14 @@ func (m *SessionManager) Count() int {
return int(m.count)
}
func (m *SessionManager) Allocate() *Session {
func (m *SessionManager) Allocate(Strategy *ClientStrategy) *Session {
m.Lock()
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
}

View File

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

View File

@@ -89,12 +89,10 @@ func UnixDestination(address Address) Destination {
// NetAddr returns the network address in this Destination in string form.
func (d Destination) NetAddr() string {
addr := ""
if d.Address != nil {
if d.Network == Network_TCP || d.Network == Network_UDP {
addr = d.Address.String() + ":" + d.Port.String()
} else if d.Network == Network_UNIX {
addr = d.Address.String()
}
if d.Network == Network_TCP || d.Network == Network_UDP {
addr = d.Address.String() + ":" + d.Port.String()
} else if d.Network == Network_UNIX {
addr = d.Address.String()
}
return addr
}

View File

@@ -1,2 +1,14 @@
// Package net is a drop-in replacement to Golang's net package, with some more functionalities.
package net // import "github.com/xtls/xray-core/common/net"
import "time"
// defines the maximum time an idle TCP session can survive in the tunnel, so
// it should be consistent across HTTP versions and with other transports.
const ConnIdleTimeout = 300 * time.Second
// consistent with quic-go
const QuicgoH3KeepAlivePeriod = 10 * time.Second
// consistent with chrome
const ChromeH2KeepAlivePeriod = 45 * time.Second

View File

@@ -76,8 +76,9 @@ type (
)
var (
ResolveUnixAddr = net.ResolveUnixAddr
ResolveTCPAddr = net.ResolveTCPAddr
ResolveUDPAddr = net.ResolveUDPAddr
ResolveUnixAddr = net.ResolveUnixAddr
)
type Resolver = net.Resolver

View File

@@ -3,6 +3,7 @@ package filesystem
import (
"io"
"os"
"path/filepath"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/platform"
@@ -28,6 +29,13 @@ func ReadAsset(file string) ([]byte, error) {
return ReadFile(platform.GetAssetLocation(file))
}
func ReadCert(file string) ([]byte, error) {
if filepath.IsAbs(file) {
return ReadFile(file)
}
return ReadFile(platform.GetCertLocation(file))
}
func CopyFile(dst string, src string) error {
bytes, err := ReadFile(src)
if err != nil {

View File

@@ -21,7 +21,7 @@ func GetToolLocation(file string) string {
return filepath.Join(toolPath, file)
}
// GetAssetLocation searches for `file` in certain locations
// GetAssetLocation searches for `file` in the env dir, the executable dir, and certain locations
func GetAssetLocation(file string) string {
assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir)
defPath := filepath.Join(assetPath, file)
@@ -42,3 +42,9 @@ func GetAssetLocation(file string) string {
// asset not found, let the caller throw out the error
return defPath
}
// GetCertLocation searches for `file` in the env dir and the executable dir
func GetCertLocation(file string) string {
certPath := NewEnvFlag(CertLocation).GetValue(getExecutableDir)
return filepath.Join(certPath, file)
}

View File

@@ -13,6 +13,7 @@ const (
ConfdirLocation = "xray.location.confdir"
ToolLocation = "xray.location.tool"
AssetLocation = "xray.location.asset"
CertLocation = "xray.location.cert"
UseReadV = "xray.buf.readv"
UseFreedomSplice = "xray.buf.splice"

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