mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-12-18 13:14:36 +03:00
Compare commits
99 Commits
v25.8.29
...
optimize-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5f17ab4fc | ||
|
|
a610a4c89a | ||
|
|
b451f8929d | ||
|
|
81f8f398c7 | ||
|
|
aea123842b | ||
|
|
bd7503d506 | ||
|
|
903214a0f0 | ||
|
|
e403abe360 | ||
|
|
c123f163c2 | ||
|
|
93312d29e5 | ||
|
|
36cb0f00bd | ||
|
|
cadcb47074 | ||
|
|
c6afcd5fb6 | ||
|
|
ed5f7e7af5 | ||
|
|
9d3401b6f0 | ||
|
|
d60ef656cc | ||
|
|
a83253f3d7 | ||
|
|
2969a189e6 | ||
|
|
d41840132a | ||
|
|
f14fd1cbee | ||
|
|
cd51f57535 | ||
|
|
4956e65824 | ||
|
|
2185a730d2 | ||
|
|
fcfb0a302a | ||
|
|
b40bf56e4e | ||
|
|
27ad487545 | ||
|
|
e914183996 | ||
|
|
acfda31e59 | ||
|
|
f9dd3aef72 | ||
|
|
b24ef88a80 | ||
|
|
b16a5f03fe | ||
|
|
2f1fabb318 | ||
|
|
1ec2966433 | ||
|
|
8a4b0a9eb0 | ||
|
|
4e8ee302a6 | ||
|
|
18a4104737 | ||
|
|
1a32d18c16 | ||
|
|
412bc17c12 | ||
|
|
9491b67f3c | ||
|
|
cb4f943f50 | ||
|
|
b69a376aa1 | ||
|
|
12f4a014e0 | ||
|
|
9cc7907234 | ||
|
|
21a9658519 | ||
|
|
7f436f5318 | ||
|
|
dcfde8dc92 | ||
|
|
898db92d51 | ||
|
|
8dd0e388a2 | ||
|
|
40f0a541bf | ||
|
|
1762d6c8cc | ||
|
|
195248801d | ||
|
|
4a825c0260 | ||
|
|
514c9e5a22 | ||
|
|
2f366aed2e | ||
|
|
c0c88f3d73 | ||
|
|
d0344bcff8 | ||
|
|
a6ebb3061c | ||
|
|
fe57507fd9 | ||
|
|
83c5370eec | ||
|
|
1a48453bea | ||
|
|
3167e5cec0 | ||
|
|
5148c5786f | ||
|
|
3edfb0e335 | ||
|
|
d3248a4f8e | ||
|
|
30e10be95d | ||
|
|
cced1477a0 | ||
|
|
9f5dcb1591 | ||
|
|
ce5c51d3ba | ||
|
|
11f670c8a6 | ||
|
|
a387ae9590 | ||
|
|
4ae497106d | ||
|
|
1f4fc2e7bb | ||
|
|
ae44b86b0d | ||
|
|
8276a443bc | ||
|
|
1e2f251bb3 | ||
|
|
845010b535 | ||
|
|
a0c63ba1cf | ||
|
|
2b82366148 | ||
|
|
ab1fa13ebe | ||
|
|
4740ba2425 | ||
|
|
4b0ee28f1c | ||
|
|
6ec0291d4e | ||
|
|
118131fcaf | ||
|
|
197b319f9a | ||
|
|
8b579bf3ec | ||
|
|
cbade89ab1 | ||
|
|
d20397c15d | ||
|
|
19f8907296 | ||
|
|
e943de5300 | ||
|
|
4064f8dd80 | ||
|
|
2acd206821 | ||
|
|
4c6fd94d97 | ||
|
|
fd54b10d97 | ||
|
|
6830089d3c | ||
|
|
6768a22f67 | ||
|
|
e8b02cd664 | ||
|
|
fbb0ecfb83 | ||
|
|
a31842feaa | ||
|
|
79325ead2e |
2
.github/docker/Dockerfile
vendored
2
.github/docker/Dockerfile
vendored
@@ -6,7 +6,7 @@ WORKDIR /src
|
||||
COPY . .
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
|
||||
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags "-s -w -buildid=" ./main
|
||||
|
||||
# Download geodat into a staging directory
|
||||
ADD https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geoip.dat /tmp/geodat/geoip.dat
|
||||
|
||||
2
.github/docker/Dockerfile.usa
vendored
2
.github/docker/Dockerfile.usa
vendored
@@ -6,7 +6,7 @@ WORKDIR /src
|
||||
COPY . .
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
|
||||
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags "-s -w -buildid=" ./main
|
||||
|
||||
# Download geodat into a staging directory
|
||||
ADD https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geoip.dat /tmp/geodat/geoip.dat
|
||||
|
||||
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@@ -65,7 +65,7 @@ jobs:
|
||||
echo "LATEST=$LATEST" >>${GITHUB_ENV}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
6
.github/workflows/release-win7.yml
vendored
6
.github/workflows/release-win7.yml
vendored
@@ -63,7 +63,7 @@ jobs:
|
||||
CGO_ENABLED: 0
|
||||
steps:
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Show workflow information
|
||||
run: |
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
@@ -132,7 +132,7 @@ jobs:
|
||||
mv build_assets Xray-${{ env.ASSET_NAME }}
|
||||
|
||||
- name: Upload files to Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: Xray-${{ env.ASSET_NAME }}
|
||||
path: |
|
||||
|
||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
|
||||
- name: Trigger Asset Update Workflow if Assets Missing
|
||||
if: steps.check-assets.outputs.missing == 'true'
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
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@v6
|
||||
|
||||
- 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@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
@@ -238,7 +238,7 @@ jobs:
|
||||
mv build_assets Xray-${{ env.ASSET_NAME }}
|
||||
|
||||
- name: Upload files to Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: Xray-${{ env.ASSET_NAME }}
|
||||
path: |
|
||||
|
||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -45,9 +45,9 @@ jobs:
|
||||
os: [windows-latest, ubuntu-latest, macos-latest]
|
||||
steps:
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
|
||||
17
README.md
17
README.md
@@ -4,12 +4,25 @@
|
||||
|
||||
[README](https://github.com/XTLS/Xray-core#readme) is open, so feel free to submit your project [here](https://github.com/XTLS/Xray-core/pulls).
|
||||
|
||||
## Sponsors
|
||||
|
||||
[](https://docs.rw)
|
||||
|
||||
[](https://happ.su)
|
||||
|
||||
[**Sponsor Xray-core**](https://github.com/XTLS/Xray-core/issues/3668)
|
||||
|
||||
## Donation & NFTs
|
||||
|
||||
### [Collect a Project X NFT to support the development of Project X!](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1)
|
||||
|
||||
[<img alt="Project X NFT" width="150px" src="https://raw2.seadn.io/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/7fa9ce900fb39b44226348db330e32/8b7fa9ce900fb39b44226348db330e32.svg" />](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1)
|
||||
|
||||
- **TRX(Tron)/USDT/USDC: `TNrDh5VSfwd4RPrwsohr6poyNTfFefNYan`**
|
||||
- **TON: `UQApeV-u2gm43aC1uP76xAC1m6vCylstaN1gpfBmre_5IyTH`**
|
||||
- **BTC: `1JpqcziZZuqv3QQJhZGNGBVdCBrGgkL6cT`**
|
||||
- **XMR: `4ABHQZ3yJZkBnLoqiKvb3f8eqUnX4iMPb6wdant5ZLGQELctcerceSGEfJnoCk6nnyRZm73wrwSgvZ2WmjYLng6R7sR67nq`**
|
||||
- **SOL/USDT/USDC: `3x5NuXHzB5APG6vRinPZcsUv5ukWUY1tBGRSJiEJWtZa`**
|
||||
- **ETH/USDT/USDC: `0xDc3Fe44F0f25D13CACb1C4896CD0D321df3146Ee`**
|
||||
- **Project X NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1**
|
||||
- **VLESS NFT: https://opensea.io/collection/vless**
|
||||
@@ -45,6 +58,8 @@
|
||||
- [wulabing/xray_docker](https://github.com/wulabing/xray_docker)
|
||||
- Web Panel - **WARNING: Please DO NOT USE plain HTTP panels like 3X-UI**, as they are believed to be bribed by Iran GFW for supporting plain HTTP by default and refused to change (https://github.com/XTLS/Xray-core/pull/3884#issuecomment-2439595331), which has already put many users' data security in danger in the past few years. **If you are already using 3X-UI, please switch to the following panels, which are verified to support HTTPS and SSH port forwarding only:**
|
||||
- [Remnawave](https://github.com/remnawave/panel)
|
||||
- [X-Panel](https://github.com/xeefei/X-Panel)
|
||||
- [PasarGuard](https://github.com/PasarGuard/panel)
|
||||
- [Marzban](https://github.com/Gozargah/Marzban)
|
||||
- [Xray-UI](https://github.com/qist/xray-ui)
|
||||
- [Hiddify](https://github.com/hiddify/Hiddify-Manager)
|
||||
@@ -106,12 +121,14 @@
|
||||
- [OneXray](https://github.com/OneXray/OneXray)
|
||||
- [GoXRay](https://github.com/goxray/desktop)
|
||||
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
|
||||
- [v2rayN](https://github.com/2dust/v2rayN)
|
||||
- Linux
|
||||
- [v2rayA](https://github.com/v2rayA/v2rayA)
|
||||
- [Furious](https://github.com/LorenEteval/Furious)
|
||||
- [GorzRay](https://github.com/ketetefid/GorzRay)
|
||||
- [GoXRay](https://github.com/goxray/desktop)
|
||||
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
|
||||
- [v2rayN](https://github.com/2dust/v2rayN)
|
||||
|
||||
## Others that support VLESS, XTLS, REALITY, XUDP, PLUX...
|
||||
|
||||
|
||||
@@ -196,6 +196,47 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
|
||||
return inboundLink, outboundLink
|
||||
}
|
||||
|
||||
func (d *DefaultDispatcher) WrapLink(ctx context.Context, link *transport.Link) *transport.Link {
|
||||
sessionInbound := session.InboundFromContext(ctx)
|
||||
var user *protocol.MemoryUser
|
||||
if sessionInbound != nil {
|
||||
user = sessionInbound.User
|
||||
}
|
||||
|
||||
link.Reader = &buf.TimeoutWrapperReader{Reader: link.Reader}
|
||||
|
||||
if user != nil && len(user.Email) > 0 {
|
||||
p := d.policy.ForLevel(user.Level)
|
||||
if p.Stats.UserUplink {
|
||||
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
|
||||
if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
|
||||
link.Reader.(*buf.TimeoutWrapperReader).Counter = c
|
||||
}
|
||||
}
|
||||
if p.Stats.UserDownlink {
|
||||
name := "user>>>" + user.Email + ">>>traffic>>>downlink"
|
||||
if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
|
||||
link.Writer = &SizeStatWriter{
|
||||
Counter: c,
|
||||
Writer: link.Writer,
|
||||
}
|
||||
}
|
||||
}
|
||||
if p.Stats.UserOnline {
|
||||
name := "user>>>" + user.Email + ">>>online"
|
||||
if om, _ := stats.GetOrRegisterOnlineMap(d.stats, name); om != nil {
|
||||
sessionInbounds := session.InboundFromContext(ctx)
|
||||
userIP := sessionInbounds.Source.Address.String()
|
||||
om.AddIP(userIP)
|
||||
// log Online user with ips
|
||||
// errors.LogDebug(ctx, "user>>>" + user.Email + ">>>online", om.Count(), om.List())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return link
|
||||
}
|
||||
|
||||
func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool {
|
||||
domain := result.Domain()
|
||||
if domain == "" {
|
||||
@@ -316,6 +357,7 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
|
||||
content = new(session.Content)
|
||||
ctx = session.ContextWithContent(ctx, content)
|
||||
}
|
||||
outbound = d.WrapLink(ctx, outbound)
|
||||
sniffingRequest := content.SniffingRequest
|
||||
if !sniffingRequest.Enabled {
|
||||
d.routedDispatch(ctx, outbound, destination)
|
||||
@@ -441,6 +483,9 @@ 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)
|
||||
|
||||
@@ -3,36 +3,55 @@ package dns
|
||||
import (
|
||||
"context"
|
||||
go_errors "errors"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/signal/pubsub"
|
||||
"github.com/xtls/xray-core/common/task"
|
||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"sync"
|
||||
"time"
|
||||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
|
||||
const (
|
||||
minSizeForEmptyRebuild = 512
|
||||
shrinkAbsoluteThreshold = 10240
|
||||
shrinkRatioThreshold = 0.65
|
||||
migrationBatchSize = 4096
|
||||
)
|
||||
|
||||
type CacheController struct {
|
||||
name string
|
||||
disableCache bool
|
||||
serveStale bool
|
||||
serveExpiredTTL int32
|
||||
|
||||
ips map[string]*record
|
||||
dirtyips map[string]*record
|
||||
|
||||
sync.RWMutex
|
||||
ips map[string]*record
|
||||
pub *pubsub.Service
|
||||
cacheCleanup *task.Periodic
|
||||
name string
|
||||
disableCache bool
|
||||
pub *pubsub.Service
|
||||
cacheCleanup *task.Periodic
|
||||
highWatermark int
|
||||
requestGroup singleflight.Group
|
||||
}
|
||||
|
||||
func NewCacheController(name string, disableCache bool) *CacheController {
|
||||
func NewCacheController(name string, disableCache bool, serveStale bool, serveExpiredTTL uint32) *CacheController {
|
||||
c := &CacheController{
|
||||
name: name,
|
||||
disableCache: disableCache,
|
||||
ips: make(map[string]*record),
|
||||
pub: pubsub.NewService(),
|
||||
name: name,
|
||||
disableCache: disableCache,
|
||||
serveStale: serveStale,
|
||||
serveExpiredTTL: -int32(serveExpiredTTL),
|
||||
ips: make(map[string]*record),
|
||||
pub: pubsub.NewService(),
|
||||
}
|
||||
|
||||
c.cacheCleanup = &task.Periodic{
|
||||
Interval: time.Minute,
|
||||
Interval: 300 * time.Second,
|
||||
Execute: c.CacheCleanup,
|
||||
}
|
||||
return c
|
||||
@@ -40,131 +59,263 @@ func NewCacheController(name string, disableCache bool) *CacheController {
|
||||
|
||||
// CacheCleanup clears expired items from cache
|
||||
func (c *CacheController) CacheCleanup() error {
|
||||
now := time.Now()
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if len(c.ips) == 0 {
|
||||
return errors.New("nothing to do. stopping...")
|
||||
expiredKeys, err := c.collectExpiredKeys()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for domain, record := range c.ips {
|
||||
if record.A != nil && record.A.Expire.Before(now) {
|
||||
record.A = nil
|
||||
}
|
||||
if record.AAAA != nil && record.AAAA.Expire.Before(now) {
|
||||
record.AAAA = nil
|
||||
}
|
||||
|
||||
if record.A == nil && record.AAAA == nil {
|
||||
errors.LogDebug(context.Background(), c.name, "cache cleanup ", domain)
|
||||
delete(c.ips, domain)
|
||||
} else {
|
||||
c.ips[domain] = record
|
||||
}
|
||||
if len(expiredKeys) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(c.ips) == 0 {
|
||||
c.ips = make(map[string]*record)
|
||||
}
|
||||
|
||||
c.writeAndShrink(expiredKeys)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CacheController) updateIP(req *dnsRequest, ipRec *IPRecord) {
|
||||
elapsed := time.Since(req.start)
|
||||
func (c *CacheController) collectExpiredKeys() ([]string, error) {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
c.Lock()
|
||||
rec, found := c.ips[req.domain]
|
||||
if !found {
|
||||
rec = &record{}
|
||||
if len(c.ips) == 0 {
|
||||
return nil, errors.New("nothing to do. stopping...")
|
||||
}
|
||||
|
||||
switch req.reqType {
|
||||
case dnsmessage.TypeA:
|
||||
rec.A = ipRec
|
||||
case dnsmessage.TypeAAAA:
|
||||
rec.AAAA = ipRec
|
||||
// skip collection if a migration is in progress
|
||||
if c.dirtyips != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
errors.LogInfo(context.Background(), c.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed)
|
||||
c.ips[req.domain] = rec
|
||||
now := time.Now()
|
||||
if c.serveStale && c.serveExpiredTTL != 0 {
|
||||
now = now.Add(time.Duration(c.serveExpiredTTL) * time.Second)
|
||||
}
|
||||
|
||||
switch req.reqType {
|
||||
case dnsmessage.TypeA:
|
||||
c.pub.Publish(req.domain+"4", nil)
|
||||
if !c.disableCache {
|
||||
_, _, err := rec.AAAA.getIPs()
|
||||
if !go_errors.Is(err, errRecordNotFound) {
|
||||
c.pub.Publish(req.domain+"6", nil)
|
||||
}
|
||||
}
|
||||
case dnsmessage.TypeAAAA:
|
||||
c.pub.Publish(req.domain+"6", nil)
|
||||
if !c.disableCache {
|
||||
_, _, err := rec.A.getIPs()
|
||||
if !go_errors.Is(err, errRecordNotFound) {
|
||||
c.pub.Publish(req.domain+"4", nil)
|
||||
}
|
||||
expiredKeys := make([]string, 0, len(c.ips)/4) // pre-allocate
|
||||
|
||||
for domain, rec := range c.ips {
|
||||
if (rec.A != nil && rec.A.Expire.Before(now)) ||
|
||||
(rec.AAAA != nil && rec.AAAA.Expire.Before(now)) {
|
||||
expiredKeys = append(expiredKeys, domain)
|
||||
}
|
||||
}
|
||||
|
||||
c.Unlock()
|
||||
common.Must(c.cacheCleanup.Start())
|
||||
return expiredKeys, nil
|
||||
}
|
||||
|
||||
func (c *CacheController) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
|
||||
func (c *CacheController) writeAndShrink(expiredKeys []string) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
// double check to prevent upper call multiple cleanup tasks
|
||||
if c.dirtyips != nil {
|
||||
return
|
||||
}
|
||||
|
||||
lenBefore := len(c.ips)
|
||||
if lenBefore > c.highWatermark {
|
||||
c.highWatermark = lenBefore
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
if c.serveStale && c.serveExpiredTTL != 0 {
|
||||
now = now.Add(time.Duration(c.serveExpiredTTL) * time.Second)
|
||||
}
|
||||
|
||||
for _, domain := range expiredKeys {
|
||||
rec := c.ips[domain]
|
||||
if rec == nil {
|
||||
continue
|
||||
}
|
||||
if rec.A != nil && rec.A.Expire.Before(now) {
|
||||
rec.A = nil
|
||||
}
|
||||
if rec.AAAA != nil && rec.AAAA.Expire.Before(now) {
|
||||
rec.AAAA = nil
|
||||
}
|
||||
if rec.A == nil && rec.AAAA == nil {
|
||||
delete(c.ips, domain)
|
||||
}
|
||||
}
|
||||
|
||||
lenAfter := len(c.ips)
|
||||
|
||||
if lenAfter == 0 {
|
||||
if c.highWatermark >= minSizeForEmptyRebuild {
|
||||
errors.LogDebug(context.Background(), c.name,
|
||||
" rebuilding empty cache map to reclaim memory.",
|
||||
" size_before_cleanup=", lenBefore,
|
||||
" peak_size_before_rebuild=", c.highWatermark,
|
||||
)
|
||||
|
||||
c.ips = make(map[string]*record)
|
||||
c.highWatermark = 0
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if reductionFromPeak := c.highWatermark - lenAfter; reductionFromPeak > shrinkAbsoluteThreshold &&
|
||||
float64(reductionFromPeak) > float64(c.highWatermark)*shrinkRatioThreshold {
|
||||
errors.LogDebug(context.Background(), c.name,
|
||||
" shrinking cache map to reclaim memory.",
|
||||
" new_size=", lenAfter,
|
||||
" peak_size_before_shrink=", c.highWatermark,
|
||||
" reduction_since_peak=", reductionFromPeak,
|
||||
)
|
||||
|
||||
c.dirtyips = c.ips
|
||||
c.ips = make(map[string]*record, int(float64(lenAfter)*1.1))
|
||||
c.highWatermark = lenAfter
|
||||
go c.migrate()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type migrationEntry struct {
|
||||
key string
|
||||
value *record
|
||||
}
|
||||
|
||||
func (c *CacheController) migrate() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
errors.LogError(context.Background(), c.name, " panic during cache migration: ", r)
|
||||
c.Lock()
|
||||
c.dirtyips = nil
|
||||
// c.ips = make(map[string]*record)
|
||||
// c.highWatermark = 0
|
||||
c.Unlock()
|
||||
}
|
||||
}()
|
||||
|
||||
c.RLock()
|
||||
record, found := c.ips[domain]
|
||||
dirtyips := c.dirtyips
|
||||
c.RUnlock()
|
||||
|
||||
if !found {
|
||||
return nil, 0, errRecordNotFound
|
||||
// double check to prevent upper call multiple cleanup tasks
|
||||
if dirtyips == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var errs []error
|
||||
var allIPs []net.IP
|
||||
var rTTL uint32 = dns_feature.DefaultTTL
|
||||
errors.LogDebug(context.Background(), c.name, " starting background cache migration for ", len(dirtyips), " items")
|
||||
|
||||
mergeReq := option.IPv4Enable && option.IPv6Enable
|
||||
batch := make([]migrationEntry, 0, migrationBatchSize)
|
||||
for domain, recD := range dirtyips {
|
||||
batch = append(batch, migrationEntry{domain, recD})
|
||||
|
||||
if option.IPv4Enable {
|
||||
ips, ttl, err := record.A.getIPs()
|
||||
if !mergeReq || go_errors.Is(err, errRecordNotFound) {
|
||||
return ips, ttl, err
|
||||
if len(batch) >= migrationBatchSize {
|
||||
c.flush(batch)
|
||||
batch = batch[:0]
|
||||
runtime.Gosched()
|
||||
}
|
||||
if ttl < rTTL {
|
||||
rTTL = ttl
|
||||
}
|
||||
if len(ips) > 0 {
|
||||
allIPs = append(allIPs, ips...)
|
||||
}
|
||||
if len(batch) > 0 {
|
||||
c.flush(batch)
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
c.dirtyips = nil
|
||||
c.Unlock()
|
||||
|
||||
errors.LogDebug(context.Background(), c.name, " cache migration completed")
|
||||
}
|
||||
|
||||
func (c *CacheController) flush(batch []migrationEntry) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
for _, dirty := range batch {
|
||||
if cur := c.ips[dirty.key]; cur != nil {
|
||||
merge := &record{}
|
||||
if cur.A == nil {
|
||||
merge.A = dirty.value.A
|
||||
} else {
|
||||
merge.A = cur.A
|
||||
}
|
||||
if cur.AAAA == nil {
|
||||
merge.AAAA = dirty.value.AAAA
|
||||
} else {
|
||||
merge.AAAA = cur.AAAA
|
||||
}
|
||||
c.ips[dirty.key] = merge
|
||||
} else {
|
||||
errs = append(errs, err)
|
||||
c.ips[dirty.key] = dirty.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CacheController) updateRecord(req *dnsRequest, rep *IPRecord) {
|
||||
rtt := time.Since(req.start)
|
||||
|
||||
switch req.reqType {
|
||||
case dnsmessage.TypeA:
|
||||
c.pub.Publish(req.domain+"4", rep)
|
||||
case dnsmessage.TypeAAAA:
|
||||
c.pub.Publish(req.domain+"6", rep)
|
||||
}
|
||||
|
||||
if c.disableCache {
|
||||
errors.LogInfo(context.Background(), c.name, " got answer: ", req.domain, " ", req.reqType, " -> ", rep.IP, ", rtt: ", rtt)
|
||||
return
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
lockWait := time.Since(req.start) - rtt
|
||||
|
||||
newRec := &record{}
|
||||
oldRec := c.ips[req.domain]
|
||||
var dirtyRec *record
|
||||
if c.dirtyips != nil {
|
||||
dirtyRec = c.dirtyips[req.domain]
|
||||
}
|
||||
|
||||
var pubRecord *IPRecord
|
||||
var pubSuffix string
|
||||
|
||||
switch req.reqType {
|
||||
case dnsmessage.TypeA:
|
||||
newRec.A = rep
|
||||
if oldRec != nil && oldRec.AAAA != nil {
|
||||
newRec.AAAA = oldRec.AAAA
|
||||
pubRecord = oldRec.AAAA
|
||||
} else if dirtyRec != nil && dirtyRec.AAAA != nil {
|
||||
pubRecord = dirtyRec.AAAA
|
||||
}
|
||||
pubSuffix = "6"
|
||||
case dnsmessage.TypeAAAA:
|
||||
newRec.AAAA = rep
|
||||
if oldRec != nil && oldRec.A != nil {
|
||||
newRec.A = oldRec.A
|
||||
pubRecord = oldRec.A
|
||||
} else if dirtyRec != nil && dirtyRec.A != nil {
|
||||
pubRecord = dirtyRec.A
|
||||
}
|
||||
pubSuffix = "4"
|
||||
}
|
||||
|
||||
c.ips[req.domain] = newRec
|
||||
c.Unlock()
|
||||
|
||||
if pubRecord != nil {
|
||||
_, ttl, err := pubRecord.getIPs()
|
||||
if ttl > 0 && !go_errors.Is(err, errRecordNotFound) {
|
||||
c.pub.Publish(req.domain+pubSuffix, pubRecord)
|
||||
}
|
||||
}
|
||||
|
||||
if option.IPv6Enable {
|
||||
ips, ttl, err := record.AAAA.getIPs()
|
||||
if !mergeReq || go_errors.Is(err, errRecordNotFound) {
|
||||
return ips, ttl, err
|
||||
}
|
||||
if ttl < rTTL {
|
||||
rTTL = ttl
|
||||
}
|
||||
if len(ips) > 0 {
|
||||
allIPs = append(allIPs, ips...)
|
||||
} else {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
errors.LogInfo(context.Background(), c.name, " got answer: ", req.domain, " ", req.reqType, " -> ", rep.IP, ", rtt: ", rtt, ", lock: ", lockWait)
|
||||
|
||||
if len(allIPs) > 0 {
|
||||
return allIPs, rTTL, nil
|
||||
if !c.serveStale || c.serveExpiredTTL != 0 {
|
||||
common.Must(c.cacheCleanup.Start())
|
||||
}
|
||||
if go_errors.Is(errs[0], errs[1]) {
|
||||
return nil, rTTL, errs[0]
|
||||
}
|
||||
|
||||
func (c *CacheController) findRecords(domain string) *record {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
rec := c.ips[domain]
|
||||
if rec == nil && c.dirtyips != nil {
|
||||
rec = c.dirtyips[domain]
|
||||
}
|
||||
return nil, rTTL, errors.Combine(errs...)
|
||||
return rec
|
||||
}
|
||||
|
||||
func (c *CacheController) registerSubscribers(domain string, option dns_feature.IPOption) (sub4 *pubsub.Subscriber, sub6 *pubsub.Subscriber) {
|
||||
|
||||
@@ -141,10 +141,13 @@ type NameServer struct {
|
||||
ActPrior bool `protobuf:"varint,8,opt,name=actPrior,proto3" json:"actPrior,omitempty"`
|
||||
Tag string `protobuf:"bytes,9,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
TimeoutMs uint64 `protobuf:"varint,10,opt,name=timeoutMs,proto3" json:"timeoutMs,omitempty"`
|
||||
DisableCache bool `protobuf:"varint,11,opt,name=disableCache,proto3" json:"disableCache,omitempty"`
|
||||
DisableCache *bool `protobuf:"varint,11,opt,name=disableCache,proto3,oneof" json:"disableCache,omitempty"`
|
||||
ServeStale *bool `protobuf:"varint,15,opt,name=serveStale,proto3,oneof" json:"serveStale,omitempty"`
|
||||
ServeExpiredTTL *uint32 `protobuf:"varint,16,opt,name=serveExpiredTTL,proto3,oneof" json:"serveExpiredTTL,omitempty"`
|
||||
FinalQuery bool `protobuf:"varint,12,opt,name=finalQuery,proto3" json:"finalQuery,omitempty"`
|
||||
UnexpectedGeoip []*router.GeoIP `protobuf:"bytes,13,rep,name=unexpected_geoip,json=unexpectedGeoip,proto3" json:"unexpected_geoip,omitempty"`
|
||||
ActUnprior bool `protobuf:"varint,14,opt,name=actUnprior,proto3" json:"actUnprior,omitempty"`
|
||||
PolicyID uint32 `protobuf:"varint,17,opt,name=policyID,proto3" json:"policyID,omitempty"`
|
||||
}
|
||||
|
||||
func (x *NameServer) Reset() {
|
||||
@@ -248,12 +251,26 @@ func (x *NameServer) GetTimeoutMs() uint64 {
|
||||
}
|
||||
|
||||
func (x *NameServer) GetDisableCache() bool {
|
||||
if x != nil {
|
||||
return x.DisableCache
|
||||
if x != nil && x.DisableCache != nil {
|
||||
return *x.DisableCache
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *NameServer) GetServeStale() bool {
|
||||
if x != nil && x.ServeStale != nil {
|
||||
return *x.ServeStale
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *NameServer) GetServeExpiredTTL() uint32 {
|
||||
if x != nil && x.ServeExpiredTTL != nil {
|
||||
return *x.ServeExpiredTTL
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *NameServer) GetFinalQuery() bool {
|
||||
if x != nil {
|
||||
return x.FinalQuery
|
||||
@@ -275,6 +292,13 @@ func (x *NameServer) GetActUnprior() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *NameServer) GetPolicyID() uint32 {
|
||||
if x != nil {
|
||||
return x.PolicyID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -291,9 +315,12 @@ type Config struct {
|
||||
Tag string `protobuf:"bytes,6,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
// DisableCache disables DNS cache
|
||||
DisableCache bool `protobuf:"varint,8,opt,name=disableCache,proto3" json:"disableCache,omitempty"`
|
||||
ServeStale bool `protobuf:"varint,12,opt,name=serveStale,proto3" json:"serveStale,omitempty"`
|
||||
ServeExpiredTTL uint32 `protobuf:"varint,13,opt,name=serveExpiredTTL,proto3" json:"serveExpiredTTL,omitempty"`
|
||||
QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"`
|
||||
DisableFallback bool `protobuf:"varint,10,opt,name=disableFallback,proto3" json:"disableFallback,omitempty"`
|
||||
DisableFallbackIfMatch bool `protobuf:"varint,11,opt,name=disableFallbackIfMatch,proto3" json:"disableFallbackIfMatch,omitempty"`
|
||||
EnableParallelQuery bool `protobuf:"varint,14,opt,name=enableParallelQuery,proto3" json:"enableParallelQuery,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
@@ -361,6 +388,20 @@ func (x *Config) GetDisableCache() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *Config) GetServeStale() bool {
|
||||
if x != nil {
|
||||
return x.ServeStale
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *Config) GetServeExpiredTTL() uint32 {
|
||||
if x != nil {
|
||||
return x.ServeExpiredTTL
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetQueryStrategy() QueryStrategy {
|
||||
if x != nil {
|
||||
return x.QueryStrategy
|
||||
@@ -382,6 +423,13 @@ func (x *Config) GetDisableFallbackIfMatch() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *Config) GetEnableParallelQuery() bool {
|
||||
if x != nil {
|
||||
return x.EnableParallelQuery
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type NameServer_PriorityDomain struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -567,7 +615,7 @@ var file_app_dns_config_proto_rawDesc = []byte{
|
||||
0x2e, 0x64, 0x6e, 0x73, 0x1a, 0x1c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74,
|
||||
0x2f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb6, 0x06, 0x0a, 0x0a,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdf, 0x07, 0x0a, 0x0a,
|
||||
0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72,
|
||||
0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e,
|
||||
@@ -599,74 +647,93 @@ var file_app_dns_config_proto_rawDesc = []byte{
|
||||
0x50, 0x72, 0x69, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x09, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x6f,
|
||||
0x75, 0x74, 0x4d, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x6f, 0x75, 0x74, 0x4d, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65,
|
||||
0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6e,
|
||||
0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66,
|
||||
0x69, 0x6e, 0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x41, 0x0a, 0x10, 0x75, 0x6e, 0x65,
|
||||
0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0d, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72,
|
||||
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0f, 0x75, 0x6e, 0x65,
|
||||
0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x1e, 0x0a, 0x0a,
|
||||
0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08,
|
||||
0x52, 0x0a, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x1a, 0x5e, 0x0a, 0x0e,
|
||||
0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x34,
|
||||
0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04,
|
||||
0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x36, 0x0a, 0x0c,
|
||||
0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65,
|
||||
0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04,
|
||||
0x73, 0x69, 0x7a, 0x65, 0x22, 0x9c, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
|
||||
0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x05,
|
||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
|
||||
0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0a,
|
||||
0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c,
|
||||
0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63,
|
||||
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69,
|
||||
0x63, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52,
|
||||
0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03,
|
||||
0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x22,
|
||||
0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x08,
|
||||
0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63,
|
||||
0x68, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61,
|
||||
0x74, 0x65, 0x67, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53,
|
||||
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74,
|
||||
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
|
||||
0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b,
|
||||
0x12, 0x36, 0x0a, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62,
|
||||
0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08,
|
||||
0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63,
|
||||
0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73,
|
||||
0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
||||
0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63,
|
||||
0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16,
|
||||
0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
|
||||
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03,
|
||||
0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65,
|
||||
0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
|
||||
0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08,
|
||||
0x07, 0x10, 0x08, 0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74,
|
||||
0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c,
|
||||
0x6c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||
0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x02, 0x12,
|
||||
0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x0d, 0x51, 0x75,
|
||||
0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0a, 0x0a, 0x06, 0x55,
|
||||
0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49,
|
||||
0x50, 0x34, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10,
|
||||
0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x53, 0x59, 0x53, 0x10, 0x03, 0x42, 0x46,
|
||||
0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64,
|
||||
0x6e, 0x73, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f,
|
||||
0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41,
|
||||
0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x6f, 0x75, 0x74, 0x4d, 0x73, 0x12, 0x27, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65,
|
||||
0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0c, 0x64,
|
||||
0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x88, 0x01, 0x01, 0x12, 0x23,
|
||||
0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x74, 0x61, 0x6c, 0x65, 0x18, 0x0f, 0x20, 0x01,
|
||||
0x28, 0x08, 0x48, 0x01, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x74, 0x61, 0x6c, 0x65,
|
||||
0x88, 0x01, 0x01, 0x12, 0x2d, 0x0a, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x45, 0x78, 0x70, 0x69,
|
||||
0x72, 0x65, 0x64, 0x54, 0x54, 0x4c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, 0x0f,
|
||||
0x73, 0x65, 0x72, 0x76, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x54, 0x54, 0x4c, 0x88,
|
||||
0x01, 0x01, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79,
|
||||
0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x51, 0x75, 0x65,
|
||||
0x72, 0x79, 0x12, 0x41, 0x0a, 0x10, 0x75, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64,
|
||||
0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47,
|
||||
0x65, 0x6f, 0x49, 0x50, 0x52, 0x0f, 0x75, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64,
|
||||
0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72,
|
||||
0x69, 0x6f, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x55, 0x6e,
|
||||
0x70, 0x72, 0x69, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49,
|
||||
0x44, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49,
|
||||
0x44, 0x1a, 0x5e, 0x0a, 0x0e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d,
|
||||
0x61, 0x69, 0x6e, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73,
|
||||
0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54,
|
||||
0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d,
|
||||
0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69,
|
||||
0x6e, 0x1a, 0x36, 0x0a, 0x0c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c,
|
||||
0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x64, 0x69,
|
||||
0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73,
|
||||
0x65, 0x72, 0x76, 0x65, 0x53, 0x74, 0x61, 0x6c, 0x65, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x65,
|
||||
0x72, 0x76, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x54, 0x54, 0x4c, 0x22, 0x98, 0x05,
|
||||
0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d,
|
||||
0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72,
|
||||
0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70,
|
||||
0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x73,
|
||||
0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
||||
0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73,
|
||||
0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63,
|
||||
0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62,
|
||||
0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64,
|
||||
0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73,
|
||||
0x65, 0x72, 0x76, 0x65, 0x53, 0x74, 0x61, 0x6c, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x74, 0x61, 0x6c, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x73,
|
||||
0x65, 0x72, 0x76, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x54, 0x54, 0x4c, 0x18, 0x0d,
|
||||
0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72,
|
||||
0x65, 0x64, 0x54, 0x54, 0x4c, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73,
|
||||
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65,
|
||||
0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72,
|
||||
0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62,
|
||||
0x61, 0x63, 0x6b, 0x12, 0x36, 0x0a, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61,
|
||||
0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c,
|
||||
0x62, 0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, 0x0a, 0x13, 0x65,
|
||||
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x51, 0x75, 0x65,
|
||||
0x72, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
|
||||
0x50, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x1a, 0x92, 0x01,
|
||||
0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a,
|
||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72,
|
||||
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
||||
0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74,
|
||||
0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69,
|
||||
0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70,
|
||||
0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08,
|
||||
0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64,
|
||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f,
|
||||
0x72, 0x64, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a,
|
||||
0x42, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
||||
0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07,
|
||||
0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45,
|
||||
0x5f, 0x49, 0x50, 0x36, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x53, 0x59,
|
||||
0x53, 0x10, 0x03, 0x42, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d,
|
||||
0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -718,6 +785,7 @@ func file_app_dns_config_proto_init() {
|
||||
if File_app_dns_config_proto != nil {
|
||||
return
|
||||
}
|
||||
file_app_dns_config_proto_msgTypes[0].OneofWrappers = []any{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
|
||||
@@ -31,10 +31,13 @@ message NameServer {
|
||||
bool actPrior = 8;
|
||||
string tag = 9;
|
||||
uint64 timeoutMs = 10;
|
||||
bool disableCache = 11;
|
||||
optional bool disableCache = 11;
|
||||
optional bool serveStale = 15;
|
||||
optional uint32 serveExpiredTTL = 16;
|
||||
bool finalQuery = 12;
|
||||
repeated xray.app.router.GeoIP unexpected_geoip = 13;
|
||||
bool actUnprior = 14;
|
||||
uint32 policyID = 17;
|
||||
}
|
||||
|
||||
enum DomainMatchingType {
|
||||
@@ -80,9 +83,13 @@ message Config {
|
||||
|
||||
// DisableCache disables DNS cache
|
||||
bool disableCache = 8;
|
||||
bool serveStale = 12;
|
||||
uint32 serveExpiredTTL = 13;
|
||||
|
||||
QueryStrategy query_strategy = 9;
|
||||
|
||||
bool disableFallback = 10;
|
||||
bool disableFallbackIfMatch = 11;
|
||||
|
||||
bool enableParallelQuery = 14;
|
||||
}
|
||||
|
||||
351
app/dns/dns.go
351
app/dns/dns.go
@@ -5,9 +5,12 @@ import (
|
||||
"context"
|
||||
go_errors "errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
@@ -22,6 +25,7 @@ type DNS struct {
|
||||
sync.Mutex
|
||||
disableFallback bool
|
||||
disableFallbackIfMatch bool
|
||||
enableParallelQuery bool
|
||||
ipOption *dns.IPOption
|
||||
hosts *StaticHosts
|
||||
clients []*Client
|
||||
@@ -117,7 +121,20 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
|
||||
myClientIP = net.IP(ns.ClientIp)
|
||||
}
|
||||
|
||||
disableCache := config.DisableCache || ns.DisableCache
|
||||
disableCache := config.DisableCache
|
||||
if ns.DisableCache != nil {
|
||||
disableCache = *ns.DisableCache
|
||||
}
|
||||
|
||||
serveStale := config.ServeStale
|
||||
if ns.ServeStale != nil {
|
||||
serveStale = *ns.ServeStale
|
||||
}
|
||||
|
||||
serveExpiredTTL := config.ServeExpiredTTL
|
||||
if ns.ServeExpiredTTL != nil {
|
||||
serveExpiredTTL = *ns.ServeExpiredTTL
|
||||
}
|
||||
|
||||
var tag = defaultTag
|
||||
if len(ns.Tag) > 0 {
|
||||
@@ -128,7 +145,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
|
||||
return nil, errors.New("no QueryStrategy available for ", ns.Address)
|
||||
}
|
||||
|
||||
client, err := NewClient(ctx, ns, myClientIP, disableCache, tag, clientIPOption, &matcherInfos, updateDomain)
|
||||
client, err := NewClient(ctx, ns, myClientIP, disableCache, serveStale, serveExpiredTTL, tag, clientIPOption, &matcherInfos, updateDomain)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to create client").Base(err)
|
||||
}
|
||||
@@ -149,6 +166,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
|
||||
matcherInfos: matcherInfos,
|
||||
disableFallback: config.DisableFallback,
|
||||
disableFallbackIfMatch: config.DisableFallbackIfMatch,
|
||||
enableParallelQuery: config.EnableParallelQuery,
|
||||
checkSystem: checkSystem,
|
||||
}, nil
|
||||
}
|
||||
@@ -191,7 +209,7 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er
|
||||
}
|
||||
|
||||
if s.checkSystem {
|
||||
supportIPv4, supportIPv6 := checkSystemNetwork()
|
||||
supportIPv4, supportIPv6 := checkRoutes()
|
||||
option.IPv4Enable = option.IPv4Enable && supportIPv4
|
||||
option.IPv6Enable = option.IPv6Enable && supportIPv6
|
||||
} else {
|
||||
@@ -227,45 +245,11 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er
|
||||
}
|
||||
|
||||
// Name servers lookup
|
||||
var errs []error
|
||||
for _, client := range s.sortClients(domain) {
|
||||
if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") {
|
||||
errors.LogDebug(s.ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
ips, ttl, err := client.QueryIP(s.ctx, domain, option)
|
||||
|
||||
if len(ips) > 0 {
|
||||
if ttl == 0 {
|
||||
ttl = 1
|
||||
}
|
||||
return ips, ttl, nil
|
||||
}
|
||||
|
||||
errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name())
|
||||
if err == nil {
|
||||
err = dns.ErrEmptyResponse
|
||||
}
|
||||
errs = append(errs, err)
|
||||
|
||||
if client.IsFinalQuery() {
|
||||
break
|
||||
}
|
||||
if s.enableParallelQuery {
|
||||
return s.parallelQuery(domain, option)
|
||||
} else {
|
||||
return s.serialQuery(domain, option)
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
allErrs := errors.Combine(errs...)
|
||||
err0 := errs[0]
|
||||
if errors.AllEqual(err0, allErrs) {
|
||||
if go_errors.Is(err0, dns.ErrEmptyResponse) {
|
||||
return nil, 0, dns.ErrEmptyResponse
|
||||
}
|
||||
return nil, 0, errors.New("returning nil for domain ", domain).Base(err0)
|
||||
}
|
||||
return nil, 0, errors.New("returning nil for domain ", domain).Base(allErrs)
|
||||
}
|
||||
return nil, 0, dns.ErrEmptyResponse
|
||||
}
|
||||
|
||||
func (s *DNS) sortClients(domain string) []*Client {
|
||||
@@ -292,6 +276,9 @@ func (s *DNS) sortClients(domain string) []*Client {
|
||||
clients = append(clients, client)
|
||||
clientNames = append(clientNames, client.Name())
|
||||
hasMatch = true
|
||||
if client.finalQuery {
|
||||
return clients
|
||||
}
|
||||
}
|
||||
|
||||
if !(s.disableFallback || s.disableFallbackIfMatch && hasMatch) {
|
||||
@@ -303,6 +290,9 @@ func (s *DNS) sortClients(domain string) []*Client {
|
||||
clientUsed[idx] = true
|
||||
clients = append(clients, client)
|
||||
clientNames = append(clientNames, client.Name())
|
||||
if client.finalQuery {
|
||||
return clients
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,35 +304,280 @@ func (s *DNS) sortClients(domain string) []*Client {
|
||||
}
|
||||
|
||||
if len(clients) == 0 {
|
||||
clients = append(clients, s.clients[0])
|
||||
clientNames = append(clientNames, s.clients[0].Name())
|
||||
errors.LogDebug(s.ctx, "domain ", domain, " will use the first DNS: ", clientNames)
|
||||
if len(s.clients) > 0 {
|
||||
clients = append(clients, s.clients[0])
|
||||
clientNames = append(clientNames, s.clients[0].Name())
|
||||
errors.LogWarning(s.ctx, "domain ", domain, " will use the first DNS: ", clientNames)
|
||||
} else {
|
||||
errors.LogError(s.ctx, "no DNS clients available for domain ", domain, " and no default clients configured")
|
||||
}
|
||||
}
|
||||
|
||||
return clients
|
||||
}
|
||||
|
||||
func mergeQueryErrors(domain string, errs []error) error {
|
||||
if len(errs) == 0 {
|
||||
return dns.ErrEmptyResponse
|
||||
}
|
||||
|
||||
var noRNF error
|
||||
for _, err := range errs {
|
||||
if go_errors.Is(err, errRecordNotFound) {
|
||||
continue // server no response, ignore
|
||||
} else if noRNF == nil {
|
||||
noRNF = err
|
||||
} else if !go_errors.Is(err, noRNF) {
|
||||
return errors.New("returning nil for domain ", domain).Base(errors.Combine(errs...))
|
||||
}
|
||||
}
|
||||
if go_errors.Is(noRNF, dns.ErrEmptyResponse) {
|
||||
return dns.ErrEmptyResponse
|
||||
}
|
||||
if noRNF == nil {
|
||||
noRNF = errRecordNotFound
|
||||
}
|
||||
return errors.New("returning nil for domain ", domain).Base(noRNF)
|
||||
}
|
||||
|
||||
func (s *DNS) serialQuery(domain string, option dns.IPOption) ([]net.IP, uint32, error) {
|
||||
var errs []error
|
||||
for _, client := range s.sortClients(domain) {
|
||||
if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") {
|
||||
errors.LogDebug(s.ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
ips, ttl, err := client.QueryIP(s.ctx, domain, option)
|
||||
|
||||
if len(ips) > 0 {
|
||||
return ips, ttl, nil
|
||||
}
|
||||
|
||||
errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name(), " in serial query mode")
|
||||
if err == nil {
|
||||
err = dns.ErrEmptyResponse
|
||||
}
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return nil, 0, mergeQueryErrors(domain, errs)
|
||||
}
|
||||
|
||||
func (s *DNS) parallelQuery(domain string, option dns.IPOption) ([]net.IP, uint32, error) {
|
||||
var errs []error
|
||||
clients := s.sortClients(domain)
|
||||
|
||||
resultsChan := asyncQueryAll(domain, option, clients, s.ctx)
|
||||
|
||||
groups, groupOf := makeGroups( /*s.ctx,*/ clients)
|
||||
results := make([]*queryResult, len(clients))
|
||||
pending := make([]int, len(groups))
|
||||
for gi, g := range groups {
|
||||
pending[gi] = g.end - g.start + 1
|
||||
}
|
||||
|
||||
nextGroup := 0
|
||||
for range clients {
|
||||
result := <-resultsChan
|
||||
results[result.index] = &result
|
||||
|
||||
gi := groupOf[result.index]
|
||||
pending[gi]--
|
||||
|
||||
for nextGroup < len(groups) {
|
||||
g := groups[nextGroup]
|
||||
|
||||
// group race, minimum rtt -> return
|
||||
for j := g.start; j <= g.end; j++ {
|
||||
r := results[j]
|
||||
if r != nil && r.err == nil && len(r.ips) > 0 {
|
||||
return r.ips, r.ttl, nil
|
||||
}
|
||||
}
|
||||
|
||||
// current group is incomplete and no one success -> continue pending
|
||||
if pending[nextGroup] > 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// all failed -> log and continue next group
|
||||
for j := g.start; j <= g.end; j++ {
|
||||
r := results[j]
|
||||
e := r.err
|
||||
if e == nil {
|
||||
e = dns.ErrEmptyResponse
|
||||
}
|
||||
errors.LogInfoInner(s.ctx, e, "failed to lookup ip for domain ", domain, " at server ", clients[j].Name(), " in parallel query mode")
|
||||
errs = append(errs, e)
|
||||
}
|
||||
nextGroup++
|
||||
}
|
||||
}
|
||||
|
||||
return nil, 0, mergeQueryErrors(domain, errs)
|
||||
}
|
||||
|
||||
type queryResult struct {
|
||||
ips []net.IP
|
||||
ttl uint32
|
||||
err error
|
||||
index int
|
||||
}
|
||||
|
||||
func asyncQueryAll(domain string, option dns.IPOption, clients []*Client, ctx context.Context) chan queryResult {
|
||||
if len(clients) == 0 {
|
||||
ch := make(chan queryResult)
|
||||
close(ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
ch := make(chan queryResult, len(clients))
|
||||
for i, client := range clients {
|
||||
if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") {
|
||||
errors.LogDebug(ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name())
|
||||
ch <- queryResult{err: dns.ErrEmptyResponse, index: i}
|
||||
continue
|
||||
}
|
||||
|
||||
go func(i int, c *Client) {
|
||||
qctx := ctx
|
||||
if !c.server.IsDisableCache() {
|
||||
nctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), c.timeoutMs*2)
|
||||
qctx = nctx
|
||||
defer cancel()
|
||||
}
|
||||
ips, ttl, err := c.QueryIP(qctx, domain, option)
|
||||
ch <- queryResult{ips: ips, ttl: ttl, err: err, index: i}
|
||||
}(i, client)
|
||||
}
|
||||
return ch
|
||||
}
|
||||
|
||||
type group struct{ start, end int }
|
||||
|
||||
// merge only adjacent and rule-equivalent Client into a single group
|
||||
func makeGroups( /*ctx context.Context,*/ clients []*Client) ([]group, []int) {
|
||||
n := len(clients)
|
||||
if n == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
groups := make([]group, 0, n)
|
||||
groupOf := make([]int, n)
|
||||
|
||||
s, e := 0, 0
|
||||
for i := 1; i < n; i++ {
|
||||
if clients[i-1].policyID == clients[i].policyID {
|
||||
e = i
|
||||
} else {
|
||||
for k := s; k <= e; k++ {
|
||||
groupOf[k] = len(groups)
|
||||
}
|
||||
groups = append(groups, group{start: s, end: e})
|
||||
s, e = i, i
|
||||
}
|
||||
}
|
||||
for k := s; k <= e; k++ {
|
||||
groupOf[k] = len(groups)
|
||||
}
|
||||
groups = append(groups, group{start: s, end: e})
|
||||
|
||||
// var b strings.Builder
|
||||
// b.WriteString("dns grouping: total clients=")
|
||||
// b.WriteString(strconv.Itoa(n))
|
||||
// b.WriteString(", groups=")
|
||||
// b.WriteString(strconv.Itoa(len(groups)))
|
||||
|
||||
// for gi, g := range groups {
|
||||
// b.WriteString("\n [")
|
||||
// b.WriteString(strconv.Itoa(g.start))
|
||||
// b.WriteString("..")
|
||||
// b.WriteString(strconv.Itoa(g.end))
|
||||
// b.WriteString("] gid=")
|
||||
// b.WriteString(strconv.Itoa(gi))
|
||||
// b.WriteString(" pid=")
|
||||
// b.WriteString(strconv.FormatUint(uint64(clients[g.start].policyID), 10))
|
||||
// b.WriteString(" members: ")
|
||||
|
||||
// for i := g.start; i <= g.end; i++ {
|
||||
// if i > g.start {
|
||||
// b.WriteString(", ")
|
||||
// }
|
||||
// b.WriteString(strconv.Itoa(i))
|
||||
// b.WriteByte(':')
|
||||
// b.WriteString(clients[i].Name())
|
||||
// }
|
||||
// }
|
||||
// errors.LogDebug(ctx, b.String())
|
||||
|
||||
return groups, groupOf
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
return New(ctx, config.(*Config))
|
||||
}))
|
||||
}
|
||||
|
||||
func checkSystemNetwork() (supportIPv4 bool, supportIPv6 bool) {
|
||||
conn4, err4 := net.Dial("udp4", "192.33.4.12:53")
|
||||
if err4 != nil {
|
||||
supportIPv4 = false
|
||||
} else {
|
||||
supportIPv4 = true
|
||||
conn4.Close()
|
||||
func probeRoutes() (ipv4 bool, ipv6 bool) {
|
||||
if conn, err := net.Dial("udp4", "192.33.4.12:53"); err == nil {
|
||||
ipv4 = true
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
conn6, err6 := net.Dial("udp6", "[2001:500:2::c]:53")
|
||||
if err6 != nil {
|
||||
supportIPv6 = false
|
||||
} else {
|
||||
supportIPv6 = true
|
||||
conn6.Close()
|
||||
if conn, err := net.Dial("udp6", "[2001:500:2::c]:53"); err == nil {
|
||||
ipv6 = true
|
||||
conn.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var routeCache struct {
|
||||
sync.Once
|
||||
sync.RWMutex
|
||||
expire time.Time
|
||||
ipv4, ipv6 bool
|
||||
}
|
||||
|
||||
func checkRoutes() (bool, bool) {
|
||||
if !isGUIPlatform {
|
||||
routeCache.Once.Do(func() {
|
||||
routeCache.ipv4, routeCache.ipv6 = probeRoutes()
|
||||
})
|
||||
return routeCache.ipv4, routeCache.ipv6
|
||||
}
|
||||
|
||||
routeCache.RWMutex.RLock()
|
||||
now := time.Now()
|
||||
if routeCache.expire.After(now) {
|
||||
routeCache.RWMutex.RUnlock()
|
||||
return routeCache.ipv4, routeCache.ipv6
|
||||
}
|
||||
routeCache.RWMutex.RUnlock()
|
||||
|
||||
routeCache.RWMutex.Lock()
|
||||
defer routeCache.RWMutex.Unlock()
|
||||
|
||||
now = time.Now()
|
||||
if routeCache.expire.After(now) { // double-check
|
||||
return routeCache.ipv4, routeCache.ipv6
|
||||
}
|
||||
routeCache.ipv4, routeCache.ipv6 = probeRoutes() // ~2ms
|
||||
routeCache.expire = now.Add(100 * time.Millisecond) // ttl
|
||||
return routeCache.ipv4, routeCache.ipv6
|
||||
}
|
||||
|
||||
var isGUIPlatform = detectGUIPlatform()
|
||||
|
||||
func detectGUIPlatform() bool {
|
||||
switch runtime.GOOS {
|
||||
case "android", "ios", "windows", "darwin":
|
||||
return true
|
||||
case "linux", "freebsd", "openbsd":
|
||||
if t := os.Getenv("XDG_SESSION_TYPE"); t == "wayland" || t == "x11" {
|
||||
return true
|
||||
}
|
||||
if os.Getenv("DISPLAY") != "" || os.Getenv("WAYLAND_DISPLAY") != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package dns
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -13,10 +14,12 @@ import (
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/core"
|
||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
)
|
||||
|
||||
// Fqdn normalizes domain make sure it ends with '.'
|
||||
// case-sensitive
|
||||
func Fqdn(domain string) string {
|
||||
if len(domain) > 0 && strings.HasSuffix(domain, ".") {
|
||||
return domain
|
||||
@@ -38,19 +41,14 @@ type IPRecord struct {
|
||||
RawHeader *dnsmessage.Header
|
||||
}
|
||||
|
||||
func (r *IPRecord) getIPs() ([]net.IP, uint32, error) {
|
||||
func (r *IPRecord) getIPs() ([]net.IP, int32, error) {
|
||||
if r == nil {
|
||||
return nil, 0, errRecordNotFound
|
||||
}
|
||||
untilExpire := time.Until(r.Expire).Seconds()
|
||||
if untilExpire <= 0 {
|
||||
return nil, 0, errRecordNotFound
|
||||
}
|
||||
|
||||
ttl := uint32(untilExpire) + 1
|
||||
if ttl == 1 {
|
||||
r.Expire = time.Now().Add(time.Second) // To ensure that two consecutive requests get the same result
|
||||
}
|
||||
untilExpire := time.Until(r.Expire).Seconds()
|
||||
ttl := int32(math.Ceil(untilExpire))
|
||||
|
||||
if r.RCode != dnsmessage.RCodeSuccess {
|
||||
return nil, ttl, dns_feature.RCodeError(r.RCode)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"math"
|
||||
"math/big"
|
||||
gonet "net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -17,7 +16,7 @@ import (
|
||||
|
||||
type Holder struct {
|
||||
domainToIP cache.Lru
|
||||
ipRange *gonet.IPNet
|
||||
ipRange *net.IPNet
|
||||
mu *sync.Mutex
|
||||
|
||||
config *FakeDnsPool
|
||||
@@ -79,10 +78,10 @@ func (fkdns *Holder) initializeFromConfig() error {
|
||||
}
|
||||
|
||||
func (fkdns *Holder) initialize(ipPoolCidr string, lruSize int) error {
|
||||
var ipRange *gonet.IPNet
|
||||
var ipRange *net.IPNet
|
||||
var err error
|
||||
|
||||
if _, ipRange, err = gonet.ParseCIDR(ipPoolCidr); err != nil {
|
||||
if _, ipRange, err = net.ParseCIDR(ipPoolCidr); err != nil {
|
||||
return errors.New("Unable to parse CIDR for Fake DNS IP assignment").Base(err).AtError()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package fakedns
|
||||
|
||||
import (
|
||||
gonet "net"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
@@ -155,7 +154,7 @@ func TestFakeDNSMulti(t *testing.T) {
|
||||
assert.True(t, inPool)
|
||||
})
|
||||
t.Run("ipv6", func(t *testing.T) {
|
||||
ip, err := gonet.ResolveIPAddr("ip", "fddd:c5b4:ff5f:f4f0::5")
|
||||
ip, err := net.ResolveIPAddr("ip", "fddd:c5b4:ff5f:f4f0::5")
|
||||
assert.Nil(t, err)
|
||||
inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP))
|
||||
assert.True(t, inPool)
|
||||
@@ -165,7 +164,7 @@ func TestFakeDNSMulti(t *testing.T) {
|
||||
assert.False(t, inPool)
|
||||
})
|
||||
t.Run("ipv6_inverse", func(t *testing.T) {
|
||||
ip, err := gonet.ResolveIPAddr("ip", "fcdd:c5b4:ff5f:f4f0::5")
|
||||
ip, err := net.ResolveIPAddr("ip", "fcdd:c5b4:ff5f:f4f0::5")
|
||||
assert.Nil(t, err)
|
||||
inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP))
|
||||
assert.False(t, inPool)
|
||||
|
||||
@@ -20,6 +20,9 @@ import (
|
||||
type Server interface {
|
||||
// Name of the Client.
|
||||
Name() string
|
||||
|
||||
IsDisableCache() bool
|
||||
|
||||
// QueryIP sends IP queries to its configured server.
|
||||
QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error)
|
||||
}
|
||||
@@ -29,8 +32,8 @@ type Client struct {
|
||||
server Server
|
||||
skipFallback bool
|
||||
domains []string
|
||||
expectedIPs []*router.GeoIPMatcher
|
||||
unexpectedIPs []*router.GeoIPMatcher
|
||||
expectedIPs router.GeoIPMatcher
|
||||
unexpectedIPs router.GeoIPMatcher
|
||||
actPrior bool
|
||||
actUnprior bool
|
||||
tag string
|
||||
@@ -38,10 +41,11 @@ type Client struct {
|
||||
finalQuery bool
|
||||
ipOption *dns.IPOption
|
||||
checkSystem bool
|
||||
policyID uint32
|
||||
}
|
||||
|
||||
// NewServer creates a name server object according to the network destination url.
|
||||
func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dispatcher, disableCache bool, clientIP net.IP) (Server, error) {
|
||||
func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dispatcher, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (Server, error) {
|
||||
if address := dest.Address; address.Family().IsDomain() {
|
||||
u, err := url.Parse(address.Domain())
|
||||
if err != nil {
|
||||
@@ -51,19 +55,19 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
|
||||
case strings.EqualFold(u.String(), "localhost"):
|
||||
return NewLocalNameServer(), nil
|
||||
case strings.EqualFold(u.Scheme, "https"): // DNS-over-HTTPS Remote mode
|
||||
return NewDoHNameServer(u, dispatcher, false, disableCache, clientIP), nil
|
||||
return NewDoHNameServer(u, dispatcher, false, disableCache, serveStale, serveExpiredTTL, clientIP), nil
|
||||
case strings.EqualFold(u.Scheme, "h2c"): // DNS-over-HTTPS h2c Remote mode
|
||||
return NewDoHNameServer(u, dispatcher, true, disableCache, clientIP), nil
|
||||
return NewDoHNameServer(u, dispatcher, true, disableCache, serveStale, serveExpiredTTL, clientIP), nil
|
||||
case strings.EqualFold(u.Scheme, "https+local"): // DNS-over-HTTPS Local mode
|
||||
return NewDoHNameServer(u, nil, false, disableCache, clientIP), nil
|
||||
return NewDoHNameServer(u, nil, false, disableCache, serveStale, serveExpiredTTL, clientIP), nil
|
||||
case strings.EqualFold(u.Scheme, "h2c+local"): // DNS-over-HTTPS h2c Local mode
|
||||
return NewDoHNameServer(u, nil, true, disableCache, clientIP), nil
|
||||
return NewDoHNameServer(u, nil, true, disableCache, serveStale, serveExpiredTTL, clientIP), nil
|
||||
case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode
|
||||
return NewQUICNameServer(u, disableCache, clientIP)
|
||||
return NewQUICNameServer(u, disableCache, serveStale, serveExpiredTTL, clientIP)
|
||||
case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode
|
||||
return NewTCPNameServer(u, dispatcher, disableCache, clientIP)
|
||||
return NewTCPNameServer(u, dispatcher, disableCache, serveStale, serveExpiredTTL, clientIP)
|
||||
case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode
|
||||
return NewTCPLocalNameServer(u, disableCache, clientIP)
|
||||
return NewTCPLocalNameServer(u, disableCache, serveStale, serveExpiredTTL, clientIP)
|
||||
case strings.EqualFold(u.String(), "fakedns"):
|
||||
var fd dns.FakeDNSEngine
|
||||
err = core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
|
||||
@@ -79,7 +83,7 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
|
||||
dest.Network = net.Network_UDP
|
||||
}
|
||||
if dest.Network == net.Network_UDP { // UDP classic DNS mode
|
||||
return NewClassicNameServer(dest, dispatcher, disableCache, clientIP), nil
|
||||
return NewClassicNameServer(dest, dispatcher, disableCache, serveStale, serveExpiredTTL, clientIP), nil
|
||||
}
|
||||
return nil, errors.New("No available name server could be created from ", dest).AtWarning()
|
||||
}
|
||||
@@ -89,7 +93,7 @@ func NewClient(
|
||||
ctx context.Context,
|
||||
ns *NameServer,
|
||||
clientIP net.IP,
|
||||
disableCache bool,
|
||||
disableCache bool, serveStale bool, serveExpiredTTL uint32,
|
||||
tag string,
|
||||
ipOption dns.IPOption,
|
||||
matcherInfos *[]*DomainMatcherInfo,
|
||||
@@ -99,7 +103,7 @@ func NewClient(
|
||||
|
||||
err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
|
||||
// Create a new server for each client for now
|
||||
server, err := NewServer(ctx, ns.Address.AsDestination(), dispatcher, disableCache, clientIP)
|
||||
server, err := NewServer(ctx, ns.Address.AsDestination(), dispatcher, disableCache, serveStale, serveExpiredTTL, clientIP)
|
||||
if err != nil {
|
||||
return errors.New("failed to create nameserver").Base(err).AtWarning()
|
||||
}
|
||||
@@ -154,23 +158,21 @@ func NewClient(
|
||||
}
|
||||
|
||||
// Establish expected IPs
|
||||
var expectedMatchers []*router.GeoIPMatcher
|
||||
for _, geoip := range ns.ExpectedGeoip {
|
||||
matcher, err := router.GlobalGeoIPContainer.Add(geoip)
|
||||
var expectedMatcher router.GeoIPMatcher
|
||||
if len(ns.ExpectedGeoip) > 0 {
|
||||
expectedMatcher, err = router.BuildOptimizedGeoIPMatcher(ns.ExpectedGeoip...)
|
||||
if err != nil {
|
||||
return errors.New("failed to create expected ip matcher").Base(err).AtWarning()
|
||||
}
|
||||
expectedMatchers = append(expectedMatchers, matcher)
|
||||
}
|
||||
|
||||
// Establish unexpected IPs
|
||||
var unexpectedMatchers []*router.GeoIPMatcher
|
||||
for _, geoip := range ns.UnexpectedGeoip {
|
||||
matcher, err := router.GlobalGeoIPContainer.Add(geoip)
|
||||
var unexpectedMatcher router.GeoIPMatcher
|
||||
if len(ns.UnexpectedGeoip) > 0 {
|
||||
unexpectedMatcher, err = router.BuildOptimizedGeoIPMatcher(ns.UnexpectedGeoip...)
|
||||
if err != nil {
|
||||
return errors.New("failed to create unexpected ip matcher").Base(err).AtWarning()
|
||||
}
|
||||
unexpectedMatchers = append(unexpectedMatchers, matcher)
|
||||
}
|
||||
|
||||
if len(clientIP) > 0 {
|
||||
@@ -192,8 +194,8 @@ func NewClient(
|
||||
client.server = server
|
||||
client.skipFallback = ns.SkipFallback
|
||||
client.domains = rules
|
||||
client.expectedIPs = expectedMatchers
|
||||
client.unexpectedIPs = unexpectedMatchers
|
||||
client.expectedIPs = expectedMatcher
|
||||
client.unexpectedIPs = unexpectedMatcher
|
||||
client.actPrior = ns.ActPrior
|
||||
client.actUnprior = ns.ActUnprior
|
||||
client.tag = tag
|
||||
@@ -201,6 +203,7 @@ func NewClient(
|
||||
client.finalQuery = ns.FinalQuery
|
||||
client.ipOption = &ipOption
|
||||
client.checkSystem = checkSystem
|
||||
client.policyID = ns.PolicyID
|
||||
return nil
|
||||
})
|
||||
return client, err
|
||||
@@ -211,14 +214,10 @@ func (c *Client) Name() string {
|
||||
return c.server.Name()
|
||||
}
|
||||
|
||||
func (c *Client) IsFinalQuery() bool {
|
||||
return c.finalQuery
|
||||
}
|
||||
|
||||
// QueryIP sends DNS query to the name server with the client's IP.
|
||||
func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error) {
|
||||
if c.checkSystem {
|
||||
supportIPv4, supportIPv6 := checkSystemNetwork()
|
||||
supportIPv4, supportIPv6 := checkRoutes()
|
||||
option.IPv4Enable = option.IPv4Enable && supportIPv4
|
||||
option.IPv6Enable = option.IPv6Enable && supportIPv6
|
||||
} else {
|
||||
@@ -243,32 +242,32 @@ func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption
|
||||
return nil, 0, dns.ErrEmptyResponse
|
||||
}
|
||||
|
||||
if len(c.expectedIPs) > 0 && !c.actPrior {
|
||||
ips = router.MatchIPs(c.expectedIPs, ips, false)
|
||||
if c.expectedIPs != nil && !c.actPrior {
|
||||
ips, _ = c.expectedIPs.FilterIPs(ips)
|
||||
errors.LogDebug(context.Background(), "domain ", domain, " expectedIPs ", ips, " matched at server ", c.Name())
|
||||
if len(ips) == 0 {
|
||||
return nil, 0, dns.ErrEmptyResponse
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.unexpectedIPs) > 0 && !c.actUnprior {
|
||||
ips = router.MatchIPs(c.unexpectedIPs, ips, true)
|
||||
if c.unexpectedIPs != nil && !c.actUnprior {
|
||||
_, ips = c.unexpectedIPs.FilterIPs(ips)
|
||||
errors.LogDebug(context.Background(), "domain ", domain, " unexpectedIPs ", ips, " matched at server ", c.Name())
|
||||
if len(ips) == 0 {
|
||||
return nil, 0, dns.ErrEmptyResponse
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.expectedIPs) > 0 && c.actPrior {
|
||||
ipsNew := router.MatchIPs(c.expectedIPs, ips, false)
|
||||
if c.expectedIPs != nil && c.actPrior {
|
||||
ipsNew, _ := c.expectedIPs.FilterIPs(ips)
|
||||
if len(ipsNew) > 0 {
|
||||
ips = ipsNew
|
||||
errors.LogDebug(context.Background(), "domain ", domain, " priorIPs ", ips, " matched at server ", c.Name())
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.unexpectedIPs) > 0 && c.actUnprior {
|
||||
ipsNew := router.MatchIPs(c.unexpectedIPs, ips, true)
|
||||
if c.unexpectedIPs != nil && c.actUnprior {
|
||||
_, ipsNew := c.unexpectedIPs.FilterIPs(ips)
|
||||
if len(ipsNew) > 0 {
|
||||
ips = ipsNew
|
||||
errors.LogDebug(context.Background(), "domain ", domain, " unpriorIPs ", ips, " matched at server ", c.Name())
|
||||
|
||||
173
app/dns/nameserver_cached.go
Normal file
173
app/dns/nameserver_cached.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
go_errors "errors"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/signal/pubsub"
|
||||
"github.com/xtls/xray-core/features/dns"
|
||||
)
|
||||
|
||||
type CachedNameserver interface {
|
||||
getCacheController() *CacheController
|
||||
|
||||
sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns.IPOption)
|
||||
}
|
||||
|
||||
// queryIP is called from dns.Server->queryIPTimeout
|
||||
func queryIP(ctx context.Context, s CachedNameserver, domain string, option dns.IPOption) ([]net.IP, uint32, error) {
|
||||
fqdn := Fqdn(domain)
|
||||
|
||||
cache := s.getCacheController()
|
||||
if !cache.disableCache {
|
||||
if rec := cache.findRecords(fqdn); rec != nil {
|
||||
ips, ttl, err := merge(option, rec.A, rec.AAAA)
|
||||
if !go_errors.Is(err, errRecordNotFound) {
|
||||
if ttl > 0 {
|
||||
errors.LogDebugInner(ctx, err, cache.name, " cache HIT ", fqdn, " -> ", ips)
|
||||
log.Record(&log.DNSLog{Server: cache.name, Domain: fqdn, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
|
||||
return ips, uint32(ttl), err
|
||||
}
|
||||
if cache.serveStale && (cache.serveExpiredTTL == 0 || cache.serveExpiredTTL < ttl) {
|
||||
errors.LogDebugInner(ctx, err, cache.name, " cache OPTIMISTE ", fqdn, " -> ", ips)
|
||||
log.Record(&log.DNSLog{Server: cache.name, Domain: fqdn, Result: ips, Status: log.DNSCacheOptimiste, Elapsed: 0, Error: err})
|
||||
go pull(ctx, s, fqdn, option)
|
||||
return ips, 1, err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", fqdn, " at ", cache.name)
|
||||
}
|
||||
|
||||
return fetch(ctx, s, fqdn, option)
|
||||
}
|
||||
|
||||
func pull(ctx context.Context, s CachedNameserver, fqdn string, option dns.IPOption) {
|
||||
nctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 8*time.Second)
|
||||
defer cancel()
|
||||
|
||||
fetch(nctx, s, fqdn, option)
|
||||
}
|
||||
|
||||
func fetch(ctx context.Context, s CachedNameserver, fqdn string, option dns.IPOption) ([]net.IP, uint32, error) {
|
||||
key := fqdn
|
||||
switch {
|
||||
case option.IPv4Enable && option.IPv6Enable:
|
||||
key = key + "46"
|
||||
case option.IPv4Enable:
|
||||
key = key + "4"
|
||||
case option.IPv6Enable:
|
||||
key = key + "6"
|
||||
}
|
||||
|
||||
v, _, _ := s.getCacheController().requestGroup.Do(key, func() (any, error) {
|
||||
return doFetch(ctx, s, fqdn, option), nil
|
||||
})
|
||||
ret := v.(result)
|
||||
|
||||
return ret.ips, ret.ttl, ret.error
|
||||
}
|
||||
|
||||
type result struct {
|
||||
ips []net.IP
|
||||
ttl uint32
|
||||
error
|
||||
}
|
||||
|
||||
func doFetch(ctx context.Context, s CachedNameserver, fqdn string, option dns.IPOption) result {
|
||||
sub4, sub6 := s.getCacheController().registerSubscribers(fqdn, option)
|
||||
defer closeSubscribers(sub4, sub6)
|
||||
|
||||
noResponseErrCh := make(chan error, 2)
|
||||
onEvent := func(sub *pubsub.Subscriber) (*IPRecord, error) {
|
||||
if sub == nil {
|
||||
return nil, nil
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case err := <-noResponseErrCh:
|
||||
return nil, err
|
||||
case msg := <-sub.Wait():
|
||||
sub.Close()
|
||||
return msg.(*IPRecord), nil // should panic
|
||||
}
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
|
||||
|
||||
rec4, err4 := onEvent(sub4)
|
||||
rec6, err6 := onEvent(sub6)
|
||||
|
||||
var errs []error
|
||||
if err4 != nil {
|
||||
errs = append(errs, err4)
|
||||
}
|
||||
if err6 != nil {
|
||||
errs = append(errs, err6)
|
||||
}
|
||||
|
||||
ips, ttl, err := merge(option, rec4, rec6, errs...)
|
||||
var rTTL uint32
|
||||
if ttl > 0 {
|
||||
rTTL = uint32(ttl)
|
||||
} else if ttl == 0 && go_errors.Is(err, errRecordNotFound) {
|
||||
rTTL = 0
|
||||
} else { // edge case: where a fast rep's ttl expires during the rtt of a slower, parallel query
|
||||
rTTL = 1
|
||||
}
|
||||
|
||||
log.Record(&log.DNSLog{Server: s.getCacheController().name, Domain: fqdn, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
||||
return result{ips, rTTL, err}
|
||||
}
|
||||
|
||||
func merge(option dns.IPOption, rec4 *IPRecord, rec6 *IPRecord, errs ...error) ([]net.IP, int32, error) {
|
||||
var allIPs []net.IP
|
||||
var rTTL int32 = dns.DefaultTTL
|
||||
|
||||
mergeReq := option.IPv4Enable && option.IPv6Enable
|
||||
|
||||
if option.IPv4Enable {
|
||||
ips, ttl, err := rec4.getIPs() // it's safe
|
||||
if !mergeReq || go_errors.Is(err, errRecordNotFound) {
|
||||
return ips, ttl, err
|
||||
}
|
||||
if ttl < rTTL {
|
||||
rTTL = ttl
|
||||
}
|
||||
if len(ips) > 0 {
|
||||
allIPs = append(allIPs, ips...)
|
||||
} else {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if option.IPv6Enable {
|
||||
ips, ttl, err := rec6.getIPs() // it's safe
|
||||
if !mergeReq || go_errors.Is(err, errRecordNotFound) {
|
||||
return ips, ttl, err
|
||||
}
|
||||
if ttl < rTTL {
|
||||
rTTL = ttl
|
||||
}
|
||||
if len(ips) > 0 {
|
||||
allIPs = append(allIPs, ips...)
|
||||
} else {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(allIPs) > 0 {
|
||||
return allIPs, rTTL, nil
|
||||
}
|
||||
if len(errs) == 2 && go_errors.Is(errs[0], errs[1]) {
|
||||
return nil, rTTL, errs[0]
|
||||
}
|
||||
return nil, rTTL, errors.Combine(errs...)
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
go_errors "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -38,7 +37,7 @@ type DoHNameServer struct {
|
||||
}
|
||||
|
||||
// NewDoHNameServer creates DOH/DOHL client object for remote/local resolving.
|
||||
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, h2c bool, disableCache bool, clientIP net.IP) *DoHNameServer {
|
||||
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, h2c bool, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) *DoHNameServer {
|
||||
url.Scheme = "https"
|
||||
mode := "DOH"
|
||||
if dispatcher == nil {
|
||||
@@ -46,7 +45,7 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, h2c bool, dis
|
||||
}
|
||||
errors.LogInfo(context.Background(), "DNS: created ", mode, " client for ", url.String(), ", with h2c ", h2c)
|
||||
s := &DoHNameServer{
|
||||
cacheController: NewCacheController(mode+"//"+url.Host, disableCache),
|
||||
cacheController: NewCacheController(mode+"//"+url.Host, disableCache, serveStale, serveExpiredTTL),
|
||||
dohURL: url.String(),
|
||||
clientIP: clientIP,
|
||||
}
|
||||
@@ -117,22 +116,35 @@ func (s *DoHNameServer) Name() string {
|
||||
return s.cacheController.name
|
||||
}
|
||||
|
||||
// IsDisableCache implements Server.
|
||||
func (s *DoHNameServer) IsDisableCache() bool {
|
||||
return s.cacheController.disableCache
|
||||
}
|
||||
|
||||
func (s *DoHNameServer) newReqID() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) {
|
||||
errors.LogInfo(ctx, s.Name(), " querying: ", domain)
|
||||
// getCacheController implements CachedNameserver.
|
||||
func (s *DoHNameServer) getCacheController() *CacheController {
|
||||
return s.cacheController
|
||||
}
|
||||
|
||||
if s.Name()+"." == "DOH//"+domain {
|
||||
errors.LogError(ctx, s.Name(), " tries to resolve itself! Use IP or set \"hosts\" instead.")
|
||||
noResponseErrCh <- errors.New("tries to resolve itself!", s.Name())
|
||||
// sendQuery implements CachedNameserver.
|
||||
func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
|
||||
errors.LogInfo(ctx, s.Name(), " querying: ", fqdn)
|
||||
|
||||
if s.Name()+"." == "DOH//"+fqdn {
|
||||
errors.LogError(ctx, s.Name(), " tries to resolve itself! Use IP or set \"hosts\" instead")
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- errors.New("tries to resolve itself!", s.Name())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// As we don't want our traffic pattern looks like DoH, we use Random-Length Padding instead of Block-Length Padding recommended in RFC 8467
|
||||
// Although DoH server like 1.1.1.1 will pad the response to Block-Length 468, at least it is better than no padding for response at all
|
||||
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, int(crypto.RandBetween(100, 300))))
|
||||
reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, int(crypto.RandBetween(100, 300))))
|
||||
|
||||
var deadline time.Time
|
||||
if d, ok := ctx.Deadline(); ok {
|
||||
@@ -166,23 +178,29 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
|
||||
|
||||
b, err := dns.PackMessage(r.msg)
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "failed to pack dns query for ", domain)
|
||||
noResponseErrCh <- err
|
||||
errors.LogErrorInner(ctx, err, "failed to pack dns query for ", fqdn)
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
resp, err := s.dohHTTPSContext(dnsCtx, b.Bytes())
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "failed to retrieve response for ", domain)
|
||||
noResponseErrCh <- err
|
||||
errors.LogErrorInner(ctx, err, "failed to retrieve response for ", fqdn)
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
rec, err := parseResponse(resp)
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "failed to handle DOH response for ", domain)
|
||||
noResponseErrCh <- err
|
||||
errors.LogErrorInner(ctx, err, "failed to handle DOH response for ", fqdn)
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
s.cacheController.updateIP(r, rec)
|
||||
s.cacheController.updateRecord(r, rec)
|
||||
}(req)
|
||||
}
|
||||
}
|
||||
@@ -216,49 +234,6 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
|
||||
}
|
||||
|
||||
// QueryIP implements Server.
|
||||
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { // nolint: dupl
|
||||
fqdn := Fqdn(domain)
|
||||
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
|
||||
defer closeSubscribers(sub4, sub6)
|
||||
|
||||
if s.cacheController.disableCache {
|
||||
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
|
||||
} else {
|
||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||
if !go_errors.Is(err, errRecordNotFound) {
|
||||
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
|
||||
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
|
||||
return ips, ttl, err
|
||||
}
|
||||
}
|
||||
|
||||
noResponseErrCh := make(chan error, 2)
|
||||
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
|
||||
start := time.Now()
|
||||
|
||||
if sub4 != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, 0, ctx.Err()
|
||||
case err := <-noResponseErrCh:
|
||||
return nil, 0, err
|
||||
case <-sub4.Wait():
|
||||
sub4.Close()
|
||||
}
|
||||
}
|
||||
if sub6 != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, 0, ctx.Err()
|
||||
case err := <-noResponseErrCh:
|
||||
return nil, 0, err
|
||||
case <-sub6.Wait():
|
||||
sub6.Close()
|
||||
}
|
||||
}
|
||||
|
||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
||||
return ips, ttl, err
|
||||
|
||||
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
|
||||
return queryIP(ctx, s, domain, option)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestDOHNameServer(t *testing.T) {
|
||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||
common.Must(err)
|
||||
|
||||
s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
|
||||
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil))
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
|
||||
IPv4Enable: true,
|
||||
@@ -34,7 +34,7 @@ func TestDOHNameServerWithCache(t *testing.T) {
|
||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||
common.Must(err)
|
||||
|
||||
s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
|
||||
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil))
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
|
||||
IPv4Enable: true,
|
||||
@@ -62,7 +62,7 @@ func TestDOHNameServerWithIPv4Override(t *testing.T) {
|
||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||
common.Must(err)
|
||||
|
||||
s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
|
||||
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil))
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
|
||||
IPv4Enable: true,
|
||||
@@ -85,7 +85,7 @@ func TestDOHNameServerWithIPv6Override(t *testing.T) {
|
||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||
common.Must(err)
|
||||
|
||||
s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
|
||||
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil))
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
|
||||
IPv4Enable: false,
|
||||
|
||||
@@ -20,6 +20,11 @@ func (FakeDNSServer) Name() string {
|
||||
return "FakeDNS"
|
||||
}
|
||||
|
||||
// IsDisableCache implements Server.
|
||||
func (s *FakeDNSServer) IsDisableCache() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, opt dns.IPOption) ([]net.IP, uint32, error) {
|
||||
if f.fakeDNSEngine == nil {
|
||||
return nil, 0, errors.New("Unable to locate a fake DNS Engine").AtError()
|
||||
|
||||
@@ -35,6 +35,11 @@ func (s *LocalNameServer) Name() string {
|
||||
return "localhost"
|
||||
}
|
||||
|
||||
// IsDisableCache implements Server.
|
||||
func (s *LocalNameServer) IsDisableCache() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// NewLocalNameServer creates localdns server object for directly lookup in system DNS.
|
||||
func NewLocalNameServer() *LocalNameServer {
|
||||
errors.LogInfo(context.Background(), "DNS: created localhost client")
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
go_errors "errors"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -37,9 +36,7 @@ type QUICNameServer struct {
|
||||
}
|
||||
|
||||
// NewQUICNameServer creates DNS-over-QUIC client object for local resolving
|
||||
func NewQUICNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*QUICNameServer, error) {
|
||||
errors.LogInfo(context.Background(), "DNS: created Local DNS-over-QUIC client for ", url.String())
|
||||
|
||||
func NewQUICNameServer(url *url.URL, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (*QUICNameServer, error) {
|
||||
var err error
|
||||
port := net.Port(853)
|
||||
if url.Port() != "" {
|
||||
@@ -51,27 +48,37 @@ func NewQUICNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*QUICN
|
||||
dest := net.UDPDestination(net.ParseAddress(url.Hostname()), port)
|
||||
|
||||
s := &QUICNameServer{
|
||||
cacheController: NewCacheController(url.String(), disableCache),
|
||||
cacheController: NewCacheController(url.String(), disableCache, serveStale, serveExpiredTTL),
|
||||
destination: &dest,
|
||||
clientIP: clientIP,
|
||||
}
|
||||
|
||||
errors.LogInfo(context.Background(), "DNS: created Local DNS-over-QUIC client for ", url.String())
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Name returns client name
|
||||
// Name implements Server.
|
||||
func (s *QUICNameServer) Name() string {
|
||||
return s.cacheController.name
|
||||
}
|
||||
|
||||
// IsDisableCache implements Server.
|
||||
func (s *QUICNameServer) IsDisableCache() bool {
|
||||
return s.cacheController.disableCache
|
||||
}
|
||||
|
||||
func (s *QUICNameServer) newReqID() uint16 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) {
|
||||
errors.LogInfo(ctx, s.Name(), " querying: ", domain)
|
||||
// getCacheController implements CachedNameServer.
|
||||
func (s *QUICNameServer) getCacheController() *CacheController { return s.cacheController }
|
||||
|
||||
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
|
||||
// sendQuery implements CachedNameServer.
|
||||
func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
|
||||
errors.LogInfo(ctx, s.Name(), " querying: ", fqdn)
|
||||
|
||||
reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
|
||||
|
||||
var deadline time.Time
|
||||
if d, ok := ctx.Deadline(); ok {
|
||||
@@ -103,7 +110,9 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
|
||||
b, err := dns.PackMessage(r.msg)
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "failed to pack dns query")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -111,13 +120,17 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
|
||||
err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len()))
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "binary write failed")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
_, err = dnsReqBuf.Write(b.Bytes())
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "buffer write failed")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
b.Release()
|
||||
@@ -125,14 +138,18 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
|
||||
conn, err := s.openStream(dnsCtx)
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "failed to open quic connection")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_, err = conn.Write(dnsReqBuf.Bytes())
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "failed to send query")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -143,81 +160,46 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
|
||||
n, err := respBuf.ReadFullFrom(conn, 2)
|
||||
if err != nil && n == 0 {
|
||||
errors.LogErrorInner(ctx, err, "failed to read response length")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
var length int16
|
||||
var length uint16
|
||||
err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "failed to parse response length")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
respBuf.Clear()
|
||||
n, err = respBuf.ReadFullFrom(conn, int32(length))
|
||||
if err != nil && n == 0 {
|
||||
errors.LogErrorInner(ctx, err, "failed to read response length")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
rec, err := parseResponse(respBuf.Bytes())
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "failed to handle response")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
s.cacheController.updateIP(r, rec)
|
||||
s.cacheController.updateRecord(r, rec)
|
||||
}(req)
|
||||
}
|
||||
}
|
||||
|
||||
// QueryIP is called from dns.Server->queryIPTimeout
|
||||
// QueryIP implements Server.
|
||||
func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
|
||||
fqdn := Fqdn(domain)
|
||||
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
|
||||
defer closeSubscribers(sub4, sub6)
|
||||
|
||||
if s.cacheController.disableCache {
|
||||
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
|
||||
} else {
|
||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||
if !go_errors.Is(err, errRecordNotFound) {
|
||||
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
|
||||
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
|
||||
return ips, ttl, err
|
||||
}
|
||||
}
|
||||
|
||||
noResponseErrCh := make(chan error, 2)
|
||||
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
|
||||
start := time.Now()
|
||||
|
||||
if sub4 != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, 0, ctx.Err()
|
||||
case err := <-noResponseErrCh:
|
||||
return nil, 0, err
|
||||
case <-sub4.Wait():
|
||||
sub4.Close()
|
||||
}
|
||||
}
|
||||
if sub6 != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, 0, ctx.Err()
|
||||
case err := <-noResponseErrCh:
|
||||
return nil, 0, err
|
||||
case <-sub6.Wait():
|
||||
sub6.Close()
|
||||
}
|
||||
}
|
||||
|
||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
||||
return ips, ttl, err
|
||||
|
||||
return queryIP(ctx, s, domain, option)
|
||||
}
|
||||
|
||||
func isActive(s *quic.Conn) bool {
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
func TestQUICNameServer(t *testing.T) {
|
||||
url, err := url.Parse("quic://dns.adguard-dns.com")
|
||||
common.Must(err)
|
||||
s, err := NewQUICNameServer(url, false, net.IP(nil))
|
||||
s, err := NewQUICNameServer(url, false, false, 0, net.IP(nil))
|
||||
common.Must(err)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
||||
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
|
||||
@@ -43,7 +43,7 @@ func TestQUICNameServer(t *testing.T) {
|
||||
func TestQUICNameServerWithIPv4Override(t *testing.T) {
|
||||
url, err := url.Parse("quic://dns.adguard-dns.com")
|
||||
common.Must(err)
|
||||
s, err := NewQUICNameServer(url, false, net.IP(nil))
|
||||
s, err := NewQUICNameServer(url, false, false, 0, net.IP(nil))
|
||||
common.Must(err)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
||||
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
|
||||
@@ -66,7 +66,7 @@ func TestQUICNameServerWithIPv4Override(t *testing.T) {
|
||||
func TestQUICNameServerWithIPv6Override(t *testing.T) {
|
||||
url, err := url.Parse("quic://dns.adguard-dns.com")
|
||||
common.Must(err)
|
||||
s, err := NewQUICNameServer(url, false, net.IP(nil))
|
||||
s, err := NewQUICNameServer(url, false, false, 0, net.IP(nil))
|
||||
common.Must(err)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
||||
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
|
||||
|
||||
@@ -4,14 +4,12 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
go_errors "errors"
|
||||
"net/url"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/common/protocol/dns"
|
||||
@@ -34,10 +32,10 @@ type TCPNameServer struct {
|
||||
func NewTCPNameServer(
|
||||
url *url.URL,
|
||||
dispatcher routing.Dispatcher,
|
||||
disableCache bool,
|
||||
disableCache bool, serveStale bool, serveExpiredTTL uint32,
|
||||
clientIP net.IP,
|
||||
) (*TCPNameServer, error) {
|
||||
s, err := baseTCPNameServer(url, "TCP", disableCache, clientIP)
|
||||
s, err := baseTCPNameServer(url, "TCP", disableCache, serveStale, serveExpiredTTL, clientIP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -54,12 +52,13 @@ func NewTCPNameServer(
|
||||
), nil
|
||||
}
|
||||
|
||||
errors.LogInfo(context.Background(), "DNS: created TCP client initialized for ", url.String())
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// NewTCPLocalNameServer creates DNS over TCP client object for local resolving
|
||||
func NewTCPLocalNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*TCPNameServer, error) {
|
||||
s, err := baseTCPNameServer(url, "TCPL", disableCache, clientIP)
|
||||
func NewTCPLocalNameServer(url *url.URL, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (*TCPNameServer, error) {
|
||||
s, err := baseTCPNameServer(url, "TCPL", disableCache, serveStale, serveExpiredTTL, clientIP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -68,10 +67,11 @@ func NewTCPLocalNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*T
|
||||
return internet.DialSystem(ctx, *s.destination, nil)
|
||||
}
|
||||
|
||||
errors.LogInfo(context.Background(), "DNS: created Local TCP client initialized for ", url.String())
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, clientIP net.IP) (*TCPNameServer, error) {
|
||||
func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (*TCPNameServer, error) {
|
||||
port := net.Port(53)
|
||||
if url.Port() != "" {
|
||||
var err error
|
||||
@@ -82,7 +82,7 @@ func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, clientIP
|
||||
dest := net.TCPDestination(net.ParseAddress(url.Hostname()), port)
|
||||
|
||||
s := &TCPNameServer{
|
||||
cacheController: NewCacheController(prefix+"//"+dest.NetAddr(), disableCache),
|
||||
cacheController: NewCacheController(prefix+"//"+dest.NetAddr(), disableCache, serveStale, serveExpiredTTL),
|
||||
destination: &dest,
|
||||
clientIP: clientIP,
|
||||
}
|
||||
@@ -95,14 +95,25 @@ func (s *TCPNameServer) Name() string {
|
||||
return s.cacheController.name
|
||||
}
|
||||
|
||||
// IsDisableCache implements Server.
|
||||
func (s *TCPNameServer) IsDisableCache() bool {
|
||||
return s.cacheController.disableCache
|
||||
}
|
||||
|
||||
func (s *TCPNameServer) newReqID() uint16 {
|
||||
return uint16(atomic.AddUint32(&s.reqID, 1))
|
||||
}
|
||||
|
||||
func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) {
|
||||
errors.LogDebug(ctx, s.Name(), " querying DNS for: ", domain)
|
||||
// getCacheController implements CachedNameserver.
|
||||
func (s *TCPNameServer) getCacheController() *CacheController {
|
||||
return s.cacheController
|
||||
}
|
||||
|
||||
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
|
||||
// sendQuery implements CachedNameserver.
|
||||
func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
|
||||
errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn)
|
||||
|
||||
reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
|
||||
|
||||
var deadline time.Time
|
||||
if d, ok := ctx.Deadline(); ok {
|
||||
@@ -131,14 +142,18 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
|
||||
b, err := dns.PackMessage(r.msg)
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "failed to pack dns query")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := s.dial(dnsCtx)
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "failed to dial namesever")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
@@ -146,13 +161,17 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
|
||||
err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len()))
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "binary write failed")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
_, err = dnsReqBuf.Write(b.Bytes())
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "buffer write failed")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
b.Release()
|
||||
@@ -160,7 +179,9 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
|
||||
_, err = conn.Write(dnsReqBuf.Bytes())
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "failed to send query")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
dnsReqBuf.Release()
|
||||
@@ -170,80 +191,45 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
|
||||
n, err := respBuf.ReadFullFrom(conn, 2)
|
||||
if err != nil && n == 0 {
|
||||
errors.LogErrorInner(ctx, err, "failed to read response length")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
var length int16
|
||||
var length uint16
|
||||
err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "failed to parse response length")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
respBuf.Clear()
|
||||
n, err = respBuf.ReadFullFrom(conn, int32(length))
|
||||
if err != nil && n == 0 {
|
||||
errors.LogErrorInner(ctx, err, "failed to read response length")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
rec, err := parseResponse(respBuf.Bytes())
|
||||
if err != nil {
|
||||
errors.LogErrorInner(ctx, err, "failed to parse DNS over TCP response")
|
||||
noResponseErrCh <- err
|
||||
if noResponseErrCh != nil {
|
||||
noResponseErrCh <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
s.cacheController.updateIP(r, rec)
|
||||
s.cacheController.updateRecord(r, rec)
|
||||
}(req)
|
||||
}
|
||||
}
|
||||
|
||||
// QueryIP implements Server.
|
||||
func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
|
||||
fqdn := Fqdn(domain)
|
||||
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
|
||||
defer closeSubscribers(sub4, sub6)
|
||||
|
||||
if s.cacheController.disableCache {
|
||||
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
|
||||
} else {
|
||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||
if !go_errors.Is(err, errRecordNotFound) {
|
||||
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
|
||||
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
|
||||
return ips, ttl, err
|
||||
}
|
||||
}
|
||||
|
||||
noResponseErrCh := make(chan error, 2)
|
||||
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
|
||||
start := time.Now()
|
||||
|
||||
if sub4 != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, 0, ctx.Err()
|
||||
case err := <-noResponseErrCh:
|
||||
return nil, 0, err
|
||||
case <-sub4.Wait():
|
||||
sub4.Close()
|
||||
}
|
||||
}
|
||||
if sub6 != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, 0, ctx.Err()
|
||||
case err := <-noResponseErrCh:
|
||||
return nil, 0, err
|
||||
case <-sub6.Wait():
|
||||
sub6.Close()
|
||||
}
|
||||
}
|
||||
|
||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
||||
return ips, ttl, err
|
||||
|
||||
return queryIP(ctx, s, domain, option)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
func TestTCPLocalNameServer(t *testing.T) {
|
||||
url, err := url.Parse("tcp+local://8.8.8.8")
|
||||
common.Must(err)
|
||||
s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
|
||||
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil))
|
||||
common.Must(err)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
|
||||
@@ -33,7 +33,7 @@ func TestTCPLocalNameServer(t *testing.T) {
|
||||
func TestTCPLocalNameServerWithCache(t *testing.T) {
|
||||
url, err := url.Parse("tcp+local://8.8.8.8")
|
||||
common.Must(err)
|
||||
s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
|
||||
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil))
|
||||
common.Must(err)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
|
||||
@@ -61,7 +61,7 @@ func TestTCPLocalNameServerWithCache(t *testing.T) {
|
||||
func TestTCPLocalNameServerWithIPv4Override(t *testing.T) {
|
||||
url, err := url.Parse("tcp+local://8.8.8.8")
|
||||
common.Must(err)
|
||||
s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
|
||||
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil))
|
||||
common.Must(err)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
|
||||
@@ -85,7 +85,7 @@ func TestTCPLocalNameServerWithIPv4Override(t *testing.T) {
|
||||
func TestTCPLocalNameServerWithIPv6Override(t *testing.T) {
|
||||
url, err := url.Parse("tcp+local://8.8.8.8")
|
||||
common.Must(err)
|
||||
s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
|
||||
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil))
|
||||
common.Must(err)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
|
||||
|
||||
@@ -2,7 +2,6 @@ package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
go_errors "errors"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -10,7 +9,6 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol/dns"
|
||||
udp_proto "github.com/xtls/xray-core/common/protocol/udp"
|
||||
@@ -39,14 +37,14 @@ type udpDnsRequest struct {
|
||||
}
|
||||
|
||||
// NewClassicNameServer creates udp server object for remote resolving.
|
||||
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, disableCache bool, clientIP net.IP) *ClassicNameServer {
|
||||
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) *ClassicNameServer {
|
||||
// default to 53 if unspecific
|
||||
if address.Port == 0 {
|
||||
address.Port = net.Port(53)
|
||||
}
|
||||
|
||||
s := &ClassicNameServer{
|
||||
cacheController: NewCacheController(strings.ToUpper(address.String()), disableCache),
|
||||
cacheController: NewCacheController(strings.ToUpper(address.String()), disableCache, serveStale, serveExpiredTTL),
|
||||
address: &address,
|
||||
requests: make(map[uint16]*udpDnsRequest),
|
||||
clientIP: clientIP,
|
||||
@@ -56,6 +54,7 @@ func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher
|
||||
Execute: s.RequestsCleanup,
|
||||
}
|
||||
s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse)
|
||||
|
||||
errors.LogInfo(context.Background(), "DNS: created UDP client initialized for ", address.NetAddr())
|
||||
return s
|
||||
}
|
||||
@@ -65,6 +64,11 @@ func (s *ClassicNameServer) Name() string {
|
||||
return s.cacheController.name
|
||||
}
|
||||
|
||||
// IsDisableCache implements Server.
|
||||
func (s *ClassicNameServer) IsDisableCache() bool {
|
||||
return s.cacheController.disableCache
|
||||
}
|
||||
|
||||
// RequestsCleanup clears expired items from cache
|
||||
func (s *ClassicNameServer) RequestsCleanup() error {
|
||||
now := time.Now()
|
||||
@@ -94,7 +98,7 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
|
||||
ipRec, err := parseResponse(payload.Bytes())
|
||||
payload.Release()
|
||||
if err != nil {
|
||||
errors.LogError(ctx, s.Name(), " fail to parse responded DNS udp")
|
||||
errors.LogErrorInner(ctx, err, s.Name(), " fail to parse responded DNS udp")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -107,7 +111,7 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
|
||||
}
|
||||
s.Unlock()
|
||||
if !ok {
|
||||
errors.LogError(ctx, s.Name(), " cannot find the pending request")
|
||||
errors.LogErrorInner(ctx, err, s.Name(), " cannot find the pending request")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -134,7 +138,7 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
|
||||
}
|
||||
}
|
||||
|
||||
s.cacheController.updateIP(&req.dnsRequest, ipRec)
|
||||
s.cacheController.updateRecord(&req.dnsRequest, ipRec)
|
||||
}
|
||||
|
||||
func (s *ClassicNameServer) newReqID() uint16 {
|
||||
@@ -150,10 +154,16 @@ func (s *ClassicNameServer) addPendingRequest(req *udpDnsRequest) {
|
||||
common.Must(s.requestsCleanup.Start())
|
||||
}
|
||||
|
||||
func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, domain string, option dns_feature.IPOption) {
|
||||
errors.LogDebug(ctx, s.Name(), " querying DNS for: ", domain)
|
||||
// getCacheController implements CachedNameserver.
|
||||
func (s *ClassicNameServer) getCacheController() *CacheController {
|
||||
return s.cacheController
|
||||
}
|
||||
|
||||
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
|
||||
// sendQuery implements CachedNameserver.
|
||||
func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, fqdn string, option dns_feature.IPOption) {
|
||||
errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn)
|
||||
|
||||
reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
|
||||
|
||||
for _, req := range reqs {
|
||||
udpReq := &udpDnsRequest{
|
||||
@@ -170,48 +180,5 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, domai
|
||||
|
||||
// QueryIP implements Server.
|
||||
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
|
||||
fqdn := Fqdn(domain)
|
||||
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
|
||||
defer closeSubscribers(sub4, sub6)
|
||||
|
||||
if s.cacheController.disableCache {
|
||||
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
|
||||
} else {
|
||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||
if !go_errors.Is(err, errRecordNotFound) {
|
||||
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
|
||||
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
|
||||
return ips, ttl, err
|
||||
}
|
||||
}
|
||||
|
||||
noResponseErrCh := make(chan error, 2)
|
||||
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
|
||||
start := time.Now()
|
||||
|
||||
if sub4 != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, 0, ctx.Err()
|
||||
case err := <-noResponseErrCh:
|
||||
return nil, 0, err
|
||||
case <-sub4.Wait():
|
||||
sub4.Close()
|
||||
}
|
||||
}
|
||||
if sub6 != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, 0, ctx.Err()
|
||||
case err := <-noResponseErrCh:
|
||||
return nil, 0, err
|
||||
case <-sub6.Wait():
|
||||
sub6.Close()
|
||||
}
|
||||
}
|
||||
|
||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
||||
return ips, ttl, err
|
||||
|
||||
return queryIP(ctx, s, domain, option)
|
||||
}
|
||||
|
||||
@@ -1,23 +1 @@
|
||||
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
|
||||
}
|
||||
|
||||
@@ -23,58 +23,6 @@ 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
|
||||
@@ -111,71 +59,6 @@ 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
|
||||
@@ -196,7 +79,7 @@ type SniffingConfig struct {
|
||||
|
||||
func (x *SniffingConfig) Reset() {
|
||||
*x = SniffingConfig{}
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[2]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -208,7 +91,7 @@ func (x *SniffingConfig) String() string {
|
||||
func (*SniffingConfig) ProtoMessage() {}
|
||||
|
||||
func (x *SniffingConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[2]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -221,7 +104,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{2}
|
||||
return file_app_proxyman_config_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *SniffingConfig) GetEnabled() bool {
|
||||
@@ -268,15 +151,14 @@ 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"`
|
||||
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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
func (x *ReceiverConfig) Reset() {
|
||||
*x = ReceiverConfig{}
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[3]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -288,7 +170,7 @@ func (x *ReceiverConfig) String() string {
|
||||
func (*ReceiverConfig) ProtoMessage() {}
|
||||
|
||||
func (x *ReceiverConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[3]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -301,7 +183,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{3}
|
||||
return file_app_proxyman_config_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *ReceiverConfig) GetPortList() *net.PortList {
|
||||
@@ -318,13 +200,6 @@ 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
|
||||
@@ -358,7 +233,7 @@ type InboundHandlerConfig struct {
|
||||
|
||||
func (x *InboundHandlerConfig) Reset() {
|
||||
*x = InboundHandlerConfig{}
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[4]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -370,7 +245,7 @@ func (x *InboundHandlerConfig) String() string {
|
||||
func (*InboundHandlerConfig) ProtoMessage() {}
|
||||
|
||||
func (x *InboundHandlerConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[4]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -383,7 +258,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{4}
|
||||
return file_app_proxyman_config_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *InboundHandlerConfig) GetTag() string {
|
||||
@@ -415,7 +290,7 @@ type OutboundConfig struct {
|
||||
|
||||
func (x *OutboundConfig) Reset() {
|
||||
*x = OutboundConfig{}
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[5]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -427,7 +302,7 @@ func (x *OutboundConfig) String() string {
|
||||
func (*OutboundConfig) ProtoMessage() {}
|
||||
|
||||
func (x *OutboundConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[5]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -440,7 +315,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{5}
|
||||
return file_app_proxyman_config_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
type SenderConfig struct {
|
||||
@@ -459,7 +334,7 @@ type SenderConfig struct {
|
||||
|
||||
func (x *SenderConfig) Reset() {
|
||||
*x = SenderConfig{}
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[6]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -471,7 +346,7 @@ func (x *SenderConfig) String() string {
|
||||
func (*SenderConfig) ProtoMessage() {}
|
||||
|
||||
func (x *SenderConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[6]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[5]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -484,7 +359,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{6}
|
||||
return file_app_proxyman_config_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *SenderConfig) GetVia() *net.IPOrDomain {
|
||||
@@ -546,7 +421,7 @@ type MultiplexingConfig struct {
|
||||
|
||||
func (x *MultiplexingConfig) Reset() {
|
||||
*x = MultiplexingConfig{}
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[7]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -558,7 +433,7 @@ func (x *MultiplexingConfig) String() string {
|
||||
func (*MultiplexingConfig) ProtoMessage() {}
|
||||
|
||||
func (x *MultiplexingConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[7]
|
||||
mi := &file_app_proxyman_config_proto_msgTypes[6]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -571,7 +446,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{7}
|
||||
return file_app_proxyman_config_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *MultiplexingConfig) GetEnabled() bool {
|
||||
@@ -602,96 +477,6 @@ 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{
|
||||
@@ -706,130 +491,98 @@ 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, 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, 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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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 (
|
||||
@@ -844,48 +597,39 @@ func file_app_proxyman_config_proto_rawDescGZIP() []byte {
|
||||
return file_app_proxyman_config_proto_rawDescData
|
||||
}
|
||||
|
||||
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_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||
var file_app_proxyman_config_proto_goTypes = []any{
|
||||
(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
|
||||
(internet.DomainStrategy)(0), // 16: xray.transport.internet.DomainStrategy
|
||||
(*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
|
||||
}
|
||||
var file_app_proxyman_config_proto_depIdxs = []int32{
|
||||
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
|
||||
16, // 14: xray.app.proxyman.SenderConfig.target_strategy:type_name -> xray.transport.internet.DomainStrategy
|
||||
15, // [15:15] is the sub-list for method output_type
|
||||
15, // [15:15] is the sub-list for method input_type
|
||||
15, // [15:15] is the sub-list for extension type_name
|
||||
15, // [15:15] is the sub-list for extension extendee
|
||||
0, // [0:15] is the sub-list for field type_name
|
||||
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
|
||||
}
|
||||
|
||||
func init() { file_app_proxyman_config_proto_init() }
|
||||
@@ -898,14 +642,13 @@ func file_app_proxyman_config_proto_init() {
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_app_proxyman_config_proto_rawDesc,
|
||||
NumEnums: 1,
|
||||
NumMessages: 10,
|
||||
NumEnums: 0,
|
||||
NumMessages: 7,
|
||||
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
|
||||
|
||||
@@ -13,33 +13,6 @@ 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;
|
||||
@@ -62,11 +35,10 @@ 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;
|
||||
AllocationStrategy allocation_strategy = 3;
|
||||
xray.transport.internet.StreamConfig stream_settings = 4;
|
||||
bool receive_original_destination = 5;
|
||||
reserved 6;
|
||||
SniffingConfig sniffing_settings = 7;
|
||||
xray.transport.internet.StreamConfig stream_settings = 3;
|
||||
bool receive_original_destination = 4;
|
||||
reserved 5;
|
||||
SniffingConfig sniffing_settings = 6;
|
||||
}
|
||||
|
||||
message InboundHandlerConfig {
|
||||
|
||||
@@ -5,7 +5,6 @@ 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"
|
||||
@@ -103,7 +102,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
|
||||
stream: mss,
|
||||
tag: tag,
|
||||
dispatcher: h.mux,
|
||||
sniffingConfig: receiverConfig.GetEffectiveSniffingSettings(),
|
||||
sniffingConfig: receiverConfig.SniffingSettings,
|
||||
uplinkCounter: uplinkCounter,
|
||||
downlinkCounter: downlinkCounter,
|
||||
ctx: ctx,
|
||||
@@ -125,7 +124,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
|
||||
recvOrigDest: receiverConfig.ReceiveOriginalDestination,
|
||||
tag: tag,
|
||||
dispatcher: h.mux,
|
||||
sniffingConfig: receiverConfig.GetEffectiveSniffingSettings(),
|
||||
sniffingConfig: receiverConfig.SniffingSettings,
|
||||
uplinkCounter: uplinkCounter,
|
||||
downlinkCounter: downlinkCounter,
|
||||
ctx: ctx,
|
||||
@@ -140,7 +139,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
|
||||
address: address,
|
||||
port: net.Port(port),
|
||||
dispatcher: h.mux,
|
||||
sniffingConfig: receiverConfig.GetEffectiveSniffingSettings(),
|
||||
sniffingConfig: receiverConfig.SniffingSettings,
|
||||
uplinkCounter: uplinkCounter,
|
||||
downlinkCounter: downlinkCounter,
|
||||
stream: mss,
|
||||
@@ -178,14 +177,6 @@ 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
|
||||
}
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -178,15 +178,7 @@ func NewHandler(ctx context.Context, config *core.InboundHandlerConfig) (inbound
|
||||
ctx = session.ContextWithAllowedNetwork(ctx, net.Network_UDP)
|
||||
}
|
||||
|
||||
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()
|
||||
return NewAlwaysOnInboundHandler(ctx, tag, receiverSettings, proxySettings)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -76,7 +76,25 @@ 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
|
||||
}
|
||||
}
|
||||
@@ -544,3 +562,18 @@ func (w *dsWorker) Close() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsLocal(ip net.IP) bool {
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
if ipnet, ok := addr.(*net.IPNet); ok {
|
||||
if ipnet.IP.Equal(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
goerrors "errors"
|
||||
"io"
|
||||
"math/big"
|
||||
gonet "net"
|
||||
"os"
|
||||
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
@@ -108,6 +107,8 @@ 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
|
||||
@@ -315,8 +316,12 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
|
||||
conn, err := internet.Dial(ctx, dest, h.streamSettings)
|
||||
conn = h.getStatCouterConnection(conn)
|
||||
outbounds := session.OutboundsFromContext(ctx)
|
||||
ob := outbounds[len(outbounds)-1]
|
||||
ob.Conn = conn
|
||||
if outbounds != nil {
|
||||
ob := outbounds[len(outbounds)-1]
|
||||
ob.Conn = conn
|
||||
} else {
|
||||
// for Vision's pre-connect
|
||||
}
|
||||
return conn, err
|
||||
}
|
||||
|
||||
@@ -392,7 +397,7 @@ func (h *Handler) ProxySettings() *serial.TypedMessage {
|
||||
|
||||
func ParseRandomIP(addr net.Address, prefix string) net.Address {
|
||||
|
||||
_, ipnet, _ := gonet.ParseCIDR(addr.IP().String() + "/" + prefix)
|
||||
_, ipnet, _ := net.ParseCIDR(addr.IP().String() + "/" + prefix)
|
||||
|
||||
ones, bits := ipnet.Mask.Size()
|
||||
subnetSize := new(big.Int).Lsh(big.NewInt(1), uint(bits-ones))
|
||||
@@ -406,5 +411,5 @@ func ParseRandomIP(addr net.Address, prefix string) net.Address {
|
||||
padded := make([]byte, len(ipnet.IP))
|
||||
copy(padded[len(padded)-len(rndBytes):], rndBytes)
|
||||
|
||||
return net.ParseAddress(gonet.IP(padded).String())
|
||||
return net.ParseAddress(net.IP(padded).String())
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"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"
|
||||
@@ -52,6 +53,11 @@ 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) {
|
||||
@@ -93,10 +99,11 @@ func (b *Bridge) Close() error {
|
||||
}
|
||||
|
||||
type BridgeWorker struct {
|
||||
tag string
|
||||
worker *mux.ServerWorker
|
||||
dispatcher routing.Dispatcher
|
||||
state Control_State
|
||||
Tag string
|
||||
Worker *mux.ServerWorker
|
||||
Dispatcher routing.Dispatcher
|
||||
State Control_State
|
||||
Timer *signal.ActivityTimer
|
||||
}
|
||||
|
||||
func NewBridgeWorker(domain string, tag string, d routing.Dispatcher) (*BridgeWorker, error) {
|
||||
@@ -114,16 +121,20 @@ 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
|
||||
}
|
||||
|
||||
@@ -140,48 +151,65 @@ func (w *BridgeWorker) Close() error {
|
||||
}
|
||||
|
||||
func (w *BridgeWorker) IsActive() bool {
|
||||
return w.state == Control_ACTIVE && !w.worker.Closed()
|
||||
return w.State == Control_ACTIVE && !w.Worker.Closed()
|
||||
}
|
||||
|
||||
func (w *BridgeWorker) Closed() bool {
|
||||
return w.Worker.Closed()
|
||||
}
|
||||
|
||||
func (w *BridgeWorker) Connections() uint32 {
|
||||
return w.worker.ActiveConnections()
|
||||
return w.Worker.ActiveConnections()
|
||||
}
|
||||
|
||||
func (w *BridgeWorker) handleInternalConn(link *transport.Link) {
|
||||
go func() {
|
||||
reader := link.Reader
|
||||
for {
|
||||
mb, err := reader.ReadMultiBuffer()
|
||||
if err != nil {
|
||||
break
|
||||
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)
|
||||
}
|
||||
}
|
||||
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) {
|
||||
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||||
Tag: w.tag,
|
||||
})
|
||||
return w.dispatcher.Dispatch(ctx, dest)
|
||||
if session.InboundFromContext(ctx) == nil {
|
||||
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...)
|
||||
|
||||
w.handleInternalConn(&transport.Link{
|
||||
go w.handleInternalConn(&transport.Link{
|
||||
Reader: downlinkReader,
|
||||
Writer: uplinkWriter,
|
||||
})
|
||||
@@ -194,12 +222,17 @@ 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) {
|
||||
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||||
Tag: w.tag,
|
||||
})
|
||||
return w.dispatcher.DispatchLink(ctx, dest, link)
|
||||
if session.InboundFromContext(ctx) == nil {
|
||||
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||||
Tag: w.Tag,
|
||||
})
|
||||
}
|
||||
return w.Dispatcher.DispatchLink(ctx, dest, link)
|
||||
}
|
||||
|
||||
if d, ok := w.Dispatcher.(routing.WrapLinkDispatcher); ok {
|
||||
link = d.WrapLink(ctx, link)
|
||||
}
|
||||
w.handleInternalConn(link)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -12,6 +12,7 @@ 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"
|
||||
@@ -82,9 +83,21 @@ 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)
|
||||
}
|
||||
|
||||
@@ -101,6 +114,7 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +160,8 @@ func (p *StaticMuxPicker) cleanup() error {
|
||||
for _, w := range p.workers {
|
||||
if !w.Closed() {
|
||||
activeWorkers = append(activeWorkers, w)
|
||||
} else {
|
||||
w.timer.SetTimeout(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,6 +228,7 @@ type PortalWorker struct {
|
||||
reader buf.Reader
|
||||
draining bool
|
||||
counter uint32
|
||||
timer *signal.ActivityTimer
|
||||
}
|
||||
|
||||
func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) {
|
||||
@@ -231,10 +248,14 @@ 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,
|
||||
@@ -261,7 +282,6 @@ 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
|
||||
@@ -273,6 +293,7 @@ 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
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
internalDomain = "reverse.internal.v2fly.org" // make reverse proxy compatible with v2fly
|
||||
internalDomain = "reverse"
|
||||
)
|
||||
|
||||
func isDomain(dest net.Destination, domain string) bool {
|
||||
|
||||
@@ -47,20 +47,6 @@ var matcherTypeMap = map[Domain_Type]strmatcher.Type{
|
||||
Domain_Full: strmatcher.Full,
|
||||
}
|
||||
|
||||
func domainToMatcher(domain *Domain) (strmatcher.Matcher, error) {
|
||||
matcherType, f := matcherTypeMap[domain.Type]
|
||||
if !f {
|
||||
return nil, errors.New("unsupported domain type", domain.Type)
|
||||
}
|
||||
|
||||
matcher, err := matcherType.New(domain.Value)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to create domain matcher").Base(err)
|
||||
}
|
||||
|
||||
return matcher, nil
|
||||
}
|
||||
|
||||
type DomainMatcher struct {
|
||||
matchers strmatcher.IndexMatcher
|
||||
}
|
||||
@@ -96,61 +82,53 @@ func (m *DomainMatcher) Apply(ctx routing.Context) bool {
|
||||
return m.ApplyDomain(domain)
|
||||
}
|
||||
|
||||
type MultiGeoIPMatcher struct {
|
||||
matchers []*GeoIPMatcher
|
||||
asType string // local, source, target
|
||||
type MatcherAsType byte
|
||||
|
||||
const (
|
||||
MatcherAsType_Local MatcherAsType = iota
|
||||
MatcherAsType_Source
|
||||
MatcherAsType_Target
|
||||
MatcherAsType_VlessRoute // for port
|
||||
)
|
||||
|
||||
type IPMatcher struct {
|
||||
matcher GeoIPMatcher
|
||||
asType MatcherAsType
|
||||
}
|
||||
|
||||
func NewMultiGeoIPMatcher(geoips []*GeoIP, asType string) (*MultiGeoIPMatcher, error) {
|
||||
var matchers []*GeoIPMatcher
|
||||
for _, geoip := range geoips {
|
||||
matcher, err := GlobalGeoIPContainer.Add(geoip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matchers = append(matchers, matcher)
|
||||
func NewIPMatcher(geoips []*GeoIP, asType MatcherAsType) (*IPMatcher, error) {
|
||||
matcher, err := BuildOptimizedGeoIPMatcher(geoips...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
matcher := &MultiGeoIPMatcher{
|
||||
matchers: matchers,
|
||||
asType: asType,
|
||||
}
|
||||
|
||||
return matcher, nil
|
||||
return &IPMatcher{matcher: matcher, asType: asType}, nil
|
||||
}
|
||||
|
||||
// Apply implements Condition.
|
||||
func (m *MultiGeoIPMatcher) Apply(ctx routing.Context) bool {
|
||||
func (m *IPMatcher) Apply(ctx routing.Context) bool {
|
||||
var ips []net.IP
|
||||
|
||||
switch m.asType {
|
||||
case "local":
|
||||
case MatcherAsType_Local:
|
||||
ips = ctx.GetLocalIPs()
|
||||
case "source":
|
||||
case MatcherAsType_Source:
|
||||
ips = ctx.GetSourceIPs()
|
||||
case "target":
|
||||
case MatcherAsType_Target:
|
||||
ips = ctx.GetTargetIPs()
|
||||
default:
|
||||
panic("unreachable, asType should be local or source or target")
|
||||
panic("unk asType")
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
for _, matcher := range m.matchers {
|
||||
if matcher.Match(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
return m.matcher.AnyMatch(ips)
|
||||
}
|
||||
|
||||
type PortMatcher struct {
|
||||
port net.MemoryPortList
|
||||
asType string // local, source, target
|
||||
asType MatcherAsType
|
||||
}
|
||||
|
||||
// NewPortMatcher create a new port matcher that can match source or local or destination port
|
||||
func NewPortMatcher(list *net.PortList, asType string) *PortMatcher {
|
||||
func NewPortMatcher(list *net.PortList, asType MatcherAsType) *PortMatcher {
|
||||
return &PortMatcher{
|
||||
port: net.PortListFromProto(list),
|
||||
asType: asType,
|
||||
@@ -160,18 +138,17 @@ func NewPortMatcher(list *net.PortList, asType string) *PortMatcher {
|
||||
// Apply implements Condition.
|
||||
func (v *PortMatcher) Apply(ctx routing.Context) bool {
|
||||
switch v.asType {
|
||||
case "local":
|
||||
case MatcherAsType_Local:
|
||||
return v.port.Contains(ctx.GetLocalPort())
|
||||
case "source":
|
||||
case MatcherAsType_Source:
|
||||
return v.port.Contains(ctx.GetSourcePort())
|
||||
case "target":
|
||||
case MatcherAsType_Target:
|
||||
return v.port.Contains(ctx.GetTargetPort())
|
||||
case "vlessRoute":
|
||||
case MatcherAsType_VlessRoute:
|
||||
return v.port.Contains(ctx.GetVlessRoute())
|
||||
default:
|
||||
panic("unreachable, asType should be local or source or target")
|
||||
panic("unk asType")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type NetworkMatcher struct {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -35,33 +35,6 @@ func getAssetPath(file string) (string, error) {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func TestGeoIPMatcherContainer(t *testing.T) {
|
||||
container := &router.GeoIPMatcherContainer{}
|
||||
|
||||
m1, err := container.Add(&router.GeoIP{
|
||||
CountryCode: "CN",
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
m2, err := container.Add(&router.GeoIP{
|
||||
CountryCode: "US",
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
m3, err := container.Add(&router.GeoIP{
|
||||
CountryCode: "CN",
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
if m1 != m3 {
|
||||
t.Error("expect same matcher for same geoip, but not")
|
||||
}
|
||||
|
||||
if m1 == m2 {
|
||||
t.Error("expect different matcher for different geoip, but actually same")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeoIPMatcher(t *testing.T) {
|
||||
cidrList := []*router.CIDR{
|
||||
{Ip: []byte{0, 0, 0, 0}, Prefix: 8},
|
||||
@@ -80,8 +53,10 @@ func TestGeoIPMatcher(t *testing.T) {
|
||||
{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
|
||||
}
|
||||
|
||||
matcher := &router.GeoIPMatcher{}
|
||||
common.Must(matcher.Init(cidrList))
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: cidrList,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
testCases := []struct {
|
||||
Input string
|
||||
@@ -140,8 +115,10 @@ func TestGeoIPMatcherRegression(t *testing.T) {
|
||||
{Ip: []byte{98, 108, 20, 0}, Prefix: 23},
|
||||
}
|
||||
|
||||
matcher := &router.GeoIPMatcher{}
|
||||
common.Must(matcher.Init(cidrList))
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: cidrList,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
testCases := []struct {
|
||||
Input string
|
||||
@@ -171,9 +148,11 @@ func TestGeoIPReverseMatcher(t *testing.T) {
|
||||
{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
|
||||
{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
|
||||
}
|
||||
matcher := &router.GeoIPMatcher{}
|
||||
matcher.SetReverseMatch(true) // Reverse match
|
||||
common.Must(matcher.Init(cidrList))
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: cidrList,
|
||||
})
|
||||
common.Must(err)
|
||||
matcher.SetReverse(true) // Reverse match
|
||||
|
||||
testCases := []struct {
|
||||
Input string
|
||||
@@ -206,8 +185,10 @@ func TestGeoIPMatcher4CN(t *testing.T) {
|
||||
ips, err := loadGeoIP("CN")
|
||||
common.Must(err)
|
||||
|
||||
matcher := &router.GeoIPMatcher{}
|
||||
common.Must(matcher.Init(ips))
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: ips,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
if matcher.Match([]byte{8, 8, 8, 8}) {
|
||||
t.Error("expect CN geoip doesn't contain 8.8.8.8, but actually does")
|
||||
@@ -218,8 +199,10 @@ func TestGeoIPMatcher6US(t *testing.T) {
|
||||
ips, err := loadGeoIP("US")
|
||||
common.Must(err)
|
||||
|
||||
matcher := &router.GeoIPMatcher{}
|
||||
common.Must(matcher.Init(ips))
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: ips,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
if !matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP()) {
|
||||
t.Error("expect US geoip contain 2001:4860:4860::8888, but actually not")
|
||||
@@ -254,8 +237,10 @@ func BenchmarkGeoIPMatcher4CN(b *testing.B) {
|
||||
ips, err := loadGeoIP("CN")
|
||||
common.Must(err)
|
||||
|
||||
matcher := &router.GeoIPMatcher{}
|
||||
common.Must(matcher.Init(ips))
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: ips,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
@@ -268,8 +253,10 @@ func BenchmarkGeoIPMatcher6US(b *testing.B) {
|
||||
ips, err := loadGeoIP("US")
|
||||
common.Must(err)
|
||||
|
||||
matcher := &router.GeoIPMatcher{}
|
||||
common.Must(matcher.Init(ips))
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: ips,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
|
||||
@@ -447,7 +447,7 @@ func BenchmarkMultiGeoIPMatcher(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
matcher, err := NewMultiGeoIPMatcher(geoips, "target")
|
||||
matcher, err := NewIPMatcher(geoips, MatcherAsType_Target)
|
||||
common.Must(err)
|
||||
|
||||
ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)})
|
||||
|
||||
@@ -32,72 +32,38 @@ func (r *Rule) Apply(ctx routing.Context) bool {
|
||||
func (rr *RoutingRule) BuildCondition() (Condition, error) {
|
||||
conds := NewConditionChan()
|
||||
|
||||
if len(rr.Domain) > 0 {
|
||||
matcher, err := NewMphMatcherGroup(rr.Domain)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to build domain condition with MphDomainMatcher").Base(err)
|
||||
}
|
||||
errors.LogDebug(context.Background(), "MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)")
|
||||
conds.Add(matcher)
|
||||
}
|
||||
|
||||
if len(rr.UserEmail) > 0 {
|
||||
conds.Add(NewUserMatcher(rr.UserEmail))
|
||||
}
|
||||
|
||||
if rr.VlessRouteList != nil {
|
||||
conds.Add(NewPortMatcher(rr.VlessRouteList, "vlessRoute"))
|
||||
}
|
||||
|
||||
if len(rr.InboundTag) > 0 {
|
||||
conds.Add(NewInboundTagMatcher(rr.InboundTag))
|
||||
}
|
||||
|
||||
if rr.PortList != nil {
|
||||
conds.Add(NewPortMatcher(rr.PortList, "target"))
|
||||
}
|
||||
|
||||
if rr.SourcePortList != nil {
|
||||
conds.Add(NewPortMatcher(rr.SourcePortList, "source"))
|
||||
}
|
||||
|
||||
if rr.LocalPortList != nil {
|
||||
conds.Add(NewPortMatcher(rr.LocalPortList, "local"))
|
||||
}
|
||||
|
||||
if len(rr.Networks) > 0 {
|
||||
conds.Add(NewNetworkMatcher(rr.Networks))
|
||||
}
|
||||
|
||||
if len(rr.Geoip) > 0 {
|
||||
cond, err := NewMultiGeoIPMatcher(rr.Geoip, "target")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conds.Add(cond)
|
||||
}
|
||||
|
||||
if len(rr.SourceGeoip) > 0 {
|
||||
cond, err := NewMultiGeoIPMatcher(rr.SourceGeoip, "source")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conds.Add(cond)
|
||||
}
|
||||
|
||||
if len(rr.LocalGeoip) > 0 {
|
||||
cond, err := NewMultiGeoIPMatcher(rr.LocalGeoip, "local")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conds.Add(cond)
|
||||
errors.LogWarning(context.Background(), "Due to some limitations, in UDP connections, localIP is always equal to listen interface IP, so \"localIP\" rule condition does not work properly on UDP inbound connections that listen on all interfaces")
|
||||
}
|
||||
|
||||
if len(rr.Protocol) > 0 {
|
||||
conds.Add(NewProtocolMatcher(rr.Protocol))
|
||||
}
|
||||
|
||||
if rr.PortList != nil {
|
||||
conds.Add(NewPortMatcher(rr.PortList, MatcherAsType_Target))
|
||||
}
|
||||
|
||||
if rr.SourcePortList != nil {
|
||||
conds.Add(NewPortMatcher(rr.SourcePortList, MatcherAsType_Source))
|
||||
}
|
||||
|
||||
if rr.LocalPortList != nil {
|
||||
conds.Add(NewPortMatcher(rr.LocalPortList, MatcherAsType_Local))
|
||||
}
|
||||
|
||||
if rr.VlessRouteList != nil {
|
||||
conds.Add(NewPortMatcher(rr.VlessRouteList, MatcherAsType_VlessRoute))
|
||||
}
|
||||
|
||||
if len(rr.UserEmail) > 0 {
|
||||
conds.Add(NewUserMatcher(rr.UserEmail))
|
||||
}
|
||||
|
||||
if len(rr.Attributes) > 0 {
|
||||
configuredKeys := make(map[string]*regexp.Regexp)
|
||||
for key, value := range rr.Attributes {
|
||||
@@ -106,6 +72,40 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
|
||||
conds.Add(&AttributeMatcher{configuredKeys})
|
||||
}
|
||||
|
||||
if len(rr.Geoip) > 0 {
|
||||
cond, err := NewIPMatcher(rr.Geoip, MatcherAsType_Target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conds.Add(cond)
|
||||
}
|
||||
|
||||
if len(rr.SourceGeoip) > 0 {
|
||||
cond, err := NewIPMatcher(rr.SourceGeoip, MatcherAsType_Source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conds.Add(cond)
|
||||
}
|
||||
|
||||
if len(rr.LocalGeoip) > 0 {
|
||||
cond, err := NewIPMatcher(rr.LocalGeoip, MatcherAsType_Local)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conds.Add(cond)
|
||||
errors.LogWarning(context.Background(), "Due to some limitations, in UDP connections, localIP is always equal to listen interface IP, so \"localIP\" rule condition does not work properly on UDP inbound connections that listen on all interfaces")
|
||||
}
|
||||
|
||||
if len(rr.Domain) > 0 {
|
||||
matcher, err := NewMphMatcherGroup(rr.Domain)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to build domain condition with MphDomainMatcher").Base(err)
|
||||
}
|
||||
errors.LogDebug(context.Background(), "MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)")
|
||||
conds.Add(matcher)
|
||||
}
|
||||
|
||||
if conds.Len() == 0 {
|
||||
return nil, errors.New("this rule has no effective fields").AtWarning()
|
||||
}
|
||||
|
||||
@@ -84,8 +84,6 @@ type Config_DomainStrategy int32
|
||||
const (
|
||||
// Use domain as is.
|
||||
Config_AsIs Config_DomainStrategy = 0
|
||||
// Always resolve IP for domains.
|
||||
Config_UseIp Config_DomainStrategy = 1
|
||||
// Resolve to IP if the domain doesn't match any rules.
|
||||
Config_IpIfNonMatch Config_DomainStrategy = 2
|
||||
// Resolve to IP if any rule requires IP matching.
|
||||
@@ -96,13 +94,11 @@ const (
|
||||
var (
|
||||
Config_DomainStrategy_name = map[int32]string{
|
||||
0: "AsIs",
|
||||
1: "UseIp",
|
||||
2: "IpIfNonMatch",
|
||||
3: "IpOnDemand",
|
||||
}
|
||||
Config_DomainStrategy_value = map[string]int32{
|
||||
"AsIs": 0,
|
||||
"UseIp": 1,
|
||||
"IpIfNonMatch": 2,
|
||||
"IpOnDemand": 3,
|
||||
}
|
||||
@@ -1171,7 +1167,7 @@ var file_app_router_config_proto_rawDesc = []byte{
|
||||
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,
|
||||
0x02, 0x52, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x90, 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,
|
||||
@@ -1185,17 +1181,16 @@ var file_app_router_config_proto_rawDesc = []byte{
|
||||
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,
|
||||
0x65, 0x22, 0x3c, 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, 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 (
|
||||
|
||||
@@ -147,8 +147,8 @@ message Config {
|
||||
// Use domain as is.
|
||||
AsIs = 0;
|
||||
|
||||
// Always resolve IP for domains.
|
||||
UseIp = 1;
|
||||
// [Deprecated] Always resolve IP for domains.
|
||||
// UseIp = 1;
|
||||
|
||||
// Resolve to IP if the domain doesn't match any rules.
|
||||
IpIfNonMatch = 2;
|
||||
|
||||
@@ -15,8 +15,6 @@ const (
|
||||
|
||||
var ErrBufferFull = errors.New("buffer is full")
|
||||
|
||||
var zero = [Size * 10]byte{0}
|
||||
|
||||
var pool = bytespool.GetPool(Size)
|
||||
|
||||
// ownership represents the data owner of the buffer.
|
||||
@@ -146,7 +144,7 @@ func (b *Buffer) Bytes() []byte {
|
||||
}
|
||||
|
||||
// Extend increases the buffer size by n bytes, and returns the extended part.
|
||||
// It panics if result size is larger than buf.Size.
|
||||
// It panics if result size is larger than size of this buffer.
|
||||
func (b *Buffer) Extend(n int32) []byte {
|
||||
end := b.end + n
|
||||
if end > int32(len(b.v)) {
|
||||
@@ -154,7 +152,7 @@ func (b *Buffer) Extend(n int32) []byte {
|
||||
}
|
||||
ext := b.v[b.end:end]
|
||||
b.end = end
|
||||
copy(ext, zero[:])
|
||||
clear(ext)
|
||||
return ext
|
||||
}
|
||||
|
||||
@@ -217,7 +215,7 @@ func (b *Buffer) Resize(from, to int32) {
|
||||
b.start += from
|
||||
b.Check()
|
||||
if b.end > oldEnd {
|
||||
copy(b.v[oldEnd:b.end], zero[:])
|
||||
clear(b.v[oldEnd:b.end])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ type TimeoutReader interface {
|
||||
|
||||
type TimeoutWrapperReader struct {
|
||||
Reader
|
||||
stats.Counter
|
||||
mb MultiBuffer
|
||||
err error
|
||||
done chan struct{}
|
||||
@@ -39,11 +40,16 @@ 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 = nil
|
||||
r.err = nil
|
||||
return r.Reader.ReadMultiBuffer()
|
||||
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) {
|
||||
@@ -54,12 +60,19 @@ func (r *TimeoutWrapperReader) ReadMultiBufferTimeout(duration time.Duration) (M
|
||||
close(r.done)
|
||||
}()
|
||||
}
|
||||
time.Sleep(duration)
|
||||
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
|
||||
default:
|
||||
case <-timeout:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,9 +75,10 @@ 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
|
||||
writer Writer
|
||||
buffer *Buffer
|
||||
buffered bool
|
||||
flushNext bool
|
||||
}
|
||||
|
||||
// NewBufferedWriter creates a new BufferedWriter.
|
||||
@@ -161,6 +162,12 @@ func (w *BufferedWriter) WriteMultiBuffer(b MultiBuffer) error {
|
||||
}
|
||||
}
|
||||
|
||||
if w.flushNext {
|
||||
w.buffered = false
|
||||
w.flushNext = false
|
||||
return w.flushInternal()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -201,6 +208,13 @@ func (w *BufferedWriter) SetBuffered(f bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetFlushNext will wait the next WriteMultiBuffer to flush and set buffered = false
|
||||
func (w *BufferedWriter) SetFlushNext() {
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
w.flushNext = true
|
||||
}
|
||||
|
||||
// ReadFrom implements io.ReaderFrom.
|
||||
func (w *BufferedWriter) ReadFrom(reader io.Reader) (int64, error) {
|
||||
if err := w.SetBuffered(false); err != nil {
|
||||
|
||||
@@ -10,6 +10,9 @@ func RandBetween(from int64, to int64) int64 {
|
||||
if from == to {
|
||||
return from
|
||||
}
|
||||
if from > to {
|
||||
from, to = to, from
|
||||
}
|
||||
bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from))
|
||||
return from + bigInt.Int64()
|
||||
}
|
||||
|
||||
@@ -43,8 +43,9 @@ func (l *DNSLog) String() string {
|
||||
type dnsStatus string
|
||||
|
||||
var (
|
||||
DNSQueried = dnsStatus("got answer:")
|
||||
DNSCacheHit = dnsStatus("cache HIT:")
|
||||
DNSQueried = dnsStatus("got answer:")
|
||||
DNSCacheHit = dnsStatus("cache HIT:")
|
||||
DNSCacheOptimiste = dnsStatus("cache OPTIMISTE:")
|
||||
)
|
||||
|
||||
func joinNetIP(ips []net.IP) string {
|
||||
|
||||
@@ -215,14 +215,20 @@ func (m *ClientWorker) Closed() bool {
|
||||
return m.done.Done()
|
||||
}
|
||||
|
||||
func (m *ClientWorker) GetTimer() *time.Ticker {
|
||||
return m.timer
|
||||
func (m *ClientWorker) WaitClosed() <-chan struct{} {
|
||||
return m.done.Wait()
|
||||
}
|
||||
|
||||
func (m *ClientWorker) Close() error {
|
||||
return m.done.Close()
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -230,8 +236,7 @@ func (m *ClientWorker) monitor() {
|
||||
common.Interrupt(m.link.Reader)
|
||||
return
|
||||
case <-m.timer.C:
|
||||
size := m.sessionManager.Size()
|
||||
if size == 0 && m.sessionManager.CloseIfNoSession() {
|
||||
if m.sessionManager.CloseIfNoSessionAndIdle(checkSize, checkCount) {
|
||||
common.Must(m.done.Close())
|
||||
}
|
||||
}
|
||||
@@ -251,7 +256,7 @@ func writeFirstPayload(reader buf.Reader, writer *Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func fetchInput(ctx context.Context, s *Session, output buf.Writer, timer *time.Ticker) {
|
||||
func fetchInput(ctx context.Context, s *Session, output buf.Writer) {
|
||||
outbounds := session.OutboundsFromContext(ctx)
|
||||
ob := outbounds[len(outbounds)-1]
|
||||
transferType := protocol.TransferTypeStream
|
||||
@@ -259,10 +264,13 @@ func fetchInput(ctx context.Context, s *Session, output buf.Writer, timer *time.
|
||||
transferType = protocol.TransferTypePacket
|
||||
}
|
||||
s.transferType = transferType
|
||||
writer := NewWriter(s.ID, ob.Target, output, transferType, xudp.GetGlobalID(ctx))
|
||||
var inbound *session.Inbound
|
||||
if session.IsReverseMuxFromContext(ctx) {
|
||||
inbound = session.InboundFromContext(ctx)
|
||||
}
|
||||
writer := NewWriter(s.ID, ob.Target, output, transferType, xudp.GetGlobalID(ctx), inbound)
|
||||
defer s.Close(false)
|
||||
defer writer.Close()
|
||||
defer timer.Reset(time.Second * 16)
|
||||
|
||||
errors.LogInfo(ctx, "dispatching request to ", ob.Target)
|
||||
if err := writeFirstPayload(s.input, writer); err != nil {
|
||||
@@ -312,10 +320,12 @@ func (m *ClientWorker) Dispatch(ctx context.Context, link *transport.Link) bool
|
||||
}
|
||||
s.input = link.Reader
|
||||
s.output = link.Writer
|
||||
if _, ok := link.Reader.(*pipe.Reader); ok {
|
||||
go fetchInput(ctx, s, m.link.Writer, m.timer)
|
||||
} else {
|
||||
fetchInput(ctx, s, m.link.Writer, m.timer)
|
||||
go fetchInput(ctx, s, m.link.Writer)
|
||||
if _, ok := link.Reader.(*pipe.Reader); !ok {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-s.done.Wait():
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -378,7 +388,7 @@ func (m *ClientWorker) fetchOutput() {
|
||||
|
||||
var meta FrameMetadata
|
||||
for {
|
||||
err := meta.Unmarshal(reader)
|
||||
err := meta.Unmarshal(reader, false)
|
||||
if err != nil {
|
||||
if errors.Cause(err) != io.EOF {
|
||||
errors.LogInfoInner(context.Background(), err, "failed to read metadata")
|
||||
|
||||
@@ -11,6 +11,7 @@ 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
|
||||
@@ -60,6 +61,7 @@ type FrameMetadata struct {
|
||||
Option bitmask.Byte
|
||||
SessionStatus SessionStatus
|
||||
GlobalID [8]byte
|
||||
Inbound *session.Inbound
|
||||
}
|
||||
|
||||
func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
||||
@@ -79,11 +81,23 @@ 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 b.UDP != nil { // make sure it's user's proxy request
|
||||
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
|
||||
b.Write(f.GlobalID[:]) // no need to check whether it's empty
|
||||
}
|
||||
} else if b.UDP != nil {
|
||||
@@ -97,7 +111,7 @@ func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
||||
}
|
||||
|
||||
// Unmarshal reads FrameMetadata from the given reader.
|
||||
func (f *FrameMetadata) Unmarshal(reader io.Reader) error {
|
||||
func (f *FrameMetadata) Unmarshal(reader io.Reader, readSourceAndLocal bool) error {
|
||||
metaLen, err := serial.ReadUint16(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -112,12 +126,12 @@ func (f *FrameMetadata) Unmarshal(reader io.Reader) error {
|
||||
if _, err := b.ReadFullFrom(reader, int32(metaLen)); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.UnmarshalFromBuffer(b)
|
||||
return f.UnmarshalFromBuffer(b, readSourceAndLocal)
|
||||
}
|
||||
|
||||
// UnmarshalFromBuffer reads a FrameMetadata from the given buffer.
|
||||
// Visible for testing only.
|
||||
func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error {
|
||||
func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer, readSourceAndLocal bool) error {
|
||||
if b.Len() < 4 {
|
||||
return errors.New("insufficient buffer: ", b.Len())
|
||||
}
|
||||
@@ -150,6 +164,54 @@ func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -10,6 +10,7 @@ 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"
|
||||
)
|
||||
|
||||
@@ -32,13 +33,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{})
|
||||
writer := NewWriter(1, dest, pWriter, protocol.TransferTypeStream, [8]byte{}, &session.Inbound{})
|
||||
|
||||
dest2 := net.TCPDestination(net.LocalHostIP, 443)
|
||||
writer2 := NewWriter(2, dest2, pWriter, protocol.TransferTypeStream, [8]byte{})
|
||||
writer2 := NewWriter(2, dest2, pWriter, protocol.TransferTypeStream, [8]byte{}, &session.Inbound{})
|
||||
|
||||
dest3 := net.TCPDestination(net.LocalHostIPv6, 18374)
|
||||
writer3 := NewWriter(3, dest3, pWriter, protocol.TransferTypeStream, [8]byte{})
|
||||
writer3 := NewWriter(3, dest3, pWriter, protocol.TransferTypeStream, [8]byte{}, &session.Inbound{})
|
||||
|
||||
writePayload := func(writer *Writer, payload ...byte) error {
|
||||
b := buf.New()
|
||||
@@ -62,7 +63,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionID: 1,
|
||||
SessionStatus: SessionStatusNew,
|
||||
@@ -81,7 +82,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionStatus: SessionStatusNew,
|
||||
SessionID: 2,
|
||||
@@ -94,7 +95,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionID: 1,
|
||||
SessionStatus: SessionStatusKeep,
|
||||
@@ -112,7 +113,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionID: 3,
|
||||
SessionStatus: SessionStatusNew,
|
||||
@@ -131,7 +132,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionID: 1,
|
||||
SessionStatus: SessionStatusEnd,
|
||||
@@ -143,7 +144,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionID: 3,
|
||||
SessionStatus: SessionStatusEnd,
|
||||
@@ -155,7 +156,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionID: 2,
|
||||
SessionStatus: SessionStatusKeep,
|
||||
@@ -173,7 +174,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
common.Must(meta.Unmarshal(bytesReader))
|
||||
common.Must(meta.Unmarshal(bytesReader, false))
|
||||
if r := cmp.Diff(meta, FrameMetadata{
|
||||
SessionID: 2,
|
||||
SessionStatus: SessionStatusEnd,
|
||||
@@ -187,7 +188,7 @@ func TestReaderWriter(t *testing.T) {
|
||||
|
||||
{
|
||||
var meta FrameMetadata
|
||||
err := meta.Unmarshal(bytesReader)
|
||||
err := meta.Unmarshal(bytesReader, false)
|
||||
if err == nil {
|
||||
t.Error("nil error")
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package mux
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
@@ -11,6 +12,7 @@ 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"
|
||||
@@ -61,8 +63,18 @@ func (s *Server) DispatchLink(ctx context.Context, dest net.Destination, link *t
|
||||
if dest.Address != muxCoolAddress {
|
||||
return s.dispatcher.DispatchLink(ctx, dest, link)
|
||||
}
|
||||
_, err := NewServerWorker(ctx, s.dispatcher, link)
|
||||
return err
|
||||
if d, ok := s.dispatcher.(routing.WrapLinkDispatcher); ok {
|
||||
link = d.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
|
||||
}
|
||||
|
||||
// Start implements common.Runnable.
|
||||
@@ -79,6 +91,8 @@ 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) {
|
||||
@@ -86,15 +100,14 @@ 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
|
||||
}
|
||||
if _, ok := link.Reader.(*pipe.Reader); ok {
|
||||
go worker.run(ctx)
|
||||
} else {
|
||||
worker.run(ctx)
|
||||
}
|
||||
go worker.run(ctx)
|
||||
go worker.monitor()
|
||||
return worker, nil
|
||||
}
|
||||
|
||||
@@ -109,12 +122,40 @@ 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.sessionManager.Closed()
|
||||
return w.done.Done()
|
||||
}
|
||||
|
||||
func (w *ServerWorker) WaitClosed() <-chan struct{} {
|
||||
return w.done.Wait()
|
||||
}
|
||||
|
||||
func (w *ServerWorker) Close() error {
|
||||
return w.done.Close()
|
||||
}
|
||||
|
||||
func (w *ServerWorker) handleStatusKeepAlive(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||
@@ -126,6 +167,14 @@ 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)
|
||||
}
|
||||
}
|
||||
errors.LogInfo(ctx, "received request for ", meta.Target)
|
||||
{
|
||||
msg := &log.AccessMessage{
|
||||
@@ -289,7 +338,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)
|
||||
err := meta.Unmarshal(reader, session.IsReverseMuxFromContext(ctx))
|
||||
if err != nil {
|
||||
return errors.New("failed to read metadata").Base(err)
|
||||
}
|
||||
@@ -300,7 +349,7 @@ func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedRead
|
||||
case SessionStatusEnd:
|
||||
err = w.handleStatusEnd(&meta, reader)
|
||||
case SessionStatusNew:
|
||||
err = w.handleStatusNew(ctx, &meta, reader)
|
||||
err = w.handleStatusNew(session.ContextWithIsReverseMux(ctx, false), &meta, reader)
|
||||
case SessionStatusKeep:
|
||||
err = w.handleStatusKeep(&meta, reader)
|
||||
default:
|
||||
@@ -315,11 +364,11 @@ func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedRead
|
||||
}
|
||||
|
||||
func (w *ServerWorker) run(ctx context.Context) {
|
||||
reader := &buf.BufferedReader{Reader: w.link.Reader}
|
||||
defer func() {
|
||||
common.Must(w.done.Close())
|
||||
}()
|
||||
|
||||
defer w.sessionManager.Close()
|
||||
defer common.Interrupt(w.link.Reader)
|
||||
defer common.Interrupt(w.link.Writer)
|
||||
reader := &buf.BufferedReader{Reader: w.link.Reader}
|
||||
|
||||
for {
|
||||
select {
|
||||
|
||||
@@ -12,6 +12,7 @@ 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"
|
||||
)
|
||||
|
||||
@@ -53,7 +54,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)
|
||||
|
||||
@@ -65,6 +66,7 @@ func (m *SessionManager) Allocate(Strategy *ClientStrategy) *Session {
|
||||
s := &Session{
|
||||
ID: m.count,
|
||||
parent: m,
|
||||
done: done.New(),
|
||||
}
|
||||
m.sessions[s.ID] = s
|
||||
return s
|
||||
@@ -115,7 +117,7 @@ func (m *SessionManager) Get(id uint16) (*Session, bool) {
|
||||
return s, found
|
||||
}
|
||||
|
||||
func (m *SessionManager) CloseIfNoSession() bool {
|
||||
func (m *SessionManager) CloseIfNoSessionAndIdle(checkSize int, checkCount int) bool {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
@@ -123,11 +125,13 @@ func (m *SessionManager) CloseIfNoSession() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(m.sessions) != 0 {
|
||||
if len(m.sessions) != 0 || checkSize != 0 || checkCount != int(m.count) {
|
||||
return false
|
||||
}
|
||||
|
||||
m.closed = true
|
||||
|
||||
m.sessions = nil
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -157,6 +161,7 @@ type Session struct {
|
||||
ID uint16
|
||||
transferType protocol.TransferType
|
||||
closed bool
|
||||
done *done.Instance
|
||||
XUDP *XUDP
|
||||
}
|
||||
|
||||
@@ -171,6 +176,9 @@ 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)
|
||||
|
||||
@@ -41,11 +41,11 @@ func TestSessionManagerClose(t *testing.T) {
|
||||
m := NewSessionManager()
|
||||
s := m.Allocate(&ClientStrategy{})
|
||||
|
||||
if m.CloseIfNoSession() {
|
||||
if m.CloseIfNoSessionAndIdle(m.Size(), m.Count()) {
|
||||
t.Error("able to close")
|
||||
}
|
||||
m.Remove(false, s.ID)
|
||||
if !m.CloseIfNoSession() {
|
||||
if !m.CloseIfNoSessionAndIdle(m.Size(), m.Count()) {
|
||||
t.Error("not able to close")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ 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 {
|
||||
@@ -16,9 +17,10 @@ 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) *Writer {
|
||||
func NewWriter(id uint16, dest net.Destination, writer buf.Writer, transferType protocol.TransferType, globalID [8]byte, inbound *session.Inbound) *Writer {
|
||||
return &Writer{
|
||||
id: id,
|
||||
dest: dest,
|
||||
@@ -26,6 +28,7 @@ func NewWriter(id uint16, dest net.Destination, writer buf.Writer, transferType
|
||||
followup: false,
|
||||
transferType: transferType,
|
||||
globalID: globalID,
|
||||
inbound: inbound,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +46,7 @@ func (w *Writer) getNextFrameMeta() FrameMetadata {
|
||||
SessionID: w.id,
|
||||
Target: w.dest,
|
||||
GlobalID: w.globalID,
|
||||
Inbound: w.inbound,
|
||||
}
|
||||
|
||||
if w.followup {
|
||||
|
||||
@@ -12,6 +12,8 @@ var (
|
||||
|
||||
type ListenConfig = net.ListenConfig
|
||||
|
||||
type KeepAliveConfig = net.KeepAliveConfig
|
||||
|
||||
var (
|
||||
Listen = net.Listen
|
||||
ListenTCP = net.ListenTCP
|
||||
@@ -26,6 +28,12 @@ var FileConn = net.FileConn
|
||||
// ParseIP is an alias of net.ParseIP
|
||||
var ParseIP = net.ParseIP
|
||||
|
||||
var ParseCIDR = net.ParseCIDR
|
||||
|
||||
var ResolveIPAddr = net.ResolveIPAddr
|
||||
|
||||
var InterfaceByName = net.InterfaceByName
|
||||
|
||||
var SplitHostPort = net.SplitHostPort
|
||||
|
||||
var CIDRMask = net.CIDRMask
|
||||
@@ -51,6 +59,8 @@ type (
|
||||
UnixConn = net.UnixConn
|
||||
)
|
||||
|
||||
type IPAddr = net.IPAddr
|
||||
|
||||
// IP is an alias for net.IP.
|
||||
type (
|
||||
IP = net.IP
|
||||
@@ -82,3 +92,11 @@ var (
|
||||
)
|
||||
|
||||
type Resolver = net.Resolver
|
||||
|
||||
var DefaultResolver = net.DefaultResolver
|
||||
|
||||
var JoinHostPort = net.JoinHostPort
|
||||
|
||||
var InterfaceAddrs = net.InterfaceAddrs
|
||||
|
||||
var Interfaces = net.Interfaces
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package ctlcmd
|
||||
|
||||
import "syscall"
|
||||
|
||||
func getSysProcAttr() *syscall.SysProcAttr {
|
||||
return nil
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package ctlcmd
|
||||
|
||||
import "syscall"
|
||||
|
||||
func getSysProcAttr() *syscall.SysProcAttr {
|
||||
return &syscall.SysProcAttr{
|
||||
HideWindow: true,
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package ctlcmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/platform"
|
||||
)
|
||||
|
||||
func Run(args []string, input io.Reader) (buf.MultiBuffer, error) {
|
||||
xctl := platform.GetToolLocation("xctl")
|
||||
if _, err := os.Stat(xctl); err != nil {
|
||||
return nil, errors.New("xctl doesn't exist").Base(err)
|
||||
}
|
||||
|
||||
var errBuffer buf.MultiBufferContainer
|
||||
var outBuffer buf.MultiBufferContainer
|
||||
|
||||
cmd := exec.Command(xctl, args...)
|
||||
cmd.Stderr = &errBuffer
|
||||
cmd.Stdout = &outBuffer
|
||||
cmd.SysProcAttr = getSysProcAttr()
|
||||
if input != nil {
|
||||
cmd.Stdin = input
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return nil, errors.New("failed to start xctl").Base(err)
|
||||
}
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
msg := "failed to execute xctl"
|
||||
if errBuffer.Len() > 0 {
|
||||
msg += ": \n" + strings.TrimSpace(errBuffer.MultiBuffer.String())
|
||||
}
|
||||
return nil, errors.New(msg).Base(err)
|
||||
}
|
||||
|
||||
// log stderr, info message
|
||||
if !errBuffer.IsEmpty() {
|
||||
errors.LogInfo(context.Background(), "<xctl message> \n", strings.TrimSpace(errBuffer.MultiBuffer.String()))
|
||||
}
|
||||
|
||||
return outBuffer.MultiBuffer, nil
|
||||
}
|
||||
@@ -8,19 +8,10 @@ import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func ExpandEnv(s string) string {
|
||||
return os.ExpandEnv(s)
|
||||
}
|
||||
|
||||
func LineSeparator() string {
|
||||
return "\n"
|
||||
}
|
||||
|
||||
func GetToolLocation(file string) string {
|
||||
toolPath := NewEnvFlag(ToolLocation).GetValue(getExecutableDir)
|
||||
return filepath.Join(toolPath, file)
|
||||
}
|
||||
|
||||
// GetAssetLocation searches for `file` in the env dir, the executable dir, and certain locations
|
||||
func GetAssetLocation(file string) string {
|
||||
assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir)
|
||||
|
||||
@@ -8,10 +8,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
PluginLocation = "xray.location.plugin"
|
||||
ConfigLocation = "xray.location.config"
|
||||
ConfdirLocation = "xray.location.confdir"
|
||||
ToolLocation = "xray.location.tool"
|
||||
AssetLocation = "xray.location.asset"
|
||||
CertLocation = "xray.location.cert"
|
||||
|
||||
@@ -79,17 +77,6 @@ func getExecutableDir() string {
|
||||
return filepath.Dir(exec)
|
||||
}
|
||||
|
||||
func getExecutableSubDir(dir string) func() string {
|
||||
return func() string {
|
||||
return filepath.Join(getExecutableDir(), dir)
|
||||
}
|
||||
}
|
||||
|
||||
func GetPluginDirectory() string {
|
||||
pluginDir := NewEnvFlag(PluginLocation).GetValue(getExecutableSubDir("plugins"))
|
||||
return pluginDir
|
||||
}
|
||||
|
||||
func GetConfigurationPath() string {
|
||||
configPath := NewEnvFlag(ConfigLocation).GetValue(getExecutableDir)
|
||||
return filepath.Join(configPath, "config.json")
|
||||
|
||||
@@ -5,20 +5,10 @@ package platform
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
func ExpandEnv(s string) string {
|
||||
// TODO
|
||||
return s
|
||||
}
|
||||
|
||||
func LineSeparator() string {
|
||||
return "\r\n"
|
||||
}
|
||||
|
||||
func GetToolLocation(file string) string {
|
||||
toolPath := NewEnvFlag(ToolLocation).GetValue(getExecutableDir)
|
||||
return filepath.Join(toolPath, file+".exe")
|
||||
}
|
||||
|
||||
// GetAssetLocation searches for `file` in the env dir and the executable dir
|
||||
func GetAssetLocation(file string) string {
|
||||
assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir)
|
||||
|
||||
@@ -5,7 +5,6 @@ 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"
|
||||
)
|
||||
|
||||
@@ -16,11 +15,12 @@ const (
|
||||
RequestCommandTCP = RequestCommand(0x01)
|
||||
RequestCommandUDP = RequestCommand(0x02)
|
||||
RequestCommandMux = RequestCommand(0x03)
|
||||
RequestCommandRvs = RequestCommand(0x04)
|
||||
)
|
||||
|
||||
func (c RequestCommand) TransferType() TransferType {
|
||||
switch c {
|
||||
case RequestCommandTCP, RequestCommandMux:
|
||||
case RequestCommandTCP, RequestCommandMux, RequestCommandRvs:
|
||||
return TransferTypeStream
|
||||
case RequestCommandUDP:
|
||||
return TransferTypePacket
|
||||
@@ -70,18 +70,10 @@ type ResponseHeader struct {
|
||||
Command ResponseCommand
|
||||
}
|
||||
|
||||
type CommandSwitchAccount struct {
|
||||
Host net.Address
|
||||
Port net.Port
|
||||
ID uuid.UUID
|
||||
Level uint32
|
||||
ValidMin byte
|
||||
}
|
||||
|
||||
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
|
||||
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"
|
||||
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
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())
|
||||
}
|
||||
}
|
||||
@@ -1,122 +1,30 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
)
|
||||
|
||||
type ValidationStrategy interface {
|
||||
IsValid() bool
|
||||
Invalidate()
|
||||
}
|
||||
|
||||
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
|
||||
Destination net.Destination
|
||||
User *MemoryUser
|
||||
}
|
||||
|
||||
func NewServerSpec(dest net.Destination, valid ValidationStrategy, users ...*MemoryUser) *ServerSpec {
|
||||
func NewServerSpec(dest net.Destination, user *MemoryUser) *ServerSpec {
|
||||
return &ServerSpec{
|
||||
dest: dest,
|
||||
users: users,
|
||||
valid: valid,
|
||||
Destination: dest,
|
||||
User: user,
|
||||
}
|
||||
}
|
||||
|
||||
func NewServerSpecFromPB(spec *ServerEndpoint) (*ServerSpec, error) {
|
||||
dest := net.TCPDestination(spec.Address.AsAddress(), net.Port(spec.Port))
|
||||
mUsers := make([]*MemoryUser, len(spec.User))
|
||||
for idx, u := range spec.User {
|
||||
mUser, err := u.ToMemoryUser()
|
||||
var dUser *MemoryUser
|
||||
if spec.User != nil {
|
||||
user, err := spec.User.ToMemoryUser()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mUsers[idx] = mUser
|
||||
dUser = user
|
||||
}
|
||||
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()
|
||||
return NewServerSpec(dest, dUser), nil
|
||||
}
|
||||
|
||||
@@ -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,rep,name=user,proto3" json:"user,omitempty"`
|
||||
User *User `protobuf:"bytes,3,opt,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, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72,
|
||||
0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72,
|
||||
0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
|
||||
0x6f, 0x6c, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x42, 0x5e, 0x0a,
|
||||
0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
||||
|
||||
@@ -12,5 +12,5 @@ import "common/protocol/user.proto";
|
||||
message ServerEndpoint {
|
||||
xray.common.net.IPOrDomain address = 1;
|
||||
uint32 port = 2;
|
||||
repeated xray.common.protocol.User user = 3;
|
||||
xray.common.protocol.User user = 3;
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ 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"
|
||||
)
|
||||
|
||||
@@ -16,13 +17,13 @@ const (
|
||||
inboundSessionKey ctx.SessionKey = 1
|
||||
outboundSessionKey ctx.SessionKey = 2
|
||||
contentSessionKey ctx.SessionKey = 3
|
||||
muxPreferredSessionKey ctx.SessionKey = 4 // unused
|
||||
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
|
||||
handlerSessionKey ctx.SessionKey = 10 // unused
|
||||
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
|
||||
)
|
||||
@@ -74,25 +75,21 @@ func ContentFromContext(ctx context.Context) *Content {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 ContextWithIsReverseMux(ctx context.Context, isReverseMux bool) context.Context {
|
||||
return context.WithValue(ctx, isReverseMuxKey, isReverseMux)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
func IsReverseMuxFromContext(ctx context.Context) bool {
|
||||
if val, ok := ctx.Value(isReverseMuxKey).(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
|
||||
@@ -163,6 +160,17 @@ 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)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ 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"
|
||||
)
|
||||
@@ -33,8 +35,26 @@ 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) {
|
||||
return w.R.Read(b)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
func (w *PipeConnWrapper) Write(p []byte) (n int, err error) {
|
||||
|
||||
@@ -64,7 +64,7 @@ func GetMergedConfig(args cmdarg.Arg) (string, error) {
|
||||
var files []*ConfigSource
|
||||
supported := []string{"json", "yaml", "toml"}
|
||||
for _, file := range args {
|
||||
format := getFormat(file)
|
||||
format := GetFormat(file)
|
||||
if slices.Contains(supported, format) {
|
||||
files = append(files, &ConfigSource{
|
||||
Name: file,
|
||||
@@ -98,7 +98,7 @@ func getExtension(filename string) string {
|
||||
return filename[idx+1:]
|
||||
}
|
||||
|
||||
func getFormat(filename string) string {
|
||||
func GetFormat(filename string) string {
|
||||
return GetFormatByExtension(getExtension(filename))
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ func LoadConfig(formatName string, input interface{}) (*Config, error) {
|
||||
|
||||
if formatName == "auto" {
|
||||
if file != "stdin:" {
|
||||
f = getFormat(file)
|
||||
f = GetFormat(file)
|
||||
} else {
|
||||
f = "json"
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ import (
|
||||
|
||||
var (
|
||||
Version_x byte = 25
|
||||
Version_y byte = 8
|
||||
Version_z byte = 29
|
||||
Version_y byte = 12
|
||||
Version_z byte = 8
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -63,17 +63,13 @@ 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(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -4,7 +4,6 @@ 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"
|
||||
)
|
||||
@@ -20,9 +19,6 @@ 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.
|
||||
|
||||
@@ -26,3 +26,9 @@ type Dispatcher interface {
|
||||
func DispatcherType() interface{} {
|
||||
return (*Dispatcher)(nil)
|
||||
}
|
||||
|
||||
// Just for type assertion
|
||||
type WrapLinkDispatcher interface {
|
||||
Dispatcher
|
||||
WrapLink(ctx context.Context, link *transport.Link) *transport.Link
|
||||
}
|
||||
|
||||
@@ -12,14 +12,19 @@ import (
|
||||
// ResolvableContext is an implementation of routing.Context, with domain resolving capability.
|
||||
type ResolvableContext struct {
|
||||
routing.Context
|
||||
dnsClient dns.Client
|
||||
resolvedIPs []net.IP
|
||||
dnsClient dns.Client
|
||||
cacheIPs []net.IP
|
||||
hasError bool
|
||||
}
|
||||
|
||||
// GetTargetIPs overrides original routing.Context's implementation.
|
||||
func (ctx *ResolvableContext) GetTargetIPs() []net.IP {
|
||||
if len(ctx.resolvedIPs) > 0 {
|
||||
return ctx.resolvedIPs
|
||||
if len(ctx.cacheIPs) > 0 {
|
||||
return ctx.cacheIPs
|
||||
}
|
||||
|
||||
if ctx.hasError {
|
||||
return nil
|
||||
}
|
||||
|
||||
if domain := ctx.GetTargetDomain(); len(domain) != 0 {
|
||||
@@ -29,16 +34,18 @@ func (ctx *ResolvableContext) GetTargetIPs() []net.IP {
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err == nil {
|
||||
ctx.resolvedIPs = ips
|
||||
ctx.cacheIPs = 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
|
||||
}
|
||||
|
||||
|
||||
33
go.mod
33
go.mod
@@ -11,23 +11,23 @@ require (
|
||||
github.com/miekg/dns v1.1.68
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/pires/go-proxyproto v0.8.1
|
||||
github.com/quic-go/quic-go v0.54.0
|
||||
github.com/refraction-networking/utls v1.8.0
|
||||
github.com/quic-go/quic-go v0.57.1
|
||||
github.com/refraction-networking/utls v1.8.1
|
||||
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/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
|
||||
github.com/vishvananda/netlink v1.3.1
|
||||
github.com/xtls/reality v0.0.0-20250828044527-046fad5ab64f
|
||||
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/sync v0.16.0
|
||||
golang.org/x/sys v0.35.0
|
||||
golang.org/x/crypto v0.46.0
|
||||
golang.org/x/net v0.48.0
|
||||
golang.org/x/sync v0.19.0
|
||||
golang.org/x/sys v0.39.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||
google.golang.org/grpc v1.75.0
|
||||
google.golang.org/protobuf v1.36.8
|
||||
google.golang.org/grpc v1.77.0
|
||||
google.golang.org/protobuf v1.36.10
|
||||
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5
|
||||
h12.io/socks v1.0.3
|
||||
lukechampine.com/blake3 v1.4.1
|
||||
@@ -41,18 +41,17 @@ require (
|
||||
github.com/juju/ratelimit v1.0.2 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/vishvananda/netns v0.0.5 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
golang.org/x/mod v0.26.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/tools v0.35.0 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
93
go.sum
93
go.sum
@@ -46,19 +46,18 @@ github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoU
|
||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
|
||||
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/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.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/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10=
|
||||
github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
|
||||
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/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=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/sagernet/sing v0.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y=
|
||||
github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||
@@ -75,41 +74,41 @@ github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW
|
||||
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
||||
github.com/vishvananda/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-20250828044527-046fad5ab64f h1:o1Kryl9qEYYzNep9RId9DM1kBn8tBrcK5UJnti/l0NI=
|
||||
github.com/xtls/reality v0.0.0-20250828044527-046fad5ab64f/go.mod h1:XxvnCCgBee4WWE0bc4E+a7wbk8gkJ/rS0vNVNtC5qp0=
|
||||
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/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.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
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.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
||||
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
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.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
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.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
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 +116,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.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
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.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
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=
|
||||
@@ -141,12 +140,12 @@ golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uI
|
||||
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-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
|
||||
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
||||
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
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=
|
||||
|
||||
@@ -16,19 +16,21 @@ import (
|
||||
)
|
||||
|
||||
type NameServerConfig struct {
|
||||
Address *Address `json:"address"`
|
||||
ClientIP *Address `json:"clientIp"`
|
||||
Port uint16 `json:"port"`
|
||||
SkipFallback bool `json:"skipFallback"`
|
||||
Domains []string `json:"domains"`
|
||||
ExpectedIPs StringList `json:"expectedIPs"`
|
||||
ExpectIPs StringList `json:"expectIPs"`
|
||||
QueryStrategy string `json:"queryStrategy"`
|
||||
Tag string `json:"tag"`
|
||||
TimeoutMs uint64 `json:"timeoutMs"`
|
||||
DisableCache bool `json:"disableCache"`
|
||||
FinalQuery bool `json:"finalQuery"`
|
||||
UnexpectedIPs StringList `json:"unexpectedIPs"`
|
||||
Address *Address `json:"address"`
|
||||
ClientIP *Address `json:"clientIp"`
|
||||
Port uint16 `json:"port"`
|
||||
SkipFallback bool `json:"skipFallback"`
|
||||
Domains []string `json:"domains"`
|
||||
ExpectedIPs StringList `json:"expectedIPs"`
|
||||
ExpectIPs StringList `json:"expectIPs"`
|
||||
QueryStrategy string `json:"queryStrategy"`
|
||||
Tag string `json:"tag"`
|
||||
TimeoutMs uint64 `json:"timeoutMs"`
|
||||
DisableCache *bool `json:"disableCache"`
|
||||
ServeStale *bool `json:"serveStale"`
|
||||
ServeExpiredTTL *uint32 `json:"serveExpiredTTL"`
|
||||
FinalQuery bool `json:"finalQuery"`
|
||||
UnexpectedIPs StringList `json:"unexpectedIPs"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON
|
||||
@@ -40,19 +42,21 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
|
||||
}
|
||||
|
||||
var advanced struct {
|
||||
Address *Address `json:"address"`
|
||||
ClientIP *Address `json:"clientIp"`
|
||||
Port uint16 `json:"port"`
|
||||
SkipFallback bool `json:"skipFallback"`
|
||||
Domains []string `json:"domains"`
|
||||
ExpectedIPs StringList `json:"expectedIPs"`
|
||||
ExpectIPs StringList `json:"expectIPs"`
|
||||
QueryStrategy string `json:"queryStrategy"`
|
||||
Tag string `json:"tag"`
|
||||
TimeoutMs uint64 `json:"timeoutMs"`
|
||||
DisableCache bool `json:"disableCache"`
|
||||
FinalQuery bool `json:"finalQuery"`
|
||||
UnexpectedIPs StringList `json:"unexpectedIPs"`
|
||||
Address *Address `json:"address"`
|
||||
ClientIP *Address `json:"clientIp"`
|
||||
Port uint16 `json:"port"`
|
||||
SkipFallback bool `json:"skipFallback"`
|
||||
Domains []string `json:"domains"`
|
||||
ExpectedIPs StringList `json:"expectedIPs"`
|
||||
ExpectIPs StringList `json:"expectIPs"`
|
||||
QueryStrategy string `json:"queryStrategy"`
|
||||
Tag string `json:"tag"`
|
||||
TimeoutMs uint64 `json:"timeoutMs"`
|
||||
DisableCache *bool `json:"disableCache"`
|
||||
ServeStale *bool `json:"serveStale"`
|
||||
ServeExpiredTTL *uint32 `json:"serveExpiredTTL"`
|
||||
FinalQuery bool `json:"finalQuery"`
|
||||
UnexpectedIPs StringList `json:"unexpectedIPs"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &advanced); err == nil {
|
||||
c.Address = advanced.Address
|
||||
@@ -66,6 +70,8 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
|
||||
c.Tag = advanced.Tag
|
||||
c.TimeoutMs = advanced.TimeoutMs
|
||||
c.DisableCache = advanced.DisableCache
|
||||
c.ServeStale = advanced.ServeStale
|
||||
c.ServeExpiredTTL = advanced.ServeExpiredTTL
|
||||
c.FinalQuery = advanced.FinalQuery
|
||||
c.UnexpectedIPs = advanced.UnexpectedIPs
|
||||
return nil
|
||||
@@ -173,6 +179,8 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
|
||||
Tag: c.Tag,
|
||||
TimeoutMs: c.TimeoutMs,
|
||||
DisableCache: c.DisableCache,
|
||||
ServeStale: c.ServeStale,
|
||||
ServeExpiredTTL: c.ServeExpiredTTL,
|
||||
FinalQuery: c.FinalQuery,
|
||||
UnexpectedGeoip: unexpectedGeoipList,
|
||||
ActUnprior: actUnprior,
|
||||
@@ -194,8 +202,11 @@ type DNSConfig struct {
|
||||
Tag string `json:"tag"`
|
||||
QueryStrategy string `json:"queryStrategy"`
|
||||
DisableCache bool `json:"disableCache"`
|
||||
ServeStale bool `json:"serveStale"`
|
||||
ServeExpiredTTL uint32 `json:"serveExpiredTTL"`
|
||||
DisableFallback bool `json:"disableFallback"`
|
||||
DisableFallbackIfMatch bool `json:"disableFallbackIfMatch"`
|
||||
EnableParallelQuery bool `json:"enableParallelQuery"`
|
||||
UseSystemHosts bool `json:"useSystemHosts"`
|
||||
}
|
||||
|
||||
@@ -391,8 +402,11 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
|
||||
config := &dns.Config{
|
||||
Tag: c.Tag,
|
||||
DisableCache: c.DisableCache,
|
||||
ServeStale: c.ServeStale,
|
||||
ServeExpiredTTL: c.ServeExpiredTTL,
|
||||
DisableFallback: c.DisableFallback,
|
||||
DisableFallbackIfMatch: c.DisableFallbackIfMatch,
|
||||
EnableParallelQuery: c.EnableParallelQuery,
|
||||
QueryStrategy: resolveQueryStrategy(c.QueryStrategy),
|
||||
}
|
||||
|
||||
@@ -403,11 +417,78 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
|
||||
config.ClientIp = []byte(c.ClientIP.IP())
|
||||
}
|
||||
|
||||
// Build PolicyID
|
||||
policyMap := map[string]uint32{}
|
||||
nextPolicyID := uint32(1)
|
||||
buildPolicyID := func(nsc *NameServerConfig) uint32 {
|
||||
var sb strings.Builder
|
||||
|
||||
// ClientIP
|
||||
if nsc.ClientIP != nil {
|
||||
sb.WriteString("client=")
|
||||
sb.WriteString(nsc.ClientIP.String())
|
||||
sb.WriteByte('|')
|
||||
} else {
|
||||
sb.WriteString("client=none|")
|
||||
}
|
||||
|
||||
// SkipFallback
|
||||
if nsc.SkipFallback {
|
||||
sb.WriteString("skip=1|")
|
||||
} else {
|
||||
sb.WriteString("skip=0|")
|
||||
}
|
||||
|
||||
// QueryStrategy
|
||||
sb.WriteString("qs=")
|
||||
sb.WriteString(strings.ToLower(strings.TrimSpace(nsc.QueryStrategy)))
|
||||
sb.WriteByte('|')
|
||||
|
||||
// Tag
|
||||
sb.WriteString("tag=")
|
||||
sb.WriteString(strings.ToLower(strings.TrimSpace(nsc.Tag)))
|
||||
sb.WriteByte('|')
|
||||
|
||||
// []string helper
|
||||
writeList := func(tag string, lst []string) {
|
||||
if len(lst) == 0 {
|
||||
sb.WriteString(tag)
|
||||
sb.WriteString("=[]|")
|
||||
return
|
||||
}
|
||||
cp := make([]string, len(lst))
|
||||
for i, s := range lst {
|
||||
cp[i] = strings.TrimSpace(strings.ToLower(s))
|
||||
}
|
||||
sort.Strings(cp)
|
||||
sb.WriteString(tag)
|
||||
sb.WriteByte('=')
|
||||
sb.WriteString(strings.Join(cp, ","))
|
||||
sb.WriteByte('|')
|
||||
}
|
||||
|
||||
writeList("domains", nsc.Domains)
|
||||
writeList("expected", nsc.ExpectedIPs)
|
||||
writeList("expect", nsc.ExpectIPs)
|
||||
writeList("unexpected", nsc.UnexpectedIPs)
|
||||
|
||||
key := sb.String()
|
||||
|
||||
if id, ok := policyMap[key]; ok {
|
||||
return id
|
||||
}
|
||||
id := nextPolicyID
|
||||
nextPolicyID++
|
||||
policyMap[key] = id
|
||||
return id
|
||||
}
|
||||
|
||||
for _, server := range c.Servers {
|
||||
ns, err := server.Build()
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to build nameserver").Base(err)
|
||||
}
|
||||
ns.PolicyID = buildPolicyID(server)
|
||||
config.NameServer = append(config.NameServer, ns)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ func TestDNSConfigParsing(t *testing.T) {
|
||||
return config.Build()
|
||||
}
|
||||
}
|
||||
|
||||
expectedServeStale := true
|
||||
expectedServeExpiredTTL := uint32(172800)
|
||||
runMultiTestCase(t, []TestCase{
|
||||
{
|
||||
Input: `{
|
||||
@@ -28,7 +29,9 @@ func TestDNSConfigParsing(t *testing.T) {
|
||||
"address": "8.8.8.8",
|
||||
"port": 5353,
|
||||
"skipFallback": true,
|
||||
"domains": ["domain:example.com"]
|
||||
"domains": ["domain:example.com"],
|
||||
"serveStale": true,
|
||||
"serveExpiredTTL": 172800
|
||||
}],
|
||||
"hosts": {
|
||||
"domain:example.com": "google.com",
|
||||
@@ -40,6 +43,8 @@ func TestDNSConfigParsing(t *testing.T) {
|
||||
"clientIp": "10.0.0.1",
|
||||
"queryStrategy": "UseIPv4",
|
||||
"disableCache": true,
|
||||
"serveStale": false,
|
||||
"serveExpiredTTL": 86400,
|
||||
"disableFallback": true
|
||||
}`,
|
||||
Parser: parserCreator(),
|
||||
@@ -68,6 +73,9 @@ func TestDNSConfigParsing(t *testing.T) {
|
||||
Size: 1,
|
||||
},
|
||||
},
|
||||
ServeStale: &expectedServeStale,
|
||||
ServeExpiredTTL: &expectedServeExpiredTTL,
|
||||
PolicyID: 1, // Servers with certain identical fields share this ID, incrementing starting from 1. See: Build PolicyID
|
||||
},
|
||||
},
|
||||
StaticHosts: []*dns.Config_HostMapping{
|
||||
@@ -100,6 +108,8 @@ func TestDNSConfigParsing(t *testing.T) {
|
||||
ClientIp: []byte{10, 0, 0, 1},
|
||||
QueryStrategy: dns.QueryStrategy_USE_IP4,
|
||||
DisableCache: true,
|
||||
ServeStale: false,
|
||||
ServeExpiredTTL: 86400,
|
||||
DisableFallback: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -51,31 +51,65 @@ type HTTPRemoteConfig struct {
|
||||
}
|
||||
|
||||
type HTTPClientConfig struct {
|
||||
Servers []*HTTPRemoteConfig `json:"servers"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
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"`
|
||||
}
|
||||
|
||||
func (v *HTTPClientConfig) Build() (proto.Message, error) {
|
||||
config := new(http.ClientConfig)
|
||||
config.Server = make([]*protocol.ServerEndpoint, len(v.Servers))
|
||||
for idx, serverConfig := range v.Servers {
|
||||
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`)
|
||||
}
|
||||
server := &protocol.ServerEndpoint{
|
||||
Address: serverConfig.Address.Build(),
|
||||
Port: uint32(serverConfig.Port),
|
||||
}
|
||||
for _, rawUser := range serverConfig.Users {
|
||||
user := new(protocol.User)
|
||||
if err := json.Unmarshal(rawUser, user); err != nil {
|
||||
return nil, errors.New("failed to parse HTTP user").Base(err).AtError()
|
||||
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()
|
||||
}
|
||||
}
|
||||
account := new(HTTPAccount)
|
||||
if err := json.Unmarshal(rawUser, account); err != nil {
|
||||
return nil, errors.New("failed to parse HTTP account").Base(err).AtError()
|
||||
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()
|
||||
}
|
||||
}
|
||||
user.Account = serial.ToTypedMessage(account.Build())
|
||||
server.User = append(server.User, user)
|
||||
server.User = user
|
||||
break
|
||||
}
|
||||
config.Server[idx] = server
|
||||
config.Server = server
|
||||
break
|
||||
}
|
||||
config.Header = make([]*http.Header, 0, 32)
|
||||
for key, value := range v.Headers {
|
||||
|
||||
@@ -83,8 +83,6 @@ func (c *RouterConfig) getDomainStrategy() router.Config_DomainStrategy {
|
||||
}
|
||||
|
||||
switch strings.ToLower(ds) {
|
||||
case "alwaysip":
|
||||
return router.Config_UseIp
|
||||
case "ipifnonmatch":
|
||||
return router.Config_IpIfNonMatch
|
||||
case "ipondemand":
|
||||
|
||||
@@ -162,22 +162,46 @@ 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 {
|
||||
Servers []*ShadowsocksServerTarget `json:"servers"`
|
||||
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"`
|
||||
}
|
||||
|
||||
func (v *ShadowsocksClientConfig) Build() (proto.Message, error) {
|
||||
if len(v.Servers) == 0 {
|
||||
return nil, errors.New("0 Shadowsocks server configured.")
|
||||
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) == 1 {
|
||||
@@ -205,8 +229,7 @@ func (v *ShadowsocksClientConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
|
||||
config := new(shadowsocks.ClientConfig)
|
||||
serverSpecs := make([]*protocol.ServerEndpoint, len(v.Servers))
|
||||
for idx, server := range v.Servers {
|
||||
for _, server := range v.Servers {
|
||||
if C.Contains(shadowaead_2022.List, server.Cipher) {
|
||||
return nil, errors.New("Shadowsocks 2022 accept no multi servers")
|
||||
}
|
||||
@@ -232,19 +255,16 @@ 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),
|
||||
},
|
||||
}
|
||||
|
||||
serverSpecs[idx] = ss
|
||||
config.Server = ss
|
||||
break
|
||||
}
|
||||
|
||||
config.Server = serverSpecs
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
@@ -70,30 +70,64 @@ type SocksRemoteConfig struct {
|
||||
}
|
||||
|
||||
type SocksClientConfig struct {
|
||||
Servers []*SocksRemoteConfig `json:"servers"`
|
||||
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"`
|
||||
}
|
||||
|
||||
func (v *SocksClientConfig) Build() (proto.Message, error) {
|
||||
config := new(socks.ClientConfig)
|
||||
config.Server = make([]*protocol.ServerEndpoint, len(v.Servers))
|
||||
for idx, serverConfig := range v.Servers {
|
||||
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`)
|
||||
}
|
||||
server := &protocol.ServerEndpoint{
|
||||
Address: serverConfig.Address.Build(),
|
||||
Port: uint32(serverConfig.Port),
|
||||
}
|
||||
for _, rawUser := range serverConfig.Users {
|
||||
user := new(protocol.User)
|
||||
if err := json.Unmarshal(rawUser, user); err != nil {
|
||||
return nil, errors.New("failed to parse Socks user").Base(err).AtError()
|
||||
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()
|
||||
}
|
||||
}
|
||||
account := new(SocksAccount)
|
||||
if err := json.Unmarshal(rawUser, account); err != nil {
|
||||
return nil, errors.New("failed to parse socks account").Base(err).AtError()
|
||||
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()
|
||||
}
|
||||
}
|
||||
user.Account = serial.ToTypedMessage(account.Build())
|
||||
server.User = append(server.User, user)
|
||||
server.User = user
|
||||
break
|
||||
}
|
||||
config.Server[idx] = server
|
||||
config.Server = server
|
||||
break
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
@@ -65,24 +65,47 @@ 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",
|
||||
}),
|
||||
},
|
||||
},
|
||||
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",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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 = 16
|
||||
c.Xmux.MaxConcurrency.To = 32
|
||||
c.Xmux.MaxConcurrency.From = 1
|
||||
c.Xmux.MaxConcurrency.To = 1
|
||||
c.Xmux.HMaxRequestTimes.From = 600
|
||||
c.Xmux.HMaxRequestTimes.To = 900
|
||||
c.Xmux.HMaxReusableSecs.From = 1800
|
||||
@@ -628,6 +628,9 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
config.ShortIds = make([][]byte, len(c.ShortIds))
|
||||
for i, s := range c.ShortIds {
|
||||
if len(s) > 16 {
|
||||
return nil, errors.New(`too long "shortIds[`, i, `]": `, s)
|
||||
}
|
||||
config.ShortIds[i] = make([]byte, 8)
|
||||
if _, err = hex.Decode(config.ShortIds[i], []byte(s)); err != nil {
|
||||
return nil, errors.New(`invalid "shortIds[`, i, `]": `, s)
|
||||
@@ -679,6 +682,9 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
|
||||
if len(c.ShortIds) != 0 {
|
||||
return nil, errors.New(`non-empty "shortIds", please use "shortId" instead`)
|
||||
}
|
||||
if len(c.ShortIds) > 16 {
|
||||
return nil, errors.New(`too long "shortId": `, c.ShortId)
|
||||
}
|
||||
config.ShortId = make([]byte, 8)
|
||||
if _, err = hex.Decode(config.ShortId, []byte(c.ShortId)); err != nil {
|
||||
return nil, errors.New(`invalid "shortId": `, c.ShortId)
|
||||
@@ -804,6 +810,7 @@ type SocketConfig struct {
|
||||
CustomSockopt []*CustomSockoptConfig `json:"customSockopt"`
|
||||
AddressPortStrategy string `json:"addressPortStrategy"`
|
||||
HappyEyeballsSettings *HappyEyeballsConfig `json:"happyEyeballs"`
|
||||
TrustedXForwardedFor []string `json:"trustedXForwardedFor"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
@@ -923,6 +930,7 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
||||
CustomSockopt: customSockopts,
|
||||
AddressPortStrategy: addressPortStrategy,
|
||||
HappyEyeballs: happyEyeballs,
|
||||
TrustedXForwardedFor: c.TrustedXForwardedFor,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -20,28 +20,44 @@ import (
|
||||
type TrojanServerTarget struct {
|
||||
Address *Address `json:"address"`
|
||||
Port uint16 `json:"port"`
|
||||
Password string `json:"password"`
|
||||
Email string `json:"email"`
|
||||
Level byte `json:"level"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Flow string `json:"flow"`
|
||||
}
|
||||
|
||||
// TrojanClientConfig is configuration of trojan servers
|
||||
type TrojanClientConfig struct {
|
||||
Servers []*TrojanServerTarget `json:"servers"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// Build implements Buildable
|
||||
func (c *TrojanClientConfig) Build() (proto.Message, error) {
|
||||
if len(c.Servers) == 0 {
|
||||
return nil, errors.New("0 Trojan server configured.")
|
||||
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`)
|
||||
}
|
||||
|
||||
config := &trojan.ClientConfig{
|
||||
Server: make([]*protocol.ServerEndpoint, len(c.Servers)),
|
||||
}
|
||||
config := &trojan.ClientConfig{}
|
||||
|
||||
for idx, rec := range c.Servers {
|
||||
for _, rec := range c.Servers {
|
||||
if rec.Address == nil {
|
||||
return nil, errors.New("Trojan server address is not set.")
|
||||
}
|
||||
@@ -55,19 +71,19 @@ func (c *TrojanClientConfig) Build() (proto.Message, error) {
|
||||
return nil, errors.PrintRemovedFeatureError(`Flow for Trojan`, ``)
|
||||
}
|
||||
|
||||
config.Server[idx] = &protocol.ServerEndpoint{
|
||||
config.Server = &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
|
||||
|
||||
@@ -34,6 +34,7 @@ type VLessInboundConfig struct {
|
||||
Decryption string `json:"decryption"`
|
||||
Fallbacks []*VLessInboundFallback `json:"fallbacks"`
|
||||
Flow string `json:"flow"`
|
||||
Testseed []uint32 `json:"testseed"`
|
||||
}
|
||||
|
||||
// Build implements Buildable
|
||||
@@ -73,8 +74,16 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||
return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`)
|
||||
}
|
||||
|
||||
if len(account.Testseed) < 4 {
|
||||
account.Testseed = c.Testseed
|
||||
}
|
||||
|
||||
if account.Encryption != "" {
|
||||
return nil, errors.New(`VLESS clients: "encryption" should not in inbound settings`)
|
||||
return nil, errors.New(`VLESS clients: "encryption" should not be in inbound settings`)
|
||||
}
|
||||
|
||||
if account.Reverse != nil && account.Reverse.Tag == "" {
|
||||
return nil, errors.New(`VLESS clients: "tag" can't be empty for "reverse"`)
|
||||
}
|
||||
|
||||
user.Account = serial.ToTypedMessage(account)
|
||||
@@ -96,23 +105,34 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||
default:
|
||||
return false
|
||||
}
|
||||
if s[2] != "1rtt" {
|
||||
t := strings.TrimSuffix(s[2], "s")
|
||||
if t == s[2] {
|
||||
return false
|
||||
}
|
||||
i, err := strconv.Atoi(t)
|
||||
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.Seconds = uint32(i)
|
||||
config.SecondsTo = int64(i)
|
||||
}
|
||||
for i := 3; i < len(s); i++ {
|
||||
if b, _ := base64.RawURLEncoding.DecodeString(s[i]); len(b) != 32 && len(b) != 64 {
|
||||
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 == "" {
|
||||
@@ -121,6 +141,10 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||
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
|
||||
var s string
|
||||
@@ -184,37 +208,69 @@ type VLessOutboundVnext struct {
|
||||
}
|
||||
|
||||
type VLessOutboundConfig struct {
|
||||
Vnext []*VLessOutboundVnext `json:"vnext"`
|
||||
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"`
|
||||
Testpre uint32 `json:"testpre"`
|
||||
Testseed []uint32 `json:"testseed"`
|
||||
Vnext []*VLessOutboundVnext `json:"vnext"`
|
||||
}
|
||||
|
||||
// Build implements Buildable
|
||||
func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
||||
config := new(outbound.Config)
|
||||
|
||||
if len(c.Vnext) != 1 {
|
||||
return nil, errors.New(`VLESS settings: "vnext" should have one and only one member`)
|
||||
if c.Address != nil {
|
||||
c.Vnext = []*VLessOutboundVnext{
|
||||
{
|
||||
Address: c.Address,
|
||||
Port: c.Port,
|
||||
Users: []json.RawMessage{{}},
|
||||
},
|
||||
}
|
||||
}
|
||||
config.Vnext = make([]*protocol.ServerEndpoint, len(c.Vnext))
|
||||
for idx, rec := range c.Vnext {
|
||||
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 {
|
||||
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`)
|
||||
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`)
|
||||
}
|
||||
spec := &protocol.ServerEndpoint{
|
||||
Address: rec.Address.Build(),
|
||||
Port: uint32(rec.Port),
|
||||
User: make([]*protocol.User, len(rec.Users)),
|
||||
}
|
||||
for idx, rawUser := range rec.Users {
|
||||
for _, rawUser := range rec.Users {
|
||||
user := new(protocol.User)
|
||||
if err := json.Unmarshal(rawUser, user); err != nil {
|
||||
return nil, errors.New(`VLESS users: invalid user`).Base(err)
|
||||
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)
|
||||
}
|
||||
}
|
||||
account := new(vless.Account)
|
||||
if err := json.Unmarshal(rawUser, account); err != nil {
|
||||
return nil, errors.New(`VLESS users: invalid user`).Base(err)
|
||||
if c.Address != nil {
|
||||
account.Id = c.Id
|
||||
account.Flow = c.Flow
|
||||
//account.Seed = c.Seed
|
||||
account.Encryption = c.Encryption
|
||||
account.Reverse = c.Reverse
|
||||
account.Testpre = c.Testpre
|
||||
account.Testseed = c.Testseed
|
||||
} else {
|
||||
if err := json.Unmarshal(rawUser, account); err != nil {
|
||||
return nil, errors.New(`VLESS users: invalid user`).Base(err)
|
||||
}
|
||||
}
|
||||
|
||||
u, err := uuid.ParseString(account.Id)
|
||||
@@ -250,12 +306,21 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
||||
default:
|
||||
return false
|
||||
}
|
||||
for i := 3; i < len(s); i++ {
|
||||
if b, _ := base64.RawURLEncoding.DecodeString(s[i]); len(b) != 32 && len(b) != 1184 {
|
||||
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 == "" {
|
||||
@@ -264,10 +329,16 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
||||
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"`)
|
||||
}
|
||||
|
||||
user.Account = serial.ToTypedMessage(account)
|
||||
spec.User[idx] = user
|
||||
spec.User = user
|
||||
break
|
||||
}
|
||||
config.Vnext[idx] = spec
|
||||
config.Vnext = spec
|
||||
break
|
||||
}
|
||||
|
||||
return config, nil
|
||||
|
||||
@@ -35,25 +35,50 @@ 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,
|
||||
},
|
||||
},
|
||||
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -46,17 +46,6 @@ 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"`
|
||||
}
|
||||
@@ -71,7 +60,6 @@ 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
|
||||
@@ -82,10 +70,6 @@ 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)
|
||||
@@ -117,23 +101,37 @@ type VMessOutboundTarget struct {
|
||||
}
|
||||
|
||||
type VMessOutboundConfig struct {
|
||||
Receivers []*VMessOutboundTarget `json:"vnext"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// Build implements Buildable
|
||||
func (c *VMessOutboundConfig) Build() (proto.Message, error) {
|
||||
config := new(outbound.Config)
|
||||
|
||||
if len(c.Receivers) == 0 {
|
||||
return nil, errors.New("0 VMess receiver configured")
|
||||
if c.Address != nil {
|
||||
c.Receivers = []*VMessOutboundTarget{
|
||||
{
|
||||
Address: c.Address,
|
||||
Port: c.Port,
|
||||
Users: []json.RawMessage{{}},
|
||||
},
|
||||
}
|
||||
}
|
||||
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 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`)
|
||||
}
|
||||
if rec.Address == nil {
|
||||
return nil, errors.New("address is not set in VMess outbound config")
|
||||
return nil, errors.New(`VMess vnext: "address" is not set`)
|
||||
}
|
||||
spec := &protocol.ServerEndpoint{
|
||||
Address: rec.Address.Build(),
|
||||
@@ -141,12 +139,23 @@ func (c *VMessOutboundConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
for _, rawUser := range rec.Users {
|
||||
user := new(protocol.User)
|
||||
if err := json.Unmarshal(rawUser, user); err != nil {
|
||||
return nil, errors.New("invalid VMess user").Base(err)
|
||||
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)
|
||||
}
|
||||
}
|
||||
account := new(VMessAccount)
|
||||
if err := json.Unmarshal(rawUser, account); err != nil {
|
||||
return nil, errors.New("invalid VMess user").Base(err)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
u, err := uuid.ParseString(account.ID)
|
||||
@@ -156,10 +165,11 @@ func (c *VMessOutboundConfig) Build() (proto.Message, error) {
|
||||
account.ID = u.String()
|
||||
|
||||
user.Account = serial.ToTypedMessage(account.Build())
|
||||
spec.User = append(spec.User, user)
|
||||
spec.User = user
|
||||
break
|
||||
}
|
||||
serverSpecs[idx] = spec
|
||||
config.Receiver = spec
|
||||
break
|
||||
}
|
||||
config.Receiver = serverSpecs
|
||||
return config, nil
|
||||
}
|
||||
|
||||
@@ -34,27 +34,53 @@ 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},
|
||||
},
|
||||
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,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -79,11 +105,7 @@ func TestVMessInbound(t *testing.T) {
|
||||
],
|
||||
"default": {
|
||||
"level": 0
|
||||
},
|
||||
"detour": {
|
||||
"to": "tag_to_detour"
|
||||
},
|
||||
"disableInsecureEncryption": true
|
||||
}
|
||||
}`,
|
||||
Parser: loadJSON(creator),
|
||||
Output: &inbound.Config{
|
||||
@@ -102,9 +124,6 @@ func TestVMessInbound(t *testing.T) {
|
||||
Default: &inbound.DefaultConfig{
|
||||
Level: 0,
|
||||
},
|
||||
Detour: &inbound.DetourConfig{
|
||||
To: "tag_to_detour",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -3,9 +3,6 @@ package conf
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -48,8 +45,6 @@ var (
|
||||
"dns": func() interface{} { return new(DNSOutboundConfig) },
|
||||
"wireguard": func() interface{} { return &WireGuardConfig{IsClient: true} },
|
||||
}, "protocol", "settings")
|
||||
|
||||
ctllog = log.New(os.Stderr, "xctl> ", 0)
|
||||
)
|
||||
|
||||
type SniffingConfig struct {
|
||||
@@ -120,47 +115,12 @@ 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"`
|
||||
}
|
||||
@@ -197,30 +157,6 @@ 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 {
|
||||
|
||||
@@ -58,10 +58,6 @@ func TestXrayConfig(t *testing.T) {
|
||||
},
|
||||
"protocol": "vmess",
|
||||
"port": "443-500",
|
||||
"allocate": {
|
||||
"strategy": "random",
|
||||
"concurrency": 3
|
||||
},
|
||||
"settings": {
|
||||
"clients": [
|
||||
{
|
||||
@@ -123,12 +119,6 @@ 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{
|
||||
|
||||
@@ -18,5 +18,6 @@ func init() {
|
||||
cmdWG,
|
||||
cmdMLDSA65,
|
||||
cmdMLKEM768,
|
||||
cmdVLESSEnc,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -61,7 +60,7 @@ func executeConvertConfigsToProtobuf(cmd *base.Command, args []string) {
|
||||
}
|
||||
|
||||
if len(optFile) > 0 {
|
||||
switch core.GetFormatByExtension(getFileExtension(optFile)){
|
||||
switch core.GetFormat(optFile){
|
||||
case "protobuf", "":
|
||||
fmt.Println("Output ProtoBuf file is ", optFile)
|
||||
default:
|
||||
@@ -106,11 +105,3 @@ func executeConvertConfigsToProtobuf(cmd *base.Command, args []string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getFileExtension(filename string) string {
|
||||
idx := strings.LastIndexByte(filename, '.')
|
||||
if idx == -1 {
|
||||
return ""
|
||||
}
|
||||
return filename[idx+1:]
|
||||
}
|
||||
|
||||
@@ -25,6 +25,21 @@ func Curve25519Genkey(StdEncoding bool, input_base64 string) {
|
||||
return
|
||||
}
|
||||
}
|
||||
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)
|
||||
@@ -39,13 +54,10 @@ func Curve25519Genkey(StdEncoding bool, input_base64 string) {
|
||||
|
||||
key, err := ecdh.X25519().NewPrivateKey(privateKey)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
returnErr = err
|
||||
return
|
||||
}
|
||||
password := key.PublicKey().Bytes()
|
||||
hash32 := blake3.Sum256(password)
|
||||
fmt.Printf("PrivateKey: %v\nPassword: %v\nHash32: %v",
|
||||
encoding.EncodeToString(privateKey),
|
||||
encoding.EncodeToString(password),
|
||||
encoding.EncodeToString(hash32[:]))
|
||||
password = key.PublicKey().Bytes()
|
||||
hash32 = blake3.Sum256(password)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func executeMLDSA65(cmd *base.Command, args []string) {
|
||||
rand.Read(seed[:])
|
||||
}
|
||||
pub, _ := mldsa65.NewKeyFromSeed(&seed)
|
||||
fmt.Printf("Seed: %v\nVerify: %v",
|
||||
fmt.Printf("Seed: %v\nVerify: %v\n",
|
||||
base64.RawURLEncoding.EncodeToString(seed[:]),
|
||||
base64.RawURLEncoding.EncodeToString(pub.Bytes()))
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ import (
|
||||
|
||||
var cmdMLKEM768 = &base.Command{
|
||||
UsageLine: `{{.Exec}} mlkem768 [-i "seed (base64.RawURLEncoding)"]`,
|
||||
Short: `Generate key pair for ML-KEM-768 post-quantum key exchange (VLESS)`,
|
||||
Short: `Generate key pair for ML-KEM-768 post-quantum key exchange (VLESS Encryption)`,
|
||||
Long: `
|
||||
Generate key pair for ML-KEM-768 post-quantum key exchange (VLESS).
|
||||
Generate key pair for ML-KEM-768 post-quantum key exchange (VLESS Encryption).
|
||||
|
||||
Random: {{.Exec}} mlkem768
|
||||
|
||||
@@ -40,11 +40,21 @@ func executeMLKEM768(cmd *base.Command, args []string) {
|
||||
} else {
|
||||
rand.Read(seed[:])
|
||||
}
|
||||
key, _ := mlkem.NewDecapsulationKey768(seed[:])
|
||||
client := key.EncapsulationKey().Bytes()
|
||||
hash32 := blake3.Sum256(client)
|
||||
fmt.Printf("Seed: %v\nClient: %v\nHash32: %v",
|
||||
seed, client, hash32 := genMLKEM768(&seed)
|
||||
fmt.Printf("Seed: %v\nClient: %v\nHash32: %v\n",
|
||||
base64.RawURLEncoding.EncodeToString(seed[:]),
|
||||
base64.RawURLEncoding.EncodeToString(client),
|
||||
base64.RawURLEncoding.EncodeToString(hash32[:]))
|
||||
}
|
||||
|
||||
func genMLKEM768(inputSeed *[64]byte) (seed [64]byte, client []byte, hash32 [32]byte) {
|
||||
if inputSeed == nil {
|
||||
rand.Read(seed[:])
|
||||
} else {
|
||||
seed = *inputSeed
|
||||
}
|
||||
key, _ := mlkem.NewDecapsulationKey768(seed[:])
|
||||
client = key.EncapsulationKey().Bytes()
|
||||
hash32 = blake3.Sum256(client)
|
||||
return
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user