diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 1531866f4..c4629cc4f 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -178,8 +178,12 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran } func shouldOverride(result SniffResult, domainOverride []string) bool { + protocolString := result.Protocol() + if resComp, ok := result.(SnifferResultComposite); ok { + protocolString = resComp.ProtocolForDomainResult() + } for _, p := range domainOverride { - if strings.HasPrefix(result.Protocol(), p) { + if strings.HasPrefix(protocolString, p) { return true } } @@ -203,15 +207,29 @@ 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(result, sniffingRequest.OverrideDestinationForProtocol) { + 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() } @@ -227,34 +245,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) { diff --git a/app/dispatcher/fakednssniffer.go b/app/dispatcher/fakednssniffer.go new file mode 100644 index 000000000..cab292551 --- /dev/null +++ b/app/dispatcher/fakednssniffer.go @@ -0,0 +1,51 @@ +// +build !confonly + +package dispatcher + +import ( + "context" + + "v2ray.com/core" + "v2ray.com/core/common" + "v2ray.com/core/common/net" + "v2ray.com/core/common/session" + "v2ray.com/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 +} diff --git a/app/dispatcher/sniffer.go b/app/dispatcher/sniffer.go index 34851ab8b..613dc8eba 100644 --- a/app/dispatcher/sniffer.go +++ b/app/dispatcher/sniffer.go @@ -3,6 +3,8 @@ package dispatcher import ( + "context" + "v2ray.com/core/common" "v2ray.com/core/common/protocol/bittorrent" "v2ray.com/core/common/protocol/http" @@ -14,30 +16,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 } @@ -53,3 +71,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 +} diff --git a/app/dns/config.proto b/app/dns/config.proto index cdce754aa..cba15eec4 100644 --- a/app/dns/config.proto +++ b/app/dns/config.proto @@ -69,4 +69,6 @@ message Config { // Tag is the inbound tag of DNS client. string tag = 6; + + reserved 7; } diff --git a/app/dns/dns.go b/app/dns/dns.go index 0e135a1f2..8c0c5dfe2 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -23,10 +23,10 @@ import ( // DNS is a DNS rely server. type DNS struct { sync.Mutex - tag string - hosts *StaticHosts - clients []*Client - + tag string + hosts *StaticHosts + clients []*Client + ctx context.Context domainMatcher strmatcher.IndexMatcher matcherInfos []DomainMatcherInfo } @@ -113,6 +113,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) { tag: tag, hosts: hosts, clients: clients, + ctx: ctx, domainMatcher: domainMatcher, matcherInfos: matcherInfos, }, nil @@ -189,7 +190,7 @@ func (s *DNS) lookupIPInternal(domain string, option IPOption) ([]net.IP, error) // Name servers lookup errs := []error{} - ctx := session.ContextWithInbound(context.Background(), &session.Inbound{Tag: s.tag}) + ctx := session.ContextWithInbound(s.ctx, &session.Inbound{Tag: s.tag}) for _, client := range s.sortClients(domain) { ips, err := client.QueryIP(ctx, domain, option) if len(ips) > 0 { diff --git a/app/dns/fakedns/errors.generated.go b/app/dns/fakedns/errors.generated.go new file mode 100644 index 000000000..c8ad60eda --- /dev/null +++ b/app/dns/fakedns/errors.generated.go @@ -0,0 +1,9 @@ +package fakedns + +import "v2ray.com/core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/dns/fakedns/fake.go b/app/dns/fakedns/fake.go new file mode 100644 index 000000000..22ff3ceb1 --- /dev/null +++ b/app/dns/fakedns/fake.go @@ -0,0 +1,132 @@ +// +build !confonly + +package fakedns + +import ( + "context" + "math" + "math/big" + gonet "net" + + "v2ray.com/core/common" + "v2ray.com/core/common/cache" + "v2ray.com/core/common/net" + "v2ray.com/core/features/dns" +) + +type Holder struct { + domainToIP cache.Lru + nextIP *big.Int + + 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.nextIP = 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, 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 ipaddr gonet.IP + var currentIP *big.Int + var err error + + if ipaddr, ipRange, err = gonet.ParseCIDR(ipPoolCidr); err != nil { + return newError("Unable to parse CIDR for Fake DNS IP assignment").Base(err).AtError() + } + + currentIP = big.NewInt(0).SetBytes(ipaddr) + if ipaddr.To4() != nil { + currentIP = big.NewInt(0).SetBytes(ipaddr.To4()) + } + + 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 + fkdns.nextIP = currentIP + 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 ip net.Address + for { + ip = net.IPAddress(fkdns.nextIP.Bytes()) + + fkdns.nextIP = fkdns.nextIP.Add(fkdns.nextIP, big.NewInt(1)) + if !fkdns.ipRange.Contains(fkdns.nextIP.Bytes()) { + fkdns.nextIP = big.NewInt(0).SetBytes(fkdns.ipRange.IP) + } + + // if we run for a long time, we may go back to beginning and start seeing the IP in use + if _, ok := fkdns.domainToIP.GetKeyFromValue(ip); !ok { + break + } + } + 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) + } + return "" +} + +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 + })) +} diff --git a/app/dns/fakedns/fakedns.go b/app/dns/fakedns/fakedns.go new file mode 100644 index 000000000..0e55e5bd5 --- /dev/null +++ b/app/dns/fakedns/fakedns.go @@ -0,0 +1,5 @@ +// +build !confonly + +package fakedns + +//go:generate go run v2ray.com/core/common/errors/errorgen diff --git a/app/dns/fakedns/fakedns.pb.go b/app/dns/fakedns/fakedns.pb.go new file mode 100644 index 000000000..1ed892ef9 --- /dev/null +++ b/app/dns/fakedns/fakedns.pb.go @@ -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: v2ray.core.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 +} diff --git a/app/dns/fakedns/fakedns.proto b/app/dns/fakedns/fakedns.proto new file mode 100644 index 000000000..9f64b3863 --- /dev/null +++ b/app/dns/fakedns/fakedns.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package v2ray.core.app.dns.fakedns; +option csharp_namespace = "V2Ray.Core.App.Dns.Fakedns"; +option go_package = "v2ray.com/core/app/dns/fakedns"; +option java_package = "com.v2ray.core.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 +} \ No newline at end of file diff --git a/app/dns/fakedns/fakedns_test.go b/app/dns/fakedns/fakedns_test.go new file mode 100644 index 000000000..8484b0c2b --- /dev/null +++ b/app/dns/fakedns/fakedns_test.go @@ -0,0 +1,114 @@ +package fakedns + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "v2ray.com/core/common" + "v2ray.com/core/common/net" + "v2ray.com/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.0.0.0", addr[0].IP().String()) +} + +func TestFakeDnsHolderCreateMappingMany(t *testing.T) { + fkdns, err := NewFakeDNSHolder() + common.Must(err) + + addr := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org") + assert.Equal(t, "240.0.0.0", addr[0].IP().String()) + + addr2 := fkdns.GetFakeIPForDomain("fakednstest2.v2fly.org") + assert.Equal(t, "240.0.0.1", addr2[0].IP().String()) +} + +func TestFakeDnsHolderCreateMappingManyAndResolve(t *testing.T) { + fkdns, err := NewFakeDNSHolder() + common.Must(err) + + { + addr := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org") + assert.Equal(t, "240.0.0.0", addr[0].IP().String()) + } + + { + addr2 := fkdns.GetFakeIPForDomain("fakednstest2.v2fly.org") + assert.Equal(t, "240.0.0.1", addr2[0].IP().String()) + } + + { + result := fkdns.GetDomainFromFakeDNS(net.ParseAddress("240.0.0.0")) + assert.Equal(t, "fakednstest.v2fly.org", result) + } + + { + result := fkdns.GetDomainFromFakeDNS(net.ParseAddress("240.0.0.1")) + assert.Equal(t, "fakednstest2.v2fly.org", result) + } +} + +func TestFakeDnsHolderCreateMappingManySingleDomain(t *testing.T) { + fkdns, err := NewFakeDNSHolder() + common.Must(err) + + addr := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org") + assert.Equal(t, "240.0.0.0", addr[0].IP().String()) + + addr2 := fkdns.GetFakeIPForDomain("fakednstest.v2fly.org") + assert.Equal(t, "240.0.0.0", 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") + assert.Equal(t, "240.0.0.0", addr[0].IP().String()) + } + + { + addr2 := fkdns.GetFakeIPForDomain("fakednstest2.v2fly.org") + assert.Equal(t, "240.0.0.1", addr2[0].IP().String()) + } + + for i := 0; i <= 8192; i++ { + { + result := fkdns.GetDomainFromFakeDNS(net.ParseAddress("240.0.0.0")) + assert.Equal(t, "fakednstest.v2fly.org", result) + } + + { + result := fkdns.GetDomainFromFakeDNS(net.ParseAddress("240.0.0.1")) + assert.Equal(t, "fakednstest2.v2fly.org", result) + } + + { + uuid := uuid.New() + domain := uuid.String() + ".fakednstest.v2fly.org" + addr := fkdns.GetFakeIPForDomain(domain) + rsaddr := addr[0].IP().String() + + result := fkdns.GetDomainFromFakeDNS(net.ParseAddress(rsaddr)) + assert.Equal(t, domain, result) + } + } +} diff --git a/app/dns/nameserver.go b/app/dns/nameserver.go index bbc1bdd1b..22aab78e5 100644 --- a/app/dns/nameserver.go +++ b/app/dns/nameserver.go @@ -55,6 +55,8 @@ func NewServer(dest net.Destination, dispatcher routing.Dispatcher) (Server, err return NewDoHLocalNameServer(u), nil case u.Scheme == "quic+local": // DNS-over-QUIC Local mode return NewQUICNameServer(u) + case u.String() == "fakedns": + return NewFakeDNSServer(), nil } } if dest.Network == net.Network_Unknown { diff --git a/app/dns/nameserver_fakedns.go b/app/dns/nameserver_fakedns.go new file mode 100644 index 000000000..9fa01b4b1 --- /dev/null +++ b/app/dns/nameserver_fakedns.go @@ -0,0 +1,41 @@ +// +build !confonly + +package dns + +import ( + "context" + + "v2ray.com/core" + "v2ray.com/core/common/net" + "v2ray.com/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, _ net.IP, _ 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, err := toNetIP(ips) + if err != nil { + return nil, newError("Unable to convert IP to net ip").Base(err).AtError() + } + + return netIP, nil +} diff --git a/app/proxyman/config.pb.go b/app/proxyman/config.pb.go index ce670a283..476e43afd 100644 --- a/app/proxyman/config.pb.go +++ b/app/proxyman/config.pb.go @@ -239,8 +239,11 @@ 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"` + // 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,3,opt,name=metadata_only,json=metadataOnly,proto3" json:"metadata_only,omitempty"` } func (x *SniffingConfig) Reset() { @@ -289,6 +292,13 @@ func (x *SniffingConfig) GetDestinationOverride() []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 @@ -757,97 +767,99 @@ var file_app_proxyman_config_proto_rawDesc = []byte{ 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 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, 0x5d, 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, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, - 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x22, 0xb4, 0x04, 0x0a, - 0x0e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x3f, 0x0a, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 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, 0x39, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x21, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x5c, 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, 0x2b, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x54, 0x0a, 0x0f, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x54, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, - 0x72, 0x69, 0x64, 0x65, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x76, 0x32, 0x72, - 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x54, 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, 0x27, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0xcc, 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, 0x53, - 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, 0x26, 0x2e, 0x76, 0x32, 0x72, 0x61, - 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x4d, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x76, 0x32, - 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0xc8, 0x02, 0x0a, 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x33, 0x0a, 0x03, 0x76, 0x69, 0x61, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x54, 0x0a, 0x0f, 0x73, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, - 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, 0x51, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, + 0x0c, 0x0a, 0x08, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x10, 0x02, 0x22, 0x82, 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, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, + 0x64, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x23, 0x0a, + 0x0d, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4f, 0x6e, + 0x6c, 0x79, 0x22, 0xb4, 0x04, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3f, 0x0a, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x61, + 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x76, 0x32, 0x72, 0x61, + 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x39, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, + 0x6f, 0x72, 0x65, 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, 0x5c, 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, 0x2b, + 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, + 0x54, 0x0a, 0x0f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x5a, 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, - 0x2b, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x56, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, - 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, - 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50, 0x01, 0x5a, 0x1b, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, - 0x79, 0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x17, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, - 0x65, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 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, 0x54, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, + 0x32, 0x27, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x54, 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, 0x27, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0xcc, 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, 0x53, 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, + 0x26, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x4d, 0x0a, 0x0e, 0x70, 0x72, 0x6f, + 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x26, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0xc8, 0x02, 0x0a, 0x0c, 0x53, + 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x33, 0x0a, 0x03, 0x76, + 0x69, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x54, 0x0a, 0x0f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x76, 0x32, 0x72, 0x61, + 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x51, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, + 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, + 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x5a, 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, 0x2b, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 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, 0x56, 0x0a, 0x1b, + 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, + 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50, 0x01, 0x5a, 0x1b, 0x76, + 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, + 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x17, 0x56, 0x32, 0x52, + 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x72, 0x6f, 0x78, + 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/app/proxyman/config.proto b/app/proxyman/config.proto index 7f4b7b8d2..ca9e625c0 100644 --- a/app/proxyman/config.proto +++ b/app/proxyman/config.proto @@ -54,8 +54,12 @@ 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; + + // 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 = 3; } message ReceiverConfig { diff --git a/app/proxyman/inbound/always.go b/app/proxyman/inbound/always.go index f44747bd9..33014416c 100644 --- a/app/proxyman/inbound/always.go +++ b/app/proxyman/inbound/always.go @@ -128,11 +128,13 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig * if net.HasNetwork(nl, net.Network_UDP) { worker := &udpWorker{ + ctx: ctx, tag: tag, proxy: p, address: address, port: net.Port(port), dispatcher: h.mux, + sniffingConfig: receiverConfig.GetEffectiveSniffingSettings(), uplinkCounter: uplinkCounter, downlinkCounter: downlinkCounter, stream: mss, diff --git a/app/proxyman/inbound/dynamic.go b/app/proxyman/inbound/dynamic.go index 3dbd89abe..f20920699 100644 --- a/app/proxyman/inbound/dynamic.go +++ b/app/proxyman/inbound/dynamic.go @@ -148,11 +148,13 @@ func (h *DynamicInboundHandler) refresh() error { if net.HasNetwork(nl, net.Network_UDP) { worker := &udpWorker{ + ctx: h.ctx, tag: h.tag, proxy: p, address: address, port: port, dispatcher: h.mux, + sniffingConfig: h.receiverConfig.GetEffectiveSniffingSettings(), uplinkCounter: uplinkCounter, downlinkCounter: downlinkCounter, stream: h.streamSettings, diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go index 16e15aa8f..38f86910d 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -87,6 +87,7 @@ func (w *tcpWorker) callback(conn internet.Connection) { 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 w.uplinkCounter != nil || w.downlinkCounter != nil { @@ -230,11 +231,14 @@ type udpWorker struct { tag string stream *internet.MemoryStreamConfig dispatcher routing.Dispatcher + sniffingConfig *proxyman.SniffingConfig uplinkCounter stats.Counter downlinkCounter stats.Counter checker *task.Periodic activeConn map[connID]*udpConn + + ctx context.Context } func (w *udpWorker) getConnection(id connID) (*udpConn, bool) { @@ -286,7 +290,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) @@ -300,6 +304,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)) } @@ -428,6 +439,7 @@ func (w *dsWorker) callback(conn internet.Connection) { 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 w.uplinkCounter != nil || w.downlinkCounter != nil { diff --git a/common/cache/lru.go b/common/cache/lru.go new file mode 100644 index 000000000..2594a8b1b --- /dev/null +++ b/common/cache/lru.go @@ -0,0 +1,76 @@ +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) + 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) 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() + } +} diff --git a/common/cache/lru_test.go b/common/cache/lru_test.go new file mode 100644 index 000000000..e2bb767f6 --- /dev/null +++ b/common/cache/lru_test.go @@ -0,0 +1,69 @@ +package cache_test + +import ( + "testing" + + . "v2ray.com/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.Put(1, 1) + v, ok := lru.GetKeyFromValue(3) + if ok { + t.Error("should get nil", v) + } + v, _ = lru.GetKeyFromValue(2) + if v != 2 { + t.Error("should get 2", v) + } +} diff --git a/common/session/session.go b/common/session/session.go index 463ebac80..2b67e2baa 100644 --- a/common/session/session.go +++ b/common/session/session.go @@ -57,6 +57,7 @@ type Outbound struct { type SniffingRequest struct { OverrideDestinationForProtocol []string Enabled bool + MetadataOnly bool } // Content is the metadata of the connection content. diff --git a/features/dns/fakedns.go b/features/dns/fakedns.go new file mode 100644 index 000000000..0206d8307 --- /dev/null +++ b/features/dns/fakedns.go @@ -0,0 +1,12 @@ +package dns + +import ( + "v2ray.com/core/common/net" + "v2ray.com/core/features" +) + +type FakeDNSEngine interface { + features.Feature + GetFakeIPForDomain(domain string) []net.Address + GetDomainFromFakeDNS(ip net.Address) string +} diff --git a/features/dns/localdns/errors.generated.go b/features/dns/localdns/errors.generated.go new file mode 100644 index 000000000..c63727c75 --- /dev/null +++ b/features/dns/localdns/errors.generated.go @@ -0,0 +1,9 @@ +package localdns + +import "v2ray.com/core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/infra/conf/fakedns.go b/infra/conf/fakedns.go new file mode 100644 index 000000000..d731bed56 --- /dev/null +++ b/infra/conf/fakedns.go @@ -0,0 +1,65 @@ +package conf + +import ( + "github.com/golang/protobuf/proto" + "v2ray.com/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 +} diff --git a/infra/conf/init.go b/infra/conf/init.go new file mode 100644 index 000000000..519d9fb25 --- /dev/null +++ b/infra/conf/init.go @@ -0,0 +1,5 @@ +package conf + +func init() { + RegisterConfigureFilePostProcessingStage("FakeDNS", &FakeDNSPostProcessingStage{}) +} diff --git a/infra/conf/lint.go b/infra/conf/lint.go new file mode 100644 index 000000000..93da35d40 --- /dev/null +++ b/infra/conf/lint.go @@ -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 +} diff --git a/infra/conf/v2ray.go b/infra/conf/v2ray.go index 5ae3aa3bb..390058992 100644 --- a/infra/conf/v2ray.go +++ b/infra/conf/v2ray.go @@ -59,6 +59,7 @@ func toProtocolList(s []string) ([]proxyman.KnownProtocols, error) { type SniffingConfig struct { Enabled bool `json:"enabled"` DestOverride *StringList `json:"destOverride"` + MetadataOnly bool `json:"metadataOnly"` } // Build implements Buildable. @@ -71,6 +72,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: ", domainOverride) } @@ -80,6 +83,7 @@ func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) { return &proxyman.SniffingConfig{ Enabled: c.Enabled, DestinationOverride: p, + MetadataOnly: c.MetadataOnly, }, nil } @@ -346,6 +350,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 { @@ -399,6 +404,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 @@ -470,6 +479,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{}), @@ -536,6 +549,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 { diff --git a/main/distro/all/all.go b/main/distro/all/all.go index f7ae90eb7..afacd2a94 100644 --- a/main/distro/all/all.go +++ b/main/distro/all/all.go @@ -16,6 +16,7 @@ import ( // Other optional features. _ "v2ray.com/core/app/dns" + _ "v2ray.com/core/app/dns/fakedns" _ "v2ray.com/core/app/log" _ "v2ray.com/core/app/policy" _ "v2ray.com/core/app/reverse" diff --git a/proxy/dns/dns.go b/proxy/dns/dns.go index 4117c0c36..cb3376870 100644 --- a/proxy/dns/dns.go +++ b/proxy/dns/dns.go @@ -38,6 +38,7 @@ type ownLinkVerifier interface { } type Handler struct { + client dns.Client ipv4Lookup dns.IPv4Lookup ipv6Lookup dns.IPv6Lookup ownLinkVerifier ownLinkVerifier @@ -45,6 +46,7 @@ type Handler struct { } func (h *Handler) Init(config *Config, dnsClient dns.Client) error { + h.client = dnsClient ipv4lookup, ok := dnsClient.(dns.IPv4Lookup) if !ok { return newError("dns.Client doesn't implement IPv4Lookup") @@ -211,6 +213,8 @@ 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) @@ -243,7 +247,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