mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-22 17:46:48 +08:00
Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1dca3cb3dd | ||
![]() |
28d17ac17f | ||
![]() |
e6019a89c9 | ||
![]() |
3213e5dd81 | ||
![]() |
c950edede2 | ||
![]() |
64892fb2c3 | ||
![]() |
0403e6ddc3 | ||
![]() |
d9d239750b | ||
![]() |
73e10f0f6f | ||
![]() |
7a9e72b133 | ||
![]() |
66b58e6076 | ||
![]() |
17cdeac57f | ||
![]() |
e4bf620795 | ||
![]() |
31c7141fef | ||
![]() |
57b9006d26 | ||
![]() |
d9d04a230f | ||
![]() |
3dc9fba20d | ||
![]() |
d45298a10d | ||
![]() |
6e8581f5dc | ||
![]() |
86a8fb5d84 | ||
![]() |
3531b95d82 | ||
![]() |
b977899926 | ||
![]() |
2220411644 | ||
![]() |
3b8618b379 | ||
![]() |
e8a8465220 | ||
![]() |
1f92b948c0 | ||
![]() |
53b99efe78 | ||
![]() |
1e3d739a5b |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -121,7 +121,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ^1.16
|
||||
go-version: ^1.17
|
||||
|
||||
- name: Get project dependencies
|
||||
run: go mod download
|
||||
|
@@ -44,6 +44,7 @@
|
||||
- [Netch (NetFilter & TUN/TAP)](https://github.com/NetchX/Netch)
|
||||
- Android
|
||||
- [v2rayNG](https://github.com/2dust/v2rayNG)
|
||||
- [AnXray](https://github.com/XTLS/AnXray)
|
||||
- [Kitsunebi](https://github.com/rurirei/Kitsunebi/tree/release_xtls)
|
||||
- iOS / Mac
|
||||
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
|
||||
|
@@ -309,10 +309,15 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (Sni
|
||||
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
|
||||
var handler outbound.Handler
|
||||
|
||||
skipRoutePick := false
|
||||
if content := session.ContentFromContext(ctx); content != nil {
|
||||
skipRoutePick = content.SkipRoutePick
|
||||
}
|
||||
|
||||
routingLink := routing_session.AsRoutingContext(ctx)
|
||||
inTag := routingLink.GetInboundTag()
|
||||
isPickRoute := false
|
||||
if d.router != nil {
|
||||
if d.router != nil && !skipRoutePick {
|
||||
if route, err := d.router.PickRoute(routingLink); err == nil {
|
||||
outTag := route.GetOutboundTag()
|
||||
isPickRoute = true
|
||||
|
@@ -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.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.15.8
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.14.0
|
||||
// source: app/dns/config.proto
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
router "github.com/xtls/xray-core/app/router"
|
||||
net "github.com/xtls/xray-core/common/net"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
@@ -22,6 +23,10 @@ const (
|
||||
_ = 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
|
||||
|
||||
const (
|
||||
@@ -74,112 +79,12 @@ func (DomainMatchingType) EnumDescriptor() ([]byte, []int) {
|
||||
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 CacheStrategy int32
|
||||
|
||||
const (
|
||||
CacheStrategy_Cache_ALL CacheStrategy = 0
|
||||
CacheStrategy_Cache_NOERROR CacheStrategy = 1
|
||||
CacheStrategy_Cache_DISABLE CacheStrategy = 2
|
||||
)
|
||||
|
||||
// Enum value maps for CacheStrategy.
|
||||
var (
|
||||
CacheStrategy_name = map[int32]string{
|
||||
0: "Cache_ALL",
|
||||
1: "Cache_NOERROR",
|
||||
2: "Cache_DISABLE",
|
||||
}
|
||||
CacheStrategy_value = map[string]int32{
|
||||
"Cache_ALL": 0,
|
||||
"Cache_NOERROR": 1,
|
||||
"Cache_DISABLE": 2,
|
||||
}
|
||||
)
|
||||
|
||||
func (x CacheStrategy) Enum() *CacheStrategy {
|
||||
p := new(CacheStrategy)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x CacheStrategy) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (CacheStrategy) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_app_dns_config_proto_enumTypes[2].Descriptor()
|
||||
}
|
||||
|
||||
func (CacheStrategy) Type() protoreflect.EnumType {
|
||||
return &file_app_dns_config_proto_enumTypes[2]
|
||||
}
|
||||
|
||||
func (x CacheStrategy) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CacheStrategy.Descriptor instead.
|
||||
func (CacheStrategy) EnumDescriptor() ([]byte, []int) {
|
||||
return file_app_dns_config_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
type NameServer struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Address *net.Endpoint `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
||||
ClientIp []byte `protobuf:"bytes,5,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"`
|
||||
SkipFallback bool `protobuf:"varint,6,opt,name=skipFallback,proto3" json:"skipFallback,omitempty"`
|
||||
PrioritizedDomain []*NameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"`
|
||||
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"`
|
||||
@@ -224,20 +129,6 @@ func (x *NameServer) GetAddress() *net.Endpoint {
|
||||
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 {
|
||||
if x != nil {
|
||||
return x.PrioritizedDomain
|
||||
@@ -283,10 +174,6 @@ type Config struct {
|
||||
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 string `protobuf:"bytes,6,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
// DisableCache disables DNS cache
|
||||
CacheStrategy CacheStrategy `protobuf:"varint,8,opt,name=cache_strategy,json=cacheStrategy,proto3,enum=xray.app.dns.CacheStrategy" json:"cache_strategy,omitempty"`
|
||||
QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"`
|
||||
DisableFallback bool `protobuf:"varint,10,opt,name=disableFallback,proto3" json:"disableFallback,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
@@ -365,27 +252,6 @@ func (x *Config) GetTag() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetCacheStrategy() CacheStrategy {
|
||||
if x != nil {
|
||||
return x.CacheStrategy
|
||||
}
|
||||
return CacheStrategy_Cache_ALL
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type NameServer_PriorityDomain struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -505,7 +371,8 @@ type Config_HostMapping struct {
|
||||
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
|
||||
Ip [][]byte `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"`
|
||||
// ProxiedDomain indicates the mapped domain has the same IP address on this
|
||||
// domain. Xray will use this domain for IP queries.
|
||||
// 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"`
|
||||
}
|
||||
|
||||
@@ -579,101 +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,
|
||||
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70,
|
||||
0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 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,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
||||
0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69,
|
||||
0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c,
|
||||
0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x46, 0x61,
|
||||
0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x6b,
|
||||
0x69, 0x70, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x56, 0x0a, 0x12, 0x70, 0x72,
|
||||
0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
||||
0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
|
||||
0x2e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52,
|
||||
0x11, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75,
|
||||
0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 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, 0xd7, 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,
|
||||
0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x56, 0x0a, 0x12, 0x70, 0x72, 0x69,
|
||||
0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18,
|
||||
0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
||||
0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
|
||||
0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x11,
|
||||
0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
||||
0x6e, 0x12, 0x2c, 0x0a, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
|
||||
0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 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, 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, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x02, 0x18,
|
||||
0x01, 0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65,
|
||||
0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69,
|
||||
0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f,
|
||||
0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72,
|
||||
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, 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,
|
||||
0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x73,
|
||||
0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61,
|
||||
0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x42, 0x0a, 0x0e,
|
||||
0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x08,
|
||||
0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
|
||||
0x64, 0x6e, 0x73, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67,
|
||||
0x79, 0x52, 0x0d, 0x63, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
||||
0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65,
|
||||
0x67, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72,
|
||||
0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61,
|
||||
0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46,
|
||||
0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64,
|
||||
0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x1a, 0x55,
|
||||
0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
|
||||
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31,
|
||||
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e,
|
||||
0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61,
|
||||
0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64,
|
||||
0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e,
|
||||
0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64,
|
||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d,
|
||||
0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52,
|
||||
0x02, 0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x5f, 0x64,
|
||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f,
|
||||
0x78, 0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08,
|
||||
0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69,
|
||||
0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x00,
|
||||
0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x01, 0x12,
|
||||
0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05,
|
||||
0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a, 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, 0x2a, 0x44,
|
||||
0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12,
|
||||
0x0d, 0x0a, 0x09, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x00, 0x12, 0x11,
|
||||
0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x4e, 0x4f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10,
|
||||
0x01, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42,
|
||||
0x4c, 0x45, 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,
|
||||
0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x02, 0x18, 0x01,
|
||||
0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
||||
0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65,
|
||||
0x6e, 0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x68,
|
||||
0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x2e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x73, 0x74,
|
||||
0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67,
|
||||
0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x1a, 0x55, 0x0a, 0x0a, 0x48,
|
||||
0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f,
|
||||
0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
|
||||
0x38, 0x01, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69,
|
||||
0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
|
||||
0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e,
|
||||
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79,
|
||||
0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70,
|
||||
0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65,
|
||||
0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
||||
0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a,
|
||||
0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f,
|
||||
0x6d, 0x61, 0x69, 0x6e, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72,
|
||||
0x64, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 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 (
|
||||
@@ -688,41 +531,37 @@ func file_app_dns_config_proto_rawDescGZIP() []byte {
|
||||
return file_app_dns_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_app_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
||||
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_goTypes = []interface{}{
|
||||
(DomainMatchingType)(0), // 0: xray.app.dns.DomainMatchingType
|
||||
(QueryStrategy)(0), // 1: xray.app.dns.QueryStrategy
|
||||
(CacheStrategy)(0), // 2: xray.app.dns.CacheStrategy
|
||||
(*NameServer)(nil), // 3: xray.app.dns.NameServer
|
||||
(*Config)(nil), // 4: xray.app.dns.Config
|
||||
(*NameServer_PriorityDomain)(nil), // 5: xray.app.dns.NameServer.PriorityDomain
|
||||
(*NameServer_OriginalRule)(nil), // 6: xray.app.dns.NameServer.OriginalRule
|
||||
nil, // 7: xray.app.dns.Config.HostsEntry
|
||||
(*Config_HostMapping)(nil), // 8: xray.app.dns.Config.HostMapping
|
||||
(*net.Endpoint)(nil), // 9: xray.common.net.Endpoint
|
||||
(*router.GeoIP)(nil), // 10: xray.app.router.GeoIP
|
||||
(*net.IPOrDomain)(nil), // 11: xray.common.net.IPOrDomain
|
||||
(*NameServer)(nil), // 1: xray.app.dns.NameServer
|
||||
(*Config)(nil), // 2: xray.app.dns.Config
|
||||
(*NameServer_PriorityDomain)(nil), // 3: xray.app.dns.NameServer.PriorityDomain
|
||||
(*NameServer_OriginalRule)(nil), // 4: xray.app.dns.NameServer.OriginalRule
|
||||
nil, // 5: xray.app.dns.Config.HostsEntry
|
||||
(*Config_HostMapping)(nil), // 6: xray.app.dns.Config.HostMapping
|
||||
(*net.Endpoint)(nil), // 7: xray.common.net.Endpoint
|
||||
(*router.GeoIP)(nil), // 8: xray.app.router.GeoIP
|
||||
(*net.IPOrDomain)(nil), // 9: xray.common.net.IPOrDomain
|
||||
}
|
||||
var file_app_dns_config_proto_depIdxs = []int32{
|
||||
9, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint
|
||||
5, // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.app.dns.NameServer.PriorityDomain
|
||||
10, // 2: xray.app.dns.NameServer.geoip:type_name -> xray.app.router.GeoIP
|
||||
6, // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule
|
||||
9, // 4: xray.app.dns.Config.NameServers:type_name -> xray.common.net.Endpoint
|
||||
3, // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer
|
||||
7, // 6: xray.app.dns.Config.Hosts:type_name -> xray.app.dns.Config.HostsEntry
|
||||
8, // 7: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping
|
||||
2, // 8: xray.app.dns.Config.cache_strategy:type_name -> xray.app.dns.CacheStrategy
|
||||
1, // 9: xray.app.dns.Config.query_strategy:type_name -> xray.app.dns.QueryStrategy
|
||||
0, // 10: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType
|
||||
11, // 11: xray.app.dns.Config.HostsEntry.value:type_name -> xray.common.net.IPOrDomain
|
||||
0, // 12: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType
|
||||
13, // [13:13] is the sub-list for method output_type
|
||||
13, // [13:13] is the sub-list for method input_type
|
||||
13, // [13:13] is the sub-list for extension type_name
|
||||
13, // [13:13] is the sub-list for extension extendee
|
||||
0, // [0:13] is the sub-list for field type_name
|
||||
7, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint
|
||||
3, // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.app.dns.NameServer.PriorityDomain
|
||||
8, // 2: xray.app.dns.NameServer.geoip:type_name -> xray.app.router.GeoIP
|
||||
4, // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule
|
||||
7, // 4: xray.app.dns.Config.NameServers:type_name -> xray.common.net.Endpoint
|
||||
1, // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer
|
||||
5, // 6: xray.app.dns.Config.Hosts:type_name -> xray.app.dns.Config.HostsEntry
|
||||
6, // 7: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping
|
||||
0, // 8: 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
|
||||
0, // 10: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType
|
||||
11, // [11:11] is the sub-list for method output_type
|
||||
11, // [11:11] is the sub-list for method input_type
|
||||
11, // [11:11] is the sub-list for extension type_name
|
||||
11, // [11:11] is the sub-list for extension extendee
|
||||
0, // [0:11] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_app_dns_config_proto_init() }
|
||||
@@ -797,7 +636,7 @@ func file_app_dns_config_proto_init() {
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_app_dns_config_proto_rawDesc,
|
||||
NumEnums: 3,
|
||||
NumEnums: 1,
|
||||
NumMessages: 6,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
|
@@ -12,8 +12,6 @@ import "app/router/config.proto";
|
||||
|
||||
message NameServer {
|
||||
xray.common.net.Endpoint address = 1;
|
||||
bytes client_ip = 5;
|
||||
bool skipFallback = 6;
|
||||
|
||||
message PriorityDomain {
|
||||
DomainMatchingType type = 1;
|
||||
@@ -37,18 +35,6 @@ enum DomainMatchingType {
|
||||
Regex = 3;
|
||||
}
|
||||
|
||||
enum QueryStrategy {
|
||||
USE_IP = 0;
|
||||
USE_IP4 = 1;
|
||||
USE_IP6 = 2;
|
||||
}
|
||||
|
||||
enum CacheStrategy {
|
||||
Cache_ALL = 0;
|
||||
Cache_NOERROR = 1;
|
||||
Cache_DISABLE = 2;
|
||||
}
|
||||
|
||||
message Config {
|
||||
// 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
|
||||
@@ -73,7 +59,8 @@ message Config {
|
||||
repeated bytes ip = 3;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -83,11 +70,4 @@ message Config {
|
||||
string tag = 6;
|
||||
|
||||
reserved 7;
|
||||
|
||||
// DisableCache disables DNS cache
|
||||
CacheStrategy cache_strategy = 8;
|
||||
|
||||
QueryStrategy query_strategy = 9;
|
||||
|
||||
bool disableFallback = 10;
|
||||
}
|
||||
|
309
app/dns/dns.go
309
app/dns/dns.go
@@ -2,312 +2,3 @@
|
||||
package dns
|
||||
|
||||
//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
|
||||
cacheStrategy CacheStrategy
|
||||
disableFallback 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,
|
||||
cacheStrategy: config.CacheStrategy,
|
||||
disableFallback: config.DisableFallback,
|
||||
}, 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) ([]net.IP, error) {
|
||||
return s.lookupIPInternal(domain, s.ipOption.Copy())
|
||||
}
|
||||
|
||||
// LookupOptions implements dns.Client.
|
||||
func (s *DNS) LookupOptions(domain string, opts ...dns.Option) ([]net.IP, error) {
|
||||
opt := s.ipOption.Copy()
|
||||
for _, o := range opts {
|
||||
if o != nil {
|
||||
o(opt)
|
||||
}
|
||||
}
|
||||
|
||||
return s.lookupIPInternal(domain, opt)
|
||||
}
|
||||
|
||||
// LookupIPv4 implements dns.IPv4Lookup.
|
||||
func (s *DNS) LookupIPv4(domain string) ([]net.IP, error) {
|
||||
return s.lookupIPInternal(domain, &dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
})
|
||||
}
|
||||
|
||||
// LookupIPv6 implements dns.IPv6Lookup.
|
||||
func (s *DNS) LookupIPv6(domain string) ([]net.IP, error) {
|
||||
return s.lookupIPInternal(domain, &dns.IPOption{
|
||||
IPv6Enable: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DNS) lookupIPInternal(domain string, option *dns.IPOption) ([]net.IP, error) {
|
||||
if domain == "" {
|
||||
return nil, newError("empty domain name")
|
||||
}
|
||||
if isQuery(option) {
|
||||
return nil, newError("empty option: Impossible.").AtWarning()
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Skip hosts mapping result in FakeDNS query.
|
||||
if isIPQuery(option) {
|
||||
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, option) {
|
||||
ips, err := client.QueryIP(ctx, domain, *option, s.cacheStrategy)
|
||||
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...))
|
||||
}
|
||||
|
||||
func (s *DNS) sortClients(domain string, option *dns.IPOption) []*Client {
|
||||
clients := make([]*Client, 0, len(s.clients))
|
||||
clientUsed := make([]bool, len(s.clients))
|
||||
clientNames := make([]string, 0, len(s.clients))
|
||||
domainRules := []string{}
|
||||
|
||||
defer func() {
|
||||
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()
|
||||
}
|
||||
}()
|
||||
|
||||
// Priority domain matching
|
||||
for _, match := range s.domainMatcher.Match(domain) {
|
||||
info := s.matcherInfos[match]
|
||||
client := s.clients[info.clientIdx]
|
||||
domainRule := client.domains[info.domainRuleIdx]
|
||||
if !canQueryOnClient(option, client) {
|
||||
newError("skipping the client " + client.Name()).AtDebug().WriteToLog()
|
||||
continue
|
||||
}
|
||||
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())
|
||||
}
|
||||
|
||||
if !s.disableFallback {
|
||||
// Default round-robin query
|
||||
for idx, client := range s.clients {
|
||||
if clientUsed[idx] || client.skipFallback {
|
||||
continue
|
||||
}
|
||||
|
||||
if !canQueryOnClient(option, client) {
|
||||
newError("skipping the client " + client.Name()).AtDebug().WriteToLog()
|
||||
continue
|
||||
}
|
||||
|
||||
clientUsed[idx] = true
|
||||
clients = append(clients, client)
|
||||
clientNames = append(clientNames, client.Name())
|
||||
}
|
||||
}
|
||||
|
||||
return clients
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
return New(ctx, config.(*Config))
|
||||
}))
|
||||
}
|
||||
|
@@ -42,10 +42,10 @@ type DoHNameServer struct {
|
||||
name string
|
||||
}
|
||||
|
||||
// NewDoHNameServer creates DOH server object for remote resolving
|
||||
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher) (*DoHNameServer, error) {
|
||||
// NewDoHNameServer creates DOH client object for remote resolving
|
||||
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, clientIP net.IP) (*DoHNameServer, error) {
|
||||
newError("DNS: created Remote DOH client for ", url.String()).AtInfo().WriteToLog()
|
||||
s := baseDOHNameServer(url, "DOH")
|
||||
s := baseDOHNameServer(url, "DOH", clientIP)
|
||||
|
||||
s.dispatcher = dispatcher
|
||||
tr := &http.Transport{
|
||||
@@ -104,9 +104,9 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher) (*DoHNameServ
|
||||
}
|
||||
|
||||
// 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"
|
||||
s := baseDOHNameServer(url, "DOHL")
|
||||
s := baseDOHNameServer(url, "DOHL", clientIP)
|
||||
tr := &http.Transport{
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
ForceAttemptHTTP2: true,
|
||||
@@ -136,12 +136,13 @@ func NewDoHLocalNameServer(url *url.URL) *DoHNameServer {
|
||||
return s
|
||||
}
|
||||
|
||||
func baseDOHNameServer(url *url.URL, prefix string) *DoHNameServer {
|
||||
func baseDOHNameServer(url *url.URL, prefix string, clientIP net.IP) *DoHNameServer {
|
||||
s := &DoHNameServer{
|
||||
ips: make(map[string]record),
|
||||
pub: pubsub.NewService(),
|
||||
name: prefix + "//" + url.Host,
|
||||
dohURL: url.String(),
|
||||
ips: make(map[string]record),
|
||||
clientIP: clientIP,
|
||||
pub: pubsub.NewService(),
|
||||
name: prefix + "//" + url.Host,
|
||||
dohURL: url.String(),
|
||||
}
|
||||
s.cleanup = &task.Periodic{
|
||||
Interval: time.Minute,
|
||||
@@ -151,7 +152,7 @@ func baseDOHNameServer(url *url.URL, prefix string) *DoHNameServer {
|
||||
return s
|
||||
}
|
||||
|
||||
// Name implements Server.
|
||||
// Name returns client name
|
||||
func (s *DoHNameServer) Name() string {
|
||||
return s.name
|
||||
}
|
||||
@@ -234,7 +235,7 @@ func (s *DoHNameServer) newReqID() uint16 {
|
||||
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))
|
||||
|
||||
if s.name+"." == "DOH//"+domain {
|
||||
@@ -242,7 +243,7 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP n
|
||||
return
|
||||
}
|
||||
|
||||
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
|
||||
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP))
|
||||
|
||||
var deadline time.Time
|
||||
if d, ok := ctx.Deadline(); ok {
|
||||
@@ -263,8 +264,8 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP n
|
||||
}
|
||||
|
||||
dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{
|
||||
Protocol: "https",
|
||||
SkipDNSResolve: true,
|
||||
Protocol: "https",
|
||||
//SkipRoutePick: true,
|
||||
})
|
||||
|
||||
// forced to use mux for DOH
|
||||
@@ -348,7 +349,7 @@ func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOpt
|
||||
}
|
||||
|
||||
if len(ips) > 0 {
|
||||
return toNetIP(ips)
|
||||
return toNetIP(ips), nil
|
||||
}
|
||||
|
||||
if lastErr != nil {
|
||||
@@ -362,21 +363,15 @@ func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOpt
|
||||
return nil, errRecordNotFound
|
||||
}
|
||||
|
||||
// QueryIP implements Server.
|
||||
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, cs CacheStrategy) ([]net.IP, error) { // nolint: dupl
|
||||
// QueryIP is called from dns.Server->queryIPTimeout
|
||||
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, error) { // nolint: dupl
|
||||
fqdn := Fqdn(domain)
|
||||
|
||||
if cs == CacheStrategy_Cache_DISABLE {
|
||||
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
||||
} else {
|
||||
ips, err := s.findIPsForDomain(fqdn, option)
|
||||
if err != errRecordNotFound {
|
||||
if cs == CacheStrategy_Cache_ALL || (cs == CacheStrategy_Cache_NOERROR && err == nil) {
|
||||
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, Error: err})
|
||||
return ips, err
|
||||
}
|
||||
}
|
||||
ips, err := s.findIPsForDomain(fqdn, option)
|
||||
if err != errRecordNotFound {
|
||||
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
|
||||
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err})
|
||||
return ips, err
|
||||
}
|
||||
|
||||
// ipv4 and ipv6 belong to different subscription groups
|
||||
@@ -405,7 +400,7 @@ func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
s.sendQuery(ctx, fqdn, clientIP, option)
|
||||
s.sendQuery(ctx, fqdn, option)
|
||||
start := time.Now()
|
||||
|
||||
for {
|
@@ -14,6 +14,25 @@ type StaticHosts struct {
|
||||
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.
|
||||
func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDomain) (*StaticHosts, error) {
|
||||
g := new(strmatcher.MatcherGroup)
|
||||
@@ -47,9 +66,6 @@ func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDoma
|
||||
id := g.Add(matcher)
|
||||
ips := make([]net.Address, 0, len(mapping.Ip)+1)
|
||||
switch {
|
||||
case len(mapping.ProxiedDomain) > 0:
|
||||
ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
|
||||
|
||||
case len(mapping.Ip) > 0:
|
||||
for _, ip := range mapping.Ip {
|
||||
addr := net.IPAddress(ip)
|
||||
@@ -59,56 +75,49 @@ func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDoma
|
||||
ips = append(ips, addr)
|
||||
}
|
||||
|
||||
case len(mapping.ProxiedDomain) > 0:
|
||||
ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
|
||||
|
||||
default:
|
||||
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
|
||||
}
|
||||
|
||||
return sh, nil
|
||||
}
|
||||
|
||||
func filterIP(ips []net.Address, option *dns.IPOption) []net.Address {
|
||||
func filterIP(ips []net.Address, option dns.IPOption) []net.Address {
|
||||
filtered := make([]net.Address, 0, len(ips))
|
||||
for _, ip := range ips {
|
||||
if (ip.Family().IsIPv4() && option.IPv4Enable) || (ip.Family().IsIPv6() && option.IPv6Enable) {
|
||||
filtered = append(filtered, ip)
|
||||
}
|
||||
}
|
||||
if len(filtered) == 0 {
|
||||
return nil
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func (h *StaticHosts) lookupInternal(domain string) []net.Address {
|
||||
var ips []net.Address
|
||||
for _, id := range h.matchers.Match(domain) {
|
||||
// LookupIP returns IP address for the given domain, if exists in this StaticHosts.
|
||||
func (h *StaticHosts) LookupIP(domain string, option dns.IPOption) []net.Address {
|
||||
indices := h.matchers.Match(domain)
|
||||
if len(indices) == 0 {
|
||||
return nil
|
||||
}
|
||||
ips := []net.Address{}
|
||||
for _, id := range indices {
|
||||
ips = append(ips, h.ips[id]...)
|
||||
}
|
||||
if len(ips) == 1 && ips[0].Family().IsDomain() {
|
||||
return ips
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
return filterIP(ips, option)
|
||||
}
|
||||
|
@@ -20,20 +20,6 @@ func TestStaticHosts(t *testing.T) {
|
||||
{1, 1, 1, 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: DomainMatchingType_Full,
|
||||
Domain: "proxy.example.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.example.com",
|
||||
},
|
||||
{
|
||||
Type: DomainMatchingType_Full,
|
||||
Domain: "proxy2.example.com",
|
||||
ProxiedDomain: "proxy.example.com",
|
||||
},
|
||||
{
|
||||
Type: DomainMatchingType_Subdomain,
|
||||
Domain: "example.cn",
|
||||
@@ -46,7 +32,6 @@ func TestStaticHosts(t *testing.T) {
|
||||
Domain: "baidu.com",
|
||||
Ip: [][]byte{
|
||||
{127, 0, 0, 1},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -55,7 +40,7 @@ func TestStaticHosts(t *testing.T) {
|
||||
common.Must(err)
|
||||
|
||||
{
|
||||
ips := hosts.Lookup("example.com", &dns.IPOption{
|
||||
ips := hosts.LookupIP("example.com", dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
})
|
||||
@@ -68,33 +53,7 @@ func TestStaticHosts(t *testing.T) {
|
||||
}
|
||||
|
||||
{
|
||||
domain := hosts.Lookup("proxy.example.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.example.com"); diff != "" {
|
||||
t.Error(diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
domain := hosts.Lookup("proxy2.example.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.example.com"); diff != "" {
|
||||
t.Error(diff)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ips := hosts.Lookup("www.example.cn", &dns.IPOption{
|
||||
ips := hosts.LookupIP("www.example.cn", dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
})
|
||||
@@ -107,7 +66,7 @@ func TestStaticHosts(t *testing.T) {
|
||||
}
|
||||
|
||||
{
|
||||
ips := hosts.Lookup("baidu.com", &dns.IPOption{
|
||||
ips := hosts.LookupIP("baidu.com", dns.IPOption{
|
||||
IPv4Enable: false,
|
||||
IPv6Enable: true,
|
||||
})
|
||||
|
@@ -2,211 +2,40 @@ package dns
|
||||
|
||||
import (
|
||||
"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/strmatcher"
|
||||
core "github.com/xtls/xray-core/core"
|
||||
"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.
|
||||
type Server interface {
|
||||
// Client is the interface for DNS client.
|
||||
type Client interface {
|
||||
// Name of the Client.
|
||||
Name() string
|
||||
|
||||
// QueryIP sends IP queries to its configured server.
|
||||
QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns.IPOption, cs CacheStrategy) ([]net.IP, error)
|
||||
QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, error)
|
||||
}
|
||||
|
||||
// Client is the interface for DNS client.
|
||||
type Client struct {
|
||||
server Server
|
||||
clientIP net.IP
|
||||
skipFallback bool
|
||||
domains []string
|
||||
expectIPs []*router.GeoIPMatcher
|
||||
type LocalNameServer struct {
|
||||
client *localdns.Client
|
||||
}
|
||||
|
||||
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.
|
||||
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.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()
|
||||
return nil, newError("neither IPv4 nor IPv6 is enabled")
|
||||
}
|
||||
|
||||
// NewClient creates a DNS client managing a name server with client IP, domain rules and expected IPs.
|
||||
func NewClient(ctx context.Context, ns *NameServer, clientIP net.IP, container router.GeoIPMatcherContainer, matcherInfos *[]DomainMatcherInfo, updateDomainRule func(strmatcher.Matcher, int, []DomainMatcherInfo) error) (*Client, error) {
|
||||
client := &Client{}
|
||||
|
||||
err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
|
||||
// Create a new server for each client for now
|
||||
server, err := NewServer(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.
|
||||
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.domains = rules
|
||||
client.expectIPs = matchers
|
||||
return nil
|
||||
})
|
||||
return client, err
|
||||
func (s *LocalNameServer) Name() string {
|
||||
return "localhost"
|
||||
}
|
||||
|
||||
// NewSimpleClient creates a DNS client with a simple destination.
|
||||
func NewSimpleClient(ctx context.Context, endpoint *net.Endpoint, clientIP net.IP) (*Client, error) {
|
||||
client := &Client{}
|
||||
err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
|
||||
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()
|
||||
}
|
||||
func NewLocalNameServer() *LocalNameServer {
|
||||
newError("DNS: created localhost client").AtInfo().WriteToLog()
|
||||
return &LocalNameServer{
|
||||
client: localdns.New(),
|
||||
}
|
||||
|
||||
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, cs CacheStrategy) ([]net.IP, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 4*time.Second)
|
||||
ips, err := c.server.QueryIP(ctx, domain, c.clientIP, option, cs)
|
||||
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,
|
||||
}, CacheStrategy_Cache_ALL)
|
||||
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,
|
||||
}, CacheStrategy_Cache_ALL)
|
||||
cancel()
|
||||
common.Must(err)
|
||||
if len(ips) == 0 {
|
||||
t.Error("expect some ips, but got 0")
|
||||
}
|
||||
|
||||
ctx2, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
||||
ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
}, CacheStrategy_Cache_ALL)
|
||||
cancel()
|
||||
common.Must(err)
|
||||
if r := cmp.Diff(ips2, ips); r != "" {
|
||||
t.Fatal(r)
|
||||
}
|
||||
}
|
@@ -16,13 +16,11 @@ func NewFakeDNSServer() *FakeDNSServer {
|
||||
return &FakeDNSServer{}
|
||||
}
|
||||
|
||||
const FakeDNSName = "FakeDNS"
|
||||
|
||||
func (FakeDNSServer) Name() string {
|
||||
return FakeDNSName
|
||||
return "FakeDNS"
|
||||
}
|
||||
|
||||
func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, _ dns.IPOption, _ CacheStrategy) ([]net.IP, error) {
|
||||
func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ dns.IPOption) ([]net.IP, error) {
|
||||
if f.fakeDNSEngine == nil {
|
||||
if err := core.RequireFeatures(ctx, func(fd dns.FakeDNSEngine) {
|
||||
f.fakeDNSEngine = fd
|
||||
@@ -32,9 +30,9 @@ func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, _
|
||||
}
|
||||
ips := f.fakeDNSEngine.GetFakeIPForDomain(domain)
|
||||
|
||||
netIP, err := toNetIP(ips)
|
||||
if err != nil {
|
||||
return nil, newError("Unable to convert IP to net ip").Base(err).AtError()
|
||||
netIP := toNetIP(ips)
|
||||
if netIP == nil {
|
||||
return nil, newError("Unable to convert IP to net ip").AtError()
|
||||
}
|
||||
|
||||
newError(f.Name(), " got answer: ", domain, " -> ", ips).AtInfo().WriteToLog()
|
||||
|
@@ -1,53 +0,0 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/features/dns"
|
||||
"github.com/xtls/xray-core/features/dns/localdns"
|
||||
)
|
||||
|
||||
// LocalNameServer is an wrapper over local DNS feature.
|
||||
type LocalNameServer struct {
|
||||
client *localdns.Client
|
||||
}
|
||||
|
||||
// QueryIP implements Server.
|
||||
func (s *LocalNameServer) QueryIP(_ context.Context, domain string, _ net.IP, option dns.IPOption, _ CacheStrategy) ([]net.IP, error) {
|
||||
var ips []net.IP
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case option.IPv4Enable && option.IPv6Enable:
|
||||
ips, err = s.client.LookupIP(domain)
|
||||
case option.IPv4Enable:
|
||||
ips, err = s.client.LookupIPv4(domain)
|
||||
case option.IPv6Enable:
|
||||
ips, err = s.client.LookupIPv6(domain)
|
||||
}
|
||||
|
||||
if len(ips) > 0 {
|
||||
newError("Localhost got answer: ", domain, " -> ", ips).AtInfo().WriteToLog()
|
||||
}
|
||||
|
||||
return ips, err
|
||||
}
|
||||
|
||||
// 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,394 +0,0 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"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"
|
||||
)
|
||||
|
||||
// NextProtoDQ - During connection establishment, DNS/QUIC support is indicated
|
||||
// by selecting the ALPN token "dq" in the crypto handshake.
|
||||
const NextProtoDQ = "doq-i00"
|
||||
|
||||
// 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.DomainAddress(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 := s.ips[req.domain]
|
||||
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 := context.Background()
|
||||
|
||||
// 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,
|
||||
})
|
||||
dnsCtx = log.ContextWithAccessMessage(dnsCtx, &log.AccessMessage{
|
||||
From: "DoQ",
|
||||
To: s.name,
|
||||
Status: log.AccessAccepted,
|
||||
Reason: "",
|
||||
})
|
||||
|
||||
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 ips []net.Address
|
||||
var lastErr error
|
||||
if option.IPv6Enable && record.AAAA != nil && record.AAAA.RCode == dnsmessage.RCodeSuccess {
|
||||
aaaa, err := record.AAAA.getIPs()
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
}
|
||||
ips = append(ips, aaaa...)
|
||||
}
|
||||
|
||||
if option.IPv4Enable && record.A != nil && record.A.RCode == dnsmessage.RCodeSuccess {
|
||||
a, err := record.A.getIPs()
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
}
|
||||
ips = append(ips, a...)
|
||||
}
|
||||
|
||||
if len(ips) > 0 {
|
||||
return toNetIP(ips)
|
||||
}
|
||||
|
||||
if lastErr != nil {
|
||||
return nil, lastErr
|
||||
}
|
||||
|
||||
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, cs CacheStrategy) ([]net.IP, error) {
|
||||
fqdn := Fqdn(domain)
|
||||
|
||||
if cs == CacheStrategy_Cache_DISABLE {
|
||||
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
||||
} else {
|
||||
ips, err := s.findIPsForDomain(fqdn, option)
|
||||
if err != errRecordNotFound {
|
||||
if cs == CacheStrategy_Cache_ALL || (cs == CacheStrategy_Cache_NOERROR && err == nil) {
|
||||
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, Error: err})
|
||||
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)
|
||||
start := time.Now()
|
||||
|
||||
for {
|
||||
ips, err := s.findIPsForDomain(fqdn, option)
|
||||
if err != errRecordNotFound {
|
||||
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSQueried, time.Since(start), err})
|
||||
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{}
|
||||
|
||||
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,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 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*5)
|
||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
}, CacheStrategy_Cache_ALL)
|
||||
cancel()
|
||||
common.Must(err)
|
||||
if len(ips) == 0 {
|
||||
t.Error("expect some ips, but got 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestQUICNameServerWithCache(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*5)
|
||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
}, CacheStrategy_Cache_ALL)
|
||||
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,
|
||||
}, CacheStrategy_Cache_ALL)
|
||||
cancel()
|
||||
common.Must(err)
|
||||
if r := cmp.Diff(ips2, ips); r != "" {
|
||||
t.Fatal(r)
|
||||
}
|
||||
}
|
@@ -7,17 +7,17 @@ import (
|
||||
|
||||
. "github.com/xtls/xray-core/app/dns"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/features/dns"
|
||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||
)
|
||||
|
||||
func TestLocalNameServer(t *testing.T) {
|
||||
s := NewLocalNameServer()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
ips, err := s.QueryIP(ctx, "google.com", net.IP{}, dns.IPOption{
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
||||
ips, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
}, CacheStrategy_Cache_ALL)
|
||||
FakeEnable: false,
|
||||
})
|
||||
cancel()
|
||||
common.Must(err)
|
||||
if len(ips) == 0 {
|
@@ -1,16 +0,0 @@
|
||||
package dns
|
||||
|
||||
import "github.com/xtls/xray-core/features/dns"
|
||||
|
||||
func isIPQuery(o *dns.IPOption) bool {
|
||||
return o.IPv4Enable || o.IPv6Enable
|
||||
}
|
||||
|
||||
func canQueryOnClient(o *dns.IPOption, c *Client) bool {
|
||||
isIPClient := !(c.Name() == FakeDNSName)
|
||||
return isIPClient && isIPQuery(o)
|
||||
}
|
||||
|
||||
func isQuery(o *dns.IPOption) bool {
|
||||
return !(o.IPv4Enable || o.IPv6Enable || o.FakeEnable)
|
||||
}
|
437
app/dns/server.go
Normal file
437
app/dns/server.go
Normal file
@@ -0,0 +1,437 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// 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,
|
||||
})
|
||||
}
|
||||
|
||||
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))
|
||||
}))
|
||||
}
|
@@ -154,7 +154,11 @@ func TestUDPServerSubnet(t *testing.T) {
|
||||
|
||||
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
|
||||
|
||||
ips, err := client.LookupIP("google.com")
|
||||
ips, err := client.LookupIP("google.com", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -209,7 +213,11 @@ func TestUDPServer(t *testing.T) {
|
||||
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
|
||||
|
||||
{
|
||||
ips, err := client.LookupIP("google.com")
|
||||
ips, err := client.LookupIP("google.com", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -220,7 +228,11 @@ func TestUDPServer(t *testing.T) {
|
||||
}
|
||||
|
||||
{
|
||||
ips, err := client.LookupIP("facebook.com")
|
||||
ips, err := client.LookupIP("facebook.com", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -231,7 +243,11 @@ func TestUDPServer(t *testing.T) {
|
||||
}
|
||||
|
||||
{
|
||||
_, err := client.LookupIP("notexist.google.com")
|
||||
_, err := client.LookupIP("notexist.google.com", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("nil error")
|
||||
}
|
||||
@@ -241,8 +257,11 @@ func TestUDPServer(t *testing.T) {
|
||||
}
|
||||
|
||||
{
|
||||
clientv6 := client.(feature_dns.IPv6Lookup)
|
||||
ips, err := clientv6.LookupIPv6("ipv4only.google.com")
|
||||
ips, err := client.LookupIP("ipv4only.google.com", feature_dns.IPOption{
|
||||
IPv4Enable: false,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != feature_dns.ErrEmptyResponse {
|
||||
t.Fatal("error: ", err)
|
||||
}
|
||||
@@ -254,7 +273,11 @@ func TestUDPServer(t *testing.T) {
|
||||
dnsServer.Shutdown()
|
||||
|
||||
{
|
||||
ips, err := client.LookupIP("google.com")
|
||||
ips, err := client.LookupIP("google.com", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -331,7 +354,11 @@ func TestPrioritizedDomain(t *testing.T) {
|
||||
startTime := time.Now()
|
||||
|
||||
{
|
||||
ips, err := client.LookupIP("google.com")
|
||||
ips, err := client.LookupIP("google.com", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -390,9 +417,12 @@ func TestUDPServerIPv6(t *testing.T) {
|
||||
common.Must(err)
|
||||
|
||||
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
|
||||
client6 := client.(feature_dns.IPv6Lookup)
|
||||
{
|
||||
ips, err := client6.LookupIPv6("ipv6.google.com")
|
||||
ips, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{
|
||||
IPv4Enable: false,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -455,7 +485,11 @@ func TestStaticHostDomain(t *testing.T) {
|
||||
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
|
||||
|
||||
{
|
||||
ips, err := client.LookupIP("example.com")
|
||||
ips, err := client.LookupIP("example.com", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -562,7 +596,11 @@ func TestIPMatch(t *testing.T) {
|
||||
startTime := time.Now()
|
||||
|
||||
{
|
||||
ips, err := client.LookupIP("google.com")
|
||||
ips, err := client.LookupIP("google.com", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -681,7 +719,11 @@ func TestLocalDomain(t *testing.T) {
|
||||
startTime := time.Now()
|
||||
|
||||
{ // Will match dotless:
|
||||
ips, err := client.LookupIP("hostname")
|
||||
ips, err := client.LookupIP("hostname", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -692,7 +734,11 @@ func TestLocalDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
{ // Will match domain:local
|
||||
ips, err := client.LookupIP("hostname.local")
|
||||
ips, err := client.LookupIP("hostname.local", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -703,7 +749,11 @@ func TestLocalDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
{ // Will match static ip
|
||||
ips, err := client.LookupIP("hostnamestatic")
|
||||
ips, err := client.LookupIP("hostnamestatic", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -714,7 +764,11 @@ func TestLocalDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
{ // Will match domain replacing
|
||||
ips, err := client.LookupIP("hostnamealias")
|
||||
ips, err := client.LookupIP("hostnamealias", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -725,7 +779,11 @@ func TestLocalDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
{ // Will match dotless:localhost, but not expectIPs: 127.0.0.2, 127.0.0.3, then matches at dotless:
|
||||
ips, err := client.LookupIP("localhost")
|
||||
ips, err := client.LookupIP("localhost", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -736,7 +794,11 @@ func TestLocalDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
{ // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3
|
||||
ips, err := client.LookupIP("localhost-a")
|
||||
ips, err := client.LookupIP("localhost-a", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -747,7 +809,11 @@ func TestLocalDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
{ // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3
|
||||
ips, err := client.LookupIP("localhost-b")
|
||||
ips, err := client.LookupIP("localhost-b", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -758,7 +824,11 @@ func TestLocalDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
{ // Will match dotless:
|
||||
ips, err := client.LookupIP("Mijia Cloud")
|
||||
ips, err := client.LookupIP("Mijia Cloud", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -920,7 +990,11 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
|
||||
startTime := time.Now()
|
||||
|
||||
{ // Will match server 1,2 and server 1 returns expected ip
|
||||
ips, err := client.LookupIP("google.com")
|
||||
ips, err := client.LookupIP("google.com", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -931,8 +1005,11 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
{ // Will match server 1,2 and server 1 returns unexpected ip, then server 2 returns expected one
|
||||
clientv4 := client.(feature_dns.IPv4Lookup)
|
||||
ips, err := clientv4.LookupIPv4("ipv6.google.com")
|
||||
ips, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: false,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -943,7 +1020,11 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
{ // Will match server 3,1,2 and server 3 returns expected one
|
||||
ips, err := client.LookupIP("api.google.com")
|
||||
ips, err := client.LookupIP("api.google.com", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
||||
@@ -954,7 +1035,11 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
|
||||
}
|
||||
|
||||
{ // Will match server 4,3,1,2 and server 4 returns expected one
|
||||
ips, err := client.LookupIP("v2.api.google.com")
|
||||
ips, err := client.LookupIP("v2.api.google.com", feature_dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: ", err)
|
||||
}
|
@@ -31,10 +31,10 @@ type ClassicNameServer struct {
|
||||
udpServer *udp.Dispatcher
|
||||
cleanup *task.Periodic
|
||||
reqID uint32
|
||||
clientIP net.IP
|
||||
}
|
||||
|
||||
// NewClassicNameServer creates udp server object for remote resolving.
|
||||
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher) *ClassicNameServer {
|
||||
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, clientIP net.IP) *ClassicNameServer {
|
||||
// default to 53 if unspecific
|
||||
if address.Port == 0 {
|
||||
address.Port = net.Port(53)
|
||||
@@ -44,6 +44,7 @@ func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher
|
||||
address: address,
|
||||
ips: make(map[string]record),
|
||||
requests: make(map[uint16]dnsRequest),
|
||||
clientIP: clientIP,
|
||||
pub: pubsub.NewService(),
|
||||
name: strings.ToUpper(address.String()),
|
||||
}
|
||||
@@ -56,12 +57,10 @@ func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher
|
||||
return s
|
||||
}
|
||||
|
||||
// Name implements Server.
|
||||
func (s *ClassicNameServer) Name() string {
|
||||
return s.name
|
||||
}
|
||||
|
||||
// Cleanup clears expired items from cache
|
||||
func (s *ClassicNameServer) Cleanup() error {
|
||||
now := time.Now()
|
||||
s.Lock()
|
||||
@@ -103,7 +102,6 @@ func (s *ClassicNameServer) Cleanup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleResponse handles udp response packet from remote DNS server.
|
||||
func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_proto.Packet) {
|
||||
ipRec, err := parseResponse(packet.Payload.Bytes())
|
||||
if err != nil {
|
||||
@@ -181,10 +179,10 @@ func (s *ClassicNameServer) addPendingRequest(req *dnsRequest) {
|
||||
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))
|
||||
|
||||
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
|
||||
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP))
|
||||
|
||||
for _, req := range reqs {
|
||||
s.addPendingRequest(req)
|
||||
@@ -193,6 +191,7 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, client
|
||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||
udpCtx = session.ContextWithInbound(udpCtx, inbound)
|
||||
}
|
||||
|
||||
udpCtx = session.ContextWithContent(udpCtx, &session.Content{
|
||||
Protocol: "dns",
|
||||
})
|
||||
@@ -234,7 +233,7 @@ func (s *ClassicNameServer) findIPsForDomain(domain string, option dns_feature.I
|
||||
}
|
||||
|
||||
if len(ips) > 0 {
|
||||
return toNetIP(ips)
|
||||
return toNetIP(ips), nil
|
||||
}
|
||||
|
||||
if lastErr != nil {
|
||||
@@ -245,20 +244,14 @@ func (s *ClassicNameServer) findIPsForDomain(domain string, option dns_feature.I
|
||||
}
|
||||
|
||||
// QueryIP implements Server.
|
||||
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, cs CacheStrategy) ([]net.IP, error) {
|
||||
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, error) {
|
||||
fqdn := Fqdn(domain)
|
||||
|
||||
if cs == CacheStrategy_Cache_DISABLE {
|
||||
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
||||
} else {
|
||||
ips, err := s.findIPsForDomain(fqdn, option)
|
||||
if err != errRecordNotFound {
|
||||
if cs == CacheStrategy_Cache_ALL || (cs == CacheStrategy_Cache_NOERROR && err == nil) {
|
||||
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, Error: err})
|
||||
return ips, err
|
||||
}
|
||||
}
|
||||
ips, err := s.findIPsForDomain(fqdn, option)
|
||||
if err != errRecordNotFound {
|
||||
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
|
||||
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err})
|
||||
return ips, err
|
||||
}
|
||||
|
||||
// ipv4 and ipv6 belong to different subscription groups
|
||||
@@ -287,7 +280,7 @@ func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
s.sendQuery(ctx, fqdn, clientIP, option)
|
||||
s.sendQuery(ctx, fqdn, option)
|
||||
start := time.Now()
|
||||
|
||||
for {
|
@@ -28,12 +28,6 @@ func (c routingContext) GetTargetPort() net.Port {
|
||||
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?
|
||||
func (c routingContext) GetSkipDNSResolve() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// AsRoutingContext converts a protobuf RoutingContext into an implementation of routing.Context.
|
||||
func AsRoutingContext(r *RoutingContext) routing.Context {
|
||||
return routingContext{r}
|
||||
|
@@ -80,13 +80,7 @@ func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) {
|
||||
}
|
||||
|
||||
func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) {
|
||||
|
||||
// SkipDNSResolve is set from DNS module.
|
||||
// the DOH remote server maybe a domain name,
|
||||
// this prevents cycle resolving dead loop
|
||||
skipDNSResolve := ctx.GetSkipDNSResolve()
|
||||
|
||||
if r.domainStrategy == Config_IpOnDemand && !skipDNSResolve {
|
||||
if r.domainStrategy == Config_IpOnDemand {
|
||||
ctx = routing_dns.ContextWithDNSClient(ctx, r.dns)
|
||||
}
|
||||
|
||||
@@ -96,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
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/features/dns"
|
||||
"github.com/xtls/xray-core/features/outbound"
|
||||
routing_session "github.com/xtls/xray-core/features/routing/session"
|
||||
"github.com/xtls/xray-core/testing/mocks"
|
||||
@@ -115,7 +116,11 @@ func TestIPOnDemand(t *testing.T) {
|
||||
defer mockCtl.Finish()
|
||||
|
||||
mockDNS := mocks.NewDNSClient(mockCtl)
|
||||
mockDNS.EXPECT().LookupIP(gomock.Eq("example.com")).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes()
|
||||
mockDNS.EXPECT().LookupIP(gomock.Eq("example.com"), dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
}).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes()
|
||||
|
||||
r := new(Router)
|
||||
common.Must(r.Init(config, mockDNS, nil))
|
||||
@@ -150,7 +155,11 @@ func TestIPIfNonMatchDomain(t *testing.T) {
|
||||
defer mockCtl.Finish()
|
||||
|
||||
mockDNS := mocks.NewDNSClient(mockCtl)
|
||||
mockDNS.EXPECT().LookupIP(gomock.Eq("example.com")).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes()
|
||||
mockDNS.EXPECT().LookupIP(gomock.Eq("example.com"), dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
}).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes()
|
||||
|
||||
r := new(Router)
|
||||
common.Must(r.Init(config, mockDNS, nil))
|
||||
|
@@ -24,10 +24,17 @@ type Buffer struct {
|
||||
UDP *net.Destination
|
||||
}
|
||||
|
||||
// New creates a Buffer with 0 length and 2K capacity.
|
||||
// New creates a Buffer with 0 length and 8K capacity.
|
||||
func New() *Buffer {
|
||||
buf := pool.Get().([]byte)
|
||||
if cap(buf) >= Size {
|
||||
buf = buf[:Size]
|
||||
} else {
|
||||
buf = make([]byte, Size)
|
||||
}
|
||||
|
||||
return &Buffer{
|
||||
v: pool.Get().([]byte),
|
||||
v: buf,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +57,15 @@ func NewExisted(b []byte) *Buffer {
|
||||
// StackNew creates a new Buffer object on stack.
|
||||
// This method is for buffers that is released in the same function.
|
||||
func StackNew() Buffer {
|
||||
buf := pool.Get().([]byte)
|
||||
if cap(buf) >= Size {
|
||||
buf = buf[:Size]
|
||||
} else {
|
||||
buf = make([]byte, Size)
|
||||
}
|
||||
|
||||
return Buffer{
|
||||
v: pool.Get().([]byte),
|
||||
v: buf,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +78,10 @@ func (b *Buffer) Release() {
|
||||
p := b.v
|
||||
b.v = nil
|
||||
b.Clear()
|
||||
pool.Put(p)
|
||||
|
||||
if cap(p) == Size {
|
||||
pool.Put(p)
|
||||
}
|
||||
b.UDP = nil
|
||||
}
|
||||
|
||||
|
@@ -4,7 +4,6 @@ package buf
|
||||
|
||||
import (
|
||||
"io"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/xtls/xray-core/common/platform"
|
||||
@@ -134,17 +133,13 @@ func (r *ReadVReader) ReadMultiBuffer() (MultiBuffer, error) {
|
||||
return mb, nil
|
||||
}
|
||||
|
||||
var useReadv = true
|
||||
var useReadv bool
|
||||
|
||||
func init() {
|
||||
const defaultFlagValue = "NOT_DEFINED_AT_ALL"
|
||||
value := platform.NewEnvFlag("xray.buf.readv").GetValue(func() string { return defaultFlagValue })
|
||||
switch value {
|
||||
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":
|
||||
case defaultFlagValue, "auto", "enable":
|
||||
useReadv = true
|
||||
}
|
||||
}
|
||||
|
@@ -75,7 +75,7 @@ type Content struct {
|
||||
|
||||
Attributes map[string]string
|
||||
|
||||
SkipDNSResolve bool
|
||||
SkipRoutePick bool
|
||||
}
|
||||
|
||||
// Sockopt is the settings for socket connection.
|
||||
|
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "1.4.2"
|
||||
version = "1.4.3"
|
||||
build = "Custom"
|
||||
codename = "Xray, Penetrates Everything."
|
||||
intro = "A unified platform for anti-censorship."
|
||||
|
@@ -14,12 +14,6 @@ type IPOption struct {
|
||||
FakeEnable bool
|
||||
}
|
||||
|
||||
func (p *IPOption) Copy() *IPOption {
|
||||
return &IPOption{p.IPv4Enable, p.IPv6Enable, p.FakeEnable}
|
||||
}
|
||||
|
||||
type Option func(dopt *IPOption) *IPOption
|
||||
|
||||
// Client is a Xray feature for querying DNS information.
|
||||
//
|
||||
// xray:api:stable
|
||||
@@ -27,24 +21,7 @@ type Client interface {
|
||||
features.Feature
|
||||
|
||||
// LookupIP returns IP address for the given domain. IPs may contain IPv4 and/or IPv6 addresses.
|
||||
LookupIP(domain string) ([]net.IP, error)
|
||||
|
||||
// LookupOptions query IP address for domain with *IPOption.
|
||||
LookupOptions(domain string, opt ...Option) ([]net.IP, error)
|
||||
}
|
||||
|
||||
// IPv4Lookup is an optional feature for querying IPv4 addresses only.
|
||||
//
|
||||
// xray:api:beta
|
||||
type IPv4Lookup interface {
|
||||
LookupIPv4(domain string) ([]net.IP, error)
|
||||
}
|
||||
|
||||
// IPv6Lookup is an optional feature for querying IPv6 addresses only.
|
||||
//
|
||||
// xray:api:beta
|
||||
type IPv6Lookup interface {
|
||||
LookupIPv6(domain string) ([]net.IP, error)
|
||||
LookupIP(domain string, option IPOption) ([]net.IP, error)
|
||||
}
|
||||
|
||||
// ClientType returns the type of Client interface. Can be used for implementing common.HasType.
|
||||
@@ -73,41 +50,3 @@ func RCodeFromError(err error) uint16 {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var (
|
||||
LookupIPv4Only = func(d *IPOption) *IPOption {
|
||||
d.IPv4Enable = true
|
||||
d.IPv6Enable = false
|
||||
return d
|
||||
}
|
||||
LookupIPv6Only = func(d *IPOption) *IPOption {
|
||||
d.IPv4Enable = false
|
||||
d.IPv6Enable = true
|
||||
return d
|
||||
}
|
||||
LookupIP = func(d *IPOption) *IPOption {
|
||||
d.IPv4Enable = true
|
||||
d.IPv6Enable = true
|
||||
return d
|
||||
}
|
||||
LookupFake = func(d *IPOption) *IPOption {
|
||||
d.FakeEnable = true
|
||||
return d
|
||||
}
|
||||
LookupFakeOnly = func(d *IPOption) *IPOption {
|
||||
d.FakeEnable = true
|
||||
d.IPv4Enable = false
|
||||
d.IPv6Enable = false
|
||||
return d
|
||||
}
|
||||
LookupNoFake = func(d *IPOption) *IPOption {
|
||||
d.FakeEnable = false
|
||||
return d
|
||||
}
|
||||
|
||||
LookupAll = func(d *IPOption) *IPOption {
|
||||
LookupIP(d)
|
||||
LookupFake(d)
|
||||
return d
|
||||
}
|
||||
)
|
||||
|
@@ -20,64 +20,41 @@ func (*Client) Start() error { return nil }
|
||||
func (*Client) Close() error { return nil }
|
||||
|
||||
// LookupIP implements Client.
|
||||
func (*Client) LookupIP(host string) ([]net.IP, error) {
|
||||
func (*Client) LookupIP(host string, option dns.IPOption) ([]net.IP, error) {
|
||||
ips, err := net.LookupIP(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parsedIPs := make([]net.IP, 0, len(ips))
|
||||
ipv4 := make([]net.IP, 0, len(ips))
|
||||
ipv6 := make([]net.IP, 0, len(ips))
|
||||
for _, ip := range ips {
|
||||
parsed := net.IPAddress(ip)
|
||||
if parsed != nil {
|
||||
parsedIPs = append(parsedIPs, parsed.IP())
|
||||
}
|
||||
}
|
||||
if len(parsedIPs) == 0 {
|
||||
return nil, dns.ErrEmptyResponse
|
||||
}
|
||||
return parsedIPs, nil
|
||||
}
|
||||
|
||||
// LookupOptions implements Client.
|
||||
func (c *Client) LookupOptions(host string, _ ...dns.Option) ([]net.IP, error) {
|
||||
return c.LookupIP(host)
|
||||
}
|
||||
|
||||
// LookupIPv4 implements IPv4Lookup.
|
||||
func (c *Client) LookupIPv4(host string) ([]net.IP, error) {
|
||||
ips, err := c.LookupIP(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ipv4 := make([]net.IP, 0, len(ips))
|
||||
for _, ip := range ips {
|
||||
if len(ip) == net.IPv4len {
|
||||
ipv4 = append(ipv4, ip)
|
||||
}
|
||||
}
|
||||
if len(ipv4) == 0 {
|
||||
return nil, dns.ErrEmptyResponse
|
||||
}
|
||||
return ipv4, nil
|
||||
}
|
||||
|
||||
// LookupIPv6 implements IPv6Lookup.
|
||||
func (c *Client) LookupIPv6(host string) ([]net.IP, error) {
|
||||
ips, err := c.LookupIP(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ipv6 := make([]net.IP, 0, len(ips))
|
||||
for _, ip := range ips {
|
||||
if len(ip) == net.IPv6len {
|
||||
ipv6 = append(ipv6, ip)
|
||||
}
|
||||
}
|
||||
if len(ipv6) == 0 {
|
||||
return nil, dns.ErrEmptyResponse
|
||||
switch {
|
||||
case option.IPv4Enable && option.IPv6Enable:
|
||||
if len(parsedIPs) > 0 {
|
||||
return parsedIPs, nil
|
||||
}
|
||||
case option.IPv4Enable:
|
||||
if len(ipv4) > 0 {
|
||||
return ipv4, nil
|
||||
}
|
||||
case option.IPv6Enable:
|
||||
if len(ipv6) > 0 {
|
||||
return ipv6, nil
|
||||
}
|
||||
}
|
||||
|
||||
return ipv6, nil
|
||||
return nil, dns.ErrEmptyResponse
|
||||
}
|
||||
|
||||
// New create a new dns.Client that queries localhost for DNS.
|
||||
|
@@ -37,7 +37,4 @@ type Context interface {
|
||||
|
||||
// GetAttributes returns extra attributes from the conneciont content.
|
||||
GetAttributes() map[string]string
|
||||
|
||||
// GetSkipDNSResolve returns a flag switch for weather skip dns resolve during route pick.
|
||||
GetSkipDNSResolve() bool
|
||||
}
|
||||
|
@@ -26,12 +26,16 @@ func (ctx *ResolvableContext) GetTargetIPs() []net.IP {
|
||||
}
|
||||
|
||||
if domain := ctx.GetTargetDomain(); len(domain) != 0 {
|
||||
ips, err := ctx.dnsClient.LookupIP(domain)
|
||||
ips, err := ctx.dnsClient.LookupIP(domain, dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
})
|
||||
if err == nil {
|
||||
ctx.resolvedIPs = ips
|
||||
return ips
|
||||
}
|
||||
newError("failed to resolve ip for ", domain).Base(err).WriteToLog()
|
||||
newError("resolve ip for ", domain).Base(err).WriteToLog()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -109,14 +109,6 @@ func (ctx *Context) GetAttributes() map[string]string {
|
||||
return ctx.Content.Attributes
|
||||
}
|
||||
|
||||
// GetSkipDNSResolve implements routing.Context.
|
||||
func (ctx *Context) GetSkipDNSResolve() bool {
|
||||
if ctx.Content == nil {
|
||||
return false
|
||||
}
|
||||
return ctx.Content.SkipDNSResolve
|
||||
}
|
||||
|
||||
// AsRoutingContext creates a context from context.context with session info.
|
||||
func AsRoutingContext(ctx context.Context) routing.Context {
|
||||
return &Context{
|
||||
|
51
go.mod
51
go.mod
@@ -1,27 +1,48 @@
|
||||
module github.com/xtls/xray-core
|
||||
|
||||
go 1.16
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
|
||||
github.com/golang/mock v1.5.0
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/google/go-cmp v0.5.5
|
||||
github.com/google/go-cmp v0.5.6
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/lucas-clemente/quic-go v0.20.0
|
||||
github.com/miekg/dns v1.1.41
|
||||
github.com/pelletier/go-toml v1.8.1
|
||||
github.com/pires/go-proxyproto v0.5.0
|
||||
github.com/refraction-networking/utls v0.0.0-20201210053706-2179f286686b
|
||||
github.com/lucas-clemente/quic-go v0.23.0
|
||||
github.com/miekg/dns v1.1.43
|
||||
github.com/pelletier/go-toml v1.9.4
|
||||
github.com/pires/go-proxyproto v0.6.0
|
||||
github.com/refraction-networking/utls v0.0.0-20210713165636-0b2885c8c0d4
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499
|
||||
go.starlark.net v0.0.0-20210312235212-74c10e2c17dc
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
||||
golang.org/x/net v0.0.0-20210330230544-e57232859fb2
|
||||
go.starlark.net v0.0.0-20210901212718-87f333178d59
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
|
||||
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44
|
||||
google.golang.org/grpc v1.36.1
|
||||
google.golang.org/protobuf v1.26.0
|
||||
h12.io/socks v1.0.2
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34
|
||||
google.golang.org/grpc v1.40.0
|
||||
google.golang.org/protobuf v1.27.1
|
||||
h12.io/socks v1.0.3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cheekybits/genny v1.0.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.4 // indirect
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/onsi/ginkgo v1.16.4 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/mod v0.5.0 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/tools v0.1.5 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
131
go.sum
131
go.sum
@@ -8,18 +8,23 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -29,29 +34,35 @@ github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fp
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew=
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
@@ -59,6 +70,7 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
@@ -69,8 +81,9 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
@@ -83,6 +96,7 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364 h1:5XxdakFhqd9dnXoAZy1Mb2R/DZ6D1e+0bGC/JhucGYI=
|
||||
github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
@@ -96,39 +110,44 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lucas-clemente/quic-go v0.20.0 h1:FSU3YN5VnLafHR27Ejs1r1CYMS7XMyIVDzRewkDLNBw=
|
||||
github.com/lucas-clemente/quic-go v0.20.0/go.mod h1:fZq/HUDIM+mW6X6wtzORjC0E/WDBMKe5Hf9bgjISwLk=
|
||||
github.com/lucas-clemente/quic-go v0.23.0 h1:5vFnKtZ6nHDFsc/F3uuiF4T3y/AXaQdxjUqiVw26GZE=
|
||||
github.com/lucas-clemente/quic-go v0.23.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.4 h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9Mvs5yNoZZ28A=
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.3 h1:XEZ1xGorVy9u+lJq+WXNE+hiqRYLNvJGYmwfwKQN2gU=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.3/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.4 h1:xbHbOGGhrenVtII6Co8akhLEdrawwB2iHl5yhJRpnco=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0 h1:P9ggrs5xtwiqXv/FHNwntmuLMNq3KaSIG93AtAZ48xk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
|
||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
|
||||
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
|
||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
|
||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pires/go-proxyproto v0.5.0 h1:A4Jv4ZCaV3AFJeGh5mGwkz4iuWUYMlQ7IoO/GTuSuLo=
|
||||
github.com/pires/go-proxyproto v0.5.0/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||
github.com/pires/go-proxyproto v0.6.0 h1:cLJUPnuQdiNf7P/wbeOKmM1khVdaMgTFDLj8h9ZrVYk=
|
||||
github.com/pires/go-proxyproto v0.6.0/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -137,8 +156,9 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/refraction-networking/utls v0.0.0-20201210053706-2179f286686b h1:lzo71oHzQEz0fKMSjR0BpVzuh2hOHvJTxnN3Rnikmtg=
|
||||
github.com/refraction-networking/utls v0.0.0-20201210053706-2179f286686b/go.mod h1:tz9gX959MEFfFN5whTIocCLUG57WiILqtdVxI8c6Wj0=
|
||||
github.com/refraction-networking/utls v0.0.0-20210713165636-0b2885c8c0d4 h1:n9NMHJusHylTmtaJ0Qe0VV9dkTZLiwAxHmrI/l98GeE=
|
||||
github.com/refraction-networking/utls v0.0.0-20210713165636-0b2885c8c0d4/go.mod h1:tz9gX959MEFfFN5whTIocCLUG57WiILqtdVxI8c6Wj0=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c h1:pqy40B3MQWYrza7YZXOXgl0Nf0QGFqrOC0BKae1UNAA=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
||||
@@ -167,6 +187,7 @@ github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYED
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
@@ -178,9 +199,12 @@ github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49u
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499 h1:QHESTXtfgc1ABV+ArlbPVqUx9Ht5I0dDkYhxYoXFxNo=
|
||||
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499/go.mod h1:5TB2+k58gx4A4g2Nf5miSHNDF6CuAzHKpWBooLAshTs=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.starlark.net v0.0.0-20210312235212-74c10e2c17dc h1:pVkptfeOTFfx+zXZo7HEHN3d5LmhatBFvHdm/f2QnpY=
|
||||
go.starlark.net v0.0.0-20210312235212-74c10e2c17dc/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.starlark.net v0.0.0-20210901212718-87f333178d59 h1:F8ArBy9n1l7HE1JjzOIYqweEqoUlywy5+L3bR0tIa9g=
|
||||
go.starlark.net v0.0.0-20210901212718-87f333178d59/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -189,14 +213,19 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.0 h1:UG21uOlmZabA4fW5i7ZX6bjw1xELEGg/ZLgZq9auk/Q=
|
||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -210,20 +239,25 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210330230544-e57232859fb2 h1:nGCZOty+lVDsc4H2qPFksI5Se296+V+GhMiL/TzmYNk=
|
||||
golang.org/x/net v0.0.0-20210330230544-e57232859fb2/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f h1:w6wWR0H+nyVpbSAQbzVEIACVyr/h8l/BEkY6Sokc7Eg=
|
||||
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -240,16 +274,24 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 h1:GkvMjFtXUmahfDtashnc1mnrCtuBVcwse5QV2lUk/tI=
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -261,6 +303,11 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -279,8 +326,10 @@ google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoA
|
||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 h1:3V2dxSZpz4zozWWUq36vUxXEKnSYitEH2LdsAx+RUmg=
|
||||
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
@@ -288,8 +337,10 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.36.1 h1:cmUfbeGKnz9+2DD/UYsMQXeqbHZqZDs4eQwW0sFOpBY=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -300,8 +351,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -311,15 +363,18 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
h12.io/socks v1.0.2 h1:cZhhbV8+DE0Y1kotwhr1a3RC3kFO7AtuZ4GLr3qKSc8=
|
||||
h12.io/socks v1.0.2/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck=
|
||||
h12.io/socks v1.0.3 h1:Ka3qaQewws4j4/eDQnOdpr4wXsC//dXtWvftlIcCQUo=
|
||||
h12.io/socks v1.0.3/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@@ -11,12 +11,10 @@ import (
|
||||
)
|
||||
|
||||
type NameServerConfig struct {
|
||||
Address *Address
|
||||
ClientIP *Address
|
||||
Port uint16
|
||||
SkipFallback bool
|
||||
Domains []string
|
||||
ExpectIPs StringList
|
||||
Address *Address
|
||||
Port uint16
|
||||
Domains []string
|
||||
ExpectIPs StringList
|
||||
}
|
||||
|
||||
func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
|
||||
@@ -27,18 +25,14 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
|
||||
}
|
||||
|
||||
var advanced struct {
|
||||
Address *Address `json:"address"`
|
||||
ClientIP *Address `json:"clientIp"`
|
||||
Port uint16 `json:"port"`
|
||||
SkipFallback bool `json:"skipFallback"`
|
||||
Domains []string `json:"domains"`
|
||||
ExpectIPs StringList `json:"expectIps"`
|
||||
Address *Address `json:"address"`
|
||||
Port uint16 `json:"port"`
|
||||
Domains []string `json:"domains"`
|
||||
ExpectIPs StringList `json:"expectIps"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &advanced); err == nil {
|
||||
c.Address = advanced.Address
|
||||
c.ClientIP = advanced.ClientIP
|
||||
c.Port = advanced.Port
|
||||
c.SkipFallback = advanced.SkipFallback
|
||||
c.Domains = advanced.Domains
|
||||
c.ExpectIPs = advanced.ExpectIPs
|
||||
return nil
|
||||
@@ -93,21 +87,12 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
|
||||
return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err)
|
||||
}
|
||||
|
||||
var myClientIP []byte
|
||||
if c.ClientIP != nil {
|
||||
if !c.ClientIP.Family().IsIP() {
|
||||
return nil, newError("not an IP address:", c.ClientIP.String())
|
||||
}
|
||||
myClientIP = []byte(c.ClientIP.IP())
|
||||
}
|
||||
return &dns.NameServer{
|
||||
Address: &net.Endpoint{
|
||||
Network: net.Network_UDP,
|
||||
Address: c.Address.Build(),
|
||||
Port: uint32(c.Port),
|
||||
},
|
||||
ClientIp: myClientIP,
|
||||
SkipFallback: c.SkipFallback,
|
||||
PrioritizedDomain: domains,
|
||||
Geoip: geoipList,
|
||||
OriginalRules: originalRules,
|
||||
@@ -123,72 +108,28 @@ var typeMap = map[router.Domain_Type]dns.DomainMatchingType{
|
||||
|
||||
// DNSConfig is a JSON serializable object for dns.Config.
|
||||
type DNSConfig struct {
|
||||
Servers []*NameServerConfig `json:"servers"`
|
||||
Hosts map[string]*HostAddress `json:"hosts"`
|
||||
ClientIP *Address `json:"clientIp"`
|
||||
Tag string `json:"tag"`
|
||||
QueryStrategy string `json:"queryStrategy"`
|
||||
CacheStrategy string `json:"cacheStrategy"`
|
||||
DisableCache bool `json:"disableCache"`
|
||||
DisableFallback bool `json:"disableFallback"`
|
||||
Servers []*NameServerConfig `json:"servers"`
|
||||
Hosts map[string]*Address `json:"hosts"`
|
||||
ClientIP *Address `json:"clientIp"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
type HostAddress struct {
|
||||
addr *Address
|
||||
addrs []*Address
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON
|
||||
func (h *HostAddress) UnmarshalJSON(data []byte) error {
|
||||
addr := new(Address)
|
||||
var addrs []*Address
|
||||
switch {
|
||||
case json.Unmarshal(data, &addr) == nil:
|
||||
h.addr = addr
|
||||
case json.Unmarshal(data, &addrs) == nil:
|
||||
h.addrs = addrs
|
||||
default:
|
||||
return newError("invalid address")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getHostMapping(ha *HostAddress) *dns.Config_HostMapping {
|
||||
if ha.addr != nil {
|
||||
if ha.addr.Family().IsDomain() {
|
||||
return &dns.Config_HostMapping{
|
||||
ProxiedDomain: ha.addr.Domain(),
|
||||
}
|
||||
}
|
||||
func getHostMapping(addr *Address) *dns.Config_HostMapping {
|
||||
if addr.Family().IsIP() {
|
||||
return &dns.Config_HostMapping{
|
||||
Ip: [][]byte{ha.addr.IP()},
|
||||
Ip: [][]byte{[]byte(addr.IP())},
|
||||
}
|
||||
}
|
||||
|
||||
ips := make([][]byte, 0, len(ha.addrs))
|
||||
for _, addr := range ha.addrs {
|
||||
if addr.Family().IsDomain() {
|
||||
return &dns.Config_HostMapping{
|
||||
ProxiedDomain: addr.Domain(),
|
||||
}
|
||||
} else {
|
||||
return &dns.Config_HostMapping{
|
||||
ProxiedDomain: addr.Domain(),
|
||||
}
|
||||
ips = append(ips, []byte(addr.IP()))
|
||||
}
|
||||
return &dns.Config_HostMapping{
|
||||
Ip: ips,
|
||||
}
|
||||
}
|
||||
|
||||
// Build implements Buildable
|
||||
func (c *DNSConfig) Build() (*dns.Config, error) {
|
||||
config := &dns.Config{
|
||||
Tag: c.Tag,
|
||||
CacheStrategy: dns.CacheStrategy_Cache_ALL,
|
||||
DisableFallback: c.DisableFallback,
|
||||
}
|
||||
|
||||
if c.DisableCache {
|
||||
config.CacheStrategy = dns.CacheStrategy_Cache_DISABLE
|
||||
Tag: c.Tag,
|
||||
}
|
||||
|
||||
if c.ClientIP != nil {
|
||||
@@ -198,25 +139,6 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
|
||||
config.ClientIp = []byte(c.ClientIP.IP())
|
||||
}
|
||||
|
||||
config.QueryStrategy = dns.QueryStrategy_USE_IP
|
||||
switch strings.ToLower(c.QueryStrategy) {
|
||||
case "useip", "use_ip", "use-ip":
|
||||
config.QueryStrategy = dns.QueryStrategy_USE_IP
|
||||
case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4":
|
||||
config.QueryStrategy = dns.QueryStrategy_USE_IP4
|
||||
case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6":
|
||||
config.QueryStrategy = dns.QueryStrategy_USE_IP6
|
||||
}
|
||||
|
||||
switch strings.ToLower(c.CacheStrategy) {
|
||||
case "noerror":
|
||||
config.CacheStrategy = dns.CacheStrategy_Cache_NOERROR
|
||||
case "all":
|
||||
config.CacheStrategy = dns.CacheStrategy_Cache_ALL
|
||||
case "disable", "none":
|
||||
config.CacheStrategy = dns.CacheStrategy_Cache_DISABLE
|
||||
}
|
||||
|
||||
for _, server := range c.Servers {
|
||||
ns, err := server.Build()
|
||||
if err != nil {
|
||||
|
@@ -1,18 +1,15 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/proxy/dns"
|
||||
)
|
||||
|
||||
type DNSOutboundConfig struct {
|
||||
Network Network `json:"network"`
|
||||
Address *Address `json:"address"`
|
||||
Port uint16 `json:"port"`
|
||||
DomainStrategy string `json:"domainStrategy"`
|
||||
Network Network `json:"network"`
|
||||
Address *Address `json:"address"`
|
||||
Port uint16 `json:"port"`
|
||||
}
|
||||
|
||||
func (c *DNSOutboundConfig) Build() (proto.Message, error) {
|
||||
@@ -25,12 +22,5 @@ func (c *DNSOutboundConfig) Build() (proto.Message, error) {
|
||||
if c.Address != nil {
|
||||
config.Server.Address = c.Address.Build()
|
||||
}
|
||||
config.DomainStrategy = dns.Config_USE_ALL
|
||||
switch strings.ToLower(c.DomainStrategy) {
|
||||
case "useip", "use_ip", "use-ip":
|
||||
config.DomainStrategy = dns.Config_USE_IP
|
||||
case "fake", "fakedns":
|
||||
config.DomainStrategy = dns.Config_USE_FAKE
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
@@ -27,7 +27,6 @@ func TestDnsProxyConfig(t *testing.T) {
|
||||
Address: net.NewIPOrDomain(net.IPAddress([]byte{8, 8, 8, 8})),
|
||||
Port: 53,
|
||||
},
|
||||
DomainStrategy: dns.Config_USE_ALL,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@@ -67,23 +67,17 @@ func TestDNSConfigParsing(t *testing.T) {
|
||||
Input: `{
|
||||
"servers": [{
|
||||
"address": "8.8.8.8",
|
||||
"clientIp": "10.0.0.1",
|
||||
"port": 5353,
|
||||
"skipFallback": true,
|
||||
"domains": ["domain:example.com"]
|
||||
}],
|
||||
"hosts": {
|
||||
"example.com": "127.0.0.1",
|
||||
"xtls.github.io": ["1.2.3.4", "5.6.7.8"],
|
||||
"domain:example.com": "google.com",
|
||||
"geosite:test": ["127.0.0.1", "127.0.0.2"],
|
||||
"keyword:google": ["8.8.8.8", "8.8.4.4"],
|
||||
"geosite:test": "10.0.0.1",
|
||||
"keyword:google": "8.8.8.8",
|
||||
"regexp:.*\\.com": "8.8.4.4"
|
||||
},
|
||||
"clientIp": "10.0.0.1",
|
||||
"queryStrategy": "UseIPv4",
|
||||
"cacheStrategy": "disable",
|
||||
"disableFallback": true
|
||||
"clientIp": "10.0.0.1"
|
||||
}`,
|
||||
Parser: parserCreator(),
|
||||
Output: &dns.Config{
|
||||
@@ -98,8 +92,6 @@ func TestDNSConfigParsing(t *testing.T) {
|
||||
Network: net.Network_UDP,
|
||||
Port: 5353,
|
||||
},
|
||||
ClientIp: []byte{10, 0, 0, 1},
|
||||
SkipFallback: true,
|
||||
PrioritizedDomain: []*dns.NameServer_PriorityDomain{
|
||||
{
|
||||
Type: dns.DomainMatchingType_Subdomain,
|
||||
@@ -128,28 +120,20 @@ func TestDNSConfigParsing(t *testing.T) {
|
||||
{
|
||||
Type: dns.DomainMatchingType_Full,
|
||||
Domain: "example.com",
|
||||
Ip: [][]byte{{127, 0, 0, 1}, {127, 0, 0, 2}},
|
||||
Ip: [][]byte{{10, 0, 0, 1}},
|
||||
},
|
||||
{
|
||||
Type: dns.DomainMatchingType_Keyword,
|
||||
Domain: "google",
|
||||
Ip: [][]byte{{8, 8, 8, 8}, {8, 8, 4, 4}},
|
||||
Ip: [][]byte{{8, 8, 8, 8}},
|
||||
},
|
||||
{
|
||||
Type: dns.DomainMatchingType_Regex,
|
||||
Domain: ".*\\.com",
|
||||
Ip: [][]byte{{8, 8, 4, 4}},
|
||||
},
|
||||
{
|
||||
Type: dns.DomainMatchingType_Full,
|
||||
Domain: "xtls.github.io",
|
||||
Ip: [][]byte{{1, 2, 3, 4}, {5, 6, 7, 8}},
|
||||
},
|
||||
},
|
||||
ClientIp: []byte{10, 0, 0, 1},
|
||||
QueryStrategy: dns.QueryStrategy_USE_IP4,
|
||||
CacheStrategy: dns.CacheStrategy_Cache_DISABLE,
|
||||
DisableFallback: true,
|
||||
ClientIp: []byte{10, 0, 0, 1},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@@ -22,11 +22,11 @@ func (c *FreedomConfig) Build() (proto.Message, error) {
|
||||
config := new(freedom.Config)
|
||||
config.DomainStrategy = freedom.Config_AS_IS
|
||||
switch strings.ToLower(c.DomainStrategy) {
|
||||
case "useip", "use_ip", "use-ip":
|
||||
case "useip", "use_ip":
|
||||
config.DomainStrategy = freedom.Config_USE_IP
|
||||
case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4":
|
||||
case "useip4", "useipv4", "use_ipv4", "use_ip_v4", "use_ip4":
|
||||
config.DomainStrategy = freedom.Config_USE_IP4
|
||||
case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6":
|
||||
case "useip6", "useipv6", "use_ipv6", "use_ip_v6", "use_ip6":
|
||||
config.DomainStrategy = freedom.Config_USE_IP6
|
||||
}
|
||||
|
||||
|
@@ -7,10 +7,25 @@ import (
|
||||
)
|
||||
|
||||
type GRPCConfig struct {
|
||||
ServiceName string `json:"serviceName"`
|
||||
MultiMode bool `json:"multiMode"`
|
||||
ServiceName string `json:"serviceName" `
|
||||
MultiMode bool `json:"multiMode"`
|
||||
IdleTimeout int32 `json:"idle_timeout"`
|
||||
HealthCheckTimeout int32 `json:"health_check_timeout"`
|
||||
PermitWithoutStream bool `json:"permit_without_stream"`
|
||||
}
|
||||
|
||||
func (g GRPCConfig) Build() (proto.Message, error) {
|
||||
return &grpc.Config{ServiceName: g.ServiceName, MultiMode: g.MultiMode}, nil
|
||||
func (g *GRPCConfig) Build() (proto.Message, error) {
|
||||
if g.IdleTimeout <= 0 {
|
||||
g.IdleTimeout = 0
|
||||
}
|
||||
if g.HealthCheckTimeout <= 0 {
|
||||
g.HealthCheckTimeout = 0
|
||||
}
|
||||
return &grpc.Config{
|
||||
ServiceName: g.ServiceName,
|
||||
MultiMode: g.MultiMode,
|
||||
IdleTimeout: g.IdleTimeout,
|
||||
HealthCheckTimeout: g.HealthCheckTimeout,
|
||||
PermitWithoutStream: g.PermitWithoutStream,
|
||||
}, nil
|
||||
}
|
||||
|
@@ -179,14 +179,24 @@ func (c *WebSocketConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
|
||||
type HTTPConfig struct {
|
||||
Host *StringList `json:"host"`
|
||||
Path string `json:"path"`
|
||||
Host *StringList `json:"host"`
|
||||
Path string `json:"path"`
|
||||
ReadIdleTimeout int32 `json:"read_idle_timeout"`
|
||||
HealthCheckTimeout int32 `json:"health_check_timeout"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
func (c *HTTPConfig) Build() (proto.Message, error) {
|
||||
if c.ReadIdleTimeout <= 0 {
|
||||
c.ReadIdleTimeout = 0
|
||||
}
|
||||
if c.HealthCheckTimeout <= 0 {
|
||||
c.HealthCheckTimeout = 0
|
||||
}
|
||||
config := &http.Config{
|
||||
Path: c.Path,
|
||||
Path: c.Path,
|
||||
IdleTimeout: c.ReadIdleTimeout,
|
||||
HealthCheckTimeout: c.HealthCheckTimeout,
|
||||
}
|
||||
if c.Host != nil {
|
||||
config.Host = []string(*c.Host)
|
||||
@@ -322,6 +332,7 @@ type TLSConfig struct {
|
||||
CipherSuites string `json:"cipherSuites"`
|
||||
PreferServerCipherSuites bool `json:"preferServerCipherSuites"`
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
RejectUnknownSNI bool `json:"rejectUnknownSni"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
@@ -350,6 +361,7 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||
config.CipherSuites = c.CipherSuites
|
||||
config.PreferServerCipherSuites = c.PreferServerCipherSuites
|
||||
config.Fingerprint = strings.ToLower(c.Fingerprint)
|
||||
config.RejectUnknownSni = c.RejectUnknownSNI
|
||||
return config, nil
|
||||
}
|
||||
|
||||
@@ -413,6 +425,7 @@ type XTLSConfig struct {
|
||||
MaxVersion string `json:"maxVersion"`
|
||||
CipherSuites string `json:"cipherSuites"`
|
||||
PreferServerCipherSuites bool `json:"preferServerCipherSuites"`
|
||||
RejectUnknownSNI bool `json:"rejectUnknownSni"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
@@ -440,6 +453,7 @@ func (c *XTLSConfig) Build() (proto.Message, error) {
|
||||
config.MaxVersion = c.MaxVersion
|
||||
config.CipherSuites = c.CipherSuites
|
||||
config.PreferServerCipherSuites = c.PreferServerCipherSuites
|
||||
config.RejectUnknownSni = c.RejectUnknownSNI
|
||||
return config, nil
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.15.8
|
||||
// protoc v3.14.0
|
||||
// source: proxy/dns/config.proto
|
||||
|
||||
package dns
|
||||
@@ -26,55 +26,6 @@ const (
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
type Config_DomainStrategy int32
|
||||
|
||||
const (
|
||||
Config_USE_ALL Config_DomainStrategy = 0
|
||||
Config_USE_IP Config_DomainStrategy = 1
|
||||
Config_USE_FAKE Config_DomainStrategy = 2
|
||||
)
|
||||
|
||||
// Enum value maps for Config_DomainStrategy.
|
||||
var (
|
||||
Config_DomainStrategy_name = map[int32]string{
|
||||
0: "USE_ALL",
|
||||
1: "USE_IP",
|
||||
2: "USE_FAKE",
|
||||
}
|
||||
Config_DomainStrategy_value = map[string]int32{
|
||||
"USE_ALL": 0,
|
||||
"USE_IP": 1,
|
||||
"USE_FAKE": 2,
|
||||
}
|
||||
)
|
||||
|
||||
func (x Config_DomainStrategy) Enum() *Config_DomainStrategy {
|
||||
p := new(Config_DomainStrategy)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x Config_DomainStrategy) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (Config_DomainStrategy) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_proxy_dns_config_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (Config_DomainStrategy) Type() protoreflect.EnumType {
|
||||
return &file_proxy_dns_config_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x Config_DomainStrategy) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Config_DomainStrategy.Descriptor instead.
|
||||
func (Config_DomainStrategy) EnumDescriptor() ([]byte, []int) {
|
||||
return file_proxy_dns_config_proto_rawDescGZIP(), []int{0, 0}
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -82,8 +33,7 @@ type Config struct {
|
||||
|
||||
// Server is the DNS server address. If specified, this address overrides the
|
||||
// original one.
|
||||
Server *net.Endpoint `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"`
|
||||
DomainStrategy Config_DomainStrategy `protobuf:"varint,2,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.proxy.dns.Config_DomainStrategy" json:"domain_strategy,omitempty"`
|
||||
Server *net.Endpoint `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
@@ -125,13 +75,6 @@ func (x *Config) GetServer() *net.Endpoint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetDomainStrategy() Config_DomainStrategy {
|
||||
if x != nil {
|
||||
return x.DomainStrategy
|
||||
}
|
||||
return Config_USE_ALL
|
||||
}
|
||||
|
||||
var File_proxy_dns_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_proxy_dns_config_proto_rawDesc = []byte{
|
||||
@@ -139,25 +82,16 @@ var file_proxy_dns_config_proto_rawDesc = []byte{
|
||||
0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6e, 0x73, 0x1a, 0x1c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
||||
0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc4, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x12, 0x31, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
|
||||
0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x65,
|
||||
0x72, 0x76, 0x65, 0x72, 0x12, 0x4e, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73,
|
||||
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61,
|
||||
0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61,
|
||||
0x74, 0x65, 0x67, 0x79, 0x22, 0x37, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74,
|
||||
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x41, 0x4c,
|
||||
0x4c, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12,
|
||||
0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x46, 0x41, 0x4b, 0x45, 0x10, 0x02, 0x42, 0x4c, 0x0a,
|
||||
0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e,
|
||||
0x64, 0x6e, 0x73, 0x50, 0x01, 0x5a, 0x23, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0e, 0x58, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x33,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3b, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x12, 0x31, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e,
|
||||
0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x65, 0x72,
|
||||
0x76, 0x65, 0x72, 0x42, 0x4c, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6e, 0x73, 0x50, 0x01, 0x5a, 0x23, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x64, 0x6e, 0x73,
|
||||
0xaa, 0x02, 0x0e, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x44, 0x6e,
|
||||
0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -172,21 +106,18 @@ func file_proxy_dns_config_proto_rawDescGZIP() []byte {
|
||||
return file_proxy_dns_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_proxy_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_proxy_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_proxy_dns_config_proto_goTypes = []interface{}{
|
||||
(Config_DomainStrategy)(0), // 0: xray.proxy.dns.Config.DomainStrategy
|
||||
(*Config)(nil), // 1: xray.proxy.dns.Config
|
||||
(*net.Endpoint)(nil), // 2: xray.common.net.Endpoint
|
||||
(*Config)(nil), // 0: xray.proxy.dns.Config
|
||||
(*net.Endpoint)(nil), // 1: xray.common.net.Endpoint
|
||||
}
|
||||
var file_proxy_dns_config_proto_depIdxs = []int32{
|
||||
2, // 0: xray.proxy.dns.Config.server:type_name -> xray.common.net.Endpoint
|
||||
0, // 1: xray.proxy.dns.Config.domain_strategy:type_name -> xray.proxy.dns.Config.DomainStrategy
|
||||
2, // [2:2] is the sub-list for method output_type
|
||||
2, // [2:2] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
1, // 0: xray.proxy.dns.Config.server:type_name -> xray.common.net.Endpoint
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_proxy_dns_config_proto_init() }
|
||||
@@ -213,14 +144,13 @@ func file_proxy_dns_config_proto_init() {
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_proxy_dns_config_proto_rawDesc,
|
||||
NumEnums: 1,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_proxy_dns_config_proto_goTypes,
|
||||
DependencyIndexes: file_proxy_dns_config_proto_depIdxs,
|
||||
EnumInfos: file_proxy_dns_config_proto_enumTypes,
|
||||
MessageInfos: file_proxy_dns_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_proxy_dns_config_proto = out.File
|
||||
|
@@ -12,10 +12,4 @@ message Config {
|
||||
// Server is the DNS server address. If specified, this address overrides the
|
||||
// original one.
|
||||
xray.common.net.Endpoint server = 1;
|
||||
enum DomainStrategy {
|
||||
USE_ALL = 0;
|
||||
USE_IP = 1;
|
||||
USE_FAKE = 2;
|
||||
}
|
||||
DomainStrategy domain_strategy = 2;
|
||||
}
|
||||
|
@@ -39,12 +39,10 @@ type Handler struct {
|
||||
client dns.Client
|
||||
ownLinkVerifier ownLinkVerifier
|
||||
server net.Destination
|
||||
DomainStrategy Config_DomainStrategy
|
||||
}
|
||||
|
||||
func (h *Handler) Init(config *Config, dnsClient dns.Client) error {
|
||||
h.client = dnsClient
|
||||
|
||||
if v, ok := dnsClient.(ownLinkVerifier); ok {
|
||||
h.ownLinkVerifier = v
|
||||
}
|
||||
@@ -200,24 +198,22 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string,
|
||||
var err error
|
||||
|
||||
var ttl uint32 = 600
|
||||
var opt []dns.Option
|
||||
|
||||
if h.DomainStrategy == Config_USE_FAKE {
|
||||
opt = append(opt, dns.LookupFakeOnly)
|
||||
} else {
|
||||
switch qType {
|
||||
case dnsmessage.TypeA:
|
||||
opt = append(opt, dns.LookupIPv4Only)
|
||||
case dnsmessage.TypeAAAA:
|
||||
opt = append(opt, dns.LookupIPv6Only)
|
||||
}
|
||||
switch qType {
|
||||
case dnsmessage.TypeA:
|
||||
ips, err = h.client.LookupIP(domain, dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: false,
|
||||
FakeEnable: true,
|
||||
})
|
||||
case dnsmessage.TypeAAAA:
|
||||
ips, err = h.client.LookupIP(domain, dns.IPOption{
|
||||
IPv4Enable: false,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: true,
|
||||
})
|
||||
}
|
||||
|
||||
if h.DomainStrategy == Config_USE_ALL {
|
||||
opt = append(opt, dns.LookupFake)
|
||||
}
|
||||
|
||||
ips, err = h.client.LookupOptions(domain, opt...)
|
||||
rcode := dns.RCodeFromError(err)
|
||||
if rcode == 0 && len(ips) == 0 && err != dns.ErrEmptyResponse {
|
||||
newError("ip query").Base(err).WriteToLog()
|
||||
@@ -232,6 +228,7 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string,
|
||||
RecursionAvailable: true,
|
||||
RecursionDesired: true,
|
||||
Response: true,
|
||||
Authoritative: true,
|
||||
})
|
||||
builder.EnableCompression()
|
||||
common.Must(builder.StartQuestions())
|
||||
|
@@ -59,14 +59,26 @@ func (h *Handler) policy() policy.Session {
|
||||
}
|
||||
|
||||
func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address {
|
||||
var opt dns.Option
|
||||
var option dns.IPOption = dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
}
|
||||
if h.config.DomainStrategy == Config_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()) {
|
||||
opt = dns.LookupIPv4Only
|
||||
option = dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: false,
|
||||
FakeEnable: false,
|
||||
}
|
||||
} else if h.config.DomainStrategy == Config_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()) {
|
||||
opt = dns.LookupIPv6Only
|
||||
option = dns.IPOption{
|
||||
IPv4Enable: false,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
}
|
||||
}
|
||||
|
||||
ips, err := h.dns.LookupOptions(domain, opt, dns.LookupNoFake)
|
||||
ips, err := h.dns.LookupIP(domain, option)
|
||||
if err != nil {
|
||||
newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
|
@@ -5,37 +5,36 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
net "net"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
dns "github.com/xtls/xray-core/features/dns"
|
||||
net "net"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// DNSClient is a mock of Client interface.
|
||||
// DNSClient is a mock of Client interface
|
||||
type DNSClient struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *DNSClientMockRecorder
|
||||
}
|
||||
|
||||
// DNSClientMockRecorder is the mock recorder for DNSClient.
|
||||
// DNSClientMockRecorder is the mock recorder for DNSClient
|
||||
type DNSClientMockRecorder struct {
|
||||
mock *DNSClient
|
||||
}
|
||||
|
||||
// NewDNSClient creates a new mock instance.
|
||||
// NewDNSClient creates a new mock instance
|
||||
func NewDNSClient(ctrl *gomock.Controller) *DNSClient {
|
||||
mock := &DNSClient{ctrl: ctrl}
|
||||
mock.recorder = &DNSClientMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *DNSClient) EXPECT() *DNSClientMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Close mocks base method.
|
||||
// Close mocks base method
|
||||
func (m *DNSClient) Close() error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Close")
|
||||
@@ -43,48 +42,28 @@ func (m *DNSClient) Close() error {
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Close indicates an expected call of Close.
|
||||
// Close indicates an expected call of Close
|
||||
func (mr *DNSClientMockRecorder) Close() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*DNSClient)(nil).Close))
|
||||
}
|
||||
|
||||
// LookupIP mocks base method.
|
||||
func (m *DNSClient) LookupIP(arg0 string) ([]net.IP, error) {
|
||||
// LookupIP mocks base method
|
||||
func (m *DNSClient) LookupIP(arg0 string, arg1 dns.IPOption) ([]net.IP, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "LookupIP", arg0)
|
||||
ret := m.ctrl.Call(m, "LookupIP", arg0, arg1)
|
||||
ret0, _ := ret[0].([]net.IP)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// LookupIP indicates an expected call of LookupIP.
|
||||
func (mr *DNSClientMockRecorder) LookupIP(arg0 interface{}) *gomock.Call {
|
||||
// LookupIP indicates an expected call of LookupIP
|
||||
func (mr *DNSClientMockRecorder) LookupIP(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupIP", reflect.TypeOf((*DNSClient)(nil).LookupIP), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupIP", reflect.TypeOf((*DNSClient)(nil).LookupIP), arg0, arg1)
|
||||
}
|
||||
|
||||
// LookupOptions mocks base method.
|
||||
func (m *DNSClient) LookupOptions(arg0 string, arg1 ...dns.Option) ([]net.IP, error) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []interface{}{arg0}
|
||||
for _, a := range arg1 {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "LookupOptions", varargs...)
|
||||
ret0, _ := ret[0].([]net.IP)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// LookupOptions indicates an expected call of LookupOptions.
|
||||
func (mr *DNSClientMockRecorder) LookupOptions(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
varargs := append([]interface{}{arg0}, arg1...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupOptions", reflect.TypeOf((*DNSClient)(nil).LookupOptions), varargs...)
|
||||
}
|
||||
|
||||
// Start mocks base method.
|
||||
// Start mocks base method
|
||||
func (m *DNSClient) Start() error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Start")
|
||||
@@ -92,13 +71,13 @@ func (m *DNSClient) Start() error {
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Start indicates an expected call of Start.
|
||||
// Start indicates an expected call of Start
|
||||
func (mr *DNSClientMockRecorder) Start() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*DNSClient)(nil).Start))
|
||||
}
|
||||
|
||||
// Type mocks base method.
|
||||
// Type mocks base method
|
||||
func (m *DNSClient) Type() interface{} {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Type")
|
||||
@@ -106,7 +85,7 @@ func (m *DNSClient) Type() interface{} {
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Type indicates an expected call of Type.
|
||||
// Type indicates an expected call of Type
|
||||
func (mr *DNSClientMockRecorder) Type() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Type", reflect.TypeOf((*DNSClient)(nil).Type))
|
||||
|
@@ -5,35 +5,34 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// Reader is a mock of Reader interface.
|
||||
// Reader is a mock of Reader interface
|
||||
type Reader struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *ReaderMockRecorder
|
||||
}
|
||||
|
||||
// ReaderMockRecorder is the mock recorder for Reader.
|
||||
// ReaderMockRecorder is the mock recorder for Reader
|
||||
type ReaderMockRecorder struct {
|
||||
mock *Reader
|
||||
}
|
||||
|
||||
// NewReader creates a new mock instance.
|
||||
// NewReader creates a new mock instance
|
||||
func NewReader(ctrl *gomock.Controller) *Reader {
|
||||
mock := &Reader{ctrl: ctrl}
|
||||
mock.recorder = &ReaderMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *Reader) EXPECT() *ReaderMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Read mocks base method.
|
||||
// Read mocks base method
|
||||
func (m *Reader) Read(arg0 []byte) (int, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Read", arg0)
|
||||
@@ -42,36 +41,36 @@ func (m *Reader) Read(arg0 []byte) (int, error) {
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Read indicates an expected call of Read.
|
||||
// Read indicates an expected call of Read
|
||||
func (mr *ReaderMockRecorder) Read(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*Reader)(nil).Read), arg0)
|
||||
}
|
||||
|
||||
// Writer is a mock of Writer interface.
|
||||
// Writer is a mock of Writer interface
|
||||
type Writer struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *WriterMockRecorder
|
||||
}
|
||||
|
||||
// WriterMockRecorder is the mock recorder for Writer.
|
||||
// WriterMockRecorder is the mock recorder for Writer
|
||||
type WriterMockRecorder struct {
|
||||
mock *Writer
|
||||
}
|
||||
|
||||
// NewWriter creates a new mock instance.
|
||||
// NewWriter creates a new mock instance
|
||||
func NewWriter(ctrl *gomock.Controller) *Writer {
|
||||
mock := &Writer{ctrl: ctrl}
|
||||
mock.recorder = &WriterMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *Writer) EXPECT() *WriterMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Write mocks base method.
|
||||
// Write mocks base method
|
||||
func (m *Writer) Write(arg0 []byte) (int, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Write", arg0)
|
||||
@@ -80,7 +79,7 @@ func (m *Writer) Write(arg0 []byte) (int, error) {
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Write indicates an expected call of Write.
|
||||
// Write indicates an expected call of Write
|
||||
func (mr *WriterMockRecorder) Write(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*Writer)(nil).Write), arg0)
|
||||
|
@@ -5,42 +5,41 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
log "github.com/xtls/xray-core/common/log"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// LogHandler is a mock of Handler interface.
|
||||
// LogHandler is a mock of Handler interface
|
||||
type LogHandler struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *LogHandlerMockRecorder
|
||||
}
|
||||
|
||||
// LogHandlerMockRecorder is the mock recorder for LogHandler.
|
||||
// LogHandlerMockRecorder is the mock recorder for LogHandler
|
||||
type LogHandlerMockRecorder struct {
|
||||
mock *LogHandler
|
||||
}
|
||||
|
||||
// NewLogHandler creates a new mock instance.
|
||||
// NewLogHandler creates a new mock instance
|
||||
func NewLogHandler(ctrl *gomock.Controller) *LogHandler {
|
||||
mock := &LogHandler{ctrl: ctrl}
|
||||
mock.recorder = &LogHandlerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *LogHandler) EXPECT() *LogHandlerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Handle mocks base method.
|
||||
// Handle mocks base method
|
||||
func (m *LogHandler) Handle(arg0 log.Message) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "Handle", arg0)
|
||||
}
|
||||
|
||||
// Handle indicates an expected call of Handle.
|
||||
// Handle indicates an expected call of Handle
|
||||
func (mr *LogHandlerMockRecorder) Handle(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Handle", reflect.TypeOf((*LogHandler)(nil).Handle), arg0)
|
||||
|
@@ -5,36 +5,35 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
mux "github.com/xtls/xray-core/common/mux"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MuxClientWorkerFactory is a mock of ClientWorkerFactory interface.
|
||||
// MuxClientWorkerFactory is a mock of ClientWorkerFactory interface
|
||||
type MuxClientWorkerFactory struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MuxClientWorkerFactoryMockRecorder
|
||||
}
|
||||
|
||||
// MuxClientWorkerFactoryMockRecorder is the mock recorder for MuxClientWorkerFactory.
|
||||
// MuxClientWorkerFactoryMockRecorder is the mock recorder for MuxClientWorkerFactory
|
||||
type MuxClientWorkerFactoryMockRecorder struct {
|
||||
mock *MuxClientWorkerFactory
|
||||
}
|
||||
|
||||
// NewMuxClientWorkerFactory creates a new mock instance.
|
||||
// NewMuxClientWorkerFactory creates a new mock instance
|
||||
func NewMuxClientWorkerFactory(ctrl *gomock.Controller) *MuxClientWorkerFactory {
|
||||
mock := &MuxClientWorkerFactory{ctrl: ctrl}
|
||||
mock.recorder = &MuxClientWorkerFactoryMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MuxClientWorkerFactory) EXPECT() *MuxClientWorkerFactoryMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Create mocks base method.
|
||||
// Create mocks base method
|
||||
func (m *MuxClientWorkerFactory) Create() (*mux.ClientWorker, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Create")
|
||||
@@ -43,7 +42,7 @@ func (m *MuxClientWorkerFactory) Create() (*mux.ClientWorker, error) {
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Create indicates an expected call of Create.
|
||||
// Create indicates an expected call of Create
|
||||
func (mr *MuxClientWorkerFactoryMockRecorder) Create() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MuxClientWorkerFactory)(nil).Create))
|
||||
|
@@ -6,36 +6,35 @@ package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
outbound "github.com/xtls/xray-core/features/outbound"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// OutboundManager is a mock of Manager interface.
|
||||
// OutboundManager is a mock of Manager interface
|
||||
type OutboundManager struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *OutboundManagerMockRecorder
|
||||
}
|
||||
|
||||
// OutboundManagerMockRecorder is the mock recorder for OutboundManager.
|
||||
// OutboundManagerMockRecorder is the mock recorder for OutboundManager
|
||||
type OutboundManagerMockRecorder struct {
|
||||
mock *OutboundManager
|
||||
}
|
||||
|
||||
// NewOutboundManager creates a new mock instance.
|
||||
// NewOutboundManager creates a new mock instance
|
||||
func NewOutboundManager(ctrl *gomock.Controller) *OutboundManager {
|
||||
mock := &OutboundManager{ctrl: ctrl}
|
||||
mock.recorder = &OutboundManagerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *OutboundManager) EXPECT() *OutboundManagerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// AddHandler mocks base method.
|
||||
// AddHandler mocks base method
|
||||
func (m *OutboundManager) AddHandler(arg0 context.Context, arg1 outbound.Handler) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "AddHandler", arg0, arg1)
|
||||
@@ -43,13 +42,13 @@ func (m *OutboundManager) AddHandler(arg0 context.Context, arg1 outbound.Handler
|
||||
return ret0
|
||||
}
|
||||
|
||||
// AddHandler indicates an expected call of AddHandler.
|
||||
// AddHandler indicates an expected call of AddHandler
|
||||
func (mr *OutboundManagerMockRecorder) AddHandler(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddHandler", reflect.TypeOf((*OutboundManager)(nil).AddHandler), arg0, arg1)
|
||||
}
|
||||
|
||||
// Close mocks base method.
|
||||
// Close mocks base method
|
||||
func (m *OutboundManager) Close() error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Close")
|
||||
@@ -57,13 +56,13 @@ func (m *OutboundManager) Close() error {
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Close indicates an expected call of Close.
|
||||
// Close indicates an expected call of Close
|
||||
func (mr *OutboundManagerMockRecorder) Close() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*OutboundManager)(nil).Close))
|
||||
}
|
||||
|
||||
// GetDefaultHandler mocks base method.
|
||||
// GetDefaultHandler mocks base method
|
||||
func (m *OutboundManager) GetDefaultHandler() outbound.Handler {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetDefaultHandler")
|
||||
@@ -71,13 +70,13 @@ func (m *OutboundManager) GetDefaultHandler() outbound.Handler {
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetDefaultHandler indicates an expected call of GetDefaultHandler.
|
||||
// GetDefaultHandler indicates an expected call of GetDefaultHandler
|
||||
func (mr *OutboundManagerMockRecorder) GetDefaultHandler() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultHandler", reflect.TypeOf((*OutboundManager)(nil).GetDefaultHandler))
|
||||
}
|
||||
|
||||
// GetHandler mocks base method.
|
||||
// GetHandler mocks base method
|
||||
func (m *OutboundManager) GetHandler(arg0 string) outbound.Handler {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetHandler", arg0)
|
||||
@@ -85,13 +84,13 @@ func (m *OutboundManager) GetHandler(arg0 string) outbound.Handler {
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetHandler indicates an expected call of GetHandler.
|
||||
// GetHandler indicates an expected call of GetHandler
|
||||
func (mr *OutboundManagerMockRecorder) GetHandler(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHandler", reflect.TypeOf((*OutboundManager)(nil).GetHandler), arg0)
|
||||
}
|
||||
|
||||
// RemoveHandler mocks base method.
|
||||
// RemoveHandler mocks base method
|
||||
func (m *OutboundManager) RemoveHandler(arg0 context.Context, arg1 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RemoveHandler", arg0, arg1)
|
||||
@@ -99,13 +98,13 @@ func (m *OutboundManager) RemoveHandler(arg0 context.Context, arg1 string) error
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RemoveHandler indicates an expected call of RemoveHandler.
|
||||
// RemoveHandler indicates an expected call of RemoveHandler
|
||||
func (mr *OutboundManagerMockRecorder) RemoveHandler(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveHandler", reflect.TypeOf((*OutboundManager)(nil).RemoveHandler), arg0, arg1)
|
||||
}
|
||||
|
||||
// Start mocks base method.
|
||||
// Start mocks base method
|
||||
func (m *OutboundManager) Start() error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Start")
|
||||
@@ -113,13 +112,13 @@ func (m *OutboundManager) Start() error {
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Start indicates an expected call of Start.
|
||||
// Start indicates an expected call of Start
|
||||
func (mr *OutboundManagerMockRecorder) Start() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*OutboundManager)(nil).Start))
|
||||
}
|
||||
|
||||
// Type mocks base method.
|
||||
// Type mocks base method
|
||||
func (m *OutboundManager) Type() interface{} {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Type")
|
||||
@@ -127,36 +126,36 @@ func (m *OutboundManager) Type() interface{} {
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Type indicates an expected call of Type.
|
||||
// Type indicates an expected call of Type
|
||||
func (mr *OutboundManagerMockRecorder) Type() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Type", reflect.TypeOf((*OutboundManager)(nil).Type))
|
||||
}
|
||||
|
||||
// OutboundHandlerSelector is a mock of HandlerSelector interface.
|
||||
// OutboundHandlerSelector is a mock of HandlerSelector interface
|
||||
type OutboundHandlerSelector struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *OutboundHandlerSelectorMockRecorder
|
||||
}
|
||||
|
||||
// OutboundHandlerSelectorMockRecorder is the mock recorder for OutboundHandlerSelector.
|
||||
// OutboundHandlerSelectorMockRecorder is the mock recorder for OutboundHandlerSelector
|
||||
type OutboundHandlerSelectorMockRecorder struct {
|
||||
mock *OutboundHandlerSelector
|
||||
}
|
||||
|
||||
// NewOutboundHandlerSelector creates a new mock instance.
|
||||
// NewOutboundHandlerSelector creates a new mock instance
|
||||
func NewOutboundHandlerSelector(ctrl *gomock.Controller) *OutboundHandlerSelector {
|
||||
mock := &OutboundHandlerSelector{ctrl: ctrl}
|
||||
mock.recorder = &OutboundHandlerSelectorMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *OutboundHandlerSelector) EXPECT() *OutboundHandlerSelectorMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Select mocks base method.
|
||||
// Select mocks base method
|
||||
func (m *OutboundHandlerSelector) Select(arg0 []string) []string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Select", arg0)
|
||||
@@ -164,7 +163,7 @@ func (m *OutboundHandlerSelector) Select(arg0 []string) []string {
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Select indicates an expected call of Select.
|
||||
// Select indicates an expected call of Select
|
||||
func (mr *OutboundHandlerSelectorMockRecorder) Select(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Select", reflect.TypeOf((*OutboundHandlerSelector)(nil).Select), arg0)
|
||||
|
@@ -6,39 +6,38 @@ package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
net "github.com/xtls/xray-core/common/net"
|
||||
routing "github.com/xtls/xray-core/features/routing"
|
||||
transport "github.com/xtls/xray-core/transport"
|
||||
internet "github.com/xtls/xray-core/transport/internet"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// ProxyInbound is a mock of Inbound interface.
|
||||
// ProxyInbound is a mock of Inbound interface
|
||||
type ProxyInbound struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *ProxyInboundMockRecorder
|
||||
}
|
||||
|
||||
// ProxyInboundMockRecorder is the mock recorder for ProxyInbound.
|
||||
// ProxyInboundMockRecorder is the mock recorder for ProxyInbound
|
||||
type ProxyInboundMockRecorder struct {
|
||||
mock *ProxyInbound
|
||||
}
|
||||
|
||||
// NewProxyInbound creates a new mock instance.
|
||||
// NewProxyInbound creates a new mock instance
|
||||
func NewProxyInbound(ctrl *gomock.Controller) *ProxyInbound {
|
||||
mock := &ProxyInbound{ctrl: ctrl}
|
||||
mock.recorder = &ProxyInboundMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *ProxyInbound) EXPECT() *ProxyInboundMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Network mocks base method.
|
||||
// Network mocks base method
|
||||
func (m *ProxyInbound) Network() []net.Network {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Network")
|
||||
@@ -46,13 +45,13 @@ func (m *ProxyInbound) Network() []net.Network {
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Network indicates an expected call of Network.
|
||||
// Network indicates an expected call of Network
|
||||
func (mr *ProxyInboundMockRecorder) Network() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Network", reflect.TypeOf((*ProxyInbound)(nil).Network))
|
||||
}
|
||||
|
||||
// Process mocks base method.
|
||||
// Process mocks base method
|
||||
func (m *ProxyInbound) Process(arg0 context.Context, arg1 net.Network, arg2 internet.Connection, arg3 routing.Dispatcher) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Process", arg0, arg1, arg2, arg3)
|
||||
@@ -60,36 +59,36 @@ func (m *ProxyInbound) Process(arg0 context.Context, arg1 net.Network, arg2 inte
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Process indicates an expected call of Process.
|
||||
// Process indicates an expected call of Process
|
||||
func (mr *ProxyInboundMockRecorder) Process(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Process", reflect.TypeOf((*ProxyInbound)(nil).Process), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// ProxyOutbound is a mock of Outbound interface.
|
||||
// ProxyOutbound is a mock of Outbound interface
|
||||
type ProxyOutbound struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *ProxyOutboundMockRecorder
|
||||
}
|
||||
|
||||
// ProxyOutboundMockRecorder is the mock recorder for ProxyOutbound.
|
||||
// ProxyOutboundMockRecorder is the mock recorder for ProxyOutbound
|
||||
type ProxyOutboundMockRecorder struct {
|
||||
mock *ProxyOutbound
|
||||
}
|
||||
|
||||
// NewProxyOutbound creates a new mock instance.
|
||||
// NewProxyOutbound creates a new mock instance
|
||||
func NewProxyOutbound(ctrl *gomock.Controller) *ProxyOutbound {
|
||||
mock := &ProxyOutbound{ctrl: ctrl}
|
||||
mock.recorder = &ProxyOutboundMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *ProxyOutbound) EXPECT() *ProxyOutboundMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Process mocks base method.
|
||||
// Process mocks base method
|
||||
func (m *ProxyOutbound) Process(arg0 context.Context, arg1 *transport.Link, arg2 internet.Dialer) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Process", arg0, arg1, arg2)
|
||||
@@ -97,7 +96,7 @@ func (m *ProxyOutbound) Process(arg0 context.Context, arg1 *transport.Link, arg2
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Process indicates an expected call of Process.
|
||||
// Process indicates an expected call of Process
|
||||
func (mr *ProxyOutboundMockRecorder) Process(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Process", reflect.TypeOf((*ProxyOutbound)(nil).Process), arg0, arg1, arg2)
|
||||
|
@@ -3,8 +3,15 @@ package internet
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"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/transport"
|
||||
"github.com/xtls/xray-core/transport/pipe"
|
||||
)
|
||||
|
||||
// Dialer is the interface for dialing outbound connections.
|
||||
@@ -62,11 +69,99 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *MemoryStrea
|
||||
return nil, newError("unknown network ", dest.Network)
|
||||
}
|
||||
|
||||
var (
|
||||
dnsClient dns.Client
|
||||
obm outbound.Manager
|
||||
)
|
||||
|
||||
func lookupIP(domain string, strategy DomainStrategy, localAddr net.Address) ([]net.IP, error) {
|
||||
if dnsClient == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var option = dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
}
|
||||
|
||||
switch {
|
||||
case strategy == DomainStrategy_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()):
|
||||
option = dns.IPOption{
|
||||
IPv4Enable: true,
|
||||
IPv6Enable: false,
|
||||
FakeEnable: false,
|
||||
}
|
||||
case strategy == DomainStrategy_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()):
|
||||
option = dns.IPOption{
|
||||
IPv4Enable: false,
|
||||
IPv6Enable: true,
|
||||
FakeEnable: false,
|
||||
}
|
||||
case strategy == DomainStrategy_AS_IS:
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return dnsClient.LookupIP(domain, option)
|
||||
}
|
||||
|
||||
func canLookupIP(ctx context.Context, dst net.Destination, sockopt *SocketConfig) bool {
|
||||
if dst.Address.Family().IsIP() || dnsClient == nil {
|
||||
return false
|
||||
}
|
||||
return sockopt.DomainStrategy != DomainStrategy_AS_IS
|
||||
}
|
||||
|
||||
func redirect(ctx context.Context, dst net.Destination, obt string) net.Conn {
|
||||
newError("redirecting request " + dst.String() + " to " + obt).WriteToLog(session.ExportIDToError(ctx))
|
||||
h := obm.GetHandler(obt)
|
||||
ctx = session.ContextWithOutbound(ctx, &session.Outbound{dst, nil})
|
||||
if h != nil {
|
||||
ur, uw := pipe.New(pipe.OptionsFromContext(ctx)...)
|
||||
dr, dw := pipe.New(pipe.OptionsFromContext(ctx)...)
|
||||
|
||||
go h.Dispatch(ctx, &transport.Link{ur, dw})
|
||||
nc := cnc.NewConnection(
|
||||
cnc.ConnectionInputMulti(uw),
|
||||
cnc.ConnectionOutputMulti(dr),
|
||||
cnc.ConnectionOnClose(common.ChainedClosable{uw, dw}),
|
||||
)
|
||||
return nc
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DialSystem calls system dialer to create a network connection.
|
||||
func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) {
|
||||
var src net.Address
|
||||
if outbound := session.OutboundFromContext(ctx); outbound != nil {
|
||||
src = outbound.Gateway
|
||||
}
|
||||
if sockopt == nil {
|
||||
return effectiveSystemDialer.Dial(ctx, src, dest, sockopt)
|
||||
}
|
||||
|
||||
if canLookupIP(ctx, dest, sockopt) {
|
||||
ips, err := lookupIP(dest.Address.String(), sockopt.DomainStrategy, src)
|
||||
if err == nil && len(ips) > 0 {
|
||||
dest.Address = net.IPAddress(ips[dice.Roll(len(ips))])
|
||||
newError("replace destination with " + dest.String()).AtInfo().WriteToLog()
|
||||
} else if err != nil {
|
||||
newError("failed to resolve ip").Base(err).AtWarning().WriteToLog()
|
||||
}
|
||||
}
|
||||
|
||||
if obm != nil && len(sockopt.DialerProxy) > 0 {
|
||||
nc := redirect(ctx, dest, sockopt.DialerProxy)
|
||||
if nc != nil {
|
||||
return nc, nil
|
||||
}
|
||||
}
|
||||
|
||||
return effectiveSystemDialer.Dial(ctx, src, dest, sockopt)
|
||||
}
|
||||
|
||||
func InitSystemDialer(dc dns.Client, om outbound.Manager) {
|
||||
dnsClient = dc
|
||||
obm = om
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
)
|
||||
@@ -12,3 +14,7 @@ func init() {
|
||||
return new(Config)
|
||||
}))
|
||||
}
|
||||
|
||||
func (c *Config) getNormalizedName() string {
|
||||
return url.PathEscape(c.ServiceName)
|
||||
}
|
||||
|
@@ -1,13 +1,12 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.15.6
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.17.3
|
||||
// source: transport/internet/grpc/config.proto
|
||||
|
||||
package grpc
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
@@ -21,18 +20,17 @@ const (
|
||||
_ = 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 {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
|
||||
ServiceName string `protobuf:"bytes,2,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"`
|
||||
MultiMode bool `protobuf:"varint,3,opt,name=multi_mode,json=multiMode,proto3" json:"multi_mode,omitempty"`
|
||||
Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
|
||||
ServiceName string `protobuf:"bytes,2,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"`
|
||||
MultiMode bool `protobuf:"varint,3,opt,name=multi_mode,json=multiMode,proto3" json:"multi_mode,omitempty"`
|
||||
IdleTimeout int32 `protobuf:"varint,4,opt,name=idle_timeout,json=idleTimeout,proto3" json:"idle_timeout,omitempty"`
|
||||
HealthCheckTimeout int32 `protobuf:"varint,5,opt,name=health_check_timeout,json=healthCheckTimeout,proto3" json:"health_check_timeout,omitempty"`
|
||||
PermitWithoutStream bool `protobuf:"varint,6,opt,name=permit_without_stream,json=permitWithoutStream,proto3" json:"permit_without_stream,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
@@ -88,6 +86,27 @@ func (x *Config) GetMultiMode() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *Config) GetIdleTimeout() int32 {
|
||||
if x != nil {
|
||||
return x.IdleTimeout
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetHealthCheckTimeout() int32 {
|
||||
if x != nil {
|
||||
return x.HealthCheckTimeout
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetPermitWithoutStream() bool {
|
||||
if x != nil {
|
||||
return x.PermitWithoutStream
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var File_transport_internet_grpc_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_transport_internet_grpc_config_proto_rawDesc = []byte{
|
||||
@@ -95,17 +114,26 @@ var file_transport_internet_grpc_config_proto_rawDesc = []byte{
|
||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x25, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
||||
0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22, 0x5e, 0x0a,
|
||||
0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73,
|
||||
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d,
|
||||
0x0a, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x09, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x33, 0x5a,
|
||||
0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73,
|
||||
0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73,
|
||||
0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x67, 0x72,
|
||||
0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22, 0xe7, 0x01,
|
||||
0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c,
|
||||
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12,
|
||||
0x1d, 0x0a, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x09, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x21,
|
||||
0x0a, 0x0c, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x69, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75,
|
||||
0x74, 0x12, 0x30, 0x0a, 0x14, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x5f, 0x63, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52,
|
||||
0x12, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65,
|
||||
0x6f, 0x75, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x5f, 0x77, 0x69,
|
||||
0x74, 0x68, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x06, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x13, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x57, 0x69, 0x74, 0x68, 0x6f, 0x75,
|
||||
0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d,
|
||||
0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69,
|
||||
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@@ -7,4 +7,7 @@ message Config {
|
||||
string host = 1;
|
||||
string service_name = 2;
|
||||
bool multi_mode = 3;
|
||||
int32 idle_timeout = 4;
|
||||
int32 health_check_timeout = 5;
|
||||
bool permit_without_stream = 6;
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ import (
|
||||
"google.golang.org/grpc/backoff"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
@@ -35,8 +36,7 @@ func init() {
|
||||
|
||||
type dialerConf struct {
|
||||
net.Destination
|
||||
*internet.SocketConfig
|
||||
*tls.Config
|
||||
*internet.MemoryStreamConfig
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -47,9 +47,7 @@ var (
|
||||
func dialgRPC(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (net.Conn, error) {
|
||||
grpcSettings := streamSettings.ProtocolSettings.(*Config)
|
||||
|
||||
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
||||
|
||||
conn, err := getGrpcClient(ctx, dest, tlsConfig, streamSettings.SocketSettings)
|
||||
conn, err := getGrpcClient(ctx, dest, streamSettings)
|
||||
|
||||
if err != nil {
|
||||
return nil, newError("Cannot dial gRPC").Base(err)
|
||||
@@ -57,14 +55,14 @@ func dialgRPC(ctx context.Context, dest net.Destination, streamSettings *interne
|
||||
client := encoding.NewGRPCServiceClient(conn)
|
||||
if grpcSettings.MultiMode {
|
||||
newError("using gRPC multi mode").AtDebug().WriteToLog()
|
||||
grpcService, err := client.(encoding.GRPCServiceClientX).TunMultiCustomName(ctx, grpcSettings.ServiceName)
|
||||
grpcService, err := client.(encoding.GRPCServiceClientX).TunMultiCustomName(ctx, grpcSettings.getNormalizedName())
|
||||
if err != nil {
|
||||
return nil, newError("Cannot dial gRPC").Base(err)
|
||||
}
|
||||
return encoding.NewMultiHunkConn(grpcService, nil), nil
|
||||
}
|
||||
|
||||
grpcService, err := client.(encoding.GRPCServiceClientX).TunCustomName(ctx, grpcSettings.ServiceName)
|
||||
grpcService, err := client.(encoding.GRPCServiceClientX).TunCustomName(ctx, grpcSettings.getNormalizedName())
|
||||
if err != nil {
|
||||
return nil, newError("Cannot dial gRPC").Base(err)
|
||||
}
|
||||
@@ -72,33 +70,22 @@ func dialgRPC(ctx context.Context, dest net.Destination, streamSettings *interne
|
||||
return encoding.NewHunkConn(grpcService, nil), nil
|
||||
}
|
||||
|
||||
func getGrpcClient(ctx context.Context, dest net.Destination, tlsConfig *tls.Config, sockopt *internet.SocketConfig) (*grpc.ClientConn, error) {
|
||||
func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (*grpc.ClientConn, error) {
|
||||
globalDialerAccess.Lock()
|
||||
defer globalDialerAccess.Unlock()
|
||||
|
||||
if globalDialerMap == nil {
|
||||
globalDialerMap = make(map[dialerConf]*grpc.ClientConn)
|
||||
}
|
||||
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
||||
sockopt := streamSettings.SocketSettings
|
||||
grpcSettings := streamSettings.ProtocolSettings.(*Config)
|
||||
|
||||
if client, found := globalDialerMap[dialerConf{dest, sockopt, tlsConfig}]; found && client.GetState() != connectivity.Shutdown {
|
||||
if client, found := globalDialerMap[dialerConf{dest, streamSettings}]; found && client.GetState() != connectivity.Shutdown {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
dialOption := grpc.WithInsecure()
|
||||
|
||||
if tlsConfig != nil {
|
||||
dialOption = grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig.GetTLSConfig()))
|
||||
}
|
||||
|
||||
var grpcDestHost string
|
||||
if dest.Address.Family().IsDomain() {
|
||||
grpcDestHost = dest.Address.Domain()
|
||||
} else {
|
||||
grpcDestHost = dest.Address.IP().String()
|
||||
}
|
||||
conn, err := grpc.Dial(
|
||||
gonet.JoinHostPort(grpcDestHost, dest.Port.String()),
|
||||
dialOption,
|
||||
var dialOptions = []grpc.DialOption{
|
||||
grpc.WithConnectParams(grpc.ConnectParams{
|
||||
Backoff: backoff.Config{
|
||||
BaseDelay: 500 * time.Millisecond,
|
||||
@@ -132,7 +119,33 @@ func getGrpcClient(ctx context.Context, dest net.Destination, tlsConfig *tls.Con
|
||||
address := net.ParseAddress(rawHost)
|
||||
return internet.DialSystem(gctx, net.TCPDestination(address, port), sockopt)
|
||||
}),
|
||||
}
|
||||
|
||||
if tlsConfig != nil {
|
||||
dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig.GetTLSConfig())))
|
||||
} else {
|
||||
dialOptions = append(dialOptions, grpc.WithInsecure())
|
||||
}
|
||||
|
||||
if grpcSettings.IdleTimeout > 0 || grpcSettings.HealthCheckTimeout > 0 || grpcSettings.PermitWithoutStream {
|
||||
dialOptions = append(dialOptions, grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
||||
Time: time.Second * time.Duration(grpcSettings.IdleTimeout),
|
||||
Timeout: time.Second * time.Duration(grpcSettings.HealthCheckTimeout),
|
||||
PermitWithoutStream: grpcSettings.PermitWithoutStream,
|
||||
}))
|
||||
}
|
||||
|
||||
var grpcDestHost string
|
||||
if dest.Address.Family().IsDomain() {
|
||||
grpcDestHost = dest.Address.Domain()
|
||||
} else {
|
||||
grpcDestHost = dest.Address.IP().String()
|
||||
}
|
||||
|
||||
conn, err := grpc.Dial(
|
||||
gonet.JoinHostPort(grpcDestHost, dest.Port.String()),
|
||||
dialOptions...,
|
||||
)
|
||||
globalDialerMap[dialerConf{dest, sockopt, tlsConfig}] = conn
|
||||
globalDialerMap[dialerConf{dest, streamSettings}] = conn
|
||||
return conn, err
|
||||
}
|
||||
|
@@ -39,7 +39,7 @@ func (c *gRPCServiceClient) TunCustomName(ctx context.Context, name string, opts
|
||||
}
|
||||
|
||||
func (c *gRPCServiceClient) TunMultiCustomName(ctx context.Context, name string, opts ...grpc.CallOption) (GRPCService_TunMultiClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &ServerDesc(name).Streams[0], "/"+name+"/TunMulti", opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &ServerDesc(name).Streams[1], "/"+name+"/TunMulti", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -2,9 +2,11 @@ package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
@@ -75,12 +77,19 @@ func Listen(ctx context.Context, address net.Address, port net.Port, settings *i
|
||||
|
||||
config := tls.ConfigFromStreamSettings(settings)
|
||||
|
||||
var options []grpc.ServerOption
|
||||
var s *grpc.Server
|
||||
if config == nil {
|
||||
s = grpc.NewServer()
|
||||
} else {
|
||||
s = grpc.NewServer(grpc.Creds(credentials.NewTLS(config.GetTLSConfig(tls.WithNextProto("h2")))))
|
||||
if config != nil {
|
||||
options = append(options, grpc.Creds(credentials.NewTLS(config.GetTLSConfig(tls.WithNextProto("h2")))))
|
||||
}
|
||||
if grpcSettings.IdleTimeout > 0 || grpcSettings.HealthCheckTimeout > 0 {
|
||||
options = append(options, grpc.KeepaliveParams(keepalive.ServerParameters{
|
||||
Time: time.Second * time.Duration(grpcSettings.IdleTimeout),
|
||||
Timeout: time.Second * time.Duration(grpcSettings.HealthCheckTimeout),
|
||||
}))
|
||||
}
|
||||
|
||||
s = grpc.NewServer(options...)
|
||||
listener.s = s
|
||||
|
||||
if settings.SocketSettings != nil && settings.SocketSettings.AcceptProxyProtocol {
|
||||
@@ -114,7 +123,7 @@ func Listen(ctx context.Context, address net.Address, port net.Port, settings *i
|
||||
}
|
||||
}
|
||||
|
||||
encoding.RegisterGRPCServiceServerX(s, listener, grpcSettings.ServiceName)
|
||||
encoding.RegisterGRPCServiceServerX(s, listener, grpcSettings.getNormalizedName())
|
||||
|
||||
if err = s.Serve(streamListener); err != nil {
|
||||
newError("Listener for gRPC ended").Base(err).WriteToLog()
|
||||
|
@@ -90,7 +90,7 @@ func (h *HeaderReader) Read(reader io.Reader) (*buf.Buffer, error) {
|
||||
buffer.Clear()
|
||||
copy(buffer.Extend(lenEnding), leftover)
|
||||
|
||||
if _, err := readRequest(bufio.NewReader(bytes.NewReader(headerBuf.Bytes())), false); err != io.ErrUnexpectedEOF {
|
||||
if _, err := readRequest(bufio.NewReader(bytes.NewReader(headerBuf.Bytes()))); err != io.ErrUnexpectedEOF {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ func (h *HeaderReader) Read(reader io.Reader) (*buf.Buffer, error) {
|
||||
}
|
||||
|
||||
// Parse the request
|
||||
if req, err := readRequest(bufio.NewReader(bytes.NewReader(headerBuf.Bytes())), false); err != nil {
|
||||
if req, err := readRequest(bufio.NewReader(bytes.NewReader(headerBuf.Bytes()))); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
h.req = req
|
||||
|
@@ -8,4 +8,4 @@ import (
|
||||
)
|
||||
|
||||
//go:linkname readRequest net/http.readRequest
|
||||
func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *http.Request, err error)
|
||||
func readRequest(b *bufio.Reader) (req *http.Request, err error)
|
||||
|
@@ -1,13 +1,12 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.14.0
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.17.3
|
||||
// source: transport/internet/http/config.proto
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
@@ -21,17 +20,15 @@ const (
|
||||
_ = 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 {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Host []string `protobuf:"bytes,1,rep,name=host,proto3" json:"host,omitempty"`
|
||||
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
|
||||
Host []string `protobuf:"bytes,1,rep,name=host,proto3" json:"host,omitempty"`
|
||||
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
|
||||
IdleTimeout int32 `protobuf:"varint,3,opt,name=idle_timeout,json=idleTimeout,proto3" json:"idle_timeout,omitempty"`
|
||||
HealthCheckTimeout int32 `protobuf:"varint,4,opt,name=health_check_timeout,json=healthCheckTimeout,proto3" json:"health_check_timeout,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
@@ -80,6 +77,20 @@ func (x *Config) GetPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetIdleTimeout() int32 {
|
||||
if x != nil {
|
||||
return x.IdleTimeout
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Config) GetHealthCheckTimeout() int32 {
|
||||
if x != nil {
|
||||
return x.HealthCheckTimeout
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_transport_internet_http_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_transport_internet_http_config_proto_rawDesc = []byte{
|
||||
@@ -87,18 +98,23 @@ var file_transport_internet_http_config_proto_rawDesc = []byte{
|
||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
||||
0x68, 0x74, 0x74, 0x70, 0x22, 0x30, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12,
|
||||
0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f,
|
||||
0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x42, 0x76, 0x0a, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
|
||||
0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69,
|
||||
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72,
|
||||
0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
||||
0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x74, 0x74, 0x70, 0xaa,
|
||||
0x02, 0x1c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
|
||||
0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x62, 0x06,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x68, 0x74, 0x74, 0x70, 0x22, 0x85, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x68,
|
||||
0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x64, 0x6c, 0x65, 0x5f,
|
||||
0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x69,
|
||||
0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x68, 0x65,
|
||||
0x61, 0x6c, 0x74, 0x68, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f,
|
||||
0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68,
|
||||
0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x42, 0x76, 0x0a, 0x20,
|
||||
0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x74, 0x74, 0x70,
|
||||
0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78,
|
||||
0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
||||
0x2f, 0x68, 0x74, 0x74, 0x70, 0xaa, 0x02, 0x1c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
||||
0x48, 0x74, 0x74, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@@ -9,4 +9,6 @@ option java_multiple_files = true;
|
||||
message Config {
|
||||
repeated string host = 1;
|
||||
string path = 2;
|
||||
int32 idle_timeout = 3;
|
||||
int32 health_check_timeout = 4;
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
@@ -20,8 +21,7 @@ import (
|
||||
|
||||
type dialerConf struct {
|
||||
net.Destination
|
||||
*internet.SocketConfig
|
||||
*tls.Config
|
||||
*internet.MemoryStreamConfig
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -29,7 +29,7 @@ var (
|
||||
globalDialerAccess sync.Mutex
|
||||
)
|
||||
|
||||
func getHTTPClient(ctx context.Context, dest net.Destination, tlsSettings *tls.Config, sockopt *internet.SocketConfig) (*http.Client, error) {
|
||||
func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (*http.Client, error) {
|
||||
globalDialerAccess.Lock()
|
||||
defer globalDialerAccess.Unlock()
|
||||
|
||||
@@ -37,7 +37,14 @@ func getHTTPClient(ctx context.Context, dest net.Destination, tlsSettings *tls.C
|
||||
globalDialerMap = make(map[dialerConf]*http.Client)
|
||||
}
|
||||
|
||||
if client, found := globalDialerMap[dialerConf{dest, sockopt, tlsSettings}]; found {
|
||||
httpSettings := streamSettings.ProtocolSettings.(*Config)
|
||||
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
||||
if tlsConfig == nil {
|
||||
return nil, newError("TLS must be enabled for http transport.").AtWarning()
|
||||
}
|
||||
sockopt := streamSettings.SocketSettings
|
||||
|
||||
if client, found := globalDialerMap[dialerConf{dest, streamSettings}]; found {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
@@ -86,25 +93,26 @@ func getHTTPClient(ctx context.Context, dest net.Destination, tlsSettings *tls.C
|
||||
}
|
||||
return cn, nil
|
||||
},
|
||||
TLSClientConfig: tlsSettings.GetTLSConfig(tls.WithDestination(dest)),
|
||||
TLSClientConfig: tlsConfig.GetTLSConfig(tls.WithDestination(dest)),
|
||||
}
|
||||
|
||||
if httpSettings.IdleTimeout > 0 || httpSettings.HealthCheckTimeout > 0 {
|
||||
transport.ReadIdleTimeout = time.Second * time.Duration(httpSettings.IdleTimeout)
|
||||
transport.PingTimeout = time.Second * time.Duration(httpSettings.HealthCheckTimeout)
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
}
|
||||
|
||||
globalDialerMap[dialerConf{dest, sockopt, tlsSettings}] = client
|
||||
globalDialerMap[dialerConf{dest, streamSettings}] = client
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// Dial dials a new TCP connection to the given destination.
|
||||
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) {
|
||||
httpSettings := streamSettings.ProtocolSettings.(*Config)
|
||||
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
|
||||
if tlsConfig == nil {
|
||||
return nil, newError("TLS must be enabled for http transport.").AtWarning()
|
||||
}
|
||||
client, err := getHTTPClient(ctx, dest, tlsConfig, streamSettings.SocketSettings)
|
||||
client, err := getHTTPClient(ctx, dest, streamSettings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -5,29 +5,18 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"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/transport"
|
||||
"github.com/xtls/xray-core/transport/pipe"
|
||||
)
|
||||
|
||||
var (
|
||||
effectiveSystemDialer SystemDialer = &DefaultSystemDialer{}
|
||||
)
|
||||
|
||||
// InitSystemDialer: It's private method and you are NOT supposed to use this function.
|
||||
func InitSystemDialer(dc dns.Client, om outbound.Manager) {
|
||||
effectiveSystemDialer.Init(dc, om)
|
||||
}
|
||||
|
||||
type SystemDialer interface {
|
||||
Dial(ctx context.Context, source net.Address, destination net.Destination, sockopt *SocketConfig) (net.Conn, error)
|
||||
Init(dc dns.Client, om outbound.Manager)
|
||||
}
|
||||
|
||||
type DefaultSystemDialer struct {
|
||||
@@ -58,68 +47,8 @@ func hasBindAddr(sockopt *SocketConfig) bool {
|
||||
return sockopt != nil && len(sockopt.BindAddress) > 0 && sockopt.BindPort > 0
|
||||
}
|
||||
|
||||
func (d *DefaultSystemDialer) lookupIP(domain string, strategy DomainStrategy, localAddr net.Address) ([]net.IP, error) {
|
||||
if d.dns == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var opt dns.Option
|
||||
switch {
|
||||
case strategy == DomainStrategy_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()):
|
||||
opt = dns.LookupIPv4Only
|
||||
case strategy == DomainStrategy_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()):
|
||||
opt = dns.LookupIPv6Only
|
||||
case strategy == DomainStrategy_AS_IS:
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return d.dns.LookupOptions(domain, opt, dns.LookupNoFake)
|
||||
}
|
||||
|
||||
func (d *DefaultSystemDialer) canLookupIP(ctx context.Context, dst net.Destination, sockopt *SocketConfig) bool {
|
||||
if sockopt == nil || dst.Address.Family().IsIP() || d.dns == nil {
|
||||
return false
|
||||
}
|
||||
return sockopt.DomainStrategy != DomainStrategy_AS_IS
|
||||
}
|
||||
|
||||
func (d *DefaultSystemDialer) redirect(ctx context.Context, dst net.Destination, obt string) net.Conn {
|
||||
newError("redirecting request " + dst.String() + " to " + obt).WriteToLog(session.ExportIDToError(ctx))
|
||||
h := d.obm.GetHandler(obt)
|
||||
ctx = session.ContextWithOutbound(ctx, &session.Outbound{dst, nil})
|
||||
if h != nil {
|
||||
ur, uw := pipe.New(pipe.OptionsFromContext(ctx)...)
|
||||
dr, dw := pipe.New(pipe.OptionsFromContext(ctx)...)
|
||||
|
||||
go h.Dispatch(ctx, &transport.Link{ur, dw})
|
||||
nc := cnc.NewConnection(
|
||||
cnc.ConnectionInputMulti(uw),
|
||||
cnc.ConnectionOutputMulti(dr),
|
||||
cnc.ConnectionOnClose(common.ChainedClosable{uw, dw}),
|
||||
)
|
||||
return nc
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) {
|
||||
newError("dialing to " + dest.String()).AtDebug().WriteToLog()
|
||||
if d.obm != nil && sockopt != nil && len(sockopt.DialerProxy) > 0 {
|
||||
nc := d.redirect(ctx, dest, sockopt.DialerProxy)
|
||||
if nc != nil {
|
||||
return nc, nil
|
||||
}
|
||||
}
|
||||
|
||||
if d.canLookupIP(ctx, dest, sockopt) {
|
||||
ips, err := d.lookupIP(dest.Address.String(), sockopt.DomainStrategy, src)
|
||||
if err == nil && len(ips) > 0 {
|
||||
dest.Address = net.IPAddress(ips[dice.Roll(len(ips))])
|
||||
newError("replace destination with " + dest.String()).AtInfo().WriteToLog()
|
||||
} else if err != nil {
|
||||
newError("failed to resolve ip").Base(err).AtWarning().WriteToLog()
|
||||
}
|
||||
}
|
||||
|
||||
if dest.Network == net.Network_UDP && !hasBindAddr(sockopt) {
|
||||
srcAddr := resolveSrcAddr(net.Network_UDP, src)
|
||||
@@ -175,11 +104,6 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne
|
||||
return dialer.DialContext(ctx, dest.Network.SystemString(), dest.NetAddr())
|
||||
}
|
||||
|
||||
func (d *DefaultSystemDialer) Init(dc dns.Client, om outbound.Manager) {
|
||||
d.dns = dc
|
||||
d.obm = om
|
||||
}
|
||||
|
||||
type PacketConnWrapper struct {
|
||||
conn net.PacketConn
|
||||
dest net.Addr
|
||||
@@ -240,8 +164,6 @@ func WithAdapter(dialer SystemDialerAdapter) SystemDialer {
|
||||
}
|
||||
}
|
||||
|
||||
func (v *SimpleSystemDialer) Init(_ dns.Client, _ outbound.Manager) {}
|
||||
|
||||
func (v *SimpleSystemDialer) Dial(ctx context.Context, src net.Address, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) {
|
||||
return v.adapter.Dial(dest.Network.SystemString(), dest.NetAddr())
|
||||
}
|
||||
|
@@ -121,7 +121,7 @@ func isCertificateExpired(c *tls.Certificate) bool {
|
||||
}
|
||||
|
||||
// If leaf is not there, the certificate is probably not used yet. We trust user to provide a valid certificate.
|
||||
return c.Leaf != nil && c.Leaf.NotAfter.Before(time.Now().Add(-time.Minute))
|
||||
return c.Leaf != nil && c.Leaf.NotAfter.Before(time.Now().Add(time.Minute*2))
|
||||
}
|
||||
|
||||
func issueCertificate(rawCA *Certificate, domain string) (*tls.Certificate, error) {
|
||||
@@ -173,6 +173,9 @@ func getGetCertificateFunc(c *tls.Config, ca []*Certificate) func(hello *tls.Cli
|
||||
for _, certificate := range c.Certificates {
|
||||
if !isCertificateExpired(&certificate) {
|
||||
newCerts = append(newCerts, certificate)
|
||||
} else if certificate.Leaf != nil {
|
||||
expTime := certificate.Leaf.NotAfter.Format(time.RFC3339)
|
||||
newError("old certificate for ", domain, " (expire on ", expTime, ") discarded").AtInfo().WriteToLog()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +193,14 @@ func getGetCertificateFunc(c *tls.Config, ca []*Certificate) func(hello *tls.Cli
|
||||
newError("failed to issue new certificate for ", domain).Base(err).WriteToLog()
|
||||
continue
|
||||
}
|
||||
parsed, err := x509.ParseCertificate(newCert.Certificate[0])
|
||||
if err == nil {
|
||||
newCert.Leaf = parsed
|
||||
expTime := parsed.NotAfter.Format(time.RFC3339)
|
||||
newError("new certificate for ", domain, " (expire on ", expTime, ") issued").AtInfo().WriteToLog()
|
||||
} else {
|
||||
newError("failed to parse new certificate for ", domain).Base(err).WriteToLog()
|
||||
}
|
||||
|
||||
access.Lock()
|
||||
c.Certificates = append(c.Certificates, *newCert)
|
||||
@@ -211,13 +222,13 @@ func getGetCertificateFunc(c *tls.Config, ca []*Certificate) func(hello *tls.Cli
|
||||
}
|
||||
}
|
||||
|
||||
func getNewGetCertficateFunc(certs []*tls.Certificate) func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
func getNewGetCertificateFunc(certs []*tls.Certificate, rejectUnknownSNI bool) func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
return func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
if len(certs) == 0 {
|
||||
return nil, newError("empty certs")
|
||||
return nil, errNoCertificates
|
||||
}
|
||||
sni := strings.ToLower(hello.ServerName)
|
||||
if len(certs) == 1 || sni == "" {
|
||||
if !rejectUnknownSNI && (len(certs) == 1 || sni == "") {
|
||||
return certs[0], nil
|
||||
}
|
||||
gsni := "*"
|
||||
@@ -234,6 +245,9 @@ func getNewGetCertficateFunc(certs []*tls.Certificate) func(hello *tls.ClientHel
|
||||
}
|
||||
}
|
||||
}
|
||||
if rejectUnknownSNI {
|
||||
return nil, errNoCertificates
|
||||
}
|
||||
return certs[0], nil
|
||||
}
|
||||
}
|
||||
@@ -275,7 +289,7 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
||||
if len(caCerts) > 0 {
|
||||
config.GetCertificate = getGetCertificateFunc(config, caCerts)
|
||||
} else {
|
||||
config.GetCertificate = getNewGetCertficateFunc(c.BuildCertificates())
|
||||
config.GetCertificate = getNewGetCertificateFunc(c.BuildCertificates(), c.RejectUnknownSni)
|
||||
}
|
||||
|
||||
if sn := c.parseServerName(); len(sn) > 0 {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.15.6
|
||||
// protoc v3.15.8
|
||||
// source: transport/internet/tls/config.proto
|
||||
|
||||
package tls
|
||||
@@ -201,7 +201,8 @@ type Config struct {
|
||||
// Whether the server selects its most preferred ciphersuite.
|
||||
PreferServerCipherSuites bool `protobuf:"varint,10,opt,name=prefer_server_cipher_suites,json=preferServerCipherSuites,proto3" json:"prefer_server_cipher_suites,omitempty"`
|
||||
// TLS Client Hello fingerprint (uTLS).
|
||||
Fingerprint string `protobuf:"bytes,11,opt,name=fingerprint,proto3" json:"fingerprint,omitempty"`
|
||||
Fingerprint string `protobuf:"bytes,11,opt,name=fingerprint,proto3" json:"fingerprint,omitempty"`
|
||||
RejectUnknownSni bool `protobuf:"varint,12,opt,name=reject_unknown_sni,json=rejectUnknownSni,proto3" json:"reject_unknown_sni,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
@@ -313,6 +314,13 @@ func (x *Config) GetFingerprint() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetRejectUnknownSni() bool {
|
||||
if x != nil {
|
||||
return x.RejectUnknownSni
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
||||
@@ -342,7 +350,7 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
||||
0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10,
|
||||
0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46, 0x59,
|
||||
0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f,
|
||||
0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xf5, 0x03, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66,
|
||||
0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xa3, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73, 0x65,
|
||||
0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f,
|
||||
0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65, 0x72,
|
||||
@@ -373,15 +381,18 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
||||
0x01, 0x28, 0x08, 0x52, 0x18, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x65,
|
||||
0x72, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x12, 0x20, 0x0a,
|
||||
0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x42,
|
||||
0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74,
|
||||
0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 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,
|
||||
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
||||
0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x12,
|
||||
0x2c, 0x0a, 0x12, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77,
|
||||
0x6e, 0x5f, 0x73, 0x6e, 0x69, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x65, 0x6a,
|
||||
0x65, 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53, 0x6e, 0x69, 0x42, 0x73, 0x0a,
|
||||
0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
|
||||
0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73,
|
||||
0x50, 0x01, 0x5a, 0x30, 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, 0x74, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
||||
0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54,
|
||||
0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@@ -67,4 +67,6 @@ message Config {
|
||||
|
||||
// TLS Client Hello fingerprint (uTLS).
|
||||
string fingerprint = 11;
|
||||
|
||||
bool reject_unknown_sni = 12;
|
||||
}
|
||||
|
6
transport/internet/tls/unsafe.go
Normal file
6
transport/internet/tls/unsafe.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package tls
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
//go:linkname errNoCertificates crypto/tls.errNoCertificates
|
||||
var errNoCertificates error
|
@@ -212,13 +212,13 @@ func getGetCertificateFunc(c *xtls.Config, ca []*Certificate) func(hello *xtls.C
|
||||
}
|
||||
}
|
||||
|
||||
func getNewGetCertficateFunc(certs []*xtls.Certificate) func(hello *xtls.ClientHelloInfo) (*xtls.Certificate, error) {
|
||||
func getNewGetCertificateFunc(certs []*xtls.Certificate, rejectUnknownSNI bool) func(hello *xtls.ClientHelloInfo) (*xtls.Certificate, error) {
|
||||
return func(hello *xtls.ClientHelloInfo) (*xtls.Certificate, error) {
|
||||
if len(certs) == 0 {
|
||||
return nil, newError("empty certs")
|
||||
return nil, errNoCertificates
|
||||
}
|
||||
sni := strings.ToLower(hello.ServerName)
|
||||
if len(certs) == 1 || sni == "" {
|
||||
if !rejectUnknownSNI && (len(certs) == 1 || sni == "") {
|
||||
return certs[0], nil
|
||||
}
|
||||
gsni := "*"
|
||||
@@ -235,6 +235,9 @@ func getNewGetCertficateFunc(certs []*xtls.Certificate) func(hello *xtls.ClientH
|
||||
}
|
||||
}
|
||||
}
|
||||
if rejectUnknownSNI {
|
||||
return nil, errNoCertificates
|
||||
}
|
||||
return certs[0], nil
|
||||
}
|
||||
}
|
||||
@@ -276,7 +279,7 @@ func (c *Config) GetXTLSConfig(opts ...Option) *xtls.Config {
|
||||
if len(caCerts) > 0 {
|
||||
config.GetCertificate = getGetCertificateFunc(config, caCerts)
|
||||
} else {
|
||||
config.GetCertificate = getNewGetCertficateFunc(c.BuildCertificates())
|
||||
config.GetCertificate = getNewGetCertificateFunc(c.BuildCertificates(), c.RejectUnknownSni)
|
||||
}
|
||||
|
||||
if sn := c.parseServerName(); len(sn) > 0 {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.14.0
|
||||
// protoc v3.15.8
|
||||
// source: transport/internet/xtls/config.proto
|
||||
|
||||
package xtls
|
||||
@@ -200,6 +200,7 @@ type Config struct {
|
||||
CipherSuites string `protobuf:"bytes,9,opt,name=cipher_suites,json=cipherSuites,proto3" json:"cipher_suites,omitempty"`
|
||||
// Whether the server selects its most preferred ciphersuite.
|
||||
PreferServerCipherSuites bool `protobuf:"varint,10,opt,name=prefer_server_cipher_suites,json=preferServerCipherSuites,proto3" json:"prefer_server_cipher_suites,omitempty"`
|
||||
RejectUnknownSni bool `protobuf:"varint,12,opt,name=reject_unknown_sni,json=rejectUnknownSni,proto3" json:"reject_unknown_sni,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
@@ -304,6 +305,13 @@ func (x *Config) GetPreferServerCipherSuites() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *Config) GetRejectUnknownSni() bool {
|
||||
if x != nil {
|
||||
return x.RejectUnknownSni
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var File_transport_internet_xtls_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_transport_internet_xtls_config_proto_rawDesc = []byte{
|
||||
@@ -333,7 +341,7 @@ var file_transport_internet_xtls_config_proto_rawDesc = []byte{
|
||||
0x0c, 0x45, 0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12,
|
||||
0x14, 0x0a, 0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52,
|
||||
0x49, 0x46, 0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49,
|
||||
0x54, 0x59, 0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xd4, 0x03, 0x0a, 0x06, 0x43,
|
||||
0x54, 0x59, 0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0x82, 0x04, 0x0a, 0x06, 0x43,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69,
|
||||
0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61,
|
||||
0x6c, 0x6c, 0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4b, 0x0a, 0x0b,
|
||||
@@ -363,15 +371,17 @@ var file_transport_internet_xtls_config_proto_rawDesc = []byte{
|
||||
0x76, 0x65, 0x72, 0x5f, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65,
|
||||
0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x53,
|
||||
0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65,
|
||||
0x73, 0x42, 0x76, 0x0a, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
||||
0x2e, 0x78, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1c, 0x58, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x58, 0x74, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
0x73, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x6e, 0x6b, 0x6e,
|
||||
0x6f, 0x77, 0x6e, 0x5f, 0x73, 0x6e, 0x69, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72,
|
||||
0x65, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53, 0x6e, 0x69, 0x42,
|
||||
0x76, 0x0a, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x78,
|
||||
0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
||||
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
|
||||
0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
|
||||
0x6e, 0x65, 0x74, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1c, 0x58, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x65, 0x74, 0x2e, 0x58, 0x74, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@@ -64,4 +64,7 @@ message Config {
|
||||
|
||||
// Whether the server selects its most preferred ciphersuite.
|
||||
bool prefer_server_cipher_suites = 10;
|
||||
|
||||
|
||||
bool reject_unknown_sni = 12;
|
||||
}
|
||||
|
6
transport/internet/xtls/unsafe.go
Normal file
6
transport/internet/xtls/unsafe.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package xtls
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
//go:linkname errNoCertificates github.com/xtls/go.errNoCertificates
|
||||
var errNoCertificates error
|
Reference in New Issue
Block a user