Compare commits

..

1 Commits

Author SHA1 Message Date
风扇滑翔翼
20825f6f1a Change to TypedSyncMap 2025-07-26 12:05:21 +00:00
200 changed files with 4501 additions and 5740 deletions

View File

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

View File

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

View File

@@ -65,7 +65,7 @@ jobs:
echo "LATEST=$LATEST" >>${GITHUB_ENV}
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3

View File

@@ -63,7 +63,7 @@ jobs:
CGO_ENABLED: 0
steps:
- name: Checkout codebase
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Show workflow information
run: |
@@ -72,7 +72,7 @@ jobs:
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
- name: Set up Go
uses: actions/setup-go@v6
uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
@@ -94,11 +94,11 @@ jobs:
mkdir -p build_assets
COMMID=$(git describe --always --dirty)
echo 'Building Xray for Windows 7...'
go build -o build_assets/xray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
go build -o build_assets/xray.exe -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
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
# go build -o build_assets/wxray.exe -trimpath -buildvcs=false -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
- name: Restore Geodat Cache
uses: actions/cache/restore@v4

View File

@@ -37,7 +37,7 @@ jobs:
- name: Trigger Asset Update Workflow if Assets Missing
if: steps.check-assets.outputs.missing == 'true'
uses: actions/github-script@v8
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@@ -153,7 +153,7 @@ jobs:
CGO_ENABLED: 0
steps:
- name: Checkout codebase
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Set up NDK
if: matrix.goos == 'android'
@@ -176,7 +176,7 @@ jobs:
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
- name: Set up Go
uses: actions/setup-go@v6
uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true
@@ -190,19 +190,17 @@ jobs:
COMMID=$(git describe --always --dirty)
if [[ ${GOOS} == 'windows' ]]; then
echo 'Building Xray for Windows...'
go build -o build_assets/xray.exe -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
go build -o build_assets/xray.exe -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
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
# go build -o build_assets/wxray.exe -trimpath -buildvcs=false -ldflags="-H windowsgui -X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
else
echo 'Building Xray...'
go build -o build_assets/xray -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
if [[ ${GOARCH} == 'mips' || ${GOARCH} == 'mipsle' ]]; then
go build -o build_assets/xray -trimpath -buildvcs=false -gcflags="-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
echo 'Building soft-float Xray for MIPS/MIPSLE 32-bit...'
GOMIPS=softfloat go build -o build_assets/xray_softfloat -trimpath -buildvcs=false -gcflags="-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
else
go build -o build_assets/xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
GOMIPS=softfloat go build -o build_assets/xray_softfloat -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=${COMMID} -s -w -buildid=" -v ./main
fi
fi

View File

@@ -45,9 +45,9 @@ jobs:
os: [windows-latest, ubuntu-latest, macos-latest]
steps:
- name: Checkout codebase
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v6
uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: true

42
.gitignore vendored
View File

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

View File

