Compare commits

...
Author SHA1 Message Date
8d0d2bbd4f fix: format transport pipe constructor
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-09-07 22:43:21 +00:00
copilot-swe-agent[bot]andGitHub 6539270b98 Initial plan 2026-09-07 22:41:39 +00:00
eef6e63bc1 XHTTP client: Fix a data race in WaitReadCloser (#6694)
https://github.com/XTLS/Xray-core/pull/6694#issuecomment-5575759500

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-09-07 22:38:16 +00:00
Hossin AsaadiandGitHub 6ce8dc53e7 common/buf/writer.go: Fix buffered writes failing when payload exceeds remaining capacity (#6723)
Fixes https://github.com/XTLS/Xray-core/issues/5287
2026-09-07 21:14:41 +00:00
01a034be53 Blackhole outbound: Add customizable response data support (#6713)
https://github.com/XTLS/Xray-core/pull/6711#issuecomment-5508683394

---------

Co-authored-by: Raaad1on <djilyawhite@gmail.com>
2026-09-07 21:09:58 +00:00
XXcipherXandGitHub de2caf3cef Hysteria server: Fix Unix masquerade socket path (#6705)
https://github.com/XTLS/Xray-core/pull/6705#issuecomment-5489788730
2026-09-07 21:01:45 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
cecc88f43c Bump google.golang.org/grpc from 1.83.1 to 1.83.2 (#6704)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.83.1 to 1.83.2.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.83.1...v1.83.2)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.83.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>
2026-09-07 20:42:22 +00:00
pexcnandGitHub cd4ce973e9 WebSocket client: Avoid panic before real dialing in delayDialConn (#6544)
Fixes https://github.com/teddysun/xray-plugin-android/issues/11
2026-09-01 04:57:10 +00:00
patternihaandGitHub fc7b980636 Sockopt: Fix indentation of the CustomSockopt block for Linux (#6568)
https://github.com/XTLS/Xray-core/pull/6568#issuecomment-5488145252

Fixes https://github.com/XTLS/Xray-core/issues/6564
Fixes https://github.com/XTLS/Xray-core/issues/6602
2026-09-01 04:47:55 +00:00
YounesandRPRX 8ee131cbbb HTTP inbound: Fix a potential panic in readResponseAndHandle100Continue() (#6545) 2026-09-01 02:59:56 +03:00
Jorik_SmithandGitHub 2776ea6d74 SS2022 outbound: Close connection to server on early returns (#6698)
https://github.com/XTLS/Xray-core/pull/6698#issuecomment-5468662565
2026-08-31 23:59:04 +00:00
meanwhile131andGitHub 5e245b082e Routing: Export HealthCheckSettings (#6680) 2026-08-30 12:19:54 +00:00
d9c54026c5 Sniffing: Support QUICv2 (#6695)
Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-08-30 12:15:25 +00:00
brookwkoandGitHub c1958dba04 TUN inbound: Support autoSystemRoutingTable and autoOutboundsInterface on FreeBSD as well (#6691)
https://github.com/XTLS/Xray-core/pull/6691#issuecomment-5445818753

Closes https://github.com/XTLS/Xray-core/issues/5594#issuecomment-4789101025
2026-08-27 22:32:45 +00:00
540b9070f5 Transport: Bind the UDP outbound socket in the destination family (#6688)
https://github.com/XTLS/Xray-core/pull/6688#issuecomment-5445559197

Completes https://github.com/XTLS/Xray-core/pull/6624#issuecomment-5430488664

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-08-27 21:46:26 +00:00
LjhAUMEMandGitHub ada99a4eb0 Hysteria: Upgrade to official v2.12.2 (#6565)
https://github.com/XTLS/Xray-core/pull/6565#issuecomment-5202554275

Fixes https://github.com/XTLS/Xray-core/issues/6408
Fixes https://github.com/XTLS/Xray-core/issues/6598
...
2026-08-27 17:25:56 +00:00
风扇滑翔翼 65458e919f Config: Fix some issues (#6640)
https://github.com/XTLS/Xray-core/pull/6640#issuecomment-5420106315

Fixes https://github.com/XTLS/Xray-core/issues/6636
Fixes https://github.com/XTLS/Xray-core/issues/6600
...
2026-08-27 20:03:59 +08:00
JidosandGitHub aa3d6589da TUN inbound: Wait() blocks via kqueue instead of busy-spinning on Darwin (#6580)
Fixes https://github.com/XTLS/Xray-core/issues/6579
2026-08-26 21:29:58 +00:00
Maksim VarentsovandGitHub 25c11e2d2b Tunnel inbound: SO_REUSEPORT fix and IPv6 support for the OpenBSD transparent proxy (#6624)
Completes https://github.com/XTLS/Xray-core/pull/6546
2026-08-26 21:05:05 +00:00
dffc7ada5e XHTTP client: Define Request.GetBody() for packet-up so h2 can replay after GOAWAY (#6632)
https://github.com/XTLS/Xray-core/pull/6632#issuecomment-5430735467

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-08-26 20:41:43 +00:00
77f98eba09 XHTTP client: Fix a race condition and a data race (#6665)
https://github.com/XTLS/Xray-core/pull/6665#issuecomment-5429028477

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-08-26 19:19:41 +00:00
风扇滑翔翼andGitHub f124daf5a3 Observatory: Fix consuming 100% CPU when no outbound matches subjectSelector (#6669)
Fixes https://github.com/XTLS/Xray-core/issues/6666
2026-08-25 18:30:33 +00:00
598bde7412 Router: Refactor to fix API data race (#6678)
Fixes https://github.com/XTLS/Xray-core/pull/6673

---------

Co-authored-by: Kosta <makostadev@xyecoc.com>
2026-08-25 18:00:14 +00:00
9b373e39ca Sniffer: Fix SniffUTP() (#6667)
Fixes https://github.com/XTLS/Xray-core/pull/6664

---------

Co-authored-by: n0ctal <4c866w5fn9@privaterelay.appleid.com>
2026-08-25 16:49:14 +00:00
c7e569b037 WireGuard outbound: Add remoteDNS & honor TTL (#6620)
Closes https://github.com/XTLS/Xray-core/pull/6569#issuecomment-5263789755

Fixes https://github.com/XTLS/Xray-core/issues/6567#issuecomment-5150957597

---------

Co-authored-by: LagPixelLOL <2282688304@qq.com>
2026-08-25 14:28:36 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
f02a357861 Bump github.com/stretchr/testify from 1.12.0 to 1.12.1 (#6657)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.12.0 to 1.12.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.12.0...v1.12.1)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-version: 1.12.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>
2026-08-24 02:38:00 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5fe6d6217a Bump google.golang.org/grpc from 1.83.0 to 1.83.1 (#6676)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.83.0 to 1.83.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.83.0...v1.83.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.83.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>
2026-08-24 02:29:35 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
0604ffa957 Bump github.com/miekg/dns from 1.1.72 to 1.1.73 (#6675)
Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.72 to 1.1.73.
- [Commits](https://github.com/miekg/dns/compare/v1.1.72...v1.1.73)

---
updated-dependencies:
- dependency-name: github.com/miekg/dns
  dependency-version: 1.1.73
  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>
2026-08-24 02:29:12 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d3f1a24285 Bump github.com/pion/stun/v3 from 3.1.6 to 3.1.7 (#6674)
Bumps [github.com/pion/stun/v3](https://github.com/pion/stun) from 3.1.6 to 3.1.7.
- [Release notes](https://github.com/pion/stun/releases)
- [Commits](https://github.com/pion/stun/compare/v3.1.6...v3.1.7)

---
updated-dependencies:
- dependency-name: github.com/pion/stun/v3
  dependency-version: 3.1.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>
2026-08-24 02:28:35 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2323273e37 Bump github.com/stretchr/testify from 1.11.1 to 1.12.0 (#6641)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.11.1 to 1.12.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.11.1...v1.12.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-version: 1.12.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>
2026-08-19 05:31:44 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
09107b71dc Bump golang.org/x/net from 0.57.0 to 0.58.0 (#6633)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.57.0 to 0.58.0.
- [Commits](https://github.com/golang/net/compare/v0.57.0...v0.58.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.58.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>
2026-08-19 05:31:41 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
7021606ad3 Bump google.golang.org/protobuf from 1.36.11 to 1.36.12 (#6616)
Bumps google.golang.org/protobuf from 1.36.11 to 1.36.12.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.12
  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>
2026-08-19 05:31:30 +00:00
风扇滑翔翼andGitHub 7d214f8b09 WireGuard outbound: Fix sendThrough support (#6570)
Fixes https://github.com/XTLS/Xray-core/issues/6559
2026-08-12 08:38:01 +00:00
yiguodevandGitHub 8b419d833d Routing: Fix process for macOS IPv4-mapped sockets (#6557)
Fixes https://github.com/XTLS/Xray-core/issues/6533
2026-08-12 05:40:07 +00:00
fanymagnetandGitHub a12801c13b Routing: Add localOS that directly matches runtime.GOOS (#6553)
https://github.com/XTLS/Xray-core/pull/6553#issuecomment-5262686006
2026-08-12 05:29:32 +00:00
Maksim VarentsovandGitHub a000371b2a Tunnel inbound: Support TPROXY on OpenBSD as well (#6546)
https://github.com/XTLS/Xray-core/pull/6546#issuecomment-5100711574
2026-08-12 04:51:38 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bc6e966af8 Bump github.com/cloudflare/circl from 1.6.4 to 1.6.5 (#6609)
Bumps [github.com/cloudflare/circl](https://github.com/cloudflare/circl) from 1.6.4 to 1.6.5.
- [Release notes](https://github.com/cloudflare/circl/releases)
- [Commits](https://github.com/cloudflare/circl/compare/v1.6.4...v1.6.5)

---
updated-dependencies:
- dependency-name: github.com/cloudflare/circl
  dependency-version: 1.6.5
  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>
2026-08-10 05:44:15 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
fc5620de98 Bump docker/login-action from 4.5.2 to 4.6.0 (#6610)
Bumps [docker/login-action](https://github.com/docker/login-action) from 4.5.2 to 4.6.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v4.5.2...v4.6.0)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 4.6.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>
2026-08-10 05:44:12 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b02bdcf4cc Bump google.golang.org/grpc from 1.82.1 to 1.83.0 (#6585)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.82.1 to 1.83.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.82.1...v1.83.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.83.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>
2026-08-09 08:27:32 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2b329b3675 Bump docker/login-action from 4 to 4.5.2 (#6556)
Bumps [docker/login-action](https://github.com/docker/login-action) from 4 to 4.5.2.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v4...v4.5.2)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 4.5.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>
2026-08-09 08:27:08 +00:00
RPRXandGitHub 5ca6f4b7d4 Xray-core v26.7.28
Sponsor & Donation & NFTs: https://github.com/XTLS/Xray-core/issues/3668
Project X Channel: https://t.me/projectXtls

Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-07-28 07:59:48 +00:00
RPRX 18e283909c XHTTP client: Reduce default maxConnections from 6 to 3 for anti-TSPU
https://github.com/XTLS/Xray-core/issues/6376#issuecomment-5101210849

Replaces https://github.com/XTLS/Xray-core/commit/18b85adb4e288f49a7894351c6e0f2428c0beef6
2026-07-28 07:57:55 +00:00
6ab123bf8f XMC finalmask: Add default directional padding/keep-alive packets, matching Minecraft 26.1.2 login/join/etc traffic shapes (#6487)
https://github.com/XTLS/Xray-core/pull/6487#issuecomment-5092235757

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-07-28 06:45:36 +00:00
4aba687dd3 XHTTP & gRPC servers: Get accurate localAddr (#6526)
Fixes https://github.com/XTLS/Xray-core/pull/6476

---------

Co-authored-by: echoowall <echoowall@gmail.com>
2026-07-27 13:11:25 +00:00
yiguodevandRPRX 5b1b41058e Routing: Exclude iOS from Darwin for process (#6524)
Fixes https://github.com/XTLS/Xray-core/pull/6434#issuecomment-5016175948
2026-07-27 12:40:18 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
6e3322d219 Bump actions/setup-go from 6 to 7 (#6511)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 6 to 7.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-go
  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>
2026-07-17 22:28:51 +00:00
Jasper344612andGitHub 1d8eb81d70 TUN inbound: Add desc config for Windows ("Wintun" by default) (#6486)
https://github.com/XTLS/Xray-docs-next/pull/872#issuecomment-4936055488
2026-07-17 22:21:33 +00:00
e78d8ef184 REALITY server: Refine warnings in parsing configuration (#6508)
https://github.com/XTLS/Xray-core/pull/6508#issuecomment-4995787310
https://github.com/XTLS/Xray-core/pull/6507#issuecomment-4995466980

---------

Co-authored-by: Enle <42325154+EnleStars@users.noreply.github.com>
Co-authored-by: Aleksey <fraybyl@gmail.com>
2026-07-17 21:51:29 +00:00
Jasper344612andGitHub 6ce924ad56 TUN inbound: Change default name to "utunN" (10~1024) (#6485)
https://github.com/XTLS/Xray-docs-next/pull/872#issuecomment-4936853064
2026-07-16 19:53:04 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
035d438979 Bump google.golang.org/grpc from 1.82.0 to 1.82.1 (#6502)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.82.0 to 1.82.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.82.0...v1.82.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.82.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>
2026-07-16 18:04:03 +00:00
RPRXandGitHub 50231eaff9 Xray-core v26.7.11
Sponsor & Donation & NFTs: https://github.com/XTLS/Xray-core/issues/3668
Project X Channel: https://t.me/projectXtls

Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-07-11 15:51:51 +00:00
1f74c480d6 Docker: Add pre-release tag (#6470)
https://github.com/XTLS/Xray-core/pull/6470#issuecomment-4947190027

---------

Co-authored-by: KobeArthurScofield <26771058+KobeArthurScofield@users.noreply.github.com>
Co-authored-by: Meow <197331664+Meo597@users.noreply.github.com>
2026-07-11 15:45:59 +00:00
RPRXandGitHub af7eb68028 REALITY server: Set default "minClientVer": "26.3.27" (change it at your own risk)
https://github.com/XTLS/Xray-core/pull/6181#issuecomment-4567373533
https://t.me/projectXtls/3373
https://t.me/projectXtls/3378
2026-07-11 14:30:54 +00:00
Exclude0122andGitHub 35387572e0 Finalmask: Add XMC (TCP, Minecraft) (#6210)
https://github.com/XTLS/Xray-core/pull/6210#issuecomment-4573680059
https://github.com/XTLS/Xray-core/pull/6210#issuecomment-4582554889
https://github.com/XTLS/Xray-core/pull/6210#issuecomment-4937008835
2026-07-11 13:59:24 +00:00
风扇滑翔翼andGitHub 8f15190c23 QUIC sniffer: Fix potential panic on malformed QUIC packets again (#6471)
Fixes https://github.com/XTLS/Xray-core/security/advisories/GHSA-xqmr-94vq-cxvx
Fixes https://github.com/XTLS/Xray-core/security/advisories/GHSA-9h6x-9r9m-5qw2
2026-07-10 21:40:50 +00:00
风扇滑翔翼andGitHub 64fada32b5 TLS client: Pinning CA must have serverName (or higher-priority verifyPeerCertByName or outbound's address) for pinnedPeerCertSha256 (#6472)
Fixes https://github.com/XTLS/Xray-core/security/advisories/GHSA-5wf9-h793-w73c
2026-07-10 21:03:35 +00:00
0bafca9486 Stats: Fix GetOrRegister*() races (#6468)
Fixes https://github.com/XTLS/Xray-core/pull/6467#issue-4856851059

---------

Co-authored-by: Joshua Su <i@joshua.su>
2026-07-10 20:34:27 +00:00
yiguodevandGitHub d5bc58dc6b Root config: Add env config (#6400)
https://github.com/XTLS/Xray-core/pull/6400#issuecomment-4880198100
https://github.com/XTLS/Xray-core/pull/6400#issuecomment-4916892425
https://github.com/XTLS/Xray-core/pull/6400#issuecomment-4929136670
2026-07-10 02:56:49 +00:00
patternihaandGitHub c18b39ed80 TLS client: Support more cipherSuites for "unsafe" (golang) fingerprint for anti-NIN (#6450)
https://github.com/XTLS/Xray-core/pull/6450#issuecomment-4929495504
2026-07-10 02:02:31 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e2ad0acf60 Bump github.com/pires/go-proxyproto from 0.14.0 to 0.15.0 (#6464)
Bumps [github.com/pires/go-proxyproto](https://github.com/pires/go-proxyproto) from 0.14.0 to 0.15.0.
- [Release notes](https://github.com/pires/go-proxyproto/releases)
- [Commits](https://github.com/pires/go-proxyproto/compare/v0.14.0...v0.15.0)

---
updated-dependencies:
- dependency-name: github.com/pires/go-proxyproto
  dependency-version: 0.15.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>
2026-07-10 01:56:16 +00:00
c320e89108 HTTPUpgrade server: Keep accepting after transient errors as RAW does; Add handshake read deadline as WS does (#6463)
https://github.com/XTLS/Xray-core/pull/6463#issuecomment-4929183257

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-07-09 20:55:40 +00:00
LjhAUMEMandGitHub 412898fed7 WireGuard outbound: Fix a race condition (#6461)
Fixes https://github.com/XTLS/Xray-core/pull/6321#issuecomment-4881463309
2026-07-09 20:40:02 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5c62d50d43 Bump golang.org/x/net from 0.56.0 to 0.57.0 (#6455)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.56.0 to 0.57.0.
- [Commits](https://github.com/golang/net/compare/v0.56.0...v0.57.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.57.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>
2026-07-09 14:11:38 +00:00
ivolframandGitHub 1aabe7ea78 XHTTP transport: Do not force trailing path / when both sessionID and seq are placed elsewhere (#6307)
https://github.com/XTLS/Xray-core/discussions/6385#discussioncomment-17454120

https://github.com/XTLS/Xray-core/pull/6307#issuecomment-4917498294

Closes https://github.com/XTLS/Xray-core/issues/6410
2026-07-08 17:59:19 +00:00
e4e7614c62 TLS ECH: Update ECH configuration parsing logic to be more robust (#6441)
https://github.com/XTLS/Xray-core/pull/6441#issuecomment-4903624643

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-07-08 11:12:37 +00:00
yiguodevandGitHub 987290ba48 Routing: process supports macOS as well (#6447)
https://github.com/XTLS/Xray-core/pull/6434#issuecomment-4903384018
2026-07-08 11:03:24 +00:00
d7fa2076c3 Xray-core: Forbid unencrypted outbounds on public Internet for VLESS and Trojan; Remove "none/zero/plain" for VMess and Shadowsocks (#6303)
https://github.com/XTLS/Xray-core/pull/5640#issuecomment-4611416512
https://github.com/XTLS/Xray-core/pull/6303#issuecomment-4668055274

---------

Co-authored-by: Meow <197331664+Meo597@users.noreply.github.com>
2026-07-07 02:01:53 +00:00
fb548f54d2 infra/conf/transport_internet.go: Split into multiple files; Rename network to method (#6426)
https://github.com/XTLS/Xray-core/issues/6376#issuecomment-4817591444
https://github.com/XTLS/Xray-core/pull/6426#issuecomment-4899048255

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-07-07 01:26:15 +00:00
yiguodevandGitHub 0495b17650 TUN inbound: Refine gateway and autoSystemRoutingTable on macOS (#6434)
https://github.com/XTLS/Xray-core/pull/6434#issuecomment-4885521586
2026-07-07 00:28:50 +00:00
yiguodevandGitHub 65f6f0a43b TUN inbound: Refine gateway and autoSystemRoutingTable on Linux (#6398)
https://github.com/XTLS/Xray-core/pull/6398#issuecomment-4880261591
2026-07-04 02:25:15 +00:00
Jasper344612andGitHub 3263ae9255 TUN inbound: Fix autoOutboundsInterface on Linux (#6413)
Fixes https://github.com/XTLS/Xray-core/issues/6412
2026-07-04 01:38:25 +00:00
LjhAUMEMandGitHub 3dc8bf3d8b Hysteria inbound: Fix dynamic UUID not accepted (#6395)
Fixes https://github.com/XTLS/Xray-core/pull/6375#issuecomment-4825923934
2026-07-04 01:28:21 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
695e68ef9e Bump github.com/pires/go-proxyproto from 0.12.0 to 0.14.0 (#6420)
Bumps [github.com/pires/go-proxyproto](https://github.com/pires/go-proxyproto) from 0.12.0 to 0.14.0.
- [Release notes](https://github.com/pires/go-proxyproto/releases)
- [Commits](https://github.com/pires/go-proxyproto/compare/v0.12.0...v0.14.0)

---
updated-dependencies:
- dependency-name: github.com/pires/go-proxyproto
  dependency-version: 0.14.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>
2026-07-03 17:53:15 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
dfdbcf86cc Bump github.com/klauspost/cpuid/v2 from 2.3.0 to 2.4.0 (#6417)
Bumps [github.com/klauspost/cpuid/v2](https://github.com/klauspost/cpuid) from 2.3.0 to 2.4.0.
- [Release notes](https://github.com/klauspost/cpuid/releases)
- [Commits](https://github.com/klauspost/cpuid/compare/v2.3.0...v2.4.0)

---
updated-dependencies:
- dependency-name: github.com/klauspost/cpuid/v2
  dependency-version: 2.4.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>
2026-07-03 17:45:51 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2b828b7bc2 Bump google.golang.org/grpc from 1.81.1 to 1.82.0 (#6415)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.81.1 to 1.82.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.81.1...v1.82.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.82.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>
2026-07-03 17:45:47 +00:00
RPRXandGitHub 45cf2898ab Xray-core v26.6.27
Sponsor & Donation & NFTs: https://github.com/XTLS/Xray-core/issues/3668
Project X Channel: https://t.me/projectXtls

Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-06-27 13:18:03 +00:00
RPRXandGitHub 18b85adb4e XHTTP client: Change default maxConnections to 6 for anti-RKN
"xmux": {
    "maxConcurrency": 0,
    "maxConnections": "6",
    "cMaxReuseTimes": 0,
    "hMaxRequestTimes": "600-900",
    "hMaxReusableSecs": "1800-3000",
    "hKeepAlivePeriod": 0
}

Replaces https://github.com/XTLS/Xray-core/commit/9cc7907234a8297a87a0ff77fc40db373b74a0f2 and https://github.com/XTLS/Xray-core/commit/4ce65fc74c4c50919b10b3faff9725f75bba5d73

Closes https://github.com/XTLS/Xray-core/issues/6376#issuecomment-4817033592
2026-06-27 12:41:39 +00:00
452b719504 Hysteria inbound: Support routing's vlessRoute as well (#6375)
https://github.com/XTLS/Xray-core/pull/6375#issuecomment-4795522284

---------

Co-authored-by: LjhAUMEM <llnu14702@gmail.com>
2026-06-27 12:04:58 +00:00
345c76f9a8 WireGuard inbound: Support dynamic peer management (#6360)
https://github.com/XTLS/Xray-core/pull/6360#issuecomment-4780311547

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: LjhAUMEM <llnu14702@gmail.com>
2026-06-27 11:41:22 +00:00
f496437b84 XHTTP server: Refactor upload_queue.go (#6372)
https://github.com/XTLS/Xray-core/pull/6372#issuecomment-4801395378

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-06-27 10:40:58 +00:00
b12bc504c8 README.md: Add Magic_V2Ray to Magisk in Installation (#6355)
https://github.com/XTLS/Xray-core/pull/6355#issuecomment-4793603321

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-06-24 21:06:24 +00:00
yiguodevandGitHub dda2b10c9d TUN inbound: Add traffic counters; Metrics: Rely on instance (#6349)
https://github.com/XTLS/Xray-core/pull/6349#issuecomment-4775121300
2026-06-24 12:00:22 +00:00
Jasper344612andGitHub 241aa38ac0 TUN inbound: Support autoSystemRoutingTable and autoOutboundsInterface on macOS and Linux as well (#6366)
https://github.com/XTLS/Xray-core/pull/6366#issuecomment-4788510365
2026-06-24 11:06:00 +00:00
风扇滑翔翼andRPRX 7e7e820763 Geodata: Apply uTLS Chrome fingerprint when downloading (#6371)
Closes https://github.com/XTLS/Xray-core/issues/6369
2026-06-24 10:52:28 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
f9eb1597ad Bump actions/cache from 5 to 6 (#6368)
Bumps [actions/cache](https://github.com/actions/cache) from 5 to 6.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '6'
  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>
2026-06-24 10:48:21 +00:00
风扇滑翔翼andGitHub ac04c445bd DNS: Fix unexpected TTL clamp (#6363)
Fixes https://github.com/XTLS/Xray-core/issues/6359
2026-06-23 11:48:17 +00:00
e7e9254630 TUN inbound: Avoid panic on nil RemoteAddr due to quickly closed connection (#6365)
Fixes https://github.com/XTLS/Xray-core/issues/6364#issuecomment-4778262075

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-06-23 11:37:21 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
fab4bcc1ed Bump github.com/cloudflare/circl from 1.6.3 to 1.6.4 (#6362)
Bumps [github.com/cloudflare/circl](https://github.com/cloudflare/circl) from 1.6.3 to 1.6.4.
- [Release notes](https://github.com/cloudflare/circl/releases)
- [Commits](https://github.com/cloudflare/circl/compare/v1.6.3...v1.6.4)

---
updated-dependencies:
- dependency-name: github.com/cloudflare/circl
  dependency-version: 1.6.4
  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>
2026-06-23 10:34:07 +00:00
RPRXandGitHub b99c3e5657 Xray-core v26.6.22
Sponsor & Donation & NFTs: https://github.com/XTLS/Xray-core/issues/3668
Project X Channel: https://t.me/projectXtls

Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-06-22 18:55:10 +00:00
OmoebaandGitHub 583bb4a63f XHTTP server: Fix scStreamUpServerSecs when xPaddingObfsMode is true (#6343)
https://github.com/XTLS/Xray-core/pull/6343#issuecomment-4771666966
2026-06-22 18:51:47 +00:00
Жора ЗмейкинandRPRX 9cd9382e3d TUN inbound: Support env XRAY_TUN_FD on Linux as well (#6338)
https://github.com/XTLS/Xray-core/pull/6338#issuecomment-4770945163
2026-06-22 17:38:36 +00:00
patternihaRPRXcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
567500c4af Fragment finalmask: Add lengths and delays (#6334)
Usage: https://github.com/XTLS/Xray-core/pull/6334#issue-4685556394

Behavior: https://github.com/XTLS/Xray-core/pull/6334#issuecomment-4751547750

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-22 17:38:12 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5aefcb41fb Bump github.com/pion/stun/v3 from 3.1.5 to 3.1.6 (#6357)
Bumps [github.com/pion/stun/v3](https://github.com/pion/stun) from 3.1.5 to 3.1.6.
- [Release notes](https://github.com/pion/stun/releases)
- [Commits](https://github.com/pion/stun/compare/v3.1.5...v3.1.6)

---
updated-dependencies:
- dependency-name: github.com/pion/stun/v3
  dependency-version: 3.1.6
  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>
2026-06-22 16:05:47 +00:00
MeowandGitHub be8009c625 Geodata: Cleanup unneeded matchers & domain: ignore case (#6342)
Completes https://github.com/XTLS/Xray-core/pull/6139
2026-06-19 12:02:27 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8734774e4a Bump actions/checkout from 6 to 7 (#6344)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [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/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  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>
2026-06-19 11:23:41 +00:00
风扇滑翔翼andGitHub 1e036ce1c5 XHTTP/3 client: Actively close underlying QUIC & UDP (#6332)
Fixes https://github.com/XTLS/Xray-core/issues/6328#issuecomment-4730379021
2026-06-18 23:55:11 +00:00
j2rong4cnandGitHub c815c2f2df Loopback outbound: Add sniffing (#6326)
Example: https://github.com/XTLS/Xray-core/pull/6326#issue-4659701786
2026-06-18 23:17:01 +00:00
bytecategoryandGitHub 986c512e0f XHTTP client: Avoid panic when host is invalid (#6316)
Fixes https://github.com/XTLS/Xray-core/issues/6315
2026-06-18 22:55:18 +00:00
MeowandGitHub 711aea4e34 XHTTP & WS & HU & gRPC servers: Require sockopt.trustedXForwardedFor (#6309)
https://github.com/XTLS/Xray-core/pull/6258#issuecomment-4663652131

Behavior: https://github.com/XTLS/Xray-core/pull/6258#issuecomment-4746598275

Replaces https://github.com/XTLS/Xray-core/pull/6159
2026-06-18 22:31:21 +00:00
风扇滑翔翼andRPRX 6412738486 Socks5 inbound: Fix issues in new UDP ASSOCIATE (#6325)
https://github.com/XTLS/Xray-core/pull/6325#issuecomment-4724008713

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

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-06-17 14:44:40 +00:00
LjhAUMEMandRPRX ad2e4cb0e1 Finalmask: Fix unexpected order and UDP's buf issue (#6331)
https://github.com/XTLS/Xray-docs-next/pull/866#issuecomment-4729859528

And https://github.com/XTLS/Xray-core/pull/6331#issuecomment-4730527410

Fixes https://github.com/XTLS/Xray-core/issues/6184#issuecomment-4725831023
2026-06-17 14:44:12 +00:00
829d54d7be Hysteria & XHTTP/3 clients: udpHop supports dialerProxy (#6320)
https://github.com/XTLS/Xray-core/pull/6320#issuecomment-4725679616

Fixes https://github.com/XTLS/Xray-core/pull/6320#issuecomment-4699599655

---------

Co-authored-by: LjhAUMEM <llnu14702@gmail.com>
2026-06-17 12:20:28 +00:00
LjhAUMEMandRPRX 862631172d WireGuard proxy: Refactor (#6287)
And https://github.com/XTLS/Xray-core/pull/6303#issuecomment-4669158076

Fixes https://github.com/XTLS/Xray-core/issues/6257
2026-06-17 12:02:10 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d27b3e46e2 Bump golang.org/x/net from 0.55.0 to 0.56.0 (#6310)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.55.0 to 0.56.0.
- [Commits](https://github.com/golang/net/compare/v0.55.0...v0.56.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.56.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>
2026-06-16 11:45:27 +00:00
LjhAUMEMandGitHub da21a8f77f TUN & WireGuard inbounds: Ignore b.UDP's domain when receiving it from outbound (#6285)
Fixes https://github.com/XTLS/Xray-core/issues/6279
2026-06-09 20:53:55 +00:00
e10347bf01 XHTTP transport: Add sessionIDTable and sessionIDLength; Rename session* to sessionID* (#6258)
https://github.com/XTLS/Xray-core/pull/6258#issuecomment-4658534046
https://github.com/XTLS/Xray-core/pull/6253#issuecomment-4657704004
https://github.com/XTLS/Xray-core/pull/6251#issuecomment-4612756220

Usage: https://github.com/XTLS/Xray-core/pull/6258#issue-4580617110

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

---------

Co-authored-by: XXcipherX <knazevvv6514@gmail.com>
2026-06-09 15:58:02 +00:00
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇andGitHub 26a022c905 GitHub Action CI, README.md: Refinements and add compliance contents (#6283)
And https://github.com/XTLS/Xray-core/pull/6283#issuecomment-4640162879
2026-06-09 12:08:38 +00:00
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇andGitHub 95e9816223 Chore: Limit sing* dependencies to shadowsocks_2022 only (#6286)
https://github.com/XTLS/Xray-core/pull/6286#issuecomment-4658968225
2026-06-09 10:55:42 +00:00
风扇滑翔翼andGitHub 3239d21168 TUN inbound: autoOutboundsInterface bypasses loopback addresses (#6276)
Fixes https://github.com/XTLS/Xray-core/issues/6269
2026-06-09 09:40:55 +00:00
风扇滑翔翼andGitHub 06b4931743 TUN inbound: Start TUN by AlwaysOnInboundHandler (#6275)
Fixes https://github.com/XTLS/Xray-core/issues/6274
2026-06-09 09:22:33 +00:00
LjhAUMEMandGitHub 6189d2bfd5 XICMP finalmask: Refine Linux sever (#6272)
Fixes https://github.com/XTLS/Xray-core/pull/6168
2026-06-09 09:14:54 +00:00
a0e9347f1b TLS ECH: Handle "h2c://" query correctly (#6261)
Fixes https://github.com/XTLS/Xray-core/issues/6259#issuecomment-4614984919

---------

Co-authored-by: j2rong4cn <36783515+j2rong4cn@users.noreply.github.com>
2026-06-09 09:03:24 +00:00
IconHHwandGitHub 83cf229909 Salamander finalmask: Replace math/rand with crypto/rand in salt generation (#6228)
And https://github.com/XTLS/Xray-core/pull/6228#issuecomment-4612712100

Fixes https://github.com/XTLS/Xray-core/pull/6228#issuecomment-4599037015
2026-06-08 19:55:06 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2249f8b5c6 Bump github.com/pion/stun/v3 from 3.1.2 to 3.1.5 (#6291)
Bumps [github.com/pion/stun/v3](https://github.com/pion/stun) from 3.1.2 to 3.1.5.
- [Release notes](https://github.com/pion/stun/releases)
- [Commits](https://github.com/pion/stun/compare/v3.1.2...v3.1.5)

---
updated-dependencies:
- dependency-name: github.com/pion/stun/v3
  dependency-version: 3.1.5
  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>
2026-06-08 19:29:14 +00:00
fdb9b616fc README.md: Add Hey to HarmonyOS in GUI Clients (#6244)
https://github.com/popsiclelmlm/Hey/blob/main/entry/src/main/cpp/README.md

---------

Co-authored-by: liumin01 <liumin01@roborock.com>
2026-06-02 23:58:56 +00:00
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇andGitHub d792fba59c GitHub Actions CI: Builders run only once in pull requests in the same repository (#6222)
https://github.com/XTLS/Xray-core/pull/6221#issuecomment-4588919827

Completes https://github.com/XTLS/Xray-core/pull/6090#issuecomment-4572094191
2026-06-02 23:40:17 +00:00
55956f8d70 TLS config: Remove some deprecated fields (#6226)
https://t.me/projectXtls/1490

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-06-02 23:36:42 +00:00
RPRXandGitHub 94ffd50060 Xray-core v26.6.1
Sponsor & Donation & NFTs: https://github.com/XTLS/Xray-core/issues/3668
Project X Channel: https://t.me/projectXtls

Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-06-01 02:11:09 +00:00
风扇滑翔翼andGitHub c4dfcd4c1a Burst observatory: Fix init check (#6221)
https://github.com/XTLS/Xray-core/pull/6106#issuecomment-4587975865
2026-06-01 01:30:40 +00:00
cb8cd048c1 DNS outbound: Replace "reject" with "return" (rCode is 0 by default) (#6214)
https://github.com/XTLS/Xray-core/pull/6214#issuecomment-4587988752

Example: https://github.com/XTLS/Xray-core/pull/6214#issue-4553786283

---------

Co-authored-by: Meo597 <197331664+Meo597@users.noreply.github.com>
2026-06-01 01:25:47 +00:00
DavoyanandGitHub 455f6bc2d5 uTLS: Update ModernFingerprints map and OtherFingerprints map (#6181)
Paying attention to https://github.com/XTLS/Xray-core/pull/6181#issuecomment-4567373533

And a real issue: https://github.com/XTLS/Xray-core/pull/6181#issuecomment-4583018017
2026-05-30 17:29:33 +00:00
LjhAUMEMandGitHub ba538619eb Realm finalmask: Fix client punch peers (#6213)
Fixes https://github.com/XTLS/Xray-core/pull/6137
2026-05-30 09:30:23 +00:00
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇andGitHub d43a808ea5 GitHub Action CI: Add Go source file format check (#6090)
https://github.com/XTLS/Xray-core/pull/6057#issuecomment-4364819830

And https://github.com/XTLS/Xray-core/pull/6149#issuecomment-4546876261
2026-05-29 15:04:59 +00:00
ca4b156b57 Burst observatory: Fix time compare, cancel pending ping on instance close or new schedule started (#6106)
https://github.com/XTLS/Xray-core/pull/6106#issuecomment-4448117773

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-05-29 11:59:24 +00:00
LjhAUMEMandRPRX aba22722a6 Finalmask: Add mkcp-legacy (UDP) to replace mkcp-* and legacy header-* (#6201)
https://github.com/XTLS/Xray-core/pull/6193#issuecomment-4543737699

https://github.com/XTLS/Xray-core/pull/6201#issuecomment-4574213827

Example: https://github.com/XTLS/Xray-core/pull/6201#issue-4525483699
2026-05-29 11:57:32 +00:00
569459c54c Sudoku finalmask: Harden UDP ReadFrom() against invalid packets (#6185)
https://github.com/XTLS/Xray-core/issues/6184#issuecomment-4573949756

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

---------

Co-authored-by: LjhAUMEM <llnu14702@gmail.com>
2026-05-29 10:47:58 +00:00
LjhAUMEMandGitHub 2b42699623 XICMP finalmask: Refactor & Speed up; Add multi ips and dgram mode (client) (#6168)
https://github.com/XTLS/Xray-core/pull/5872#issuecomment-4198514120
https://github.com/XTLS/Xray-core/pull/6168#issuecomment-4571606666
https://github.com/XTLS/Xray-core/pull/6168#issuecomment-4573294637

Example: https://github.com/XTLS/Xray-core/pull/6168#issue-4484980927

Closes https://github.com/XTLS/Xray-core/discussions/5879
2026-05-29 09:44:06 +00:00
LjhAUMEMandRPRX 66a8100737 Salamander finalmask: Support packetSize (Gecko in Hysteria v2.9.2) (#6198)
And some other refinements

https://github.com/XTLS/Xray-core/pull/6198#issuecomment-4567438813

Example: https://github.com/XTLS/Xray-core/pull/6198#issue-4522226670
2026-05-29 08:51:41 +00:00
LjhAUMEMandGitHub 36303694d1 Finalmask: Add Realm (UDP hole punching in Hysteria v2.9.1) (#6137)
https://github.com/XTLS/Xray-core/pull/5657#issuecomment-4446406536

https://github.com/XTLS/Xray-core/pull/6137#issuecomment-4469822775

Example: https://github.com/XTLS/Xray-core/pull/6137#issue-4454013510
2026-05-29 08:29:53 +00:00
Jason FoxandGitHub 4dcf802ae3 README.md: Add flutter_vless to Xray Wrapper in Others (#6197)
https://pub.dev/packages/flutter_vless
2026-05-28 20:15:38 +00:00
MeowandGitHub a2cec2e580 DNS: Avoid panic on domain too long (#6207)
Fixes https://github.com/XTLS/Xray-core/issues/6204
2026-05-28 20:06:32 +00:00
LjhAUMEMandGitHub 1cd7d25fec header-custom finalmask: Remove headerConnMode headerReadAddrAware interface (#6193)
https://github.com/XTLS/Xray-core/pull/5920#issuecomment-4253108964

https://github.com/XTLS/Xray-core/pull/6193#issuecomment-4531875088
2026-05-28 19:44:16 +00:00
LjhAUMEMandGitHub 09002ab763 noise finalmask: Better reset (#6188)
https://github.com/XTLS/Xray-core/pull/6170

Explanation: https://github.com/XTLS/Xray-core/pull/5657#issue-3901983829
2026-05-28 19:24:22 +00:00
cb206ddc74 DNS: Avoid passing domain to WriteTo func (#6163)
https://github.com/XTLS/Xray-core/pull/6163#issuecomment-4528771758

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-05-28 18:27:08 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
fa466f8174 Bump golang.org/x/net from 0.54.0 to 0.55.0 (#6192)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.54.0 to 0.55.0.
- [Commits](https://github.com/golang/net/compare/v0.54.0...v0.55.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.55.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>
2026-05-28 18:20:27 +00:00
e26f5e9548 Socks5 server: More standard UDP ASSOCIATE (RFC 1928) (#6149)
https://github.com/XTLS/Xray-core/pull/6149#issuecomment-4529069218

Fixes https://github.com/XTLS/Xray-core/issues/6145#issuecomment-4467482623

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-05-28 14:22:57 +00:00
风扇滑翔翼andRPRX 787aa7677b Hysteria server: tls.WithNextProto("h3") by default (#6186)
Client: https://github.com/XTLS/Xray-core/pull/6186#issuecomment-4528041838
2026-05-24 14:35:12 +00:00
风扇滑翔翼andGitHub d878fc83f8 SS2022 inbound: Fix potential panic when dispatching (#6162)
https://github.com/XTLS/Xray-core/pull/6162#issuecomment-4496738737
2026-05-24 12:50:02 +00:00
风扇滑翔翼andGitHub ee2b2c5ab6 leastLoad balancer: Implement missing tolerance (#6156)
Closes https://github.com/XTLS/Xray-core/issues/6154
2026-05-24 12:27:12 +00:00
Whale ChoiandGitHub 81d993f49d README.md: Remove Xray4Magisk; Add AsteriskNG to Android in GUI Clients (#6153)
https://github.com/XTLS/Xray-core/pull/6153#issuecomment-4522913987
2026-05-24 12:19:00 +00:00
风扇滑翔翼andGitHub 4c3842711d XHTTP client: Fix packet-up OpenUsage counting and H3 keepAlivePeriod parameter (#6140)
Fixes https://github.com/XTLS/Xray-core/pull/6140#issuecomment-4525409315
2026-05-24 12:05:40 +00:00
MeowandGitHub ab69985fcc Config: Warn when sockopt.trustedXForwardedFor is not set for XHTTP/WS/HU inbounds (#6159)
https://github.com/XTLS/Xray-core/pull/6110#issuecomment-4470157219

Usage: https://github.com/XTLS/Xray-core/pull/5331#issue-3655317949
2026-05-23 13:50:01 +00:00
风扇滑翔翼andGitHub 56bb63668c Geodata: Cleanup unneeded shared matchers (weakly referenced in map) (#6139)
https://github.com/XTLS/Xray-core/pull/6139#issuecomment-4525195265
2026-05-23 12:23:52 +00:00
bytecategoryandGitHub 359a28f876 XDNS finalmask: Support AAAA & A (#6123)
https://github.com/XTLS/Xray-core/pull/6123#issuecomment-4439994266
https://github.com/XTLS/Xray-core/pull/6123#issuecomment-4441819696
https://github.com/XTLS/Xray-core/pull/6123#issuecomment-4522352460

Example: https://github.com/XTLS/Xray-core/pull/6123#issue-4436283689

Document: https://xtls.github.io/config/transports/finalmask.html#xdns
2026-05-23 09:24:37 +00:00
Yury KastovandGitHub da9ba693cb Config: Support abstract unix sockets in remote loader (#6111)
Replaces https://github.com/XTLS/Xray-core/pull/5200
2026-05-22 19:49:19 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5488b86c67 Bump google.golang.org/grpc from 1.81.0 to 1.81.1 (#6133)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.81.0 to 1.81.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.81.0...v1.81.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.81.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>
2026-05-22 19:37:44 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
a3c054a54c Bump golang.org/x/net from 0.53.0 to 0.54.0 (#6113)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.53.0 to 0.54.0.
- [Commits](https://github.com/golang/net/compare/v0.53.0...v0.54.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  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>
2026-05-22 19:37:25 +00:00
RPRXandGitHub 1bdb488c9e Xray-core v26.5.9
Sponsor & Donation & NFTs: https://github.com/XTLS/Xray-core/issues/3668
Project X Channel: https://t.me/projectXtls

Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-05-09 17:36:29 +00:00
fanymagnetandGitHub d38ba9d507 XDNS finalmask: Allow "domain+udp://0.0.0.0:0" for using dialerProxy (#6086)
Fixes https://github.com/XTLS/Xray-core/issues/6046#issuecomment-4364873593
2026-05-09 17:30:08 +00:00
风扇滑翔翼andGitHub 9d9eaf399f XHTTP client: Fix potential memory leak in stream-up/one (#6095)
Fixes https://github.com/XTLS/Xray-core/pull/6087#issuecomment-4402474552
2026-05-09 16:54:57 +00:00
MeowandRPRX eb32d166af Docker: Update confdir's default files (#6089)
https://github.com/XTLS/Xray-core/pull/5101#issuecomment-4321731035
2026-05-09 12:35:16 +00:00
MeowandRPRX 1dbafe629a Config: Rename network/address/port in Tunnel inbound and DNS outbound (#6084)
https://github.com/XTLS/Xray-core/pull/6083#issuecomment-4387702965
https://github.com/XTLS/Xray-core/pull/6084#issuecomment-4395333530
2026-05-07 14:13:25 +00:00
MeowandRPRX c42deab55c Config: Rename inbounds' clients/accounts to users (#6083)
https://github.com/XTLS/Xray-core/pull/6055#issuecomment-4363896372
https://github.com/XTLS/Xray-core/pull/6082#issuecomment-4384523482
2026-05-07 14:13:05 +00:00
MeowandRPRX 906d49a271 Direct/Freedom outbound: Prefer IPv4 for finalRules' "AsIs" (#6075)
https://github.com/XTLS/Xray-core/pull/6075#issuecomment-4391684629
https://github.com/XTLS/Xray-core/pull/6075#issuecomment-4392629645
https://github.com/XTLS/Xray-core/pull/6075#issuecomment-4395258425
2026-05-07 14:12:39 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4192ca0827 Bump google.golang.org/grpc from 1.80.0 to 1.81.0 (#6077)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.80.0 to 1.81.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.80.0...v1.81.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.81.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>
2026-05-07 10:43:21 +00:00
RPRXandGitHub 228f1e13aa Xray-core v26.5.3
Sponsor & Donation & NFTs: https://github.com/XTLS/Xray-core/issues/3668
Project X Channel: https://t.me/projectXtls

Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-05-03 11:53:13 +00:00
LjhAUMEMandGitHub 15968585f3 quicParams config: Better unmarshaling udpHop (#6068)
Fixes https://github.com/XTLS/Xray-core/issues/6067#issuecomment-4365869178
2026-05-03 11:04:54 +00:00
4951994ebe README.md: Remove NetProxy-Magisk from Magisk & Android Clients (#6066)
https://github.com/XTLS/Xray-core/pull/6066#issuecomment-4365784320

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-05-03 08:51:14 +00:00
LjhAUMEMandGitHub 756a2d1327 Hysteria client: Fix sendThrough (#6063)
And fixes https://github.com/XTLS/Xray-core/issues/6046
2026-05-03 07:18:23 +00:00
RPRXandGitHub b279076ba1 Browser Dialer: Fix WSS with no early data
Fixes https://github.com/2dust/v2rayNG/issues/5519#issuecomment-4357741914
2026-05-02 21:55:12 +00:00
KostaandGitHub e61eeae258 Tunnel inbound: Fix panic when listening on UDS for Xray's internal services (e.g. API) (#6062)
And API supports listening on UNIX domain socket directly

Completes https://github.com/XTLS/Xray-core/pull/5693
2026-05-02 20:56:47 +00:00
MeowandGitHub 958eb9ea8f Direct/Freedom outbound: Add blockDelay to finalRules (30~90s by default) (#6060)
Document: https://xtls.github.io/config/outbounds/freedom.html#finalruleobject

https://github.com/XTLS/Xray-core/pull/6058#issuecomment-4363489838
https://github.com/XTLS/Xray-core/pull/6058#issuecomment-4363522836
https://github.com/XTLS/Xray-core/pull/6060#issuecomment-4363675060
https://github.com/XTLS/Xray-core/pull/6060#issuecomment-4364587508
2026-05-02 20:40:46 +00:00
8381a5a8a6 Block/Blackhole outbound: Better blocking UDP (#6057)
https://github.com/XTLS/Xray-core/issues/6051#issuecomment-4364008008

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

---------

Co-authored-by: Meo597 <197331664+Meo597@users.noreply.github.com>
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-05-02 15:27:30 +00:00
Yury KastovandGitHub 1ead940a71 Config: Parallel for for inbounds' clients (#6055)
https://github.com/XTLS/Xray-core/pull/6055#issuecomment-4360958652
2026-05-02 13:32:59 +00:00
bdff2fa72e Config: Support env XRAY_JSON_STRICT=true (#6053)
https://github.com/XTLS/Xray-core/pull/6053#issuecomment-4363840170

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-05-02 13:11:40 +00:00
LjhAUMEMandGitHub 1d62941bd2 Hysteria: Upgrade to official v2.8.2 (#6041)
https://github.com/XTLS/Xray-core/pull/6041#issuecomment-4357417742

And fixes https://github.com/XTLS/Xray-core/issues/6039
2026-05-02 12:27:27 +00:00
LjhAUMEMandGitHub 52cf9ef5d6 TUN inbound: Better "autoOutboundsInterface": "auto" (#6035)
https://github.com/XTLS/Xray-core/pull/6035#issuecomment-4336755860

Fixes https://github.com/XTLS/Xray-core/issues/6030
2026-05-02 12:18:25 +00:00
风扇滑翔翼andGitHub 16568314d8 TLS for WSS/HUS: Allow outer "alpn": ["h2", "http/1.1"] for camouflage (#6034)
https://github.com/XTLS/Xray-core/pull/6034#issuecomment-4363639160

Closes https://github.com/XTLS/Xray-core/issues/6024#issuecomment-4328306231
2026-05-02 11:07:12 +00:00
风扇滑翔翼andGitHub 1fc6850dc4 TLS ECH: Remove echForceQuery (ECH is forced now if configured) (#6032)
https://github.com/XTLS/Xray-core/pull/5887#issuecomment-4184701517
2026-05-02 10:33:43 +00:00
MeowandRPRX 4e87f59628 Direct/Freedom outbound: Add finalRules (matches network, port and ip, then action) with default safe policies (#6027)
Document: https://xtls.github.io/config/outbounds/freedom.html#finalruleobject

https://github.com/XTLS/Xray-core/pull/6027#issuecomment-4335790980
https://github.com/XTLS/Xray-core/pull/6027#issuecomment-4336309055
https://github.com/XTLS/Xray-core/pull/6027#issuecomment-4338269638

Closes https://github.com/XTLS/Xray-core/issues/6018#issuecomment-4329273637

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-05-02 01:58:43 +00:00
风扇滑翔翼andGitHub 7ab0a3ccb7 FakeDNS: Little fix (#6022)
https://github.com/XTLS/Xray-core/pull/6021#issuecomment-4344638838
2026-05-01 22:57:51 +00:00
yiguodevandGitHub 2fff03720d TUN inbound: Reply fake pong to ICMP ping (#6015)
https://github.com/XTLS/Xray-core/pull/6015#issuecomment-4321525342
2026-05-01 22:51:42 +00:00
ZeroandGitHub 7f7fc5a829 README.md: Add XrayUI-dev to Windows in GUI Clients (#6013)
https://github.com/XTLS/Xray-core/issues/6011#issuecomment-4320674957
2026-05-01 22:46:05 +00:00
yiguodevandGitHub ff6c060168 README.md Add OneXray to more platforms in GUI Clients (#5990)
https://t.me/projectXtls/2214
2026-05-01 22:38:26 +00:00
hiDandelionandGitHub 5b552db781 README.md: Add Anywhere to Others/iOS (#5762)
https://t.me/projectXtls/2237
2026-05-01 22:34:53 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
108bf7ff82 Bump github.com/robfig/cron/v3 from 3.0.0 to 3.0.1 (#6020)
Bumps [github.com/robfig/cron/v3](https://github.com/robfig/cron) from 3.0.0 to 3.0.1.
- [Commits](https://github.com/robfig/cron/compare/v3.0.0...v3.0.1)

---
updated-dependencies:
- dependency-name: github.com/robfig/cron/v3
  dependency-version: 3.0.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>
2026-05-01 22:31:59 +00:00
RPRXandGitHub 1836b1c6e4 Browser Dialer: Potential optimized IP and non-standard port
Fixes https://github.com/2dust/v2rayNG/issues/5519#issuecomment-4335112057
2026-04-28 20:57:20 +00:00
RPRXandGitHub b4f08981be v26.4.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

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-04-25 23:16:35 +00:00
RPRXandGitHub cd4d0baacd Xray-core: Mark "legacy reverse" as removed to avoid confusions
https://github.com/XTLS/Xray-core/issues/5973#issuecomment-4273582111

https://github.com/XTLS/Xray-core/pull/5947#issuecomment-4273415252
2026-04-25 23:03:19 +00:00
MeowandGitHub 3bc24a3d5d Geodata: Support automatically updating .dat files and hot reloading (#5992)
https://github.com/XTLS/Xray-core/pull/5992#issuecomment-4320551920

Usage: https://github.com/XTLS/Xray-core/pull/5992#issuecomment-4291168039
2026-04-25 21:20:42 +00:00
LjhAUMEMandRPRX fa07b34956 XDNS finalmask: Use single UDP socket for multiple resolvers for now (#5982)
https://github.com/XTLS/Xray-core/pull/5982#issuecomment-4302271929

Closes https://github.com/XTLS/Xray-core/pull/5976#issuecomment-4320460288
2026-04-25 20:26:15 +00:00
fish4terrisa-MSDSMandGitHub 85a8bf5f39 Browser Dialer: Allow being switched on runtime when Xray is used as a lib (#5978)
https://github.com/XTLS/Xray-core/pull/5978#issuecomment-4279520473

https://github.com/XTLS/Xray-core/pull/5978#issuecomment-4320401635
2026-04-25 19:48:49 +00:00
d0f533f94a app/router/condition.go: Retrieve original target in net.FindProcess() when "IPIfNonMatch" is enabled (#5979)
Fixes https://github.com/XTLS/Xray-core/issues/5980

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-04-25 17:45:03 +00:00
MeowandGitHub d1db1d6a27 DNS outbound: Add rules (matches qtype and domain, then action) (#5981)
https://github.com/XTLS/Xray-core/pull/5981#issuecomment-4279809648

Example: https://github.com/XTLS/Xray-core/pull/5981#issuecomment-4283200236

Closes https://github.com/XTLS/Xray-core/issues/5218
2026-04-25 17:27:39 +00:00
1a14ffcec6 Geodata strmatcher: Restore lenient Type.New(Domain) behavior (#5989)
https://github.com/XTLS/Xray-core/pull/5989#issuecomment-4288238360

Fixes https://github.com/XTLS/Xray-core/issues/5986#issuecomment-4288010800

---------

Co-authored-by: Meow <197331664+Meo597@users.noreply.github.com>
2026-04-25 17:08:23 +00:00
风扇滑翔翼andGitHub 454c930d13 Loopback outbound: Use DispatchLink() (#6005)
https://github.com/XTLS/Xray-core/pull/6000

Fixes https://github.com/XTLS/Xray-core/issues/5917
2026-04-25 16:48:49 +00:00
MeowandGitHub bc590bcb56 Geodata: Reduce memory usage again (#5975)
https://github.com/XTLS/Xray-core/pull/5975#issuecomment-4274779560
2026-04-25 16:15:37 +00:00
MeowandGitHub 7cf25970de IPMatcher: Fix full CIDR issue (#5971)
Fixes https://github.com/XTLS/Xray-core/issues/5977
2026-04-25 16:07:04 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d837687368 Bump golang.zx2c4.com/wireguard/windows from 0.6.1 to 1.0.1 (#5985)
Bumps golang.zx2c4.com/wireguard/windows from 0.6.1 to 1.0.1.

---
updated-dependencies:
- dependency-name: golang.zx2c4.com/wireguard/windows
  dependency-version: 1.0.1
  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>
2026-04-25 15:09:05 +00:00
RPRXandGitHub b4650360d6 v26.4.17
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-04-17 23:04:05 +00:00
d52f15060b Direct/Freedom outbound: Block UDP responses that are come from ipsBlocked as well (#5952)
https://github.com/XTLS/Xray-core/pull/5947#issuecomment-4258980670

https://github.com/XTLS/Xray-core/pull/5952#issuecomment-4259324234

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-04-17 22:56:27 +00:00
MeowandGitHub 31ab22c33d Geodata: Support reversed CIDR rules in IP rules (#5951)
https://github.com/XTLS/Xray-core/pull/5947#issuecomment-4258063215

https://github.com/XTLS/Xray-core/pull/5951#issuecomment-4260093653
2026-04-17 22:13:35 +00:00
MeowandGitHub d42c981f9c DomainMatcher: Fix Match() result slice aliasing race (#5959)
Fixes https://github.com/XTLS/Xray-core/pull/5814
2026-04-17 22:07:58 +00:00
ИванandGitHub cb1106c2fb header-custom finalmask: Extend expression primitives for 1:1 handshakes (#5949)
https://github.com/XTLS/Xray-core/pull/5945
https://github.com/XTLS/Xray-core/pull/5920
2026-04-17 22:01:54 +00:00
风扇滑翔翼andGitHub df4b97097c Loopback outbound: Avoid directly modifying potential shared ctx (#5960)
Fixes https://github.com/XTLS/Xray-core/issues/5958
2026-04-17 21:41:10 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
a9cec25b8d Bump github.com/pires/go-proxyproto from 0.11.0 to 0.12.0 (#5948)
Bumps [github.com/pires/go-proxyproto](https://github.com/pires/go-proxyproto) from 0.11.0 to 0.12.0.
- [Release notes](https://github.com/pires/go-proxyproto/releases)
- [Commits](https://github.com/pires/go-proxyproto/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: github.com/pires/go-proxyproto
  dependency-version: 0.12.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>
2026-04-17 21:33:38 +00:00
RPRXandGitHub c5edc122b7 v26.4.15
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-04-15 23:56:34 +00:00
RPRXandGitHub 9dd17d55fb XUDP GetGlobalID(): Remove inbound.Name == "wireguard" for now
https://github.com/XTLS/Xray-core/pull/5947#issuecomment-4256423483
2026-04-15 23:47:21 +00:00
310b764811 Direct/Freedom outbound: Add ipsBlocked (supports IP, CIDR, "geoip:", "ext:") and apply a default safe policy (#5947)
https://github.com/XTLS/Xray-core/pull/5892#issuecomment-4254056911

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-04-15 23:41:11 +00:00
风扇滑翔翼andGitHub 3691741440 Chore: Use buf.New() instead of buf.NewWithSize() (#5946)
https://github.com/XTLS/Xray-core/pull/5941#issuecomment-4252905907

https://github.com/XTLS/Xray-core/pull/5946#issuecomment-4253919073
2026-04-15 16:57:51 +00:00
ИванandGitHub 05e259c8e4 header-custom finalmask: Add UDP standalone handshake mode (#5945)
https://github.com/XTLS/Xray-core/commit/175502d8079aa5a151242ed911d01a1b90b98b28
2026-04-15 16:21:23 +00:00
ИванandGitHub 175502d807 header-custom finalmask: Add programmable handshake templates and runtime core (#5920)
https://github.com/XTLS/Xray-core/pull/5920#issuecomment-4252579201
https://github.com/XTLS/Xray-core/pull/5920#issuecomment-4231698135

https://t.me/projectXtls/1829
https://t.me/projectXtls/1640
2026-04-15 14:17:51 +00:00
6780045550 TUN inbound: Add FreeBSD support (#5891)
And reverts "refactor `mtu` to support setting IPv4/v6 separately" https://github.com/XTLS/Xray-core/pull/5891#issuecomment-4245677624

And fixes `autoOutboundsInterface` on Windows https://github.com/XTLS/Xray-core/pull/5887#issuecomment-4251719900

---------

Co-authored-by: LjhAUMEM <llnu14702@gmail.com>
2026-04-15 12:40:19 +00:00
LjhAUMEMandGitHub ff6126463b Hysteria inbound: Use transport's authentication when there are no clients (#5942) 2026-04-15 12:13:15 +00:00
LjhAUMEMandGitHub 5c3d639c09 Chore: Use buf.NewWithSize() (#5941) 2026-04-15 12:11:51 +00:00
MeowandGitHub 7c56b7beea DNS: Log rule matches and client order when finalQuery returns early (#5936) 2026-04-15 12:06:13 +00:00
MeowandGitHub 5b91b152bb DomainMatcher: Reduce startup time on Android as well (#5935)
https://github.com/XTLS/Xray-core/pull/5924#issuecomment-4242355080
2026-04-15 12:00:41 +00:00
MeowandGitHub ef77a42063 DomainMatcher: Fix CompactDomainMatcher rule indices (#5934)
Fixes https://github.com/XTLS/Xray-core/pull/5924
2026-04-15 11:56:31 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
dab99614dc Bump golang.zx2c4.com/wireguard/windows from 0.5.3 to 0.6.1 (#5932)
Bumps golang.zx2c4.com/wireguard/windows from 0.5.3 to 0.6.1.

---
updated-dependencies:
- dependency-name: golang.zx2c4.com/wireguard/windows
  dependency-version: 0.6.1
  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>
2026-04-15 11:54:49 +00:00
RPRXandGitHub 14e8ecfacf v26.4.13
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-04-13 22:49:40 +00:00
LjhAUMEMandGitHub 7094f8dc07 mKCP transport: Add cwndMultiplier; Apply unaggressive strategy by default (#5890)
https://github.com/XTLS/Xray-core/issues/4846#issuecomment-4150329444

https://github.com/XTLS/Xray-core/pull/5872#issuecomment-4184774915

https://github.com/XTLS/Xray-core/pull/5890#issuecomment-4240052251
2026-04-13 22:41:38 +00:00
MeowandGitHub d342361c89 Sniffing: Add ipsExcluded (supports IP, CIDR, "geoip:", "ext:") (#5929)
https://github.com/XTLS/Xray-core/pull/5927#issuecomment-4238197075

https://github.com/XTLS/Xray-core/pull/5929#issuecomment-4238550443
2026-04-13 18:08:51 +00:00
MeowandGitHub f17fabfff5 Sniffing: domainsExcluded supports "geosite:" (#5927)
https://github.com/XTLS/Xray-core/pull/5927#issuecomment-4238238050

https://github.com/XTLS/Xray-core/pull/5927#issuecomment-4238119874
2026-04-13 17:39:53 +00:00
MeowandGitHub 05a11910d4 DomainMatcher: Reduce runtime memory usage and startup peak memory on iOS (#5924)
https://github.com/XTLS/Xray-core/pull/5814#issuecomment-4231071433

Closes https://github.com/XTLS/Xray-core/issues/4422
2026-04-13 16:54:43 +00:00
MeowandGitHub 82624bcaf0 Xray-core: Refactor geodata (#5814)
https://github.com/XTLS/Xray-core/issues/4422#issuecomment-3533007890

Breaking changes https://github.com/XTLS/Xray-core/pull/5569

Reverts https://github.com/XTLS/Xray-core/pull/5505

Closes https://github.com/XTLS/Xray-core/pull/643
2026-04-13 16:42:29 +00:00
风扇滑翔翼andGitHub e9f7d61c2e Hysteria transport: Fix client-side clientManager (#5928)
Fixes https://github.com/XTLS/Xray-core/issues/5911
2026-04-13 16:14:15 +00:00
LjhAUMEMandGitHub 806b8dc27d TUN inbound: Add gateway, dns, autoSystemRoutingTable, autoOutboundsInterface for Windows (#5887)
And refactor `mtu` to support setting IPv4/v6 separately

Example: https://github.com/XTLS/Xray-core/pull/5887#issue-4198837696
2026-04-13 13:38:10 +00:00
f27edc3172 Routing: process supports UID on Android (#5915)
Example: https://github.com/XTLS/Xray-core/pull/5915#issuecomment-4232122895

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-04-13 13:17:53 +00:00
Lumière ÉlevéandGitHub c93478b891 Global HTTP headers' masquerading: Add "curl"; Improve version generators (#5916)
https://github.com/XTLS/Xray-core/pull/5802
https://github.com/XTLS/Xray-core/pull/5689
https://github.com/XTLS/Xray-core/pull/5658
2026-04-11 21:16:58 +00:00
1642fdfbdd XDNS finalmask: Support resolvers (client) and domains (server) instead of domain (#5872)
https://github.com/XTLS/Xray-core/pull/5872#issuecomment-4192730898

Example: https://github.com/XTLS/Xray-core/pull/5872#issuecomment-4196172391

---------

Co-authored-by: LjhAUMEM <llnu14702@gmail.com>
2026-04-11 19:37:32 +00:00
Yury KastovandGitHub a91a88c7b2 API & Commands: Add GetUsersStatsRequest(); Improve api statsonlineiplist (#5776)
https://github.com/XTLS/Xray-core/pull/5776#issuecomment-4230007504
2026-04-11 19:09:24 +00:00
32937846c5 Tunnel inbound: Compatible with listening UNIX domain socket (#5693)
https://github.com/XTLS/Xray-core/pull/5693#issuecomment-4229947428

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-04-11 18:39:32 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
93225a1132 Bump golang.org/x/net from 0.52.0 to 0.53.0 (#5899)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.52.0 to 0.53.0.
- [Commits](https://github.com/golang/net/compare/v0.52.0...v0.53.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.53.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>
2026-04-11 17:24:31 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4d958cbfd3 Bump actions/github-script from 8 to 9 (#5898)
Bumps [actions/github-script](https://github.com/actions/github-script) from 8 to 9.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v8...v9)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '9'
  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>
2026-04-11 17:24:14 +00:00
e5a9fb752e QUIC sniffer: Fix potential panic on malformed QUIC packets (#5866)
Fixes https://github.com/XTLS/Xray-core/security/advisories/GHSA-hrp5-2rwj-wvmv

---------

Co-authored-by: kastov <yk@sent.com>
2026-04-07 10:10:12 +00:00
RynnyaandFangliding 6a1a13b797 TUN inbound: Closable by AlwaysOnInboundHandler (#5860)
https://github.com/XTLS/Xray-core/pull/5860#issuecomment-4193477738
2026-04-07 16:32:06 +08:00
Alexey CherednichenkoandFangliding 6c4008edad Observatory: Clear removed outbounds (#5876)
* fix: prune stale observatory status

* More readable

Refactor observer to clear removed outbounds instead of updating status. Introduced slices package for improved outbound checking.

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-04-07 16:32:06 +08:00
LjhAUMEMandGitHub 3f608b3a58 Finalmask quicParams: Add bbrProfile ("conservative" / "standard" (default) / "aggressive") (#5869)
And Update github.com/apernet/quic-go to 20260330051153
2026-04-05 13:45:50 +00:00
LjhAUMEMandGitHub 4c3020ca6f TUN inbound: Fix UDP FullCone NAT (#5888)
Fixes https://github.com/XTLS/Xray-core/issues/5845
2026-04-05 12:59:22 +00:00
LjhAUMEMandGitHub ba88aa173c WireGuard outbound: Fix UDP FullCone NAT on Linux (#5858)
Fixes https://github.com/XTLS/Xray-core/issues/5848
2026-04-05 12:57:08 +00:00
hexbandandGitHub 08301e272c Hysteria inbound: Unwrap stats conn before extracting user (#5870)
Fixes https://github.com/XTLS/Xray-core/issues/5868
2026-04-03 21:26:25 +00:00
风扇滑翔翼andGitHub 6eccc59728 WireGuard: Use Xray's buffer (#5880)
Fixes https://github.com/XTLS/Xray-core/issues/5878
2026-04-03 21:22:45 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1e89a8fd98 Bump google.golang.org/grpc from 1.79.3 to 1.80.0 (#5885)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.79.3 to 1.80.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.79.3...v1.80.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.80.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>
2026-04-03 18:52:07 +00:00
RPRXandGitHub d2758a023c v26.3.27
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-03-27 17:45:40 +00:00
LjhAUMEMandGitHub 8aacdbd71b WireGuard inbound: Fix multi-peer; Fix potential routing issue (#5843)
Fixes https://github.com/XTLS/Xray-core/pull/5554

Fixes https://github.com/XTLS/Xray-core/issues/4760
2026-03-27 17:30:21 +00:00
LjhAUMEMandGitHub 14524cc3b7 Finalmask: Add randRange to "noise" (UDP), as the same as "header-custom"'s (TCP & UDP) (#5850)
https://github.com/XTLS/Xray-core/pull/5812
2026-03-27 17:20:43 +00:00
RPRXandGitHub cb7bfeb54c v26.3.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

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-03-23 10:21:21 +00:00
风扇滑翔翼andGitHub d62f5cfb62 Loopback outbound: Fix potential nil InboundFromContext (#5836)
Fixes https://github.com/XTLS/Xray-core/issues/5710
2026-03-23 10:11:17 +00:00
755f0a1d12 VLESS Reverse Proxy: Add "sniffing" to outbound's "reverse" (which is actually an inbound) (#5837)
Closes https://github.com/XTLS/Xray-core/issues/5662

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-03-23 09:49:32 +00:00
d8a8629a14 WireGuard outbound: Fix multi-peer's readQueue issue (#5554)
Fixes https://github.com/XTLS/Xray-core/issues/4507

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-03-23 08:08:28 +00:00
风扇滑翔翼andGitHub 982c95d89a OpenBSD: Disable readV (#5786)
https://github.com/XTLS/Xray-core/pull/5784#issuecomment-4024880917
https://github.com/XTLS/Xray-core/issues/5756#issuecomment-4015530258
https://github.com/XTLS/Xray-core/pull/5824#issuecomment-4103829456
2026-03-23 07:57:35 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ae3ddd1c06 Bump nick-fields/retry from 3 to 4 (#5838)
Bumps [nick-fields/retry](https://github.com/nick-fields/retry) from 3 to 4.
- [Release notes](https://github.com/nick-fields/retry/releases)
- [Commits](https://github.com/nick-fields/retry/compare/v3...v4)

---
updated-dependencies:
- dependency-name: nick-fields/retry
  dependency-version: '4'
  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>
2026-03-23 07:40:05 +00:00
HeXis-YSandGitHub f926ee4aa0 XTLS Vision: Defer Splice handoff until write completes (#5737)
Fixes https://github.com/XTLS/Xray-core/issues/4878
2026-03-22 17:48:33 +00:00
67a71adad1 WireGuard: Implement UDP FullCone NAT (#5833)
Fixes https://github.com/XTLS/Xray-core/issues/5601

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-03-22 17:42:40 +00:00
ClickDevTechandGitHub ce66db7032 README.md: Add CELERITY to Web Panel (#5834) 2026-03-22 17:14:16 +00:00
Boris KovalskiiandGitHub 7d93062f3d README.md: Add INCY to iOS & macOS Clients (#5832) 2026-03-22 17:10:29 +00:00
RPRX 2320416ca3 Update github.com/xtls/reality to 20260322125925
https://github.com/XTLS/REALITY/commit/9234c772ba8f181f31c3e81dc2b4177322e5a9a9
https://github.com/XTLS/REALITY/commit/ad4fbafc4bf56064c58f6c33ac2264264b76b5a5
https://github.com/XTLS/REALITY/commit/cd53f7d5023741301aabb5a7274ed0dd024fbd2c
2026-03-22 13:35:23 +00:00
RPRXandGitHub e0ab00f6a8 README.md: Add BlancVPN to Sponsors
Sponsor Xray-core: https://github.com/XTLS/Xray-core/issues/3668
2026-03-21 14:48:21 +00:00
RPRXandGitHub 157e65b34d REALITY config: Print Warning when user is choosing apple/icloud as the target or listening on non-443 ports
https://t.me/projectXtls/1754
https://github.com/XTLS/BBS/issues/21#issuecomment-4103308607
2026-03-21 13:19:32 +00:00
c1b67a961e XHTTP transport: Some optimizations (#5803)
https://github.com/XTLS/Xray-core/pull/5801
https://github.com/XTLS/Xray-core/pull/5808

---------

Co-authored-by: Sergei Ozeranskii <sergey.ozeranskiy@gmail.com>
Co-authored-by: rufsieus <rufsieus@gmail.com>
2026-03-21 12:48:47 +00:00
Lumière ÉlevéandGitHub 9e09399087 Xray-core: More robust browser header masquerading (chrome, firefox, edge) (#5802)
Fixes https://github.com/XTLS/Xray-core/issues/5800
2026-03-21 12:24:08 +00:00
bb05684407 VLESS Reverse Proxy: Check burstObservatory immediately after inbound adds new reverse-mux to reverse-outbound (#5752)
Fixes https://github.com/XTLS/Xray-core/issues/5750

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-03-21 11:16:24 +00:00
LjhAUMEMandGitHub 06dc4cf8bd Finalmask: Refactor header conns to avoid multiple-copy; Add randRange to "header-custom" (TCP & UDP) (#5812)
https://github.com/XTLS/Xray-core/pull/5657#issuecomment-4016760602
https://github.com/XTLS/Xray-core/pull/5657#issuecomment-4052921628
2026-03-21 09:04:22 +00:00
MatthewandRPRX 35800e953e Commands: x25519 outputs "Password" -> "Password (PublicKey)" (#5759)
https://github.com/XTLS/Xray-core/discussions/5084#discussioncomment-14312223
https://github.com/XTLS/Xray-core/discussions/5123#discussioncomment-14364120
...

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-03-19 15:57:27 +00:00
KatanaandRPRX 50fc324728 REALITY config: Fix client's shortId length check (#5738) 2026-03-19 15:50:23 +00:00
ec732b0b40 API: Fix potential nil pointer dereference in executeAddRules() (#5749)
Fixes https://github.com/XTLS/Xray-core/issues/5748

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-03-19 10:33:34 +00:00
风扇滑翔翼andGitHub 85f1234863 TUN inbound: Generate deterministic GUID on Windows (#5811)
Closes https://github.com/XTLS/Xray-core/issues/5810
2026-03-19 10:18:07 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
695a28c424 Bump google.golang.org/grpc from 1.79.2 to 1.79.3 (#5821)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.79.2 to 1.79.3.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.79.2...v1.79.3)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.79.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>
2026-03-18 09:18:01 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
9fd3d9a1eb Bump golang.org/x/net from 0.51.0 to 0.52.0 (#5793)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.51.0 to 0.52.0.
- [Commits](https://github.com/golang/net/compare/v0.51.0...v0.52.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  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>
2026-03-18 09:17:49 +00:00
风扇滑翔翼andGitHub e86c365572 TLS ECH: Avoid outer ALPN http/1.1 for WSS & HUS; Change echForceQuery's default value to "full"; Update github.com/refraction-networking/utls to 20260301010127; Add irrelevant tests for uTLS-REALITY (#5725)
https://github.com/XTLS/Xray-core/pull/5725#issuecomment-3982680111
2026-03-09 12:49:49 +00:00
LjhAUMEMandGitHub 0321cdd0d2 Hysteria & XHTTP/3: Unified Finalmask's quicParams to set congestion, brutalUp, brutalDown, udpHop (ports & interval), etc. (#5772)
https://github.com/XTLS/Xray-core/pull/5772#issuecomment-4023006179
2026-03-09 12:17:32 +00:00
LjhAUMEMandGitHub 766fa71eb1 Update github.com/apernet/quic-go to 20260217092621 (#5782) 2026-03-09 12:10:03 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
01951163fd Bump google.golang.org/grpc from 1.79.1 to 1.79.2 (#5777)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.79.1 to 1.79.2.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.79.1...v1.79.2)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.79.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>
2026-03-09 12:05:14 +00:00
saba-futaiandGitHub acb06e831b Finalmask: Add Sudoku (TCP & UDP) (#5685)
https://github.com/SUDOKU-ASCII/sudoku/issues/23#issuecomment-3859972396
2026-03-07 18:21:35 +00:00
LjhAUMEMandGitHub a204873d79 Finalmask: Add header-custom (TCP & UDP), fragment (TCP), noise (UDP); Support dialer-proxy, XHTTP/3; Fix XDNS, XICMP potential panic (#5657)
https://github.com/XTLS/Xray-core/pull/5657#issuecomment-4016609446
2026-03-07 15:42:18 +00:00
LjhAUMEMandGitHub ea87941b77 mKCP transport: Make sure ACKs are limited within MTU (#5773)
https://github.com/XTLS/Xray-core/pull/5657#issuecomment-3984236113
2026-03-07 15:21:25 +00:00
88a2589498 mKCP config: Check TTI 10~100 -> Check TTI 10~5000 (#5755)
https://github.com/XTLS/Xray-core/pull/5755#issuecomment-3992400360

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-03-07 14:11:56 +00:00
Жора ЗмейкинandGitHub 5138ffcf22 XHTTP transport: Add "bbr" (default) and "force-brutal" congestion control for H3 (#5711)
https://github.com/XTLS/Xray-core/pull/5711#issuecomment-3984037632
2026-03-07 12:46:40 +00:00
26X23andGitHub 0ac13bd910 XHTTP transport: Bugfixes for obfuscations (#5720)
https://github.com/XTLS/Xray-core/pull/5720#issuecomment-4016290343
2026-03-07 12:34:41 +00:00
Yury KastovandGitHub eec280262d API: Fix Online Map (#5732)
https://github.com/XTLS/Xray-core/pull/5732#pullrequestreview-3863990264
2026-03-07 10:56:11 +00:00
Yury KastovandGitHub 78fc2865ea Routing: Add webhook to rules (#5722)
https://github.com/XTLS/Xray-core/pull/5722#issuecomment-3953836108
2026-03-07 10:49:46 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ee8eb99bed Bump docker/build-push-action from 6 to 7 (#5765)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6 to 7.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6...v7)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  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>
2026-03-07 10:33:11 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
52e4abd2ba Bump docker/setup-buildx-action from 3 to 4 (#5764)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3 to 4.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: '4'
  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>
2026-03-07 10:32:58 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1dbac90b22 Bump docker/setup-qemu-action from 3 to 4 (#5761)
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3 to 4.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: '4'
  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>
2026-03-07 10:32:22 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
0b8ec6804f Bump docker/login-action from 3 to 4 (#5760)
Bumps [docker/login-action](https://github.com/docker/login-action) from 3 to 4.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: '4'
  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>
2026-03-07 10:31:09 +00:00
MinyandGitHub 9514e988d8 VLESS Encryption: Check 17~17000 -> Check 17~16640 (#5698)
https://github.com/XTLS/Xray-core/pull/5698#issuecomment-3938558695
2026-03-03 12:08:02 +00:00
Random GuyandGitHub 7dada1da2b VLESS config: Remove "with no flow" warning for now (#5671)
https://github.com/XTLS/Xray-core/pull/5671#issuecomment-3891166246
2026-03-03 11:10:19 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
0bffea3390 Bump actions/upload-artifact from 6 to 7 (#5733)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  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>
2026-03-01 18:45:58 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2805774f72 Bump golang.org/x/net from 0.50.0 to 0.51.0 (#5728)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.50.0 to 0.51.0.
- [Commits](https://github.com/golang/net/compare/v0.50.0...v0.51.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  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>
2026-03-01 18:45:45 +00:00
owoandGitHub e6207e3a97 README.md: Add XrayFA to Android Clients (#5715) 2026-02-22 07:05:07 +00:00
C O M P Ξ ZandGitHub f0f765f9eb README.md: Add GenyConnect to Windows & Linux & Android Clients (#5713) 2026-02-22 07:04:15 +00:00
efdf21efb5 README.md: Add NetProxy-Magisk to Magisk & Android Clients (#5708)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-02-21 09:59:07 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
07374ae5a5 Bump google.golang.org/grpc from 1.79.0 to 1.79.1 (#5695)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.79.0 to 1.79.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.79.0...v1.79.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.79.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>
2026-02-21 09:48:35 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b6a7609c87 Bump google.golang.org/grpc from 1.78.0 to 1.79.0 (#5686)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.78.0 to 1.79.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.78.0...v1.79.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.79.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>
2026-02-13 19:51:58 +00:00
b43276c6d3 gRPC client: Strip "grpc-go/version" suffix from User-Agent header (#5689)
Fixes https://github.com/XTLS/Xray-core/pull/5658#issuecomment-3894269376

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-02-13 19:49:47 +00:00
LjhAUMEMandGitHub 6a909b2507 Proxy: Add Hysteria 2 inbound & transport (supports listening port range, Salamander finalmask) (#5679)
https://github.com/XTLS/Xray-core/pull/5679#issuecomment-3888548778

Closes https://github.com/XTLS/Xray-core/issues/5605
2026-02-12 14:56:06 +00:00
风扇滑翔翼andGitHub 7abad3fac0 HTTPUpgrade server: Fix certain stuck in Handle() (#5661)
https://github.com/XTLS/Xray-core/pull/5661#issuecomment-3890662818
2026-02-12 14:18:38 +00:00
风扇滑翔翼andGitHub 1fe6d4a0f5 core/core.go: Replace "Custom" with vcs info if available (#5665)
https://github.com/XTLS/Xray-core/pull/5665#issuecomment-3890500863
2026-02-12 14:00:15 +00:00
风扇滑翔翼andGitHub d100be5ad5 Chore: Migrate to Go 1.26 (#5680) 2026-02-12 04:08:59 +00:00
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇andGitHub 7dc3f87cd8 Build: Remove Windows ARM 32-bit build (#4584) 2026-02-12 04:06:33 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
a079890ef0 Bump github.com/klauspost/cpuid/v2 from 2.0.12 to 2.3.0 (#5668)
Bumps [github.com/klauspost/cpuid/v2](https://github.com/klauspost/cpuid) from 2.0.12 to 2.3.0.
- [Release notes](https://github.com/klauspost/cpuid/releases)
- [Commits](https://github.com/klauspost/cpuid/compare/v2.0.12...v2.3.0)

---
updated-dependencies:
- dependency-name: github.com/klauspost/cpuid/v2
  dependency-version: 2.3.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>
2026-02-12 03:55:39 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
fb0fa80c8c Bump github.com/pires/go-proxyproto from 0.9.2 to 0.11.0 (#5678)
Bumps [github.com/pires/go-proxyproto](https://github.com/pires/go-proxyproto) from 0.9.2 to 0.11.0.
- [Release notes](https://github.com/pires/go-proxyproto/releases)
- [Commits](https://github.com/pires/go-proxyproto/compare/v0.9.2...v0.11.0)

---
updated-dependencies:
- dependency-name: github.com/pires/go-proxyproto
  dependency-version: 0.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-12 03:55:27 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e3e7b28c08 Bump golang.org/x/net from 0.49.0 to 0.50.0 (#5676)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.49.0 to 0.50.0.
- [Commits](https://github.com/golang/net/compare/v0.49.0...v0.50.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.50.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>
2026-02-12 03:55:12 +00:00
RPRXandGitHub 12ee51e4bb v26.2.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

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-02-06 09:42:41 +00:00
LjhAUMEMandGitHub 957e5a6b15 XICMP finalmask: Refine seq (#5652)
Example: https://github.com/XTLS/Xray-core/pull/5633#issue-3881559866
2026-02-06 08:44:50 +00:00
风扇滑翔翼andGitHub 0710c2b195 Workflows: Add simple consistency check for *.pb.go files to test.yml (#5646)
https://github.com/XTLS/Xray-core/commit/d14767d4f307dd42e6b41c4871480bcb85437b21
2026-02-06 08:37:22 +00:00
风扇滑翔翼andGitHub 4632984b66 TLS client: Simplify cert's verification code (#5656)
Fixes https://github.com/XTLS/Xray-core/issues/5655
2026-02-06 01:57:32 +00:00
b7a22c729b Xray-core: Dynamic Chrome User-Agent for all HTTP requests by default (overwriteable through config) (#5658)
https://github.com/XTLS/Xray-core/issues/4996#issuecomment-3855274627
https://github.com/XTLS/Xray-core/pull/5658#issuecomment-3857332687

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
Co-authored-by: Fangliding <63339210+Fangliding@users.noreply.github.com>
2026-02-06 01:42:31 +00:00
RPRXandGitHub 8c3f246dcb v26.2.4
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-02-04 00:35:09 +00:00
LjhAUMEMandGitHub 888c0d2e1f Finalmask UDP: Support WireGuard & Shadowsocks AEAD/2022 (#5643)
https://github.com/XTLS/Xray-core/pull/5633#issuecomment-3833910076
2026-02-04 00:29:45 +00:00
74c726ff62 Commands: Print CA cert's SHA256 in tls ping (#5644)
And https://github.com/XTLS/Xray-core/issues/5642#issuecomment-3840806246

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-02-03 21:03:48 +00:00
RPRXandGitHub d14767d4f3 Chore: Generate *.pb.go files with protoc v6.33.5
Download https://github.com/protocolbuffers/protobuf/releases/tag/v33.5
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.11
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.6.0
(Xray-core/) go run ./infra/vprotogen
2026-02-03 09:34:02 +00:00
RPRXandGitHub af2f0484b9 v26.2.2
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-02-02 09:35:31 +00:00
LjhAUMEMandRPRX 25d16914ff Finalmask: Add XICMP (relies mKCP/QUIC or WireGuard) (#5633)
https://t.me/projectXtls/1473
2026-02-02 09:12:42 +00:00
Dmitrii MakhnoandGitHub a6ec3b6e70 XHTTP transport: Fix "auto" mode with REALITY (#5638)
Fixes https://github.com/XTLS/Xray-core/issues/5635

BTW, fixes https://github.com/XTLS/Xray-core/issues/5631
2026-02-02 08:07:45 +00:00
RPRXandGitHub 20cf00c271 v26.1.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

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-01-31 14:02:33 +00:00
LjhAUMEMandGitHub c180c5980c Finalmask: Add XDNS (relies mKCP, like DNSTT), header-*, mkcp-* (#5560)
And https://github.com/XTLS/Xray-core/pull/5560#issuecomment-3825430761
2026-01-31 13:53:19 +00:00
5b849d51a9 XHTTP transport: New options for bypassing CDN's detection (#5414)
Usage: https://github.com/XTLS/Xray-core/pull/5414#issuecomment-3770071786

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

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2026-01-31 13:34:13 +00:00
Hossin AsaadiandRPRX 61e1153157 MPH domian matcher: Support building & using cache directly (instead of building from geosite.dat when Xray starts) (#5505)
Like https://github.com/XTLS/Xray-core/pull/5488#issuecomment-3710995080
2026-01-31 13:22:00 +00:00
afcfdbca70 Commands: Print leaf cert's SHA256 in tls ping (#5628)
And https://github.com/XTLS/Xray-core/pull/5628#issuecomment-3828445442

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-31 13:11:36 +00:00
RPRX 2c92339f95 TLS config: allowInsecure->pinnedPeerCertSha256; verifyPeerCertInNames->verifyPeerCertByName
And use `,` as the separator instead of `~`/array

https://github.com/XTLS/Xray-core/pull/5567#issuecomment-3766081805
https://t.me/projectXtls/1464
https://t.me/projectXtls/1465
https://t.me/projectXtls/1466
https://github.com/XTLS/Xray-core/pull/5625#issuecomment-3824855736
2026-01-31 09:32:51 +00:00
RPRXandGitHub 9c46a2d55a Upgrade gVisor to latest version v0.0.0-20260122175437-89a5d21be8f0
https://github.com/XTLS/Xray-core/issues/5561#issuecomment-3767618362
2026-01-30 21:39:13 +00:00
nasaboyandGitHub 19186edfa1 README.md: Add Egern & Quantumult X to Others (#5624) 2026-01-30 14:06:17 +00:00
风扇滑翔翼andGitHub f6a7e93923 VMess inbound: Optimize replay filter (#5562)
And https://github.com/XTLS/Xray-core/pull/5562#issuecomment-3765387903
2026-01-27 18:34:46 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
077070dbe0 Bump github.com/pires/go-proxyproto from 0.9.1 to 0.9.2 (#5614)
Bumps [github.com/pires/go-proxyproto](https://github.com/pires/go-proxyproto) from 0.9.1 to 0.9.2.
- [Release notes](https://github.com/pires/go-proxyproto/releases)
- [Commits](https://github.com/pires/go-proxyproto/compare/v0.9.1...v0.9.2)

---
updated-dependencies:
- dependency-name: github.com/pires/go-proxyproto
  dependency-version: 0.9.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>
2026-01-27 10:11:28 +00:00
Evozi TeamandGitHub c3af657c0e TUN inbound: Add iOS support (#5612)
And https://github.com/XTLS/Xray-core/pull/5612#issuecomment-3799070838
2026-01-26 12:43:10 +00:00
MeowandGitHub 9a04eecaf9 Geodat: Reduce peak memory usage (#5581)
Fixes https://github.com/XTLS/Xray-core/commit/5f7474120f523ad1e36174481e0b16c3446cc29c
2026-01-26 10:45:25 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1951a278ac Bump github.com/pires/go-proxyproto from 0.9.0 to 0.9.1 (#5608)
Bumps [github.com/pires/go-proxyproto](https://github.com/pires/go-proxyproto) from 0.9.0 to 0.9.1.
- [Release notes](https://github.com/pires/go-proxyproto/releases)
- [Commits](https://github.com/pires/go-proxyproto/compare/v0.9.0...v0.9.1)

---
updated-dependencies:
- dependency-name: github.com/pires/go-proxyproto
  dependency-version: 0.9.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>
2026-01-26 10:31:34 +00:00
LjhAUMEMandGitHub f1aee0b7c5 Hysteria transport: Support range & random for interval in udphop as well (#5603)
https://github.com/XTLS/Xray-core/pull/5560#issuecomment-3794621260
2026-01-25 18:28:51 +00:00
OwersunandGitHub 5173e5c15d TUN inbound: Enhance Darwin interface support (#5598)
* Proxy: TUN: Enhance Darwin interface support.
- reduce number of actions done to create/configure the interface in the system
- assign synthetic static link-local ipv4/ipv6 addresses to the interface, that are required by the OS for the routing to work
- make tun_darwin_endpoint be implemented significantly more similar to tun_windows_enpoint, preparing them for potential unification

* Proxy: TUN: Unify Darwin/Windows endpoint, which are now extremely similar, into one GVisorEndpoint.
Making darwin/windows tun implement GVisorDevice with simple readpacket/writepacket methods that GVisorEndpoint untilise
2026-01-25 18:19:05 +00:00
风扇滑翔翼andRPRX daf9cba29f XUDP client: Initialize Global ID's BaseKey correctly (#5602)
https://t.me/projectXray/4624679
2026-01-24 15:11:44 +00:00
kiandGitHub 445c0d456c TUN inbound: Disable RACK/TLP recovery to fix connection stalls (#5600)
https://github.com/XTLS/Xray-core/issues/5599#issuecomment-3794495254

Fixes https://github.com/XTLS/Xray-core/issues/5599
2026-01-24 12:39:07 +00:00
RPRXandGitHub 0a42dba13e v26.1.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

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-01-23 15:48:16 +00:00
cd8aab9544 common/errors/feature_errors.go: Add PrintNonRemovalDeprecatedFeatureWarning() (#5567)
And https://github.com/XTLS/Xray-core/pull/5567#issuecomment-3765466219

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-23 15:45:20 +00:00
MouMengandGitHub 59dc2cee2e API: Add ListRule() for routing (#5569)
https://github.com/XTLS/Xray-core/pull/5569#issuecomment-3766310407
2026-01-23 15:44:16 +00:00
风扇滑翔翼andGitHub 5846f94784 Log config: More flexible maskAddress (#5570)
https://github.com/XTLS/Xray-core/pull/5566#issuecomment-3765429984
2026-01-23 13:49:08 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e0ee2350fc Bump github.com/miekg/dns from 1.1.70 to 1.1.72 (#5590)
Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.70 to 1.1.72.
- [Commits](https://github.com/miekg/dns/compare/v1.1.70...v1.1.72)

---
updated-dependencies:
- dependency-name: github.com/miekg/dns
  dependency-version: 1.1.72
  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>
2026-01-23 13:11:41 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
48164c8267 Bump github.com/cloudflare/circl from 1.6.2 to 1.6.3 (#5589)
Bumps [github.com/cloudflare/circl](https://github.com/cloudflare/circl) from 1.6.2 to 1.6.3.
- [Release notes](https://github.com/cloudflare/circl/releases)
- [Commits](https://github.com/cloudflare/circl/compare/v1.6.2...v1.6.3)

---
updated-dependencies:
- dependency-name: github.com/cloudflare/circl
  dependency-version: 1.6.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>
2026-01-23 13:11:20 +00:00
LjhAUMEMandGitHub 2d2102f654 Hysteria transport: Fix speedtest issue (#5587)
Fixes https://github.com/XTLS/Xray-core/issues/5546
2026-01-22 13:37:27 +00:00
RPRXandGitHub 262770564f README.md: Add fancyss to Asuswrt-Merlin Clients 2026-01-22 03:08:46 +00:00
风扇滑翔翼andGitHub 777e31302c Router: Fix panic in ProcessNameMatcher when source IPs are empty (#5574)
Fixes https://github.com/XTLS/Xray-core/issues/5573
2026-01-21 13:24:51 +00:00
gamekiller0010andGitHub 30d6a5221b README.md: Update links for PassWall & PassWall 2 (#5572) 2026-01-21 13:16:27 +00:00
风扇滑翔翼andGitHub a778d3d273 Tests: Reduce RAM usage (#5577)
https://github.com/XTLS/Xray-core/pull/5577#issuecomment-3768963110
2026-01-21 13:02:04 +00:00
e813a3744f TUN inbound: Cancel ctx when handling is done (#5565)
https://github.com/XTLS/Xray-core/pull/5565#issuecomment-3777939907

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-21 12:58:30 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
66628943d8 Bump github.com/pires/go-proxyproto from 0.8.1 to 0.9.0 (#5582)
Bumps [github.com/pires/go-proxyproto](https://github.com/pires/go-proxyproto) from 0.8.1 to 0.9.0.
- [Release notes](https://github.com/pires/go-proxyproto/releases)
- [Commits](https://github.com/pires/go-proxyproto/compare/v0.8.1...v0.9.0)

---
updated-dependencies:
- dependency-name: github.com/pires/go-proxyproto
  dependency-version: 0.9.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>
2026-01-21 12:23:59 +00:00
RPRXandGitHub 7ff06f65ec v26.1.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

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-01-18 05:43:07 +00:00
RPRXandGitHub 1cf5662949 Create SECURITY.md
All for the FREE Internet!
2026-01-18 05:11:51 +00:00
LjhAUMEMandGitHub a6aca101d6 Hysteria outbound: Fix ContextWithRequireDatagram() (#5558)
Fixes https://github.com/XTLS/Xray-core/pull/5549
2026-01-18 04:25:36 +00:00
RPRXandGitHub 5836f36f69 Config: Add Warning for deprecated features (allowInsecure, Shadowsocks, VMess, Trojan, VLESS without flow)
Accelerate!
2026-01-18 04:17:25 +00:00
osypaiandGitHub 6d6c045a5a TUN inbound: Add macOS support (#5559) 2026-01-18 03:39:39 +00:00
LjhAUMEMandGitHub cfc78b3ac1 Hysteria transport: Add congestion config (""/"reno"/"bbr"/"brutal"/"force-brutal") (#5549)
Closes https://github.com/XTLS/Xray-core/issues/5546
2026-01-17 13:29:50 +00:00
风扇滑翔翼andRPRX 5f7474120f Geofiles: Revert related changes for now, waiting for better changes (#5557)
Reverts https://github.com/XTLS/Xray-core/commit/5d94a62a83073bc4aa378fb7e2198e15878e90da https://github.com/XTLS/Xray-core/commit/c715154309e41e759f544edd3667889e257fb3af https://github.com/XTLS/Xray-core/commit/961c352127f32c04034a88d021a683e6a80eac20 https://github.com/XTLS/Xray-core/commit/36425d2a6e53c755c96d25ef2323b7fffd1fab3f

Fixes https://github.com/XTLS/Xray-core/issues/5538 https://github.com/XTLS/Xray-core/issues/5536
2026-01-17 12:40:53 +00:00
风扇滑翔翼andGitHub 09f619d67c TLS client: Add pin_test.go for leaf and CA (#5553)
https://github.com/XTLS/Xray-core/pull/5532#issuecomment-3760231005
2026-01-17 09:42:06 +00:00
RPRXandGitHub 760223ad70 TLS client: Skip TLS' built-in verification when using pinnedPeerCertSha256; Fixes
https://github.com/XTLS/Xray-core/pull/5532#issuecomment-3745598515
https://github.com/XTLS/Xray-core/pull/5532#issuecomment-3759930283
https://github.com/XTLS/Xray-core/pull/5532#issuecomment-3760057266
https://github.com/XTLS/Xray-core/pull/5532#issuecomment-3760540231
2026-01-16 15:23:39 +00:00
nobodyandGitHub d75b33a3a3 Commands: "xray run -dump" supports reading JSON from STDIN (#5550)
Fixes https://github.com/XTLS/Xray-core/issues/5534
2026-01-16 10:55:43 +00:00
Happ-devandGitHub 7c418486c8 README.md: Add Happ RU to iOS & macOS Clients (#5551) 2026-01-16 10:44:44 +00:00
风扇滑翔翼andGitHub a384be0f84 SS2022 outbound: Fix UDP leak (#5544)
Fixes https://github.com/XTLS/Xray-core/issues/5541
2026-01-14 13:31:43 +00:00
LjhAUMEMandGitHub 649e989fa2 Hysteria: Fix transport's "udphop without salamander" dialing issue; Require "version": 2 in outbound's settings as well (#5537)
Updated example: https://github.com/XTLS/Xray-core/pull/5508#issue-3795798712
2026-01-14 10:42:07 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
0443de7798 Bump github.com/refraction-networking/utls from 1.8.1 to 1.8.2 (#5535)
Bumps [github.com/refraction-networking/utls](https://github.com/refraction-networking/utls) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/refraction-networking/utls/releases)
- [Commits](https://github.com/refraction-networking/utls/compare/v1.8.1...v1.8.2)

---
updated-dependencies:
- dependency-name: github.com/refraction-networking/utls
  dependency-version: 1.8.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>
2026-01-14 08:24:12 +00:00
RPRXandGitHub 9a121a489b v26.1.13
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2026-01-13 15:32:01 +00:00
风扇滑翔翼andGitHub 4bdf6e5c92 TLS client: Verify leaf cert (name, time) when pinning self-signed CA (#5532)
https://github.com/XTLS/Xray-core/pull/5154#issuecomment-3732159602
2026-01-13 14:35:24 +00:00
OwersunandRPRX ef2a967f12 TUN inbound: Close connection when handling is done (#5531)
https://github.com/XTLS/Xray-core/pull/5531#issuecomment-3744446015
2026-01-13 14:25:42 +00:00
LjhAUMEMandGitHub 92ada2dd1d Proxy: Add Hysteria outbound & transport (version 2, udphop) and Salamander udpmask (#5508)
https://github.com/XTLS/Xray-core/issues/3547#issuecomment-3549896520
https://github.com/XTLS/Xray-core/issues/2635#issuecomment-3570871754
2026-01-13 13:31:51 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8a9dbd407f Bump github.com/miekg/dns from 1.1.69 to 1.1.70 (#5528)
Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.69 to 1.1.70.
- [Commits](https://github.com/miekg/dns/compare/v1.1.69...v1.1.70)

---
updated-dependencies:
- dependency-name: github.com/miekg/dns
  dependency-version: 1.1.70
  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>
2026-01-13 09:55:42 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
de6be7c5a9 Bump golang.org/x/net from 0.48.0 to 0.49.0 (#5530)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.48.0 to 0.49.0.
- [Commits](https://github.com/golang/net/compare/v0.48.0...v0.49.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.49.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>
2026-01-13 09:49:58 +00:00
OwersunandGitHub e742e84ded Upgrade gVisor to latest version v0.0.0-20260109181451-4be7c433dae2 (#5527)
https://github.com/XTLS/Xray-core/pull/5526#issuecomment-3738638586
2026-01-12 18:18:02 +00:00
7726fbece0 TUN inbound: Make udp_fullcone pure side effect free udp connection (#5526)
https://github.com/XTLS/Xray-core/pull/5526#issue-3804306341

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-12 15:34:09 +00:00
0c09f4342b TUN inbound: Fix log, CanSpliceCopy, tag, sniffing, and port config issues (#5522)
Fixes https://github.com/XTLS/Xray-core/pull/5509#issuecomment-3732488294 & https://github.com/XTLS/Xray-core/pull/5509#issuecomment-3732740897

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-12 10:01:27 +00:00
14e171ac8e TUN inbound: Implement UDP FullCone NAT (#5509)
https://github.com/XTLS/Xray-core/pull/5509#issuecomment-3732898130

---------

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
Co-authored-by: Fangliding <45535409+Fangliding@users.noreply.github.com>
Co-authored-by: Owersun <4807375+Owersun@users.noreply.github.com>
2026-01-11 14:26:45 +00:00
风扇滑翔翼andGitHub 07a0dafa41 DNS: Check err for UDP dns.PackMessage(req.msg) (#5512)
Fixes https://github.com/XTLS/Xray-core/issues/5506
2026-01-09 14:22:07 +00:00
0ca13452b8 TLS config: Add pinnedPeerCertSha256; Remove pinnedPeerCertificateChainSha256 and pinnedPeerCertificatePublicKeySha256 (#5154)
Usage: https://github.com/XTLS/Xray-core/pull/5507

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-09 00:11:24 +00:00
Hossin AsaadiandGitHub 36425d2a6e Tests: Improve geosite & geoip tests (#5502)
https://github.com/XTLS/Xray-core/pull/5488#issuecomment-3711843548
2026-01-08 08:00:49 +00:00
39ba1f7952 Proxy: Add TUN inbound for Windows & Linux, including Android (#5464)
* Proxy: Implement tun raw network interface inbound support for Linux

* Proxy: Tun. Include "android" as build condition for build of tun_default implementation

* Proxy: Tun. Add .Close() cleanup calls to Handler.Init() where needed

* Proxy: Add Tun for Android

* Proxy: Tun. Implement Windows support

---------

Co-authored-by: yuhan6665 <1588741+yuhan6665@users.noreply.github.com>
2026-01-07 22:05:08 +00:00
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇andGitHub 394e117998 GitHub Actions: Add wintun.dll into Windows zips; Workflow refinement (#5501)
For https://github.com/XTLS/Xray-core/pull/5464
2026-01-07 12:59:23 +00:00
风扇滑翔翼andRPRX 446df149bd Routing config: Replace processName with process (full-name/abs-path/abs-folder) (#5496)
About `self/` & `xray/`: https://github.com/XTLS/Xray-core/pull/5496#issuecomment-3714620380

Replaces https://github.com/XTLS/Xray-core/pull/5489
2026-01-06 14:03:21 +00:00
风扇滑翔翼andGitHub d9025857fe transport/pipe/impl.go: Remove runtime.Gosched() in WriteMultiBuffer() (#5467) 2026-01-06 12:43:08 +00:00
Alireza AhmandandGitHub ced3e75bf3 README.md: Add TX-UI to Web Panels (#4981) 2026-01-06 12:24:32 +00:00
Hossin AsaadiandGitHub 961c352127 DNS: Fix parse domain and geoip (#5499)
Fixes https://github.com/XTLS/Xray-core/pull/5488#issuecomment-3712856715
2026-01-06 12:21:50 +00:00
Hossin AsaadiandGitHub c715154309 Routing: Reduce peak memory usage (#5488)
https://github.com/XTLS/Xray-core/pull/5488#issuecomment-3711430369

For https://github.com/XTLS/Xray-core/issues/4422
2026-01-05 23:02:40 +00:00
RPRXandGitHub b38a41249f README.md: Re-add 3X-UI to Web Panels
https://github.com/XTLS/Xray-core/pull/3884#issuecomment-3678495173

https://github.com/XTLS/Xray-core/issues/5478#issuecomment-3700567911
2026-01-05 08:42:35 +00:00
风扇滑翔翼andGitHub 7265b5ac3f Routing config: Add processName (#5489) 2026-01-05 01:12:13 +00:00
fanymagnetandRPRX e7c72c011f XHTTP server: Fix ScStreamUpServerSecs' non-default value (#5486) 2026-01-05 01:07:00 +00:00
yuhan6665andGitHub a54e1f2be4 Remove redundant stats in mux and bridge dispatcher (#5466)
Fixes https://github.com/XTLS/Xray-core/issues/5446
2025-12-31 11:00:45 +00:00
Hossin AsaadiandGitHub 5d94a62a83 Geofiles: Implement mmap in filesystem to reduce ram usage (#5480)
For https://github.com/XTLS/Xray-core/issues/4422
2025-12-31 08:50:30 +00:00
Maxim PlotnikovandGitHub ad468e462d API: Add GetAllOnlineUsers RPC to StatsService for retrieving online users (#5080) 2025-12-26 16:07:06 -05:00
ari-ahmandGitHub 6738ecf68e common/uuid: fix panic when parsing 32-len invalid UUID string. (#5468)
* common/uuid: fix panic when parsing 32-len invalid UUID string.

* fix: removed typo
2025-12-26 15:17:24 -05:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
36968909a1 Bump google.golang.org/grpc from 1.77.0 to 1.78.0 (#5469)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.77.0 to 1.78.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.77.0...v1.78.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.78.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-12-24 20:45:54 -05:00
MeowandGitHub 7f6ceb39f7 DomainMatcher: Prevent illegal domain rules from causing core startup failures (#5430)
Closes https://github.com/XTLS/Xray-core/issues/5429
2025-12-23 10:14:42 +00:00
风扇滑翔翼andGitHub fa64775f07 Tunnel/Dokodemo: Fix stats conn unwrap (#5440)
Fixes https://github.com/XTLS/Xray-core/issues/5439
2025-12-23 09:44:54 +00:00
a6792dda69 TLS ECH: Increase DOH timeout (#5455)
Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2025-12-23 09:41:01 +00:00
3572209cbd REALITY client: Clearer log when receiving real certificate (#5427)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-12-23 08:58:43 +00:00
dd757ca27c VLESS inbound: Print invalid UUID string (#5426)
Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2025-12-23 08:27:40 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
04b433dd97 Bump github.com/cloudflare/circl from 1.6.1 to 1.6.2 (#5465)
Bumps [github.com/cloudflare/circl](https://github.com/cloudflare/circl) from 1.6.1 to 1.6.2.
- [Release notes](https://github.com/cloudflare/circl/releases)
- [Commits](https://github.com/cloudflare/circl/compare/v1.6.1...v1.6.2)

---
updated-dependencies:
- dependency-name: github.com/cloudflare/circl
  dependency-version: 1.6.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-12-23 08:13:09 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
6bf0376773 Bump github.com/quic-go/quic-go from 0.57.1 to 0.58.0 (#5459)
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.57.1 to 0.58.0.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.57.1...v0.58.0)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-version: 0.58.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-12-21 22:28:33 -05:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
74df63add2 Bump actions/upload-artifact from 5 to 6 (#5425)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  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-12-14 22:01:02 -05:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c40326dfd7 Bump google.golang.org/protobuf from 1.36.10 to 1.36.11 (#5424)
Bumps google.golang.org/protobuf from 1.36.10 to 1.36.11.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.11
  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-12-14 22:00:53 -05:00
MeowandGitHub a3ba3eefb6 Wireguard: Decouple server endpoint DNS from address option (#5417)
* Wireguard: Decouple server endpoint DNS from address option

Previously, Wireguard server endpoint's domain resolution was incorrectly constrained by the local `address` option. For example, `ForceIPv6v4` might fail to resolve AAAA records for the server domain if no IPv6 was explicitly configured in the `address` option.

This commit decouples the server endpoint's domain resolution from the local `address` configuration. It ensures the Wireguard server address is resolved independently, allowing its `domainStrategy` to function correctly without being limited by the client's local network or `address` settings.

* Delete code instead of commenting it out
2025-12-14 10:13:47 -05:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
9cf22114a1 Bump github.com/miekg/dns from 1.1.68 to 1.1.69 (#5410)
Bumps [github.com/miekg/dns](https://github.com/miekg/dns) from 1.1.68 to 1.1.69.
- [Commits](https://github.com/miekg/dns/compare/v1.1.68...v1.1.69)

---
updated-dependencies:
- dependency-name: github.com/miekg/dns
  dependency-version: 1.1.69
  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-12-12 06:01:44 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
a903e80356 Bump actions/cache from 4 to 5 (#5411)
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  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-12-12 06:01:24 +00:00
风扇滑翔翼andGitHub a610a4c89a Chore: Remove all double gonet import (#5402) 2025-12-10 07:17:29 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b451f8929d Bump golang.org/x/net from 0.47.0 to 0.48.0 (#5397)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.47.0 to 0.48.0.
- [Commits](https://github.com/golang/net/compare/v0.47.0...v0.48.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.48.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-12-10 07:16:37 +00:00
RPRXandGitHub 81f8f398c7 v25.12.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

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-12-08 13:35:16 +00:00
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇andGitHub aea123842b Chore: Remove ctlcmd and leftover envvar (#5392)
https://github.com/v2fly/v2ray-core/issues/360
2025-12-08 13:27:22 +00:00
RPRXandGitHub bd7503d506 XTLS Vision: LogInfo() -> LogDebug()
https://t.me/projectXray/4543105
2025-12-08 13:19:59 +00:00
yuhan6665andGitHub 903214a0f0 XTLS Vision: Fix enabled uplink splice flag by mistake (#5391)
Fixes https://github.com/XTLS/Xray-core/issues/5379
2025-12-08 13:13:43 +00:00
RPRXandGitHub e403abe360 v25.12.2
Announcement of NFTs by Project X: https://github.com/XTLS/Xray-core/discussions/3633
Project X NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-12-02 13:53:08 +00:00
RPRX c123f163c2 XTLS Vision: Discard expired pre-connect conn automatically
https://t.me/projectXray/4538408

https://github.com/XTLS/Xray-core/pull/5270#issuecomment-3602122299
2025-12-02 13:44:27 +00:00
风扇滑翔翼andGitHub 93312d29e5 XTLS Vision: Fix IsCompleteRecord() (#5365)
Fixes https://github.com/XTLS/Xray-core/pull/5179
2025-12-02 13:01:44 +00:00
RPRX 36cb0f00bd v25.12.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

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

XHTTP: Beyond REALITY: https://github.com/XTLS/Xray-core/discussions/4113
REALITY NFT: https://opensea.io/assets/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2
2025-12-01 13:27:50 +00:00
RPRXand风扇滑翔翼 cadcb47074 XTLS Vision: Add testpre (outbound pre-connect) and testseed (outbound & inbound) (#5270)
https://t.me/projectXtls/1034

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2025-12-01 13:27:27 +00:00
yuhan6665andGitHub c6afcd5fb6 XTLS Vision: Check TLS record isComplete (#5179)
Fixes https://github.com/XTLS/Xray-core/discussions/5169#discussioncomment-14482684
2025-11-30 10:28:01 +00:00
MeowandGitHub ed5f7e7af5 fix(dns): inheritance issue with disableCache (#5351) 2025-11-27 02:12:37 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
9d3401b6f0 Bump github.com/quic-go/quic-go from 0.57.0 to 0.57.1 (#5352)
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.57.0 to 0.57.1.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.57.0...v0.57.1)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-version: 0.57.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-11-27 02:11:33 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d60ef656cc Bump github.com/quic-go/quic-go from 0.56.0 to 0.57.0 (#5335)
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.56.0 to 0.57.0.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.56.0...v0.57.0)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-version: 0.57.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-11-24 00:55:50 +00:00
RPRXandGitHub a83253f3d7 VLESS Reverse Proxy: Forbid reverse-proxy UUID using forward-proxy, enabled by default
https://t.me/projectXtls/1070

https://github.com/XTLS/Xray-core/pull/5101#issuecomment-3567464144
2025-11-23 04:23:48 +00:00
RPRXandGitHub 2969a189e6 Sockopt config: Add trustedXForwardedFor (for XHTTP, WS, HU inbounds) (#5331)
Fixes https://github.com/XTLS/Xray-core/pull/5101#issuecomment-3404979909
2025-11-23 01:09:49 +00:00
MeowandGitHub d41840132a Router: Remove the deprecated UseIP option (#5323) 2025-11-21 05:46:41 +00:00
MeowandGitHub f14fd1cbee feat(dns): add parallel query (#5239) 2025-11-21 05:45:42 +00:00
MeowandGitHub cd51f57535 feat(dns): add optimistic caching (#5237) 2025-11-21 05:38:06 +00:00
MeowandGitHub 4956e65824 perf(dns): cache network capability check (#5244) 2025-11-21 05:35:45 +00:00
MeowandGitHub 2185a730d2 perf(router): adjust the order of rules to optimize performance (#5267) 2025-11-21 05:30:16 +00:00
MeowandGitHub fcfb0a302a perf(GeoIPMatcher): faster heuristic matching with reduced memory usage (#5289) 2025-11-21 02:54:01 +00:00
MeowandGitHub b40bf56e4e refactor(dns): enhance cache safety, optimize performance, and refactor query logic (#5248) 2025-11-21 02:45:09 +00:00
27ad487545 DNS: Fix wrong protocol parse (#5232)
Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2025-11-21 02:31:34 +00:00
hax0r31337andGitHub e914183996 HTTP outbound: Read negotiated protocol from uTLS (#5251) 2025-11-21 02:29:25 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
acfda31e59 Bump actions/checkout from 5 to 6 (#5324)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [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/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  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-11-21 00:13:10 +00:00
风扇滑翔翼andGitHub f9dd3aef72 Refactor WrapLink logic (#5288)
https://github.com/XTLS/Xray-core/pull/5133
https://github.com/XTLS/Xray-core/pull/5286
2025-11-20 15:53:42 +00:00
MeowandGitHub b24ef88a80 Docker: Use more aggressive inlining for higher efficiency (#5242)
Syncs https://github.com/XTLS/Xray-core/pull/5026
2025-11-20 13:20:53 +00:00
b16a5f03fe Socks: Fix buffer full panic when encoding large UDP packets (#5252)
Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2025-11-20 12:21:22 +00:00
Aron YangandGitHub 2f1fabb318 README.md: Add v2rayN to macOS & Linux Clients (#5271) 2025-11-20 12:08:39 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1ec2966433 Bump golang.org/x/net (#5318)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.46.1-0.20251013234738-63d1a5100f82 to 0.47.0.
- [Commits](https://github.com/golang/net/commits/v0.47.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.47.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-11-20 00:11:02 +00:00
RPRXandGitHub 8a4b0a9eb0 README.md: Add TRX & TON & BTC & XMR & SOL to Donation & NFTs
https://t.me/projectXtls/1145
2025-11-19 12:39:32 +00:00
RPRX 4e8ee302a6 README.md: Add Remnawave & Happ to Sponsors
Sponsor Xray-core: https://github.com/XTLS/Xray-core/issues/3668
2025-11-19 12:36:25 +00:00
Exclude0122andRPRX 18a4104737 Fix wireguard not discarding broken connection on android (#5304)
Fixes https://github.com/XTLS/Xray-core/issues/5303
2025-11-19 04:53:54 +00:00
1a32d18c16 REALITY config: Return error when short id is too long (#5276)
Closes https://github.com/XTLS/Xray-core/issues/5273

---------

Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
2025-11-19 03:25:32 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
412bc17c12 Bump google.golang.org/grpc from 1.76.0 to 1.77.0 (#5308)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.76.0 to 1.77.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.76.0...v1.77.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.77.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-11-19 02:43:07 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
9491b67f3c Bump github.com/quic-go/quic-go from 0.55.0 to 0.56.0 (#5292)
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.55.0 to 0.56.0.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.55.0...v0.56.0)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-version: 0.56.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-11-14 20:51:29 -05:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
cb4f943f50 Bump actions/upload-artifact from 4 to 5 (#5259)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  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-11-01 11:07:19 +00:00
708 changed files with 61960 additions and 17983 deletions
+1 -2
View File
@@ -29,6 +29,5 @@
"openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" },
"windows-386": { "friendlyName": "windows-32" },
"windows-amd64": { "friendlyName": "windows-64" },
"windows-arm64": { "friendlyName": "windows-arm64-v8a" },
"windows-arm7": { "friendlyName": "windows-arm32-v7a" }
"windows-arm64": { "friendlyName": "windows-arm64-v8a" }
}
+1
View File
@@ -0,0 +1 @@
powershell.exe -ExecutionPolicy Bypass -File ".\xray_no_window.ps1"
+1
View File
@@ -0,0 +1 @@
Start-Process -FilePath ".\xray.exe" -ArgumentList "-config .\config.json" -WindowStyle Hidden
+1
View File
@@ -0,0 +1 @@
CreateObject("Wscript.Shell").Run "xray.exe -config config.json",0
+10 -6
View File
@@ -6,7 +6,7 @@ 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
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -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
@@ -32,9 +32,13 @@ 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
RUN echo '{}' >/tmp/usr/local/etc/xray/07_stats.json
RUN echo '{}' >/tmp/usr/local/etc/xray/08_fakedns.json
RUN echo '{}' >/tmp/usr/local/etc/xray/09_metrics.json
RUN echo '{}' >/tmp/usr/local/etc/xray/10_observatory.json
RUN echo '{}' >/tmp/usr/local/etc/xray/11_geodata.json
RUN echo '{}' >/tmp/usr/local/etc/xray/12_env.json
RUN echo '{}' >/tmp/usr/local/etc/xray/99_version.json
# Create log files
RUN mkdir -p /tmp/var/log/xray && touch \
@@ -45,8 +49,8 @@ RUN mkdir -p /tmp/var/log/xray && touch \
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=65532:65532 --chmod=755 /tmp/empty /usr/local/share/xray
COPY --from=build --chown=65532:65532 --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
+10 -6
View File
@@ -6,7 +6,7 @@ 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
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -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
@@ -32,9 +32,13 @@ 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
RUN echo '{}' >/tmp/usr/local/etc/xray/07_stats.json
RUN echo '{}' >/tmp/usr/local/etc/xray/08_fakedns.json
RUN echo '{}' >/tmp/usr/local/etc/xray/09_metrics.json
RUN echo '{}' >/tmp/usr/local/etc/xray/10_observatory.json
RUN echo '{}' >/tmp/usr/local/etc/xray/11_geodata.json
RUN echo '{}' >/tmp/usr/local/etc/xray/12_env.json
RUN echo '{}' >/tmp/usr/local/etc/xray/99_version.json
# Create log files
RUN mkdir -p /tmp/var/log/xray && touch \
@@ -54,8 +58,8 @@ RUN mkdir -p /tmp/var/log/xray && touch \
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=65532:65532 --chmod=755 /tmp/empty /usr/local/share/xray
COPY --from=build --chown=65532:65532 --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
+25 -6
View File
@@ -64,17 +64,25 @@ jobs:
echo "Latest: '$LATEST'."
echo "LATEST=$LATEST" >>${GITHUB_ENV}
NEWEST=false
if [[ "${{ github.event_name }}" == "release" ]]; then
NEWEST=true
fi
echo "Newest: '$NEWEST'."
echo "NEWEST=$NEWEST" >>${GITHUB_ENV}
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v7
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -82,7 +90,7 @@ jobs:
- name: Build Docker image (main architectures)
id: build_main_arches
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
context: .
file: .github/docker/Dockerfile
@@ -97,7 +105,7 @@ jobs:
- name: Build Docker image (additional architectures)
id: build_additional_arches
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
context: .
file: .github/docker/Dockerfile.usa
@@ -124,6 +132,13 @@ jobs:
${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}
fi
if [[ "${{ env.NEWEST }}" == "true" ]]; then
echo "Adding 'pre-release' tag to manifest: '${{ env.FULL_IMAGE_NAME }}:pre-release'."
docker buildx imagetools create \
--tag ${{ env.FULL_IMAGE_NAME }}:pre-release \
${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}
fi
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}
@@ -131,3 +146,7 @@ jobs:
if [[ "${{ env.LATEST }}" == "true" ]]; then
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:latest
fi
if [[ "${{ env.NEWEST }}" == "true" ]]; then
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:pre-release
fi
+54 -18
View File
@@ -11,13 +11,20 @@ on:
jobs:
check-assets:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
steps:
- name: Restore Geodat Cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v6
with:
path: resources
key: xray-geodat-
- name: Restore Wintun Cache
uses: actions/cache/restore@v6
with:
path: resources
key: xray-wintun-
- name: Check Assets Existence
id: check-assets
run: |
@@ -34,6 +41,18 @@ jobs:
break
fi
done
LIST=('amd64' 'x86')
for ARCHITECTURE in "${LIST[@]}"
do
echo -e "Checking wintun.dll for ${ARCHITECTURE}..."
if [ -s "./resources/wintun/bin/${ARCHITECTURE}/wintun.dll" ]; then
echo -e "wintun.dll for ${ARCHITECTURE} exists."
else
echo -e "wintun.dll for ${ARCHITECTURE} is missing."
echo "missing=true" >> $GITHUB_OUTPUT
break
fi
done
- name: Sleep for 90 seconds if Assets Missing
if: steps.check-assets.outputs.missing == 'true'
@@ -57,13 +76,14 @@ jobs:
fail-fast: false
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
env:
GOOS: ${{ matrix.goos}}
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: 0
steps:
- name: Checkout codebase
uses: actions/checkout@v5
uses: actions/checkout@v7
- name: Show workflow information
run: |
@@ -72,7 +92,7 @@ jobs:
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
- name: Set up Go
uses: actions/setup-go@v6
uses: actions/setup-go@v7
with:
go-version-file: go.mod
check-latest: true
@@ -95,20 +115,40 @@ jobs:
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
uses: actions/cache/restore@v6
with:
path: resources
key: xray-geodat-
- name: Restore Wintun Cache
uses: actions/cache/restore@v6
with:
path: resources
key: xray-wintun-
- name: Add additional assets into package
run: |
mv -f resources/geo* build_assets/
if [[ ${GOOS} == 'windows' ]]; then
cp .github/build/windows/* build_assets/
fi
if [[ ${GOOS} == 'windows' ]]; then
echo 'Adding Wintun into packages'
if [[ ${GOARCH} == 'amd64' ]]; then
mv resources/wintun/bin/amd64/wintun.dll build_assets/
fi
if [[ ${GOARCH} == '386' ]]; then
mv resources/wintun/bin/x86/wintun.dll build_assets/
fi
mv resources/wintun/LICENSE.txt build_assets/LICENSE-Wintun
fi
- 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
@@ -127,17 +167,6 @@ jobs:
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'
@@ -146,3 +175,10 @@ jobs:
file: ./Xray-${{ env.ASSET_NAME }}.zip*
tag: ${{ github.ref }}
file_glob: true
- name: Upload files to Artifacts
uses: actions/upload-artifact@v7
with:
name: Xray-${{ env.ASSET_NAME }}
path: |
./build_assets/*
+62 -22
View File
@@ -11,13 +11,20 @@ on:
jobs:
check-assets:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
steps:
- name: Restore Geodat Cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v6
with:
path: resources
key: xray-geodat-
- name: Restore Wintun Cache
uses: actions/cache/restore@v6
with:
path: resources
key: xray-wintun-
- name: Check Assets Existence
id: check-assets
run: |
@@ -34,10 +41,22 @@ jobs:
break
fi
done
LIST=('amd64' 'x86' 'arm64')
for ARCHITECTURE in "${LIST[@]}"
do
echo -e "Checking wintun.dll for ${ARCHITECTURE}..."
if [ -s "./resources/wintun/bin/${ARCHITECTURE}/wintun.dll" ]; then
echo -e "wintun.dll for ${ARCHITECTURE} exists."
else
echo -e "wintun.dll for ${ARCHITECTURE} is missing."
echo "missing=true" >> $GITHUB_OUTPUT
break
fi
done
- name: Trigger Asset Update Workflow if Assets Missing
if: steps.check-assets.outputs.missing == 'true'
uses: actions/github-script@v8
uses: actions/github-script@v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@@ -96,9 +115,6 @@ jobs:
# Windows ARM
- goos: windows
goarch: arm64
- goos: windows
goarch: arm
goarm: 7
# BEGIN Other architectures
# BEGIN riscv64 & ARM64 & LOONG64
- goos: linux
@@ -146,6 +162,7 @@ jobs:
fail-fast: false
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
@@ -153,7 +170,7 @@ jobs:
CGO_ENABLED: 0
steps:
- name: Checkout codebase
uses: actions/checkout@v5
uses: actions/checkout@v7
- name: Set up NDK
if: matrix.goos == 'android'
@@ -176,7 +193,7 @@ jobs:
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
- name: Set up Go
uses: actions/setup-go@v6
uses: actions/setup-go@v7
with:
go-version-file: go.mod
check-latest: true
@@ -191,10 +208,11 @@ jobs:
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
elif [[ ${GOOS} == 'android' ]]; then
echo 'Building Xray for Android...'
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= -checklinkname=0" -v ./main
else
echo 'Building Xray...'
if [[ ${GOARCH} == 'mips' || ${GOARCH} == 'mipsle' ]]; then
@@ -207,14 +225,40 @@ jobs:
fi
- name: Restore Geodat Cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v6
with:
path: resources
key: xray-geodat-
- name: Restore Wintun Cache
if: matrix.goos == 'windows'
uses: actions/cache/restore@v6
with:
path: resources
key: xray-wintun-
- name: Add additional assets into package
run: |
mv -f resources/geo* build_assets/
if [[ ${GOOS} == 'windows' ]]; then
cp .github/build/windows/* build_assets/
fi
if [[ ${GOOS} == 'windows' ]]; then
echo 'Adding Wintun into packages'
if [[ ${GOARCH} == 'amd64' ]]; then
mv resources/wintun/bin/amd64/wintun.dll build_assets/
fi
if [[ ${GOARCH} == '386' ]]; then
mv resources/wintun/bin/x86/wintun.dll build_assets/
fi
if [[ ${GOARCH} == 'arm64' ]]; then
mv resources/wintun/bin/arm64/wintun.dll build_assets/
fi
mv resources/wintun/LICENSE.txt build_assets/LICENSE-Wintun
fi
- 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
@@ -233,17 +277,6 @@ jobs:
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'
@@ -252,3 +285,10 @@ jobs:
file: ./Xray-${{ env.ASSET_NAME }}.zip*
tag: ${{ github.ref }}
file_glob: true
- name: Upload files to Artifacts
uses: actions/upload-artifact@v7
with:
name: Xray-${{ env.ASSET_NAME }}
path: |
./build_assets/*
+75 -4
View File
@@ -4,6 +4,7 @@ name: Scheduled assets update
# routine manner, for example: GeoIP/GeoSite.
# Currently updating:
# - Geodat (GeoIP/Geosite)
# - Wintun (wintun.dll)
on:
workflow_dispatch:
@@ -21,18 +22,18 @@ on:
jobs:
geodat:
if: github.event.schedule == '30 22 * * *' || github.event_name == 'push'|| github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'
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
uses: actions/cache/restore@v6
with:
path: resources
key: xray-geodat-
- name: Update Geodat
id: update
uses: nick-fields/retry@v3
uses: nick-fields/retry@v4
with:
timeout_minutes: 60
retry_wait_seconds: 60
@@ -58,8 +59,78 @@ jobs:
done
- name: Save Geodat Cache
uses: actions/cache/save@v4
uses: actions/cache/save@v6
if: ${{ steps.update.outputs.unhit }}
with:
path: resources
key: xray-geodat-${{ github.sha }}-${{ github.run_number }}
wintun:
if: github.event.schedule == '30 22 * * *' || github.event_name == 'push' || github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
env:
ASSETVER: 0.14.1
ASSETHASH: 07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51
steps:
- name: Restore Wintun Cache
uses: actions/cache/restore@v6
with:
path: resources
key: xray-wintun-
- name: Force downloading if run manually or on file update
if: github.event_name == 'workflow_dispatch' || github.event_name == 'push'
run: |
echo "FORCE_UPDATE=true" >> $GITHUB_ENV
- name: Update Wintun
id: update
uses: nick-fields/retry@v4
with:
timeout_minutes: 60
retry_wait_seconds: 60
max_attempts: 60
command: |
[ -d 'resources' ] || mkdir resources
LIST=('amd64' 'x86' 'arm64')
for ARCHITECTURE in "${LIST[@]}"
do
FILE_PATH="resources/wintun/bin/${ARCHITECTURE}/wintun.dll"
echo -e "Checking if wintun.dll for ${ARCHITECTURE} exists..."
if [ -s "./resources/wintun/bin/${ARCHITECTURE}/wintun.dll" ]; then
echo -e "wintun.dll for ${ARCHITECTURE} exists"
else
echo -e "wintun.dll for ${ARCHITECTURE} is missing"
missing=true
fi
done
if [ -s "./resources/wintun/LICENSE.txt" ]; then
echo -e "LICENSE for Wintun exists"
else
echo -e "LICENSE for Wintun is missing"
missing=true
fi
if [[ -v FORCE_UPDATE ]]; then
missing=true
fi
if [[ "$missing" == true ]]; then
FILENAME=wintun.zip
DOWNLOAD_FILE=wintun-${ASSETVER}.zip
echo -e "Downloading https://www.wintun.net/builds/${DOWNLOAD_FILE}..."
curl -L "https://www.wintun.net/builds/${DOWNLOAD_FILE}" -o "${FILENAME}"
if [[ "$(sha256sum "./${FILENAME}" | awk -F ' ' '{print $1}')" == "${ASSETHASH}" ]]; then
echo -e "Unpacking wintun..."
unzip -u ${FILENAME} -d resources/
echo "unhit=true" >> $GITHUB_OUTPUT
else
echo -e "Digest of ${FILENAME} mismatch."
exit 1
fi
fi
- name: Save Wintun Cache
uses: actions/cache/save@v6
if: ${{ steps.update.outputs.unhit }}
with:
path: resources
key: xray-wintun-${{ github.sha }}-${{ github.run_number }}
+43 -5
View File
@@ -1,4 +1,4 @@
name: Test
name: Tests and Checkings
on:
push:
@@ -8,9 +8,10 @@ on:
jobs:
check-assets:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
steps:
- name: Restore Geodat Cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v6
with:
path: resources
key: xray-geodat-
@@ -34,8 +35,45 @@ jobs:
if: steps.check-assets.outputs.missing == 'true'
run: sleep 90
check-proto:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
steps:
- name: Checkout codebase
uses: actions/checkout@v7
- name: Check Proto Version Header
run: |
head -n 4 core/config.pb.go > ref.txt
find . -name "*.pb.go" ! -name "*_grpc.pb.go" -print0 | while IFS= read -r -d '' file; do
if ! cmp -s ref.txt <(head -n 4 "$file"); then
echo "Error: Header mismatch in $file"
head -n 4 "$file"
exit 1
fi
done
check-format:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
permissions:
contents: read
steps:
- name: Checkout codebase
uses: actions/checkout@v7
- name: Set up Go
uses: actions/setup-go@v7
with:
go-version-file: go.mod
check-latest: true
cache: false
- name: Check Format
run: |
go install -v mvdan.cc/gofumpt@latest
go run ./infra/vformat/main.go -mode check -pwd ./
test:
needs: check-assets
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
permissions:
contents: read
runs-on: ${{ matrix.os }}
@@ -45,14 +83,14 @@ jobs:
os: [windows-latest, ubuntu-latest, macos-latest]
steps:
- name: Checkout codebase
uses: actions/checkout@v5
uses: actions/checkout@v7
- name: Set up Go
uses: actions/setup-go@v6
uses: actions/setup-go@v7
with:
go-version-file: go.mod
check-latest: true
- name: Restore Geodat Cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v6
with:
path: resources
key: xray-geodat-
+74 -8
View File
@@ -4,12 +4,27 @@
[README](https://github.com/XTLS/Xray-core#readme) is open, so feel free to submit your project [here](https://github.com/XTLS/Xray-core/pulls).
## Sponsors
[![Remnawave](https://github.com/user-attachments/assets/a22d34ae-01ee-441c-843a-85356748ed1e)](https://docs.rw)
[![Happ](https://github.com/user-attachments/assets/14055dab-e8bb-48bd-89e8-962709e4098e)](https://happ.su)
[![BlancVPN](https://github.com/user-attachments/assets/9145ea7d-5da3-446e-8143-710dba4292c3)](https://blanc.link/VMTSDqW)
[**Sponsor Xray-core**](https://github.com/XTLS/Xray-core/issues/3668)
## 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)
- **TRX(Tron)/USDT/USDC: `TNrDh5VSfwd4RPrwsohr6poyNTfFefNYan`**
- **TON: `UQApeV-u2gm43aC1uP76xAC1m6vCylstaN1gpfBmre_5IyTH`**
- **BTC: `1JpqcziZZuqv3QQJhZGNGBVdCBrGgkL6cT`**
- **XMR: `4ABHQZ3yJZkBnLoqiKvb3f8eqUnX4iMPb6wdant5ZLGQELctcerceSGEfJnoCk6nnyRZm73wrwSgvZ2WmjYLng6R7sR67nq`**
- **SOL/USDT/USDC: `3x5NuXHzB5APG6vRinPZcsUv5ukWUY1tBGRSJiEJWtZa`**
- **ETH/USDT/USDC: `0xDc3Fe44F0f25D13CACb1C4896CD0D321df3146Ee`**
- **Project X NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1**
- **VLESS NFT: https://opensea.io/collection/vless**
@@ -43,19 +58,22 @@
- [ghcr.io/xtls/xray-core](https://ghcr.io/xtls/xray-core) (**Official**)
- [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:**
- [X-Panel](https://github.com/xeefei/X-Panel)
- [PasarGuard](https://github.com/PasarGuard/panel)
- Web Panel
- [Remnawave](https://github.com/remnawave/panel)
- [Marzban](https://github.com/Gozargah/Marzban)
- [3X-UI](https://github.com/MHSanaei/3x-ui)
- [PasarGuard](https://github.com/PasarGuard/panel)
- [Xray-UI](https://github.com/qist/xray-ui)
- [X-Panel](https://github.com/xeefei/X-Panel)
- [Marzban](https://github.com/Gozargah/Marzban)
- [Hiddify](https://github.com/hiddify/Hiddify-Manager)
- [TX-UI](https://github.com/AghayeCoder/tx-ui)
- [CELERITY](https://github.com/ClickDevTech/CELERITY-panel)
- 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), [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)
- [Magic_V2Ray](https://github.com/vincentng295/Magic_V2Ray)
- [Xray_For_Magisk](https://github.com/E7KMbb/Xray_For_Magisk)
- Homebrew
- `brew install xray`
@@ -80,46 +98,65 @@
## GUI Clients
- OpenWrt
- [PassWall](https://github.com/xiaorouji/openwrt-passwall), [PassWall 2](https://github.com/xiaorouji/openwrt-passwall2)
- [PassWall](https://github.com/Openwrt-Passwall/openwrt-passwall), [PassWall 2](https://github.com/Openwrt-Passwall/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)
- [fancyss](https://github.com/hq450/fancyss)
- 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)
- [GenyConnect](https://github.com/genyleap/GenyConnect)
- [OneXray](https://github.com/OneXray/OneXray)
- [XrayUI-dev](https://github.com/PhoenixNil/XrayUI-dev)
- Android
- [v2rayNG](https://github.com/2dust/v2rayNG)
- [X-flutter](https://github.com/XTLS/X-flutter)
- [SaeedDev94/Xray](https://github.com/SaeedDev94/Xray)
- [SimpleXray](https://github.com/lhear/SimpleXray)
- [XrayFA](https://github.com/Q7DF1/XrayFA)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- [OneXray](https://github.com/OneXray/OneXray)
- [AsteriskNG](https://github.com/Asterisk4Magisk/AsteriskNG)
- 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))
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) | [Happ RU](https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973) | [Happ 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)
- [INCY](https://apps.apple.com/en/app/incy/id6756943388)
- macOS arm64 & x64
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215)
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) | [Happ RU](https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973)
- [V2rayU](https://github.com/yanue/V2rayU)
- [V2RayXS](https://github.com/tzmax/V2RayXS)
- [Furious](https://github.com/LorenEteval/Furious)
- [OneXray](https://github.com/OneXray/OneXray)
- [GoXRay](https://github.com/goxray/desktop)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- [v2rayN](https://github.com/2dust/v2rayN)
- [GenyConnect](https://github.com/genyleap/GenyConnect)
- [INCY](https://apps.apple.com/en/app/incy/id6756943388)
- 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)
- [v2rayN](https://github.com/2dust/v2rayN)
- [GenyConnect](https://github.com/genyleap/GenyConnect)
- [OneXray](https://github.com/OneXray/OneXray)
- HarmonyOS
- [Hey](https://github.com/popsiclelmlm/Hey)
## Others that support VLESS, XTLS, REALITY, XUDP, PLUX...
- iOS & macOS arm64 & tvOS
- [Anywhere](https://github.com/NodePassProject/Anywhere)
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
- [Loon](https://apps.apple.com/us/app/loon/id1373567447)
- [Egern](https://apps.apple.com/us/app/egern/id1616105820)
- [Quantumult X](https://apps.apple.com/us/app/quantumult-x/id1443988620)
- Xray Tools
- [xray-knife](https://github.com/lilendian0x00/xray-knife)
- [xray-checker](https://github.com/kutovoys/xray-checker)
@@ -128,6 +165,7 @@
- [xtls-sdk](https://github.com/remnawave/xtls-sdk)
- [xtlsapi](https://github.com/hiddify/xtlsapi)
- [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite)
- [flutter_vless](https://github.com/XIIIFOX/flutter_vless)
- [Xray-core-python](https://github.com/LorenEteval/Xray-core-python)
- [xray-api](https://github.com/XVGuardian/xray-api)
- [XrayR](https://github.com/XrayR-project/XrayR)
@@ -149,6 +187,27 @@
- [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).
### Bundled Third-Party Components Redistribution
**Certain optional features dynamically load third-party components. These optional components are separate works distributed under their own licenses, and are bundled into the ZIP package for ease of use. Users may replace these components under the licenses from these components.**
These components include:
#### Wintun
This distribution contains unmodified official precompiled and pre-signed Wintun binaries.
- Project: Wintun
- Copyright: Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
- Redistribution License: Prebuilt Binaries License (PBL) bundled with official precompiled and pre-signed binaries from wintun.net
- Component(s): wintun.dll
- Source: https://www.wintun.net/
- Included in:
- Windows x86 (windows-32, win7-32)
- Windows x86-64 (windows-64, win7-64)
- Windows AArch64 (windows-arm64)
- Notes: Wintun is an optional runtime-loaded component only used for TUN inbound functionality on supported Windows platforms.
## One-line Compilation
### Windows (PowerShell)
@@ -172,6 +231,13 @@ Make sure that you are using the same Go version, and remember to set the git co
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
```
For Android:
```bash
GOOS=android GOARCH=arm64 CGO_ENABLED=1 CC=/path/to/aarch64-linux-android24-clang go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid= -checklinkname=0" -v ./main
GOOS=android GOARCH=amd64 CGO_ENABLED=1 CC=/path/to/x86_64-linux-android24-clang go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid= -checklinkname=0" -v ./main
```
If you are compiling a 32-bit MIPS/MIPSLE target, use this command instead:
```bash
+5
View File
@@ -0,0 +1,5 @@
# Security Policy
If you found an issue related to security vulnerability or protocol-identification problem, please report it to us via "[Report a vulnerability](https://github.com/XTLS/Xray-core/security/advisories/new)" privately, instead of publish it publicly before we release the fixed version.
Thanks for your contribution to the FREE Internet!
+19 -5
View File
@@ -3,6 +3,7 @@ package commander
import (
"context"
"net"
"strings"
"sync"
"github.com/xtls/xray-core/common"
@@ -10,6 +11,7 @@ import (
"github.com/xtls/xray-core/common/signal/done"
core "github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/transport/internet"
"google.golang.org/grpc"
)
@@ -67,20 +69,32 @@ func (c *Commander) Start() error {
}
c.Unlock()
var listen = func(listener net.Listener) {
listen := func(listener net.Listener) {
if err := c.server.Serve(listener); err != nil {
errors.LogErrorInner(context.Background(), err, "failed to start grpc server")
}
}
if len(c.listen) > 0 {
if l, err := net.Listen("tcp", c.listen); err != nil {
var addr net.Addr
if strings.HasPrefix(c.listen, "/") || strings.HasPrefix(c.listen, "@") {
addr = &net.UnixAddr{Name: c.listen, Net: "unix"}
} else {
tcpAddr, err := net.ResolveTCPAddr("tcp", c.listen)
if err != nil {
errors.LogErrorInner(context.Background(), err, "API server failed to parse listen address ", c.listen)
return err
}
addr = tcpAddr
}
l, err := internet.ListenSystem(context.Background(), addr, nil)
if err != nil {
errors.LogErrorInner(context.Background(), err, "API server failed to listen on ", c.listen)
return err
} else {
errors.LogInfo(context.Background(), "API server listening on ", l.Addr())
go listen(l)
}
errors.LogInfo(context.Background(), "API server listening on ", l.Addr())
go listen(l)
return nil
}
+21 -35
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/commander/config.proto
package commander
@@ -12,6 +12,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -23,17 +24,16 @@ const (
// Config is the settings for Commander.
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState `protogen:"open.v1"`
// Tag of the outbound handler that handles grpc connections.
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
// Network address of commander grpc service.
Listen string `protobuf:"bytes,3,opt,name=listen,proto3" json:"listen,omitempty"`
// Services that supported by this server. All services must implement Service
// interface.
Service []*serial.TypedMessage `protobuf:"bytes,2,rep,name=service,proto3" json:"service,omitempty"`
Service []*serial.TypedMessage `protobuf:"bytes,2,rep,name=service,proto3" json:"service,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
@@ -89,9 +89,9 @@ func (x *Config) GetService() []*serial.TypedMessage {
// ReflectionConfig is the placeholder config for ReflectionService.
type ReflectionConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ReflectionConfig) Reset() {
@@ -126,37 +126,24 @@ func (*ReflectionConfig) Descriptor() ([]byte, []int) {
var File_app_commander_config_proto protoreflect.FileDescriptor
var file_app_commander_config_proto_rawDesc = []byte{
0x0a, 0x1a, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x2f,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72,
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, 0x6e, 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, 0x12,
0x16, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x12, 0x3a, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 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, 0x07, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65,
0x72, 0x50, 0x01, 0x5a, 0x27, 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, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0xaa, 0x02, 0x12, 0x58,
0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65,
0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
const file_app_commander_config_proto_rawDesc = "" +
"\n" +
"\x1aapp/commander/config.proto\x12\x12xray.app.commander\x1a!common/serial/typed_message.proto\"n\n" +
"\x06Config\x12\x10\n" +
"\x03tag\x18\x01 \x01(\tR\x03tag\x12\x16\n" +
"\x06listen\x18\x03 \x01(\tR\x06listen\x12:\n" +
"\aservice\x18\x02 \x03(\v2 .xray.common.serial.TypedMessageR\aservice\"\x12\n" +
"\x10ReflectionConfigBX\n" +
"\x16com.xray.app.commanderP\x01Z'github.com/xtls/xray-core/app/commander\xaa\x02\x12Xray.App.Commanderb\x06proto3"
var (
file_app_commander_config_proto_rawDescOnce sync.Once
file_app_commander_config_proto_rawDescData = file_app_commander_config_proto_rawDesc
file_app_commander_config_proto_rawDescData []byte
)
func file_app_commander_config_proto_rawDescGZIP() []byte {
file_app_commander_config_proto_rawDescOnce.Do(func() {
file_app_commander_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_commander_config_proto_rawDescData)
file_app_commander_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_commander_config_proto_rawDesc), len(file_app_commander_config_proto_rawDesc)))
})
return file_app_commander_config_proto_rawDescData
}
@@ -185,7 +172,7 @@ func file_app_commander_config_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_commander_config_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_commander_config_proto_rawDesc), len(file_app_commander_config_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
@@ -196,7 +183,6 @@ func file_app_commander_config_proto_init() {
MessageInfos: file_app_commander_config_proto_msgTypes,
}.Build()
File_app_commander_config_proto = out.File
file_app_commander_config_proto_rawDesc = nil
file_app_commander_config_proto_goTypes = nil
file_app_commander_config_proto_depIdxs = nil
}
+18 -30
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/dispatcher/config.proto
package dispatcher
@@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -21,9 +22,9 @@ const (
)
type SessionConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SessionConfig) Reset() {
@@ -57,11 +58,10 @@ func (*SessionConfig) Descriptor() ([]byte, []int) {
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Settings *SessionConfig `protobuf:"bytes,1,opt,name=settings,proto3" json:"settings,omitempty"`
unknownFields protoimpl.UnknownFields
Settings *SessionConfig `protobuf:"bytes,1,opt,name=settings,proto3" json:"settings,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
@@ -103,33 +103,22 @@ func (x *Config) GetSettings() *SessionConfig {
var File_app_dispatcher_config_proto protoreflect.FileDescriptor
var file_app_dispatcher_config_proto_rawDesc = []byte{
0x0a, 0x1b, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72,
0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68,
0x65, 0x72, 0x22, 0x15, 0x0a, 0x0d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x48, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69,
0x6e, 0x67, 0x73, 0x42, 0x5b, 0x0a, 0x17, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x50, 0x01,
0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c,
0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f,
0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0xaa, 0x02, 0x13, 0x58, 0x72, 0x61,
0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
const file_app_dispatcher_config_proto_rawDesc = "" +
"\n" +
"\x1bapp/dispatcher/config.proto\x12\x13xray.app.dispatcher\"\x15\n" +
"\rSessionConfigJ\x04\b\x01\x10\x02\"H\n" +
"\x06Config\x12>\n" +
"\bsettings\x18\x01 \x01(\v2\".xray.app.dispatcher.SessionConfigR\bsettingsB[\n" +
"\x17com.xray.app.dispatcherP\x01Z(github.com/xtls/xray-core/app/dispatcher\xaa\x02\x13Xray.App.Dispatcherb\x06proto3"
var (
file_app_dispatcher_config_proto_rawDescOnce sync.Once
file_app_dispatcher_config_proto_rawDescData = file_app_dispatcher_config_proto_rawDesc
file_app_dispatcher_config_proto_rawDescData []byte
)
func file_app_dispatcher_config_proto_rawDescGZIP() []byte {
file_app_dispatcher_config_proto_rawDescOnce.Do(func() {
file_app_dispatcher_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_dispatcher_config_proto_rawDescData)
file_app_dispatcher_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_dispatcher_config_proto_rawDesc), len(file_app_dispatcher_config_proto_rawDesc)))
})
return file_app_dispatcher_config_proto_rawDescData
}
@@ -157,7 +146,7 @@ func file_app_dispatcher_config_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_dispatcher_config_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_dispatcher_config_proto_rawDesc), len(file_app_dispatcher_config_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
@@ -168,7 +157,6 @@ func file_app_dispatcher_config_proto_init() {
MessageInfos: file_app_dispatcher_config_proto_msgTypes,
}.Build()
File_app_dispatcher_config_proto = out.File
file_app_dispatcher_config_proto_rawDesc = nil
file_app_dispatcher_config_proto_goTypes = nil
file_app_dispatcher_config_proto_depIdxs = nil
}
+23 -41
View File
@@ -2,7 +2,6 @@ package dispatcher
import (
"context"
"regexp"
"strings"
"sync"
"time"
@@ -163,7 +162,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
p := d.policy.ForLevel(user.Level)
if p.Stats.UserUplink {
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
if c, _ := d.stats.GetOrRegisterCounter(name); c != nil {
inboundLink.Writer = &SizeStatWriter{
Counter: c,
Writer: inboundLink.Writer,
@@ -172,7 +171,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
}
if p.Stats.UserDownlink {
name := "user>>>" + user.Email + ">>>traffic>>>downlink"
if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
if c, _ := d.stats.GetOrRegisterCounter(name); c != nil {
outboundLink.Writer = &SizeStatWriter{
Counter: c,
Writer: outboundLink.Writer,
@@ -181,22 +180,14 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
}
if p.Stats.UserOnline {
name := "user>>>" + user.Email + ">>>online"
if om, _ := stats.GetOrRegisterOnlineMap(d.stats, name); om != nil {
sessionInbounds := session.InboundFromContext(ctx)
userIP := sessionInbounds.Source.Address.String()
om.AddIP(userIP)
// log Online user with ips
// errors.LogDebug(ctx, "user>>>" + user.Email + ">>>online", om.Count(), om.List())
}
trackOnlineIP(ctx, d.stats, user.Email, sessionInbound.Source.Address.String())
}
}
return inboundLink, outboundLink
}
func (d *DefaultDispatcher) WrapLink(ctx context.Context, link *transport.Link) *transport.Link {
func WrapLink(ctx context.Context, policyManager policy.Manager, statsManager stats.Manager, link *transport.Link) *transport.Link {
sessionInbound := session.InboundFromContext(ctx)
var user *protocol.MemoryUser
if sessionInbound != nil {
@@ -206,16 +197,16 @@ func (d *DefaultDispatcher) WrapLink(ctx context.Context, link *transport.Link)
link.Reader = &buf.TimeoutWrapperReader{Reader: link.Reader}
if user != nil && len(user.Email) > 0 {
p := d.policy.ForLevel(user.Level)
p := policyManager.ForLevel(user.Level)
if p.Stats.UserUplink {
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
if c, _ := statsManager.GetOrRegisterCounter(name); c != nil {
link.Reader.(*buf.TimeoutWrapperReader).Counter = c
}
}
if p.Stats.UserDownlink {
name := "user>>>" + user.Email + ">>>traffic>>>downlink"
if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
if c, _ := statsManager.GetOrRegisterCounter(name); c != nil {
link.Writer = &SizeStatWriter{
Counter: c,
Writer: link.Writer,
@@ -223,41 +214,31 @@ func (d *DefaultDispatcher) WrapLink(ctx context.Context, link *transport.Link)
}
}
if p.Stats.UserOnline {
name := "user>>>" + user.Email + ">>>online"
if om, _ := stats.GetOrRegisterOnlineMap(d.stats, name); om != nil {
sessionInbounds := session.InboundFromContext(ctx)
userIP := sessionInbounds.Source.Address.String()
om.AddIP(userIP)
// log Online user with ips
// errors.LogDebug(ctx, "user>>>" + user.Email + ">>>online", om.Count(), om.List())
}
trackOnlineIP(ctx, statsManager, user.Email, sessionInbound.Source.Address.String())
}
}
return link
}
func trackOnlineIP(ctx context.Context, sm stats.Manager, email, ip string) {
name := "user>>>" + email + ">>>online"
if om, _ := sm.GetOrRegisterOnlineMap(name); om != nil {
om.AddIP(ip)
context.AfterFunc(ctx, func() { om.RemoveIP(ip) })
}
}
func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool {
domain := result.Domain()
if domain == "" {
return false
}
for _, d := range request.ExcludeForDomain {
if strings.HasPrefix(d, "regexp:") {
pattern := d[7:]
re, err := regexp.Compile(pattern)
if err != nil {
errors.LogInfo(ctx, "Unable to compile regex")
continue
}
if re.MatchString(domain) {
return false
}
} else {
if strings.ToLower(domain) == d {
return false
}
}
if request.ExcludeForDomain != nil && request.ExcludeForDomain.MatchAny(strings.ToLower(domain)) {
return false
}
if request.ExcludeForIP != nil && destination.Address.Family().IsIP() && request.ExcludeForIP.Match(destination.Address.IP()) {
return false
}
protocolString := result.Protocol()
if resComp, ok := result.(SnifferResultComposite); ok {
@@ -357,7 +338,7 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
content = new(session.Content)
ctx = session.ContextWithContent(ctx, content)
}
outbound = d.WrapLink(ctx, outbound)
outbound = WrapLink(ctx, d.policy, d.stats, outbound)
sniffingRequest := content.SniffingRequest
if !sniffingRequest.Enabled {
d.routedDispatch(ctx, outbound, destination)
@@ -449,6 +430,7 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
}
return contentResult, contentErr
}
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
outbounds := session.OutboundsFromContext(ctx)
ob := outbounds[len(outbounds)-1]
+260 -108
View File
@@ -3,36 +3,55 @@ package dns
import (
"context"
go_errors "errors"
"runtime"
"sync"
"time"
"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"
"golang.org/x/sync/singleflight"
)
const (
minSizeForEmptyRebuild = 512
shrinkAbsoluteThreshold = 10240
shrinkRatioThreshold = 0.65
migrationBatchSize = 4096
)
type CacheController struct {
name string
disableCache bool
serveStale bool
serveExpiredTTL int32
ips map[string]*record
dirtyips map[string]*record
sync.RWMutex
ips map[string]*record
pub *pubsub.Service
cacheCleanup *task.Periodic
name string
disableCache bool
pub *pubsub.Service
cacheCleanup *task.Periodic
highWatermark int
requestGroup singleflight.Group
}
func NewCacheController(name string, disableCache bool) *CacheController {
func NewCacheController(name string, disableCache bool, serveStale bool, serveExpiredTTL uint32) *CacheController {
c := &CacheController{
name: name,
disableCache: disableCache,
ips: make(map[string]*record),
pub: pubsub.NewService(),
name: name,
disableCache: disableCache,
serveStale: serveStale,
serveExpiredTTL: -int32(serveExpiredTTL),
ips: make(map[string]*record),
pub: pubsub.NewService(),
}
c.cacheCleanup = &task.Periodic{
Interval: time.Minute,
Interval: 300 * time.Second,
Execute: c.CacheCleanup,
}
return c
@@ -40,131 +59,264 @@ func NewCacheController(name string, disableCache bool) *CacheController {
// 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...")
expiredKeys, err := c.collectExpiredKeys()
if err != nil {
return err
}
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(expiredKeys) == 0 {
return nil
}
if len(c.ips) == 0 {
c.ips = make(map[string]*record)
}
c.writeAndShrink(expiredKeys)
return nil
}
func (c *CacheController) updateIP(req *dnsRequest, ipRec *IPRecord) {
elapsed := time.Since(req.start)
func (c *CacheController) collectExpiredKeys() ([]string, error) {
c.RLock()
defer c.RUnlock()
c.Lock()
rec, found := c.ips[req.domain]
if !found {
rec = &record{}
if len(c.ips) == 0 {
return nil, errors.New("nothing to do. stopping...")
}
switch req.reqType {
case dnsmessage.TypeA:
rec.A = ipRec
case dnsmessage.TypeAAAA:
rec.AAAA = ipRec
// skip collection if a migration is in progress
if c.dirtyips != nil {
return nil, nil
}
errors.LogInfo(context.Background(), c.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed)
c.ips[req.domain] = rec
now := time.Now()
if c.serveStale && c.serveExpiredTTL != 0 {
now = now.Add(time.Duration(c.serveExpiredTTL) * time.Second)
}
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)
}
expiredKeys := make([]string, 0, len(c.ips)/4) // pre-allocate
for domain, rec := range c.ips {
if (rec.A != nil && rec.A.Expire.Before(now)) ||
(rec.AAAA != nil && rec.AAAA.Expire.Before(now)) {
expiredKeys = append(expiredKeys, domain)
}
}
c.Unlock()
common.Must(c.cacheCleanup.Start())
return expiredKeys, nil
}
func (c *CacheController) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
func (c *CacheController) writeAndShrink(expiredKeys []string) {
c.Lock()
defer c.Unlock()
// double check to prevent upper call multiple cleanup tasks
if c.dirtyips != nil {
return
}
lenBefore := len(c.ips)
if lenBefore > c.highWatermark {
c.highWatermark = lenBefore
}
now := time.Now()
if c.serveStale && c.serveExpiredTTL != 0 {
now = now.Add(time.Duration(c.serveExpiredTTL) * time.Second)
}
for _, domain := range expiredKeys {
rec := c.ips[domain]
if rec == nil {
continue
}
if rec.A != nil && rec.A.Expire.Before(now) {
rec.A = nil
}
if rec.AAAA != nil && rec.AAAA.Expire.Before(now) {
rec.AAAA = nil
}
if rec.A == nil && rec.AAAA == nil {
delete(c.ips, domain)
}
}
lenAfter := len(c.ips)
if lenAfter == 0 {
if c.highWatermark >= minSizeForEmptyRebuild {
errors.LogDebug(
context.Background(), c.name,
" rebuilding empty cache map to reclaim memory.",
" size_before_cleanup=", lenBefore,
" peak_size_before_rebuild=", c.highWatermark,
)
c.ips = make(map[string]*record)
c.highWatermark = 0
}
return
}
if reductionFromPeak := c.highWatermark - lenAfter; reductionFromPeak > shrinkAbsoluteThreshold &&
float64(reductionFromPeak) > float64(c.highWatermark)*shrinkRatioThreshold {
errors.LogDebug(
context.Background(), c.name,
" shrinking cache map to reclaim memory.",
" new_size=", lenAfter,
" peak_size_before_shrink=", c.highWatermark,
" reduction_since_peak=", reductionFromPeak,
)
c.dirtyips = c.ips
c.ips = make(map[string]*record, int(float64(lenAfter)*1.1))
c.highWatermark = lenAfter
go c.migrate()
}
}
type migrationEntry struct {
key string
value *record
}
func (c *CacheController) migrate() {
defer func() {
if r := recover(); r != nil {
errors.LogError(context.Background(), c.name, " panic during cache migration: ", r)
c.Lock()
c.dirtyips = nil
// c.ips = make(map[string]*record)
// c.highWatermark = 0
c.Unlock()
}
}()
c.RLock()
record, found := c.ips[domain]
dirtyips := c.dirtyips
c.RUnlock()
if !found {
return nil, 0, errRecordNotFound
// double check to prevent upper call multiple cleanup tasks
if dirtyips == nil {
return
}
var errs []error
var allIPs []net.IP
var rTTL uint32 = dns_feature.DefaultTTL
errors.LogDebug(context.Background(), c.name, " starting background cache migration for ", len(dirtyips), " items")
mergeReq := option.IPv4Enable && option.IPv6Enable
batch := make([]migrationEntry, 0, migrationBatchSize)
for domain, recD := range dirtyips {
batch = append(batch, migrationEntry{domain, recD})
if option.IPv4Enable {
ips, ttl, err := record.A.getIPs()
if !mergeReq || go_errors.Is(err, errRecordNotFound) {
return ips, ttl, err
if len(batch) >= migrationBatchSize {
c.flush(batch)
batch = batch[:0]
runtime.Gosched()
}
if ttl < rTTL {
rTTL = ttl
}
if len(ips) > 0 {
allIPs = append(allIPs, ips...)
}
if len(batch) > 0 {
c.flush(batch)
}
c.Lock()
c.dirtyips = nil
c.Unlock()
errors.LogDebug(context.Background(), c.name, " cache migration completed")
}
func (c *CacheController) flush(batch []migrationEntry) {
c.Lock()
defer c.Unlock()
for _, dirty := range batch {
if cur := c.ips[dirty.key]; cur != nil {
merge := &record{}
if cur.A == nil {
merge.A = dirty.value.A
} else {
merge.A = cur.A
}
if cur.AAAA == nil {
merge.AAAA = dirty.value.AAAA
} else {
merge.AAAA = cur.AAAA
}
c.ips[dirty.key] = merge
} else {
errs = append(errs, err)
c.ips[dirty.key] = dirty.value
}
}
}
func (c *CacheController) updateRecord(req *dnsRequest, rep *IPRecord) {
rtt := time.Since(req.start)
switch req.reqType {
case dnsmessage.TypeA:
c.pub.Publish(req.domain+"4", rep)
case dnsmessage.TypeAAAA:
c.pub.Publish(req.domain+"6", rep)
}
if c.disableCache {
errors.LogInfo(context.Background(), c.name, " got answer: ", req.domain, " ", req.reqType, " -> ", rep.IP, ", rtt: ", rtt)
return
}
c.Lock()
lockWait := time.Since(req.start) - rtt
newRec := &record{}
oldRec := c.ips[req.domain]
var dirtyRec *record
if c.dirtyips != nil {
dirtyRec = c.dirtyips[req.domain]
}
var pubRecord *IPRecord
var pubSuffix string
switch req.reqType {
case dnsmessage.TypeA:
newRec.A = rep
if oldRec != nil && oldRec.AAAA != nil {
newRec.AAAA = oldRec.AAAA
pubRecord = oldRec.AAAA
} else if dirtyRec != nil && dirtyRec.AAAA != nil {
pubRecord = dirtyRec.AAAA
}
pubSuffix = "6"
case dnsmessage.TypeAAAA:
newRec.AAAA = rep
if oldRec != nil && oldRec.A != nil {
newRec.A = oldRec.A
pubRecord = oldRec.A
} else if dirtyRec != nil && dirtyRec.A != nil {
pubRecord = dirtyRec.A
}
pubSuffix = "4"
}
c.ips[req.domain] = newRec
c.Unlock()
if pubRecord != nil {
_, ttl, err := pubRecord.getIPs()
if ttl > 0 && !go_errors.Is(err, errRecordNotFound) {
c.pub.Publish(req.domain+pubSuffix, pubRecord)
}
}
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)
}
}
errors.LogInfo(context.Background(), c.name, " got answer: ", req.domain, " ", req.reqType, " -> ", rep.IP, ", rtt: ", rtt, ", lock: ", lockWait)
if len(allIPs) > 0 {
return allIPs, rTTL, nil
if !c.serveStale || c.serveExpiredTTL != 0 {
common.Must(c.cacheCleanup.Start())
}
if go_errors.Is(errs[0], errs[1]) {
return nil, rTTL, errs[0]
}
func (c *CacheController) findRecords(domain string) *record {
c.RLock()
defer c.RUnlock()
rec := c.ips[domain]
if rec == nil && c.dirtyips != nil {
rec = c.dirtyips[domain]
}
return nil, rTTL, errors.Combine(errs...)
return rec
}
func (c *CacheController) registerSubscribers(domain string, option dns_feature.IPOption) (sub4 *pubsub.Subscriber, sub6 *pubsub.Subscriber) {
+11 -35
View File
@@ -2,48 +2,24 @@ package dns
import (
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/strmatcher"
"github.com/xtls/xray-core/common/uuid"
)
var typeMap = map[DomainMatchingType]strmatcher.Type{
DomainMatchingType_Full: strmatcher.Full,
DomainMatchingType_Subdomain: strmatcher.Domain,
DomainMatchingType_Keyword: strmatcher.Substr,
DomainMatchingType_Regex: strmatcher.Regex,
}
// References:
// https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml
// https://unix.stackexchange.com/questions/92441/whats-the-difference-between-local-home-and-lan
var localTLDsAndDotlessDomains = []*NameServer_PriorityDomain{
{Type: DomainMatchingType_Regex, Domain: "^[^.]+$"}, // This will only match domains without any dot
{Type: DomainMatchingType_Subdomain, Domain: "local"},
{Type: DomainMatchingType_Subdomain, Domain: "localdomain"},
{Type: DomainMatchingType_Subdomain, Domain: "localhost"},
{Type: DomainMatchingType_Subdomain, Domain: "lan"},
{Type: DomainMatchingType_Subdomain, Domain: "home.arpa"},
{Type: DomainMatchingType_Subdomain, Domain: "example"},
{Type: DomainMatchingType_Subdomain, Domain: "invalid"},
{Type: DomainMatchingType_Subdomain, Domain: "test"},
}
var localTLDsAndDotlessDomainsRule = &NameServer_OriginalRule{
Rule: "geosite:private",
Size: uint32(len(localTLDsAndDotlessDomains)),
}
func toStrMatcher(t DomainMatchingType, domain string) (strmatcher.Matcher, error) {
strMType, f := typeMap[t]
if !f {
return nil, errors.New("unknown mapping type", t).AtWarning()
}
matcher, err := strMType.New(domain)
if err != nil {
return nil, errors.New("failed to create str matcher").Base(err)
}
return matcher, nil
var localTLDsAndDotlessDomainsRules = []*geodata.DomainRule{
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Regex, Value: "^[^.]+$"}}}, // This will only match domains without any dot
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "local"}}},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "localdomain"}}},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "localhost"}}},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "lan"}}},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "home.arpa"}}},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "example"}}},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "invalid"}}},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "test"}}},
}
func toNetIP(addrs []net.Address) ([]net.IP, error) {
+175 -355
View File
@@ -1,18 +1,19 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/dns/config.proto
package dns
import (
router "github.com/xtls/xray-core/app/router"
geodata "github.com/xtls/xray-core/common/geodata"
net "github.com/xtls/xray-core/common/net"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -22,58 +23,6 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type DomainMatchingType int32
const (
DomainMatchingType_Full DomainMatchingType = 0
DomainMatchingType_Subdomain DomainMatchingType = 1
DomainMatchingType_Keyword DomainMatchingType = 2
DomainMatchingType_Regex DomainMatchingType = 3
)
// Enum value maps for DomainMatchingType.
var (
DomainMatchingType_name = map[int32]string{
0: "Full",
1: "Subdomain",
2: "Keyword",
3: "Regex",
}
DomainMatchingType_value = map[string]int32{
"Full": 0,
"Subdomain": 1,
"Keyword": 2,
"Regex": 3,
}
)
func (x DomainMatchingType) Enum() *DomainMatchingType {
p := new(DomainMatchingType)
*p = x
return p
}
func (x DomainMatchingType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (DomainMatchingType) Descriptor() protoreflect.EnumDescriptor {
return file_app_dns_config_proto_enumTypes[0].Descriptor()
}
func (DomainMatchingType) Type() protoreflect.EnumType {
return &file_app_dns_config_proto_enumTypes[0]
}
func (x DomainMatchingType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use DomainMatchingType.Descriptor instead.
func (DomainMatchingType) EnumDescriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{0}
}
type QueryStrategy int32
const (
@@ -110,11 +59,11 @@ func (x QueryStrategy) String() string {
}
func (QueryStrategy) Descriptor() protoreflect.EnumDescriptor {
return file_app_dns_config_proto_enumTypes[1].Descriptor()
return file_app_dns_config_proto_enumTypes[0].Descriptor()
}
func (QueryStrategy) Type() protoreflect.EnumType {
return &file_app_dns_config_proto_enumTypes[1]
return &file_app_dns_config_proto_enumTypes[0]
}
func (x QueryStrategy) Number() protoreflect.EnumNumber {
@@ -123,28 +72,29 @@ func (x QueryStrategy) Number() protoreflect.EnumNumber {
// Deprecated: Use QueryStrategy.Descriptor instead.
func (QueryStrategy) EnumDescriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{1}
return file_app_dns_config_proto_rawDescGZIP(), []int{0}
}
type NameServer struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Address *net.Endpoint `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
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"`
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"`
state protoimpl.MessageState `protogen:"open.v1"`
Address *net.Endpoint `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
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"`
Domain []*geodata.DomainRule `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"`
ExpectedIp []*geodata.IPRule `protobuf:"bytes,3,rep,name=expected_ip,json=expectedIp,proto3" json:"expected_ip,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,oneof" json:"disableCache,omitempty"`
ServeStale *bool `protobuf:"varint,15,opt,name=serveStale,proto3,oneof" json:"serveStale,omitempty"`
ServeExpiredTTL *uint32 `protobuf:"varint,16,opt,name=serveExpiredTTL,proto3,oneof" json:"serveExpiredTTL,omitempty"`
FinalQuery bool `protobuf:"varint,12,opt,name=finalQuery,proto3" json:"finalQuery,omitempty"`
UnexpectedIp []*geodata.IPRule `protobuf:"bytes,13,rep,name=unexpected_ip,json=unexpectedIp,proto3" json:"unexpected_ip,omitempty"`
ActUnprior bool `protobuf:"varint,14,opt,name=actUnprior,proto3" json:"actUnprior,omitempty"`
PolicyID uint32 `protobuf:"varint,17,opt,name=policyID,proto3" json:"policyID,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *NameServer) Reset() {
@@ -198,23 +148,16 @@ func (x *NameServer) GetSkipFallback() bool {
return false
}
func (x *NameServer) GetPrioritizedDomain() []*NameServer_PriorityDomain {
func (x *NameServer) GetDomain() []*geodata.DomainRule {
if x != nil {
return x.PrioritizedDomain
return x.Domain
}
return nil
}
func (x *NameServer) GetExpectedGeoip() []*router.GeoIP {
func (x *NameServer) GetExpectedIp() []*geodata.IPRule {
if x != nil {
return x.ExpectedGeoip
}
return nil
}
func (x *NameServer) GetOriginalRules() []*NameServer_OriginalRule {
if x != nil {
return x.OriginalRules
return x.ExpectedIp
}
return nil
}
@@ -248,12 +191,26 @@ func (x *NameServer) GetTimeoutMs() uint64 {
}
func (x *NameServer) GetDisableCache() bool {
if x != nil {
return x.DisableCache
if x != nil && x.DisableCache != nil {
return *x.DisableCache
}
return false
}
func (x *NameServer) GetServeStale() bool {
if x != nil && x.ServeStale != nil {
return *x.ServeStale
}
return false
}
func (x *NameServer) GetServeExpiredTTL() uint32 {
if x != nil && x.ServeExpiredTTL != nil {
return *x.ServeExpiredTTL
}
return 0
}
func (x *NameServer) GetFinalQuery() bool {
if x != nil {
return x.FinalQuery
@@ -261,9 +218,9 @@ func (x *NameServer) GetFinalQuery() bool {
return false
}
func (x *NameServer) GetUnexpectedGeoip() []*router.GeoIP {
func (x *NameServer) GetUnexpectedIp() []*geodata.IPRule {
if x != nil {
return x.UnexpectedGeoip
return x.UnexpectedIp
}
return nil
}
@@ -275,11 +232,15 @@ func (x *NameServer) GetActUnprior() bool {
return false
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
func (x *NameServer) GetPolicyID() uint32 {
if x != nil {
return x.PolicyID
}
return 0
}
type Config struct {
state protoimpl.MessageState `protogen:"open.v1"`
// NameServer list used by this DNS client.
// A special value 'localhost' as a domain address can be set to use DNS on local system.
NameServer []*NameServer `protobuf:"bytes,5,rep,name=name_server,json=nameServer,proto3" json:"name_server,omitempty"`
@@ -291,9 +252,14 @@ type Config struct {
Tag string `protobuf:"bytes,6,opt,name=tag,proto3" json:"tag,omitempty"`
// DisableCache disables DNS cache
DisableCache bool `protobuf:"varint,8,opt,name=disableCache,proto3" json:"disableCache,omitempty"`
ServeStale bool `protobuf:"varint,12,opt,name=serveStale,proto3" json:"serveStale,omitempty"`
ServeExpiredTTL uint32 `protobuf:"varint,13,opt,name=serveExpiredTTL,proto3" json:"serveExpiredTTL,omitempty"`
QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"`
DisableFallback bool `protobuf:"varint,10,opt,name=disableFallback,proto3" json:"disableFallback,omitempty"`
DisableFallbackIfMatch bool `protobuf:"varint,11,opt,name=disableFallbackIfMatch,proto3" json:"disableFallbackIfMatch,omitempty"`
EnableParallelQuery bool `protobuf:"varint,14,opt,name=enableParallelQuery,proto3" json:"enableParallelQuery,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
@@ -361,6 +327,20 @@ func (x *Config) GetDisableCache() bool {
return false
}
func (x *Config) GetServeStale() bool {
if x != nil {
return x.ServeStale
}
return false
}
func (x *Config) GetServeExpiredTTL() uint32 {
if x != nil {
return x.ServeExpiredTTL
}
return 0
}
func (x *Config) GetQueryStrategy() QueryStrategy {
if x != nil {
return x.QueryStrategy
@@ -382,128 +362,27 @@ func (x *Config) GetDisableFallbackIfMatch() bool {
return false
}
type NameServer_PriorityDomain struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Type DomainMatchingType `protobuf:"varint,1,opt,name=type,proto3,enum=xray.app.dns.DomainMatchingType" json:"type,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
}
func (x *NameServer_PriorityDomain) Reset() {
*x = NameServer_PriorityDomain{}
mi := &file_app_dns_config_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *NameServer_PriorityDomain) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*NameServer_PriorityDomain) ProtoMessage() {}
func (x *NameServer_PriorityDomain) ProtoReflect() protoreflect.Message {
mi := &file_app_dns_config_proto_msgTypes[2]
func (x *Config) GetEnableParallelQuery() bool {
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
return x.EnableParallelQuery
}
return mi.MessageOf(x)
}
// Deprecated: Use NameServer_PriorityDomain.ProtoReflect.Descriptor instead.
func (*NameServer_PriorityDomain) Descriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{0, 0}
}
func (x *NameServer_PriorityDomain) GetType() DomainMatchingType {
if x != nil {
return x.Type
}
return DomainMatchingType_Full
}
func (x *NameServer_PriorityDomain) GetDomain() string {
if x != nil {
return x.Domain
}
return ""
}
type NameServer_OriginalRule struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Rule string `protobuf:"bytes,1,opt,name=rule,proto3" json:"rule,omitempty"`
Size uint32 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"`
}
func (x *NameServer_OriginalRule) Reset() {
*x = NameServer_OriginalRule{}
mi := &file_app_dns_config_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *NameServer_OriginalRule) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*NameServer_OriginalRule) ProtoMessage() {}
func (x *NameServer_OriginalRule) ProtoReflect() protoreflect.Message {
mi := &file_app_dns_config_proto_msgTypes[3]
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 NameServer_OriginalRule.ProtoReflect.Descriptor instead.
func (*NameServer_OriginalRule) Descriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{0, 1}
}
func (x *NameServer_OriginalRule) GetRule() string {
if x != nil {
return x.Rule
}
return ""
}
func (x *NameServer_OriginalRule) GetSize() uint32 {
if x != nil {
return x.Size
}
return 0
return false
}
type Config_HostMapping struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Type DomainMatchingType `protobuf:"varint,1,opt,name=type,proto3,enum=xray.app.dns.DomainMatchingType" json:"type,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
Ip [][]byte `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"`
state protoimpl.MessageState `protogen:"open.v1"`
Domain *geodata.DomainRule `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
Ip [][]byte `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"`
// ProxiedDomain indicates the mapped domain has the same IP address on this
// domain. Xray will use this domain for IP queries.
ProxiedDomain string `protobuf:"bytes,4,opt,name=proxied_domain,json=proxiedDomain,proto3" json:"proxied_domain,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Config_HostMapping) Reset() {
*x = Config_HostMapping{}
mi := &file_app_dns_config_proto_msgTypes[4]
mi := &file_app_dns_config_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -515,7 +394,7 @@ func (x *Config_HostMapping) String() string {
func (*Config_HostMapping) ProtoMessage() {}
func (x *Config_HostMapping) ProtoReflect() protoreflect.Message {
mi := &file_app_dns_config_proto_msgTypes[4]
mi := &file_app_dns_config_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -531,18 +410,11 @@ func (*Config_HostMapping) Descriptor() ([]byte, []int) {
return file_app_dns_config_proto_rawDescGZIP(), []int{1, 0}
}
func (x *Config_HostMapping) GetType() DomainMatchingType {
if x != nil {
return x.Type
}
return DomainMatchingType_Full
}
func (x *Config_HostMapping) GetDomain() string {
func (x *Config_HostMapping) GetDomain() *geodata.DomainRule {
if x != nil {
return x.Domain
}
return ""
return nil
}
func (x *Config_HostMapping) GetIp() [][]byte {
@@ -561,156 +433,104 @@ func (x *Config_HostMapping) GetProxiedDomain() string {
var File_app_dns_config_proto protoreflect.FileDescriptor
var file_app_dns_config_proto_rawDesc = []byte{
0x0a, 0x14, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
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, 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,
0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12,
0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x22, 0x0a, 0x0c,
0x73, 0x6b, 0x69, 0x70, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01,
0x28, 0x08, 0x52, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b,
0x12, 0x56, 0x0a, 0x12, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f,
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 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, 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, 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,
}
const file_app_dns_config_proto_rawDesc = "" +
"\n" +
"\x14app/dns/config.proto\x12\fxray.app.dns\x1a\x1ccommon/net/destination.proto\x1a\x1bcommon/geodata/geodat.proto\"\xde\x05\n" +
"\n" +
"NameServer\x123\n" +
"\aaddress\x18\x01 \x01(\v2\x19.xray.common.net.EndpointR\aaddress\x12\x1b\n" +
"\tclient_ip\x18\x05 \x01(\fR\bclientIp\x12\"\n" +
"\fskipFallback\x18\x06 \x01(\bR\fskipFallback\x127\n" +
"\x06domain\x18\x02 \x03(\v2\x1f.xray.common.geodata.DomainRuleR\x06domain\x12<\n" +
"\vexpected_ip\x18\x03 \x03(\v2\x1b.xray.common.geodata.IPRuleR\n" +
"expectedIp\x12B\n" +
"\x0equery_strategy\x18\a \x01(\x0e2\x1b.xray.app.dns.QueryStrategyR\rqueryStrategy\x12\x1a\n" +
"\bactPrior\x18\b \x01(\bR\bactPrior\x12\x10\n" +
"\x03tag\x18\t \x01(\tR\x03tag\x12\x1c\n" +
"\ttimeoutMs\x18\n" +
" \x01(\x04R\ttimeoutMs\x12'\n" +
"\fdisableCache\x18\v \x01(\bH\x00R\fdisableCache\x88\x01\x01\x12#\n" +
"\n" +
"serveStale\x18\x0f \x01(\bH\x01R\n" +
"serveStale\x88\x01\x01\x12-\n" +
"\x0fserveExpiredTTL\x18\x10 \x01(\rH\x02R\x0fserveExpiredTTL\x88\x01\x01\x12\x1e\n" +
"\n" +
"finalQuery\x18\f \x01(\bR\n" +
"finalQuery\x12@\n" +
"\runexpected_ip\x18\r \x03(\v2\x1b.xray.common.geodata.IPRuleR\funexpectedIp\x12\x1e\n" +
"\n" +
"actUnprior\x18\x0e \x01(\bR\n" +
"actUnprior\x12\x1a\n" +
"\bpolicyID\x18\x11 \x01(\rR\bpolicyIDB\x0f\n" +
"\r_disableCacheB\r\n" +
"\v_serveStaleB\x12\n" +
"\x10_serveExpiredTTLJ\x04\b\x04\x10\x05\"\x82\x05\n" +
"\x06Config\x129\n" +
"\vname_server\x18\x05 \x03(\v2\x18.xray.app.dns.NameServerR\n" +
"nameServer\x12\x1b\n" +
"\tclient_ip\x18\x03 \x01(\fR\bclientIp\x12C\n" +
"\fstatic_hosts\x18\x04 \x03(\v2 .xray.app.dns.Config.HostMappingR\vstaticHosts\x12\x10\n" +
"\x03tag\x18\x06 \x01(\tR\x03tag\x12\"\n" +
"\fdisableCache\x18\b \x01(\bR\fdisableCache\x12\x1e\n" +
"\n" +
"serveStale\x18\f \x01(\bR\n" +
"serveStale\x12(\n" +
"\x0fserveExpiredTTL\x18\r \x01(\rR\x0fserveExpiredTTL\x12B\n" +
"\x0equery_strategy\x18\t \x01(\x0e2\x1b.xray.app.dns.QueryStrategyR\rqueryStrategy\x12(\n" +
"\x0fdisableFallback\x18\n" +
" \x01(\bR\x0fdisableFallback\x126\n" +
"\x16disableFallbackIfMatch\x18\v \x01(\bR\x16disableFallbackIfMatch\x120\n" +
"\x13enableParallelQuery\x18\x0e \x01(\bR\x13enableParallelQuery\x1a}\n" +
"\vHostMapping\x127\n" +
"\x06domain\x18\x02 \x01(\v2\x1f.xray.common.geodata.DomainRuleR\x06domain\x12\x0e\n" +
"\x02ip\x18\x03 \x03(\fR\x02ip\x12%\n" +
"\x0eproxied_domain\x18\x04 \x01(\tR\rproxiedDomainJ\x04\b\a\x10\b*B\n" +
"\rQueryStrategy\x12\n" +
"\n" +
"\x06USE_IP\x10\x00\x12\v\n" +
"\aUSE_IP4\x10\x01\x12\v\n" +
"\aUSE_IP6\x10\x02\x12\v\n" +
"\aUSE_SYS\x10\x03BF\n" +
"\x10com.xray.app.dnsP\x01Z!github.com/xtls/xray-core/app/dns\xaa\x02\fXray.App.Dnsb\x06proto3"
var (
file_app_dns_config_proto_rawDescOnce sync.Once
file_app_dns_config_proto_rawDescData = file_app_dns_config_proto_rawDesc
file_app_dns_config_proto_rawDescData []byte
)
func file_app_dns_config_proto_rawDescGZIP() []byte {
file_app_dns_config_proto_rawDescOnce.Do(func() {
file_app_dns_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_dns_config_proto_rawDescData)
file_app_dns_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_dns_config_proto_rawDesc), len(file_app_dns_config_proto_rawDesc)))
})
return file_app_dns_config_proto_rawDescData
}
var file_app_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_app_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_app_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_app_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_app_dns_config_proto_goTypes = []any{
(DomainMatchingType)(0), // 0: xray.app.dns.DomainMatchingType
(QueryStrategy)(0), // 1: xray.app.dns.QueryStrategy
(*NameServer)(nil), // 2: xray.app.dns.NameServer
(*Config)(nil), // 3: xray.app.dns.Config
(*NameServer_PriorityDomain)(nil), // 4: xray.app.dns.NameServer.PriorityDomain
(*NameServer_OriginalRule)(nil), // 5: xray.app.dns.NameServer.OriginalRule
(*Config_HostMapping)(nil), // 6: xray.app.dns.Config.HostMapping
(*net.Endpoint)(nil), // 7: xray.common.net.Endpoint
(*router.GeoIP)(nil), // 8: xray.app.router.GeoIP
(QueryStrategy)(0), // 0: xray.app.dns.QueryStrategy
(*NameServer)(nil), // 1: xray.app.dns.NameServer
(*Config)(nil), // 2: xray.app.dns.Config
(*Config_HostMapping)(nil), // 3: xray.app.dns.Config.HostMapping
(*net.Endpoint)(nil), // 4: xray.common.net.Endpoint
(*geodata.DomainRule)(nil), // 5: xray.common.geodata.DomainRule
(*geodata.IPRule)(nil), // 6: xray.common.geodata.IPRule
}
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.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
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
4, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint
5, // 1: xray.app.dns.NameServer.domain:type_name -> xray.common.geodata.DomainRule
6, // 2: xray.app.dns.NameServer.expected_ip:type_name -> xray.common.geodata.IPRule
0, // 3: xray.app.dns.NameServer.query_strategy:type_name -> xray.app.dns.QueryStrategy
6, // 4: xray.app.dns.NameServer.unexpected_ip:type_name -> xray.common.geodata.IPRule
1, // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer
3, // 6: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping
0, // 7: xray.app.dns.Config.query_strategy:type_name -> xray.app.dns.QueryStrategy
5, // 8: xray.app.dns.Config.HostMapping.domain:type_name -> xray.common.geodata.DomainRule
9, // [9:9] is the sub-list for method output_type
9, // [9:9] is the sub-list for method input_type
9, // [9:9] is the sub-list for extension type_name
9, // [9:9] is the sub-list for extension extendee
0, // [0:9] is the sub-list for field type_name
}
func init() { file_app_dns_config_proto_init() }
@@ -718,13 +538,14 @@ func file_app_dns_config_proto_init() {
if File_app_dns_config_proto != nil {
return
}
file_app_dns_config_proto_msgTypes[0].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_dns_config_proto_rawDesc,
NumEnums: 2,
NumMessages: 5,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_dns_config_proto_rawDesc), len(file_app_dns_config_proto_rawDesc)),
NumEnums: 1,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
@@ -734,7 +555,6 @@ func file_app_dns_config_proto_init() {
MessageInfos: file_app_dns_config_proto_msgTypes,
}.Build()
File_app_dns_config_proto = out.File
file_app_dns_config_proto_rawDesc = nil
file_app_dns_config_proto_goTypes = nil
file_app_dns_config_proto_depIdxs = nil
}
+14 -26
View File
@@ -7,41 +7,26 @@ option java_package = "com.xray.app.dns";
option java_multiple_files = true;
import "common/net/destination.proto";
import "app/router/config.proto";
import "common/geodata/geodat.proto";
message NameServer {
xray.common.net.Endpoint address = 1;
bytes client_ip = 5;
bool skipFallback = 6;
message PriorityDomain {
DomainMatchingType type = 1;
string domain = 2;
}
message OriginalRule {
string rule = 1;
uint32 size = 2;
}
repeated PriorityDomain prioritized_domain = 2;
repeated xray.app.router.GeoIP expected_geoip = 3;
repeated OriginalRule original_rules = 4;
repeated xray.common.geodata.DomainRule domain = 2;
repeated xray.common.geodata.IPRule expected_ip = 3;
reserved 4;
QueryStrategy query_strategy = 7;
bool actPrior = 8;
string tag = 9;
uint64 timeoutMs = 10;
bool disableCache = 11;
optional bool disableCache = 11;
optional bool serveStale = 15;
optional uint32 serveExpiredTTL = 16;
bool finalQuery = 12;
repeated xray.app.router.GeoIP unexpected_geoip = 13;
repeated xray.common.geodata.IPRule unexpected_ip = 13;
bool actUnprior = 14;
}
enum DomainMatchingType {
Full = 0;
Subdomain = 1;
Keyword = 2;
Regex = 3;
uint32 policyID = 17;
}
enum QueryStrategy {
@@ -61,8 +46,7 @@ message Config {
bytes client_ip = 3;
message HostMapping {
DomainMatchingType type = 1;
string domain = 2;
xray.common.geodata.DomainRule domain = 2;
repeated bytes ip = 3;
@@ -80,9 +64,13 @@ message Config {
// DisableCache disables DNS cache
bool disableCache = 8;
bool serveStale = 12;
uint32 serveExpiredTTL = 13;
QueryStrategy query_strategy = 9;
bool disableFallback = 10;
bool disableFallbackIfMatch = 11;
bool enableParallelQuery = 14;
}
+298 -107
View File
@@ -11,9 +11,10 @@ import (
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/geodata"
"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/common/utils"
"github.com/xtls/xray-core/features/dns"
)
@@ -22,19 +23,20 @@ type DNS struct {
sync.Mutex
disableFallback bool
disableFallbackIfMatch bool
enableParallelQuery bool
ipOption *dns.IPOption
hosts *StaticHosts
clients []*Client
ctx context.Context
domainMatcher strmatcher.IndexMatcher
domainMatcher geodata.DomainMatcher
matcherInfos []*DomainMatcherInfo
checkSystem bool
}
// DomainMatcherInfo contains information attached to index returned by Server.domainMatcher
// DomainMatcherInfo contains information attached to index returned by Server.domainMatcher.
type DomainMatcherInfo struct {
clientIdx uint16
domainRuleIdx uint16
clientIdx uint16
domainRule string
}
// New creates a new DNS server with given configuration.
@@ -84,31 +86,36 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
return nil, errors.New("failed to create hosts").Base(err)
}
var clients []*Client
domainRuleCount := 0
var defaultTag = config.Tag
defaultTag := config.Tag
if len(config.Tag) == 0 {
defaultTag = generateRandomTag()
}
for _, ns := range config.NameServer {
domainRuleCount += len(ns.PrioritizedDomain)
}
// 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{}
clients := make([]*Client, 0, len(config.NameServer))
matcherInfos := make([]*DomainMatcherInfo, 0)
effectiveRules := make([]*geodata.DomainRule, 0)
for _, ns := range config.NameServer {
clientIdx := len(clients)
updateDomain := func(domainRule strmatcher.Matcher, originalRuleIdx int, matcherInfos []*DomainMatcherInfo) error {
midx := domainMatcher.Add(domainRule)
matcherInfos[midx] = &DomainMatcherInfo{
clientIdx: uint16(clientIdx),
domainRuleIdx: uint16(originalRuleIdx),
updateRules := func(isLocalNameServer bool) {
// Prioritize local domains with specific TLDs or those without any dot for the local DNS
if isLocalNameServer {
effectiveRules = append(effectiveRules, localTLDsAndDotlessDomainsRules...)
for _, rule := range localTLDsAndDotlessDomainsRules {
matcherInfos = append(matcherInfos, &DomainMatcherInfo{
clientIdx: uint16(clientIdx),
domainRule: rule.String(),
})
}
}
effectiveRules = append(effectiveRules, ns.Domain...)
for _, rule := range ns.Domain {
matcherInfos = append(matcherInfos, &DomainMatcherInfo{
clientIdx: uint16(clientIdx),
domainRule: rule.String(),
})
}
return nil
}
myClientIP := clientIP
@@ -117,24 +124,46 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
myClientIP = net.IP(ns.ClientIp)
}
disableCache := config.DisableCache || ns.DisableCache
disableCache := config.DisableCache
if ns.DisableCache != nil {
disableCache = *ns.DisableCache
}
var tag = defaultTag
serveStale := config.ServeStale
if ns.ServeStale != nil {
serveStale = *ns.ServeStale
}
serveExpiredTTL := config.ServeExpiredTTL
if ns.ServeExpiredTTL != nil {
serveExpiredTTL = *ns.ServeExpiredTTL
}
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)
client, err := NewClient(ctx, ns, myClientIP, disableCache, serveStale, serveExpiredTTL, tag, clientIPOption, updateRules)
if err != nil {
return nil, errors.New("failed to create client").Base(err)
}
clients = append(clients, client)
}
var domainMatcher geodata.DomainMatcher
if len(effectiveRules) > 0 {
domainMatcher, err = geodata.DomainReg.BuildDomainMatcher(effectiveRules)
if err != nil {
return nil, err
}
}
// If there is no DNS client in config, add a `localhost` DNS client
if len(clients) == 0 {
clients = append(clients, NewLocalDNSClient(ipOption))
@@ -149,6 +178,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
matcherInfos: matcherInfos,
disableFallback: config.DisableFallback,
disableFallbackIfMatch: config.DisableFallbackIfMatch,
enableParallelQuery: config.EnableParallelQuery,
checkSystem: checkSystem,
}, nil
}
@@ -191,7 +221,7 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er
}
if s.checkSystem {
supportIPv4, supportIPv6 := checkSystemNetwork()
supportIPv4, supportIPv6 := utils.CheckRoutes()
option.IPv4Enable = option.IPv4Enable && supportIPv4
option.IPv6Enable = option.IPv6Enable && supportIPv6
} else {
@@ -227,45 +257,11 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er
}
// Name servers lookup
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, ttl, err := client.QueryIP(s.ctx, domain, option)
if len(ips) > 0 {
if ttl == 0 {
ttl = 1
}
return ips, ttl, nil
}
errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name())
if err == nil {
err = dns.ErrEmptyResponse
}
errs = append(errs, err)
if client.IsFinalQuery() {
break
}
if s.enableParallelQuery {
return s.parallelQuery(domain, option)
} else {
return s.serialQuery(domain, option)
}
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)
}
return nil, 0, dns.ErrEmptyResponse
}
func (s *DNS) sortClients(domain string) []*Client {
@@ -276,22 +272,28 @@ func (s *DNS) sortClients(domain string) []*Client {
// Priority domain matching
hasMatch := false
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]
domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", domainRule, info.clientIdx))
if clientUsed[info.clientIdx] {
continue
if s.domainMatcher != nil {
matchSlice := s.domainMatcher.Match(strings.ToLower(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 := info.domainRule
domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", domainRule, info.clientIdx))
if clientUsed[info.clientIdx] {
continue
}
clientUsed[info.clientIdx] = true
clients = append(clients, client)
clientNames = append(clientNames, client.Name())
hasMatch = true
if client.finalQuery {
logDecision(s.ctx, domain, domainRules, clientNames)
return clients
}
}
clientUsed[info.clientIdx] = true
clients = append(clients, client)
clientNames = append(clientNames, client.Name())
hasMatch = true
}
if !(s.disableFallback || s.disableFallbackIfMatch && hasMatch) {
@@ -303,46 +305,235 @@ func (s *DNS) sortClients(domain string) []*Client {
clientUsed[idx] = true
clients = append(clients, client)
clientNames = append(clientNames, client.Name())
if client.finalQuery {
logDecision(s.ctx, domain, domainRules, clientNames)
return clients
}
}
}
if len(domainRules) > 0 {
errors.LogDebug(s.ctx, "domain ", domain, " matches following rules: ", domainRules)
}
if len(clientNames) > 0 {
errors.LogDebug(s.ctx, "domain ", domain, " will use DNS in order: ", clientNames)
}
logDecision(s.ctx, domain, domainRules, clientNames)
if len(clients) == 0 {
clients = append(clients, s.clients[0])
clientNames = append(clientNames, s.clients[0].Name())
errors.LogDebug(s.ctx, "domain ", domain, " will use the first DNS: ", clientNames)
if len(s.clients) > 0 {
clients = append(clients, s.clients[0])
clientNames = append(clientNames, s.clients[0].Name())
errors.LogWarning(s.ctx, "domain ", domain, " will use the first DNS: ", clientNames)
} else {
errors.LogError(s.ctx, "no DNS clients available for domain ", domain, " and no default clients configured")
}
}
return clients
}
func logDecision(ctx context.Context, domain string, domainRules []string, clientNames []string) {
if len(domainRules) > 0 {
errors.LogDebug(ctx, "domain ", domain, " matches following rules: ", domainRules)
}
if len(clientNames) > 0 {
errors.LogDebug(ctx, "domain ", domain, " will use DNS in order: ", clientNames)
}
}
func mergeQueryErrors(domain string, errs []error) error {
if len(errs) == 0 {
return dns.ErrEmptyResponse
}
var noRNF error
for _, err := range errs {
if go_errors.Is(err, errRecordNotFound) {
continue // server no response, ignore
} else if noRNF == nil {
noRNF = err
} else if !go_errors.Is(err, noRNF) {
return errors.New("returning nil for domain ", domain).Base(errors.Combine(errs...))
}
}
if go_errors.Is(noRNF, dns.ErrEmptyResponse) {
return dns.ErrEmptyResponse
}
if noRNF == nil {
noRNF = errRecordNotFound
}
return errors.New("returning nil for domain ", domain).Base(noRNF)
}
func (s *DNS) serialQuery(domain string, option dns.IPOption) ([]net.IP, uint32, error) {
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, ttl, err := client.QueryIP(s.ctx, domain, option)
if len(ips) > 0 {
return ips, ttl, nil
}
errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name(), " in serial query mode")
if err == nil {
err = dns.ErrEmptyResponse
}
errs = append(errs, err)
}
return nil, 0, mergeQueryErrors(domain, errs)
}
func (s *DNS) parallelQuery(domain string, option dns.IPOption) ([]net.IP, uint32, error) {
var errs []error
clients := s.sortClients(domain)
resultsChan := asyncQueryAll(domain, option, clients, s.ctx)
groups, groupOf := makeGroups( /*s.ctx,*/ clients)
results := make([]*queryResult, len(clients))
pending := make([]int, len(groups))
for gi, g := range groups {
pending[gi] = g.end - g.start + 1
}
nextGroup := 0
for range clients {
result := <-resultsChan
results[result.index] = &result
gi := groupOf[result.index]
pending[gi]--
for nextGroup < len(groups) {
g := groups[nextGroup]
// group race, minimum rtt -> return
for j := g.start; j <= g.end; j++ {
r := results[j]
if r != nil && r.err == nil && len(r.ips) > 0 {
return r.ips, r.ttl, nil
}
}
// current group is incomplete and no one success -> continue pending
if pending[nextGroup] > 0 {
break
}
// all failed -> log and continue next group
for j := g.start; j <= g.end; j++ {
r := results[j]
e := r.err
if e == nil {
e = dns.ErrEmptyResponse
}
errors.LogInfoInner(s.ctx, e, "failed to lookup ip for domain ", domain, " at server ", clients[j].Name(), " in parallel query mode")
errs = append(errs, e)
}
nextGroup++
}
}
return nil, 0, mergeQueryErrors(domain, errs)
}
type queryResult struct {
ips []net.IP
ttl uint32
err error
index int
}
func asyncQueryAll(domain string, option dns.IPOption, clients []*Client, ctx context.Context) chan queryResult {
if len(clients) == 0 {
ch := make(chan queryResult)
close(ch)
return ch
}
ch := make(chan queryResult, len(clients))
for i, client := range clients {
if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") {
errors.LogDebug(ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name())
ch <- queryResult{err: dns.ErrEmptyResponse, index: i}
continue
}
go func(i int, c *Client) {
qctx := ctx
if !c.server.IsDisableCache() {
nctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), c.timeoutMs*2)
qctx = nctx
defer cancel()
}
ips, ttl, err := c.QueryIP(qctx, domain, option)
ch <- queryResult{ips: ips, ttl: ttl, err: err, index: i}
}(i, client)
}
return ch
}
type group struct{ start, end int }
// merge only adjacent and rule-equivalent Client into a single group
func makeGroups( /*ctx context.Context,*/ clients []*Client) ([]group, []int) {
n := len(clients)
if n == 0 {
return nil, nil
}
groups := make([]group, 0, n)
groupOf := make([]int, n)
s, e := 0, 0
for i := 1; i < n; i++ {
if clients[i-1].policyID == clients[i].policyID {
e = i
} else {
for k := s; k <= e; k++ {
groupOf[k] = len(groups)
}
groups = append(groups, group{start: s, end: e})
s, e = i, i
}
}
for k := s; k <= e; k++ {
groupOf[k] = len(groups)
}
groups = append(groups, group{start: s, end: e})
// var b strings.Builder
// b.WriteString("dns grouping: total clients=")
// b.WriteString(strconv.Itoa(n))
// b.WriteString(", groups=")
// b.WriteString(strconv.Itoa(len(groups)))
// for gi, g := range groups {
// b.WriteString("\n [")
// b.WriteString(strconv.Itoa(g.start))
// b.WriteString("..")
// b.WriteString(strconv.Itoa(g.end))
// b.WriteString("] gid=")
// b.WriteString(strconv.Itoa(gi))
// b.WriteString(" pid=")
// b.WriteString(strconv.FormatUint(uint64(clients[g.start].policyID), 10))
// b.WriteString(" members: ")
// for i := g.start; i <= g.end; i++ {
// if i > g.start {
// b.WriteString(", ")
// }
// b.WriteString(strconv.Itoa(i))
// b.WriteByte(':')
// b.WriteString(clients[i].Name())
// }
// }
// errors.LogDebug(ctx, b.String())
return groups, groupOf
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return New(ctx, config.(*Config))
}))
}
func checkSystemNetwork() (supportIPv4 bool, supportIPv6 bool) {
conn4, err4 := net.Dial("udp4", "192.33.4.12:53")
if err4 != nil {
supportIPv4 = false
} else {
supportIPv4 = true
conn4.Close()
}
conn6, err6 := net.Dial("udp6", "[2001:500:2::c]:53")
if err6 != nil {
supportIPv6 = false
} else {
supportIPv6 = true
conn6.Close()
}
return
}
+68 -100
View File
@@ -11,9 +11,9 @@ import (
"github.com/xtls/xray-core/app/policy"
"github.com/xtls/xray-core/app/proxyman"
_ "github.com/xtls/xray-core/app/proxyman/outbound"
"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/geodata"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/core"
@@ -147,7 +147,9 @@ func TestUDPServerSubnet(t *testing.T) {
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
ProxySettings: serial.ToTypedMessage(&freedom.Config{
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
},
},
}
@@ -207,7 +209,9 @@ func TestUDPServer(t *testing.T) {
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
ProxySettings: serial.ToTypedMessage(&freedom.Config{
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
},
},
}
@@ -331,10 +335,9 @@ func TestPrioritizedDomain(t *testing.T) {
},
Port: uint32(port),
},
PrioritizedDomain: []*NameServer_PriorityDomain{
Domain: []*geodata.DomainRule{
{
Type: DomainMatchingType_Full,
Domain: "google.com",
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "google.com"}},
},
},
},
@@ -346,7 +349,9 @@ func TestPrioritizedDomain(t *testing.T) {
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
ProxySettings: serial.ToTypedMessage(&freedom.Config{
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
},
},
}
@@ -415,7 +420,9 @@ func TestUDPServerIPv6(t *testing.T) {
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
ProxySettings: serial.ToTypedMessage(&freedom.Config{
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
},
},
}
@@ -471,8 +478,7 @@ func TestStaticHostDomain(t *testing.T) {
},
StaticHosts: []*Config_HostMapping{
{
Type: DomainMatchingType_Full,
Domain: "example.com",
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "example.com"}}},
ProxiedDomain: "google.com",
},
},
@@ -483,7 +489,9 @@ func TestStaticHostDomain(t *testing.T) {
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
ProxySettings: serial.ToTypedMessage(&freedom.Config{
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
},
},
}
@@ -539,17 +547,9 @@ func TestIPMatch(t *testing.T) {
},
Port: uint32(port),
},
ExpectedGeoip: []*router.GeoIP{
{
CountryCode: "local",
Cidr: []*router.CIDR{
{
// inner ip, will not match
Ip: []byte{192, 168, 11, 1},
Prefix: 32,
},
},
},
ExpectedIp: []*geodata.IPRule{
// inner ip, will not match
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{192, 168, 11, 1}, Prefix: 32}}}},
},
},
// second dns, match ip
@@ -563,25 +563,9 @@ func TestIPMatch(t *testing.T) {
},
Port: uint32(port),
},
ExpectedGeoip: []*router.GeoIP{
{
CountryCode: "test",
Cidr: []*router.CIDR{
{
Ip: []byte{8, 8, 8, 8},
Prefix: 32,
},
},
},
{
CountryCode: "test",
Cidr: []*router.CIDR{
{
Ip: []byte{8, 8, 8, 4},
Prefix: 32,
},
},
},
ExpectedIp: []*geodata.IPRule{
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{8, 8, 8, 8}, Prefix: 32}}}},
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{8, 8, 8, 4}, Prefix: 32}}}},
},
},
},
@@ -592,7 +576,9 @@ func TestIPMatch(t *testing.T) {
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
ProxySettings: serial.ToTypedMessage(&freedom.Config{
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
},
},
}
@@ -663,19 +649,15 @@ func TestLocalDomain(t *testing.T) {
},
Port: uint32(port),
},
PrioritizedDomain: []*NameServer_PriorityDomain{
Domain: []*geodata.DomainRule{
// Equivalent of dotless:localhost
{Type: DomainMatchingType_Regex, Domain: "^[^.]*localhost[^.]*$"},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Regex, Value: "^[^.]*localhost[^.]*$"}}},
},
ExpectedGeoip: []*router.GeoIP{
{ // Will match localhost, localhost-a and localhost-b,
CountryCode: "local",
Cidr: []*router.CIDR{
{Ip: []byte{127, 0, 0, 2}, Prefix: 32},
{Ip: []byte{127, 0, 0, 3}, Prefix: 32},
{Ip: []byte{127, 0, 0, 4}, Prefix: 32},
},
},
ExpectedIp: []*geodata.IPRule{
// Will match localhost, localhost-a and localhost-b,
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{127, 0, 0, 2}, Prefix: 32}}}},
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{127, 0, 0, 3}, Prefix: 32}}}},
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{127, 0, 0, 4}, Prefix: 32}}}},
},
},
{
@@ -688,23 +670,21 @@ func TestLocalDomain(t *testing.T) {
},
Port: uint32(port),
},
PrioritizedDomain: []*NameServer_PriorityDomain{
Domain: []*geodata.DomainRule{
// Equivalent of dotless: and domain:local
{Type: DomainMatchingType_Regex, Domain: "^[^.]*$"},
{Type: DomainMatchingType_Subdomain, Domain: "local"},
{Type: DomainMatchingType_Subdomain, Domain: "localdomain"},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Regex, Value: "^[^.]*$"}}},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "local"}}},
{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "localdomain"}}},
},
},
},
StaticHosts: []*Config_HostMapping{
{
Type: DomainMatchingType_Full,
Domain: "hostnamestatic",
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "hostnamestatic"}}},
Ip: [][]byte{{127, 0, 0, 53}},
},
{
Type: DomainMatchingType_Full,
Domain: "hostnamealias",
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "hostnamealias"}}},
ProxiedDomain: "hostname.localdomain",
},
},
@@ -715,7 +695,9 @@ func TestLocalDomain(t *testing.T) {
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
ProxySettings: serial.ToTypedMessage(&freedom.Config{
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
},
},
}
@@ -891,19 +873,15 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
},
Port: uint32(port),
},
PrioritizedDomain: []*NameServer_PriorityDomain{
Domain: []*geodata.DomainRule{
{
Type: DomainMatchingType_Subdomain,
Domain: "google.com",
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "google.com"}},
},
},
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},
{Ip: []byte{8, 8, 4, 4}, Prefix: 32},
},
},
ExpectedIp: []*geodata.IPRule{
// Will only match 8.8.8.8 and 8.8.4.4
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{8, 8, 8, 8}, Prefix: 32}}}},
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{8, 8, 4, 4}, Prefix: 32}}}},
},
},
{
@@ -916,18 +894,14 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
},
Port: uint32(port),
},
PrioritizedDomain: []*NameServer_PriorityDomain{
Domain: []*geodata.DomainRule{
{
Type: DomainMatchingType_Subdomain,
Domain: "google.com",
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "google.com"}},
},
},
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},
},
},
ExpectedIp: []*geodata.IPRule{
// Will match 8.8.8.8 and 8.8.8.7, etc
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{8, 8, 8, 7}, Prefix: 24}}}},
},
},
{
@@ -940,18 +914,14 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
},
Port: uint32(port),
},
PrioritizedDomain: []*NameServer_PriorityDomain{
Domain: []*geodata.DomainRule{
{
Type: DomainMatchingType_Subdomain,
Domain: "api.google.com",
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "api.google.com"}},
},
},
ExpectedGeoip: []*router.GeoIP{
{ // Will only match 8.8.7.7 (api.google.com)
Cidr: []*router.CIDR{
{Ip: []byte{8, 8, 7, 7}, Prefix: 32},
},
},
ExpectedIp: []*geodata.IPRule{
// Will only match 8.8.7.7 (api.google.com)
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{8, 8, 7, 7}, Prefix: 32}}}},
},
},
{
@@ -964,18 +934,14 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
},
Port: uint32(port),
},
PrioritizedDomain: []*NameServer_PriorityDomain{
Domain: []*geodata.DomainRule{
{
Type: DomainMatchingType_Full,
Domain: "v2.api.google.com",
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "v2.api.google.com"}},
},
},
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},
},
},
ExpectedIp: []*geodata.IPRule{
// Will only match 8.8.7.8 (v2.api.google.com)
{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{8, 8, 7, 8}, Prefix: 32}}}},
},
},
},
@@ -986,7 +952,9 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
ProxySettings: serial.ToTypedMessage(&freedom.Config{
FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}},
}),
},
},
}
+23 -15
View File
@@ -3,6 +3,7 @@ package dns
import (
"context"
"encoding/binary"
"math"
"strings"
"time"
@@ -13,10 +14,12 @@ import (
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/core"
dns_feature "github.com/xtls/xray-core/features/dns"
"golang.org/x/net/dns/dnsmessage"
)
// Fqdn normalizes domain make sure it ends with '.'
// case-sensitive
func Fqdn(domain string) string {
if len(domain) > 0 && strings.HasSuffix(domain, ".") {
return domain
@@ -38,19 +41,14 @@ type IPRecord struct {
RawHeader *dnsmessage.Header
}
func (r *IPRecord) getIPs() ([]net.IP, uint32, error) {
func (r *IPRecord) getIPs() ([]net.IP, int32, 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
}
untilExpire := time.Until(r.Expire).Seconds()
ttl := int32(math.Ceil(untilExpire))
if r.RCode != dnsmessage.RCodeSuccess {
return nil, ttl, dns_feature.RCodeError(r.RCode)
}
@@ -129,15 +127,20 @@ func genEDNS0Options(clientIP net.IP, padding int) *dnsmessage.Resource {
return opt
}
func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() uint16, reqOpts *dnsmessage.Resource) []*dnsRequest {
func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() uint16, reqOpts *dnsmessage.Resource) ([]*dnsRequest, error) {
name, err := dnsmessage.NewName(domain)
if err != nil {
return nil, err
}
qA := dnsmessage.Question{
Name: dnsmessage.MustNewName(domain),
Name: name,
Type: dnsmessage.TypeA,
Class: dnsmessage.ClassINET,
}
qAAAA := dnsmessage.Question{
Name: dnsmessage.MustNewName(domain),
Name: name,
Type: dnsmessage.TypeAAAA,
Class: dnsmessage.ClassINET,
}
@@ -177,7 +180,7 @@ func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() ui
})
}
return reqs
return reqs, nil
}
// parseResponse parses DNS answers from the returned payload
@@ -195,9 +198,14 @@ func parseResponse(payload []byte) (*IPRecord, error) {
ipRecord := &IPRecord{
ReqID: h.ID,
RCode: h.RCode,
Expire: now.Add(time.Second * dns_feature.DefaultTTL),
RawHeader: &h,
}
defer func() {
// set to default TTL if no valid TTL is found
if ipRecord.Expire.IsZero() {
ipRecord.Expire = now.Add(time.Second * dns_feature.DefaultTTL)
}
}()
L:
for {
@@ -214,7 +222,7 @@ L:
ttl = 1
}
expire := now.Add(time.Duration(ttl) * time.Second)
if ipRecord.Expire.After(expire) {
if ipRecord.Expire.IsZero() || ipRecord.Expire.After(expire) {
ipRecord.Expire = expire
}
+11 -3
View File
@@ -2,6 +2,7 @@ package dns
import (
"math/rand"
"strings"
"testing"
"time"
@@ -24,7 +25,8 @@ func Test_parseResponse(t *testing.T) {
ans = new(dns.Msg)
ans.Id = 1
ans.Answer = append(ans.Answer,
ans.Answer = append(
ans.Answer,
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")),
common.Must2(dns.NewRR("google.com. IN A 8.8.8.8")),
@@ -34,7 +36,8 @@ func Test_parseResponse(t *testing.T) {
ans = new(dns.Msg)
ans.Id = 2
ans.Answer = append(ans.Answer,
ans.Answer = append(
ans.Answer,
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")),
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
@@ -131,10 +134,15 @@ func Test_buildReqMsgs(t *testing.T) {
IPv6Enable: false,
FakeEnable: false,
}, nil}, 0},
{"name too long", args{strings.Repeat("a", 256), dns_feature.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
}, nil}, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := buildReqMsgs(tt.args.domain, tt.args.option, stubID, tt.args.reqOpts); !(len(got) == tt.want) {
if got, _ := buildReqMsgs(tt.args.domain, tt.args.option, stubID, tt.args.reqOpts); !(len(got) == tt.want) {
t.Errorf("buildReqMsgs() = %v, want %v", got, tt.want)
}
})
+11 -16
View File
@@ -4,7 +4,6 @@ import (
"context"
"math"
"math/big"
gonet "net"
"sync"
"time"
@@ -17,8 +16,8 @@ import (
type Holder struct {
domainToIP cache.Lru
ipRange *gonet.IPNet
mu *sync.Mutex
ipRange *net.IPNet
mu sync.Mutex
config *FakeDnsPool
}
@@ -50,9 +49,7 @@ func (fkdns *Holder) Start() error {
}
func (fkdns *Holder) Close() error {
fkdns.domainToIP = nil
fkdns.ipRange = nil
fkdns.mu = nil
// nothing to do for now, just wait GC
return nil
}
@@ -71,7 +68,7 @@ func NewFakeDNSHolder() (*Holder, error) {
}
func NewFakeDNSHolderConfigOnly(conf *FakeDnsPool) (*Holder, error) {
return &Holder{nil, nil, nil, conf}, nil
return &Holder{config: conf}, nil
}
func (fkdns *Holder) initializeFromConfig() error {
@@ -79,10 +76,10 @@ func (fkdns *Holder) initializeFromConfig() error {
}
func (fkdns *Holder) initialize(ipPoolCidr string, lruSize int) error {
var ipRange *gonet.IPNet
var ipRange *net.IPNet
var err error
if _, ipRange, err = gonet.ParseCIDR(ipPoolCidr); err != nil {
if _, ipRange, err = net.ParseCIDR(ipPoolCidr); err != nil {
return errors.New("Unable to parse CIDR for Fake DNS IP assignment").Base(err).AtError()
}
@@ -93,7 +90,6 @@ func (fkdns *Holder) initialize(ipPoolCidr string, lruSize int) error {
}
fkdns.domainToIP = cache.NewLru(lruSize)
fkdns.ipRange = ipRange
fkdns.mu = new(sync.Mutex)
return nil
}
@@ -104,7 +100,7 @@ func (fkdns *Holder) GetFakeIPForDomain(domain string) []net.Address {
if v, ok := fkdns.domainToIP.Get(domain); ok {
return []net.Address{v.(net.Address)}
}
currentTimeMillis := uint64(time.Now().UnixNano() / 1e6)
currentTimeMillis := uint64(time.Now().UnixMilli())
ones, bits := fkdns.ipRange.Mask.Size()
rooms := bits - ones
if rooms < 64 {
@@ -203,12 +199,11 @@ func (h *HolderMulti) Start() error {
}
func (h *HolderMulti) Close() error {
var errs []error
for _, v := range h.holders {
if err := v.Close(); err != nil {
return errors.New("Cannot close all fake dns pools").Base(err)
}
errs = append(errs, v.Close())
}
return nil
return errors.Combine(errs...)
}
func (h *HolderMulti) createHolderGroups() error {
@@ -223,7 +218,7 @@ func (h *HolderMulti) createHolderGroups() error {
}
func NewFakeDNSHolderMulti(conf *FakeDnsPoolMulti) (*HolderMulti, error) {
holderMulti := &HolderMulti{nil, conf}
holderMulti := &HolderMulti{config: conf}
if err := holderMulti.createHolderGroups(); err != nil {
return nil, err
}
+22 -36
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/dns/fakedns/fakedns.proto
package fakedns
@@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -21,12 +22,11 @@ const (
)
type FakeDnsPool struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
IpPool string `protobuf:"bytes,1,opt,name=ip_pool,json=ipPool,proto3" json:"ip_pool,omitempty"` //CIDR of IP pool used as fake DNS IP
LruSize int64 `protobuf:"varint,2,opt,name=lruSize,proto3" json:"lruSize,omitempty"` //Size of Pool for remembering relationship between domain name and IP address
unknownFields protoimpl.UnknownFields
IpPool string `protobuf:"bytes,1,opt,name=ip_pool,json=ipPool,proto3" json:"ip_pool,omitempty"` //CIDR of IP pool used as fake DNS IP
LruSize int64 `protobuf:"varint,2,opt,name=lruSize,proto3" json:"lruSize,omitempty"` //Size of Pool for remembering relationship between domain name and IP address
sizeCache protoimpl.SizeCache
}
func (x *FakeDnsPool) Reset() {
@@ -74,11 +74,10 @@ func (x *FakeDnsPool) GetLruSize() int64 {
}
type FakeDnsPoolMulti struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Pools []*FakeDnsPool `protobuf:"bytes,1,rep,name=pools,proto3" json:"pools,omitempty"`
unknownFields protoimpl.UnknownFields
Pools []*FakeDnsPool `protobuf:"bytes,1,rep,name=pools,proto3" json:"pools,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *FakeDnsPoolMulti) Reset() {
@@ -120,36 +119,24 @@ func (x *FakeDnsPoolMulti) GetPools() []*FakeDnsPool {
var File_app_dns_fakedns_fakedns_proto protoreflect.FileDescriptor
var file_app_dns_fakedns_fakedns_proto_rawDesc = []byte{
0x0a, 0x1d, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e,
0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x14, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61,
0x6b, 0x65, 0x64, 0x6e, 0x73, 0x22, 0x40, 0x0a, 0x0b, 0x46, 0x61, 0x6b, 0x65, 0x44, 0x6e, 0x73,
0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x70, 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x70, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x18, 0x0a,
0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07,
0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x4b, 0x0a, 0x10, 0x46, 0x61, 0x6b, 0x65, 0x44,
0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x12, 0x37, 0x0a, 0x05, 0x70,
0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e,
0x73, 0x2e, 0x46, 0x61, 0x6b, 0x65, 0x44, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x05, 0x70,
0x6f, 0x6f, 0x6c, 0x73, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73,
0x50, 0x01, 0x5a, 0x29, 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, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x14,
0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x2e, 0x46, 0x61, 0x6b,
0x65, 0x64, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
const file_app_dns_fakedns_fakedns_proto_rawDesc = "" +
"\n" +
"\x1dapp/dns/fakedns/fakedns.proto\x12\x14xray.app.dns.fakedns\"@\n" +
"\vFakeDnsPool\x12\x17\n" +
"\aip_pool\x18\x01 \x01(\tR\x06ipPool\x12\x18\n" +
"\alruSize\x18\x02 \x01(\x03R\alruSize\"K\n" +
"\x10FakeDnsPoolMulti\x127\n" +
"\x05pools\x18\x01 \x03(\v2!.xray.app.dns.fakedns.FakeDnsPoolR\x05poolsB^\n" +
"\x18com.xray.app.dns.fakednsP\x01Z)github.com/xtls/xray-core/app/dns/fakedns\xaa\x02\x14Xray.App.Dns.Fakednsb\x06proto3"
var (
file_app_dns_fakedns_fakedns_proto_rawDescOnce sync.Once
file_app_dns_fakedns_fakedns_proto_rawDescData = file_app_dns_fakedns_fakedns_proto_rawDesc
file_app_dns_fakedns_fakedns_proto_rawDescData []byte
)
func file_app_dns_fakedns_fakedns_proto_rawDescGZIP() []byte {
file_app_dns_fakedns_fakedns_proto_rawDescOnce.Do(func() {
file_app_dns_fakedns_fakedns_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_dns_fakedns_fakedns_proto_rawDescData)
file_app_dns_fakedns_fakedns_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_dns_fakedns_fakedns_proto_rawDesc), len(file_app_dns_fakedns_fakedns_proto_rawDesc)))
})
return file_app_dns_fakedns_fakedns_proto_rawDescData
}
@@ -177,7 +164,7 @@ func file_app_dns_fakedns_fakedns_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_dns_fakedns_fakedns_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_dns_fakedns_fakedns_proto_rawDesc), len(file_app_dns_fakedns_fakedns_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
@@ -188,7 +175,6 @@ func file_app_dns_fakedns_fakedns_proto_init() {
MessageInfos: file_app_dns_fakedns_fakedns_proto_msgTypes,
}.Build()
File_app_dns_fakedns_fakedns_proto = out.File
file_app_dns_fakedns_fakedns_proto_rawDesc = nil
file_app_dns_fakedns_fakedns_proto_goTypes = nil
file_app_dns_fakedns_fakedns_proto_depIdxs = nil
}
+12 -12
View File
@@ -1,7 +1,6 @@
package fakedns
import (
gonet "net"
"strconv"
"testing"
@@ -130,15 +129,16 @@ func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) {
}
func TestFakeDNSMulti(t *testing.T) {
fakeMulti, err := NewFakeDNSHolderMulti(&FakeDnsPoolMulti{
Pools: []*FakeDnsPool{{
IpPool: "240.0.0.0/12",
LruSize: 256,
}, {
IpPool: "fddd:c5b4:ff5f:f4f0::/64",
LruSize: 256,
}},
},
fakeMulti, err := NewFakeDNSHolderMulti(
&FakeDnsPoolMulti{
Pools: []*FakeDnsPool{{
IpPool: "240.0.0.0/12",
LruSize: 256,
}, {
IpPool: "fddd:c5b4:ff5f:f4f0::/64",
LruSize: 256,
}},
},
)
common.Must(err)
@@ -155,7 +155,7 @@ func TestFakeDNSMulti(t *testing.T) {
assert.True(t, inPool)
})
t.Run("ipv6", func(t *testing.T) {
ip, err := gonet.ResolveIPAddr("ip", "fddd:c5b4:ff5f:f4f0::5")
ip, err := net.ResolveIPAddr("ip", "fddd:c5b4:ff5f:f4f0::5")
assert.Nil(t, err)
inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP))
assert.True(t, inPool)
@@ -165,7 +165,7 @@ func TestFakeDNSMulti(t *testing.T) {
assert.False(t, inPool)
})
t.Run("ipv6_inverse", func(t *testing.T) {
ip, err := gonet.ResolveIPAddr("ip", "fcdd:c5b4:ff5f:f4f0::5")
ip, err := net.ResolveIPAddr("ip", "fcdd:c5b4:ff5f:f4f0::5")
assert.Nil(t, err)
inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP))
assert.False(t, inPool)
+37 -26
View File
@@ -3,34 +3,27 @@ package dns
import (
"context"
"strconv"
"strings"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/strmatcher"
"github.com/xtls/xray-core/features/dns"
)
// StaticHosts represents static domain-ip mapping in DNS server.
type StaticHosts struct {
ips [][]net.Address
matchers *strmatcher.MatcherGroup
responses [][]net.Address
matcher geodata.DomainMatcher
}
// NewStaticHosts creates a new StaticHosts instance.
func NewStaticHosts(hosts []*Config_HostMapping) (*StaticHosts, error) {
g := new(strmatcher.MatcherGroup)
sh := &StaticHosts{
ips: make([][]net.Address, len(hosts)+16),
matchers: g,
}
reps := make([][]net.Address, 0, len(hosts))
rules := make([]*geodata.DomainRule, 0, len(hosts))
for _, mapping := range hosts {
matcher, err := toStrMatcher(mapping.Type, mapping.Domain)
if err != nil {
return nil, errors.New("failed to create domain matcher").Base(err)
}
id := g.Add(matcher)
ips := make([]net.Address, 0, len(mapping.Ip)+1)
rep := make([]net.Address, 0, len(mapping.Ip))
switch {
case len(mapping.ProxiedDomain) > 0:
if mapping.ProxiedDomain[0] == '#' {
@@ -38,24 +31,36 @@ func NewStaticHosts(hosts []*Config_HostMapping) (*StaticHosts, error) {
if err != nil {
return nil, err
}
ips = append(ips, dns.RCodeError(rcode))
rep = append(rep, dns.RCodeError(rcode))
} else {
ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
rep = append(rep, net.DomainAddress(mapping.ProxiedDomain))
}
case len(mapping.Ip) > 0:
for _, ip := range mapping.Ip {
addr := net.IPAddress(ip)
if addr == nil {
return nil, errors.New("invalid IP address in static hosts: ", ip).AtWarning()
errors.LogError(context.Background(), "invalid IP address in static hosts: ", ip, ", ignore this ip for rule: ", mapping.Domain)
continue
}
ips = append(ips, addr)
rep = append(rep, addr)
}
}
sh.ips[id] = ips
reps = append(reps, rep)
rules = append(rules, mapping.Domain)
}
return sh, nil
if len(rules) == 0 {
return &StaticHosts{}, nil
}
matcher, err := geodata.DomainReg.BuildDomainMatcher(rules)
if err != nil {
return nil, err
}
return &StaticHosts{
responses: reps,
matcher: matcher,
}, nil
}
func filterIP(ips []net.Address, option dns.IPOption) []net.Address {
@@ -71,16 +76,16 @@ func filterIP(ips []net.Address, option dns.IPOption) []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 {
for _, idx := range h.matcher.Match(domain) {
for _, rep := range h.responses[idx] {
if err, ok := rep.(dns.RCodeError); ok {
if uint16(err) == 0 {
return nil, dns.ErrEmptyResponse
}
return nil, err
}
}
ips = append(ips, h.ips[id]...)
ips = append(ips, h.responses[idx]...)
found = true
}
if !found {
@@ -90,10 +95,13 @@ func (h *StaticHosts) lookupInternal(domain string) ([]net.Address, error) {
}
func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) ([]net.Address, error) {
domain = strings.ToLower(domain)
switch addrs, err := h.lookupInternal(domain); {
case err != nil:
return nil, err
case len(addrs) == 0: // Not recorded in static hosts, return nil
case addrs == nil: // Not recorded in static hosts, return nil
return nil, nil
case len(addrs) == 0: // Domain recorded, but no valid IP returned
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")
@@ -114,5 +122,8 @@ func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) (
// 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, error) {
if h.matcher == nil {
return nil, nil
}
return h.lookup(domain, option, 5)
}
+7 -12
View File
@@ -6,6 +6,7 @@ import (
"github.com/google/go-cmp/cmp"
. "github.com/xtls/xray-core/app/dns"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/features/dns"
)
@@ -13,20 +14,17 @@ import (
func TestStaticHosts(t *testing.T) {
pb := []*Config_HostMapping{
{
Type: DomainMatchingType_Subdomain,
Domain: "lan",
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "lan"}}},
ProxiedDomain: "#3",
},
{
Type: DomainMatchingType_Full,
Domain: "example.com",
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "example.com"}}},
Ip: [][]byte{
{1, 1, 1, 1},
},
},
{
Type: DomainMatchingType_Full,
Domain: "proxy.xray.com",
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "proxy.xray.com"}}},
Ip: [][]byte{
{1, 2, 3, 4},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
@@ -34,20 +32,17 @@ func TestStaticHosts(t *testing.T) {
ProxiedDomain: "another-proxy.xray.com",
},
{
Type: DomainMatchingType_Full,
Domain: "proxy2.xray.com",
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Full, Value: "proxy2.xray.com"}}},
ProxiedDomain: "proxy.xray.com",
},
{
Type: DomainMatchingType_Subdomain,
Domain: "example.cn",
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "example.cn"}}},
Ip: [][]byte{
{2, 2, 2, 2},
},
},
{
Type: DomainMatchingType_Subdomain,
Domain: "baidu.com",
Domain: &geodata.DomainRule{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "baidu.com"}}},
Ip: [][]byte{
{127, 0, 0, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
+41 -92
View File
@@ -6,11 +6,11 @@ import (
"strings"
"time"
"github.com/xtls/xray-core/app/router"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/geodata"
"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/common/utils"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/routing"
@@ -20,6 +20,9 @@ import (
type Server interface {
// Name of the Client.
Name() string
IsDisableCache() bool
// QueryIP sends IP queries to its configured server.
QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error)
}
@@ -28,9 +31,8 @@ type Server interface {
type Client struct {
server Server
skipFallback bool
domains []string
expectedIPs []*router.GeoIPMatcher
unexpectedIPs []*router.GeoIPMatcher
expectedIPs geodata.IPMatcher
unexpectedIPs geodata.IPMatcher
actPrior bool
actUnprior bool
tag string
@@ -38,10 +40,11 @@ type Client struct {
finalQuery bool
ipOption *dns.IPOption
checkSystem bool
policyID uint32
}
// NewServer creates a name server object according to the network destination url.
func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dispatcher, disableCache bool, clientIP net.IP) (Server, error) {
func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dispatcher, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (Server, error) {
if address := dest.Address; address.Family().IsDomain() {
u, err := url.Parse(address.Domain())
if err != nil {
@@ -51,19 +54,19 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
case strings.EqualFold(u.String(), "localhost"):
return NewLocalNameServer(), nil
case strings.EqualFold(u.Scheme, "https"): // DNS-over-HTTPS Remote mode
return NewDoHNameServer(u, dispatcher, false, disableCache, clientIP), nil
return NewDoHNameServer(u, dispatcher, false, disableCache, serveStale, serveExpiredTTL, clientIP), nil
case strings.EqualFold(u.Scheme, "h2c"): // DNS-over-HTTPS h2c Remote mode
return NewDoHNameServer(u, dispatcher, true, disableCache, clientIP), nil
return NewDoHNameServer(u, dispatcher, true, disableCache, serveStale, serveExpiredTTL, clientIP), nil
case strings.EqualFold(u.Scheme, "https+local"): // DNS-over-HTTPS Local mode
return NewDoHNameServer(u, nil, false, disableCache, clientIP), nil
return NewDoHNameServer(u, nil, false, disableCache, serveStale, serveExpiredTTL, clientIP), nil
case strings.EqualFold(u.Scheme, "h2c+local"): // DNS-over-HTTPS h2c Local mode
return NewDoHNameServer(u, nil, true, disableCache, clientIP), nil
return NewDoHNameServer(u, nil, true, disableCache, serveStale, serveExpiredTTL, clientIP), nil
case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode
return NewQUICNameServer(u, disableCache, clientIP)
return NewQUICNameServer(u, disableCache, serveStale, serveExpiredTTL, clientIP)
case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode
return NewTCPNameServer(u, dispatcher, disableCache, clientIP)
return NewTCPNameServer(u, dispatcher, disableCache, serveStale, serveExpiredTTL, clientIP)
case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode
return NewTCPLocalNameServer(u, disableCache, clientIP)
return NewTCPLocalNameServer(u, disableCache, serveStale, serveExpiredTTL, clientIP)
case strings.EqualFold(u.String(), "fakedns"):
var fd dns.FakeDNSEngine
err = core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
@@ -79,7 +82,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, disableCache, clientIP), nil
return NewClassicNameServer(dest, dispatcher, disableCache, serveStale, serveExpiredTTL, clientIP), nil
}
return nil, errors.New("No available name server could be created from ", dest).AtWarning()
}
@@ -89,88 +92,38 @@ func NewClient(
ctx context.Context,
ns *NameServer,
clientIP net.IP,
disableCache bool,
disableCache bool, serveStale bool, serveExpiredTTL uint32,
tag string,
ipOption dns.IPOption,
matcherInfos *[]*DomainMatcherInfo,
updateDomainRule func(strmatcher.Matcher, int, []*DomainMatcherInfo) error,
updateRules func(bool),
) (*Client, error) {
client := &Client{}
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, disableCache, clientIP)
server, err := NewServer(ctx, ns.Address.AsDestination(), dispatcher, disableCache, serveStale, serveExpiredTTL, clientIP)
if err != nil {
return errors.New("failed to create nameserver").Base(err).AtWarning()
}
// Prioritize local domains with specific TLDs or those without any dot for the local DNS
if _, isLocalDNS := server.(*LocalNameServer); isLocalDNS {
ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...)
ns.OriginalRules = append(ns.OriginalRules, localTLDsAndDotlessDomainsRule)
// The following lines is a solution to avoid core panicsrule index out of range when setting `localhost` DNS client in config.
// Because the `localhost` DNS client will append len(localTLDsAndDotlessDomains) rules into matcherInfos to match `geosite:private` default rule.
// But `matcherInfos` has no enough length to add rules, which leads to core panics (rule index out of range).
// To avoid this, the length of `matcherInfos` must be equal to the expected, so manually append it with Golang default zero value first for later modification.
// Related issues:
// https://github.com/v2fly/v2ray-core/issues/529
// https://github.com/v2fly/v2ray-core/issues/719
for i := 0; i < len(localTLDsAndDotlessDomains); i++ {
*matcherInfos = append(*matcherInfos, &DomainMatcherInfo{
clientIdx: uint16(0),
domainRuleIdx: uint16(0),
})
}
}
// Establish domain rules
var rules []string
ruleCurr := 0
ruleIter := 0
for _, domain := range ns.PrioritizedDomain {
domainRule, err := toStrMatcher(domain.Type, domain.Domain)
if err != nil {
return errors.New("failed to create prioritized domain").Base(err).AtWarning()
}
originalRuleIdx := ruleCurr
if ruleCurr < len(ns.OriginalRules) {
rule := ns.OriginalRules[ruleCurr]
if ruleCurr >= len(rules) {
rules = append(rules, rule.Rule)
}
ruleIter++
if ruleIter >= int(rule.Size) {
ruleIter = 0
ruleCurr++
}
} else { // No original rule, generate one according to current domain matcher (majorly for compatibility with tests)
rules = append(rules, domainRule.String())
ruleCurr++
}
err = updateDomainRule(domainRule, originalRuleIdx, *matcherInfos)
if err != nil {
return errors.New("failed to create prioritized domain").Base(err).AtWarning()
}
}
_, isLocalDNS := server.(*LocalNameServer)
updateRules(isLocalDNS)
// Establish expected IPs
var expectedMatchers []*router.GeoIPMatcher
for _, geoip := range ns.ExpectedGeoip {
matcher, err := router.GlobalGeoIPContainer.Add(geoip)
var expectedMatcher geodata.IPMatcher
if len(ns.ExpectedIp) > 0 {
expectedMatcher, err = geodata.IPReg.BuildIPMatcher(ns.ExpectedIp)
if err != nil {
return errors.New("failed to create expected ip matcher").Base(err).AtWarning()
}
expectedMatchers = append(expectedMatchers, matcher)
}
// Establish unexpected IPs
var unexpectedMatchers []*router.GeoIPMatcher
for _, geoip := range ns.UnexpectedGeoip {
matcher, err := router.GlobalGeoIPContainer.Add(geoip)
var unexpectedMatcher geodata.IPMatcher
if len(ns.UnexpectedIp) > 0 {
unexpectedMatcher, err = geodata.IPReg.BuildIPMatcher(ns.UnexpectedIp)
if err != nil {
return errors.New("failed to create unexpected ip matcher").Base(err).AtWarning()
}
unexpectedMatchers = append(unexpectedMatchers, matcher)
}
if len(clientIP) > 0 {
@@ -182,7 +135,7 @@ func NewClient(
}
}
var timeoutMs = 4000 * time.Millisecond
timeoutMs := 4000 * time.Millisecond
if ns.TimeoutMs > 0 {
timeoutMs = time.Duration(ns.TimeoutMs) * time.Millisecond
}
@@ -191,9 +144,8 @@ func NewClient(
client.server = server
client.skipFallback = ns.SkipFallback
client.domains = rules
client.expectedIPs = expectedMatchers
client.unexpectedIPs = unexpectedMatchers
client.expectedIPs = expectedMatcher
client.unexpectedIPs = unexpectedMatcher
client.actPrior = ns.ActPrior
client.actUnprior = ns.ActUnprior
client.tag = tag
@@ -201,6 +153,7 @@ func NewClient(
client.finalQuery = ns.FinalQuery
client.ipOption = &ipOption
client.checkSystem = checkSystem
client.policyID = ns.PolicyID
return nil
})
return client, err
@@ -211,14 +164,10 @@ 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) ([]net.IP, uint32, error) {
if c.checkSystem {
supportIPv4, supportIPv6 := checkSystemNetwork()
supportIPv4, supportIPv6 := utils.CheckRoutes()
option.IPv4Enable = option.IPv4Enable && supportIPv4
option.IPv6Enable = option.IPv6Enable && supportIPv6
} else {
@@ -243,32 +192,32 @@ func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption
return nil, 0, dns.ErrEmptyResponse
}
if len(c.expectedIPs) > 0 && !c.actPrior {
ips = router.MatchIPs(c.expectedIPs, ips, false)
if c.expectedIPs != nil && !c.actPrior {
ips, _ = c.expectedIPs.FilterIPs(ips)
errors.LogDebug(context.Background(), "domain ", domain, " expectedIPs ", ips, " matched at server ", c.Name())
if len(ips) == 0 {
return nil, 0, dns.ErrEmptyResponse
}
}
if len(c.unexpectedIPs) > 0 && !c.actUnprior {
ips = router.MatchIPs(c.unexpectedIPs, ips, true)
if c.unexpectedIPs != nil && !c.actUnprior {
_, ips = c.unexpectedIPs.FilterIPs(ips)
errors.LogDebug(context.Background(), "domain ", domain, " unexpectedIPs ", ips, " matched at server ", c.Name())
if len(ips) == 0 {
return nil, 0, dns.ErrEmptyResponse
}
}
if len(c.expectedIPs) > 0 && c.actPrior {
ipsNew := router.MatchIPs(c.expectedIPs, ips, false)
if c.expectedIPs != nil && c.actPrior {
ipsNew, _ := c.expectedIPs.FilterIPs(ips)
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 c.unexpectedIPs != nil && c.actUnprior {
_, ipsNew := c.unexpectedIPs.FilterIPs(ips)
if len(ipsNew) > 0 {
ips = ipsNew
errors.LogDebug(context.Background(), "domain ", domain, " unpriorIPs ", ips, " matched at server ", c.Name())
+173
View File
@@ -0,0 +1,173 @@
package dns
import (
"context"
go_errors "errors"
"time"
"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/signal/pubsub"
"github.com/xtls/xray-core/features/dns"
)
type CachedNameserver interface {
getCacheController() *CacheController
sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns.IPOption)
}
// queryIP is called from dns.Server->queryIPTimeout
func queryIP(ctx context.Context, s CachedNameserver, domain string, option dns.IPOption) ([]net.IP, uint32, error) {
fqdn := Fqdn(domain)
cache := s.getCacheController()
if !cache.disableCache {
if rec := cache.findRecords(fqdn); rec != nil {
ips, ttl, err := merge(option, rec.A, rec.AAAA)
if !go_errors.Is(err, errRecordNotFound) {
if ttl > 0 {
errors.LogDebugInner(ctx, err, cache.name, " cache HIT ", fqdn, " -> ", ips)
log.Record(&log.DNSLog{Server: cache.name, Domain: fqdn, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, uint32(ttl), err
}
if cache.serveStale && (cache.serveExpiredTTL == 0 || cache.serveExpiredTTL < ttl) {
errors.LogDebugInner(ctx, err, cache.name, " cache OPTIMISTE ", fqdn, " -> ", ips)
log.Record(&log.DNSLog{Server: cache.name, Domain: fqdn, Result: ips, Status: log.DNSCacheOptimiste, Elapsed: 0, Error: err})
go pull(ctx, s, fqdn, option)
return ips, 1, err
}
}
}
} else {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", fqdn, " at ", cache.name)
}
return fetch(ctx, s, fqdn, option)
}
func pull(ctx context.Context, s CachedNameserver, fqdn string, option dns.IPOption) {
nctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 8*time.Second)
defer cancel()
fetch(nctx, s, fqdn, option)
}
func fetch(ctx context.Context, s CachedNameserver, fqdn string, option dns.IPOption) ([]net.IP, uint32, error) {
key := fqdn
switch {
case option.IPv4Enable && option.IPv6Enable:
key = key + "46"
case option.IPv4Enable:
key = key + "4"
case option.IPv6Enable:
key = key + "6"
}
v, _, _ := s.getCacheController().requestGroup.Do(key, func() (any, error) {
return doFetch(ctx, s, fqdn, option), nil
})
ret := v.(result)
return ret.ips, ret.ttl, ret.error
}
type result struct {
ips []net.IP
ttl uint32
error
}
func doFetch(ctx context.Context, s CachedNameserver, fqdn string, option dns.IPOption) result {
sub4, sub6 := s.getCacheController().registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
noResponseErrCh := make(chan error, 2)
onEvent := func(sub *pubsub.Subscriber) (*IPRecord, error) {
if sub == nil {
return nil, nil
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case err := <-noResponseErrCh:
return nil, err
case msg := <-sub.Wait():
sub.Close()
return msg.(*IPRecord), nil // should panic
}
}
start := time.Now()
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
rec4, err4 := onEvent(sub4)
rec6, err6 := onEvent(sub6)
var errs []error
if err4 != nil {
errs = append(errs, err4)
}
if err6 != nil {
errs = append(errs, err6)
}
ips, ttl, err := merge(option, rec4, rec6, errs...)
var rTTL uint32
if ttl > 0 {
rTTL = uint32(ttl)
} else if ttl == 0 && go_errors.Is(err, errRecordNotFound) {
rTTL = 0
} else { // edge case: where a fast rep's ttl expires during the rtt of a slower, parallel query
rTTL = 1
}
log.Record(&log.DNSLog{Server: s.getCacheController().name, Domain: fqdn, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return result{ips, rTTL, err}
}
func merge(option dns.IPOption, rec4 *IPRecord, rec6 *IPRecord, errs ...error) ([]net.IP, int32, error) {
var allIPs []net.IP
var rTTL int32 = dns.DefaultTTL
mergeReq := option.IPv4Enable && option.IPv6Enable
if option.IPv4Enable {
ips, ttl, err := rec4.getIPs() // it's safe
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 := rec6.getIPs() // it's safe
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 len(errs) == 2 && go_errors.Is(errs[0], errs[1]) {
return nil, rTTL, errs[0]
}
return nil, rTTL, errors.Combine(errs...)
}
+57 -64
View File
@@ -4,12 +4,10 @@ import (
"bytes"
"context"
"crypto/tls"
go_errors "errors"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
utls "github.com/refraction-networking/utls"
@@ -21,6 +19,7 @@ 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/utils"
dns_feature "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport/internet"
@@ -38,7 +37,7 @@ type DoHNameServer struct {
}
// 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 {
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, h2c bool, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) *DoHNameServer {
url.Scheme = "https"
mode := "DOH"
if dispatcher == nil {
@@ -46,7 +45,7 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, h2c bool, dis
}
errors.LogInfo(context.Background(), "DNS: created ", mode, " client for ", url.String(), ", with h2c ", h2c)
s := &DoHNameServer{
cacheController: NewCacheController(mode+"//"+url.Host, disableCache),
cacheController: NewCacheController(mode+"//"+url.Host, disableCache, serveStale, serveExpiredTTL),
dohURL: url.String(),
clientIP: clientIP,
}
@@ -117,22 +116,53 @@ func (s *DoHNameServer) Name() string {
return s.cacheController.name
}
// IsDisableCache implements Server.
func (s *DoHNameServer) IsDisableCache() bool {
return s.cacheController.disableCache
}
func (s *DoHNameServer) newReqID() uint16 {
return 0
}
func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying: ", domain)
// getCacheController implements CachedNameserver.
func (s *DoHNameServer) getCacheController() *CacheController {
return s.cacheController
}
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())
// sendQuery implements CachedNameserver.
func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying: ", fqdn)
if s.Name()+"." == "DOH//"+fqdn {
errors.LogError(ctx, s.Name(), " tries to resolve itself! Use IP or set \"hosts\" instead")
if noResponseErrCh != nil {
err := errors.New("tries to resolve itself!", s.Name())
if option.IPv4Enable {
noResponseErrCh <- err
}
if option.IPv6Enable {
noResponseErrCh <- err
}
}
return
}
// 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))))
reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, int(crypto.RandBetween(100, 300))))
if err != nil {
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
if noResponseErrCh != nil {
if option.IPv4Enable {
noResponseErrCh <- err
}
if option.IPv6Enable {
noResponseErrCh <- err
}
}
return
}
var deadline time.Time
if d, ok := ctx.Deadline(); ok {
@@ -166,23 +196,29 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
b, err := dns.PackMessage(r.msg)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query for ", domain)
noResponseErrCh <- err
errors.LogErrorInner(ctx, err, "failed to pack dns query for ", fqdn)
if noResponseErrCh != nil {
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
errors.LogErrorInner(ctx, err, "failed to retrieve response for ", fqdn)
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
rec, err := parseResponse(resp)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to handle DOH response for ", domain)
noResponseErrCh <- err
errors.LogErrorInner(ctx, err, "failed to handle DOH response for ", fqdn)
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
s.cacheController.updateIP(r, rec)
s.cacheController.updateRecord(r, rec)
}(req)
}
}
@@ -196,8 +232,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))))
utils.TryDefaultHeadersWith(req.Header, "fetch")
req.Header.Set("X-Padding", utils.H2Base62Pad(crypto.RandBetween(100, 1000)))
hc := s.httpClient
@@ -216,49 +252,6 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
}
// QueryIP implements Server.
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { // nolint: dupl
fqdn := Fqdn(domain)
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
if s.cacheController.disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
} else {
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
}
}
noResponseErrCh := make(chan error, 2)
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
start := time.Now()
if sub4 != nil {
select {
case <-ctx.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 (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
return queryIP(ctx, s, domain, option)
}
+4 -4
View File
@@ -17,7 +17,7 @@ func TestDOHNameServer(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err)
s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: true,
@@ -34,7 +34,7 @@ func TestDOHNameServerWithCache(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err)
s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: true,
@@ -62,7 +62,7 @@ func TestDOHNameServerWithIPv4Override(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err)
s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: true,
@@ -85,7 +85,7 @@ func TestDOHNameServerWithIPv6Override(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err)
s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: false,
+5
View File
@@ -20,6 +20,11 @@ func (FakeDNSServer) Name() string {
return "FakeDNS"
}
// IsDisableCache implements Server.
func (s *FakeDNSServer) IsDisableCache() bool {
return true
}
func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, opt dns.IPOption) ([]net.IP, uint32, error) {
if f.fakeDNSEngine == nil {
return nil, 0, errors.New("Unable to locate a fake DNS Engine").AtError()
+5 -1
View File
@@ -18,7 +18,6 @@ type LocalNameServer struct {
// QueryIP implements Server.
func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, option dns.IPOption) (ips []net.IP, ttl uint32, err error) {
start := time.Now()
ips, ttl, err = s.client.LookupIP(domain, option)
@@ -35,6 +34,11 @@ func (s *LocalNameServer) Name() string {
return "localhost"
}
// IsDisableCache implements Server.
func (s *LocalNameServer) IsDisableCache() bool {
return true
}
// NewLocalNameServer creates localdns server object for directly lookup in system DNS.
func NewLocalNameServer() *LocalNameServer {
errors.LogInfo(context.Background(), "DNS: created localhost client")
+60 -66
View File
@@ -4,12 +4,11 @@ import (
"bytes"
"context"
"encoding/binary"
go_errors "errors"
"net/url"
"sync"
"time"
"github.com/quic-go/quic-go"
"github.com/apernet/quic-go"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log"
@@ -37,9 +36,7 @@ type QUICNameServer struct {
}
// NewQUICNameServer creates DNS-over-QUIC client object for local resolving
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())
func NewQUICNameServer(url *url.URL, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (*QUICNameServer, error) {
var err error
port := net.Port(853)
if url.Port() != "" {
@@ -51,27 +48,49 @@ func NewQUICNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*QUICN
dest := net.UDPDestination(net.ParseAddress(url.Hostname()), port)
s := &QUICNameServer{
cacheController: NewCacheController(url.String(), disableCache),
cacheController: NewCacheController(url.String(), disableCache, serveStale, serveExpiredTTL),
destination: &dest,
clientIP: clientIP,
}
errors.LogInfo(context.Background(), "DNS: created Local DNS-over-QUIC client for ", url.String())
return s, nil
}
// Name returns client name
// Name implements Server.
func (s *QUICNameServer) Name() string {
return s.cacheController.name
}
// IsDisableCache implements Server.
func (s *QUICNameServer) IsDisableCache() bool {
return s.cacheController.disableCache
}
func (s *QUICNameServer) newReqID() uint16 {
return 0
}
func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying: ", domain)
// getCacheController implements CachedNameServer.
func (s *QUICNameServer) getCacheController() *CacheController { return s.cacheController }
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
// sendQuery implements CachedNameServer.
func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying: ", fqdn)
reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
if err != nil {
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
if noResponseErrCh != nil {
if option.IPv4Enable {
noResponseErrCh <- err
}
if option.IPv6Enable {
noResponseErrCh <- err
}
}
return
}
var deadline time.Time
if d, ok := ctx.Deadline(); ok {
@@ -103,7 +122,9 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
b, err := dns.PackMessage(r.msg)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query")
noResponseErrCh <- err
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
@@ -111,13 +132,17 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len()))
if err != nil {
errors.LogErrorInner(ctx, err, "binary write failed")
noResponseErrCh <- err
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
_, err = dnsReqBuf.Write(b.Bytes())
if err != nil {
errors.LogErrorInner(ctx, err, "buffer write failed")
noResponseErrCh <- err
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
b.Release()
@@ -125,14 +150,18 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
conn, err := s.openStream(dnsCtx)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to open quic connection")
noResponseErrCh <- err
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
_, err = conn.Write(dnsReqBuf.Bytes())
if err != nil {
errors.LogErrorInner(ctx, err, "failed to send query")
noResponseErrCh <- err
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
@@ -143,81 +172,46 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
n, err := respBuf.ReadFullFrom(conn, 2)
if err != nil && n == 0 {
errors.LogErrorInner(ctx, err, "failed to read response length")
noResponseErrCh <- err
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
var length int16
var length uint16
err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to parse response length")
noResponseErrCh <- err
if noResponseErrCh != nil {
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
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
rec, err := parseResponse(respBuf.Bytes())
if err != nil {
errors.LogErrorInner(ctx, err, "failed to handle response")
noResponseErrCh <- err
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
s.cacheController.updateIP(r, rec)
s.cacheController.updateRecord(r, rec)
}(req)
}
}
// QueryIP is called from dns.Server->queryIPTimeout
// QueryIP implements Server.
func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
fqdn := Fqdn(domain)
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
if s.cacheController.disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
} else {
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
}
}
noResponseErrCh := make(chan error, 2)
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
start := time.Now()
if sub4 != nil {
select {
case <-ctx.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
return queryIP(ctx, s, domain, option)
}
func isActive(s *quic.Conn) bool {
+3 -3
View File
@@ -16,7 +16,7 @@ import (
func TestQUICNameServer(t *testing.T) {
url, err := url.Parse("quic://dns.adguard-dns.com")
common.Must(err)
s, err := NewQUICNameServer(url, false, net.IP(nil))
s, err := NewQUICNameServer(url, false, false, 0, net.IP(nil))
common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
@@ -43,7 +43,7 @@ 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, false, net.IP(nil))
s, err := NewQUICNameServer(url, false, false, 0, net.IP(nil))
common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
@@ -66,7 +66,7 @@ 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, false, net.IP(nil))
s, err := NewQUICNameServer(url, false, false, 0, net.IP(nil))
common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
+64 -66
View File
@@ -4,14 +4,12 @@ import (
"bytes"
"context"
"encoding/binary"
go_errors "errors"
"net/url"
"sync/atomic"
"time"
"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/net/cnc"
"github.com/xtls/xray-core/common/protocol/dns"
@@ -34,10 +32,10 @@ type TCPNameServer struct {
func NewTCPNameServer(
url *url.URL,
dispatcher routing.Dispatcher,
disableCache bool,
disableCache bool, serveStale bool, serveExpiredTTL uint32,
clientIP net.IP,
) (*TCPNameServer, error) {
s, err := baseTCPNameServer(url, "TCP", disableCache, clientIP)
s, err := baseTCPNameServer(url, "TCP", disableCache, serveStale, serveExpiredTTL, clientIP)
if err != nil {
return nil, err
}
@@ -54,12 +52,13 @@ func NewTCPNameServer(
), nil
}
errors.LogInfo(context.Background(), "DNS: created TCP client initialized for ", url.String())
return s, nil
}
// NewTCPLocalNameServer creates DNS over TCP client object for local resolving
func NewTCPLocalNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*TCPNameServer, error) {
s, err := baseTCPNameServer(url, "TCPL", disableCache, clientIP)
func NewTCPLocalNameServer(url *url.URL, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (*TCPNameServer, error) {
s, err := baseTCPNameServer(url, "TCPL", disableCache, serveStale, serveExpiredTTL, clientIP)
if err != nil {
return nil, err
}
@@ -68,10 +67,11 @@ func NewTCPLocalNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*T
return internet.DialSystem(ctx, *s.destination, nil)
}
errors.LogInfo(context.Background(), "DNS: created Local TCP client initialized for ", url.String())
return s, nil
}
func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, clientIP net.IP) (*TCPNameServer, error) {
func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (*TCPNameServer, error) {
port := net.Port(53)
if url.Port() != "" {
var err error
@@ -82,7 +82,7 @@ func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, clientIP
dest := net.TCPDestination(net.ParseAddress(url.Hostname()), port)
s := &TCPNameServer{
cacheController: NewCacheController(prefix+"//"+dest.NetAddr(), disableCache),
cacheController: NewCacheController(prefix+"//"+dest.NetAddr(), disableCache, serveStale, serveExpiredTTL),
destination: &dest,
clientIP: clientIP,
}
@@ -95,14 +95,37 @@ func (s *TCPNameServer) Name() string {
return s.cacheController.name
}
// IsDisableCache implements Server.
func (s *TCPNameServer) IsDisableCache() bool {
return s.cacheController.disableCache
}
func (s *TCPNameServer) newReqID() uint16 {
return uint16(atomic.AddUint32(&s.reqID, 1))
}
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)
// getCacheController implements CachedNameserver.
func (s *TCPNameServer) getCacheController() *CacheController {
return s.cacheController
}
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
// sendQuery implements CachedNameserver.
func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn)
reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
if err != nil {
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
if noResponseErrCh != nil {
if option.IPv4Enable {
noResponseErrCh <- err
}
if option.IPv6Enable {
noResponseErrCh <- err
}
}
return
}
var deadline time.Time
if d, ok := ctx.Deadline(); ok {
@@ -131,14 +154,18 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
b, err := dns.PackMessage(r.msg)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query")
noResponseErrCh <- err
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
conn, err := s.dial(dnsCtx)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to dial namesever")
noResponseErrCh <- err
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
defer conn.Close()
@@ -146,13 +173,17 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len()))
if err != nil {
errors.LogErrorInner(ctx, err, "binary write failed")
noResponseErrCh <- err
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
_, err = dnsReqBuf.Write(b.Bytes())
if err != nil {
errors.LogErrorInner(ctx, err, "buffer write failed")
noResponseErrCh <- err
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
b.Release()
@@ -160,7 +191,9 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
_, err = conn.Write(dnsReqBuf.Bytes())
if err != nil {
errors.LogErrorInner(ctx, err, "failed to send query")
noResponseErrCh <- err
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
dnsReqBuf.Release()
@@ -170,80 +203,45 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
n, err := respBuf.ReadFullFrom(conn, 2)
if err != nil && n == 0 {
errors.LogErrorInner(ctx, err, "failed to read response length")
noResponseErrCh <- err
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
var length int16
var length uint16
err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to parse response length")
noResponseErrCh <- err
if noResponseErrCh != nil {
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
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
rec, err := parseResponse(respBuf.Bytes())
if err != nil {
errors.LogErrorInner(ctx, err, "failed to parse DNS over TCP response")
noResponseErrCh <- err
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
s.cacheController.updateIP(r, rec)
s.cacheController.updateRecord(r, rec)
}(req)
}
}
// QueryIP implements Server.
func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
fqdn := Fqdn(domain)
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
if s.cacheController.disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
} else {
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
}
}
noResponseErrCh := make(chan error, 2)
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
start := time.Now()
if sub4 != nil {
select {
case <-ctx.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
return queryIP(ctx, s, domain, option)
}
+4 -4
View File
@@ -16,7 +16,7 @@ import (
func TestTCPLocalNameServer(t *testing.T) {
url, err := url.Parse("tcp+local://8.8.8.8")
common.Must(err)
s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil))
common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
@@ -33,7 +33,7 @@ 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, false, net.IP(nil))
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil))
common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
@@ -61,7 +61,7 @@ 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, false, net.IP(nil))
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil))
common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
@@ -85,7 +85,7 @@ 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, false, net.IP(nil))
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil))
common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
+41 -59
View File
@@ -2,7 +2,6 @@ package dns
import (
"context"
go_errors "errors"
"strings"
"sync"
"sync/atomic"
@@ -10,7 +9,6 @@ import (
"github.com/xtls/xray-core/common"
"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"
udp_proto "github.com/xtls/xray-core/common/protocol/udp"
@@ -39,14 +37,14 @@ type udpDnsRequest struct {
}
// NewClassicNameServer creates udp server object for remote resolving.
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, disableCache bool, clientIP net.IP) *ClassicNameServer {
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) *ClassicNameServer {
// default to 53 if unspecific
if address.Port == 0 {
address.Port = net.Port(53)
}
s := &ClassicNameServer{
cacheController: NewCacheController(strings.ToUpper(address.String()), disableCache),
cacheController: NewCacheController(strings.ToUpper(address.String()), disableCache, serveStale, serveExpiredTTL),
address: &address,
requests: make(map[uint16]*udpDnsRequest),
clientIP: clientIP,
@@ -56,6 +54,7 @@ func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher
Execute: s.RequestsCleanup,
}
s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse)
errors.LogInfo(context.Background(), "DNS: created UDP client initialized for ", address.NetAddr())
return s
}
@@ -65,6 +64,11 @@ func (s *ClassicNameServer) Name() string {
return s.cacheController.name
}
// IsDisableCache implements Server.
func (s *ClassicNameServer) IsDisableCache() bool {
return s.cacheController.disableCache
}
// RequestsCleanup clears expired items from cache
func (s *ClassicNameServer) RequestsCleanup() error {
now := time.Now()
@@ -94,7 +98,7 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
ipRec, err := parseResponse(payload.Bytes())
payload.Release()
if err != nil {
errors.LogError(ctx, s.Name(), " fail to parse responded DNS udp")
errors.LogErrorInner(ctx, err, s.Name(), " fail to parse responded DNS udp")
return
}
@@ -107,7 +111,7 @@ 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.LogErrorInner(ctx, err, s.Name(), " cannot find the pending request")
return
}
@@ -127,14 +131,12 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
newReq.msg = &newMsg
s.addPendingRequest(&newReq)
b, _ := dns.PackMessage(newReq.msg)
copyDest := net.UDPDestination(s.address.Address, s.address.Port)
b.UDP = &copyDest
s.udpServer.Dispatch(toDnsContext(newReq.ctx, s.address.String()), *s.address, b)
return
}
}
s.cacheController.updateIP(&req.dnsRequest, ipRec)
s.cacheController.updateRecord(&req.dnsRequest, ipRec)
}
func (s *ClassicNameServer) newReqID() uint16 {
@@ -150,10 +152,28 @@ func (s *ClassicNameServer) addPendingRequest(req *udpDnsRequest) {
common.Must(s.requestsCleanup.Start())
}
func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, domain string, option dns_feature.IPOption) {
errors.LogDebug(ctx, s.Name(), " querying DNS for: ", domain)
// getCacheController implements CachedNameserver.
func (s *ClassicNameServer) getCacheController() *CacheController {
return s.cacheController
}
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
// sendQuery implements CachedNameserver.
func (s *ClassicNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn)
reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
if err != nil {
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
if noResponseErrCh != nil {
if option.IPv4Enable {
noResponseErrCh <- err
}
if option.IPv6Enable {
noResponseErrCh <- err
}
}
return
}
for _, req := range reqs {
udpReq := &udpDnsRequest{
@@ -161,57 +181,19 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, domai
ctx: ctx,
}
s.addPendingRequest(udpReq)
b, _ := dns.PackMessage(req.msg)
copyDest := net.UDPDestination(s.address.Address, s.address.Port)
b.UDP = &copyDest
b, err := dns.PackMessage(req.msg)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query")
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
s.udpServer.Dispatch(toDnsContext(ctx, s.address.String()), *s.address, b)
}
}
// QueryIP implements Server.
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
fqdn := Fqdn(domain)
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
if s.cacheController.disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
} else {
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
}
}
noResponseErrCh := make(chan error, 2)
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
start := time.Now()
if sub4 != nil {
select {
case <-ctx.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
return queryIP(ctx, s, domain, option)
}
+198
View File
@@ -0,0 +1,198 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/geodata/config.proto
package geodata
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
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 Asset struct {
state protoimpl.MessageState `protogen:"open.v1"`
Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
File string `protobuf:"bytes,2,opt,name=file,proto3" json:"file,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Asset) Reset() {
*x = Asset{}
mi := &file_app_geodata_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Asset) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Asset) ProtoMessage() {}
func (x *Asset) ProtoReflect() protoreflect.Message {
mi := &file_app_geodata_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 Asset.ProtoReflect.Descriptor instead.
func (*Asset) Descriptor() ([]byte, []int) {
return file_app_geodata_config_proto_rawDescGZIP(), []int{0}
}
func (x *Asset) GetUrl() string {
if x != nil {
return x.Url
}
return ""
}
func (x *Asset) GetFile() string {
if x != nil {
return x.File
}
return ""
}
type Config struct {
state protoimpl.MessageState `protogen:"open.v1"`
Cron string `protobuf:"bytes,1,opt,name=cron,proto3" json:"cron,omitempty"`
Outbound string `protobuf:"bytes,2,opt,name=outbound,proto3" json:"outbound,omitempty"`
Assets []*Asset `protobuf:"bytes,3,rep,name=assets,proto3" json:"assets,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
*x = Config{}
mi := &file_app_geodata_config_proto_msgTypes[1]
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_geodata_config_proto_msgTypes[1]
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_geodata_config_proto_rawDescGZIP(), []int{1}
}
func (x *Config) GetCron() string {
if x != nil {
return x.Cron
}
return ""
}
func (x *Config) GetOutbound() string {
if x != nil {
return x.Outbound
}
return ""
}
func (x *Config) GetAssets() []*Asset {
if x != nil {
return x.Assets
}
return nil
}
var File_app_geodata_config_proto protoreflect.FileDescriptor
const file_app_geodata_config_proto_rawDesc = "" +
"\n" +
"\x18app/geodata/config.proto\x12\x10xray.app.geodata\"-\n" +
"\x05Asset\x12\x10\n" +
"\x03url\x18\x01 \x01(\tR\x03url\x12\x12\n" +
"\x04file\x18\x02 \x01(\tR\x04file\"i\n" +
"\x06Config\x12\x12\n" +
"\x04cron\x18\x01 \x01(\tR\x04cron\x12\x1a\n" +
"\boutbound\x18\x02 \x01(\tR\boutbound\x12/\n" +
"\x06assets\x18\x03 \x03(\v2\x17.xray.app.geodata.AssetR\x06assetsBR\n" +
"\x14com.xray.app.geodataP\x01Z%github.com/xtls/xray-core/app/geodata\xaa\x02\x10Xray.App.Geodatab\x06proto3"
var (
file_app_geodata_config_proto_rawDescOnce sync.Once
file_app_geodata_config_proto_rawDescData []byte
)
func file_app_geodata_config_proto_rawDescGZIP() []byte {
file_app_geodata_config_proto_rawDescOnce.Do(func() {
file_app_geodata_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_geodata_config_proto_rawDesc), len(file_app_geodata_config_proto_rawDesc)))
})
return file_app_geodata_config_proto_rawDescData
}
var file_app_geodata_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_app_geodata_config_proto_goTypes = []any{
(*Asset)(nil), // 0: xray.app.geodata.Asset
(*Config)(nil), // 1: xray.app.geodata.Config
}
var file_app_geodata_config_proto_depIdxs = []int32{
0, // 0: xray.app.geodata.Config.assets:type_name -> xray.app.geodata.Asset
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_app_geodata_config_proto_init() }
func file_app_geodata_config_proto_init() {
if File_app_geodata_config_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_geodata_config_proto_rawDesc), len(file_app_geodata_config_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_app_geodata_config_proto_goTypes,
DependencyIndexes: file_app_geodata_config_proto_depIdxs,
MessageInfos: file_app_geodata_config_proto_msgTypes,
}.Build()
File_app_geodata_config_proto = out.File
file_app_geodata_config_proto_goTypes = nil
file_app_geodata_config_proto_depIdxs = nil
}
+21
View File
@@ -0,0 +1,21 @@
syntax = "proto3";
package xray.app.geodata;
option csharp_namespace = "Xray.App.Geodata";
option go_package = "github.com/xtls/xray-core/app/geodata";
option java_package = "com.xray.app.geodata";
option java_multiple_files = true;
message Asset {
string url = 1;
string file = 2;
}
message Config {
string cron = 1;
string outbound = 2;
repeated Asset assets = 3;
}
+346
View File
@@ -0,0 +1,346 @@
package geodata
import (
"context"
"crypto/tls"
go_errors "errors"
"io"
"net/http"
"os"
"path/filepath"
"time"
utls "github.com/refraction-networking/utls"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/platform/filesystem"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/common/utils"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport/internet/tagged"
"golang.org/x/net/http2"
)
const idleTimeout = 30 * time.Second
type stage struct {
target string
temp string
}
type downloader struct {
ctx context.Context
httpClient *http.Client
httpsClient *http.Client
}
type idleConn struct {
net.Conn
}
func (c *idleConn) Read(b []byte) (int, error) {
t := time.AfterFunc(idleTimeout, func() {
_ = c.Close()
})
n, err := c.Conn.Read(b)
if !t.Stop() {
_ = c.Close()
return n, errors.New("connection idle timeout")
}
return n, err
}
func (c *idleConn) Write(b []byte) (int, error) {
return c.Conn.Write(b)
}
func newDownloader(ctx context.Context, dispatcher routing.Dispatcher, outbound string) *downloader {
return &downloader{
ctx: ctx,
httpClient: newClient(ctx, dispatcher, outbound, false),
httpsClient: newClient(ctx, dispatcher, outbound, true),
}
}
func newClient(baseCtx context.Context, dispatcher routing.Dispatcher, outbound string, isHTTPS bool) *http.Client {
dial := func(ctx context.Context, network, address string) (net.Conn, error) {
var conn net.Conn
err := task.Run(ctx, func() error {
if tagged.Dialer == nil {
return errors.New("tagged dialer is not initialized")
}
dest, err := net.ParseDestination(network + ":" + address)
if err != nil {
return errors.New("cannot understand address").Base(err)
}
c, err := tagged.Dialer(baseCtx, dispatcher, dest, outbound)
if err != nil {
return errors.New("cannot dial remote address ", dest).Base(err)
}
conn = c
return nil
})
if err != nil {
return nil, errors.New("cannot finish connection").Base(err)
}
return &idleConn{
Conn: conn,
}, nil
}
if isHTTPS {
return &http.Client{
Transport: &http2.Transport{
DialTLSContext: func(ctx context.Context, network string, address string, cfg *tls.Config) (net.Conn, error) {
conn, err := dial(ctx, network, address)
if err != nil {
return nil, err
}
host, _, _ := net.SplitHostPort(address)
tlsConn := utls.UClient(conn, &utls.Config{ServerName: host}, utls.HelloChrome_Auto)
handshakeCtx, cancel := context.WithTimeout(ctx, idleTimeout)
defer cancel()
if err := tlsConn.HandshakeContext(handshakeCtx); err != nil {
conn.Close()
return nil, err
}
return tlsConn, nil
},
},
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if req.URL.Scheme != "https" {
return errors.New("redirected to non-https URL: ", req.URL.String())
}
if len(via) >= 10 {
return errors.New("stopped after 10 redirects")
}
return nil
},
}
} else {
return &http.Client{
Transport: &http.Transport{
Proxy: nil,
DisableKeepAlives: true,
DialContext: dial,
ResponseHeaderTimeout: idleTimeout,
},
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if req.URL.Scheme != "https" {
return errors.New("redirected to non-https URL: ", req.URL.String())
}
if len(via) >= 10 {
return errors.New("stopped after 10 redirects")
}
return nil
},
}
}
}
func (d *downloader) download(assets []*Asset) ([]stage, error) {
staged := make([]stage, 0, len(assets))
for _, asset := range assets {
stage, err := d.downloadOne(asset)
if err != nil {
clean(staged)
return nil, err
}
staged = append(staged, stage)
}
return staged, nil
}
func (d *downloader) downloadOne(asset *Asset) (stage, error) {
target, err := filesystem.ResolveAsset(asset.File)
if err != nil {
return stage{}, err
}
errors.LogInfo(d.ctx, "downloading geodata asset from ", asset.Url, " to ", target)
temp, err := tempFile(target, ".tmp")
if err != nil {
return stage{}, err
}
tempName := temp.Name()
keepTemp := false
defer func() {
if !keepTemp {
os.Remove(tempName)
}
}()
if err := d.fetch(asset.Url, temp); err != nil {
temp.Close()
return stage{}, err
}
if err := temp.Chmod(0o644); err != nil {
temp.Close()
return stage{}, err
}
if err := temp.Close(); err != nil {
return stage{}, err
}
keepTemp = true
return stage{
target: target,
temp: tempName,
}, nil
}
func (d *downloader) fetch(rawURL string, writer io.Writer) error {
req, err := http.NewRequestWithContext(d.ctx, http.MethodGet, rawURL, nil)
if err != nil {
return err
}
utils.TryDefaultHeadersWith(req.Header, "nav")
var client *http.Client
if req.URL.Scheme == "https" {
client = d.httpsClient
} else {
client = d.httpClient
}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
io.Copy(io.Discard, resp.Body)
return errors.New("unexpected status code: ", resp.StatusCode)
}
n, err := io.Copy(writer, resp.Body)
if err != nil {
return err
}
if n == 0 {
return errors.New("empty response body")
}
return nil
}
func clean(assets []stage) {
for _, asset := range assets {
if asset.temp != "" {
os.Remove(asset.temp)
}
}
}
type tx struct {
swaps []swap
}
type swap struct {
target string
backup string
hadOriginal bool
}
func swapAll(assets []stage) (*tx, error) {
t := &tx{}
for _, asset := range assets {
s, err := swapOne(asset)
if err != nil {
return nil, errors.Combine(err, t.rollback())
}
t.swaps = append(t.swaps, s)
}
return t, nil
}
func swapOne(asset stage) (swap, error) {
backup, err := backupFile(asset.target)
if err != nil {
return swap{}, err
}
s := swap{
target: asset.target,
backup: backup,
}
if err := os.Rename(asset.target, backup); err != nil {
if !go_errors.Is(err, os.ErrNotExist) {
return swap{}, err
}
if err := os.Remove(backup); err != nil && !go_errors.Is(err, os.ErrNotExist) {
return swap{}, err
}
} else {
s.hadOriginal = true
}
if err := os.Rename(asset.temp, asset.target); err != nil {
if s.hadOriginal {
if restoreErr := os.Rename(backup, asset.target); restoreErr != nil {
return swap{}, errors.Combine(err, restoreErr)
}
}
return swap{}, err
}
return s, nil
}
func (t *tx) rollback() error {
var errs []error
for i := len(t.swaps) - 1; i >= 0; i-- {
if err := t.swaps[i].rollback(); err != nil {
errs = append(errs, err)
}
}
return errors.Combine(errs...)
}
func (s swap) rollback() error {
var errs []error
if err := os.Remove(s.target); err != nil && !go_errors.Is(err, os.ErrNotExist) {
errs = append(errs, err)
}
if s.hadOriginal {
if err := os.Rename(s.backup, s.target); err != nil {
errs = append(errs, err)
}
} else if err := os.Remove(s.backup); err != nil && !go_errors.Is(err, os.ErrNotExist) {
errs = append(errs, err)
}
return errors.Combine(errs...)
}
func (t *tx) commit() error {
var errs []error
for _, swap := range t.swaps {
if err := os.Remove(swap.backup); err != nil && !go_errors.Is(err, os.ErrNotExist) {
errs = append(errs, err)
}
}
return errors.Combine(errs...)
}
func tempFile(target string, suffix string) (*os.File, error) {
dir := filepath.Dir(target)
if err := os.MkdirAll(dir, 0o755); err != nil {
return nil, err
}
return os.CreateTemp(dir, "."+filepath.Base(target)+".*"+suffix)
}
func backupFile(target string) (string, error) {
file, err := tempFile(target, ".bak")
if err != nil {
return "", err
}
name := file.Name()
if err := file.Close(); err != nil {
os.Remove(name)
return "", err
}
if err := os.Remove(name); err != nil {
return "", err
}
return name, nil
}
+134
View File
@@ -0,0 +1,134 @@
package geodata
import (
"context"
"sync"
"github.com/robfig/cron/v3"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
commongeodata "github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/routing"
)
type Instance struct {
assets []*Asset
downloader *downloader
tasker *cron.Cron
mu sync.Mutex
running bool
}
func New(ctx context.Context, config *Config) (*Instance, error) {
if config.Cron == "" {
return &Instance{}, nil
}
g := &Instance{
assets: config.Assets,
}
if len(g.assets) > 0 {
var dispatcher routing.Dispatcher
if err := core.RequireFeatures(ctx, func(d routing.Dispatcher) {
dispatcher = d
}); err != nil {
return nil, errors.New("failed to get dispatcher for geodata downloader").Base(err)
}
g.downloader = newDownloader(ctx, dispatcher, config.Outbound)
}
g.tasker = cron.New(
cron.WithChain(cron.SkipIfStillRunning(cron.DiscardLogger)),
cron.WithLogger(cron.DiscardLogger),
)
if _, err := g.tasker.AddFunc(config.Cron, g.execute); err != nil {
return nil, errors.New("invalid geodata cron").Base(err)
}
errors.LogInfo(ctx, "scheduled geodata reload with cron: ", config.Cron)
return g, nil
}
func (g *Instance) execute() {
var err error
if g.downloader != nil {
err = g.reloadWithUpdate()
} else {
err = reload()
}
if err != nil {
errors.LogErrorInner(context.Background(), err, "scheduled geodata reload failed")
}
}
func (g *Instance) reloadWithUpdate() error {
staged, err := g.downloader.download(g.assets)
if err != nil {
return err
}
defer clean(staged)
tx, err := swapAll(staged)
if err != nil {
return err
}
if err := reload(); err != nil {
errors.LogErrorInner(context.Background(), err, "failed to reload geodata after downloading assets, rolling back")
rollbackErr := tx.rollback()
return errors.Combine(err, rollbackErr)
}
return tx.commit()
}
func reload() error {
return errors.Combine(commongeodata.IPReg.Reload(), commongeodata.DomainReg.Reload())
}
func (g *Instance) Type() interface{} {
return (*Instance)(nil)
}
func (g *Instance) Start() error {
g.mu.Lock()
defer g.mu.Unlock()
if g.running {
return nil
}
if g.tasker != nil {
g.tasker.Start()
}
g.running = true
return nil
}
func (g *Instance) Close() error {
g.mu.Lock()
defer g.mu.Unlock()
if !g.running {
return nil
}
if g.tasker != nil {
<-g.tasker.Stop().Done()
}
g.running = false
return nil
}
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
return New(ctx, cfg.(*Config))
}))
}
+21 -36
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/log/command/config.proto
package command
@@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -21,9 +22,9 @@ const (
)
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
@@ -57,9 +58,9 @@ func (*Config) Descriptor() ([]byte, []int) {
}
type RestartLoggerRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RestartLoggerRequest) Reset() {
@@ -93,9 +94,9 @@ func (*RestartLoggerRequest) Descriptor() ([]byte, []int) {
}
type RestartLoggerResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RestartLoggerResponse) Reset() {
@@ -130,39 +131,24 @@ func (*RestartLoggerResponse) Descriptor() ([]byte, []int) {
var File_app_log_command_config_proto protoreflect.FileDescriptor
var file_app_log_command_config_proto_rawDesc = []byte{
0x0a, 0x1c, 0x61, 0x70, 0x70, 0x2f, 0x6c, 0x6f, 0x67, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x61, 0x6e, 0x64, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x16,
0x0a, 0x14, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x17, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72,
0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32,
0x7b, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x12, 0x6a, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65,
0x72, 0x12, 0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74,
0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x6f, 0x67, 0x67,
0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x5e, 0x0a, 0x18,
0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x29, 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, 0x6c, 0x6f, 0x67, 0x2f, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70,
0x2e, 0x4c, 0x6f, 0x67, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
const file_app_log_command_config_proto_rawDesc = "" +
"\n" +
"\x1capp/log/command/config.proto\x12\x14xray.app.log.command\"\b\n" +
"\x06Config\"\x16\n" +
"\x14RestartLoggerRequest\"\x17\n" +
"\x15RestartLoggerResponse2{\n" +
"\rLoggerService\x12j\n" +
"\rRestartLogger\x12*.xray.app.log.command.RestartLoggerRequest\x1a+.xray.app.log.command.RestartLoggerResponse\"\x00B^\n" +
"\x18com.xray.app.log.commandP\x01Z)github.com/xtls/xray-core/app/log/command\xaa\x02\x14Xray.App.Log.Commandb\x06proto3"
var (
file_app_log_command_config_proto_rawDescOnce sync.Once
file_app_log_command_config_proto_rawDescData = file_app_log_command_config_proto_rawDesc
file_app_log_command_config_proto_rawDescData []byte
)
func file_app_log_command_config_proto_rawDescGZIP() []byte {
file_app_log_command_config_proto_rawDescOnce.Do(func() {
file_app_log_command_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_log_command_config_proto_rawDescData)
file_app_log_command_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_log_command_config_proto_rawDesc), len(file_app_log_command_config_proto_rawDesc)))
})
return file_app_log_command_config_proto_rawDescData
}
@@ -192,7 +178,7 @@ func file_app_log_command_config_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_log_command_config_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_log_command_config_proto_rawDesc), len(file_app_log_command_config_proto_rawDesc)),
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
@@ -203,7 +189,6 @@ func file_app_log_command_config_proto_init() {
MessageInfos: file_app_log_command_config_proto_msgTypes,
}.Build()
File_app_log_command_config_proto = out.File
file_app_log_command_config_proto_rawDesc = nil
file_app_log_command_config_proto_goTypes = nil
file_app_log_command_config_proto_depIdxs = nil
}
+4 -4
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.28.2
// - protoc-gen-go-grpc v1.6.0
// - protoc v6.33.5
// source: app/log/command/config.proto
package command
@@ -63,7 +63,7 @@ type LoggerServiceServer interface {
type UnimplementedLoggerServiceServer struct{}
func (UnimplementedLoggerServiceServer) RestartLogger(context.Context, *RestartLoggerRequest) (*RestartLoggerResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RestartLogger not implemented")
return nil, status.Error(codes.Unimplemented, "method RestartLogger not implemented")
}
func (UnimplementedLoggerServiceServer) mustEmbedUnimplementedLoggerServiceServer() {}
func (UnimplementedLoggerServiceServer) testEmbeddedByValue() {}
@@ -76,7 +76,7 @@ type UnsafeLoggerServiceServer interface {
}
func RegisterLoggerServiceServer(s grpc.ServiceRegistrar, srv LoggerServiceServer) {
// If the following call pancis, it indicates UnimplementedLoggerServiceServer was
// If the following call panics, it indicates UnimplementedLoggerServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
+32 -53
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/log/config.proto
package log
@@ -12,6 +12,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -74,17 +75,16 @@ func (LogType) EnumDescriptor() ([]byte, []int) {
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
ErrorLogType LogType `protobuf:"varint,1,opt,name=error_log_type,json=errorLogType,proto3,enum=xray.app.log.LogType" json:"error_log_type,omitempty"`
ErrorLogLevel log.Severity `protobuf:"varint,2,opt,name=error_log_level,json=errorLogLevel,proto3,enum=xray.common.log.Severity" json:"error_log_level,omitempty"`
ErrorLogPath string `protobuf:"bytes,3,opt,name=error_log_path,json=errorLogPath,proto3" json:"error_log_path,omitempty"`
AccessLogType LogType `protobuf:"varint,4,opt,name=access_log_type,json=accessLogType,proto3,enum=xray.app.log.LogType" json:"access_log_type,omitempty"`
AccessLogPath string `protobuf:"bytes,5,opt,name=access_log_path,json=accessLogPath,proto3" json:"access_log_path,omitempty"`
EnableDnsLog bool `protobuf:"varint,6,opt,name=enable_dns_log,json=enableDnsLog,proto3" json:"enable_dns_log,omitempty"`
MaskAddress string `protobuf:"bytes,7,opt,name=mask_address,json=maskAddress,proto3" json:"mask_address,omitempty"`
unknownFields protoimpl.UnknownFields
ErrorLogType LogType `protobuf:"varint,1,opt,name=error_log_type,json=errorLogType,proto3,enum=xray.app.log.LogType" json:"error_log_type,omitempty"`
ErrorLogLevel log.Severity `protobuf:"varint,2,opt,name=error_log_level,json=errorLogLevel,proto3,enum=xray.common.log.Severity" json:"error_log_level,omitempty"`
ErrorLogPath string `protobuf:"bytes,3,opt,name=error_log_path,json=errorLogPath,proto3" json:"error_log_path,omitempty"`
AccessLogType LogType `protobuf:"varint,4,opt,name=access_log_type,json=accessLogType,proto3,enum=xray.app.log.LogType" json:"access_log_type,omitempty"`
AccessLogPath string `protobuf:"bytes,5,opt,name=access_log_path,json=accessLogPath,proto3" json:"access_log_path,omitempty"`
EnableDnsLog bool `protobuf:"varint,6,opt,name=enable_dns_log,json=enableDnsLog,proto3" json:"enable_dns_log,omitempty"`
MaskAddress string `protobuf:"bytes,7,opt,name=mask_address,json=maskAddress,proto3" json:"mask_address,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
@@ -168,52 +168,32 @@ func (x *Config) GetMaskAddress() string {
var File_app_log_config_proto protoreflect.FileDescriptor
var file_app_log_config_proto_rawDesc = []byte{
0x0a, 0x14, 0x61, 0x70, 0x70, 0x2f, 0x6c, 0x6f, 0x67, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x6c, 0x6f, 0x67, 0x1a, 0x14, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6c, 0x6f, 0x67,
0x2f, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xde, 0x02, 0x0a, 0x06, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3b, 0x0a, 0x0e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6c,
0x6f, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x2e, 0x4c, 0x6f, 0x67,
0x54, 0x79, 0x70, 0x65, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x6f, 0x67, 0x54, 0x79,
0x70, 0x65, 0x12, 0x41, 0x0a, 0x0f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x5f,
0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6c, 0x6f, 0x67, 0x2e, 0x53, 0x65,
0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x6f, 0x67,
0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x24, 0x0a, 0x0e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6c,
0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65,
0x72, 0x72, 0x6f, 0x72, 0x4c, 0x6f, 0x67, 0x50, 0x61, 0x74, 0x68, 0x12, 0x3d, 0x0a, 0x0f, 0x61,
0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x6c, 0x6f, 0x67, 0x2e, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0d, 0x61, 0x63, 0x63,
0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x63,
0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x50, 0x61,
0x74, 0x68, 0x12, 0x24, 0x0a, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x64, 0x6e, 0x73,
0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x65, 0x6e, 0x61, 0x62,
0x6c, 0x65, 0x44, 0x6e, 0x73, 0x4c, 0x6f, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x73, 0x6b,
0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
0x6d, 0x61, 0x73, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2a, 0x35, 0x0a, 0x07, 0x4c,
0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00,
0x12, 0x0b, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x10, 0x01, 0x12, 0x08, 0x0a,
0x04, 0x46, 0x69, 0x6c, 0x65, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74,
0x10, 0x03, 0x42, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 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, 0x6c, 0x6f, 0x67, 0xaa, 0x02, 0x0c, 0x58, 0x72,
0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4c, 0x6f, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
const file_app_log_config_proto_rawDesc = "" +
"\n" +
"\x14app/log/config.proto\x12\fxray.app.log\x1a\x14common/log/log.proto\"\xde\x02\n" +
"\x06Config\x12;\n" +
"\x0eerror_log_type\x18\x01 \x01(\x0e2\x15.xray.app.log.LogTypeR\ferrorLogType\x12A\n" +
"\x0ferror_log_level\x18\x02 \x01(\x0e2\x19.xray.common.log.SeverityR\rerrorLogLevel\x12$\n" +
"\x0eerror_log_path\x18\x03 \x01(\tR\ferrorLogPath\x12=\n" +
"\x0faccess_log_type\x18\x04 \x01(\x0e2\x15.xray.app.log.LogTypeR\raccessLogType\x12&\n" +
"\x0faccess_log_path\x18\x05 \x01(\tR\raccessLogPath\x12$\n" +
"\x0eenable_dns_log\x18\x06 \x01(\bR\fenableDnsLog\x12!\n" +
"\fmask_address\x18\a \x01(\tR\vmaskAddress*5\n" +
"\aLogType\x12\b\n" +
"\x04None\x10\x00\x12\v\n" +
"\aConsole\x10\x01\x12\b\n" +
"\x04File\x10\x02\x12\t\n" +
"\x05Event\x10\x03BF\n" +
"\x10com.xray.app.logP\x01Z!github.com/xtls/xray-core/app/log\xaa\x02\fXray.App.Logb\x06proto3"
var (
file_app_log_config_proto_rawDescOnce sync.Once
file_app_log_config_proto_rawDescData = file_app_log_config_proto_rawDesc
file_app_log_config_proto_rawDescData []byte
)
func file_app_log_config_proto_rawDescGZIP() []byte {
file_app_log_config_proto_rawDescOnce.Do(func() {
file_app_log_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_log_config_proto_rawDescData)
file_app_log_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_log_config_proto_rawDesc), len(file_app_log_config_proto_rawDesc)))
})
return file_app_log_config_proto_rawDescData
}
@@ -245,7 +225,7 @@ func file_app_log_config_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_log_config_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_log_config_proto_rawDesc), len(file_app_log_config_proto_rawDesc)),
NumEnums: 1,
NumMessages: 1,
NumExtensions: 0,
@@ -257,7 +237,6 @@ func file_app_log_config_proto_init() {
MessageInfos: file_app_log_config_proto_msgTypes,
}.Build()
File_app_log_config_proto = out.File
file_app_log_config_proto_rawDesc = nil
file_app_log_config_proto_goTypes = nil
file_app_log_config_proto_depIdxs = nil
}
+83 -33
View File
@@ -2,8 +2,9 @@ package log
import (
"context"
"fmt"
"net"
"regexp"
"strconv"
"strings"
"sync"
@@ -20,14 +21,23 @@ type Instance struct {
errorLogger log.Handler
active bool
dns bool
mask4 int
mask6 int
}
// New creates a new log.Instance based on the given config.
func New(ctx context.Context, config *Config) (*Instance, error) {
m4, m6, err := ParseMaskAddress(config.MaskAddress)
if err != nil {
return nil, err
}
g := &Instance{
config: config,
active: false,
dns: config.EnableDnsLog,
mask4: m4,
mask6: m6,
}
log.RegisterHandler(g)
@@ -104,7 +114,11 @@ func (g *Instance) Handle(msg log.Message) {
var Msg log.Message
if g.config.MaskAddress != "" {
Msg = &MaskedMsgWrapper{Message: msg, config: g.config}
Msg = &MaskedMsgWrapper{
Message: msg,
Mask4: g.mask4,
Mask6: g.mask6,
}
} else {
Msg = msg
}
@@ -149,51 +163,87 @@ func (g *Instance) Close() error {
return nil
}
func ParseMaskAddress(c string) (int, int, error) {
var m4, m6 int
switch c {
case "half":
m4, m6 = 16, 32
case "quarter":
m4, m6 = 8, 16
case "full":
m4, m6 = 0, 0
case "":
// do nothing
default:
if parts := strings.Split(c, "+"); len(parts) > 0 {
if len(parts) >= 1 && parts[0] != "" {
i, err := strconv.Atoi(strings.TrimPrefix(parts[0], "/"))
if err != nil {
return 32, 128, err
}
m4 = i
}
if len(parts) >= 2 && parts[1] != "" {
i, err := strconv.Atoi(strings.TrimPrefix(parts[1], "/"))
if err != nil {
return 32, 128, err
}
m6 = i
}
}
}
if m4%8 != 0 || m4 > 32 || m4 < 0 {
return 32, 128, errors.New("Log Mask: ipv4 mask must be divisible by 8 and between 0-32")
}
return m4, m6, nil
}
// MaskedMsgWrapper is to wrap the string() method to mask IP addresses in the log.
type MaskedMsgWrapper struct {
log.Message
config *Config
Mask4 int
Mask6 int
}
var (
ipv4Regex = regexp.MustCompile(`(\d{1,3}\.){3}\d{1,3}`)
ipv6Regex = regexp.MustCompile(`(?:[\da-fA-F]{0,4}:[\da-fA-F]{0,4}){2,7}`)
)
func (m *MaskedMsgWrapper) String() string {
str := m.Message.String()
ipv4Regex := regexp.MustCompile(`(\d{1,3}\.){3}\d{1,3}`)
ipv6Regex := regexp.MustCompile(`((?:[\da-fA-F]{0,4}:[\da-fA-F]{0,4}){2,7})(?:[\/\\%](\d{1,3}))?`)
// Process ipv4
maskedMsg := ipv4Regex.ReplaceAllStringFunc(str, func(ip string) string {
parts := strings.Split(ip, ".")
switch m.config.MaskAddress {
case "half":
return fmt.Sprintf("%s.%s.*.*", parts[0], parts[1])
case "quarter":
return fmt.Sprintf("%s.*.*.*", parts[0])
case "full":
return "[Masked IPv4]"
default:
return ip
maskedMsg := ipv4Regex.ReplaceAllStringFunc(str, func(s string) string {
if m.Mask4 == 32 {
return s
}
if m.Mask4 == 0 {
return "[Masked IPv4]"
}
parts := strings.Split(s, ".")
for i := m.Mask4 / 8; i < 4; i++ {
parts[i] = "*"
}
return strings.Join(parts, ".")
})
// process ipv6
maskedMsg = ipv6Regex.ReplaceAllStringFunc(maskedMsg, func(ip string) string {
parts := strings.Split(ip, ":")
switch m.config.MaskAddress {
case "half":
if len(parts) >= 2 {
return fmt.Sprintf("%s:%s::/32", parts[0], parts[1])
}
case "quarter":
if len(parts) >= 1 {
return fmt.Sprintf("%s::/16", parts[0])
}
case "full":
return "Masked IPv6" // Do not use [Masked IPv6] like ipv4, or you will get "[[Masked IPv6]]" (v6 address already has [])
default:
return ip
maskedMsg = ipv6Regex.ReplaceAllStringFunc(maskedMsg, func(s string) string {
if m.Mask6 == 128 {
return s
}
return ip
if m.Mask6 == 0 {
return "Masked IPv6"
}
ip := net.ParseIP(s)
if ip == nil {
return s
}
return ip.Mask(net.CIDRMask(m.Mask6, 128)).String() + "/" + strconv.Itoa(m.Mask6)
})
return maskedMsg
+37
View File
@@ -2,6 +2,7 @@ package log_test
import (
"context"
"net"
"testing"
"github.com/golang/mock/gomock"
@@ -50,3 +51,39 @@ func TestCustomLogHandler(t *testing.T) {
common.Must(logger.Close())
}
func TestMaskAddress(t *testing.T) {
m4, m6, err := log.ParseMaskAddress("half")
if err != nil {
t.Fatal(err)
}
maskedAddr := log.MaskedMsgWrapper{
Mask4: m4,
Mask6: m6,
}
maskedAddr.Message = net.ParseIP("11.45.1.4")
if maskedAddr.String() != "11.45.*.*" {
t.Fatal("expected '11.45.*.*', but actually ", maskedAddr.String())
}
maskedAddr.Message = net.ParseIP("11:45:14:19:19:81:0::")
if maskedAddr.String() != "11:45::/32" {
t.Fatal("expected '11:45::/32', but actually", maskedAddr.String())
}
m4, m6, err = log.ParseMaskAddress("/16+/64")
if err != nil {
t.Fatal(err)
}
maskedAddr = log.MaskedMsgWrapper{
Mask4: m4,
Mask6: m6,
}
maskedAddr.Message = net.ParseIP("11.45.1.4")
if maskedAddr.String() != "11.45.*.*" {
t.Fatal("expected '11.45.*.*', but actually ", maskedAddr.String())
}
maskedAddr.Message = net.ParseIP("11:45:14:19:19:81:0::")
if maskedAddr.String() != "11:45:14:19::/64" {
t.Fatal("expected '11:45:14:19::/64', but actually", maskedAddr.String())
}
}
+18 -26
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/metrics/config.proto
package metrics
@@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -22,13 +23,12 @@ const (
// Config is the settings for metrics.
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState `protogen:"open.v1"`
// Tag of the outbound handler that handles metrics http connections.
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Listen string `protobuf:"bytes,2,opt,name=listen,proto3" json:"listen,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"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
@@ -77,29 +77,22 @@ func (x *Config) GetListen() string {
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, 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, 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,
}
const file_app_metrics_config_proto_rawDesc = "" +
"\n" +
"\x18app/metrics/config.proto\x12\x10xray.app.metrics\"2\n" +
"\x06Config\x12\x10\n" +
"\x03tag\x18\x01 \x01(\tR\x03tag\x12\x16\n" +
"\x06listen\x18\x02 \x01(\tR\x06listenBR\n" +
"\x14com.xray.app.metricsP\x01Z%github.com/xtls/xray-core/app/metrics\xaa\x02\x10Xray.App.Metricsb\x06proto3"
var (
file_app_metrics_config_proto_rawDescOnce sync.Once
file_app_metrics_config_proto_rawDescData = file_app_metrics_config_proto_rawDesc
file_app_metrics_config_proto_rawDescData []byte
)
func file_app_metrics_config_proto_rawDescGZIP() []byte {
file_app_metrics_config_proto_rawDescOnce.Do(func() {
file_app_metrics_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_metrics_config_proto_rawDescData)
file_app_metrics_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_metrics_config_proto_rawDesc), len(file_app_metrics_config_proto_rawDesc)))
})
return file_app_metrics_config_proto_rawDescData
}
@@ -125,7 +118,7 @@ func file_app_metrics_config_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_metrics_config_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_metrics_config_proto_rawDesc), len(file_app_metrics_config_proto_rawDesc)),
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
@@ -136,7 +129,6 @@ func file_app_metrics_config_proto_init() {
MessageInfos: file_app_metrics_config_proto_msgTypes,
}.Build()
File_app_metrics_config_proto = out.File
file_app_metrics_config_proto_rawDesc = nil
file_app_metrics_config_proto_goTypes = nil
file_app_metrics_config_proto_depIdxs = nil
}
+150 -64
View File
@@ -2,16 +2,18 @@ package metrics
import (
"context"
"encoding/json"
stderrors "errors"
"expvar"
stdnet "net"
"net/http"
_ "net/http/pprof"
"net/http/pprof"
"strings"
"github.com/xtls/xray-core/app/observatory"
"github.com/xtls/xray-core/app/stats"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
xnet "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/signal/done"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/extension"
@@ -22,15 +24,17 @@ import (
type MetricsHandler struct {
ohm outbound.Manager
statsManager feature_stats.Manager
observatory extension.Observatory
ctx context.Context
tag string
listen string
tcpListener net.Listener
tcpListener xnet.Listener
listener *OutboundListener
}
// NewMetricsHandler creates a new MetricsHandler based on the given config.
func NewMetricsHandler(ctx context.Context, config *Config) (*MetricsHandler, error) {
c := &MetricsHandler{
ctx: ctx,
tag: config.Tag,
listen: config.Listen,
}
@@ -38,50 +42,6 @@ func NewMetricsHandler(ctx context.Context, config *Config) (*MetricsHandler, er
c.statsManager = sm
c.ohm = om
}))
expvar.Publish("stats", expvar.Func(func() interface{} {
manager, ok := c.statsManager.(*stats.Manager)
if !ok {
return nil
}
resp := map[string]map[string]map[string]int64{
"inbound": {},
"outbound": {},
"user": {},
}
manager.VisitCounters(func(name string, counter feature_stats.Counter) bool {
nameSplit := strings.Split(name, ">>>")
typeName, tagOrUser, direction := nameSplit[0], nameSplit[1], nameSplit[3]
if item, found := resp[typeName][tagOrUser]; found {
item[direction] = counter.Value()
} else {
resp[typeName][tagOrUser] = map[string]int64{
direction: counter.Value(),
}
}
return true
})
return resp
}))
expvar.Publish("observatory", expvar.Func(func() interface{} {
if c.observatory == nil {
common.Must(core.RequireFeatures(ctx, func(observatory extension.Observatory) error {
c.observatory = observatory
return nil
}))
if c.observatory == nil {
return nil
}
}
resp := map[string]*observatory.OutboundStatus{}
if o, err := c.observatory.GetObservation(context.Background()); err != nil {
return err
} else {
for _, x := range o.(*observatory.ObservationResult).GetStatus() {
resp[x.OutboundTag] = x
}
}
return resp
}))
return c, nil
}
@@ -90,46 +50,172 @@ func (p *MetricsHandler) Type() interface{} {
}
func (p *MetricsHandler) Start() error {
handler := p.httpHandler()
// direct listen a port if listen is set
if p.listen != "" {
TCPlistener, err := net.Listen("tcp", p.listen)
TCPlistener, err := xnet.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")
}
}()
go p.serve(TCPlistener, handler)
}
if p.tag == "" {
if p.tcpListener == nil {
return errors.New("metrics must have a tag or listen address")
}
return nil
}
listener := &OutboundListener{
buffer: make(chan net.Conn, 4),
buffer: make(chan xnet.Conn, 4),
done: done.New(),
}
p.listener = listener
go func() {
if err := http.Serve(listener, http.DefaultServeMux); err != nil {
errors.LogErrorInner(context.Background(), err, "failed to start metrics server")
}
}()
go p.serve(listener, handler)
if err := p.ohm.RemoveHandler(context.Background(), p.tag); err != nil {
errors.LogInfo(context.Background(), "failed to remove existing handler")
}
return p.ohm.AddHandler(context.Background(), &Outbound{
if err := p.ohm.AddHandler(context.Background(), &Outbound{
tag: p.tag,
listener: listener,
})
}); err != nil {
if closeErr := p.Close(); closeErr != nil {
errors.LogErrorInner(context.Background(), closeErr, "failed to close metrics server after start failure")
}
return err
}
return nil
}
func (p *MetricsHandler) Close() error {
return nil
var errs []error
if p.tcpListener != nil {
errs = append(errs, p.tcpListener.Close())
p.tcpListener = nil
}
if p.listener != nil {
errs = append(errs, p.listener.Close())
p.listener = nil
}
if p.ohm != nil && p.tag != "" {
if err := p.ohm.RemoveHandler(context.Background(), p.tag); err != nil {
errors.LogInfo(context.Background(), "failed to remove metrics handler")
}
}
return errors.Combine(errs...)
}
func (p *MetricsHandler) serve(listener xnet.Listener, handler http.Handler) {
if err := http.Serve(listener, handler); err != nil && !isClosedListenerError(err) {
errors.LogErrorInner(context.Background(), err, "failed to start metrics server")
}
}
func isClosedListenerError(err error) bool {
if err == nil {
return true
}
if stderrors.Is(err, stdnet.ErrClosed) || stderrors.Is(err, http.ErrServerClosed) {
return true
}
errText := err.Error()
return strings.Contains(errText, "listen closed") ||
strings.Contains(errText, "use of closed network connection")
}
func (p *MetricsHandler) httpHandler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/debug/vars", p.handleDebugVars)
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
return mux
}
func (p *MetricsHandler) handleDebugVars(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
vars := map[string]json.RawMessage{}
expvar.Do(func(kv expvar.KeyValue) {
value := json.RawMessage(kv.Value.String())
if !json.Valid(value) {
value = json.RawMessage("null")
}
vars[kv.Key] = value
})
vars["stats"] = marshalJSON(p.stats())
vars["observatory"] = marshalJSON(p.observatoryStatus())
payload, err := json.Marshal(vars)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(payload)
}
func marshalJSON(value interface{}) json.RawMessage {
data, err := json.Marshal(value)
if err != nil {
return json.RawMessage("null")
}
return data
}
func (p *MetricsHandler) stats() map[string]map[string]map[string]int64 {
resp := map[string]map[string]map[string]int64{
"inbound": {},
"outbound": {},
"user": {},
}
p.statsManager.VisitCounters(func(name string, counter feature_stats.Counter) bool {
nameSplit := strings.Split(name, ">>>")
if len(nameSplit) < 4 {
return true
}
typeName, tagOrUser, direction := nameSplit[0], nameSplit[1], nameSplit[3]
items, found := resp[typeName]
if !found {
items = map[string]map[string]int64{}
resp[typeName] = items
}
if item, found := items[tagOrUser]; found {
item[direction] = counter.Value()
} else {
items[tagOrUser] = map[string]int64{
direction: counter.Value(),
}
}
return true
})
return resp
}
func (p *MetricsHandler) observatoryStatus() interface{} {
feature := core.MustFromContext(p.ctx).GetFeature(extension.ObservatoryType())
if feature == nil {
return nil
}
observatoryFeature := feature.(extension.Observatory)
resp := map[string]*observatory.OutboundStatus{}
if o, err := observatoryFeature.GetObservation(context.Background()); err != nil {
return err
} else {
for _, x := range o.(*observatory.ObservationResult).GetStatus() {
resp[x.OutboundTag] = x
}
}
return resp
}
func init() {
+161
View File
@@ -0,0 +1,161 @@
package metrics
import (
"context"
"encoding/json"
stdnet "net"
"net/http"
"net/http/httptest"
"testing"
"github.com/xtls/xray-core/app/dispatcher"
"github.com/xtls/xray-core/app/proxyman"
_ "github.com/xtls/xray-core/app/proxyman/inbound"
_ "github.com/xtls/xray-core/app/proxyman/outbound"
appstats "github.com/xtls/xray-core/app/stats"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/core"
feature_outbound "github.com/xtls/xray-core/features/outbound"
)
func TestMetricsCanRestartInSameProcess(t *testing.T) {
for i := 0; i < 2; i++ {
server := startMetricsTestServer(t)
readMetricsVars(t, server)
readMetricsPprof(t, server)
if err := server.Close(); err != nil {
t.Fatalf("failed to close metrics server: %v", err)
}
}
}
func TestMetricsCanRunMultipleInstancesInSameProcess(t *testing.T) {
server1 := startMetricsTestServer(t)
t.Cleanup(func() {
_ = server1.Close()
})
server2 := startMetricsTestServer(t)
t.Cleanup(func() {
_ = server2.Close()
})
readMetricsVars(t, server1)
readMetricsVars(t, server2)
}
func TestMetricsListenOnlyWithoutTagDoesNotRegisterOutbound(t *testing.T) {
listen := pickMetricsListenAddress(t)
server := startMetricsTestServerWithMetricsConfig(t, &Config{
Listen: listen,
})
t.Cleanup(func() {
_ = server.Close()
})
response, err := http.Get("http://" + listen + "/debug/vars")
if err != nil {
t.Fatalf("failed to read listen-only metrics: %v", err)
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
t.Fatalf("unexpected listen-only metrics status: %d", response.StatusCode)
}
outboundManager := server.GetFeature(feature_outbound.ManagerType()).(feature_outbound.Manager)
if handlers := outboundManager.ListHandlers(context.Background()); len(handlers) != 0 {
t.Fatalf("listen-only metrics registered outbound handlers: got %d, want 0", len(handlers))
}
}
func startMetricsTestServer(t *testing.T) *core.Instance {
return startMetricsTestServerWithMetricsConfig(t, &Config{
Tag: "metrics_out",
})
}
func startMetricsTestServerWithMetricsConfig(t *testing.T, metricsConfig *Config) *core.Instance {
t.Helper()
server, err := core.New(metricsTestConfig(metricsConfig))
if err != nil {
t.Fatalf("failed to create metrics server: %v", err)
}
if err := server.Start(); err != nil {
_ = server.Close()
t.Fatalf("failed to start metrics server: %v", err)
}
return server
}
func metricsTestConfig(metricsConfig *Config) *core.Config {
return &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(&dispatcher.Config{}),
serial.ToTypedMessage(&proxyman.InboundConfig{}),
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
serial.ToTypedMessage(&appstats.Config{}),
serial.ToTypedMessage(metricsConfig),
},
}
}
func pickMetricsListenAddress(t *testing.T) string {
t.Helper()
listener, err := stdnet.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to pick metrics listen address: %v", err)
}
defer listener.Close()
return listener.Addr().String()
}
func readMetricsVars(t *testing.T, server *core.Instance) {
t.Helper()
recorder := httptest.NewRecorder()
metricsHandler(t, server).httpHandler().ServeHTTP(
recorder,
httptest.NewRequest(http.MethodGet, "/debug/vars", nil),
)
if recorder.Code != http.StatusOK {
t.Fatalf("unexpected metrics vars status: %d", recorder.Code)
}
var payload map[string]interface{}
if err := json.NewDecoder(recorder.Body).Decode(&payload); err != nil {
t.Fatalf("failed to decode metrics vars: %v", err)
}
if _, found := payload["stats"]; !found {
t.Fatal("metrics vars missing stats")
}
if _, found := payload["observatory"]; !found {
t.Fatal("metrics vars missing observatory")
}
}
func readMetricsPprof(t *testing.T, server *core.Instance) {
t.Helper()
recorder := httptest.NewRecorder()
metricsHandler(t, server).httpHandler().ServeHTTP(
recorder,
httptest.NewRequest(http.MethodGet, "/debug/pprof/goroutine?debug=1", nil),
)
if recorder.Code != http.StatusOK {
t.Fatalf("unexpected metrics pprof status: %d", recorder.Code)
}
}
func metricsHandler(t *testing.T, server *core.Instance) *MetricsHandler {
t.Helper()
feature := server.GetFeature((*MetricsHandler)(nil))
handler, ok := feature.(*MetricsHandler)
if !ok || handler == nil {
t.Fatal("metrics handler not registered")
}
return handler
}
+4 -2
View File
@@ -2,7 +2,6 @@ package burst
import (
"context"
"sync"
"github.com/xtls/xray-core/app/observatory"
@@ -32,6 +31,10 @@ func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) {
return &observatory.ObservationResult{Status: o.createResult()}, nil
}
func (o *Observer) Check(tag []string) {
o.hp.Check(tag)
}
func (o *Observer) createResult() []*observatory.OutboundStatus {
var result []*observatory.OutboundStatus
o.hp.access.Lock()
@@ -68,7 +71,6 @@ func (o *Observer) Start() error {
o.hp.StartScheduler(func() ([]string, error) {
hs, ok := o.ohm.(outbound.HandlerSelector)
if !ok {
return nil, errors.New("outbound.Manager is not a HandlerSelector")
}
+30 -51
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/observatory/burst/config.proto
package burst
@@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -21,13 +22,12 @@ const (
)
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState `protogen:"open.v1"`
// @Document The selectors for outbound under observation
SubjectSelector []string `protobuf:"bytes,2,rep,name=subject_selector,json=subjectSelector,proto3" json:"subject_selector,omitempty"`
PingConfig *HealthPingConfig `protobuf:"bytes,3,opt,name=ping_config,json=pingConfig,proto3" json:"ping_config,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
@@ -75,10 +75,7 @@ func (x *Config) GetPingConfig() *HealthPingConfig {
}
type HealthPingConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState `protogen:"open.v1"`
// destination url, need 204 for success return
// default https://connectivitycheck.gstatic.com/generate_204
Destination string `protobuf:"bytes,1,opt,name=destination,proto3" json:"destination,omitempty"`
@@ -91,7 +88,9 @@ type HealthPingConfig struct {
// 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"`
HttpMethod string `protobuf:"bytes,6,opt,name=httpMethod,proto3" json:"httpMethod,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *HealthPingConfig) Reset() {
@@ -168,51 +167,32 @@ func (x *HealthPingConfig) GetHttpMethod() string {
var File_app_observatory_burst_config_proto protoreflect.FileDescriptor
var file_app_observatory_burst_config_proto_rawDesc = []byte{
0x0a, 0x22, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
0x79, 0x2f, 0x62, 0x75, 0x72, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e,
0x62, 0x75, 0x72, 0x73, 0x74, 0x22, 0x87, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x12, 0x29, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65,
0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x52, 0x0a, 0x0b, 0x70,
0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x31, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70,
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,
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,
0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f,
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69,
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, 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,
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76,
0x61, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x62, 0x75, 0x72, 0x73, 0x74, 0xaa, 0x02, 0x1a, 0x58, 0x72,
0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f,
0x72, 0x79, 0x2e, 0x42, 0x75, 0x72, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
const file_app_observatory_burst_config_proto_rawDesc = "" +
"\n" +
"\"app/observatory/burst/config.proto\x12\x1fxray.core.app.observatory.burst\"\x87\x01\n" +
"\x06Config\x12)\n" +
"\x10subject_selector\x18\x02 \x03(\tR\x0fsubjectSelector\x12R\n" +
"\vping_config\x18\x03 \x01(\v21.xray.core.app.observatory.burst.HealthPingConfigR\n" +
"pingConfig\"\xd4\x01\n" +
"\x10HealthPingConfig\x12 \n" +
"\vdestination\x18\x01 \x01(\tR\vdestination\x12\"\n" +
"\fconnectivity\x18\x02 \x01(\tR\fconnectivity\x12\x1a\n" +
"\binterval\x18\x03 \x01(\x03R\binterval\x12$\n" +
"\rsamplingCount\x18\x04 \x01(\x05R\rsamplingCount\x12\x18\n" +
"\atimeout\x18\x05 \x01(\x03R\atimeout\x12\x1e\n" +
"\n" +
"httpMethod\x18\x06 \x01(\tR\n" +
"httpMethodBp\n" +
"\x1ecom.xray.app.observatory.burstP\x01Z/github.com/xtls/xray-core/app/observatory/burst\xaa\x02\x1aXray.App.Observatory.Burstb\x06proto3"
var (
file_app_observatory_burst_config_proto_rawDescOnce sync.Once
file_app_observatory_burst_config_proto_rawDescData = file_app_observatory_burst_config_proto_rawDesc
file_app_observatory_burst_config_proto_rawDescData []byte
)
func file_app_observatory_burst_config_proto_rawDescGZIP() []byte {
file_app_observatory_burst_config_proto_rawDescOnce.Do(func() {
file_app_observatory_burst_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_observatory_burst_config_proto_rawDescData)
file_app_observatory_burst_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_observatory_burst_config_proto_rawDesc), len(file_app_observatory_burst_config_proto_rawDesc)))
})
return file_app_observatory_burst_config_proto_rawDescData
}
@@ -240,7 +220,7 @@ func file_app_observatory_burst_config_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_observatory_burst_config_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_observatory_burst_config_proto_rawDesc), len(file_app_observatory_burst_config_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
@@ -251,7 +231,6 @@ func file_app_observatory_burst_config_proto_init() {
MessageInfos: file_app_observatory_burst_config_proto_msgTypes,
}.Build()
File_app_observatory_burst_config_proto = out.File
file_app_observatory_burst_config_proto_rawDesc = nil
file_app_observatory_burst_config_proto_goTypes = nil
file_app_observatory_burst_config_proto_depIdxs = nil
}
+42 -24
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/xtls/xray-core/common/dice"
@@ -24,11 +25,12 @@ type HealthPingSettings struct {
// HealthPing is the health checker for balancers
type HealthPing struct {
ctx context.Context
dispatcher routing.Dispatcher
access sync.Mutex
ticker *time.Ticker
tickerClose chan struct{}
ctx context.Context
cancelCtx context.CancelFunc
cancelPending atomic.Pointer[context.CancelFunc]
dispatcher routing.Dispatcher
access sync.Mutex
ticker *time.Ticker
Settings *HealthPingSettings
Results map[string]*HealthPingRTTS
@@ -62,10 +64,10 @@ func NewHealthPing(ctx context.Context, dispatcher routing.Dispatcher, config *H
settings.Destination = "https://connectivitycheck.gstatic.com/generate_204"
}
if settings.Interval == 0 {
settings.Interval = time.Duration(1) * time.Minute
} else if settings.Interval < 10 {
settings.Interval = 1 * time.Minute
} else if settings.Interval < 10*time.Second {
errors.LogWarning(ctx, "health check interval is too small, 10s is applied")
settings.Interval = time.Duration(10) * time.Second
settings.Interval = 10 * time.Second
}
if settings.SamplingCount <= 0 {
settings.SamplingCount = 10
@@ -73,10 +75,12 @@ func NewHealthPing(ctx context.Context, dispatcher routing.Dispatcher, config *H
if settings.Timeout <= 0 {
// results are saved after all health pings finish,
// a larger timeout could possibly makes checks run longer
settings.Timeout = time.Duration(5) * time.Second
settings.Timeout = 5 * time.Second
}
ctx, cancel := context.WithCancel(ctx)
return &HealthPing{
ctx: ctx,
cancelCtx: cancel,
dispatcher: dispatcher,
Settings: settings,
Results: nil,
@@ -90,9 +94,9 @@ func (h *HealthPing) StartScheduler(selector func() ([]string, error)) {
}
interval := h.Settings.Interval * time.Duration(h.Settings.SamplingCount)
ticker := time.NewTicker(interval)
tickerClose := make(chan struct{})
h.ticker = ticker
h.tickerClose = tickerClose
// init run to get a fast check result
go func() {
tags, err := selector()
if err != nil {
@@ -110,13 +114,20 @@ func (h *HealthPing) StartScheduler(selector func() ([]string, error)) {
errors.LogWarning(h.ctx, "error select outbounds for scheduled health check: ", err)
return
}
h.doCheck(tags, interval, h.Settings.SamplingCount)
subCtx, cancel := context.WithCancel(h.ctx)
old := h.cancelPending.Swap(&cancel)
if old != nil {
errors.LogDebug(h.ctx, "scheduled health check not finished before next round, canceling previous one")
(*old)()
}
h.doCheck(subCtx, tags, interval, h.Settings.SamplingCount)
h.cancelPending.CompareAndSwap(&cancel, nil)
h.Cleanup(tags)
}()
select {
case <-ticker.C:
continue
case <-tickerClose:
case <-h.ctx.Done():
return
}
}
@@ -130,8 +141,7 @@ func (h *HealthPing) StopScheduler() {
}
h.ticker.Stop()
h.ticker = nil
close(h.tickerClose)
h.tickerClose = nil
h.cancelCtx()
}
// Check implements the HealthChecker
@@ -140,7 +150,7 @@ func (h *HealthPing) Check(tags []string) error {
return nil
}
errors.LogInfo(h.ctx, "perform one-time health check for tags ", tags)
h.doCheck(tags, 0, 1)
h.doCheck(h.ctx, tags, 0, 1)
return nil
}
@@ -151,13 +161,14 @@ type rtt struct {
// doCheck performs the 'rounds' amount checks in given 'duration'. You should make
// sure all tags are valid for current balancer
func (h *HealthPing) doCheck(tags []string, duration time.Duration, rounds int) {
// cancel ctx will stop all pending checks
func (h *HealthPing) doCheck(ctx context.Context, tags []string, duration time.Duration, rounds int) {
count := len(tags) * rounds
if count == 0 {
return
}
ch := make(chan *rtt, count)
timers := make([]*time.Timer, 0, count)
for _, tag := range tags {
handler := tag
client := newPingClient(
@@ -172,7 +183,7 @@ func (h *HealthPing) doCheck(tags []string, duration time.Duration, rounds int)
if duration > 0 {
delay = time.Duration(dice.RollInt63n(int64(duration)))
}
time.AfterFunc(delay, func() {
timers = append(timers, time.AfterFunc(delay, func() {
errors.LogDebug(h.ctx, "checking ", handler)
delay, err := client.MeasureDelay(h.Settings.HttpMethod)
if err == nil {
@@ -200,14 +211,21 @@ func (h *HealthPing) doCheck(tags []string, duration time.Duration, rounds int)
handler: handler,
value: rttFailed,
}
})
}))
}
}
for i := 0; i < count; i++ {
rtt := <-ch
if rtt.value > 0 {
// should not put results when network is down
h.PutResult(rtt.handler, rtt.value)
select {
case rtt := <-ch:
if rtt.value > 0 {
// should not put results when network is down
h.PutResult(rtt.handler, rtt.value)
}
case <-ctx.Done():
for _, timer := range timers {
timer.Stop()
}
return
}
}
}
+2 -2
View File
@@ -59,7 +59,7 @@ func (h *HealthPingRTTS) Put(d time.Duration) {
if h.rtts == nil {
h.rtts = make([]*pingRTT, h.cap)
for i := 0; i < h.cap; i++ {
h.rtts[i] = &pingRTT{}
h.rtts[i] = &pingRTT{value: rttUntested}
}
h.idx = -1
}
@@ -88,7 +88,7 @@ func (h *HealthPingRTTS) getStatistics() *HealthPingStats {
validRTTs := make([]time.Duration, 0)
for _, rtt := range h.rtts {
switch {
case rtt.value == 0 || time.Since(rtt.time) > h.validity:
case rtt.value == rttUntested || time.Since(rtt.time) > h.validity:
continue
case rtt.value == rttFailed:
stats.Fail++
+2
View File
@@ -7,6 +7,7 @@ import (
"time"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/utils"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport/internet/tagged"
)
@@ -61,6 +62,7 @@ func (s *pingClient) MeasureDelay(httpMethod string) (time.Duration, error) {
if err != nil {
return rttFailed, err
}
utils.TryDefaultHeadersWith(req.Header, "nav")
start := time.Now()
resp, err := s.httpClient.Do(req)
+23 -51
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/observatory/command/command.proto
package command
@@ -12,6 +12,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -22,9 +23,9 @@ const (
)
type GetOutboundStatusRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetOutboundStatusRequest) Reset() {
@@ -58,11 +59,10 @@ func (*GetOutboundStatusRequest) Descriptor() ([]byte, []int) {
}
type GetOutboundStatusResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Status *observatory.ObservationResult `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"`
unknownFields protoimpl.UnknownFields
Status *observatory.ObservationResult `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *GetOutboundStatusResponse) Reset() {
@@ -103,9 +103,9 @@ func (x *GetOutboundStatusResponse) GetStatus() *observatory.ObservationResult {
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
@@ -140,52 +140,25 @@ func (*Config) Descriptor() ([]byte, []int) {
var File_app_observatory_command_command_proto protoreflect.FileDescriptor
var file_app_observatory_command_command_proto_rawDesc = []byte{
0x0a, 0x25, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
0x79, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f,
0x72, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x1c, 0x61, 0x70, 0x70, 0x2f,
0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1a, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4f,
0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x22, 0x61, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x44, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x4f, 0x62,
0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52,
0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x32, 0xa7, 0x01, 0x0a, 0x12, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x90, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74,
0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3b,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f,
0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3c, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65,
0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e,
0x47, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x80, 0x01, 0x0a, 0x25,
0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x31, 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, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f,
0x72, 0x79, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x21, 0x58, 0x72, 0x61,
0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72,
0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
const file_app_observatory_command_command_proto_rawDesc = "" +
"\n" +
"%app/observatory/command/command.proto\x12!xray.core.app.observatory.command\x1a\x1capp/observatory/config.proto\"\x1a\n" +
"\x18GetOutboundStatusRequest\"a\n" +
"\x19GetOutboundStatusResponse\x12D\n" +
"\x06status\x18\x01 \x01(\v2,.xray.core.app.observatory.ObservationResultR\x06status\"\b\n" +
"\x06Config2\xa7\x01\n" +
"\x12ObservatoryService\x12\x90\x01\n" +
"\x11GetOutboundStatus\x12;.xray.core.app.observatory.command.GetOutboundStatusRequest\x1a<.xray.core.app.observatory.command.GetOutboundStatusResponse\"\x00B\x80\x01\n" +
"%com.xray.core.app.observatory.commandP\x01Z1github.com/xtls/xray-core/app/observatory/command\xaa\x02!Xray.Core.App.Observatory.Commandb\x06proto3"
var (
file_app_observatory_command_command_proto_rawDescOnce sync.Once
file_app_observatory_command_command_proto_rawDescData = file_app_observatory_command_command_proto_rawDesc
file_app_observatory_command_command_proto_rawDescData []byte
)
func file_app_observatory_command_command_proto_rawDescGZIP() []byte {
file_app_observatory_command_command_proto_rawDescOnce.Do(func() {
file_app_observatory_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_observatory_command_command_proto_rawDescData)
file_app_observatory_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_observatory_command_command_proto_rawDesc), len(file_app_observatory_command_command_proto_rawDesc)))
})
return file_app_observatory_command_command_proto_rawDescData
}
@@ -217,7 +190,7 @@ func file_app_observatory_command_command_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_observatory_command_command_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_observatory_command_command_proto_rawDesc), len(file_app_observatory_command_command_proto_rawDesc)),
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
@@ -228,7 +201,6 @@ func file_app_observatory_command_command_proto_init() {
MessageInfos: file_app_observatory_command_command_proto_msgTypes,
}.Build()
File_app_observatory_command_command_proto = out.File
file_app_observatory_command_command_proto_rawDesc = nil
file_app_observatory_command_command_proto_goTypes = nil
file_app_observatory_command_command_proto_depIdxs = nil
}
+4 -4
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.28.2
// - protoc-gen-go-grpc v1.6.0
// - protoc v6.33.5
// source: app/observatory/command/command.proto
package command
@@ -63,7 +63,7 @@ type ObservatoryServiceServer interface {
type UnimplementedObservatoryServiceServer struct{}
func (UnimplementedObservatoryServiceServer) GetOutboundStatus(context.Context, *GetOutboundStatusRequest) (*GetOutboundStatusResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetOutboundStatus not implemented")
return nil, status.Error(codes.Unimplemented, "method GetOutboundStatus not implemented")
}
func (UnimplementedObservatoryServiceServer) mustEmbedUnimplementedObservatoryServiceServer() {}
func (UnimplementedObservatoryServiceServer) testEmbeddedByValue() {}
@@ -76,7 +76,7 @@ type UnsafeObservatoryServiceServer interface {
}
func RegisterObservatoryServiceServer(s grpc.ServiceRegistrar, srv ObservatoryServiceServer) {
// If the following call pancis, it indicates UnimplementedObservatoryServiceServer was
// If the following call panics, it indicates UnimplementedObservatoryServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
+64 -104
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/observatory/config.proto
package observatory
@@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -21,11 +22,10 @@ const (
)
type ObservationResult struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Status []*OutboundStatus `protobuf:"bytes,1,rep,name=status,proto3" json:"status,omitempty"`
unknownFields protoimpl.UnknownFields
Status []*OutboundStatus `protobuf:"bytes,1,rep,name=status,proto3" json:"status,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *ObservationResult) Reset() {
@@ -66,16 +66,15 @@ func (x *ObservationResult) GetStatus() []*OutboundStatus {
}
type HealthPingMeasurementResult struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
All int64 `protobuf:"varint,1,opt,name=all,proto3" json:"all,omitempty"`
Fail int64 `protobuf:"varint,2,opt,name=fail,proto3" json:"fail,omitempty"`
Deviation int64 `protobuf:"varint,3,opt,name=deviation,proto3" json:"deviation,omitempty"`
Average int64 `protobuf:"varint,4,opt,name=average,proto3" json:"average,omitempty"`
Max int64 `protobuf:"varint,5,opt,name=max,proto3" json:"max,omitempty"`
Min int64 `protobuf:"varint,6,opt,name=min,proto3" json:"min,omitempty"`
unknownFields protoimpl.UnknownFields
All int64 `protobuf:"varint,1,opt,name=all,proto3" json:"all,omitempty"`
Fail int64 `protobuf:"varint,2,opt,name=fail,proto3" json:"fail,omitempty"`
Deviation int64 `protobuf:"varint,3,opt,name=deviation,proto3" json:"deviation,omitempty"`
Average int64 `protobuf:"varint,4,opt,name=average,proto3" json:"average,omitempty"`
Max int64 `protobuf:"varint,5,opt,name=max,proto3" json:"max,omitempty"`
Min int64 `protobuf:"varint,6,opt,name=min,proto3" json:"min,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *HealthPingMeasurementResult) Reset() {
@@ -151,10 +150,7 @@ func (x *HealthPingMeasurementResult) GetMin() int64 {
}
type OutboundStatus struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState `protogen:"open.v1"`
// @Document Whether this outbound is usable
// @Restriction ReadOnlyForUser
Alive bool `protobuf:"varint,1,opt,name=alive,proto3" json:"alive,omitempty"`
@@ -173,8 +169,10 @@ type OutboundStatus struct {
LastSeenTime int64 `protobuf:"varint,5,opt,name=last_seen_time,json=lastSeenTime,proto3" json:"last_seen_time,omitempty"`
// @Document The time this outbound is tried
// @Type id.outboundTag
LastTryTime int64 `protobuf:"varint,6,opt,name=last_try_time,json=lastTryTime,proto3" json:"last_try_time,omitempty"`
HealthPing *HealthPingMeasurementResult `protobuf:"bytes,7,opt,name=health_ping,json=healthPing,proto3" json:"health_ping,omitempty"`
LastTryTime int64 `protobuf:"varint,6,opt,name=last_try_time,json=lastTryTime,proto3" json:"last_try_time,omitempty"`
HealthPing *HealthPingMeasurementResult `protobuf:"bytes,7,opt,name=health_ping,json=healthPing,proto3" json:"health_ping,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *OutboundStatus) Reset() {
@@ -257,10 +255,7 @@ func (x *OutboundStatus) GetHealthPing() *HealthPingMeasurementResult {
}
type ProbeResult struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState `protogen:"open.v1"`
// @Document Whether this outbound is usable
// @Restriction ReadOnlyForUser
Alive bool `protobuf:"varint,1,opt,name=alive,proto3" json:"alive,omitempty"`
@@ -271,6 +266,8 @@ type ProbeResult struct {
// @Document The error caused this outbound failed to relay probe request
// @Restriction NotMachineReadable
LastErrorReason string `protobuf:"bytes,3,opt,name=last_error_reason,json=lastErrorReason,proto3" json:"last_error_reason,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ProbeResult) Reset() {
@@ -325,13 +322,12 @@ func (x *ProbeResult) GetLastErrorReason() string {
}
type Intensity struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState `protogen:"open.v1"`
// @Document The time interval for a probe request in ms.
// @Type time.ms
ProbeInterval uint32 `protobuf:"varint,1,opt,name=probe_interval,json=probeInterval,proto3" json:"probe_interval,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Intensity) Reset() {
@@ -372,15 +368,14 @@ func (x *Intensity) GetProbeInterval() uint32 {
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState `protogen:"open.v1"`
// @Document The selectors for outbound under observation
SubjectSelector []string `protobuf:"bytes,2,rep,name=subject_selector,json=subjectSelector,proto3" json:"subject_selector,omitempty"`
ProbeUrl string `protobuf:"bytes,3,opt,name=probe_url,json=probeUrl,proto3" json:"probe_url,omitempty"`
ProbeInterval int64 `protobuf:"varint,4,opt,name=probe_interval,json=probeInterval,proto3" json:"probe_interval,omitempty"`
EnableConcurrency bool `protobuf:"varint,5,opt,name=enable_concurrency,json=enableConcurrency,proto3" json:"enable_concurrency,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
@@ -443,82 +438,48 @@ func (x *Config) GetEnableConcurrency() bool {
var File_app_observatory_config_proto protoreflect.FileDescriptor
var file_app_observatory_config_proto_rawDesc = []byte{
0x0a, 0x1c, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
0x79, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62,
0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x56, 0x0a, 0x11, 0x4f, 0x62, 0x73,
0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x41,
0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f,
0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
0x73, 0x22, 0x9f, 0x01, 0x0a, 0x1b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67,
0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c,
0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03,
0x61, 0x6c, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28,
0x03, 0x52, 0x04, 0x66, 0x61, 0x69, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x64, 0x65, 0x76, 0x69,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65,
0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x12,
0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6d, 0x61,
0x78, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03,
0x6d, 0x69, 0x6e, 0x22, 0xae, 0x02, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64,
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05,
0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x64, 0x65, 0x6c,
0x61, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72,
0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c,
0x61, 0x73, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x21,
0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x04,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61,
0x67, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x74,
0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x53,
0x65, 0x65, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f,
0x74, 0x72, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b,
0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x57, 0x0a, 0x0b, 0x68,
0x65, 0x61, 0x6c, 0x74, 0x68, 0x5f, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x36, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x48, 0x65, 0x61,
0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0a, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68,
0x50, 0x69, 0x6e, 0x67, 0x22, 0x65, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73,
0x75, 0x6c, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x6c,
0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12,
0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x72, 0x65,
0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74,
0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x32, 0x0a, 0x09, 0x49,
0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x62,
0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x0d, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22,
0xa6, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x75,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02,
0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x6c,
0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x75,
0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x55,
0x72, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x62,
0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2d, 0x0a, 0x12, 0x65, 0x6e, 0x61,
0x62, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18,
0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6e,
0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61,
0x74, 0x6f, 0x72, 0x79, 0x50, 0x01, 0x5a, 0x29, 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, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
0x79, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4f, 0x62, 0x73,
0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
const file_app_observatory_config_proto_rawDesc = "" +
"\n" +
"\x1capp/observatory/config.proto\x12\x19xray.core.app.observatory\"V\n" +
"\x11ObservationResult\x12A\n" +
"\x06status\x18\x01 \x03(\v2).xray.core.app.observatory.OutboundStatusR\x06status\"\x9f\x01\n" +
"\x1bHealthPingMeasurementResult\x12\x10\n" +
"\x03all\x18\x01 \x01(\x03R\x03all\x12\x12\n" +
"\x04fail\x18\x02 \x01(\x03R\x04fail\x12\x1c\n" +
"\tdeviation\x18\x03 \x01(\x03R\tdeviation\x12\x18\n" +
"\aaverage\x18\x04 \x01(\x03R\aaverage\x12\x10\n" +
"\x03max\x18\x05 \x01(\x03R\x03max\x12\x10\n" +
"\x03min\x18\x06 \x01(\x03R\x03min\"\xae\x02\n" +
"\x0eOutboundStatus\x12\x14\n" +
"\x05alive\x18\x01 \x01(\bR\x05alive\x12\x14\n" +
"\x05delay\x18\x02 \x01(\x03R\x05delay\x12*\n" +
"\x11last_error_reason\x18\x03 \x01(\tR\x0flastErrorReason\x12!\n" +
"\foutbound_tag\x18\x04 \x01(\tR\voutboundTag\x12$\n" +
"\x0elast_seen_time\x18\x05 \x01(\x03R\flastSeenTime\x12\"\n" +
"\rlast_try_time\x18\x06 \x01(\x03R\vlastTryTime\x12W\n" +
"\vhealth_ping\x18\a \x01(\v26.xray.core.app.observatory.HealthPingMeasurementResultR\n" +
"healthPing\"e\n" +
"\vProbeResult\x12\x14\n" +
"\x05alive\x18\x01 \x01(\bR\x05alive\x12\x14\n" +
"\x05delay\x18\x02 \x01(\x03R\x05delay\x12*\n" +
"\x11last_error_reason\x18\x03 \x01(\tR\x0flastErrorReason\"2\n" +
"\tIntensity\x12%\n" +
"\x0eprobe_interval\x18\x01 \x01(\rR\rprobeInterval\"\xa6\x01\n" +
"\x06Config\x12)\n" +
"\x10subject_selector\x18\x02 \x03(\tR\x0fsubjectSelector\x12\x1b\n" +
"\tprobe_url\x18\x03 \x01(\tR\bprobeUrl\x12%\n" +
"\x0eprobe_interval\x18\x04 \x01(\x03R\rprobeInterval\x12-\n" +
"\x12enable_concurrency\x18\x05 \x01(\bR\x11enableConcurrencyB^\n" +
"\x18com.xray.app.observatoryP\x01Z)github.com/xtls/xray-core/app/observatory\xaa\x02\x14Xray.App.Observatoryb\x06proto3"
var (
file_app_observatory_config_proto_rawDescOnce sync.Once
file_app_observatory_config_proto_rawDescData = file_app_observatory_config_proto_rawDesc
file_app_observatory_config_proto_rawDescData []byte
)
func file_app_observatory_config_proto_rawDescGZIP() []byte {
file_app_observatory_config_proto_rawDescOnce.Do(func() {
file_app_observatory_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_observatory_config_proto_rawDescData)
file_app_observatory_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_observatory_config_proto_rawDesc), len(file_app_observatory_config_proto_rawDesc)))
})
return file_app_observatory_config_proto_rawDescData
}
@@ -551,7 +512,7 @@ func file_app_observatory_config_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_observatory_config_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_observatory_config_proto_rawDesc), len(file_app_observatory_config_proto_rawDesc)),
NumEnums: 0,
NumMessages: 6,
NumExtensions: 0,
@@ -562,7 +523,6 @@ func file_app_observatory_config_proto_init() {
MessageInfos: file_app_observatory_config_proto_msgTypes,
}.Build()
File_app_observatory_config_proto = out.File
file_app_observatory_config_proto_rawDesc = nil
file_app_observatory_config_proto_goTypes = nil
file_app_observatory_config_proto_depIdxs = nil
}
+24 -6
View File
@@ -5,6 +5,7 @@ import (
"net"
"net/http"
"net/url"
"slices"
"sort"
"sync"
"time"
@@ -15,6 +16,7 @@ import (
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal/done"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/common/utils"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/extension"
"github.com/xtls/xray-core/features/outbound"
@@ -69,13 +71,19 @@ func (o *Observer) background() {
outbounds := hs.Select(o.config.SubjectSelector)
o.updateStatus(outbounds)
o.clearRemovedOutbounds(outbounds)
sleepTime := time.Second * 10
if o.config.ProbeInterval != 0 {
sleepTime = time.Duration(o.config.ProbeInterval)
}
if len(outbounds) == 0 {
errors.LogWarning(o.ctx, "no outbound matches subjectSelector ", o.config.SubjectSelector)
time.Sleep(sleepTime)
continue
}
if !o.config.EnableConcurrency {
sort.Strings(outbounds)
for _, v := range outbounds {
@@ -110,11 +118,19 @@ func (o *Observer) background() {
}
}
func (o *Observer) updateStatus(outbounds []string) {
func (o *Observer) clearRemovedOutbounds(outbounds []string) {
o.statusLock.Lock()
defer o.statusLock.Unlock()
// TODO should remove old inbound that is removed
_ = outbounds
if len(o.status) == 0 {
return
}
var pruned []*OutboundStatus
for _, status := range o.status {
if slices.Contains(outbounds, status.OutboundTag) {
pruned = append(pruned, status)
}
}
o.status = pruned
}
func (o *Observer) probe(outbound string) ProbeResult {
@@ -162,7 +178,9 @@ func (o *Observer) probe(outbound string) ProbeResult {
if o.config.ProbeUrl != "" {
probeURL = o.config.ProbeUrl
}
response, err := httpClient.Get(probeURL)
req, _ := http.NewRequest(http.MethodGet, probeURL, nil)
utils.TryDefaultHeadersWith(req.Header, "nav")
response, err := httpClient.Do(req)
if err != nil {
return errors.New("outbound failed to relay connection").Base(err)
}
@@ -174,7 +192,7 @@ func (o *Observer) probe(outbound string) ProbeResult {
return nil
})
if err != nil {
var errorMessage = "the outbound " + outbound + " is dead: GET request failed:" + err.Error() + "with outbound handler report underlying connection failed"
errorMessage := "the outbound " + outbound + " is dead: GET request failed:" + err.Error() + "with outbound handler report underlying connection failed"
errors.LogInfoInner(o.ctx, errorCollectorForRequest.UnderlyingError(), errorMessage)
return ProbeResult{Alive: false, LastErrorReason: errorMessage}
}
+64
View File
@@ -0,0 +1,64 @@
package observatory
import "testing"
func TestObserverUpdateStatusPrunesStaleOutbounds(t *testing.T) {
observer := &Observer{
status: []*OutboundStatus{
{
OutboundTag: "keep",
Alive: true,
Delay: 42,
LastErrorReason: "",
LastSeenTime: 111,
LastTryTime: 222,
},
{
OutboundTag: "drop",
Alive: false,
Delay: 99999999,
LastErrorReason: "probe failed",
LastSeenTime: 333,
LastTryTime: 444,
},
},
}
observer.clearRemovedOutbounds([]string{"keep"})
if len(observer.status) != 1 {
t.Fatalf("expected 1 status after pruning, got %d", len(observer.status))
}
got := observer.status[0]
if got.OutboundTag != "keep" {
t.Fatalf("expected remaining status for keep, got %q", got.OutboundTag)
}
if !got.Alive {
t.Fatal("expected remaining status to preserve Alive field")
}
if got.Delay != 42 {
t.Fatalf("expected remaining status to preserve Delay, got %d", got.Delay)
}
if got.LastSeenTime != 111 {
t.Fatalf("expected remaining status to preserve LastSeenTime, got %d", got.LastSeenTime)
}
if got.LastTryTime != 222 {
t.Fatalf("expected remaining status to preserve LastTryTime, got %d", got.LastTryTime)
}
}
func TestObserverUpdateStatusClearsWhenNoOutboundsRemain(t *testing.T) {
observer := &Observer{
status: []*OutboundStatus{
{OutboundTag: "drop-1"},
{OutboundTag: "drop-2"},
},
}
observer.clearRemovedOutbounds(nil)
if len(observer.status) != 0 {
t.Fatalf("expected all statuses to be removed, got %d", len(observer.status))
}
}
+84 -130
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/policy/config.proto
package policy
@@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -21,11 +22,10 @@ const (
)
type Second struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
unknownFields protoimpl.UnknownFields
Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *Second) Reset() {
@@ -66,13 +66,12 @@ func (x *Second) GetValue() uint32 {
}
type Policy struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Timeout *Policy_Timeout `protobuf:"bytes,1,opt,name=timeout,proto3" json:"timeout,omitempty"`
Stats *Policy_Stats `protobuf:"bytes,2,opt,name=stats,proto3" json:"stats,omitempty"`
Buffer *Policy_Buffer `protobuf:"bytes,3,opt,name=buffer,proto3" json:"buffer,omitempty"`
unknownFields protoimpl.UnknownFields
Timeout *Policy_Timeout `protobuf:"bytes,1,opt,name=timeout,proto3" json:"timeout,omitempty"`
Stats *Policy_Stats `protobuf:"bytes,2,opt,name=stats,proto3" json:"stats,omitempty"`
Buffer *Policy_Buffer `protobuf:"bytes,3,opt,name=buffer,proto3" json:"buffer,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *Policy) Reset() {
@@ -127,11 +126,10 @@ func (x *Policy) GetBuffer() *Policy_Buffer {
}
type SystemPolicy struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Stats *SystemPolicy_Stats `protobuf:"bytes,1,opt,name=stats,proto3" json:"stats,omitempty"`
unknownFields protoimpl.UnknownFields
Stats *SystemPolicy_Stats `protobuf:"bytes,1,opt,name=stats,proto3" json:"stats,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *SystemPolicy) Reset() {
@@ -172,12 +170,11 @@ func (x *SystemPolicy) GetStats() *SystemPolicy_Stats {
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Level map[uint32]*Policy `protobuf:"bytes,1,rep,name=level,proto3" json:"level,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
System *SystemPolicy `protobuf:"bytes,2,opt,name=system,proto3" json:"system,omitempty"`
unknownFields protoimpl.UnknownFields
Level map[uint32]*Policy `protobuf:"bytes,1,rep,name=level,proto3" json:"level,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
System *SystemPolicy `protobuf:"bytes,2,opt,name=system,proto3" json:"system,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
@@ -226,14 +223,13 @@ func (x *Config) GetSystem() *SystemPolicy {
// Timeout is a message for timeout settings in various stages, in seconds.
type Policy_Timeout struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Handshake *Second `protobuf:"bytes,1,opt,name=handshake,proto3" json:"handshake,omitempty"`
ConnectionIdle *Second `protobuf:"bytes,2,opt,name=connection_idle,json=connectionIdle,proto3" json:"connection_idle,omitempty"`
UplinkOnly *Second `protobuf:"bytes,3,opt,name=uplink_only,json=uplinkOnly,proto3" json:"uplink_only,omitempty"`
DownlinkOnly *Second `protobuf:"bytes,4,opt,name=downlink_only,json=downlinkOnly,proto3" json:"downlink_only,omitempty"`
state protoimpl.MessageState `protogen:"open.v1"`
Handshake *Second `protobuf:"bytes,1,opt,name=handshake,proto3" json:"handshake,omitempty"`
ConnectionIdle *Second `protobuf:"bytes,2,opt,name=connection_idle,json=connectionIdle,proto3" json:"connection_idle,omitempty"`
UplinkOnly *Second `protobuf:"bytes,3,opt,name=uplink_only,json=uplinkOnly,proto3" json:"uplink_only,omitempty"`
DownlinkOnly *Second `protobuf:"bytes,4,opt,name=downlink_only,json=downlinkOnly,proto3" json:"downlink_only,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Policy_Timeout) Reset() {
@@ -295,13 +291,12 @@ func (x *Policy_Timeout) GetDownlinkOnly() *Second {
}
type Policy_Stats struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
UserUplink bool `protobuf:"varint,1,opt,name=user_uplink,json=userUplink,proto3" json:"user_uplink,omitempty"`
UserDownlink bool `protobuf:"varint,2,opt,name=user_downlink,json=userDownlink,proto3" json:"user_downlink,omitempty"`
UserOnline bool `protobuf:"varint,3,opt,name=user_online,json=userOnline,proto3" json:"user_online,omitempty"`
unknownFields protoimpl.UnknownFields
UserUplink bool `protobuf:"varint,1,opt,name=user_uplink,json=userUplink,proto3" json:"user_uplink,omitempty"`
UserDownlink bool `protobuf:"varint,2,opt,name=user_downlink,json=userDownlink,proto3" json:"user_downlink,omitempty"`
UserOnline bool `protobuf:"varint,3,opt,name=user_online,json=userOnline,proto3" json:"user_online,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *Policy_Stats) Reset() {
@@ -356,12 +351,11 @@ func (x *Policy_Stats) GetUserOnline() bool {
}
type Policy_Buffer struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState `protogen:"open.v1"`
// Buffer size per connection, in bytes. -1 for unlimited buffer.
Connection int32 `protobuf:"varint,1,opt,name=connection,proto3" json:"connection,omitempty"`
Connection int32 `protobuf:"varint,1,opt,name=connection,proto3" json:"connection,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Policy_Buffer) Reset() {
@@ -402,14 +396,13 @@ func (x *Policy_Buffer) GetConnection() int32 {
}
type SystemPolicy_Stats struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
InboundUplink bool `protobuf:"varint,1,opt,name=inbound_uplink,json=inboundUplink,proto3" json:"inbound_uplink,omitempty"`
InboundDownlink bool `protobuf:"varint,2,opt,name=inbound_downlink,json=inboundDownlink,proto3" json:"inbound_downlink,omitempty"`
OutboundUplink bool `protobuf:"varint,3,opt,name=outbound_uplink,json=outboundUplink,proto3" json:"outbound_uplink,omitempty"`
OutboundDownlink bool `protobuf:"varint,4,opt,name=outbound_downlink,json=outboundDownlink,proto3" json:"outbound_downlink,omitempty"`
state protoimpl.MessageState `protogen:"open.v1"`
InboundUplink bool `protobuf:"varint,1,opt,name=inbound_uplink,json=inboundUplink,proto3" json:"inbound_uplink,omitempty"`
InboundDownlink bool `protobuf:"varint,2,opt,name=inbound_downlink,json=inboundDownlink,proto3" json:"inbound_downlink,omitempty"`
OutboundUplink bool `protobuf:"varint,3,opt,name=outbound_uplink,json=outboundUplink,proto3" json:"outbound_uplink,omitempty"`
OutboundDownlink bool `protobuf:"varint,4,opt,name=outbound_downlink,json=outboundDownlink,proto3" json:"outbound_downlink,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SystemPolicy_Stats) Reset() {
@@ -472,93 +465,55 @@ func (x *SystemPolicy_Stats) GetOutboundDownlink() bool {
var File_app_policy_config_proto protoreflect.FileDescriptor
var file_app_policy_config_proto_rawDesc = []byte{
0x0a, 0x17, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x63, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x1e, 0x0a, 0x06, 0x53, 0x65,
0x63, 0x6f, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xc7, 0x04, 0x0a, 0x06, 0x50,
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x39, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e,
0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
0x12, 0x33, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63,
0x79, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05,
0x73, 0x74, 0x61, 0x74, 0x73, 0x12, 0x36, 0x0a, 0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x18,
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x42,
0x75, 0x66, 0x66, 0x65, 0x72, 0x52, 0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x1a, 0xfa, 0x01,
0x0a, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x35, 0x0a, 0x09, 0x68, 0x61, 0x6e,
0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53,
0x65, 0x63, 0x6f, 0x6e, 0x64, 0x52, 0x09, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65,
0x12, 0x40, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69,
0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x65, 0x63, 0x6f,
0x6e, 0x64, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64,
0x6c, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6f, 0x6e, 0x6c,
0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64,
0x52, 0x0a, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x3c, 0x0a, 0x0d,
0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x04, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70,
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x52, 0x0c, 0x64, 0x6f,
0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4f, 0x6e, 0x6c, 0x79, 0x1a, 0x6e, 0x0a, 0x05, 0x53, 0x74,
0x61, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x75, 0x70, 0x6c, 0x69,
0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x55, 0x70,
0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x64, 0x6f, 0x77,
0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x75, 0x73, 0x65,
0x72, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x73, 0x65,
0x72, 0x5f, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a,
0x75, 0x73, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x1a, 0x28, 0x0a, 0x06, 0x42, 0x75,
0x66, 0x66, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x22, 0xfb, 0x01, 0x0a, 0x0c, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50,
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x39, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x6f, 0x6c,
0x69, 0x63, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73,
0x1a, 0xaf, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e,
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01,
0x28, 0x08, 0x52, 0x0d, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x70, 0x6c, 0x69, 0x6e,
0x6b, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x64, 0x6f, 0x77,
0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x27, 0x0a, 0x0f,
0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18,
0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55,
0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e,
0x64, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69,
0x6e, 0x6b, 0x22, 0xcc, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x38, 0x0a,
0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79,
0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65,
0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x1a, 0x51,
0x0a, 0x0a, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d,
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e,
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
0x01, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 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, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79,
0xaa, 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x6f, 0x6c, 0x69,
0x63, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
const file_app_policy_config_proto_rawDesc = "" +
"\n" +
"\x17app/policy/config.proto\x12\x0fxray.app.policy\"\x1e\n" +
"\x06Second\x12\x14\n" +
"\x05value\x18\x01 \x01(\rR\x05value\"\xc7\x04\n" +
"\x06Policy\x129\n" +
"\atimeout\x18\x01 \x01(\v2\x1f.xray.app.policy.Policy.TimeoutR\atimeout\x123\n" +
"\x05stats\x18\x02 \x01(\v2\x1d.xray.app.policy.Policy.StatsR\x05stats\x126\n" +
"\x06buffer\x18\x03 \x01(\v2\x1e.xray.app.policy.Policy.BufferR\x06buffer\x1a\xfa\x01\n" +
"\aTimeout\x125\n" +
"\thandshake\x18\x01 \x01(\v2\x17.xray.app.policy.SecondR\thandshake\x12@\n" +
"\x0fconnection_idle\x18\x02 \x01(\v2\x17.xray.app.policy.SecondR\x0econnectionIdle\x128\n" +
"\vuplink_only\x18\x03 \x01(\v2\x17.xray.app.policy.SecondR\n" +
"uplinkOnly\x12<\n" +
"\rdownlink_only\x18\x04 \x01(\v2\x17.xray.app.policy.SecondR\fdownlinkOnly\x1an\n" +
"\x05Stats\x12\x1f\n" +
"\vuser_uplink\x18\x01 \x01(\bR\n" +
"userUplink\x12#\n" +
"\ruser_downlink\x18\x02 \x01(\bR\fuserDownlink\x12\x1f\n" +
"\vuser_online\x18\x03 \x01(\bR\n" +
"userOnline\x1a(\n" +
"\x06Buffer\x12\x1e\n" +
"\n" +
"connection\x18\x01 \x01(\x05R\n" +
"connection\"\xfb\x01\n" +
"\fSystemPolicy\x129\n" +
"\x05stats\x18\x01 \x01(\v2#.xray.app.policy.SystemPolicy.StatsR\x05stats\x1a\xaf\x01\n" +
"\x05Stats\x12%\n" +
"\x0einbound_uplink\x18\x01 \x01(\bR\rinboundUplink\x12)\n" +
"\x10inbound_downlink\x18\x02 \x01(\bR\x0finboundDownlink\x12'\n" +
"\x0foutbound_uplink\x18\x03 \x01(\bR\x0eoutboundUplink\x12+\n" +
"\x11outbound_downlink\x18\x04 \x01(\bR\x10outboundDownlink\"\xcc\x01\n" +
"\x06Config\x128\n" +
"\x05level\x18\x01 \x03(\v2\".xray.app.policy.Config.LevelEntryR\x05level\x125\n" +
"\x06system\x18\x02 \x01(\v2\x1d.xray.app.policy.SystemPolicyR\x06system\x1aQ\n" +
"\n" +
"LevelEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\rR\x03key\x12-\n" +
"\x05value\x18\x02 \x01(\v2\x17.xray.app.policy.PolicyR\x05value:\x028\x01BO\n" +
"\x13com.xray.app.policyP\x01Z$github.com/xtls/xray-core/app/policy\xaa\x02\x0fXray.App.Policyb\x06proto3"
var (
file_app_policy_config_proto_rawDescOnce sync.Once
file_app_policy_config_proto_rawDescData = file_app_policy_config_proto_rawDesc
file_app_policy_config_proto_rawDescData []byte
)
func file_app_policy_config_proto_rawDescGZIP() []byte {
file_app_policy_config_proto_rawDescOnce.Do(func() {
file_app_policy_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_policy_config_proto_rawDescData)
file_app_policy_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_policy_config_proto_rawDesc), len(file_app_policy_config_proto_rawDesc)))
})
return file_app_policy_config_proto_rawDescData
}
@@ -603,7 +558,7 @@ func file_app_policy_config_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_policy_config_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_policy_config_proto_rawDesc), len(file_app_policy_config_proto_rawDesc)),
NumEnums: 0,
NumMessages: 9,
NumExtensions: 0,
@@ -614,7 +569,6 @@ func file_app_policy_config_proto_init() {
MessageInfos: file_app_policy_config_proto_msgTypes,
}.Build()
File_app_policy_config_proto = out.File
file_app_policy_config_proto_rawDesc = nil
file_app_policy_config_proto_goTypes = nil
file_app_policy_config_proto_depIdxs = nil
}
+1 -1
View File
@@ -138,7 +138,7 @@ func (s *handlerServer) GetInboundUsers(ctx context.Context, request *GetInbound
if len(request.Email) > 0 {
return &GetInboundUserResponse{Users: []*protocol.User{protocol.ToProtoUser(um.GetUser(ctx, request.Email))}}, nil
}
var result = make([]*protocol.User, 0, 100)
result := make([]*protocol.User, 0, 100)
users := um.GetUsers(ctx)
for _, u := range users {
result = append(result, protocol.ToProtoUser(u))
+124 -249
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/proxyman/command/command.proto
package command
@@ -14,6 +14,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -24,11 +25,10 @@ const (
)
type AddUserOperation struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
User *protocol.User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
unknownFields protoimpl.UnknownFields
User *protocol.User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *AddUserOperation) Reset() {
@@ -69,11 +69,10 @@ func (x *AddUserOperation) GetUser() *protocol.User {
}
type RemoveUserOperation struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
unknownFields protoimpl.UnknownFields
Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *RemoveUserOperation) Reset() {
@@ -114,11 +113,10 @@ func (x *RemoveUserOperation) GetEmail() string {
}
type AddInboundRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Inbound *core.InboundHandlerConfig `protobuf:"bytes,1,opt,name=inbound,proto3" json:"inbound,omitempty"`
unknownFields protoimpl.UnknownFields
Inbound *core.InboundHandlerConfig `protobuf:"bytes,1,opt,name=inbound,proto3" json:"inbound,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *AddInboundRequest) Reset() {
@@ -159,9 +157,9 @@ func (x *AddInboundRequest) GetInbound() *core.InboundHandlerConfig {
}
type AddInboundResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AddInboundResponse) Reset() {
@@ -195,11 +193,10 @@ func (*AddInboundResponse) Descriptor() ([]byte, []int) {
}
type RemoveInboundRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *RemoveInboundRequest) Reset() {
@@ -240,9 +237,9 @@ func (x *RemoveInboundRequest) GetTag() string {
}
type RemoveInboundResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RemoveInboundResponse) Reset() {
@@ -276,12 +273,11 @@ func (*RemoveInboundResponse) Descriptor() ([]byte, []int) {
}
type AlterInboundRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Operation *serial.TypedMessage `protobuf:"bytes,2,opt,name=operation,proto3" json:"operation,omitempty"`
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Operation *serial.TypedMessage `protobuf:"bytes,2,opt,name=operation,proto3" json:"operation,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *AlterInboundRequest) Reset() {
@@ -329,9 +325,9 @@ func (x *AlterInboundRequest) GetOperation() *serial.TypedMessage {
}
type AlterInboundResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AlterInboundResponse) Reset() {
@@ -365,11 +361,10 @@ func (*AlterInboundResponse) Descriptor() ([]byte, []int) {
}
type ListInboundsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
IsOnlyTags bool `protobuf:"varint,1,opt,name=isOnlyTags,proto3" json:"isOnlyTags,omitempty"`
unknownFields protoimpl.UnknownFields
IsOnlyTags bool `protobuf:"varint,1,opt,name=isOnlyTags,proto3" json:"isOnlyTags,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *ListInboundsRequest) Reset() {
@@ -410,11 +405,10 @@ func (x *ListInboundsRequest) GetIsOnlyTags() bool {
}
type ListInboundsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Inbounds []*core.InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbounds,proto3" json:"inbounds,omitempty"`
unknownFields protoimpl.UnknownFields
Inbounds []*core.InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbounds,proto3" json:"inbounds,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *ListInboundsResponse) Reset() {
@@ -455,12 +449,11 @@ func (x *ListInboundsResponse) GetInbounds() []*core.InboundHandlerConfig {
}
type GetInboundUserRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *GetInboundUserRequest) Reset() {
@@ -508,11 +501,10 @@ func (x *GetInboundUserRequest) GetEmail() string {
}
type GetInboundUserResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Users []*protocol.User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"`
unknownFields protoimpl.UnknownFields
Users []*protocol.User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *GetInboundUserResponse) Reset() {
@@ -553,11 +545,10 @@ func (x *GetInboundUserResponse) GetUsers() []*protocol.User {
}
type GetInboundUsersCountResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Count int64 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"`
unknownFields protoimpl.UnknownFields
Count int64 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *GetInboundUsersCountResponse) Reset() {
@@ -598,11 +589,10 @@ func (x *GetInboundUsersCountResponse) GetCount() int64 {
}
type AddOutboundRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Outbound *core.OutboundHandlerConfig `protobuf:"bytes,1,opt,name=outbound,proto3" json:"outbound,omitempty"`
unknownFields protoimpl.UnknownFields
Outbound *core.OutboundHandlerConfig `protobuf:"bytes,1,opt,name=outbound,proto3" json:"outbound,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *AddOutboundRequest) Reset() {
@@ -643,9 +633,9 @@ func (x *AddOutboundRequest) GetOutbound() *core.OutboundHandlerConfig {
}
type AddOutboundResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AddOutboundResponse) Reset() {
@@ -679,11 +669,10 @@ func (*AddOutboundResponse) Descriptor() ([]byte, []int) {
}
type RemoveOutboundRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *RemoveOutboundRequest) Reset() {
@@ -724,9 +713,9 @@ func (x *RemoveOutboundRequest) GetTag() string {
}
type RemoveOutboundResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RemoveOutboundResponse) Reset() {
@@ -760,12 +749,11 @@ func (*RemoveOutboundResponse) Descriptor() ([]byte, []int) {
}
type AlterOutboundRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Operation *serial.TypedMessage `protobuf:"bytes,2,opt,name=operation,proto3" json:"operation,omitempty"`
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Operation *serial.TypedMessage `protobuf:"bytes,2,opt,name=operation,proto3" json:"operation,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *AlterOutboundRequest) Reset() {
@@ -813,9 +801,9 @@ func (x *AlterOutboundRequest) GetOperation() *serial.TypedMessage {
}
type AlterOutboundResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AlterOutboundResponse) Reset() {
@@ -849,9 +837,9 @@ func (*AlterOutboundResponse) Descriptor() ([]byte, []int) {
}
type ListOutboundsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListOutboundsRequest) Reset() {
@@ -885,11 +873,10 @@ func (*ListOutboundsRequest) Descriptor() ([]byte, []int) {
}
type ListOutboundsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Outbounds []*core.OutboundHandlerConfig `protobuf:"bytes,1,rep,name=outbounds,proto3" json:"outbounds,omitempty"`
unknownFields protoimpl.UnknownFields
Outbounds []*core.OutboundHandlerConfig `protobuf:"bytes,1,rep,name=outbounds,proto3" json:"outbounds,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *ListOutboundsResponse) Reset() {
@@ -930,9 +917,9 @@ func (x *ListOutboundsResponse) GetOutbounds() []*core.OutboundHandlerConfig {
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
@@ -967,183 +954,72 @@ func (*Config) Descriptor() ([]byte, []int) {
var File_app_proxyman_command_command_proto protoreflect.FileDescriptor
var file_app_proxyman_command_command_proto_rawDesc = []byte{
0x0a, 0x22, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2f, 0x63,
0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 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, 0x1a,
0x1a, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
0x2f, 0x75, 0x73, 0x65, 0x72, 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, 0x1a, 0x11,
0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x22, 0x42, 0x0a, 0x10, 0x41, 0x64, 0x64, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20,
0x01, 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,
0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x2b, 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x55,
0x73, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05,
0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61,
0x69, 0x6c, 0x22, 0x4e, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x75,
0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 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, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x75,
0x6e, 0x64, 0x22, 0x14, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f,
0x76, 0x65, 0x49, 0x6e, 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, 0x17, 0x0a, 0x15, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x49, 0x6e, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, 0x0a, 0x13, 0x41,
0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 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, 0x16, 0x0a, 0x14, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x62,
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, 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, 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, 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,
0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55,
0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 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, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e,
0x64, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
0x83, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x73,
0x65, 0x72, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 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, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55,
0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 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, 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, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x4f, 0x75, 0x74, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x12, 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, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 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, 0x64, 0x64, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x77, 0x0a, 0x0e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4f,
0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 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, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75,
0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 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, 0x4f, 0x75, 0x74, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74,
0x0a, 0x0d, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 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, 0x41, 0x6c, 0x74, 0x65,
0x72, 0x4f, 0x75, 0x74, 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, 0x6c, 0x74,
0x65, 0x72, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
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,
}
const file_app_proxyman_command_command_proto_rawDesc = "" +
"\n" +
"\"app/proxyman/command/command.proto\x12\x19xray.app.proxyman.command\x1a\x1acommon/protocol/user.proto\x1a!common/serial/typed_message.proto\x1a\x11core/config.proto\"B\n" +
"\x10AddUserOperation\x12.\n" +
"\x04user\x18\x01 \x01(\v2\x1a.xray.common.protocol.UserR\x04user\"+\n" +
"\x13RemoveUserOperation\x12\x14\n" +
"\x05email\x18\x01 \x01(\tR\x05email\"N\n" +
"\x11AddInboundRequest\x129\n" +
"\ainbound\x18\x01 \x01(\v2\x1f.xray.core.InboundHandlerConfigR\ainbound\"\x14\n" +
"\x12AddInboundResponse\"(\n" +
"\x14RemoveInboundRequest\x12\x10\n" +
"\x03tag\x18\x01 \x01(\tR\x03tag\"\x17\n" +
"\x15RemoveInboundResponse\"g\n" +
"\x13AlterInboundRequest\x12\x10\n" +
"\x03tag\x18\x01 \x01(\tR\x03tag\x12>\n" +
"\toperation\x18\x02 \x01(\v2 .xray.common.serial.TypedMessageR\toperation\"\x16\n" +
"\x14AlterInboundResponse\"5\n" +
"\x13ListInboundsRequest\x12\x1e\n" +
"\n" +
"isOnlyTags\x18\x01 \x01(\bR\n" +
"isOnlyTags\"S\n" +
"\x14ListInboundsResponse\x12;\n" +
"\binbounds\x18\x01 \x03(\v2\x1f.xray.core.InboundHandlerConfigR\binbounds\"?\n" +
"\x15GetInboundUserRequest\x12\x10\n" +
"\x03tag\x18\x01 \x01(\tR\x03tag\x12\x14\n" +
"\x05email\x18\x02 \x01(\tR\x05email\"J\n" +
"\x16GetInboundUserResponse\x120\n" +
"\x05users\x18\x01 \x03(\v2\x1a.xray.common.protocol.UserR\x05users\"4\n" +
"\x1cGetInboundUsersCountResponse\x12\x14\n" +
"\x05count\x18\x01 \x01(\x03R\x05count\"R\n" +
"\x12AddOutboundRequest\x12<\n" +
"\boutbound\x18\x01 \x01(\v2 .xray.core.OutboundHandlerConfigR\boutbound\"\x15\n" +
"\x13AddOutboundResponse\")\n" +
"\x15RemoveOutboundRequest\x12\x10\n" +
"\x03tag\x18\x01 \x01(\tR\x03tag\"\x18\n" +
"\x16RemoveOutboundResponse\"h\n" +
"\x14AlterOutboundRequest\x12\x10\n" +
"\x03tag\x18\x01 \x01(\tR\x03tag\x12>\n" +
"\toperation\x18\x02 \x01(\v2 .xray.common.serial.TypedMessageR\toperation\"\x17\n" +
"\x15AlterOutboundResponse\"\x16\n" +
"\x14ListOutboundsRequest\"W\n" +
"\x15ListOutboundsResponse\x12>\n" +
"\toutbounds\x18\x01 \x03(\v2 .xray.core.OutboundHandlerConfigR\toutbounds\"\b\n" +
"\x06Config2\xae\t\n" +
"\x0eHandlerService\x12k\n" +
"\n" +
"AddInbound\x12,.xray.app.proxyman.command.AddInboundRequest\x1a-.xray.app.proxyman.command.AddInboundResponse\"\x00\x12t\n" +
"\rRemoveInbound\x12/.xray.app.proxyman.command.RemoveInboundRequest\x1a0.xray.app.proxyman.command.RemoveInboundResponse\"\x00\x12q\n" +
"\fAlterInbound\x12..xray.app.proxyman.command.AlterInboundRequest\x1a/.xray.app.proxyman.command.AlterInboundResponse\"\x00\x12q\n" +
"\fListInbounds\x12..xray.app.proxyman.command.ListInboundsRequest\x1a/.xray.app.proxyman.command.ListInboundsResponse\"\x00\x12x\n" +
"\x0fGetInboundUsers\x120.xray.app.proxyman.command.GetInboundUserRequest\x1a1.xray.app.proxyman.command.GetInboundUserResponse\"\x00\x12\x83\x01\n" +
"\x14GetInboundUsersCount\x120.xray.app.proxyman.command.GetInboundUserRequest\x1a7.xray.app.proxyman.command.GetInboundUsersCountResponse\"\x00\x12n\n" +
"\vAddOutbound\x12-.xray.app.proxyman.command.AddOutboundRequest\x1a..xray.app.proxyman.command.AddOutboundResponse\"\x00\x12w\n" +
"\x0eRemoveOutbound\x120.xray.app.proxyman.command.RemoveOutboundRequest\x1a1.xray.app.proxyman.command.RemoveOutboundResponse\"\x00\x12t\n" +
"\rAlterOutbound\x12/.xray.app.proxyman.command.AlterOutboundRequest\x1a0.xray.app.proxyman.command.AlterOutboundResponse\"\x00\x12t\n" +
"\rListOutbounds\x12/.xray.app.proxyman.command.ListOutboundsRequest\x1a0.xray.app.proxyman.command.ListOutboundsResponse\"\x00Bm\n" +
"\x1dcom.xray.app.proxyman.commandP\x01Z.github.com/xtls/xray-core/app/proxyman/command\xaa\x02\x19Xray.App.Proxyman.Commandb\x06proto3"
var (
file_app_proxyman_command_command_proto_rawDescOnce sync.Once
file_app_proxyman_command_command_proto_rawDescData = file_app_proxyman_command_command_proto_rawDesc
file_app_proxyman_command_command_proto_rawDescData []byte
)
func file_app_proxyman_command_command_proto_rawDescGZIP() []byte {
file_app_proxyman_command_command_proto_rawDescOnce.Do(func() {
file_app_proxyman_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_proxyman_command_command_proto_rawDescData)
file_app_proxyman_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_proxyman_command_command_proto_rawDesc), len(file_app_proxyman_command_command_proto_rawDesc)))
})
return file_app_proxyman_command_command_proto_rawDescData
}
@@ -1222,7 +1098,7 @@ func file_app_proxyman_command_command_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_proxyman_command_command_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_proxyman_command_command_proto_rawDesc), len(file_app_proxyman_command_command_proto_rawDesc)),
NumEnums: 0,
NumMessages: 22,
NumExtensions: 0,
@@ -1233,7 +1109,6 @@ func file_app_proxyman_command_command_proto_init() {
MessageInfos: file_app_proxyman_command_command_proto_msgTypes,
}.Build()
File_app_proxyman_command_command_proto = out.File
file_app_proxyman_command_command_proto_rawDesc = nil
file_app_proxyman_command_command_proto_goTypes = nil
file_app_proxyman_command_command_proto_depIdxs = nil
}
+13 -13
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.28.2
// - protoc-gen-go-grpc v1.6.0
// - protoc v6.33.5
// source: app/proxyman/command/command.proto
package command
@@ -180,34 +180,34 @@ type HandlerServiceServer interface {
type UnimplementedHandlerServiceServer struct{}
func (UnimplementedHandlerServiceServer) AddInbound(context.Context, *AddInboundRequest) (*AddInboundResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddInbound not implemented")
return nil, status.Error(codes.Unimplemented, "method AddInbound not implemented")
}
func (UnimplementedHandlerServiceServer) RemoveInbound(context.Context, *RemoveInboundRequest) (*RemoveInboundResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RemoveInbound not implemented")
return nil, status.Error(codes.Unimplemented, "method RemoveInbound not implemented")
}
func (UnimplementedHandlerServiceServer) AlterInbound(context.Context, *AlterInboundRequest) (*AlterInboundResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AlterInbound not implemented")
return nil, status.Error(codes.Unimplemented, "method AlterInbound not implemented")
}
func (UnimplementedHandlerServiceServer) ListInbounds(context.Context, *ListInboundsRequest) (*ListInboundsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListInbounds not implemented")
return nil, status.Error(codes.Unimplemented, "method ListInbounds not implemented")
}
func (UnimplementedHandlerServiceServer) GetInboundUsers(context.Context, *GetInboundUserRequest) (*GetInboundUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetInboundUsers not implemented")
return nil, status.Error(codes.Unimplemented, "method GetInboundUsers not implemented")
}
func (UnimplementedHandlerServiceServer) GetInboundUsersCount(context.Context, *GetInboundUserRequest) (*GetInboundUsersCountResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetInboundUsersCount not implemented")
return nil, status.Error(codes.Unimplemented, "method GetInboundUsersCount not implemented")
}
func (UnimplementedHandlerServiceServer) AddOutbound(context.Context, *AddOutboundRequest) (*AddOutboundResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddOutbound not implemented")
return nil, status.Error(codes.Unimplemented, "method AddOutbound not implemented")
}
func (UnimplementedHandlerServiceServer) RemoveOutbound(context.Context, *RemoveOutboundRequest) (*RemoveOutboundResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RemoveOutbound not implemented")
return nil, status.Error(codes.Unimplemented, "method RemoveOutbound not implemented")
}
func (UnimplementedHandlerServiceServer) AlterOutbound(context.Context, *AlterOutboundRequest) (*AlterOutboundResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AlterOutbound not implemented")
return nil, status.Error(codes.Unimplemented, "method AlterOutbound not implemented")
}
func (UnimplementedHandlerServiceServer) ListOutbounds(context.Context, *ListOutboundsRequest) (*ListOutboundsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListOutbounds not implemented")
return nil, status.Error(codes.Unimplemented, "method ListOutbounds not implemented")
}
func (UnimplementedHandlerServiceServer) mustEmbedUnimplementedHandlerServiceServer() {}
func (UnimplementedHandlerServiceServer) testEmbeddedByValue() {}
@@ -220,7 +220,7 @@ type UnsafeHandlerServiceServer interface {
}
func RegisterHandlerServiceServer(s grpc.ServiceRegistrar, srv HandlerServiceServer) {
// If the following call pancis, it indicates UnimplementedHandlerServiceServer was
// If the following call panics, it indicates UnimplementedHandlerServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
+33
View File
@@ -1 +1,34 @@
package proxyman
import (
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/session"
)
func BuildSniffingRequest(config *SniffingConfig) (session.SniffingRequest, error) {
if config == nil {
return session.SniffingRequest{}, nil
}
request := session.SniffingRequest{
Enabled: config.Enabled,
OverrideDestinationForProtocol: config.DestinationOverride,
MetadataOnly: config.MetadataOnly,
RouteOnly: config.RouteOnly,
}
if len(config.DomainsExcluded) > 0 {
excludeForDomain, err := geodata.DomainReg.BuildDomainMatcher(config.DomainsExcluded)
if err != nil {
return session.SniffingRequest{}, err
}
request.ExcludeForDomain = excludeForDomain
}
if len(config.IpsExcluded) > 0 {
excludeForIP, err := geodata.IPReg.BuildIPMatcher(config.IpsExcluded)
if err != nil {
return session.SniffingRequest{}, err
}
request.ExcludeForIP = excludeForIP
}
return request, nil
}
+104 -165
View File
@@ -1,12 +1,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/proxyman/config.proto
package proxyman
import (
geodata "github.com/xtls/xray-core/common/geodata"
net "github.com/xtls/xray-core/common/net"
serial "github.com/xtls/xray-core/common/serial"
internet "github.com/xtls/xray-core/transport/internet"
@@ -14,6 +15,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -24,9 +26,9 @@ const (
)
type InboundConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *InboundConfig) Reset() {
@@ -60,21 +62,21 @@ func (*InboundConfig) Descriptor() ([]byte, []int) {
}
type SniffingConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState `protogen:"open.v1"`
// Whether or not to enable content sniffing on an inbound connection.
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
// Override target destination if sniff'ed protocol is in the given list.
// Supported values are "http", "tls", "fakedns".
DestinationOverride []string `protobuf:"bytes,2,rep,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"`
DomainsExcluded []string `protobuf:"bytes,3,rep,name=domains_excluded,json=domainsExcluded,proto3" json:"domains_excluded,omitempty"`
DestinationOverride []string `protobuf:"bytes,2,rep,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"`
DomainsExcluded []*geodata.DomainRule `protobuf:"bytes,3,rep,name=domains_excluded,json=domainsExcluded,proto3" json:"domains_excluded,omitempty"`
IpsExcluded []*geodata.IPRule `protobuf:"bytes,6,rep,name=ips_excluded,json=ipsExcluded,proto3" json:"ips_excluded,omitempty"`
// Whether should only try to sniff metadata without waiting for client input.
// Can be used to support SMTP like protocol where server send the first
// message.
MetadataOnly bool `protobuf:"varint,4,opt,name=metadata_only,json=metadataOnly,proto3" json:"metadata_only,omitempty"`
RouteOnly bool `protobuf:"varint,5,opt,name=route_only,json=routeOnly,proto3" json:"route_only,omitempty"`
MetadataOnly bool `protobuf:"varint,4,opt,name=metadata_only,json=metadataOnly,proto3" json:"metadata_only,omitempty"`
RouteOnly bool `protobuf:"varint,5,opt,name=route_only,json=routeOnly,proto3" json:"route_only,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SniffingConfig) Reset() {
@@ -121,13 +123,20 @@ func (x *SniffingConfig) GetDestinationOverride() []string {
return nil
}
func (x *SniffingConfig) GetDomainsExcluded() []string {
func (x *SniffingConfig) GetDomainsExcluded() []*geodata.DomainRule {
if x != nil {
return x.DomainsExcluded
}
return nil
}
func (x *SniffingConfig) GetIpsExcluded() []*geodata.IPRule {
if x != nil {
return x.IpsExcluded
}
return nil
}
func (x *SniffingConfig) GetMetadataOnly() bool {
if x != nil {
return x.MetadataOnly
@@ -143,10 +152,7 @@ func (x *SniffingConfig) GetRouteOnly() bool {
}
type ReceiverConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState `protogen:"open.v1"`
// PortList specifies the ports which the Receiver should listen on.
PortList *net.PortList `protobuf:"bytes,1,opt,name=port_list,json=portList,proto3" json:"port_list,omitempty"`
// Listen specifies the IP address that the Receiver should listen on.
@@ -154,6 +160,8 @@ type ReceiverConfig struct {
StreamSettings *internet.StreamConfig `protobuf:"bytes,3,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"`
ReceiveOriginalDestination bool `protobuf:"varint,4,opt,name=receive_original_destination,json=receiveOriginalDestination,proto3" json:"receive_original_destination,omitempty"`
SniffingSettings *SniffingConfig `protobuf:"bytes,6,opt,name=sniffing_settings,json=sniffingSettings,proto3" json:"sniffing_settings,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ReceiverConfig) Reset() {
@@ -222,13 +230,12 @@ func (x *ReceiverConfig) GetSniffingSettings() *SniffingConfig {
}
type InboundHandlerConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
ReceiverSettings *serial.TypedMessage `protobuf:"bytes,2,opt,name=receiver_settings,json=receiverSettings,proto3" json:"receiver_settings,omitempty"`
ProxySettings *serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"`
state protoimpl.MessageState `protogen:"open.v1"`
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
ReceiverSettings *serial.TypedMessage `protobuf:"bytes,2,opt,name=receiver_settings,json=receiverSettings,proto3" json:"receiver_settings,omitempty"`
ProxySettings *serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *InboundHandlerConfig) Reset() {
@@ -283,9 +290,9 @@ func (x *InboundHandlerConfig) GetProxySettings() *serial.TypedMessage {
}
type OutboundConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *OutboundConfig) Reset() {
@@ -319,10 +326,7 @@ func (*OutboundConfig) Descriptor() ([]byte, []int) {
}
type SenderConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState `protogen:"open.v1"`
// 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"`
@@ -330,6 +334,8 @@ type SenderConfig struct {
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"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SenderConfig) Reset() {
@@ -405,10 +411,7 @@ func (x *SenderConfig) GetTargetStrategy() internet.DomainStrategy {
}
type MultiplexingConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState `protogen:"open.v1"`
// Whether or not Mux is enabled.
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
// Max number of concurrent connections that one Mux connection can handle.
@@ -417,6 +420,8 @@ type MultiplexingConfig struct {
XudpConcurrency int32 `protobuf:"varint,3,opt,name=xudpConcurrency,proto3" json:"xudpConcurrency,omitempty"`
// "reject" (default), "allow" or "skip".
XudpProxyUDP443 string `protobuf:"bytes,4,opt,name=xudpProxyUDP443,proto3" json:"xudpProxyUDP443,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *MultiplexingConfig) Reset() {
@@ -479,120 +484,51 @@ func (x *MultiplexingConfig) GetXudpProxyUDP443() string {
var File_app_proxyman_config_proto protoreflect.FileDescriptor
var file_app_proxyman_config_proto_rawDesc = []byte{
0x0a, 0x19, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2f, 0x63,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x1a, 0x18,
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x64, 0x64, 0x72, 0x65,
0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
0x1f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x0f, 0x0a, 0x0d, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x22, 0xcc, 0x01, 0x0a, 0x0e, 0x53, 0x6e, 0x69, 0x66, 0x66, 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, 0x31, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72,
0x72, 0x69, 0x64, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x5f,
0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f,
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x12,
0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6f, 0x6e, 0x6c, 0x79,
0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x6f, 0x6e,
0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x4f,
0x6e, 0x6c, 0x79, 0x22, 0xe5, 0x02, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c,
0x69, 0x73, 0x74, 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, 0x50, 0x6f, 0x72, 0x74,
0x4c, 0x69, 0x73, 0x74, 0x52, 0x08, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x33,
0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x02, 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, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x6c, 0x69, 0x73,
0x74, 0x65, 0x6e, 0x12, 0x4e, 0x0a, 0x0f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 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, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x74, 0x74, 0x69,
0x6e, 0x67, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x6f,
0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72, 0x65, 0x63, 0x65, 0x69,
0x76, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x11, 0x73, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e,
0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78,
0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x53, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x52, 0x10, 0x73, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74,
0x74, 0x69, 0x6e, 0x67, 0x73, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0xc0, 0x01, 0x0a, 0x14,
0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x4d, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76,
0x65, 0x72, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 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, 0x10, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x53, 0x65, 0x74,
0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73,
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 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,
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, 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, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x03, 0x76, 0x69, 0x61,
0x12, 0x4e, 0x0a, 0x0f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69,
0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 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, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x52, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
0x12, 0x4b, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 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, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d,
0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x54, 0x0a,
0x12, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69,
0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4d, 0x75,
0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 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, 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,
}
const file_app_proxyman_config_proto_rawDesc = "" +
"\n" +
"\x19app/proxyman/config.proto\x12\x11xray.app.proxyman\x1a\x18common/net/address.proto\x1a\x15common/net/port.proto\x1a\x1ftransport/internet/config.proto\x1a!common/serial/typed_message.proto\x1a\x1bcommon/geodata/geodat.proto\"\x0f\n" +
"\rInboundConfig\"\xad\x02\n" +
"\x0eSniffingConfig\x12\x18\n" +
"\aenabled\x18\x01 \x01(\bR\aenabled\x121\n" +
"\x14destination_override\x18\x02 \x03(\tR\x13destinationOverride\x12J\n" +
"\x10domains_excluded\x18\x03 \x03(\v2\x1f.xray.common.geodata.DomainRuleR\x0fdomainsExcluded\x12>\n" +
"\fips_excluded\x18\x06 \x03(\v2\x1b.xray.common.geodata.IPRuleR\vipsExcluded\x12#\n" +
"\rmetadata_only\x18\x04 \x01(\bR\fmetadataOnly\x12\x1d\n" +
"\n" +
"route_only\x18\x05 \x01(\bR\trouteOnly\"\xe5\x02\n" +
"\x0eReceiverConfig\x126\n" +
"\tport_list\x18\x01 \x01(\v2\x19.xray.common.net.PortListR\bportList\x123\n" +
"\x06listen\x18\x02 \x01(\v2\x1b.xray.common.net.IPOrDomainR\x06listen\x12N\n" +
"\x0fstream_settings\x18\x03 \x01(\v2%.xray.transport.internet.StreamConfigR\x0estreamSettings\x12@\n" +
"\x1creceive_original_destination\x18\x04 \x01(\bR\x1areceiveOriginalDestination\x12N\n" +
"\x11sniffing_settings\x18\x06 \x01(\v2!.xray.app.proxyman.SniffingConfigR\x10sniffingSettingsJ\x04\b\x05\x10\x06\"\xc0\x01\n" +
"\x14InboundHandlerConfig\x12\x10\n" +
"\x03tag\x18\x01 \x01(\tR\x03tag\x12M\n" +
"\x11receiver_settings\x18\x02 \x01(\v2 .xray.common.serial.TypedMessageR\x10receiverSettings\x12G\n" +
"\x0eproxy_settings\x18\x03 \x01(\v2 .xray.common.serial.TypedMessageR\rproxySettings\"\x10\n" +
"\x0eOutboundConfig\"\x9d\x03\n" +
"\fSenderConfig\x12-\n" +
"\x03via\x18\x01 \x01(\v2\x1b.xray.common.net.IPOrDomainR\x03via\x12N\n" +
"\x0fstream_settings\x18\x02 \x01(\v2%.xray.transport.internet.StreamConfigR\x0estreamSettings\x12K\n" +
"\x0eproxy_settings\x18\x03 \x01(\v2$.xray.transport.internet.ProxyConfigR\rproxySettings\x12T\n" +
"\x12multiplex_settings\x18\x04 \x01(\v2%.xray.app.proxyman.MultiplexingConfigR\x11multiplexSettings\x12\x19\n" +
"\bvia_cidr\x18\x05 \x01(\tR\aviaCidr\x12P\n" +
"\x0ftarget_strategy\x18\x06 \x01(\x0e2'.xray.transport.internet.DomainStrategyR\x0etargetStrategy\"\xa4\x01\n" +
"\x12MultiplexingConfig\x12\x18\n" +
"\aenabled\x18\x01 \x01(\bR\aenabled\x12 \n" +
"\vconcurrency\x18\x02 \x01(\x05R\vconcurrency\x12(\n" +
"\x0fxudpConcurrency\x18\x03 \x01(\x05R\x0fxudpConcurrency\x12(\n" +
"\x0fxudpProxyUDP443\x18\x04 \x01(\tR\x0fxudpProxyUDP443BU\n" +
"\x15com.xray.app.proxymanP\x01Z&github.com/xtls/xray-core/app/proxyman\xaa\x02\x11Xray.App.Proxymanb\x06proto3"
var (
file_app_proxyman_config_proto_rawDescOnce sync.Once
file_app_proxyman_config_proto_rawDescData = file_app_proxyman_config_proto_rawDesc
file_app_proxyman_config_proto_rawDescData []byte
)
func file_app_proxyman_config_proto_rawDescGZIP() []byte {
file_app_proxyman_config_proto_rawDescOnce.Do(func() {
file_app_proxyman_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_proxyman_config_proto_rawDescData)
file_app_proxyman_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_proxyman_config_proto_rawDesc), len(file_app_proxyman_config_proto_rawDesc)))
})
return file_app_proxyman_config_proto_rawDescData
}
@@ -606,30 +542,34 @@ var file_app_proxyman_config_proto_goTypes = []any{
(*OutboundConfig)(nil), // 4: xray.app.proxyman.OutboundConfig
(*SenderConfig)(nil), // 5: xray.app.proxyman.SenderConfig
(*MultiplexingConfig)(nil), // 6: xray.app.proxyman.MultiplexingConfig
(*net.PortList)(nil), // 7: xray.common.net.PortList
(*net.IPOrDomain)(nil), // 8: xray.common.net.IPOrDomain
(*internet.StreamConfig)(nil), // 9: xray.transport.internet.StreamConfig
(*serial.TypedMessage)(nil), // 10: xray.common.serial.TypedMessage
(*internet.ProxyConfig)(nil), // 11: xray.transport.internet.ProxyConfig
(internet.DomainStrategy)(0), // 12: xray.transport.internet.DomainStrategy
(*geodata.DomainRule)(nil), // 7: xray.common.geodata.DomainRule
(*geodata.IPRule)(nil), // 8: xray.common.geodata.IPRule
(*net.PortList)(nil), // 9: xray.common.net.PortList
(*net.IPOrDomain)(nil), // 10: xray.common.net.IPOrDomain
(*internet.StreamConfig)(nil), // 11: xray.transport.internet.StreamConfig
(*serial.TypedMessage)(nil), // 12: xray.common.serial.TypedMessage
(*internet.ProxyConfig)(nil), // 13: xray.transport.internet.ProxyConfig
(internet.DomainStrategy)(0), // 14: xray.transport.internet.DomainStrategy
}
var file_app_proxyman_config_proto_depIdxs = []int32{
7, // 0: xray.app.proxyman.ReceiverConfig.port_list:type_name -> xray.common.net.PortList
8, // 1: xray.app.proxyman.ReceiverConfig.listen:type_name -> xray.common.net.IPOrDomain
9, // 2: xray.app.proxyman.ReceiverConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
1, // 3: xray.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> xray.app.proxyman.SniffingConfig
10, // 4: xray.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage
10, // 5: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage
8, // 6: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain
9, // 7: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
11, // 8: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig
6, // 9: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig
12, // 10: xray.app.proxyman.SenderConfig.target_strategy:type_name -> xray.transport.internet.DomainStrategy
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
7, // 0: xray.app.proxyman.SniffingConfig.domains_excluded:type_name -> xray.common.geodata.DomainRule
8, // 1: xray.app.proxyman.SniffingConfig.ips_excluded:type_name -> xray.common.geodata.IPRule
9, // 2: xray.app.proxyman.ReceiverConfig.port_list:type_name -> xray.common.net.PortList
10, // 3: xray.app.proxyman.ReceiverConfig.listen:type_name -> xray.common.net.IPOrDomain
11, // 4: xray.app.proxyman.ReceiverConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
1, // 5: xray.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> xray.app.proxyman.SniffingConfig
12, // 6: xray.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage
12, // 7: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage
10, // 8: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain
11, // 9: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
13, // 10: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig
6, // 11: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig
14, // 12: xray.app.proxyman.SenderConfig.target_strategy:type_name -> xray.transport.internet.DomainStrategy
13, // [13:13] is the sub-list for method output_type
13, // [13:13] is the sub-list for method input_type
13, // [13:13] is the sub-list for extension type_name
13, // [13:13] is the sub-list for extension extendee
0, // [0:13] is the sub-list for field type_name
}
func init() { file_app_proxyman_config_proto_init() }
@@ -641,7 +581,7 @@ func file_app_proxyman_config_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_proxyman_config_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_proxyman_config_proto_rawDesc), len(file_app_proxyman_config_proto_rawDesc)),
NumEnums: 0,
NumMessages: 7,
NumExtensions: 0,
@@ -652,7 +592,6 @@ func file_app_proxyman_config_proto_init() {
MessageInfos: file_app_proxyman_config_proto_msgTypes,
}.Build()
File_app_proxyman_config_proto = out.File
file_app_proxyman_config_proto_rawDesc = nil
file_app_proxyman_config_proto_goTypes = nil
file_app_proxyman_config_proto_depIdxs = nil
}
+5 -1
View File
@@ -10,6 +10,7 @@ import "common/net/address.proto";
import "common/net/port.proto";
import "transport/internet/config.proto";
import "common/serial/typed_message.proto";
import "common/geodata/geodat.proto";
message InboundConfig {}
@@ -20,7 +21,10 @@ message SniffingConfig {
// Override target destination if sniff'ed protocol is in the given list.
// Supported values are "http", "tls", "fakedns".
repeated string destination_override = 2;
repeated string domains_excluded = 3;
repeated xray.common.geodata.DomainRule domains_excluded = 3;
repeated xray.common.geodata.IPRule ips_excluded = 6;
// Whether should only try to sniff metadata without waiting for client input.
// Can be used to support SMTP like protocol where server send the first
+34 -11
View File
@@ -9,6 +9,7 @@ import (
"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/core"
"github.com/xtls/xray-core/features/policy"
"github.com/xtls/xray-core/features/stats"
@@ -25,7 +26,7 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter)
if len(tag) > 0 && policy.ForSystem().Stats.InboundUplink {
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
name := "inbound>>>" + tag + ">>>traffic>>>uplink"
c, _ := stats.GetOrRegisterCounter(statsManager, name)
c, _ := statsManager.GetOrRegisterCounter(name)
if c != nil {
uplinkCounter = c
}
@@ -33,7 +34,7 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter)
if len(tag) > 0 && policy.ForSystem().Stats.InboundDownlink {
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
name := "inbound>>>" + tag + ">>>traffic>>>downlink"
c, _ := stats.GetOrRegisterCounter(statsManager, name)
c, _ := statsManager.GetOrRegisterCounter(name)
if c != nil {
downlinkCounter = c
}
@@ -52,7 +53,27 @@ type AlwaysOnInboundHandler struct {
}
func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*AlwaysOnInboundHandler, error) {
rawProxy, err := common.CreateObject(ctx, proxyConfig)
sniffingRequest, err := proxyman.BuildSniffingRequest(receiverConfig.SniffingSettings)
if err != nil {
return nil, err
}
src := net.TCPDestination(net.AnyIP, 0)
if receiverConfig.Listen != nil {
src.Address = receiverConfig.Listen.AsAddress()
}
if receiverConfig.PortList != nil && len(receiverConfig.PortList.Range) > 0 {
src.Port = net.Port(receiverConfig.PortList.Range[0].From)
}
mss, err := internet.ToMemoryStreamConfig(receiverConfig.StreamSettings)
if err != nil {
return nil, errors.New("failed to parse stream config").Base(err).AtWarning()
}
newCtx := session.ContextWithInbound(ctx, &session.Inbound{Tag: tag, Source: src})
newCtx = session.ContextWithContent(newCtx, &session.Content{SniffingRequest: sniffingRequest})
newCtx = session.ContextWithStreamSettings(newCtx, mss)
rawProxy, err := common.CreateObject(newCtx, proxyConfig)
if err != nil {
return nil, err
}
@@ -78,11 +99,6 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
address = net.AnyIP
}
mss, err := internet.ToMemoryStreamConfig(receiverConfig.StreamSettings)
if err != nil {
return nil, errors.New("failed to parse stream config").Base(err).AtWarning()
}
if receiverConfig.ReceiveOriginalDestination {
if mss.SocketSettings == nil {
mss.SocketSettings = &internet.SocketConfig{}
@@ -102,7 +118,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
stream: mss,
tag: tag,
dispatcher: h.mux,
sniffingConfig: receiverConfig.SniffingSettings,
sniffingRequest: sniffingRequest,
uplinkCounter: uplinkCounter,
downlinkCounter: downlinkCounter,
ctx: ctx,
@@ -124,7 +140,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
recvOrigDest: receiverConfig.ReceiveOriginalDestination,
tag: tag,
dispatcher: h.mux,
sniffingConfig: receiverConfig.SniffingSettings,
sniffingRequest: sniffingRequest,
uplinkCounter: uplinkCounter,
downlinkCounter: downlinkCounter,
ctx: ctx,
@@ -139,7 +155,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
address: address,
port: net.Port(port),
dispatcher: h.mux,
sniffingConfig: receiverConfig.SniffingSettings,
sniffingRequest: sniffingRequest,
uplinkCounter: uplinkCounter,
downlinkCounter: downlinkCounter,
stream: mss,
@@ -156,6 +172,12 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
// Start implements common.Runnable.
func (h *AlwaysOnInboundHandler) Start() error {
// for inbound without worker (TUN)
if run, ok := h.proxy.(common.Runnable); ok {
if err := run.Start(); err != nil {
return errors.New("failed to start proxy").Base(err)
}
}
for _, worker := range h.workers {
if err := worker.Start(); err != nil {
return err
@@ -171,6 +193,7 @@ func (h *AlwaysOnInboundHandler) Close() error {
errs = append(errs, worker.Close())
}
errs = append(errs, h.mux.Close())
errs = append(errs, common.Close(h.proxy))
if err := errors.Combine(errs...); err != nil {
return errors.New("failed to close all resources").Base(err)
}
+3 -3
View File
@@ -16,10 +16,10 @@ import (
// Manager manages all inbound handlers.
type Manager struct {
access sync.RWMutex
access sync.RWMutex
untaggedHandlers []inbound.Handler
taggedHandlers map[string]inbound.Handler
running bool
taggedHandlers map[string]inbound.Handler
running bool
}
// New returns a new Manager for inbound handlers.
+15 -28
View File
@@ -2,12 +2,10 @@ package inbound
import (
"context"
gonet "net"
"sync"
"sync/atomic"
"time"
"github.com/xtls/xray-core/app/proxyman"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
c "github.com/xtls/xray-core/common/ctx"
@@ -20,7 +18,9 @@ import (
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/features/stats"
"github.com/xtls/xray-core/proxy"
hysteria_proxy "github.com/xtls/xray-core/proxy/hysteria"
"github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/hysteria"
"github.com/xtls/xray-core/transport/internet/stat"
"github.com/xtls/xray-core/transport/internet/tcp"
"github.com/xtls/xray-core/transport/internet/udp"
@@ -42,7 +42,7 @@ type tcpWorker struct {
recvOrigDest bool
tag string
dispatcher routing.Dispatcher
sniffingConfig *proxyman.SniffingConfig
sniffingRequest session.SniffingRequest
uplinkCounter stats.Counter
downlinkCounter stats.Counter
@@ -117,13 +117,7 @@ func (w *tcpWorker) callback(conn stat.Connection) {
})
content := new(session.Content)
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
}
content.SniffingRequest = w.sniffingRequest
ctx = session.ContextWithContent(ctx, content)
if err := w.proxy.Process(ctx, net.Network_TCP, conn, w.dispatcher); err != nil {
@@ -139,6 +133,11 @@ func (w *tcpWorker) Proxy() proxy.Inbound {
func (w *tcpWorker) Start() error {
ctx := context.Background()
if v, ok := w.proxy.(*hysteria_proxy.Server); ok {
ctx = hysteria.ContextWithValidator(ctx, v.HysteriaInboundValidator())
}
hub, err := internet.ListenTCP(ctx, w.address, w.port, w.stream, func(conn stat.Connection) {
go w.callback(conn)
})
@@ -267,7 +266,7 @@ type udpWorker struct {
tag string
stream *internet.MemoryStreamConfig
dispatcher routing.Dispatcher
sniffingConfig *proxyman.SniffingConfig
sniffingRequest session.SniffingRequest
uplinkCounter stats.Counter
downlinkCounter stats.Counter
@@ -357,13 +356,7 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
Tag: w.tag,
})
content := new(session.Content)
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
}
content.SniffingRequest = w.sniffingRequest
ctx = session.ContextWithContent(ctx, content)
if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher); err != nil {
errors.LogInfoInner(ctx, err, "connection ends")
@@ -479,7 +472,7 @@ type dsWorker struct {
stream *internet.MemoryStreamConfig
tag string
dispatcher routing.Dispatcher
sniffingConfig *proxyman.SniffingConfig
sniffingRequest session.SniffingRequest
uplinkCounter stats.Counter
downlinkCounter stats.Counter
@@ -509,13 +502,7 @@ func (w *dsWorker) callback(conn stat.Connection) {
})
content := new(session.Content)
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
}
content.SniffingRequest = w.sniffingRequest
ctx = session.ContextWithContent(ctx, content)
if err := w.proxy.Process(ctx, net.Network_UNIX, conn, w.dispatcher); err != nil {
@@ -565,12 +552,12 @@ func (w *dsWorker) Close() error {
}
func IsLocal(ip net.IP) bool {
addrs, err := gonet.InterfaceAddrs()
addrs, err := net.InterfaceAddrs()
if err != nil {
return false
}
for _, addr := range addrs {
if ipnet, ok := addr.(*gonet.IPNet); ok {
if ipnet, ok := addr.(*net.IPNet); ok {
if ipnet.IP.Equal(ip) {
return true
}
+14 -17
View File
@@ -6,8 +6,6 @@ import (
goerrors "errors"
"io"
"math/big"
gonet "net"
"os"
"github.com/xtls/xray-core/common/dice"
@@ -41,7 +39,7 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter)
if len(tag) > 0 && policy.ForSystem().Stats.OutboundUplink {
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
name := "outbound>>>" + tag + ">>>traffic>>>uplink"
c, _ := stats.GetOrRegisterCounter(statsManager, name)
c, _ := statsManager.GetOrRegisterCounter(name)
if c != nil {
uplinkCounter = c
}
@@ -49,7 +47,7 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter)
if len(tag) > 0 && policy.ForSystem().Stats.OutboundDownlink {
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
name := "outbound>>>" + tag + ">>>traffic>>>downlink"
c, _ := stats.GetOrRegisterCounter(statsManager, name)
c, _ := statsManager.GetOrRegisterCounter(name)
if c != nil {
downlinkCounter = c
}
@@ -110,7 +108,9 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou
ctx = session.ContextWithFullHandler(ctx, h)
rawProxyHandler, err := common.CreateObject(ctx, proxyConfig)
newCtx := session.ContextWithStreamSettings(ctx, h.streamSettings)
rawProxyHandler, err := common.CreateObject(newCtx, proxyConfig)
if err != nil {
return nil, err
}
@@ -307,18 +307,17 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
ob := outbounds[len(outbounds)-1]
h.SetOutboundGateway(ctx, ob)
}
}
if conn, err := h.getUoTConnection(ctx, dest); err != os.ErrInvalid {
return conn, err
}
conn, err := internet.Dial(ctx, dest, h.streamSettings)
conn = h.getStatCouterConnection(conn)
outbounds := session.OutboundsFromContext(ctx)
ob := outbounds[len(outbounds)-1]
ob.Conn = conn
if outbounds != nil {
ob := outbounds[len(outbounds)-1]
ob.Conn = conn
} else {
// for Vision's pre-connect
}
return conn, err
}
@@ -345,10 +344,9 @@ func (h *Handler) SetOutboundGateway(ctx context.Context, ob *session.Outbound)
errors.LogDebug(ctx, "use inbound source ip as sendthrough: ", inbound.Source.Address.String())
}
}
//case addr.Family().IsDomain():
// case addr.Family().IsDomain():
default:
ob.Gateway = addr
}
}
@@ -393,8 +391,7 @@ func (h *Handler) ProxySettings() *serial.TypedMessage {
}
func ParseRandomIP(addr net.Address, prefix string) net.Address {
_, ipnet, _ := gonet.ParseCIDR(addr.IP().String() + "/" + prefix)
_, ipnet, _ := net.ParseCIDR(addr.IP().String() + "/" + prefix)
ones, bits := ipnet.Mask.Size()
subnetSize := new(big.Int).Lsh(big.NewInt(1), uint(bits-ones))
@@ -408,5 +405,5 @@ func ParseRandomIP(addr net.Address, prefix string) net.Address {
padded := make([]byte, len(ipnet.IP))
copy(padded[len(padded)-len(rndBytes):], rndBytes)
return net.ParseAddress(gonet.IP(padded).String())
return net.ParseAddress(net.IP(padded).String())
}
+7 -8
View File
@@ -22,8 +22,8 @@ import (
)
func TestInterfaces(t *testing.T) {
_ = (outbound.Handler)(new(Handler))
_ = (outbound.Manager)(new(Manager))
_ = outbound.Handler(new(Handler))
_ = outbound.Manager(new(Manager))
}
const xrayKey core.XrayKey = 1
@@ -43,12 +43,12 @@ func TestOutboundWithoutStatCounter(t *testing.T) {
}
v, _ := core.New(config)
v.AddFeature((outbound.Manager)(new(Manager)))
v.AddFeature(outbound.Manager(new(Manager)))
ctx := context.WithValue(context.Background(), xrayKey, v)
ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}})
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
Tag: "tag",
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
ProxySettings: serial.ToTypedMessage(&freedom.Config{FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}}}),
})
conn, _ := h.(*Handler).Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146))
_, ok := conn.(*stat.CounterConnection)
@@ -73,12 +73,12 @@ func TestOutboundWithStatCounter(t *testing.T) {
}
v, _ := core.New(config)
v.AddFeature((outbound.Manager)(new(Manager)))
v.AddFeature(outbound.Manager(new(Manager)))
ctx := context.WithValue(context.Background(), xrayKey, v)
ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}})
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
Tag: "tag",
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
ProxySettings: serial.ToTypedMessage(&freedom.Config{FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}}}),
})
conn, _ := h.(*Handler).Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146))
_, ok := conn.(*stat.CounterConnection)
@@ -88,7 +88,6 @@ func TestOutboundWithStatCounter(t *testing.T) {
}
func TestTagsCache(t *testing.T) {
test_duration := 10 * time.Second
threads_num := 50
delay := 10 * time.Millisecond
@@ -118,7 +117,7 @@ func TestTagsCache(t *testing.T) {
tag := fmt.Sprintf("%s%d", tags_prefix, idx)
cfg := &core.OutboundHandlerConfig{
Tag: tag,
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
ProxySettings: serial.ToTypedMessage(&freedom.Config{FinalRules: []*freedom.FinalRuleConfig{{Action: freedom.RuleAction_Allow}}}),
}
if h, err := NewHandler(ctx, cfg); err == nil {
if err := ohm.AddHandler(ctx, h); err == nil {
-1
View File
@@ -162,7 +162,6 @@ func (m *Manager) ListHandlers(ctx context.Context) []outbound.Handler {
// Select implements outbound.HandlerSelector.
func (m *Manager) Select(selectors []string) []string {
key := strings.Join(selectors, ",")
if cache, ok := m.tagsCache.Load(key); ok {
return cache.([]string)
-35
View File
@@ -1,35 +0,0 @@
package outbound
import (
"context"
"os"
"github.com/sagernet/sing/common/uot"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/stat"
)
func (h *Handler) getUoTConnection(ctx context.Context, dest net.Destination) (stat.Connection, error) {
if dest.Address == nil {
return nil, errors.New("nil destination address")
}
if !dest.Address.Family().IsDomain() {
return nil, os.ErrInvalid
}
var uotVersion int
if dest.Address.Domain() == uot.MagicAddress {
uotVersion = uot.Version
} else if dest.Address.Domain() == uot.LegacyMagicAddress {
uotVersion = uot.LegacyVersion
} else {
return nil, os.ErrInvalid
}
packetConn, err := internet.ListenSystemPacket(ctx, &net.UDPAddr{IP: net.AnyIP.IP(), Port: 0}, h.streamSettings.SocketSettings)
if err != nil {
return nil, errors.New("unable to listen socket").Base(err)
}
conn := uot.NewServerConn(packetConn, uotVersion)
return h.getStatCouterConnection(conn), nil
}
-3
View File
@@ -4,7 +4,6 @@ import (
"context"
"time"
"github.com/xtls/xray-core/app/dispatcher"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/mux"
"github.com/xtls/xray-core/common/net"
@@ -230,8 +229,6 @@ func (w *BridgeWorker) DispatchLink(ctx context.Context, dest net.Destination, l
}
return w.Dispatcher.DispatchLink(ctx, dest, link)
}
link = w.Dispatcher.(*dispatcher.DefaultDispatcher).WrapLink(ctx, link)
w.handleInternalConn(link)
return nil
+42 -61
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/reverse/config.proto
package reverse
@@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -67,12 +68,11 @@ func (Control_State) EnumDescriptor() ([]byte, []int) {
}
type Control struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
State Control_State `protobuf:"varint,1,opt,name=state,proto3,enum=xray.app.reverse.Control_State" json:"state,omitempty"`
Random []byte `protobuf:"bytes,99,opt,name=random,proto3" json:"random,omitempty"`
unknownFields protoimpl.UnknownFields
State Control_State `protobuf:"varint,1,opt,name=state,proto3,enum=xray.app.reverse.Control_State" json:"state,omitempty"`
Random []byte `protobuf:"bytes,99,opt,name=random,proto3" json:"random,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *Control) Reset() {
@@ -120,12 +120,11 @@ func (x *Control) GetRandom() []byte {
}
type BridgeConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *BridgeConfig) Reset() {
@@ -173,12 +172,11 @@ func (x *BridgeConfig) GetDomain() string {
}
type PortalConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *PortalConfig) Reset() {
@@ -226,12 +224,11 @@ func (x *PortalConfig) GetDomain() string {
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
BridgeConfig []*BridgeConfig `protobuf:"bytes,1,rep,name=bridge_config,json=bridgeConfig,proto3" json:"bridge_config,omitempty"`
PortalConfig []*PortalConfig `protobuf:"bytes,2,rep,name=portal_config,json=portalConfig,proto3" json:"portal_config,omitempty"`
unknownFields protoimpl.UnknownFields
BridgeConfig []*BridgeConfig `protobuf:"bytes,1,rep,name=bridge_config,json=bridgeConfig,proto3" json:"bridge_config,omitempty"`
PortalConfig []*PortalConfig `protobuf:"bytes,2,rep,name=portal_config,json=portalConfig,proto3" json:"portal_config,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
@@ -280,50 +277,35 @@ func (x *Config) GetPortalConfig() []*PortalConfig {
var File_app_reverse_config_proto protoreflect.FileDescriptor
var file_app_reverse_config_proto_rawDesc = []byte{
0x0a, 0x18, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2f, 0x63, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x22, 0x78, 0x0a, 0x07,
0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16,
0x0a, 0x06, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x18, 0x63, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06,
0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x22, 0x1e, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
0x0a, 0x0a, 0x06, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x44,
0x52, 0x41, 0x49, 0x4e, 0x10, 0x01, 0x22, 0x38, 0x0a, 0x0c, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61,
0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x22, 0x38, 0x0a, 0x0c, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74,
0x61, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x92, 0x01, 0x0a, 0x06, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x43, 0x0a, 0x0d, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x5f,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e,
0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x62, 0x72,
0x69, 0x64, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x43, 0x0a, 0x0d, 0x70, 0x6f,
0x72, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x65, 0x76,
0x65, 0x72, 0x73, 0x65, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x52, 0x0c, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42,
0x56, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78,
0x79, 0x2e, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 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, 0x72, 0x65, 0x76, 0x65, 0x72,
0x73, 0x65, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e,
0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
const file_app_reverse_config_proto_rawDesc = "" +
"\n" +
"\x18app/reverse/config.proto\x12\x10xray.app.reverse\"x\n" +
"\aControl\x125\n" +
"\x05state\x18\x01 \x01(\x0e2\x1f.xray.app.reverse.Control.StateR\x05state\x12\x16\n" +
"\x06random\x18c \x01(\fR\x06random\"\x1e\n" +
"\x05State\x12\n" +
"\n" +
"\x06ACTIVE\x10\x00\x12\t\n" +
"\x05DRAIN\x10\x01\"8\n" +
"\fBridgeConfig\x12\x10\n" +
"\x03tag\x18\x01 \x01(\tR\x03tag\x12\x16\n" +
"\x06domain\x18\x02 \x01(\tR\x06domain\"8\n" +
"\fPortalConfig\x12\x10\n" +
"\x03tag\x18\x01 \x01(\tR\x03tag\x12\x16\n" +
"\x06domain\x18\x02 \x01(\tR\x06domain\"\x92\x01\n" +
"\x06Config\x12C\n" +
"\rbridge_config\x18\x01 \x03(\v2\x1e.xray.app.reverse.BridgeConfigR\fbridgeConfig\x12C\n" +
"\rportal_config\x18\x02 \x03(\v2\x1e.xray.app.reverse.PortalConfigR\fportalConfigBV\n" +
"\x16com.xray.proxy.reverseP\x01Z%github.com/xtls/xray-core/app/reverse\xaa\x02\x12Xray.Proxy.Reverseb\x06proto3"
var (
file_app_reverse_config_proto_rawDescOnce sync.Once
file_app_reverse_config_proto_rawDescData = file_app_reverse_config_proto_rawDesc
file_app_reverse_config_proto_rawDescData []byte
)
func file_app_reverse_config_proto_rawDescGZIP() []byte {
file_app_reverse_config_proto_rawDescOnce.Do(func() {
file_app_reverse_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_reverse_config_proto_rawDescData)
file_app_reverse_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_reverse_config_proto_rawDesc), len(file_app_reverse_config_proto_rawDesc)))
})
return file_app_reverse_config_proto_rawDescData
}
@@ -357,7 +339,7 @@ func file_app_reverse_config_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_reverse_config_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_reverse_config_proto_rawDesc), len(file_app_reverse_config_proto_rawDesc)),
NumEnums: 1,
NumMessages: 4,
NumExtensions: 0,
@@ -369,7 +351,6 @@ func file_app_reverse_config_proto_init() {
MessageInfos: file_app_reverse_config_proto_msgTypes,
}.Build()
File_app_reverse_config_proto = out.File
file_app_reverse_config_proto_rawDesc = nil
file_app_reverse_config_proto_goTypes = nil
file_app_reverse_config_proto_depIdxs = nil
}
+3 -3
View File
@@ -136,7 +136,7 @@ func (b *Balancer) SelectOutbounds() ([]string, error) {
// GetPrincipleTarget implements routing.BalancerPrincipleTarget
func (r *Router) GetPrincipleTarget(tag string) ([]string, error) {
if b, ok := r.balancers[tag]; ok {
if b, ok := (*r.balancers.Load())[tag]; ok {
if s, ok := b.strategy.(BalancingPrincipleTarget); ok {
candidates, err := b.SelectOutbounds()
if err != nil {
@@ -151,7 +151,7 @@ func (r *Router) GetPrincipleTarget(tag string) ([]string, error) {
// SetOverrideTarget implements routing.BalancerOverrider
func (r *Router) SetOverrideTarget(tag, target string) error {
if b, ok := r.balancers[tag]; ok {
if b, ok := (*r.balancers.Load())[tag]; ok {
b.override.Put(target)
return nil
}
@@ -160,7 +160,7 @@ func (r *Router) SetOverrideTarget(tag, target string) error {
// GetOverrideTarget implements routing.BalancerOverrider
func (r *Router) GetOverrideTarget(tag string) (string, error) {
if b, ok := r.balancers[tag]; ok {
if b, ok := (*r.balancers.Load())[tag]; ok {
return b.override.Get(), nil
}
return "", errors.New("cannot find tag")
-17
View File
@@ -2,25 +2,8 @@ package router
import (
sync "sync"
"github.com/xtls/xray-core/common/errors"
)
func (r *Router) OverrideBalancer(balancer string, target string) error {
var b *Balancer
for tag, bl := range r.balancers {
if tag == balancer {
b = bl
break
}
}
if b == nil {
return errors.New("balancer '", balancer, "' not found")
}
b.override.Put(target)
return nil
}
type overrideSettings struct {
target string
}
+15 -1
View File
@@ -58,8 +58,8 @@ func (s *routingServer) AddRule(ctx context.Context, request *AddRuleRequest) (*
return &AddRuleResponse{}, bo.AddRule(request.Config, request.ShouldAppend)
}
return nil, errors.New("unsupported router implementation")
}
func (s *routingServer) RemoveRule(ctx context.Context, request *RemoveRuleRequest) (*RemoveRuleResponse, error) {
if bo, ok := s.router.(routing.Router); ok {
return &RemoveRuleResponse{}, bo.RemoveRule(request.RuleTag)
@@ -67,6 +67,20 @@ func (s *routingServer) RemoveRule(ctx context.Context, request *RemoveRuleReque
return nil, errors.New("unsupported router implementation")
}
func (s *routingServer) ListRule(ctx context.Context, request *ListRuleRequest) (*ListRuleResponse, error) {
if bo, ok := s.router.(routing.Router); ok {
response := &ListRuleResponse{}
for _, v := range bo.ListRule() {
response.Rules = append(response.Rules, &ListRuleItem{
Tag: v.GetOutboundTag(),
RuleTag: v.GetRuleTag(),
})
}
return response, nil
}
return nil, errors.New("unsupported router implementation")
}
// NewRoutingServer creates a statistics service with statistics manager.
func NewRoutingServer(router routing.Router, routingStats stats.Channel) RoutingServiceServer {
return &routingServer{
+316 -273
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/router/command/command.proto
package command
@@ -13,6 +13,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -26,25 +27,24 @@ const (
// It conforms to the structure of xray.features.routing.Context and
// xray.features.routing.Route.
type RoutingContext struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
InboundTag string `protobuf:"bytes,1,opt,name=InboundTag,proto3" json:"InboundTag,omitempty"`
Network net.Network `protobuf:"varint,2,opt,name=Network,proto3,enum=xray.common.net.Network" json:"Network,omitempty"`
SourceIPs [][]byte `protobuf:"bytes,3,rep,name=SourceIPs,proto3" json:"SourceIPs,omitempty"`
TargetIPs [][]byte `protobuf:"bytes,4,rep,name=TargetIPs,proto3" json:"TargetIPs,omitempty"`
SourcePort uint32 `protobuf:"varint,5,opt,name=SourcePort,proto3" json:"SourcePort,omitempty"`
TargetPort uint32 `protobuf:"varint,6,opt,name=TargetPort,proto3" json:"TargetPort,omitempty"`
TargetDomain string `protobuf:"bytes,7,opt,name=TargetDomain,proto3" json:"TargetDomain,omitempty"`
Protocol string `protobuf:"bytes,8,opt,name=Protocol,proto3" json:"Protocol,omitempty"`
User string `protobuf:"bytes,9,opt,name=User,proto3" json:"User,omitempty"`
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"`
state protoimpl.MessageState `protogen:"open.v1"`
InboundTag string `protobuf:"bytes,1,opt,name=InboundTag,proto3" json:"InboundTag,omitempty"`
Network net.Network `protobuf:"varint,2,opt,name=Network,proto3,enum=xray.common.net.Network" json:"Network,omitempty"`
SourceIPs [][]byte `protobuf:"bytes,3,rep,name=SourceIPs,proto3" json:"SourceIPs,omitempty"`
TargetIPs [][]byte `protobuf:"bytes,4,rep,name=TargetIPs,proto3" json:"TargetIPs,omitempty"`
SourcePort uint32 `protobuf:"varint,5,opt,name=SourcePort,proto3" json:"SourcePort,omitempty"`
TargetPort uint32 `protobuf:"varint,6,opt,name=TargetPort,proto3" json:"TargetPort,omitempty"`
TargetDomain string `protobuf:"bytes,7,opt,name=TargetDomain,proto3" json:"TargetDomain,omitempty"`
Protocol string `protobuf:"bytes,8,opt,name=Protocol,proto3" json:"Protocol,omitempty"`
User string `protobuf:"bytes,9,opt,name=User,proto3" json:"User,omitempty"`
Attributes map[string]string `protobuf:"bytes,10,rep,name=Attributes,proto3" json:"Attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
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"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RoutingContext) Reset() {
@@ -201,11 +201,10 @@ func (x *RoutingContext) GetVlessRoute() uint32 {
//
// * If FieldSelectors is left empty, all fields will be returned.
type SubscribeRoutingStatsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
FieldSelectors []string `protobuf:"bytes,1,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"`
state protoimpl.MessageState `protogen:"open.v1"`
FieldSelectors []string `protobuf:"bytes,1,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SubscribeRoutingStatsRequest) Reset() {
@@ -253,13 +252,12 @@ func (x *SubscribeRoutingStatsRequest) GetFieldSelectors() []string {
// * PublishResult broadcasts the routing result to routing statistics channel
// if set true.
type TestRouteRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
RoutingContext *RoutingContext `protobuf:"bytes,1,opt,name=RoutingContext,proto3" json:"RoutingContext,omitempty"`
FieldSelectors []string `protobuf:"bytes,2,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"`
PublishResult bool `protobuf:"varint,3,opt,name=PublishResult,proto3" json:"PublishResult,omitempty"`
state protoimpl.MessageState `protogen:"open.v1"`
RoutingContext *RoutingContext `protobuf:"bytes,1,opt,name=RoutingContext,proto3" json:"RoutingContext,omitempty"`
FieldSelectors []string `protobuf:"bytes,2,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"`
PublishResult bool `protobuf:"varint,3,opt,name=PublishResult,proto3" json:"PublishResult,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *TestRouteRequest) Reset() {
@@ -314,11 +312,10 @@ func (x *TestRouteRequest) GetPublishResult() bool {
}
type PrincipleTargetInfo struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Tag []string `protobuf:"bytes,1,rep,name=tag,proto3" json:"tag,omitempty"`
unknownFields protoimpl.UnknownFields
Tag []string `protobuf:"bytes,1,rep,name=tag,proto3" json:"tag,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *PrincipleTargetInfo) Reset() {
@@ -359,11 +356,10 @@ func (x *PrincipleTargetInfo) GetTag() []string {
}
type OverrideInfo struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
unknownFields protoimpl.UnknownFields
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *OverrideInfo) Reset() {
@@ -404,12 +400,11 @@ func (x *OverrideInfo) GetTarget() string {
}
type BalancerMsg struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Override *OverrideInfo `protobuf:"bytes,5,opt,name=override,proto3" json:"override,omitempty"`
PrincipleTarget *PrincipleTargetInfo `protobuf:"bytes,6,opt,name=principle_target,json=principleTarget,proto3" json:"principle_target,omitempty"`
state protoimpl.MessageState `protogen:"open.v1"`
Override *OverrideInfo `protobuf:"bytes,5,opt,name=override,proto3" json:"override,omitempty"`
PrincipleTarget *PrincipleTargetInfo `protobuf:"bytes,6,opt,name=principle_target,json=principleTarget,proto3" json:"principle_target,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *BalancerMsg) Reset() {
@@ -457,11 +452,10 @@ func (x *BalancerMsg) GetPrincipleTarget() *PrincipleTargetInfo {
}
type GetBalancerInfoRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *GetBalancerInfoRequest) Reset() {
@@ -502,11 +496,10 @@ func (x *GetBalancerInfoRequest) GetTag() string {
}
type GetBalancerInfoResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Balancer *BalancerMsg `protobuf:"bytes,1,opt,name=balancer,proto3" json:"balancer,omitempty"`
unknownFields protoimpl.UnknownFields
Balancer *BalancerMsg `protobuf:"bytes,1,opt,name=balancer,proto3" json:"balancer,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *GetBalancerInfoResponse) Reset() {
@@ -547,12 +540,11 @@ func (x *GetBalancerInfoResponse) GetBalancer() *BalancerMsg {
}
type OverrideBalancerTargetRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
BalancerTag string `protobuf:"bytes,1,opt,name=balancerTag,proto3" json:"balancerTag,omitempty"`
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
unknownFields protoimpl.UnknownFields
BalancerTag string `protobuf:"bytes,1,opt,name=balancerTag,proto3" json:"balancerTag,omitempty"`
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *OverrideBalancerTargetRequest) Reset() {
@@ -600,9 +592,9 @@ func (x *OverrideBalancerTargetRequest) GetTarget() string {
}
type OverrideBalancerTargetResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *OverrideBalancerTargetResponse) Reset() {
@@ -636,12 +628,11 @@ func (*OverrideBalancerTargetResponse) Descriptor() ([]byte, []int) {
}
type AddRuleRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Config *serial.TypedMessage `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"`
ShouldAppend bool `protobuf:"varint,2,opt,name=shouldAppend,proto3" json:"shouldAppend,omitempty"`
unknownFields protoimpl.UnknownFields
Config *serial.TypedMessage `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"`
ShouldAppend bool `protobuf:"varint,2,opt,name=shouldAppend,proto3" json:"shouldAppend,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *AddRuleRequest) Reset() {
@@ -689,9 +680,9 @@ func (x *AddRuleRequest) GetShouldAppend() bool {
}
type AddRuleResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AddRuleResponse) Reset() {
@@ -725,11 +716,10 @@ func (*AddRuleResponse) Descriptor() ([]byte, []int) {
}
type RemoveRuleRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
RuleTag string `protobuf:"bytes,1,opt,name=ruleTag,proto3" json:"ruleTag,omitempty"`
unknownFields protoimpl.UnknownFields
RuleTag string `protobuf:"bytes,1,opt,name=ruleTag,proto3" json:"ruleTag,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *RemoveRuleRequest) Reset() {
@@ -770,9 +760,9 @@ func (x *RemoveRuleRequest) GetRuleTag() string {
}
type RemoveRuleResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RemoveRuleResponse) Reset() {
@@ -805,15 +795,147 @@ func (*RemoveRuleResponse) Descriptor() ([]byte, []int) {
return file_app_router_command_command_proto_rawDescGZIP(), []int{13}
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
type ListRuleRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListRuleRequest) Reset() {
*x = ListRuleRequest{}
mi := &file_app_router_command_command_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListRuleRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListRuleRequest) ProtoMessage() {}
func (x *ListRuleRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_router_command_command_proto_msgTypes[14]
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 ListRuleRequest.ProtoReflect.Descriptor instead.
func (*ListRuleRequest) Descriptor() ([]byte, []int) {
return file_app_router_command_command_proto_rawDescGZIP(), []int{14}
}
type ListRuleItem struct {
state protoimpl.MessageState `protogen:"open.v1"`
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
RuleTag string `protobuf:"bytes,2,opt,name=ruleTag,proto3" json:"ruleTag,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListRuleItem) Reset() {
*x = ListRuleItem{}
mi := &file_app_router_command_command_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListRuleItem) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListRuleItem) ProtoMessage() {}
func (x *ListRuleItem) ProtoReflect() protoreflect.Message {
mi := &file_app_router_command_command_proto_msgTypes[15]
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 ListRuleItem.ProtoReflect.Descriptor instead.
func (*ListRuleItem) Descriptor() ([]byte, []int) {
return file_app_router_command_command_proto_rawDescGZIP(), []int{15}
}
func (x *ListRuleItem) GetTag() string {
if x != nil {
return x.Tag
}
return ""
}
func (x *ListRuleItem) GetRuleTag() string {
if x != nil {
return x.RuleTag
}
return ""
}
type ListRuleResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Rules []*ListRuleItem `protobuf:"bytes,1,rep,name=rules,proto3" json:"rules,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListRuleResponse) Reset() {
*x = ListRuleResponse{}
mi := &file_app_router_command_command_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListRuleResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListRuleResponse) ProtoMessage() {}
func (x *ListRuleResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_router_command_command_proto_msgTypes[16]
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 ListRuleResponse.ProtoReflect.Descriptor instead.
func (*ListRuleResponse) Descriptor() ([]byte, []int) {
return file_app_router_command_command_proto_rawDescGZIP(), []int{16}
}
func (x *ListRuleResponse) GetRules() []*ListRuleItem {
if x != nil {
return x.Rules
}
return nil
}
type Config struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
*x = Config{}
mi := &file_app_router_command_command_proto_msgTypes[14]
mi := &file_app_router_command_command_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -825,7 +947,7 @@ func (x *Config) String() string {
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_router_command_command_proto_msgTypes[14]
mi := &file_app_router_command_command_proto_msgTypes[17]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -838,187 +960,103 @@ func (x *Config) ProtoReflect() protoreflect.Message {
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
return file_app_router_command_command_proto_rawDescGZIP(), []int{14}
return file_app_router_command_command_proto_rawDescGZIP(), []int{17}
}
var File_app_router_command_command_proto protoreflect.FileDescriptor
var file_app_router_command_command_proto_rawDesc = []byte{
0x0a, 0x20, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6d,
0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x12, 0x17, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75,
0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x18, 0x63, 0x6f, 0x6d,
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, 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,
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e,
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12,
0x1c, 0x0a, 0x09, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x50, 0x73, 0x18, 0x03, 0x20, 0x03,
0x28, 0x0c, 0x52, 0x09, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x50, 0x73, 0x12, 0x1c, 0x0a,
0x09, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x50, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c,
0x52, 0x09, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x50, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x53,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x54,
0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x0a, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x54,
0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0c, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12,
0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x55,
0x73, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12,
0x57, 0x0a, 0x0a, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0a, 0x20,
0x03, 0x28, 0x0b, 0x32, 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, 0x52, 0x6f,
0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x41, 0x74, 0x74,
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x41, 0x74,
0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x4f, 0x75, 0x74, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x61, 0x67, 0x73, 0x18, 0x0b, 0x20,
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, 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, 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, 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, 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, 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,
}
const file_app_router_command_command_proto_rawDesc = "" +
"\n" +
" app/router/command/command.proto\x12\x17xray.app.router.command\x1a\x18common/net/network.proto\x1a!common/serial/typed_message.proto\"\xf6\x04\n" +
"\x0eRoutingContext\x12\x1e\n" +
"\n" +
"InboundTag\x18\x01 \x01(\tR\n" +
"InboundTag\x122\n" +
"\aNetwork\x18\x02 \x01(\x0e2\x18.xray.common.net.NetworkR\aNetwork\x12\x1c\n" +
"\tSourceIPs\x18\x03 \x03(\fR\tSourceIPs\x12\x1c\n" +
"\tTargetIPs\x18\x04 \x03(\fR\tTargetIPs\x12\x1e\n" +
"\n" +
"SourcePort\x18\x05 \x01(\rR\n" +
"SourcePort\x12\x1e\n" +
"\n" +
"TargetPort\x18\x06 \x01(\rR\n" +
"TargetPort\x12\"\n" +
"\fTargetDomain\x18\a \x01(\tR\fTargetDomain\x12\x1a\n" +
"\bProtocol\x18\b \x01(\tR\bProtocol\x12\x12\n" +
"\x04User\x18\t \x01(\tR\x04User\x12W\n" +
"\n" +
"Attributes\x18\n" +
" \x03(\v27.xray.app.router.command.RoutingContext.AttributesEntryR\n" +
"Attributes\x12,\n" +
"\x11OutboundGroupTags\x18\v \x03(\tR\x11OutboundGroupTags\x12 \n" +
"\vOutboundTag\x18\f \x01(\tR\vOutboundTag\x12\x1a\n" +
"\bLocalIPs\x18\r \x03(\fR\bLocalIPs\x12\x1c\n" +
"\tLocalPort\x18\x0e \x01(\rR\tLocalPort\x12\x1e\n" +
"\n" +
"VlessRoute\x18\x0f \x01(\rR\n" +
"VlessRoute\x1a=\n" +
"\x0fAttributesEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"F\n" +
"\x1cSubscribeRoutingStatsRequest\x12&\n" +
"\x0eFieldSelectors\x18\x01 \x03(\tR\x0eFieldSelectors\"\xb1\x01\n" +
"\x10TestRouteRequest\x12O\n" +
"\x0eRoutingContext\x18\x01 \x01(\v2'.xray.app.router.command.RoutingContextR\x0eRoutingContext\x12&\n" +
"\x0eFieldSelectors\x18\x02 \x03(\tR\x0eFieldSelectors\x12$\n" +
"\rPublishResult\x18\x03 \x01(\bR\rPublishResult\"'\n" +
"\x13PrincipleTargetInfo\x12\x10\n" +
"\x03tag\x18\x01 \x03(\tR\x03tag\"&\n" +
"\fOverrideInfo\x12\x16\n" +
"\x06target\x18\x02 \x01(\tR\x06target\"\xa9\x01\n" +
"\vBalancerMsg\x12A\n" +
"\boverride\x18\x05 \x01(\v2%.xray.app.router.command.OverrideInfoR\boverride\x12W\n" +
"\x10principle_target\x18\x06 \x01(\v2,.xray.app.router.command.PrincipleTargetInfoR\x0fprincipleTarget\"*\n" +
"\x16GetBalancerInfoRequest\x12\x10\n" +
"\x03tag\x18\x01 \x01(\tR\x03tag\"[\n" +
"\x17GetBalancerInfoResponse\x12@\n" +
"\bbalancer\x18\x01 \x01(\v2$.xray.app.router.command.BalancerMsgR\bbalancer\"Y\n" +
"\x1dOverrideBalancerTargetRequest\x12 \n" +
"\vbalancerTag\x18\x01 \x01(\tR\vbalancerTag\x12\x16\n" +
"\x06target\x18\x02 \x01(\tR\x06target\" \n" +
"\x1eOverrideBalancerTargetResponse\"n\n" +
"\x0eAddRuleRequest\x128\n" +
"\x06config\x18\x01 \x01(\v2 .xray.common.serial.TypedMessageR\x06config\x12\"\n" +
"\fshouldAppend\x18\x02 \x01(\bR\fshouldAppend\"\x11\n" +
"\x0fAddRuleResponse\"-\n" +
"\x11RemoveRuleRequest\x12\x18\n" +
"\aruleTag\x18\x01 \x01(\tR\aruleTag\"\x14\n" +
"\x12RemoveRuleResponse\"\x11\n" +
"\x0fListRuleRequest\":\n" +
"\fListRuleItem\x12\x10\n" +
"\x03tag\x18\x01 \x01(\tR\x03tag\x12\x18\n" +
"\aruleTag\x18\x02 \x01(\tR\aruleTag\"O\n" +
"\x10ListRuleResponse\x12;\n" +
"\x05rules\x18\x01 \x03(\v2%.xray.app.router.command.ListRuleItemR\x05rules\"\b\n" +
"\x06Config2\xa2\x06\n" +
"\x0eRoutingService\x12{\n" +
"\x15SubscribeRoutingStats\x125.xray.app.router.command.SubscribeRoutingStatsRequest\x1a'.xray.app.router.command.RoutingContext\"\x000\x01\x12a\n" +
"\tTestRoute\x12).xray.app.router.command.TestRouteRequest\x1a'.xray.app.router.command.RoutingContext\"\x00\x12v\n" +
"\x0fGetBalancerInfo\x12/.xray.app.router.command.GetBalancerInfoRequest\x1a0.xray.app.router.command.GetBalancerInfoResponse\"\x00\x12\x8b\x01\n" +
"\x16OverrideBalancerTarget\x126.xray.app.router.command.OverrideBalancerTargetRequest\x1a7.xray.app.router.command.OverrideBalancerTargetResponse\"\x00\x12^\n" +
"\aAddRule\x12'.xray.app.router.command.AddRuleRequest\x1a(.xray.app.router.command.AddRuleResponse\"\x00\x12g\n" +
"\n" +
"RemoveRule\x12*.xray.app.router.command.RemoveRuleRequest\x1a+.xray.app.router.command.RemoveRuleResponse\"\x00\x12a\n" +
"\bListRule\x12(.xray.app.router.command.ListRuleRequest\x1a).xray.app.router.command.ListRuleResponse\"\x00Bg\n" +
"\x1bcom.xray.app.router.commandP\x01Z,github.com/xtls/xray-core/app/router/command\xaa\x02\x17Xray.App.Router.Commandb\x06proto3"
var (
file_app_router_command_command_proto_rawDescOnce sync.Once
file_app_router_command_command_proto_rawDescData = file_app_router_command_command_proto_rawDesc
file_app_router_command_command_proto_rawDescData []byte
)
func file_app_router_command_command_proto_rawDescGZIP() []byte {
file_app_router_command_command_proto_rawDescOnce.Do(func() {
file_app_router_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_router_command_command_proto_rawDescData)
file_app_router_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_router_command_command_proto_rawDesc), len(file_app_router_command_command_proto_rawDesc)))
})
return file_app_router_command_command_proto_rawDescData
}
var file_app_router_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 16)
var file_app_router_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
var file_app_router_command_command_proto_goTypes = []any{
(*RoutingContext)(nil), // 0: xray.app.router.command.RoutingContext
(*SubscribeRoutingStatsRequest)(nil), // 1: xray.app.router.command.SubscribeRoutingStatsRequest
@@ -1034,36 +1072,42 @@ var file_app_router_command_command_proto_goTypes = []any{
(*AddRuleResponse)(nil), // 11: xray.app.router.command.AddRuleResponse
(*RemoveRuleRequest)(nil), // 12: xray.app.router.command.RemoveRuleRequest
(*RemoveRuleResponse)(nil), // 13: xray.app.router.command.RemoveRuleResponse
(*Config)(nil), // 14: xray.app.router.command.Config
nil, // 15: xray.app.router.command.RoutingContext.AttributesEntry
(net.Network)(0), // 16: xray.common.net.Network
(*serial.TypedMessage)(nil), // 17: xray.common.serial.TypedMessage
(*ListRuleRequest)(nil), // 14: xray.app.router.command.ListRuleRequest
(*ListRuleItem)(nil), // 15: xray.app.router.command.ListRuleItem
(*ListRuleResponse)(nil), // 16: xray.app.router.command.ListRuleResponse
(*Config)(nil), // 17: xray.app.router.command.Config
nil, // 18: xray.app.router.command.RoutingContext.AttributesEntry
(net.Network)(0), // 19: xray.common.net.Network
(*serial.TypedMessage)(nil), // 20: xray.common.serial.TypedMessage
}
var file_app_router_command_command_proto_depIdxs = []int32{
16, // 0: xray.app.router.command.RoutingContext.Network:type_name -> xray.common.net.Network
15, // 1: xray.app.router.command.RoutingContext.Attributes:type_name -> xray.app.router.command.RoutingContext.AttributesEntry
19, // 0: xray.app.router.command.RoutingContext.Network:type_name -> xray.common.net.Network
18, // 1: xray.app.router.command.RoutingContext.Attributes:type_name -> xray.app.router.command.RoutingContext.AttributesEntry
0, // 2: xray.app.router.command.TestRouteRequest.RoutingContext:type_name -> xray.app.router.command.RoutingContext
4, // 3: xray.app.router.command.BalancerMsg.override:type_name -> xray.app.router.command.OverrideInfo
3, // 4: xray.app.router.command.BalancerMsg.principle_target:type_name -> xray.app.router.command.PrincipleTargetInfo
5, // 5: xray.app.router.command.GetBalancerInfoResponse.balancer:type_name -> xray.app.router.command.BalancerMsg
17, // 6: xray.app.router.command.AddRuleRequest.config:type_name -> xray.common.serial.TypedMessage
1, // 7: xray.app.router.command.RoutingService.SubscribeRoutingStats:input_type -> xray.app.router.command.SubscribeRoutingStatsRequest
2, // 8: xray.app.router.command.RoutingService.TestRoute:input_type -> xray.app.router.command.TestRouteRequest
6, // 9: xray.app.router.command.RoutingService.GetBalancerInfo:input_type -> xray.app.router.command.GetBalancerInfoRequest
8, // 10: xray.app.router.command.RoutingService.OverrideBalancerTarget:input_type -> xray.app.router.command.OverrideBalancerTargetRequest
10, // 11: xray.app.router.command.RoutingService.AddRule:input_type -> xray.app.router.command.AddRuleRequest
12, // 12: xray.app.router.command.RoutingService.RemoveRule:input_type -> xray.app.router.command.RemoveRuleRequest
0, // 13: xray.app.router.command.RoutingService.SubscribeRoutingStats:output_type -> xray.app.router.command.RoutingContext
0, // 14: xray.app.router.command.RoutingService.TestRoute:output_type -> xray.app.router.command.RoutingContext
7, // 15: xray.app.router.command.RoutingService.GetBalancerInfo:output_type -> xray.app.router.command.GetBalancerInfoResponse
9, // 16: xray.app.router.command.RoutingService.OverrideBalancerTarget:output_type -> xray.app.router.command.OverrideBalancerTargetResponse
11, // 17: xray.app.router.command.RoutingService.AddRule:output_type -> xray.app.router.command.AddRuleResponse
13, // 18: xray.app.router.command.RoutingService.RemoveRule:output_type -> xray.app.router.command.RemoveRuleResponse
13, // [13:19] is the sub-list for method output_type
7, // [7:13] is the sub-list for method input_type
7, // [7:7] is the sub-list for extension type_name
7, // [7:7] is the sub-list for extension extendee
0, // [0:7] is the sub-list for field type_name
20, // 6: xray.app.router.command.AddRuleRequest.config:type_name -> xray.common.serial.TypedMessage
15, // 7: xray.app.router.command.ListRuleResponse.rules:type_name -> xray.app.router.command.ListRuleItem
1, // 8: xray.app.router.command.RoutingService.SubscribeRoutingStats:input_type -> xray.app.router.command.SubscribeRoutingStatsRequest
2, // 9: xray.app.router.command.RoutingService.TestRoute:input_type -> xray.app.router.command.TestRouteRequest
6, // 10: xray.app.router.command.RoutingService.GetBalancerInfo:input_type -> xray.app.router.command.GetBalancerInfoRequest
8, // 11: xray.app.router.command.RoutingService.OverrideBalancerTarget:input_type -> xray.app.router.command.OverrideBalancerTargetRequest
10, // 12: xray.app.router.command.RoutingService.AddRule:input_type -> xray.app.router.command.AddRuleRequest
12, // 13: xray.app.router.command.RoutingService.RemoveRule:input_type -> xray.app.router.command.RemoveRuleRequest
14, // 14: xray.app.router.command.RoutingService.ListRule:input_type -> xray.app.router.command.ListRuleRequest
0, // 15: xray.app.router.command.RoutingService.SubscribeRoutingStats:output_type -> xray.app.router.command.RoutingContext
0, // 16: xray.app.router.command.RoutingService.TestRoute:output_type -> xray.app.router.command.RoutingContext
7, // 17: xray.app.router.command.RoutingService.GetBalancerInfo:output_type -> xray.app.router.command.GetBalancerInfoResponse
9, // 18: xray.app.router.command.RoutingService.OverrideBalancerTarget:output_type -> xray.app.router.command.OverrideBalancerTargetResponse
11, // 19: xray.app.router.command.RoutingService.AddRule:output_type -> xray.app.router.command.AddRuleResponse
13, // 20: xray.app.router.command.RoutingService.RemoveRule:output_type -> xray.app.router.command.RemoveRuleResponse
16, // 21: xray.app.router.command.RoutingService.ListRule:output_type -> xray.app.router.command.ListRuleResponse
15, // [15:22] is the sub-list for method output_type
8, // [8:15] 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_router_command_command_proto_init() }
@@ -1075,9 +1119,9 @@ func file_app_router_command_command_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_router_command_command_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_router_command_command_proto_rawDesc), len(file_app_router_command_command_proto_rawDesc)),
NumEnums: 0,
NumMessages: 16,
NumMessages: 19,
NumExtensions: 0,
NumServices: 1,
},
@@ -1086,7 +1130,6 @@ func file_app_router_command_command_proto_init() {
MessageInfos: file_app_router_command_command_proto_msgTypes,
}.Build()
File_app_router_command_command_proto = out.File
file_app_router_command_command_proto_rawDesc = nil
file_app_router_command_command_proto_goTypes = nil
file_app_router_command_command_proto_depIdxs = nil
}
+13
View File
@@ -104,6 +104,17 @@ message RemoveRuleRequest {
message RemoveRuleResponse {}
message ListRuleRequest {}
message ListRuleItem {
string tag = 1;
string ruleTag = 2;
}
message ListRuleResponse{
repeated ListRuleItem rules = 1;
}
service RoutingService {
rpc SubscribeRoutingStats(SubscribeRoutingStatsRequest)
returns (stream RoutingContext) {}
@@ -114,6 +125,8 @@ service RoutingService {
rpc AddRule(AddRuleRequest) returns (AddRuleResponse) {}
rpc RemoveRule(RemoveRuleRequest) returns (RemoveRuleResponse) {}
rpc ListRule(ListRuleRequest) returns (ListRuleResponse) {}
}
message Config {}
+47 -9
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.28.2
// - protoc-gen-go-grpc v1.6.0
// - protoc v6.33.5
// source: app/router/command/command.proto
package command
@@ -25,6 +25,7 @@ const (
RoutingService_OverrideBalancerTarget_FullMethodName = "/xray.app.router.command.RoutingService/OverrideBalancerTarget"
RoutingService_AddRule_FullMethodName = "/xray.app.router.command.RoutingService/AddRule"
RoutingService_RemoveRule_FullMethodName = "/xray.app.router.command.RoutingService/RemoveRule"
RoutingService_ListRule_FullMethodName = "/xray.app.router.command.RoutingService/ListRule"
)
// RoutingServiceClient is the client API for RoutingService service.
@@ -37,6 +38,7 @@ type RoutingServiceClient interface {
OverrideBalancerTarget(ctx context.Context, in *OverrideBalancerTargetRequest, opts ...grpc.CallOption) (*OverrideBalancerTargetResponse, error)
AddRule(ctx context.Context, in *AddRuleRequest, opts ...grpc.CallOption) (*AddRuleResponse, error)
RemoveRule(ctx context.Context, in *RemoveRuleRequest, opts ...grpc.CallOption) (*RemoveRuleResponse, error)
ListRule(ctx context.Context, in *ListRuleRequest, opts ...grpc.CallOption) (*ListRuleResponse, error)
}
type routingServiceClient struct {
@@ -116,6 +118,16 @@ func (c *routingServiceClient) RemoveRule(ctx context.Context, in *RemoveRuleReq
return out, nil
}
func (c *routingServiceClient) ListRule(ctx context.Context, in *ListRuleRequest, opts ...grpc.CallOption) (*ListRuleResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListRuleResponse)
err := c.cc.Invoke(ctx, RoutingService_ListRule_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// RoutingServiceServer is the server API for RoutingService service.
// All implementations must embed UnimplementedRoutingServiceServer
// for forward compatibility.
@@ -126,6 +138,7 @@ type RoutingServiceServer interface {
OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error)
AddRule(context.Context, *AddRuleRequest) (*AddRuleResponse, error)
RemoveRule(context.Context, *RemoveRuleRequest) (*RemoveRuleResponse, error)
ListRule(context.Context, *ListRuleRequest) (*ListRuleResponse, error)
mustEmbedUnimplementedRoutingServiceServer()
}
@@ -137,22 +150,25 @@ type RoutingServiceServer interface {
type UnimplementedRoutingServiceServer struct{}
func (UnimplementedRoutingServiceServer) SubscribeRoutingStats(*SubscribeRoutingStatsRequest, grpc.ServerStreamingServer[RoutingContext]) error {
return status.Errorf(codes.Unimplemented, "method SubscribeRoutingStats not implemented")
return status.Error(codes.Unimplemented, "method SubscribeRoutingStats not implemented")
}
func (UnimplementedRoutingServiceServer) TestRoute(context.Context, *TestRouteRequest) (*RoutingContext, error) {
return nil, status.Errorf(codes.Unimplemented, "method TestRoute not implemented")
return nil, status.Error(codes.Unimplemented, "method TestRoute not implemented")
}
func (UnimplementedRoutingServiceServer) GetBalancerInfo(context.Context, *GetBalancerInfoRequest) (*GetBalancerInfoResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetBalancerInfo not implemented")
return nil, status.Error(codes.Unimplemented, "method GetBalancerInfo not implemented")
}
func (UnimplementedRoutingServiceServer) OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method OverrideBalancerTarget not implemented")
return nil, status.Error(codes.Unimplemented, "method OverrideBalancerTarget not implemented")
}
func (UnimplementedRoutingServiceServer) AddRule(context.Context, *AddRuleRequest) (*AddRuleResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddRule not implemented")
return nil, status.Error(codes.Unimplemented, "method AddRule not implemented")
}
func (UnimplementedRoutingServiceServer) RemoveRule(context.Context, *RemoveRuleRequest) (*RemoveRuleResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RemoveRule not implemented")
return nil, status.Error(codes.Unimplemented, "method RemoveRule not implemented")
}
func (UnimplementedRoutingServiceServer) ListRule(context.Context, *ListRuleRequest) (*ListRuleResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListRule not implemented")
}
func (UnimplementedRoutingServiceServer) mustEmbedUnimplementedRoutingServiceServer() {}
func (UnimplementedRoutingServiceServer) testEmbeddedByValue() {}
@@ -165,7 +181,7 @@ type UnsafeRoutingServiceServer interface {
}
func RegisterRoutingServiceServer(s grpc.ServiceRegistrar, srv RoutingServiceServer) {
// If the following call pancis, it indicates UnimplementedRoutingServiceServer was
// If the following call panics, it indicates UnimplementedRoutingServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@@ -276,6 +292,24 @@ func _RoutingService_RemoveRule_Handler(srv interface{}, ctx context.Context, de
return interceptor(ctx, in, info, handler)
}
func _RoutingService_ListRule_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListRuleRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RoutingServiceServer).ListRule(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: RoutingService_ListRule_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RoutingServiceServer).ListRule(ctx, req.(*ListRuleRequest))
}
return interceptor(ctx, in, info, handler)
}
// RoutingService_ServiceDesc is the grpc.ServiceDesc for RoutingService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -303,6 +337,10 @@ var RoutingService_ServiceDesc = grpc.ServiceDesc{
MethodName: "RemoveRule",
Handler: _RoutingService_RemoveRule_Handler,
},
{
MethodName: "ListRule",
Handler: _RoutingService_ListRule_Handler,
},
},
Streams: []grpc.StreamDesc{
{
+4 -3
View File
@@ -12,6 +12,7 @@ import (
. "github.com/xtls/xray-core/app/router/command"
"github.com/xtls/xray-core/app/stats"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/testing/mocks"
@@ -303,12 +304,12 @@ func TestServiceTestRoute(t *testing.T) {
TargetTag: &router.RoutingRule_Tag{Tag: "out"},
},
{
Domain: []*router.Domain{{Type: router.Domain_Domain, Value: "com"}},
Domain: []*geodata.DomainRule{{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "com"}}}},
TargetTag: &router.RoutingRule_Tag{Tag: "out"},
},
{
SourceGeoip: []*router.GeoIP{{CountryCode: "private", Cidr: []*router.CIDR{{Ip: []byte{127, 0, 0, 0}, Prefix: 8}}}},
TargetTag: &router.RoutingRule_Tag{Tag: "out"},
SourceIp: []*geodata.IPRule{{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDRRule{Cidr: &geodata.CIDR{Ip: []byte{127, 0, 0, 0}, Prefix: 8}}}}},
TargetTag: &router.RoutingRule_Tag{Tag: "out"},
},
{
UserEmail: []string{"example@example.com"},
+168 -80
View File
@@ -1,13 +1,19 @@
package router
import (
"context"
"os"
"path/filepath"
"regexp"
"runtime"
"slices"
"strings"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/strmatcher"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/features/routing/dns"
)
type Condition interface {
@@ -40,51 +46,18 @@ func (v *ConditionChan) Len() int {
return len(*v)
}
var matcherTypeMap = map[Domain_Type]strmatcher.Type{
Domain_Plain: strmatcher.Substr,
Domain_Regex: strmatcher.Regex,
Domain_Domain: strmatcher.Domain,
Domain_Full: strmatcher.Full,
}
type DomainMatcher struct{ geodata.DomainMatcher }
func domainToMatcher(domain *Domain) (strmatcher.Matcher, error) {
matcherType, f := matcherTypeMap[domain.Type]
if !f {
return nil, errors.New("unsupported domain type", domain.Type)
}
matcher, err := matcherType.New(domain.Value)
func NewDomainMatcher(rules []*geodata.DomainRule) (*DomainMatcher, error) {
m, err := geodata.DomainReg.BuildDomainMatcher(rules)
if err != nil {
return nil, errors.New("failed to create domain matcher").Base(err)
return nil, err
}
return matcher, nil
}
type DomainMatcher struct {
matchers strmatcher.IndexMatcher
}
func NewMphMatcherGroup(domains []*Domain) (*DomainMatcher, error) {
g := strmatcher.NewMphMatcherGroup()
for _, d := range domains {
matcherType, f := matcherTypeMap[d.Type]
if !f {
return nil, errors.New("unsupported domain type", d.Type)
}
_, err := g.AddPattern(d.Value, matcherType)
if err != nil {
return nil, err
}
}
g.Build()
return &DomainMatcher{
matchers: g,
}, nil
return &DomainMatcher{DomainMatcher: m}, nil
}
func (m *DomainMatcher) ApplyDomain(domain string) bool {
return len(m.matchers.Match(strings.ToLower(domain))) > 0
return m.DomainMatcher.MatchAny(strings.ToLower(domain))
}
// Apply implements Condition.
@@ -93,64 +66,56 @@ func (m *DomainMatcher) Apply(ctx routing.Context) bool {
if len(domain) == 0 {
return false
}
return m.ApplyDomain(domain)
return m.DomainMatcher.MatchAny(strings.ToLower(domain))
}
type MultiGeoIPMatcher struct {
matchers []*GeoIPMatcher
asType string // local, source, target
type MatcherAsType byte
const (
MatcherAsType_Local MatcherAsType = iota
MatcherAsType_Source
MatcherAsType_Target
MatcherAsType_VlessRoute // for port
)
type IPMatcher struct {
matcher geodata.IPMatcher
asType MatcherAsType
}
func NewMultiGeoIPMatcher(geoips []*GeoIP, asType string) (*MultiGeoIPMatcher, error) {
var matchers []*GeoIPMatcher
for _, geoip := range geoips {
matcher, err := GlobalGeoIPContainer.Add(geoip)
if err != nil {
return nil, err
}
matchers = append(matchers, matcher)
func NewIPMatcher(rules []*geodata.IPRule, asType MatcherAsType) (*IPMatcher, error) {
m, err := geodata.IPReg.BuildIPMatcher(rules)
if err != nil {
return nil, err
}
matcher := &MultiGeoIPMatcher{
matchers: matchers,
asType: asType,
}
return matcher, nil
return &IPMatcher{matcher: m, asType: asType}, nil
}
// Apply implements Condition.
func (m *MultiGeoIPMatcher) Apply(ctx routing.Context) bool {
func (m *IPMatcher) Apply(ctx routing.Context) bool {
var ips []net.IP
switch m.asType {
case "local":
case MatcherAsType_Local:
ips = ctx.GetLocalIPs()
case "source":
case MatcherAsType_Source:
ips = ctx.GetSourceIPs()
case "target":
case MatcherAsType_Target:
ips = ctx.GetTargetIPs()
default:
panic("unreachable, asType should be local or source or target")
panic("unk asType")
}
for _, ip := range ips {
for _, matcher := range m.matchers {
if matcher.Match(ip) {
return true
}
}
}
return false
return m.matcher.AnyMatch(ips)
}
type PortMatcher struct {
port net.MemoryPortList
asType string // local, source, target
asType MatcherAsType
}
// NewPortMatcher create a new port matcher that can match source or local or destination port
func NewPortMatcher(list *net.PortList, asType string) *PortMatcher {
func NewPortMatcher(list *net.PortList, asType MatcherAsType) *PortMatcher {
return &PortMatcher{
port: net.PortListFromProto(list),
asType: asType,
@@ -160,18 +125,17 @@ func NewPortMatcher(list *net.PortList, asType string) *PortMatcher {
// Apply implements Condition.
func (v *PortMatcher) Apply(ctx routing.Context) bool {
switch v.asType {
case "local":
case MatcherAsType_Local:
return v.port.Contains(ctx.GetLocalPort())
case "source":
case MatcherAsType_Source:
return v.port.Contains(ctx.GetSourcePort())
case "target":
case MatcherAsType_Target:
return v.port.Contains(ctx.GetTargetPort())
case "vlessRoute":
case MatcherAsType_VlessRoute:
return v.port.Contains(ctx.GetVlessRoute())
default:
panic("unreachable, asType should be local or source or target")
panic("unk asType")
}
}
type NetworkMatcher struct {
@@ -325,3 +289,127 @@ func (m *AttributeMatcher) Apply(ctx routing.Context) bool {
}
return m.Match(attributes)
}
type ProcessNameMatcher struct {
ProcessNames []string
AbsPaths []string
Folders []string
MatchXraySelf bool
}
func NewProcessNameMatcher(names []string) *ProcessNameMatcher {
processNames := []string{}
folders := []string{}
absPaths := []string{}
matchXraySelf := false
for _, name := range names {
if name == "self/" {
matchXraySelf = true
continue
}
// replace xray/ with self executable path
if name == "xray/" {
xrayPath, err := os.Executable()
if err != nil {
errors.LogError(context.Background(), "Failed to get xray executable path: ", err)
continue
}
name = xrayPath
}
name := filepath.ToSlash(name)
// /usr/bin/
if strings.HasSuffix(name, "/") {
folders = append(folders, name)
continue
}
// /usr/bin/curl
if strings.Contains(name, "/") {
absPaths = append(absPaths, name)
continue
}
// curl.exe or curl
processNames = append(processNames, strings.TrimSuffix(name, ".exe"))
}
return &ProcessNameMatcher{
ProcessNames: processNames,
AbsPaths: absPaths,
Folders: folders,
MatchXraySelf: matchXraySelf,
}
}
func (m *ProcessNameMatcher) Apply(ctx routing.Context) bool {
if len(ctx.GetSourceIPs()) == 0 {
return false
}
srcPort := uint16(ctx.GetSourcePort())
srcIP := ctx.GetSourceIPs()[0].String()
var network string
switch ctx.GetNetwork() {
case net.Network_TCP:
network = "tcp"
case net.Network_UDP:
network = "udp"
default:
return false
}
var dstIP string
var dstPort uint16 = 0
// do not use resolved IP because Android process lookup needs original dst ip
resolvableContext, ok := ctx.(*dns.ResolvableContext)
if ok && len(resolvableContext.Context.GetTargetIPs()) > 0 {
dstIP = resolvableContext.Context.GetTargetIPs()[0].String()
dstPort = uint16(resolvableContext.Context.GetTargetPort())
} else if len(ctx.GetTargetIPs()) > 0 {
dstIP = ctx.GetTargetIPs()[0].String()
dstPort = uint16(ctx.GetTargetPort())
}
pid, name, absPath, err := net.FindProcess(network, srcIP, uint16(srcPort), dstIP, uint16(dstPort))
if err != nil {
if err != net.ErrNotLocal {
errors.LogError(context.Background(), "Unables to find local process name: ", err)
}
return false
}
if m.MatchXraySelf {
if pid == os.Getpid() {
return true
}
}
if slices.Contains(m.ProcessNames, name) {
return true
}
if slices.Contains(m.AbsPaths, absPath) {
return true
}
for _, f := range m.Folders {
if strings.HasPrefix(absPath, f) {
return true
}
}
return false
}
// LocalOSMatcher matches the operating system Xray itself is running on. That never
// changes while Xray is running, so the result is resolved when the rule is built.
type LocalOSMatcher struct {
matched bool
}
func NewLocalOSMatcher(names []string) *LocalOSMatcher {
return &LocalOSMatcher{
matched: slices.ContainsFunc(names, func(name string) bool {
return strings.EqualFold(name, runtime.GOOS)
}),
}
}
// Apply implements Condition.
func (m *LocalOSMatcher) Apply(_ routing.Context) bool {
return m.matched
}
-144
View File
@@ -1,144 +0,0 @@
package router
import (
"net/netip"
"strconv"
"github.com/xtls/xray-core/common/net"
"go4.org/netipx"
)
type GeoIPMatcher struct {
countryCode string
reverseMatch bool
ip4 *netipx.IPSet
ip6 *netipx.IPSet
}
func (m *GeoIPMatcher) Init(cidrs []*CIDR) error {
var builder4, builder6 netipx.IPSetBuilder
for _, cidr := range cidrs {
ip := net.IP(cidr.GetIp())
ipPrefixString := ip.String() + "/" + strconv.Itoa(int(cidr.GetPrefix()))
ipPrefix, err := netip.ParsePrefix(ipPrefixString)
if err != nil {
return err
}
switch len(ip) {
case net.IPv4len:
builder4.AddPrefix(ipPrefix)
case net.IPv6len:
builder6.AddPrefix(ipPrefix)
}
}
if ip4, err := builder4.IPSet(); err != nil {
return err
} else {
m.ip4 = ip4
}
if ip6, err := builder6.IPSet(); err != nil {
return err
} else {
m.ip6 = ip6
}
return nil
}
func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) {
m.reverseMatch = isReverseMatch
}
func (m *GeoIPMatcher) match4(ip net.IP) bool {
nip, ok := netipx.FromStdIP(ip)
if !ok {
return false
}
return m.ip4.Contains(nip)
}
func (m *GeoIPMatcher) match6(ip net.IP) bool {
nip, ok := netipx.FromStdIP(ip)
if !ok {
return false
}
return m.ip6.Contains(nip)
}
// Match returns true if the given ip is included by the GeoIP.
func (m *GeoIPMatcher) Match(ip net.IP) bool {
isMatched := false
switch len(ip) {
case net.IPv4len:
isMatched = m.match4(ip)
case net.IPv6len:
isMatched = m.match6(ip)
}
if m.reverseMatch {
return !isMatched
}
return isMatched
}
// GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code.
type GeoIPMatcherContainer struct {
matchers []*GeoIPMatcher
}
// Add adds a new GeoIP set into the container.
// If the country code of GeoIP is not empty, GeoIPMatcherContainer will try to find an existing one, instead of adding a new one.
func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) {
if len(geoip.CountryCode) > 0 {
for _, m := range c.matchers {
if m.countryCode == geoip.CountryCode && m.reverseMatch == geoip.ReverseMatch {
return m, nil
}
}
}
m := &GeoIPMatcher{
countryCode: geoip.CountryCode,
reverseMatch: geoip.ReverseMatch,
}
if err := m.Init(geoip.Cidr); err != nil {
return nil, err
}
if len(geoip.CountryCode) > 0 {
c.matchers = append(c.matchers, m)
}
return m, nil
}
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
}
-279
View File
@@ -1,279 +0,0 @@
package router_test
import (
"fmt"
"os"
"path/filepath"
"testing"
"github.com/xtls/xray-core/app/router"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/platform"
"github.com/xtls/xray-core/common/platform/filesystem"
"google.golang.org/protobuf/proto"
)
func getAssetPath(file string) (string, error) {
path := platform.GetAssetLocation(file)
_, err := os.Stat(path)
if os.IsNotExist(err) {
path := filepath.Join("..", "..", "resources", file)
_, err := os.Stat(path)
if os.IsNotExist(err) {
return "", fmt.Errorf("can't find %s in standard asset locations or {project_root}/resources", file)
}
if err != nil {
return "", fmt.Errorf("can't stat %s: %v", path, err)
}
return path, nil
}
if err != nil {
return "", fmt.Errorf("can't stat %s: %v", path, err)
}
return path, nil
}
func TestGeoIPMatcherContainer(t *testing.T) {
container := &router.GeoIPMatcherContainer{}
m1, err := container.Add(&router.GeoIP{
CountryCode: "CN",
})
common.Must(err)
m2, err := container.Add(&router.GeoIP{
CountryCode: "US",
})
common.Must(err)
m3, err := container.Add(&router.GeoIP{
CountryCode: "CN",
})
common.Must(err)
if m1 != m3 {
t.Error("expect same matcher for same geoip, but not")
}
if m1 == m2 {
t.Error("expect different matcher for different geoip, but actually same")
}
}
func TestGeoIPMatcher(t *testing.T) {
cidrList := []*router.CIDR{
{Ip: []byte{0, 0, 0, 0}, Prefix: 8},
{Ip: []byte{10, 0, 0, 0}, Prefix: 8},
{Ip: []byte{100, 64, 0, 0}, Prefix: 10},
{Ip: []byte{127, 0, 0, 0}, Prefix: 8},
{Ip: []byte{169, 254, 0, 0}, Prefix: 16},
{Ip: []byte{172, 16, 0, 0}, Prefix: 12},
{Ip: []byte{192, 0, 0, 0}, Prefix: 24},
{Ip: []byte{192, 0, 2, 0}, Prefix: 24},
{Ip: []byte{192, 168, 0, 0}, Prefix: 16},
{Ip: []byte{192, 18, 0, 0}, Prefix: 15},
{Ip: []byte{198, 51, 100, 0}, Prefix: 24},
{Ip: []byte{203, 0, 113, 0}, Prefix: 24},
{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
}
matcher := &router.GeoIPMatcher{}
common.Must(matcher.Init(cidrList))
testCases := []struct {
Input string
Output bool
}{
{
Input: "192.168.1.1",
Output: true,
},
{
Input: "192.0.0.0",
Output: true,
},
{
Input: "192.0.1.0",
Output: false,
},
{
Input: "0.1.0.0",
Output: true,
},
{
Input: "1.0.0.1",
Output: false,
},
{
Input: "8.8.8.7",
Output: false,
},
{
Input: "8.8.8.8",
Output: true,
},
{
Input: "2001:cdba::3257:9652",
Output: false,
},
{
Input: "91.108.255.254",
Output: true,
},
}
for _, testCase := range testCases {
ip := net.ParseAddress(testCase.Input).IP()
actual := matcher.Match(ip)
if actual != testCase.Output {
t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
}
}
}
func TestGeoIPMatcherRegression(t *testing.T) {
cidrList := []*router.CIDR{
{Ip: []byte{98, 108, 20, 0}, Prefix: 22},
{Ip: []byte{98, 108, 20, 0}, Prefix: 23},
}
matcher := &router.GeoIPMatcher{}
common.Must(matcher.Init(cidrList))
testCases := []struct {
Input string
Output bool
}{
{
Input: "98.108.22.11",
Output: true,
},
{
Input: "98.108.25.0",
Output: false,
},
}
for _, testCase := range testCases {
ip := net.ParseAddress(testCase.Input).IP()
actual := matcher.Match(ip)
if actual != testCase.Output {
t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
}
}
}
func TestGeoIPReverseMatcher(t *testing.T) {
cidrList := []*router.CIDR{
{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
}
matcher := &router.GeoIPMatcher{}
matcher.SetReverseMatch(true) // Reverse match
common.Must(matcher.Init(cidrList))
testCases := []struct {
Input string
Output bool
}{
{
Input: "8.8.8.8",
Output: false,
},
{
Input: "2001:cdba::3257:9652",
Output: true,
},
{
Input: "91.108.255.254",
Output: false,
},
}
for _, testCase := range testCases {
ip := net.ParseAddress(testCase.Input).IP()
actual := matcher.Match(ip)
if actual != testCase.Output {
t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
}
}
}
func TestGeoIPMatcher4CN(t *testing.T) {
ips, err := loadGeoIP("CN")
common.Must(err)
matcher := &router.GeoIPMatcher{}
common.Must(matcher.Init(ips))
if matcher.Match([]byte{8, 8, 8, 8}) {
t.Error("expect CN geoip doesn't contain 8.8.8.8, but actually does")
}
}
func TestGeoIPMatcher6US(t *testing.T) {
ips, err := loadGeoIP("US")
common.Must(err)
matcher := &router.GeoIPMatcher{}
common.Must(matcher.Init(ips))
if !matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP()) {
t.Error("expect US geoip contain 2001:4860:4860::8888, but actually not")
}
}
func loadGeoIP(country string) ([]*router.CIDR, error) {
path, err := getAssetPath("geoip.dat")
if err != nil {
return nil, err
}
geoipBytes, err := filesystem.ReadFile(path)
if err != nil {
return nil, err
}
var geoipList router.GeoIPList
if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
return nil, err
}
for _, geoip := range geoipList.Entry {
if geoip.CountryCode == country {
return geoip.Cidr, nil
}
}
panic("country not found: " + country)
}
func BenchmarkGeoIPMatcher4CN(b *testing.B) {
ips, err := loadGeoIP("CN")
common.Must(err)
matcher := &router.GeoIPMatcher{}
common.Must(matcher.Init(ips))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = matcher.Match([]byte{8, 8, 8, 8})
}
}
func BenchmarkGeoIPMatcher6US(b *testing.B) {
ips, err := loadGeoIP("US")
common.Must(err)
matcher := &router.GeoIPMatcher{}
common.Must(matcher.Init(ips))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP())
}
}
+65 -94
View File
@@ -1,20 +1,21 @@
package router_test
import (
"path/filepath"
"runtime"
"strconv"
"strings"
"testing"
. "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/geodata"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/platform/filesystem"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/protocol/http"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/features/routing"
routing_session "github.com/xtls/xray-core/features/routing/session"
"google.golang.org/protobuf/proto"
)
func withBackground() routing.Context {
@@ -45,18 +46,15 @@ func TestRoutingRule(t *testing.T) {
}{
{
rule: &RoutingRule{
Domain: []*Domain{
Domain: []*geodata.DomainRule{
{
Value: "example.com",
Type: Domain_Plain,
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Substr, Value: "example.com"}},
},
{
Value: "google.com",
Type: Domain_Domain,
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "google.com"}},
},
{
Value: "^facebook\\.com$",
Type: Domain_Regex,
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Regex, Value: "^facebook\\.com$"}},
},
},
},
@@ -93,20 +91,25 @@ func TestRoutingRule(t *testing.T) {
},
{
rule: &RoutingRule{
Geoip: []*GeoIP{
Ip: []*geodata.IPRule{
{
Cidr: []*CIDR{
{
Ip: []byte{8, 8, 8, 8},
Prefix: 32,
Value: &geodata.IPRule_Custom{
Custom: &geodata.CIDRRule{
Cidr: &geodata.CIDR{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
},
{
Ip: []byte{8, 8, 8, 8},
Prefix: 32,
},
},
{
Value: &geodata.IPRule_Custom{
Custom: &geodata.CIDRRule{
Cidr: &geodata.CIDR{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
},
{
Ip: net.ParseAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334").IP(),
Prefix: 128,
},
},
{
Value: &geodata.IPRule_Custom{
Custom: &geodata.CIDRRule{
Cidr: &geodata.CIDR{Ip: net.ParseAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334").IP(), Prefix: 128},
},
},
},
@@ -133,12 +136,11 @@ func TestRoutingRule(t *testing.T) {
},
{
rule: &RoutingRule{
SourceGeoip: []*GeoIP{
SourceIp: []*geodata.IPRule{
{
Cidr: []*CIDR{
{
Ip: []byte{192, 168, 0, 0},
Prefix: 16,
Value: &geodata.IPRule_Custom{
Custom: &geodata.CIDRRule{
Cidr: &geodata.CIDR{Ip: []byte{192, 168, 0, 0}, Prefix: 16},
},
},
},
@@ -300,35 +302,12 @@ func TestRoutingRule(t *testing.T) {
}
}
func loadGeoSite(country string) ([]*Domain, error) {
path, err := getAssetPath("geosite.dat")
if err != nil {
return nil, err
}
geositeBytes, err := filesystem.ReadFile(path)
if err != nil {
return nil, err
}
var geositeList GeoSiteList
if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
return nil, err
}
for _, site := range geositeList.Entry {
if site.CountryCode == country {
return site.Domain, nil
}
}
return nil, errors.New("country not found: " + country)
}
func TestChinaSites(t *testing.T) {
domains, err := loadGeoSite("CN")
t.Setenv("xray.location.asset", filepath.Join("..", "..", "resources"))
rules, err := geodata.ParseDomainRules([]string{"geosite:cn"}, geodata.Domain_Substr)
common.Must(err)
acMatcher, err := NewMphMatcherGroup(domains)
matcher, err := NewDomainMatcher(rules)
common.Must(err)
type TestCase struct {
@@ -359,18 +338,44 @@ func TestChinaSites(t *testing.T) {
}
for _, testCase := range testCases {
r := acMatcher.ApplyDomain(testCase.Domain)
r := matcher.ApplyDomain(testCase.Domain)
if r != testCase.Output {
t.Error("ACDomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r)
t.Error("DomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r)
}
}
}
func TestLocalOSRule(t *testing.T) {
otherOS := "plan9"
if runtime.GOOS == otherOS {
otherOS = "linux"
}
cases := []struct {
localOS []string
output bool
}{
{localOS: []string{runtime.GOOS}, output: true},
{localOS: []string{otherOS}, output: false},
{localOS: []string{otherOS, runtime.GOOS}, output: true},
{localOS: []string{strings.ToUpper(runtime.GOOS)}, output: true},
}
for _, test := range cases {
cond, err := (&RoutingRule{LocalOs: test.localOS}).BuildCondition()
common.Must(err)
if got := cond.Apply(withBackground()); got != test.output {
t.Errorf("for localOS %v on %s: expected %v, got %v", test.localOS, runtime.GOOS, test.output, got)
}
}
}
func BenchmarkMphDomainMatcher(b *testing.B) {
domains, err := loadGeoSite("CN")
b.Setenv("xray.location.asset", filepath.Join("..", "..", "resources"))
rules, err := geodata.ParseDomainRules([]string{"geosite:cn"}, geodata.Domain_Substr)
common.Must(err)
matcher, err := NewMphMatcherGroup(domains)
matcher, err := NewDomainMatcher(rules)
common.Must(err)
type TestCase struct {
@@ -409,45 +414,11 @@ func BenchmarkMphDomainMatcher(b *testing.B) {
}
func BenchmarkMultiGeoIPMatcher(b *testing.B) {
var geoips []*GeoIP
b.Setenv("xray.location.asset", filepath.Join("..", "..", "resources"))
rules, err := geodata.ParseIPRules([]string{"geoip:cn", "geoip:jp", "geoip:ca", "geoip:us"})
common.Must(err)
{
ips, err := loadGeoIP("CN")
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "CN",
Cidr: ips,
})
}
{
ips, err := loadGeoIP("JP")
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "JP",
Cidr: ips,
})
}
{
ips, err := loadGeoIP("CA")
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "CA",
Cidr: ips,
})
}
{
ips, err := loadGeoIP("US")
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "US",
Cidr: ips,
})
}
matcher, err := NewMultiGeoIPMatcher(geoips, "target")
matcher, err := NewIPMatcher(rules, MatcherAsType_Target)
common.Must(err)
ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)})
+60 -52
View File
@@ -15,6 +15,7 @@ type Rule struct {
RuleTag string
Balancer *Balancer
Condition Condition
Webhook *WebhookNotifier
}
func (r *Rule) GetTag() (string, error) {
@@ -32,72 +33,42 @@ func (r *Rule) Apply(ctx routing.Context) bool {
func (rr *RoutingRule) BuildCondition() (Condition, error) {
conds := NewConditionChan()
if len(rr.Domain) > 0 {
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.LocalOs) > 0 {
conds.Add(NewLocalOSMatcher(rr.LocalOs))
}
if len(rr.InboundTag) > 0 {
conds.Add(NewInboundTagMatcher(rr.InboundTag))
}
if rr.PortList != nil {
conds.Add(NewPortMatcher(rr.PortList, "target"))
}
if rr.SourcePortList != nil {
conds.Add(NewPortMatcher(rr.SourcePortList, "source"))
}
if rr.LocalPortList != nil {
conds.Add(NewPortMatcher(rr.LocalPortList, "local"))
}
if len(rr.Networks) > 0 {
conds.Add(NewNetworkMatcher(rr.Networks))
}
if len(rr.Geoip) > 0 {
cond, err := NewMultiGeoIPMatcher(rr.Geoip, "target")
if err != nil {
return nil, err
}
conds.Add(cond)
}
if len(rr.SourceGeoip) > 0 {
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))
}
if rr.PortList != nil {
conds.Add(NewPortMatcher(rr.PortList, MatcherAsType_Target))
}
if rr.SourcePortList != nil {
conds.Add(NewPortMatcher(rr.SourcePortList, MatcherAsType_Source))
}
if rr.LocalPortList != nil {
conds.Add(NewPortMatcher(rr.LocalPortList, MatcherAsType_Local))
}
if rr.VlessRouteList != nil {
conds.Add(NewPortMatcher(rr.VlessRouteList, MatcherAsType_VlessRoute))
}
if len(rr.UserEmail) > 0 {
conds.Add(NewUserMatcher(rr.UserEmail))
}
if len(rr.Attributes) > 0 {
configuredKeys := make(map[string]*regexp.Regexp)
for key, value := range rr.Attributes {
@@ -106,6 +77,43 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
conds.Add(&AttributeMatcher{configuredKeys})
}
if len(rr.Ip) > 0 {
cond, err := NewIPMatcher(rr.Ip, MatcherAsType_Target)
if err != nil {
return nil, err
}
conds.Add(cond)
}
if len(rr.SourceIp) > 0 {
cond, err := NewIPMatcher(rr.SourceIp, MatcherAsType_Source)
if err != nil {
return nil, err
}
conds.Add(cond)
}
if len(rr.LocalIp) > 0 {
cond, err := NewIPMatcher(rr.LocalIp, MatcherAsType_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.Domain) > 0 {
cond, err := NewDomainMatcher(rr.Domain)
if err != nil {
return nil, err
}
conds.Add(cond)
}
if len(rr.Process) > 0 {
conds.Add(NewProcessNameMatcher(rr.Process))
}
if conds.Len() == 0 {
return nil, errors.New("this rule has no effective fields").AtWarning()
}
+273 -773
View File
File diff suppressed because it is too large Load Diff
+26 -75
View File
@@ -9,67 +9,7 @@ option java_multiple_files = true;
import "common/serial/typed_message.proto";
import "common/net/port.proto";
import "common/net/network.proto";
// Domain for routing decision.
message Domain {
// Type of domain value.
enum Type {
// The value is used as is.
Plain = 0;
// The value is used as a regular expression.
Regex = 1;
// The value is a root domain.
Domain = 2;
// The value is a domain.
Full = 3;
}
// Domain matching type.
Type type = 1;
// Domain value.
string value = 2;
message Attribute {
string key = 1;
oneof typed_value {
bool bool_value = 2;
int64 int_value = 3;
}
}
// Attributes of this domain. May be used for filtering.
repeated Attribute attribute = 3;
}
// IP for routing decision, in CIDR form.
message CIDR {
// IP address, should be either 4 or 16 bytes.
bytes ip = 1;
// Number of leading ones in the network mask.
uint32 prefix = 2;
}
message GeoIP {
string country_code = 1;
repeated CIDR cidr = 2;
bool reverse_match = 3;
}
message GeoIPList {
repeated GeoIP entry = 1;
}
message GeoSite {
string country_code = 1;
repeated Domain domain = 2;
}
message GeoSiteList {
repeated GeoSite entry = 1;
}
import "common/geodata/geodat.proto";
message RoutingRule {
oneof target_tag {
@@ -79,26 +19,23 @@ message RoutingRule {
// Tag of routing balancer.
string balancing_tag = 12;
}
string rule_tag = 19;
string rule_tag = 19;
// List of domains for target domain matching.
repeated Domain domain = 2;
repeated xray.common.geodata.DomainRule domain = 2;
// List of GeoIPs for target IP address matching. If this entry exists, the
// cidr above will have no effect. GeoIP fields with the same country code are
// supposed to contain exactly same content. They will be merged during
// runtime. For customized GeoIPs, please leave country code empty.
repeated GeoIP geoip = 10;
// List of IPs for target IP address matching.
repeated xray.common.geodata.IPRule ip = 10;
// List of ports.
// List of ports for target port matching.
xray.common.net.PortList port_list = 14;
// List of networks for matching.
repeated xray.common.net.Network networks = 13;
// List of GeoIPs for source IP address matching. If this entry exists, the
// source_cidr above will have no effect.
repeated GeoIP source_geoip = 11;
// List of IPs for source IP address matching.
repeated xray.common.geodata.IPRule source_ip = 11;
// List of ports for source port matching.
xray.common.net.PortList source_port_list = 16;
@@ -109,10 +46,25 @@ message RoutingRule {
map<string, string> attributes = 15;
repeated GeoIP local_geoip = 17;
// List of IPs for local IP address matching.
repeated xray.common.geodata.IPRule local_ip = 17;
// List of ports for local port matching.
xray.common.net.PortList local_port_list = 18;
xray.common.net.PortList vless_route_list = 20;
repeated string process = 21;
WebhookConfig webhook = 22;
// List of operating systems for matching the one Xray itself is running on.
repeated string local_os = 23;
}
message WebhookConfig {
string url = 1;
uint32 deduplication = 2;
map<string, string> headers = 3;
}
message BalancingRule {
@@ -147,8 +99,7 @@ message Config {
// Use domain as is.
AsIs = 0;
// Always resolve IP for domains.
UseIp = 1;
reserved 1;
// Resolve to IP if the domain doesn't match any rules.
IpIfNonMatch = 2;
+91 -73
View File
@@ -2,7 +2,9 @@ package router
import (
"context"
sync "sync"
"maps"
"sync"
"sync/atomic"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
@@ -17,8 +19,8 @@ import (
// Router is an implementation of routing.Router.
type Router struct {
domainStrategy Config_DomainStrategy
rules []*Rule
balancers map[string]*Balancer
rules atomic.Pointer[[]*Rule]
balancers atomic.Pointer[map[string]*Balancer]
dns dns.Client
ctx context.Context
@@ -43,43 +45,14 @@ func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm out
r.ohm = ohm
r.dispatcher = dispatcher
r.balancers = make(map[string]*Balancer, len(config.BalancingRule))
for _, rule := range config.BalancingRule {
balancer, err := rule.Build(ohm, dispatcher)
if err != nil {
return err
}
balancer.InjectContext(ctx)
r.balancers[rule.Tag] = balancer
}
r.rules = make([]*Rule, 0, len(config.Rule))
for _, rule := range config.Rule {
cond, err := rule.BuildCondition()
if err != nil {
return err
}
rr := &Rule{
Condition: cond,
Tag: rule.GetTag(),
RuleTag: rule.GetRuleTag(),
}
btag := rule.GetBalancingTag()
if len(btag) > 0 {
brule, found := r.balancers[btag]
if !found {
return errors.New("balancer ", btag, " not found")
}
rr.Balancer = brule
}
r.rules = append(r.rules, rr)
}
return nil
r.rules.Store(new([]*Rule))
r.balancers.Store(&map[string]*Balancer{})
return r.ReloadRules(config, false)
}
// PickRoute implements routing.Router.
func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) {
originalCtx := ctx
rule, ctx, err := r.pickRouteInternal(ctx)
if err != nil {
return nil, err
@@ -88,12 +61,14 @@ func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) {
if err != nil {
return nil, err
}
if rule.Webhook != nil {
rule.Webhook.Fire(originalCtx, tag)
}
return &Route{Context: ctx, outboundTag: tag, ruleTag: rule.RuleTag}, nil
}
// AddRule implements routing.Router.
func (r *Router) AddRule(config *serial.TypedMessage, shouldAppend bool) error {
inst, err := config.GetInstance()
if err != nil {
return err
@@ -108,13 +83,22 @@ func (r *Router) ReloadRules(config *Config, shouldAppend bool) error {
r.mu.Lock()
defer r.mu.Unlock()
if !shouldAppend {
r.balancers = make(map[string]*Balancer, len(config.BalancingRule))
r.rules = make([]*Rule, 0, len(config.Rule))
oldRules := *r.rules.Load()
oldBalancers := *r.balancers.Load()
var newRules []*Rule
newBalancers := make(map[string]*Balancer)
existTags := make(map[string]bool, len(oldRules)+len(config.Rule))
if shouldAppend {
newRules = append(newRules, oldRules...)
maps.Copy(newBalancers, oldBalancers)
for _, rule := range oldRules {
existTags[rule.RuleTag] = true
}
}
for _, rule := range config.BalancingRule {
_, found := r.balancers[rule.Tag]
if found {
if _, found := newBalancers[rule.Tag]; found {
return errors.New("duplicate balancer tag")
}
balancer, err := rule.Build(r.ohm, r.dispatcher)
@@ -122,13 +106,10 @@ func (r *Router) ReloadRules(config *Config, shouldAppend bool) error {
return err
}
balancer.InjectContext(r.ctx)
r.balancers[rule.Tag] = balancer
newBalancers[rule.Tag] = balancer
}
for _, rule := range config.Rule {
if r.RuleExists(rule.GetRuleTag()) {
return errors.New("duplicate ruleTag ", rule.GetRuleTag())
}
cond, err := rule.BuildCondition()
if err != nil {
return err
@@ -138,49 +119,72 @@ func (r *Router) ReloadRules(config *Config, shouldAppend bool) error {
Tag: rule.GetTag(),
RuleTag: rule.GetRuleTag(),
}
btag := rule.GetBalancingTag()
if len(btag) > 0 {
brule, found := r.balancers[btag]
if rr.RuleTag != "" && existTags[rr.RuleTag] {
return errors.New("duplicate ruleTag ", rr.RuleTag)
}
existTags[rr.RuleTag] = true
if wh := rule.GetWebhook(); wh != nil {
notifier, err := NewWebhookNotifier(wh)
if err != nil {
return err
}
rr.Webhook = notifier
}
if btag := rule.GetBalancingTag(); len(btag) > 0 {
brule, found := newBalancers[btag]
if !found {
return errors.New("balancer ", btag, " not found")
}
rr.Balancer = brule
}
r.rules = append(r.rules, rr)
newRules = append(newRules, rr)
}
r.balancers.Store(&newBalancers)
r.rules.Store(&newRules)
if !shouldAppend {
closeWebhooks(oldRules)
}
return nil
}
func (r *Router) RuleExists(tag string) bool {
if tag != "" {
for _, rule := range r.rules {
if rule.RuleTag == tag {
return true
}
}
}
return false
}
// RemoveRule implements routing.Router.
func (r *Router) RemoveRule(tag string) error {
if tag == "" {
return errors.New("empty tag name!")
}
r.mu.Lock()
defer r.mu.Unlock()
newRules := []*Rule{}
if tag != "" {
for _, rule := range r.rules {
if rule.RuleTag != tag {
newRules = append(newRules, rule)
}
oldRules := *r.rules.Load()
newRules := make([]*Rule, 0, len(oldRules))
var removed []*Rule
for _, rule := range oldRules {
if rule.RuleTag != tag {
newRules = append(newRules, rule)
} else {
removed = append(removed, rule)
}
r.rules = newRules
return nil
}
return errors.New("empty tag name!")
r.rules.Store(&newRules)
closeWebhooks(removed)
return nil
}
// ListRule implements routing.Router
func (r *Router) ListRule() []routing.Route {
rules := *r.rules.Load()
ruleList := make([]routing.Route, 0, len(rules))
for _, rule := range rules {
ruleList = append(ruleList, &Route{
outboundTag: rule.Tag,
ruleTag: rule.RuleTag,
})
}
return ruleList
}
func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) {
// SkipDNSResolve is set from DNS module.
// the DOH remote server maybe a domain name,
@@ -191,7 +195,9 @@ func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context,
ctx = routing_dns.ContextWithDNSClient(ctx, r.dns)
}
for _, rule := range r.rules {
rules := *r.rules.Load()
for _, rule := range rules {
if rule.Apply(ctx) {
return rule, ctx, nil
}
@@ -204,7 +210,7 @@ func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context,
ctx = routing_dns.ContextWithDNSClient(ctx, r.dns)
// Try applying rules again if we have IPs.
for _, rule := range r.rules {
for _, rule := range rules {
if rule.Apply(ctx) {
return rule, ctx, nil
}
@@ -218,8 +224,20 @@ func (r *Router) Start() error {
return nil
}
// closeWebhooks closes all webhook notifiers in the given rule set.
func closeWebhooks(rules []*Rule) {
for _, rule := range rules {
if rule.Webhook != nil {
rule.Webhook.Close()
}
}
}
// Close implements common.Closable.
func (r *Router) Close() error {
r.mu.Lock()
defer r.mu.Unlock()
closeWebhooks(*r.rules.Load())
return nil
}
+13 -15
View File
@@ -7,6 +7,7 @@ import (
"github.com/golang/mock/gomock"
. "github.com/xtls/xray-core/app/router"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/features/dns"
@@ -155,12 +156,11 @@ func TestIPOnDemand(t *testing.T) {
TargetTag: &RoutingRule_Tag{
Tag: "test",
},
Geoip: []*GeoIP{
Ip: []*geodata.IPRule{
{
Cidr: []*CIDR{
{
Ip: []byte{192, 168, 0, 0},
Prefix: 16,
Value: &geodata.IPRule_Custom{
Custom: &geodata.CIDRRule{
Cidr: &geodata.CIDR{Ip: []byte{192, 168, 0, 0}, Prefix: 16},
},
},
},
@@ -200,12 +200,11 @@ func TestIPIfNonMatchDomain(t *testing.T) {
TargetTag: &RoutingRule_Tag{
Tag: "test",
},
Geoip: []*GeoIP{
Ip: []*geodata.IPRule{
{
Cidr: []*CIDR{
{
Ip: []byte{192, 168, 0, 0},
Prefix: 16,
Value: &geodata.IPRule_Custom{
Custom: &geodata.CIDRRule{
Cidr: &geodata.CIDR{Ip: []byte{192, 168, 0, 0}, Prefix: 16},
},
},
},
@@ -245,12 +244,11 @@ func TestIPIfNonMatchIP(t *testing.T) {
TargetTag: &RoutingRule_Tag{
Tag: "test",
},
Geoip: []*GeoIP{
Ip: []*geodata.IPRule{
{
Cidr: []*CIDR{
{
Ip: []byte{127, 0, 0, 0},
Prefix: 8,
Value: &geodata.IPRule_Custom{
Custom: &geodata.CIDRRule{
Cidr: &geodata.CIDR{Ip: []byte{127, 0, 0, 0}, Prefix: 8},
},
},
},
+22 -6
View File
@@ -3,6 +3,7 @@ package router
import (
"context"
"math"
"slices"
"sort"
"time"
@@ -77,7 +78,7 @@ func (s *LeastLoadStrategy) PickOutbound(candidates []string) string {
}
func (s *LeastLoadStrategy) pickOutbounds(candidates []string) []*node {
qualified := s.getNodes(candidates, time.Duration(s.settings.MaxRTT))
qualified := s.getNodes(candidates)
selects := s.selectLeastLoad(qualified)
return selects
}
@@ -138,7 +139,7 @@ func (s *LeastLoadStrategy) selectLeastLoad(nodes []*node) []*node {
return nodes[:count]
}
func (s *LeastLoadStrategy) getNodes(candidates []string, maxRTT time.Duration) []*node {
func (s *LeastLoadStrategy) getNodes(candidates []string) []*node {
if s.observer == nil {
errors.LogError(s.ctx, "observer is nil")
return make([]*node, 0)
@@ -151,12 +152,10 @@ func (s *LeastLoadStrategy) getNodes(candidates []string, maxRTT time.Duration)
results := observeResult.(*observatory.ObservationResult)
outboundlist := outboundList(candidates)
var ret []*node
for _, v := range results.Status {
if v.Alive && (v.Delay < maxRTT.Milliseconds() || maxRTT == 0) && outboundlist.contains(v.OutboundTag) {
if s.shouldSelectNode(v, candidates) {
record := &node{
Tag: v.OutboundTag,
CountAll: 1,
@@ -172,8 +171,8 @@ func (s *LeastLoadStrategy) getNodes(candidates []string, maxRTT time.Duration)
record.RTTDeviationCost = time.Duration(s.costs.Apply(v.OutboundTag, float64(v.HealthPing.Deviation)))
record.CountAll = int(v.HealthPing.All)
record.CountFail = int(v.HealthPing.Fail)
}
ret = append(ret, record)
}
}
@@ -182,6 +181,23 @@ func (s *LeastLoadStrategy) getNodes(candidates []string, maxRTT time.Duration)
return ret
}
func (s *LeastLoadStrategy) shouldSelectNode(v *observatory.OutboundStatus, candidates []string) bool {
maxRTT := time.Duration(s.settings.MaxRTT)
if !v.Alive {
return false
}
if maxRTT != 0 && v.Delay >= maxRTT.Milliseconds() {
return false
}
if !slices.Contains(candidates, v.OutboundTag) {
return false
}
if v.HealthPing != nil && v.HealthPing.All > 0 && s.settings.Tolerance > 0 && float64(v.HealthPing.Fail)/float64(v.HealthPing.All) > float64(s.settings.Tolerance) {
return false
}
return true
}
func leastloadSort(nodes []*node) {
sort.Slice(nodes, func(i, j int) bool {
left := nodes[i]
+5
View File
@@ -85,6 +85,7 @@ func TestSelectLeastExpected(t *testing.T) {
t.Errorf("expected: %v, actual: %v", expected, len(ns))
}
}
func TestSelectLeastExpected2(t *testing.T) {
strategy := &LeastLoadStrategy{
settings: &StrategyLeastLoadConfig{
@@ -102,6 +103,7 @@ func TestSelectLeastExpected2(t *testing.T) {
t.Errorf("expected: %v, actual: %v", expected, len(ns))
}
}
func TestSelectLeastExpectedAndBaselines(t *testing.T) {
strategy := &LeastLoadStrategy{
settings: &StrategyLeastLoadConfig{
@@ -122,6 +124,7 @@ func TestSelectLeastExpectedAndBaselines(t *testing.T) {
t.Errorf("expected: %v, actual: %v", expected, len(ns))
}
}
func TestSelectLeastExpectedAndBaselines2(t *testing.T) {
strategy := &LeastLoadStrategy{
settings: &StrategyLeastLoadConfig{
@@ -142,6 +145,7 @@ func TestSelectLeastExpectedAndBaselines2(t *testing.T) {
t.Errorf("expected: %v, actual: %v", expected, len(ns))
}
}
func TestSelectLeastLoadBaselines(t *testing.T) {
strategy := &LeastLoadStrategy{
settings: &StrategyLeastLoadConfig{
@@ -160,6 +164,7 @@ func TestSelectLeastLoadBaselines(t *testing.T) {
t.Errorf("expected: %v, actual: %v", expected, len(ns))
}
}
func TestSelectLeastLoadBaselinesNoQualified(t *testing.T) {
strategy := &LeastLoadStrategy{
settings: &StrategyLeastLoadConfig{
+235
View File
@@ -0,0 +1,235 @@
package router
import (
"bytes"
"context"
"encoding/json"
"io"
"net"
"net/http"
"sync"
"sync/atomic"
"time"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/utils"
"github.com/xtls/xray-core/features/routing"
routing_session "github.com/xtls/xray-core/features/routing/session"
)
func ptr[T any](v T) *T { return &v }
type event struct {
Email *string `json:"email"`
Level *uint32 `json:"level"`
Protocol *string `json:"protocol"`
Network *string `json:"network"`
Source *string `json:"source"`
Destination *string `json:"destination"`
OriginalTarget *string `json:"originalTarget"`
RouteTarget *string `json:"routeTarget"`
InboundTag *string `json:"inboundTag"`
InboundName *string `json:"inboundName"`
InboundLocal *string `json:"inboundLocal"`
OutboundTag *string `json:"outboundTag"`
Timestamp int64 `json:"ts"`
}
type WebhookNotifier struct {
url string
headers map[string]string
deduplication uint32
client *http.Client
seen sync.Map
lastSweep atomic.Int64
done chan struct{}
wg sync.WaitGroup
closeOnce sync.Once
}
func NewWebhookNotifier(cfg *WebhookConfig) (*WebhookNotifier, error) {
if cfg == nil || cfg.Url == "" {
return nil, nil
}
httpURL, socketPath := utils.SplitHTTPUnixURL(cfg.Url)
h := &WebhookNotifier{
url: httpURL,
deduplication: cfg.Deduplication,
client: &http.Client{
Timeout: 5 * time.Second,
},
done: make(chan struct{}),
}
if socketPath != "" {
dialAddr := utils.ResolveSocketPath(socketPath)
h.client.Transport = &http.Transport{
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, "unix", dialAddr)
},
}
}
if len(cfg.Headers) > 0 {
h.headers = make(map[string]string, len(cfg.Headers))
for k, v := range cfg.Headers {
h.headers[k] = v
}
}
return h, nil
}
func (h *WebhookNotifier) Fire(ctx routing.Context, outboundTag string) {
ev := buildEvent(ctx, outboundTag)
email := ""
if ev.Email != nil {
email = *ev.Email
}
if h.isDuplicate(email) {
return
}
h.wg.Add(1)
select {
case <-h.done:
h.wg.Done()
return
default:
}
go func() {
defer h.wg.Done()
h.post(ev)
}()
}
func buildEvent(ctx routing.Context, outboundTag string) *event {
ev := &event{
Timestamp: time.Now().Unix(),
OutboundTag: ptr(outboundTag),
InboundTag: ptr(ctx.GetInboundTag()),
Protocol: ptr(ctx.GetProtocol()),
Network: ptr(ctx.GetNetwork().SystemString()),
}
if user := ctx.GetUser(); user != "" {
ev.Email = ptr(user)
}
if srcIPs := ctx.GetSourceIPs(); len(srcIPs) > 0 {
srcPort := ctx.GetSourcePort()
ev.Source = ptr(net.JoinHostPort(srcIPs[0].String(), srcPort.String()))
}
targetPort := ctx.GetTargetPort()
if domain := ctx.GetTargetDomain(); domain != "" {
ev.Destination = ptr(net.JoinHostPort(domain, targetPort.String()))
} else if targetIPs := ctx.GetTargetIPs(); len(targetIPs) > 0 {
ev.Destination = ptr(net.JoinHostPort(targetIPs[0].String(), targetPort.String()))
}
if localIPs := ctx.GetLocalIPs(); len(localIPs) > 0 {
localPort := ctx.GetLocalPort()
ev.InboundLocal = ptr(net.JoinHostPort(localIPs[0].String(), localPort.String()))
}
if sctx, ok := ctx.(*routing_session.Context); ok {
enrichFromSession(ev, sctx)
}
return ev
}
func enrichFromSession(ev *event, sctx *routing_session.Context) {
if sctx.Inbound != nil {
ev.InboundName = ptr(sctx.Inbound.Name)
if sctx.Inbound.User != nil {
ev.Level = ptr(sctx.Inbound.User.Level)
}
}
if sctx.Outbound != nil {
if sctx.Outbound.OriginalTarget.Address != nil {
ev.OriginalTarget = ptr(sctx.Outbound.OriginalTarget.String())
}
if sctx.Outbound.RouteTarget.Address != nil {
ev.RouteTarget = ptr(sctx.Outbound.RouteTarget.String())
}
}
}
func (h *WebhookNotifier) post(ev *event) {
body, err := json.Marshal(ev)
if err != nil {
errors.LogWarning(context.Background(), "webhook: marshal failed: ", err)
return
}
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, h.url, bytes.NewReader(body))
if err != nil {
errors.LogWarning(context.Background(), "webhook: request build failed: ", err)
return
}
req.Header.Set("Content-Type", "application/json")
for k, v := range h.headers {
req.Header.Set(k, v)
}
resp, err := h.client.Do(req)
if err != nil {
errors.LogInfo(context.Background(), "webhook: POST failed: ", err)
return
}
defer func() {
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
if resp.StatusCode >= 400 {
errors.LogWarning(context.Background(), "webhook: POST returned status ", resp.StatusCode)
}
}
func (h *WebhookNotifier) isDuplicate(email string) bool {
if h.deduplication == 0 || email == "" {
return false
}
ttl := time.Duration(h.deduplication) * time.Second
now := time.Now()
h.maybeSweep(now, ttl)
if v, loaded := h.seen.LoadOrStore(email, now); loaded {
if now.Sub(v.(time.Time)) < ttl {
return true
}
h.seen.Store(email, now)
}
return false
}
func (h *WebhookNotifier) maybeSweep(now time.Time, ttl time.Duration) {
last := h.lastSweep.Load()
if now.UnixNano()-last < int64(ttl) {
return
}
if !h.lastSweep.CompareAndSwap(last, now.UnixNano()) {
return // another goroutine did the sweep
}
h.seen.Range(func(key, value any) bool {
if now.Sub(value.(time.Time)) >= ttl {
h.seen.Delete(key)
}
return true
})
}
// Only need to call if the Notifier is really used, otherwise GC can clean it
func (h *WebhookNotifier) Close() error {
h.closeOnce.Do(func() {
close(h.done)
})
h.wg.Wait()
h.client.CloseIdleConnections()
return nil
}
+88 -17
View File
@@ -3,12 +3,10 @@ package command
import (
"context"
"runtime"
"strings"
"time"
"github.com/xtls/xray-core/app/stats"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/strmatcher"
"github.com/xtls/xray-core/core"
feature_stats "github.com/xtls/xray-core/features/stats"
grpc "google.golang.org/grpc"
@@ -70,9 +68,10 @@ func (s *statsServer) GetStatsOnlineIpList(ctx context.Context, request *GetStat
}
ips := make(map[string]int64)
for ip, t := range c.IpTimeMap() {
ips[ip] = t.Unix()
}
c.ForEach(func(ip string, lastSeen int64) bool {
ips[ip] = lastSeen
return true
})
return &GetStatsOnlineIpListResponse{
Name: request.Name,
@@ -80,21 +79,93 @@ func (s *statsServer) GetStatsOnlineIpList(ctx context.Context, request *GetStat
}, nil
}
func (s *statsServer) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) {
matcher, err := strmatcher.Substr.New(request.Pattern)
if err != nil {
return nil, err
func (s *statsServer) GetAllOnlineUsers(ctx context.Context, request *GetAllOnlineUsersRequest) (*GetAllOnlineUsersResponse, error) {
return &GetAllOnlineUsersResponse{
Users: s.stats.GetAllOnlineUsers(),
}, nil
}
func (s *statsServer) GetUsersStats(ctx context.Context, request *GetUsersStatsRequest) (*GetUsersStatsResponse, error) {
userMap := make(map[string]*UserStat)
s.stats.VisitOnlineMaps(func(name string, om feature_stats.OnlineMap) bool {
if om.Count() == 0 {
return true
}
_, rest, _ := strings.Cut(name, ">>>")
email, _, _ := strings.Cut(rest, ">>>")
user := &UserStat{Email: email}
om.ForEach(func(ip string, lastSeen int64) bool {
user.Ips = append(user.Ips, &OnlineIPEntry{
Ip: ip,
LastSeen: lastSeen,
})
return true
})
if len(user.Ips) > 0 {
userMap[email] = user
}
return true
})
if request.IncludeTraffic {
for _, u := range userMap {
u.Traffic = &TrafficUserStat{}
}
const (
prefixUser = "user>>>"
suffixUplink = ">>>traffic>>>uplink"
suffixDownlink = ">>>traffic>>>downlink"
)
s.stats.VisitCounters(func(name string, c feature_stats.Counter) bool {
var email string
var isUplink bool
if strings.HasSuffix(name, suffixUplink) {
email = name[len(prefixUser) : len(name)-len(suffixUplink)]
isUplink = true
} else if strings.HasSuffix(name, suffixDownlink) {
email = name[len(prefixUser) : len(name)-len(suffixDownlink)]
} else {
return true
}
u, ok := userMap[email]
if !ok {
return true
}
var value int64
if request.Reset_ {
value = c.Set(0)
} else {
value = c.Value()
}
if isUplink {
u.Traffic.Uplink = value
} else {
u.Traffic.Downlink = value
}
return true
})
}
resp := &GetUsersStatsResponse{}
for _, u := range userMap {
resp.Users = append(resp.Users, u)
}
return resp, nil
}
func (s *statsServer) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) {
response := &QueryStatsResponse{}
manager, ok := s.stats.(*stats.Manager)
if !ok {
return nil, errors.New("QueryStats only works its own stats.Manager.")
}
manager.VisitCounters(func(name string, c feature_stats.Counter) bool {
if matcher.Match(name) {
s.stats.VisitCounters(func(name string, c feature_stats.Counter) bool {
if strings.Contains(name, request.Pattern) {
var value int64
if request.Reset_ {
value = c.Set(0)
+490 -176
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc v5.28.2
// protoc-gen-go v1.36.11
// protoc v6.33.5
// source: app/stats/command/command.proto
package command
@@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -21,14 +22,13 @@ const (
)
type GetStatsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState `protogen:"open.v1"`
// Name of the stat counter.
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// Whether or not to reset the counter to fetching its value.
Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"`
Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetStatsRequest) Reset() {
@@ -76,12 +76,11 @@ func (x *GetStatsRequest) GetReset_() bool {
}
type Stat struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Value int64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"`
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Value int64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *Stat) Reset() {
@@ -129,11 +128,10 @@ func (x *Stat) GetValue() int64 {
}
type GetStatsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Stat *Stat `protobuf:"bytes,1,opt,name=stat,proto3" json:"stat,omitempty"`
unknownFields protoimpl.UnknownFields
Stat *Stat `protobuf:"bytes,1,opt,name=stat,proto3" json:"stat,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *GetStatsResponse) Reset() {
@@ -174,12 +172,11 @@ func (x *GetStatsResponse) GetStat() *Stat {
}
type QueryStatsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"`
Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"`
unknownFields protoimpl.UnknownFields
Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"`
Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *QueryStatsRequest) Reset() {
@@ -227,11 +224,10 @@ func (x *QueryStatsRequest) GetReset_() bool {
}
type QueryStatsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Stat []*Stat `protobuf:"bytes,1,rep,name=stat,proto3" json:"stat,omitempty"`
unknownFields protoimpl.UnknownFields
Stat []*Stat `protobuf:"bytes,1,rep,name=stat,proto3" json:"stat,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *QueryStatsResponse) Reset() {
@@ -272,9 +268,9 @@ func (x *QueryStatsResponse) GetStat() []*Stat {
}
type SysStatsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SysStatsRequest) Reset() {
@@ -308,20 +304,19 @@ func (*SysStatsRequest) Descriptor() ([]byte, []int) {
}
type SysStatsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
NumGoroutine uint32 `protobuf:"varint,1,opt,name=NumGoroutine,proto3" json:"NumGoroutine,omitempty"`
NumGC uint32 `protobuf:"varint,2,opt,name=NumGC,proto3" json:"NumGC,omitempty"`
Alloc uint64 `protobuf:"varint,3,opt,name=Alloc,proto3" json:"Alloc,omitempty"`
TotalAlloc uint64 `protobuf:"varint,4,opt,name=TotalAlloc,proto3" json:"TotalAlloc,omitempty"`
Sys uint64 `protobuf:"varint,5,opt,name=Sys,proto3" json:"Sys,omitempty"`
Mallocs uint64 `protobuf:"varint,6,opt,name=Mallocs,proto3" json:"Mallocs,omitempty"`
Frees uint64 `protobuf:"varint,7,opt,name=Frees,proto3" json:"Frees,omitempty"`
LiveObjects uint64 `protobuf:"varint,8,opt,name=LiveObjects,proto3" json:"LiveObjects,omitempty"`
PauseTotalNs uint64 `protobuf:"varint,9,opt,name=PauseTotalNs,proto3" json:"PauseTotalNs,omitempty"`
Uptime uint32 `protobuf:"varint,10,opt,name=Uptime,proto3" json:"Uptime,omitempty"`
unknownFields protoimpl.UnknownFields
NumGoroutine uint32 `protobuf:"varint,1,opt,name=NumGoroutine,proto3" json:"NumGoroutine,omitempty"`
NumGC uint32 `protobuf:"varint,2,opt,name=NumGC,proto3" json:"NumGC,omitempty"`
Alloc uint64 `protobuf:"varint,3,opt,name=Alloc,proto3" json:"Alloc,omitempty"`
TotalAlloc uint64 `protobuf:"varint,4,opt,name=TotalAlloc,proto3" json:"TotalAlloc,omitempty"`
Sys uint64 `protobuf:"varint,5,opt,name=Sys,proto3" json:"Sys,omitempty"`
Mallocs uint64 `protobuf:"varint,6,opt,name=Mallocs,proto3" json:"Mallocs,omitempty"`
Frees uint64 `protobuf:"varint,7,opt,name=Frees,proto3" json:"Frees,omitempty"`
LiveObjects uint64 `protobuf:"varint,8,opt,name=LiveObjects,proto3" json:"LiveObjects,omitempty"`
PauseTotalNs uint64 `protobuf:"varint,9,opt,name=PauseTotalNs,proto3" json:"PauseTotalNs,omitempty"`
Uptime uint32 `protobuf:"varint,10,opt,name=Uptime,proto3" json:"Uptime,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *SysStatsResponse) Reset() {
@@ -425,12 +420,11 @@ func (x *SysStatsResponse) GetUptime() uint32 {
}
type GetStatsOnlineIpListResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
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" protobuf_val:"varint,2,opt,name=value"`
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"`
sizeCache protoimpl.SizeCache
}
func (x *GetStatsOnlineIpListResponse) Reset() {
@@ -477,15 +471,355 @@ func (x *GetStatsOnlineIpListResponse) GetIps() map[string]int64 {
return nil
}
type Config struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
type GetAllOnlineUsersRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetAllOnlineUsersRequest) Reset() {
*x = GetAllOnlineUsersRequest{}
mi := &file_app_stats_command_command_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetAllOnlineUsersRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetAllOnlineUsersRequest) ProtoMessage() {}
func (x *GetAllOnlineUsersRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_stats_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 GetAllOnlineUsersRequest.ProtoReflect.Descriptor instead.
func (*GetAllOnlineUsersRequest) Descriptor() ([]byte, []int) {
return file_app_stats_command_command_proto_rawDescGZIP(), []int{8}
}
type GetAllOnlineUsersResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Users []string `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetAllOnlineUsersResponse) Reset() {
*x = GetAllOnlineUsersResponse{}
mi := &file_app_stats_command_command_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetAllOnlineUsersResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetAllOnlineUsersResponse) ProtoMessage() {}
func (x *GetAllOnlineUsersResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_stats_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 GetAllOnlineUsersResponse.ProtoReflect.Descriptor instead.
func (*GetAllOnlineUsersResponse) Descriptor() ([]byte, []int) {
return file_app_stats_command_command_proto_rawDescGZIP(), []int{9}
}
func (x *GetAllOnlineUsersResponse) GetUsers() []string {
if x != nil {
return x.Users
}
return nil
}
type OnlineIPEntry struct {
state protoimpl.MessageState `protogen:"open.v1"`
Ip string `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"`
LastSeen int64 `protobuf:"varint,2,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *OnlineIPEntry) Reset() {
*x = OnlineIPEntry{}
mi := &file_app_stats_command_command_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *OnlineIPEntry) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*OnlineIPEntry) ProtoMessage() {}
func (x *OnlineIPEntry) ProtoReflect() protoreflect.Message {
mi := &file_app_stats_command_command_proto_msgTypes[10]
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 OnlineIPEntry.ProtoReflect.Descriptor instead.
func (*OnlineIPEntry) Descriptor() ([]byte, []int) {
return file_app_stats_command_command_proto_rawDescGZIP(), []int{10}
}
func (x *OnlineIPEntry) GetIp() string {
if x != nil {
return x.Ip
}
return ""
}
func (x *OnlineIPEntry) GetLastSeen() int64 {
if x != nil {
return x.LastSeen
}
return 0
}
type TrafficUserStat struct {
state protoimpl.MessageState `protogen:"open.v1"`
Uplink int64 `protobuf:"varint,1,opt,name=uplink,proto3" json:"uplink,omitempty"`
Downlink int64 `protobuf:"varint,2,opt,name=downlink,proto3" json:"downlink,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *TrafficUserStat) Reset() {
*x = TrafficUserStat{}
mi := &file_app_stats_command_command_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *TrafficUserStat) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TrafficUserStat) ProtoMessage() {}
func (x *TrafficUserStat) ProtoReflect() protoreflect.Message {
mi := &file_app_stats_command_command_proto_msgTypes[11]
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 TrafficUserStat.ProtoReflect.Descriptor instead.
func (*TrafficUserStat) Descriptor() ([]byte, []int) {
return file_app_stats_command_command_proto_rawDescGZIP(), []int{11}
}
func (x *TrafficUserStat) GetUplink() int64 {
if x != nil {
return x.Uplink
}
return 0
}
func (x *TrafficUserStat) GetDownlink() int64 {
if x != nil {
return x.Downlink
}
return 0
}
type UserStat struct {
state protoimpl.MessageState `protogen:"open.v1"`
Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
Ips []*OnlineIPEntry `protobuf:"bytes,2,rep,name=ips,proto3" json:"ips,omitempty"`
Traffic *TrafficUserStat `protobuf:"bytes,3,opt,name=traffic,proto3" json:"traffic,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *UserStat) Reset() {
*x = UserStat{}
mi := &file_app_stats_command_command_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *UserStat) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UserStat) ProtoMessage() {}
func (x *UserStat) ProtoReflect() protoreflect.Message {
mi := &file_app_stats_command_command_proto_msgTypes[12]
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 UserStat.ProtoReflect.Descriptor instead.
func (*UserStat) Descriptor() ([]byte, []int) {
return file_app_stats_command_command_proto_rawDescGZIP(), []int{12}
}
func (x *UserStat) GetEmail() string {
if x != nil {
return x.Email
}
return ""
}
func (x *UserStat) GetIps() []*OnlineIPEntry {
if x != nil {
return x.Ips
}
return nil
}
func (x *UserStat) GetTraffic() *TrafficUserStat {
if x != nil {
return x.Traffic
}
return nil
}
type GetUsersStatsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
IncludeTraffic bool `protobuf:"varint,1,opt,name=include_traffic,json=includeTraffic,proto3" json:"include_traffic,omitempty"`
Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetUsersStatsRequest) Reset() {
*x = GetUsersStatsRequest{}
mi := &file_app_stats_command_command_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetUsersStatsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetUsersStatsRequest) ProtoMessage() {}
func (x *GetUsersStatsRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_stats_command_command_proto_msgTypes[13]
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 GetUsersStatsRequest.ProtoReflect.Descriptor instead.
func (*GetUsersStatsRequest) Descriptor() ([]byte, []int) {
return file_app_stats_command_command_proto_rawDescGZIP(), []int{13}
}
func (x *GetUsersStatsRequest) GetIncludeTraffic() bool {
if x != nil {
return x.IncludeTraffic
}
return false
}
func (x *GetUsersStatsRequest) GetReset_() bool {
if x != nil {
return x.Reset_
}
return false
}
type GetUsersStatsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Users []*UserStat `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetUsersStatsResponse) Reset() {
*x = GetUsersStatsResponse{}
mi := &file_app_stats_command_command_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetUsersStatsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetUsersStatsResponse) ProtoMessage() {}
func (x *GetUsersStatsResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_stats_command_command_proto_msgTypes[14]
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 GetUsersStatsResponse.ProtoReflect.Descriptor instead.
func (*GetUsersStatsResponse) Descriptor() ([]byte, []int) {
return file_app_stats_command_command_proto_rawDescGZIP(), []int{14}
}
func (x *GetUsersStatsResponse) GetUsers() []*UserStat {
if x != nil {
return x.Users
}
return nil
}
type Config struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
*x = Config{}
mi := &file_app_stats_command_command_proto_msgTypes[8]
mi := &file_app_stats_command_command_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -497,7 +831,7 @@ func (x *Config) String() string {
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_stats_command_command_proto_msgTypes[8]
mi := &file_app_stats_command_command_proto_msgTypes[15]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -510,124 +844,91 @@ 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{8}
return file_app_stats_command_command_proto_rawDescGZIP(), []int{15}
}
var File_app_stats_command_command_proto protoreflect.FileDescriptor
var file_app_stats_command_command_proto_rawDesc = []byte{
0x0a, 0x1f, 0x61, 0x70, 0x70, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2f, 0x63, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x16, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74,
0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x22, 0x3b, 0x0a, 0x0f, 0x47, 0x65, 0x74,
0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
0x05, 0x72, 0x65, 0x73, 0x65, 0x74, 0x22, 0x30, 0x0a, 0x04, 0x53, 0x74, 0x61, 0x74, 0x12, 0x12,
0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x44, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53,
0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x04,
0x73, 0x74, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 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, 0x74, 0x61, 0x74, 0x52, 0x04, 0x73, 0x74, 0x61, 0x74, 0x22, 0x43,
0x0a, 0x11, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x14, 0x0a,
0x05, 0x72, 0x65, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65,
0x73, 0x65, 0x74, 0x22, 0x46, 0x0a, 0x12, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x04, 0x73, 0x74, 0x61,
0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 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, 0x74, 0x61, 0x74, 0x52, 0x04, 0x73, 0x74, 0x61, 0x74, 0x22, 0x11, 0x0a, 0x0f, 0x53,
0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa2,
0x02, 0x0a, 0x10, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x4e, 0x75, 0x6d, 0x47, 0x6f, 0x72, 0x6f, 0x75, 0x74,
0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x4e, 0x75, 0x6d, 0x47, 0x6f,
0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x75, 0x6d, 0x47, 0x43,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x4e, 0x75, 0x6d, 0x47, 0x43, 0x12, 0x14, 0x0a,
0x05, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x41, 0x6c,
0x6c, 0x6f, 0x63, 0x12, 0x1e, 0x0a, 0x0a, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f,
0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c,
0x6c, 0x6f, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x53, 0x79, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04,
0x52, 0x03, 0x53, 0x79, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x73,
0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x4d, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x73, 0x12,
0x14, 0x0a, 0x05, 0x46, 0x72, 0x65, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05,
0x46, 0x72, 0x65, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x4c, 0x69, 0x76, 0x65, 0x4f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x4c, 0x69, 0x76, 0x65,
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x50, 0x61, 0x75, 0x73, 0x65,
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, 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, 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, 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,
}
const file_app_stats_command_command_proto_rawDesc = "" +
"\n" +
"\x1fapp/stats/command/command.proto\x12\x16xray.app.stats.command\";\n" +
"\x0fGetStatsRequest\x12\x12\n" +
"\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n" +
"\x05reset\x18\x02 \x01(\bR\x05reset\"0\n" +
"\x04Stat\x12\x12\n" +
"\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n" +
"\x05value\x18\x02 \x01(\x03R\x05value\"D\n" +
"\x10GetStatsResponse\x120\n" +
"\x04stat\x18\x01 \x01(\v2\x1c.xray.app.stats.command.StatR\x04stat\"C\n" +
"\x11QueryStatsRequest\x12\x18\n" +
"\apattern\x18\x01 \x01(\tR\apattern\x12\x14\n" +
"\x05reset\x18\x02 \x01(\bR\x05reset\"F\n" +
"\x12QueryStatsResponse\x120\n" +
"\x04stat\x18\x01 \x03(\v2\x1c.xray.app.stats.command.StatR\x04stat\"\x11\n" +
"\x0fSysStatsRequest\"\xa2\x02\n" +
"\x10SysStatsResponse\x12\"\n" +
"\fNumGoroutine\x18\x01 \x01(\rR\fNumGoroutine\x12\x14\n" +
"\x05NumGC\x18\x02 \x01(\rR\x05NumGC\x12\x14\n" +
"\x05Alloc\x18\x03 \x01(\x04R\x05Alloc\x12\x1e\n" +
"\n" +
"TotalAlloc\x18\x04 \x01(\x04R\n" +
"TotalAlloc\x12\x10\n" +
"\x03Sys\x18\x05 \x01(\x04R\x03Sys\x12\x18\n" +
"\aMallocs\x18\x06 \x01(\x04R\aMallocs\x12\x14\n" +
"\x05Frees\x18\a \x01(\x04R\x05Frees\x12 \n" +
"\vLiveObjects\x18\b \x01(\x04R\vLiveObjects\x12\"\n" +
"\fPauseTotalNs\x18\t \x01(\x04R\fPauseTotalNs\x12\x16\n" +
"\x06Uptime\x18\n" +
" \x01(\rR\x06Uptime\"\xbb\x01\n" +
"\x1cGetStatsOnlineIpListResponse\x12\x12\n" +
"\x04name\x18\x01 \x01(\tR\x04name\x12O\n" +
"\x03ips\x18\x02 \x03(\v2=.xray.app.stats.command.GetStatsOnlineIpListResponse.IpsEntryR\x03ips\x1a6\n" +
"\bIpsEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\x03R\x05value:\x028\x01\"\x1a\n" +
"\x18GetAllOnlineUsersRequest\"1\n" +
"\x19GetAllOnlineUsersResponse\x12\x14\n" +
"\x05users\x18\x01 \x03(\tR\x05users\"<\n" +
"\rOnlineIPEntry\x12\x0e\n" +
"\x02ip\x18\x01 \x01(\tR\x02ip\x12\x1b\n" +
"\tlast_seen\x18\x02 \x01(\x03R\blastSeen\"E\n" +
"\x0fTrafficUserStat\x12\x16\n" +
"\x06uplink\x18\x01 \x01(\x03R\x06uplink\x12\x1a\n" +
"\bdownlink\x18\x02 \x01(\x03R\bdownlink\"\x9c\x01\n" +
"\bUserStat\x12\x14\n" +
"\x05email\x18\x01 \x01(\tR\x05email\x127\n" +
"\x03ips\x18\x02 \x03(\v2%.xray.app.stats.command.OnlineIPEntryR\x03ips\x12A\n" +
"\atraffic\x18\x03 \x01(\v2'.xray.app.stats.command.TrafficUserStatR\atraffic\"U\n" +
"\x14GetUsersStatsRequest\x12'\n" +
"\x0finclude_traffic\x18\x01 \x01(\bR\x0eincludeTraffic\x12\x14\n" +
"\x05reset\x18\x02 \x01(\bR\x05reset\"O\n" +
"\x15GetUsersStatsResponse\x126\n" +
"\x05users\x18\x01 \x03(\v2 .xray.app.stats.command.UserStatR\x05users\"\b\n" +
"\x06Config2\x86\x06\n" +
"\fStatsService\x12_\n" +
"\bGetStats\x12'.xray.app.stats.command.GetStatsRequest\x1a(.xray.app.stats.command.GetStatsResponse\"\x00\x12e\n" +
"\x0eGetStatsOnline\x12'.xray.app.stats.command.GetStatsRequest\x1a(.xray.app.stats.command.GetStatsResponse\"\x00\x12e\n" +
"\n" +
"QueryStats\x12).xray.app.stats.command.QueryStatsRequest\x1a*.xray.app.stats.command.QueryStatsResponse\"\x00\x12b\n" +
"\vGetSysStats\x12'.xray.app.stats.command.SysStatsRequest\x1a(.xray.app.stats.command.SysStatsResponse\"\x00\x12w\n" +
"\x14GetStatsOnlineIpList\x12'.xray.app.stats.command.GetStatsRequest\x1a4.xray.app.stats.command.GetStatsOnlineIpListResponse\"\x00\x12z\n" +
"\x11GetAllOnlineUsers\x120.xray.app.stats.command.GetAllOnlineUsersRequest\x1a1.xray.app.stats.command.GetAllOnlineUsersResponse\"\x00\x12n\n" +
"\rGetUsersStats\x12,.xray.app.stats.command.GetUsersStatsRequest\x1a-.xray.app.stats.command.GetUsersStatsResponse\"\x00Bd\n" +
"\x1acom.xray.app.stats.commandP\x01Z+github.com/xtls/xray-core/app/stats/command\xaa\x02\x16Xray.App.Stats.Commandb\x06proto3"
var (
file_app_stats_command_command_proto_rawDescOnce sync.Once
file_app_stats_command_command_proto_rawDescData = file_app_stats_command_command_proto_rawDesc
file_app_stats_command_command_proto_rawDescData []byte
)
func file_app_stats_command_command_proto_rawDescGZIP() []byte {
file_app_stats_command_command_proto_rawDescOnce.Do(func() {
file_app_stats_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_stats_command_command_proto_rawDescData)
file_app_stats_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_stats_command_command_proto_rawDesc), len(file_app_stats_command_command_proto_rawDesc)))
})
return file_app_stats_command_command_proto_rawDescData
}
var file_app_stats_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
var file_app_stats_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 17)
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
@@ -637,28 +938,42 @@ var file_app_stats_command_command_proto_goTypes = []any{
(*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
(*GetAllOnlineUsersRequest)(nil), // 8: xray.app.stats.command.GetAllOnlineUsersRequest
(*GetAllOnlineUsersResponse)(nil), // 9: xray.app.stats.command.GetAllOnlineUsersResponse
(*OnlineIPEntry)(nil), // 10: xray.app.stats.command.OnlineIPEntry
(*TrafficUserStat)(nil), // 11: xray.app.stats.command.TrafficUserStat
(*UserStat)(nil), // 12: xray.app.stats.command.UserStat
(*GetUsersStatsRequest)(nil), // 13: xray.app.stats.command.GetUsersStatsRequest
(*GetUsersStatsResponse)(nil), // 14: xray.app.stats.command.GetUsersStatsResponse
(*Config)(nil), // 15: xray.app.stats.command.Config
nil, // 16: 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
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
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
16, // 2: xray.app.stats.command.GetStatsOnlineIpListResponse.ips:type_name -> xray.app.stats.command.GetStatsOnlineIpListResponse.IpsEntry
10, // 3: xray.app.stats.command.UserStat.ips:type_name -> xray.app.stats.command.OnlineIPEntry
11, // 4: xray.app.stats.command.UserStat.traffic:type_name -> xray.app.stats.command.TrafficUserStat
12, // 5: xray.app.stats.command.GetUsersStatsResponse.users:type_name -> xray.app.stats.command.UserStat
0, // 6: xray.app.stats.command.StatsService.GetStats:input_type -> xray.app.stats.command.GetStatsRequest
0, // 7: xray.app.stats.command.StatsService.GetStatsOnline:input_type -> xray.app.stats.command.GetStatsRequest
3, // 8: xray.app.stats.command.StatsService.QueryStats:input_type -> xray.app.stats.command.QueryStatsRequest
5, // 9: xray.app.stats.command.StatsService.GetSysStats:input_type -> xray.app.stats.command.SysStatsRequest
0, // 10: xray.app.stats.command.StatsService.GetStatsOnlineIpList:input_type -> xray.app.stats.command.GetStatsRequest
8, // 11: xray.app.stats.command.StatsService.GetAllOnlineUsers:input_type -> xray.app.stats.command.GetAllOnlineUsersRequest
13, // 12: xray.app.stats.command.StatsService.GetUsersStats:input_type -> xray.app.stats.command.GetUsersStatsRequest
2, // 13: xray.app.stats.command.StatsService.GetStats:output_type -> xray.app.stats.command.GetStatsResponse
2, // 14: xray.app.stats.command.StatsService.GetStatsOnline:output_type -> xray.app.stats.command.GetStatsResponse
4, // 15: xray.app.stats.command.StatsService.QueryStats:output_type -> xray.app.stats.command.QueryStatsResponse
6, // 16: xray.app.stats.command.StatsService.GetSysStats:output_type -> xray.app.stats.command.SysStatsResponse
7, // 17: xray.app.stats.command.StatsService.GetStatsOnlineIpList:output_type -> xray.app.stats.command.GetStatsOnlineIpListResponse
9, // 18: xray.app.stats.command.StatsService.GetAllOnlineUsers:output_type -> xray.app.stats.command.GetAllOnlineUsersResponse
14, // 19: xray.app.stats.command.StatsService.GetUsersStats:output_type -> xray.app.stats.command.GetUsersStatsResponse
13, // [13:20] is the sub-list for method output_type
6, // [6:13] 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
}
func init() { file_app_stats_command_command_proto_init() }
@@ -670,9 +985,9 @@ func file_app_stats_command_command_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_stats_command_command_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_stats_command_command_proto_rawDesc), len(file_app_stats_command_command_proto_rawDesc)),
NumEnums: 0,
NumMessages: 10,
NumMessages: 17,
NumExtensions: 0,
NumServices: 1,
},
@@ -681,7 +996,6 @@ func file_app_stats_command_command_proto_init() {
MessageInfos: file_app_stats_command_command_proto_msgTypes,
}.Build()
File_app_stats_command_command_proto = out.File
file_app_stats_command_command_proto_rawDesc = nil
file_app_stats_command_command_proto_goTypes = nil
file_app_stats_command_command_proto_depIdxs = nil
}

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