mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-22 17:46:48 +08:00
Compare commits
3 Commits
dns
...
features-l
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e14a4dc10b | ||
![]() |
ab3b0f843d | ||
![]() |
98050aaf02 |
@@ -179,7 +179,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
|
||||
func shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool {
|
||||
domain := result.Domain()
|
||||
for _, d := range request.ExcludeForDomain {
|
||||
if strings.ToLower(domain) == d {
|
||||
if domain == d {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,13 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.15.8
|
||||
// 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 (
|
||||
|
@@ -80,6 +80,12 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher) (*DoHNameServ
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Record(&log.AccessMessage{
|
||||
From: "DoH",
|
||||
To: s.dohURL,
|
||||
Status: log.AccessAccepted,
|
||||
Detour: "local",
|
||||
})
|
||||
|
||||
cc := common.ChainedClosable{}
|
||||
if cw, ok := link.Writer.(common.Closable); ok {
|
||||
@@ -371,9 +377,9 @@ func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net
|
||||
} else {
|
||||
ips, err := s.findIPsForDomain(fqdn, option)
|
||||
if err != errRecordNotFound {
|
||||
if cs == CacheStrategy_Cache_ALL || (cs == CacheStrategy_Cache_NOERROR && err == nil) {
|
||||
if 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})
|
||||
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err})
|
||||
return ips, err
|
||||
}
|
||||
}
|
||||
|
@@ -276,9 +276,9 @@ func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP ne
|
||||
} else {
|
||||
ips, err := s.findIPsForDomain(fqdn, option)
|
||||
if err != errRecordNotFound {
|
||||
if cs == CacheStrategy_Cache_ALL || (cs == CacheStrategy_Cache_NOERROR && err == nil) {
|
||||
if 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})
|
||||
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err})
|
||||
return ips, err
|
||||
}
|
||||
}
|
||||
|
@@ -253,9 +253,9 @@ func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP
|
||||
} else {
|
||||
ips, err := s.findIPsForDomain(fqdn, option)
|
||||
if err != errRecordNotFound {
|
||||
if cs == CacheStrategy_Cache_ALL || (cs == CacheStrategy_Cache_NOERROR && err == nil) {
|
||||
if 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})
|
||||
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err})
|
||||
return ips, err
|
||||
}
|
||||
}
|
||||
|
@@ -66,24 +66,6 @@ type DomainMatcher struct {
|
||||
matchers strmatcher.IndexMatcher
|
||||
}
|
||||
|
||||
func NewMphMatcherGroup(domains []*Domain) (*DomainMatcher, error) {
|
||||
g := strmatcher.NewMphMatcherGroup()
|
||||
for _, d := range domains {
|
||||
matcherType, f := matcherTypeMap[d.Type]
|
||||
if !f {
|
||||
return nil, newError("unsupported domain type", d.Type)
|
||||
}
|
||||
_, err := g.AddPattern(d.Value, matcherType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
g.Build()
|
||||
return &DomainMatcher{
|
||||
matchers: g,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewDomainMatcher(domains []*Domain) (*DomainMatcher, error) {
|
||||
g := new(strmatcher.MatcherGroup)
|
||||
for _, d := range domains {
|
||||
@@ -100,7 +82,7 @@ func NewDomainMatcher(domains []*Domain) (*DomainMatcher, error) {
|
||||
}
|
||||
|
||||
func (m *DomainMatcher) ApplyDomain(domain string) bool {
|
||||
return len(m.matchers.Match(strings.ToLower(domain))) > 0
|
||||
return len(m.matchers.Match(domain)) > 0
|
||||
}
|
||||
|
||||
// Apply implements Condition.
|
||||
@@ -109,7 +91,7 @@ func (m *DomainMatcher) Apply(ctx routing.Context) bool {
|
||||
if len(domain) == 0 {
|
||||
return false
|
||||
}
|
||||
return m.ApplyDomain(domain)
|
||||
return m.ApplyDomain(strings.ToLower(domain))
|
||||
}
|
||||
|
||||
type MultiGeoIPMatcher struct {
|
||||
|
@@ -359,9 +359,6 @@ func TestChinaSites(t *testing.T) {
|
||||
matcher, err := NewDomainMatcher(domains)
|
||||
common.Must(err)
|
||||
|
||||
acMatcher, err := NewMphMatcherGroup(domains)
|
||||
common.Must(err)
|
||||
|
||||
type TestCase struct {
|
||||
Domain string
|
||||
Output bool
|
||||
@@ -390,96 +387,9 @@ func TestChinaSites(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
r1 := matcher.ApplyDomain(testCase.Domain)
|
||||
r2 := acMatcher.ApplyDomain(testCase.Domain)
|
||||
if r1 != testCase.Output {
|
||||
t.Error("DomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r1)
|
||||
} else if r2 != testCase.Output {
|
||||
t.Error("ACDomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMphDomainMatcher(b *testing.B) {
|
||||
domains, err := loadGeoSite("CN")
|
||||
common.Must(err)
|
||||
|
||||
matcher, err := NewMphMatcherGroup(domains)
|
||||
common.Must(err)
|
||||
|
||||
type TestCase struct {
|
||||
Domain string
|
||||
Output bool
|
||||
}
|
||||
testCases := []TestCase{
|
||||
{
|
||||
Domain: "163.com",
|
||||
Output: true,
|
||||
},
|
||||
{
|
||||
Domain: "163.com",
|
||||
Output: true,
|
||||
},
|
||||
{
|
||||
Domain: "164.com",
|
||||
Output: false,
|
||||
},
|
||||
{
|
||||
Domain: "164.com",
|
||||
Output: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i := 0; i < 1024; i++ {
|
||||
testCases = append(testCases, TestCase{Domain: strconv.Itoa(i) + ".not-exists.com", Output: false})
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, testCase := range testCases {
|
||||
_ = matcher.ApplyDomain(testCase.Domain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDomainMatcher(b *testing.B) {
|
||||
domains, err := loadGeoSite("CN")
|
||||
common.Must(err)
|
||||
|
||||
matcher, err := NewDomainMatcher(domains)
|
||||
common.Must(err)
|
||||
|
||||
type TestCase struct {
|
||||
Domain string
|
||||
Output bool
|
||||
}
|
||||
testCases := []TestCase{
|
||||
{
|
||||
Domain: "163.com",
|
||||
Output: true,
|
||||
},
|
||||
{
|
||||
Domain: "163.com",
|
||||
Output: true,
|
||||
},
|
||||
{
|
||||
Domain: "164.com",
|
||||
Output: false,
|
||||
},
|
||||
{
|
||||
Domain: "164.com",
|
||||
Output: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i := 0; i < 1024; i++ {
|
||||
testCases = append(testCases, TestCase{Domain: strconv.Itoa(i) + ".not-exists.com", Output: false})
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, testCase := range testCases {
|
||||
_ = matcher.ApplyDomain(testCase.Domain)
|
||||
r := matcher.ApplyDomain(testCase.Domain)
|
||||
if r != testCase.Output {
|
||||
t.Error("expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -67,24 +67,11 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
|
||||
conds := NewConditionChan()
|
||||
|
||||
if len(rr.Domain) > 0 {
|
||||
switch rr.DomainMatcher {
|
||||
case "linear":
|
||||
matcher, err := NewDomainMatcher(rr.Domain)
|
||||
if err != nil {
|
||||
return nil, newError("failed to build domain condition").Base(err)
|
||||
}
|
||||
conds.Add(matcher)
|
||||
case "mph", "hybrid":
|
||||
fallthrough
|
||||
default:
|
||||
matcher, err := NewMphMatcherGroup(rr.Domain)
|
||||
if err != nil {
|
||||
return nil, newError("failed to build domain condition with MphDomainMatcher").Base(err)
|
||||
}
|
||||
newError("MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)").AtDebug().WriteToLog()
|
||||
conds.Add(matcher)
|
||||
matcher, err := NewDomainMatcher(rr.Domain)
|
||||
if err != nil {
|
||||
return nil, newError("failed to build domain condition").Base(err)
|
||||
}
|
||||
|
||||
conds.Add(matcher)
|
||||
}
|
||||
|
||||
if len(rr.UserEmail) > 0 {
|
||||
|
@@ -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/router/config.proto
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
net "github.com/xtls/xray-core/common/net"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
@@ -21,6 +22,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 of domain value.
|
||||
type Domain_Type int32
|
||||
|
||||
@@ -510,7 +515,6 @@ type RoutingRule struct {
|
||||
InboundTag []string `protobuf:"bytes,8,rep,name=inbound_tag,json=inboundTag,proto3" json:"inbound_tag,omitempty"`
|
||||
Protocol []string `protobuf:"bytes,9,rep,name=protocol,proto3" json:"protocol,omitempty"`
|
||||
Attributes string `protobuf:"bytes,15,opt,name=attributes,proto3" json:"attributes,omitempty"`
|
||||
DomainMatcher string `protobuf:"bytes,17,opt,name=domain_matcher,json=domainMatcher,proto3" json:"domain_matcher,omitempty"`
|
||||
}
|
||||
|
||||
func (x *RoutingRule) Reset() {
|
||||
@@ -668,13 +672,6 @@ func (x *RoutingRule) GetAttributes() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RoutingRule) GetDomainMatcher() string {
|
||||
if x != nil {
|
||||
return x.DomainMatcher
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type isRoutingRule_TargetTag interface {
|
||||
isRoutingRule_TargetTag()
|
||||
}
|
||||
@@ -949,7 +946,7 @@ var file_app_router_config_proto_rawDesc = []byte{
|
||||
0x74, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
|
||||
0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72,
|
||||
0x79, 0x22, 0xb5, 0x06, 0x0a, 0x0b, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c,
|
||||
0x79, 0x22, 0x8e, 0x06, 0x0a, 0x0b, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c,
|
||||
0x65, 0x12, 0x12, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00,
|
||||
0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x25, 0x0a, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69,
|
||||
0x6e, 0x67, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c,
|
||||
@@ -997,38 +994,36 @@ var file_app_router_config_proto_rawDesc = []byte{
|
||||
0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
|
||||
0x65, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62,
|
||||
0x75, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x6d,
|
||||
0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x6f,
|
||||
0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x42, 0x0c, 0x0a, 0x0a, 0x74,
|
||||
0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x22, 0x4e, 0x0a, 0x0d, 0x42, 0x61, 0x6c,
|
||||
0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61,
|
||||
0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x2b, 0x0a, 0x11,
|
||||
0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f,
|
||||
0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e,
|
||||
0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x9b, 0x02, 0x0a, 0x06, 0x43, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73,
|
||||
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e,
|
||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72,
|
||||
0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72,
|
||||
0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x30, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72,
|
||||
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c,
|
||||
0x65, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x45, 0x0a, 0x0e, 0x62, 0x61, 0x6c, 0x61, 0x6e,
|
||||
0x63, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,
|
||||
0x72, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52,
|
||||
0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x22, 0x47,
|
||||
0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
||||
0x12, 0x08, 0x0a, 0x04, 0x41, 0x73, 0x49, 0x73, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x73,
|
||||
0x65, 0x49, 0x70, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x70, 0x49, 0x66, 0x4e, 0x6f, 0x6e,
|
||||
0x4d, 0x61, 0x74, 0x63, 0x68, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x70, 0x4f, 0x6e, 0x44,
|
||||
0x65, 0x6d, 0x61, 0x6e, 0x64, 0x10, 0x03, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x50, 0x01,
|
||||
0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c,
|
||||
0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f,
|
||||
0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0xaa, 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70,
|
||||
0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x75, 0x74, 0x65, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74,
|
||||
0x61, 0x67, 0x22, 0x4e, 0x0a, 0x0d, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52,
|
||||
0x75, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e,
|
||||
0x64, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
|
||||
0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
|
||||
0x6f, 0x72, 0x22, 0x9b, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a,
|
||||
0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
||||
0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
|
||||
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e,
|
||||
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x30,
|
||||
0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x52,
|
||||
0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65,
|
||||
0x12, 0x45, 0x0a, 0x0e, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75,
|
||||
0x6c, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e,
|
||||
0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63,
|
||||
0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x22, 0x47, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
||||
0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x73, 0x49,
|
||||
0x73, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x73, 0x65, 0x49, 0x70, 0x10, 0x01, 0x12, 0x10,
|
||||
0x0a, 0x0c, 0x49, 0x70, 0x49, 0x66, 0x4e, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x10, 0x02,
|
||||
0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x70, 0x4f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x10, 0x03,
|
||||
0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
||||
0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d,
|
||||
0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0xaa,
|
||||
0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65,
|
||||
0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@@ -119,8 +119,6 @@ message RoutingRule {
|
||||
repeated string protocol = 9;
|
||||
|
||||
string attributes = 15;
|
||||
|
||||
string domain_matcher = 17;
|
||||
}
|
||||
|
||||
message BalancingRule {
|
||||
|
@@ -102,7 +102,7 @@ func ReadClientHello(data []byte, h *SniffHeader) error {
|
||||
return errNotClientHello
|
||||
}
|
||||
if nameType == 0 {
|
||||
serverName := string(d[:nameLen])
|
||||
serverName := strings.ToLower(string(d[:nameLen]))
|
||||
// An SNI value may not include a
|
||||
// trailing dot. See
|
||||
// https://tools.ietf.org/html/rfc6066#section-3.
|
||||
|
@@ -1,243 +0,0 @@
|
||||
package strmatcher
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
)
|
||||
|
||||
const validCharCount = 53
|
||||
|
||||
type MatchType struct {
|
||||
matchType Type
|
||||
exist bool
|
||||
}
|
||||
|
||||
const (
|
||||
TrieEdge bool = true
|
||||
FailEdge bool = false
|
||||
)
|
||||
|
||||
type Edge struct {
|
||||
edgeType bool
|
||||
nextNode int
|
||||
}
|
||||
|
||||
type ACAutomaton struct {
|
||||
trie [][validCharCount]Edge
|
||||
fail []int
|
||||
exists []MatchType
|
||||
count int
|
||||
}
|
||||
|
||||
func newNode() [validCharCount]Edge {
|
||||
var s [validCharCount]Edge
|
||||
for i := range s {
|
||||
s[i] = Edge{
|
||||
edgeType: FailEdge,
|
||||
nextNode: 0,
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
var char2Index = []int{
|
||||
'A': 0,
|
||||
'a': 0,
|
||||
'B': 1,
|
||||
'b': 1,
|
||||
'C': 2,
|
||||
'c': 2,
|
||||
'D': 3,
|
||||
'd': 3,
|
||||
'E': 4,
|
||||
'e': 4,
|
||||
'F': 5,
|
||||
'f': 5,
|
||||
'G': 6,
|
||||
'g': 6,
|
||||
'H': 7,
|
||||
'h': 7,
|
||||
'I': 8,
|
||||
'i': 8,
|
||||
'J': 9,
|
||||
'j': 9,
|
||||
'K': 10,
|
||||
'k': 10,
|
||||
'L': 11,
|
||||
'l': 11,
|
||||
'M': 12,
|
||||
'm': 12,
|
||||
'N': 13,
|
||||
'n': 13,
|
||||
'O': 14,
|
||||
'o': 14,
|
||||
'P': 15,
|
||||
'p': 15,
|
||||
'Q': 16,
|
||||
'q': 16,
|
||||
'R': 17,
|
||||
'r': 17,
|
||||
'S': 18,
|
||||
's': 18,
|
||||
'T': 19,
|
||||
't': 19,
|
||||
'U': 20,
|
||||
'u': 20,
|
||||
'V': 21,
|
||||
'v': 21,
|
||||
'W': 22,
|
||||
'w': 22,
|
||||
'X': 23,
|
||||
'x': 23,
|
||||
'Y': 24,
|
||||
'y': 24,
|
||||
'Z': 25,
|
||||
'z': 25,
|
||||
'!': 26,
|
||||
'$': 27,
|
||||
'&': 28,
|
||||
'\'': 29,
|
||||
'(': 30,
|
||||
')': 31,
|
||||
'*': 32,
|
||||
'+': 33,
|
||||
',': 34,
|
||||
';': 35,
|
||||
'=': 36,
|
||||
':': 37,
|
||||
'%': 38,
|
||||
'-': 39,
|
||||
'.': 40,
|
||||
'_': 41,
|
||||
'~': 42,
|
||||
'0': 43,
|
||||
'1': 44,
|
||||
'2': 45,
|
||||
'3': 46,
|
||||
'4': 47,
|
||||
'5': 48,
|
||||
'6': 49,
|
||||
'7': 50,
|
||||
'8': 51,
|
||||
'9': 52,
|
||||
}
|
||||
|
||||
func NewACAutomaton() *ACAutomaton {
|
||||
var ac = new(ACAutomaton)
|
||||
ac.trie = append(ac.trie, newNode())
|
||||
ac.fail = append(ac.fail, 0)
|
||||
ac.exists = append(ac.exists, MatchType{
|
||||
matchType: Full,
|
||||
exist: false,
|
||||
})
|
||||
return ac
|
||||
}
|
||||
|
||||
func (ac *ACAutomaton) Add(domain string, t Type) {
|
||||
var node = 0
|
||||
for i := len(domain) - 1; i >= 0; i-- {
|
||||
var idx = char2Index[domain[i]]
|
||||
if ac.trie[node][idx].nextNode == 0 {
|
||||
ac.count++
|
||||
if len(ac.trie) < ac.count+1 {
|
||||
ac.trie = append(ac.trie, newNode())
|
||||
ac.fail = append(ac.fail, 0)
|
||||
ac.exists = append(ac.exists, MatchType{
|
||||
matchType: Full,
|
||||
exist: false,
|
||||
})
|
||||
}
|
||||
ac.trie[node][idx] = Edge{
|
||||
edgeType: TrieEdge,
|
||||
nextNode: ac.count,
|
||||
}
|
||||
}
|
||||
node = ac.trie[node][idx].nextNode
|
||||
}
|
||||
ac.exists[node] = MatchType{
|
||||
matchType: t,
|
||||
exist: true,
|
||||
}
|
||||
switch t {
|
||||
case Domain:
|
||||
ac.exists[node] = MatchType{
|
||||
matchType: Full,
|
||||
exist: true,
|
||||
}
|
||||
var idx = char2Index['.']
|
||||
if ac.trie[node][idx].nextNode == 0 {
|
||||
ac.count++
|
||||
if len(ac.trie) < ac.count+1 {
|
||||
ac.trie = append(ac.trie, newNode())
|
||||
ac.fail = append(ac.fail, 0)
|
||||
ac.exists = append(ac.exists, MatchType{
|
||||
matchType: Full,
|
||||
exist: false,
|
||||
})
|
||||
}
|
||||
ac.trie[node][idx] = Edge{
|
||||
edgeType: TrieEdge,
|
||||
nextNode: ac.count,
|
||||
}
|
||||
}
|
||||
node = ac.trie[node][idx].nextNode
|
||||
ac.exists[node] = MatchType{
|
||||
matchType: t,
|
||||
exist: true,
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func (ac *ACAutomaton) Build() {
|
||||
var queue = list.New()
|
||||
for i := 0; i < validCharCount; i++ {
|
||||
if ac.trie[0][i].nextNode != 0 {
|
||||
queue.PushBack(ac.trie[0][i])
|
||||
}
|
||||
}
|
||||
for {
|
||||
var front = queue.Front()
|
||||
if front == nil {
|
||||
break
|
||||
} else {
|
||||
var node = front.Value.(Edge).nextNode
|
||||
queue.Remove(front)
|
||||
for i := 0; i < validCharCount; i++ {
|
||||
if ac.trie[node][i].nextNode != 0 {
|
||||
ac.fail[ac.trie[node][i].nextNode] = ac.trie[ac.fail[node]][i].nextNode
|
||||
queue.PushBack(ac.trie[node][i])
|
||||
} else {
|
||||
ac.trie[node][i] = Edge{
|
||||
edgeType: FailEdge,
|
||||
nextNode: ac.trie[ac.fail[node]][i].nextNode,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ac *ACAutomaton) Match(s string) bool {
|
||||
var node = 0
|
||||
var fullMatch = true
|
||||
// 1. the match string is all through trie edge. FULL MATCH or DOMAIN
|
||||
// 2. the match string is through a fail edge. NOT FULL MATCH
|
||||
// 2.1 Through a fail edge, but there exists a valid node. SUBSTR
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
var idx = char2Index[s[i]]
|
||||
fullMatch = fullMatch && ac.trie[node][idx].edgeType
|
||||
node = ac.trie[node][idx].nextNode
|
||||
switch ac.exists[node].matchType {
|
||||
case Substr:
|
||||
return true
|
||||
case Domain:
|
||||
if fullMatch {
|
||||
return true
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
return fullMatch && ac.exists[node].exist
|
||||
}
|
@@ -8,19 +8,6 @@ import (
|
||||
. "github.com/xtls/xray-core/common/strmatcher"
|
||||
)
|
||||
|
||||
func BenchmarkACAutomaton(b *testing.B) {
|
||||
ac := NewACAutomaton()
|
||||
for i := 1; i <= 1024; i++ {
|
||||
ac.Add(strconv.Itoa(i)+".v2ray.com", Domain)
|
||||
}
|
||||
ac.Build()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = ac.Match("0.v2ray.com")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDomainMatcherGroup(b *testing.B) {
|
||||
g := new(DomainMatcherGroup)
|
||||
|
||||
|
@@ -1,301 +0,0 @@
|
||||
package strmatcher
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// PrimeRK is the prime base used in Rabin-Karp algorithm.
|
||||
const PrimeRK = 16777619
|
||||
|
||||
// calculate the rolling murmurHash of given string
|
||||
func RollingHash(s string) uint32 {
|
||||
h := uint32(0)
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
h = h*PrimeRK + uint32(s[i])
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// A MphMatcherGroup is divided into three parts:
|
||||
// 1. `full` and `domain` patterns are matched by Rabin-Karp algorithm and minimal perfect hash table;
|
||||
// 2. `substr` patterns are matched by ac automaton;
|
||||
// 3. `regex` patterns are matched with the regex library.
|
||||
type MphMatcherGroup struct {
|
||||
ac *ACAutomaton
|
||||
otherMatchers []matcherEntry
|
||||
rules []string
|
||||
level0 []uint32
|
||||
level0Mask int
|
||||
level1 []uint32
|
||||
level1Mask int
|
||||
count uint32
|
||||
ruleMap *map[string]uint32
|
||||
}
|
||||
|
||||
func (g *MphMatcherGroup) AddFullOrDomainPattern(pattern string, t Type) {
|
||||
h := RollingHash(pattern)
|
||||
switch t {
|
||||
case Domain:
|
||||
(*g.ruleMap)["."+pattern] = h*PrimeRK + uint32('.')
|
||||
fallthrough
|
||||
case Full:
|
||||
(*g.ruleMap)[pattern] = h
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func NewMphMatcherGroup() *MphMatcherGroup {
|
||||
return &MphMatcherGroup{
|
||||
ac: nil,
|
||||
otherMatchers: nil,
|
||||
rules: nil,
|
||||
level0: nil,
|
||||
level0Mask: 0,
|
||||
level1: nil,
|
||||
level1Mask: 0,
|
||||
count: 1,
|
||||
ruleMap: &map[string]uint32{},
|
||||
}
|
||||
}
|
||||
|
||||
// AddPattern adds a pattern to MphMatcherGroup
|
||||
func (g *MphMatcherGroup) AddPattern(pattern string, t Type) (uint32, error) {
|
||||
switch t {
|
||||
case Substr:
|
||||
if g.ac == nil {
|
||||
g.ac = NewACAutomaton()
|
||||
}
|
||||
g.ac.Add(pattern, t)
|
||||
case Full, Domain:
|
||||
pattern = strings.ToLower(pattern)
|
||||
g.AddFullOrDomainPattern(pattern, t)
|
||||
case Regex:
|
||||
r, err := regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
g.otherMatchers = append(g.otherMatchers, matcherEntry{
|
||||
m: ®exMatcher{pattern: r},
|
||||
id: g.count,
|
||||
})
|
||||
default:
|
||||
panic("Unknown type")
|
||||
}
|
||||
return g.count, nil
|
||||
}
|
||||
|
||||
// Build builds a minimal perfect hash table and ac automaton from insert rules
|
||||
func (g *MphMatcherGroup) Build() {
|
||||
if g.ac != nil {
|
||||
g.ac.Build()
|
||||
}
|
||||
keyLen := len(*g.ruleMap)
|
||||
if keyLen == 0 {
|
||||
keyLen = 1
|
||||
(*g.ruleMap)["empty___"] = RollingHash("empty___")
|
||||
}
|
||||
g.level0 = make([]uint32, nextPow2(keyLen/4))
|
||||
g.level0Mask = len(g.level0) - 1
|
||||
g.level1 = make([]uint32, nextPow2(keyLen))
|
||||
g.level1Mask = len(g.level1) - 1
|
||||
var sparseBuckets = make([][]int, len(g.level0))
|
||||
var ruleIdx int
|
||||
for rule, hash := range *g.ruleMap {
|
||||
n := int(hash) & g.level0Mask
|
||||
g.rules = append(g.rules, rule)
|
||||
sparseBuckets[n] = append(sparseBuckets[n], ruleIdx)
|
||||
ruleIdx++
|
||||
}
|
||||
g.ruleMap = nil
|
||||
var buckets []indexBucket
|
||||
for n, vals := range sparseBuckets {
|
||||
if len(vals) > 0 {
|
||||
buckets = append(buckets, indexBucket{n, vals})
|
||||
}
|
||||
}
|
||||
sort.Sort(bySize(buckets))
|
||||
|
||||
occ := make([]bool, len(g.level1))
|
||||
var tmpOcc []int
|
||||
for _, bucket := range buckets {
|
||||
var seed = uint32(0)
|
||||
for {
|
||||
findSeed := true
|
||||
tmpOcc = tmpOcc[:0]
|
||||
for _, i := range bucket.vals {
|
||||
n := int(strhashFallback(unsafe.Pointer(&g.rules[i]), uintptr(seed))) & g.level1Mask
|
||||
if occ[n] {
|
||||
for _, n := range tmpOcc {
|
||||
occ[n] = false
|
||||
}
|
||||
seed++
|
||||
findSeed = false
|
||||
break
|
||||
}
|
||||
occ[n] = true
|
||||
tmpOcc = append(tmpOcc, n)
|
||||
g.level1[n] = uint32(i)
|
||||
}
|
||||
if findSeed {
|
||||
g.level0[bucket.n] = seed
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func nextPow2(v int) int {
|
||||
if v <= 1 {
|
||||
return 1
|
||||
}
|
||||
const MaxUInt = ^uint(0)
|
||||
n := (MaxUInt >> bits.LeadingZeros(uint(v))) + 1
|
||||
return int(n)
|
||||
}
|
||||
|
||||
// Lookup searches for s in t and returns its index and whether it was found.
|
||||
func (g *MphMatcherGroup) Lookup(h uint32, s string) bool {
|
||||
i0 := int(h) & g.level0Mask
|
||||
seed := g.level0[i0]
|
||||
i1 := int(strhashFallback(unsafe.Pointer(&s), uintptr(seed))) & g.level1Mask
|
||||
n := g.level1[i1]
|
||||
return s == g.rules[int(n)]
|
||||
}
|
||||
|
||||
// Match implements IndexMatcher.Match.
|
||||
func (g *MphMatcherGroup) Match(pattern string) []uint32 {
|
||||
result := []uint32{}
|
||||
hash := uint32(0)
|
||||
for i := len(pattern) - 1; i >= 0; i-- {
|
||||
hash = hash*PrimeRK + uint32(pattern[i])
|
||||
if pattern[i] == '.' {
|
||||
if g.Lookup(hash, pattern[i:]) {
|
||||
result = append(result, 1)
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
if g.Lookup(hash, pattern) {
|
||||
result = append(result, 1)
|
||||
return result
|
||||
}
|
||||
if g.ac != nil && g.ac.Match(pattern) {
|
||||
result = append(result, 1)
|
||||
return result
|
||||
}
|
||||
for _, e := range g.otherMatchers {
|
||||
if e.m.Match(pattern) {
|
||||
result = append(result, e.id)
|
||||
return result
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type indexBucket struct {
|
||||
n int
|
||||
vals []int
|
||||
}
|
||||
|
||||
type bySize []indexBucket
|
||||
|
||||
func (s bySize) Len() int { return len(s) }
|
||||
func (s bySize) Less(i, j int) bool { return len(s[i].vals) > len(s[j].vals) }
|
||||
func (s bySize) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
type stringStruct struct {
|
||||
str unsafe.Pointer
|
||||
len int
|
||||
}
|
||||
|
||||
func strhashFallback(a unsafe.Pointer, h uintptr) uintptr {
|
||||
x := (*stringStruct)(a)
|
||||
return memhashFallback(x.str, h, uintptr(x.len))
|
||||
}
|
||||
|
||||
const (
|
||||
// Constants for multiplication: four random odd 64-bit numbers.
|
||||
m1 = 16877499708836156737
|
||||
m2 = 2820277070424839065
|
||||
m3 = 9497967016996688599
|
||||
m4 = 15839092249703872147
|
||||
)
|
||||
|
||||
var hashkey = [4]uintptr{1, 1, 1, 1}
|
||||
|
||||
func memhashFallback(p unsafe.Pointer, seed, s uintptr) uintptr {
|
||||
h := uint64(seed + s*hashkey[0])
|
||||
tail:
|
||||
switch {
|
||||
case s == 0:
|
||||
case s < 4:
|
||||
h ^= uint64(*(*byte)(p))
|
||||
h ^= uint64(*(*byte)(add(p, s>>1))) << 8
|
||||
h ^= uint64(*(*byte)(add(p, s-1))) << 16
|
||||
h = rotl31(h*m1) * m2
|
||||
case s <= 8:
|
||||
h ^= uint64(readUnaligned32(p))
|
||||
h ^= uint64(readUnaligned32(add(p, s-4))) << 32
|
||||
h = rotl31(h*m1) * m2
|
||||
case s <= 16:
|
||||
h ^= readUnaligned64(p)
|
||||
h = rotl31(h*m1) * m2
|
||||
h ^= readUnaligned64(add(p, s-8))
|
||||
h = rotl31(h*m1) * m2
|
||||
case s <= 32:
|
||||
h ^= readUnaligned64(p)
|
||||
h = rotl31(h*m1) * m2
|
||||
h ^= readUnaligned64(add(p, 8))
|
||||
h = rotl31(h*m1) * m2
|
||||
h ^= readUnaligned64(add(p, s-16))
|
||||
h = rotl31(h*m1) * m2
|
||||
h ^= readUnaligned64(add(p, s-8))
|
||||
h = rotl31(h*m1) * m2
|
||||
default:
|
||||
v1 := h
|
||||
v2 := uint64(seed * hashkey[1])
|
||||
v3 := uint64(seed * hashkey[2])
|
||||
v4 := uint64(seed * hashkey[3])
|
||||
for s >= 32 {
|
||||
v1 ^= readUnaligned64(p)
|
||||
v1 = rotl31(v1*m1) * m2
|
||||
p = add(p, 8)
|
||||
v2 ^= readUnaligned64(p)
|
||||
v2 = rotl31(v2*m2) * m3
|
||||
p = add(p, 8)
|
||||
v3 ^= readUnaligned64(p)
|
||||
v3 = rotl31(v3*m3) * m4
|
||||
p = add(p, 8)
|
||||
v4 ^= readUnaligned64(p)
|
||||
v4 = rotl31(v4*m4) * m1
|
||||
p = add(p, 8)
|
||||
s -= 32
|
||||
}
|
||||
h = v1 ^ v2 ^ v3 ^ v4
|
||||
goto tail
|
||||
}
|
||||
|
||||
h ^= h >> 29
|
||||
h *= m3
|
||||
h ^= h >> 32
|
||||
return uintptr(h)
|
||||
}
|
||||
func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(p) + x)
|
||||
}
|
||||
func readUnaligned32(p unsafe.Pointer) uint32 {
|
||||
q := (*[4]byte)(p)
|
||||
return uint32(q[0]) | uint32(q[1])<<8 | uint32(q[2])<<16 | uint32(q[3])<<24
|
||||
}
|
||||
|
||||
func rotl31(x uint64) uint64 {
|
||||
return (x << 31) | (x >> (64 - 31))
|
||||
}
|
||||
func readUnaligned64(p unsafe.Pointer) uint64 {
|
||||
q := (*[8]byte)(p)
|
||||
return uint64(q[0]) | uint64(q[1])<<8 | uint64(q[2])<<16 | uint64(q[3])<<24 | uint64(q[4])<<32 | uint64(q[5])<<40 | uint64(q[6])<<48 | uint64(q[7])<<56
|
||||
}
|
@@ -27,7 +27,6 @@ const (
|
||||
|
||||
// New creates a new Matcher based on the given pattern.
|
||||
func (t Type) New(pattern string) (Matcher, error) {
|
||||
// 1. regex matching is case-sensitive
|
||||
switch t {
|
||||
case Full:
|
||||
return fullMatcher(pattern), nil
|
||||
|
@@ -91,172 +91,3 @@ func TestMatcherGroup(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestACAutomaton(t *testing.T) {
|
||||
cases1 := []struct {
|
||||
pattern string
|
||||
mType Type
|
||||
input string
|
||||
output bool
|
||||
}{
|
||||
{
|
||||
pattern: "xtls.github.io",
|
||||
mType: Domain,
|
||||
input: "www.xtls.github.io",
|
||||
output: true,
|
||||
},
|
||||
{
|
||||
pattern: "xtls.github.io",
|
||||
mType: Domain,
|
||||
input: "xtls.github.io",
|
||||
output: true,
|
||||
},
|
||||
{
|
||||
pattern: "xtls.github.io",
|
||||
mType: Domain,
|
||||
input: "www.xtis.github.io",
|
||||
output: false,
|
||||
},
|
||||
{
|
||||
pattern: "xtls.github.io",
|
||||
mType: Domain,
|
||||
input: "tls.github.io",
|
||||
output: false,
|
||||
},
|
||||
{
|
||||
pattern: "xtls.github.io",
|
||||
mType: Domain,
|
||||
input: "xxtls.github.io",
|
||||
output: false,
|
||||
},
|
||||
{
|
||||
pattern: "xtls.github.io",
|
||||
mType: Full,
|
||||
input: "xtls.github.io",
|
||||
output: true,
|
||||
},
|
||||
{
|
||||
pattern: "xtls.github.io",
|
||||
mType: Full,
|
||||
input: "xxtls.github.io",
|
||||
output: false,
|
||||
},
|
||||
}
|
||||
for _, test := range cases1 {
|
||||
var ac = NewACAutomaton()
|
||||
ac.Add(test.pattern, test.mType)
|
||||
ac.Build()
|
||||
if m := ac.Match(test.input); m != test.output {
|
||||
t.Error("unexpected output: ", m, " for test case ", test)
|
||||
}
|
||||
}
|
||||
{
|
||||
cases2Input := []struct {
|
||||
pattern string
|
||||
mType Type
|
||||
}{
|
||||
{
|
||||
pattern: "163.com",
|
||||
mType: Domain,
|
||||
},
|
||||
{
|
||||
pattern: "m.126.com",
|
||||
mType: Full,
|
||||
},
|
||||
{
|
||||
pattern: "3.com",
|
||||
mType: Full,
|
||||
},
|
||||
{
|
||||
pattern: "google.com",
|
||||
mType: Substr,
|
||||
},
|
||||
{
|
||||
pattern: "vgoogle.com",
|
||||
mType: Substr,
|
||||
},
|
||||
}
|
||||
var ac = NewACAutomaton()
|
||||
for _, test := range cases2Input {
|
||||
ac.Add(test.pattern, test.mType)
|
||||
}
|
||||
ac.Build()
|
||||
cases2Output := []struct {
|
||||
pattern string
|
||||
res bool
|
||||
}{
|
||||
{
|
||||
pattern: "126.com",
|
||||
res: false,
|
||||
},
|
||||
{
|
||||
pattern: "m.163.com",
|
||||
res: true,
|
||||
},
|
||||
{
|
||||
pattern: "mm163.com",
|
||||
res: false,
|
||||
},
|
||||
{
|
||||
pattern: "m.126.com",
|
||||
res: true,
|
||||
},
|
||||
{
|
||||
pattern: "163.com",
|
||||
res: true,
|
||||
},
|
||||
{
|
||||
pattern: "63.com",
|
||||
res: false,
|
||||
},
|
||||
{
|
||||
pattern: "oogle.com",
|
||||
res: false,
|
||||
},
|
||||
{
|
||||
pattern: "vvgoogle.com",
|
||||
res: true,
|
||||
},
|
||||
}
|
||||
for _, test := range cases2Output {
|
||||
if m := ac.Match(test.pattern); m != test.res {
|
||||
t.Error("unexpected output: ", m, " for test case ", test)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
cases3Input := []struct {
|
||||
pattern string
|
||||
mType Type
|
||||
}{
|
||||
{
|
||||
pattern: "video.google.com",
|
||||
mType: Domain,
|
||||
},
|
||||
{
|
||||
pattern: "gle.com",
|
||||
mType: Domain,
|
||||
},
|
||||
}
|
||||
var ac = NewACAutomaton()
|
||||
for _, test := range cases3Input {
|
||||
ac.Add(test.pattern, test.mType)
|
||||
}
|
||||
ac.Build()
|
||||
cases3Output := []struct {
|
||||
pattern string
|
||||
res bool
|
||||
}{
|
||||
{
|
||||
pattern: "google.com",
|
||||
res: false,
|
||||
},
|
||||
}
|
||||
for _, test := range cases3Output {
|
||||
if m := ac.Match(test.pattern); m != test.res {
|
||||
t.Error("unexpected output: ", m, " for test case ", test)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -94,12 +94,6 @@ var (
|
||||
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
|
||||
|
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
14
infra/conf/loopback.go
Normal file
14
infra/conf/loopback.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/xtls/xray-core/proxy/loopback"
|
||||
)
|
||||
|
||||
type LoopbackConfig struct {
|
||||
InboundTag string `json:"inboundTag"`
|
||||
}
|
||||
|
||||
func (l LoopbackConfig) Build() (proto.Message, error) {
|
||||
return &loopback.Config{InboundTag: l.InboundTag}, nil
|
||||
}
|
@@ -98,8 +98,6 @@ type RouterRule struct {
|
||||
Type string `json:"type"`
|
||||
OutboundTag string `json:"outboundTag"`
|
||||
BalancerTag string `json:"balancerTag"`
|
||||
|
||||
DomainMatcher string `json:"domainMatcher"`
|
||||
}
|
||||
|
||||
func ParseIP(s string) (*router.CIDR, error) {
|
||||
@@ -493,10 +491,6 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
||||
return nil, newError("neither outboundTag nor balancerTag is specified in routing rule")
|
||||
}
|
||||
|
||||
if rawFieldRule.DomainMatcher != "" {
|
||||
rule.DomainMatcher = rawFieldRule.DomainMatcher
|
||||
}
|
||||
|
||||
if rawFieldRule.Domain != nil {
|
||||
for _, domain := range *rawFieldRule.Domain {
|
||||
rules, err := parseDomainRule(domain)
|
||||
|
@@ -2,11 +2,12 @@ package conf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
|
||||
"github.com/xtls/xray-core/app/dispatcher"
|
||||
"github.com/xtls/xray-core/app/proxyman"
|
||||
"github.com/xtls/xray-core/app/stats"
|
||||
@@ -38,6 +39,7 @@ var (
|
||||
"trojan": func() interface{} { return new(TrojanClientConfig) },
|
||||
"mtproto": func() interface{} { return new(MTProtoClientConfig) },
|
||||
"dns": func() interface{} { return new(DNSOutboundConfig) },
|
||||
"loopback": func() interface{} { return new(LoopbackConfig) },
|
||||
}, "protocol", "settings")
|
||||
|
||||
ctllog = log.New(os.Stderr, "xctl> ", 0)
|
||||
|
@@ -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,7 +39,6 @@ type Handler struct {
|
||||
client dns.Client
|
||||
ownLinkVerifier ownLinkVerifier
|
||||
server net.Destination
|
||||
DomainStrategy Config_DomainStrategy
|
||||
}
|
||||
|
||||
func (h *Handler) Init(config *Config, dnsClient dns.Client) error {
|
||||
@@ -200,24 +199,16 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string,
|
||||
var err error
|
||||
|
||||
var ttl uint32 = 600
|
||||
var opt []dns.Option
|
||||
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:
|
||||
opt = dns.LookupIPv4Only
|
||||
case dnsmessage.TypeAAAA:
|
||||
opt = dns.LookupIPv6Only
|
||||
}
|
||||
|
||||
if h.DomainStrategy == Config_USE_ALL {
|
||||
opt = append(opt, dns.LookupFake)
|
||||
}
|
||||
|
||||
ips, err = h.client.LookupOptions(domain, opt...)
|
||||
ips, err = h.client.LookupOptions(domain, opt, dns.LookupFake)
|
||||
rcode := dns.RCodeFromError(err)
|
||||
if rcode == 0 && len(ips) == 0 && err != dns.ErrEmptyResponse {
|
||||
newError("ip query").Base(err).WriteToLog()
|
||||
|
149
proxy/loopback/config.pb.go
Normal file
149
proxy/loopback/config.pb.go
Normal file
@@ -0,0 +1,149 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.15.8
|
||||
// source: proxy/loopback/config.proto
|
||||
|
||||
package loopback
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
InboundTag string `protobuf:"bytes,1,opt,name=inbound_tag,json=inboundTag,proto3" json:"inbound_tag,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proxy_loopback_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proxy_loopback_config_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_proxy_loopback_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Config) GetInboundTag() string {
|
||||
if x != nil {
|
||||
return x.InboundTag
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_proxy_loopback_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_proxy_loopback_config_proto_rawDesc = []byte{
|
||||
0x0a, 0x1b, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x62, 0x61, 0x63, 0x6b,
|
||||
0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x78,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x62, 0x61,
|
||||
0x63, 0x6b, 0x22, 0x29, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1f, 0x0a, 0x0b,
|
||||
0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x0a, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x42, 0x5b, 0x0a,
|
||||
0x17, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e,
|
||||
0x6c, 0x6f, 0x6f, 0x70, 0x62, 0x61, 0x63, 0x6b, 0x50, 0x01, 0x5a, 0x28, 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, 0x6c, 0x6f, 0x6f, 0x70,
|
||||
0x62, 0x61, 0x63, 0x6b, 0xaa, 0x02, 0x13, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78,
|
||||
0x79, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x62, 0x61, 0x63, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_proxy_loopback_config_proto_rawDescOnce sync.Once
|
||||
file_proxy_loopback_config_proto_rawDescData = file_proxy_loopback_config_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_proxy_loopback_config_proto_rawDescGZIP() []byte {
|
||||
file_proxy_loopback_config_proto_rawDescOnce.Do(func() {
|
||||
file_proxy_loopback_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_proxy_loopback_config_proto_rawDescData)
|
||||
})
|
||||
return file_proxy_loopback_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_proxy_loopback_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_proxy_loopback_config_proto_goTypes = []interface{}{
|
||||
(*Config)(nil), // 0: xray.proxy.loopback.Config
|
||||
}
|
||||
var file_proxy_loopback_config_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_proxy_loopback_config_proto_init() }
|
||||
func file_proxy_loopback_config_proto_init() {
|
||||
if File_proxy_loopback_config_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_proxy_loopback_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Config); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_proxy_loopback_config_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_proxy_loopback_config_proto_goTypes,
|
||||
DependencyIndexes: file_proxy_loopback_config_proto_depIdxs,
|
||||
MessageInfos: file_proxy_loopback_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_proxy_loopback_config_proto = out.File
|
||||
file_proxy_loopback_config_proto_rawDesc = nil
|
||||
file_proxy_loopback_config_proto_goTypes = nil
|
||||
file_proxy_loopback_config_proto_depIdxs = nil
|
||||
}
|
11
proxy/loopback/config.proto
Normal file
11
proxy/loopback/config.proto
Normal file
@@ -0,0 +1,11 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.proxy.loopback;
|
||||
option csharp_namespace = "Xray.Proxy.Loopback";
|
||||
option go_package = "github.com/xtls/xray-core/proxy/loopback";
|
||||
option java_package = "com.xray.proxy.loopback";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message Config {
|
||||
string inbound_tag = 1;
|
||||
}
|
9
proxy/loopback/errors.generated.go
Normal file
9
proxy/loopback/errors.generated.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package loopback
|
||||
|
||||
import "github.com/xtls/xray-core/common/errors"
|
||||
|
||||
type errPathObjHolder struct{}
|
||||
|
||||
func newError(values ...interface{}) *errors.Error {
|
||||
return errors.New(values...).WithPathObj(errPathObjHolder{})
|
||||
}
|
121
proxy/loopback/lookback.go
Normal file
121
proxy/loopback/lookback.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package loopback
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/common/retry"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/task"
|
||||
core "github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
)
|
||||
|
||||
type Loopback struct {
|
||||
config *Config
|
||||
dispatcherInstance routing.Dispatcher
|
||||
}
|
||||
|
||||
func (l *Loopback) Process(ctx context.Context, link *transport.Link, _ internet.Dialer) error {
|
||||
outbound := session.OutboundFromContext(ctx)
|
||||
if outbound == nil || !outbound.Target.IsValid() {
|
||||
return newError("target not specified.")
|
||||
}
|
||||
destination := outbound.Target
|
||||
|
||||
newError("opening connection to ", destination).WriteToLog(session.ExportIDToError(ctx))
|
||||
|
||||
input := link.Reader
|
||||
output := link.Writer
|
||||
|
||||
var conn internet.Connection
|
||||
err := retry.ExponentialBackoff(5, 100).On(func() error {
|
||||
dialDest := destination
|
||||
|
||||
content := new(session.Content)
|
||||
content.SkipDNSResolve = true
|
||||
|
||||
ctx = session.ContextWithContent(ctx, content)
|
||||
|
||||
inbound := session.InboundFromContext(ctx)
|
||||
|
||||
inbound.Tag = l.config.InboundTag
|
||||
|
||||
ctx = session.ContextWithInbound(ctx, inbound)
|
||||
|
||||
rawConn, err := l.dispatcherInstance.Dispatch(ctx, dialDest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var readerOpt cnc.ConnectionOption
|
||||
if dialDest.Network == net.Network_TCP {
|
||||
readerOpt = cnc.ConnectionOutputMulti(rawConn.Reader)
|
||||
} else {
|
||||
readerOpt = cnc.ConnectionOutputMultiUDP(rawConn.Reader)
|
||||
}
|
||||
|
||||
conn = cnc.NewConnection(cnc.ConnectionInputMulti(rawConn.Writer), readerOpt)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return newError("failed to open connection to ", destination).Base(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
requestDone := func() error {
|
||||
var writer buf.Writer
|
||||
if destination.Network == net.Network_TCP {
|
||||
writer = buf.NewWriter(conn)
|
||||
} else {
|
||||
writer = &buf.SequentialWriter{Writer: conn}
|
||||
}
|
||||
|
||||
if err := buf.Copy(input, writer); err != nil {
|
||||
return newError("failed to process request").Base(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
responseDone := func() error {
|
||||
var reader buf.Reader
|
||||
if destination.Network == net.Network_TCP {
|
||||
reader = buf.NewReader(conn)
|
||||
} else {
|
||||
reader = buf.NewPacketReader(conn)
|
||||
}
|
||||
if err := buf.Copy(reader, output); err != nil {
|
||||
return newError("failed to process response").Base(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := task.Run(ctx, requestDone, task.OnSuccess(responseDone, task.Close(output))); err != nil {
|
||||
return newError("connection ends").Base(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Loopback) init(config *Config, dispatcherInstance routing.Dispatcher) error {
|
||||
l.dispatcherInstance = dispatcherInstance
|
||||
l.config = config
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
l := new(Loopback)
|
||||
err := core.RequireFeatures(ctx, func(dispatcherInstance routing.Dispatcher) error {
|
||||
return l.init(config.(*Config), dispatcherInstance)
|
||||
})
|
||||
return l, err
|
||||
}))
|
||||
}
|
3
proxy/loopback/loopback.go
Normal file
3
proxy/loopback/loopback.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package loopback
|
||||
|
||||
//go:generate go run github.com/xray/xray-core/common/errors/errorgen
|
Reference in New Issue
Block a user