@@ -1,20 +1,19 @@
# Project X
[![Project X NFT](https://raw2.seadn.io/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/7fa9ce900fb39b44226348db330e32/8b7fa9ce900fb39b44226348db330e32.svg)](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1)
### [Collect a Project X NFT to support the development of Project X!](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1)
[Project X](https://github.com/XTLS) originates from XTLS protocol, providing a set of network tools such as [Xray-core](https://github.com/XTLS/Xray-core) and [REALITY](https://github.com/XTLS/REALITY).
[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).
## Donation & NFTs
### [Collect a Project X NFT to support the development of Project X!](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1)
[<img alt="Project X NFT" width="150px" src="https://raw2.seadn.io/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/7fa9ce900fb39b44226348db330e32/8b7fa9ce900fb39b44226348db330e32.svg" />](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1)
- **ETH/USDT/USDC: `0xDc3Fe44F0f25D13CACb1C4896CD0D321df3146Ee`**
- **Project X NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1**
- **VLESS NFT: https://opensea.io/collection/vless**
- **REALITY NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2**
- **Related links: [VLESS Post-Quantum Encryption](https://github.com/XTLS/Xray-core/pull/5067), [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113), [Announcement of NFTs by Project X](https://github.com/XTLS/Xray-core/discussions/3633)**
- **Related links: https://opensea.io/collection/xtls, [Announcement of NFTs by Project X](https://github.com/XTLS/Xray-core/discussions/3633), [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113)**
## License
@@ -44,8 +43,6 @@
- [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)
- [Remnawave](https://github.com/remnawave/panel)
- [Marzban](https://github.com/Gozargah/Marzban)
- [Xray-UI](https://github.com/qist/xray-ui)
@@ -169,13 +166,7 @@ CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -ldflags="-s -w -buildi
Make sure that you are using the same Go version, and remember to set the git commit id (7 bytes):
```bash
CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid=" -v ./main
```
If you are compiling a 32-bit MIPS/MIPSLE target, use this command instead:
```bash
CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="-l=4" -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid=" -v ./main
CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -ldflags="-X github.com/xtls/xray-core/core.build=REPLACE -s -w -buildid=" -v ./main
```
## Stargazers over time

View File

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

View File

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

View File

@@ -42,15 +42,12 @@ func (r *IPRecord) getIPs() ([]net.IP, uint32, error) {
if r == nil {
return nil, 0, errRecordNotFound
}
untilExpire := time.Until(r.Expire).Seconds()
untilExpire := time.Until(r.Expire)
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
}
ttl := uint32(untilExpire/time.Second) + uint32(1)
if r.RCode != dnsmessage.RCodeSuccess {
return nil, ttl, dns_feature.RCodeError(r.RCode)
}

View File

@@ -18,31 +18,31 @@ func Test_parseResponse(t *testing.T) {
ans := new(dns.Msg)
ans.Id = 0
p = append(p, common.Must2(ans.Pack()))
p = append(p, common.Must2(ans.Pack()).([]byte))
p = append(p, []byte{})
ans = new(dns.Msg)
ans.Id = 1
ans.Answer = append(ans.Answer,
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")),
common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")),
common.Must2(dns.NewRR("google.com. IN A 8.8.8.8")),
common.Must2(dns.NewRR("google.com. IN A 8.8.4.4")),
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")).(dns.RR),
common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")).(dns.RR),
common.Must2(dns.NewRR("google.com. IN A 8.8.8.8")).(dns.RR),
common.Must2(dns.NewRR("google.com. IN A 8.8.4.4")).(dns.RR),
)
p = append(p, common.Must2(ans.Pack()))
p = append(p, common.Must2(ans.Pack()).([]byte))
ans = new(dns.Msg)
ans.Id = 2
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")),
common.Must2(dns.NewRR("google.com. IN CNAME test.google.com")),
common.Must2(dns.NewRR("google.com. IN AAAA 2001:4860:4860::8888")),
common.Must2(dns.NewRR("google.com. IN AAAA 2001:4860:4860::8844")),
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")).(dns.RR),
common.Must2(dns.NewRR("google.com. IN CNAME fake.google.com")).(dns.RR),
common.Must2(dns.NewRR("google.com. IN CNAME m.test.google.com")).(dns.RR),
common.Must2(dns.NewRR("google.com. IN CNAME test.google.com")).(dns.RR),
common.Must2(dns.NewRR("google.com. IN AAAA 2001::123:8888")).(dns.RR),
common.Must2(dns.NewRR("google.com. IN AAAA 2001::123:8844")).(dns.RR),
)
p = append(p, common.Must2(ans.Pack()))
p = append(p, common.Must2(ans.Pack()).([]byte))
tests := []struct {
name string
@@ -72,7 +72,7 @@ func Test_parseResponse(t *testing.T) {
},
{
"aaaa record",
&IPRecord{2, []net.IP{net.ParseIP("2001:4860:4860::8888"), net.ParseIP("2001:4860:4860::8844")}, time.Time{}, dnsmessage.RCodeSuccess, nil},
&IPRecord{2, []net.IP{net.ParseIP("2001::123:8888"), net.ParseIP("2001::123:8844")}, time.Time{}, dnsmessage.RCodeSuccess, nil},
false,
},
}

View File

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

View File

@@ -1 +1,23 @@
package proxyman
func (s *AllocationStrategy) GetConcurrencyValue() uint32 {
if s == nil || s.Concurrency == nil {
return 3
}
return s.Concurrency.Value
}
func (s *AllocationStrategy) GetRefreshValue() uint32 {
if s == nil || s.Refresh == nil {
return 5
}
return s.Refresh.Value
}
func (c *ReceiverConfig) GetEffectiveSniffingSettings() *SniffingConfig {
if c.SniffingSettings != nil {
return c.SniffingSettings
}
return nil
}

View File

@@ -23,6 +23,58 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type AllocationStrategy_Type int32
const (
// Always allocate all connection handlers.
AllocationStrategy_Always AllocationStrategy_Type = 0
// Randomly allocate specific range of handlers.
AllocationStrategy_Random AllocationStrategy_Type = 1
// External. Not supported yet.
AllocationStrategy_External AllocationStrategy_Type = 2
)
// Enum value maps for AllocationStrategy_Type.
var (
AllocationStrategy_Type_name = map[int32]string{
0: "Always",
1: "Random",
2: "External",
}
AllocationStrategy_Type_value = map[string]int32{
"Always": 0,
"Random": 1,
"External": 2,
}
)
func (x AllocationStrategy_Type) Enum() *AllocationStrategy_Type {
p := new(AllocationStrategy_Type)
*p = x
return p
}
func (x AllocationStrategy_Type) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (AllocationStrategy_Type) Descriptor() protoreflect.EnumDescriptor {
return file_app_proxyman_config_proto_enumTypes[0].Descriptor()
}
func (AllocationStrategy_Type) Type() protoreflect.EnumType {
return &file_app_proxyman_config_proto_enumTypes[0]
}
func (x AllocationStrategy_Type) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use AllocationStrategy_Type.Descriptor instead.
func (AllocationStrategy_Type) EnumDescriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{1, 0}
}
type InboundConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -59,6 +111,71 @@ func (*InboundConfig) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{0}
}
type AllocationStrategy struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Type AllocationStrategy_Type `protobuf:"varint,1,opt,name=type,proto3,enum=xray.app.proxyman.AllocationStrategy_Type" json:"type,omitempty"`
// Number of handlers (ports) running in parallel.
// Default value is 3 if unset.
Concurrency *AllocationStrategy_AllocationStrategyConcurrency `protobuf:"bytes,2,opt,name=concurrency,proto3" json:"concurrency,omitempty"`
// Number of minutes before a handler is regenerated.
// Default value is 5 if unset.
Refresh *AllocationStrategy_AllocationStrategyRefresh `protobuf:"bytes,3,opt,name=refresh,proto3" json:"refresh,omitempty"`
}
func (x *AllocationStrategy) Reset() {
*x = AllocationStrategy{}
mi := &file_app_proxyman_config_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AllocationStrategy) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AllocationStrategy) ProtoMessage() {}
func (x *AllocationStrategy) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_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 AllocationStrategy.ProtoReflect.Descriptor instead.
func (*AllocationStrategy) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{1}
}
func (x *AllocationStrategy) GetType() AllocationStrategy_Type {
if x != nil {
return x.Type
}
return AllocationStrategy_Always
}
func (x *AllocationStrategy) GetConcurrency() *AllocationStrategy_AllocationStrategyConcurrency {
if x != nil {
return x.Concurrency
}
return nil
}
func (x *AllocationStrategy) GetRefresh() *AllocationStrategy_AllocationStrategyRefresh {
if x != nil {
return x.Refresh
}
return nil
}
type SniffingConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -79,7 +196,7 @@ type SniffingConfig struct {
func (x *SniffingConfig) Reset() {
*x = SniffingConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[1]
mi := &file_app_proxyman_config_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -91,7 +208,7 @@ func (x *SniffingConfig) String() string {
func (*SniffingConfig) ProtoMessage() {}
func (x *SniffingConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[1]
mi := &file_app_proxyman_config_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -104,7 +221,7 @@ func (x *SniffingConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use SniffingConfig.ProtoReflect.Descriptor instead.
func (*SniffingConfig) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{1}
return file_app_proxyman_config_proto_rawDescGZIP(), []int{2}
}
func (x *SniffingConfig) GetEnabled() bool {
@@ -151,14 +268,15 @@ type ReceiverConfig struct {
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.
Listen *net.IPOrDomain `protobuf:"bytes,2,opt,name=listen,proto3" json:"listen,omitempty"`
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"`
AllocationStrategy *AllocationStrategy `protobuf:"bytes,3,opt,name=allocation_strategy,json=allocationStrategy,proto3" json:"allocation_strategy,omitempty"`
StreamSettings *internet.StreamConfig `protobuf:"bytes,4,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"`
ReceiveOriginalDestination bool `protobuf:"varint,5,opt,name=receive_original_destination,json=receiveOriginalDestination,proto3" json:"receive_original_destination,omitempty"`
SniffingSettings *SniffingConfig `protobuf:"bytes,7,opt,name=sniffing_settings,json=sniffingSettings,proto3" json:"sniffing_settings,omitempty"`
}
func (x *ReceiverConfig) Reset() {
*x = ReceiverConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[2]
mi := &file_app_proxyman_config_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -170,7 +288,7 @@ func (x *ReceiverConfig) String() string {
func (*ReceiverConfig) ProtoMessage() {}
func (x *ReceiverConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[2]
mi := &file_app_proxyman_config_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -183,7 +301,7 @@ func (x *ReceiverConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use ReceiverConfig.ProtoReflect.Descriptor instead.
func (*ReceiverConfig) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{2}
return file_app_proxyman_config_proto_rawDescGZIP(), []int{3}
}
func (x *ReceiverConfig) GetPortList() *net.PortList {
@@ -200,6 +318,13 @@ func (x *ReceiverConfig) GetListen() *net.IPOrDomain {
return nil
}
func (x *ReceiverConfig) GetAllocationStrategy() *AllocationStrategy {
if x != nil {
return x.AllocationStrategy
}
return nil
}
func (x *ReceiverConfig) GetStreamSettings() *internet.StreamConfig {
if x != nil {
return x.StreamSettings
@@ -233,7 +358,7 @@ type InboundHandlerConfig struct {
func (x *InboundHandlerConfig) Reset() {
*x = InboundHandlerConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[3]
mi := &file_app_proxyman_config_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -245,7 +370,7 @@ func (x *InboundHandlerConfig) String() string {
func (*InboundHandlerConfig) ProtoMessage() {}
func (x *InboundHandlerConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[3]
mi := &file_app_proxyman_config_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -258,7 +383,7 @@ func (x *InboundHandlerConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use InboundHandlerConfig.ProtoReflect.Descriptor instead.
func (*InboundHandlerConfig) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{3}
return file_app_proxyman_config_proto_rawDescGZIP(), []int{4}
}
func (x *InboundHandlerConfig) GetTag() string {
@@ -290,7 +415,7 @@ type OutboundConfig struct {
func (x *OutboundConfig) Reset() {
*x = OutboundConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[4]
mi := &file_app_proxyman_config_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -302,7 +427,7 @@ func (x *OutboundConfig) String() string {
func (*OutboundConfig) ProtoMessage() {}
func (x *OutboundConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[4]
mi := &file_app_proxyman_config_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -315,7 +440,7 @@ func (x *OutboundConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use OutboundConfig.ProtoReflect.Descriptor instead.
func (*OutboundConfig) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{4}
return file_app_proxyman_config_proto_rawDescGZIP(), []int{5}
}
type SenderConfig struct {
@@ -324,17 +449,16 @@ type SenderConfig struct {
unknownFields protoimpl.UnknownFields
// Send traffic through the given IP. Only IP is allowed.
Via *net.IPOrDomain `protobuf:"bytes,1,opt,name=via,proto3" json:"via,omitempty"`
StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"`
ProxySettings *internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"`
MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"`
ViaCidr string `protobuf:"bytes,5,opt,name=via_cidr,json=viaCidr,proto3" json:"via_cidr,omitempty"`
TargetStrategy internet.DomainStrategy `protobuf:"varint,6,opt,name=target_strategy,json=targetStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"target_strategy,omitempty"`
Via *net.IPOrDomain `protobuf:"bytes,1,opt,name=via,proto3" json:"via,omitempty"`
StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"`
ProxySettings *internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"`
MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"`
ViaCidr string `protobuf:"bytes,5,opt,name=via_cidr,json=viaCidr,proto3" json:"via_cidr,omitempty"`
}
func (x *SenderConfig) Reset() {
*x = SenderConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[5]
mi := &file_app_proxyman_config_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -346,7 +470,7 @@ func (x *SenderConfig) String() string {
func (*SenderConfig) ProtoMessage() {}
func (x *SenderConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[5]
mi := &file_app_proxyman_config_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -359,7 +483,7 @@ func (x *SenderConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use SenderConfig.ProtoReflect.Descriptor instead.
func (*SenderConfig) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{5}
return file_app_proxyman_config_proto_rawDescGZIP(), []int{6}
}
func (x *SenderConfig) GetVia() *net.IPOrDomain {
@@ -397,13 +521,6 @@ func (x *SenderConfig) GetViaCidr() string {
return ""
}
func (x *SenderConfig) GetTargetStrategy() internet.DomainStrategy {
if x != nil {
return x.TargetStrategy
}
return internet.DomainStrategy(0)
}
type MultiplexingConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -421,7 +538,7 @@ type MultiplexingConfig struct {
func (x *MultiplexingConfig) Reset() {
*x = MultiplexingConfig{}
mi := &file_app_proxyman_config_proto_msgTypes[6]
mi := &file_app_proxyman_config_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -433,7 +550,7 @@ func (x *MultiplexingConfig) String() string {
func (*MultiplexingConfig) ProtoMessage() {}
func (x *MultiplexingConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_proto_msgTypes[6]
mi := &file_app_proxyman_config_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -446,7 +563,7 @@ func (x *MultiplexingConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use MultiplexingConfig.ProtoReflect.Descriptor instead.
func (*MultiplexingConfig) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{6}
return file_app_proxyman_config_proto_rawDescGZIP(), []int{7}
}
func (x *MultiplexingConfig) GetEnabled() bool {
@@ -477,6 +594,96 @@ func (x *MultiplexingConfig) GetXudpProxyUDP443() string {
return ""
}
type AllocationStrategy_AllocationStrategyConcurrency struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *AllocationStrategy_AllocationStrategyConcurrency) Reset() {
*x = AllocationStrategy_AllocationStrategyConcurrency{}
mi := &file_app_proxyman_config_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AllocationStrategy_AllocationStrategyConcurrency) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AllocationStrategy_AllocationStrategyConcurrency) ProtoMessage() {}
func (x *AllocationStrategy_AllocationStrategyConcurrency) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_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 AllocationStrategy_AllocationStrategyConcurrency.ProtoReflect.Descriptor instead.
func (*AllocationStrategy_AllocationStrategyConcurrency) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{1, 0}
}
func (x *AllocationStrategy_AllocationStrategyConcurrency) GetValue() uint32 {
if x != nil {
return x.Value
}
return 0
}
type AllocationStrategy_AllocationStrategyRefresh struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *AllocationStrategy_AllocationStrategyRefresh) Reset() {
*x = AllocationStrategy_AllocationStrategyRefresh{}
mi := &file_app_proxyman_config_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AllocationStrategy_AllocationStrategyRefresh) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AllocationStrategy_AllocationStrategyRefresh) ProtoMessage() {}
func (x *AllocationStrategy_AllocationStrategyRefresh) ProtoReflect() protoreflect.Message {
mi := &file_app_proxyman_config_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 AllocationStrategy_AllocationStrategyRefresh.ProtoReflect.Descriptor instead.
func (*AllocationStrategy_AllocationStrategyRefresh) Descriptor() ([]byte, []int) {
return file_app_proxyman_config_proto_rawDescGZIP(), []int{1, 1}
}
func (x *AllocationStrategy_AllocationStrategyRefresh) GetValue() uint32 {
if x != nil {
return x.Value
}
return 0
}
var File_app_proxyman_config_proto protoreflect.FileDescriptor
var file_app_proxyman_config_proto_rawDesc = []byte{
@@ -491,98 +698,125 @@ var file_app_proxyman_config_proto_rawDesc = []byte{
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,
0x6e, 0x66, 0x69, 0x67, 0x22, 0xae, 0x03, 0x0a, 0x12, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x3e, 0x0a, 0x04, 0x74,
0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c,
0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x65, 0x0a, 0x0b, 0x63,
0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x43, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78,
0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53,
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72,
0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
0x63, 0x79, 0x12, 0x59, 0x0a, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x03, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70,
0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x65, 0x66,
0x72, 0x65, 0x73, 0x68, 0x52, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x1a, 0x35, 0x0a,
0x1d, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74,
0x65, 0x67, 0x79, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14,
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x1a, 0x31, 0x0a, 0x19, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73,
0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2c, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
0x0a, 0x0a, 0x06, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x52,
0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x78, 0x74, 0x65, 0x72,
0x6e, 0x61, 0x6c, 0x10, 0x02, 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, 0xbd, 0x03, 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, 0x56, 0x0a, 0x13, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 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, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x4e, 0x0a, 0x0f,
0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18,
0x04, 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, 0x05, 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, 0x07, 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, 0x06, 0x10, 0x07, 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, 0xcb, 0x02, 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, 0x22, 0xa4, 0x01, 0x0a, 0x12, 0x4d, 0x75, 0x6c, 0x74,
0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18,
0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63,
0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x63,
0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75,
0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20,
0x01, 0x28, 0x05, 0x52, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72,
0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78,
0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x78,
0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x42, 0x55,
0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70,
0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50, 0x01, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d,
0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61,
0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x72, 0x6f,
0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -597,39 +831,46 @@ func file_app_proxyman_config_proto_rawDescGZIP() []byte {
return file_app_proxyman_config_proto_rawDescData
}
var file_app_proxyman_config_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_app_proxyman_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_app_proxyman_config_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
var file_app_proxyman_config_proto_goTypes = []any{
(*InboundConfig)(nil), // 0: xray.app.proxyman.InboundConfig
(*SniffingConfig)(nil), // 1: xray.app.proxyman.SniffingConfig
(*ReceiverConfig)(nil), // 2: xray.app.proxyman.ReceiverConfig
(*InboundHandlerConfig)(nil), // 3: xray.app.proxyman.InboundHandlerConfig
(*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
(AllocationStrategy_Type)(0), // 0: xray.app.proxyman.AllocationStrategy.Type
(*InboundConfig)(nil), // 1: xray.app.proxyman.InboundConfig
(*AllocationStrategy)(nil), // 2: xray.app.proxyman.AllocationStrategy
(*SniffingConfig)(nil), // 3: xray.app.proxyman.SniffingConfig
(*ReceiverConfig)(nil), // 4: xray.app.proxyman.ReceiverConfig
(*InboundHandlerConfig)(nil), // 5: xray.app.proxyman.InboundHandlerConfig
(*OutboundConfig)(nil), // 6: xray.app.proxyman.OutboundConfig
(*SenderConfig)(nil), // 7: xray.app.proxyman.SenderConfig
(*MultiplexingConfig)(nil), // 8: xray.app.proxyman.MultiplexingConfig
(*AllocationStrategy_AllocationStrategyConcurrency)(nil), // 9: xray.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency
(*AllocationStrategy_AllocationStrategyRefresh)(nil), // 10: xray.app.proxyman.AllocationStrategy.AllocationStrategyRefresh
(*net.PortList)(nil), // 11: xray.common.net.PortList
(*net.IPOrDomain)(nil), // 12: xray.common.net.IPOrDomain
(*internet.StreamConfig)(nil), // 13: xray.transport.internet.StreamConfig
(*serial.TypedMessage)(nil), // 14: xray.common.serial.TypedMessage
(*internet.ProxyConfig)(nil), // 15: xray.transport.internet.ProxyConfig
}
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
0, // 0: xray.app.proxyman.AllocationStrategy.type:type_name -> xray.app.proxyman.AllocationStrategy.Type
9, // 1: xray.app.proxyman.AllocationStrategy.concurrency:type_name -> xray.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency
10, // 2: xray.app.proxyman.AllocationStrategy.refresh:type_name -> xray.app.proxyman.AllocationStrategy.AllocationStrategyRefresh
11, // 3: xray.app.proxyman.ReceiverConfig.port_list:type_name -> xray.common.net.PortList
12, // 4: xray.app.proxyman.ReceiverConfig.listen:type_name -> xray.common.net.IPOrDomain
2, // 5: xray.app.proxyman.ReceiverConfig.allocation_strategy:type_name -> xray.app.proxyman.AllocationStrategy
13, // 6: xray.app.proxyman.ReceiverConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
3, // 7: xray.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> xray.app.proxyman.SniffingConfig
14, // 8: xray.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage
14, // 9: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage
12, // 10: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain
13, // 11: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
15, // 12: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig
8, // 13: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig
14, // [14:14] is the sub-list for method output_type
14, // [14:14] is the sub-list for method input_type
14, // [14:14] is the sub-list for extension type_name
14, // [14:14] is the sub-list for extension extendee
0, // [0:14] is the sub-list for field type_name
}
func init() { file_app_proxyman_config_proto_init() }
@@ -642,13 +883,14 @@ func file_app_proxyman_config_proto_init() {
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_app_proxyman_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 7,
NumEnums: 1,
NumMessages: 10,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_app_proxyman_config_proto_goTypes,
DependencyIndexes: file_app_proxyman_config_proto_depIdxs,
EnumInfos: file_app_proxyman_config_proto_enumTypes,
MessageInfos: file_app_proxyman_config_proto_msgTypes,
}.Build()
File_app_proxyman_config_proto = out.File

View File

@@ -13,6 +13,33 @@ import "common/serial/typed_message.proto";
message InboundConfig {}
message AllocationStrategy {
enum Type {
// Always allocate all connection handlers.
Always = 0;
// Randomly allocate specific range of handlers.
Random = 1;
// External. Not supported yet.
External = 2;
}
Type type = 1;
message AllocationStrategyConcurrency { uint32 value = 1; }
// Number of handlers (ports) running in parallel.
// Default value is 3 if unset.
AllocationStrategyConcurrency concurrency = 2;
message AllocationStrategyRefresh { uint32 value = 1; }
// Number of minutes before a handler is regenerated.
// Default value is 5 if unset.
AllocationStrategyRefresh refresh = 3;
}
message SniffingConfig {
// Whether or not to enable content sniffing on an inbound connection.
bool enabled = 1;
@@ -35,10 +62,11 @@ message ReceiverConfig {
xray.common.net.PortList port_list = 1;
// Listen specifies the IP address that the Receiver should listen on.
xray.common.net.IPOrDomain listen = 2;
xray.transport.internet.StreamConfig stream_settings = 3;
bool receive_original_destination = 4;
reserved 5;
SniffingConfig sniffing_settings = 6;
AllocationStrategy allocation_strategy = 3;
xray.transport.internet.StreamConfig stream_settings = 4;
bool receive_original_destination = 5;
reserved 6;
SniffingConfig sniffing_settings = 7;
}
message InboundHandlerConfig {
@@ -56,7 +84,6 @@ message SenderConfig {
xray.transport.internet.ProxyConfig proxy_settings = 3;
MultiplexingConfig multiplex_settings = 4;
string via_cidr = 5;
xray.transport.internet.DomainStrategy target_strategy = 6;
}
message MultiplexingConfig {

View File

@@ -5,6 +5,7 @@ import (
"github.com/xtls/xray-core/app/proxyman"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/dice"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/mux"
"github.com/xtls/xray-core/common/net"
@@ -102,7 +103,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
stream: mss,
tag: tag,
dispatcher: h.mux,
sniffingConfig: receiverConfig.SniffingSettings,
sniffingConfig: receiverConfig.GetEffectiveSniffingSettings(),
uplinkCounter: uplinkCounter,
downlinkCounter: downlinkCounter,
ctx: ctx,
@@ -124,7 +125,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
recvOrigDest: receiverConfig.ReceiveOriginalDestination,
tag: tag,
dispatcher: h.mux,
sniffingConfig: receiverConfig.SniffingSettings,
sniffingConfig: receiverConfig.GetEffectiveSniffingSettings(),
uplinkCounter: uplinkCounter,
downlinkCounter: downlinkCounter,
ctx: ctx,
@@ -139,7 +140,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
address: address,
port: net.Port(port),
dispatcher: h.mux,
sniffingConfig: receiverConfig.SniffingSettings,
sniffingConfig: receiverConfig.GetEffectiveSniffingSettings(),
uplinkCounter: uplinkCounter,
downlinkCounter: downlinkCounter,
stream: mss,
@@ -177,6 +178,14 @@ func (h *AlwaysOnInboundHandler) Close() error {
return nil
}
func (h *AlwaysOnInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, int) {
if len(h.workers) == 0 {
return nil, 0, 0
}
w := h.workers[dice.Roll(len(h.workers))]
return w.Proxy(), w.Port(), 9999
}
func (h *AlwaysOnInboundHandler) Tag() string {
return h.tag
}

View File

@@ -0,0 +1,222 @@
package inbound
import (
"context"
"sync"
"time"
"github.com/xtls/xray-core/app/proxyman"
"github.com/xtls/xray-core/common/dice"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/mux"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/proxy"
"github.com/xtls/xray-core/transport/internet"
"google.golang.org/protobuf/proto"
)
type DynamicInboundHandler struct {
tag string
v *core.Instance
proxyConfig interface{}
receiverConfig *proxyman.ReceiverConfig
streamSettings *internet.MemoryStreamConfig
portMutex sync.Mutex
portsInUse map[net.Port]struct{}
workerMutex sync.RWMutex
worker []worker
lastRefresh time.Time
mux *mux.Server
task *task.Periodic
ctx context.Context
}
func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*DynamicInboundHandler, error) {
v := core.MustFromContext(ctx)
h := &DynamicInboundHandler{
tag: tag,
proxyConfig: proxyConfig,
receiverConfig: receiverConfig,
portsInUse: make(map[net.Port]struct{}),
mux: mux.NewServer(ctx),
v: v,
ctx: ctx,
}
mss, err := internet.ToMemoryStreamConfig(receiverConfig.StreamSettings)
if err != nil {
return nil, errors.New("failed to parse stream settings").Base(err).AtWarning()
}
if receiverConfig.ReceiveOriginalDestination {
if mss.SocketSettings == nil {
mss.SocketSettings = &internet.SocketConfig{}
}
if mss.SocketSettings.Tproxy == internet.SocketConfig_Off {
mss.SocketSettings.Tproxy = internet.SocketConfig_Redirect
}
mss.SocketSettings.ReceiveOriginalDestAddress = true
}
h.streamSettings = mss
h.task = &task.Periodic{
Interval: time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue()),
Execute: h.refresh,
}
return h, nil
}
func (h *DynamicInboundHandler) allocatePort() net.Port {
allPorts := []int32{}
for _, pr := range h.receiverConfig.PortList.Range {
for i := pr.From; i <= pr.To; i++ {
allPorts = append(allPorts, int32(i))
}
}
h.portMutex.Lock()
defer h.portMutex.Unlock()
for {
r := dice.Roll(len(allPorts))
port := net.Port(allPorts[r])
_, used := h.portsInUse[port]
if !used {
h.portsInUse[port] = struct{}{}
return port
}
}
}
func (h *DynamicInboundHandler) closeWorkers(workers []worker) {
ports2Del := make([]net.Port, len(workers))
for idx, worker := range workers {
ports2Del[idx] = worker.Port()
if err := worker.Close(); err != nil {
errors.LogInfoInner(h.ctx, err, "failed to close worker")
}
}
h.portMutex.Lock()
for _, port := range ports2Del {
delete(h.portsInUse, port)
}
h.portMutex.Unlock()
}
func (h *DynamicInboundHandler) refresh() error {
h.lastRefresh = time.Now()
timeout := time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue()) * 2
concurrency := h.receiverConfig.AllocationStrategy.GetConcurrencyValue()
workers := make([]worker, 0, concurrency)
address := h.receiverConfig.Listen.AsAddress()
if address == nil {
address = net.AnyIP
}
uplinkCounter, downlinkCounter := getStatCounter(h.v, h.tag)
for i := uint32(0); i < concurrency; i++ {
port := h.allocatePort()
rawProxy, err := core.CreateObject(h.v, h.proxyConfig)
if err != nil {
errors.LogWarningInner(h.ctx, err, "failed to create proxy instance")
continue
}
p := rawProxy.(proxy.Inbound)
nl := p.Network()
if net.HasNetwork(nl, net.Network_TCP) {
worker := &tcpWorker{
tag: h.tag,
address: address,
port: port,
proxy: p,
stream: h.streamSettings,
recvOrigDest: h.receiverConfig.ReceiveOriginalDestination,
dispatcher: h.mux,
sniffingConfig: h.receiverConfig.GetEffectiveSniffingSettings(),
uplinkCounter: uplinkCounter,
downlinkCounter: downlinkCounter,
ctx: h.ctx,
}
if err := worker.Start(); err != nil {
errors.LogWarningInner(h.ctx, err, "failed to create TCP worker")
continue
}
workers = append(workers, worker)
}
if net.HasNetwork(nl, net.Network_UDP) {
worker := &udpWorker{
tag: h.tag,
proxy: p,
address: address,
port: port,
dispatcher: h.mux,
sniffingConfig: h.receiverConfig.GetEffectiveSniffingSettings(),
uplinkCounter: uplinkCounter,
downlinkCounter: downlinkCounter,
stream: h.streamSettings,
ctx: h.ctx,
}
if err := worker.Start(); err != nil {
errors.LogWarningInner(h.ctx, err, "failed to create UDP worker")
continue
}
workers = append(workers, worker)
}
}
h.workerMutex.Lock()
h.worker = workers
h.workerMutex.Unlock()
time.AfterFunc(timeout, func() {
h.closeWorkers(workers)
})
return nil
}
func (h *DynamicInboundHandler) Start() error {
return h.task.Start()
}
func (h *DynamicInboundHandler) Close() error {
return h.task.Close()
}
func (h *DynamicInboundHandler) GetRandomInboundProxy() (interface{}, net.Port, int) {
h.workerMutex.RLock()
defer h.workerMutex.RUnlock()
if len(h.worker) == 0 {
return nil, 0, 0
}
w := h.worker[dice.Roll(len(h.worker))]
expire := h.receiverConfig.AllocationStrategy.GetRefreshValue() - uint32(time.Since(h.lastRefresh)/time.Minute)
return w.Proxy(), w.Port(), int(expire)
}
func (h *DynamicInboundHandler) Tag() string {
return h.tag
}
// ReceiverSettings implements inbound.Handler.
func (h *DynamicInboundHandler) ReceiverSettings() *serial.TypedMessage {
return serial.ToTypedMessage(h.receiverConfig)
}
// ProxySettings implements inbound.Handler.
func (h *DynamicInboundHandler) ProxySettings() *serial.TypedMessage {
if v, ok := h.proxyConfig.(proto.Message); ok {
return serial.ToTypedMessage(v)
}
return nil
}

View File

@@ -17,7 +17,7 @@ import (
// Manager manages all inbound handlers.
type Manager struct {
access sync.RWMutex
untaggedHandlers []inbound.Handler
untaggedHandler []inbound.Handler
taggedHandlers map[string]inbound.Handler
running bool
}
@@ -47,7 +47,7 @@ func (m *Manager) AddHandler(ctx context.Context, handler inbound.Handler) error
}
m.taggedHandlers[tag] = handler
} else {
m.untaggedHandlers = append(m.untaggedHandlers, handler)
m.untaggedHandler = append(m.untaggedHandler, handler)
}
if m.running {
@@ -94,8 +94,8 @@ func (m *Manager) ListHandlers(ctx context.Context) []inbound.Handler {
m.access.RLock()
defer m.access.RUnlock()
response := make([]inbound.Handler, len(m.untaggedHandlers))
copy(response, m.untaggedHandlers)
var response []inbound.Handler
copy(m.untaggedHandler, response)
for _, v := range m.taggedHandlers {
response = append(response, v)
@@ -117,7 +117,7 @@ func (m *Manager) Start() error {
}
}
for _, handler := range m.untaggedHandlers {
for _, handler := range m.untaggedHandler {
if err := handler.Start(); err != nil {
return err
}
@@ -138,7 +138,7 @@ func (m *Manager) Close() error {
errs = append(errs, err)
}
}
for _, handler := range m.untaggedHandlers {
for _, handler := range m.untaggedHandler {
if err := handler.Close(); err != nil {
errs = append(errs, err)
}
@@ -178,7 +178,15 @@ func NewHandler(ctx context.Context, config *core.InboundHandlerConfig) (inbound
ctx = session.ContextWithAllowedNetwork(ctx, net.Network_UDP)
}
return NewAlwaysOnInboundHandler(ctx, tag, receiverSettings, proxySettings)
allocStrategy := receiverSettings.AllocationStrategy
if allocStrategy == nil || allocStrategy.Type == proxyman.AllocationStrategy_Always {
return NewAlwaysOnInboundHandler(ctx, tag, receiverSettings, proxySettings)
}
if allocStrategy.Type == proxyman.AllocationStrategy_Random {
return NewDynamicInboundHandler(ctx, tag, receiverSettings, proxySettings)
}
return nil, errors.New("unknown allocation strategy: ", receiverSettings.AllocationStrategy.Type).AtError()
}
func init() {

View File

@@ -2,7 +2,6 @@ package inbound
import (
"context"
gonet "net"
"sync"
"sync/atomic"
"time"
@@ -77,25 +76,7 @@ func (w *tcpWorker) callback(conn stat.Connection) {
case internet.SocketConfig_TProxy:
dest = net.DestinationFromAddr(conn.LocalAddr())
}
if dest.IsValid() {
// Check if try to connect to this inbound itself (can cause loopback)
var isLoopBack bool
if w.address == net.AnyIP || w.address == net.AnyIPv6 {
if dest.Port.Value() == w.port.Value() && IsLocal(dest.Address.IP()) {
isLoopBack = true
}
} else {
if w.hub.Addr().String() == dest.NetAddr() {
isLoopBack = true
}
}
if isLoopBack {
cancel()
conn.Close()
errors.LogError(ctx, errors.New("loopback connection detected"))
return
}
outbounds[0].Target = dest
}
}
@@ -110,7 +91,6 @@ func (w *tcpWorker) callback(conn stat.Connection) {
}
ctx = session.ContextWithInbound(ctx, &session.Inbound{
Source: net.DestinationFromAddr(conn.RemoteAddr()),
Local: net.DestinationFromAddr(conn.LocalAddr()),
Gateway: net.TCPDestination(w.address, w.port),
Tag: w.tag,
Conn: conn,
@@ -341,18 +321,8 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
outbounds[0].Target = originalDest
}
ctx = session.ContextWithOutbounds(ctx, outbounds)
local := net.DestinationFromAddr(w.hub.Addr())
if local.Address == net.AnyIP || local.Address == net.AnyIPv6 {
if source.Address.Family().IsIPv4() {
local.Address = net.AnyIP
} else if source.Address.Family().IsIPv6() {
local.Address = net.AnyIPv6
}
}
ctx = session.ContextWithInbound(ctx, &session.Inbound{
Source: source,
Local: local, // Due to some limitations, in UDP connections, localIP is always equal to listen interface IP
Gateway: net.UDPDestination(w.address, w.port),
Tag: w.tag,
})
@@ -502,7 +472,6 @@ func (w *dsWorker) callback(conn stat.Connection) {
}
ctx = session.ContextWithInbound(ctx, &session.Inbound{
Source: net.DestinationFromAddr(conn.RemoteAddr()),
Local: net.DestinationFromAddr(conn.LocalAddr()),
Gateway: net.UnixDestination(w.address),
Tag: w.tag,
Conn: conn,
@@ -563,18 +532,3 @@ func (w *dsWorker) Close() error {
return nil
}
func IsLocal(ip net.IP) bool {
addrs, err := gonet.InterfaceAddrs()
if err != nil {
return false
}
for _, addr := range addrs {
if ipnet, ok := addr.(*gonet.IPNet); ok {
if ipnet.IP.Equal(ip) {
return true
}
}
}
return false
}

View File

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

View File

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

View File

@@ -4,12 +4,10 @@ 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"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport"
@@ -54,11 +52,6 @@ func (b *Bridge) cleanup() {
if w.IsActive() {
activeWorkers = append(activeWorkers, w)
}
if w.Closed() {
if w.Timer != nil {
w.Timer.SetTimeout(0)
}
}
}
if len(activeWorkers) != len(b.workers) {
@@ -100,11 +93,10 @@ func (b *Bridge) Close() error {
}
type BridgeWorker struct {
Tag string
Worker *mux.ServerWorker
Dispatcher routing.Dispatcher
State Control_State
Timer *signal.ActivityTimer
tag string
worker *mux.ServerWorker
dispatcher routing.Dispatcher
state Control_State
}
func NewBridgeWorker(domain string, tag string, d routing.Dispatcher) (*BridgeWorker, error) {
@@ -122,20 +114,16 @@ func NewBridgeWorker(domain string, tag string, d routing.Dispatcher) (*BridgeWo
}
w := &BridgeWorker{
Dispatcher: d,
Tag: tag,
dispatcher: d,
tag: tag,
}
worker, err := mux.NewServerWorker(context.Background(), w, link)
if err != nil {
return nil, err
}
w.Worker = worker
w.worker = worker
terminate := func() {
worker.Close()
}
w.Timer = signal.CancelAfterInactivity(ctx, terminate, 60*time.Second)
return w, nil
}
@@ -152,65 +140,48 @@ func (w *BridgeWorker) Close() error {
}
func (w *BridgeWorker) IsActive() bool {
return w.State == Control_ACTIVE && !w.Worker.Closed()
}
func (w *BridgeWorker) Closed() bool {
return w.Worker.Closed()
return w.state == Control_ACTIVE && !w.worker.Closed()
}
func (w *BridgeWorker) Connections() uint32 {
return w.Worker.ActiveConnections()
return w.worker.ActiveConnections()
}
func (w *BridgeWorker) handleInternalConn(link *transport.Link) {
reader := link.Reader
for {
mb, err := reader.ReadMultiBuffer()
if err != nil {
if w.Timer != nil {
if w.Closed() {
w.Timer.SetTimeout(0)
} else {
w.Timer.SetTimeout(24 * time.Hour)
go func() {
reader := link.Reader
for {
mb, err := reader.ReadMultiBuffer()
if err != nil {
break
}
for _, b := range mb {
var ctl Control
if err := proto.Unmarshal(b.Bytes(), &ctl); err != nil {
errors.LogInfoInner(context.Background(), err, "failed to parse proto message")
break
}
if ctl.State != w.state {
w.state = ctl.State
}
}
return
}
if w.Timer != nil {
w.Timer.Update()
}
for _, b := range mb {
var ctl Control
if err := proto.Unmarshal(b.Bytes(), &ctl); err != nil {
errors.LogInfoInner(context.Background(), err, "failed to parse proto message")
if w.Timer != nil {
w.Timer.SetTimeout(0)
}
return
}
if ctl.State != w.State {
w.State = ctl.State
}
}
}
}()
}
func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) {
if !isInternalDomain(dest) {
if session.InboundFromContext(ctx) == nil {
ctx = session.ContextWithInbound(ctx, &session.Inbound{
Tag: w.Tag,
})
}
return w.Dispatcher.Dispatch(ctx, dest)
ctx = session.ContextWithInbound(ctx, &session.Inbound{
Tag: w.tag,
})
return w.dispatcher.Dispatch(ctx, dest)
}
opt := []pipe.Option{pipe.WithSizeLimit(16 * 1024)}
uplinkReader, uplinkWriter := pipe.New(opt...)
downlinkReader, downlinkWriter := pipe.New(opt...)
go w.handleInternalConn(&transport.Link{
w.handleInternalConn(&transport.Link{
Reader: downlinkReader,
Writer: uplinkWriter,
})
@@ -223,15 +194,12 @@ func (w *BridgeWorker) Dispatch(ctx context.Context, dest net.Destination) (*tra
func (w *BridgeWorker) DispatchLink(ctx context.Context, dest net.Destination, link *transport.Link) error {
if !isInternalDomain(dest) {
if session.InboundFromContext(ctx) == nil {
ctx = session.ContextWithInbound(ctx, &session.Inbound{
Tag: w.Tag,
})
}
return w.Dispatcher.DispatchLink(ctx, dest, link)
ctx = session.ContextWithInbound(ctx, &session.Inbound{
Tag: w.tag,
})
return w.dispatcher.DispatchLink(ctx, dest, link)
}
link = w.Dispatcher.(*dispatcher.DefaultDispatcher).WrapLink(ctx, link)
w.handleInternalConn(link)
return nil

View File

@@ -12,7 +12,6 @@ import (
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/transport"
@@ -83,21 +82,9 @@ func (p *Portal) HandleConnection(ctx context.Context, link *transport.Link) err
}
p.picker.AddWorker(worker)
if _, ok := link.Reader.(*pipe.Reader); !ok {
select {
case <-ctx.Done():
case <-muxClient.WaitClosed():
}
}
return nil
}
if ob.Target.Network == net.Network_UDP && ob.OriginalTarget.Address != nil && ob.OriginalTarget.Address != ob.Target.Address {
link.Reader = &buf.EndpointOverrideReader{Reader: link.Reader, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
link.Writer = &buf.EndpointOverrideWriter{Writer: link.Writer, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
}
return p.client.Dispatch(ctx, link)
}
@@ -114,7 +101,6 @@ func (o *Outbound) Dispatch(ctx context.Context, link *transport.Link) {
if err := o.portal.HandleConnection(ctx, link); err != nil {
errors.LogInfoInner(ctx, err, "failed to process reverse connection")
common.Interrupt(link.Writer)
common.Interrupt(link.Reader)
}
}
@@ -160,8 +146,6 @@ func (p *StaticMuxPicker) cleanup() error {
for _, w := range p.workers {
if !w.Closed() {
activeWorkers = append(activeWorkers, w)
} else {
w.timer.SetTimeout(0)
}
}
@@ -228,7 +212,6 @@ type PortalWorker struct {
reader buf.Reader
draining bool
counter uint32
timer *signal.ActivityTimer
}
func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) {
@@ -248,14 +231,10 @@ func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) {
if !f {
return nil, errors.New("unable to dispatch control connection")
}
terminate := func() {
client.Close()
}
w := &PortalWorker{
client: client,
reader: downlinkReader,
writer: uplinkWriter,
timer: signal.CancelAfterInactivity(ctx, terminate, 24*time.Hour), // // prevent leak
}
w.control = &task.Periodic{
Execute: w.heartbeat,
@@ -282,6 +261,7 @@ func (w *PortalWorker) heartbeat() error {
msg.State = Control_DRAIN
defer func() {
w.client.GetTimer().Reset(time.Second * 16)
common.Close(w.writer)
common.Interrupt(w.reader)
w.writer = nil
@@ -293,7 +273,6 @@ func (w *PortalWorker) heartbeat() error {
b, err := proto.Marshal(msg)
common.Must(err)
mb := buf.MergeBytes(nil, b)
w.timer.Update()
return w.writer.WriteMultiBuffer(mb)
}
return nil

View File

@@ -12,7 +12,7 @@ import (
)
const (
internalDomain = "reverse"
internalDomain = "reverse.internal.v2fly.org" // make reverse proxy compatible with v2fly
)
func isDomain(dest net.Destination, domain string) bool {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -13,7 +13,7 @@ const (
Size = 8192
)
var ErrBufferFull = errors.New("buffer is full")
var zero = [Size * 10]byte{0}
var pool = bytespool.GetPool(Size)
@@ -144,7 +144,7 @@ func (b *Buffer) Bytes() []byte {
}
// Extend increases the buffer size by n bytes, and returns the extended part.
// It panics if result size is larger than size of this buffer.
// It panics if result size is larger than buf.Size.
func (b *Buffer) Extend(n int32) []byte {
end := b.end + n
if end > int32(len(b.v)) {
@@ -152,7 +152,7 @@ func (b *Buffer) Extend(n int32) []byte {
}
ext := b.v[b.end:end]
b.end = end
clear(ext)
copy(ext, zero[:])
return ext
}
@@ -215,7 +215,7 @@ func (b *Buffer) Resize(from, to int32) {
b.start += from
b.Check()
if b.end > oldEnd {
clear(b.v[oldEnd:b.end])
copy(b.v[oldEnd:b.end], zero[:])
}
}
@@ -244,14 +244,6 @@ func (b *Buffer) Cap() int32 {
return int32(len(b.v))
}
// Available returns the available capacity of the buffer content.
func (b *Buffer) Available() int32 {
if b == nil {
return 0
}
return int32(len(b.v)) - b.end
}
// IsEmpty returns true if the buffer is empty.
func (b *Buffer) IsEmpty() bool {
return b.Len() == 0
@@ -266,16 +258,13 @@ func (b *Buffer) IsFull() bool {
func (b *Buffer) Write(data []byte) (int, error) {
nBytes := copy(b.v[b.end:], data)
b.end += int32(nBytes)
if nBytes < len(data) {
return nBytes, ErrBufferFull
}
return nBytes, nil
}
// WriteByte writes a single byte into the buffer.
func (b *Buffer) WriteByte(v byte) error {
if b.IsFull() {
return ErrBufferFull
return errors.New("buffer full")
}
b.v[b.end] = v
b.end++

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,6 @@ import (
"go/build"
"os"
"path/filepath"
"reflect"
"strings"
"github.com/xtls/xray-core/common/errors"
@@ -24,9 +23,7 @@ func Must(err error) {
}
// Must2 panics if the second parameter is not nil, otherwise returns the first parameter.
// This is useful when function returned "sth, err" and avoid many "if err != nil"
// Internal usage only, if user input can cause err, it must be handled
func Must2[T any](v T, err error) T {
func Must2(v interface{}, err error) interface{} {
Must(err)
return v
}
@@ -154,14 +151,3 @@ func GetModuleName(pathToProjectRoot string) (string, error) {
}
return moduleName, fmt.Errorf("no `go.mod` file in every parent directory of `%s`", pathToProjectRoot)
}
// CloseIfExists call obj.Close() if obj is not nil.
func CloseIfExists(obj any) error {
if obj != nil {
v := reflect.ValueOf(obj)
if !v.IsNil() {
return Close(obj)
}
}
return nil
}

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,6 @@ package mux
import (
"context"
goerrors "errors"
"io"
"sync"
"time"
@@ -155,11 +154,8 @@ func (f *DialingWorkerFactory) Create() (*ClientWorker, error) {
ctx := session.ContextWithOutbounds(context.Background(), outbounds)
ctx, cancel := context.WithCancel(ctx)
if errP := p.Process(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter}, d); errP != nil {
errC := errors.Cause(errP)
if !(goerrors.Is(errC, io.EOF) || goerrors.Is(errC, io.ErrClosedPipe) || goerrors.Is(errC, context.Canceled)) {
errors.LogInfoInner(ctx, errP, "failed to handler mux client connection")
}
if err := p.Process(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter}, d); err != nil {
errors.LogInfoInner(ctx, err, "failed to handler mux client connection")
}
common.Must(c.Close())
cancel()
@@ -215,28 +211,23 @@ func (m *ClientWorker) Closed() bool {
return m.done.Done()
}
func (m *ClientWorker) WaitClosed() <-chan struct{} {
return m.done.Wait()
}
func (m *ClientWorker) Close() error {
return m.done.Close()
func (m *ClientWorker) GetTimer() *time.Ticker {
return m.timer
}
func (m *ClientWorker) monitor() {
defer m.timer.Stop()
for {
checkSize := m.sessionManager.Size()
checkCount := m.sessionManager.Count()
select {
case <-m.done.Wait():
m.sessionManager.Close()
common.Interrupt(m.link.Writer)
common.Close(m.link.Writer)
common.Interrupt(m.link.Reader)
return
case <-m.timer.C:
if m.sessionManager.CloseIfNoSessionAndIdle(checkSize, checkCount) {
size := m.sessionManager.Size()
if size == 0 && m.sessionManager.CloseIfNoSession() {
common.Must(m.done.Close())
}
}
@@ -264,11 +255,7 @@ func fetchInput(ctx context.Context, s *Session, output buf.Writer) {
transferType = protocol.TransferTypePacket
}
s.transferType = transferType
var inbound *session.Inbound
if session.IsReverseMuxFromContext(ctx) {
inbound = session.InboundFromContext(ctx)
}
writer := NewWriter(s.ID, ob.Target, output, transferType, xudp.GetGlobalID(ctx), inbound)
writer := NewWriter(s.ID, ob.Target, output, transferType, xudp.GetGlobalID(ctx))
defer s.Close(false)
defer writer.Close()
@@ -321,12 +308,6 @@ func (m *ClientWorker) Dispatch(ctx context.Context, link *transport.Link) bool
s.input = link.Reader
s.output = link.Writer
go fetchInput(ctx, s, m.link.Writer)
if _, ok := link.Reader.(*pipe.Reader); !ok {
select {
case <-ctx.Done():
case <-s.done.Wait():
}
}
return true
}
@@ -388,7 +369,7 @@ func (m *ClientWorker) fetchOutput() {
var meta FrameMetadata
for {
err := meta.Unmarshal(reader, false)
err := meta.Unmarshal(reader)
if err != nil {
if errors.Cause(err) != io.EOF {
errors.LogInfoInner(context.Background(), err, "failed to read metadata")

View File

@@ -11,7 +11,6 @@ import (
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/session"
)
type SessionStatus byte
@@ -61,7 +60,6 @@ type FrameMetadata struct {
Option bitmask.Byte
SessionStatus SessionStatus
GlobalID [8]byte
Inbound *session.Inbound
}
func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
@@ -81,23 +79,11 @@ func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
case net.Network_UDP:
common.Must(b.WriteByte(byte(TargetNetworkUDP)))
}
if err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil {
return err
}
if f.Inbound != nil {
if f.Inbound.Source.Network == net.Network_TCP || f.Inbound.Source.Network == net.Network_UDP {
common.Must(b.WriteByte(byte(f.Inbound.Source.Network - 1)))
if err := addrParser.WriteAddressPort(b, f.Inbound.Source.Address, f.Inbound.Source.Port); err != nil {
return err
}
if f.Inbound.Local.Network == net.Network_TCP || f.Inbound.Local.Network == net.Network_UDP {
common.Must(b.WriteByte(byte(f.Inbound.Local.Network - 1)))
if err := addrParser.WriteAddressPort(b, f.Inbound.Local.Address, f.Inbound.Local.Port); err != nil {
return err
}
}
}
} else if b.UDP != nil { // make sure it's user's proxy request
if b.UDP != nil { // make sure it's user's proxy request
b.Write(f.GlobalID[:]) // no need to check whether it's empty
}
} else if b.UDP != nil {
@@ -111,7 +97,7 @@ func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
}
// Unmarshal reads FrameMetadata from the given reader.
func (f *FrameMetadata) Unmarshal(reader io.Reader, readSourceAndLocal bool) error {
func (f *FrameMetadata) Unmarshal(reader io.Reader) error {
metaLen, err := serial.ReadUint16(reader)
if err != nil {
return err
@@ -126,12 +112,12 @@ func (f *FrameMetadata) Unmarshal(reader io.Reader, readSourceAndLocal bool) err
if _, err := b.ReadFullFrom(reader, int32(metaLen)); err != nil {
return err
}
return f.UnmarshalFromBuffer(b, readSourceAndLocal)
return f.UnmarshalFromBuffer(b)
}
// UnmarshalFromBuffer reads a FrameMetadata from the given buffer.
// Visible for testing only.
func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer, readSourceAndLocal bool) error {
func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error {
if b.Len() < 4 {
return errors.New("insufficient buffer: ", b.Len())
}
@@ -164,54 +150,6 @@ func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer, readSourceAndLocal bo
}
}
if f.SessionStatus == SessionStatusNew && readSourceAndLocal {
f.Inbound = &session.Inbound{}
if b.Len() == 0 {
return nil // for heartbeat, etc.
}
network := TargetNetwork(b.Byte(0))
if network == 0 {
return nil // may be padding
}
b.Advance(1)
addr, port, err := addrParser.ReadAddressPort(nil, b)
if err != nil {
return errors.New("reading source: failed to parse address and port").Base(err)
}
switch network {
case TargetNetworkTCP:
f.Inbound.Source = net.TCPDestination(addr, port)
case TargetNetworkUDP:
f.Inbound.Source = net.UDPDestination(addr, port)
default:
return errors.New("reading source: unknown network type: ", network)
}
if b.Len() == 0 {
return nil
}
network = TargetNetwork(b.Byte(0))
if network == 0 {
return nil
}
b.Advance(1)
addr, port, err = addrParser.ReadAddressPort(nil, b)
if err != nil {
return errors.New("reading local: failed to parse address and port").Base(err)
}
switch network {
case TargetNetworkTCP:
f.Inbound.Local = net.TCPDestination(addr, port)
case TargetNetworkUDP:
f.Inbound.Local = net.UDPDestination(addr, port)
default:
return errors.New("reading local: unknown network type: ", network)
}
return nil
}
// Application data is essential, to test whether the pipe is closed.
if f.SessionStatus == SessionStatusNew && f.Option.Has(OptionData) &&
f.Target.Network == net.Network_UDP && b.Len() >= 8 {

View File

@@ -10,7 +10,6 @@ import (
. "github.com/xtls/xray-core/common/mux"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/transport/pipe"
)
@@ -33,13 +32,13 @@ func TestReaderWriter(t *testing.T) {
pReader, pWriter := pipe.New(pipe.WithSizeLimit(1024))
dest := net.TCPDestination(net.DomainAddress("example.com"), 80)
writer := NewWriter(1, dest, pWriter, protocol.TransferTypeStream, [8]byte{}, &session.Inbound{})
writer := NewWriter(1, dest, pWriter, protocol.TransferTypeStream, [8]byte{})
dest2 := net.TCPDestination(net.LocalHostIP, 443)
writer2 := NewWriter(2, dest2, pWriter, protocol.TransferTypeStream, [8]byte{}, &session.Inbound{})
writer2 := NewWriter(2, dest2, pWriter, protocol.TransferTypeStream, [8]byte{})
dest3 := net.TCPDestination(net.LocalHostIPv6, 18374)
writer3 := NewWriter(3, dest3, pWriter, protocol.TransferTypeStream, [8]byte{}, &session.Inbound{})
writer3 := NewWriter(3, dest3, pWriter, protocol.TransferTypeStream, [8]byte{})
writePayload := func(writer *Writer, payload ...byte) error {
b := buf.New()
@@ -63,7 +62,7 @@ func TestReaderWriter(t *testing.T) {
{
var meta FrameMetadata
common.Must(meta.Unmarshal(bytesReader, false))
common.Must(meta.Unmarshal(bytesReader))
if r := cmp.Diff(meta, FrameMetadata{
SessionID: 1,
SessionStatus: SessionStatusNew,
@@ -82,7 +81,7 @@ func TestReaderWriter(t *testing.T) {
{
var meta FrameMetadata
common.Must(meta.Unmarshal(bytesReader, false))
common.Must(meta.Unmarshal(bytesReader))
if r := cmp.Diff(meta, FrameMetadata{
SessionStatus: SessionStatusNew,
SessionID: 2,
@@ -95,7 +94,7 @@ func TestReaderWriter(t *testing.T) {
{
var meta FrameMetadata
common.Must(meta.Unmarshal(bytesReader, false))
common.Must(meta.Unmarshal(bytesReader))
if r := cmp.Diff(meta, FrameMetadata{
SessionID: 1,
SessionStatus: SessionStatusKeep,
@@ -113,7 +112,7 @@ func TestReaderWriter(t *testing.T) {
{
var meta FrameMetadata
common.Must(meta.Unmarshal(bytesReader, false))
common.Must(meta.Unmarshal(bytesReader))
if r := cmp.Diff(meta, FrameMetadata{
SessionID: 3,
SessionStatus: SessionStatusNew,
@@ -132,7 +131,7 @@ func TestReaderWriter(t *testing.T) {
{
var meta FrameMetadata
common.Must(meta.Unmarshal(bytesReader, false))
common.Must(meta.Unmarshal(bytesReader))
if r := cmp.Diff(meta, FrameMetadata{
SessionID: 1,
SessionStatus: SessionStatusEnd,
@@ -144,7 +143,7 @@ func TestReaderWriter(t *testing.T) {
{
var meta FrameMetadata
common.Must(meta.Unmarshal(bytesReader, false))
common.Must(meta.Unmarshal(bytesReader))
if r := cmp.Diff(meta, FrameMetadata{
SessionID: 3,
SessionStatus: SessionStatusEnd,
@@ -156,7 +155,7 @@ func TestReaderWriter(t *testing.T) {
{
var meta FrameMetadata
common.Must(meta.Unmarshal(bytesReader, false))
common.Must(meta.Unmarshal(bytesReader))
if r := cmp.Diff(meta, FrameMetadata{
SessionID: 2,
SessionStatus: SessionStatusKeep,
@@ -174,7 +173,7 @@ func TestReaderWriter(t *testing.T) {
{
var meta FrameMetadata
common.Must(meta.Unmarshal(bytesReader, false))
common.Must(meta.Unmarshal(bytesReader))
if r := cmp.Diff(meta, FrameMetadata{
SessionID: 2,
SessionStatus: SessionStatusEnd,
@@ -188,7 +187,7 @@ func TestReaderWriter(t *testing.T) {
{
var meta FrameMetadata
err := meta.Unmarshal(bytesReader, false)
err := meta.Unmarshal(bytesReader)
if err == nil {
t.Error("nil error")
}

View File

@@ -3,9 +3,7 @@ package mux
import (
"context"
"io"
"time"
"github.com/xtls/xray-core/app/dispatcher"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
@@ -13,7 +11,6 @@ import (
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal/done"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport"
@@ -64,16 +61,8 @@ func (s *Server) DispatchLink(ctx context.Context, dest net.Destination, link *t
if dest.Address != muxCoolAddress {
return s.dispatcher.DispatchLink(ctx, dest, link)
}
link = s.dispatcher.(*dispatcher.DefaultDispatcher).WrapLink(ctx, link)
worker, err := NewServerWorker(ctx, s.dispatcher, link)
if err != nil {
return err
}
select {
case <-ctx.Done():
case <-worker.done.Wait():
}
return nil
_, err := NewServerWorker(ctx, s.dispatcher, link)
return err
}
// Start implements common.Runnable.
@@ -90,8 +79,6 @@ type ServerWorker struct {
dispatcher routing.Dispatcher
link *transport.Link
sessionManager *SessionManager
done *done.Instance
timer *time.Ticker
}
func NewServerWorker(ctx context.Context, d routing.Dispatcher, link *transport.Link) (*ServerWorker, error) {
@@ -99,14 +86,8 @@ func NewServerWorker(ctx context.Context, d routing.Dispatcher, link *transport.
dispatcher: d,
link: link,
sessionManager: NewSessionManager(),
done: done.New(),
timer: time.NewTicker(60 * time.Second),
}
if inbound := session.InboundFromContext(ctx); inbound != nil {
inbound.CanSpliceCopy = 3
}
go worker.run(ctx)
go worker.monitor()
return worker, nil
}
@@ -121,40 +102,12 @@ func handle(ctx context.Context, s *Session, output buf.Writer) {
s.Close(false)
}
func (w *ServerWorker) monitor() {
defer w.timer.Stop()
for {
checkSize := w.sessionManager.Size()
checkCount := w.sessionManager.Count()
select {
case <-w.done.Wait():
w.sessionManager.Close()
common.Interrupt(w.link.Writer)
common.Interrupt(w.link.Reader)
return
case <-w.timer.C:
if w.sessionManager.CloseIfNoSessionAndIdle(checkSize, checkCount) {
common.Must(w.done.Close())
}
}
}
}
func (w *ServerWorker) ActiveConnections() uint32 {
return uint32(w.sessionManager.Size())
}
func (w *ServerWorker) Closed() bool {
return w.done.Done()
}
func (w *ServerWorker) WaitClosed() <-chan struct{} {
return w.done.Wait()
}
func (w *ServerWorker) Close() error {
return w.done.Close()
return w.sessionManager.Closed()
}
func (w *ServerWorker) handleStatusKeepAlive(meta *FrameMetadata, reader *buf.BufferedReader) error {
@@ -165,15 +118,9 @@ func (w *ServerWorker) handleStatusKeepAlive(meta *FrameMetadata, reader *buf.Bu
}
func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, reader *buf.BufferedReader) error {
ctx = session.SubContextFromMuxInbound(ctx)
if meta.Inbound != nil && meta.Inbound.Source.IsValid() && meta.Inbound.Local.IsValid() {
if inbound := session.InboundFromContext(ctx); inbound != nil {
newInbound := *inbound
newInbound.Source = meta.Inbound.Source
newInbound.Local = meta.Inbound.Local
ctx = session.ContextWithInbound(ctx, &newInbound)
}
}
// deep-clone outbounds because it is going to be mutated concurrently
// (Target and OriginalTarget)
ctx = session.ContextCloneOutboundsAndContent(ctx)
errors.LogInfo(ctx, "received request for ", meta.Target)
{
msg := &log.AccessMessage{
@@ -337,7 +284,7 @@ func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.Buffered
func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedReader) error {
var meta FrameMetadata
err := meta.Unmarshal(reader, session.IsReverseMuxFromContext(ctx))
err := meta.Unmarshal(reader)
if err != nil {
return errors.New("failed to read metadata").Base(err)
}
@@ -348,7 +295,7 @@ func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedRead
case SessionStatusEnd:
err = w.handleStatusEnd(&meta, reader)
case SessionStatusNew:
err = w.handleStatusNew(session.ContextWithIsReverseMux(ctx, false), &meta, reader)
err = w.handleStatusNew(ctx, &meta, reader)
case SessionStatusKeep:
err = w.handleStatusKeep(&meta, reader)
default:
@@ -363,12 +310,12 @@ func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedRead
}
func (w *ServerWorker) run(ctx context.Context) {
defer func() {
common.Must(w.done.Close())
}()
reader := &buf.BufferedReader{Reader: w.link.Reader}
defer w.sessionManager.Close()
defer common.Close(w.link.Writer)
defer common.Interrupt(w.link.Reader)
for {
select {
case <-ctx.Done():

View File

@@ -12,7 +12,6 @@ import (
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/signal/done"
"github.com/xtls/xray-core/transport/pipe"
)
@@ -54,7 +53,7 @@ func (m *SessionManager) Count() int {
func (m *SessionManager) Allocate(Strategy *ClientStrategy) *Session {
m.Lock()
defer m.Unlock()
MaxConcurrency := int(Strategy.MaxConcurrency)
MaxConnection := uint16(Strategy.MaxConnection)
@@ -66,7 +65,6 @@ func (m *SessionManager) Allocate(Strategy *ClientStrategy) *Session {
s := &Session{
ID: m.count,
parent: m,
done: done.New(),
}
m.sessions[s.ID] = s
return s
@@ -117,7 +115,7 @@ func (m *SessionManager) Get(id uint16) (*Session, bool) {
return s, found
}
func (m *SessionManager) CloseIfNoSessionAndIdle(checkSize int, checkCount int) bool {
func (m *SessionManager) CloseIfNoSession() bool {
m.Lock()
defer m.Unlock()
@@ -125,13 +123,11 @@ func (m *SessionManager) CloseIfNoSessionAndIdle(checkSize int, checkCount int)
return true
}
if len(m.sessions) != 0 || checkSize != 0 || checkCount != int(m.count) {
if len(m.sessions) != 0 {
return false
}
m.closed = true
m.sessions = nil
return true
}
@@ -161,7 +157,6 @@ type Session struct {
ID uint16
transferType protocol.TransferType
closed bool
done *done.Instance
XUDP *XUDP
}
@@ -176,9 +171,6 @@ func (s *Session) Close(locked bool) error {
return nil
}
s.closed = true
if s.done != nil {
s.done.Close()
}
if s.XUDP == nil {
common.Interrupt(s.input)
common.Close(s.output)

View File

@@ -41,11 +41,11 @@ func TestSessionManagerClose(t *testing.T) {
m := NewSessionManager()
s := m.Allocate(&ClientStrategy{})
if m.CloseIfNoSessionAndIdle(m.Size(), m.Count()) {
if m.CloseIfNoSession() {
t.Error("able to close")
}
m.Remove(false, s.ID)
if !m.CloseIfNoSessionAndIdle(m.Size(), m.Count()) {
if !m.CloseIfNoSession() {
t.Error("not able to close")
}
}

View File

@@ -6,7 +6,6 @@ import (
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/session"
)
type Writer struct {
@@ -17,10 +16,9 @@ type Writer struct {
hasError bool
transferType protocol.TransferType
globalID [8]byte
inbound *session.Inbound
}
func NewWriter(id uint16, dest net.Destination, writer buf.Writer, transferType protocol.TransferType, globalID [8]byte, inbound *session.Inbound) *Writer {
func NewWriter(id uint16, dest net.Destination, writer buf.Writer, transferType protocol.TransferType, globalID [8]byte) *Writer {
return &Writer{
id: id,
dest: dest,
@@ -28,7 +26,6 @@ func NewWriter(id uint16, dest net.Destination, writer buf.Writer, transferType
followup: false,
transferType: transferType,
globalID: globalID,
inbound: inbound,
}
}
@@ -46,7 +43,6 @@ func (w *Writer) getNextFrameMeta() FrameMetadata {
SessionID: w.id,
Target: w.dest,
GlobalID: w.globalID,
Inbound: w.inbound,
}
if w.followup {

View File

@@ -5,6 +5,7 @@ import (
"github.com/xtls/xray-core/common/bitmask"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/uuid"
"golang.org/x/sys/cpu"
)
@@ -15,12 +16,11 @@ const (
RequestCommandTCP = RequestCommand(0x01)
RequestCommandUDP = RequestCommand(0x02)
RequestCommandMux = RequestCommand(0x03)
RequestCommandRvs = RequestCommand(0x04)
)
func (c RequestCommand) TransferType() TransferType {
switch c {
case RequestCommandTCP, RequestCommandMux, RequestCommandRvs:
case RequestCommandTCP, RequestCommandMux:
return TransferTypeStream
case RequestCommandUDP:
return TransferTypePacket
@@ -70,19 +70,29 @@ type ResponseHeader struct {
Command ResponseCommand
}
var (
// Keep in sync with crypto/tls/cipher_suites.go.
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ && cpu.X86.HasSSE41 && cpu.X86.HasSSSE3
hasGCMAsmARM64 = (cpu.ARM64.HasAES && cpu.ARM64.HasPMULL) || (runtime.GOOS == "darwin" && runtime.GOARCH == "arm64")
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCTR && cpu.S390X.HasGHASH
hasGCMAsmPPC64 = runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le"
type CommandSwitchAccount struct {
Host net.Address
Port net.Port
ID uuid.UUID
Level uint32
ValidMin byte
}
HasAESGCMHardwareSupport = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X || hasGCMAsmPPC64
var (
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
// Keep in sync with crypto/aes/cipher_s390x.go.
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
(cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
runtime.GOARCH == "s390x" && hasGCMAsmS390X
)
func (sc *SecurityConfig) GetSecurityType() SecurityType {
if sc == nil || sc.Type == SecurityType_AUTO {
if HasAESGCMHardwareSupport {
if hasAESGCMHardwareSupport {
return SecurityType_AES128_GCM
}
return SecurityType_CHACHA20_POLY1305

View File

@@ -0,0 +1,89 @@
package protocol
import (
"sync"
)
type ServerList struct {
sync.RWMutex
servers []*ServerSpec
}
func NewServerList() *ServerList {
return &ServerList{}
}
func (sl *ServerList) AddServer(server *ServerSpec) {
sl.Lock()
defer sl.Unlock()
sl.servers = append(sl.servers, server)
}
func (sl *ServerList) Size() uint32 {
sl.RLock()
defer sl.RUnlock()
return uint32(len(sl.servers))
}
func (sl *ServerList) GetServer(idx uint32) *ServerSpec {
sl.Lock()
defer sl.Unlock()
for {
if idx >= uint32(len(sl.servers)) {
return nil
}
server := sl.servers[idx]
if !server.IsValid() {
sl.removeServer(idx)
continue
}
return server
}
}
func (sl *ServerList) removeServer(idx uint32) {
n := len(sl.servers)
sl.servers[idx] = sl.servers[n-1]
sl.servers = sl.servers[:n-1]
}
type ServerPicker interface {
PickServer() *ServerSpec
}
type RoundRobinServerPicker struct {
sync.Mutex
serverlist *ServerList
nextIndex uint32
}
func NewRoundRobinServerPicker(serverlist *ServerList) *RoundRobinServerPicker {
return &RoundRobinServerPicker{
serverlist: serverlist,
nextIndex: 0,
}
}
func (p *RoundRobinServerPicker) PickServer() *ServerSpec {
p.Lock()
defer p.Unlock()
next := p.nextIndex
server := p.serverlist.GetServer(next)
if server == nil {
next = 0
server = p.serverlist.GetServer(0)
}
next++
if next >= p.serverlist.Size() {
next = 0
}
p.nextIndex = next
return server
}

View File

@@ -0,0 +1,71 @@
package protocol_test
import (
"testing"
"time"
"github.com/xtls/xray-core/common/net"
. "github.com/xtls/xray-core/common/protocol"
)
func TestServerList(t *testing.T) {
list := NewServerList()
list.AddServer(NewServerSpec(net.TCPDestination(net.LocalHostIP, net.Port(1)), AlwaysValid()))
if list.Size() != 1 {
t.Error("list size: ", list.Size())
}
list.AddServer(NewServerSpec(net.TCPDestination(net.LocalHostIP, net.Port(2)), BeforeTime(time.Now().Add(time.Second))))
if list.Size() != 2 {
t.Error("list.size: ", list.Size())
}
server := list.GetServer(1)
if server.Destination().Port != 2 {
t.Error("server: ", server.Destination())
}
time.Sleep(2 * time.Second)
server = list.GetServer(1)
if server != nil {
t.Error("server: ", server)
}
server = list.GetServer(0)
if server.Destination().Port != 1 {
t.Error("server: ", server.Destination())
}
}
func TestServerPicker(t *testing.T) {
list := NewServerList()
list.AddServer(NewServerSpec(net.TCPDestination(net.LocalHostIP, net.Port(1)), AlwaysValid()))
list.AddServer(NewServerSpec(net.TCPDestination(net.LocalHostIP, net.Port(2)), BeforeTime(time.Now().Add(time.Second))))
list.AddServer(NewServerSpec(net.TCPDestination(net.LocalHostIP, net.Port(3)), BeforeTime(time.Now().Add(time.Second))))
picker := NewRoundRobinServerPicker(list)
server := picker.PickServer()
if server.Destination().Port != 1 {
t.Error("server: ", server.Destination())
}
server = picker.PickServer()
if server.Destination().Port != 2 {
t.Error("server: ", server.Destination())
}
server = picker.PickServer()
if server.Destination().Port != 3 {
t.Error("server: ", server.Destination())
}
server = picker.PickServer()
if server.Destination().Port != 1 {
t.Error("server: ", server.Destination())
}
time.Sleep(2 * time.Second)
server = picker.PickServer()
if server.Destination().Port != 1 {
t.Error("server: ", server.Destination())
}
server = picker.PickServer()
if server.Destination().Port != 1 {
t.Error("server: ", server.Destination())
}
}

View File

@@ -1,30 +1,122 @@
package protocol
import (
"sync"
"time"
"github.com/xtls/xray-core/common/dice"
"github.com/xtls/xray-core/common/net"
)
type ServerSpec struct {
Destination net.Destination
User *MemoryUser
type ValidationStrategy interface {
IsValid() bool
Invalidate()
}
func NewServerSpec(dest net.Destination, user *MemoryUser) *ServerSpec {
type alwaysValidStrategy struct{}
func AlwaysValid() ValidationStrategy {
return alwaysValidStrategy{}
}
func (alwaysValidStrategy) IsValid() bool {
return true
}
func (alwaysValidStrategy) Invalidate() {}
type timeoutValidStrategy struct {
until time.Time
}
func BeforeTime(t time.Time) ValidationStrategy {
return &timeoutValidStrategy{
until: t,
}
}
func (s *timeoutValidStrategy) IsValid() bool {
return s.until.After(time.Now())
}
func (s *timeoutValidStrategy) Invalidate() {
s.until = time.Time{}
}
type ServerSpec struct {
sync.RWMutex
dest net.Destination
users []*MemoryUser
valid ValidationStrategy
}
func NewServerSpec(dest net.Destination, valid ValidationStrategy, users ...*MemoryUser) *ServerSpec {
return &ServerSpec{
Destination: dest,
User: user,
dest: dest,
users: users,
valid: valid,
}
}
func NewServerSpecFromPB(spec *ServerEndpoint) (*ServerSpec, error) {
dest := net.TCPDestination(spec.Address.AsAddress(), net.Port(spec.Port))
var dUser *MemoryUser
if spec.User != nil {
user, err := spec.User.ToMemoryUser()
mUsers := make([]*MemoryUser, len(spec.User))
for idx, u := range spec.User {
mUser, err := u.ToMemoryUser()
if err != nil {
return nil, err
}
dUser = user
mUsers[idx] = mUser
}
return NewServerSpec(dest, dUser), nil
return NewServerSpec(dest, AlwaysValid(), mUsers...), nil
}
func (s *ServerSpec) Destination() net.Destination {
return s.dest
}
func (s *ServerSpec) HasUser(user *MemoryUser) bool {
s.RLock()
defer s.RUnlock()
for _, u := range s.users {
if u.Account.Equals(user.Account) {
return true
}
}
return false
}
func (s *ServerSpec) AddUser(user *MemoryUser) {
if s.HasUser(user) {
return
}
s.Lock()
defer s.Unlock()
s.users = append(s.users, user)
}
func (s *ServerSpec) PickUser() *MemoryUser {
s.RLock()
defer s.RUnlock()
userCount := len(s.users)
switch userCount {
case 0:
return nil
case 1:
return s.users[0]
default:
return s.users[dice.Roll(userCount)]
}
}
func (s *ServerSpec) IsValid() bool {
return s.valid.IsValid()
}
func (s *ServerSpec) Invalidate() {
s.valid.Invalidate()
}

View File

@@ -28,7 +28,7 @@ type ServerEndpoint struct {
Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"`
User *User `protobuf:"bytes,3,opt,name=user,proto3" json:"user,omitempty"`
User []*User `protobuf:"bytes,3,rep,name=user,proto3" json:"user,omitempty"`
}
func (x *ServerEndpoint) Reset() {
@@ -75,7 +75,7 @@ func (x *ServerEndpoint) GetPort() uint32 {
return 0
}
func (x *ServerEndpoint) GetUser() *User {
func (x *ServerEndpoint) GetUser() []*User {
if x != nil {
return x.User
}
@@ -98,7 +98,7 @@ var file_common_protocol_server_spec_proto_rawDesc = []byte{
0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72,
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x2e, 0x0a,
0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72,
0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x03, 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, 0x04, 0x75, 0x73, 0x65, 0x72, 0x42, 0x5e, 0x0a,
0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,

View File

@@ -12,5 +12,5 @@ import "common/protocol/user.proto";
message ServerEndpoint {
xray.common.net.IPOrDomain address = 1;
uint32 port = 2;
xray.common.protocol.User user = 3;
repeated xray.common.protocol.User user = 3;
}

View File

@@ -0,0 +1,79 @@
package protocol_test
import (
"strings"
"testing"
"time"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/net"
. "github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/uuid"
"github.com/xtls/xray-core/proxy/vmess"
)
func TestAlwaysValidStrategy(t *testing.T) {
strategy := AlwaysValid()
if !strategy.IsValid() {
t.Error("strategy not valid")
}
strategy.Invalidate()
if !strategy.IsValid() {
t.Error("strategy not valid")
}
}
func TestTimeoutValidStrategy(t *testing.T) {
strategy := BeforeTime(time.Now().Add(2 * time.Second))
if !strategy.IsValid() {
t.Error("strategy not valid")
}
time.Sleep(3 * time.Second)
if strategy.IsValid() {
t.Error("strategy is valid")
}
strategy = BeforeTime(time.Now().Add(2 * time.Second))
strategy.Invalidate()
if strategy.IsValid() {
t.Error("strategy is valid")
}
}
func TestUserInServerSpec(t *testing.T) {
uuid1 := uuid.New()
uuid2 := uuid.New()
toAccount := func(a *vmess.Account) Account {
account, err := a.AsAccount()
common.Must(err)
return account
}
spec := NewServerSpec(net.Destination{}, AlwaysValid(), &MemoryUser{
Email: "test1@example.com",
Account: toAccount(&vmess.Account{Id: uuid1.String()}),
})
if spec.HasUser(&MemoryUser{
Email: "test1@example.com",
Account: toAccount(&vmess.Account{Id: uuid2.String()}),
}) {
t.Error("has user: ", uuid2)
}
spec.AddUser(&MemoryUser{Email: "test2@example.com"})
if !spec.HasUser(&MemoryUser{
Email: "test1@example.com",
Account: toAccount(&vmess.Account{Id: uuid1.String()}),
}) {
t.Error("not having user: ", uuid1)
}
}
func TestPickUser(t *testing.T) {
spec := NewServerSpec(net.Destination{}, AlwaysValid(), &MemoryUser{Email: "test1@example.com"}, &MemoryUser{Email: "test2@example.com"}, &MemoryUser{Email: "test3@example.com"})
user := spec.PickUser()
if !strings.HasSuffix(user.Email, "@example.com") {
t.Error("user: ", user.Email)
}
}

View File

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

View File

@@ -6,7 +6,6 @@ import (
"github.com/xtls/xray-core/common/ctx"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/features/routing"
)
@@ -17,15 +16,15 @@ const (
inboundSessionKey ctx.SessionKey = 1
outboundSessionKey ctx.SessionKey = 2
contentSessionKey ctx.SessionKey = 3
isReverseMuxKey ctx.SessionKey = 4 // is reverse mux
sockoptSessionKey ctx.SessionKey = 5 // used by dokodemo to only receive sockopt.Mark
trackedConnectionErrorKey ctx.SessionKey = 6 // used by observer to get outbound error
dispatcherKey ctx.SessionKey = 7 // used by ss2022 inbounds to get dispatcher
timeoutOnlyKey ctx.SessionKey = 8 // mux context's child contexts to only cancel when its own traffic times out
allowedNetworkKey ctx.SessionKey = 9 // muxcool server control incoming request tcp/udp
fullHandlerKey ctx.SessionKey = 10 // outbound gets full handler
mitmAlpn11Key ctx.SessionKey = 11 // used by TLS dialer
mitmServerNameKey ctx.SessionKey = 12 // used by TLS dialer
muxPreferredSessionKey ctx.SessionKey = 4
sockoptSessionKey ctx.SessionKey = 5
trackedConnectionErrorKey ctx.SessionKey = 6
dispatcherKey ctx.SessionKey = 7
timeoutOnlyKey ctx.SessionKey = 8
allowedNetworkKey ctx.SessionKey = 9
handlerSessionKey ctx.SessionKey = 10
mitmAlpn11Key ctx.SessionKey = 11
mitmServerNameKey ctx.SessionKey = 12
)
func ContextWithInbound(ctx context.Context, inbound *Inbound) context.Context {
@@ -43,8 +42,18 @@ func ContextWithOutbounds(ctx context.Context, outbounds []*Outbound) context.Co
return context.WithValue(ctx, outboundSessionKey, outbounds)
}
func SubContextFromMuxInbound(ctx context.Context) context.Context {
newOutbounds := []*Outbound{{}}
func ContextCloneOutboundsAndContent(ctx context.Context) context.Context {
outbounds := OutboundsFromContext(ctx)
newOutbounds := make([]*Outbound, len(outbounds))
for i, ob := range outbounds {
if ob == nil {
continue
}
// copy outbound by value
v := *ob
newOutbounds[i] = &v
}
content := ContentFromContext(ctx)
newContent := Content{}
@@ -75,21 +84,25 @@ func ContentFromContext(ctx context.Context) *Content {
return nil
}
func ContextWithIsReverseMux(ctx context.Context, isReverseMux bool) context.Context {
return context.WithValue(ctx, isReverseMuxKey, isReverseMux)
// ContextWithMuxPreferred returns a new context with the given bool
func ContextWithMuxPreferred(ctx context.Context, forced bool) context.Context {
return context.WithValue(ctx, muxPreferredSessionKey, forced)
}
func IsReverseMuxFromContext(ctx context.Context) bool {
if val, ok := ctx.Value(isReverseMuxKey).(bool); ok {
// MuxPreferredFromContext returns value in this context, or false if not contained.
func MuxPreferredFromContext(ctx context.Context) bool {
if val, ok := ctx.Value(muxPreferredSessionKey).(bool); ok {
return val
}
return false
}
// ContextWithSockopt returns a new context with Socket configs included
func ContextWithSockopt(ctx context.Context, s *Sockopt) context.Context {
return context.WithValue(ctx, sockoptSessionKey, s)
}
// SockoptFromContext returns Socket configs in this context, or nil if not contained.
func SockoptFromContext(ctx context.Context) *Sockopt {
if sockopt, ok := ctx.Value(sockoptSessionKey).(*Sockopt); ok {
return sockopt
@@ -160,17 +173,6 @@ func AllowedNetworkFromContext(ctx context.Context) net.Network {
return net.Network_Unknown
}
func ContextWithFullHandler(ctx context.Context, handler outbound.Handler) context.Context {
return context.WithValue(ctx, fullHandlerKey, handler)
}
func FullHandlerFromContext(ctx context.Context) outbound.Handler {
if val, ok := ctx.Value(fullHandlerKey).(outbound.Handler); ok {
return val
}
return nil
}
func ContextWithMitmAlpn11(ctx context.Context, alpn11 bool) context.Context {
return context.WithValue(ctx, mitmAlpn11Key, alpn11)
}

View File

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

View File

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

View File

@@ -4,10 +4,8 @@ import (
"context"
"io"
"net"
"time"
"github.com/sagernet/sing/common/bufio"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/transport"
)
@@ -35,26 +33,8 @@ func (w *PipeConnWrapper) Close() error {
return nil
}
// This Read implemented a timeout to avoid goroutine leak.
// as a temporarily solution
func (w *PipeConnWrapper) Read(b []byte) (n int, err error) {
type readResult struct {
n int
err error
}
c := make(chan readResult, 1)
go func() {
n, err := w.R.Read(b)
c <- readResult{n: n, err: err}
}()
select {
case result := <-c:
return result.n, result.err
case <-time.After(300 * time.Second):
common.Close(w.R)
common.Interrupt(w.R)
return 0, buf.ErrReadTimeout
}
return w.R.Read(b)
}
func (w *PipeConnWrapper) Write(p []byte) (n int, err error) {

View File

@@ -15,8 +15,8 @@ type TypedSyncMap[K, V any] struct {
// K is key type, V is value type
// It is recommended to use pointer types for V because sync.Map might return nil
// If sync.Map methods really returned nil, it will return the zero value of the type V
func NewTypedSyncMap[K any, V any]() *TypedSyncMap[K, V] {
return &TypedSyncMap[K, V]{
func NewTypedSyncMap[K any, V any]() TypedSyncMap[K, V] {
return TypedSyncMap[K, V]{
syncMap: &sync.Map{},
}
}

View File

@@ -18,8 +18,8 @@ import (
var (
Version_x byte = 25
Version_y byte = 10
Version_z byte = 15
Version_y byte = 7
Version_z byte = 26
)
var (

View File

@@ -63,13 +63,17 @@ func TestXrayClose(t *testing.T) {
Outbound: []*OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&outbound.Config{
Receiver: &protocol.ServerEndpoint{
Address: net.NewIPOrDomain(net.LocalHostIP),
Port: uint32(0),
User: &protocol.User{
Account: serial.ToTypedMessage(&vmess.Account{
Id: userID.String(),
}),
Receiver: []*protocol.ServerEndpoint{
{
Address: net.NewIPOrDomain(net.LocalHostIP),
Port: uint32(0),
User: []*protocol.User{
{
Account: serial.ToTypedMessage(&vmess.Account{
Id: userID.String(),
}),
},
},
},
},
}),

View File

@@ -68,6 +68,5 @@ func RCodeFromError(err error) uint16 {
if r, ok := cause.(RCodeError); ok {
return uint16(r)
}
// unknown error
return 2
return 0
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/features"
)
@@ -19,6 +20,9 @@ type Handler interface {
ReceiverSettings() *serial.TypedMessage
// Returns the active proxy settings.
ProxySettings() *serial.TypedMessage
// Deprecated: Do not use in new code.
GetRandomInboundProxy() (interface{}, net.Port, int)
}
// Manager is a feature that manages InboundHandlers.

View File

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

View File

@@ -12,19 +12,14 @@ import (
// ResolvableContext is an implementation of routing.Context, with domain resolving capability.
type ResolvableContext struct {
routing.Context
dnsClient dns.Client
cacheIPs []net.IP
hasError bool
dnsClient dns.Client
resolvedIPs []net.IP
}
// GetTargetIPs overrides original routing.Context's implementation.
func (ctx *ResolvableContext) GetTargetIPs() []net.IP {
if len(ctx.cacheIPs) > 0 {
return ctx.cacheIPs
}
if ctx.hasError {
return nil
if len(ctx.resolvedIPs) > 0 {
return ctx.resolvedIPs
}
if domain := ctx.GetTargetDomain(); len(domain) != 0 {
@@ -34,18 +29,16 @@ func (ctx *ResolvableContext) GetTargetIPs() []net.IP {
FakeEnable: false,
})
if err == nil {
ctx.cacheIPs = ips
ctx.resolvedIPs = ips
return ips
}
errors.LogInfoInner(context.Background(), err, "resolve ip for ", domain)
}
if ips := ctx.Context.GetTargetIPs(); len(ips) != 0 {
ctx.cacheIPs = ips
return ips
}
ctx.hasError = true
return nil
}

View File

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

33
go.mod
View File

@@ -1,6 +1,6 @@
module github.com/xtls/xray-core
go 1.25
go 1.24
require (
github.com/cloudflare/circl v1.6.1
@@ -8,26 +8,26 @@ require (
github.com/golang/mock v1.7.0-rc.1
github.com/google/go-cmp v0.7.0
github.com/gorilla/websocket v1.5.3
github.com/miekg/dns v1.1.68
github.com/miekg/dns v1.1.67
github.com/pelletier/go-toml v1.9.5
github.com/pires/go-proxyproto v0.8.1
github.com/quic-go/quic-go v0.55.0
github.com/refraction-networking/utls v1.8.1
github.com/quic-go/quic-go v0.54.0
github.com/refraction-networking/utls v1.8.0
github.com/sagernet/sing v0.5.1
github.com/sagernet/sing-shadowsocks v0.2.7
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771
github.com/stretchr/testify v1.11.1
github.com/stretchr/testify v1.10.0
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
github.com/vishvananda/netlink v1.3.1
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535
github.com/xtls/reality v0.0.0-20250725142056-5b52a03d4fb7
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/crypto v0.43.0
golang.org/x/net v0.46.0
golang.org/x/sync v0.17.0
golang.org/x/sys v0.37.0
golang.org/x/crypto v0.40.0
golang.org/x/net v0.42.0
golang.org/x/sync v0.16.0
golang.org/x/sys v0.34.0
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
google.golang.org/grpc v1.76.0
google.golang.org/protobuf v1.36.10
google.golang.org/grpc v1.74.2
google.golang.org/protobuf v1.36.6
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5
h12.io/socks v1.0.3
lukechampine.com/blake3 v1.4.1
@@ -46,12 +46,13 @@ require (
github.com/quic-go/qpack v0.5.1 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/vishvananda/netns v0.0.5 // indirect
golang.org/x/mod v0.28.0 // indirect
golang.org/x/text v0.30.0 // indirect
go.uber.org/mock v0.5.0 // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/text v0.27.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.37.0 // indirect
golang.org/x/tools v0.34.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

86
go.sum
View File

@@ -38,8 +38,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0=
github.com/miekg/dns v1.1.67/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
@@ -51,10 +51,10 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U=
github.com/refraction-networking/utls v1.8.1 h1:yNY1kapmQU8JeM1sSw2H2asfTIwWxIkrMJI0pRUOCAo=
github.com/refraction-networking/utls v1.8.1/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
github.com/refraction-networking/utls v1.8.0 h1:L38krhiTAyj9EeiQQa2sg+hYb4qwLCqdMcpZrRfbONE=
github.com/refraction-networking/utls v1.8.0/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
@@ -67,49 +67,49 @@ github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0=
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535 h1:nwobseOLLRtdbP6z7Z2aVI97u8ZptTgD1ofovhAKmeU=
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535/go.mod h1:vbHCV/3VWUvy1oKvTxxWJRPEWSeR1sYgQHIh6u/JiZQ=
github.com/xtls/reality v0.0.0-20250725142056-5b52a03d4fb7 h1:Ript0vN+nSO33+Vj4n0mgNY5M+oOxFQJdrJ1VnwTBO0=
github.com/xtls/reality v0.0.0-20250725142056-5b52a03d4fb7/go.mod h1:XxvnCCgBee4WWE0bc4E+a7wbk8gkJ/rS0vNVNtC5qp0=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -117,21 +117,21 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -139,14 +139,12 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b h1:zPKJod4w6F1+nRGDI9ubnXYhU9NSWoFAijkHkUXeTK8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -51,65 +51,31 @@ type HTTPRemoteConfig struct {
}
type HTTPClientConfig struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Level uint32 `json:"level"`
Email string `json:"email"`
Username string `json:"user"`
Password string `json:"pass"`
Servers []*HTTPRemoteConfig `json:"servers"`
Headers map[string]string `json:"headers"`
Servers []*HTTPRemoteConfig `json:"servers"`
Headers map[string]string `json:"headers"`
}
func (v *HTTPClientConfig) Build() (proto.Message, error) {
config := new(http.ClientConfig)
if v.Address != nil {
v.Servers = []*HTTPRemoteConfig{
{
Address: v.Address,
Port: v.Port,
},
}
if len(v.Username) > 0 {
v.Servers[0].Users = []json.RawMessage{{}}
}
}
if len(v.Servers) != 1 {
return nil, errors.New(`HTTP settings: "servers" should have one and only one member. Multiple endpoints in "servers" should use multiple HTTP outbounds and routing balancer instead`)
}
for _, serverConfig := range v.Servers {
if len(serverConfig.Users) > 1 {
return nil, errors.New(`HTTP servers: "users" should have one member at most. Multiple members in "users" should use multiple HTTP outbounds and routing balancer instead`)
}
config.Server = make([]*protocol.ServerEndpoint, len(v.Servers))
for idx, serverConfig := range v.Servers {
server := &protocol.ServerEndpoint{
Address: serverConfig.Address.Build(),
Port: uint32(serverConfig.Port),
}
for _, rawUser := range serverConfig.Users {
user := new(protocol.User)
if v.Address != nil {
user.Level = v.Level
user.Email = v.Email
} else {
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, errors.New("failed to parse HTTP user").Base(err).AtError()
}
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, errors.New("failed to parse HTTP user").Base(err).AtError()
}
account := new(HTTPAccount)
if v.Address != nil {
account.Username = v.Username
account.Password = v.Password
} else {
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New("failed to parse HTTP account").Base(err).AtError()
}
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New("failed to parse HTTP account").Base(err).AtError()
}
user.Account = serial.ToTypedMessage(account.Build())
server.User = user
break
server.User = append(server.User, user)
}
config.Server = server
break
config.Server[idx] = server
}
config.Header = make([]*http.Header, 0, 32)
for key, value := range v.Headers {

View File

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

View File

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

View File

@@ -162,46 +162,22 @@ func buildShadowsocks2022(v *ShadowsocksServerConfig) (proto.Message, error) {
type ShadowsocksServerTarget struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Level byte `json:"level"`
Email string `json:"email"`
Cipher string `json:"method"`
Password string `json:"password"`
Email string `json:"email"`
Level byte `json:"level"`
IVCheck bool `json:"ivCheck"`
UoT bool `json:"uot"`
UoTVersion int `json:"uotVersion"`
}
type ShadowsocksClientConfig struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Level byte `json:"level"`
Email string `json:"email"`
Cipher string `json:"method"`
Password string `json:"password"`
IVCheck bool `json:"ivCheck"`
UoT bool `json:"uot"`
UoTVersion int `json:"uotVersion"`
Servers []*ShadowsocksServerTarget `json:"servers"`
Servers []*ShadowsocksServerTarget `json:"servers"`
}
func (v *ShadowsocksClientConfig) Build() (proto.Message, error) {
if v.Address != nil {
v.Servers = []*ShadowsocksServerTarget{
{
Address: v.Address,
Port: v.Port,
Level: v.Level,
Email: v.Email,
Cipher: v.Cipher,
Password: v.Password,
IVCheck: v.IVCheck,
UoT: v.UoT,
UoTVersion: v.UoTVersion,
},
}
}
if len(v.Servers) != 1 {
return nil, errors.New(`Shadowsocks settings: "servers" should have one and only one member. Multiple endpoints in "servers" should use multiple Shadowsocks outbounds and routing balancer instead`)
if len(v.Servers) == 0 {
return nil, errors.New("0 Shadowsocks server configured.")
}
if len(v.Servers) == 1 {
@@ -229,7 +205,8 @@ func (v *ShadowsocksClientConfig) Build() (proto.Message, error) {
}
config := new(shadowsocks.ClientConfig)
for _, server := range v.Servers {
serverSpecs := make([]*protocol.ServerEndpoint, len(v.Servers))
for idx, server := range v.Servers {
if C.Contains(shadowaead_2022.List, server.Cipher) {
return nil, errors.New("Shadowsocks 2022 accept no multi servers")
}
@@ -255,16 +232,19 @@ func (v *ShadowsocksClientConfig) Build() (proto.Message, error) {
ss := &protocol.ServerEndpoint{
Address: server.Address.Build(),
Port: uint32(server.Port),
User: &protocol.User{
Level: uint32(server.Level),
Email: server.Email,
Account: serial.ToTypedMessage(account),
User: []*protocol.User{
{
Level: uint32(server.Level),
Email: server.Email,
Account: serial.ToTypedMessage(account),
},
},
}
config.Server = ss
break
serverSpecs[idx] = ss
}
config.Server = serverSpecs
return config, nil
}

View File

@@ -70,64 +70,30 @@ type SocksRemoteConfig struct {
}
type SocksClientConfig struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Level uint32 `json:"level"`
Email string `json:"email"`
Username string `json:"user"`
Password string `json:"pass"`
Servers []*SocksRemoteConfig `json:"servers"`
Servers []*SocksRemoteConfig `json:"servers"`
}
func (v *SocksClientConfig) Build() (proto.Message, error) {
config := new(socks.ClientConfig)
if v.Address != nil {
v.Servers = []*SocksRemoteConfig{
{
Address: v.Address,
Port: v.Port,
},
}
if len(v.Username) > 0 {
v.Servers[0].Users = []json.RawMessage{{}}
}
}
if len(v.Servers) != 1 {
return nil, errors.New(`SOCKS settings: "servers" should have one and only one member. Multiple endpoints in "servers" should use multiple SOCKS outbounds and routing balancer instead`)
}
for _, serverConfig := range v.Servers {
if len(serverConfig.Users) > 1 {
return nil, errors.New(`SOCKS servers: "users" should have one member at most. Multiple members in "users" should use multiple SOCKS outbounds and routing balancer instead`)
}
config.Server = make([]*protocol.ServerEndpoint, len(v.Servers))
for idx, serverConfig := range v.Servers {
server := &protocol.ServerEndpoint{
Address: serverConfig.Address.Build(),
Port: uint32(serverConfig.Port),
}
for _, rawUser := range serverConfig.Users {
user := new(protocol.User)
if v.Address != nil {
user.Level = v.Level
user.Email = v.Email
} else {
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, errors.New("failed to parse Socks user").Base(err).AtError()
}
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, errors.New("failed to parse Socks user").Base(err).AtError()
}
account := new(SocksAccount)
if v.Address != nil {
account.Username = v.Username
account.Password = v.Password
} else {
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New("failed to parse socks account").Base(err).AtError()
}
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New("failed to parse socks account").Base(err).AtError()
}
user.Account = serial.ToTypedMessage(account.Build())
server.User = user
break
server.User = append(server.User, user)
}
config.Server = server
break
config.Server[idx] = server
}
return config, nil
}

View File

@@ -65,47 +65,24 @@ func TestSocksOutboundConfig(t *testing.T) {
}`,
Parser: loadJSON(creator),
Output: &socks.ClientConfig{
Server: &protocol.ServerEndpoint{
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Ip{
Ip: []byte{127, 0, 0, 1},
Server: []*protocol.ServerEndpoint{
{
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Ip{
Ip: []byte{127, 0, 0, 1},
},
},
},
Port: 1234,
User: &protocol.User{
Email: "test@email.com",
Account: serial.ToTypedMessage(&socks.Account{
Username: "test user",
Password: "test pass",
}),
},
},
},
},
{
Input: `{
"address": "127.0.0.1",
"port": 1234,
"user": "test user",
"pass": "test pass",
"email": "test@email.com"
}`,
Parser: loadJSON(creator),
Output: &socks.ClientConfig{
Server: &protocol.ServerEndpoint{
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Ip{
Ip: []byte{127, 0, 0, 1},
Port: 1234,
User: []*protocol.User{
{
Email: "test@email.com",
Account: serial.ToTypedMessage(&socks.Account{
Username: "test user",
Password: "test pass",
}),
},
},
},
Port: 1234,
User: &protocol.User{
Email: "test@email.com",
Account: serial.ToTypedMessage(&socks.Account{
Username: "test user",
Password: "test pass",
}),
},
},
},
},

View File

@@ -289,8 +289,8 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
return nil, errors.New("maxConnections cannot be specified together with maxConcurrency")
}
if c.Xmux == (XmuxConfig{}) {
c.Xmux.MaxConcurrency.From = 1
c.Xmux.MaxConcurrency.To = 1
c.Xmux.MaxConcurrency.From = 16
c.Xmux.MaxConcurrency.To = 32
c.Xmux.HMaxRequestTimes.From = 600
c.Xmux.HMaxRequestTimes.To = 900
c.Xmux.HMaxReusableSecs.From = 1800
@@ -412,10 +412,8 @@ type TLSConfig struct {
MasterKeyLog string `json:"masterKeyLog"`
ServerNameToVerify string `json:"serverNameToVerify"`
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
ECHServerKeys string `json:"echServerKeys"`
ECHConfigList string `json:"echConfigList"`
ECHForceQuery string `json:"echForceQuery"`
ECHSocketSettings *SocketConfig `json:"echSockopt"`
ECHServerKeys string `json:"echServerKeys"`
}
// Build implements Buildable.
@@ -439,7 +437,7 @@ func (c *TLSConfig) Build() (proto.Message, error) {
}
if len(config.NextProtocol) > 1 {
for _, p := range config.NextProtocol {
if tls.IsFromMitm(p) {
if tcp.IsFromMitm(p) {
return nil, errors.New(`only one element is allowed in "alpn" when using "fromMitm" in it`)
}
}
@@ -487,6 +485,8 @@ func (c *TLSConfig) Build() (proto.Message, error) {
}
config.VerifyPeerCertInNames = c.VerifyPeerCertInNames
config.EchConfigList = c.ECHConfigList
if c.ECHServerKeys != "" {
EchPrivateKey, err := base64.StdEncoding.DecodeString(c.ECHServerKeys)
if err != nil {
@@ -494,21 +494,6 @@ func (c *TLSConfig) Build() (proto.Message, error) {
}
config.EchServerKeys = EchPrivateKey
}
switch c.ECHForceQuery {
case "none", "half", "full", "":
config.EchForceQuery = c.ECHForceQuery
default:
return nil, errors.New(`invalid "echForceQuery": `, c.ECHForceQuery)
}
config.EchForceQuery = c.ECHForceQuery
config.EchConfigList = c.ECHConfigList
if c.ECHSocketSettings != nil {
ss, err := c.ECHSocketSettings.Build()
if err != nil {
return nil, errors.New("Failed to build ech sockopt.").Base(err)
}
config.EchSocketSettings = ss
}
return config, nil
}

View File

@@ -20,44 +20,28 @@ import (
type TrojanServerTarget struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Level byte `json:"level"`
Email string `json:"email"`
Password string `json:"password"`
Email string `json:"email"`
Level byte `json:"level"`
Flow string `json:"flow"`
}
// TrojanClientConfig is configuration of trojan servers
type TrojanClientConfig struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Level byte `json:"level"`
Email string `json:"email"`
Password string `json:"password"`
Flow string `json:"flow"`
Servers []*TrojanServerTarget `json:"servers"`
Servers []*TrojanServerTarget `json:"servers"`
}
// Build implements Buildable
func (c *TrojanClientConfig) Build() (proto.Message, error) {
if c.Address != nil {
c.Servers = []*TrojanServerTarget{
{
Address: c.Address,
Port: c.Port,
Level: c.Level,
Email: c.Email,
Password: c.Password,
Flow: c.Flow,
},
}
}
if len(c.Servers) != 1 {
return nil, errors.New(`Trojan settings: "servers" should have one and only one member. Multiple endpoints in "servers" should use multiple Trojan outbounds and routing balancer instead`)
if len(c.Servers) == 0 {
return nil, errors.New("0 Trojan server configured.")
}
config := &trojan.ClientConfig{}
config := &trojan.ClientConfig{
Server: make([]*protocol.ServerEndpoint, len(c.Servers)),
}
for _, rec := range c.Servers {
for idx, rec := range c.Servers {
if rec.Address == nil {
return nil, errors.New("Trojan server address is not set.")
}
@@ -71,19 +55,19 @@ func (c *TrojanClientConfig) Build() (proto.Message, error) {
return nil, errors.PrintRemovedFeatureError(`Flow for Trojan`, ``)
}
config.Server = &protocol.ServerEndpoint{
config.Server[idx] = &protocol.ServerEndpoint{
Address: rec.Address.Build(),
Port: uint32(rec.Port),
User: &protocol.User{
Level: uint32(rec.Level),
Email: rec.Email,
Account: serial.ToTypedMessage(&trojan.Account{
Password: rec.Password,
}),
User: []*protocol.User{
{
Level: uint32(rec.Level),
Email: rec.Email,
Account: serial.ToTypedMessage(&trojan.Account{
Password: rec.Password,
}),
},
},
}
break
}
return config, nil

View File

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

View File

@@ -1,7 +1,6 @@
package conf
import (
"encoding/base64"
"encoding/json"
"path/filepath"
"runtime"
@@ -33,20 +32,12 @@ type VLessInboundConfig struct {
Clients []json.RawMessage `json:"clients"`
Decryption string `json:"decryption"`
Fallbacks []*VLessInboundFallback `json:"fallbacks"`
Flow string `json:"flow"`
}
// Build implements Buildable
func (c *VLessInboundConfig) Build() (proto.Message, error) {
config := new(inbound.Config)
config.Clients = make([]*protocol.User, len(c.Clients))
switch c.Flow {
case vless.None:
c.Flow = ""
case "", vless.XRV:
default:
return nil, errors.New(`VLESS "settings.flow" doesn't support "` + c.Flow + `" in this version`)
}
for idx, rawUser := range c.Clients {
user := new(protocol.User)
if err := json.Unmarshal(rawUser, user); err != nil {
@@ -64,81 +55,23 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
account.Id = u.String()
switch account.Flow {
case "":
account.Flow = c.Flow
case vless.None:
account.Flow = ""
case vless.XRV:
case "", vless.XRV:
default:
return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`)
}
if account.Encryption != "" {
return nil, errors.New(`VLESS clients: "encryption" should not be in inbound settings`)
}
if account.Reverse != nil && account.Reverse.Tag == "" {
return nil, errors.New(`VLESS clients: "tag" can't be empty for "reverse"`)
return nil, errors.New(`VLESS clients: "encryption" should not in inbound settings`)
}
user.Account = serial.ToTypedMessage(account)
config.Clients[idx] = user
}
if c.Decryption != "none" {
return nil, errors.New(`VLESS settings: please add/set "decryption":"none" to every settings`)
}
config.Decryption = c.Decryption
if !func() bool {
s := strings.Split(config.Decryption, ".")
if len(s) < 4 || s[0] != "mlkem768x25519plus" {
return false
}
switch s[1] {
case "native":
case "xorpub":
config.XorMode = 1
case "random":
config.XorMode = 2
default:
return false
}
t := strings.SplitN(strings.TrimSuffix(s[2], "s"), "-", 2)
i, err := strconv.Atoi(t[0])
if err != nil {
return false
}
config.SecondsFrom = int64(i)
if len(t) == 2 {
i, err := strconv.Atoi(t[1])
if err != nil {
return false
}
config.SecondsTo = int64(i)
}
padding := 0
for _, r := range s[3:] {
if len(r) < 20 {
padding += len(r) + 1
continue
}
if b, _ := base64.RawURLEncoding.DecodeString(r); len(b) != 32 && len(b) != 64 {
return false
}
}
config.Decryption = config.Decryption[27+len(s[2]):]
if padding > 0 {
config.Padding = config.Decryption[:padding-1]
config.Decryption = config.Decryption[padding:]
}
return true
}() && config.Decryption != "none" {
if config.Decryption == "" {
return nil, errors.New(`VLESS settings: please add/set "decryption":"none" to every settings`)
}
return nil, errors.New(`VLESS settings: unsupported "decryption": ` + config.Decryption)
}
if config.Decryption != "none" && c.Fallbacks != nil {
return nil, errors.New(`VLESS settings: "fallbacks" can not be used together with "decryption"`)
}
for _, fb := range c.Fallbacks {
var i uint16
@@ -203,65 +136,37 @@ type VLessOutboundVnext struct {
}
type VLessOutboundConfig struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Level uint32 `json:"level"`
Email string `json:"email"`
Id string `json:"id"`
Flow string `json:"flow"`
Seed string `json:"seed"`
Encryption string `json:"encryption"`
Reverse *vless.Reverse `json:"reverse"`
Vnext []*VLessOutboundVnext `json:"vnext"`
Vnext []*VLessOutboundVnext `json:"vnext"`
}
// Build implements Buildable
func (c *VLessOutboundConfig) Build() (proto.Message, error) {
config := new(outbound.Config)
if c.Address != nil {
c.Vnext = []*VLessOutboundVnext{
{
Address: c.Address,
Port: c.Port,
Users: []json.RawMessage{{}},
},
}
if len(c.Vnext) == 0 {
return nil, errors.New(`VLESS settings: "vnext" is empty`)
}
if len(c.Vnext) != 1 {
return nil, errors.New(`VLESS settings: "vnext" should have one and only one member. Multiple endpoints in "vnext" should use multiple VLESS outbounds and routing balancer instead`)
}
for _, rec := range c.Vnext {
config.Vnext = make([]*protocol.ServerEndpoint, len(c.Vnext))
for idx, rec := range c.Vnext {
if rec.Address == nil {
return nil, errors.New(`VLESS vnext: "address" is not set`)
}
if len(rec.Users) != 1 {
return nil, errors.New(`VLESS vnext: "users" should have one and only one member. Multiple members in "users" should use multiple VLESS outbounds and routing balancer instead`)
if len(rec.Users) == 0 {
return nil, errors.New(`VLESS vnext: "users" is empty`)
}
spec := &protocol.ServerEndpoint{
Address: rec.Address.Build(),
Port: uint32(rec.Port),
User: make([]*protocol.User, len(rec.Users)),
}
for _, rawUser := range rec.Users {
for idx, rawUser := range rec.Users {
user := new(protocol.User)
if c.Address != nil {
user.Level = c.Level
user.Email = c.Email
} else {
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, errors.New(`VLESS users: invalid user`).Base(err)
}
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, errors.New(`VLESS users: invalid user`).Base(err)
}
account := new(vless.Account)
if c.Address != nil {
account.Id = c.Id
account.Flow = c.Flow
//account.Seed = c.Seed
account.Encryption = c.Encryption
account.Reverse = c.Reverse
} else {
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New(`VLESS users: invalid user`).Base(err)
}
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New(`VLESS users: invalid user`).Base(err)
}
u, err := uuid.ParseString(account.Id)
@@ -276,60 +181,14 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
return nil, errors.New(`VLESS users: "flow" doesn't support "` + account.Flow + `" in this version`)
}
if !func() bool {
s := strings.Split(account.Encryption, ".")
if len(s) < 4 || s[0] != "mlkem768x25519plus" {
return false
}
switch s[1] {
case "native":
case "xorpub":
account.XorMode = 1
case "random":
account.XorMode = 2
default:
return false
}
switch s[2] {
case "1rtt":
case "0rtt":
account.Seconds = 1
default:
return false
}
padding := 0
for _, r := range s[3:] {
if len(r) < 20 {
padding += len(r) + 1
continue
}
if b, _ := base64.RawURLEncoding.DecodeString(r); len(b) != 32 && len(b) != 1184 {
return false
}
}
account.Encryption = account.Encryption[27+len(s[2]):]
if padding > 0 {
account.Padding = account.Encryption[:padding-1]
account.Encryption = account.Encryption[padding:]
}
return true
}() && account.Encryption != "none" {
if account.Encryption == "" {
return nil, errors.New(`VLESS users: please add/set "encryption":"none" for every user`)
}
return nil, errors.New(`VLESS users: unsupported "encryption": ` + account.Encryption)
}
if account.Reverse != nil && account.Reverse.Tag == "" {
return nil, errors.New(`VLESS clients: "tag" can't be empty for "reverse"`)
if account.Encryption != "none" {
return nil, errors.New(`VLESS users: please add/set "encryption":"none" for every user`)
}
user.Account = serial.ToTypedMessage(account)
spec.User = user
break
spec.User[idx] = user
}
config.Vnext = spec
break
config.Vnext[idx] = spec
}
return config, nil

View File

@@ -35,50 +35,25 @@ func TestVLessOutbound(t *testing.T) {
}`,
Parser: loadJSON(creator),
Output: &outbound.Config{
Vnext: &protocol.ServerEndpoint{
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Domain{
Domain: "example.com",
Vnext: []*protocol.ServerEndpoint{
{
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Domain{
Domain: "example.com",
},
},
},
Port: 443,
User: &protocol.User{
Account: serial.ToTypedMessage(&vless.Account{
Id: "27848739-7e62-4138-9fd3-098a63964b6b",
Flow: "xtls-rprx-vision-udp443",
Encryption: "none",
}),
Level: 0,
},
},
},
},
{
Input: `{
"address": "example.com",
"port": 443,
"id": "27848739-7e62-4138-9fd3-098a63964b6b",
"flow": "xtls-rprx-vision-udp443",
"encryption": "none",
"level": 0
}`,
Parser: loadJSON(creator),
Output: &outbound.Config{
Vnext: &protocol.ServerEndpoint{
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Domain{
Domain: "example.com",
Port: 443,
User: []*protocol.User{
{
Account: serial.ToTypedMessage(&vless.Account{
Id: "27848739-7e62-4138-9fd3-098a63964b6b",
Flow: "xtls-rprx-vision-udp443",
Encryption: "none",
}),
Level: 0,
},
},
},
Port: 443,
User: &protocol.User{
Account: serial.ToTypedMessage(&vless.Account{
Id: "27848739-7e62-4138-9fd3-098a63964b6b",
Flow: "xtls-rprx-vision-udp443",
Encryption: "none",
}),
Level: 0,
},
},
},
},

View File

@@ -46,6 +46,17 @@ func (a *VMessAccount) Build() *vmess.Account {
}
}
type VMessDetourConfig struct {
ToTag string `json:"to"`
}
// Build implements Buildable
func (c *VMessDetourConfig) Build() *inbound.DetourConfig {
return &inbound.DetourConfig{
To: c.ToTag,
}
}
type VMessDefaultConfig struct {
Level byte `json:"level"`
}
@@ -60,6 +71,7 @@ func (c *VMessDefaultConfig) Build() *inbound.DefaultConfig {
type VMessInboundConfig struct {
Users []json.RawMessage `json:"clients"`
Defaults *VMessDefaultConfig `json:"default"`
DetourConfig *VMessDetourConfig `json:"detour"`
}
// Build implements Buildable
@@ -70,6 +82,10 @@ func (c *VMessInboundConfig) Build() (proto.Message, error) {
config.Default = c.Defaults.Build()
}
if c.DetourConfig != nil {
config.Detour = c.DetourConfig.Build()
}
config.User = make([]*protocol.User, len(c.Users))
for idx, rawData := range c.Users {
user := new(protocol.User)
@@ -101,37 +117,23 @@ type VMessOutboundTarget struct {
}
type VMessOutboundConfig struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Level uint32 `json:"level"`
Email string `json:"email"`
ID string `json:"id"`
Security string `json:"security"`
Experiments string `json:"experiments"`
Receivers []*VMessOutboundTarget `json:"vnext"`
Receivers []*VMessOutboundTarget `json:"vnext"`
}
// Build implements Buildable
func (c *VMessOutboundConfig) Build() (proto.Message, error) {
config := new(outbound.Config)
if c.Address != nil {
c.Receivers = []*VMessOutboundTarget{
{
Address: c.Address,
Port: c.Port,
Users: []json.RawMessage{{}},
},
}
if len(c.Receivers) == 0 {
return nil, errors.New("0 VMess receiver configured")
}
if len(c.Receivers) != 1 {
return nil, errors.New(`VMess settings: "vnext" should have one and only one member. Multiple endpoints in "vnext" should use multiple VMess outbounds and routing balancer instead`)
}
for _, rec := range c.Receivers {
if len(rec.Users) != 1 {
return nil, errors.New(`VMess vnext: "users" should have one and only one member. Multiple members in "users" should use multiple VMess outbounds and routing balancer instead`)
serverSpecs := make([]*protocol.ServerEndpoint, len(c.Receivers))
for idx, rec := range c.Receivers {
if len(rec.Users) == 0 {
return nil, errors.New("0 user configured for VMess outbound")
}
if rec.Address == nil {
return nil, errors.New(`VMess vnext: "address" is not set`)
return nil, errors.New("address is not set in VMess outbound config")
}
spec := &protocol.ServerEndpoint{
Address: rec.Address.Build(),
@@ -139,23 +141,12 @@ func (c *VMessOutboundConfig) Build() (proto.Message, error) {
}
for _, rawUser := range rec.Users {
user := new(protocol.User)
if c.Address != nil {
user.Level = c.Level
user.Email = c.Email
} else {
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, errors.New("invalid VMess user").Base(err)
}
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, errors.New("invalid VMess user").Base(err)
}
account := new(VMessAccount)
if c.Address != nil {
account.ID = c.ID
account.Security = c.Security
account.Experiments = c.Experiments
} else {
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New("invalid VMess user").Base(err)
}
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New("invalid VMess user").Base(err)
}
u, err := uuid.ParseString(account.ID)
@@ -165,11 +156,10 @@ func (c *VMessOutboundConfig) Build() (proto.Message, error) {
account.ID = u.String()
user.Account = serial.ToTypedMessage(account.Build())
spec.User = user
break
spec.User = append(spec.User, user)
}
config.Receiver = spec
break
serverSpecs[idx] = spec
}
config.Receiver = serverSpecs
return config, nil
}

View File

@@ -34,52 +34,26 @@ func TestVMessOutbound(t *testing.T) {
}`,
Parser: loadJSON(creator),
Output: &outbound.Config{
Receiver: &protocol.ServerEndpoint{
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Ip{
Ip: []byte{127, 0, 0, 1},
},
},
Port: 80,
User: &protocol.User{
Email: "love@example.com",
Level: 255,
Account: serial.ToTypedMessage(&vmess.Account{
Id: "e641f5ad-9397-41e3-bf1a-e8740dfed019",
SecuritySettings: &protocol.SecurityConfig{
Type: protocol.SecurityType_AUTO,
Receiver: []*protocol.ServerEndpoint{
{
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Ip{
Ip: []byte{127, 0, 0, 1},
},
}),
},
},
},
},
{
Input: `{
"address": "127.0.0.1",
"port": 80,
"id": "e641f5ad-9397-41e3-bf1a-e8740dfed019",
"email": "love@example.com",
"level": 255
}`,
Parser: loadJSON(creator),
Output: &outbound.Config{
Receiver: &protocol.ServerEndpoint{
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Ip{
Ip: []byte{127, 0, 0, 1},
},
},
Port: 80,
User: &protocol.User{
Email: "love@example.com",
Level: 255,
Account: serial.ToTypedMessage(&vmess.Account{
Id: "e641f5ad-9397-41e3-bf1a-e8740dfed019",
SecuritySettings: &protocol.SecurityConfig{
Type: protocol.SecurityType_AUTO,
Port: 80,
User: []*protocol.User{
{
Email: "love@example.com",
Level: 255,
Account: serial.ToTypedMessage(&vmess.Account{
Id: "e641f5ad-9397-41e3-bf1a-e8740dfed019",
SecuritySettings: &protocol.SecurityConfig{
Type: protocol.SecurityType_AUTO,
},
}),
},
}),
},
},
},
},
@@ -105,7 +79,11 @@ func TestVMessInbound(t *testing.T) {
],
"default": {
"level": 0
}
},
"detour": {
"to": "tag_to_detour"
},
"disableInsecureEncryption": true
}`,
Parser: loadJSON(creator),
Output: &inbound.Config{
@@ -124,6 +102,9 @@ func TestVMessInbound(t *testing.T) {
Default: &inbound.DefaultConfig{
Level: 0,
},
Detour: &inbound.DetourConfig{
To: "tag_to_detour",
},
},
},
})

View File

@@ -3,6 +3,7 @@ package conf
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
@@ -20,7 +21,6 @@ import (
var (
inboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
"tunnel": func() interface{} { return new(DokodemoConfig) },
"dokodemo-door": func() interface{} { return new(DokodemoConfig) },
"http": func() interface{} { return new(HTTPServerConfig) },
"shadowsocks": func() interface{} { return new(ShadowsocksServerConfig) },
@@ -33,10 +33,8 @@ var (
}, "protocol", "settings")
outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
"block": func() interface{} { return new(BlackholeConfig) },
"blackhole": func() interface{} { return new(BlackholeConfig) },
"loopback": func() interface{} { return new(LoopbackConfig) },
"direct": func() interface{} { return new(FreedomConfig) },
"freedom": func() interface{} { return new(FreedomConfig) },
"http": func() interface{} { return new(HTTPClientConfig) },
"shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) },
@@ -119,12 +117,47 @@ func (m *MuxConfig) Build() (*proxyman.MultiplexingConfig, error) {
}, nil
}
type InboundDetourAllocationConfig struct {
Strategy string `json:"strategy"`
Concurrency *uint32 `json:"concurrency"`
RefreshMin *uint32 `json:"refresh"`
}
// Build implements Buildable.
func (c *InboundDetourAllocationConfig) Build() (*proxyman.AllocationStrategy, error) {
config := new(proxyman.AllocationStrategy)
switch strings.ToLower(c.Strategy) {
case "always":
config.Type = proxyman.AllocationStrategy_Always
case "random":
config.Type = proxyman.AllocationStrategy_Random
case "external":
config.Type = proxyman.AllocationStrategy_External
default:
return nil, errors.New("unknown allocation strategy: ", c.Strategy)
}
if c.Concurrency != nil {
config.Concurrency = &proxyman.AllocationStrategy_AllocationStrategyConcurrency{
Value: *c.Concurrency,
}
}
if c.RefreshMin != nil {
config.Refresh = &proxyman.AllocationStrategy_AllocationStrategyRefresh{
Value: *c.RefreshMin,
}
}
return config, nil
}
type InboundDetourConfig struct {
Protocol string `json:"protocol"`
PortList *PortList `json:"port"`
ListenOn *Address `json:"listen"`
Settings *json.RawMessage `json:"settings"`
Tag string `json:"tag"`
Allocation *InboundDetourAllocationConfig `json:"allocate"`
StreamSetting *StreamConfig `json:"streamSettings"`
SniffingConfig *SniffingConfig `json:"sniffing"`
}
@@ -161,6 +194,30 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
}
}
if c.Allocation != nil {
concurrency := -1
if c.Allocation.Concurrency != nil && c.Allocation.Strategy == "random" {
concurrency = int(*c.Allocation.Concurrency)
}
portRange := 0
for _, pr := range c.PortList.Range {
portRange += int(pr.To - pr.From + 1)
}
if concurrency >= 0 && concurrency >= portRange {
var ports strings.Builder
for _, pr := range c.PortList.Range {
fmt.Fprintf(&ports, "%d-%d ", pr.From, pr.To)
}
return nil, errors.New("not enough ports. concurrency = ", concurrency, " ports: ", ports.String())
}
as, err := c.Allocation.Build()
if err != nil {
return nil, err
}
receiverSettings.AllocationStrategy = as
}
if c.StreamSetting != nil {
ss, err := c.StreamSetting.Build()
if err != nil {
@@ -185,7 +242,7 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
return nil, errors.New("failed to load inbound detour config for protocol ", c.Protocol).Base(err)
}
if dokodemoConfig, ok := rawConfig.(*DokodemoConfig); ok {
receiverSettings.ReceiveOriginalDestination = dokodemoConfig.FollowRedirect
receiverSettings.ReceiveOriginalDestination = dokodemoConfig.Redirect
}
ts, err := rawConfig.(Buildable).Build()
if err != nil {
@@ -200,14 +257,13 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
}
type OutboundDetourConfig struct {
Protocol string `json:"protocol"`
SendThrough *string `json:"sendThrough"`
Tag string `json:"tag"`
Settings *json.RawMessage `json:"settings"`
StreamSetting *StreamConfig `json:"streamSettings"`
ProxySettings *ProxyConfig `json:"proxySettings"`
MuxSettings *MuxConfig `json:"mux"`
TargetStrategy string `json:"targetStrategy"`
Protocol string `json:"protocol"`
SendThrough *string `json:"sendThrough"`
Tag string `json:"tag"`
Settings *json.RawMessage `json:"settings"`
StreamSetting *StreamConfig `json:"streamSettings"`
ProxySettings *ProxyConfig `json:"proxySettings"`
MuxSettings *MuxConfig `json:"mux"`
}
func (c *OutboundDetourConfig) checkChainProxyConfig() error {
@@ -223,32 +279,6 @@ func (c *OutboundDetourConfig) checkChainProxyConfig() error {
// Build implements Buildable.
func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
senderSettings := &proxyman.SenderConfig{}
switch strings.ToLower(c.TargetStrategy) {
case "asis", "":
senderSettings.TargetStrategy = internet.DomainStrategy_AS_IS
case "useip":
senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP
case "useipv4":
senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP4
case "useipv6":
senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP6
case "useipv4v6":
senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP46
case "useipv6v4":
senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP64
case "forceip":
senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP
case "forceipv4":
senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP4
case "forceipv6":
senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP6
case "forceipv4v6":
senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP46
case "forceipv6v4":
senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP64
default:
return nil, errors.New("unsupported target domain strategy: ", c.TargetStrategy)
}
if err := c.checkChainProxyConfig(); err != nil {
return nil, err
}
@@ -350,7 +380,6 @@ type Config struct {
FakeDNS *FakeDNSConfig `json:"fakeDns"`
Observatory *ObservatoryConfig `json:"observatory"`
BurstObservatory *BurstObservatoryConfig `json:"burstObservatory"`
Version *VersionConfig `json:"version"`
}
func (c *Config) findInboundTag(tag string) int {
@@ -419,10 +448,6 @@ func (c *Config) Override(o *Config, fn string) {
c.BurstObservatory = o.BurstObservatory
}
if o.Version != nil {
c.Version = o.Version
}
// update the Inbound in slice if the only one in override config has same tag
if len(o.InboundConfigs) > 0 {
for i := range o.InboundConfigs {
@@ -563,14 +588,6 @@ func (c *Config) Build() (*core.Config, error) {
config.App = append(config.App, serial.ToTypedMessage(r))
}
if c.Version != nil {
r, err := c.Version.Build()
if err != nil {
return nil, errors.New("failed to build version configuration").Base(err)
}
config.App = append(config.App, serial.ToTypedMessage(r))
}
var inbounds []InboundDetourConfig
if len(c.InboundConfigs) > 0 {

View File

@@ -58,6 +58,10 @@ func TestXrayConfig(t *testing.T) {
},
"protocol": "vmess",
"port": "443-500",
"allocate": {
"strategy": "random",
"concurrency": 3
},
"settings": {
"clients": [
{
@@ -73,6 +77,7 @@ func TestXrayConfig(t *testing.T) {
"ip": [
"10.0.0.0/8"
],
"type": "field",
"outboundTag": "blocked"
}
]
@@ -119,6 +124,12 @@ func TestXrayConfig(t *testing.T) {
From: 443,
To: 500,
}}},
AllocationStrategy: &proxyman.AllocationStrategy{
Type: proxyman.AllocationStrategy_Random,
Concurrency: &proxyman.AllocationStrategy_AllocationStrategyConcurrency{
Value: 3,
},
},
StreamSettings: &internet.StreamConfig{
ProtocolName: "websocket",
TransportSettings: []*internet.TransportConfig{

View File

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

View File

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

View File

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

View File

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

View File

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

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