mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-23 01:56:48 +08:00
Compare commits
2 Commits
v1.5.0
...
grpc/block
Author | SHA1 | Date | |
---|---|---|---|
![]() |
dee122c5f3 | ||
![]() |
ba4aa2896d |
3
.github/build/friendly-filenames.json
vendored
3
.github/build/friendly-filenames.json
vendored
@@ -29,6 +29,5 @@
|
|||||||
"openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" },
|
"openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" },
|
||||||
"windows-386": { "friendlyName": "windows-32" },
|
"windows-386": { "friendlyName": "windows-32" },
|
||||||
"windows-amd64": { "friendlyName": "windows-64" },
|
"windows-amd64": { "friendlyName": "windows-64" },
|
||||||
"windows-arm64": { "friendlyName": "windows-arm64-v8a" },
|
|
||||||
"windows-arm7": { "friendlyName": "windows-arm32-v7a" }
|
"windows-arm7": { "friendlyName": "windows-arm32-v7a" }
|
||||||
}
|
}
|
||||||
|
11
.github/dependabot.yml
vendored
11
.github/dependabot.yml
vendored
@@ -1,11 +0,0 @@
|
|||||||
# To get started with Dependabot version updates, you'll need to specify which
|
|
||||||
# package ecosystems to update and where the package manifests are located.
|
|
||||||
# Please see the documentation for all configuration options:
|
|
||||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "gomod"
|
|
||||||
directory: "/" # Location of package manifests
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -52,9 +52,7 @@ jobs:
|
|||||||
- goos: android
|
- goos: android
|
||||||
goarch: arm64
|
goarch: arm64
|
||||||
# END Android ARM 8
|
# END Android ARM 8
|
||||||
# Windows ARM
|
# Windows ARM 7
|
||||||
- goos: windows
|
|
||||||
goarch: arm64
|
|
||||||
- goos: windows
|
- goos: windows
|
||||||
goarch: arm
|
goarch: arm
|
||||||
goarm: 7
|
goarm: 7
|
||||||
@@ -123,7 +121,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: ^1.17.2
|
go-version: ^1.16
|
||||||
|
|
||||||
- name: Get project dependencies
|
- name: Get project dependencies
|
||||||
run: go mod download
|
run: go mod download
|
||||||
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: ^1.17.2
|
go-version: ^1.16
|
||||||
- name: Checkout codebase
|
- name: Checkout codebase
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
@@ -40,16 +40,12 @@
|
|||||||
- [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray))
|
- [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray))
|
||||||
- Windows
|
- Windows
|
||||||
- [v2rayN](https://github.com/2dust/v2rayN)
|
- [v2rayN](https://github.com/2dust/v2rayN)
|
||||||
- [Qv2ray](https://github.com/Qv2ray/Qv2ray) (This project had been archived and currently inactive)
|
|
||||||
- [Netch (NetFilter & TUN/TAP)](https://github.com/NetchX/Netch)
|
- [Netch (NetFilter & TUN/TAP)](https://github.com/NetchX/Netch)
|
||||||
- Android
|
- Android
|
||||||
- [v2rayNG](https://github.com/2dust/v2rayNG)
|
- [v2rayNG](https://github.com/2dust/v2rayNG)
|
||||||
- [AnXray](https://github.com/XTLS/AnXray)
|
|
||||||
- [Kitsunebi](https://github.com/rurirei/Kitsunebi/tree/release_xtls)
|
- [Kitsunebi](https://github.com/rurirei/Kitsunebi/tree/release_xtls)
|
||||||
- iOS & macOS (with M1 chip)
|
- iOS / Mac
|
||||||
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
|
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
|
||||||
- macOS (Intel chip & M1 chip)
|
|
||||||
- [Qv2ray](https://github.com/Qv2ray/Qv2ray) (This project had been archived and currently inactive)
|
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: app/commander/config.proto
|
// source: app/commander/config.proto
|
||||||
|
|
||||||
package commander
|
package commander
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
serial "github.com/xtls/xray-core/common/serial"
|
serial "github.com/xtls/xray-core/common/serial"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
@@ -21,6 +22,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
// Config is the settings for Commander.
|
// Config is the settings for Commander.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
|
@@ -3,10 +3,9 @@ package commander
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/reflection"
|
"google.golang.org/grpc/reflection"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service is a Commander service.
|
// Service is a Commander service.
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: app/dispatcher/config.proto
|
// source: app/dispatcher/config.proto
|
||||||
|
|
||||||
package dispatcher
|
package dispatcher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -20,6 +21,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type SessionConfig struct {
|
type SessionConfig struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@@ -25,7 +25,9 @@ import (
|
|||||||
"github.com/xtls/xray-core/transport/pipe"
|
"github.com/xtls/xray-core/transport/pipe"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errSniffingTimeout = newError("timeout on sniffing")
|
var (
|
||||||
|
errSniffingTimeout = newError("timeout on sniffing")
|
||||||
|
)
|
||||||
|
|
||||||
type cachedReader struct {
|
type cachedReader struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
@@ -193,15 +195,10 @@ func shouldOverride(ctx context.Context, result SniffResult, request session.Sni
|
|||||||
if strings.HasPrefix(protocolString, p) {
|
if strings.HasPrefix(protocolString, p) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" &&
|
if fakeDNSEngine != nil && protocolString != "bittorrent" && p == "fakedns" &&
|
||||||
destination.Address.Family().IsIP() && fkr0.IsIPInIPPool(destination.Address) {
|
destination.Address.Family().IsIP() && fakeDNSEngine.GetFakeIPRange().Contains(destination.Address.IP()) {
|
||||||
newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx))
|
newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx))
|
||||||
return true
|
return true
|
||||||
}
|
|
||||||
if resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok {
|
|
||||||
if resultSubset.IsProtoSubsetOf(p) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,14 +309,23 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (Sni
|
|||||||
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
|
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
|
||||||
var handler outbound.Handler
|
var handler outbound.Handler
|
||||||
|
|
||||||
if d.router != nil {
|
skipRoutePick := false
|
||||||
if route, err := d.router.PickRoute(routing_session.AsRoutingContext(ctx)); err == nil {
|
if content := session.ContentFromContext(ctx); content != nil {
|
||||||
tag := route.GetOutboundTag()
|
skipRoutePick = content.SkipRoutePick
|
||||||
if h := d.ohm.GetHandler(tag); h != nil {
|
}
|
||||||
newError("taking detour [", tag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
|
routingLink := routing_session.AsRoutingContext(ctx)
|
||||||
|
inTag := routingLink.GetInboundTag()
|
||||||
|
isPickRoute := false
|
||||||
|
if d.router != nil && !skipRoutePick {
|
||||||
|
if route, err := d.router.PickRoute(routingLink); err == nil {
|
||||||
|
outTag := route.GetOutboundTag()
|
||||||
|
isPickRoute = true
|
||||||
|
if h := d.ohm.GetHandler(outTag); h != nil {
|
||||||
|
newError("taking detour [", outTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
|
||||||
handler = h
|
handler = h
|
||||||
} else {
|
} else {
|
||||||
newError("non existing outTag: ", tag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
newError("non existing outTag: ", outTag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newError("default route for ", destination).WriteToLog(session.ExportIDToError(ctx))
|
newError("default route for ", destination).WriteToLog(session.ExportIDToError(ctx))
|
||||||
@@ -339,7 +345,19 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
|||||||
|
|
||||||
if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil {
|
if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil {
|
||||||
if tag := handler.Tag(); tag != "" {
|
if tag := handler.Tag(); tag != "" {
|
||||||
accessMessage.Detour = tag
|
if isPickRoute {
|
||||||
|
if inTag != "" {
|
||||||
|
accessMessage.Detour = inTag + " -> " + tag
|
||||||
|
} else {
|
||||||
|
accessMessage.Detour = tag
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if inTag != "" {
|
||||||
|
accessMessage.Detour = inTag + " >> " + tag
|
||||||
|
} else {
|
||||||
|
accessMessage.Detour = tag
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log.Record(accessMessage)
|
log.Record(accessMessage)
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,6 @@ package dispatcher
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
@@ -33,15 +32,6 @@ func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error)
|
|||||||
return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
|
return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ipAddressInRangeValueI := ctx.Value(ipAddressInRange); ipAddressInRangeValueI != nil {
|
|
||||||
ipAddressInRangeValue := ipAddressInRangeValueI.(*ipAddressInRangeOpt)
|
|
||||||
if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok {
|
|
||||||
inPool := fkr0.IsIPInIPPool(Target.Address)
|
|
||||||
ipAddressInRangeValue.addressInRange = &inPool
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, common.ErrNoClue
|
return nil, common.ErrNoClue
|
||||||
}, metadataSniffer: true}, nil
|
}, metadataSniffer: true}, nil
|
||||||
}
|
}
|
||||||
@@ -57,61 +47,3 @@ func (fakeDNSSniffResult) Protocol() string {
|
|||||||
func (f fakeDNSSniffResult) Domain() string {
|
func (f fakeDNSSniffResult) Domain() string {
|
||||||
return f.domainName
|
return f.domainName
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakeDNSExtraOpts int
|
|
||||||
|
|
||||||
const ipAddressInRange fakeDNSExtraOpts = 1
|
|
||||||
|
|
||||||
type ipAddressInRangeOpt struct {
|
|
||||||
addressInRange *bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type DNSThenOthersSniffResult struct {
|
|
||||||
domainName string
|
|
||||||
protocolOriginalName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f DNSThenOthersSniffResult) IsProtoSubsetOf(protocolName string) bool {
|
|
||||||
return strings.HasPrefix(protocolName, f.protocolOriginalName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (DNSThenOthersSniffResult) Protocol() string {
|
|
||||||
return "fakedns+others"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f DNSThenOthersSniffResult) Domain() string {
|
|
||||||
return f.domainName
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFakeDNSThenOthers(ctx context.Context, fakeDNSSniffer protocolSnifferWithMetadata, others []protocolSnifferWithMetadata) (
|
|
||||||
protocolSnifferWithMetadata, error) { // nolint: unparam
|
|
||||||
// ctx may be used in the future
|
|
||||||
_ = ctx
|
|
||||||
return protocolSnifferWithMetadata{
|
|
||||||
protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
|
|
||||||
ipAddressInRangeValue := &ipAddressInRangeOpt{}
|
|
||||||
ctx = context.WithValue(ctx, ipAddressInRange, ipAddressInRangeValue)
|
|
||||||
result, err := fakeDNSSniffer.protocolSniffer(ctx, bytes)
|
|
||||||
if err == nil {
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
if ipAddressInRangeValue.addressInRange != nil {
|
|
||||||
if *ipAddressInRangeValue.addressInRange {
|
|
||||||
for _, v := range others {
|
|
||||||
if v.metadataSniffer || bytes != nil {
|
|
||||||
if result, err := v.protocolSniffer(ctx, bytes); err == nil {
|
|
||||||
return DNSThenOthersSniffResult{domainName: result.Domain(), protocolOriginalName: result.Protocol()}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, common.ErrNoClue
|
|
||||||
}
|
|
||||||
newError("ip address not in fake dns range, return as is").AtDebug().WriteToLog()
|
|
||||||
return nil, common.ErrNoClue
|
|
||||||
}
|
|
||||||
newError("fake dns sniffer did not set address in range option, assume false.").AtWarning().WriteToLog()
|
|
||||||
return nil, common.ErrNoClue
|
|
||||||
},
|
|
||||||
metadataSniffer: false,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
@@ -37,12 +37,7 @@ func NewSniffer(ctx context.Context) *Sniffer {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
if sniffer, err := newFakeDNSSniffer(ctx); err == nil {
|
if sniffer, err := newFakeDNSSniffer(ctx); err == nil {
|
||||||
others := ret.sniffer
|
|
||||||
ret.sniffer = append(ret.sniffer, sniffer)
|
ret.sniffer = append(ret.sniffer, sniffer)
|
||||||
fakeDNSThenOthers, err := newFakeDNSThenOthers(ctx, sniffer, others)
|
|
||||||
if err == nil {
|
|
||||||
ret.sniffer = append([]protocolSnifferWithMetadata{fakeDNSThenOthers}, ret.sniffer...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
@@ -126,7 +121,3 @@ func (c compositeResult) ProtocolForDomainResult() string {
|
|||||||
type SnifferResultComposite interface {
|
type SnifferResultComposite interface {
|
||||||
ProtocolForDomainResult() string
|
ProtocolForDomainResult() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type SnifferIsProtoSubsetOf interface {
|
|
||||||
IsProtoSubsetOf(protocolName string) bool
|
|
||||||
}
|
|
||||||
|
@@ -1,63 +0,0 @@
|
|||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
|
||||||
"github.com/xtls/xray-core/common/strmatcher"
|
|
||||||
"github.com/xtls/xray-core/common/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
var typeMap = map[DomainMatchingType]strmatcher.Type{
|
|
||||||
DomainMatchingType_Full: strmatcher.Full,
|
|
||||||
DomainMatchingType_Subdomain: strmatcher.Domain,
|
|
||||||
DomainMatchingType_Keyword: strmatcher.Substr,
|
|
||||||
DomainMatchingType_Regex: strmatcher.Regex,
|
|
||||||
}
|
|
||||||
|
|
||||||
// References:
|
|
||||||
// https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml
|
|
||||||
// https://unix.stackexchange.com/questions/92441/whats-the-difference-between-local-home-and-lan
|
|
||||||
var localTLDsAndDotlessDomains = []*NameServer_PriorityDomain{
|
|
||||||
{Type: DomainMatchingType_Regex, Domain: "^[^.]+$"}, // This will only match domains without any dot
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "local"},
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "localdomain"},
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "localhost"},
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "lan"},
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "home.arpa"},
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "example"},
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "invalid"},
|
|
||||||
{Type: DomainMatchingType_Subdomain, Domain: "test"},
|
|
||||||
}
|
|
||||||
|
|
||||||
var localTLDsAndDotlessDomainsRule = &NameServer_OriginalRule{
|
|
||||||
Rule: "geosite:private",
|
|
||||||
Size: uint32(len(localTLDsAndDotlessDomains)),
|
|
||||||
}
|
|
||||||
|
|
||||||
func toStrMatcher(t DomainMatchingType, domain string) (strmatcher.Matcher, error) {
|
|
||||||
strMType, f := typeMap[t]
|
|
||||||
if !f {
|
|
||||||
return nil, newError("unknown mapping type", t).AtWarning()
|
|
||||||
}
|
|
||||||
matcher, err := strMType.New(domain)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to create str matcher").Base(err)
|
|
||||||
}
|
|
||||||
return matcher, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func toNetIP(addrs []net.Address) ([]net.IP, error) {
|
|
||||||
ips := make([]net.IP, 0, len(addrs))
|
|
||||||
for _, addr := range addrs {
|
|
||||||
if addr.Family().IsIP() {
|
|
||||||
ips = append(ips, addr.IP())
|
|
||||||
} else {
|
|
||||||
return nil, newError("Failed to convert address", addr, "to Net IP.").AtWarning()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ips, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateRandomTag() string {
|
|
||||||
id := uuid.New()
|
|
||||||
return "xray.system." + id.String()
|
|
||||||
}
|
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: app/dns/config.proto
|
// source: app/dns/config.proto
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
router "github.com/xtls/xray-core/app/router"
|
router "github.com/xtls/xray-core/app/router"
|
||||||
net "github.com/xtls/xray-core/common/net"
|
net "github.com/xtls/xray-core/common/net"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
@@ -22,6 +23,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type DomainMatchingType int32
|
type DomainMatchingType int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -74,63 +79,12 @@ func (DomainMatchingType) EnumDescriptor() ([]byte, []int) {
|
|||||||
return file_app_dns_config_proto_rawDescGZIP(), []int{0}
|
return file_app_dns_config_proto_rawDescGZIP(), []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryStrategy int32
|
|
||||||
|
|
||||||
const (
|
|
||||||
QueryStrategy_USE_IP QueryStrategy = 0
|
|
||||||
QueryStrategy_USE_IP4 QueryStrategy = 1
|
|
||||||
QueryStrategy_USE_IP6 QueryStrategy = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
// Enum value maps for QueryStrategy.
|
|
||||||
var (
|
|
||||||
QueryStrategy_name = map[int32]string{
|
|
||||||
0: "USE_IP",
|
|
||||||
1: "USE_IP4",
|
|
||||||
2: "USE_IP6",
|
|
||||||
}
|
|
||||||
QueryStrategy_value = map[string]int32{
|
|
||||||
"USE_IP": 0,
|
|
||||||
"USE_IP4": 1,
|
|
||||||
"USE_IP6": 2,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func (x QueryStrategy) Enum() *QueryStrategy {
|
|
||||||
p := new(QueryStrategy)
|
|
||||||
*p = x
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x QueryStrategy) String() string {
|
|
||||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (QueryStrategy) Descriptor() protoreflect.EnumDescriptor {
|
|
||||||
return file_app_dns_config_proto_enumTypes[1].Descriptor()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (QueryStrategy) Type() protoreflect.EnumType {
|
|
||||||
return &file_app_dns_config_proto_enumTypes[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x QueryStrategy) Number() protoreflect.EnumNumber {
|
|
||||||
return protoreflect.EnumNumber(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use QueryStrategy.Descriptor instead.
|
|
||||||
func (QueryStrategy) EnumDescriptor() ([]byte, []int) {
|
|
||||||
return file_app_dns_config_proto_rawDescGZIP(), []int{1}
|
|
||||||
}
|
|
||||||
|
|
||||||
type NameServer struct {
|
type NameServer struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
Address *net.Endpoint `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
Address *net.Endpoint `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
||||||
ClientIp []byte `protobuf:"bytes,5,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"`
|
|
||||||
SkipFallback bool `protobuf:"varint,6,opt,name=skipFallback,proto3" json:"skipFallback,omitempty"`
|
|
||||||
PrioritizedDomain []*NameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"`
|
PrioritizedDomain []*NameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"`
|
||||||
Geoip []*router.GeoIP `protobuf:"bytes,3,rep,name=geoip,proto3" json:"geoip,omitempty"`
|
Geoip []*router.GeoIP `protobuf:"bytes,3,rep,name=geoip,proto3" json:"geoip,omitempty"`
|
||||||
OriginalRules []*NameServer_OriginalRule `protobuf:"bytes,4,rep,name=original_rules,json=originalRules,proto3" json:"original_rules,omitempty"`
|
OriginalRules []*NameServer_OriginalRule `protobuf:"bytes,4,rep,name=original_rules,json=originalRules,proto3" json:"original_rules,omitempty"`
|
||||||
@@ -175,20 +129,6 @@ func (x *NameServer) GetAddress() *net.Endpoint {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *NameServer) GetClientIp() []byte {
|
|
||||||
if x != nil {
|
|
||||||
return x.ClientIp
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *NameServer) GetSkipFallback() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.SkipFallback
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *NameServer) GetPrioritizedDomain() []*NameServer_PriorityDomain {
|
func (x *NameServer) GetPrioritizedDomain() []*NameServer_PriorityDomain {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.PrioritizedDomain
|
return x.PrioritizedDomain
|
||||||
@@ -234,11 +174,6 @@ type Config struct {
|
|||||||
StaticHosts []*Config_HostMapping `protobuf:"bytes,4,rep,name=static_hosts,json=staticHosts,proto3" json:"static_hosts,omitempty"`
|
StaticHosts []*Config_HostMapping `protobuf:"bytes,4,rep,name=static_hosts,json=staticHosts,proto3" json:"static_hosts,omitempty"`
|
||||||
// Tag is the inbound tag of DNS client.
|
// Tag is the inbound tag of DNS client.
|
||||||
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 bool `protobuf:"varint,8,opt,name=disableCache,proto3" json:"disableCache,omitempty"`
|
|
||||||
QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"`
|
|
||||||
DisableFallback bool `protobuf:"varint,10,opt,name=disableFallback,proto3" json:"disableFallback,omitempty"`
|
|
||||||
DisableFallbackIfMatch bool `protobuf:"varint,11,opt,name=disableFallbackIfMatch,proto3" json:"disableFallbackIfMatch,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
@@ -317,34 +252,6 @@ func (x *Config) GetTag() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetDisableCache() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.DisableCache
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) GetQueryStrategy() QueryStrategy {
|
|
||||||
if x != nil {
|
|
||||||
return x.QueryStrategy
|
|
||||||
}
|
|
||||||
return QueryStrategy_USE_IP
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) GetDisableFallback() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.DisableFallback
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) GetDisableFallbackIfMatch() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.DisableFallbackIfMatch
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type NameServer_PriorityDomain struct {
|
type NameServer_PriorityDomain struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -464,7 +371,8 @@ type Config_HostMapping struct {
|
|||||||
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
|
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
|
||||||
Ip [][]byte `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"`
|
Ip [][]byte `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"`
|
||||||
// ProxiedDomain indicates the mapped domain has the same IP address on this
|
// ProxiedDomain indicates the mapped domain has the same IP address on this
|
||||||
// domain. Xray will use this domain for IP queries.
|
// domain. Xray will use this domain for IP queries. This field is only
|
||||||
|
// effective if ip is empty.
|
||||||
ProxiedDomain string `protobuf:"bytes,4,opt,name=proxied_domain,json=proxiedDomain,proto3" json:"proxied_domain,omitempty"`
|
ProxiedDomain string `protobuf:"bytes,4,opt,name=proxied_domain,json=proxiedDomain,proto3" json:"proxied_domain,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,98 +446,77 @@ var file_app_dns_config_proto_rawDesc = []byte{
|
|||||||
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69,
|
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69,
|
||||||
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70,
|
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70,
|
||||||
0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
|
0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xee, 0x03, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65,
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xad, 0x03, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65,
|
||||||
0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18,
|
0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
||||||
0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69,
|
0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x56, 0x0a, 0x12, 0x70, 0x72, 0x69,
|
||||||
0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c,
|
0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18,
|
||||||
0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x46, 0x61,
|
0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
||||||
0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x6b,
|
0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
|
||||||
0x69, 0x70, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x56, 0x0a, 0x12, 0x70, 0x72,
|
0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x11,
|
||||||
0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
||||||
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
0x6e, 0x12, 0x2c, 0x0a, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
|
||||||
0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
|
0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
|
||||||
0x2e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52,
|
0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x12,
|
||||||
0x11, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61,
|
0x4c, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65,
|
||||||
0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28,
|
0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
|
||||||
0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75,
|
|
||||||
0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70,
|
|
||||||
0x12, 0x4c, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x75, 0x6c,
|
|
||||||
0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
|
||||||
0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76,
|
|
||||||
0x65, 0x72, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52,
|
|
||||||
0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x1a, 0x5e,
|
|
||||||
0x0a, 0x0e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
|
||||||
0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20,
|
|
||||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f,
|
|
||||||
0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65,
|
|
||||||
0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x36,
|
|
||||||
0x0a, 0x0c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12,
|
|
||||||
0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75,
|
|
||||||
0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
|
|
||||||
0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xef, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
|
||||||
0x67, 0x12, 0x3f, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73,
|
|
||||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
|
|
||||||
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e,
|
|
||||||
0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65,
|
|
||||||
0x72, 0x73, 0x12, 0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65,
|
|
||||||
0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
|
|
||||||
0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65,
|
0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65,
|
||||||
0x72, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x39, 0x0a,
|
0x72, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d,
|
||||||
0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78,
|
0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x1a, 0x5e, 0x0a,
|
||||||
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66,
|
0x0e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12,
|
||||||
0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x02, 0x18,
|
0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e,
|
||||||
0x01, 0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65,
|
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d,
|
||||||
0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69,
|
0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52,
|
||||||
0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f,
|
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18,
|
||||||
0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72,
|
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x36, 0x0a,
|
||||||
|
0x0c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a,
|
||||||
|
0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c,
|
||||||
|
0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
||||||
|
0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x9f, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||||
|
0x12, 0x3f, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18,
|
||||||
|
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
|
0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
||||||
|
0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
|
||||||
|
0x73, 0x12, 0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
|
||||||
|
0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
||||||
|
0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
|
||||||
|
0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x05,
|
||||||
|
0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72,
|
||||||
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x73,
|
0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x02, 0x18, 0x01,
|
||||||
0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61,
|
0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
||||||
0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x22, 0x0a, 0x0c,
|
0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65,
|
||||||
0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x08, 0x20, 0x01,
|
0x6e, 0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x68,
|
||||||
0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65,
|
0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61,
|
||||||
0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65,
|
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||||
0x67, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
0x2e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x73, 0x74,
|
||||||
0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72,
|
0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67,
|
||||||
0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61,
|
0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x1a, 0x55, 0x0a, 0x0a, 0x48,
|
||||||
0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46,
|
0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
|
||||||
0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76,
|
||||||
0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x36,
|
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61,
|
||||||
0x0a, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63,
|
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f,
|
||||||
0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16,
|
0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
|
||||||
0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49,
|
0x38, 0x01, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69,
|
||||||
0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x55, 0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45,
|
0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
|
||||||
0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e,
|
||||||
0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
|
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79,
|
||||||
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61,
|
||||||
0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61,
|
0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||||
0x69, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x92, 0x01,
|
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70,
|
||||||
0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a,
|
0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61,
|
||||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72,
|
0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65,
|
||||||
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
||||||
0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74,
|
0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a,
|
||||||
0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20,
|
0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69,
|
0x6d, 0x61, 0x69, 0x6e, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72,
|
||||||
0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70,
|
0x64, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x42, 0x46,
|
||||||
0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20,
|
0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61,
|
0x6e, 0x73, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
0x69, 0x6e, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61,
|
0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f,
|
||||||
0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08,
|
0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41,
|
||||||
0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64,
|
0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f,
|
|
||||||
0x72, 0x64, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a,
|
|
||||||
0x35, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
|
||||||
0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07,
|
|
||||||
0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45,
|
|
||||||
0x5f, 0x49, 0x50, 0x36, 0x10, 0x02, 0x42, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
|
|
||||||
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69,
|
|
||||||
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72,
|
|
||||||
0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0xaa,
|
|
||||||
0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06,
|
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -644,39 +531,37 @@ func file_app_dns_config_proto_rawDescGZIP() []byte {
|
|||||||
return file_app_dns_config_proto_rawDescData
|
return file_app_dns_config_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_app_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
var file_app_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
var file_app_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
var file_app_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||||
var file_app_dns_config_proto_goTypes = []interface{}{
|
var file_app_dns_config_proto_goTypes = []interface{}{
|
||||||
(DomainMatchingType)(0), // 0: xray.app.dns.DomainMatchingType
|
(DomainMatchingType)(0), // 0: xray.app.dns.DomainMatchingType
|
||||||
(QueryStrategy)(0), // 1: xray.app.dns.QueryStrategy
|
(*NameServer)(nil), // 1: xray.app.dns.NameServer
|
||||||
(*NameServer)(nil), // 2: xray.app.dns.NameServer
|
(*Config)(nil), // 2: xray.app.dns.Config
|
||||||
(*Config)(nil), // 3: xray.app.dns.Config
|
(*NameServer_PriorityDomain)(nil), // 3: xray.app.dns.NameServer.PriorityDomain
|
||||||
(*NameServer_PriorityDomain)(nil), // 4: xray.app.dns.NameServer.PriorityDomain
|
(*NameServer_OriginalRule)(nil), // 4: xray.app.dns.NameServer.OriginalRule
|
||||||
(*NameServer_OriginalRule)(nil), // 5: xray.app.dns.NameServer.OriginalRule
|
nil, // 5: xray.app.dns.Config.HostsEntry
|
||||||
nil, // 6: xray.app.dns.Config.HostsEntry
|
(*Config_HostMapping)(nil), // 6: xray.app.dns.Config.HostMapping
|
||||||
(*Config_HostMapping)(nil), // 7: xray.app.dns.Config.HostMapping
|
(*net.Endpoint)(nil), // 7: xray.common.net.Endpoint
|
||||||
(*net.Endpoint)(nil), // 8: xray.common.net.Endpoint
|
(*router.GeoIP)(nil), // 8: xray.app.router.GeoIP
|
||||||
(*router.GeoIP)(nil), // 9: xray.app.router.GeoIP
|
(*net.IPOrDomain)(nil), // 9: xray.common.net.IPOrDomain
|
||||||
(*net.IPOrDomain)(nil), // 10: xray.common.net.IPOrDomain
|
|
||||||
}
|
}
|
||||||
var file_app_dns_config_proto_depIdxs = []int32{
|
var file_app_dns_config_proto_depIdxs = []int32{
|
||||||
8, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint
|
7, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint
|
||||||
4, // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.app.dns.NameServer.PriorityDomain
|
3, // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.app.dns.NameServer.PriorityDomain
|
||||||
9, // 2: xray.app.dns.NameServer.geoip:type_name -> xray.app.router.GeoIP
|
8, // 2: xray.app.dns.NameServer.geoip:type_name -> xray.app.router.GeoIP
|
||||||
5, // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule
|
4, // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule
|
||||||
8, // 4: xray.app.dns.Config.NameServers:type_name -> xray.common.net.Endpoint
|
7, // 4: xray.app.dns.Config.NameServers:type_name -> xray.common.net.Endpoint
|
||||||
2, // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer
|
1, // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer
|
||||||
6, // 6: xray.app.dns.Config.Hosts:type_name -> xray.app.dns.Config.HostsEntry
|
5, // 6: xray.app.dns.Config.Hosts:type_name -> xray.app.dns.Config.HostsEntry
|
||||||
7, // 7: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping
|
6, // 7: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping
|
||||||
1, // 8: xray.app.dns.Config.query_strategy:type_name -> xray.app.dns.QueryStrategy
|
0, // 8: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType
|
||||||
0, // 9: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType
|
9, // 9: xray.app.dns.Config.HostsEntry.value:type_name -> xray.common.net.IPOrDomain
|
||||||
10, // 10: xray.app.dns.Config.HostsEntry.value:type_name -> xray.common.net.IPOrDomain
|
0, // 10: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType
|
||||||
0, // 11: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType
|
11, // [11:11] is the sub-list for method output_type
|
||||||
12, // [12:12] is the sub-list for method output_type
|
11, // [11:11] is the sub-list for method input_type
|
||||||
12, // [12:12] is the sub-list for method input_type
|
11, // [11:11] is the sub-list for extension type_name
|
||||||
12, // [12:12] is the sub-list for extension type_name
|
11, // [11:11] is the sub-list for extension extendee
|
||||||
12, // [12:12] is the sub-list for extension extendee
|
0, // [0:11] is the sub-list for field type_name
|
||||||
0, // [0:12] is the sub-list for field type_name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_app_dns_config_proto_init() }
|
func init() { file_app_dns_config_proto_init() }
|
||||||
@@ -751,7 +636,7 @@ func file_app_dns_config_proto_init() {
|
|||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_app_dns_config_proto_rawDesc,
|
RawDescriptor: file_app_dns_config_proto_rawDesc,
|
||||||
NumEnums: 2,
|
NumEnums: 1,
|
||||||
NumMessages: 6,
|
NumMessages: 6,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
|
@@ -12,8 +12,6 @@ import "app/router/config.proto";
|
|||||||
|
|
||||||
message NameServer {
|
message NameServer {
|
||||||
xray.common.net.Endpoint address = 1;
|
xray.common.net.Endpoint address = 1;
|
||||||
bytes client_ip = 5;
|
|
||||||
bool skipFallback = 6;
|
|
||||||
|
|
||||||
message PriorityDomain {
|
message PriorityDomain {
|
||||||
DomainMatchingType type = 1;
|
DomainMatchingType type = 1;
|
||||||
@@ -37,12 +35,6 @@ enum DomainMatchingType {
|
|||||||
Regex = 3;
|
Regex = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum QueryStrategy {
|
|
||||||
USE_IP = 0;
|
|
||||||
USE_IP4 = 1;
|
|
||||||
USE_IP6 = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Config {
|
message Config {
|
||||||
// Nameservers used by this DNS. Only traditional UDP servers are support at
|
// Nameservers used by this DNS. Only traditional UDP servers are support at
|
||||||
// the moment. A special value 'localhost' as a domain address can be set to
|
// the moment. A special value 'localhost' as a domain address can be set to
|
||||||
@@ -67,7 +59,8 @@ message Config {
|
|||||||
repeated bytes ip = 3;
|
repeated bytes ip = 3;
|
||||||
|
|
||||||
// ProxiedDomain indicates the mapped domain has the same IP address on this
|
// ProxiedDomain indicates the mapped domain has the same IP address on this
|
||||||
// domain. Xray will use this domain for IP queries.
|
// domain. Xray will use this domain for IP queries. This field is only
|
||||||
|
// effective if ip is empty.
|
||||||
string proxied_domain = 4;
|
string proxied_domain = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,12 +70,4 @@ message Config {
|
|||||||
string tag = 6;
|
string tag = 6;
|
||||||
|
|
||||||
reserved 7;
|
reserved 7;
|
||||||
|
|
||||||
// DisableCache disables DNS cache
|
|
||||||
bool disableCache = 8;
|
|
||||||
|
|
||||||
QueryStrategy query_strategy = 9;
|
|
||||||
|
|
||||||
bool disableFallback = 10;
|
|
||||||
bool disableFallbackIfMatch = 11;
|
|
||||||
}
|
}
|
||||||
|
292
app/dns/dns.go
292
app/dns/dns.go
@@ -2,295 +2,3 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/router"
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
|
||||||
"github.com/xtls/xray-core/common/session"
|
|
||||||
"github.com/xtls/xray-core/common/strmatcher"
|
|
||||||
"github.com/xtls/xray-core/features"
|
|
||||||
"github.com/xtls/xray-core/features/dns"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DNS is a DNS rely server.
|
|
||||||
type DNS struct {
|
|
||||||
sync.Mutex
|
|
||||||
tag string
|
|
||||||
disableCache bool
|
|
||||||
disableFallback bool
|
|
||||||
disableFallbackIfMatch bool
|
|
||||||
ipOption *dns.IPOption
|
|
||||||
hosts *StaticHosts
|
|
||||||
clients []*Client
|
|
||||||
ctx context.Context
|
|
||||||
domainMatcher strmatcher.IndexMatcher
|
|
||||||
matcherInfos []*DomainMatcherInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainMatcherInfo contains information attached to index returned by Server.domainMatcher
|
|
||||||
type DomainMatcherInfo struct {
|
|
||||||
clientIdx uint16
|
|
||||||
domainRuleIdx uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new DNS server with given configuration.
|
|
||||||
func New(ctx context.Context, config *Config) (*DNS, error) {
|
|
||||||
var tag string
|
|
||||||
if len(config.Tag) > 0 {
|
|
||||||
tag = config.Tag
|
|
||||||
} else {
|
|
||||||
tag = generateRandomTag()
|
|
||||||
}
|
|
||||||
|
|
||||||
var clientIP net.IP
|
|
||||||
switch len(config.ClientIp) {
|
|
||||||
case 0, net.IPv4len, net.IPv6len:
|
|
||||||
clientIP = net.IP(config.ClientIp)
|
|
||||||
default:
|
|
||||||
return nil, newError("unexpected client IP length ", len(config.ClientIp))
|
|
||||||
}
|
|
||||||
|
|
||||||
var ipOption *dns.IPOption
|
|
||||||
switch config.QueryStrategy {
|
|
||||||
case QueryStrategy_USE_IP:
|
|
||||||
ipOption = &dns.IPOption{
|
|
||||||
IPv4Enable: true,
|
|
||||||
IPv6Enable: true,
|
|
||||||
FakeEnable: false,
|
|
||||||
}
|
|
||||||
case QueryStrategy_USE_IP4:
|
|
||||||
ipOption = &dns.IPOption{
|
|
||||||
IPv4Enable: true,
|
|
||||||
IPv6Enable: false,
|
|
||||||
FakeEnable: false,
|
|
||||||
}
|
|
||||||
case QueryStrategy_USE_IP6:
|
|
||||||
ipOption = &dns.IPOption{
|
|
||||||
IPv4Enable: false,
|
|
||||||
IPv6Enable: true,
|
|
||||||
FakeEnable: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hosts, err := NewStaticHosts(config.StaticHosts, config.Hosts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to create hosts").Base(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
clients := []*Client{}
|
|
||||||
domainRuleCount := 0
|
|
||||||
for _, ns := range config.NameServer {
|
|
||||||
domainRuleCount += len(ns.PrioritizedDomain)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatcherInfos is ensured to cover the maximum index domainMatcher could return, where matcher's index starts from 1
|
|
||||||
matcherInfos := make([]*DomainMatcherInfo, domainRuleCount+1)
|
|
||||||
domainMatcher := &strmatcher.MatcherGroup{}
|
|
||||||
geoipContainer := router.GeoIPMatcherContainer{}
|
|
||||||
|
|
||||||
for _, endpoint := range config.NameServers {
|
|
||||||
features.PrintDeprecatedFeatureWarning("simple DNS server")
|
|
||||||
client, err := NewSimpleClient(ctx, endpoint, clientIP)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to create client").Base(err)
|
|
||||||
}
|
|
||||||
clients = append(clients, client)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ns := range config.NameServer {
|
|
||||||
clientIdx := len(clients)
|
|
||||||
updateDomain := func(domainRule strmatcher.Matcher, originalRuleIdx int, matcherInfos []*DomainMatcherInfo) error {
|
|
||||||
midx := domainMatcher.Add(domainRule)
|
|
||||||
matcherInfos[midx] = &DomainMatcherInfo{
|
|
||||||
clientIdx: uint16(clientIdx),
|
|
||||||
domainRuleIdx: uint16(originalRuleIdx),
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
myClientIP := clientIP
|
|
||||||
switch len(ns.ClientIp) {
|
|
||||||
case net.IPv4len, net.IPv6len:
|
|
||||||
myClientIP = net.IP(ns.ClientIp)
|
|
||||||
}
|
|
||||||
client, err := NewClient(ctx, ns, myClientIP, geoipContainer, &matcherInfos, updateDomain)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("failed to create client").Base(err)
|
|
||||||
}
|
|
||||||
clients = append(clients, client)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is no DNS client in config, add a `localhost` DNS client
|
|
||||||
if len(clients) == 0 {
|
|
||||||
clients = append(clients, NewLocalDNSClient())
|
|
||||||
}
|
|
||||||
|
|
||||||
return &DNS{
|
|
||||||
tag: tag,
|
|
||||||
hosts: hosts,
|
|
||||||
ipOption: ipOption,
|
|
||||||
clients: clients,
|
|
||||||
ctx: ctx,
|
|
||||||
domainMatcher: domainMatcher,
|
|
||||||
matcherInfos: matcherInfos,
|
|
||||||
disableCache: config.DisableCache,
|
|
||||||
disableFallback: config.DisableFallback,
|
|
||||||
disableFallbackIfMatch: config.DisableFallbackIfMatch,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type implements common.HasType.
|
|
||||||
func (*DNS) Type() interface{} {
|
|
||||||
return dns.ClientType()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start implements common.Runnable.
|
|
||||||
func (s *DNS) Start() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close implements common.Closable.
|
|
||||||
func (s *DNS) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsOwnLink implements proxy.dns.ownLinkVerifier
|
|
||||||
func (s *DNS) IsOwnLink(ctx context.Context) bool {
|
|
||||||
inbound := session.InboundFromContext(ctx)
|
|
||||||
return inbound != nil && inbound.Tag == s.tag
|
|
||||||
}
|
|
||||||
|
|
||||||
// LookupIP implements dns.Client.
|
|
||||||
func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) {
|
|
||||||
if domain == "" {
|
|
||||||
return nil, newError("empty domain name")
|
|
||||||
}
|
|
||||||
|
|
||||||
option.IPv4Enable = option.IPv4Enable && s.ipOption.IPv4Enable
|
|
||||||
option.IPv6Enable = option.IPv6Enable && s.ipOption.IPv6Enable
|
|
||||||
|
|
||||||
if !option.IPv4Enable && !option.IPv6Enable {
|
|
||||||
return nil, dns.ErrEmptyResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize the FQDN form query
|
|
||||||
if strings.HasSuffix(domain, ".") {
|
|
||||||
domain = domain[:len(domain)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Static host lookup
|
|
||||||
switch addrs := s.hosts.Lookup(domain, option); {
|
|
||||||
case addrs == nil: // Domain not recorded in static host
|
|
||||||
break
|
|
||||||
case len(addrs) == 0: // Domain recorded, but no valid IP returned (e.g. IPv4 address with only IPv6 enabled)
|
|
||||||
return nil, dns.ErrEmptyResponse
|
|
||||||
case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Domain replacement
|
|
||||||
newError("domain replaced: ", domain, " -> ", addrs[0].Domain()).WriteToLog()
|
|
||||||
domain = addrs[0].Domain()
|
|
||||||
default: // Successfully found ip records in static host
|
|
||||||
newError("returning ", len(addrs), " IP(s) for domain ", domain, " -> ", addrs).WriteToLog()
|
|
||||||
return toNetIP(addrs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name servers lookup
|
|
||||||
errs := []error{}
|
|
||||||
ctx := session.ContextWithInbound(s.ctx, &session.Inbound{Tag: s.tag})
|
|
||||||
for _, client := range s.sortClients(domain) {
|
|
||||||
if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") {
|
|
||||||
newError("skip DNS resolution for domain ", domain, " at server ", client.Name()).AtDebug().WriteToLog()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ips, err := client.QueryIP(ctx, domain, option, s.disableCache)
|
|
||||||
if len(ips) > 0 {
|
|
||||||
return ips, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
newError("failed to lookup ip for domain ", domain, " at server ", client.Name()).Base(err).WriteToLog()
|
|
||||||
errs = append(errs, err)
|
|
||||||
}
|
|
||||||
if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, newError("returning nil for domain ", domain).Base(errors.Combine(errs...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetIPOption implements ClientWithIPOption.
|
|
||||||
func (s *DNS) GetIPOption() *dns.IPOption {
|
|
||||||
return s.ipOption
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetQueryOption implements ClientWithIPOption.
|
|
||||||
func (s *DNS) SetQueryOption(isIPv4Enable, isIPv6Enable bool) {
|
|
||||||
s.ipOption.IPv4Enable = isIPv4Enable
|
|
||||||
s.ipOption.IPv6Enable = isIPv6Enable
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFakeDNSOption implements ClientWithIPOption.
|
|
||||||
func (s *DNS) SetFakeDNSOption(isFakeEnable bool) {
|
|
||||||
s.ipOption.FakeEnable = isFakeEnable
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DNS) sortClients(domain string) []*Client {
|
|
||||||
clients := make([]*Client, 0, len(s.clients))
|
|
||||||
clientUsed := make([]bool, len(s.clients))
|
|
||||||
clientNames := make([]string, 0, len(s.clients))
|
|
||||||
domainRules := []string{}
|
|
||||||
|
|
||||||
// Priority domain matching
|
|
||||||
hasMatch := false
|
|
||||||
for _, match := range s.domainMatcher.Match(domain) {
|
|
||||||
info := s.matcherInfos[match]
|
|
||||||
client := s.clients[info.clientIdx]
|
|
||||||
domainRule := client.domains[info.domainRuleIdx]
|
|
||||||
domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", domainRule, info.clientIdx))
|
|
||||||
if clientUsed[info.clientIdx] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
clientUsed[info.clientIdx] = true
|
|
||||||
clients = append(clients, client)
|
|
||||||
clientNames = append(clientNames, client.Name())
|
|
||||||
hasMatch = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !(s.disableFallback || s.disableFallbackIfMatch && hasMatch) {
|
|
||||||
// Default round-robin query
|
|
||||||
for idx, client := range s.clients {
|
|
||||||
if clientUsed[idx] || client.skipFallback {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
clientUsed[idx] = true
|
|
||||||
clients = append(clients, client)
|
|
||||||
clientNames = append(clientNames, client.Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(domainRules) > 0 {
|
|
||||||
newError("domain ", domain, " matches following rules: ", domainRules).AtDebug().WriteToLog()
|
|
||||||
}
|
|
||||||
if len(clientNames) > 0 {
|
|
||||||
newError("domain ", domain, " will use DNS in order: ", clientNames).AtDebug().WriteToLog()
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(clients) == 0 {
|
|
||||||
clients = append(clients, s.clients[0])
|
|
||||||
clientNames = append(clientNames, s.clients[0].Name())
|
|
||||||
newError("domain ", domain, " will use the first DNS: ", clientNames).AtDebug().WriteToLog()
|
|
||||||
}
|
|
||||||
|
|
||||||
return clients
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
|
||||||
return New(ctx, config.(*Config))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
@@ -5,12 +5,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
|
||||||
|
|
||||||
"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/net"
|
||||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||||
|
"golang.org/x/net/dns/dnsmessage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fqdn normalize domain make sure it ends with '.'
|
// Fqdn normalize domain make sure it ends with '.'
|
||||||
@@ -54,7 +53,9 @@ func isNewer(baseRec *IPRecord, newRec *IPRecord) bool {
|
|||||||
return baseRec.Expire.Before(newRec.Expire)
|
return baseRec.Expire.Before(newRec.Expire)
|
||||||
}
|
}
|
||||||
|
|
||||||
var errRecordNotFound = errors.New("record not found")
|
var (
|
||||||
|
errRecordNotFound = errors.New("record not found")
|
||||||
|
)
|
||||||
|
|
||||||
type dnsRequest struct {
|
type dnsRequest struct {
|
||||||
reqType dnsmessage.Type
|
reqType dnsmessage.Type
|
||||||
@@ -211,7 +212,7 @@ L:
|
|||||||
case dnsmessage.TypeAAAA:
|
case dnsmessage.TypeAAAA:
|
||||||
ans, err := parser.AAAAResource()
|
ans, err := parser.AAAAResource()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newError("failed to parse AAAA record for domain: ", ah.Name).Base(err).WriteToLog()
|
newError("failed to parse A record for domain: ", ah.Name).Base(err).WriteToLog()
|
||||||
break L
|
break L
|
||||||
}
|
}
|
||||||
ipRecord.IP = append(ipRecord.IP, net.IPAddress(ans.AAAA[:]))
|
ipRecord.IP = append(ipRecord.IP, net.IPAddress(ans.AAAA[:]))
|
||||||
|
@@ -7,11 +7,10 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||||
|
"golang.org/x/net/dns/dnsmessage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_parseResponse(t *testing.T) {
|
func Test_parseResponse(t *testing.T) {
|
||||||
@@ -50,28 +49,20 @@ func Test_parseResponse(t *testing.T) {
|
|||||||
want *IPRecord
|
want *IPRecord
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{"empty",
|
||||||
"empty",
|
|
||||||
&IPRecord{0, []net.Address(nil), time.Time{}, dnsmessage.RCodeSuccess},
|
&IPRecord{0, []net.Address(nil), time.Time{}, dnsmessage.RCodeSuccess},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{"error",
|
||||||
"error",
|
|
||||||
nil,
|
nil,
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{"a record",
|
||||||
"a record",
|
&IPRecord{1, []net.Address{net.ParseAddress("8.8.8.8"), net.ParseAddress("8.8.4.4")},
|
||||||
&IPRecord{
|
time.Time{}, dnsmessage.RCodeSuccess},
|
||||||
1,
|
|
||||||
[]net.Address{net.ParseAddress("8.8.8.8"), net.ParseAddress("8.8.4.4")},
|
|
||||||
time.Time{},
|
|
||||||
dnsmessage.RCodeSuccess,
|
|
||||||
},
|
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{"aaaa record",
|
||||||
"aaaa record",
|
|
||||||
&IPRecord{2, []net.Address{net.ParseAddress("2001::123:8888"), net.ParseAddress("2001::123:8844")}, time.Time{}, dnsmessage.RCodeSuccess},
|
&IPRecord{2, []net.Address{net.ParseAddress("2001::123:8888"), net.ParseAddress("2001::123:8844")}, time.Time{}, dnsmessage.RCodeSuccess},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
@@ -5,14 +5,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/log"
|
"github.com/xtls/xray-core/common/log"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
@@ -24,6 +23,7 @@ import (
|
|||||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
"golang.org/x/net/dns/dnsmessage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DoHNameServer implemented DNS over HTTPS (RFC8484) Wire Format,
|
// DoHNameServer implemented DNS over HTTPS (RFC8484) Wire Format,
|
||||||
@@ -32,19 +32,20 @@ import (
|
|||||||
type DoHNameServer struct {
|
type DoHNameServer struct {
|
||||||
dispatcher routing.Dispatcher
|
dispatcher routing.Dispatcher
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
ips map[string]*record
|
ips map[string]record
|
||||||
pub *pubsub.Service
|
pub *pubsub.Service
|
||||||
cleanup *task.Periodic
|
cleanup *task.Periodic
|
||||||
reqID uint32
|
reqID uint32
|
||||||
|
clientIP net.IP
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
dohURL string
|
dohURL string
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDoHNameServer creates DOH server object for remote resolving.
|
// NewDoHNameServer creates DOH client object for remote resolving
|
||||||
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher) (*DoHNameServer, error) {
|
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, clientIP net.IP) (*DoHNameServer, error) {
|
||||||
newError("DNS: created Remote DOH client for ", url.String()).AtInfo().WriteToLog()
|
newError("DNS: created Remote DOH client for ", url.String()).AtInfo().WriteToLog()
|
||||||
s := baseDOHNameServer(url, "DOH")
|
s := baseDOHNameServer(url, "DOH", clientIP)
|
||||||
|
|
||||||
s.dispatcher = dispatcher
|
s.dispatcher = dispatcher
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
@@ -103,9 +104,9 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher) (*DoHNameServ
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewDoHLocalNameServer creates DOH client object for local resolving
|
// NewDoHLocalNameServer creates DOH client object for local resolving
|
||||||
func NewDoHLocalNameServer(url *url.URL) *DoHNameServer {
|
func NewDoHLocalNameServer(url *url.URL, clientIP net.IP) *DoHNameServer {
|
||||||
url.Scheme = "https"
|
url.Scheme = "https"
|
||||||
s := baseDOHNameServer(url, "DOHL")
|
s := baseDOHNameServer(url, "DOHL", clientIP)
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
IdleConnTimeout: 90 * time.Second,
|
IdleConnTimeout: 90 * time.Second,
|
||||||
ForceAttemptHTTP2: true,
|
ForceAttemptHTTP2: true,
|
||||||
@@ -135,21 +136,23 @@ func NewDoHLocalNameServer(url *url.URL) *DoHNameServer {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func baseDOHNameServer(url *url.URL, prefix string) *DoHNameServer {
|
func baseDOHNameServer(url *url.URL, prefix string, clientIP net.IP) *DoHNameServer {
|
||||||
s := &DoHNameServer{
|
s := &DoHNameServer{
|
||||||
ips: make(map[string]*record),
|
ips: make(map[string]record),
|
||||||
pub: pubsub.NewService(),
|
clientIP: clientIP,
|
||||||
name: prefix + "//" + url.Host,
|
pub: pubsub.NewService(),
|
||||||
dohURL: url.String(),
|
name: prefix + "//" + url.Host,
|
||||||
|
dohURL: url.String(),
|
||||||
}
|
}
|
||||||
s.cleanup = &task.Periodic{
|
s.cleanup = &task.Periodic{
|
||||||
Interval: time.Minute,
|
Interval: time.Minute,
|
||||||
Execute: s.Cleanup,
|
Execute: s.Cleanup,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name implements Server.
|
// Name returns client name
|
||||||
func (s *DoHNameServer) Name() string {
|
func (s *DoHNameServer) Name() string {
|
||||||
return s.name
|
return s.name
|
||||||
}
|
}
|
||||||
@@ -181,7 +184,7 @@ func (s *DoHNameServer) Cleanup() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(s.ips) == 0 {
|
if len(s.ips) == 0 {
|
||||||
s.ips = make(map[string]*record)
|
s.ips = make(map[string]record)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -191,10 +194,7 @@ func (s *DoHNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) {
|
|||||||
elapsed := time.Since(req.start)
|
elapsed := time.Since(req.start)
|
||||||
|
|
||||||
s.Lock()
|
s.Lock()
|
||||||
rec, found := s.ips[req.domain]
|
rec := s.ips[req.domain]
|
||||||
if !found {
|
|
||||||
rec = &record{}
|
|
||||||
}
|
|
||||||
updated := false
|
updated := false
|
||||||
|
|
||||||
switch req.reqType {
|
switch req.reqType {
|
||||||
@@ -204,7 +204,7 @@ func (s *DoHNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) {
|
|||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
case dnsmessage.TypeAAAA:
|
case dnsmessage.TypeAAAA:
|
||||||
addr := make([]net.Address, 0, len(ipRec.IP))
|
addr := make([]net.Address, 0)
|
||||||
for _, ip := range ipRec.IP {
|
for _, ip := range ipRec.IP {
|
||||||
if len(ip.IP()) == net.IPv6len {
|
if len(ip.IP()) == net.IPv6len {
|
||||||
addr = append(addr, ip)
|
addr = append(addr, ip)
|
||||||
@@ -235,7 +235,7 @@ func (s *DoHNameServer) newReqID() uint16 {
|
|||||||
return uint16(atomic.AddUint32(&s.reqID, 1))
|
return uint16(atomic.AddUint32(&s.reqID, 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
|
func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, option dns_feature.IPOption) {
|
||||||
newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx))
|
newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
|
||||||
if s.name+"." == "DOH//"+domain {
|
if s.name+"." == "DOH//"+domain {
|
||||||
@@ -243,7 +243,7 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP n
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
|
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP))
|
||||||
|
|
||||||
var deadline time.Time
|
var deadline time.Time
|
||||||
if d, ok := ctx.Deadline(); ok {
|
if d, ok := ctx.Deadline(); ok {
|
||||||
@@ -256,7 +256,7 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP n
|
|||||||
go func(r *dnsRequest) {
|
go func(r *dnsRequest) {
|
||||||
// generate new context for each req, using same context
|
// generate new context for each req, using same context
|
||||||
// may cause reqs all aborted if any one encounter an error
|
// may cause reqs all aborted if any one encounter an error
|
||||||
dnsCtx := ctx
|
dnsCtx := context.Background()
|
||||||
|
|
||||||
// reserve internal dns server requested Inbound
|
// reserve internal dns server requested Inbound
|
||||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||||
@@ -264,8 +264,8 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP n
|
|||||||
}
|
}
|
||||||
|
|
||||||
dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{
|
dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{
|
||||||
Protocol: "https",
|
Protocol: "https",
|
||||||
SkipDNSResolve: true,
|
//SkipRoutePick: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
// forced to use mux for DOH
|
// forced to use mux for DOH
|
||||||
@@ -314,11 +314,11 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
|
|||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
io.Copy(io.Discard, resp.Body) // flush resp.Body so that the conn is reusable
|
io.Copy(ioutil.Discard, resp.Body) // flush resp.Body so that the conn is reusable
|
||||||
return nil, fmt.Errorf("DOH server returned code %d", resp.StatusCode)
|
return nil, fmt.Errorf("DOH server returned code %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
return io.ReadAll(resp.Body)
|
return ioutil.ReadAll(resp.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
|
func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
|
||||||
@@ -330,30 +330,30 @@ func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOpt
|
|||||||
return nil, errRecordNotFound
|
return nil, errRecordNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
var err4 error
|
|
||||||
var err6 error
|
|
||||||
var ips []net.Address
|
var ips []net.Address
|
||||||
var ip6 []net.Address
|
var lastErr error
|
||||||
|
if option.IPv6Enable && record.AAAA != nil && record.AAAA.RCode == dnsmessage.RCodeSuccess {
|
||||||
if option.IPv4Enable {
|
aaaa, err := record.AAAA.getIPs()
|
||||||
ips, err4 = record.A.getIPs()
|
if err != nil {
|
||||||
|
lastErr = err
|
||||||
|
}
|
||||||
|
ips = append(ips, aaaa...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if option.IPv6Enable {
|
if option.IPv4Enable && record.A != nil && record.A.RCode == dnsmessage.RCodeSuccess {
|
||||||
ip6, err6 = record.AAAA.getIPs()
|
a, err := record.A.getIPs()
|
||||||
ips = append(ips, ip6...)
|
if err != nil {
|
||||||
|
lastErr = err
|
||||||
|
}
|
||||||
|
ips = append(ips, a...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ips) > 0 {
|
if len(ips) > 0 {
|
||||||
return toNetIP(ips)
|
return toNetIP(ips), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err4 != nil {
|
if lastErr != nil {
|
||||||
return nil, err4
|
return nil, lastErr
|
||||||
}
|
|
||||||
|
|
||||||
if err6 != nil {
|
|
||||||
return nil, err6
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (option.IPv4Enable && record.A != nil) || (option.IPv6Enable && record.AAAA != nil) {
|
if (option.IPv4Enable && record.A != nil) || (option.IPv6Enable && record.AAAA != nil) {
|
||||||
@@ -363,19 +363,15 @@ func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOpt
|
|||||||
return nil, errRecordNotFound
|
return nil, errRecordNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryIP implements Server.
|
// QueryIP is called from dns.Server->queryIPTimeout
|
||||||
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { // nolint: dupl
|
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, error) { // nolint: dupl
|
||||||
fqdn := Fqdn(domain)
|
fqdn := Fqdn(domain)
|
||||||
|
|
||||||
if disableCache {
|
ips, err := s.findIPsForDomain(fqdn, option)
|
||||||
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
if err != errRecordNotFound {
|
||||||
} else {
|
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
|
||||||
ips, err := s.findIPsForDomain(fqdn, option)
|
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err})
|
||||||
if err != errRecordNotFound {
|
return ips, err
|
||||||
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
|
|
||||||
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
|
|
||||||
return ips, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ipv4 and ipv6 belong to different subscription groups
|
// ipv4 and ipv6 belong to different subscription groups
|
||||||
@@ -404,13 +400,13 @@ func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net
|
|||||||
}
|
}
|
||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
s.sendQuery(ctx, fqdn, clientIP, option)
|
s.sendQuery(ctx, fqdn, option)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
ips, err := s.findIPsForDomain(fqdn, option)
|
ips, err := s.findIPsForDomain(fqdn, option)
|
||||||
if err != errRecordNotFound {
|
if err != errRecordNotFound {
|
||||||
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSQueried, time.Since(start), err})
|
||||||
return ips, err
|
return ips, err
|
||||||
}
|
}
|
||||||
|
|
@@ -20,30 +20,12 @@ type Holder struct {
|
|||||||
config *FakeDnsPool
|
config *FakeDnsPool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fkdns *Holder) IsIPInIPPool(ip net.Address) bool {
|
|
||||||
if ip.Family().IsDomain() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return fkdns.ipRange.Contains(ip.IP())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fkdns *Holder) GetFakeIPForDomain3(domain string, ipv4, ipv6 bool) []net.Address {
|
|
||||||
isIPv6 := fkdns.ipRange.IP.To4() == nil
|
|
||||||
if (isIPv6 && ipv6) || (!isIPv6 && ipv4) {
|
|
||||||
return fkdns.GetFakeIPForDomain(domain)
|
|
||||||
}
|
|
||||||
return []net.Address{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Holder) Type() interface{} {
|
func (*Holder) Type() interface{} {
|
||||||
return (*dns.FakeDNSEngine)(nil)
|
return (*dns.FakeDNSEngine)(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fkdns *Holder) Start() error {
|
func (fkdns *Holder) Start() error {
|
||||||
if fkdns.config != nil && fkdns.config.IpPool != "" && fkdns.config.LruSize != 0 {
|
return fkdns.initializeFromConfig()
|
||||||
return fkdns.initializeFromConfig()
|
|
||||||
}
|
|
||||||
return newError("invalid fakeDNS setting")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fkdns *Holder) Close() error {
|
func (fkdns *Holder) Close() error {
|
||||||
@@ -59,7 +41,7 @@ func NewFakeDNSHolder() (*Holder, error) {
|
|||||||
if fkdns, err = NewFakeDNSHolderConfigOnly(nil); err != nil {
|
if fkdns, err = NewFakeDNSHolderConfigOnly(nil); err != nil {
|
||||||
return nil, newError("Unable to create Fake Dns Engine").Base(err).AtError()
|
return nil, newError("Unable to create Fake Dns Engine").Base(err).AtError()
|
||||||
}
|
}
|
||||||
err = fkdns.initialize(dns.FakeIPv4Pool, 65535)
|
err = fkdns.initialize(dns.FakeIPPool, 65535)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -97,13 +79,13 @@ func (fkdns *Holder) GetFakeIPForDomain(domain string) []net.Address {
|
|||||||
if v, ok := fkdns.domainToIP.Get(domain); ok {
|
if v, ok := fkdns.domainToIP.Get(domain); ok {
|
||||||
return []net.Address{v.(net.Address)}
|
return []net.Address{v.(net.Address)}
|
||||||
}
|
}
|
||||||
currentTimeMillis := uint64(time.Now().UnixNano() / 1e6)
|
var currentTimeMillis = uint64(time.Now().UnixNano() / 1e6)
|
||||||
ones, bits := fkdns.ipRange.Mask.Size()
|
ones, bits := fkdns.ipRange.Mask.Size()
|
||||||
rooms := bits - ones
|
rooms := bits - ones
|
||||||
if rooms < 64 {
|
if rooms < 64 {
|
||||||
currentTimeMillis %= (uint64(1) << rooms)
|
currentTimeMillis %= (uint64(1) << rooms)
|
||||||
}
|
}
|
||||||
bigIntIP := big.NewInt(0).SetBytes(fkdns.ipRange.IP)
|
var bigIntIP = big.NewInt(0).SetBytes(fkdns.ipRange.IP)
|
||||||
bigIntIP = bigIntIP.Add(bigIntIP, new(big.Int).SetUint64(currentTimeMillis))
|
bigIntIP = bigIntIP.Add(bigIntIP, new(big.Int).SetUint64(currentTimeMillis))
|
||||||
var ip net.Address
|
var ip net.Address
|
||||||
for {
|
for {
|
||||||
@@ -135,92 +117,9 @@ func (fkdns *Holder) GetDomainFromFakeDNS(ip net.Address) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
type HolderMulti struct {
|
// GetFakeIPRange return fake IP range from configuration
|
||||||
holders []*Holder
|
func (fkdns *Holder) GetFakeIPRange() *gonet.IPNet {
|
||||||
|
return fkdns.ipRange
|
||||||
config *FakeDnsPoolMulti
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HolderMulti) IsIPInIPPool(ip net.Address) bool {
|
|
||||||
if ip.Family().IsDomain() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, v := range h.holders {
|
|
||||||
if v.IsIPInIPPool(ip) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HolderMulti) GetFakeIPForDomain3(domain string, ipv4, ipv6 bool) []net.Address {
|
|
||||||
var ret []net.Address
|
|
||||||
for _, v := range h.holders {
|
|
||||||
ret = append(ret, v.GetFakeIPForDomain3(domain, ipv4, ipv6)...)
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HolderMulti) GetFakeIPForDomain(domain string) []net.Address {
|
|
||||||
var ret []net.Address
|
|
||||||
for _, v := range h.holders {
|
|
||||||
ret = append(ret, v.GetFakeIPForDomain(domain)...)
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HolderMulti) GetDomainFromFakeDNS(ip net.Address) string {
|
|
||||||
for _, v := range h.holders {
|
|
||||||
if domain := v.GetDomainFromFakeDNS(ip); domain != "" {
|
|
||||||
return domain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HolderMulti) Type() interface{} {
|
|
||||||
return (*dns.FakeDNSEngine)(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HolderMulti) Start() error {
|
|
||||||
for _, v := range h.holders {
|
|
||||||
if v.config != nil && v.config.IpPool != "" && v.config.LruSize != 0 {
|
|
||||||
if err := v.Start(); err != nil {
|
|
||||||
return newError("Cannot start all fake dns pools").Base(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return newError("invalid fakeDNS setting")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HolderMulti) Close() error {
|
|
||||||
for _, v := range h.holders {
|
|
||||||
if err := v.Close(); err != nil {
|
|
||||||
return newError("Cannot close all fake dns pools").Base(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HolderMulti) createHolderGroups() error {
|
|
||||||
for _, v := range h.config.Pools {
|
|
||||||
holder, err := NewFakeDNSHolderConfigOnly(v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
h.holders = append(h.holders, holder)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFakeDNSHolderMulti(conf *FakeDnsPoolMulti) (*HolderMulti, error) {
|
|
||||||
holderMulti := &HolderMulti{nil, conf}
|
|
||||||
if err := holderMulti.createHolderGroups(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return holderMulti, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -232,13 +131,4 @@ func init() {
|
|||||||
}
|
}
|
||||||
return f, nil
|
return f, nil
|
||||||
}))
|
}))
|
||||||
|
|
||||||
common.Must(common.RegisterConfig((*FakeDnsPoolMulti)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
|
||||||
var f *HolderMulti
|
|
||||||
var err error
|
|
||||||
if f, err = NewFakeDNSHolderMulti(config.(*FakeDnsPoolMulti)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return f, nil
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.13.0
|
||||||
// source: app/dns/fakedns/fakedns.proto
|
// source: app/dns/fakedns/fakedns.proto
|
||||||
|
|
||||||
package fakedns
|
package fakedns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -20,6 +21,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type FakeDnsPool struct {
|
type FakeDnsPool struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -75,75 +80,24 @@ func (x *FakeDnsPool) GetLruSize() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
type FakeDnsPoolMulti struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
Pools []*FakeDnsPool `protobuf:"bytes,1,rep,name=pools,proto3" json:"pools,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FakeDnsPoolMulti) Reset() {
|
|
||||||
*x = FakeDnsPoolMulti{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[1]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FakeDnsPoolMulti) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*FakeDnsPoolMulti) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *FakeDnsPoolMulti) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[1]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use FakeDnsPoolMulti.ProtoReflect.Descriptor instead.
|
|
||||||
func (*FakeDnsPoolMulti) Descriptor() ([]byte, []int) {
|
|
||||||
return file_app_dns_fakedns_fakedns_proto_rawDescGZIP(), []int{1}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FakeDnsPoolMulti) GetPools() []*FakeDnsPool {
|
|
||||||
if x != nil {
|
|
||||||
return x.Pools
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_app_dns_fakedns_fakedns_proto protoreflect.FileDescriptor
|
var File_app_dns_fakedns_fakedns_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_app_dns_fakedns_fakedns_proto_rawDesc = []byte{
|
var file_app_dns_fakedns_fakedns_proto_rawDesc = []byte{
|
||||||
0x0a, 0x1d, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e,
|
0x0a, 0x1d, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e,
|
||||||
0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
|
0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
|
||||||
0x14, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61,
|
0x1a, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e,
|
||||||
0x6b, 0x65, 0x64, 0x6e, 0x73, 0x22, 0x40, 0x0a, 0x0b, 0x46, 0x61, 0x6b, 0x65, 0x44, 0x6e, 0x73,
|
0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x22, 0x40, 0x0a, 0x0b, 0x46,
|
||||||
0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x70, 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x18,
|
0x61, 0x6b, 0x65, 0x44, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x70,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x70, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x18, 0x0a,
|
0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x70, 0x50,
|
||||||
0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07,
|
0x6f, 0x6f, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x02,
|
||||||
0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x4b, 0x0a, 0x10, 0x46, 0x61, 0x6b, 0x65, 0x44,
|
0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x5f, 0x0a,
|
||||||
0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x12, 0x37, 0x0a, 0x05, 0x70,
|
0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
|
||||||
0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61,
|
0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x50,
|
||||||
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e,
|
0x01, 0x5a, 0x1e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72,
|
||||||
0x73, 0x2e, 0x46, 0x61, 0x6b, 0x65, 0x44, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x05, 0x70,
|
0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e,
|
||||||
0x6f, 0x6f, 0x6c, 0x73, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
0x73, 0xaa, 0x02, 0x1a, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41,
|
||||||
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73,
|
0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x2e, 0x46, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x62, 0x06,
|
||||||
0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78,
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70,
|
|
||||||
0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x14,
|
|
||||||
0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x2e, 0x46, 0x61, 0x6b,
|
|
||||||
0x65, 0x64, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -158,18 +112,16 @@ func file_app_dns_fakedns_fakedns_proto_rawDescGZIP() []byte {
|
|||||||
return file_app_dns_fakedns_fakedns_proto_rawDescData
|
return file_app_dns_fakedns_fakedns_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_app_dns_fakedns_fakedns_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
var file_app_dns_fakedns_fakedns_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
var file_app_dns_fakedns_fakedns_proto_goTypes = []interface{}{
|
var file_app_dns_fakedns_fakedns_proto_goTypes = []interface{}{
|
||||||
(*FakeDnsPool)(nil), // 0: xray.app.dns.fakedns.FakeDnsPool
|
(*FakeDnsPool)(nil), // 0: xray.app.dns.fakedns.FakeDnsPool
|
||||||
(*FakeDnsPoolMulti)(nil), // 1: xray.app.dns.fakedns.FakeDnsPoolMulti
|
|
||||||
}
|
}
|
||||||
var file_app_dns_fakedns_fakedns_proto_depIdxs = []int32{
|
var file_app_dns_fakedns_fakedns_proto_depIdxs = []int32{
|
||||||
0, // 0: xray.app.dns.fakedns.FakeDnsPoolMulti.pools:type_name -> xray.app.dns.fakedns.FakeDnsPool
|
0, // [0:0] is the sub-list for method output_type
|
||||||
1, // [1:1] is the sub-list for method output_type
|
0, // [0:0] is the sub-list for method input_type
|
||||||
1, // [1:1] is the sub-list for method input_type
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
1, // [1:1] is the sub-list for extension type_name
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
1, // [1:1] is the sub-list for extension extendee
|
0, // [0:0] is the sub-list for field type_name
|
||||||
0, // [0:1] is the sub-list for field type_name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_app_dns_fakedns_fakedns_proto_init() }
|
func init() { file_app_dns_fakedns_fakedns_proto_init() }
|
||||||
@@ -190,18 +142,6 @@ func file_app_dns_fakedns_fakedns_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_app_dns_fakedns_fakedns_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*FakeDnsPoolMulti); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
type x struct{}
|
type x struct{}
|
||||||
out := protoimpl.TypeBuilder{
|
out := protoimpl.TypeBuilder{
|
||||||
@@ -209,7 +149,7 @@ func file_app_dns_fakedns_fakedns_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_app_dns_fakedns_fakedns_proto_rawDesc,
|
RawDescriptor: file_app_dns_fakedns_fakedns_proto_rawDesc,
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 2,
|
NumMessages: 1,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
|
@@ -9,8 +9,4 @@ option java_multiple_files = true;
|
|||||||
message FakeDnsPool{
|
message FakeDnsPool{
|
||||||
string ip_pool = 1; //CIDR of IP pool used as fake DNS IP
|
string ip_pool = 1; //CIDR of IP pool used as fake DNS IP
|
||||||
int64 lruSize = 2; //Size of Pool for remembering relationship between domain name and IP address
|
int64 lruSize = 2; //Size of Pool for remembering relationship between domain name and IP address
|
||||||
}
|
|
||||||
|
|
||||||
message FakeDnsPoolMulti{
|
|
||||||
repeated FakeDnsPool pools = 1;
|
|
||||||
}
|
}
|
@@ -3,8 +3,6 @@ package fakedns
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
gonet "net"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
@@ -13,7 +11,9 @@ import (
|
|||||||
"github.com/xtls/xray-core/features/dns"
|
"github.com/xtls/xray-core/features/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ipPrefix = "198.1"
|
var (
|
||||||
|
ipPrefix = "198.18."
|
||||||
|
)
|
||||||
|
|
||||||
func TestNewFakeDnsHolder(_ *testing.T) {
|
func TestNewFakeDnsHolder(_ *testing.T) {
|
||||||
_, err := NewFakeDNSHolder()
|
_, err := NewFakeDNSHolder()
|
||||||
@@ -69,7 +69,7 @@ func TestFakeDnsHolderCreateMappingManySingleDomain(t *testing.T) {
|
|||||||
|
|
||||||
func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) {
|
func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) {
|
||||||
fkdns, err := NewFakeDNSHolderConfigOnly(&FakeDnsPool{
|
fkdns, err := NewFakeDNSHolderConfigOnly(&FakeDnsPool{
|
||||||
IpPool: dns.FakeIPv4Pool,
|
IpPool: dns.FakeIPPool,
|
||||||
LruSize: 256,
|
LruSize: 256,
|
||||||
})
|
})
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
@@ -103,79 +103,3 @@ func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFakeDNSMulti(t *testing.T) {
|
|
||||||
fakeMulti, err := NewFakeDNSHolderMulti(&FakeDnsPoolMulti{
|
|
||||||
Pools: []*FakeDnsPool{{
|
|
||||||
IpPool: "240.0.0.0/12",
|
|
||||||
LruSize: 256,
|
|
||||||
}, {
|
|
||||||
IpPool: "fddd:c5b4:ff5f:f4f0::/64",
|
|
||||||
LruSize: 256,
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
common.Must(err)
|
|
||||||
|
|
||||||
err = fakeMulti.Start()
|
|
||||||
|
|
||||||
common.Must(err)
|
|
||||||
|
|
||||||
assert.Nil(t, err, "Should not throw error")
|
|
||||||
_ = fakeMulti
|
|
||||||
|
|
||||||
t.Run("checkInRange", func(t *testing.T) {
|
|
||||||
t.Run("ipv4", func(t *testing.T) {
|
|
||||||
inPool := fakeMulti.IsIPInIPPool(net.IPAddress([]byte{240, 0, 0, 5}))
|
|
||||||
assert.True(t, inPool)
|
|
||||||
})
|
|
||||||
t.Run("ipv6", func(t *testing.T) {
|
|
||||||
ip, err := gonet.ResolveIPAddr("ip", "fddd:c5b4:ff5f:f4f0::5")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP))
|
|
||||||
assert.True(t, inPool)
|
|
||||||
})
|
|
||||||
t.Run("ipv4_inverse", func(t *testing.T) {
|
|
||||||
inPool := fakeMulti.IsIPInIPPool(net.IPAddress([]byte{241, 0, 0, 5}))
|
|
||||||
assert.False(t, inPool)
|
|
||||||
})
|
|
||||||
t.Run("ipv6_inverse", func(t *testing.T) {
|
|
||||||
ip, err := gonet.ResolveIPAddr("ip", "fcdd:c5b4:ff5f:f4f0::5")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP))
|
|
||||||
assert.False(t, inPool)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("allocateTwoAddressForTwoPool", func(t *testing.T) {
|
|
||||||
address := fakeMulti.GetFakeIPForDomain("fakednstest.v2fly.org")
|
|
||||||
assert.Len(t, address, 2, "should be 2 address one for each pool")
|
|
||||||
t.Run("eachOfThemShouldResolve:0", func(t *testing.T) {
|
|
||||||
domain := fakeMulti.GetDomainFromFakeDNS(address[0])
|
|
||||||
assert.Equal(t, "fakednstest.v2fly.org", domain)
|
|
||||||
})
|
|
||||||
t.Run("eachOfThemShouldResolve:1", func(t *testing.T) {
|
|
||||||
domain := fakeMulti.GetDomainFromFakeDNS(address[1])
|
|
||||||
assert.Equal(t, "fakednstest.v2fly.org", domain)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("understandIPTypeSelector", func(t *testing.T) {
|
|
||||||
t.Run("ipv4", func(t *testing.T) {
|
|
||||||
address := fakeMulti.GetFakeIPForDomain3("fakednstestipv4.v2fly.org", true, false)
|
|
||||||
assert.Len(t, address, 1, "should be 1 address")
|
|
||||||
assert.True(t, address[0].Family().IsIPv4())
|
|
||||||
})
|
|
||||||
t.Run("ipv6", func(t *testing.T) {
|
|
||||||
address := fakeMulti.GetFakeIPForDomain3("fakednstestipv6.v2fly.org", false, true)
|
|
||||||
assert.Len(t, address, 1, "should be 1 address")
|
|
||||||
assert.True(t, address[0].Family().IsIPv6())
|
|
||||||
})
|
|
||||||
t.Run("ipv46", func(t *testing.T) {
|
|
||||||
address := fakeMulti.GetFakeIPForDomain3("fakednstestipv46.v2fly.org", true, true)
|
|
||||||
assert.Len(t, address, 2, "should be 2 address")
|
|
||||||
assert.True(t, address[0].Family().IsIPv4())
|
|
||||||
assert.True(t, address[1].Family().IsIPv6())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@@ -14,6 +14,25 @@ type StaticHosts struct {
|
|||||||
matchers *strmatcher.MatcherGroup
|
matchers *strmatcher.MatcherGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var typeMap = map[DomainMatchingType]strmatcher.Type{
|
||||||
|
DomainMatchingType_Full: strmatcher.Full,
|
||||||
|
DomainMatchingType_Subdomain: strmatcher.Domain,
|
||||||
|
DomainMatchingType_Keyword: strmatcher.Substr,
|
||||||
|
DomainMatchingType_Regex: strmatcher.Regex,
|
||||||
|
}
|
||||||
|
|
||||||
|
func toStrMatcher(t DomainMatchingType, domain string) (strmatcher.Matcher, error) {
|
||||||
|
strMType, f := typeMap[t]
|
||||||
|
if !f {
|
||||||
|
return nil, newError("unknown mapping type", t).AtWarning()
|
||||||
|
}
|
||||||
|
matcher, err := strMType.New(domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("failed to create str matcher").Base(err)
|
||||||
|
}
|
||||||
|
return matcher, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewStaticHosts creates a new StaticHosts instance.
|
// NewStaticHosts creates a new StaticHosts instance.
|
||||||
func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDomain) (*StaticHosts, error) {
|
func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDomain) (*StaticHosts, error) {
|
||||||
g := new(strmatcher.MatcherGroup)
|
g := new(strmatcher.MatcherGroup)
|
||||||
@@ -47,8 +66,6 @@ func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDoma
|
|||||||
id := g.Add(matcher)
|
id := g.Add(matcher)
|
||||||
ips := make([]net.Address, 0, len(mapping.Ip)+1)
|
ips := make([]net.Address, 0, len(mapping.Ip)+1)
|
||||||
switch {
|
switch {
|
||||||
case len(mapping.ProxiedDomain) > 0:
|
|
||||||
ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
|
|
||||||
case len(mapping.Ip) > 0:
|
case len(mapping.Ip) > 0:
|
||||||
for _, ip := range mapping.Ip {
|
for _, ip := range mapping.Ip {
|
||||||
addr := net.IPAddress(ip)
|
addr := net.IPAddress(ip)
|
||||||
@@ -57,10 +74,19 @@ func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDoma
|
|||||||
}
|
}
|
||||||
ips = append(ips, addr)
|
ips = append(ips, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case len(mapping.ProxiedDomain) > 0:
|
||||||
|
ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, newError("neither IP address nor proxied domain specified for domain: ", mapping.Domain).AtWarning()
|
return nil, newError("neither IP address nor proxied domain specified for domain: ", mapping.Domain).AtWarning()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Special handling for localhost IPv6. This is a dirty workaround as JSON config supports only single IP mapping.
|
||||||
|
if len(ips) == 1 && ips[0] == net.LocalHostIP {
|
||||||
|
ips = append(ips, net.LocalHostIPv6)
|
||||||
|
}
|
||||||
|
|
||||||
sh.ips[id] = ips
|
sh.ips[id] = ips
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,36 +100,24 @@ func filterIP(ips []net.Address, option dns.IPOption) []net.Address {
|
|||||||
filtered = append(filtered, ip)
|
filtered = append(filtered, ip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(filtered) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return filtered
|
return filtered
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *StaticHosts) lookupInternal(domain string) []net.Address {
|
// LookupIP returns IP address for the given domain, if exists in this StaticHosts.
|
||||||
var ips []net.Address
|
func (h *StaticHosts) LookupIP(domain string, option dns.IPOption) []net.Address {
|
||||||
for _, id := range h.matchers.Match(domain) {
|
indices := h.matchers.Match(domain)
|
||||||
|
if len(indices) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ips := []net.Address{}
|
||||||
|
for _, id := range indices {
|
||||||
ips = append(ips, h.ips[id]...)
|
ips = append(ips, h.ips[id]...)
|
||||||
}
|
}
|
||||||
return ips
|
if len(ips) == 1 && ips[0].Family().IsDomain() {
|
||||||
}
|
return ips
|
||||||
|
|
||||||
func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) []net.Address {
|
|
||||||
switch addrs := h.lookupInternal(domain); {
|
|
||||||
case len(addrs) == 0: // Not recorded in static hosts, return nil
|
|
||||||
return nil
|
|
||||||
case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Try to unwrap domain
|
|
||||||
newError("found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it").AtDebug().WriteToLog()
|
|
||||||
if maxDepth > 0 {
|
|
||||||
unwrapped := h.lookup(addrs[0].Domain(), option, maxDepth-1)
|
|
||||||
if unwrapped != nil {
|
|
||||||
return unwrapped
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return addrs
|
|
||||||
default: // IP record found, return a non-nil IP array
|
|
||||||
return filterIP(addrs, option)
|
|
||||||
}
|
}
|
||||||
}
|
return filterIP(ips, option)
|
||||||
|
|
||||||
// Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts.
|
|
||||||
func (h *StaticHosts) Lookup(domain string, option dns.IPOption) []net.Address {
|
|
||||||
return h.lookup(domain, option, 5)
|
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
. "github.com/xtls/xray-core/app/dns"
|
. "github.com/xtls/xray-core/app/dns"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
@@ -19,20 +20,6 @@ func TestStaticHosts(t *testing.T) {
|
|||||||
{1, 1, 1, 1},
|
{1, 1, 1, 1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Type: DomainMatchingType_Full,
|
|
||||||
Domain: "proxy.xray.com",
|
|
||||||
Ip: [][]byte{
|
|
||||||
{1, 2, 3, 4},
|
|
||||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
|
||||||
},
|
|
||||||
ProxiedDomain: "another-proxy.xray.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: DomainMatchingType_Full,
|
|
||||||
Domain: "proxy2.xray.com",
|
|
||||||
ProxiedDomain: "proxy.xray.com",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Type: DomainMatchingType_Subdomain,
|
Type: DomainMatchingType_Subdomain,
|
||||||
Domain: "example.cn",
|
Domain: "example.cn",
|
||||||
@@ -45,7 +32,6 @@ func TestStaticHosts(t *testing.T) {
|
|||||||
Domain: "baidu.com",
|
Domain: "baidu.com",
|
||||||
Ip: [][]byte{
|
Ip: [][]byte{
|
||||||
{127, 0, 0, 1},
|
{127, 0, 0, 1},
|
||||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -54,7 +40,7 @@ func TestStaticHosts(t *testing.T) {
|
|||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
{
|
{
|
||||||
ips := hosts.Lookup("example.com", dns.IPOption{
|
ips := hosts.LookupIP("example.com", dns.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
IPv6Enable: true,
|
IPv6Enable: true,
|
||||||
})
|
})
|
||||||
@@ -67,33 +53,7 @@ func TestStaticHosts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
domain := hosts.Lookup("proxy.xray.com", dns.IPOption{
|
ips := hosts.LookupIP("www.example.cn", dns.IPOption{
|
||||||
IPv4Enable: true,
|
|
||||||
IPv6Enable: false,
|
|
||||||
})
|
|
||||||
if len(domain) != 1 {
|
|
||||||
t.Error("expect 1 domain, but got ", len(domain))
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(domain[0].Domain(), "another-proxy.xray.com"); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
domain := hosts.Lookup("proxy2.xray.com", dns.IPOption{
|
|
||||||
IPv4Enable: true,
|
|
||||||
IPv6Enable: false,
|
|
||||||
})
|
|
||||||
if len(domain) != 1 {
|
|
||||||
t.Error("expect 1 domain, but got ", len(domain))
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(domain[0].Domain(), "another-proxy.xray.com"); diff != "" {
|
|
||||||
t.Error(diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
ips := hosts.Lookup("www.example.cn", dns.IPOption{
|
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
IPv6Enable: true,
|
IPv6Enable: true,
|
||||||
})
|
})
|
||||||
@@ -106,7 +66,7 @@ func TestStaticHosts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ips := hosts.Lookup("baidu.com", dns.IPOption{
|
ips := hosts.LookupIP("baidu.com", dns.IPOption{
|
||||||
IPv4Enable: false,
|
IPv4Enable: false,
|
||||||
IPv6Enable: true,
|
IPv6Enable: true,
|
||||||
})
|
})
|
||||||
|
@@ -2,219 +2,40 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/router"
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/strmatcher"
|
|
||||||
"github.com/xtls/xray-core/core"
|
|
||||||
"github.com/xtls/xray-core/features/dns"
|
"github.com/xtls/xray-core/features/dns"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/dns/localdns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server is the interface for Name Server.
|
// Client is the interface for DNS client.
|
||||||
type Server interface {
|
type Client interface {
|
||||||
// Name of the Client.
|
// Name of the Client.
|
||||||
Name() string
|
Name() string
|
||||||
|
|
||||||
// QueryIP sends IP queries to its configured server.
|
// QueryIP sends IP queries to its configured server.
|
||||||
QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns.IPOption, disableCache bool) ([]net.IP, error)
|
QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client is the interface for DNS client.
|
type LocalNameServer struct {
|
||||||
type Client struct {
|
client *localdns.Client
|
||||||
server Server
|
|
||||||
clientIP net.IP
|
|
||||||
skipFallback bool
|
|
||||||
domains []string
|
|
||||||
expectIPs []*router.GeoIPMatcher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var errExpectedIPNonMatch = errors.New("expectIPs not match")
|
func (s *LocalNameServer) QueryIP(_ context.Context, domain string, option dns.IPOption) ([]net.IP, error) {
|
||||||
|
if option.IPv4Enable || option.IPv6Enable {
|
||||||
|
return s.client.LookupIP(domain, option)
|
||||||
|
}
|
||||||
|
|
||||||
// NewServer creates a name server object according to the network destination url.
|
return nil, newError("neither IPv4 nor IPv6 is enabled")
|
||||||
func NewServer(dest net.Destination, dispatcher routing.Dispatcher) (Server, error) {
|
|
||||||
if address := dest.Address; address.Family().IsDomain() {
|
|
||||||
u, err := url.Parse(address.Domain())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case strings.EqualFold(u.String(), "localhost"):
|
|
||||||
return NewLocalNameServer(), nil
|
|
||||||
case strings.EqualFold(u.Scheme, "https"): // DOH Remote mode
|
|
||||||
return NewDoHNameServer(u, dispatcher)
|
|
||||||
case strings.EqualFold(u.Scheme, "https+local"): // DOH Local mode
|
|
||||||
return NewDoHLocalNameServer(u), nil
|
|
||||||
case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode
|
|
||||||
return NewQUICNameServer(u)
|
|
||||||
case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode
|
|
||||||
return NewTCPNameServer(u, dispatcher)
|
|
||||||
case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode
|
|
||||||
return NewTCPLocalNameServer(u)
|
|
||||||
case strings.EqualFold(u.String(), "fakedns"):
|
|
||||||
return NewFakeDNSServer(), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if dest.Network == net.Network_Unknown {
|
|
||||||
dest.Network = net.Network_UDP
|
|
||||||
}
|
|
||||||
if dest.Network == net.Network_UDP { // UDP classic DNS mode
|
|
||||||
return NewClassicNameServer(dest, dispatcher), nil
|
|
||||||
}
|
|
||||||
return nil, newError("No available name server could be created from ", dest).AtWarning()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient creates a DNS client managing a name server with client IP, domain rules and expected IPs.
|
func (s *LocalNameServer) Name() string {
|
||||||
func NewClient(ctx context.Context, ns *NameServer, clientIP net.IP, container router.GeoIPMatcherContainer, matcherInfos *[]*DomainMatcherInfo, updateDomainRule func(strmatcher.Matcher, int, []*DomainMatcherInfo) error) (*Client, error) {
|
return "localhost"
|
||||||
client := &Client{}
|
|
||||||
|
|
||||||
err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
|
|
||||||
// Create a new server for each client for now
|
|
||||||
server, err := NewServer(ns.Address.AsDestination(), dispatcher)
|
|
||||||
if err != nil {
|
|
||||||
return newError("failed to create nameserver").Base(err).AtWarning()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Priotize local domains with specific TLDs or without any dot to local DNS
|
|
||||||
if _, isLocalDNS := server.(*LocalNameServer); isLocalDNS {
|
|
||||||
ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...)
|
|
||||||
ns.OriginalRules = append(ns.OriginalRules, localTLDsAndDotlessDomainsRule)
|
|
||||||
// The following lines is a solution to avoid core panics(rule index out of range) when setting `localhost` DNS client in config.
|
|
||||||
// Because the `localhost` DNS client will apend len(localTLDsAndDotlessDomains) rules into matcherInfos to match `geosite:private` default rule.
|
|
||||||
// But `matcherInfos` has no enough length to add rules, which leads to core panics (rule index out of range).
|
|
||||||
// To avoid this, the length of `matcherInfos` must be equal to the expected, so manually append it with Golang default zero value first for later modification.
|
|
||||||
// Related issues:
|
|
||||||
// https://github.com/v2fly/v2ray-core/issues/529
|
|
||||||
// https://github.com/v2fly/v2ray-core/issues/719
|
|
||||||
for i := 0; i < len(localTLDsAndDotlessDomains); i++ {
|
|
||||||
*matcherInfos = append(*matcherInfos, &DomainMatcherInfo{
|
|
||||||
clientIdx: uint16(0),
|
|
||||||
domainRuleIdx: uint16(0),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Establish domain rules
|
|
||||||
var rules []string
|
|
||||||
ruleCurr := 0
|
|
||||||
ruleIter := 0
|
|
||||||
for _, domain := range ns.PrioritizedDomain {
|
|
||||||
domainRule, err := toStrMatcher(domain.Type, domain.Domain)
|
|
||||||
if err != nil {
|
|
||||||
return newError("failed to create prioritized domain").Base(err).AtWarning()
|
|
||||||
}
|
|
||||||
originalRuleIdx := ruleCurr
|
|
||||||
if ruleCurr < len(ns.OriginalRules) {
|
|
||||||
rule := ns.OriginalRules[ruleCurr]
|
|
||||||
if ruleCurr >= len(rules) {
|
|
||||||
rules = append(rules, rule.Rule)
|
|
||||||
}
|
|
||||||
ruleIter++
|
|
||||||
if ruleIter >= int(rule.Size) {
|
|
||||||
ruleIter = 0
|
|
||||||
ruleCurr++
|
|
||||||
}
|
|
||||||
} else { // No original rule, generate one according to current domain matcher (majorly for compatibility with tests)
|
|
||||||
rules = append(rules, domainRule.String())
|
|
||||||
ruleCurr++
|
|
||||||
}
|
|
||||||
err = updateDomainRule(domainRule, originalRuleIdx, *matcherInfos)
|
|
||||||
if err != nil {
|
|
||||||
return newError("failed to create prioritized domain").Base(err).AtWarning()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Establish expected IPs
|
|
||||||
var matchers []*router.GeoIPMatcher
|
|
||||||
for _, geoip := range ns.Geoip {
|
|
||||||
matcher, err := container.Add(geoip)
|
|
||||||
if err != nil {
|
|
||||||
return newError("failed to create ip matcher").Base(err).AtWarning()
|
|
||||||
}
|
|
||||||
matchers = append(matchers, matcher)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(clientIP) > 0 {
|
|
||||||
switch ns.Address.Address.GetAddress().(type) {
|
|
||||||
case *net.IPOrDomain_Domain:
|
|
||||||
newError("DNS: client ", ns.Address.Address.GetDomain(), " uses clientIP ", clientIP.String()).AtInfo().WriteToLog()
|
|
||||||
case *net.IPOrDomain_Ip:
|
|
||||||
newError("DNS: client ", ns.Address.Address.GetIp(), " uses clientIP ", clientIP.String()).AtInfo().WriteToLog()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
client.server = server
|
|
||||||
client.clientIP = clientIP
|
|
||||||
client.skipFallback = ns.SkipFallback
|
|
||||||
client.domains = rules
|
|
||||||
client.expectIPs = matchers
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return client, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSimpleClient creates a DNS client with a simple destination.
|
func NewLocalNameServer() *LocalNameServer {
|
||||||
func NewSimpleClient(ctx context.Context, endpoint *net.Endpoint, clientIP net.IP) (*Client, error) {
|
newError("DNS: created localhost client").AtInfo().WriteToLog()
|
||||||
client := &Client{}
|
return &LocalNameServer{
|
||||||
err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
|
client: localdns.New(),
|
||||||
server, err := NewServer(endpoint.AsDestination(), dispatcher)
|
|
||||||
if err != nil {
|
|
||||||
return newError("failed to create nameserver").Base(err).AtWarning()
|
|
||||||
}
|
|
||||||
client.server = server
|
|
||||||
client.clientIP = clientIP
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if len(clientIP) > 0 {
|
|
||||||
switch endpoint.Address.GetAddress().(type) {
|
|
||||||
case *net.IPOrDomain_Domain:
|
|
||||||
newError("DNS: client ", endpoint.Address.GetDomain(), " uses clientIP ", clientIP.String()).AtInfo().WriteToLog()
|
|
||||||
case *net.IPOrDomain_Ip:
|
|
||||||
newError("DNS: client ", endpoint.Address.GetIp(), " uses clientIP ", clientIP.String()).AtInfo().WriteToLog()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return client, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the server name the client manages.
|
|
||||||
func (c *Client) Name() string {
|
|
||||||
return c.server.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryIP send DNS query to the name server with the client's IP.
|
|
||||||
func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption, disableCache bool) ([]net.IP, error) {
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, 4*time.Second)
|
|
||||||
ips, err := c.server.QueryIP(ctx, domain, c.clientIP, option, disableCache)
|
|
||||||
cancel()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return ips, err
|
|
||||||
}
|
|
||||||
return c.MatchExpectedIPs(domain, ips)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchExpectedIPs matches queried domain IPs with expected IPs and returns matched ones.
|
|
||||||
func (c *Client) MatchExpectedIPs(domain string, ips []net.IP) ([]net.IP, error) {
|
|
||||||
if len(c.expectIPs) == 0 {
|
|
||||||
return ips, nil
|
|
||||||
}
|
|
||||||
newIps := []net.IP{}
|
|
||||||
for _, ip := range ips {
|
|
||||||
for _, matcher := range c.expectIPs {
|
|
||||||
if matcher.Match(ip) {
|
|
||||||
newIps = append(newIps, ip)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(newIps) == 0 {
|
|
||||||
return nil, errExpectedIPNonMatch
|
|
||||||
}
|
|
||||||
newError("domain ", domain, " expectIPs ", newIps, " matched at server ", c.Name()).AtDebug().WriteToLog()
|
|
||||||
return newIps, nil
|
|
||||||
}
|
}
|
||||||
|
@@ -1,60 +0,0 @@
|
|||||||
package dns_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/url"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
|
|
||||||
. "github.com/xtls/xray-core/app/dns"
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
|
||||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDOHNameServer(t *testing.T) {
|
|
||||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
|
||||||
common.Must(err)
|
|
||||||
|
|
||||||
s := NewDoHLocalNameServer(url)
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
|
||||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
|
||||||
IPv4Enable: true,
|
|
||||||
IPv6Enable: true,
|
|
||||||
}, false)
|
|
||||||
cancel()
|
|
||||||
common.Must(err)
|
|
||||||
if len(ips) == 0 {
|
|
||||||
t.Error("expect some ips, but got 0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDOHNameServerWithCache(t *testing.T) {
|
|
||||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
|
||||||
common.Must(err)
|
|
||||||
|
|
||||||
s := NewDoHLocalNameServer(url)
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
|
||||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
|
||||||
IPv4Enable: true,
|
|
||||||
IPv6Enable: true,
|
|
||||||
}, false)
|
|
||||||
cancel()
|
|
||||||
common.Must(err)
|
|
||||||
if len(ips) == 0 {
|
|
||||||
t.Error("expect some ips, but got 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
|
||||||
ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{
|
|
||||||
IPv4Enable: true,
|
|
||||||
IPv6Enable: true,
|
|
||||||
}, true)
|
|
||||||
cancel()
|
|
||||||
common.Must(err)
|
|
||||||
if r := cmp.Diff(ips2, ips); r != "" {
|
|
||||||
t.Fatal(r)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -20,7 +20,7 @@ func (FakeDNSServer) Name() string {
|
|||||||
return "FakeDNS"
|
return "FakeDNS"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, opt dns.IPOption, _ bool) ([]net.IP, error) {
|
func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ dns.IPOption) ([]net.IP, error) {
|
||||||
if f.fakeDNSEngine == nil {
|
if f.fakeDNSEngine == nil {
|
||||||
if err := core.RequireFeatures(ctx, func(fd dns.FakeDNSEngine) {
|
if err := core.RequireFeatures(ctx, func(fd dns.FakeDNSEngine) {
|
||||||
f.fakeDNSEngine = fd
|
f.fakeDNSEngine = fd
|
||||||
@@ -28,22 +28,14 @@ func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, op
|
|||||||
return nil, newError("Unable to locate a fake DNS Engine").Base(err).AtError()
|
return nil, newError("Unable to locate a fake DNS Engine").Base(err).AtError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var ips []net.Address
|
ips := f.fakeDNSEngine.GetFakeIPForDomain(domain)
|
||||||
if fkr0, ok := f.fakeDNSEngine.(dns.FakeDNSEngineRev0); ok {
|
|
||||||
ips = fkr0.GetFakeIPForDomain3(domain, opt.IPv4Enable, opt.IPv6Enable)
|
|
||||||
} else {
|
|
||||||
ips = f.fakeDNSEngine.GetFakeIPForDomain(domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
netIP, err := toNetIP(ips)
|
netIP := toNetIP(ips)
|
||||||
if err != nil {
|
if netIP == nil {
|
||||||
return nil, newError("Unable to convert IP to net ip").Base(err).AtError()
|
return nil, newError("Unable to convert IP to net ip").AtError()
|
||||||
}
|
}
|
||||||
|
|
||||||
newError(f.Name(), " got answer: ", domain, " -> ", ips).AtInfo().WriteToLog()
|
newError(f.Name(), " got answer: ", domain, " -> ", ips).AtInfo().WriteToLog()
|
||||||
|
|
||||||
if len(netIP) > 0 {
|
return netIP, nil
|
||||||
return netIP, nil
|
|
||||||
}
|
|
||||||
return nil, dns.ErrEmptyResponse
|
|
||||||
}
|
}
|
||||||
|
@@ -1,51 +0,0 @@
|
|||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/features/dns"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
|
||||||
"github.com/xtls/xray-core/features/dns/localdns"
|
|
||||||
)
|
|
||||||
|
|
||||||
// LocalNameServer is an wrapper over local DNS feature.
|
|
||||||
type LocalNameServer struct {
|
|
||||||
client *localdns.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
const errEmptyResponse = "No address associated with hostname"
|
|
||||||
|
|
||||||
// QueryIP implements Server.
|
|
||||||
func (s *LocalNameServer) QueryIP(_ context.Context, domain string, _ net.IP, option dns.IPOption, _ bool) (ips []net.IP, err error) {
|
|
||||||
ips, err = s.client.LookupIP(domain, option)
|
|
||||||
|
|
||||||
if err != nil && strings.HasSuffix(err.Error(), errEmptyResponse) {
|
|
||||||
err = dns.ErrEmptyResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ips) > 0 {
|
|
||||||
newError("Localhost got answer: ", domain, " -> ", ips).AtInfo().WriteToLog()
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name implements Server.
|
|
||||||
func (s *LocalNameServer) Name() string {
|
|
||||||
return "localhost"
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewLocalNameServer creates localdns server object for directly lookup in system DNS.
|
|
||||||
func NewLocalNameServer() *LocalNameServer {
|
|
||||||
newError("DNS: created localhost client").AtInfo().WriteToLog()
|
|
||||||
return &LocalNameServer{
|
|
||||||
client: localdns.New(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewLocalDNSClient creates localdns client object for directly lookup in system DNS.
|
|
||||||
func NewLocalDNSClient() *Client {
|
|
||||||
return &Client{server: NewLocalNameServer()}
|
|
||||||
}
|
|
@@ -1,389 +0,0 @@
|
|||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/url"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/lucas-clemente/quic-go"
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/buf"
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
|
||||||
"github.com/xtls/xray-core/common/protocol/dns"
|
|
||||||
"github.com/xtls/xray-core/common/session"
|
|
||||||
"github.com/xtls/xray-core/common/signal/pubsub"
|
|
||||||
"github.com/xtls/xray-core/common/task"
|
|
||||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/tls"
|
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
|
||||||
"golang.org/x/net/http2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NextProtoDQ - During connection establishment, DNS/QUIC support is indicated
|
|
||||||
// by selecting the ALPN token "dq" in the crypto handshake.
|
|
||||||
const NextProtoDQ = "doq-i00"
|
|
||||||
|
|
||||||
const handshakeTimeout = time.Second * 8
|
|
||||||
|
|
||||||
// QUICNameServer implemented DNS over QUIC
|
|
||||||
type QUICNameServer struct {
|
|
||||||
sync.RWMutex
|
|
||||||
ips map[string]*record
|
|
||||||
pub *pubsub.Service
|
|
||||||
cleanup *task.Periodic
|
|
||||||
reqID uint32
|
|
||||||
name string
|
|
||||||
destination *net.Destination
|
|
||||||
session quic.Session
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewQUICNameServer creates DNS-over-QUIC client object for local resolving
|
|
||||||
func NewQUICNameServer(url *url.URL) (*QUICNameServer, error) {
|
|
||||||
newError("DNS: created Local DNS-over-QUIC client for ", url.String()).AtInfo().WriteToLog()
|
|
||||||
|
|
||||||
var err error
|
|
||||||
port := net.Port(784)
|
|
||||||
if url.Port() != "" {
|
|
||||||
port, err = net.PortFromString(url.Port())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dest := net.UDPDestination(net.ParseAddress(url.Hostname()), port)
|
|
||||||
|
|
||||||
s := &QUICNameServer{
|
|
||||||
ips: make(map[string]*record),
|
|
||||||
pub: pubsub.NewService(),
|
|
||||||
name: url.String(),
|
|
||||||
destination: &dest,
|
|
||||||
}
|
|
||||||
s.cleanup = &task.Periodic{
|
|
||||||
Interval: time.Minute,
|
|
||||||
Execute: s.Cleanup,
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns client name
|
|
||||||
func (s *QUICNameServer) Name() string {
|
|
||||||
return s.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup clears expired items from cache
|
|
||||||
func (s *QUICNameServer) Cleanup() error {
|
|
||||||
now := time.Now()
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
if len(s.ips) == 0 {
|
|
||||||
return newError("nothing to do. stopping...")
|
|
||||||
}
|
|
||||||
|
|
||||||
for domain, record := range s.ips {
|
|
||||||
if record.A != nil && record.A.Expire.Before(now) {
|
|
||||||
record.A = nil
|
|
||||||
}
|
|
||||||
if record.AAAA != nil && record.AAAA.Expire.Before(now) {
|
|
||||||
record.AAAA = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if record.A == nil && record.AAAA == nil {
|
|
||||||
newError(s.name, " cleanup ", domain).AtDebug().WriteToLog()
|
|
||||||
delete(s.ips, domain)
|
|
||||||
} else {
|
|
||||||
s.ips[domain] = record
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(s.ips) == 0 {
|
|
||||||
s.ips = make(map[string]*record)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *QUICNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) {
|
|
||||||
elapsed := time.Since(req.start)
|
|
||||||
|
|
||||||
s.Lock()
|
|
||||||
rec, found := s.ips[req.domain]
|
|
||||||
if !found {
|
|
||||||
rec = &record{}
|
|
||||||
}
|
|
||||||
updated := false
|
|
||||||
|
|
||||||
switch req.reqType {
|
|
||||||
case dnsmessage.TypeA:
|
|
||||||
if isNewer(rec.A, ipRec) {
|
|
||||||
rec.A = ipRec
|
|
||||||
updated = true
|
|
||||||
}
|
|
||||||
case dnsmessage.TypeAAAA:
|
|
||||||
addr := make([]net.Address, 0)
|
|
||||||
for _, ip := range ipRec.IP {
|
|
||||||
if len(ip.IP()) == net.IPv6len {
|
|
||||||
addr = append(addr, ip)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ipRec.IP = addr
|
|
||||||
if isNewer(rec.AAAA, ipRec) {
|
|
||||||
rec.AAAA = ipRec
|
|
||||||
updated = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newError(s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed).AtInfo().WriteToLog()
|
|
||||||
|
|
||||||
if updated {
|
|
||||||
s.ips[req.domain] = rec
|
|
||||||
}
|
|
||||||
switch req.reqType {
|
|
||||||
case dnsmessage.TypeA:
|
|
||||||
s.pub.Publish(req.domain+"4", nil)
|
|
||||||
case dnsmessage.TypeAAAA:
|
|
||||||
s.pub.Publish(req.domain+"6", nil)
|
|
||||||
}
|
|
||||||
s.Unlock()
|
|
||||||
common.Must(s.cleanup.Start())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *QUICNameServer) newReqID() uint16 {
|
|
||||||
return uint16(atomic.AddUint32(&s.reqID, 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
|
|
||||||
newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
|
|
||||||
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
|
|
||||||
|
|
||||||
var deadline time.Time
|
|
||||||
if d, ok := ctx.Deadline(); ok {
|
|
||||||
deadline = d
|
|
||||||
} else {
|
|
||||||
deadline = time.Now().Add(time.Second * 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, req := range reqs {
|
|
||||||
go func(r *dnsRequest) {
|
|
||||||
// generate new context for each req, using same context
|
|
||||||
// may cause reqs all aborted if any one encounter an error
|
|
||||||
dnsCtx := ctx
|
|
||||||
|
|
||||||
// reserve internal dns server requested Inbound
|
|
||||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
|
||||||
dnsCtx = session.ContextWithInbound(dnsCtx, inbound)
|
|
||||||
}
|
|
||||||
|
|
||||||
dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{
|
|
||||||
Protocol: "quic",
|
|
||||||
SkipDNSResolve: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
b, err := dns.PackMessage(r.msg)
|
|
||||||
if err != nil {
|
|
||||||
newError("failed to pack dns query").Base(err).AtError().WriteToLog()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := s.openStream(dnsCtx)
|
|
||||||
if err != nil {
|
|
||||||
newError("failed to open quic session").Base(err).AtError().WriteToLog()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = conn.Write(b.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
newError("failed to send query").Base(err).AtError().WriteToLog()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = conn.Close()
|
|
||||||
|
|
||||||
respBuf := buf.New()
|
|
||||||
defer respBuf.Release()
|
|
||||||
n, err := respBuf.ReadFrom(conn)
|
|
||||||
if err != nil && n == 0 {
|
|
||||||
newError("failed to read response").Base(err).AtError().WriteToLog()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rec, err := parseResponse(respBuf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
newError("failed to handle response").Base(err).AtError().WriteToLog()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.updateIP(r, rec)
|
|
||||||
}(req)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *QUICNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
|
|
||||||
s.RLock()
|
|
||||||
record, found := s.ips[domain]
|
|
||||||
s.RUnlock()
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return nil, errRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
var err4 error
|
|
||||||
var err6 error
|
|
||||||
var ips []net.Address
|
|
||||||
var ip6 []net.Address
|
|
||||||
|
|
||||||
if option.IPv4Enable {
|
|
||||||
ips, err4 = record.A.getIPs()
|
|
||||||
}
|
|
||||||
|
|
||||||
if option.IPv6Enable {
|
|
||||||
ip6, err6 = record.AAAA.getIPs()
|
|
||||||
ips = append(ips, ip6...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ips) > 0 {
|
|
||||||
return toNetIP(ips)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err4 != nil {
|
|
||||||
return nil, err4
|
|
||||||
}
|
|
||||||
|
|
||||||
if err6 != nil {
|
|
||||||
return nil, err6
|
|
||||||
}
|
|
||||||
|
|
||||||
if (option.IPv4Enable && record.A != nil) || (option.IPv6Enable && record.AAAA != nil) {
|
|
||||||
return nil, dns_feature.ErrEmptyResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryIP is called from dns.Server->queryIPTimeout
|
|
||||||
func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) {
|
|
||||||
fqdn := Fqdn(domain)
|
|
||||||
|
|
||||||
if disableCache {
|
|
||||||
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
|
||||||
} else {
|
|
||||||
ips, err := s.findIPsForDomain(fqdn, option)
|
|
||||||
if err != errRecordNotFound {
|
|
||||||
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
|
|
||||||
return ips, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ipv4 and ipv6 belong to different subscription groups
|
|
||||||
var sub4, sub6 *pubsub.Subscriber
|
|
||||||
if option.IPv4Enable {
|
|
||||||
sub4 = s.pub.Subscribe(fqdn + "4")
|
|
||||||
defer sub4.Close()
|
|
||||||
}
|
|
||||||
if option.IPv6Enable {
|
|
||||||
sub6 = s.pub.Subscribe(fqdn + "6")
|
|
||||||
defer sub6.Close()
|
|
||||||
}
|
|
||||||
done := make(chan interface{})
|
|
||||||
go func() {
|
|
||||||
if sub4 != nil {
|
|
||||||
select {
|
|
||||||
case <-sub4.Wait():
|
|
||||||
case <-ctx.Done():
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if sub6 != nil {
|
|
||||||
select {
|
|
||||||
case <-sub6.Wait():
|
|
||||||
case <-ctx.Done():
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
s.sendQuery(ctx, fqdn, clientIP, option)
|
|
||||||
|
|
||||||
for {
|
|
||||||
ips, err := s.findIPsForDomain(fqdn, option)
|
|
||||||
if err != errRecordNotFound {
|
|
||||||
return ips, err
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return nil, ctx.Err()
|
|
||||||
case <-done:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isActive(s quic.Session) bool {
|
|
||||||
select {
|
|
||||||
case <-s.Context().Done():
|
|
||||||
return false
|
|
||||||
default:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *QUICNameServer) getSession() (quic.Session, error) {
|
|
||||||
var session quic.Session
|
|
||||||
s.RLock()
|
|
||||||
session = s.session
|
|
||||||
if session != nil && isActive(session) {
|
|
||||||
s.RUnlock()
|
|
||||||
return session, nil
|
|
||||||
}
|
|
||||||
if session != nil {
|
|
||||||
// we're recreating the session, let's create a new one
|
|
||||||
_ = session.CloseWithError(0, "")
|
|
||||||
}
|
|
||||||
s.RUnlock()
|
|
||||||
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
var err error
|
|
||||||
session, err = s.openSession()
|
|
||||||
if err != nil {
|
|
||||||
// This does not look too nice, but QUIC (or maybe quic-go)
|
|
||||||
// doesn't seem stable enough.
|
|
||||||
// Maybe retransmissions aren't fully implemented in quic-go?
|
|
||||||
// Anyways, the simple solution is to make a second try when
|
|
||||||
// it fails to open the QUIC session.
|
|
||||||
session, err = s.openSession()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.session = session
|
|
||||||
return session, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *QUICNameServer) openSession() (quic.Session, error) {
|
|
||||||
tlsConfig := tls.Config{}
|
|
||||||
quicConfig := &quic.Config{
|
|
||||||
HandshakeIdleTimeout: handshakeTimeout,
|
|
||||||
}
|
|
||||||
|
|
||||||
session, err := quic.DialAddrContext(context.Background(), s.destination.NetAddr(), tlsConfig.GetTLSConfig(tls.WithNextProto("http/1.1", http2.NextProtoTLS, NextProtoDQ)), quicConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return session, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *QUICNameServer) openStream(ctx context.Context) (quic.Stream, error) {
|
|
||||||
session, err := s.getSession()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// open a new stream
|
|
||||||
return session.OpenStreamSync(ctx)
|
|
||||||
}
|
|
@@ -1,44 +0,0 @@
|
|||||||
package dns_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/url"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/features/dns"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
|
|
||||||
. "github.com/xtls/xray-core/app/dns"
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestQUICNameServer(t *testing.T) {
|
|
||||||
url, err := url.Parse("quic://dns.adguard.com")
|
|
||||||
common.Must(err)
|
|
||||||
s, err := NewQUICNameServer(url)
|
|
||||||
common.Must(err)
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
|
||||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns.IPOption{
|
|
||||||
IPv4Enable: true,
|
|
||||||
IPv6Enable: true,
|
|
||||||
}, false)
|
|
||||||
cancel()
|
|
||||||
common.Must(err)
|
|
||||||
if len(ips) == 0 {
|
|
||||||
t.Error("expect some ips, but got 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
|
||||||
ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns.IPOption{
|
|
||||||
IPv4Enable: true,
|
|
||||||
IPv6Enable: true,
|
|
||||||
}, true)
|
|
||||||
cancel()
|
|
||||||
common.Must(err)
|
|
||||||
if r := cmp.Diff(ips2, ips); r != "" {
|
|
||||||
t.Fatal(r)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,362 +0,0 @@
|
|||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/binary"
|
|
||||||
"net/url"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/buf"
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
|
||||||
"github.com/xtls/xray-core/common/net/cnc"
|
|
||||||
"github.com/xtls/xray-core/common/protocol/dns"
|
|
||||||
"github.com/xtls/xray-core/common/session"
|
|
||||||
"github.com/xtls/xray-core/common/signal/pubsub"
|
|
||||||
"github.com/xtls/xray-core/common/task"
|
|
||||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
|
||||||
"github.com/xtls/xray-core/features/routing"
|
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TCPNameServer implemented DNS over TCP (RFC7766).
|
|
||||||
type TCPNameServer struct {
|
|
||||||
sync.RWMutex
|
|
||||||
name string
|
|
||||||
destination *net.Destination
|
|
||||||
ips map[string]*record
|
|
||||||
pub *pubsub.Service
|
|
||||||
cleanup *task.Periodic
|
|
||||||
reqID uint32
|
|
||||||
dial func(context.Context) (net.Conn, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTCPNameServer creates DNS over TCP server object for remote resolving.
|
|
||||||
func NewTCPNameServer(url *url.URL, dispatcher routing.Dispatcher) (*TCPNameServer, error) {
|
|
||||||
s, err := baseTCPNameServer(url, "TCP")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.dial = func(ctx context.Context) (net.Conn, error) {
|
|
||||||
link, err := dispatcher.Dispatch(ctx, *s.destination)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cnc.NewConnection(
|
|
||||||
cnc.ConnectionInputMulti(link.Writer),
|
|
||||||
cnc.ConnectionOutputMulti(link.Reader),
|
|
||||||
), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTCPLocalNameServer creates DNS over TCP client object for local resolving
|
|
||||||
func NewTCPLocalNameServer(url *url.URL) (*TCPNameServer, error) {
|
|
||||||
s, err := baseTCPNameServer(url, "TCPL")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.dial = func(ctx context.Context) (net.Conn, error) {
|
|
||||||
return internet.DialSystem(ctx, *s.destination, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func baseTCPNameServer(url *url.URL, prefix string) (*TCPNameServer, error) {
|
|
||||||
var err error
|
|
||||||
port := net.Port(53)
|
|
||||||
if url.Port() != "" {
|
|
||||||
port, err = net.PortFromString(url.Port())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dest := net.TCPDestination(net.ParseAddress(url.Hostname()), port)
|
|
||||||
|
|
||||||
s := &TCPNameServer{
|
|
||||||
destination: &dest,
|
|
||||||
ips: make(map[string]*record),
|
|
||||||
pub: pubsub.NewService(),
|
|
||||||
name: prefix + "//" + dest.NetAddr(),
|
|
||||||
}
|
|
||||||
s.cleanup = &task.Periodic{
|
|
||||||
Interval: time.Minute,
|
|
||||||
Execute: s.Cleanup,
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name implements Server.
|
|
||||||
func (s *TCPNameServer) Name() string {
|
|
||||||
return s.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup clears expired items from cache
|
|
||||||
func (s *TCPNameServer) Cleanup() error {
|
|
||||||
now := time.Now()
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
if len(s.ips) == 0 {
|
|
||||||
return newError("nothing to do. stopping...")
|
|
||||||
}
|
|
||||||
|
|
||||||
for domain, record := range s.ips {
|
|
||||||
if record.A != nil && record.A.Expire.Before(now) {
|
|
||||||
record.A = nil
|
|
||||||
}
|
|
||||||
if record.AAAA != nil && record.AAAA.Expire.Before(now) {
|
|
||||||
record.AAAA = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if record.A == nil && record.AAAA == nil {
|
|
||||||
newError(s.name, " cleanup ", domain).AtDebug().WriteToLog()
|
|
||||||
delete(s.ips, domain)
|
|
||||||
} else {
|
|
||||||
s.ips[domain] = record
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(s.ips) == 0 {
|
|
||||||
s.ips = make(map[string]*record)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TCPNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) {
|
|
||||||
elapsed := time.Since(req.start)
|
|
||||||
|
|
||||||
s.Lock()
|
|
||||||
rec, found := s.ips[req.domain]
|
|
||||||
if !found {
|
|
||||||
rec = &record{}
|
|
||||||
}
|
|
||||||
updated := false
|
|
||||||
|
|
||||||
switch req.reqType {
|
|
||||||
case dnsmessage.TypeA:
|
|
||||||
if isNewer(rec.A, ipRec) {
|
|
||||||
rec.A = ipRec
|
|
||||||
updated = true
|
|
||||||
}
|
|
||||||
case dnsmessage.TypeAAAA:
|
|
||||||
addr := make([]net.Address, 0)
|
|
||||||
for _, ip := range ipRec.IP {
|
|
||||||
if len(ip.IP()) == net.IPv6len {
|
|
||||||
addr = append(addr, ip)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ipRec.IP = addr
|
|
||||||
if isNewer(rec.AAAA, ipRec) {
|
|
||||||
rec.AAAA = ipRec
|
|
||||||
updated = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newError(s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed).AtInfo().WriteToLog()
|
|
||||||
|
|
||||||
if updated {
|
|
||||||
s.ips[req.domain] = rec
|
|
||||||
}
|
|
||||||
switch req.reqType {
|
|
||||||
case dnsmessage.TypeA:
|
|
||||||
s.pub.Publish(req.domain+"4", nil)
|
|
||||||
case dnsmessage.TypeAAAA:
|
|
||||||
s.pub.Publish(req.domain+"6", nil)
|
|
||||||
}
|
|
||||||
s.Unlock()
|
|
||||||
common.Must(s.cleanup.Start())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TCPNameServer) newReqID() uint16 {
|
|
||||||
return uint16(atomic.AddUint32(&s.reqID, 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TCPNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
|
|
||||||
newError(s.name, " querying DNS for: ", domain).AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
|
|
||||||
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
|
|
||||||
|
|
||||||
var deadline time.Time
|
|
||||||
if d, ok := ctx.Deadline(); ok {
|
|
||||||
deadline = d
|
|
||||||
} else {
|
|
||||||
deadline = time.Now().Add(time.Second * 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, req := range reqs {
|
|
||||||
go func(r *dnsRequest) {
|
|
||||||
dnsCtx := ctx
|
|
||||||
|
|
||||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
|
||||||
dnsCtx = session.ContextWithInbound(dnsCtx, inbound)
|
|
||||||
}
|
|
||||||
|
|
||||||
dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{
|
|
||||||
Protocol: "dns",
|
|
||||||
SkipDNSResolve: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
b, err := dns.PackMessage(r.msg)
|
|
||||||
if err != nil {
|
|
||||||
newError("failed to pack dns query").Base(err).AtError().WriteToLog()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := s.dial(dnsCtx)
|
|
||||||
if err != nil {
|
|
||||||
newError("failed to dial namesever").Base(err).AtError().WriteToLog()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
dnsReqBuf := buf.New()
|
|
||||||
binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len()))
|
|
||||||
dnsReqBuf.Write(b.Bytes())
|
|
||||||
b.Release()
|
|
||||||
|
|
||||||
_, err = conn.Write(dnsReqBuf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
newError("failed to send query").Base(err).AtError().WriteToLog()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dnsReqBuf.Release()
|
|
||||||
|
|
||||||
respBuf := buf.New()
|
|
||||||
defer respBuf.Release()
|
|
||||||
n, err := respBuf.ReadFullFrom(conn, 2)
|
|
||||||
if err != nil && n == 0 {
|
|
||||||
newError("failed to read response length").Base(err).AtError().WriteToLog()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var length int16
|
|
||||||
err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length)
|
|
||||||
if err != nil {
|
|
||||||
newError("failed to parse response length").Base(err).AtError().WriteToLog()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
respBuf.Clear()
|
|
||||||
n, err = respBuf.ReadFullFrom(conn, int32(length))
|
|
||||||
if err != nil && n == 0 {
|
|
||||||
newError("failed to read response length").Base(err).AtError().WriteToLog()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rec, err := parseResponse(respBuf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
newError("failed to parse DNS over TCP response").Base(err).AtError().WriteToLog()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s.updateIP(r, rec)
|
|
||||||
}(req)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TCPNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
|
|
||||||
s.RLock()
|
|
||||||
record, found := s.ips[domain]
|
|
||||||
s.RUnlock()
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return nil, errRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
var err4 error
|
|
||||||
var err6 error
|
|
||||||
var ips []net.Address
|
|
||||||
var ip6 []net.Address
|
|
||||||
|
|
||||||
if option.IPv4Enable {
|
|
||||||
ips, err4 = record.A.getIPs()
|
|
||||||
}
|
|
||||||
|
|
||||||
if option.IPv6Enable {
|
|
||||||
ip6, err6 = record.AAAA.getIPs()
|
|
||||||
ips = append(ips, ip6...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ips) > 0 {
|
|
||||||
return toNetIP(ips)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err4 != nil {
|
|
||||||
return nil, err4
|
|
||||||
}
|
|
||||||
|
|
||||||
if err6 != nil {
|
|
||||||
return nil, err6
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, dns_feature.ErrEmptyResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryIP implements Server.
|
|
||||||
func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) {
|
|
||||||
fqdn := Fqdn(domain)
|
|
||||||
|
|
||||||
if disableCache {
|
|
||||||
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
|
||||||
} else {
|
|
||||||
ips, err := s.findIPsForDomain(fqdn, option)
|
|
||||||
if err != errRecordNotFound {
|
|
||||||
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
|
|
||||||
return ips, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ipv4 and ipv6 belong to different subscription groups
|
|
||||||
var sub4, sub6 *pubsub.Subscriber
|
|
||||||
if option.IPv4Enable {
|
|
||||||
sub4 = s.pub.Subscribe(fqdn + "4")
|
|
||||||
defer sub4.Close()
|
|
||||||
}
|
|
||||||
if option.IPv6Enable {
|
|
||||||
sub6 = s.pub.Subscribe(fqdn + "6")
|
|
||||||
defer sub6.Close()
|
|
||||||
}
|
|
||||||
done := make(chan interface{})
|
|
||||||
go func() {
|
|
||||||
if sub4 != nil {
|
|
||||||
select {
|
|
||||||
case <-sub4.Wait():
|
|
||||||
case <-ctx.Done():
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if sub6 != nil {
|
|
||||||
select {
|
|
||||||
case <-sub6.Wait():
|
|
||||||
case <-ctx.Done():
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
s.sendQuery(ctx, fqdn, clientIP, option)
|
|
||||||
|
|
||||||
for {
|
|
||||||
ips, err := s.findIPsForDomain(fqdn, option)
|
|
||||||
if err != errRecordNotFound {
|
|
||||||
return ips, err
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return nil, ctx.Err()
|
|
||||||
case <-done:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,60 +0,0 @@
|
|||||||
package dns_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/url"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
|
|
||||||
. "github.com/xtls/xray-core/app/dns"
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
|
||||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTCPLocalNameServer(t *testing.T) {
|
|
||||||
url, err := url.Parse("tcp+local://8.8.8.8")
|
|
||||||
common.Must(err)
|
|
||||||
s, err := NewTCPLocalNameServer(url)
|
|
||||||
common.Must(err)
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
|
||||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
|
||||||
IPv4Enable: true,
|
|
||||||
IPv6Enable: true,
|
|
||||||
}, false)
|
|
||||||
cancel()
|
|
||||||
common.Must(err)
|
|
||||||
if len(ips) == 0 {
|
|
||||||
t.Error("expect some ips, but got 0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTCPLocalNameServerWithCache(t *testing.T) {
|
|
||||||
url, err := url.Parse("tcp+local://8.8.8.8")
|
|
||||||
common.Must(err)
|
|
||||||
s, err := NewTCPLocalNameServer(url)
|
|
||||||
common.Must(err)
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
|
||||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
|
||||||
IPv4Enable: true,
|
|
||||||
IPv6Enable: true,
|
|
||||||
}, false)
|
|
||||||
cancel()
|
|
||||||
common.Must(err)
|
|
||||||
if len(ips) == 0 {
|
|
||||||
t.Error("expect some ips, but got 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
|
||||||
ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{
|
|
||||||
IPv4Enable: true,
|
|
||||||
IPv6Enable: true,
|
|
||||||
}, true)
|
|
||||||
cancel()
|
|
||||||
common.Must(err)
|
|
||||||
if r := cmp.Diff(ips2, ips); r != "" {
|
|
||||||
t.Fatal(r)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -5,21 +5,19 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
|
||||||
|
|
||||||
. "github.com/xtls/xray-core/app/dns"
|
. "github.com/xtls/xray-core/app/dns"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/features/dns"
|
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLocalNameServer(t *testing.T) {
|
func TestLocalNameServer(t *testing.T) {
|
||||||
s := NewLocalNameServer()
|
s := NewLocalNameServer()
|
||||||
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", net.IP{}, dns.IPOption{
|
ips, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
IPv6Enable: true,
|
IPv6Enable: true,
|
||||||
FakeEnable: false,
|
FakeEnable: false,
|
||||||
}, false)
|
})
|
||||||
cancel()
|
cancel()
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
if len(ips) == 0 {
|
if len(ips) == 0 {
|
438
app/dns/server.go
Normal file
438
app/dns/server.go
Normal file
@@ -0,0 +1,438 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/app/router"
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
"github.com/xtls/xray-core/common/strmatcher"
|
||||||
|
"github.com/xtls/xray-core/common/uuid"
|
||||||
|
core "github.com/xtls/xray-core/core"
|
||||||
|
"github.com/xtls/xray-core/features"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
|
"github.com/xtls/xray-core/features/routing"
|
||||||
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Server is a DNS rely server.
|
||||||
|
type Server struct {
|
||||||
|
sync.Mutex
|
||||||
|
hosts *StaticHosts
|
||||||
|
clientIP net.IP
|
||||||
|
clients []Client // clientIdx -> Client
|
||||||
|
ctx context.Context
|
||||||
|
ipIndexMap []*MultiGeoIPMatcher // clientIdx -> *MultiGeoIPMatcher
|
||||||
|
domainRules [][]string // clientIdx -> domainRuleIdx -> DomainRule
|
||||||
|
domainMatcher strmatcher.IndexMatcher
|
||||||
|
matcherInfos []DomainMatcherInfo // matcherIdx -> DomainMatcherInfo
|
||||||
|
tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainMatcherInfo contains information attached to index returned by Server.domainMatcher
|
||||||
|
type DomainMatcherInfo struct {
|
||||||
|
clientIdx uint16
|
||||||
|
domainRuleIdx uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiGeoIPMatcher for match
|
||||||
|
type MultiGeoIPMatcher struct {
|
||||||
|
matchers []*router.GeoIPMatcher
|
||||||
|
}
|
||||||
|
|
||||||
|
var errExpectedIPNonMatch = errors.New("expectIPs not match")
|
||||||
|
|
||||||
|
// Match check ip match
|
||||||
|
func (c *MultiGeoIPMatcher) Match(ip net.IP) bool {
|
||||||
|
for _, matcher := range c.matchers {
|
||||||
|
if matcher.Match(ip) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasMatcher check has matcher
|
||||||
|
func (c *MultiGeoIPMatcher) HasMatcher() bool {
|
||||||
|
return len(c.matchers) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateRandomTag() string {
|
||||||
|
id := uuid.New()
|
||||||
|
return "xray.system." + id.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new DNS server with given configuration.
|
||||||
|
func New(ctx context.Context, config *Config) (*Server, error) {
|
||||||
|
server := &Server{
|
||||||
|
clients: make([]Client, 0, len(config.NameServers)+len(config.NameServer)),
|
||||||
|
ctx: ctx,
|
||||||
|
tag: config.Tag,
|
||||||
|
}
|
||||||
|
if server.tag == "" {
|
||||||
|
server.tag = generateRandomTag()
|
||||||
|
}
|
||||||
|
if len(config.ClientIp) > 0 {
|
||||||
|
if len(config.ClientIp) != net.IPv4len && len(config.ClientIp) != net.IPv6len {
|
||||||
|
return nil, newError("unexpected IP length", len(config.ClientIp))
|
||||||
|
}
|
||||||
|
server.clientIP = net.IP(config.ClientIp)
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts, err := NewStaticHosts(config.StaticHosts, config.Hosts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("failed to create hosts").Base(err)
|
||||||
|
}
|
||||||
|
server.hosts = hosts
|
||||||
|
|
||||||
|
addNameServer := func(ns *NameServer) int {
|
||||||
|
endpoint := ns.Address
|
||||||
|
address := endpoint.Address.AsAddress()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case address.Family().IsDomain() && address.Domain() == "localhost":
|
||||||
|
server.clients = append(server.clients, NewLocalNameServer())
|
||||||
|
// Priotize local domains with specific TLDs or without any dot to local DNS
|
||||||
|
// References:
|
||||||
|
// https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml
|
||||||
|
// https://unix.stackexchange.com/questions/92441/whats-the-difference-between-local-home-and-lan
|
||||||
|
localTLDsAndDotlessDomains := []*NameServer_PriorityDomain{
|
||||||
|
{Type: DomainMatchingType_Regex, Domain: "^[^.]+$"}, // This will only match domains without any dot
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "local"},
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "localdomain"},
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "localhost"},
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "lan"},
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "home.arpa"},
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "example"},
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "invalid"},
|
||||||
|
{Type: DomainMatchingType_Subdomain, Domain: "test"},
|
||||||
|
}
|
||||||
|
ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...)
|
||||||
|
|
||||||
|
case address.Family().IsDomain() && strings.HasPrefix(address.Domain(), "https+local://"):
|
||||||
|
// URI schemed string treated as domain
|
||||||
|
// DOH Local mode
|
||||||
|
u, err := url.Parse(address.Domain())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(newError("DNS config error").Base(err))
|
||||||
|
}
|
||||||
|
server.clients = append(server.clients, NewDoHLocalNameServer(u, server.clientIP))
|
||||||
|
|
||||||
|
case address.Family().IsDomain() && strings.HasPrefix(address.Domain(), "https://"):
|
||||||
|
// DOH Remote mode
|
||||||
|
u, err := url.Parse(address.Domain())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(newError("DNS config error").Base(err))
|
||||||
|
}
|
||||||
|
idx := len(server.clients)
|
||||||
|
server.clients = append(server.clients, nil)
|
||||||
|
|
||||||
|
// need the core dispatcher, register DOHClient at callback
|
||||||
|
common.Must(core.RequireFeatures(ctx, func(d routing.Dispatcher) {
|
||||||
|
c, err := NewDoHNameServer(u, d, server.clientIP)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(newError("DNS config error").Base(err))
|
||||||
|
}
|
||||||
|
server.clients[idx] = c
|
||||||
|
}))
|
||||||
|
|
||||||
|
case address.Family().IsDomain() && address.Domain() == "fakedns":
|
||||||
|
server.clients = append(server.clients, NewFakeDNSServer())
|
||||||
|
|
||||||
|
default:
|
||||||
|
// UDP classic DNS mode
|
||||||
|
dest := endpoint.AsDestination()
|
||||||
|
if dest.Network == net.Network_Unknown {
|
||||||
|
dest.Network = net.Network_UDP
|
||||||
|
}
|
||||||
|
if dest.Network == net.Network_UDP {
|
||||||
|
idx := len(server.clients)
|
||||||
|
server.clients = append(server.clients, nil)
|
||||||
|
|
||||||
|
common.Must(core.RequireFeatures(ctx, func(d routing.Dispatcher) {
|
||||||
|
server.clients[idx] = NewClassicNameServer(dest, d, server.clientIP)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
server.ipIndexMap = append(server.ipIndexMap, nil)
|
||||||
|
return len(server.clients) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(config.NameServers) > 0 {
|
||||||
|
features.PrintDeprecatedFeatureWarning("simple DNS server")
|
||||||
|
for _, destPB := range config.NameServers {
|
||||||
|
addNameServer(&NameServer{Address: destPB})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(config.NameServer) > 0 {
|
||||||
|
clientIndices := []int{}
|
||||||
|
domainRuleCount := 0
|
||||||
|
for _, ns := range config.NameServer {
|
||||||
|
idx := addNameServer(ns)
|
||||||
|
clientIndices = append(clientIndices, idx)
|
||||||
|
domainRuleCount += len(ns.PrioritizedDomain)
|
||||||
|
}
|
||||||
|
|
||||||
|
domainRules := make([][]string, len(server.clients))
|
||||||
|
domainMatcher := &strmatcher.MatcherGroup{}
|
||||||
|
matcherInfos := make([]DomainMatcherInfo, domainRuleCount+1) // matcher index starts from 1
|
||||||
|
var geoIPMatcherContainer router.GeoIPMatcherContainer
|
||||||
|
for nidx, ns := range config.NameServer {
|
||||||
|
idx := clientIndices[nidx]
|
||||||
|
|
||||||
|
// Establish domain rule matcher
|
||||||
|
rules := []string{}
|
||||||
|
ruleCurr := 0
|
||||||
|
ruleIter := 0
|
||||||
|
for _, domain := range ns.PrioritizedDomain {
|
||||||
|
matcher, err := toStrMatcher(domain.Type, domain.Domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("failed to create prioritized domain").Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
midx := domainMatcher.Add(matcher)
|
||||||
|
if midx >= uint32(len(matcherInfos)) { // This rarely happens according to current matcher's implementation
|
||||||
|
newError("expanding domain matcher info array to size ", midx, " when adding ", matcher).AtDebug().WriteToLog()
|
||||||
|
matcherInfos = append(matcherInfos, make([]DomainMatcherInfo, midx-uint32(len(matcherInfos))+1)...)
|
||||||
|
}
|
||||||
|
info := &matcherInfos[midx]
|
||||||
|
info.clientIdx = uint16(idx)
|
||||||
|
if ruleCurr < len(ns.OriginalRules) {
|
||||||
|
info.domainRuleIdx = uint16(ruleCurr)
|
||||||
|
rule := ns.OriginalRules[ruleCurr]
|
||||||
|
if ruleCurr >= len(rules) {
|
||||||
|
rules = append(rules, rule.Rule)
|
||||||
|
}
|
||||||
|
ruleIter++
|
||||||
|
if ruleIter >= int(rule.Size) {
|
||||||
|
ruleIter = 0
|
||||||
|
ruleCurr++
|
||||||
|
}
|
||||||
|
} else { // No original rule, generate one according to current domain matcher (majorly for compatibility with tests)
|
||||||
|
info.domainRuleIdx = uint16(len(rules))
|
||||||
|
rules = append(rules, matcher.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
domainRules[idx] = rules
|
||||||
|
|
||||||
|
// only add to ipIndexMap if GeoIP is configured
|
||||||
|
if len(ns.Geoip) > 0 {
|
||||||
|
var matchers []*router.GeoIPMatcher
|
||||||
|
for _, geoip := range ns.Geoip {
|
||||||
|
matcher, err := geoIPMatcherContainer.Add(geoip)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("failed to create ip matcher").Base(err).AtWarning()
|
||||||
|
}
|
||||||
|
matchers = append(matchers, matcher)
|
||||||
|
}
|
||||||
|
matcher := &MultiGeoIPMatcher{matchers: matchers}
|
||||||
|
server.ipIndexMap[idx] = matcher
|
||||||
|
}
|
||||||
|
}
|
||||||
|
server.domainRules = domainRules
|
||||||
|
server.domainMatcher = domainMatcher
|
||||||
|
server.matcherInfos = matcherInfos
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(server.clients) == 0 {
|
||||||
|
server.clients = append(server.clients, NewLocalNameServer())
|
||||||
|
server.ipIndexMap = append(server.ipIndexMap, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements common.HasType.
|
||||||
|
func (*Server) Type() interface{} {
|
||||||
|
return dns.ClientType()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start implements common.Runnable.
|
||||||
|
func (s *Server) Start() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements common.Closable.
|
||||||
|
func (s *Server) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) IsOwnLink(ctx context.Context) bool {
|
||||||
|
inbound := session.InboundFromContext(ctx)
|
||||||
|
return inbound != nil && inbound.Tag == s.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match check dns ip match geoip
|
||||||
|
func (s *Server) Match(idx int, client Client, domain string, ips []net.IP) ([]net.IP, error) {
|
||||||
|
var matcher *MultiGeoIPMatcher
|
||||||
|
if idx < len(s.ipIndexMap) {
|
||||||
|
matcher = s.ipIndexMap[idx]
|
||||||
|
}
|
||||||
|
if matcher == nil {
|
||||||
|
return ips, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matcher.HasMatcher() {
|
||||||
|
newError("domain ", domain, " server has no valid matcher: ", client.Name(), " idx:", idx).AtDebug().WriteToLog()
|
||||||
|
return ips, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
newIps := []net.IP{}
|
||||||
|
for _, ip := range ips {
|
||||||
|
if matcher.Match(ip) {
|
||||||
|
newIps = append(newIps, ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(newIps) == 0 {
|
||||||
|
return nil, errExpectedIPNonMatch
|
||||||
|
}
|
||||||
|
newError("domain ", domain, " expectIPs ", newIps, " matched at server ", client.Name(), " idx:", idx).AtDebug().WriteToLog()
|
||||||
|
return newIps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) queryIPTimeout(idx int, client Client, domain string, option dns.IPOption) ([]net.IP, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(s.ctx, time.Second*4)
|
||||||
|
if len(s.tag) > 0 {
|
||||||
|
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||||||
|
Tag: s.tag,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ctx = internet.ContextWithLookupDomain(ctx, domain)
|
||||||
|
ips, err := client.QueryIP(ctx, domain, option)
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ips, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ips, err = s.Match(idx, client, domain, ips)
|
||||||
|
return ips, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) lookupStatic(domain string, option dns.IPOption, depth int32) []net.Address {
|
||||||
|
ips := s.hosts.LookupIP(domain, option)
|
||||||
|
if ips == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if ips[0].Family().IsDomain() && depth < 5 {
|
||||||
|
if newIPs := s.lookupStatic(ips[0].Domain(), option, depth+1); newIPs != nil {
|
||||||
|
return newIPs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ips
|
||||||
|
}
|
||||||
|
|
||||||
|
func toNetIP(ips []net.Address) []net.IP {
|
||||||
|
if len(ips) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
netips := make([]net.IP, 0, len(ips))
|
||||||
|
for _, ip := range ips {
|
||||||
|
netips = append(netips, ip.IP())
|
||||||
|
}
|
||||||
|
return netips
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupIP implements dns.Client.
|
||||||
|
func (s *Server) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) {
|
||||||
|
if domain == "" {
|
||||||
|
return nil, newError("empty domain name")
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize the FQDN form query
|
||||||
|
if strings.HasSuffix(domain, ".") {
|
||||||
|
domain = domain[:len(domain)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
ips := s.lookupStatic(domain, option, 0)
|
||||||
|
if ips != nil && ips[0].Family().IsIP() {
|
||||||
|
newError("returning ", len(ips), " IPs for domain ", domain).WriteToLog()
|
||||||
|
return toNetIP(ips), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if ips != nil && ips[0].Family().IsDomain() {
|
||||||
|
newdomain := ips[0].Domain()
|
||||||
|
newError("domain replaced: ", domain, " -> ", newdomain).WriteToLog()
|
||||||
|
domain = newdomain
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastErr error
|
||||||
|
var matchedClient Client
|
||||||
|
if s.domainMatcher != nil {
|
||||||
|
indices := s.domainMatcher.Match(domain)
|
||||||
|
domainRules := []string{}
|
||||||
|
matchingDNS := []string{}
|
||||||
|
for _, idx := range indices {
|
||||||
|
info := s.matcherInfos[idx]
|
||||||
|
rule := s.domainRules[info.clientIdx][info.domainRuleIdx]
|
||||||
|
domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", rule, info.clientIdx))
|
||||||
|
matchingDNS = append(matchingDNS, s.clients[info.clientIdx].Name())
|
||||||
|
}
|
||||||
|
if len(domainRules) > 0 {
|
||||||
|
newError("domain ", domain, " matches following rules: ", domainRules).AtDebug().WriteToLog()
|
||||||
|
}
|
||||||
|
if len(matchingDNS) > 0 {
|
||||||
|
newError("domain ", domain, " uses following DNS first: ", matchingDNS).AtDebug().WriteToLog()
|
||||||
|
}
|
||||||
|
for _, idx := range indices {
|
||||||
|
clientIdx := int(s.matcherInfos[idx].clientIdx)
|
||||||
|
matchedClient = s.clients[clientIdx]
|
||||||
|
if !option.FakeEnable && strings.EqualFold(matchedClient.Name(), "FakeDNS") {
|
||||||
|
newError("skip DNS resolution for domain ", domain, " at server ", matchedClient.Name()).AtDebug().WriteToLog()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ips, err := s.queryIPTimeout(clientIdx, matchedClient, domain, option)
|
||||||
|
if len(ips) > 0 {
|
||||||
|
return ips, nil
|
||||||
|
}
|
||||||
|
if err == dns.ErrEmptyResponse {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
newError("failed to lookup ip for domain ", domain, " at server ", matchedClient.Name()).Base(err).WriteToLog()
|
||||||
|
lastErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, client := range s.clients {
|
||||||
|
if client == matchedClient {
|
||||||
|
newError("domain ", domain, " at server ", client.Name(), " idx:", idx, " already lookup failed, just ignore").AtDebug().WriteToLog()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") {
|
||||||
|
newError("skip DNS resolution for domain ", domain, " at server ", client.Name()).AtDebug().WriteToLog()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ips, err := s.queryIPTimeout(idx, client, domain, option)
|
||||||
|
if len(ips) > 0 {
|
||||||
|
return ips, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
newError("failed to lookup ip for domain ", domain, " at server ", client.Name()).Base(err).WriteToLog()
|
||||||
|
lastErr = err
|
||||||
|
}
|
||||||
|
if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, newError("returning nil for domain ", domain).Base(lastErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
|
return New(ctx, config.(*Config))
|
||||||
|
}))
|
||||||
|
}
|
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/dispatcher"
|
"github.com/xtls/xray-core/app/dispatcher"
|
||||||
. "github.com/xtls/xray-core/app/dns"
|
. "github.com/xtls/xray-core/app/dns"
|
||||||
"github.com/xtls/xray-core/app/policy"
|
"github.com/xtls/xray-core/app/policy"
|
||||||
@@ -21,7 +22,8 @@ import (
|
|||||||
"github.com/xtls/xray-core/testing/servers/udp"
|
"github.com/xtls/xray-core/testing/servers/udp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type staticHandler struct{}
|
type staticHandler struct {
|
||||||
|
}
|
||||||
|
|
||||||
func (*staticHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
func (*staticHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||||
ans := new(dns.Msg)
|
ans := new(dns.Msg)
|
@@ -2,13 +2,12 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/log"
|
"github.com/xtls/xray-core/common/log"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
@@ -20,32 +19,33 @@ import (
|
|||||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
"github.com/xtls/xray-core/transport/internet/udp"
|
"github.com/xtls/xray-core/transport/internet/udp"
|
||||||
|
"golang.org/x/net/dns/dnsmessage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ClassicNameServer implemented traditional UDP DNS.
|
|
||||||
type ClassicNameServer struct {
|
type ClassicNameServer struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
name string
|
name string
|
||||||
address *net.Destination
|
address net.Destination
|
||||||
ips map[string]*record
|
ips map[string]record
|
||||||
requests map[uint16]*dnsRequest
|
requests map[uint16]dnsRequest
|
||||||
pub *pubsub.Service
|
pub *pubsub.Service
|
||||||
udpServer *udp.Dispatcher
|
udpServer *udp.Dispatcher
|
||||||
cleanup *task.Periodic
|
cleanup *task.Periodic
|
||||||
reqID uint32
|
reqID uint32
|
||||||
|
clientIP net.IP
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClassicNameServer creates udp server object for remote resolving.
|
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, clientIP net.IP) *ClassicNameServer {
|
||||||
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher) *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{
|
||||||
address: &address,
|
address: address,
|
||||||
ips: make(map[string]*record),
|
ips: make(map[string]record),
|
||||||
requests: make(map[uint16]*dnsRequest),
|
requests: make(map[uint16]dnsRequest),
|
||||||
|
clientIP: clientIP,
|
||||||
pub: pubsub.NewService(),
|
pub: pubsub.NewService(),
|
||||||
name: strings.ToUpper(address.String()),
|
name: strings.ToUpper(address.String()),
|
||||||
}
|
}
|
||||||
@@ -58,12 +58,10 @@ func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name implements Server.
|
|
||||||
func (s *ClassicNameServer) Name() string {
|
func (s *ClassicNameServer) Name() string {
|
||||||
return s.name
|
return s.name
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup clears expired items from cache
|
|
||||||
func (s *ClassicNameServer) Cleanup() error {
|
func (s *ClassicNameServer) Cleanup() error {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
s.Lock()
|
s.Lock()
|
||||||
@@ -82,7 +80,6 @@ func (s *ClassicNameServer) Cleanup() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if record.A == nil && record.AAAA == nil {
|
if record.A == nil && record.AAAA == nil {
|
||||||
newError(s.name, " cleanup ", domain).AtDebug().WriteToLog()
|
|
||||||
delete(s.ips, domain)
|
delete(s.ips, domain)
|
||||||
} else {
|
} else {
|
||||||
s.ips[domain] = record
|
s.ips[domain] = record
|
||||||
@@ -90,7 +87,7 @@ func (s *ClassicNameServer) Cleanup() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(s.ips) == 0 {
|
if len(s.ips) == 0 {
|
||||||
s.ips = make(map[string]*record)
|
s.ips = make(map[string]record)
|
||||||
}
|
}
|
||||||
|
|
||||||
for id, req := range s.requests {
|
for id, req := range s.requests {
|
||||||
@@ -100,13 +97,12 @@ func (s *ClassicNameServer) Cleanup() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(s.requests) == 0 {
|
if len(s.requests) == 0 {
|
||||||
s.requests = make(map[uint16]*dnsRequest)
|
s.requests = make(map[uint16]dnsRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleResponse handles udp response packet from remote DNS server.
|
|
||||||
func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_proto.Packet) {
|
func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_proto.Packet) {
|
||||||
ipRec, err := parseResponse(packet.Payload.Bytes())
|
ipRec, err := parseResponse(packet.Payload.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -138,17 +134,15 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_prot
|
|||||||
elapsed := time.Since(req.start)
|
elapsed := time.Since(req.start)
|
||||||
newError(s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed).AtInfo().WriteToLog()
|
newError(s.name, " got answer: ", req.domain, " ", req.reqType, " -> ", ipRec.IP, " ", elapsed).AtInfo().WriteToLog()
|
||||||
if len(req.domain) > 0 && (rec.A != nil || rec.AAAA != nil) {
|
if len(req.domain) > 0 && (rec.A != nil || rec.AAAA != nil) {
|
||||||
s.updateIP(req.domain, &rec)
|
s.updateIP(req.domain, rec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ClassicNameServer) updateIP(domain string, newRec *record) {
|
func (s *ClassicNameServer) updateIP(domain string, newRec record) {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
|
|
||||||
rec, found := s.ips[domain]
|
newError(s.name, " updating IP records for domain:", domain).AtDebug().WriteToLog()
|
||||||
if !found {
|
rec := s.ips[domain]
|
||||||
rec = &record{}
|
|
||||||
}
|
|
||||||
|
|
||||||
updated := false
|
updated := false
|
||||||
if isNewer(rec.A, newRec.A) {
|
if isNewer(rec.A, newRec.A) {
|
||||||
@@ -161,7 +155,6 @@ func (s *ClassicNameServer) updateIP(domain string, newRec *record) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if updated {
|
if updated {
|
||||||
newError(s.name, " updating IP records for domain:", domain).AtDebug().WriteToLog()
|
|
||||||
s.ips[domain] = rec
|
s.ips[domain] = rec
|
||||||
}
|
}
|
||||||
if newRec.A != nil {
|
if newRec.A != nil {
|
||||||
@@ -184,13 +177,13 @@ func (s *ClassicNameServer) addPendingRequest(req *dnsRequest) {
|
|||||||
|
|
||||||
id := req.msg.ID
|
id := req.msg.ID
|
||||||
req.expire = time.Now().Add(time.Second * 8)
|
req.expire = time.Now().Add(time.Second * 8)
|
||||||
s.requests[id] = req
|
s.requests[id] = *req
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
|
func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, option dns_feature.IPOption) {
|
||||||
newError(s.name, " querying DNS for: ", domain).AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
newError(s.name, " querying DNS for: ", domain).AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
|
||||||
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
|
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP))
|
||||||
|
|
||||||
for _, req := range reqs {
|
for _, req := range reqs {
|
||||||
s.addPendingRequest(req)
|
s.addPendingRequest(req)
|
||||||
@@ -199,7 +192,7 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, client
|
|||||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||||
udpCtx = session.ContextWithInbound(udpCtx, inbound)
|
udpCtx = session.ContextWithInbound(udpCtx, inbound)
|
||||||
}
|
}
|
||||||
|
udpCtx = internet.ContextWithLookupDomain(udpCtx, internet.LookupDomainFromContext(ctx))
|
||||||
udpCtx = session.ContextWithContent(udpCtx, &session.Content{
|
udpCtx = session.ContextWithContent(udpCtx, &session.Content{
|
||||||
Protocol: "dns",
|
Protocol: "dns",
|
||||||
})
|
})
|
||||||
@@ -209,7 +202,7 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, client
|
|||||||
Status: log.AccessAccepted,
|
Status: log.AccessAccepted,
|
||||||
Reason: "",
|
Reason: "",
|
||||||
})
|
})
|
||||||
s.udpServer.Dispatch(udpCtx, *s.address, b)
|
s.udpServer.Dispatch(udpCtx, s.address, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,48 +215,44 @@ func (s *ClassicNameServer) findIPsForDomain(domain string, option dns_feature.I
|
|||||||
return nil, errRecordNotFound
|
return nil, errRecordNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
var err4 error
|
|
||||||
var err6 error
|
|
||||||
var ips []net.Address
|
var ips []net.Address
|
||||||
var ip6 []net.Address
|
var lastErr error
|
||||||
|
|
||||||
if option.IPv4Enable {
|
if option.IPv4Enable {
|
||||||
ips, err4 = record.A.getIPs()
|
a, err := record.A.getIPs()
|
||||||
|
if err != nil {
|
||||||
|
lastErr = err
|
||||||
|
}
|
||||||
|
ips = append(ips, a...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if option.IPv6Enable {
|
if option.IPv6Enable {
|
||||||
ip6, err6 = record.AAAA.getIPs()
|
aaaa, err := record.AAAA.getIPs()
|
||||||
ips = append(ips, ip6...)
|
if err != nil {
|
||||||
|
lastErr = err
|
||||||
|
}
|
||||||
|
ips = append(ips, aaaa...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ips) > 0 {
|
if len(ips) > 0 {
|
||||||
return toNetIP(ips)
|
return toNetIP(ips), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err4 != nil {
|
if lastErr != nil {
|
||||||
return nil, err4
|
return nil, lastErr
|
||||||
}
|
|
||||||
|
|
||||||
if err6 != nil {
|
|
||||||
return nil, err6
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, dns_feature.ErrEmptyResponse
|
return nil, dns_feature.ErrEmptyResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryIP implements Server.
|
// QueryIP implements Server.
|
||||||
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) {
|
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, error) {
|
||||||
fqdn := Fqdn(domain)
|
fqdn := Fqdn(domain)
|
||||||
|
|
||||||
if disableCache {
|
ips, err := s.findIPsForDomain(fqdn, option)
|
||||||
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
if err != errRecordNotFound {
|
||||||
} else {
|
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
|
||||||
ips, err := s.findIPsForDomain(fqdn, option)
|
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err})
|
||||||
if err != errRecordNotFound {
|
return ips, err
|
||||||
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
|
|
||||||
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
|
|
||||||
return ips, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ipv4 and ipv6 belong to different subscription groups
|
// ipv4 and ipv6 belong to different subscription groups
|
||||||
@@ -292,13 +281,13 @@ func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP
|
|||||||
}
|
}
|
||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
s.sendQuery(ctx, fqdn, clientIP, option)
|
s.sendQuery(ctx, fqdn, option)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
ips, err := s.findIPsForDomain(fqdn, option)
|
ips, err := s.findIPsForDomain(fqdn, option)
|
||||||
if err != errRecordNotFound {
|
if err != errRecordNotFound {
|
||||||
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSQueried, time.Since(start), err})
|
||||||
return ips, err
|
return ips, err
|
||||||
}
|
}
|
||||||
|
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: app/log/command/config.proto
|
// source: app/log/command/config.proto
|
||||||
|
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -20,6 +21,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: app/log/config.proto
|
// source: app/log/config.proto
|
||||||
|
|
||||||
package log
|
package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
log "github.com/xtls/xray-core/common/log"
|
log "github.com/xtls/xray-core/common/log"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
@@ -21,6 +22,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type LogType int32
|
type LogType int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -129,7 +134,7 @@ func (x *Config) GetErrorLogLevel() log.Severity {
|
|||||||
if x != nil {
|
if x != nil {
|
||||||
return x.ErrorLogLevel
|
return x.ErrorLogLevel
|
||||||
}
|
}
|
||||||
return log.Severity(0)
|
return log.Severity_Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetErrorLogPath() string {
|
func (x *Config) GetErrorLogPath() string {
|
||||||
|
@@ -11,7 +11,9 @@ type HandlerCreatorOptions struct {
|
|||||||
|
|
||||||
type HandlerCreator func(LogType, HandlerCreatorOptions) (log.Handler, error)
|
type HandlerCreator func(LogType, HandlerCreatorOptions) (log.Handler, error)
|
||||||
|
|
||||||
var handlerCreatorMap = make(map[LogType]HandlerCreator)
|
var (
|
||||||
|
handlerCreatorMap = make(map[LogType]HandlerCreator)
|
||||||
|
)
|
||||||
|
|
||||||
func RegisterHandlerCreator(logType LogType, f HandlerCreator) error {
|
func RegisterHandlerCreator(logType LogType, f HandlerCreator) error {
|
||||||
if f == nil {
|
if f == nil {
|
||||||
|
@@ -5,7 +5,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/log"
|
"github.com/xtls/xray-core/app/log"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
clog "github.com/xtls/xray-core/common/log"
|
clog "github.com/xtls/xray-core/common/log"
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: app/policy/config.proto
|
// source: app/policy/config.proto
|
||||||
|
|
||||||
package policy
|
package policy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -20,6 +21,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type Second struct {
|
type Second struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: app/proxyman/command/command.proto
|
// source: app/proxyman/command/command.proto
|
||||||
|
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
protocol "github.com/xtls/xray-core/common/protocol"
|
protocol "github.com/xtls/xray-core/common/protocol"
|
||||||
serial "github.com/xtls/xray-core/common/serial"
|
serial "github.com/xtls/xray-core/common/serial"
|
||||||
core "github.com/xtls/xray-core/core"
|
core "github.com/xtls/xray-core/core"
|
||||||
@@ -23,6 +24,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type AddUserOperation struct {
|
type AddUserOperation struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc (unknown)
|
||||||
// source: app/proxyman/config.proto
|
// source: app/proxyman/config.proto
|
||||||
|
|
||||||
package proxyman
|
package proxyman
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
net "github.com/xtls/xray-core/common/net"
|
net "github.com/xtls/xray-core/common/net"
|
||||||
serial "github.com/xtls/xray-core/common/serial"
|
serial "github.com/xtls/xray-core/common/serial"
|
||||||
internet "github.com/xtls/xray-core/transport/internet"
|
internet "github.com/xtls/xray-core/transport/internet"
|
||||||
@@ -23,6 +24,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type KnownProtocols int32
|
type KnownProtocols int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@@ -6,8 +6,6 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/proxyman"
|
"github.com/xtls/xray-core/app/proxyman"
|
||||||
"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"
|
||||||
@@ -56,7 +54,7 @@ func getTProxyType(s *internet.MemoryStreamConfig) internet.SocketConfig_TProxyM
|
|||||||
return s.SocketSettings.Tproxy
|
return s.SocketSettings.Tproxy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *tcpWorker) callback(conn stat.Connection) {
|
func (w *tcpWorker) callback(conn internet.Connection) {
|
||||||
ctx, cancel := context.WithCancel(w.ctx)
|
ctx, cancel := context.WithCancel(w.ctx)
|
||||||
sid := session.NewID()
|
sid := session.NewID()
|
||||||
ctx = session.ContextWithID(ctx, sid)
|
ctx = session.ContextWithID(ctx, sid)
|
||||||
@@ -82,7 +80,7 @@ func (w *tcpWorker) callback(conn stat.Connection) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if w.uplinkCounter != nil || w.downlinkCounter != nil {
|
if w.uplinkCounter != nil || w.downlinkCounter != nil {
|
||||||
conn = &stat.CounterConnection{
|
conn = &internet.StatCouterConnection{
|
||||||
Connection: conn,
|
Connection: conn,
|
||||||
ReadCounter: w.uplinkCounter,
|
ReadCounter: w.uplinkCounter,
|
||||||
WriteCounter: w.downlinkCounter,
|
WriteCounter: w.downlinkCounter,
|
||||||
@@ -119,7 +117,7 @@ func (w *tcpWorker) Proxy() proxy.Inbound {
|
|||||||
|
|
||||||
func (w *tcpWorker) Start() error {
|
func (w *tcpWorker) Start() error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
hub, err := internet.ListenTCP(ctx, w.address, w.port, w.stream, func(conn stat.Connection) {
|
hub, err := internet.ListenTCP(ctx, w.address, w.port, w.stream, func(conn internet.Connection) {
|
||||||
go w.callback(conn)
|
go w.callback(conn)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -438,13 +436,13 @@ type dsWorker struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *dsWorker) callback(conn stat.Connection) {
|
func (w *dsWorker) callback(conn internet.Connection) {
|
||||||
ctx, cancel := context.WithCancel(w.ctx)
|
ctx, cancel := context.WithCancel(w.ctx)
|
||||||
sid := session.NewID()
|
sid := session.NewID()
|
||||||
ctx = session.ContextWithID(ctx, sid)
|
ctx = session.ContextWithID(ctx, sid)
|
||||||
|
|
||||||
if w.uplinkCounter != nil || w.downlinkCounter != nil {
|
if w.uplinkCounter != nil || w.downlinkCounter != nil {
|
||||||
conn = &stat.CounterConnection{
|
conn = &internet.StatCouterConnection{
|
||||||
Connection: conn,
|
Connection: conn,
|
||||||
ReadCounter: w.uplinkCounter,
|
ReadCounter: w.uplinkCounter,
|
||||||
WriteCounter: w.downlinkCounter,
|
WriteCounter: w.downlinkCounter,
|
||||||
@@ -482,10 +480,9 @@ func (w *dsWorker) Proxy() proxy.Inbound {
|
|||||||
func (w *dsWorker) Port() net.Port {
|
func (w *dsWorker) Port() net.Port {
|
||||||
return net.Port(0)
|
return net.Port(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *dsWorker) Start() error {
|
func (w *dsWorker) Start() error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
hub, err := internet.ListenUnix(ctx, w.address, w.stream, func(conn stat.Connection) {
|
hub, err := internet.ListenUnix(ctx, w.address, w.stream, func(conn internet.Connection) {
|
||||||
go w.callback(conn)
|
go w.callback(conn)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -3,8 +3,6 @@ package outbound
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/proxyman"
|
"github.com/xtls/xray-core/app/proxyman"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/mux"
|
"github.com/xtls/xray-core/common/mux"
|
||||||
@@ -160,7 +158,7 @@ func (h *Handler) Address() net.Address {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dial implements internet.Dialer.
|
// Dial implements internet.Dialer.
|
||||||
func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connection, error) {
|
func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Connection, error) {
|
||||||
if h.senderSettings != nil {
|
if h.senderSettings != nil {
|
||||||
if h.senderSettings.ProxySettings.HasTag() {
|
if h.senderSettings.ProxySettings.HasTag() {
|
||||||
tag := h.senderSettings.ProxySettings.Tag
|
tag := h.senderSettings.ProxySettings.Tag
|
||||||
@@ -203,9 +201,9 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
|
|||||||
return h.getStatCouterConnection(conn), err
|
return h.getStatCouterConnection(conn), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) getStatCouterConnection(conn stat.Connection) stat.Connection {
|
func (h *Handler) getStatCouterConnection(conn internet.Connection) internet.Connection {
|
||||||
if h.uplinkCounter != nil || h.downlinkCounter != nil {
|
if h.uplinkCounter != nil || h.downlinkCounter != nil {
|
||||||
return &stat.CounterConnection{
|
return &internet.StatCouterConnection{
|
||||||
Connection: conn,
|
Connection: conn,
|
||||||
ReadCounter: h.downlinkCounter,
|
ReadCounter: h.downlinkCounter,
|
||||||
WriteCounter: h.uplinkCounter,
|
WriteCounter: h.uplinkCounter,
|
||||||
|
@@ -4,8 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/policy"
|
"github.com/xtls/xray-core/app/policy"
|
||||||
. "github.com/xtls/xray-core/app/proxyman/outbound"
|
. "github.com/xtls/xray-core/app/proxyman/outbound"
|
||||||
"github.com/xtls/xray-core/app/stats"
|
"github.com/xtls/xray-core/app/stats"
|
||||||
@@ -14,6 +12,7 @@ import (
|
|||||||
core "github.com/xtls/xray-core/core"
|
core "github.com/xtls/xray-core/core"
|
||||||
"github.com/xtls/xray-core/features/outbound"
|
"github.com/xtls/xray-core/features/outbound"
|
||||||
"github.com/xtls/xray-core/proxy/freedom"
|
"github.com/xtls/xray-core/proxy/freedom"
|
||||||
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInterfaces(t *testing.T) {
|
func TestInterfaces(t *testing.T) {
|
||||||
@@ -45,9 +44,9 @@ func TestOutboundWithoutStatCounter(t *testing.T) {
|
|||||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||||
})
|
})
|
||||||
conn, _ := h.(*Handler).Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146))
|
conn, _ := h.(*Handler).Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146))
|
||||||
_, ok := conn.(*stat.CounterConnection)
|
_, ok := conn.(*internet.StatCouterConnection)
|
||||||
if ok {
|
if ok {
|
||||||
t.Errorf("Expected conn to not be CounterConnection")
|
t.Errorf("Expected conn to not be StatCouterConnection")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,8 +73,8 @@ func TestOutboundWithStatCounter(t *testing.T) {
|
|||||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||||
})
|
})
|
||||||
conn, _ := h.(*Handler).Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146))
|
conn, _ := h.(*Handler).Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), 13146))
|
||||||
_, ok := conn.(*stat.CounterConnection)
|
_, ok := conn.(*internet.StatCouterConnection)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("Expected conn to be CounterConnection")
|
t.Errorf("Expected conn to be StatCouterConnection")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
"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"
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: app/reverse/config.proto
|
// source: app/reverse/config.proto
|
||||||
|
|
||||||
package reverse
|
package reverse
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -20,6 +21,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type Control_State int32
|
type Control_State int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@@ -6,7 +6,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
"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/mux"
|
"github.com/xtls/xray-core/common/mux"
|
||||||
|
@@ -9,7 +9,8 @@ type BalancingStrategy interface {
|
|||||||
PickOutbound([]string) string
|
PickOutbound([]string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
type RandomStrategy struct{}
|
type RandomStrategy struct {
|
||||||
|
}
|
||||||
|
|
||||||
func (s *RandomStrategy) PickOutbound(tags []string) string {
|
func (s *RandomStrategy) PickOutbound(tags []string) string {
|
||||||
n := len(tags)
|
n := len(tags)
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: app/router/command/command.proto
|
// source: app/router/command/command.proto
|
||||||
|
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
net "github.com/xtls/xray-core/common/net"
|
net "github.com/xtls/xray-core/common/net"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
@@ -21,6 +22,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
// RoutingContext is the context with information relative to routing process.
|
// RoutingContext is the context with information relative to routing process.
|
||||||
// It conforms to the structure of xray.features.routing.Context and
|
// It conforms to the structure of xray.features.routing.Context and
|
||||||
// xray.features.routing.Route.
|
// xray.features.routing.Route.
|
||||||
@@ -86,7 +91,7 @@ func (x *RoutingContext) GetNetwork() net.Network {
|
|||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Network
|
return x.Network
|
||||||
}
|
}
|
||||||
return net.Network(0)
|
return net.Network_Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RoutingContext) GetSourceIPs() [][]byte {
|
func (x *RoutingContext) GetSourceIPs() [][]byte {
|
||||||
|
@@ -8,9 +8,6 @@ import (
|
|||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/google/go-cmp/cmp/cmpopts"
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/test/bufconn"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/router"
|
"github.com/xtls/xray-core/app/router"
|
||||||
. "github.com/xtls/xray-core/app/router/command"
|
. "github.com/xtls/xray-core/app/router/command"
|
||||||
"github.com/xtls/xray-core/app/stats"
|
"github.com/xtls/xray-core/app/stats"
|
||||||
@@ -18,6 +15,8 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
"github.com/xtls/xray-core/testing/mocks"
|
"github.com/xtls/xray-core/testing/mocks"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/test/bufconn"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestServiceSubscribeRoutingStats(t *testing.T) {
|
func TestServiceSubscribeRoutingStats(t *testing.T) {
|
||||||
@@ -45,6 +44,7 @@ func TestServiceSubscribeRoutingStats(t *testing.T) {
|
|||||||
{SourceIPs: [][]byte{{127, 0, 0, 1}}, Attributes: map[string]string{"attr": "value"}, OutboundTag: "out"},
|
{SourceIPs: [][]byte{{127, 0, 0, 1}}, Attributes: map[string]string{"attr": "value"}, OutboundTag: "out"},
|
||||||
}
|
}
|
||||||
errCh := make(chan error)
|
errCh := make(chan error)
|
||||||
|
nextPub := make(chan struct{})
|
||||||
|
|
||||||
// Server goroutine
|
// Server goroutine
|
||||||
go func() {
|
go func() {
|
||||||
@@ -76,6 +76,13 @@ func TestServiceSubscribeRoutingStats(t *testing.T) {
|
|||||||
if err := publishTestCases(); err != nil {
|
if err := publishTestCases(); err != nil {
|
||||||
errCh <- err
|
errCh <- err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for next round of publishing
|
||||||
|
<-nextPub
|
||||||
|
|
||||||
|
if err := publishTestCases(); err != nil {
|
||||||
|
errCh <- err
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Client goroutine
|
// Client goroutine
|
||||||
@@ -137,92 +144,6 @@ func TestServiceSubscribeRoutingStats(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := testRetrievingAllFields(); err != nil {
|
|
||||||
errCh <- err
|
|
||||||
}
|
|
||||||
errCh <- nil // Client passed all tests successfully
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Wait for goroutines to complete
|
|
||||||
select {
|
|
||||||
case <-time.After(2 * time.Second):
|
|
||||||
t.Fatal("Test timeout after 2s")
|
|
||||||
case err := <-errCh:
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestServiceSubscribeSubsetOfFields(t *testing.T) {
|
|
||||||
c := stats.NewChannel(&stats.ChannelConfig{
|
|
||||||
SubscriberLimit: 1,
|
|
||||||
BufferSize: 0,
|
|
||||||
Blocking: true,
|
|
||||||
})
|
|
||||||
common.Must(c.Start())
|
|
||||||
defer c.Close()
|
|
||||||
|
|
||||||
lis := bufconn.Listen(1024 * 1024)
|
|
||||||
bufDialer := func(context.Context, string) (net.Conn, error) {
|
|
||||||
return lis.Dial()
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []*RoutingContext{
|
|
||||||
{InboundTag: "in", OutboundTag: "out"},
|
|
||||||
{TargetIPs: [][]byte{{1, 2, 3, 4}}, TargetPort: 8080, OutboundTag: "out"},
|
|
||||||
{TargetDomain: "example.com", TargetPort: 443, OutboundTag: "out"},
|
|
||||||
{SourcePort: 9999, TargetPort: 9999, OutboundTag: "out"},
|
|
||||||
{Network: net.Network_UDP, OutboundGroupTags: []string{"outergroup", "innergroup"}, OutboundTag: "out"},
|
|
||||||
{Protocol: "bittorrent", OutboundTag: "blocked"},
|
|
||||||
{User: "example@example.com", OutboundTag: "out"},
|
|
||||||
{SourceIPs: [][]byte{{127, 0, 0, 1}}, Attributes: map[string]string{"attr": "value"}, OutboundTag: "out"},
|
|
||||||
}
|
|
||||||
errCh := make(chan error)
|
|
||||||
|
|
||||||
// Server goroutine
|
|
||||||
go func() {
|
|
||||||
server := grpc.NewServer()
|
|
||||||
RegisterRoutingServiceServer(server, NewRoutingServer(nil, c))
|
|
||||||
errCh <- server.Serve(lis)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Publisher goroutine
|
|
||||||
go func() {
|
|
||||||
publishTestCases := func() error {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
||||||
defer cancel()
|
|
||||||
for { // Wait until there's one subscriber in routing stats channel
|
|
||||||
if len(c.Subscribers()) > 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if ctx.Err() != nil {
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, tc := range testCases {
|
|
||||||
c.Publish(context.Background(), AsRoutingRoute(tc))
|
|
||||||
time.Sleep(time.Millisecond)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := publishTestCases(); err != nil {
|
|
||||||
errCh <- err
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Client goroutine
|
|
||||||
go func() {
|
|
||||||
defer lis.Close()
|
|
||||||
conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure())
|
|
||||||
if err != nil {
|
|
||||||
errCh <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
client := NewRoutingServiceClient(conn)
|
|
||||||
|
|
||||||
// Test retrieving only a subset of fields
|
// Test retrieving only a subset of fields
|
||||||
testRetrievingSubsetOfFields := func() error {
|
testRetrievingSubsetOfFields := func() error {
|
||||||
streamCtx, streamClose := context.WithCancel(context.Background())
|
streamCtx, streamClose := context.WithCancel(context.Background())
|
||||||
@@ -234,6 +155,9 @@ func TestServiceSubscribeSubsetOfFields(t *testing.T) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send nextPub signal to start next round of publishing
|
||||||
|
close(nextPub)
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
msg, err := stream.Recv()
|
msg, err := stream.Recv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -255,6 +179,10 @@ func TestServiceSubscribeSubsetOfFields(t *testing.T) {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := testRetrievingAllFields(); err != nil {
|
||||||
|
errCh <- err
|
||||||
|
}
|
||||||
if err := testRetrievingSubsetOfFields(); err != nil {
|
if err := testRetrievingSubsetOfFields(); err != nil {
|
||||||
errCh <- err
|
errCh <- err
|
||||||
}
|
}
|
||||||
|
@@ -28,13 +28,6 @@ func (c routingContext) GetTargetPort() net.Port {
|
|||||||
return net.Port(c.RoutingContext.GetTargetPort())
|
return net.Port(c.RoutingContext.GetTargetPort())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSkipDNSResolve is a mock implementation here to match the interface,
|
|
||||||
// SkipDNSResolve is set from dns module, no use if coming from a protobuf object?
|
|
||||||
// TODO: please confirm @Vigilans
|
|
||||||
func (c routingContext) GetSkipDNSResolve() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// AsRoutingContext converts a protobuf RoutingContext into an implementation of routing.Context.
|
// AsRoutingContext converts a protobuf RoutingContext into an implementation of routing.Context.
|
||||||
func AsRoutingContext(r *RoutingContext) routing.Context {
|
func AsRoutingContext(r *RoutingContext) routing.Context {
|
||||||
return routingContext{r}
|
return routingContext{r}
|
||||||
|
@@ -186,4 +186,6 @@ func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) {
|
|||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var globalGeoIPContainer GeoIPMatcherContainer
|
var (
|
||||||
|
globalGeoIPContainer GeoIPMatcherContainer
|
||||||
|
)
|
||||||
|
@@ -6,7 +6,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/router"
|
"github.com/xtls/xray-core/app/router"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
@@ -89,8 +88,7 @@ func TestGeoIPMatcher(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Input: "192.0.1.0",
|
Input: "192.0.1.0",
|
||||||
Output: false,
|
Output: false,
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
Input: "0.1.0.0",
|
Input: "0.1.0.0",
|
||||||
Output: true,
|
Output: true,
|
||||||
},
|
},
|
||||||
|
@@ -84,6 +84,7 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
|
|||||||
newError("MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)").AtDebug().WriteToLog()
|
newError("MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)").AtDebug().WriteToLog()
|
||||||
conds.Add(matcher)
|
conds.Add(matcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rr.UserEmail) > 0 {
|
if len(rr.UserEmail) > 0 {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.26.0
|
||||||
// protoc v3.18.0
|
// protoc v3.15.8
|
||||||
// source: app/router/config.proto
|
// source: app/router/config.proto
|
||||||
|
|
||||||
package router
|
package router
|
||||||
|
@@ -80,12 +80,7 @@ func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) {
|
func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) {
|
||||||
// SkipDNSResolve is set from DNS module.
|
if r.domainStrategy == Config_IpOnDemand {
|
||||||
// the DOH remote server maybe a domain name,
|
|
||||||
// this prevents cycle resolving dead loop
|
|
||||||
skipDNSResolve := ctx.GetSkipDNSResolve()
|
|
||||||
|
|
||||||
if r.domainStrategy == Config_IpOnDemand && !skipDNSResolve {
|
|
||||||
ctx = routing_dns.ContextWithDNSClient(ctx, r.dns)
|
ctx = routing_dns.ContextWithDNSClient(ctx, r.dns)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +90,7 @@ func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.domainStrategy != Config_IpIfNonMatch || len(ctx.GetTargetDomain()) == 0 || skipDNSResolve {
|
if r.domainStrategy != Config_IpIfNonMatch || len(ctx.GetTargetDomain()) == 0 {
|
||||||
return nil, ctx, common.ErrNoClue
|
return nil, ctx, common.ErrNoClue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,14 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/features/dns"
|
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
|
|
||||||
. "github.com/xtls/xray-core/app/router"
|
. "github.com/xtls/xray-core/app/router"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
"github.com/xtls/xray-core/features/outbound"
|
"github.com/xtls/xray-core/features/outbound"
|
||||||
routing_session "github.com/xtls/xray-core/features/routing/session"
|
routing_session "github.com/xtls/xray-core/features/routing/session"
|
||||||
"github.com/xtls/xray-core/testing/mocks"
|
"github.com/xtls/xray-core/testing/mocks"
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: app/stats/command/command.proto
|
// source: app/stats/command/command.proto
|
||||||
|
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -20,6 +21,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type GetStatsRequest struct {
|
type GetStatsRequest struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: app/stats/config.proto
|
// source: app/stats/config.proto
|
||||||
|
|
||||||
package stats
|
package stats
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -20,6 +21,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@@ -24,17 +24,10 @@ type Buffer struct {
|
|||||||
UDP *net.Destination
|
UDP *net.Destination
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a Buffer with 0 length and 8K capacity.
|
// New creates a Buffer with 0 length and 2K capacity.
|
||||||
func New() *Buffer {
|
func New() *Buffer {
|
||||||
buf := pool.Get().([]byte)
|
|
||||||
if cap(buf) >= Size {
|
|
||||||
buf = buf[:Size]
|
|
||||||
} else {
|
|
||||||
buf = make([]byte, Size)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Buffer{
|
return &Buffer{
|
||||||
v: buf,
|
v: pool.Get().([]byte),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,15 +50,8 @@ func NewExisted(b []byte) *Buffer {
|
|||||||
// StackNew creates a new Buffer object on stack.
|
// StackNew creates a new Buffer object on stack.
|
||||||
// This method is for buffers that is released in the same function.
|
// This method is for buffers that is released in the same function.
|
||||||
func StackNew() Buffer {
|
func StackNew() Buffer {
|
||||||
buf := pool.Get().([]byte)
|
|
||||||
if cap(buf) >= Size {
|
|
||||||
buf = buf[:Size]
|
|
||||||
} else {
|
|
||||||
buf = make([]byte, Size)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Buffer{
|
return Buffer{
|
||||||
v: buf,
|
v: pool.Get().([]byte),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,10 +64,7 @@ func (b *Buffer) Release() {
|
|||||||
p := b.v
|
p := b.v
|
||||||
b.v = nil
|
b.v = nil
|
||||||
b.Clear()
|
b.Clear()
|
||||||
|
pool.Put(p)
|
||||||
if cap(p) == Size {
|
|
||||||
pool.Put(p)
|
|
||||||
}
|
|
||||||
b.UDP = nil
|
b.UDP = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,7 +6,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
@@ -78,7 +77,6 @@ func TestBufferByte(t *testing.T) {
|
|||||||
buffer.Release()
|
buffer.Release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBufferResize(t *testing.T) {
|
func TestBufferResize(t *testing.T) {
|
||||||
buffer := New()
|
buffer := New()
|
||||||
defer buffer.Release()
|
defer buffer.Release()
|
||||||
|
@@ -6,9 +6,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/features/stats"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reader extends io.Reader with MultiBuffer.
|
// Reader extends io.Reader with MultiBuffer.
|
||||||
@@ -32,17 +29,9 @@ type Writer interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteAllBytes ensures all bytes are written into the given writer.
|
// WriteAllBytes ensures all bytes are written into the given writer.
|
||||||
func WriteAllBytes(writer io.Writer, payload []byte, c stats.Counter) error {
|
func WriteAllBytes(writer io.Writer, payload []byte) error {
|
||||||
wc := 0
|
|
||||||
defer func() {
|
|
||||||
if c != nil {
|
|
||||||
c.Add(int64(wc))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for len(payload) > 0 {
|
for len(payload) > 0 {
|
||||||
n, err := writer.Write(payload)
|
n, err := writer.Write(payload)
|
||||||
wc += n
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -76,13 +65,7 @@ func NewReader(reader io.Reader) Reader {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
newError("failed to get sysconn").Base(err).WriteToLog()
|
newError("failed to get sysconn").Base(err).WriteToLog()
|
||||||
} else {
|
} else {
|
||||||
var counter stats.Counter
|
return NewReadVReader(reader, rawConn)
|
||||||
|
|
||||||
if statConn, ok := reader.(*stat.CounterConnection); ok {
|
|
||||||
reader = statConn.Connection
|
|
||||||
counter = statConn.ReadCounter
|
|
||||||
}
|
|
||||||
return NewReadVReader(reader, rawConn, counter)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,24 +104,13 @@ func NewWriter(writer io.Writer) Writer {
|
|||||||
return mw
|
return mw
|
||||||
}
|
}
|
||||||
|
|
||||||
iConn := writer
|
if isPacketWriter(writer) {
|
||||||
if statConn, ok := writer.(*stat.CounterConnection); ok {
|
|
||||||
iConn = statConn.Connection
|
|
||||||
}
|
|
||||||
|
|
||||||
if isPacketWriter(iConn) {
|
|
||||||
return &SequentialWriter{
|
return &SequentialWriter{
|
||||||
Writer: writer,
|
Writer: writer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var counter stats.Counter
|
|
||||||
|
|
||||||
if statConn, ok := writer.(*stat.CounterConnection); ok {
|
|
||||||
counter = statConn.WriteCounter
|
|
||||||
}
|
|
||||||
return &BufferToBytesWriter{
|
return &BufferToBytesWriter{
|
||||||
Writer: iConn,
|
Writer: writer,
|
||||||
counter: counter,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,11 +4,11 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
@@ -119,7 +119,7 @@ func TestMultiBufferReadAllToByte(t *testing.T) {
|
|||||||
common.Must(err)
|
common.Must(err)
|
||||||
f.Close()
|
f.Close()
|
||||||
|
|
||||||
cnt, err := os.ReadFile(dat)
|
cnt, err := ioutil.ReadFile(dat)
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
if d := cmp.Diff(buf2, cnt); d != "" {
|
if d := cmp.Diff(buf2, cnt); d != "" {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
//go:build !windows && !wasm && !illumos
|
// +build !windows
|
||||||
// +build !windows,!wasm,!illumos
|
// +build !wasm
|
||||||
|
// +build !illumos
|
||||||
|
|
||||||
package buf
|
package buf
|
||||||
|
|
||||||
|
@@ -1,14 +1,12 @@
|
|||||||
//go:build !wasm
|
|
||||||
// +build !wasm
|
// +build !wasm
|
||||||
|
|
||||||
package buf
|
package buf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/features/stats"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/platform"
|
"github.com/xtls/xray-core/common/platform"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -56,19 +54,17 @@ type ReadVReader struct {
|
|||||||
rawConn syscall.RawConn
|
rawConn syscall.RawConn
|
||||||
mr multiReader
|
mr multiReader
|
||||||
alloc allocStrategy
|
alloc allocStrategy
|
||||||
counter stats.Counter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewReadVReader creates a new ReadVReader.
|
// NewReadVReader creates a new ReadVReader.
|
||||||
func NewReadVReader(reader io.Reader, rawConn syscall.RawConn, counter stats.Counter) *ReadVReader {
|
func NewReadVReader(reader io.Reader, rawConn syscall.RawConn) *ReadVReader {
|
||||||
return &ReadVReader{
|
return &ReadVReader{
|
||||||
Reader: reader,
|
Reader: reader,
|
||||||
rawConn: rawConn,
|
rawConn: rawConn,
|
||||||
alloc: allocStrategy{
|
alloc: allocStrategy{
|
||||||
current: 1,
|
current: 1,
|
||||||
},
|
},
|
||||||
mr: newMultiReader(),
|
mr: newMultiReader(),
|
||||||
counter: counter,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,16 +123,10 @@ func (r *ReadVReader) ReadMultiBuffer() (MultiBuffer, error) {
|
|||||||
if b.IsFull() {
|
if b.IsFull() {
|
||||||
r.alloc.Adjust(1)
|
r.alloc.Adjust(1)
|
||||||
}
|
}
|
||||||
if r.counter != nil && b != nil {
|
|
||||||
r.counter.Add(int64(b.Len()))
|
|
||||||
}
|
|
||||||
return MultiBuffer{b}, err
|
return MultiBuffer{b}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mb, err := r.readMulti()
|
mb, err := r.readMulti()
|
||||||
if r.counter != nil && mb != nil {
|
|
||||||
r.counter.Add(int64(mb.Len()))
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -144,13 +134,17 @@ func (r *ReadVReader) ReadMultiBuffer() (MultiBuffer, error) {
|
|||||||
return mb, nil
|
return mb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var useReadv bool
|
var useReadv = true
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
const defaultFlagValue = "NOT_DEFINED_AT_ALL"
|
const defaultFlagValue = "NOT_DEFINED_AT_ALL"
|
||||||
value := platform.NewEnvFlag("xray.buf.readv").GetValue(func() string { return defaultFlagValue })
|
value := platform.NewEnvFlag("xray.buf.readv").GetValue(func() string { return defaultFlagValue })
|
||||||
switch value {
|
switch value {
|
||||||
case defaultFlagValue, "auto", "enable":
|
case defaultFlagValue, "auto":
|
||||||
|
if (runtime.GOARCH == "386" || runtime.GOARCH == "amd64" || runtime.GOARCH == "s390x") && (runtime.GOOS == "linux" || runtime.GOOS == "darwin" || runtime.GOOS == "windows") {
|
||||||
|
useReadv = true
|
||||||
|
}
|
||||||
|
case "enable":
|
||||||
useReadv = true
|
useReadv = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
//go:build wasm
|
|
||||||
// +build wasm
|
// +build wasm
|
||||||
|
|
||||||
package buf
|
package buf
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
//go:build !wasm
|
|
||||||
// +build !wasm
|
// +build !wasm
|
||||||
|
|
||||||
package buf_test
|
package buf_test
|
||||||
@@ -10,11 +9,10 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
|
|
||||||
"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/testing/servers/tcp"
|
"github.com/xtls/xray-core/testing/servers/tcp"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestReadvReader(t *testing.T) {
|
func TestReadvReader(t *testing.T) {
|
||||||
@@ -52,7 +50,7 @@ func TestReadvReader(t *testing.T) {
|
|||||||
rawConn, err := conn.(*net.TCPConn).SyscallConn()
|
rawConn, err := conn.(*net.TCPConn).SyscallConn()
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
reader := NewReadVReader(conn, rawConn, nil)
|
reader := NewReadVReader(conn, rawConn)
|
||||||
var rmb MultiBuffer
|
var rmb MultiBuffer
|
||||||
for {
|
for {
|
||||||
mb, err := reader.ReadMultiBuffer()
|
mb, err := reader.ReadMultiBuffer()
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
//go:build illumos
|
|
||||||
// +build illumos
|
// +build illumos
|
||||||
|
|
||||||
package buf
|
package buf
|
||||||
|
@@ -5,8 +5,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/features/stats"
|
|
||||||
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
@@ -15,8 +13,7 @@ import (
|
|||||||
type BufferToBytesWriter struct {
|
type BufferToBytesWriter struct {
|
||||||
io.Writer
|
io.Writer
|
||||||
|
|
||||||
counter stats.Counter
|
cache [][]byte
|
||||||
cache [][]byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteMultiBuffer implements Writer. This method takes ownership of the given buffer.
|
// WriteMultiBuffer implements Writer. This method takes ownership of the given buffer.
|
||||||
@@ -29,7 +26,7 @@ func (w *BufferToBytesWriter) WriteMultiBuffer(mb MultiBuffer) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(mb) == 1 {
|
if len(mb) == 1 {
|
||||||
return WriteAllBytes(w.Writer, mb[0].Bytes(), w.counter)
|
return WriteAllBytes(w.Writer, mb[0].Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
if cap(w.cache) < len(mb) {
|
if cap(w.cache) < len(mb) {
|
||||||
@@ -48,15 +45,9 @@ func (w *BufferToBytesWriter) WriteMultiBuffer(mb MultiBuffer) error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
nb := net.Buffers(bs)
|
nb := net.Buffers(bs)
|
||||||
wc := int64(0)
|
|
||||||
defer func() {
|
|
||||||
if w.counter != nil {
|
|
||||||
w.counter.Add(wc)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
for size > 0 {
|
for size > 0 {
|
||||||
n, err := nb.WriteTo(w.Writer)
|
n, err := nb.WriteTo(w.Writer)
|
||||||
wc += n
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -182,7 +173,7 @@ func (w *BufferedWriter) flushInternal() error {
|
|||||||
w.buffer = nil
|
w.buffer = nil
|
||||||
|
|
||||||
if writer, ok := w.writer.(io.Writer); ok {
|
if writer, ok := w.writer.(io.Writer); ok {
|
||||||
err := WriteAllBytes(writer, b.Bytes(), nil)
|
err := WriteAllBytes(writer, b.Bytes())
|
||||||
b.Release()
|
b.Release()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
38
common/cache/lru.go
vendored
38
common/cache/lru.go
vendored
@@ -2,7 +2,7 @@ package cache
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"sync"
|
sync "sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Lru simple, fast lru cache implementation
|
// Lru simple, fast lru cache implementation
|
||||||
@@ -28,7 +28,7 @@ type lruElement struct {
|
|||||||
|
|
||||||
// NewLru init a lru cache
|
// NewLru init a lru cache
|
||||||
func NewLru(cap int) Lru {
|
func NewLru(cap int) Lru {
|
||||||
return &lru{
|
return lru{
|
||||||
capacity: cap,
|
capacity: cap,
|
||||||
doubleLinkedlist: list.New(),
|
doubleLinkedlist: list.New(),
|
||||||
keyToElement: new(sync.Map),
|
keyToElement: new(sync.Map),
|
||||||
@@ -37,53 +37,49 @@ func NewLru(cap int) Lru {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *lru) Get(key interface{}) (value interface{}, ok bool) {
|
func (l lru) Get(key interface{}) (value interface{}, ok bool) {
|
||||||
l.mu.Lock()
|
|
||||||
defer l.mu.Unlock()
|
|
||||||
if v, ok := l.keyToElement.Load(key); ok {
|
if v, ok := l.keyToElement.Load(key); ok {
|
||||||
element := v.(*list.Element)
|
element := v.(*list.Element)
|
||||||
l.doubleLinkedlist.MoveToFront(element)
|
l.doubleLinkedlist.MoveBefore(element, l.doubleLinkedlist.Front())
|
||||||
return element.Value.(*lruElement).value, true
|
return element.Value.(lruElement).value, true
|
||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *lru) GetKeyFromValue(value interface{}) (key interface{}, ok bool) {
|
func (l lru) GetKeyFromValue(value interface{}) (key interface{}, ok bool) {
|
||||||
l.mu.Lock()
|
|
||||||
defer l.mu.Unlock()
|
|
||||||
if k, ok := l.valueToElement.Load(value); ok {
|
if k, ok := l.valueToElement.Load(value); ok {
|
||||||
element := k.(*list.Element)
|
element := k.(*list.Element)
|
||||||
l.doubleLinkedlist.MoveToFront(element)
|
l.doubleLinkedlist.MoveBefore(element, l.doubleLinkedlist.Front())
|
||||||
return element.Value.(*lruElement).key, true
|
return element.Value.(lruElement).key, true
|
||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *lru) PeekKeyFromValue(value interface{}) (key interface{}, ok bool) {
|
func (l lru) PeekKeyFromValue(value interface{}) (key interface{}, ok bool) {
|
||||||
if k, ok := l.valueToElement.Load(value); ok {
|
if k, ok := l.valueToElement.Load(value); ok {
|
||||||
element := k.(*list.Element)
|
element := k.(*list.Element)
|
||||||
return element.Value.(*lruElement).key, true
|
return element.Value.(lruElement).key, true
|
||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *lru) Put(key, value interface{}) {
|
func (l lru) Put(key, value interface{}) {
|
||||||
l.mu.Lock()
|
e := lruElement{key, value}
|
||||||
e := &lruElement{key, value}
|
|
||||||
if v, ok := l.keyToElement.Load(key); ok {
|
if v, ok := l.keyToElement.Load(key); ok {
|
||||||
element := v.(*list.Element)
|
element := v.(*list.Element)
|
||||||
element.Value = e
|
element.Value = e
|
||||||
l.doubleLinkedlist.MoveToFront(element)
|
l.doubleLinkedlist.MoveBefore(element, l.doubleLinkedlist.Front())
|
||||||
} else {
|
} else {
|
||||||
|
l.mu.Lock()
|
||||||
element := l.doubleLinkedlist.PushFront(e)
|
element := l.doubleLinkedlist.PushFront(e)
|
||||||
l.keyToElement.Store(key, element)
|
l.keyToElement.Store(key, element)
|
||||||
l.valueToElement.Store(value, element)
|
l.valueToElement.Store(value, element)
|
||||||
if l.doubleLinkedlist.Len() > l.capacity {
|
if l.doubleLinkedlist.Len() > l.capacity {
|
||||||
toBeRemove := l.doubleLinkedlist.Back()
|
toBeRemove := l.doubleLinkedlist.Back()
|
||||||
l.doubleLinkedlist.Remove(toBeRemove)
|
l.doubleLinkedlist.Remove(toBeRemove)
|
||||||
l.keyToElement.Delete(toBeRemove.Value.(*lruElement).key)
|
l.keyToElement.Delete(toBeRemove.Value.(lruElement).key)
|
||||||
l.valueToElement.Delete(toBeRemove.Value.(*lruElement).value)
|
l.valueToElement.Delete(toBeRemove.Value.(lruElement).value)
|
||||||
}
|
}
|
||||||
|
l.mu.Unlock()
|
||||||
}
|
}
|
||||||
l.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ package common
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/build"
|
"go/build"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -14,8 +15,10 @@ import (
|
|||||||
|
|
||||||
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
||||||
|
|
||||||
// ErrNoClue is for the situation that existing information is not enough to make a decision. For example, Router may return this error when there is no suitable route.
|
var (
|
||||||
var ErrNoClue = errors.New("not enough information for making a decision")
|
// ErrNoClue is for the situation that existing information is not enough to make a decision. For example, Router may return this error when there is no suitable route.
|
||||||
|
ErrNoClue = errors.New("not enough information for making a decision")
|
||||||
|
)
|
||||||
|
|
||||||
// Must panics if err is not nil.
|
// Must panics if err is not nil.
|
||||||
func Must(err error) {
|
func Must(err error) {
|
||||||
@@ -66,7 +69,7 @@ func GetRuntimeEnv(key string) (string, error) {
|
|||||||
}
|
}
|
||||||
var data []byte
|
var data []byte
|
||||||
var runtimeEnv string
|
var runtimeEnv string
|
||||||
data, readErr := os.ReadFile(file)
|
data, readErr := ioutil.ReadFile(file)
|
||||||
if readErr != nil {
|
if readErr != nil {
|
||||||
return "", readErr
|
return "", readErr
|
||||||
}
|
}
|
||||||
@@ -128,7 +131,7 @@ func GetModuleName(pathToProjectRoot string) (string, error) {
|
|||||||
for {
|
for {
|
||||||
if idx := strings.LastIndex(loopPath, string(filepath.Separator)); idx >= 0 {
|
if idx := strings.LastIndex(loopPath, string(filepath.Separator)); idx >= 0 {
|
||||||
gomodPath := filepath.Join(loopPath, "go.mod")
|
gomodPath := filepath.Join(loopPath, "go.mod")
|
||||||
gomodBytes, err := os.ReadFile(gomodPath)
|
gomodBytes, err := ioutil.ReadFile(gomodPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
loopPath = loopPath[:idx]
|
loopPath = loopPath[:idx]
|
||||||
continue
|
continue
|
||||||
|
@@ -290,6 +290,7 @@ func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error {
|
|||||||
mb = nb
|
mb = nb
|
||||||
|
|
||||||
eb, err := w.seal(rawBytes[:nBytes])
|
eb, err := w.seal(rawBytes[:nBytes])
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
buf.ReleaseMulti(mb2Write)
|
buf.ReleaseMulti(mb2Write)
|
||||||
return err
|
return err
|
||||||
|
@@ -18,7 +18,7 @@ func mustDecodeHex(s string) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestChaCha20Stream(t *testing.T) {
|
func TestChaCha20Stream(t *testing.T) {
|
||||||
cases := []struct {
|
var cases = []struct {
|
||||||
key []byte
|
key []byte
|
||||||
iv []byte
|
iv []byte
|
||||||
output []byte
|
output []byte
|
||||||
|
@@ -3,7 +3,7 @@ package internal
|
|||||||
import "encoding/binary"
|
import "encoding/binary"
|
||||||
|
|
||||||
func ChaCha20Block(s *[16]uint32, out []byte, rounds int) {
|
func ChaCha20Block(s *[16]uint32, out []byte, rounds int) {
|
||||||
x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 := s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], s[12], s[13], s[14], s[15]
|
var x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 = s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], s[12], s[13], s[14], s[15]
|
||||||
for i := 0; i < rounds; i += 2 {
|
for i := 0; i < rounds; i += 2 {
|
||||||
var x uint32
|
var x uint32
|
||||||
|
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
//go:build generate
|
|
||||||
// +build generate
|
// +build generate
|
||||||
|
|
||||||
package main
|
package main
|
||||||
@@ -56,7 +55,7 @@ func ChaCha20Block(s *[16]uint32, out []byte, rounds int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
file, err := os.OpenFile("chacha_core.generated.go", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0o644)
|
file, err := os.OpenFile("chacha_core.generated.go", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to generate chacha_core.go: %v", err)
|
log.Fatalf("Failed to generate chacha_core.go: %v", err)
|
||||||
}
|
}
|
||||||
|
@@ -27,7 +27,9 @@ func (r *CryptionReader) Read(data []byte) (int, error) {
|
|||||||
return nBytes, err
|
return nBytes, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ buf.Writer = (*CryptionWriter)(nil)
|
var (
|
||||||
|
_ buf.Writer = (*CryptionWriter)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
type CryptionWriter struct {
|
type CryptionWriter struct {
|
||||||
stream cipher.Stream
|
stream cipher.Stream
|
||||||
@@ -48,7 +50,7 @@ func NewCryptionWriter(stream cipher.Stream, writer io.Writer) *CryptionWriter {
|
|||||||
func (w *CryptionWriter) Write(data []byte) (int, error) {
|
func (w *CryptionWriter) Write(data []byte) (int, error) {
|
||||||
w.stream.XORKeyStream(data, data)
|
w.stream.XORKeyStream(data, data)
|
||||||
|
|
||||||
if err := buf.WriteAllBytes(w.writer, data, nil); err != nil {
|
if err := buf.WriteAllBytes(w.writer, data); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
return len(data), nil
|
return len(data), nil
|
||||||
|
@@ -2,8 +2,11 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -17,21 +20,26 @@ func main() {
|
|||||||
pkg = "core"
|
pkg = "core"
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.OpenFile("errors.generated.go", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0o644)
|
moduleName, gmnErr := common.GetModuleName(pwd)
|
||||||
|
if gmnErr != nil {
|
||||||
|
fmt.Println("can not get module path", gmnErr)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.OpenFile("errors.generated.go", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to generate errors.generated.go: %v", err)
|
log.Fatalf("Failed to generate errors.generated.go: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
fmt.Fprintf(file, `package %s
|
fmt.Fprintln(file, "package", pkg)
|
||||||
|
fmt.Fprintln(file, "")
|
||||||
import "github.com/xtls/xray-core/common/errors"
|
fmt.Fprintln(file, "import \""+moduleName+"/common/errors\"")
|
||||||
|
fmt.Fprintln(file, "")
|
||||||
type errPathObjHolder struct{}
|
fmt.Fprintln(file, "type errPathObjHolder struct{}")
|
||||||
|
fmt.Fprintln(file, "")
|
||||||
func newError(values ...interface{}) *errors.Error {
|
fmt.Fprintln(file, "func newError(values ...interface{}) *errors.Error {")
|
||||||
return errors.New(values...).WithPathObj(errPathObjHolder{})
|
fmt.Fprintln(file, " return errors.New(values...).WithPathObj(errPathObjHolder{})")
|
||||||
}
|
fmt.Fprintln(file, "}")
|
||||||
`, pkg)
|
|
||||||
}
|
}
|
||||||
|
@@ -32,7 +32,9 @@ func Record(msg Message) {
|
|||||||
logHandler.Handle(msg)
|
logHandler.Handle(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
var logHandler syncHandler
|
var (
|
||||||
|
logHandler syncHandler
|
||||||
|
)
|
||||||
|
|
||||||
// RegisterHandler register a new handler as current log handler. Previous registered handler will be discarded.
|
// RegisterHandler register a new handler as current log handler. Previous registered handler will be discarded.
|
||||||
func RegisterHandler(handler Handler) {
|
func RegisterHandler(handler Handler) {
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: common/log/log.proto
|
// source: common/log/log.proto
|
||||||
|
|
||||||
package log
|
package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -20,6 +21,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type Severity int32
|
type Severity int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@@ -130,13 +130,13 @@ func CreateStderrLogWriter() WriterCreator {
|
|||||||
|
|
||||||
// CreateFileLogWriter returns a LogWriterCreator that creates LogWriter for the given file.
|
// CreateFileLogWriter returns a LogWriterCreator that creates LogWriter for the given file.
|
||||||
func CreateFileLogWriter(path string) (WriterCreator, error) {
|
func CreateFileLogWriter(path string) (WriterCreator, error) {
|
||||||
file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600)
|
file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
file.Close()
|
file.Close()
|
||||||
return func() Writer {
|
return func() Writer {
|
||||||
file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600)
|
file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package log_test
|
package log_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -12,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestFileLogger(t *testing.T) {
|
func TestFileLogger(t *testing.T) {
|
||||||
f, err := os.CreateTemp("", "vtest")
|
f, err := ioutil.TempFile("", "vtest")
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
path := f.Name()
|
path := f.Name()
|
||||||
common.Must(f.Close())
|
common.Must(f.Close())
|
||||||
|
@@ -142,6 +142,7 @@ func (f *DialingWorkerFactory) Create() (*ClientWorker, error) {
|
|||||||
Reader: downlinkReader,
|
Reader: downlinkReader,
|
||||||
Writer: upLinkWriter,
|
Writer: upLinkWriter,
|
||||||
}, f.Strategy)
|
}, f.Strategy)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -174,10 +175,8 @@ type ClientWorker struct {
|
|||||||
strategy ClientStrategy
|
strategy ClientStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var muxCoolAddress = net.DomainAddress("v1.mux.cool")
|
||||||
muxCoolAddress = net.DomainAddress("v1.mux.cool")
|
var muxCoolPort = net.Port(9527)
|
||||||
muxCoolPort = net.Port(9527)
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewClientWorker creates a new mux.Client.
|
// NewClientWorker creates a new mux.Client.
|
||||||
func NewClientWorker(stream transport.Link, s ClientStrategy) (*ClientWorker, error) {
|
func NewClientWorker(stream transport.Link, s ClientStrategy) (*ClientWorker, error) {
|
||||||
|
@@ -6,7 +6,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
|
|
||||||
"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/mux"
|
"github.com/xtls/xray-core/common/mux"
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: common/net/address.proto
|
// source: common/net/address.proto
|
||||||
|
|
||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -20,6 +21,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
// Address of a network host. It may be either an IP address or a domain
|
// Address of a network host. It may be either an IP address or a domain
|
||||||
// address.
|
// address.
|
||||||
type IPOrDomain struct {
|
type IPOrDomain struct {
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: common/net/destination.proto
|
// source: common/net/destination.proto
|
||||||
|
|
||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -20,6 +21,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
// Endpoint of a network connection.
|
// Endpoint of a network connection.
|
||||||
type Endpoint struct {
|
type Endpoint struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: common/net/network.proto
|
// source: common/net/network.proto
|
||||||
|
|
||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -20,6 +21,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type Network int32
|
type Network int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: common/net/port.proto
|
// source: common/net/port.proto
|
||||||
|
|
||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -20,6 +21,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
// PortRange represents a range of ports.
|
// PortRange represents a range of ports.
|
||||||
type PortRange struct {
|
type PortRange struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
|
@@ -3,21 +3,17 @@ package net
|
|||||||
import "net"
|
import "net"
|
||||||
|
|
||||||
// DialTCP is an alias of net.DialTCP.
|
// DialTCP is an alias of net.DialTCP.
|
||||||
var (
|
var DialTCP = net.DialTCP
|
||||||
DialTCP = net.DialTCP
|
var DialUDP = net.DialUDP
|
||||||
DialUDP = net.DialUDP
|
var DialUnix = net.DialUnix
|
||||||
DialUnix = net.DialUnix
|
var Dial = net.Dial
|
||||||
Dial = net.Dial
|
|
||||||
)
|
|
||||||
|
|
||||||
type ListenConfig = net.ListenConfig
|
type ListenConfig = net.ListenConfig
|
||||||
|
|
||||||
var (
|
var Listen = net.Listen
|
||||||
Listen = net.Listen
|
var ListenTCP = net.ListenTCP
|
||||||
ListenTCP = net.ListenTCP
|
var ListenUDP = net.ListenUDP
|
||||||
ListenUDP = net.ListenUDP
|
var ListenUnix = net.ListenUnix
|
||||||
ListenUnix = net.ListenUnix
|
|
||||||
)
|
|
||||||
|
|
||||||
var LookupIP = net.LookupIP
|
var LookupIP = net.LookupIP
|
||||||
|
|
||||||
@@ -30,54 +26,36 @@ var SplitHostPort = net.SplitHostPort
|
|||||||
|
|
||||||
var CIDRMask = net.CIDRMask
|
var CIDRMask = net.CIDRMask
|
||||||
|
|
||||||
type (
|
type Addr = net.Addr
|
||||||
Addr = net.Addr
|
type Conn = net.Conn
|
||||||
Conn = net.Conn
|
type PacketConn = net.PacketConn
|
||||||
PacketConn = net.PacketConn
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
type TCPAddr = net.TCPAddr
|
||||||
TCPAddr = net.TCPAddr
|
type TCPConn = net.TCPConn
|
||||||
TCPConn = net.TCPConn
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
type UDPAddr = net.UDPAddr
|
||||||
UDPAddr = net.UDPAddr
|
type UDPConn = net.UDPConn
|
||||||
UDPConn = net.UDPConn
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
type UnixAddr = net.UnixAddr
|
||||||
UnixAddr = net.UnixAddr
|
type UnixConn = net.UnixConn
|
||||||
UnixConn = net.UnixConn
|
|
||||||
)
|
|
||||||
|
|
||||||
// IP is an alias for net.IP.
|
// IP is an alias for net.IP.
|
||||||
type (
|
type IP = net.IP
|
||||||
IP = net.IP
|
type IPMask = net.IPMask
|
||||||
IPMask = net.IPMask
|
type IPNet = net.IPNet
|
||||||
IPNet = net.IPNet
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const IPv4len = net.IPv4len
|
||||||
IPv4len = net.IPv4len
|
const IPv6len = net.IPv6len
|
||||||
IPv6len = net.IPv6len
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
type Error = net.Error
|
||||||
Error = net.Error
|
type AddrError = net.AddrError
|
||||||
AddrError = net.AddrError
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
type Dialer = net.Dialer
|
||||||
Dialer = net.Dialer
|
type Listener = net.Listener
|
||||||
Listener = net.Listener
|
type TCPListener = net.TCPListener
|
||||||
TCPListener = net.TCPListener
|
type UnixListener = net.UnixListener
|
||||||
UnixListener = net.UnixListener
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var ResolveUnixAddr = net.ResolveUnixAddr
|
||||||
ResolveUnixAddr = net.ResolveUnixAddr
|
var ResolveUDPAddr = net.ResolveUDPAddr
|
||||||
ResolveUDPAddr = net.ResolveUDPAddr
|
|
||||||
)
|
|
||||||
|
|
||||||
type Resolver = net.Resolver
|
type Resolver = net.Resolver
|
||||||
|
@@ -4,7 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"io"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -56,6 +56,7 @@ func GetOCSPForCert(cert [][]byte) ([]byte, error) {
|
|||||||
pemBundle := bundle.Bytes()
|
pemBundle := bundle.Bytes()
|
||||||
|
|
||||||
certificates, err := parsePEMBundle(pemBundle)
|
certificates, err := parsePEMBundle(pemBundle)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -73,7 +74,7 @@ func GetOCSPForCert(cert [][]byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
issuerBytes, errC := io.ReadAll(resp.Body)
|
issuerBytes, errC := ioutil.ReadAll(resp.Body)
|
||||||
if errC != nil {
|
if errC != nil {
|
||||||
return nil, newError(errC)
|
return nil, newError(errC)
|
||||||
}
|
}
|
||||||
@@ -97,11 +98,13 @@ func GetOCSPForCert(cert [][]byte) ([]byte, error) {
|
|||||||
return nil, newError(err)
|
return nil, newError(err)
|
||||||
}
|
}
|
||||||
defer req.Body.Close()
|
defer req.Body.Close()
|
||||||
ocspResBytes, err := io.ReadAll(req.Body)
|
ocspResBytes, err := ioutil.ReadAll(req.Body)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, newError(err)
|
return nil, newError(err)
|
||||||
}
|
}
|
||||||
return ocspResBytes, nil
|
return ocspResBytes, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parsePEMBundle parses a certificate bundle from top to bottom and returns
|
// parsePEMBundle parses a certificate bundle from top to bottom and returns
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
//go:build !windows
|
|
||||||
// +build !windows
|
// +build !windows
|
||||||
|
|
||||||
package ctlcmd
|
package ctlcmd
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
//go:build windows
|
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package ctlcmd
|
package ctlcmd
|
||||||
|
@@ -33,7 +33,7 @@ func CopyFile(dst string, src string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, 0o644)
|
f, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
//go:build !windows
|
|
||||||
// +build !windows
|
// +build !windows
|
||||||
|
|
||||||
package platform
|
package platform
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
//go:build windows
|
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package platform
|
package platform
|
||||||
|
@@ -6,7 +6,8 @@ import (
|
|||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SniffHeader struct{}
|
type SniffHeader struct {
|
||||||
|
}
|
||||||
|
|
||||||
func (h *SniffHeader) Protocol() string {
|
func (h *SniffHeader) Protocol() string {
|
||||||
return "bittorrent"
|
return "bittorrent"
|
||||||
|
@@ -4,11 +4,10 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/net/dns/dnsmessage"
|
|
||||||
|
|
||||||
"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/serial"
|
"github.com/xtls/xray-core/common/serial"
|
||||||
|
"golang.org/x/net/dns/dnsmessage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PackMessage(msg *dnsmessage.Message) (*buf.Buffer, error) {
|
func PackMessage(msg *dnsmessage.Message) (*buf.Buffer, error) {
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.15.4
|
||||||
// source: common/protocol/headers.proto
|
// source: common/protocol/headers.proto
|
||||||
|
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -20,6 +21,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type SecurityType int32
|
type SecurityType int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.27.1
|
// protoc-gen-go v1.25.0
|
||||||
// protoc v3.18.0
|
// protoc v3.14.0
|
||||||
// source: common/protocol/server_spec.proto
|
// source: common/protocol/server_spec.proto
|
||||||
|
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
net "github.com/xtls/xray-core/common/net"
|
net "github.com/xtls/xray-core/common/net"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
@@ -21,6 +22,10 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
type ServerEndpoint struct {
|
type ServerEndpoint struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user