mirror of https://github.com/XTLS/Xray-core
				
				
				
			Add Fake DNS support (#309)
Co-authored-by: Xiaokang Wang <xiaokangwang@outlook.com> Co-authored-by: loyalsoldier <10487845+Loyalsoldier@users.noreply.github.com> Co-authored-by: kslr <kslrwang@gmail.com>pull/345/head
							parent
							
								
									db32ce6fd9
								
							
						
					
					
						commit
						f50eff5ebb
					
				| 
						 | 
				
			
			@ -15,6 +15,7 @@ import (
 | 
			
		|||
	"github.com/xtls/xray-core/common/protocol"
 | 
			
		||||
	"github.com/xtls/xray-core/common/session"
 | 
			
		||||
	"github.com/xtls/xray-core/core"
 | 
			
		||||
	"github.com/xtls/xray-core/features/dns"
 | 
			
		||||
	"github.com/xtls/xray-core/features/outbound"
 | 
			
		||||
	"github.com/xtls/xray-core/features/policy"
 | 
			
		||||
	"github.com/xtls/xray-core/features/routing"
 | 
			
		||||
| 
						 | 
				
			
			@ -175,17 +176,28 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
 | 
			
		|||
	return inboundLink, outboundLink
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func shouldOverride(result SniffResult, request session.SniffingRequest) bool {
 | 
			
		||||
func shouldOverride(ctx context.Context, result SniffResult, request session.SniffingRequest, destination net.Destination) bool {
 | 
			
		||||
	domain := result.Domain()
 | 
			
		||||
	for _, d := range request.ExcludeForDomain {
 | 
			
		||||
		if domain == d {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protocol := result.Protocol()
 | 
			
		||||
	var fakeDNSEngine dns.FakeDNSEngine
 | 
			
		||||
	core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
 | 
			
		||||
		fakeDNSEngine = fdns
 | 
			
		||||
	})
 | 
			
		||||
	protocolString := result.Protocol()
 | 
			
		||||
	if resComp, ok := result.(SnifferResultComposite); ok {
 | 
			
		||||
		protocolString = resComp.ProtocolForDomainResult()
 | 
			
		||||
	}
 | 
			
		||||
	for _, p := range request.OverrideDestinationForProtocol {
 | 
			
		||||
		if strings.HasPrefix(protocol, p) {
 | 
			
		||||
		if strings.HasPrefix(protocolString, p) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		if fakeDNSEngine != nil && protocolString != "bittorrent" && p == "fakedns" &&
 | 
			
		||||
			fakeDNSEngine.GetFakeIPRange().Contains(destination.Address.IP()) {
 | 
			
		||||
			newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx))
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -210,19 +222,33 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
 | 
			
		|||
		ctx = session.ContextWithContent(ctx, content)
 | 
			
		||||
	}
 | 
			
		||||
	sniffingRequest := content.SniffingRequest
 | 
			
		||||
	if destination.Network != net.Network_TCP || !sniffingRequest.Enabled {
 | 
			
		||||
	switch {
 | 
			
		||||
	case !sniffingRequest.Enabled:
 | 
			
		||||
		go d.routedDispatch(ctx, outbound, destination)
 | 
			
		||||
	} else {
 | 
			
		||||
	case destination.Network != net.Network_TCP:
 | 
			
		||||
		// Only metadata sniff will be used for non tcp connection
 | 
			
		||||
		result, err := sniffer(ctx, nil, true)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			content.Protocol = result.Protocol()
 | 
			
		||||
			if shouldOverride(ctx, result, sniffingRequest, destination) {
 | 
			
		||||
				domain := result.Domain()
 | 
			
		||||
				newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
 | 
			
		||||
				destination.Address = net.ParseAddress(domain)
 | 
			
		||||
				ob.Target = destination
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		go d.routedDispatch(ctx, outbound, destination)
 | 
			
		||||
	default:
 | 
			
		||||
		go func() {
 | 
			
		||||
			cReader := &cachedReader{
 | 
			
		||||
				reader: outbound.Reader.(*pipe.Reader),
 | 
			
		||||
			}
 | 
			
		||||
			outbound.Reader = cReader
 | 
			
		||||
			result, err := sniffer(ctx, cReader)
 | 
			
		||||
			result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				content.Protocol = result.Protocol()
 | 
			
		||||
			}
 | 
			
		||||
			if err == nil && shouldOverride(result, sniffingRequest) {
 | 
			
		||||
			if err == nil && shouldOverride(ctx, result, sniffingRequest, destination) {
 | 
			
		||||
				domain := result.Domain()
 | 
			
		||||
				newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
 | 
			
		||||
				destination.Address = net.ParseAddress(domain)
 | 
			
		||||
| 
						 | 
				
			
			@ -234,34 +260,50 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
 | 
			
		|||
	return inbound, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sniffer(ctx context.Context, cReader *cachedReader) (SniffResult, error) {
 | 
			
		||||
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (SniffResult, error) {
 | 
			
		||||
	payload := buf.New()
 | 
			
		||||
	defer payload.Release()
 | 
			
		||||
 | 
			
		||||
	sniffer := NewSniffer()
 | 
			
		||||
	totalAttempt := 0
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case <-ctx.Done():
 | 
			
		||||
			return nil, ctx.Err()
 | 
			
		||||
		default:
 | 
			
		||||
			totalAttempt++
 | 
			
		||||
			if totalAttempt > 2 {
 | 
			
		||||
				return nil, errSniffingTimeout
 | 
			
		||||
			}
 | 
			
		||||
	sniffer := NewSniffer(ctx)
 | 
			
		||||
 | 
			
		||||
			cReader.Cache(payload)
 | 
			
		||||
			if !payload.IsEmpty() {
 | 
			
		||||
				result, err := sniffer.Sniff(payload.Bytes())
 | 
			
		||||
				if err != common.ErrNoClue {
 | 
			
		||||
					return result, err
 | 
			
		||||
	metaresult, metadataErr := sniffer.SniffMetadata(ctx)
 | 
			
		||||
 | 
			
		||||
	if metadataOnly {
 | 
			
		||||
		return metaresult, metadataErr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	contentResult, contentErr := func() (SniffResult, error) {
 | 
			
		||||
		totalAttempt := 0
 | 
			
		||||
		for {
 | 
			
		||||
			select {
 | 
			
		||||
			case <-ctx.Done():
 | 
			
		||||
				return nil, ctx.Err()
 | 
			
		||||
			default:
 | 
			
		||||
				totalAttempt++
 | 
			
		||||
				if totalAttempt > 2 {
 | 
			
		||||
					return nil, errSniffingTimeout
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				cReader.Cache(payload)
 | 
			
		||||
				if !payload.IsEmpty() {
 | 
			
		||||
					result, err := sniffer.Sniff(ctx, payload.Bytes())
 | 
			
		||||
					if err != common.ErrNoClue {
 | 
			
		||||
						return result, err
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				if payload.IsFull() {
 | 
			
		||||
					return nil, errUnknownContent
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if payload.IsFull() {
 | 
			
		||||
				return nil, errUnknownContent
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	if contentErr != nil && metadataErr == nil {
 | 
			
		||||
		return metaresult, nil
 | 
			
		||||
	}
 | 
			
		||||
	if contentErr == nil && metadataErr == nil {
 | 
			
		||||
		return CompositeResult(metaresult, contentResult), nil
 | 
			
		||||
	}
 | 
			
		||||
	return contentResult, contentErr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
// +build !confonly
 | 
			
		||||
 | 
			
		||||
package dispatcher
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	"github.com/xtls/xray-core/core"
 | 
			
		||||
	"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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// newFakeDNSSniffer Create a Fake DNS metadata sniffer
 | 
			
		||||
func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error) {
 | 
			
		||||
	var fakeDNSEngine dns.FakeDNSEngine
 | 
			
		||||
	err := core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
 | 
			
		||||
		fakeDNSEngine = fdns
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return protocolSnifferWithMetadata{}, err
 | 
			
		||||
	}
 | 
			
		||||
	if fakeDNSEngine == nil {
 | 
			
		||||
		errNotInit := newError("FakeDNSEngine is not initialized, but such a sniffer is used").AtError()
 | 
			
		||||
		return protocolSnifferWithMetadata{}, errNotInit
 | 
			
		||||
	}
 | 
			
		||||
	return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
 | 
			
		||||
		Target := session.OutboundFromContext(ctx).Target
 | 
			
		||||
		if Target.Network == net.Network_TCP || Target.Network == net.Network_UDP {
 | 
			
		||||
			domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(Target.Address)
 | 
			
		||||
			if domainFromFakeDNS != "" {
 | 
			
		||||
				newError("fake dns got domain: ", domainFromFakeDNS, " for ip: ", Target.Address.String()).WriteToLog(session.ExportIDToError(ctx))
 | 
			
		||||
				return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return nil, common.ErrNoClue
 | 
			
		||||
	}, metadataSniffer: true}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type fakeDNSSniffResult struct {
 | 
			
		||||
	domainName string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (fakeDNSSniffResult) Protocol() string {
 | 
			
		||||
	return "fakedns"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f fakeDNSSniffResult) Domain() string {
 | 
			
		||||
	return f.domainName
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
package dispatcher
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	"github.com/xtls/xray-core/common"
 | 
			
		||||
	"github.com/xtls/xray-core/common/protocol/bittorrent"
 | 
			
		||||
	"github.com/xtls/xray-core/common/protocol/http"
 | 
			
		||||
| 
						 | 
				
			
			@ -12,30 +14,46 @@ type SniffResult interface {
 | 
			
		|||
	Domain() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type protocolSniffer func([]byte) (SniffResult, error)
 | 
			
		||||
type protocolSniffer func(context.Context, []byte) (SniffResult, error)
 | 
			
		||||
 | 
			
		||||
type Sniffer struct {
 | 
			
		||||
	sniffer []protocolSniffer
 | 
			
		||||
type protocolSnifferWithMetadata struct {
 | 
			
		||||
	protocolSniffer protocolSniffer
 | 
			
		||||
	// A Metadata sniffer will be invoked on connection establishment only, with nil body,
 | 
			
		||||
	// for both TCP and UDP connections
 | 
			
		||||
	// It will not be shown as a traffic type for routing unless there is no other successful sniffing.
 | 
			
		||||
	metadataSniffer bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSniffer() *Sniffer {
 | 
			
		||||
	return &Sniffer{
 | 
			
		||||
		sniffer: []protocolSniffer{
 | 
			
		||||
			func(b []byte) (SniffResult, error) { return http.SniffHTTP(b) },
 | 
			
		||||
			func(b []byte) (SniffResult, error) { return tls.SniffTLS(b) },
 | 
			
		||||
			func(b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) },
 | 
			
		||||
type Sniffer struct {
 | 
			
		||||
	sniffer []protocolSnifferWithMetadata
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSniffer(ctx context.Context) *Sniffer {
 | 
			
		||||
	ret := &Sniffer{
 | 
			
		||||
		sniffer: []protocolSnifferWithMetadata{
 | 
			
		||||
			{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false},
 | 
			
		||||
			{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false},
 | 
			
		||||
			{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	if sniffer, err := newFakeDNSSniffer(ctx); err == nil {
 | 
			
		||||
		ret.sniffer = append(ret.sniffer, sniffer)
 | 
			
		||||
	}
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errUnknownContent = newError("unknown content")
 | 
			
		||||
 | 
			
		||||
func (s *Sniffer) Sniff(payload []byte) (SniffResult, error) {
 | 
			
		||||
	var pendingSniffer []protocolSniffer
 | 
			
		||||
	for _, s := range s.sniffer {
 | 
			
		||||
		result, err := s(payload)
 | 
			
		||||
func (s *Sniffer) Sniff(c context.Context, payload []byte) (SniffResult, error) {
 | 
			
		||||
	var pendingSniffer []protocolSnifferWithMetadata
 | 
			
		||||
	for _, si := range s.sniffer {
 | 
			
		||||
		s := si.protocolSniffer
 | 
			
		||||
		if si.metadataSniffer {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		result, err := s(c, payload)
 | 
			
		||||
		if err == common.ErrNoClue {
 | 
			
		||||
			pendingSniffer = append(pendingSniffer, s)
 | 
			
		||||
			pendingSniffer = append(pendingSniffer, si)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -51,3 +69,55 @@ func (s *Sniffer) Sniff(payload []byte) (SniffResult, error) {
 | 
			
		|||
 | 
			
		||||
	return nil, errUnknownContent
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Sniffer) SniffMetadata(c context.Context) (SniffResult, error) {
 | 
			
		||||
	var pendingSniffer []protocolSnifferWithMetadata
 | 
			
		||||
	for _, si := range s.sniffer {
 | 
			
		||||
		s := si.protocolSniffer
 | 
			
		||||
		if !si.metadataSniffer {
 | 
			
		||||
			pendingSniffer = append(pendingSniffer, si)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		result, err := s(c, nil)
 | 
			
		||||
		if err == common.ErrNoClue {
 | 
			
		||||
			pendingSniffer = append(pendingSniffer, si)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err == nil && result != nil {
 | 
			
		||||
			return result, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(pendingSniffer) > 0 {
 | 
			
		||||
		s.sniffer = pendingSniffer
 | 
			
		||||
		return nil, common.ErrNoClue
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, errUnknownContent
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CompositeResult(domainResult SniffResult, protocolResult SniffResult) SniffResult {
 | 
			
		||||
	return &compositeResult{domainResult: domainResult, protocolResult: protocolResult}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type compositeResult struct {
 | 
			
		||||
	domainResult   SniffResult
 | 
			
		||||
	protocolResult SniffResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c compositeResult) Protocol() string {
 | 
			
		||||
	return c.protocolResult.Protocol()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c compositeResult) Domain() string {
 | 
			
		||||
	return c.domainResult.Domain()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c compositeResult) ProtocolForDomainResult() string {
 | 
			
		||||
	return c.domainResult.Protocol()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SnifferResultComposite interface {
 | 
			
		||||
	ProtocolForDomainResult() string
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,4 +68,6 @@ message Config {
 | 
			
		|||
 | 
			
		||||
  // Tag is the inbound tag of DNS client.
 | 
			
		||||
  string tag = 6;
 | 
			
		||||
 | 
			
		||||
  reserved 7;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ package dns
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/xtls/xray-core/common"
 | 
			
		||||
| 
						 | 
				
			
			@ -13,7 +14,7 @@ import (
 | 
			
		|||
 | 
			
		||||
// Fqdn normalize domain make sure it ends with '.'
 | 
			
		||||
func Fqdn(domain string) string {
 | 
			
		||||
	if len(domain) > 0 && domain[len(domain)-1] == '.' {
 | 
			
		||||
	if len(domain) > 0 && strings.HasSuffix(domain, ".") {
 | 
			
		||||
		return domain
 | 
			
		||||
	}
 | 
			
		||||
	return domain + "."
 | 
			
		||||
| 
						 | 
				
			
			@ -112,7 +113,7 @@ func genEDNS0Options(clientIP net.IP) *dnsmessage.Resource {
 | 
			
		|||
	return opt
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func buildReqMsgs(domain string, option IPOption, reqIDGen func() uint16, reqOpts *dnsmessage.Resource) []*dnsRequest {
 | 
			
		||||
func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() uint16, reqOpts *dnsmessage.Resource) []*dnsRequest {
 | 
			
		||||
	qA := dnsmessage.Question{
 | 
			
		||||
		Name:  dnsmessage.MustNewName(domain),
 | 
			
		||||
		Type:  dnsmessage.TypeA,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,7 @@ import (
 | 
			
		|||
	"github.com/miekg/dns"
 | 
			
		||||
	"github.com/xtls/xray-core/common"
 | 
			
		||||
	"github.com/xtls/xray-core/common/net"
 | 
			
		||||
	dns_feature "github.com/xtls/xray-core/features/dns"
 | 
			
		||||
	"golang.org/x/net/dns/dnsmessage"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -92,7 +93,7 @@ func Test_buildReqMsgs(t *testing.T) {
 | 
			
		|||
	}
 | 
			
		||||
	type args struct {
 | 
			
		||||
		domain  string
 | 
			
		||||
		option  IPOption
 | 
			
		||||
		option  dns_feature.IPOption
 | 
			
		||||
		reqOpts *dnsmessage.Resource
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -100,10 +101,26 @@ func Test_buildReqMsgs(t *testing.T) {
 | 
			
		|||
		args args
 | 
			
		||||
		want int
 | 
			
		||||
	}{
 | 
			
		||||
		{"dual stack", args{"test.com", IPOption{true, true}, nil}, 2},
 | 
			
		||||
		{"ipv4 only", args{"test.com", IPOption{true, false}, nil}, 1},
 | 
			
		||||
		{"ipv6 only", args{"test.com", IPOption{false, true}, nil}, 1},
 | 
			
		||||
		{"none/error", args{"test.com", IPOption{false, false}, nil}, 0},
 | 
			
		||||
		{"dual stack", args{"test.com", dns_feature.IPOption{
 | 
			
		||||
			IPv4Enable: true,
 | 
			
		||||
			IPv6Enable: true,
 | 
			
		||||
			FakeEnable: false,
 | 
			
		||||
		}, nil}, 2},
 | 
			
		||||
		{"ipv4 only", args{"test.com", dns_feature.IPOption{
 | 
			
		||||
			IPv4Enable: true,
 | 
			
		||||
			IPv6Enable: false,
 | 
			
		||||
			FakeEnable: false,
 | 
			
		||||
		}, nil}, 1},
 | 
			
		||||
		{"ipv6 only", args{"test.com", dns_feature.IPOption{
 | 
			
		||||
			IPv4Enable: false,
 | 
			
		||||
			IPv6Enable: true,
 | 
			
		||||
			FakeEnable: false,
 | 
			
		||||
		}, nil}, 1},
 | 
			
		||||
		{"none/error", args{"test.com", dns_feature.IPOption{
 | 
			
		||||
			IPv4Enable: false,
 | 
			
		||||
			IPv6Enable: false,
 | 
			
		||||
			FakeEnable: false,
 | 
			
		||||
		}, nil}, 0},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -234,7 +234,7 @@ func (s *DoHNameServer) newReqID() uint16 {
 | 
			
		|||
	return uint16(atomic.AddUint32(&s.reqID, 1))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, option 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 {
 | 
			
		||||
| 
						 | 
				
			
			@ -320,7 +320,7 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
 | 
			
		|||
	return ioutil.ReadAll(resp.Body)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *DoHNameServer) findIPsForDomain(domain string, option IPOption) ([]net.IP, error) {
 | 
			
		||||
func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
 | 
			
		||||
	s.RLock()
 | 
			
		||||
	record, found := s.ips[domain]
 | 
			
		||||
	s.RUnlock()
 | 
			
		||||
| 
						 | 
				
			
			@ -363,7 +363,7 @@ func (s *DoHNameServer) findIPsForDomain(domain string, option IPOption) ([]net.
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// QueryIP is called from dns.Server->queryIPTimeout
 | 
			
		||||
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option IPOption) ([]net.IP, error) {
 | 
			
		||||
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, error) { // nolint: dupl
 | 
			
		||||
	fqdn := Fqdn(domain)
 | 
			
		||||
 | 
			
		||||
	ips, err := s.findIPsForDomain(fqdn, option)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
package fakedns
 | 
			
		||||
 | 
			
		||||
import "github.com/xtls/xray-core/common/errors"
 | 
			
		||||
 | 
			
		||||
type errPathObjHolder struct{}
 | 
			
		||||
 | 
			
		||||
func newError(values ...interface{}) *errors.Error {
 | 
			
		||||
	return errors.New(values...).WithPathObj(errPathObjHolder{})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,136 @@
 | 
			
		|||
// +build !confonly
 | 
			
		||||
 | 
			
		||||
package fakedns
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"math"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	gonet "net"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/xtls/xray-core/common"
 | 
			
		||||
	"github.com/xtls/xray-core/common/cache"
 | 
			
		||||
	"github.com/xtls/xray-core/common/net"
 | 
			
		||||
	"github.com/xtls/xray-core/features/dns"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Holder struct {
 | 
			
		||||
	domainToIP cache.Lru
 | 
			
		||||
	ipRange *gonet.IPNet
 | 
			
		||||
 | 
			
		||||
	config *FakeDnsPool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*Holder) Type() interface{} {
 | 
			
		||||
	return (*dns.FakeDNSEngine)(nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (fkdns *Holder) Start() error {
 | 
			
		||||
	return fkdns.initializeFromConfig()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (fkdns *Holder) Close() error {
 | 
			
		||||
	fkdns.domainToIP = nil
 | 
			
		||||
	fkdns.ipRange = nil
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewFakeDNSHolder() (*Holder, error) {
 | 
			
		||||
	var fkdns *Holder
 | 
			
		||||
	var err error
 | 
			
		||||
 | 
			
		||||
	if fkdns, err = NewFakeDNSHolderConfigOnly(nil); err != nil {
 | 
			
		||||
		return nil, newError("Unable to create Fake Dns Engine").Base(err).AtError()
 | 
			
		||||
	}
 | 
			
		||||
	err = fkdns.initialize("240.0.0.0/8", 65535)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return fkdns, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewFakeDNSHolderConfigOnly(conf *FakeDnsPool) (*Holder, error) {
 | 
			
		||||
	return &Holder{nil, nil, conf}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (fkdns *Holder) initializeFromConfig() error {
 | 
			
		||||
	return fkdns.initialize(fkdns.config.IpPool, int(fkdns.config.LruSize))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (fkdns *Holder) initialize(ipPoolCidr string, lruSize int) error {
 | 
			
		||||
	var ipRange *gonet.IPNet
 | 
			
		||||
	var err error
 | 
			
		||||
 | 
			
		||||
	if _, ipRange, err = gonet.ParseCIDR(ipPoolCidr); err != nil {
 | 
			
		||||
		return newError("Unable to parse CIDR for Fake DNS IP assignment").Base(err).AtError()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ones, bits := ipRange.Mask.Size()
 | 
			
		||||
	rooms := bits - ones
 | 
			
		||||
	if math.Log2(float64(lruSize)) >= float64(rooms) {
 | 
			
		||||
		return newError("LRU size is bigger than subnet size").AtError()
 | 
			
		||||
	}
 | 
			
		||||
	fkdns.domainToIP = cache.NewLru(lruSize)
 | 
			
		||||
	fkdns.ipRange = ipRange
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetFakeIPForDomain check and generate a fake IP for a domain name
 | 
			
		||||
func (fkdns *Holder) GetFakeIPForDomain(domain string) []net.Address {
 | 
			
		||||
	if v, ok := fkdns.domainToIP.Get(domain); ok {
 | 
			
		||||
		return []net.Address{v.(net.Address)}
 | 
			
		||||
	}
 | 
			
		||||
	var currentTimeMillis = uint64(time.Now().UnixNano() / 1e6)
 | 
			
		||||
	ones, bits := fkdns.ipRange.Mask.Size()
 | 
			
		||||
	rooms := bits - ones
 | 
			
		||||
	if rooms < 64 {
 | 
			
		||||
		currentTimeMillis %= (uint64(1) << rooms)
 | 
			
		||||
	}
 | 
			
		||||
	var bigIntIP = big.NewInt(0).SetBytes(fkdns.ipRange.IP)
 | 
			
		||||
	bigIntIP = bigIntIP.Add(bigIntIP, new(big.Int).SetUint64(currentTimeMillis))
 | 
			
		||||
	var ip net.Address
 | 
			
		||||
	for {
 | 
			
		||||
		ip = net.IPAddress(bigIntIP.Bytes())
 | 
			
		||||
 | 
			
		||||
		// if we run for a long time, we may go back to beginning and start seeing the IP in use
 | 
			
		||||
		if _, ok := fkdns.domainToIP.PeekKeyFromValue(ip); !ok {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bigIntIP = bigIntIP.Add(bigIntIP, big.NewInt(1))
 | 
			
		||||
		if !fkdns.ipRange.Contains(bigIntIP.Bytes()) {
 | 
			
		||||
			bigIntIP = big.NewInt(0).SetBytes(fkdns.ipRange.IP)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	fkdns.domainToIP.Put(domain, ip)
 | 
			
		||||
	return []net.Address{ip}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDomainFromFakeDNS check if an IP is a fake IP and have corresponding domain name
 | 
			
		||||
func (fkdns *Holder) GetDomainFromFakeDNS(ip net.Address) string {
 | 
			
		||||
	if !ip.Family().IsIP() || !fkdns.ipRange.Contains(ip.IP()) {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	if k, ok := fkdns.domainToIP.GetKeyFromValue(ip); ok {
 | 
			
		||||
		return k.(string)
 | 
			
		||||
	}
 | 
			
		||||
	newError("A fake ip request to ", ip, ", however there is no matching domain name in fake DNS").AtInfo().WriteToLog()
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetFakeIPRange return fake IP range from configuration
 | 
			
		||||
func (fkdns *Holder) GetFakeIPRange() *gonet.IPNet {
 | 
			
		||||
	return fkdns.ipRange
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	common.Must(common.RegisterConfig((*FakeDnsPool)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
 | 
			
		||||
		var f *Holder
 | 
			
		||||
		var err error
 | 
			
		||||
		if f, err = NewFakeDNSHolderConfigOnly(config.(*FakeDnsPool)); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		return f, nil
 | 
			
		||||
	}))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
// +build !confonly
 | 
			
		||||
 | 
			
		||||
package fakedns
 | 
			
		||||
 | 
			
		||||
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,164 @@
 | 
			
		|||
// Code generated by protoc-gen-go. DO NOT EDIT.
 | 
			
		||||
// versions:
 | 
			
		||||
// 	protoc-gen-go v1.25.0
 | 
			
		||||
// 	protoc        v3.13.0
 | 
			
		||||
// source: app/dns/fakedns/fakedns.proto
 | 
			
		||||
 | 
			
		||||
package fakedns
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	proto "github.com/golang/protobuf/proto"
 | 
			
		||||
	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)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// This is a compile-time assertion that a sufficiently up-to-date version
 | 
			
		||||
// of the legacy proto package is being used.
 | 
			
		||||
const _ = proto.ProtoPackageIsVersion4
 | 
			
		||||
 | 
			
		||||
type FakeDnsPool struct {
 | 
			
		||||
	state         protoimpl.MessageState
 | 
			
		||||
	sizeCache     protoimpl.SizeCache
 | 
			
		||||
	unknownFields protoimpl.UnknownFields
 | 
			
		||||
 | 
			
		||||
	IpPool  string `protobuf:"bytes,1,opt,name=ip_pool,json=ipPool,proto3" json:"ip_pool,omitempty"` //CIDR of IP pool used as fake DNS IP
 | 
			
		||||
	LruSize int64  `protobuf:"varint,2,opt,name=lruSize,proto3" json:"lruSize,omitempty"`            //Size of Pool for remembering relationship between domain name and IP address
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *FakeDnsPool) Reset() {
 | 
			
		||||
	*x = FakeDnsPool{}
 | 
			
		||||
	if protoimpl.UnsafeEnabled {
 | 
			
		||||
		mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[0]
 | 
			
		||||
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
			
		||||
		ms.StoreMessageInfo(mi)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *FakeDnsPool) String() string {
 | 
			
		||||
	return protoimpl.X.MessageStringOf(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*FakeDnsPool) ProtoMessage() {}
 | 
			
		||||
 | 
			
		||||
func (x *FakeDnsPool) ProtoReflect() protoreflect.Message {
 | 
			
		||||
	mi := &file_app_dns_fakedns_fakedns_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 FakeDnsPool.ProtoReflect.Descriptor instead.
 | 
			
		||||
func (*FakeDnsPool) Descriptor() ([]byte, []int) {
 | 
			
		||||
	return file_app_dns_fakedns_fakedns_proto_rawDescGZIP(), []int{0}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *FakeDnsPool) GetIpPool() string {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.IpPool
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *FakeDnsPool) GetLruSize() int64 {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.LruSize
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var File_app_dns_fakedns_fakedns_proto protoreflect.FileDescriptor
 | 
			
		||||
 | 
			
		||||
var file_app_dns_fakedns_fakedns_proto_rawDesc = []byte{
 | 
			
		||||
	0x0a, 0x1d, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e,
 | 
			
		||||
	0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
 | 
			
		||||
	0x1a, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e,
 | 
			
		||||
	0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x22, 0x40, 0x0a, 0x0b, 0x46,
 | 
			
		||||
	0x61, 0x6b, 0x65, 0x44, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x70,
 | 
			
		||||
	0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x70, 0x50,
 | 
			
		||||
	0x6f, 0x6f, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x02,
 | 
			
		||||
	0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x5f, 0x0a,
 | 
			
		||||
	0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
 | 
			
		||||
	0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x50,
 | 
			
		||||
	0x01, 0x5a, 0x1e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72,
 | 
			
		||||
	0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e,
 | 
			
		||||
	0x73, 0xaa, 0x02, 0x1a, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41,
 | 
			
		||||
	0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x2e, 0x46, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x62, 0x06,
 | 
			
		||||
	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	file_app_dns_fakedns_fakedns_proto_rawDescOnce sync.Once
 | 
			
		||||
	file_app_dns_fakedns_fakedns_proto_rawDescData = file_app_dns_fakedns_fakedns_proto_rawDesc
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func file_app_dns_fakedns_fakedns_proto_rawDescGZIP() []byte {
 | 
			
		||||
	file_app_dns_fakedns_fakedns_proto_rawDescOnce.Do(func() {
 | 
			
		||||
		file_app_dns_fakedns_fakedns_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_dns_fakedns_fakedns_proto_rawDescData)
 | 
			
		||||
	})
 | 
			
		||||
	return file_app_dns_fakedns_fakedns_proto_rawDescData
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var file_app_dns_fakedns_fakedns_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
 | 
			
		||||
var file_app_dns_fakedns_fakedns_proto_goTypes = []interface{}{
 | 
			
		||||
	(*FakeDnsPool)(nil), // 0: xray.app.dns.fakedns.FakeDnsPool
 | 
			
		||||
}
 | 
			
		||||
var file_app_dns_fakedns_fakedns_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_app_dns_fakedns_fakedns_proto_init() }
 | 
			
		||||
func file_app_dns_fakedns_fakedns_proto_init() {
 | 
			
		||||
	if File_app_dns_fakedns_fakedns_proto != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if !protoimpl.UnsafeEnabled {
 | 
			
		||||
		file_app_dns_fakedns_fakedns_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
 | 
			
		||||
			switch v := v.(*FakeDnsPool); 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_app_dns_fakedns_fakedns_proto_rawDesc,
 | 
			
		||||
			NumEnums:      0,
 | 
			
		||||
			NumMessages:   1,
 | 
			
		||||
			NumExtensions: 0,
 | 
			
		||||
			NumServices:   0,
 | 
			
		||||
		},
 | 
			
		||||
		GoTypes:           file_app_dns_fakedns_fakedns_proto_goTypes,
 | 
			
		||||
		DependencyIndexes: file_app_dns_fakedns_fakedns_proto_depIdxs,
 | 
			
		||||
		MessageInfos:      file_app_dns_fakedns_fakedns_proto_msgTypes,
 | 
			
		||||
	}.Build()
 | 
			
		||||
	File_app_dns_fakedns_fakedns_proto = out.File
 | 
			
		||||
	file_app_dns_fakedns_fakedns_proto_rawDesc = nil
 | 
			
		||||
	file_app_dns_fakedns_fakedns_proto_goTypes = nil
 | 
			
		||||
	file_app_dns_fakedns_fakedns_proto_depIdxs = nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
syntax = "proto3";
 | 
			
		||||
 | 
			
		||||
package xray.app.dns.fakedns;
 | 
			
		||||
option csharp_namespace = "Xray.App.Dns.Fakedns";
 | 
			
		||||
option go_package = "github.com/xtls/xray-core/app/dns/fakedns";
 | 
			
		||||
option java_package = "com.xray.app.dns.fakedns";
 | 
			
		||||
option java_multiple_files = true;
 | 
			
		||||
 | 
			
		||||
message FakeDnsPool{
 | 
			
		||||
  string ip_pool = 1; //CIDR of IP pool used as fake DNS IP
 | 
			
		||||
  int64  lruSize = 2; //Size of Pool for remembering relationship between domain name and IP address
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,99 @@
 | 
			
		|||
package fakedns
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"github.com/xtls/xray-core/common"
 | 
			
		||||
	"github.com/xtls/xray-core/common/net"
 | 
			
		||||
	"github.com/xtls/xray-core/common/uuid"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestNewFakeDnsHolder(_ *testing.T) {
 | 
			
		||||
	_, err := NewFakeDNSHolder()
 | 
			
		||||
	common.Must(err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFakeDnsHolderCreateMapping(t *testing.T) {
 | 
			
		||||
	fkdns, err := NewFakeDNSHolder()
 | 
			
		||||
	common.Must(err)
 | 
			
		||||
 | 
			
		||||
	addr := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org")
 | 
			
		||||
	assert.Equal(t, "240.", addr[0].IP().String()[0:4])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFakeDnsHolderCreateMappingMany(t *testing.T) {
 | 
			
		||||
	fkdns, err := NewFakeDNSHolder()
 | 
			
		||||
	common.Must(err)
 | 
			
		||||
 | 
			
		||||
	addr := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org")
 | 
			
		||||
	assert.Equal(t, "240.", addr[0].IP().String()[0:4])
 | 
			
		||||
 | 
			
		||||
	addr2 := fkdns.GetFakeIPForDomain("fakednstest2.v2fly.org")
 | 
			
		||||
	assert.Equal(t, "240.", addr2[0].IP().String()[0:4])
 | 
			
		||||
	assert.NotEqual(t, addr[0].IP().String(), addr2[0].IP().String())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFakeDnsHolderCreateMappingManyAndResolve(t *testing.T) {
 | 
			
		||||
	fkdns, err := NewFakeDNSHolder()
 | 
			
		||||
	common.Must(err)
 | 
			
		||||
 | 
			
		||||
	addr := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org")
 | 
			
		||||
	addr2 := fkdns.GetFakeIPForDomain("fakednstest2.v2fly.org")
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		result := fkdns.GetDomainFromFakeDNS(addr[0])
 | 
			
		||||
		assert.Equal(t, "fakednstest.v2fly.org", result)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		result := fkdns.GetDomainFromFakeDNS(addr2[0])
 | 
			
		||||
		assert.Equal(t, "fakednstest2.v2fly.org", result)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFakeDnsHolderCreateMappingManySingleDomain(t *testing.T) {
 | 
			
		||||
	fkdns, err := NewFakeDNSHolder()
 | 
			
		||||
	common.Must(err)
 | 
			
		||||
 | 
			
		||||
	addr := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org")
 | 
			
		||||
	addr2 := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org")
 | 
			
		||||
	assert.Equal(t, addr[0].IP().String(), addr2[0].IP().String())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) {
 | 
			
		||||
	fkdns, err := NewFakeDNSHolderConfigOnly(&FakeDnsPool{
 | 
			
		||||
		IpPool:  "240.0.0.0/12",
 | 
			
		||||
		LruSize: 256,
 | 
			
		||||
	})
 | 
			
		||||
	common.Must(err)
 | 
			
		||||
 | 
			
		||||
	err = fkdns.Start()
 | 
			
		||||
 | 
			
		||||
	common.Must(err)
 | 
			
		||||
 | 
			
		||||
	addr := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org")
 | 
			
		||||
	addr2 := fkdns.GetFakeIPForDomain("fakednstest2.v2fly.org")
 | 
			
		||||
 | 
			
		||||
	for i := 0; i <= 8192; i++ {
 | 
			
		||||
		{
 | 
			
		||||
			result := fkdns.GetDomainFromFakeDNS(addr[0])
 | 
			
		||||
			assert.Equal(t, "fakednstest.v2fly.org", result)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			result := fkdns.GetDomainFromFakeDNS(addr2[0])
 | 
			
		||||
			assert.Equal(t, "fakednstest2.v2fly.org", result)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			uuid := uuid.New()
 | 
			
		||||
			domain := uuid.String() + ".fakednstest.v2fly.org"
 | 
			
		||||
			tempAddr := fkdns.GetFakeIPForDomain(domain)
 | 
			
		||||
			rsaddr := tempAddr[0].IP().String()
 | 
			
		||||
 | 
			
		||||
			result := fkdns.GetDomainFromFakeDNS(net.ParseAddress(rsaddr))
 | 
			
		||||
			assert.Equal(t, domain, result)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -5,6 +5,7 @@ import (
 | 
			
		|||
	"github.com/xtls/xray-core/common/net"
 | 
			
		||||
	"github.com/xtls/xray-core/common/strmatcher"
 | 
			
		||||
	"github.com/xtls/xray-core/features"
 | 
			
		||||
	"github.com/xtls/xray-core/features/dns"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// StaticHosts represents static domain-ip mapping in DNS server.
 | 
			
		||||
| 
						 | 
				
			
			@ -92,7 +93,7 @@ func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDoma
 | 
			
		|||
	return sh, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func filterIP(ips []net.Address, option 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) {
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +107,7 @@ func filterIP(ips []net.Address, option IPOption) []net.Address {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// LookupIP returns IP address for the given domain, if exists in this StaticHosts.
 | 
			
		||||
func (h *StaticHosts) LookupIP(domain string, option IPOption) []net.Address {
 | 
			
		||||
func (h *StaticHosts) LookupIP(domain string, option dns.IPOption) []net.Address {
 | 
			
		||||
	indices := h.matchers.Match(domain)
 | 
			
		||||
	if len(indices) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@ 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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestStaticHosts(t *testing.T) {
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +40,7 @@ func TestStaticHosts(t *testing.T) {
 | 
			
		|||
	common.Must(err)
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		ips := hosts.LookupIP("example.com", IPOption{
 | 
			
		||||
		ips := hosts.LookupIP("example.com", dns.IPOption{
 | 
			
		||||
			IPv4Enable: true,
 | 
			
		||||
			IPv6Enable: true,
 | 
			
		||||
		})
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +53,7 @@ func TestStaticHosts(t *testing.T) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		ips := hosts.LookupIP("www.example.cn", IPOption{
 | 
			
		||||
		ips := hosts.LookupIP("www.example.cn", dns.IPOption{
 | 
			
		||||
			IPv4Enable: true,
 | 
			
		||||
			IPv6Enable: true,
 | 
			
		||||
		})
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +66,7 @@ func TestStaticHosts(t *testing.T) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		ips := hosts.LookupIP("baidu.com", IPOption{
 | 
			
		||||
		ips := hosts.LookupIP("baidu.com", dns.IPOption{
 | 
			
		||||
			IPv4Enable: false,
 | 
			
		||||
			IPv6Enable: true,
 | 
			
		||||
		})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,39 +4,26 @@ import (
 | 
			
		|||
	"context"
 | 
			
		||||
 | 
			
		||||
	"github.com/xtls/xray-core/common/net"
 | 
			
		||||
	"github.com/xtls/xray-core/features/dns"
 | 
			
		||||
	"github.com/xtls/xray-core/features/dns/localdns"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// IPOption is an object for IP query options.
 | 
			
		||||
type IPOption struct {
 | 
			
		||||
	IPv4Enable bool
 | 
			
		||||
	IPv6Enable bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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, option IPOption) ([]net.IP, error)
 | 
			
		||||
	QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type LocalNameServer struct {
 | 
			
		||||
	client *localdns.Client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, option IPOption) ([]net.IP, error) {
 | 
			
		||||
	if option.IPv4Enable && option.IPv6Enable {
 | 
			
		||||
		return s.client.LookupIP(domain)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if option.IPv4Enable {
 | 
			
		||||
		return s.client.LookupIPv4(domain)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if option.IPv6Enable {
 | 
			
		||||
		return s.client.LookupIPv6(domain)
 | 
			
		||||
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)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, newError("neither IPv4 nor IPv6 is enabled")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
// +build !confonly
 | 
			
		||||
 | 
			
		||||
package dns
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	"github.com/xtls/xray-core/core"
 | 
			
		||||
	"github.com/xtls/xray-core/common/net"
 | 
			
		||||
	"github.com/xtls/xray-core/features/dns"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type FakeDNSServer struct {
 | 
			
		||||
	fakeDNSEngine dns.FakeDNSEngine
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewFakeDNSServer() *FakeDNSServer {
 | 
			
		||||
	return &FakeDNSServer{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (FakeDNSServer) Name() string {
 | 
			
		||||
	return "FakeDNS"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
		}); err != nil {
 | 
			
		||||
			return nil, newError("Unable to locate a fake DNS Engine").Base(err).AtError()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ips := f.fakeDNSEngine.GetFakeIPForDomain(domain)
 | 
			
		||||
 | 
			
		||||
	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()
 | 
			
		||||
 | 
			
		||||
	return netIP, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -7,14 +7,16 @@ import (
 | 
			
		|||
 | 
			
		||||
	. "github.com/xtls/xray-core/app/dns"
 | 
			
		||||
	"github.com/xtls/xray-core/common"
 | 
			
		||||
	dns_feature "github.com/xtls/xray-core/features/dns"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestLocalNameServer(t *testing.T) {
 | 
			
		||||
	s := NewLocalNameServer()
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
 | 
			
		||||
	ips, err := s.QueryIP(ctx, "google.com", IPOption{
 | 
			
		||||
	ips, err := s.QueryIP(ctx, "google.com", dns_feature.IPOption{
 | 
			
		||||
		IPv4Enable: true,
 | 
			
		||||
		IPv6Enable: true,
 | 
			
		||||
		FakeEnable: false,
 | 
			
		||||
	})
 | 
			
		||||
	cancel()
 | 
			
		||||
	common.Must(err)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,7 @@ type Server struct {
 | 
			
		|||
	hosts         *StaticHosts
 | 
			
		||||
	clientIP      net.IP
 | 
			
		||||
	clients       []Client             // clientIdx -> Client
 | 
			
		||||
	ctx           context.Context
 | 
			
		||||
	ipIndexMap    []*MultiGeoIPMatcher // clientIdx -> *MultiGeoIPMatcher
 | 
			
		||||
	domainRules   [][]string           // clientIdx -> domainRuleIdx -> DomainRule
 | 
			
		||||
	domainMatcher strmatcher.IndexMatcher
 | 
			
		||||
| 
						 | 
				
			
			@ -75,6 +76,7 @@ func generateRandomTag() string {
 | 
			
		|||
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 == "" {
 | 
			
		||||
| 
						 | 
				
			
			@ -144,6 +146,9 @@ func New(ctx context.Context, config *Config) (*Server, error) {
 | 
			
		|||
				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()
 | 
			
		||||
| 
						 | 
				
			
			@ -295,8 +300,8 @@ func (s *Server) Match(idx int, client Client, domain string, ips []net.IP) ([]n
 | 
			
		|||
	return newIps, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Server) queryIPTimeout(idx int, client Client, domain string, option IPOption) ([]net.IP, error) {
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*4)
 | 
			
		||||
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,
 | 
			
		||||
| 
						 | 
				
			
			@ -314,31 +319,7 @@ func (s *Server) queryIPTimeout(idx int, client Client, domain string, option IP
 | 
			
		|||
	return ips, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LookupIP implements dns.Client.
 | 
			
		||||
func (s *Server) LookupIP(domain string) ([]net.IP, error) {
 | 
			
		||||
	return s.lookupIPInternal(domain, IPOption{
 | 
			
		||||
		IPv4Enable: true,
 | 
			
		||||
		IPv6Enable: true,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LookupIPv4 implements dns.IPv4Lookup.
 | 
			
		||||
func (s *Server) LookupIPv4(domain string) ([]net.IP, error) {
 | 
			
		||||
	return s.lookupIPInternal(domain, IPOption{
 | 
			
		||||
		IPv4Enable: true,
 | 
			
		||||
		IPv6Enable: false,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LookupIPv6 implements dns.IPv6Lookup.
 | 
			
		||||
func (s *Server) LookupIPv6(domain string) ([]net.IP, error) {
 | 
			
		||||
	return s.lookupIPInternal(domain, IPOption{
 | 
			
		||||
		IPv4Enable: false,
 | 
			
		||||
		IPv6Enable: true,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Server) lookupStatic(domain string, option IPOption, depth int32) []net.Address {
 | 
			
		||||
func (s *Server) lookupStatic(domain string, option dns.IPOption, depth int32) []net.Address {
 | 
			
		||||
	ips := s.hosts.LookupIP(domain, option)
 | 
			
		||||
	if ips == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
| 
						 | 
				
			
			@ -362,14 +343,15 @@ func toNetIP(ips []net.Address) []net.IP {
 | 
			
		|||
	return netips
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Server) lookupIPInternal(domain string, option IPOption) ([]net.IP, error) {
 | 
			
		||||
// LookupIP implements dns.Client.
 | 
			
		||||
func (s *Server) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) {
 | 
			
		||||
	if domain == "" {
 | 
			
		||||
		return nil, newError("empty domain name")
 | 
			
		||||
	}
 | 
			
		||||
	domain = strings.ToLower(domain)
 | 
			
		||||
 | 
			
		||||
	// normalize the FQDN form query
 | 
			
		||||
	if domain[len(domain)-1] == '.' {
 | 
			
		||||
	if strings.HasSuffix(domain, ".") {
 | 
			
		||||
		domain = domain[:len(domain)-1]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -406,6 +388,10 @@ func (s *Server) lookupIPInternal(domain string, option IPOption) ([]net.IP, err
 | 
			
		|||
		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
 | 
			
		||||
| 
						 | 
				
			
			@ -425,7 +411,10 @@ func (s *Server) lookupIPInternal(domain string, option IPOption) ([]net.IP, err
 | 
			
		|||
			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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,10 +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)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -456,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)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -563,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)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -682,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)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -693,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)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -704,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)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -715,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)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -726,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)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -737,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)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -748,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)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -759,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)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -921,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)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -932,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)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -944,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)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -955,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)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,7 +54,7 @@ func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher
 | 
			
		|||
		Execute:  s.Cleanup,
 | 
			
		||||
	}
 | 
			
		||||
	s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse)
 | 
			
		||||
	newError("DNS: created udp client inited for ", address.NetAddr()).AtInfo().WriteToLog()
 | 
			
		||||
	newError("DNS: created UDP client initialized for ", address.NetAddr()).AtInfo().WriteToLog()
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -180,7 +180,7 @@ func (s *ClassicNameServer) addPendingRequest(req *dnsRequest) {
 | 
			
		|||
	s.requests[id] = *req
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, option 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(s.clientIP))
 | 
			
		||||
| 
						 | 
				
			
			@ -206,7 +206,7 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, option
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *ClassicNameServer) findIPsForDomain(domain string, option IPOption) ([]net.IP, error) {
 | 
			
		||||
func (s *ClassicNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
 | 
			
		||||
	s.RLock()
 | 
			
		||||
	record, found := s.ips[domain]
 | 
			
		||||
	s.RUnlock()
 | 
			
		||||
| 
						 | 
				
			
			@ -244,7 +244,8 @@ func (s *ClassicNameServer) findIPsForDomain(domain string, option IPOption) ([]
 | 
			
		|||
	return nil, dns_feature.ErrEmptyResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option IPOption) ([]net.IP, error) {
 | 
			
		||||
// QueryIP implements Server.
 | 
			
		||||
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, error) {
 | 
			
		||||
	fqdn := Fqdn(domain)
 | 
			
		||||
 | 
			
		||||
	ips, err := s.findIPsForDomain(fqdn, option)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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        (unknown)
 | 
			
		||||
// source: app/proxyman/config.proto
 | 
			
		||||
 | 
			
		||||
package proxyman
 | 
			
		||||
| 
						 | 
				
			
			@ -239,9 +239,12 @@ type SniffingConfig struct {
 | 
			
		|||
	// Whether or not to enable content sniffing on an inbound connection.
 | 
			
		||||
	Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
 | 
			
		||||
	// Override target destination if sniff'ed protocol is in the given list.
 | 
			
		||||
	// Supported values are "http", "tls".
 | 
			
		||||
	// Supported values are "http", "tls", "fakedns".
 | 
			
		||||
	DestinationOverride []string `protobuf:"bytes,2,rep,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"`
 | 
			
		||||
	DomainsExcluded     []string `protobuf:"bytes,3,rep,name=domains_excluded,json=domainsExcluded,proto3" json:"domains_excluded,omitempty"`
 | 
			
		||||
	// Whether should only try to sniff metadata without waiting for client input.
 | 
			
		||||
	// Can be used to support SMTP like protocol where server send the first message.
 | 
			
		||||
	MetadataOnly bool `protobuf:"varint,4,opt,name=metadata_only,json=metadataOnly,proto3" json:"metadata_only,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *SniffingConfig) Reset() {
 | 
			
		||||
| 
						 | 
				
			
			@ -297,6 +300,13 @@ func (x *SniffingConfig) GetDomainsExcluded() []string {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (x *SniffingConfig) GetMetadataOnly() bool {
 | 
			
		||||
	if x != nil {
 | 
			
		||||
		return x.MetadataOnly
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ReceiverConfig struct {
 | 
			
		||||
	state         protoimpl.MessageState
 | 
			
		||||
	sizeCache     protoimpl.SizeCache
 | 
			
		||||
| 
						 | 
				
			
			@ -764,7 +774,7 @@ var file_app_proxyman_config_proto_rawDesc = []byte{
 | 
			
		|||
	0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2c, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
 | 
			
		||||
	0x0a, 0x0a, 0x06, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x52,
 | 
			
		||||
	0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x78, 0x74, 0x65, 0x72,
 | 
			
		||||
	0x6e, 0x61, 0x6c, 0x10, 0x02, 0x22, 0x88, 0x01, 0x0a, 0x0e, 0x53, 0x6e, 0x69, 0x66, 0x66, 0x69,
 | 
			
		||||
	0x6e, 0x61, 0x6c, 0x10, 0x02, 0x22, 0xad, 0x01, 0x0a, 0x0e, 0x53, 0x6e, 0x69, 0x66, 0x66, 0x69,
 | 
			
		||||
	0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62,
 | 
			
		||||
	0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
 | 
			
		||||
	0x65, 0x64, 0x12, 0x31, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f,
 | 
			
		||||
| 
						 | 
				
			
			@ -773,86 +783,88 @@ var file_app_proxyman_config_proto_rawDesc = []byte{
 | 
			
		|||
	0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73,
 | 
			
		||||
	0x5f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52,
 | 
			
		||||
	0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64,
 | 
			
		||||
	0x22, 0x90, 0x04, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e,
 | 
			
		||||
	0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67,
 | 
			
		||||
	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63,
 | 
			
		||||
	0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x61,
 | 
			
		||||
	0x6e, 0x67, 0x65, 0x52, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x33,
 | 
			
		||||
	0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b,
 | 
			
		||||
	0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74,
 | 
			
		||||
	0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x6c, 0x69, 0x73,
 | 
			
		||||
	0x74, 0x65, 0x6e, 0x12, 0x56, 0x0a, 0x13, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f,
 | 
			
		||||
	0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
 | 
			
		||||
	0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78,
 | 
			
		||||
	0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53,
 | 
			
		||||
	0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74,
 | 
			
		||||
	0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x4e, 0x0a, 0x0f, 0x73,
 | 
			
		||||
	0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04,
 | 
			
		||||
	0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
 | 
			
		||||
	0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53,
 | 
			
		||||
	0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x74, 0x72,
 | 
			
		||||
	0x65, 0x61, 0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x72,
 | 
			
		||||
	0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f,
 | 
			
		||||
	0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28,
 | 
			
		||||
	0x08, 0x52, 0x1a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e,
 | 
			
		||||
	0x61, 0x6c, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a,
 | 
			
		||||
	0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65,
 | 
			
		||||
	0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
 | 
			
		||||
	0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e,
 | 
			
		||||
	0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x64,
 | 
			
		||||
	0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x4e, 0x0a,
 | 
			
		||||
	0x11, 0x73, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e,
 | 
			
		||||
	0x67, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
 | 
			
		||||
	0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x53, 0x6e, 0x69,
 | 
			
		||||
	0x66, 0x66, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x10, 0x73, 0x6e, 0x69,
 | 
			
		||||
	0x66, 0x66, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x4a, 0x04, 0x08,
 | 
			
		||||
	0x06, 0x10, 0x07, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48,
 | 
			
		||||
	0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03,
 | 
			
		||||
	0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x4d,
 | 
			
		||||
	0x0a, 0x11, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69,
 | 
			
		||||
	0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79,
 | 
			
		||||
	0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54,
 | 
			
		||||
	0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x10, 0x72, 0x65, 0x63,
 | 
			
		||||
	0x65, 0x69, 0x76, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x47, 0x0a,
 | 
			
		||||
	0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6f, 0x6e, 0x6c,
 | 
			
		||||
	0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
 | 
			
		||||
	0x61, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x90, 0x04, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76,
 | 
			
		||||
	0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x0a, 0x70, 0x6f, 0x72, 0x74,
 | 
			
		||||
	0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78,
 | 
			
		||||
	0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50,
 | 
			
		||||
	0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61,
 | 
			
		||||
	0x6e, 0x67, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x02, 0x20,
 | 
			
		||||
	0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
 | 
			
		||||
	0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
 | 
			
		||||
	0x52, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x12, 0x56, 0x0a, 0x13, 0x61, 0x6c, 0x6c, 0x6f,
 | 
			
		||||
	0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18,
 | 
			
		||||
	0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
 | 
			
		||||
	0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61,
 | 
			
		||||
	0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x12, 0x61, 0x6c,
 | 
			
		||||
	0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
 | 
			
		||||
	0x12, 0x4e, 0x0a, 0x0f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69,
 | 
			
		||||
	0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79,
 | 
			
		||||
	0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72,
 | 
			
		||||
	0x6e, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
 | 
			
		||||
	0x52, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
 | 
			
		||||
	0x12, 0x40, 0x0a, 0x1c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x72, 0x69, 0x67,
 | 
			
		||||
	0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e,
 | 
			
		||||
	0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x4f,
 | 
			
		||||
	0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69,
 | 
			
		||||
	0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x6f, 0x76, 0x65,
 | 
			
		||||
	0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x78, 0x72,
 | 
			
		||||
	0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e,
 | 
			
		||||
	0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x42, 0x02,
 | 
			
		||||
	0x18, 0x01, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69,
 | 
			
		||||
	0x64, 0x65, 0x12, 0x4e, 0x0a, 0x11, 0x73, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x5f, 0x73,
 | 
			
		||||
	0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e,
 | 
			
		||||
	0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61,
 | 
			
		||||
	0x6e, 0x2e, 0x53, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
 | 
			
		||||
	0x52, 0x10, 0x73, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
 | 
			
		||||
	0x67, 0x73, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x49, 0x6e, 0x62,
 | 
			
		||||
	0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69,
 | 
			
		||||
	0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
 | 
			
		||||
	0x74, 0x61, 0x67, 0x12, 0x4d, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f,
 | 
			
		||||
	0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20,
 | 
			
		||||
	0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72,
 | 
			
		||||
	0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
 | 
			
		||||
	0x52, 0x10, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
 | 
			
		||||
	0x67, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74,
 | 
			
		||||
	0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61,
 | 
			
		||||
	0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e,
 | 
			
		||||
	0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72,
 | 
			
		||||
	0x6f, 0x78, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x4f,
 | 
			
		||||
	0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xb0, 0x02,
 | 
			
		||||
	0x0a, 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d,
 | 
			
		||||
	0x0a, 0x03, 0x76, 0x69, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72,
 | 
			
		||||
	0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50,
 | 
			
		||||
	0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x03, 0x76, 0x69, 0x61, 0x12, 0x4e, 0x0a,
 | 
			
		||||
	0x0f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
 | 
			
		||||
	0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72,
 | 
			
		||||
	0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
 | 
			
		||||
	0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73,
 | 
			
		||||
	0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4b, 0x0a,
 | 
			
		||||
	0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18,
 | 
			
		||||
	0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
 | 
			
		||||
	0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64,
 | 
			
		||||
	0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65,
 | 
			
		||||
	0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75,
 | 
			
		||||
	0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xb0, 0x02, 0x0a, 0x0c, 0x53, 0x65, 0x6e,
 | 
			
		||||
	0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x0a, 0x03, 0x76, 0x69, 0x61,
 | 
			
		||||
	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
 | 
			
		||||
	0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d,
 | 
			
		||||
	0x61, 0x69, 0x6e, 0x52, 0x03, 0x76, 0x69, 0x61, 0x12, 0x4e, 0x0a, 0x0f, 0x73, 0x74, 0x72, 0x65,
 | 
			
		||||
	0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
 | 
			
		||||
	0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
 | 
			
		||||
	0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x72, 0x65,
 | 
			
		||||
	0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d,
 | 
			
		||||
	0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78,
 | 
			
		||||
	0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
 | 
			
		||||
	0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
 | 
			
		||||
	0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79,
 | 
			
		||||
	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65, 0x74,
 | 
			
		||||
	0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c,
 | 
			
		||||
	0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
 | 
			
		||||
	0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f,
 | 
			
		||||
	0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69,
 | 
			
		||||
	0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70,
 | 
			
		||||
	0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x50, 0x0a, 0x12, 0x4d,
 | 
			
		||||
	0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69,
 | 
			
		||||
	0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01,
 | 
			
		||||
	0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63,
 | 
			
		||||
	0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
 | 
			
		||||
	0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x2a, 0x23, 0x0a,
 | 
			
		||||
	0x0e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x12,
 | 
			
		||||
	0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x4c, 0x53,
 | 
			
		||||
	0x10, 0x01, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
 | 
			
		||||
	0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50, 0x01, 0x5a, 0x26, 0x67,
 | 
			
		||||
	0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78,
 | 
			
		||||
	0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f,
 | 
			
		||||
	0x78, 0x79, 0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70,
 | 
			
		||||
	0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 | 
			
		||||
	0x33,
 | 
			
		||||
	0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
 | 
			
		||||
	0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
 | 
			
		||||
	0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x70, 0x72, 0x6f,
 | 
			
		||||
	0x78, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x6d, 0x75,
 | 
			
		||||
	0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
 | 
			
		||||
	0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
 | 
			
		||||
	0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69,
 | 
			
		||||
	0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d,
 | 
			
		||||
	0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
 | 
			
		||||
	0x22, 0x50, 0x0a, 0x12, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67,
 | 
			
		||||
	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
 | 
			
		||||
	0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
 | 
			
		||||
	0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18,
 | 
			
		||||
	0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
 | 
			
		||||
	0x63, 0x79, 0x2a, 0x23, 0x0a, 0x0e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f,
 | 
			
		||||
	0x63, 0x6f, 0x6c, 0x73, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x07,
 | 
			
		||||
	0x0a, 0x03, 0x54, 0x4c, 0x53, 0x10, 0x01, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
 | 
			
		||||
	0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e,
 | 
			
		||||
	0x50, 0x01, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78,
 | 
			
		||||
	0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70,
 | 
			
		||||
	0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61,
 | 
			
		||||
	0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06,
 | 
			
		||||
	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,9 +54,13 @@ message SniffingConfig {
 | 
			
		|||
  bool enabled = 1;
 | 
			
		||||
 | 
			
		||||
  // Override target destination if sniff'ed protocol is in the given list.
 | 
			
		||||
  // Supported values are "http", "tls".
 | 
			
		||||
  // Supported values are "http", "tls", "fakedns".
 | 
			
		||||
  repeated string destination_override = 2;
 | 
			
		||||
  repeated string domains_excluded = 3;
 | 
			
		||||
  
 | 
			
		||||
  // Whether should only try to sniff metadata without waiting for client input.
 | 
			
		||||
  // Can be used to support SMTP like protocol where server send the first message.
 | 
			
		||||
  bool metadata_only = 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ReceiverConfig {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -133,6 +133,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
 | 
			
		|||
					address:         address,
 | 
			
		||||
					port:            net.Port(port),
 | 
			
		||||
					dispatcher:      h.mux,
 | 
			
		||||
					sniffingConfig:  receiverConfig.GetEffectiveSniffingSettings(),
 | 
			
		||||
					uplinkCounter:   uplinkCounter,
 | 
			
		||||
					downlinkCounter: downlinkCounter,
 | 
			
		||||
					stream:          mss,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -153,6 +153,7 @@ func (h *DynamicInboundHandler) refresh() error {
 | 
			
		|||
				address:         address,
 | 
			
		||||
				port:            port,
 | 
			
		||||
				dispatcher:      h.mux,
 | 
			
		||||
				sniffingConfig:  h.receiverConfig.GetEffectiveSniffingSettings(),
 | 
			
		||||
				uplinkCounter:   uplinkCounter,
 | 
			
		||||
				downlinkCounter: downlinkCounter,
 | 
			
		||||
				stream:          h.streamSettings,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -98,6 +98,7 @@ func (w *tcpWorker) callback(conn internet.Connection) {
 | 
			
		|||
		content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
 | 
			
		||||
		content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
 | 
			
		||||
		content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
 | 
			
		||||
		content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
 | 
			
		||||
	}
 | 
			
		||||
	ctx = session.ContextWithContent(ctx, content)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -235,6 +236,7 @@ type udpWorker struct {
 | 
			
		|||
	tag             string
 | 
			
		||||
	stream          *internet.MemoryStreamConfig
 | 
			
		||||
	dispatcher      routing.Dispatcher
 | 
			
		||||
	sniffingConfig  *proxyman.SniffingConfig
 | 
			
		||||
	uplinkCounter   stats.Counter
 | 
			
		||||
	downlinkCounter stats.Counter
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -297,7 +299,7 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
 | 
			
		|||
		common.Must(w.checker.Start())
 | 
			
		||||
 | 
			
		||||
		go func() {
 | 
			
		||||
			ctx := context.Background()
 | 
			
		||||
			ctx := w.ctx
 | 
			
		||||
			sid := session.NewID()
 | 
			
		||||
			ctx = session.ContextWithID(ctx, sid)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -311,6 +313,13 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
 | 
			
		|||
				Gateway: net.UDPDestination(w.address, w.port),
 | 
			
		||||
				Tag:     w.tag,
 | 
			
		||||
			})
 | 
			
		||||
			content := new(session.Content)
 | 
			
		||||
			if w.sniffingConfig != nil {
 | 
			
		||||
				content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
 | 
			
		||||
				content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
 | 
			
		||||
				content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
 | 
			
		||||
			}
 | 
			
		||||
			ctx = session.ContextWithContent(ctx, content)
 | 
			
		||||
			if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher); err != nil {
 | 
			
		||||
				newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx))
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -451,6 +460,7 @@ func (w *dsWorker) callback(conn internet.Connection) {
 | 
			
		|||
		content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
 | 
			
		||||
		content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
 | 
			
		||||
		content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
 | 
			
		||||
		content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
 | 
			
		||||
	}
 | 
			
		||||
	ctx = session.ContextWithContent(ctx, content)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,85 @@
 | 
			
		|||
package cache
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"container/list"
 | 
			
		||||
	sync "sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Lru simple, fast lru cache implementation
 | 
			
		||||
type Lru interface {
 | 
			
		||||
	Get(key interface{}) (value interface{}, ok bool)
 | 
			
		||||
	GetKeyFromValue(value interface{}) (key interface{}, ok bool)
 | 
			
		||||
	PeekKeyFromValue(value interface{}) (key interface{}, ok bool) // Peek means check but NOT bring to top
 | 
			
		||||
	Put(key, value interface{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type lru struct {
 | 
			
		||||
	capacity         int
 | 
			
		||||
	doubleLinkedlist *list.List
 | 
			
		||||
	keyToElement     *sync.Map
 | 
			
		||||
	valueToElement   *sync.Map
 | 
			
		||||
	mu               *sync.Mutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type lruElement struct {
 | 
			
		||||
	key   interface{}
 | 
			
		||||
	value interface{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewLru init a lru cache
 | 
			
		||||
func NewLru(cap int) Lru {
 | 
			
		||||
	return lru{
 | 
			
		||||
		capacity:         cap,
 | 
			
		||||
		doubleLinkedlist: list.New(),
 | 
			
		||||
		keyToElement:     new(sync.Map),
 | 
			
		||||
		valueToElement:   new(sync.Map),
 | 
			
		||||
		mu:               new(sync.Mutex),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l lru) Get(key interface{}) (value interface{}, ok bool) {
 | 
			
		||||
	if v, ok := l.keyToElement.Load(key); ok {
 | 
			
		||||
		element := v.(*list.Element)
 | 
			
		||||
		l.doubleLinkedlist.MoveBefore(element, l.doubleLinkedlist.Front())
 | 
			
		||||
		return element.Value.(lruElement).value, true
 | 
			
		||||
	}
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l lru) GetKeyFromValue(value interface{}) (key interface{}, ok bool) {
 | 
			
		||||
	if k, ok := l.valueToElement.Load(value); ok {
 | 
			
		||||
		element := k.(*list.Element)
 | 
			
		||||
		l.doubleLinkedlist.MoveBefore(element, l.doubleLinkedlist.Front())
 | 
			
		||||
		return element.Value.(lruElement).key, true
 | 
			
		||||
	}
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l lru) PeekKeyFromValue(value interface{}) (key interface{}, ok bool) {
 | 
			
		||||
	if k, ok := l.valueToElement.Load(value); ok {
 | 
			
		||||
		element := k.(*list.Element)
 | 
			
		||||
		return element.Value.(lruElement).key, true
 | 
			
		||||
	}
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l lru) Put(key, value interface{}) {
 | 
			
		||||
	e := lruElement{key, value}
 | 
			
		||||
	if v, ok := l.keyToElement.Load(key); ok {
 | 
			
		||||
		element := v.(*list.Element)
 | 
			
		||||
		element.Value = e
 | 
			
		||||
		l.doubleLinkedlist.MoveBefore(element, l.doubleLinkedlist.Front())
 | 
			
		||||
	} else {
 | 
			
		||||
		l.mu.Lock()
 | 
			
		||||
		element := l.doubleLinkedlist.PushFront(e)
 | 
			
		||||
		l.keyToElement.Store(key, element)
 | 
			
		||||
		l.valueToElement.Store(value, element)
 | 
			
		||||
		if l.doubleLinkedlist.Len() > l.capacity {
 | 
			
		||||
			toBeRemove := l.doubleLinkedlist.Back()
 | 
			
		||||
			l.doubleLinkedlist.Remove(toBeRemove)
 | 
			
		||||
			l.keyToElement.Delete(toBeRemove.Value.(lruElement).key)
 | 
			
		||||
			l.valueToElement.Delete(toBeRemove.Value.(lruElement).value)
 | 
			
		||||
		}
 | 
			
		||||
		l.mu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,86 @@
 | 
			
		|||
package cache_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	. "github.com/xtls/xray-core/common/cache"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestLruReplaceValue(t *testing.T) {
 | 
			
		||||
	lru := NewLru(2)
 | 
			
		||||
	lru.Put(2, 6)
 | 
			
		||||
	lru.Put(1, 5)
 | 
			
		||||
	lru.Put(1, 2)
 | 
			
		||||
	v, _ := lru.Get(1)
 | 
			
		||||
	if v != 2 {
 | 
			
		||||
		t.Error("should get 2", v)
 | 
			
		||||
	}
 | 
			
		||||
	v, _ = lru.Get(2)
 | 
			
		||||
	if v != 6 {
 | 
			
		||||
		t.Error("should get 6", v)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestLruRemoveOld(t *testing.T) {
 | 
			
		||||
	lru := NewLru(2)
 | 
			
		||||
	v, ok := lru.Get(2)
 | 
			
		||||
	if ok {
 | 
			
		||||
		t.Error("should get nil", v)
 | 
			
		||||
	}
 | 
			
		||||
	lru.Put(1, 1)
 | 
			
		||||
	lru.Put(2, 2)
 | 
			
		||||
	v, _ = lru.Get(1)
 | 
			
		||||
	if v != 1 {
 | 
			
		||||
		t.Error("should get 1", v)
 | 
			
		||||
	}
 | 
			
		||||
	lru.Put(3, 3)
 | 
			
		||||
	v, ok = lru.Get(2)
 | 
			
		||||
	if ok {
 | 
			
		||||
		t.Error("should get nil", v)
 | 
			
		||||
	}
 | 
			
		||||
	lru.Put(4, 4)
 | 
			
		||||
	v, ok = lru.Get(1)
 | 
			
		||||
	if ok {
 | 
			
		||||
		t.Error("should get nil", v)
 | 
			
		||||
	}
 | 
			
		||||
	v, _ = lru.Get(3)
 | 
			
		||||
	if v != 3 {
 | 
			
		||||
		t.Error("should get 3", v)
 | 
			
		||||
	}
 | 
			
		||||
	v, _ = lru.Get(4)
 | 
			
		||||
	if v != 4 {
 | 
			
		||||
		t.Error("should get 4", v)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetKeyFromValue(t *testing.T) {
 | 
			
		||||
	lru := NewLru(2)
 | 
			
		||||
	lru.Put(3, 3)
 | 
			
		||||
	lru.Put(2, 2)
 | 
			
		||||
	lru.GetKeyFromValue(3)
 | 
			
		||||
	lru.Put(1, 1)
 | 
			
		||||
	v, ok := lru.GetKeyFromValue(2)
 | 
			
		||||
	if ok {
 | 
			
		||||
		t.Error("should get nil", v)
 | 
			
		||||
	}
 | 
			
		||||
	v, _ = lru.GetKeyFromValue(3)
 | 
			
		||||
	if v != 3 {
 | 
			
		||||
		t.Error("should get 3", v)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPeekKeyFromValue(t *testing.T) {
 | 
			
		||||
	lru := NewLru(2)
 | 
			
		||||
	lru.Put(3, 3)
 | 
			
		||||
	lru.Put(2, 2)
 | 
			
		||||
	lru.PeekKeyFromValue(3)
 | 
			
		||||
	lru.Put(1, 1)
 | 
			
		||||
	v, ok := lru.PeekKeyFromValue(3)
 | 
			
		||||
	if ok {
 | 
			
		||||
		t.Error("should get nil", v)
 | 
			
		||||
	}
 | 
			
		||||
	v, _ = lru.PeekKeyFromValue(2)
 | 
			
		||||
	if v != 2 {
 | 
			
		||||
		t.Error("should get 2", v)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -63,6 +63,7 @@ type SniffingRequest struct {
 | 
			
		|||
	ExcludeForDomain               []string
 | 
			
		||||
	OverrideDestinationForProtocol []string
 | 
			
		||||
	Enabled                        bool
 | 
			
		||||
	MetadataOnly                   bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Content is the metadata of the connection content.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,13 @@ import (
 | 
			
		|||
	"github.com/xtls/xray-core/features"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// IPOption is an object for IP query options.
 | 
			
		||||
type IPOption struct {
 | 
			
		||||
	IPv4Enable bool
 | 
			
		||||
	IPv6Enable bool
 | 
			
		||||
	FakeEnable bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Client is a Xray feature for querying DNS information.
 | 
			
		||||
//
 | 
			
		||||
// xray:api:stable
 | 
			
		||||
| 
						 | 
				
			
			@ -14,21 +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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
package dns
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	gonet "net"
 | 
			
		||||
 | 
			
		||||
	"github.com/xtls/xray-core/common/net"
 | 
			
		||||
	"github.com/xtls/xray-core/features"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type FakeDNSEngine interface {
 | 
			
		||||
	features.Feature
 | 
			
		||||
	GetFakeIPForDomain(domain string) []net.Address
 | 
			
		||||
	GetDomainFromFakeDNS(ip net.Address) string
 | 
			
		||||
	GetFakeIPRange() *gonet.IPNet
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -20,58 +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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
package localdns
 | 
			
		||||
 | 
			
		||||
import "github.com/xtls/xray-core/common/errors"
 | 
			
		||||
 | 
			
		||||
type errPathObjHolder struct{}
 | 
			
		||||
 | 
			
		||||
func newError(values ...interface{}) *errors.Error {
 | 
			
		||||
	return errors.New(values...).WithPathObj(errPathObjHolder{})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +26,11 @@ 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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,65 @@
 | 
			
		|||
package conf
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
	"github.com/xtls/xray-core/app/dns/fakedns"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type FakeDNSConfig struct {
 | 
			
		||||
	IPPool  string `json:"ipPool"`
 | 
			
		||||
	LruSize int64  `json:"poolSize"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f FakeDNSConfig) Build() (proto.Message, error) {
 | 
			
		||||
	return &fakedns.FakeDnsPool{
 | 
			
		||||
		IpPool:  f.IPPool,
 | 
			
		||||
		LruSize: f.LruSize,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FakeDNSPostProcessingStage struct{}
 | 
			
		||||
 | 
			
		||||
func (FakeDNSPostProcessingStage) Process(conf *Config) error {
 | 
			
		||||
	var fakeDNSInUse bool
 | 
			
		||||
 | 
			
		||||
	if conf.DNSConfig != nil {
 | 
			
		||||
		for _, v := range conf.DNSConfig.Servers {
 | 
			
		||||
			if v.Address.Family().IsDomain() {
 | 
			
		||||
				if v.Address.Domain() == "fakedns" {
 | 
			
		||||
					fakeDNSInUse = true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if fakeDNSInUse {
 | 
			
		||||
		if conf.FakeDNS == nil {
 | 
			
		||||
			// Add a Fake DNS Config if there is none
 | 
			
		||||
			conf.FakeDNS = &FakeDNSConfig{
 | 
			
		||||
				IPPool:  "240.0.0.0/8",
 | 
			
		||||
				LruSize: 65535,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		found := false
 | 
			
		||||
		// Check if there is a Outbound with necessary sniffer on
 | 
			
		||||
		var inbounds []InboundDetourConfig
 | 
			
		||||
 | 
			
		||||
		if len(conf.InboundConfigs) > 0 {
 | 
			
		||||
			inbounds = append(inbounds, conf.InboundConfigs...)
 | 
			
		||||
		}
 | 
			
		||||
		for _, v := range inbounds {
 | 
			
		||||
			if v.SniffingConfig != nil && v.SniffingConfig.Enabled && v.SniffingConfig.DestOverride != nil {
 | 
			
		||||
				for _, dov := range *v.SniffingConfig.DestOverride {
 | 
			
		||||
					if dov == "fakedns" {
 | 
			
		||||
						found = true
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !found {
 | 
			
		||||
			newError("Defined Fake DNS but haven't enabled fake dns sniffing at any inbound.").AtWarning().WriteToLog()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
package conf
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	RegisterConfigureFilePostProcessingStage("FakeDNS", &FakeDNSPostProcessingStage{})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
package conf
 | 
			
		||||
 | 
			
		||||
type ConfigureFilePostProcessingStage interface {
 | 
			
		||||
	Process(conf *Config) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var configureFilePostProcessingStages map[string]ConfigureFilePostProcessingStage
 | 
			
		||||
 | 
			
		||||
func RegisterConfigureFilePostProcessingStage(name string, stage ConfigureFilePostProcessingStage) {
 | 
			
		||||
	if configureFilePostProcessingStages == nil {
 | 
			
		||||
		configureFilePostProcessingStages = make(map[string]ConfigureFilePostProcessingStage)
 | 
			
		||||
	}
 | 
			
		||||
	configureFilePostProcessingStages[name] = stage
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PostProcessConfigureFile(conf *Config) error {
 | 
			
		||||
	for k, v := range configureFilePostProcessingStages {
 | 
			
		||||
		if err := v.Process(conf); err != nil {
 | 
			
		||||
			return newError("Rejected by Postprocessing Stage ", k).AtError().Base(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -62,6 +62,7 @@ type SniffingConfig struct {
 | 
			
		|||
	Enabled         bool        `json:"enabled"`
 | 
			
		||||
	DestOverride    *StringList `json:"destOverride"`
 | 
			
		||||
	DomainsExcluded *StringList `json:"domainsExcluded"`
 | 
			
		||||
	MetadataOnly    bool        `json:"metadataOnly"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Build implements Buildable.
 | 
			
		||||
| 
						 | 
				
			
			@ -74,6 +75,8 @@ func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) {
 | 
			
		|||
				p = append(p, "http")
 | 
			
		||||
			case "tls", "https", "ssl":
 | 
			
		||||
				p = append(p, "tls")
 | 
			
		||||
			case "fakedns":
 | 
			
		||||
				p = append(p, "fakedns")
 | 
			
		||||
			default:
 | 
			
		||||
				return nil, newError("unknown protocol: ", protocol)
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -91,6 +94,7 @@ func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) {
 | 
			
		|||
		Enabled:             c.Enabled,
 | 
			
		||||
		DestinationOverride: p,
 | 
			
		||||
		DomainsExcluded:     d,
 | 
			
		||||
		MetadataOnly:        c.MetadataOnly,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -395,6 +399,7 @@ type Config struct {
 | 
			
		|||
	API             *APIConfig             `json:"api"`
 | 
			
		||||
	Stats           *StatsConfig           `json:"stats"`
 | 
			
		||||
	Reverse         *ReverseConfig         `json:"reverse"`
 | 
			
		||||
	FakeDNS         *FakeDNSConfig         `json:"fakeDns"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Config) findInboundTag(tag string) int {
 | 
			
		||||
| 
						 | 
				
			
			@ -448,6 +453,10 @@ func (c *Config) Override(o *Config, fn string) {
 | 
			
		|||
		c.Reverse = o.Reverse
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.FakeDNS != nil {
 | 
			
		||||
		c.FakeDNS = o.FakeDNS
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// deprecated attrs... keep them for now
 | 
			
		||||
	if o.InboundConfig != nil {
 | 
			
		||||
		c.InboundConfig = o.InboundConfig
 | 
			
		||||
| 
						 | 
				
			
			@ -519,6 +528,10 @@ func applyTransportConfig(s *StreamConfig, t *TransportConfig) {
 | 
			
		|||
 | 
			
		||||
// Build implements Buildable.
 | 
			
		||||
func (c *Config) Build() (*core.Config, error) {
 | 
			
		||||
	if err := PostProcessConfigureFile(c); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	config := &core.Config{
 | 
			
		||||
		App: []*serial.TypedMessage{
 | 
			
		||||
			serial.ToTypedMessage(&dispatcher.Config{}),
 | 
			
		||||
| 
						 | 
				
			
			@ -585,6 +598,14 @@ func (c *Config) Build() (*core.Config, error) {
 | 
			
		|||
		config.App = append(config.App, serial.ToTypedMessage(r))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.FakeDNS != nil {
 | 
			
		||||
		r, err := c.FakeDNS.Build()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		config.App = append(config.App, serial.ToTypedMessage(r))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var inbounds []InboundDetourConfig
 | 
			
		||||
 | 
			
		||||
	if c.InboundConfig != nil {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,6 +16,7 @@ import (
 | 
			
		|||
 | 
			
		||||
	// Other optional features.
 | 
			
		||||
	_ "github.com/xtls/xray-core/app/dns"
 | 
			
		||||
	_ "github.com/xtls/xray-core/app/dns/fakedns"
 | 
			
		||||
	_ "github.com/xtls/xray-core/app/log"
 | 
			
		||||
	_ "github.com/xtls/xray-core/app/policy"
 | 
			
		||||
	_ "github.com/xtls/xray-core/app/reverse"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,25 +36,13 @@ type ownLinkVerifier interface {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
type Handler struct {
 | 
			
		||||
	ipv4Lookup      dns.IPv4Lookup
 | 
			
		||||
	ipv6Lookup      dns.IPv6Lookup
 | 
			
		||||
	client          dns.Client
 | 
			
		||||
	ownLinkVerifier ownLinkVerifier
 | 
			
		||||
	server          net.Destination
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Handler) Init(config *Config, dnsClient dns.Client) error {
 | 
			
		||||
	ipv4lookup, ok := dnsClient.(dns.IPv4Lookup)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return newError("dns.Client doesn't implement IPv4Lookup")
 | 
			
		||||
	}
 | 
			
		||||
	h.ipv4Lookup = ipv4lookup
 | 
			
		||||
 | 
			
		||||
	ipv6lookup, ok := dnsClient.(dns.IPv6Lookup)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return newError("dns.Client doesn't implement IPv6Lookup")
 | 
			
		||||
	}
 | 
			
		||||
	h.ipv6Lookup = ipv6lookup
 | 
			
		||||
 | 
			
		||||
	h.client = dnsClient
 | 
			
		||||
	if v, ok := dnsClient.(ownLinkVerifier); ok {
 | 
			
		||||
		h.ownLinkVerifier = v
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -209,11 +197,21 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string,
 | 
			
		|||
	var ips []net.IP
 | 
			
		||||
	var err error
 | 
			
		||||
 | 
			
		||||
	var ttl uint32 = 600
 | 
			
		||||
 | 
			
		||||
	switch qType {
 | 
			
		||||
	case dnsmessage.TypeA:
 | 
			
		||||
		ips, err = h.ipv4Lookup.LookupIPv4(domain)
 | 
			
		||||
		ips, err = h.client.LookupIP(domain, dns.IPOption{
 | 
			
		||||
			IPv4Enable: true,
 | 
			
		||||
			IPv6Enable: false,
 | 
			
		||||
			FakeEnable: true,
 | 
			
		||||
		})
 | 
			
		||||
	case dnsmessage.TypeAAAA:
 | 
			
		||||
		ips, err = h.ipv6Lookup.LookupIPv6(domain)
 | 
			
		||||
		ips, err = h.client.LookupIP(domain, dns.IPOption{
 | 
			
		||||
			IPv4Enable: false,
 | 
			
		||||
			IPv6Enable: true,
 | 
			
		||||
			FakeEnable: true,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rcode := dns.RCodeFromError(err)
 | 
			
		||||
| 
						 | 
				
			
			@ -241,7 +239,7 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string,
 | 
			
		|||
	}))
 | 
			
		||||
	common.Must(builder.StartAnswers())
 | 
			
		||||
 | 
			
		||||
	rHeader := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: 600}
 | 
			
		||||
	rHeader := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: ttl}
 | 
			
		||||
	for _, ip := range ips {
 | 
			
		||||
		if len(ip) == net.IPv4len {
 | 
			
		||||
			var r dnsmessage.AResource
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,19 +59,26 @@ func (h *Handler) policy() policy.Session {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address {
 | 
			
		||||
	var lookupFunc func(string) ([]net.IP, error) = h.dns.LookupIP
 | 
			
		||||
 | 
			
		||||
	var option dns.IPOption = dns.IPOption{
 | 
			
		||||
		IPv4Enable: true,
 | 
			
		||||
		IPv6Enable: true,
 | 
			
		||||
		FakeEnable: false,
 | 
			
		||||
	}
 | 
			
		||||
	if h.config.DomainStrategy == Config_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()) {
 | 
			
		||||
		if lookupIPv4, ok := h.dns.(dns.IPv4Lookup); ok {
 | 
			
		||||
			lookupFunc = lookupIPv4.LookupIPv4
 | 
			
		||||
		option = dns.IPOption{
 | 
			
		||||
			IPv4Enable: true,
 | 
			
		||||
			IPv6Enable: false,
 | 
			
		||||
			FakeEnable: false,
 | 
			
		||||
		}
 | 
			
		||||
	} else if h.config.DomainStrategy == Config_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()) {
 | 
			
		||||
		if lookupIPv6, ok := h.dns.(dns.IPv6Lookup); ok {
 | 
			
		||||
			lookupFunc = lookupIPv6.LookupIPv6
 | 
			
		||||
		option = dns.IPOption{
 | 
			
		||||
			IPv4Enable: false,
 | 
			
		||||
			IPv6Enable: true,
 | 
			
		||||
			FakeEnable: false,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ips, err := lookupFunc(domain)
 | 
			
		||||
	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))
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -125,7 +132,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
 | 
			
		|||
					Address: ip,
 | 
			
		||||
					Port:    dialDest.Port,
 | 
			
		||||
				}
 | 
			
		||||
				newError("dialing to to ", dialDest).WriteToLog(session.ExportIDToError(ctx))
 | 
			
		||||
				newError("dialing to ", dialDest).WriteToLog(session.ExportIDToError(ctx))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@ package mocks
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	gomock "github.com/golang/mock/gomock"
 | 
			
		||||
	dns "github.com/xtls/xray-core/features/dns"
 | 
			
		||||
	net "net"
 | 
			
		||||
	reflect "reflect"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -48,18 +49,18 @@ func (mr *DNSClientMockRecorder) Close() *gomock.Call {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// LookupIP mocks base method
 | 
			
		||||
func (m *DNSClient) LookupIP(arg0 string) ([]net.IP, error) {
 | 
			
		||||
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 {
 | 
			
		||||
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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start mocks base method
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue