Compare commits

..
Author SHA1 Message Date
风扇滑翔翼 964ebe8ffa DNS Outbound: Return rcode2 when encountered internal err 2025-10-19 16:55:59 +00:00
87 changed files with 1252 additions and 2899 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ WORKDIR /src
COPY . . COPY . .
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags "-s -w -buildid=" ./main RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
# Download geodat into a staging directory # Download geodat into a staging directory
ADD https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geoip.dat /tmp/geodat/geoip.dat ADD https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geoip.dat /tmp/geodat/geoip.dat
+1 -1
View File
@@ -6,7 +6,7 @@ WORKDIR /src
COPY . . COPY . .
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -buildvcs=false -gcflags="all=-l=4" -ldflags "-s -w -buildid=" ./main RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
# Download geodat into a staging directory # Download geodat into a staging directory
ADD https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geoip.dat /tmp/geodat/geoip.dat ADD https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/geoip.dat /tmp/geodat/geoip.dat
+1 -1
View File
@@ -65,7 +65,7 @@ jobs:
echo "LATEST=$LATEST" >>${GITHUB_ENV} echo "LATEST=$LATEST" >>${GITHUB_ENV}
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3
+2 -2
View File
@@ -63,7 +63,7 @@ jobs:
CGO_ENABLED: 0 CGO_ENABLED: 0
steps: steps:
- name: Checkout codebase - name: Checkout codebase
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Show workflow information - name: Show workflow information
run: | run: |
@@ -132,7 +132,7 @@ jobs:
mv build_assets Xray-${{ env.ASSET_NAME }} mv build_assets Xray-${{ env.ASSET_NAME }}
- name: Upload files to Artifacts - name: Upload files to Artifacts
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v4
with: with:
name: Xray-${{ env.ASSET_NAME }} name: Xray-${{ env.ASSET_NAME }}
path: | path: |
+2 -2
View File
@@ -153,7 +153,7 @@ jobs:
CGO_ENABLED: 0 CGO_ENABLED: 0
steps: steps:
- name: Checkout codebase - name: Checkout codebase
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Set up NDK - name: Set up NDK
if: matrix.goos == 'android' if: matrix.goos == 'android'
@@ -238,7 +238,7 @@ jobs:
mv build_assets Xray-${{ env.ASSET_NAME }} mv build_assets Xray-${{ env.ASSET_NAME }}
- name: Upload files to Artifacts - name: Upload files to Artifacts
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v4
with: with:
name: Xray-${{ env.ASSET_NAME }} name: Xray-${{ env.ASSET_NAME }}
path: | path: |
+1 -1
View File
@@ -45,7 +45,7 @@ jobs:
os: [windows-latest, ubuntu-latest, macos-latest] os: [windows-latest, ubuntu-latest, macos-latest]
steps: steps:
- name: Checkout codebase - name: Checkout codebase
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v6 uses: actions/setup-go@v6
with: with:
+1 -16
View File
@@ -4,25 +4,12 @@
[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). [README](https://github.com/XTLS/Xray-core#readme) is open, so feel free to submit your project [here](https://github.com/XTLS/Xray-core/pulls).
## Sponsors
[![Remnawave](https://github.com/user-attachments/assets/a22d34ae-01ee-441c-843a-85356748ed1e)](https://docs.rw)
[![Happ](https://github.com/user-attachments/assets/14055dab-e8bb-48bd-89e8-962709e4098e)](https://happ.su)
[**Sponsor Xray-core**](https://github.com/XTLS/Xray-core/issues/3668)
## Donation & NFTs ## Donation & NFTs
### [Collect a Project X NFT to support the development of Project X!](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1) ### [Collect a Project X NFT to support the development of Project X!](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1)
[<img alt="Project X NFT" width="150px" src="https://raw2.seadn.io/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/7fa9ce900fb39b44226348db330e32/8b7fa9ce900fb39b44226348db330e32.svg" />](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`** - **ETH/USDT/USDC: `0xDc3Fe44F0f25D13CACb1C4896CD0D321df3146Ee`**
- **Project X NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1** - **Project X NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1**
- **VLESS NFT: https://opensea.io/collection/vless** - **VLESS NFT: https://opensea.io/collection/vless**
@@ -57,9 +44,9 @@
- [teddysun/xray](https://hub.docker.com/r/teddysun/xray) - [teddysun/xray](https://hub.docker.com/r/teddysun/xray)
- [wulabing/xray_docker](https://github.com/wulabing/xray_docker) - [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:** - 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) - [X-Panel](https://github.com/xeefei/X-Panel)
- [PasarGuard](https://github.com/PasarGuard/panel) - [PasarGuard](https://github.com/PasarGuard/panel)
- [Remnawave](https://github.com/remnawave/panel)
- [Marzban](https://github.com/Gozargah/Marzban) - [Marzban](https://github.com/Gozargah/Marzban)
- [Xray-UI](https://github.com/qist/xray-ui) - [Xray-UI](https://github.com/qist/xray-ui)
- [Hiddify](https://github.com/hiddify/Hiddify-Manager) - [Hiddify](https://github.com/hiddify/Hiddify-Manager)
@@ -121,14 +108,12 @@
- [OneXray](https://github.com/OneXray/OneXray) - [OneXray](https://github.com/OneXray/OneXray)
- [GoXRay](https://github.com/goxray/desktop) - [GoXRay](https://github.com/goxray/desktop)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal) - [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- [v2rayN](https://github.com/2dust/v2rayN)
- Linux - Linux
- [v2rayA](https://github.com/v2rayA/v2rayA) - [v2rayA](https://github.com/v2rayA/v2rayA)
- [Furious](https://github.com/LorenEteval/Furious) - [Furious](https://github.com/LorenEteval/Furious)
- [GorzRay](https://github.com/ketetefid/GorzRay) - [GorzRay](https://github.com/ketetefid/GorzRay)
- [GoXRay](https://github.com/goxray/desktop) - [GoXRay](https://github.com/goxray/desktop)
- [AnyPortal](https://github.com/AnyPortal/AnyPortal) - [AnyPortal](https://github.com/AnyPortal/AnyPortal)
- [v2rayN](https://github.com/2dust/v2rayN)
## Others that support VLESS, XTLS, REALITY, XUDP, PLUX... ## Others that support VLESS, XTLS, REALITY, XUDP, PLUX...
+94 -245
View File
@@ -3,55 +3,36 @@ package dns
import ( import (
"context" "context"
go_errors "errors" go_errors "errors"
"runtime"
"sync"
"time"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/signal/pubsub" "github.com/xtls/xray-core/common/signal/pubsub"
"github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/common/task"
dns_feature "github.com/xtls/xray-core/features/dns" dns_feature "github.com/xtls/xray-core/features/dns"
"golang.org/x/net/dns/dnsmessage" "golang.org/x/net/dns/dnsmessage"
"golang.org/x/sync/singleflight" "sync"
) "time"
const (
minSizeForEmptyRebuild = 512
shrinkAbsoluteThreshold = 10240
shrinkRatioThreshold = 0.65
migrationBatchSize = 4096
) )
type CacheController struct { type CacheController struct {
name string
disableCache bool
serveStale bool
serveExpiredTTL int32
ips map[string]*record
dirtyips map[string]*record
sync.RWMutex sync.RWMutex
ips map[string]*record
pub *pubsub.Service pub *pubsub.Service
cacheCleanup *task.Periodic cacheCleanup *task.Periodic
highWatermark int name string
requestGroup singleflight.Group disableCache bool
} }
func NewCacheController(name string, disableCache bool, serveStale bool, serveExpiredTTL uint32) *CacheController { func NewCacheController(name string, disableCache bool) *CacheController {
c := &CacheController{ c := &CacheController{
name: name, name: name,
disableCache: disableCache, disableCache: disableCache,
serveStale: serveStale,
serveExpiredTTL: -int32(serveExpiredTTL),
ips: make(map[string]*record), ips: make(map[string]*record),
pub: pubsub.NewService(), pub: pubsub.NewService(),
} }
c.cacheCleanup = &task.Periodic{ c.cacheCleanup = &task.Periodic{
Interval: 300 * time.Second, Interval: time.Minute,
Execute: c.CacheCleanup, Execute: c.CacheCleanup,
} }
return c return c
@@ -59,263 +40,131 @@ func NewCacheController(name string, disableCache bool, serveStale bool, serveEx
// CacheCleanup clears expired items from cache // CacheCleanup clears expired items from cache
func (c *CacheController) CacheCleanup() error { func (c *CacheController) CacheCleanup() error {
expiredKeys, err := c.collectExpiredKeys() now := time.Now()
if err != nil { c.Lock()
return err defer c.Unlock()
}
if len(expiredKeys) == 0 {
return nil
}
c.writeAndShrink(expiredKeys)
return nil
}
func (c *CacheController) collectExpiredKeys() ([]string, error) {
c.RLock()
defer c.RUnlock()
if len(c.ips) == 0 { if len(c.ips) == 0 {
return nil, errors.New("nothing to do. stopping...") return errors.New("nothing to do. stopping...")
} }
// skip collection if a migration is in progress for domain, record := range c.ips {
if c.dirtyips != nil { if record.A != nil && record.A.Expire.Before(now) {
return nil, nil record.A = nil
}
if record.AAAA != nil && record.AAAA.Expire.Before(now) {
record.AAAA = nil
} }
now := time.Now() if record.A == nil && record.AAAA == nil {
if c.serveStale && c.serveExpiredTTL != 0 { errors.LogDebug(context.Background(), c.name, "cache cleanup ", domain)
now = now.Add(time.Duration(c.serveExpiredTTL) * time.Second)
}
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)
}
}
return expiredKeys, nil
}
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) delete(c.ips, domain)
} else {
c.ips[domain] = record
} }
} }
lenAfter := len(c.ips) if len(c.ips) == 0 {
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.ips = make(map[string]*record)
c.highWatermark = 0
}
return
} }
if reductionFromPeak := c.highWatermark - lenAfter; reductionFromPeak > shrinkAbsoluteThreshold && return nil
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()
} }
} func (c *CacheController) updateIP(req *dnsRequest, ipRec *IPRecord) {
elapsed := time.Since(req.start)
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()
dirtyips := c.dirtyips
c.RUnlock()
// double check to prevent upper call multiple cleanup tasks
if dirtyips == nil {
return
}
errors.LogDebug(context.Background(), c.name, " starting background cache migration for ", len(dirtyips), " items")
batch := make([]migrationEntry, 0, migrationBatchSize)
for domain, recD := range dirtyips {
batch = append(batch, migrationEntry{domain, recD})
if len(batch) >= migrationBatchSize {
c.flush(batch)
batch = batch[:0]
runtime.Gosched()
}
}
if len(batch) > 0 {
c.flush(batch)
}
c.Lock() c.Lock()
c.dirtyips = nil rec, found := c.ips[req.domain]
c.Unlock() if !found {
rec = &record{}
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 {
c.ips[dirty.key] = dirty.value
}
}
}
func (c *CacheController) updateRecord(req *dnsRequest, rep *IPRecord) {
rtt := time.Since(req.start)
switch req.reqType { switch req.reqType {
case dnsmessage.TypeA: case dnsmessage.TypeA:
c.pub.Publish(req.domain+"4", rep) rec.A = ipRec
case dnsmessage.TypeAAAA: case dnsmessage.TypeAAAA:
c.pub.Publish(req.domain+"6", rep) rec.AAAA = ipRec
} }
if c.disableCache { errors.LogInfo(context.Background(), c.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed)
errors.LogInfo(context.Background(), c.name, " got answer: ", req.domain, " ", req.reqType, " -> ", rep.IP, ", rtt: ", rtt) c.ips[req.domain] = rec
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 { switch req.reqType {
case dnsmessage.TypeA: case dnsmessage.TypeA:
newRec.A = rep c.pub.Publish(req.domain+"4", nil)
if oldRec != nil && oldRec.AAAA != nil { if !c.disableCache {
newRec.AAAA = oldRec.AAAA _, _, err := rec.AAAA.getIPs()
pubRecord = oldRec.AAAA if !go_errors.Is(err, errRecordNotFound) {
} else if dirtyRec != nil && dirtyRec.AAAA != nil { c.pub.Publish(req.domain+"6", nil)
pubRecord = dirtyRec.AAAA }
} }
pubSuffix = "6"
case dnsmessage.TypeAAAA: case dnsmessage.TypeAAAA:
newRec.AAAA = rep c.pub.Publish(req.domain+"6", nil)
if oldRec != nil && oldRec.A != nil { if !c.disableCache {
newRec.A = oldRec.A _, _, err := rec.A.getIPs()
pubRecord = oldRec.A if !go_errors.Is(err, errRecordNotFound) {
} else if dirtyRec != nil && dirtyRec.A != nil { c.pub.Publish(req.domain+"4", nil)
pubRecord = dirtyRec.A }
} }
pubSuffix = "4"
} }
c.ips[req.domain] = newRec
c.Unlock() c.Unlock()
if pubRecord != nil {
_, ttl, err := pubRecord.getIPs()
if ttl > 0 && !go_errors.Is(err, errRecordNotFound) {
c.pub.Publish(req.domain+pubSuffix, pubRecord)
}
}
errors.LogInfo(context.Background(), c.name, " got answer: ", req.domain, " ", req.reqType, " -> ", rep.IP, ", rtt: ", rtt, ", lock: ", lockWait)
if !c.serveStale || c.serveExpiredTTL != 0 {
common.Must(c.cacheCleanup.Start()) common.Must(c.cacheCleanup.Start())
} }
}
func (c *CacheController) findRecords(domain string) *record { func (c *CacheController) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
c.RLock() c.RLock()
defer c.RUnlock() record, found := c.ips[domain]
c.RUnlock()
rec := c.ips[domain] if !found {
if rec == nil && c.dirtyips != nil { return nil, 0, errRecordNotFound
rec = c.dirtyips[domain]
} }
return rec
var errs []error
var allIPs []net.IP
var rTTL uint32 = dns_feature.DefaultTTL
mergeReq := option.IPv4Enable && option.IPv6Enable
if option.IPv4Enable {
ips, ttl, err := record.A.getIPs()
if !mergeReq || go_errors.Is(err, errRecordNotFound) {
return ips, ttl, err
}
if ttl < rTTL {
rTTL = ttl
}
if len(ips) > 0 {
allIPs = append(allIPs, ips...)
} else {
errs = append(errs, err)
}
}
if option.IPv6Enable {
ips, ttl, err := record.AAAA.getIPs()
if !mergeReq || go_errors.Is(err, errRecordNotFound) {
return ips, ttl, err
}
if ttl < rTTL {
rTTL = ttl
}
if len(ips) > 0 {
allIPs = append(allIPs, ips...)
} else {
errs = append(errs, err)
}
}
if len(allIPs) > 0 {
return allIPs, rTTL, nil
}
if go_errors.Is(errs[0], errs[1]) {
return nil, rTTL, errs[0]
}
return nil, rTTL, errors.Combine(errs...)
} }
func (c *CacheController) registerSubscribers(domain string, option dns_feature.IPOption) (sub4 *pubsub.Subscriber, sub6 *pubsub.Subscriber) { func (c *CacheController) registerSubscribers(domain string, option dns_feature.IPOption) (sub4 *pubsub.Subscriber, sub6 *pubsub.Subscriber) {
+72 -140
View File
@@ -141,13 +141,10 @@ type NameServer struct {
ActPrior bool `protobuf:"varint,8,opt,name=actPrior,proto3" json:"actPrior,omitempty"` ActPrior bool `protobuf:"varint,8,opt,name=actPrior,proto3" json:"actPrior,omitempty"`
Tag string `protobuf:"bytes,9,opt,name=tag,proto3" json:"tag,omitempty"` Tag string `protobuf:"bytes,9,opt,name=tag,proto3" json:"tag,omitempty"`
TimeoutMs uint64 `protobuf:"varint,10,opt,name=timeoutMs,proto3" json:"timeoutMs,omitempty"` TimeoutMs uint64 `protobuf:"varint,10,opt,name=timeoutMs,proto3" json:"timeoutMs,omitempty"`
DisableCache *bool `protobuf:"varint,11,opt,name=disableCache,proto3,oneof" json:"disableCache,omitempty"` DisableCache bool `protobuf:"varint,11,opt,name=disableCache,proto3" 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"` 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"` 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"` 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() { func (x *NameServer) Reset() {
@@ -251,26 +248,12 @@ func (x *NameServer) GetTimeoutMs() uint64 {
} }
func (x *NameServer) GetDisableCache() bool { func (x *NameServer) GetDisableCache() bool {
if x != nil && x.DisableCache != nil { if x != nil {
return *x.DisableCache return x.DisableCache
} }
return false 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 { func (x *NameServer) GetFinalQuery() bool {
if x != nil { if x != nil {
return x.FinalQuery return x.FinalQuery
@@ -292,13 +275,6 @@ func (x *NameServer) GetActUnprior() bool {
return false return false
} }
func (x *NameServer) GetPolicyID() uint32 {
if x != nil {
return x.PolicyID
}
return 0
}
type Config struct { type Config struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -315,12 +291,9 @@ type Config struct {
Tag string `protobuf:"bytes,6,opt,name=tag,proto3" json:"tag,omitempty"` Tag string `protobuf:"bytes,6,opt,name=tag,proto3" json:"tag,omitempty"`
// DisableCache disables DNS cache // DisableCache disables DNS cache
DisableCache bool `protobuf:"varint,8,opt,name=disableCache,proto3" json:"disableCache,omitempty"` 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"` 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"` DisableFallback bool `protobuf:"varint,10,opt,name=disableFallback,proto3" json:"disableFallback,omitempty"`
DisableFallbackIfMatch bool `protobuf:"varint,11,opt,name=disableFallbackIfMatch,proto3" json:"disableFallbackIfMatch,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() { func (x *Config) Reset() {
@@ -388,20 +361,6 @@ func (x *Config) GetDisableCache() bool {
return false 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 { func (x *Config) GetQueryStrategy() QueryStrategy {
if x != nil { if x != nil {
return x.QueryStrategy return x.QueryStrategy
@@ -423,13 +382,6 @@ func (x *Config) GetDisableFallbackIfMatch() bool {
return false return false
} }
func (x *Config) GetEnableParallelQuery() bool {
if x != nil {
return x.EnableParallelQuery
}
return false
}
type NameServer_PriorityDomain struct { type NameServer_PriorityDomain struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -615,7 +567,7 @@ var file_app_dns_config_proto_rawDesc = []byte{
0x2e, 0x64, 0x6e, 0x73, 0x1a, 0x1c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 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, 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, 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, 0xdf, 0x07, 0x0a, 0x0a, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb6, 0x06, 0x0a, 0x0a,
0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64, 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, 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, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e,
@@ -647,93 +599,74 @@ var file_app_dns_config_proto_rawDesc = []byte{
0x50, 0x72, 0x69, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x09, 0x20, 0x01, 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, 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, 0x75, 0x74, 0x4d, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65,
0x6f, 0x75, 0x74, 0x4d, 0x73, 0x12, 0x27, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 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, 0x48, 0x00, 0x52, 0x0c, 0x64, 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73,
0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x88, 0x01, 0x01, 0x12, 0x23, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6e,
0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x74, 0x61, 0x6c, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66,
0x28, 0x08, 0x48, 0x01, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x74, 0x61, 0x6c, 0x65, 0x69, 0x6e, 0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x41, 0x0a, 0x10, 0x75, 0x6e, 0x65,
0x88, 0x01, 0x01, 0x12, 0x2d, 0x0a, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x45, 0x78, 0x70, 0x69, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0d, 0x20,
0x72, 0x65, 0x64, 0x54, 0x54, 0x4c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, 0x0f, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72,
0x73, 0x65, 0x72, 0x76, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x54, 0x54, 0x4c, 0x88, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0f, 0x75, 0x6e, 0x65,
0x01, 0x01, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x1e, 0x0a, 0x0a,
0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x51, 0x75, 0x65, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08,
0x72, 0x79, 0x12, 0x41, 0x0a, 0x10, 0x75, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x1a, 0x5e, 0x0a, 0x0e,
0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x34,
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78,
0x65, 0x6f, 0x49, 0x50, 0x52, 0x0f, 0x75, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61,
0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04,
0x69, 0x6f, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02,
0x70, 0x72, 0x69, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x36, 0x0a, 0x0c,
0x44, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04,
0x44, 0x1a, 0x5e, 0x0a, 0x0e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65,
0x61, 0x69, 0x6e, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04,
0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x9c, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x05,
0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0a,
0x6e, 0x1a, 0x36, 0x0a, 0x0c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c,
0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63,
0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69,
0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x64, 0x69, 0x63, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e,
0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x73, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e,
0x65, 0x72, 0x76, 0x65, 0x53, 0x74, 0x61, 0x6c, 0x65, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x65, 0x66, 0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52,
0x72, 0x76, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x54, 0x54, 0x4c, 0x22, 0x98, 0x05, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03,
0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x22,
0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x08,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63,
0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x68, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61,
0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x74, 0x65, 0x67, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53,
0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74,
0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52,
0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b,
0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x12, 0x36, 0x0a, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62,
0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08,
0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63,
0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73,
0x65, 0x72, 0x76, 0x65, 0x53, 0x74, 0x61, 0x6c, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x74, 0x61, 0x6c, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x65, 0x72, 0x76, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x54, 0x54, 0x4c, 0x18, 0x0d, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63,
0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16,
0x65, 0x64, 0x54, 0x54, 0x4c, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65,
0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08,
0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x07, 0x10, 0x08, 0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74,
0x28, 0x08, 0x52, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c,
0x61, 0x63, 0x6b, 0x12, 0x36, 0x0a, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x02, 0x12,
0x01, 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x0d, 0x51, 0x75,
0x62, 0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, 0x0a, 0x13, 0x65, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0a, 0x0a, 0x06, 0x55,
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x51, 0x75, 0x65, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49,
0x72, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x34, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10,
0x50, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x1a, 0x92, 0x01, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x53, 0x59, 0x53, 0x10, 0x03, 0x42, 0x46,
0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64,
0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x6e, 0x73, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f,
0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41,
0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
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 ( var (
@@ -785,7 +718,6 @@ func file_app_dns_config_proto_init() {
if File_app_dns_config_proto != nil { if File_app_dns_config_proto != nil {
return return
} }
file_app_dns_config_proto_msgTypes[0].OneofWrappers = []any{}
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
+1 -8
View File
@@ -31,13 +31,10 @@ message NameServer {
bool actPrior = 8; bool actPrior = 8;
string tag = 9; string tag = 9;
uint64 timeoutMs = 10; uint64 timeoutMs = 10;
optional bool disableCache = 11; bool disableCache = 11;
optional bool serveStale = 15;
optional uint32 serveExpiredTTL = 16;
bool finalQuery = 12; bool finalQuery = 12;
repeated xray.app.router.GeoIP unexpected_geoip = 13; repeated xray.app.router.GeoIP unexpected_geoip = 13;
bool actUnprior = 14; bool actUnprior = 14;
uint32 policyID = 17;
} }
enum DomainMatchingType { enum DomainMatchingType {
@@ -83,13 +80,9 @@ message Config {
// DisableCache disables DNS cache // DisableCache disables DNS cache
bool disableCache = 8; bool disableCache = 8;
bool serveStale = 12;
uint32 serveExpiredTTL = 13;
QueryStrategy query_strategy = 9; QueryStrategy query_strategy = 9;
bool disableFallback = 10; bool disableFallback = 10;
bool disableFallbackIfMatch = 11; bool disableFallbackIfMatch = 11;
bool enableParallelQuery = 14;
} }
+56 -291
View File
@@ -5,12 +5,9 @@ import (
"context" "context"
go_errors "errors" go_errors "errors"
"fmt" "fmt"
"os"
"runtime"
"sort" "sort"
"strings" "strings"
"sync" "sync"
"time"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
@@ -25,7 +22,6 @@ type DNS struct {
sync.Mutex sync.Mutex
disableFallback bool disableFallback bool
disableFallbackIfMatch bool disableFallbackIfMatch bool
enableParallelQuery bool
ipOption *dns.IPOption ipOption *dns.IPOption
hosts *StaticHosts hosts *StaticHosts
clients []*Client clients []*Client
@@ -121,20 +117,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
myClientIP = net.IP(ns.ClientIp) myClientIP = net.IP(ns.ClientIp)
} }
disableCache := config.DisableCache disableCache := config.DisableCache || ns.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 var tag = defaultTag
if len(ns.Tag) > 0 { if len(ns.Tag) > 0 {
@@ -145,7 +128,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
return nil, errors.New("no QueryStrategy available for ", ns.Address) return nil, errors.New("no QueryStrategy available for ", ns.Address)
} }
client, err := NewClient(ctx, ns, myClientIP, disableCache, serveStale, serveExpiredTTL, tag, clientIPOption, &matcherInfos, updateDomain) client, err := NewClient(ctx, ns, myClientIP, disableCache, tag, clientIPOption, &matcherInfos, updateDomain)
if err != nil { if err != nil {
return nil, errors.New("failed to create client").Base(err) return nil, errors.New("failed to create client").Base(err)
} }
@@ -166,7 +149,6 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
matcherInfos: matcherInfos, matcherInfos: matcherInfos,
disableFallback: config.DisableFallback, disableFallback: config.DisableFallback,
disableFallbackIfMatch: config.DisableFallbackIfMatch, disableFallbackIfMatch: config.DisableFallbackIfMatch,
enableParallelQuery: config.EnableParallelQuery,
checkSystem: checkSystem, checkSystem: checkSystem,
}, nil }, nil
} }
@@ -209,7 +191,7 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er
} }
if s.checkSystem { if s.checkSystem {
supportIPv4, supportIPv6 := checkRoutes() supportIPv4, supportIPv6 := checkSystemNetwork()
option.IPv4Enable = option.IPv4Enable && supportIPv4 option.IPv4Enable = option.IPv4Enable && supportIPv4
option.IPv6Enable = option.IPv6Enable && supportIPv6 option.IPv6Enable = option.IPv6Enable && supportIPv6
} else { } else {
@@ -245,11 +227,45 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er
} }
// Name servers lookup // Name servers lookup
if s.enableParallelQuery { var errs []error
return s.parallelQuery(domain, option) for _, client := range s.sortClients(domain) {
} else { if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") {
return s.serialQuery(domain, option) 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 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 { func (s *DNS) sortClients(domain string) []*Client {
@@ -276,9 +292,6 @@ func (s *DNS) sortClients(domain string) []*Client {
clients = append(clients, client) clients = append(clients, client)
clientNames = append(clientNames, client.Name()) clientNames = append(clientNames, client.Name())
hasMatch = true hasMatch = true
if client.finalQuery {
return clients
}
} }
if !(s.disableFallback || s.disableFallbackIfMatch && hasMatch) { if !(s.disableFallback || s.disableFallbackIfMatch && hasMatch) {
@@ -290,9 +303,6 @@ func (s *DNS) sortClients(domain string) []*Client {
clientUsed[idx] = true clientUsed[idx] = true
clients = append(clients, client) clients = append(clients, client)
clientNames = append(clientNames, client.Name()) clientNames = append(clientNames, client.Name())
if client.finalQuery {
return clients
}
} }
} }
@@ -304,280 +314,35 @@ func (s *DNS) sortClients(domain string) []*Client {
} }
if len(clients) == 0 { if len(clients) == 0 {
if len(s.clients) > 0 {
clients = append(clients, s.clients[0]) clients = append(clients, s.clients[0])
clientNames = append(clientNames, s.clients[0].Name()) clientNames = append(clientNames, s.clients[0].Name())
errors.LogWarning(s.ctx, "domain ", domain, " will use the first DNS: ", clientNames) errors.LogDebug(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 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() { func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return New(ctx, config.(*Config)) return New(ctx, config.(*Config))
})) }))
} }
func probeRoutes() (ipv4 bool, ipv6 bool) { func checkSystemNetwork() (supportIPv4 bool, supportIPv6 bool) {
if conn, err := net.Dial("udp4", "192.33.4.12:53"); err == nil { conn4, err4 := net.Dial("udp4", "192.33.4.12:53")
ipv4 = true if err4 != nil {
conn.Close() supportIPv4 = false
} else {
supportIPv4 = true
conn4.Close()
} }
if conn, err := net.Dial("udp6", "[2001:500:2::c]:53"); err == nil {
ipv6 = true conn6, err6 := net.Dial("udp6", "[2001:500:2::c]:53")
conn.Close() if err6 != nil {
supportIPv6 = false
} else {
supportIPv6 = true
conn6.Close()
} }
return 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
}
+8 -6
View File
@@ -3,7 +3,6 @@ package dns
import ( import (
"context" "context"
"encoding/binary" "encoding/binary"
"math"
"strings" "strings"
"time" "time"
@@ -14,12 +13,10 @@ import (
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/core" "github.com/xtls/xray-core/core"
dns_feature "github.com/xtls/xray-core/features/dns" dns_feature "github.com/xtls/xray-core/features/dns"
"golang.org/x/net/dns/dnsmessage" "golang.org/x/net/dns/dnsmessage"
) )
// Fqdn normalizes domain make sure it ends with '.' // Fqdn normalizes domain make sure it ends with '.'
// case-sensitive
func Fqdn(domain string) string { func Fqdn(domain string) string {
if len(domain) > 0 && strings.HasSuffix(domain, ".") { if len(domain) > 0 && strings.HasSuffix(domain, ".") {
return domain return domain
@@ -41,14 +38,19 @@ type IPRecord struct {
RawHeader *dnsmessage.Header RawHeader *dnsmessage.Header
} }
func (r *IPRecord) getIPs() ([]net.IP, int32, error) { func (r *IPRecord) getIPs() ([]net.IP, uint32, error) {
if r == nil { if r == nil {
return nil, 0, errRecordNotFound return nil, 0, errRecordNotFound
} }
untilExpire := time.Until(r.Expire).Seconds() untilExpire := time.Until(r.Expire).Seconds()
ttl := int32(math.Ceil(untilExpire)) if untilExpire <= 0 {
return nil, 0, errRecordNotFound
}
ttl := uint32(untilExpire) + 1
if ttl == 1 {
r.Expire = time.Now().Add(time.Second) // To ensure that two consecutive requests get the same result
}
if r.RCode != dnsmessage.RCodeSuccess { if r.RCode != dnsmessage.RCodeSuccess {
return nil, ttl, dns_feature.RCodeError(r.RCode) return nil, ttl, dns_feature.RCodeError(r.RCode)
} }
+4 -3
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"math" "math"
"math/big" "math/big"
gonet "net"
"sync" "sync"
"time" "time"
@@ -16,7 +17,7 @@ import (
type Holder struct { type Holder struct {
domainToIP cache.Lru domainToIP cache.Lru
ipRange *net.IPNet ipRange *gonet.IPNet
mu *sync.Mutex mu *sync.Mutex
config *FakeDnsPool config *FakeDnsPool
@@ -78,10 +79,10 @@ func (fkdns *Holder) initializeFromConfig() error {
} }
func (fkdns *Holder) initialize(ipPoolCidr string, lruSize int) error { func (fkdns *Holder) initialize(ipPoolCidr string, lruSize int) error {
var ipRange *net.IPNet var ipRange *gonet.IPNet
var err error var err error
if _, ipRange, err = net.ParseCIDR(ipPoolCidr); err != nil { if _, ipRange, err = gonet.ParseCIDR(ipPoolCidr); err != nil {
return errors.New("Unable to parse CIDR for Fake DNS IP assignment").Base(err).AtError() return errors.New("Unable to parse CIDR for Fake DNS IP assignment").Base(err).AtError()
} }
+3 -2
View File
@@ -1,6 +1,7 @@
package fakedns package fakedns
import ( import (
gonet "net"
"strconv" "strconv"
"testing" "testing"
@@ -154,7 +155,7 @@ func TestFakeDNSMulti(t *testing.T) {
assert.True(t, inPool) assert.True(t, inPool)
}) })
t.Run("ipv6", func(t *testing.T) { t.Run("ipv6", func(t *testing.T) {
ip, err := net.ResolveIPAddr("ip", "fddd:c5b4:ff5f:f4f0::5") ip, err := gonet.ResolveIPAddr("ip", "fddd:c5b4:ff5f:f4f0::5")
assert.Nil(t, err) assert.Nil(t, err)
inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP)) inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP))
assert.True(t, inPool) assert.True(t, inPool)
@@ -164,7 +165,7 @@ func TestFakeDNSMulti(t *testing.T) {
assert.False(t, inPool) assert.False(t, inPool)
}) })
t.Run("ipv6_inverse", func(t *testing.T) { t.Run("ipv6_inverse", func(t *testing.T) {
ip, err := net.ResolveIPAddr("ip", "fcdd:c5b4:ff5f:f4f0::5") ip, err := gonet.ResolveIPAddr("ip", "fcdd:c5b4:ff5f:f4f0::5")
assert.Nil(t, err) assert.Nil(t, err)
inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP)) inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP))
assert.False(t, inPool) assert.False(t, inPool)
+36 -35
View File
@@ -20,9 +20,6 @@ import (
type Server interface { type Server interface {
// Name of the Client. // Name of the Client.
Name() string Name() string
IsDisableCache() bool
// QueryIP sends IP queries to its configured server. // QueryIP sends IP queries to its configured server.
QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error)
} }
@@ -32,8 +29,8 @@ type Client struct {
server Server server Server
skipFallback bool skipFallback bool
domains []string domains []string
expectedIPs router.GeoIPMatcher expectedIPs []*router.GeoIPMatcher
unexpectedIPs router.GeoIPMatcher unexpectedIPs []*router.GeoIPMatcher
actPrior bool actPrior bool
actUnprior bool actUnprior bool
tag string tag string
@@ -41,11 +38,10 @@ type Client struct {
finalQuery bool finalQuery bool
ipOption *dns.IPOption ipOption *dns.IPOption
checkSystem bool checkSystem bool
policyID uint32
} }
// NewServer creates a name server object according to the network destination url. // 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, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (Server, error) { func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dispatcher, disableCache bool, clientIP net.IP) (Server, error) {
if address := dest.Address; address.Family().IsDomain() { if address := dest.Address; address.Family().IsDomain() {
u, err := url.Parse(address.Domain()) u, err := url.Parse(address.Domain())
if err != nil { if err != nil {
@@ -55,19 +51,19 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
case strings.EqualFold(u.String(), "localhost"): case strings.EqualFold(u.String(), "localhost"):
return NewLocalNameServer(), nil return NewLocalNameServer(), nil
case strings.EqualFold(u.Scheme, "https"): // DNS-over-HTTPS Remote mode case strings.EqualFold(u.Scheme, "https"): // DNS-over-HTTPS Remote mode
return NewDoHNameServer(u, dispatcher, false, disableCache, serveStale, serveExpiredTTL, clientIP), nil return NewDoHNameServer(u, dispatcher, false, disableCache, clientIP), nil
case strings.EqualFold(u.Scheme, "h2c"): // DNS-over-HTTPS h2c Remote mode case strings.EqualFold(u.Scheme, "h2c"): // DNS-over-HTTPS h2c Remote mode
return NewDoHNameServer(u, dispatcher, true, disableCache, serveStale, serveExpiredTTL, clientIP), nil return NewDoHNameServer(u, dispatcher, true, disableCache, clientIP), nil
case strings.EqualFold(u.Scheme, "https+local"): // DNS-over-HTTPS Local mode case strings.EqualFold(u.Scheme, "https+local"): // DNS-over-HTTPS Local mode
return NewDoHNameServer(u, nil, false, disableCache, serveStale, serveExpiredTTL, clientIP), nil return NewDoHNameServer(u, nil, false, disableCache, clientIP), nil
case strings.EqualFold(u.Scheme, "h2c+local"): // DNS-over-HTTPS h2c Local mode case strings.EqualFold(u.Scheme, "h2c+local"): // DNS-over-HTTPS h2c Local mode
return NewDoHNameServer(u, nil, true, disableCache, serveStale, serveExpiredTTL, clientIP), nil return NewDoHNameServer(u, nil, true, disableCache, clientIP), nil
case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode
return NewQUICNameServer(u, disableCache, serveStale, serveExpiredTTL, clientIP) return NewQUICNameServer(u, disableCache, clientIP)
case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode
return NewTCPNameServer(u, dispatcher, disableCache, serveStale, serveExpiredTTL, clientIP) return NewTCPNameServer(u, dispatcher, disableCache, clientIP)
case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode
return NewTCPLocalNameServer(u, disableCache, serveStale, serveExpiredTTL, clientIP) return NewTCPLocalNameServer(u, disableCache, clientIP)
case strings.EqualFold(u.String(), "fakedns"): case strings.EqualFold(u.String(), "fakedns"):
var fd dns.FakeDNSEngine var fd dns.FakeDNSEngine
err = core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) { err = core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
@@ -83,7 +79,7 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
dest.Network = net.Network_UDP dest.Network = net.Network_UDP
} }
if dest.Network == net.Network_UDP { // UDP classic DNS mode if dest.Network == net.Network_UDP { // UDP classic DNS mode
return NewClassicNameServer(dest, dispatcher, disableCache, serveStale, serveExpiredTTL, clientIP), nil return NewClassicNameServer(dest, dispatcher, disableCache, clientIP), nil
} }
return nil, errors.New("No available name server could be created from ", dest).AtWarning() return nil, errors.New("No available name server could be created from ", dest).AtWarning()
} }
@@ -93,7 +89,7 @@ func NewClient(
ctx context.Context, ctx context.Context,
ns *NameServer, ns *NameServer,
clientIP net.IP, clientIP net.IP,
disableCache bool, serveStale bool, serveExpiredTTL uint32, disableCache bool,
tag string, tag string,
ipOption dns.IPOption, ipOption dns.IPOption,
matcherInfos *[]*DomainMatcherInfo, matcherInfos *[]*DomainMatcherInfo,
@@ -103,7 +99,7 @@ func NewClient(
err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error { err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
// Create a new server for each client for now // Create a new server for each client for now
server, err := NewServer(ctx, ns.Address.AsDestination(), dispatcher, disableCache, serveStale, serveExpiredTTL, clientIP) server, err := NewServer(ctx, ns.Address.AsDestination(), dispatcher, disableCache, clientIP)
if err != nil { if err != nil {
return errors.New("failed to create nameserver").Base(err).AtWarning() return errors.New("failed to create nameserver").Base(err).AtWarning()
} }
@@ -158,21 +154,23 @@ func NewClient(
} }
// Establish expected IPs // Establish expected IPs
var expectedMatcher router.GeoIPMatcher var expectedMatchers []*router.GeoIPMatcher
if len(ns.ExpectedGeoip) > 0 { for _, geoip := range ns.ExpectedGeoip {
expectedMatcher, err = router.BuildOptimizedGeoIPMatcher(ns.ExpectedGeoip...) matcher, err := router.GlobalGeoIPContainer.Add(geoip)
if err != nil { if err != nil {
return errors.New("failed to create expected ip matcher").Base(err).AtWarning() return errors.New("failed to create expected ip matcher").Base(err).AtWarning()
} }
expectedMatchers = append(expectedMatchers, matcher)
} }
// Establish unexpected IPs // Establish unexpected IPs
var unexpectedMatcher router.GeoIPMatcher var unexpectedMatchers []*router.GeoIPMatcher
if len(ns.UnexpectedGeoip) > 0 { for _, geoip := range ns.UnexpectedGeoip {
unexpectedMatcher, err = router.BuildOptimizedGeoIPMatcher(ns.UnexpectedGeoip...) matcher, err := router.GlobalGeoIPContainer.Add(geoip)
if err != nil { if err != nil {
return errors.New("failed to create unexpected ip matcher").Base(err).AtWarning() return errors.New("failed to create unexpected ip matcher").Base(err).AtWarning()
} }
unexpectedMatchers = append(unexpectedMatchers, matcher)
} }
if len(clientIP) > 0 { if len(clientIP) > 0 {
@@ -194,8 +192,8 @@ func NewClient(
client.server = server client.server = server
client.skipFallback = ns.SkipFallback client.skipFallback = ns.SkipFallback
client.domains = rules client.domains = rules
client.expectedIPs = expectedMatcher client.expectedIPs = expectedMatchers
client.unexpectedIPs = unexpectedMatcher client.unexpectedIPs = unexpectedMatchers
client.actPrior = ns.ActPrior client.actPrior = ns.ActPrior
client.actUnprior = ns.ActUnprior client.actUnprior = ns.ActUnprior
client.tag = tag client.tag = tag
@@ -203,7 +201,6 @@ func NewClient(
client.finalQuery = ns.FinalQuery client.finalQuery = ns.FinalQuery
client.ipOption = &ipOption client.ipOption = &ipOption
client.checkSystem = checkSystem client.checkSystem = checkSystem
client.policyID = ns.PolicyID
return nil return nil
}) })
return client, err return client, err
@@ -214,10 +211,14 @@ func (c *Client) Name() string {
return c.server.Name() 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. // 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) { func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error) {
if c.checkSystem { if c.checkSystem {
supportIPv4, supportIPv6 := checkRoutes() supportIPv4, supportIPv6 := checkSystemNetwork()
option.IPv4Enable = option.IPv4Enable && supportIPv4 option.IPv4Enable = option.IPv4Enable && supportIPv4
option.IPv6Enable = option.IPv6Enable && supportIPv6 option.IPv6Enable = option.IPv6Enable && supportIPv6
} else { } else {
@@ -242,32 +243,32 @@ func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption
return nil, 0, dns.ErrEmptyResponse return nil, 0, dns.ErrEmptyResponse
} }
if c.expectedIPs != nil && !c.actPrior { if len(c.expectedIPs) > 0 && !c.actPrior {
ips, _ = c.expectedIPs.FilterIPs(ips) ips = router.MatchIPs(c.expectedIPs, ips, false)
errors.LogDebug(context.Background(), "domain ", domain, " expectedIPs ", ips, " matched at server ", c.Name()) errors.LogDebug(context.Background(), "domain ", domain, " expectedIPs ", ips, " matched at server ", c.Name())
if len(ips) == 0 { if len(ips) == 0 {
return nil, 0, dns.ErrEmptyResponse return nil, 0, dns.ErrEmptyResponse
} }
} }
if c.unexpectedIPs != nil && !c.actUnprior { if len(c.unexpectedIPs) > 0 && !c.actUnprior {
_, ips = c.unexpectedIPs.FilterIPs(ips) ips = router.MatchIPs(c.unexpectedIPs, ips, true)
errors.LogDebug(context.Background(), "domain ", domain, " unexpectedIPs ", ips, " matched at server ", c.Name()) errors.LogDebug(context.Background(), "domain ", domain, " unexpectedIPs ", ips, " matched at server ", c.Name())
if len(ips) == 0 { if len(ips) == 0 {
return nil, 0, dns.ErrEmptyResponse return nil, 0, dns.ErrEmptyResponse
} }
} }
if c.expectedIPs != nil && c.actPrior { if len(c.expectedIPs) > 0 && c.actPrior {
ipsNew, _ := c.expectedIPs.FilterIPs(ips) ipsNew := router.MatchIPs(c.expectedIPs, ips, false)
if len(ipsNew) > 0 { if len(ipsNew) > 0 {
ips = ipsNew ips = ipsNew
errors.LogDebug(context.Background(), "domain ", domain, " priorIPs ", ips, " matched at server ", c.Name()) errors.LogDebug(context.Background(), "domain ", domain, " priorIPs ", ips, " matched at server ", c.Name())
} }
} }
if c.unexpectedIPs != nil && c.actUnprior { if len(c.unexpectedIPs) > 0 && c.actUnprior {
_, ipsNew := c.unexpectedIPs.FilterIPs(ips) ipsNew := router.MatchIPs(c.unexpectedIPs, ips, true)
if len(ipsNew) > 0 { if len(ipsNew) > 0 {
ips = ipsNew ips = ipsNew
errors.LogDebug(context.Background(), "domain ", domain, " unpriorIPs ", ips, " matched at server ", c.Name()) errors.LogDebug(context.Background(), "domain ", domain, " unpriorIPs ", ips, " matched at server ", c.Name())
-173
View File
@@ -1,173 +0,0 @@
package dns
import (
"context"
go_errors "errors"
"time"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/signal/pubsub"
"github.com/xtls/xray-core/features/dns"
)
type CachedNameserver interface {
getCacheController() *CacheController
sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns.IPOption)
}
// queryIP is called from dns.Server->queryIPTimeout
func queryIP(ctx context.Context, s CachedNameserver, domain string, option dns.IPOption) ([]net.IP, uint32, error) {
fqdn := Fqdn(domain)
cache := s.getCacheController()
if !cache.disableCache {
if rec := cache.findRecords(fqdn); rec != nil {
ips, ttl, err := merge(option, rec.A, rec.AAAA)
if !go_errors.Is(err, errRecordNotFound) {
if ttl > 0 {
errors.LogDebugInner(ctx, err, cache.name, " cache HIT ", fqdn, " -> ", ips)
log.Record(&log.DNSLog{Server: cache.name, Domain: fqdn, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, uint32(ttl), err
}
if cache.serveStale && (cache.serveExpiredTTL == 0 || cache.serveExpiredTTL < ttl) {
errors.LogDebugInner(ctx, err, cache.name, " cache OPTIMISTE ", fqdn, " -> ", ips)
log.Record(&log.DNSLog{Server: cache.name, Domain: fqdn, Result: ips, Status: log.DNSCacheOptimiste, Elapsed: 0, Error: err})
go pull(ctx, s, fqdn, option)
return ips, 1, err
}
}
}
} else {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", fqdn, " at ", cache.name)
}
return fetch(ctx, s, fqdn, option)
}
func pull(ctx context.Context, s CachedNameserver, fqdn string, option dns.IPOption) {
nctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 8*time.Second)
defer cancel()
fetch(nctx, s, fqdn, option)
}
func fetch(ctx context.Context, s CachedNameserver, fqdn string, option dns.IPOption) ([]net.IP, uint32, error) {
key := fqdn
switch {
case option.IPv4Enable && option.IPv6Enable:
key = key + "46"
case option.IPv4Enable:
key = key + "4"
case option.IPv6Enable:
key = key + "6"
}
v, _, _ := s.getCacheController().requestGroup.Do(key, func() (any, error) {
return doFetch(ctx, s, fqdn, option), nil
})
ret := v.(result)
return ret.ips, ret.ttl, ret.error
}
type result struct {
ips []net.IP
ttl uint32
error
}
func doFetch(ctx context.Context, s CachedNameserver, fqdn string, option dns.IPOption) result {
sub4, sub6 := s.getCacheController().registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
noResponseErrCh := make(chan error, 2)
onEvent := func(sub *pubsub.Subscriber) (*IPRecord, error) {
if sub == nil {
return nil, nil
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case err := <-noResponseErrCh:
return nil, err
case msg := <-sub.Wait():
sub.Close()
return msg.(*IPRecord), nil // should panic
}
}
start := time.Now()
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
rec4, err4 := onEvent(sub4)
rec6, err6 := onEvent(sub6)
var errs []error
if err4 != nil {
errs = append(errs, err4)
}
if err6 != nil {
errs = append(errs, err6)
}
ips, ttl, err := merge(option, rec4, rec6, errs...)
var rTTL uint32
if ttl > 0 {
rTTL = uint32(ttl)
} else if ttl == 0 && go_errors.Is(err, errRecordNotFound) {
rTTL = 0
} else { // edge case: where a fast rep's ttl expires during the rtt of a slower, parallel query
rTTL = 1
}
log.Record(&log.DNSLog{Server: s.getCacheController().name, Domain: fqdn, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return result{ips, rTTL, err}
}
func merge(option dns.IPOption, rec4 *IPRecord, rec6 *IPRecord, errs ...error) ([]net.IP, int32, error) {
var allIPs []net.IP
var rTTL int32 = dns.DefaultTTL
mergeReq := option.IPv4Enable && option.IPv6Enable
if option.IPv4Enable {
ips, ttl, err := rec4.getIPs() // it's safe
if !mergeReq || go_errors.Is(err, errRecordNotFound) {
return ips, ttl, err
}
if ttl < rTTL {
rTTL = ttl
}
if len(ips) > 0 {
allIPs = append(allIPs, ips...)
} else {
errs = append(errs, err)
}
}
if option.IPv6Enable {
ips, ttl, err := rec6.getIPs() // it's safe
if !mergeReq || go_errors.Is(err, errRecordNotFound) {
return ips, ttl, err
}
if ttl < rTTL {
rTTL = ttl
}
if len(ips) > 0 {
allIPs = append(allIPs, ips...)
} else {
errs = append(errs, err)
}
}
if len(allIPs) > 0 {
return allIPs, rTTL, nil
}
if len(errs) == 2 && go_errors.Is(errs[0], errs[1]) {
return nil, rTTL, errs[0]
}
return nil, rTTL, errors.Combine(errs...)
}
+57 -32
View File
@@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"crypto/tls" "crypto/tls"
go_errors "errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@@ -37,7 +38,7 @@ type DoHNameServer struct {
} }
// NewDoHNameServer creates DOH/DOHL client object for remote/local resolving. // NewDoHNameServer creates DOH/DOHL client object for remote/local resolving.
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, h2c bool, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) *DoHNameServer { func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, h2c bool, disableCache bool, clientIP net.IP) *DoHNameServer {
url.Scheme = "https" url.Scheme = "https"
mode := "DOH" mode := "DOH"
if dispatcher == nil { if dispatcher == nil {
@@ -45,7 +46,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) errors.LogInfo(context.Background(), "DNS: created ", mode, " client for ", url.String(), ", with h2c ", h2c)
s := &DoHNameServer{ s := &DoHNameServer{
cacheController: NewCacheController(mode+"//"+url.Host, disableCache, serveStale, serveExpiredTTL), cacheController: NewCacheController(mode+"//"+url.Host, disableCache),
dohURL: url.String(), dohURL: url.String(),
clientIP: clientIP, clientIP: clientIP,
} }
@@ -116,35 +117,22 @@ func (s *DoHNameServer) Name() string {
return s.cacheController.name return s.cacheController.name
} }
// IsDisableCache implements Server.
func (s *DoHNameServer) IsDisableCache() bool {
return s.cacheController.disableCache
}
func (s *DoHNameServer) newReqID() uint16 { func (s *DoHNameServer) newReqID() uint16 {
return 0 return 0
} }
// getCacheController implements CachedNameserver. func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) {
func (s *DoHNameServer) getCacheController() *CacheController { errors.LogInfo(ctx, s.Name(), " querying: ", domain)
return s.cacheController
}
// sendQuery implements CachedNameserver. if s.Name()+"." == "DOH//"+domain {
func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) { errors.LogError(ctx, s.Name(), " tries to resolve itself! Use IP or set \"hosts\" instead.")
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()) noResponseErrCh <- errors.New("tries to resolve itself!", s.Name())
}
return 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 // 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 // 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(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, int(crypto.RandBetween(100, 300)))) reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, int(crypto.RandBetween(100, 300))))
var deadline time.Time var deadline time.Time
if d, ok := ctx.Deadline(); ok { if d, ok := ctx.Deadline(); ok {
@@ -178,29 +166,23 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
b, err := dns.PackMessage(r.msg) b, err := dns.PackMessage(r.msg)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query for ", fqdn) errors.LogErrorInner(ctx, err, "failed to pack dns query for ", domain)
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
resp, err := s.dohHTTPSContext(dnsCtx, b.Bytes()) resp, err := s.dohHTTPSContext(dnsCtx, b.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to retrieve response for ", fqdn) errors.LogErrorInner(ctx, err, "failed to retrieve response for ", domain)
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
rec, err := parseResponse(resp) rec, err := parseResponse(resp)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to handle DOH response for ", fqdn) errors.LogErrorInner(ctx, err, "failed to handle DOH response for ", domain)
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
s.cacheController.updateRecord(r, rec) s.cacheController.updateIP(r, rec)
}(req) }(req)
} }
} }
@@ -234,6 +216,49 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
} }
// QueryIP implements Server. // QueryIP implements Server.
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { // nolint: dupl
return queryIP(ctx, s, domain, option) 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
} }
+4 -4
View File
@@ -17,7 +17,7 @@ func TestDOHNameServer(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query") url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err) common.Must(err)
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil)) s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: true, IPv4Enable: true,
@@ -34,7 +34,7 @@ func TestDOHNameServerWithCache(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query") url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err) common.Must(err)
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil)) s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: true, IPv4Enable: true,
@@ -62,7 +62,7 @@ func TestDOHNameServerWithIPv4Override(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query") url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err) common.Must(err)
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil)) s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: true, IPv4Enable: true,
@@ -85,7 +85,7 @@ func TestDOHNameServerWithIPv6Override(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query") url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err) common.Must(err)
s := NewDoHNameServer(url, nil, false, false, false, 0, net.IP(nil)) s := NewDoHNameServer(url, nil, false, false, net.IP(nil))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
IPv4Enable: false, IPv4Enable: false,
-5
View File
@@ -20,11 +20,6 @@ func (FakeDNSServer) Name() string {
return "FakeDNS" 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) { func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, opt dns.IPOption) ([]net.IP, uint32, error) {
if f.fakeDNSEngine == nil { if f.fakeDNSEngine == nil {
return nil, 0, errors.New("Unable to locate a fake DNS Engine").AtError() return nil, 0, errors.New("Unable to locate a fake DNS Engine").AtError()
-5
View File
@@ -35,11 +35,6 @@ func (s *LocalNameServer) Name() string {
return "localhost" return "localhost"
} }
// IsDisableCache implements Server.
func (s *LocalNameServer) IsDisableCache() bool {
return true
}
// NewLocalNameServer creates localdns server object for directly lookup in system DNS. // NewLocalNameServer creates localdns server object for directly lookup in system DNS.
func NewLocalNameServer() *LocalNameServer { func NewLocalNameServer() *LocalNameServer {
errors.LogInfo(context.Background(), "DNS: created localhost client") errors.LogInfo(context.Background(), "DNS: created localhost client")
+56 -38
View File
@@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/binary" "encoding/binary"
go_errors "errors"
"net/url" "net/url"
"sync" "sync"
"time" "time"
@@ -36,7 +37,9 @@ type QUICNameServer struct {
} }
// NewQUICNameServer creates DNS-over-QUIC client object for local resolving // NewQUICNameServer creates DNS-over-QUIC client object for local resolving
func NewQUICNameServer(url *url.URL, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (*QUICNameServer, error) { func NewQUICNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*QUICNameServer, error) {
errors.LogInfo(context.Background(), "DNS: created Local DNS-over-QUIC client for ", url.String())
var err error var err error
port := net.Port(853) port := net.Port(853)
if url.Port() != "" { if url.Port() != "" {
@@ -48,37 +51,27 @@ func NewQUICNameServer(url *url.URL, disableCache bool, serveStale bool, serveEx
dest := net.UDPDestination(net.ParseAddress(url.Hostname()), port) dest := net.UDPDestination(net.ParseAddress(url.Hostname()), port)
s := &QUICNameServer{ s := &QUICNameServer{
cacheController: NewCacheController(url.String(), disableCache, serveStale, serveExpiredTTL), cacheController: NewCacheController(url.String(), disableCache),
destination: &dest, destination: &dest,
clientIP: clientIP, clientIP: clientIP,
} }
errors.LogInfo(context.Background(), "DNS: created Local DNS-over-QUIC client for ", url.String())
return s, nil return s, nil
} }
// Name implements Server. // Name returns client name
func (s *QUICNameServer) Name() string { func (s *QUICNameServer) Name() string {
return s.cacheController.name return s.cacheController.name
} }
// IsDisableCache implements Server.
func (s *QUICNameServer) IsDisableCache() bool {
return s.cacheController.disableCache
}
func (s *QUICNameServer) newReqID() uint16 { func (s *QUICNameServer) newReqID() uint16 {
return 0 return 0
} }
// getCacheController implements CachedNameServer. func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) {
func (s *QUICNameServer) getCacheController() *CacheController { return s.cacheController } errors.LogInfo(ctx, s.Name(), " querying: ", domain)
// sendQuery implements CachedNameServer. reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
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 var deadline time.Time
if d, ok := ctx.Deadline(); ok { if d, ok := ctx.Deadline(); ok {
@@ -110,9 +103,7 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
b, err := dns.PackMessage(r.msg) b, err := dns.PackMessage(r.msg)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query") errors.LogErrorInner(ctx, err, "failed to pack dns query")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
@@ -120,17 +111,13 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len())) err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len()))
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "binary write failed") errors.LogErrorInner(ctx, err, "binary write failed")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
_, err = dnsReqBuf.Write(b.Bytes()) _, err = dnsReqBuf.Write(b.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "buffer write failed") errors.LogErrorInner(ctx, err, "buffer write failed")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
b.Release() b.Release()
@@ -138,18 +125,14 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
conn, err := s.openStream(dnsCtx) conn, err := s.openStream(dnsCtx)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to open quic connection") errors.LogErrorInner(ctx, err, "failed to open quic connection")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
_, err = conn.Write(dnsReqBuf.Bytes()) _, err = conn.Write(dnsReqBuf.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to send query") errors.LogErrorInner(ctx, err, "failed to send query")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
@@ -160,46 +143,81 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- e
n, err := respBuf.ReadFullFrom(conn, 2) n, err := respBuf.ReadFullFrom(conn, 2)
if err != nil && n == 0 { if err != nil && n == 0 {
errors.LogErrorInner(ctx, err, "failed to read response length") errors.LogErrorInner(ctx, err, "failed to read response length")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
var length uint16 var length int16
err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length) err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to parse response length") errors.LogErrorInner(ctx, err, "failed to parse response length")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
respBuf.Clear() respBuf.Clear()
n, err = respBuf.ReadFullFrom(conn, int32(length)) n, err = respBuf.ReadFullFrom(conn, int32(length))
if err != nil && n == 0 { if err != nil && n == 0 {
errors.LogErrorInner(ctx, err, "failed to read response length") errors.LogErrorInner(ctx, err, "failed to read response length")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
rec, err := parseResponse(respBuf.Bytes()) rec, err := parseResponse(respBuf.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to handle response") errors.LogErrorInner(ctx, err, "failed to handle response")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
s.cacheController.updateRecord(r, rec) s.cacheController.updateIP(r, rec)
}(req) }(req)
} }
} }
// QueryIP implements Server. // QueryIP is called from dns.Server->queryIPTimeout
func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
return queryIP(ctx, s, domain, option) 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 isActive(s *quic.Conn) bool { func isActive(s *quic.Conn) bool {
+3 -3
View File
@@ -16,7 +16,7 @@ import (
func TestQUICNameServer(t *testing.T) { func TestQUICNameServer(t *testing.T) {
url, err := url.Parse("quic://dns.adguard-dns.com") url, err := url.Parse("quic://dns.adguard-dns.com")
common.Must(err) common.Must(err)
s, err := NewQUICNameServer(url, false, false, 0, net.IP(nil)) s, err := NewQUICNameServer(url, false, net.IP(nil))
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
@@ -43,7 +43,7 @@ func TestQUICNameServer(t *testing.T) {
func TestQUICNameServerWithIPv4Override(t *testing.T) { func TestQUICNameServerWithIPv4Override(t *testing.T) {
url, err := url.Parse("quic://dns.adguard-dns.com") url, err := url.Parse("quic://dns.adguard-dns.com")
common.Must(err) common.Must(err)
s, err := NewQUICNameServer(url, false, false, 0, net.IP(nil)) s, err := NewQUICNameServer(url, false, net.IP(nil))
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
@@ -66,7 +66,7 @@ func TestQUICNameServerWithIPv4Override(t *testing.T) {
func TestQUICNameServerWithIPv6Override(t *testing.T) { func TestQUICNameServerWithIPv6Override(t *testing.T) {
url, err := url.Parse("quic://dns.adguard-dns.com") url, err := url.Parse("quic://dns.adguard-dns.com")
common.Must(err) common.Must(err)
s, err := NewQUICNameServer(url, false, false, 0, net.IP(nil)) s, err := NewQUICNameServer(url, false, net.IP(nil))
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns.IPOption{
+57 -43
View File
@@ -4,12 +4,14 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/binary" "encoding/binary"
go_errors "errors"
"net/url" "net/url"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors" "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"
"github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/protocol/dns" "github.com/xtls/xray-core/common/protocol/dns"
@@ -32,10 +34,10 @@ type TCPNameServer struct {
func NewTCPNameServer( func NewTCPNameServer(
url *url.URL, url *url.URL,
dispatcher routing.Dispatcher, dispatcher routing.Dispatcher,
disableCache bool, serveStale bool, serveExpiredTTL uint32, disableCache bool,
clientIP net.IP, clientIP net.IP,
) (*TCPNameServer, error) { ) (*TCPNameServer, error) {
s, err := baseTCPNameServer(url, "TCP", disableCache, serveStale, serveExpiredTTL, clientIP) s, err := baseTCPNameServer(url, "TCP", disableCache, clientIP)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -52,13 +54,12 @@ func NewTCPNameServer(
), nil ), nil
} }
errors.LogInfo(context.Background(), "DNS: created TCP client initialized for ", url.String())
return s, nil return s, nil
} }
// NewTCPLocalNameServer creates DNS over TCP client object for local resolving // NewTCPLocalNameServer creates DNS over TCP client object for local resolving
func NewTCPLocalNameServer(url *url.URL, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (*TCPNameServer, error) { func NewTCPLocalNameServer(url *url.URL, disableCache bool, clientIP net.IP) (*TCPNameServer, error) {
s, err := baseTCPNameServer(url, "TCPL", disableCache, serveStale, serveExpiredTTL, clientIP) s, err := baseTCPNameServer(url, "TCPL", disableCache, clientIP)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -67,11 +68,10 @@ func NewTCPLocalNameServer(url *url.URL, disableCache bool, serveStale bool, ser
return internet.DialSystem(ctx, *s.destination, nil) return internet.DialSystem(ctx, *s.destination, nil)
} }
errors.LogInfo(context.Background(), "DNS: created Local TCP client initialized for ", url.String())
return s, nil return s, nil
} }
func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) (*TCPNameServer, error) { func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, clientIP net.IP) (*TCPNameServer, error) {
port := net.Port(53) port := net.Port(53)
if url.Port() != "" { if url.Port() != "" {
var err error var err error
@@ -82,7 +82,7 @@ func baseTCPNameServer(url *url.URL, prefix string, disableCache bool, serveStal
dest := net.TCPDestination(net.ParseAddress(url.Hostname()), port) dest := net.TCPDestination(net.ParseAddress(url.Hostname()), port)
s := &TCPNameServer{ s := &TCPNameServer{
cacheController: NewCacheController(prefix+"//"+dest.NetAddr(), disableCache, serveStale, serveExpiredTTL), cacheController: NewCacheController(prefix+"//"+dest.NetAddr(), disableCache),
destination: &dest, destination: &dest,
clientIP: clientIP, clientIP: clientIP,
} }
@@ -95,25 +95,14 @@ func (s *TCPNameServer) Name() string {
return s.cacheController.name return s.cacheController.name
} }
// IsDisableCache implements Server.
func (s *TCPNameServer) IsDisableCache() bool {
return s.cacheController.disableCache
}
func (s *TCPNameServer) newReqID() uint16 { func (s *TCPNameServer) newReqID() uint16 {
return uint16(atomic.AddUint32(&s.reqID, 1)) return uint16(atomic.AddUint32(&s.reqID, 1))
} }
// getCacheController implements CachedNameserver. func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, domain string, option dns_feature.IPOption) {
func (s *TCPNameServer) getCacheController() *CacheController { errors.LogDebug(ctx, s.Name(), " querying DNS for: ", domain)
return s.cacheController
}
// sendQuery implements CachedNameserver. reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
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 var deadline time.Time
if d, ok := ctx.Deadline(); ok { if d, ok := ctx.Deadline(); ok {
@@ -142,18 +131,14 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
b, err := dns.PackMessage(r.msg) b, err := dns.PackMessage(r.msg)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query") errors.LogErrorInner(ctx, err, "failed to pack dns query")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
conn, err := s.dial(dnsCtx) conn, err := s.dial(dnsCtx)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to dial namesever") errors.LogErrorInner(ctx, err, "failed to dial namesever")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
defer conn.Close() defer conn.Close()
@@ -161,17 +146,13 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len())) err = binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len()))
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "binary write failed") errors.LogErrorInner(ctx, err, "binary write failed")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
_, err = dnsReqBuf.Write(b.Bytes()) _, err = dnsReqBuf.Write(b.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "buffer write failed") errors.LogErrorInner(ctx, err, "buffer write failed")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
b.Release() b.Release()
@@ -179,9 +160,7 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
_, err = conn.Write(dnsReqBuf.Bytes()) _, err = conn.Write(dnsReqBuf.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to send query") errors.LogErrorInner(ctx, err, "failed to send query")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
dnsReqBuf.Release() dnsReqBuf.Release()
@@ -191,45 +170,80 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
n, err := respBuf.ReadFullFrom(conn, 2) n, err := respBuf.ReadFullFrom(conn, 2)
if err != nil && n == 0 { if err != nil && n == 0 {
errors.LogErrorInner(ctx, err, "failed to read response length") errors.LogErrorInner(ctx, err, "failed to read response length")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
var length uint16 var length int16
err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length) err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length)
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to parse response length") errors.LogErrorInner(ctx, err, "failed to parse response length")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
respBuf.Clear() respBuf.Clear()
n, err = respBuf.ReadFullFrom(conn, int32(length)) n, err = respBuf.ReadFullFrom(conn, int32(length))
if err != nil && n == 0 { if err != nil && n == 0 {
errors.LogErrorInner(ctx, err, "failed to read response length") errors.LogErrorInner(ctx, err, "failed to read response length")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
rec, err := parseResponse(respBuf.Bytes()) rec, err := parseResponse(respBuf.Bytes())
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, "failed to parse DNS over TCP response") errors.LogErrorInner(ctx, err, "failed to parse DNS over TCP response")
if noResponseErrCh != nil {
noResponseErrCh <- err noResponseErrCh <- err
}
return return
} }
s.cacheController.updateRecord(r, rec) s.cacheController.updateIP(r, rec)
}(req) }(req)
} }
} }
// QueryIP implements Server. // QueryIP implements Server.
func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
return queryIP(ctx, s, domain, option) 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
} }
+4 -4
View File
@@ -16,7 +16,7 @@ import (
func TestTCPLocalNameServer(t *testing.T) { func TestTCPLocalNameServer(t *testing.T) {
url, err := url.Parse("tcp+local://8.8.8.8") url, err := url.Parse("tcp+local://8.8.8.8")
common.Must(err) common.Must(err)
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil)) s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
@@ -33,7 +33,7 @@ func TestTCPLocalNameServer(t *testing.T) {
func TestTCPLocalNameServerWithCache(t *testing.T) { func TestTCPLocalNameServerWithCache(t *testing.T) {
url, err := url.Parse("tcp+local://8.8.8.8") url, err := url.Parse("tcp+local://8.8.8.8")
common.Must(err) common.Must(err)
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil)) s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
@@ -61,7 +61,7 @@ func TestTCPLocalNameServerWithCache(t *testing.T) {
func TestTCPLocalNameServerWithIPv4Override(t *testing.T) { func TestTCPLocalNameServerWithIPv4Override(t *testing.T) {
url, err := url.Parse("tcp+local://8.8.8.8") url, err := url.Parse("tcp+local://8.8.8.8")
common.Must(err) common.Must(err)
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil)) s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
@@ -85,7 +85,7 @@ func TestTCPLocalNameServerWithIPv4Override(t *testing.T) {
func TestTCPLocalNameServerWithIPv6Override(t *testing.T) { func TestTCPLocalNameServerWithIPv6Override(t *testing.T) {
url, err := url.Parse("tcp+local://8.8.8.8") url, err := url.Parse("tcp+local://8.8.8.8")
common.Must(err) common.Must(err)
s, err := NewTCPLocalNameServer(url, false, false, 0, net.IP(nil)) s, err := NewTCPLocalNameServer(url, false, net.IP(nil))
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{ ips, _, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
+54 -21
View File
@@ -2,6 +2,7 @@ package dns
import ( import (
"context" "context"
go_errors "errors"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
@@ -9,6 +10,7 @@ import (
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors" "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"
"github.com/xtls/xray-core/common/protocol/dns" "github.com/xtls/xray-core/common/protocol/dns"
udp_proto "github.com/xtls/xray-core/common/protocol/udp" udp_proto "github.com/xtls/xray-core/common/protocol/udp"
@@ -37,14 +39,14 @@ type udpDnsRequest struct {
} }
// NewClassicNameServer creates udp server object for remote resolving. // NewClassicNameServer creates udp server object for remote resolving.
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) *ClassicNameServer { func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, disableCache bool, clientIP net.IP) *ClassicNameServer {
// default to 53 if unspecific // default to 53 if unspecific
if address.Port == 0 { if address.Port == 0 {
address.Port = net.Port(53) address.Port = net.Port(53)
} }
s := &ClassicNameServer{ s := &ClassicNameServer{
cacheController: NewCacheController(strings.ToUpper(address.String()), disableCache, serveStale, serveExpiredTTL), cacheController: NewCacheController(strings.ToUpper(address.String()), disableCache),
address: &address, address: &address,
requests: make(map[uint16]*udpDnsRequest), requests: make(map[uint16]*udpDnsRequest),
clientIP: clientIP, clientIP: clientIP,
@@ -54,7 +56,6 @@ func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher
Execute: s.RequestsCleanup, Execute: s.RequestsCleanup,
} }
s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse) s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse)
errors.LogInfo(context.Background(), "DNS: created UDP client initialized for ", address.NetAddr()) errors.LogInfo(context.Background(), "DNS: created UDP client initialized for ", address.NetAddr())
return s return s
} }
@@ -64,11 +65,6 @@ func (s *ClassicNameServer) Name() string {
return s.cacheController.name return s.cacheController.name
} }
// IsDisableCache implements Server.
func (s *ClassicNameServer) IsDisableCache() bool {
return s.cacheController.disableCache
}
// RequestsCleanup clears expired items from cache // RequestsCleanup clears expired items from cache
func (s *ClassicNameServer) RequestsCleanup() error { func (s *ClassicNameServer) RequestsCleanup() error {
now := time.Now() now := time.Now()
@@ -98,7 +94,7 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
ipRec, err := parseResponse(payload.Bytes()) ipRec, err := parseResponse(payload.Bytes())
payload.Release() payload.Release()
if err != nil { if err != nil {
errors.LogErrorInner(ctx, err, s.Name(), " fail to parse responded DNS udp") errors.LogError(ctx, s.Name(), " fail to parse responded DNS udp")
return return
} }
@@ -111,7 +107,7 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
} }
s.Unlock() s.Unlock()
if !ok { if !ok {
errors.LogErrorInner(ctx, err, s.Name(), " cannot find the pending request") errors.LogError(ctx, s.Name(), " cannot find the pending request")
return return
} }
@@ -138,7 +134,7 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
} }
} }
s.cacheController.updateRecord(&req.dnsRequest, ipRec) s.cacheController.updateIP(&req.dnsRequest, ipRec)
} }
func (s *ClassicNameServer) newReqID() uint16 { func (s *ClassicNameServer) newReqID() uint16 {
@@ -154,16 +150,10 @@ func (s *ClassicNameServer) addPendingRequest(req *udpDnsRequest) {
common.Must(s.requestsCleanup.Start()) common.Must(s.requestsCleanup.Start())
} }
// getCacheController implements CachedNameserver. func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, domain string, option dns_feature.IPOption) {
func (s *ClassicNameServer) getCacheController() *CacheController { errors.LogDebug(ctx, s.Name(), " querying DNS for: ", domain)
return s.cacheController
}
// sendQuery implements CachedNameserver. reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
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 { for _, req := range reqs {
udpReq := &udpDnsRequest{ udpReq := &udpDnsRequest{
@@ -180,5 +170,48 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, fqdn
// QueryIP implements Server. // QueryIP implements Server.
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
return queryIP(ctx, s, domain, option) 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
} }
+3 -2
View File
@@ -2,6 +2,7 @@ package inbound
import ( import (
"context" "context"
gonet "net"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@@ -564,12 +565,12 @@ func (w *dsWorker) Close() error {
} }
func IsLocal(ip net.IP) bool { func IsLocal(ip net.IP) bool {
addrs, err := net.InterfaceAddrs() addrs, err := gonet.InterfaceAddrs()
if err != nil { if err != nil {
return false return false
} }
for _, addr := range addrs { for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok { if ipnet, ok := addr.(*gonet.IPNet); ok {
if ipnet.IP.Equal(ip) { if ipnet.IP.Equal(ip) {
return true return true
} }
+3 -6
View File
@@ -6,6 +6,7 @@ import (
goerrors "errors" goerrors "errors"
"io" "io"
"math/big" "math/big"
gonet "net"
"os" "os"
"github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/dice"
@@ -316,12 +317,8 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
conn, err := internet.Dial(ctx, dest, h.streamSettings) conn, err := internet.Dial(ctx, dest, h.streamSettings)
conn = h.getStatCouterConnection(conn) conn = h.getStatCouterConnection(conn)
outbounds := session.OutboundsFromContext(ctx) outbounds := session.OutboundsFromContext(ctx)
if outbounds != nil {
ob := outbounds[len(outbounds)-1] ob := outbounds[len(outbounds)-1]
ob.Conn = conn ob.Conn = conn
} else {
// for Vision's pre-connect
}
return conn, err return conn, err
} }
@@ -397,7 +394,7 @@ func (h *Handler) ProxySettings() *serial.TypedMessage {
func ParseRandomIP(addr net.Address, prefix string) net.Address { func ParseRandomIP(addr net.Address, prefix string) net.Address {
_, ipnet, _ := net.ParseCIDR(addr.IP().String() + "/" + prefix) _, ipnet, _ := gonet.ParseCIDR(addr.IP().String() + "/" + prefix)
ones, bits := ipnet.Mask.Size() ones, bits := ipnet.Mask.Size()
subnetSize := new(big.Int).Lsh(big.NewInt(1), uint(bits-ones)) subnetSize := new(big.Int).Lsh(big.NewInt(1), uint(bits-ones))
@@ -411,5 +408,5 @@ func ParseRandomIP(addr net.Address, prefix string) net.Address {
padded := make([]byte, len(ipnet.IP)) padded := make([]byte, len(ipnet.IP))
copy(padded[len(padded)-len(rndBytes):], rndBytes) copy(padded[len(padded)-len(rndBytes):], rndBytes)
return net.ParseAddress(net.IP(padded).String()) return net.ParseAddress(gonet.IP(padded).String())
} }
+2 -3
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"time" "time"
"github.com/xtls/xray-core/app/dispatcher"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/mux" "github.com/xtls/xray-core/common/mux"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
@@ -230,9 +231,7 @@ func (w *BridgeWorker) DispatchLink(ctx context.Context, dest net.Destination, l
return w.Dispatcher.DispatchLink(ctx, dest, link) return w.Dispatcher.DispatchLink(ctx, dest, link)
} }
if d, ok := w.Dispatcher.(routing.WrapLinkDispatcher); ok { link = w.Dispatcher.(*dispatcher.DefaultDispatcher).WrapLink(ctx, link)
link = d.WrapLink(ctx, link)
}
w.handleInternalConn(link) w.handleInternalConn(link)
return nil return nil
+51 -28
View File
@@ -47,6 +47,20 @@ var matcherTypeMap = map[Domain_Type]strmatcher.Type{
Domain_Full: strmatcher.Full, 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 { type DomainMatcher struct {
matchers strmatcher.IndexMatcher matchers strmatcher.IndexMatcher
} }
@@ -82,53 +96,61 @@ func (m *DomainMatcher) Apply(ctx routing.Context) bool {
return m.ApplyDomain(domain) return m.ApplyDomain(domain)
} }
type MatcherAsType byte type MultiGeoIPMatcher struct {
matchers []*GeoIPMatcher
const ( asType string // local, source, target
MatcherAsType_Local MatcherAsType = iota
MatcherAsType_Source
MatcherAsType_Target
MatcherAsType_VlessRoute // for port
)
type IPMatcher struct {
matcher GeoIPMatcher
asType MatcherAsType
} }
func NewIPMatcher(geoips []*GeoIP, asType MatcherAsType) (*IPMatcher, error) { func NewMultiGeoIPMatcher(geoips []*GeoIP, asType string) (*MultiGeoIPMatcher, error) {
matcher, err := BuildOptimizedGeoIPMatcher(geoips...) var matchers []*GeoIPMatcher
for _, geoip := range geoips {
matcher, err := GlobalGeoIPContainer.Add(geoip)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &IPMatcher{matcher: matcher, asType: asType}, nil matchers = append(matchers, matcher)
}
matcher := &MultiGeoIPMatcher{
matchers: matchers,
asType: asType,
}
return matcher, nil
} }
// Apply implements Condition. // Apply implements Condition.
func (m *IPMatcher) Apply(ctx routing.Context) bool { func (m *MultiGeoIPMatcher) Apply(ctx routing.Context) bool {
var ips []net.IP var ips []net.IP
switch m.asType { switch m.asType {
case MatcherAsType_Local: case "local":
ips = ctx.GetLocalIPs() ips = ctx.GetLocalIPs()
case MatcherAsType_Source: case "source":
ips = ctx.GetSourceIPs() ips = ctx.GetSourceIPs()
case MatcherAsType_Target: case "target":
ips = ctx.GetTargetIPs() ips = ctx.GetTargetIPs()
default: default:
panic("unk asType") panic("unreachable, asType should be local or source or target")
} }
return m.matcher.AnyMatch(ips) for _, ip := range ips {
for _, matcher := range m.matchers {
if matcher.Match(ip) {
return true
}
}
}
return false
} }
type PortMatcher struct { type PortMatcher struct {
port net.MemoryPortList port net.MemoryPortList
asType MatcherAsType asType string // local, source, target
} }
// NewPortMatcher create a new port matcher that can match source or local or destination port // NewPortMatcher create a new port matcher that can match source or local or destination port
func NewPortMatcher(list *net.PortList, asType MatcherAsType) *PortMatcher { func NewPortMatcher(list *net.PortList, asType string) *PortMatcher {
return &PortMatcher{ return &PortMatcher{
port: net.PortListFromProto(list), port: net.PortListFromProto(list),
asType: asType, asType: asType,
@@ -138,17 +160,18 @@ func NewPortMatcher(list *net.PortList, asType MatcherAsType) *PortMatcher {
// Apply implements Condition. // Apply implements Condition.
func (v *PortMatcher) Apply(ctx routing.Context) bool { func (v *PortMatcher) Apply(ctx routing.Context) bool {
switch v.asType { switch v.asType {
case MatcherAsType_Local: case "local":
return v.port.Contains(ctx.GetLocalPort()) return v.port.Contains(ctx.GetLocalPort())
case MatcherAsType_Source: case "source":
return v.port.Contains(ctx.GetSourcePort()) return v.port.Contains(ctx.GetSourcePort())
case MatcherAsType_Target: case "target":
return v.port.Contains(ctx.GetTargetPort()) return v.port.Contains(ctx.GetTargetPort())
case MatcherAsType_VlessRoute: case "vlessRoute":
return v.port.Contains(ctx.GetVlessRoute()) return v.port.Contains(ctx.GetVlessRoute())
default: default:
panic("unk asType") panic("unreachable, asType should be local or source or target")
} }
} }
type NetworkMatcher struct { type NetworkMatcher struct {
File diff suppressed because it is too large Load Diff
+42 -29
View File
@@ -35,6 +35,33 @@ func getAssetPath(file string) (string, error) {
return path, nil 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) { func TestGeoIPMatcher(t *testing.T) {
cidrList := []*router.CIDR{ cidrList := []*router.CIDR{
{Ip: []byte{0, 0, 0, 0}, Prefix: 8}, {Ip: []byte{0, 0, 0, 0}, Prefix: 8},
@@ -53,10 +80,8 @@ func TestGeoIPMatcher(t *testing.T) {
{Ip: []byte{91, 108, 4, 0}, Prefix: 16}, {Ip: []byte{91, 108, 4, 0}, Prefix: 16},
} }
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{ matcher := &router.GeoIPMatcher{}
Cidr: cidrList, common.Must(matcher.Init(cidrList))
})
common.Must(err)
testCases := []struct { testCases := []struct {
Input string Input string
@@ -115,10 +140,8 @@ func TestGeoIPMatcherRegression(t *testing.T) {
{Ip: []byte{98, 108, 20, 0}, Prefix: 23}, {Ip: []byte{98, 108, 20, 0}, Prefix: 23},
} }
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{ matcher := &router.GeoIPMatcher{}
Cidr: cidrList, common.Must(matcher.Init(cidrList))
})
common.Must(err)
testCases := []struct { testCases := []struct {
Input string Input string
@@ -148,11 +171,9 @@ func TestGeoIPReverseMatcher(t *testing.T) {
{Ip: []byte{8, 8, 8, 8}, Prefix: 32}, {Ip: []byte{8, 8, 8, 8}, Prefix: 32},
{Ip: []byte{91, 108, 4, 0}, Prefix: 16}, {Ip: []byte{91, 108, 4, 0}, Prefix: 16},
} }
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{ matcher := &router.GeoIPMatcher{}
Cidr: cidrList, matcher.SetReverseMatch(true) // Reverse match
}) common.Must(matcher.Init(cidrList))
common.Must(err)
matcher.SetReverse(true) // Reverse match
testCases := []struct { testCases := []struct {
Input string Input string
@@ -185,10 +206,8 @@ func TestGeoIPMatcher4CN(t *testing.T) {
ips, err := loadGeoIP("CN") ips, err := loadGeoIP("CN")
common.Must(err) common.Must(err)
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{ matcher := &router.GeoIPMatcher{}
Cidr: ips, common.Must(matcher.Init(ips))
})
common.Must(err)
if matcher.Match([]byte{8, 8, 8, 8}) { if matcher.Match([]byte{8, 8, 8, 8}) {
t.Error("expect CN geoip doesn't contain 8.8.8.8, but actually does") t.Error("expect CN geoip doesn't contain 8.8.8.8, but actually does")
@@ -199,10 +218,8 @@ func TestGeoIPMatcher6US(t *testing.T) {
ips, err := loadGeoIP("US") ips, err := loadGeoIP("US")
common.Must(err) common.Must(err)
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{ matcher := &router.GeoIPMatcher{}
Cidr: ips, common.Must(matcher.Init(ips))
})
common.Must(err)
if !matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP()) { if !matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP()) {
t.Error("expect US geoip contain 2001:4860:4860::8888, but actually not") t.Error("expect US geoip contain 2001:4860:4860::8888, but actually not")
@@ -237,10 +254,8 @@ func BenchmarkGeoIPMatcher4CN(b *testing.B) {
ips, err := loadGeoIP("CN") ips, err := loadGeoIP("CN")
common.Must(err) common.Must(err)
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{ matcher := &router.GeoIPMatcher{}
Cidr: ips, common.Must(matcher.Init(ips))
})
common.Must(err)
b.ResetTimer() b.ResetTimer()
@@ -253,10 +268,8 @@ func BenchmarkGeoIPMatcher6US(b *testing.B) {
ips, err := loadGeoIP("US") ips, err := loadGeoIP("US")
common.Must(err) common.Must(err)
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{ matcher := &router.GeoIPMatcher{}
Cidr: ips, common.Must(matcher.Init(ips))
})
common.Must(err)
b.ResetTimer() b.ResetTimer()
+1 -1
View File
@@ -447,7 +447,7 @@ func BenchmarkMultiGeoIPMatcher(b *testing.B) {
}) })
} }
matcher, err := NewIPMatcher(geoips, MatcherAsType_Target) matcher, err := NewMultiGeoIPMatcher(geoips, "target")
common.Must(err) common.Must(err)
ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)}) ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)})
+54 -54
View File
@@ -32,38 +32,72 @@ func (r *Rule) Apply(ctx routing.Context) bool {
func (rr *RoutingRule) BuildCondition() (Condition, error) { func (rr *RoutingRule) BuildCondition() (Condition, error) {
conds := NewConditionChan() 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 { if len(rr.InboundTag) > 0 {
conds.Add(NewInboundTagMatcher(rr.InboundTag)) 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 { if len(rr.Networks) > 0 {
conds.Add(NewNetworkMatcher(rr.Networks)) 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 { if len(rr.Protocol) > 0 {
conds.Add(NewProtocolMatcher(rr.Protocol)) 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 { if len(rr.Attributes) > 0 {
configuredKeys := make(map[string]*regexp.Regexp) configuredKeys := make(map[string]*regexp.Regexp)
for key, value := range rr.Attributes { for key, value := range rr.Attributes {
@@ -72,40 +106,6 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
conds.Add(&AttributeMatcher{configuredKeys}) 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 { if conds.Len() == 0 {
return nil, errors.New("this rule has no effective fields").AtWarning() return nil, errors.New("this rule has no effective fields").AtWarning()
} }
+16 -11
View File
@@ -84,6 +84,8 @@ type Config_DomainStrategy int32
const ( const (
// Use domain as is. // Use domain as is.
Config_AsIs Config_DomainStrategy = 0 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. // Resolve to IP if the domain doesn't match any rules.
Config_IpIfNonMatch Config_DomainStrategy = 2 Config_IpIfNonMatch Config_DomainStrategy = 2
// Resolve to IP if any rule requires IP matching. // Resolve to IP if any rule requires IP matching.
@@ -94,11 +96,13 @@ const (
var ( var (
Config_DomainStrategy_name = map[int32]string{ Config_DomainStrategy_name = map[int32]string{
0: "AsIs", 0: "AsIs",
1: "UseIp",
2: "IpIfNonMatch", 2: "IpIfNonMatch",
3: "IpOnDemand", 3: "IpOnDemand",
} }
Config_DomainStrategy_value = map[string]int32{ Config_DomainStrategy_value = map[string]int32{
"AsIs": 0, "AsIs": 0,
"UseIp": 1,
"IpIfNonMatch": 2, "IpIfNonMatch": 2,
"IpOnDemand": 3, "IpOnDemand": 3,
} }
@@ -1167,7 +1171,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, 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, 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, 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, 0x90, 0x02, 0x0a, 0x02, 0x52, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x9b, 0x02, 0x0a,
0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 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, 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, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
@@ -1181,16 +1185,17 @@ var file_app_router_config_proto_rawDesc = []byte{
0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 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, 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, 0x6c, 0x65, 0x52, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c,
0x65, 0x22, 0x3c, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 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, 0x10, 0x0a, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x73, 0x49, 0x73, 0x10, 0x00, 0x12, 0x09, 0x0a,
0x0c, 0x49, 0x70, 0x49, 0x66, 0x4e, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x10, 0x02, 0x12, 0x05, 0x55, 0x73, 0x65, 0x49, 0x70, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x70, 0x49, 0x66,
0x0e, 0x0a, 0x0a, 0x49, 0x70, 0x4f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x10, 0x03, 0x42, 0x4e, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x70,
0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x4f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x10, 0x03, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f,
0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x72, 0x50, 0x01, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0xaa, 0x02, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61,
0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0xaa, 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
} }
var ( var (
+2 -2
View File
@@ -147,8 +147,8 @@ message Config {
// Use domain as is. // Use domain as is.
AsIs = 0; AsIs = 0;
// [Deprecated] Always resolve IP for domains. // Always resolve IP for domains.
// UseIp = 1; UseIp = 1;
// Resolve to IP if the domain doesn't match any rules. // Resolve to IP if the domain doesn't match any rules.
IpIfNonMatch = 2; IpIfNonMatch = 2;
+1 -117
View File
@@ -2,12 +2,10 @@ package buf
import ( import (
"io" "io"
"sync"
"time" "time"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/signal" "github.com/xtls/xray-core/common/signal"
"github.com/xtls/xray-core/features/policy"
"github.com/xtls/xray-core/features/stats" "github.com/xtls/xray-core/features/stats"
) )
@@ -115,12 +113,7 @@ func Copy(reader Reader, writer Writer, options ...CopyOption) error {
for _, option := range options { for _, option := range options {
option(&handler) option(&handler)
} }
var err error err := copyInternal(reader, writer, &handler)
if shouldUseCopyV(reader, writer) {
err = copyVInternal(reader, writer, &handler)
} else {
err = copyInternal(reader, writer, &handler)
}
if err != nil && errors.Cause(err) != io.EOF { if err != nil && errors.Cause(err) != io.EOF {
return err return err
} }
@@ -140,112 +133,3 @@ func CopyOnceTimeout(reader Reader, writer Writer, timeout time.Duration) error
} }
return writer.WriteMultiBuffer(mb) return writer.WriteMultiBuffer(mb)
} }
func shouldUseCopyV(reader Reader, writer Writer) bool {
// if writer is not support writeV, directly return false
if _, ok := writer.(*BufferToBytesWriter); !ok {
return false
}
// try to figure out if the underlying reader is SingleReader
var doCopyV bool
if tr, ok := reader.(*TimeoutWrapperReader); ok {
if _, ok := tr.Reader.(*SingleReader); ok {
doCopyV = true
}
}
if _, ok := reader.(*SingleReader); ok {
doCopyV = true
}
return doCopyV
}
func copyVInternal(r Reader, w Writer, handler *copyHandler) error {
// channel buffer size is maxBuffer/maxPerPacketLen (ignore the case of many small packets)
// default buffer size:
// 0 in ARM MIPS MIPSLE
// 4kb in ARM64 MIPS64 MIPS64LE
// 512kb in others
channelBuffer := (policy.SessionDefault().Buffer.PerConnection) / Size
if channelBuffer <= 0 {
channelBuffer = 4
}
cache := make(chan *Buffer, channelBuffer)
stopRead := make(chan struct{})
var rErr error
var wErr error
wg := sync.WaitGroup{}
wg.Add(2)
// downlink
go func() {
defer wg.Done()
defer close(cache)
for {
mb, err := r.ReadMultiBuffer()
for _, b := range mb {
if err == nil {
select {
case cache <- b:
// must be write error
case <-stopRead:
b.Release()
return
}
} else {
rErr = err
select {
case cache <- b:
case <-stopRead:
b.Release()
}
return
}
}
}
}()
// uplink
go func() {
defer wg.Done()
for {
b, ok := <-cache
if !ok {
return
}
var buffers = []*Buffer{b}
for stop := false; !stop; {
select {
case b, ok := <-cache:
if !ok {
stop = true
continue
}
buffers = append(buffers, b)
default:
stop = true
}
}
mb := MultiBuffer(buffers)
err := w.WriteMultiBuffer(mb)
for _, handler := range handler.onData {
handler(mb)
}
ReleaseMulti(mb)
if err != nil {
wErr = err
close(stopRead)
return
}
}
}()
wg.Wait()
// drain cache
for b := range cache {
b.Release()
}
if wErr != nil {
return writeError{wErr}
}
if rErr != nil {
return readError{rErr}
}
return nil
}
-5
View File
@@ -159,11 +159,6 @@ func (r *SingleReader) ReadMultiBuffer() (MultiBuffer, error) {
return MultiBuffer{b}, err return MultiBuffer{b}, err
} }
func (r *SingleReader) readBuffer() (*Buffer, error) {
b, err := ReadBuffer(r.Reader)
return b, err
}
// PacketReader is a Reader that read one Buffer every time. // PacketReader is a Reader that read one Buffer every time.
type PacketReader struct { type PacketReader struct {
io.Reader io.Reader
-1
View File
@@ -45,7 +45,6 @@ type dnsStatus string
var ( var (
DNSQueried = dnsStatus("got answer:") DNSQueried = dnsStatus("got answer:")
DNSCacheHit = dnsStatus("cache HIT:") DNSCacheHit = dnsStatus("cache HIT:")
DNSCacheOptimiste = dnsStatus("cache OPTIMISTE:")
) )
func joinNetIP(ips []net.IP) string { func joinNetIP(ips []net.IP) string {
+2 -3
View File
@@ -5,6 +5,7 @@ import (
"io" "io"
"time" "time"
"github.com/xtls/xray-core/app/dispatcher"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
@@ -63,9 +64,7 @@ func (s *Server) DispatchLink(ctx context.Context, dest net.Destination, link *t
if dest.Address != muxCoolAddress { if dest.Address != muxCoolAddress {
return s.dispatcher.DispatchLink(ctx, dest, link) return s.dispatcher.DispatchLink(ctx, dest, link)
} }
if d, ok := s.dispatcher.(routing.WrapLinkDispatcher); ok { link = s.dispatcher.(*dispatcher.DefaultDispatcher).WrapLink(ctx, link)
link = d.WrapLink(ctx, link)
}
worker, err := NewServerWorker(ctx, s.dispatcher, link) worker, err := NewServerWorker(ctx, s.dispatcher, link)
if err != nil { if err != nil {
return err return err
-18
View File
@@ -12,8 +12,6 @@ var (
type ListenConfig = net.ListenConfig type ListenConfig = net.ListenConfig
type KeepAliveConfig = net.KeepAliveConfig
var ( var (
Listen = net.Listen Listen = net.Listen
ListenTCP = net.ListenTCP ListenTCP = net.ListenTCP
@@ -28,12 +26,6 @@ var FileConn = net.FileConn
// ParseIP is an alias of net.ParseIP // ParseIP is an alias of net.ParseIP
var ParseIP = net.ParseIP var ParseIP = net.ParseIP
var ParseCIDR = net.ParseCIDR
var ResolveIPAddr = net.ResolveIPAddr
var InterfaceByName = net.InterfaceByName
var SplitHostPort = net.SplitHostPort var SplitHostPort = net.SplitHostPort
var CIDRMask = net.CIDRMask var CIDRMask = net.CIDRMask
@@ -59,8 +51,6 @@ type (
UnixConn = net.UnixConn UnixConn = net.UnixConn
) )
type IPAddr = net.IPAddr
// IP is an alias for net.IP. // IP is an alias for net.IP.
type ( type (
IP = net.IP IP = net.IP
@@ -92,11 +82,3 @@ var (
) )
type Resolver = net.Resolver type Resolver = net.Resolver
var DefaultResolver = net.DefaultResolver
var JoinHostPort = net.JoinHostPort
var InterfaceAddrs = net.InterfaceAddrs
var Interfaces = net.Interfaces
+10
View File
@@ -0,0 +1,10 @@
//go:build !windows
// +build !windows
package ctlcmd
import "syscall"
func getSysProcAttr() *syscall.SysProcAttr {
return nil
}
+12
View File
@@ -0,0 +1,12 @@
//go:build windows
// +build windows
package ctlcmd
import "syscall"
func getSysProcAttr() *syscall.SysProcAttr {
return &syscall.SysProcAttr{
HideWindow: true,
}
}
+50
View File
@@ -0,0 +1,50 @@
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
}
+9
View File
@@ -8,10 +8,19 @@ import (
"path/filepath" "path/filepath"
) )
func ExpandEnv(s string) string {
return os.ExpandEnv(s)
}
func LineSeparator() string { func LineSeparator() string {
return "\n" 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 // GetAssetLocation searches for `file` in the env dir, the executable dir, and certain locations
func GetAssetLocation(file string) string { func GetAssetLocation(file string) string {
assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir) assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir)
+13
View File
@@ -8,8 +8,10 @@ import (
) )
const ( const (
PluginLocation = "xray.location.plugin"
ConfigLocation = "xray.location.config" ConfigLocation = "xray.location.config"
ConfdirLocation = "xray.location.confdir" ConfdirLocation = "xray.location.confdir"
ToolLocation = "xray.location.tool"
AssetLocation = "xray.location.asset" AssetLocation = "xray.location.asset"
CertLocation = "xray.location.cert" CertLocation = "xray.location.cert"
@@ -77,6 +79,17 @@ func getExecutableDir() string {
return filepath.Dir(exec) 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 { func GetConfigurationPath() string {
configPath := NewEnvFlag(ConfigLocation).GetValue(getExecutableDir) configPath := NewEnvFlag(ConfigLocation).GetValue(getExecutableDir)
return filepath.Join(configPath, "config.json") return filepath.Join(configPath, "config.json")
+10
View File
@@ -5,10 +5,20 @@ package platform
import "path/filepath" import "path/filepath"
func ExpandEnv(s string) string {
// TODO
return s
}
func LineSeparator() string { func LineSeparator() string {
return "\r\n" 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 // GetAssetLocation searches for `file` in the env dir and the executable dir
func GetAssetLocation(file string) string { func GetAssetLocation(file string) string {
assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir) assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir)
+3 -3
View File
@@ -64,7 +64,7 @@ func GetMergedConfig(args cmdarg.Arg) (string, error) {
var files []*ConfigSource var files []*ConfigSource
supported := []string{"json", "yaml", "toml"} supported := []string{"json", "yaml", "toml"}
for _, file := range args { for _, file := range args {
format := GetFormat(file) format := getFormat(file)
if slices.Contains(supported, format) { if slices.Contains(supported, format) {
files = append(files, &ConfigSource{ files = append(files, &ConfigSource{
Name: file, Name: file,
@@ -98,7 +98,7 @@ func getExtension(filename string) string {
return filename[idx+1:] return filename[idx+1:]
} }
func GetFormat(filename string) string { func getFormat(filename string) string {
return GetFormatByExtension(getExtension(filename)) return GetFormatByExtension(getExtension(filename))
} }
@@ -112,7 +112,7 @@ func LoadConfig(formatName string, input interface{}) (*Config, error) {
if formatName == "auto" { if formatName == "auto" {
if file != "stdin:" { if file != "stdin:" {
f = GetFormat(file) f = getFormat(file)
} else { } else {
f = "json" f = "json"
} }
+2 -2
View File
@@ -18,8 +18,8 @@ import (
var ( var (
Version_x byte = 25 Version_x byte = 25
Version_y byte = 12 Version_y byte = 10
Version_z byte = 8 Version_z byte = 15
) )
var ( var (
+2 -1
View File
@@ -68,5 +68,6 @@ func RCodeFromError(err error) uint16 {
if r, ok := cause.(RCodeError); ok { if r, ok := cause.(RCodeError); ok {
return uint16(r) return uint16(r)
} }
return 0 // unknown error
return 2
} }
-6
View File
@@ -26,9 +26,3 @@ type Dispatcher interface {
func DispatcherType() interface{} { func DispatcherType() interface{} {
return (*Dispatcher)(nil) return (*Dispatcher)(nil)
} }
// Just for type assertion
type WrapLinkDispatcher interface {
Dispatcher
WrapLink(ctx context.Context, link *transport.Link) *transport.Link
}
+13 -13
View File
@@ -11,7 +11,7 @@ require (
github.com/miekg/dns v1.1.68 github.com/miekg/dns v1.1.68
github.com/pelletier/go-toml v1.9.5 github.com/pelletier/go-toml v1.9.5
github.com/pires/go-proxyproto v0.8.1 github.com/pires/go-proxyproto v0.8.1
github.com/quic-go/quic-go v0.57.1 github.com/quic-go/quic-go v0.55.0
github.com/refraction-networking/utls v1.8.1 github.com/refraction-networking/utls v1.8.1
github.com/sagernet/sing v0.5.1 github.com/sagernet/sing v0.5.1
github.com/sagernet/sing-shadowsocks v0.2.7 github.com/sagernet/sing-shadowsocks v0.2.7
@@ -21,12 +21,12 @@ require (
github.com/vishvananda/netlink v1.3.1 github.com/vishvananda/netlink v1.3.1
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535 github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/crypto v0.46.0 golang.org/x/crypto v0.43.0
golang.org/x/net v0.48.0 golang.org/x/net v0.46.0
golang.org/x/sync v0.19.0 golang.org/x/sync v0.17.0
golang.org/x/sys v0.39.0 golang.org/x/sys v0.37.0
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
google.golang.org/grpc v1.77.0 google.golang.org/grpc v1.76.0
google.golang.org/protobuf v1.36.10 google.golang.org/protobuf v1.36.10
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5 gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5
h12.io/socks v1.0.3 h12.io/socks v1.0.3
@@ -41,17 +41,17 @@ require (
github.com/juju/ratelimit v1.0.2 // indirect github.com/juju/ratelimit v1.0.2 // indirect
github.com/klauspost/compress v1.17.4 // indirect github.com/klauspost/compress v1.17.4 // indirect
github.com/klauspost/cpuid/v2 v2.0.12 // indirect github.com/klauspost/cpuid/v2 v2.0.12 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/pretty v0.3.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/qpack v0.5.1 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/vishvananda/netns v0.0.5 // indirect github.com/vishvananda/netns v0.0.5 // indirect
golang.org/x/mod v0.30.0 // indirect golang.org/x/mod v0.28.0 // indirect
golang.org/x/text v0.32.0 // indirect golang.org/x/text v0.30.0 // indirect
golang.org/x/time v0.12.0 // indirect golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.39.0 // indirect golang.org/x/tools v0.37.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )
+39 -38
View File
@@ -46,18 +46,19 @@ 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/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0= github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10= github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U=
github.com/refraction-networking/utls v1.8.1 h1:yNY1kapmQU8JeM1sSw2H2asfTIwWxIkrMJI0pRUOCAo= github.com/refraction-networking/utls v1.8.1 h1:yNY1kapmQU8JeM1sSw2H2asfTIwWxIkrMJI0pRUOCAo=
github.com/refraction-networking/utls v1.8.1/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM= 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 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sagernet/sing v0.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y= github.com/sagernet/sing v0.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y=
github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8= github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
@@ -77,38 +78,38 @@ github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZla
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535 h1:nwobseOLLRtdbP6z7Z2aVI97u8ZptTgD1ofovhAKmeU= github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535 h1:nwobseOLLRtdbP6z7Z2aVI97u8ZptTgD1ofovhAKmeU=
github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535/go.mod h1:vbHCV/3VWUvy1oKvTxxWJRPEWSeR1sYgQHIh6u/JiZQ= github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535/go.mod h1:vbHCV/3VWUvy1oKvTxxWJRPEWSeR1sYgQHIh6u/JiZQ=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= 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 h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.17.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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -116,21 +117,21 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -140,10 +141,10 @@ 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= 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 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk= google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b h1:zPKJod4w6F1+nRGDI9ubnXYhU9NSWoFAijkHkUXeTK8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 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 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+2 -83
View File
@@ -26,9 +26,7 @@ type NameServerConfig struct {
QueryStrategy string `json:"queryStrategy"` QueryStrategy string `json:"queryStrategy"`
Tag string `json:"tag"` Tag string `json:"tag"`
TimeoutMs uint64 `json:"timeoutMs"` TimeoutMs uint64 `json:"timeoutMs"`
DisableCache *bool `json:"disableCache"` DisableCache bool `json:"disableCache"`
ServeStale *bool `json:"serveStale"`
ServeExpiredTTL *uint32 `json:"serveExpiredTTL"`
FinalQuery bool `json:"finalQuery"` FinalQuery bool `json:"finalQuery"`
UnexpectedIPs StringList `json:"unexpectedIPs"` UnexpectedIPs StringList `json:"unexpectedIPs"`
} }
@@ -52,9 +50,7 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
QueryStrategy string `json:"queryStrategy"` QueryStrategy string `json:"queryStrategy"`
Tag string `json:"tag"` Tag string `json:"tag"`
TimeoutMs uint64 `json:"timeoutMs"` TimeoutMs uint64 `json:"timeoutMs"`
DisableCache *bool `json:"disableCache"` DisableCache bool `json:"disableCache"`
ServeStale *bool `json:"serveStale"`
ServeExpiredTTL *uint32 `json:"serveExpiredTTL"`
FinalQuery bool `json:"finalQuery"` FinalQuery bool `json:"finalQuery"`
UnexpectedIPs StringList `json:"unexpectedIPs"` UnexpectedIPs StringList `json:"unexpectedIPs"`
} }
@@ -70,8 +66,6 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
c.Tag = advanced.Tag c.Tag = advanced.Tag
c.TimeoutMs = advanced.TimeoutMs c.TimeoutMs = advanced.TimeoutMs
c.DisableCache = advanced.DisableCache c.DisableCache = advanced.DisableCache
c.ServeStale = advanced.ServeStale
c.ServeExpiredTTL = advanced.ServeExpiredTTL
c.FinalQuery = advanced.FinalQuery c.FinalQuery = advanced.FinalQuery
c.UnexpectedIPs = advanced.UnexpectedIPs c.UnexpectedIPs = advanced.UnexpectedIPs
return nil return nil
@@ -179,8 +173,6 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
Tag: c.Tag, Tag: c.Tag,
TimeoutMs: c.TimeoutMs, TimeoutMs: c.TimeoutMs,
DisableCache: c.DisableCache, DisableCache: c.DisableCache,
ServeStale: c.ServeStale,
ServeExpiredTTL: c.ServeExpiredTTL,
FinalQuery: c.FinalQuery, FinalQuery: c.FinalQuery,
UnexpectedGeoip: unexpectedGeoipList, UnexpectedGeoip: unexpectedGeoipList,
ActUnprior: actUnprior, ActUnprior: actUnprior,
@@ -202,11 +194,8 @@ type DNSConfig struct {
Tag string `json:"tag"` Tag string `json:"tag"`
QueryStrategy string `json:"queryStrategy"` QueryStrategy string `json:"queryStrategy"`
DisableCache bool `json:"disableCache"` DisableCache bool `json:"disableCache"`
ServeStale bool `json:"serveStale"`
ServeExpiredTTL uint32 `json:"serveExpiredTTL"`
DisableFallback bool `json:"disableFallback"` DisableFallback bool `json:"disableFallback"`
DisableFallbackIfMatch bool `json:"disableFallbackIfMatch"` DisableFallbackIfMatch bool `json:"disableFallbackIfMatch"`
EnableParallelQuery bool `json:"enableParallelQuery"`
UseSystemHosts bool `json:"useSystemHosts"` UseSystemHosts bool `json:"useSystemHosts"`
} }
@@ -402,11 +391,8 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
config := &dns.Config{ config := &dns.Config{
Tag: c.Tag, Tag: c.Tag,
DisableCache: c.DisableCache, DisableCache: c.DisableCache,
ServeStale: c.ServeStale,
ServeExpiredTTL: c.ServeExpiredTTL,
DisableFallback: c.DisableFallback, DisableFallback: c.DisableFallback,
DisableFallbackIfMatch: c.DisableFallbackIfMatch, DisableFallbackIfMatch: c.DisableFallbackIfMatch,
EnableParallelQuery: c.EnableParallelQuery,
QueryStrategy: resolveQueryStrategy(c.QueryStrategy), QueryStrategy: resolveQueryStrategy(c.QueryStrategy),
} }
@@ -417,78 +403,11 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
config.ClientIp = []byte(c.ClientIP.IP()) 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 { for _, server := range c.Servers {
ns, err := server.Build() ns, err := server.Build()
if err != nil { if err != nil {
return nil, errors.New("failed to build nameserver").Base(err) return nil, errors.New("failed to build nameserver").Base(err)
} }
ns.PolicyID = buildPolicyID(server)
config.NameServer = append(config.NameServer, ns) config.NameServer = append(config.NameServer, ns)
} }
+2 -12
View File
@@ -20,8 +20,7 @@ func TestDNSConfigParsing(t *testing.T) {
return config.Build() return config.Build()
} }
} }
expectedServeStale := true
expectedServeExpiredTTL := uint32(172800)
runMultiTestCase(t, []TestCase{ runMultiTestCase(t, []TestCase{
{ {
Input: `{ Input: `{
@@ -29,9 +28,7 @@ func TestDNSConfigParsing(t *testing.T) {
"address": "8.8.8.8", "address": "8.8.8.8",
"port": 5353, "port": 5353,
"skipFallback": true, "skipFallback": true,
"domains": ["domain:example.com"], "domains": ["domain:example.com"]
"serveStale": true,
"serveExpiredTTL": 172800
}], }],
"hosts": { "hosts": {
"domain:example.com": "google.com", "domain:example.com": "google.com",
@@ -43,8 +40,6 @@ func TestDNSConfigParsing(t *testing.T) {
"clientIp": "10.0.0.1", "clientIp": "10.0.0.1",
"queryStrategy": "UseIPv4", "queryStrategy": "UseIPv4",
"disableCache": true, "disableCache": true,
"serveStale": false,
"serveExpiredTTL": 86400,
"disableFallback": true "disableFallback": true
}`, }`,
Parser: parserCreator(), Parser: parserCreator(),
@@ -73,9 +68,6 @@ func TestDNSConfigParsing(t *testing.T) {
Size: 1, 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{ StaticHosts: []*dns.Config_HostMapping{
@@ -108,8 +100,6 @@ func TestDNSConfigParsing(t *testing.T) {
ClientIp: []byte{10, 0, 0, 1}, ClientIp: []byte{10, 0, 0, 1},
QueryStrategy: dns.QueryStrategy_USE_IP4, QueryStrategy: dns.QueryStrategy_USE_IP4,
DisableCache: true, DisableCache: true,
ServeStale: false,
ServeExpiredTTL: 86400,
DisableFallback: true, DisableFallback: true,
}, },
}, },
+2
View File
@@ -83,6 +83,8 @@ func (c *RouterConfig) getDomainStrategy() router.Config_DomainStrategy {
} }
switch strings.ToLower(ds) { switch strings.ToLower(ds) {
case "alwaysip":
return router.Config_UseIp
case "ipifnonmatch": case "ipifnonmatch":
return router.Config_IpIfNonMatch return router.Config_IpIfNonMatch
case "ipondemand": case "ipondemand":
-8
View File
@@ -628,9 +628,6 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
} }
config.ShortIds = make([][]byte, len(c.ShortIds)) config.ShortIds = make([][]byte, len(c.ShortIds))
for i, s := range 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) config.ShortIds[i] = make([]byte, 8)
if _, err = hex.Decode(config.ShortIds[i], []byte(s)); err != nil { if _, err = hex.Decode(config.ShortIds[i], []byte(s)); err != nil {
return nil, errors.New(`invalid "shortIds[`, i, `]": `, s) return nil, errors.New(`invalid "shortIds[`, i, `]": `, s)
@@ -682,9 +679,6 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
if len(c.ShortIds) != 0 { if len(c.ShortIds) != 0 {
return nil, errors.New(`non-empty "shortIds", please use "shortId" instead`) 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) config.ShortId = make([]byte, 8)
if _, err = hex.Decode(config.ShortId, []byte(c.ShortId)); err != nil { if _, err = hex.Decode(config.ShortId, []byte(c.ShortId)); err != nil {
return nil, errors.New(`invalid "shortId": `, c.ShortId) return nil, errors.New(`invalid "shortId": `, c.ShortId)
@@ -810,7 +804,6 @@ type SocketConfig struct {
CustomSockopt []*CustomSockoptConfig `json:"customSockopt"` CustomSockopt []*CustomSockoptConfig `json:"customSockopt"`
AddressPortStrategy string `json:"addressPortStrategy"` AddressPortStrategy string `json:"addressPortStrategy"`
HappyEyeballsSettings *HappyEyeballsConfig `json:"happyEyeballs"` HappyEyeballsSettings *HappyEyeballsConfig `json:"happyEyeballs"`
TrustedXForwardedFor []string `json:"trustedXForwardedFor"`
} }
// Build implements Buildable. // Build implements Buildable.
@@ -930,7 +923,6 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
CustomSockopt: customSockopts, CustomSockopt: customSockopts,
AddressPortStrategy: addressPortStrategy, AddressPortStrategy: addressPortStrategy,
HappyEyeballs: happyEyeballs, HappyEyeballs: happyEyeballs,
TrustedXForwardedFor: c.TrustedXForwardedFor,
}, nil }, nil
} }
-9
View File
@@ -34,7 +34,6 @@ type VLessInboundConfig struct {
Decryption string `json:"decryption"` Decryption string `json:"decryption"`
Fallbacks []*VLessInboundFallback `json:"fallbacks"` Fallbacks []*VLessInboundFallback `json:"fallbacks"`
Flow string `json:"flow"` Flow string `json:"flow"`
Testseed []uint32 `json:"testseed"`
} }
// Build implements Buildable // Build implements Buildable
@@ -74,10 +73,6 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`) return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`)
} }
if len(account.Testseed) < 4 {
account.Testseed = c.Testseed
}
if account.Encryption != "" { if account.Encryption != "" {
return nil, errors.New(`VLESS clients: "encryption" should not be in inbound settings`) return nil, errors.New(`VLESS clients: "encryption" should not be in inbound settings`)
} }
@@ -217,8 +212,6 @@ type VLessOutboundConfig struct {
Seed string `json:"seed"` Seed string `json:"seed"`
Encryption string `json:"encryption"` Encryption string `json:"encryption"`
Reverse *vless.Reverse `json:"reverse"` Reverse *vless.Reverse `json:"reverse"`
Testpre uint32 `json:"testpre"`
Testseed []uint32 `json:"testseed"`
Vnext []*VLessOutboundVnext `json:"vnext"` Vnext []*VLessOutboundVnext `json:"vnext"`
} }
@@ -265,8 +258,6 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
//account.Seed = c.Seed //account.Seed = c.Seed
account.Encryption = c.Encryption account.Encryption = c.Encryption
account.Reverse = c.Reverse account.Reverse = c.Reverse
account.Testpre = c.Testpre
account.Testseed = c.Testseed
} else { } else {
if err := json.Unmarshal(rawUser, account); err != nil { if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New(`VLESS users: invalid user`).Base(err) return nil, errors.New(`VLESS users: invalid user`).Base(err)
+4
View File
@@ -3,6 +3,8 @@ package conf
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"log"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -45,6 +47,8 @@ var (
"dns": func() interface{} { return new(DNSOutboundConfig) }, "dns": func() interface{} { return new(DNSOutboundConfig) },
"wireguard": func() interface{} { return &WireGuardConfig{IsClient: true} }, "wireguard": func() interface{} { return &WireGuardConfig{IsClient: true} },
}, "protocol", "settings") }, "protocol", "settings")
ctllog = log.New(os.Stderr, "xctl> ", 0)
) )
type SniffingConfig struct { type SniffingConfig struct {
+10 -1
View File
@@ -3,6 +3,7 @@ package convert
import ( import (
"fmt" "fmt"
"os" "os"
"strings"
"github.com/xtls/xray-core/common/cmdarg" "github.com/xtls/xray-core/common/cmdarg"
creflect "github.com/xtls/xray-core/common/reflect" creflect "github.com/xtls/xray-core/common/reflect"
@@ -60,7 +61,7 @@ func executeConvertConfigsToProtobuf(cmd *base.Command, args []string) {
} }
if len(optFile) > 0 { if len(optFile) > 0 {
switch core.GetFormat(optFile){ switch core.GetFormatByExtension(getFileExtension(optFile)){
case "protobuf", "": case "protobuf", "":
fmt.Println("Output ProtoBuf file is ", optFile) fmt.Println("Output ProtoBuf file is ", optFile)
default: default:
@@ -105,3 +106,11 @@ func executeConvertConfigsToProtobuf(cmd *base.Command, args []string) {
} }
} }
} }
func getFileExtension(filename string) string {
idx := strings.LastIndexByte(filename, '.')
if idx == -1 {
return ""
}
return filename[idx+1:]
}
+12
View File
@@ -10,10 +10,12 @@ import (
type ( type (
configFileLoader func(string) (io.Reader, error) configFileLoader func(string) (io.Reader, error)
extconfigLoader func([]string, io.Reader) (io.Reader, error)
) )
var ( var (
EffectiveConfigFileLoader configFileLoader EffectiveConfigFileLoader configFileLoader
EffectiveExtConfigLoader extconfigLoader
) )
// LoadConfig reads from a path/url/stdin // LoadConfig reads from a path/url/stdin
@@ -25,3 +27,13 @@ func LoadConfig(file string) (io.Reader, error) {
} }
return EffectiveConfigFileLoader(file) return EffectiveConfigFileLoader(file)
} }
// LoadExtConfig calls xctl to handle multiple config
// the actual work also in external module
func LoadExtConfig(files []string, reader io.Reader) (io.Reader, error) {
if EffectiveExtConfigLoader == nil {
return nil, errors.New("external config module not loaded").AtError()
}
return EffectiveExtConfigLoader(files, reader)
}
+11
View File
@@ -13,6 +13,7 @@ import (
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/platform/ctlcmd"
"github.com/xtls/xray-core/main/confloader" "github.com/xtls/xray-core/main/confloader"
) )
@@ -128,6 +129,16 @@ func FetchUnixSocketHTTPContent(target string) ([]byte, error) {
return content, nil return content, nil
} }
func ExtConfigLoader(files []string, reader io.Reader) (io.Reader, error) {
buf, err := ctlcmd.Run(append([]string{"convert"}, files...), reader)
if err != nil {
return nil, err
}
return strings.NewReader(buf.String()), nil
}
func init() { func init() {
confloader.EffectiveConfigFileLoader = ConfigLoader confloader.EffectiveConfigFileLoader = ConfigLoader
confloader.EffectiveExtConfigLoader = ExtConfigLoader
} }
-6
View File
@@ -308,12 +308,6 @@ func setUpHTTPTunnel(ctx context.Context, dest net.Destination, target string, u
return nil, err return nil, err
} }
nextProto = tlsConn.ConnectionState().NegotiatedProtocol nextProto = tlsConn.ConnectionState().NegotiatedProtocol
} else if tlsConn, ok := iConn.(*tls.UConn); ok {
if err := tlsConn.HandshakeContext(ctx); err != nil {
rawConn.Close()
return nil, err
}
nextProto = tlsConn.ConnectionState().NegotiatedProtocol
} }
switch nextProto { switch nextProto {
+29 -90
View File
@@ -248,7 +248,7 @@ func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
*withinPaddingBuffers = false *withinPaddingBuffers = false
*switchToDirectCopy = true *switchToDirectCopy = true
} else { } else {
errors.LogDebug(w.ctx, "XtlsRead unknown command ", *currentCommand, buffer.Len()) errors.LogInfo(w.ctx, "XtlsRead unknown command ", *currentCommand, buffer.Len())
} }
} }
if w.trafficState.NumberOfPacketToFilter > 0 { if w.trafficState.NumberOfPacketToFilter > 0 {
@@ -269,9 +269,9 @@ func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
w.rawInput = nil w.rawInput = nil
if inbound := session.InboundFromContext(w.ctx); inbound != nil && inbound.Conn != nil { if inbound := session.InboundFromContext(w.ctx); inbound != nil && inbound.Conn != nil {
// if w.isUplink && inbound.CanSpliceCopy == 2 { // TODO: enable uplink splice if w.isUplink && inbound.CanSpliceCopy == 2 {
// inbound.CanSpliceCopy = 1 inbound.CanSpliceCopy = 1
// } }
if !w.isUplink && w.ob != nil && w.ob.CanSpliceCopy == 2 { // ob need to be passed in due to context can have more than one ob if !w.isUplink && w.ob != nil && w.ob.CanSpliceCopy == 2 { // ob need to be passed in due to context can have more than one ob
w.ob.CanSpliceCopy = 1 w.ob.CanSpliceCopy = 1
} }
@@ -296,16 +296,11 @@ type VisionWriter struct {
// internal // internal
writeOnceUserUUID []byte writeOnceUserUUID []byte
directWriteCounter stats.Counter directWriteCounter stats.Counter
testseed []uint32
} }
func NewVisionWriter(writer buf.Writer, trafficState *TrafficState, isUplink bool, ctx context.Context, conn net.Conn, ob *session.Outbound, testseed []uint32) *VisionWriter { func NewVisionWriter(writer buf.Writer, trafficState *TrafficState, isUplink bool, ctx context.Context, conn net.Conn, ob *session.Outbound) *VisionWriter {
w := make([]byte, len(trafficState.UserUUID)) w := make([]byte, len(trafficState.UserUUID))
copy(w, trafficState.UserUUID) copy(w, trafficState.UserUUID)
if len(testseed) < 4 {
testseed = []uint32{900, 500, 900, 256}
}
return &VisionWriter{ return &VisionWriter{
Writer: writer, Writer: writer,
trafficState: trafficState, trafficState: trafficState,
@@ -314,7 +309,6 @@ func NewVisionWriter(writer buf.Writer, trafficState *TrafficState, isUplink boo
isUplink: isUplink, isUplink: isUplink,
conn: conn, conn: conn,
ob: ob, ob: ob,
testseed: testseed,
} }
} }
@@ -334,9 +328,9 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
if !w.isUplink && inbound.CanSpliceCopy == 2 { if !w.isUplink && inbound.CanSpliceCopy == 2 {
inbound.CanSpliceCopy = 1 inbound.CanSpliceCopy = 1
} }
// if w.isUplink && w.ob != nil && w.ob.CanSpliceCopy == 2 { // TODO: enable uplink splice if w.isUplink && w.ob != nil && w.ob.CanSpliceCopy == 2 {
// w.ob.CanSpliceCopy = 1 w.ob.CanSpliceCopy = 1
// } }
} }
rawConn, _, writerCounter := UnwrapRawConn(w.conn) rawConn, _, writerCounter := UnwrapRawConn(w.conn)
w.Writer = buf.NewWriter(rawConn) w.Writer = buf.NewWriter(rawConn)
@@ -353,14 +347,13 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
if *isPadding { if *isPadding {
if len(mb) == 1 && mb[0] == nil { if len(mb) == 1 && mb[0] == nil {
mb[0] = XtlsPadding(nil, CommandPaddingContinue, &w.writeOnceUserUUID, true, w.ctx, w.testseed) // we do a long padding to hide vless header mb[0] = XtlsPadding(nil, CommandPaddingContinue, &w.writeOnceUserUUID, true, w.ctx) // we do a long padding to hide vless header
return w.Writer.WriteMultiBuffer(mb) return w.Writer.WriteMultiBuffer(mb)
} }
isComplete := IsCompleteRecord(mb)
mb = ReshapeMultiBuffer(w.ctx, mb) mb = ReshapeMultiBuffer(w.ctx, mb)
longPadding := w.trafficState.IsTLS longPadding := w.trafficState.IsTLS
for i, b := range mb { for i, b := range mb {
if w.trafficState.IsTLS && b.Len() >= 6 && bytes.Equal(TlsApplicationDataStart, b.BytesTo(3)) && isComplete { if w.trafficState.IsTLS && b.Len() >= 6 && bytes.Equal(TlsApplicationDataStart, b.BytesTo(3)) {
if w.trafficState.EnableXtls { if w.trafficState.EnableXtls {
*switchToDirectCopy = true *switchToDirectCopy = true
} }
@@ -371,13 +364,13 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
command = CommandPaddingDirect command = CommandPaddingDirect
} }
} }
mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, true, w.ctx, w.testseed) mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, true, w.ctx)
*isPadding = false // padding going to end *isPadding = false // padding going to end
longPadding = false longPadding = false
continue continue
} else if !w.trafficState.IsTLS12orAbove && w.trafficState.NumberOfPacketToFilter <= 1 { // For compatibility with earlier vision receiver, we finish padding 1 packet early } else if !w.trafficState.IsTLS12orAbove && w.trafficState.NumberOfPacketToFilter <= 1 { // For compatibility with earlier vision receiver, we finish padding 1 packet early
*isPadding = false *isPadding = false
mb[i] = XtlsPadding(b, CommandPaddingEnd, &w.writeOnceUserUUID, longPadding, w.ctx, w.testseed) mb[i] = XtlsPadding(b, CommandPaddingEnd, &w.writeOnceUserUUID, longPadding, w.ctx)
break break
} }
var command byte = CommandPaddingContinue var command byte = CommandPaddingContinue
@@ -387,66 +380,12 @@ func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
command = CommandPaddingDirect command = CommandPaddingDirect
} }
} }
mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, longPadding, w.ctx, w.testseed) mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, longPadding, w.ctx)
} }
} }
return w.Writer.WriteMultiBuffer(mb) return w.Writer.WriteMultiBuffer(mb)
} }
// IsCompleteRecord Is complete tls data record
func IsCompleteRecord(buffer buf.MultiBuffer) bool {
b := make([]byte, buffer.Len())
if buffer.Copy(b) != int(buffer.Len()) {
panic("impossible bytes allocation")
}
var headerLen int = 5
var recordLen int
totalLen := len(b)
i := 0
for i < totalLen {
// record header: 0x17 0x3 0x3 + 2 bytes length
if headerLen > 0 {
data := b[i]
i++
switch headerLen {
case 5:
if data != 0x17 {
return false
}
case 4:
if data != 0x03 {
return false
}
case 3:
if data != 0x03 {
return false
}
case 2:
recordLen = int(data) << 8
case 1:
recordLen = recordLen | int(data)
}
headerLen--
} else if recordLen > 0 {
remaining := totalLen - i
if remaining < recordLen {
return false
} else {
i += recordLen
recordLen = 0
headerLen = 5
}
} else {
return false
}
}
if headerLen == 5 && recordLen == 0 {
return true
}
return false
}
// ReshapeMultiBuffer prepare multi buffer for padding structure (max 21 bytes) // ReshapeMultiBuffer prepare multi buffer for padding structure (max 21 bytes)
func ReshapeMultiBuffer(ctx context.Context, buffer buf.MultiBuffer) buf.MultiBuffer { func ReshapeMultiBuffer(ctx context.Context, buffer buf.MultiBuffer) buf.MultiBuffer {
needReshape := 0 needReshape := 0
@@ -478,25 +417,25 @@ func ReshapeMultiBuffer(ctx context.Context, buffer buf.MultiBuffer) buf.MultiBu
buffer[i] = nil buffer[i] = nil
} }
buffer = buffer[:0] buffer = buffer[:0]
errors.LogDebug(ctx, "ReshapeMultiBuffer ", toPrint) errors.LogInfo(ctx, "ReshapeMultiBuffer ", toPrint)
return mb2 return mb2
} }
// XtlsPadding add padding to eliminate length signature during tls handshake // XtlsPadding add padding to eliminate length signature during tls handshake
func XtlsPadding(b *buf.Buffer, command byte, userUUID *[]byte, longPadding bool, ctx context.Context, testseed []uint32) *buf.Buffer { func XtlsPadding(b *buf.Buffer, command byte, userUUID *[]byte, longPadding bool, ctx context.Context) *buf.Buffer {
var contentLen int32 = 0 var contentLen int32 = 0
var paddingLen int32 = 0 var paddingLen int32 = 0
if b != nil { if b != nil {
contentLen = b.Len() contentLen = b.Len()
} }
if contentLen < int32(testseed[0]) && longPadding { if contentLen < 900 && longPadding {
l, err := rand.Int(rand.Reader, big.NewInt(int64(testseed[1]))) l, err := rand.Int(rand.Reader, big.NewInt(500))
if err != nil { if err != nil {
errors.LogDebugInner(ctx, err, "failed to generate padding") errors.LogDebugInner(ctx, err, "failed to generate padding")
} }
paddingLen = int32(l.Int64()) + int32(testseed[2]) - contentLen paddingLen = int32(l.Int64()) + 900 - contentLen
} else { } else {
l, err := rand.Int(rand.Reader, big.NewInt(int64(testseed[3]))) l, err := rand.Int(rand.Reader, big.NewInt(256))
if err != nil { if err != nil {
errors.LogDebugInner(ctx, err, "failed to generate padding") errors.LogDebugInner(ctx, err, "failed to generate padding")
} }
@@ -517,7 +456,7 @@ func XtlsPadding(b *buf.Buffer, command byte, userUUID *[]byte, longPadding bool
b = nil b = nil
} }
newbuffer.Extend(paddingLen) newbuffer.Extend(paddingLen)
errors.LogDebug(ctx, "XtlsPadding ", contentLen, " ", paddingLen, " ", command) errors.LogInfo(ctx, "XtlsPadding ", contentLen, " ", paddingLen, " ", command)
return newbuffer return newbuffer
} }
@@ -564,7 +503,7 @@ func XtlsUnpadding(b *buf.Buffer, s *TrafficState, isUplink bool, ctx context.Co
*remainingPadding = int32(data) << 8 *remainingPadding = int32(data) << 8
case 1: case 1:
*remainingPadding = *remainingPadding | int32(data) *remainingPadding = *remainingPadding | int32(data)
errors.LogDebug(ctx, "Xtls Unpadding new block, content ", *remainingContent, " padding ", *remainingPadding, " command ", *currentCommand) errors.LogInfo(ctx, "Xtls Unpadding new block, content ", *remainingContent, " padding ", *remainingPadding, " command ", *currentCommand)
} }
*remainingCommand-- *remainingCommand--
} else if *remainingContent > 0 { } else if *remainingContent > 0 {
@@ -623,11 +562,11 @@ func XtlsFilterTls(buffer buf.MultiBuffer, trafficState *TrafficState, ctx conte
cipherSuite := b.BytesRange(43+sessionIdLen+1, 43+sessionIdLen+3) cipherSuite := b.BytesRange(43+sessionIdLen+1, 43+sessionIdLen+3)
trafficState.Cipher = uint16(cipherSuite[0])<<8 | uint16(cipherSuite[1]) trafficState.Cipher = uint16(cipherSuite[0])<<8 | uint16(cipherSuite[1])
} else { } else {
errors.LogDebug(ctx, "XtlsFilterTls short server hello, tls 1.2 or older? ", b.Len(), " ", trafficState.RemainingServerHello) errors.LogInfo(ctx, "XtlsFilterTls short server hello, tls 1.2 or older? ", b.Len(), " ", trafficState.RemainingServerHello)
} }
} else if bytes.Equal(TlsClientHandShakeStart, startsBytes[:2]) && startsBytes[5] == TlsHandshakeTypeClientHello { } else if bytes.Equal(TlsClientHandShakeStart, startsBytes[:2]) && startsBytes[5] == TlsHandshakeTypeClientHello {
trafficState.IsTLS = true trafficState.IsTLS = true
errors.LogDebug(ctx, "XtlsFilterTls found tls client hello! ", buffer.Len()) errors.LogInfo(ctx, "XtlsFilterTls found tls client hello! ", buffer.Len())
} }
} }
if trafficState.RemainingServerHello > 0 { if trafficState.RemainingServerHello > 0 {
@@ -643,18 +582,18 @@ func XtlsFilterTls(buffer buf.MultiBuffer, trafficState *TrafficState, ctx conte
} else if v != "TLS_AES_128_CCM_8_SHA256" { } else if v != "TLS_AES_128_CCM_8_SHA256" {
trafficState.EnableXtls = true trafficState.EnableXtls = true
} }
errors.LogDebug(ctx, "XtlsFilterTls found tls 1.3! ", b.Len(), " ", v) errors.LogInfo(ctx, "XtlsFilterTls found tls 1.3! ", b.Len(), " ", v)
trafficState.NumberOfPacketToFilter = 0 trafficState.NumberOfPacketToFilter = 0
return return
} else if trafficState.RemainingServerHello <= 0 { } else if trafficState.RemainingServerHello <= 0 {
errors.LogDebug(ctx, "XtlsFilterTls found tls 1.2! ", b.Len()) errors.LogInfo(ctx, "XtlsFilterTls found tls 1.2! ", b.Len())
trafficState.NumberOfPacketToFilter = 0 trafficState.NumberOfPacketToFilter = 0
return return
} }
errors.LogDebug(ctx, "XtlsFilterTls inconclusive server hello ", b.Len(), " ", trafficState.RemainingServerHello) errors.LogInfo(ctx, "XtlsFilterTls inconclusive server hello ", b.Len(), " ", trafficState.RemainingServerHello)
} }
if trafficState.NumberOfPacketToFilter <= 0 { if trafficState.NumberOfPacketToFilter <= 0 {
errors.LogDebug(ctx, "XtlsFilterTls stop filtering", buffer.Len()) errors.LogInfo(ctx, "XtlsFilterTls stop filtering", buffer.Len())
} }
} }
} }
@@ -736,7 +675,7 @@ func CopyRawConnIfExist(ctx context.Context, readerConn net.Conn, writerConn net
} }
} }
if splice { if splice {
errors.LogDebug(ctx, "CopyRawConn splice") errors.LogInfo(ctx, "CopyRawConn splice")
statWriter, _ := writer.(*dispatcher.SizeStatWriter) statWriter, _ := writer.(*dispatcher.SizeStatWriter)
//runtime.Gosched() // necessary //runtime.Gosched() // necessary
time.Sleep(time.Millisecond) // without this, there will be a rare ssl error for freedom splice time.Sleep(time.Millisecond) // without this, there will be a rare ssl error for freedom splice
@@ -779,7 +718,7 @@ func CopyRawConnIfExist(ctx context.Context, readerConn net.Conn, writerConn net
} }
func readV(ctx context.Context, reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, readCounter stats.Counter) error { func readV(ctx context.Context, reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, readCounter stats.Counter) error {
errors.LogDebug(ctx, "CopyRawConn (maybe) readv") errors.LogInfo(ctx, "CopyRawConn (maybe) readv")
if err := buf.Copy(reader, writer, buf.UpdateActivity(timer), buf.AddToStatCounter(readCounter)); err != nil { if err := buf.Copy(reader, writer, buf.UpdateActivity(timer), buf.AddToStatCounter(readCounter)); err != nil {
return errors.New("failed to process response").Base(err) return errors.New("failed to process response").Base(err)
} }
-5
View File
@@ -353,11 +353,6 @@ func EncodeUDPPacket(request *protocol.RequestHeader, data []byte) (*buf.Buffer,
b.Release() b.Release()
return nil, err return nil, err
} }
// if data is too large, return an empty buffer (drop too big data)
if b.Available() < int32(len(data)) {
b.Clear()
return b, nil
}
common.Must2(b.Write(data)) common.Must2(b.Write(data))
return b, nil return b, nil
} }
-7
View File
@@ -22,8 +22,6 @@ func (a *Account) AsAccount() (protocol.Account, error) {
Seconds: a.Seconds, Seconds: a.Seconds,
Padding: a.Padding, Padding: a.Padding,
Reverse: a.Reverse, Reverse: a.Reverse,
Testpre: a.Testpre,
Testseed: a.Testseed,
}, nil }, nil
} }
@@ -40,9 +38,6 @@ type MemoryAccount struct {
Padding string Padding string
Reverse *Reverse Reverse *Reverse
Testpre uint32
Testseed []uint32
} }
// Equals implements protocol.Account.Equals(). // Equals implements protocol.Account.Equals().
@@ -63,7 +58,5 @@ func (a *MemoryAccount) ToProto() proto.Message {
Seconds: a.Seconds, Seconds: a.Seconds,
Padding: a.Padding, Padding: a.Padding,
Reverse: a.Reverse, Reverse: a.Reverse,
Testpre: a.Testpre,
Testseed: a.Testseed,
} }
} }
+8 -27
View File
@@ -79,8 +79,6 @@ type Account struct {
Seconds uint32 `protobuf:"varint,5,opt,name=seconds,proto3" json:"seconds,omitempty"` Seconds uint32 `protobuf:"varint,5,opt,name=seconds,proto3" json:"seconds,omitempty"`
Padding string `protobuf:"bytes,6,opt,name=padding,proto3" json:"padding,omitempty"` Padding string `protobuf:"bytes,6,opt,name=padding,proto3" json:"padding,omitempty"`
Reverse *Reverse `protobuf:"bytes,7,opt,name=reverse,proto3" json:"reverse,omitempty"` Reverse *Reverse `protobuf:"bytes,7,opt,name=reverse,proto3" json:"reverse,omitempty"`
Testpre uint32 `protobuf:"varint,8,opt,name=testpre,proto3" json:"testpre,omitempty"`
Testseed []uint32 `protobuf:"varint,9,rep,packed,name=testseed,proto3" json:"testseed,omitempty"`
} }
func (x *Account) Reset() { func (x *Account) Reset() {
@@ -162,20 +160,6 @@ func (x *Account) GetReverse() *Reverse {
return nil return nil
} }
func (x *Account) GetTestpre() uint32 {
if x != nil {
return x.Testpre
}
return 0
}
func (x *Account) GetTestseed() []uint32 {
if x != nil {
return x.Testseed
}
return nil
}
var File_proxy_vless_account_proto protoreflect.FileDescriptor var File_proxy_vless_account_proto protoreflect.FileDescriptor
var file_proxy_vless_account_proto_rawDesc = []byte{ var file_proxy_vless_account_proto_rawDesc = []byte{
@@ -183,7 +167,7 @@ var file_proxy_vless_account_proto_rawDesc = []byte{
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x22, 0x1b, 0x0a, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x22, 0x1b, 0x0a,
0x07, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x07, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x86, 0x02, 0x0a, 0x07, 0x41, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0xd0, 0x01, 0x0a, 0x07, 0x41,
0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e,
@@ -196,16 +180,13 @@ var file_proxy_vless_account_proto_rawDesc = []byte{
0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x33, 0x0a, 0x07, 0x72, 0x65, 0x76, 0x65, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x33, 0x0a, 0x07, 0x72, 0x65, 0x76, 0x65,
0x72, 0x73, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x72, 0x73, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x52, 0x65, 0x76, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x52, 0x65, 0x76,
0x65, 0x72, 0x73, 0x65, 0x52, 0x07, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x65, 0x72, 0x73, 0x65, 0x52, 0x07, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x42, 0x52, 0x0a,
0x07, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e,
0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x73, 0x74, 0x73, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x65, 0x65, 0x64, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x74, 0x65, 0x73, 0x74, 0x73, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f,
0x65, 0x65, 0x64, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0xaa, 0x02,
0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76,
0x6c, 0x65, 0x73, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78,
0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
-3
View File
@@ -22,7 +22,4 @@ message Account {
string padding = 6; string padding = 6;
Reverse reverse = 7; Reverse reverse = 7;
uint32 testpre = 8;
repeated uint32 testseed = 9;
} }
+1 -1
View File
@@ -68,7 +68,7 @@ func EncodeBodyAddons(writer buf.Writer, request *protocol.RequestHeader, reques
return NewMultiLengthPacketWriter(writer) return NewMultiLengthPacketWriter(writer)
} }
if requestAddons.Flow == vless.XRV { if requestAddons.Flow == vless.XRV {
return proxy.NewVisionWriter(writer, state, isUplink, context, conn, ob, request.User.Account.(*vless.MemoryAccount).Testseed) return proxy.NewVisionWriter(writer, state, isUplink, context, conn, ob)
} }
return writer return writer
} }
+4 -14
View File
@@ -12,6 +12,7 @@ import (
"time" "time"
"unsafe" "unsafe"
"github.com/xtls/xray-core/app/dispatcher"
"github.com/xtls/xray-core/app/reverse" "github.com/xtls/xray-core/app/reverse"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
@@ -75,7 +76,7 @@ type Handler struct {
validator vless.Validator validator vless.Validator
decryption *encryption.ServerInstance decryption *encryption.ServerInstance
outboundHandlerManager outbound.Manager outboundHandlerManager outbound.Manager
wrapLink func(ctx context.Context, link *transport.Link) *transport.Link defaultDispatcher *dispatcher.DefaultDispatcher
ctx context.Context ctx context.Context
fallbacks map[string]map[string]map[string]*Fallback // or nil fallbacks map[string]map[string]map[string]*Fallback // or nil
// regexps map[string]*regexp.Regexp // or nil // regexps map[string]*regexp.Regexp // or nil
@@ -84,16 +85,12 @@ type Handler struct {
// New creates a new VLess inbound handler. // New creates a new VLess inbound handler.
func New(ctx context.Context, config *Config, dc dns.Client, validator vless.Validator) (*Handler, error) { func New(ctx context.Context, config *Config, dc dns.Client, validator vless.Validator) (*Handler, error) {
v := core.MustFromContext(ctx) v := core.MustFromContext(ctx)
var wrapLinkFunc func(ctx context.Context, link *transport.Link) *transport.Link
if dispatcher, ok := v.GetFeature(routing.DispatcherType()).(routing.WrapLinkDispatcher); ok {
wrapLinkFunc = dispatcher.WrapLink
}
handler := &Handler{ handler := &Handler{
inboundHandlerManager: v.GetFeature(feature_inbound.ManagerType()).(feature_inbound.Manager), inboundHandlerManager: v.GetFeature(feature_inbound.ManagerType()).(feature_inbound.Manager),
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
validator: validator, validator: validator,
outboundHandlerManager: v.GetFeature(outbound.ManagerType()).(outbound.Manager), outboundHandlerManager: v.GetFeature(outbound.ManagerType()).(outbound.Manager),
wrapLink: wrapLinkFunc, defaultDispatcher: v.GetFeature(routing.DispatcherType()).(*dispatcher.DefaultDispatcher),
ctx: ctx, ctx: ctx,
} }
@@ -538,10 +535,6 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
account := request.User.Account.(*vless.MemoryAccount) account := request.User.Account.(*vless.MemoryAccount)
if account.Reverse != nil && request.Command != protocol.RequestCommandRvs {
return errors.New("for safety reasons, user " + account.ID.String() + " is not allowed to use forward proxy")
}
responseAddons := &encoding.Addons{ responseAddons := &encoding.Addons{
// Flow: requestAddons.Flow, // Flow: requestAddons.Flow,
} }
@@ -626,10 +619,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
if err != nil { if err != nil {
return err return err
} }
if h.wrapLink == nil { return r.NewMux(ctx, h.defaultDispatcher.WrapLink(ctx, &transport.Link{Reader: clientReader, Writer: clientWriter}))
return errors.New("VLESS reverse must have a dispatcher that implemented routing.WrapLinkDispatcher")
}
return r.NewMux(ctx, h.wrapLink(ctx, &transport.Link{Reader: clientReader, Writer: clientWriter}))
} }
if err := dispatcher.DispatchLink(ctx, request.Destination(), &transport.Link{ if err := dispatcher.DispatchLink(ctx, request.Destination(), &transport.Link{
-52
View File
@@ -7,7 +7,6 @@ import (
"encoding/base64" "encoding/base64"
"reflect" "reflect"
"strings" "strings"
"sync"
"time" "time"
"unsafe" "unsafe"
@@ -16,7 +15,6 @@ import (
"github.com/xtls/xray-core/app/reverse" "github.com/xtls/xray-core/app/reverse"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
xctx "github.com/xtls/xray-core/common/ctx"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/mux" "github.com/xtls/xray-core/common/mux"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
@@ -54,15 +52,6 @@ type Handler struct {
cone bool cone bool
encryption *encryption.ClientInstance encryption *encryption.ClientInstance
reverse *Reverse reverse *Reverse
testpre uint32
initpre sync.Once
preConns chan *ConnExpire
}
type ConnExpire struct {
Conn stat.Connection
Expire time.Time
} }
// New creates a new VLess outbound handler. // New creates a new VLess outbound handler.
@@ -116,16 +105,11 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
}() }()
} }
handler.testpre = a.Testpre
return handler, nil return handler, nil
} }
// Close implements common.Closable.Close(). // Close implements common.Closable.Close().
func (h *Handler) Close() error { func (h *Handler) Close() error {
if h.preConns != nil {
close(h.preConns)
}
if h.reverse != nil { if h.reverse != nil {
return h.reverse.Close() return h.reverse.Close()
} }
@@ -144,39 +128,6 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
rec := h.server rec := h.server
var conn stat.Connection var conn stat.Connection
if h.testpre > 0 && h.reverse == nil {
h.initpre.Do(func() {
h.preConns = make(chan *ConnExpire)
for range h.testpre { // TODO: randomize
go func() {
defer func() { recover() }()
ctx := xctx.ContextWithID(context.Background(), session.NewID())
for {
conn, err := dialer.Dial(ctx, rec.Destination)
if err != nil {
errors.LogWarningInner(ctx, err, "pre-connect failed")
continue
}
h.preConns <- &ConnExpire{Conn: conn, Expire: time.Now().Add(time.Minute * 2)} // TODO: customize & randomize
time.Sleep(time.Millisecond * 200) // TODO: customize & randomize
}
}()
}
})
for {
connTime := <-h.preConns
if connTime == nil {
return errors.New("closed handler").AtWarning()
}
if time.Now().Before(connTime.Expire) {
conn = connTime.Conn
break
}
connTime.Conn.Close()
}
}
if conn == nil {
if err := retry.ExponentialBackoff(5, 200).On(func() error { if err := retry.ExponentialBackoff(5, 200).On(func() error {
var err error var err error
conn, err = dialer.Dial(ctx, rec.Destination) conn, err = dialer.Dial(ctx, rec.Destination)
@@ -187,11 +138,8 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
}); err != nil { }); err != nil {
return errors.New("failed to find an available destination").Base(err).AtWarning() return errors.New("failed to find an available destination").Base(err).AtWarning()
} }
}
defer conn.Close() defer conn.Close()
ob.Conn = conn // for Vision's pre-connect
iConn := conn iConn := conn
if statConn, ok := iConn.(*stat.CounterConnection); ok { if statConn, ok := iConn.(*stat.CounterConnection); ok {
iConn = statConn.Connection iConn = statConn.Connection
+12 -10
View File
@@ -3,13 +3,15 @@ package wireguard
import ( import (
"context" "context"
"errors" "errors"
"io"
"net"
"net/netip" "net/netip"
"strconv" "strconv"
"sync" "sync"
"golang.zx2c4.com/wireguard/conn" "golang.zx2c4.com/wireguard/conn"
"github.com/xtls/xray-core/common/net" xnet "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet"
) )
@@ -50,21 +52,21 @@ func (n *netBind) ParseEndpoint(s string) (conn.Endpoint, error) {
return nil, err return nil, err
} }
addr := net.ParseAddress(ipStr) addr := xnet.ParseAddress(ipStr)
if addr.Family() == net.AddressFamilyDomain { if addr.Family() == xnet.AddressFamilyDomain {
ips, _, err := n.dns.LookupIP(addr.Domain(), n.dnsOption) ips, _, err := n.dns.LookupIP(addr.Domain(), n.dnsOption)
if err != nil { if err != nil {
return nil, err return nil, err
} else if len(ips) == 0 { } else if len(ips) == 0 {
return nil, dns.ErrEmptyResponse return nil, dns.ErrEmptyResponse
} }
addr = net.IPAddress(ips[0]) addr = xnet.IPAddress(ips[0])
} }
dst := net.Destination{ dst := xnet.Destination{
Address: addr, Address: addr,
Port: net.Port(portNum), Port: xnet.Port(portNum),
Network: net.Network_UDP, Network: xnet.Network_UDP,
} }
return &netEndpoint{ return &netEndpoint{
@@ -151,7 +153,7 @@ func (bind *netBindClient) connectTo(endpoint *netEndpoint) error {
v.endpoint = endpoint v.endpoint = endpoint
v.err = err v.err = err
v.waiter.Done() v.waiter.Done()
if err != nil { if err != nil && errors.Is(err, io.EOF) {
endpoint.conn = nil endpoint.conn = nil
return return
} }
@@ -213,7 +215,7 @@ func (bind *netBindServer) Send(buff [][]byte, endpoint conn.Endpoint) error {
} }
type netEndpoint struct { type netEndpoint struct {
dst net.Destination dst xnet.Destination
conn net.Conn conn net.Conn
} }
@@ -246,7 +248,7 @@ func (e netEndpoint) SrcToString() string {
return "" return ""
} }
func toNetIpAddr(addr net.Address) netip.Addr { func toNetIpAddr(addr xnet.Address) netip.Addr {
if addr.Family().IsIPv4() { if addr.Family().IsIPv4() {
ip := addr.IP() ip := addr.IP()
return netip.AddrFrom4([4]byte{ip[0], ip[1], ip[2], ip[3]}) return netip.AddrFrom4([4]byte{ip[0], ip[1], ip[2], ip[3]})
+5 -4
View File
@@ -3,6 +3,7 @@ package wireguard
import ( import (
"context" "context"
"fmt" "fmt"
"net"
"net/netip" "net/netip"
"runtime" "runtime"
"strconv" "strconv"
@@ -12,7 +13,7 @@ import (
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net" xnet "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/proxy/wireguard/gvisortun" "github.com/xtls/xray-core/proxy/wireguard/gvisortun"
"gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
@@ -27,7 +28,7 @@ import (
type tunCreator func(localAddresses []netip.Addr, mtu int, handler promiscuousModeHandler) (Tunnel, error) type tunCreator func(localAddresses []netip.Addr, mtu int, handler promiscuousModeHandler) (Tunnel, error)
type promiscuousModeHandler func(dest net.Destination, conn net.Conn) type promiscuousModeHandler func(dest xnet.Destination, conn net.Conn)
type Tunnel interface { type Tunnel interface {
BuildDevice(ipc string, bind conn.Bind) error BuildDevice(ipc string, bind conn.Bind) error
@@ -168,7 +169,7 @@ func createGVisorTun(localAddresses []netip.Addr, mtu int, handler promiscuousMo
ep.SocketOptions().SetKeepAlive(true) ep.SocketOptions().SetKeepAlive(true)
// local address is actually destination // local address is actually destination
handler(net.TCPDestination(net.IPAddress(id.LocalAddress.AsSlice()), net.Port(id.LocalPort)), gonet.NewTCPConn(&wq, ep)) handler(xnet.TCPDestination(xnet.IPAddress(id.LocalAddress.AsSlice()), xnet.Port(id.LocalPort)), gonet.NewTCPConn(&wq, ep))
}(r) }(r)
}) })
stack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket) stack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
@@ -193,7 +194,7 @@ func createGVisorTun(localAddresses []netip.Addr, mtu int, handler promiscuousMo
Timeout: 15 * time.Second, Timeout: 15 * time.Second,
}) })
handler(net.UDPDestination(net.IPAddress(id.LocalAddress.AsSlice()), net.Port(id.LocalPort)), gonet.NewUDPConn(&wq, ep)) handler(xnet.UDPDestination(xnet.IPAddress(id.LocalAddress.AsSlice()), xnet.Port(id.LocalPort)), gonet.NewUDPConn(&wq, ep))
}(r) }(r)
}) })
stack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket) stack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
+43 -55
View File
@@ -530,7 +530,6 @@ type SocketConfig struct {
CustomSockopt []*CustomSockopt `protobuf:"bytes,20,rep,name=customSockopt,proto3" json:"customSockopt,omitempty"` CustomSockopt []*CustomSockopt `protobuf:"bytes,20,rep,name=customSockopt,proto3" json:"customSockopt,omitempty"`
AddressPortStrategy AddressPortStrategy `protobuf:"varint,21,opt,name=address_port_strategy,json=addressPortStrategy,proto3,enum=xray.transport.internet.AddressPortStrategy" json:"address_port_strategy,omitempty"` AddressPortStrategy AddressPortStrategy `protobuf:"varint,21,opt,name=address_port_strategy,json=addressPortStrategy,proto3,enum=xray.transport.internet.AddressPortStrategy" json:"address_port_strategy,omitempty"`
HappyEyeballs *HappyEyeballsConfig `protobuf:"bytes,22,opt,name=happy_eyeballs,json=happyEyeballs,proto3" json:"happy_eyeballs,omitempty"` HappyEyeballs *HappyEyeballsConfig `protobuf:"bytes,22,opt,name=happy_eyeballs,json=happyEyeballs,proto3" json:"happy_eyeballs,omitempty"`
TrustedXForwardedFor []string `protobuf:"bytes,23,rep,name=trusted_x_forwarded_for,json=trustedXForwardedFor,proto3" json:"trusted_x_forwarded_for,omitempty"`
} }
func (x *SocketConfig) Reset() { func (x *SocketConfig) Reset() {
@@ -717,13 +716,6 @@ func (x *SocketConfig) GetHappyEyeballs() *HappyEyeballsConfig {
return nil return nil
} }
func (x *SocketConfig) GetTrustedXForwardedFor() []string {
if x != nil {
return x.TrustedXForwardedFor
}
return nil
}
type HappyEyeballsConfig struct { type HappyEyeballsConfig struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -851,7 +843,7 @@ var file_transport_internet_config_proto_rawDesc = []byte{
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6f, 0x70, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6f, 0x70, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x74, 0x79, 0x70, 0x65, 0x22, 0x89, 0x09, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x74, 0x79, 0x70, 0x65, 0x22, 0xd2, 0x08, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20,
0x01, 0x28, 0x05, 0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f,
0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74,
@@ -917,52 +909,48 @@ var file_transport_internet_config_proto_rawDesc = []byte{
0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x61, 0x70, 0x70, 0x79, 0x45, 0x79, 0x65, 0x62, 0x61, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x61, 0x70, 0x70, 0x79, 0x45, 0x79, 0x65, 0x62, 0x61,
0x6c, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x68, 0x61, 0x70, 0x70, 0x79, 0x6c, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x68, 0x61, 0x70, 0x70, 0x79,
0x45, 0x79, 0x65, 0x62, 0x61, 0x6c, 0x6c, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x74, 0x72, 0x75, 0x73, 0x45, 0x79, 0x65, 0x62, 0x61, 0x6c, 0x6c, 0x73, 0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f,
0x74, 0x65, 0x64, 0x5f, 0x78, 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x64, 0x5f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12,
0x66, 0x6f, 0x72, 0x18, 0x17, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x74, 0x72, 0x75, 0x73, 0x74, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52,
0x65, 0x64, 0x58, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x22, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x22, 0xad, 0x01, 0x0a, 0x13, 0x48, 0x61,
0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x70, 0x70, 0x79, 0x45, 0x79, 0x65, 0x62, 0x61, 0x6c, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x5f,
0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x69, 0x70, 0x76, 0x36, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x70, 0x72, 0x69, 0x6f,
0x22, 0xad, 0x01, 0x0a, 0x13, 0x48, 0x61, 0x70, 0x70, 0x79, 0x45, 0x79, 0x65, 0x62, 0x61, 0x6c, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x49, 0x70, 0x76, 0x36, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e,
0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x69, 0x6f, 0x74, 0x65, 0x72, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a,
0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x5f, 0x69, 0x70, 0x76, 0x36, 0x18, 0x01, 0x20, 0x01, 0x28, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x72,
0x08, 0x52, 0x0e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x49, 0x70, 0x76, 0x79, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52,
0x36, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x18, 0x0a, 0x74, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6d,
0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6c, 0x65, 0x61, 0x76, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x72,
0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x73, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63,
0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x74, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x79, 0x2a, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f,
0x4d, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05,
0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49,
0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x79, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02,
0x2a, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a,
0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55,
0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52,
0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45,
0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f,
0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x05, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49,
0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, 0x12, 0x0d, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49,
0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x2a, 0x97, 0x01, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, 0x0a, 0x0a, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a,
0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, 0x0a, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x72, 0x76, 0x50, 0x6f,
0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x2a, 0x97, 0x01, 0x0a, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x72, 0x76, 0x41,
0x13, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11,
0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x0f, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
0x0a, 0x0b, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x01, 0x12, 0x73, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e,
0x12, 0x0a, 0x0e, 0x53, 0x72, 0x76, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e, 0x6c, 0x6c, 0x79, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x78, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65,
0x79, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x6e, 0x73, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x05, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x78, 0x74, 0x50,
0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x78, 0x6f, 0x72, 0x74, 0x41, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x06, 0x42,
0x74, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
0x78, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x05, 0x12, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01,
0x15, 0x0a, 0x11, 0x54, 0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c,
0x72, 0x65, 0x73, 0x73, 0x10, 0x06, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e,
0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02,
0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61,
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
-2
View File
@@ -132,8 +132,6 @@ message SocketConfig {
AddressPortStrategy address_port_strategy = 21; AddressPortStrategy address_port_strategy = 21;
HappyEyeballsConfig happy_eyeballs = 22; HappyEyeballsConfig happy_eyeballs = 22;
repeated string trusted_x_forwarded_for = 23;
} }
message HappyEyeballsConfig { message HappyEyeballsConfig {
+3 -2
View File
@@ -3,6 +3,7 @@ package internet
import ( import (
"context" "context"
"fmt" "fmt"
gonet "net"
"strings" "strings"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
@@ -182,7 +183,7 @@ func checkAddressPortStrategy(ctx context.Context, dest net.Destination, sockopt
if len(parts) != 3 { if len(parts) != 3 {
return nil, errors.New("invalid address format", dest.Address.String()) return nil, errors.New("invalid address format", dest.Address.String())
} }
_, srvRecords, err := net.DefaultResolver.LookupSRV(context.Background(), parts[0][1:], parts[1][1:], parts[2]) _, srvRecords, err := gonet.DefaultResolver.LookupSRV(context.Background(), parts[0][1:], parts[1][1:], parts[2])
if err != nil { if err != nil {
return nil, errors.New("failed to lookup SRV record").Base(err) return nil, errors.New("failed to lookup SRV record").Base(err)
} }
@@ -197,7 +198,7 @@ func checkAddressPortStrategy(ctx context.Context, dest net.Destination, sockopt
} }
if OverrideBy == "txt" { if OverrideBy == "txt" {
errors.LogDebug(ctx, "query TXT record for "+dest.Address.String()) errors.LogDebug(ctx, "query TXT record for "+dest.Address.String())
txtRecords, err := net.DefaultResolver.LookupTXT(ctx, dest.Address.String()) txtRecords, err := gonet.DefaultResolver.LookupTXT(ctx, dest.Address.String())
if err != nil { if err != nil {
errors.LogError(ctx, "failed to lookup SRV record: "+err.Error()) errors.LogError(ctx, "failed to lookup SRV record: "+err.Error())
return nil, errors.New("failed to lookup SRV record").Base(err) return nil, errors.New("failed to lookup SRV record").Base(err)
+3 -2
View File
@@ -2,6 +2,7 @@ package grpc
import ( import (
"context" "context"
gonet "net"
"sync" "sync"
"time" "time"
@@ -98,7 +99,7 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in
}, },
MinConnectTimeout: 5 * time.Second, MinConnectTimeout: 5 * time.Second,
}), }),
grpc.WithContextDialer(func(gctx context.Context, s string) (net.Conn, error) { grpc.WithContextDialer(func(gctx context.Context, s string) (gonet.Conn, error) {
select { select {
case <-gctx.Done(): case <-gctx.Done():
return nil, gctx.Err() return nil, gctx.Err()
@@ -179,7 +180,7 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in
} }
conn, err := grpc.Dial( conn, err := grpc.Dial(
net.JoinHostPort(grpcDestHost, dest.Port.String()), gonet.JoinHostPort(grpcDestHost, dest.Port.String()),
dialOptions..., dialOptions...,
) )
globalDialerMap[dialerConf{dest, streamSettings}] = conn globalDialerMap[dialerConf{dest, streamSettings}] = conn
+3 -2
View File
@@ -3,10 +3,11 @@ package encoding
import ( import (
"context" "context"
"io" "io"
"net"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net" xnet "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/signal/done" "github.com/xtls/xray-core/common/signal/done"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
@@ -54,7 +55,7 @@ func NewHunkConn(hc HunkConn, cancel context.CancelFunc) net.Conn {
if ok { if ok {
header := md.Get("x-real-ip") header := md.Get("x-real-ip")
if len(header) > 0 { if len(header) > 0 {
realip := net.ParseAddress(header[0]) realip := xnet.ParseAddress(header[0])
if realip.Family().IsIP() { if realip.Family().IsIP() {
rAddr = &net.TCPAddr{ rAddr = &net.TCPAddr{
IP: realip.IP(), IP: realip.IP(),
+1 -13
View File
@@ -20,7 +20,6 @@ type server struct {
config *Config config *Config
addConn internet.ConnHandler addConn internet.ConnHandler
innnerListener net.Listener innnerListener net.Listener
socketSettings *internet.SocketConfig
} }
func (s *server) Close() error { func (s *server) Close() error {
@@ -71,17 +70,7 @@ func (s *server) Handle(conn net.Conn) (stat.Connection, error) {
return nil, err return nil, err
} }
var forwardedAddrs []net.Address forwardedAddrs := http_proto.ParseXForwardedFor(req.Header)
if s.socketSettings != nil && len(s.socketSettings.TrustedXForwardedFor) > 0 {
for _, key := range s.socketSettings.TrustedXForwardedFor {
if len(req.Header.Values(key)) > 0 {
forwardedAddrs = http_proto.ParseXForwardedFor(req.Header)
break
}
}
} else {
forwardedAddrs = http_proto.ParseXForwardedFor(req.Header)
}
remoteAddr := conn.RemoteAddr() remoteAddr := conn.RemoteAddr()
if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() { if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() {
remoteAddr = &net.TCPAddr{ remoteAddr = &net.TCPAddr{
@@ -152,7 +141,6 @@ func ListenHTTPUpgrade(ctx context.Context, address net.Address, port net.Port,
config: transportConfiguration, config: transportConfiguration,
addConn: addConn, addConn: addConn,
innnerListener: listener, innnerListener: listener,
socketSettings: streamSettings.SocketSettings,
} }
go serverInstance.keepAccepting() go serverInstance.keepAccepting()
return serverInstance, nil return serverInstance, nil
+3 -2
View File
@@ -2,6 +2,7 @@ package internet
import ( import (
"context" "context"
gonet "net"
"os" "os"
"runtime" "runtime"
"strconv" "strconv"
@@ -134,7 +135,7 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf
} }
if config.Interface != "" { if config.Interface != "" {
iface, err := net.InterfaceByName(config.Interface) iface, err := gonet.InterfaceByName(config.Interface)
if err != nil { if err != nil {
return errors.New("failed to get interface ", config.Interface).Base(err) return errors.New("failed to get interface ", config.Interface).Base(err)
@@ -225,7 +226,7 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig)
} }
if config.Interface != "" { if config.Interface != "" {
iface, err := net.InterfaceByName(config.Interface) iface, err := gonet.InterfaceByName(config.Interface)
if err != nil { if err != nil {
return errors.New("failed to get interface ", config.Interface).Base(err) return errors.New("failed to get interface ", config.Interface).Base(err)
@@ -3,9 +3,9 @@ package splithttp
import ( import (
"context" "context"
"io" "io"
gonet "net"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/transport/internet/browser_dialer" "github.com/xtls/xray-core/transport/internet/browser_dialer"
"github.com/xtls/xray-core/transport/internet/websocket" "github.com/xtls/xray-core/transport/internet/websocket"
) )
@@ -19,13 +19,13 @@ func (c *BrowserDialerClient) IsClosed() bool {
panic("not implemented yet") panic("not implemented yet")
} }
func (c *BrowserDialerClient) OpenStream(ctx context.Context, url string, body io.Reader, uploadOnly bool) (io.ReadCloser, net.Addr, net.Addr, error) { func (c *BrowserDialerClient) OpenStream(ctx context.Context, url string, body io.Reader, uploadOnly bool) (io.ReadCloser, gonet.Addr, gonet.Addr, error) {
if body != nil { if body != nil {
return nil, nil, nil, errors.New("bidirectional streaming for browser dialer not implemented yet") return nil, nil, nil, errors.New("bidirectional streaming for browser dialer not implemented yet")
} }
conn, err := browser_dialer.DialGet(url, c.transportConfig.GetRequestHeader(url)) conn, err := browser_dialer.DialGet(url, c.transportConfig.GetRequestHeader(url))
dummyAddr := &net.IPAddr{} dummyAddr := &gonet.IPAddr{}
if err != nil { if err != nil {
return nil, dummyAddr, dummyAddr, err return nil, dummyAddr, dummyAddr, err
} }
+2 -1
View File
@@ -5,6 +5,7 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
gonet "net"
"net/http" "net/http"
"net/http/httptrace" "net/http/httptrace"
"sync" "sync"
@@ -41,7 +42,7 @@ func (c *DefaultDialerClient) IsClosed() bool {
return c.closed return c.closed
} }
func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, body io.Reader, uploadOnly bool) (wrc io.ReadCloser, remoteAddr, localAddr net.Addr, err error) { func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, body io.Reader, uploadOnly bool) (wrc io.ReadCloser, remoteAddr, localAddr gonet.Addr, err error) {
// this is done when the TCP/UDP connection to the server was established, // this is done when the TCP/UDP connection to the server was established,
// and we can unblock the Dial function and print correct net addresses in // and we can unblock the Dial function and print correct net addresses in
// logs // logs
+1 -13
View File
@@ -34,7 +34,6 @@ type requestHandler struct {
sessionMu *sync.Mutex sessionMu *sync.Mutex
sessions sync.Map sessions sync.Map
localAddr net.Addr localAddr net.Addr
socketSettings *internet.SocketConfig
} }
type httpSession struct { type httpSession struct {
@@ -140,17 +139,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
return return
} }
var forwardedAddrs []net.Address forwardedAddrs := http_proto.ParseXForwardedFor(request.Header)
if h.socketSettings != nil && len(h.socketSettings.TrustedXForwardedFor) > 0 {
for _, key := range h.socketSettings.TrustedXForwardedFor {
if len(request.Header.Values(key)) > 0 {
forwardedAddrs = http_proto.ParseXForwardedFor(request.Header)
break
}
}
} else {
forwardedAddrs = http_proto.ParseXForwardedFor(request.Header)
}
var remoteAddr net.Addr var remoteAddr net.Addr
var err error var err error
remoteAddr, err = net.ResolveTCPAddr("tcp", request.RemoteAddr) remoteAddr, err = net.ResolveTCPAddr("tcp", request.RemoteAddr)
@@ -373,7 +362,6 @@ func ListenXH(ctx context.Context, address net.Address, port net.Port, streamSet
ln: l, ln: l,
sessionMu: &sync.Mutex{}, sessionMu: &sync.Mutex{},
sessions: sync.Map{}, sessions: sync.Map{},
socketSettings: streamSettings.SocketSettings,
} }
tlsConfig := getTLSConfig(streamSettings) tlsConfig := getTLSConfig(streamSettings)
l.isH3 = len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "h3" l.isH3 = len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "h3"
+2 -1
View File
@@ -3,6 +3,7 @@ package internet
import ( import (
"context" "context"
"math/rand" "math/rand"
gonet "net"
"syscall" "syscall"
"time" "time"
@@ -88,7 +89,7 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne
}, nil }, nil
} }
// Chrome defaults // Chrome defaults
keepAliveConfig := net.KeepAliveConfig{ keepAliveConfig := gonet.KeepAliveConfig{
Enable: true, Enable: true,
Idle: 45 * time.Second, Idle: 45 * time.Second,
Interval: 45 * time.Second, Interval: 45 * time.Second,
+2 -1
View File
@@ -2,6 +2,7 @@ package internet
import ( import (
"context" "context"
gonet "net"
"os" "os"
"runtime" "runtime"
"strconv" "strconv"
@@ -94,7 +95,7 @@ func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *S
if sockopt.TcpKeepAliveIdle*sockopt.TcpKeepAliveInterval < 0 { if sockopt.TcpKeepAliveIdle*sockopt.TcpKeepAliveInterval < 0 {
return nil, errors.New("invalid TcpKeepAliveIdle or TcpKeepAliveInterval value: ", sockopt.TcpKeepAliveIdle, " ", sockopt.TcpKeepAliveInterval) return nil, errors.New("invalid TcpKeepAliveIdle or TcpKeepAliveInterval value: ", sockopt.TcpKeepAliveIdle, " ", sockopt.TcpKeepAliveInterval)
} }
lc.KeepAliveConfig = net.KeepAliveConfig{ lc.KeepAliveConfig = gonet.KeepAliveConfig{
Enable: false, Enable: false,
Idle: -1, Idle: -1,
Interval: -1, Interval: -1,
+2 -1
View File
@@ -5,6 +5,7 @@ import (
_ "embed" _ "embed"
"encoding/base64" "encoding/base64"
"io" "io"
gonet "net"
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
@@ -63,7 +64,7 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in
tlsConfig := tConfig.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("http/1.1")) tlsConfig := tConfig.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("http/1.1"))
dialer.TLSClientConfig = tlsConfig dialer.TLSClientConfig = tlsConfig
if fingerprint := tls.GetFingerprint(tConfig.Fingerprint); fingerprint != nil { if fingerprint := tls.GetFingerprint(tConfig.Fingerprint); fingerprint != nil {
dialer.NetDialTLSContext = func(_ context.Context, _, addr string) (net.Conn, error) { dialer.NetDialTLSContext = func(_ context.Context, _, addr string) (gonet.Conn, error) {
// Like the NetDial in the dialer // Like the NetDial in the dialer
pconn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings) pconn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
if err != nil { if err != nil {
+1 -13
View File
@@ -24,7 +24,6 @@ type requestHandler struct {
host string host string
path string path string
ln *Listener ln *Listener
socketSettings *internet.SocketConfig
} }
var replacer = strings.NewReplacer("+", "-", "/", "_", "=", "") var replacer = strings.NewReplacer("+", "-", "/", "_", "=", "")
@@ -65,17 +64,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
return return
} }
var forwardedAddrs []net.Address forwardedAddrs := http_proto.ParseXForwardedFor(request.Header)
if h.socketSettings != nil && len(h.socketSettings.TrustedXForwardedFor) > 0 {
for _, key := range h.socketSettings.TrustedXForwardedFor {
if len(request.Header.Values(key)) > 0 {
forwardedAddrs = http_proto.ParseXForwardedFor(request.Header)
break
}
}
} else {
forwardedAddrs = http_proto.ParseXForwardedFor(request.Header)
}
remoteAddr := conn.RemoteAddr() remoteAddr := conn.RemoteAddr()
if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() { if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() {
remoteAddr = &net.TCPAddr{ remoteAddr = &net.TCPAddr{
@@ -146,7 +135,6 @@ func ListenWS(ctx context.Context, address net.Address, port net.Port, streamSet
host: wsSettings.Host, host: wsSettings.Host,
path: wsSettings.GetNormalizedPath(), path: wsSettings.GetNormalizedPath(),
ln: l, ln: l,
socketSettings: streamSettings.SocketSettings,
}, },
ReadHeaderTimeout: time.Second * 4, ReadHeaderTimeout: time.Second * 4,
MaxHeaderBytes: 8192, MaxHeaderBytes: 8192,
+22 -13
View File
@@ -3,6 +3,7 @@ package pipe
import ( import (
"errors" "errors"
"io" "io"
"runtime"
"sync" "sync"
"time" "time"
@@ -135,12 +136,13 @@ func (p *pipe) writeMultiBufferInternal(mb buf.MultiBuffer) error {
if p.data == nil { if p.data == nil {
p.data = mb p.data = mb
} else {
p.data, _ = buf.MergeMulti(p.data, mb)
}
return nil return nil
} }
p.data, _ = buf.MergeMulti(p.data, mb)
return errSlowDown
}
func (p *pipe) WriteMultiBuffer(mb buf.MultiBuffer) error { func (p *pipe) WriteMultiBuffer(mb buf.MultiBuffer) error {
if mb.IsEmpty() { if mb.IsEmpty() {
return nil return nil
@@ -153,24 +155,31 @@ func (p *pipe) WriteMultiBuffer(mb buf.MultiBuffer) error {
return nil return nil
} }
if err == errBufferFull { if err == errSlowDown {
if p.option.discardOverflow { p.readSignal.Signal()
// Yield current goroutine. Hopefully the reading counterpart can pick up the payload.
runtime.Gosched()
return nil
}
if err == errBufferFull && p.option.discardOverflow {
buf.ReleaseMulti(mb) buf.ReleaseMulti(mb)
return nil return nil
} }
select {
case <-p.writeSignal.Wait():
continue
case <-p.done.Wait():
buf.ReleaseMulti(mb)
return io.ErrClosedPipe
}
}
if err != errBufferFull {
buf.ReleaseMulti(mb) buf.ReleaseMulti(mb)
p.readSignal.Signal() p.readSignal.Signal()
return err return err
} }
select {
case <-p.writeSignal.Wait():
case <-p.done.Wait():
return io.ErrClosedPipe
}
}
} }
func (p *pipe) Close() error { func (p *pipe) Close() error {