diff --git a/infra/conf/wireguard.go b/infra/conf/wireguard.go index 0c79297b..7b2b6bbf 100644 --- a/infra/conf/wireguard.go +++ b/infra/conf/wireguard.go @@ -3,6 +3,7 @@ package conf import ( "encoding/base64" "encoding/hex" + "strings" "github.com/xtls/xray-core/proxy/wireguard" "google.golang.org/protobuf/proto" @@ -47,12 +48,13 @@ func (c *WireGuardPeerConfig) Build() (proto.Message, error) { } type WireGuardConfig struct { - SecretKey string `json:"secretKey"` - Address []string `json:"address"` - Peers []*WireGuardPeerConfig `json:"peers"` - MTU int `json:"mtu"` - NumWorkers int `json:"workers"` - Reserved []byte `json:"reserved"` + SecretKey string `json:"secretKey"` + Address []string `json:"address"` + Peers []*WireGuardPeerConfig `json:"peers"` + MTU int `json:"mtu"` + NumWorkers int `json:"workers"` + Reserved []byte `json:"reserved"` + DomainStrategy string `json:"domainStrategy"` } func (c *WireGuardConfig) Build() (proto.Message, error) { @@ -96,6 +98,21 @@ func (c *WireGuardConfig) Build() (proto.Message, error) { } config.Reserved = c.Reserved + switch strings.ToLower(c.DomainStrategy) { + case "forceip", "": + config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP + case "forceipv4": + config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP4 + case "forceipv6": + config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP6 + case "forceipv4v6": + config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP46 + case "forceipv6v4": + config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP64 + default: + return nil, newError("unsupported domain strategy: ", c.DomainStrategy) + } + return config, nil } diff --git a/infra/conf/wireguard_test.go b/infra/conf/wireguard_test.go index f0136bf0..7a4adf36 100644 --- a/infra/conf/wireguard_test.go +++ b/infra/conf/wireguard_test.go @@ -24,7 +24,8 @@ func TestWireGuardOutbound(t *testing.T) { } ], "mtu": 1300, - "workers": 2 + "workers": 2, + "domainStrategy": "ForceIPv6v4" }`, Parser: loadJSON(creator), Output: &wireguard.DeviceConfig{ @@ -41,8 +42,9 @@ func TestWireGuardOutbound(t *testing.T) { AllowedIps: []string{"0.0.0.0/0", "::0/0"}, }, }, - Mtu: 1300, - NumWorkers: 2, + Mtu: 1300, + NumWorkers: 2, + DomainStrategy: wireguard.DeviceConfig_FORCE_IP64, }, }, }) diff --git a/proxy/wireguard/config.go b/proxy/wireguard/config.go new file mode 100644 index 00000000..75622753 --- /dev/null +++ b/proxy/wireguard/config.go @@ -0,0 +1,25 @@ +package wireguard + +func (c *DeviceConfig) preferIP4() bool { + return c.DomainStrategy == DeviceConfig_FORCE_IP || + c.DomainStrategy == DeviceConfig_FORCE_IP4 || + c.DomainStrategy == DeviceConfig_FORCE_IP46 +} + +func (c *DeviceConfig) preferIP6() bool { + return c.DomainStrategy == DeviceConfig_FORCE_IP || + c.DomainStrategy == DeviceConfig_FORCE_IP6 || + c.DomainStrategy == DeviceConfig_FORCE_IP64 +} + +func (c *DeviceConfig) hasFallback() bool { + return c.DomainStrategy == DeviceConfig_FORCE_IP46 || c.DomainStrategy == DeviceConfig_FORCE_IP64 +} + +func (c *DeviceConfig) fallbackIP4() bool { + return c.DomainStrategy == DeviceConfig_FORCE_IP64 +} + +func (c *DeviceConfig) fallbackIP6() bool { + return c.DomainStrategy == DeviceConfig_FORCE_IP46 +} diff --git a/proxy/wireguard/config.pb.go b/proxy/wireguard/config.pb.go index 442d78f0..6af1f41c 100644 --- a/proxy/wireguard/config.pb.go +++ b/proxy/wireguard/config.pb.go @@ -20,6 +20,61 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type DeviceConfig_DomainStrategy int32 + +const ( + DeviceConfig_FORCE_IP DeviceConfig_DomainStrategy = 0 + DeviceConfig_FORCE_IP4 DeviceConfig_DomainStrategy = 1 + DeviceConfig_FORCE_IP6 DeviceConfig_DomainStrategy = 2 + DeviceConfig_FORCE_IP46 DeviceConfig_DomainStrategy = 3 + DeviceConfig_FORCE_IP64 DeviceConfig_DomainStrategy = 4 +) + +// Enum value maps for DeviceConfig_DomainStrategy. +var ( + DeviceConfig_DomainStrategy_name = map[int32]string{ + 0: "FORCE_IP", + 1: "FORCE_IP4", + 2: "FORCE_IP6", + 3: "FORCE_IP46", + 4: "FORCE_IP64", + } + DeviceConfig_DomainStrategy_value = map[string]int32{ + "FORCE_IP": 0, + "FORCE_IP4": 1, + "FORCE_IP6": 2, + "FORCE_IP46": 3, + "FORCE_IP64": 4, + } +) + +func (x DeviceConfig_DomainStrategy) Enum() *DeviceConfig_DomainStrategy { + p := new(DeviceConfig_DomainStrategy) + *p = x + return p +} + +func (x DeviceConfig_DomainStrategy) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (DeviceConfig_DomainStrategy) Descriptor() protoreflect.EnumDescriptor { + return file_proxy_wireguard_config_proto_enumTypes[0].Descriptor() +} + +func (DeviceConfig_DomainStrategy) Type() protoreflect.EnumType { + return &file_proxy_wireguard_config_proto_enumTypes[0] +} + +func (x DeviceConfig_DomainStrategy) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use DeviceConfig_DomainStrategy.Descriptor instead. +func (DeviceConfig_DomainStrategy) EnumDescriptor() ([]byte, []int) { + return file_proxy_wireguard_config_proto_rawDescGZIP(), []int{1, 0} +} + type PeerConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -104,12 +159,13 @@ type DeviceConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - SecretKey string `protobuf:"bytes,1,opt,name=secret_key,json=secretKey,proto3" json:"secret_key,omitempty"` - Endpoint []string `protobuf:"bytes,2,rep,name=endpoint,proto3" json:"endpoint,omitempty"` - Peers []*PeerConfig `protobuf:"bytes,3,rep,name=peers,proto3" json:"peers,omitempty"` - Mtu int32 `protobuf:"varint,4,opt,name=mtu,proto3" json:"mtu,omitempty"` - NumWorkers int32 `protobuf:"varint,5,opt,name=num_workers,json=numWorkers,proto3" json:"num_workers,omitempty"` - Reserved []byte `protobuf:"bytes,6,opt,name=reserved,proto3" json:"reserved,omitempty"` + SecretKey string `protobuf:"bytes,1,opt,name=secret_key,json=secretKey,proto3" json:"secret_key,omitempty"` + Endpoint []string `protobuf:"bytes,2,rep,name=endpoint,proto3" json:"endpoint,omitempty"` + Peers []*PeerConfig `protobuf:"bytes,3,rep,name=peers,proto3" json:"peers,omitempty"` + Mtu int32 `protobuf:"varint,4,opt,name=mtu,proto3" json:"mtu,omitempty"` + NumWorkers int32 `protobuf:"varint,5,opt,name=num_workers,json=numWorkers,proto3" json:"num_workers,omitempty"` + Reserved []byte `protobuf:"bytes,6,opt,name=reserved,proto3" json:"reserved,omitempty"` + DomainStrategy DeviceConfig_DomainStrategy `protobuf:"varint,7,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.proxy.wireguard.DeviceConfig_DomainStrategy" json:"domain_strategy,omitempty"` } func (x *DeviceConfig) Reset() { @@ -186,6 +242,13 @@ func (x *DeviceConfig) GetReserved() []byte { return nil } +func (x *DeviceConfig) GetDomainStrategy() DeviceConfig_DomainStrategy { + if x != nil { + return x.DomainStrategy + } + return DeviceConfig_FORCE_IP +} + var File_proxy_wireguard_config_proto protoreflect.FileDescriptor var file_proxy_wireguard_config_proto_rawDesc = []byte{ @@ -203,7 +266,7 @@ var file_proxy_wireguard_config_proto_rawDesc = []byte{ 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, - 0x64, 0x49, 0x70, 0x73, 0x22, 0xd0, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, + 0x64, 0x49, 0x70, 0x73, 0x22, 0x8a, 0x03, 0x0a, 0x0c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, @@ -216,13 +279,25 @@ var file_proxy_wireguard_config_proto_rawDesc = []byte{ 0x6d, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, - 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, - 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, - 0x61, 0x72, 0x64, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, - 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, - 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x57, 0x69, - 0x72, 0x65, 0x47, 0x75, 0x61, 0x72, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x12, 0x5a, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x31, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x77, 0x69, + 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, + 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, + 0x65, 0x67, 0x79, 0x22, 0x5c, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, + 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, + 0x50, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, + 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, + 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, + 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, + 0x04, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x2e, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x50, 0x01, 0x5a, + 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, + 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x2f, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, + 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x57, 0x69, 0x72, 0x65, 0x47, 0x75, 0x61, 0x72, + 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -237,18 +312,21 @@ func file_proxy_wireguard_config_proto_rawDescGZIP() []byte { return file_proxy_wireguard_config_proto_rawDescData } +var file_proxy_wireguard_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_proxy_wireguard_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_proxy_wireguard_config_proto_goTypes = []interface{}{ - (*PeerConfig)(nil), // 0: xray.proxy.wireguard.PeerConfig - (*DeviceConfig)(nil), // 1: xray.proxy.wireguard.DeviceConfig + (DeviceConfig_DomainStrategy)(0), // 0: xray.proxy.wireguard.DeviceConfig.DomainStrategy + (*PeerConfig)(nil), // 1: xray.proxy.wireguard.PeerConfig + (*DeviceConfig)(nil), // 2: xray.proxy.wireguard.DeviceConfig } var file_proxy_wireguard_config_proto_depIdxs = []int32{ - 0, // 0: xray.proxy.wireguard.DeviceConfig.peers:type_name -> xray.proxy.wireguard.PeerConfig - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 1, // 0: xray.proxy.wireguard.DeviceConfig.peers:type_name -> xray.proxy.wireguard.PeerConfig + 0, // 1: xray.proxy.wireguard.DeviceConfig.domain_strategy:type_name -> xray.proxy.wireguard.DeviceConfig.DomainStrategy + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_proxy_wireguard_config_proto_init() } @@ -287,13 +365,14 @@ func file_proxy_wireguard_config_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proxy_wireguard_config_proto_rawDesc, - NumEnums: 0, + NumEnums: 1, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_wireguard_config_proto_goTypes, DependencyIndexes: file_proxy_wireguard_config_proto_depIdxs, + EnumInfos: file_proxy_wireguard_config_proto_enumTypes, MessageInfos: file_proxy_wireguard_config_proto_msgTypes, }.Build() File_proxy_wireguard_config_proto = out.File diff --git a/proxy/wireguard/config.proto b/proxy/wireguard/config.proto index 810a1126..0a12c009 100644 --- a/proxy/wireguard/config.proto +++ b/proxy/wireguard/config.proto @@ -15,10 +15,18 @@ message PeerConfig { } message DeviceConfig { + enum DomainStrategy { + FORCE_IP = 0; + FORCE_IP4 = 1; + FORCE_IP6 = 2; + FORCE_IP46 = 3; + FORCE_IP64 = 4; + } string secret_key = 1; repeated string endpoint = 2; repeated PeerConfig peers = 3; int32 mtu = 4; int32 num_workers = 5; bytes reserved = 6; + DomainStrategy domain_strategy = 7; } \ No newline at end of file diff --git a/proxy/wireguard/wireguard.go b/proxy/wireguard/wireguard.go index 231776e7..48e2ace3 100644 --- a/proxy/wireguard/wireguard.go +++ b/proxy/wireguard/wireguard.go @@ -31,6 +31,7 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" @@ -159,15 +160,23 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte addr := destination.Address if addr.Family().IsDomain() { ips, err := h.dns.LookupIP(addr.Domain(), dns.IPOption{ - IPv4Enable: h.hasIPv4, - IPv6Enable: h.hasIPv6, + IPv4Enable: h.hasIPv4 && h.conf.preferIP4(), + IPv6Enable: h.hasIPv6 && h.conf.preferIP6(), }) + { // Resolve fallback + if (len(ips) == 0 || err != nil) && h.conf.hasFallback() { + ips, err = h.dns.LookupIP(addr.Domain(), dns.IPOption{ + IPv4Enable: h.hasIPv4 && h.conf.fallbackIP4(), + IPv6Enable: h.hasIPv6 && h.conf.fallbackIP6(), + }) + } + } if err != nil { return newError("failed to lookup DNS").Base(err) } else if len(ips) == 0 { return dns.ErrEmptyResponse } - addr = net.IPAddress(ips[0]) + addr = net.IPAddress(ips[dice.Roll(len(ips))]) } var newCtx context.Context