diff --git a/app/dns/dnscommon.go b/app/dns/dnscommon.go index 0bd712ff..15ac2efe 100644 --- a/app/dns/dnscommon.go +++ b/app/dns/dnscommon.go @@ -42,12 +42,15 @@ func (r *IPRecord) getIPs() ([]net.IP, uint32, error) { if r == nil { return nil, 0, errRecordNotFound } - untilExpire := time.Until(r.Expire) + untilExpire := time.Until(r.Expire).Seconds() if untilExpire <= 0 { return nil, 0, errRecordNotFound } - ttl := uint32(untilExpire/time.Second) + uint32(1) + ttl := uint32(untilExpire) + 1 + if ttl == 1 { + r.Expire = time.Now().Add(time.Second) // To ensure that two consecutive requests get the same result + } if r.RCode != dnsmessage.RCodeSuccess { return nil, ttl, dns_feature.RCodeError(r.RCode) } diff --git a/app/proxyman/config.pb.go b/app/proxyman/config.pb.go index d644e3d1..9713521b 100644 --- a/app/proxyman/config.pb.go +++ b/app/proxyman/config.pb.go @@ -449,11 +449,12 @@ type SenderConfig struct { unknownFields protoimpl.UnknownFields // Send traffic through the given IP. Only IP is allowed. - Via *net.IPOrDomain `protobuf:"bytes,1,opt,name=via,proto3" json:"via,omitempty"` - StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"` - ProxySettings *internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"` - MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"` - ViaCidr string `protobuf:"bytes,5,opt,name=via_cidr,json=viaCidr,proto3" json:"via_cidr,omitempty"` + Via *net.IPOrDomain `protobuf:"bytes,1,opt,name=via,proto3" json:"via,omitempty"` + StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"` + ProxySettings *internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"` + MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"` + ViaCidr string `protobuf:"bytes,5,opt,name=via_cidr,json=viaCidr,proto3" json:"via_cidr,omitempty"` + TargetStrategy internet.DomainStrategy `protobuf:"varint,6,opt,name=target_strategy,json=targetStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"target_strategy,omitempty"` } func (x *SenderConfig) Reset() { @@ -521,6 +522,13 @@ func (x *SenderConfig) GetViaCidr() string { return "" } +func (x *SenderConfig) GetTargetStrategy() internet.DomainStrategy { + if x != nil { + return x.TargetStrategy + } + return internet.DomainStrategy(0) +} + type MultiplexingConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -779,7 +787,7 @@ var file_app_proxyman_config_proto_rawDesc = []byte{ 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, 0xcb, 0x02, 0x0a, 0x0c, 0x53, 0x65, + 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x9d, 0x03, 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, @@ -800,23 +808,28 @@ var file_app_proxyman_config_proto_rawDesc = []byte{ 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, 0x12, 0x19, 0x0a, 0x08, 0x76, 0x69, 0x61, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x76, 0x69, 0x61, 0x43, 0x69, 0x64, 0x72, 0x22, 0xa4, 0x01, 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, 0x05, 0x52, 0x0b, 0x63, - 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75, - 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78, - 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x78, - 0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 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, + 0x76, 0x69, 0x61, 0x43, 0x69, 0x64, 0x72, 0x12, 0x50, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x27, 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, 0x44, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x22, 0xa4, 0x01, 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, 0x05, 0x52, + 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, + 0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72, + 0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, + 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 ( @@ -850,6 +863,7 @@ var file_app_proxyman_config_proto_goTypes = []any{ (*internet.StreamConfig)(nil), // 13: xray.transport.internet.StreamConfig (*serial.TypedMessage)(nil), // 14: xray.common.serial.TypedMessage (*internet.ProxyConfig)(nil), // 15: xray.transport.internet.ProxyConfig + (internet.DomainStrategy)(0), // 16: xray.transport.internet.DomainStrategy } var file_app_proxyman_config_proto_depIdxs = []int32{ 0, // 0: xray.app.proxyman.AllocationStrategy.type:type_name -> xray.app.proxyman.AllocationStrategy.Type @@ -866,11 +880,12 @@ var file_app_proxyman_config_proto_depIdxs = []int32{ 13, // 11: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig 15, // 12: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig 8, // 13: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig - 14, // [14:14] is the sub-list for method output_type - 14, // [14:14] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name + 16, // 14: xray.app.proxyman.SenderConfig.target_strategy:type_name -> xray.transport.internet.DomainStrategy + 15, // [15:15] is the sub-list for method output_type + 15, // [15:15] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name } func init() { file_app_proxyman_config_proto_init() } diff --git a/app/proxyman/config.proto b/app/proxyman/config.proto index 635ffad2..a267a00b 100644 --- a/app/proxyman/config.proto +++ b/app/proxyman/config.proto @@ -84,6 +84,7 @@ message SenderConfig { xray.transport.internet.ProxyConfig proxy_settings = 3; MultiplexingConfig multiplex_settings = 4; string via_cidr = 5; + xray.transport.internet.DomainStrategy target_strategy = 6; } message MultiplexingConfig { diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go index 0e3483e1..44eba272 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -325,7 +325,7 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest ctx = session.ContextWithInbound(ctx, &session.Inbound{ Source: source, - Local: net.DestinationFromAddr(w.hub.Addr()), + Local: net.DestinationFromAddr(w.hub.Addr()), // Due to some limitations, in UDP connections, localIP is always equal to listen interface IP Gateway: net.UDPDestination(w.address, w.port), Tag: w.tag, }) diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index bc9c635b..c9021608 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -4,6 +4,7 @@ import ( "context" "crypto/rand" goerrors "errors" + "github.com/xtls/xray-core/common/dice" "io" "math/big" gonet "net" @@ -177,6 +178,25 @@ func (h *Handler) Tag() string { func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) { outbounds := session.OutboundsFromContext(ctx) ob := outbounds[len(outbounds)-1] + content := session.ContentFromContext(ctx) + if h.senderSettings != nil && h.senderSettings.TargetStrategy.HasStrategy() && ob.Target.Address.Family().IsDomain() && (content == nil || !content.SkipDNSResolve) { + ips, err := internet.LookupForIP(ob.Target.Address.Domain(), h.senderSettings.TargetStrategy, nil) + if err != nil { + errors.LogInfoInner(ctx, err, "failed to resolve ip for target ", ob.Target.Address.Domain()) + if h.senderSettings.TargetStrategy.ForceIP() { + err := errors.New("failed to resolve ip for target ", ob.Target.Address.Domain()).Base(err) + session.SubmitOutboundErrorToOriginator(ctx, err) + common.Interrupt(link.Writer) + common.Interrupt(link.Reader) + return + } + + } else { + unchangedDomain := ob.Target.Address.Domain() + ob.Target.Address = net.IPAddress(ips[dice.Roll(len(ips))]) + errors.LogInfo(ctx, "target: ", unchangedDomain, " resolved to: ", ob.Target.Address.String()) + } + } if ob.Target.Network == net.Network_UDP && ob.OriginalTarget.Address != nil && ob.OriginalTarget.Address != ob.Target.Address { link.Reader = &buf.EndpointOverrideReader{Reader: link.Reader, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address} link.Writer = &buf.EndpointOverrideWriter{Writer: link.Writer, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address} @@ -188,6 +208,7 @@ func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) { session.SubmitOutboundErrorToOriginator(ctx, err) errors.LogInfo(ctx, err.Error()) common.Interrupt(link.Writer) + common.Interrupt(link.Reader) } } if ob.Target.Network == net.Network_UDP && ob.Target.Port == 443 { @@ -287,26 +308,18 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti ob.Gateway = ParseRandomIP(addr, h.senderSettings.ViaCidr) case domain == "origin": - if inbound := session.InboundFromContext(ctx); inbound != nil { - if inbound.Conn != nil { - origin, _, err := net.SplitHostPort(inbound.Conn.LocalAddr().String()) - if err == nil { - ob.Gateway = net.ParseAddress(origin) - errors.LogDebug(ctx, "use receive package ip as snedthrough: ", origin) - } + if inbound.Local.IsValid() && inbound.Local.Address.Family().IsIP() { + ob.Gateway = inbound.Local.Address + errors.LogDebug(ctx, "use inbound local ip as sendthrough: ", inbound.Local.Address.String()) } } case domain == "srcip": if inbound := session.InboundFromContext(ctx); inbound != nil { - if inbound.Conn != nil { - clientaddr, _, err := net.SplitHostPort(inbound.Conn.RemoteAddr().String()) - if err == nil { - ob.Gateway = net.ParseAddress(clientaddr) - errors.LogDebug(ctx, "use client src ip as snedthrough: ", clientaddr) - } + if inbound.Source.IsValid() && inbound.Source.Address.Family().IsIP() { + ob.Gateway = inbound.Source.Address + errors.LogDebug(ctx, "use inbound source ip as sendthrough: ", inbound.Source.Address.String()) } - } //case addr.Family().IsDomain(): default: diff --git a/infra/conf/xray.go b/infra/conf/xray.go index 0810dd80..88f01322 100644 --- a/infra/conf/xray.go +++ b/infra/conf/xray.go @@ -260,13 +260,14 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) { } type OutboundDetourConfig struct { - Protocol string `json:"protocol"` - SendThrough *string `json:"sendThrough"` - Tag string `json:"tag"` - Settings *json.RawMessage `json:"settings"` - StreamSetting *StreamConfig `json:"streamSettings"` - ProxySettings *ProxyConfig `json:"proxySettings"` - MuxSettings *MuxConfig `json:"mux"` + Protocol string `json:"protocol"` + SendThrough *string `json:"sendThrough"` + Tag string `json:"tag"` + Settings *json.RawMessage `json:"settings"` + StreamSetting *StreamConfig `json:"streamSettings"` + ProxySettings *ProxyConfig `json:"proxySettings"` + MuxSettings *MuxConfig `json:"mux"` + TargetStrategy string `json:"targetStrategy"` } func (c *OutboundDetourConfig) checkChainProxyConfig() error { @@ -282,6 +283,32 @@ func (c *OutboundDetourConfig) checkChainProxyConfig() error { // Build implements Buildable. func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) { senderSettings := &proxyman.SenderConfig{} + switch strings.ToLower(c.TargetStrategy) { + case "asis", "": + senderSettings.TargetStrategy = internet.DomainStrategy_AS_IS + case "useip": + senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP + case "useipv4": + senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP4 + case "useipv6": + senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP6 + case "useipv4v6": + senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP46 + case "useipv6v4": + senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP64 + case "forceip": + senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP + case "forceipv4": + senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP4 + case "forceipv6": + senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP6 + case "forceipv4v6": + senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP46 + case "forceipv6v4": + senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP64 + default: + return nil, errors.New("unsupported target domain strategy: ", c.TargetStrategy) + } if err := c.checkChainProxyConfig(); err != nil { return nil, err } diff --git a/transport/internet/config.go b/transport/internet/config.go index 8608b872..6978eace 100644 --- a/transport/internet/config.go +++ b/transport/internet/config.go @@ -100,30 +100,30 @@ func (m SocketConfig_TProxyMode) IsEnabled() bool { return m != SocketConfig_Off } -func (s DomainStrategy) hasStrategy() bool { +func (s DomainStrategy) HasStrategy() bool { return strategy[s][0] != 0 } -func (s DomainStrategy) forceIP() bool { +func (s DomainStrategy) ForceIP() bool { return strategy[s][0] == 2 } -func (s DomainStrategy) preferIP4() bool { +func (s DomainStrategy) PreferIP4() bool { return strategy[s][1] == 4 || strategy[s][1] == 0 } -func (s DomainStrategy) preferIP6() bool { +func (s DomainStrategy) PreferIP6() bool { return strategy[s][1] == 6 || strategy[s][1] == 0 } -func (s DomainStrategy) hasFallback() bool { +func (s DomainStrategy) HasFallback() bool { return strategy[s][2] != 0 } -func (s DomainStrategy) fallbackIP4() bool { +func (s DomainStrategy) FallbackIP4() bool { return strategy[s][2] == 4 } -func (s DomainStrategy) fallbackIP6() bool { +func (s DomainStrategy) FallbackIP6() bool { return strategy[s][2] == 6 } diff --git a/transport/internet/dialer.go b/transport/internet/dialer.go index 5e403f67..a53498ab 100644 --- a/transport/internet/dialer.go +++ b/transport/internet/dialer.go @@ -85,20 +85,20 @@ var ( obm outbound.Manager ) -func lookupIP(domain string, strategy DomainStrategy, localAddr net.Address) ([]net.IP, error) { +func LookupForIP(domain string, strategy DomainStrategy, localAddr net.Address) ([]net.IP, error) { if dnsClient == nil { return nil, errors.New("DNS client not initialized").AtError() } ips, _, err := dnsClient.LookupIP(domain, dns.IPOption{ - IPv4Enable: (localAddr == nil || localAddr.Family().IsIPv4()) && strategy.preferIP4(), - IPv6Enable: (localAddr == nil || localAddr.Family().IsIPv6()) && strategy.preferIP6(), + IPv4Enable: (localAddr == nil || localAddr.Family().IsIPv4()) && strategy.PreferIP4(), + IPv6Enable: (localAddr == nil || localAddr.Family().IsIPv6()) && strategy.PreferIP6(), }) { // Resolve fallback - if (len(ips) == 0 || err != nil) && strategy.hasFallback() && localAddr == nil { + if (len(ips) == 0 || err != nil) && strategy.HasFallback() && localAddr == nil { ips, _, err = dnsClient.LookupIP(domain, dns.IPOption{ - IPv4Enable: strategy.fallbackIP4(), - IPv6Enable: strategy.fallbackIP6(), + IPv4Enable: strategy.FallbackIP4(), + IPv6Enable: strategy.FallbackIP6(), }) } } @@ -113,7 +113,7 @@ func canLookupIP(dst net.Destination, sockopt *SocketConfig) bool { if dst.Address.Family().IsIP() { return false } - return sockopt.DomainStrategy.hasStrategy() + return sockopt.DomainStrategy.HasStrategy() } func redirect(ctx context.Context, dst net.Destination, obt string, h outbound.Handler) net.Conn { @@ -249,17 +249,17 @@ func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig } if canLookupIP(dest, sockopt) { - ips, err := lookupIP(dest.Address.String(), sockopt.DomainStrategy, src) + ips, err := LookupForIP(dest.Address.String(), sockopt.DomainStrategy, src) if err != nil { errors.LogErrorInner(ctx, err, "failed to resolve ip") - if sockopt.DomainStrategy.forceIP() { + if sockopt.DomainStrategy.ForceIP() { return nil, err } } else if sockopt.HappyEyeballs == nil || sockopt.HappyEyeballs.TryDelayMs == 0 || sockopt.HappyEyeballs.MaxConcurrentTry == 0 || len(ips) < 2 || len(sockopt.DialerProxy) > 0 || dest.Network != net.Network_TCP { dest.Address = net.IPAddress(ips[dice.Roll(len(ips))]) errors.LogInfo(ctx, "replace destination with "+dest.String()) } else { - return TcpRaceDial(ctx, src, ips, dest.Port, sockopt) + return TcpRaceDial(ctx, src, ips, dest.Port, sockopt, dest.Address.String()) } } diff --git a/transport/internet/happy_eyeballs.go b/transport/internet/happy_eyeballs.go index 10adb235..d7f30227 100644 --- a/transport/internet/happy_eyeballs.go +++ b/transport/internet/happy_eyeballs.go @@ -2,6 +2,7 @@ package internet import ( "context" + "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "time" ) @@ -12,7 +13,7 @@ type result struct { index int } -func TcpRaceDial(ctx context.Context, src net.Address, ips []net.IP, port net.Port, sockopt *SocketConfig) (net.Conn, error) { +func TcpRaceDial(ctx context.Context, src net.Address, ips []net.IP, port net.Port, sockopt *SocketConfig, domain string) (net.Conn, error) { if len(ips) < 2 { panic("at least 2 ips is required to race dial") } @@ -30,6 +31,7 @@ func TcpRaceDial(ctx context.Context, src net.Address, ips []net.IP, port net.Po activeNum := uint32(0) timer := time.NewTimer(0) var winConn net.Conn + errors.LogDebug(ctx, "happy eyeballs racing dial for ", domain, " with IPs ", ips) for { select { case r := <-resultCh: @@ -54,6 +56,7 @@ func TcpRaceDial(ctx context.Context, src net.Address, ips []net.IP, port net.Po timer.Stop() if winConn == nil { winConn = r.conn + errors.LogDebug(ctx, "happy eyeballs established connection for ", domain, " with IP ", ips[r.index]) } else { r.conn.Close() } @@ -69,6 +72,7 @@ func TcpRaceDial(ctx context.Context, src net.Address, ips []net.IP, port net.Po continue } if activeNum == 0 { + errors.LogDebugInner(ctx, r.err, "happy eyeballs no connection established for ", domain) return nil, r.err } timer.Stop()