1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-06-26 01:15:38 +00:00

support subdomain matching in dns hosts

This commit is contained in:
Darien Raymond 2018-06-26 22:34:05 +02:00
parent cb0eb91f2b
commit d5f7728acd
No known key found for this signature in database
GPG Key ID: 7251FFA14BB18169
6 changed files with 262 additions and 55 deletions

View File

@ -1,18 +0,0 @@
package dns
import (
"net"
)
func (c *Config) GetInternalHosts() map[string]net.IP {
hosts := make(map[string]net.IP)
for domain, ipOrDomain := range c.GetHosts() {
address := ipOrDomain.AsAddress()
if address.Family().IsDomain() {
newError("ignoring domain address in static hosts: ", address.Domain()).AtWarning().WriteToLog()
continue
}
hosts[domain] = address.IP()
}
return hosts
}

View File

@ -16,24 +16,49 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type Config_HostMapping_Type int32
const (
Config_HostMapping_Full Config_HostMapping_Type = 0
Config_HostMapping_SubDomain Config_HostMapping_Type = 1
)
var Config_HostMapping_Type_name = map[int32]string{
0: "Full",
1: "SubDomain",
}
var Config_HostMapping_Type_value = map[string]int32{
"Full": 0,
"SubDomain": 1,
}
func (x Config_HostMapping_Type) String() string {
return proto.EnumName(Config_HostMapping_Type_name, int32(x))
}
func (Config_HostMapping_Type) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_config_299ae69458dda3a0, []int{0, 2, 0}
}
type Config struct {
// Nameservers used by this DNS. Only traditional UDP servers are support at the moment.
// A special value 'localhost' as a domain address can be set to use DNS on local system.
NameServers []*net.Endpoint `protobuf:"bytes,1,rep,name=NameServers,proto3" json:"NameServers,omitempty"`
// Static hosts. Domain to IP.
Hosts map[string]*net.IPOrDomain `protobuf:"bytes,2,rep,name=Hosts,proto3" json:"Hosts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
// Deprecated. Use static_hosts.
Hosts map[string]*net.IPOrDomain `protobuf:"bytes,2,rep,name=Hosts,proto3" json:"Hosts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Deprecated: Do not use.
// Client IP for EDNS client subnet.
ClientIp *Config_ClientIP `protobuf:"bytes,3,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
ClientIp *Config_ClientIP `protobuf:"bytes,3,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"`
StaticHosts []*Config_HostMapping `protobuf:"bytes,4,rep,name=static_hosts,json=staticHosts,proto3" json:"static_hosts,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Config) Reset() { *m = Config{} }
func (m *Config) String() string { return proto.CompactTextString(m) }
func (*Config) ProtoMessage() {}
func (*Config) Descriptor() ([]byte, []int) {
return fileDescriptor_config_209d2630698ab6f5, []int{0}
return fileDescriptor_config_299ae69458dda3a0, []int{0}
}
func (m *Config) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Config.Unmarshal(m, b)
@ -60,6 +85,7 @@ func (m *Config) GetNameServers() []*net.Endpoint {
return nil
}
// Deprecated: Do not use.
func (m *Config) GetHosts() map[string]*net.IPOrDomain {
if m != nil {
return m.Hosts
@ -74,6 +100,13 @@ func (m *Config) GetClientIp() *Config_ClientIP {
return nil
}
func (m *Config) GetStaticHosts() []*Config_HostMapping {
if m != nil {
return m.StaticHosts
}
return nil
}
type Config_ClientIP struct {
// IPv4 address of the client. Must be 4 bytes.
V4 []byte `protobuf:"bytes,1,opt,name=v4,proto3" json:"v4,omitempty"`
@ -88,7 +121,7 @@ func (m *Config_ClientIP) Reset() { *m = Config_ClientIP{} }
func (m *Config_ClientIP) String() string { return proto.CompactTextString(m) }
func (*Config_ClientIP) ProtoMessage() {}
func (*Config_ClientIP) Descriptor() ([]byte, []int) {
return fileDescriptor_config_209d2630698ab6f5, []int{0, 1}
return fileDescriptor_config_299ae69458dda3a0, []int{0, 1}
}
func (m *Config_ClientIP) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Config_ClientIP.Unmarshal(m, b)
@ -122,38 +155,100 @@ func (m *Config_ClientIP) GetV6() []byte {
return nil
}
type Config_HostMapping struct {
Type Config_HostMapping_Type `protobuf:"varint,1,opt,name=type,proto3,enum=v2ray.core.app.dns.Config_HostMapping_Type" json:"type,omitempty"`
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
Ip [][]byte `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Config_HostMapping) Reset() { *m = Config_HostMapping{} }
func (m *Config_HostMapping) String() string { return proto.CompactTextString(m) }
func (*Config_HostMapping) ProtoMessage() {}
func (*Config_HostMapping) Descriptor() ([]byte, []int) {
return fileDescriptor_config_299ae69458dda3a0, []int{0, 2}
}
func (m *Config_HostMapping) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Config_HostMapping.Unmarshal(m, b)
}
func (m *Config_HostMapping) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Config_HostMapping.Marshal(b, m, deterministic)
}
func (dst *Config_HostMapping) XXX_Merge(src proto.Message) {
xxx_messageInfo_Config_HostMapping.Merge(dst, src)
}
func (m *Config_HostMapping) XXX_Size() int {
return xxx_messageInfo_Config_HostMapping.Size(m)
}
func (m *Config_HostMapping) XXX_DiscardUnknown() {
xxx_messageInfo_Config_HostMapping.DiscardUnknown(m)
}
var xxx_messageInfo_Config_HostMapping proto.InternalMessageInfo
func (m *Config_HostMapping) GetType() Config_HostMapping_Type {
if m != nil {
return m.Type
}
return Config_HostMapping_Full
}
func (m *Config_HostMapping) GetDomain() string {
if m != nil {
return m.Domain
}
return ""
}
func (m *Config_HostMapping) GetIp() [][]byte {
if m != nil {
return m.Ip
}
return nil
}
func init() {
proto.RegisterType((*Config)(nil), "v2ray.core.app.dns.Config")
proto.RegisterMapType((map[string]*net.IPOrDomain)(nil), "v2ray.core.app.dns.Config.HostsEntry")
proto.RegisterType((*Config_ClientIP)(nil), "v2ray.core.app.dns.Config.ClientIP")
proto.RegisterType((*Config_HostMapping)(nil), "v2ray.core.app.dns.Config.HostMapping")
proto.RegisterEnum("v2ray.core.app.dns.Config_HostMapping_Type", Config_HostMapping_Type_name, Config_HostMapping_Type_value)
}
func init() {
proto.RegisterFile("v2ray.com/core/app/dns/config.proto", fileDescriptor_config_209d2630698ab6f5)
proto.RegisterFile("v2ray.com/core/app/dns/config.proto", fileDescriptor_config_299ae69458dda3a0)
}
var fileDescriptor_config_209d2630698ab6f5 = []byte{
// 338 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0x51, 0x4b, 0xf3, 0x30,
0x14, 0x86, 0x69, 0xcb, 0xc6, 0x96, 0x7d, 0x7c, 0x48, 0x2e, 0xa4, 0xf4, 0xc6, 0xe9, 0x10, 0x87,
0x42, 0x0a, 0x75, 0x4c, 0xd1, 0x1b, 0xe7, 0x36, 0x70, 0x37, 0x3a, 0x2a, 0x78, 0xa1, 0x17, 0x12,
0x9b, 0x28, 0xc1, 0xf5, 0x24, 0x24, 0xb1, 0xd0, 0xbf, 0xe4, 0xdf, 0xf1, 0x0f, 0xc9, 0x12, 0x86,
0x43, 0x9d, 0x77, 0x3d, 0xf4, 0x7d, 0x9e, 0xf7, 0x1c, 0x82, 0x7a, 0x55, 0xa6, 0x69, 0x4d, 0x0a,
0x59, 0xa6, 0x85, 0xd4, 0x3c, 0xa5, 0x4a, 0xa5, 0x0c, 0x4c, 0x5a, 0x48, 0x78, 0x16, 0x2f, 0x44,
0x69, 0x69, 0x25, 0xc6, 0xab, 0x90, 0xe6, 0x84, 0x2a, 0x45, 0x18, 0x98, 0xe4, 0xe0, 0x1b, 0x58,
0xc8, 0xb2, 0x94, 0x90, 0x02, 0xb7, 0x29, 0x65, 0x4c, 0x73, 0x63, 0x3c, 0x9c, 0x1c, 0x6d, 0x0e,
0x32, 0x6e, 0xac, 0x00, 0x6a, 0x85, 0x04, 0x1f, 0xde, 0xfb, 0x08, 0x51, 0x73, 0xec, 0xaa, 0xf1,
0x08, 0x75, 0xae, 0x69, 0xc9, 0x6f, 0xb9, 0xae, 0xb8, 0x36, 0x71, 0xd0, 0x8d, 0xfa, 0x9d, 0x6c,
0x87, 0xac, 0xad, 0xe2, 0x4d, 0x04, 0xb8, 0x25, 0x53, 0x60, 0x4a, 0x0a, 0xb0, 0xf9, 0x3a, 0x83,
0xcf, 0x51, 0xe3, 0x4a, 0x1a, 0x6b, 0xe2, 0xd0, 0xc1, 0xfb, 0xe4, 0xe7, 0x1d, 0xc4, 0xb7, 0x11,
0x97, 0x9b, 0x82, 0xd5, 0x75, 0xee, 0x19, 0x7c, 0x81, 0xda, 0xc5, 0x42, 0x70, 0xb0, 0x8f, 0x42,
0xc5, 0x51, 0x37, 0xe8, 0x77, 0xb2, 0xde, 0x1f, 0x82, 0xb1, 0xcb, 0xce, 0xe6, 0x79, 0xcb, 0x53,
0x33, 0x95, 0x3c, 0x20, 0xf4, 0xa5, 0xc5, 0x5b, 0x28, 0x7a, 0xe5, 0x75, 0x1c, 0x74, 0x83, 0x7e,
0x3b, 0x5f, 0x7e, 0xe2, 0x13, 0xd4, 0xa8, 0xe8, 0xe2, 0x8d, 0xc7, 0xa1, 0xb3, 0xef, 0x6e, 0xb8,
0x6d, 0x36, 0xbf, 0xd1, 0x13, 0x59, 0x52, 0x01, 0xb9, 0xcf, 0x9f, 0x85, 0xa7, 0x41, 0x72, 0x88,
0x5a, 0xab, 0x4a, 0xfc, 0x1f, 0x85, 0xd5, 0xc0, 0x99, 0xff, 0xe5, 0x61, 0x35, 0x70, 0xf3, 0xd0,
0x59, 0x97, 0xf3, 0xf0, 0x72, 0x80, 0xb6, 0x0b, 0x59, 0xfe, 0xb2, 0xfc, 0x3c, 0xb8, 0x8f, 0x18,
0x98, 0xf7, 0x10, 0xdf, 0x65, 0x39, 0xad, 0xc9, 0x78, 0xf9, 0x6f, 0xa4, 0x14, 0x99, 0x80, 0x79,
0x6a, 0xba, 0x27, 0x39, 0xfe, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x55, 0xb0, 0x77, 0x62, 0x23, 0x02,
0x00, 0x00,
var fileDescriptor_config_299ae69458dda3a0 = []byte{
// 444 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xd1, 0x6e, 0xd3, 0x30,
0x14, 0x86, 0x71, 0x92, 0x55, 0xed, 0xc9, 0x98, 0x2a, 0x5f, 0x4c, 0x51, 0x6e, 0x56, 0x36, 0x01,
0x15, 0x93, 0x1c, 0x29, 0x54, 0x03, 0x71, 0x33, 0xb6, 0x6e, 0x88, 0x5e, 0x00, 0x95, 0x87, 0xb8,
0x80, 0x8b, 0xc9, 0x4b, 0xcc, 0xb0, 0x68, 0x6c, 0xcb, 0x76, 0x23, 0xe5, 0x49, 0x78, 0x07, 0x9e,
0x81, 0x87, 0x43, 0xb1, 0x99, 0xa8, 0x80, 0x01, 0x77, 0x3d, 0xea, 0xff, 0x9d, 0xff, 0x3b, 0x56,
0xe0, 0xa0, 0x2d, 0x0d, 0xeb, 0x48, 0xa5, 0x9a, 0xa2, 0x52, 0x86, 0x17, 0x4c, 0xeb, 0xa2, 0x96,
0xb6, 0xa8, 0x94, 0xfc, 0x28, 0xae, 0x89, 0x36, 0xca, 0x29, 0x8c, 0x6f, 0x42, 0x86, 0x13, 0xa6,
0x35, 0xa9, 0xa5, 0xcd, 0x1f, 0xfe, 0x02, 0x56, 0xaa, 0x69, 0x94, 0x2c, 0x24, 0x77, 0x05, 0xab,
0x6b, 0xc3, 0xad, 0x0d, 0x70, 0x7e, 0x78, 0x7b, 0xb0, 0xe6, 0xd6, 0x09, 0xc9, 0x9c, 0x50, 0x32,
0x84, 0xf7, 0xbf, 0x25, 0x30, 0x98, 0xfb, 0x6a, 0x7c, 0x02, 0xe9, 0x6b, 0xd6, 0xf0, 0x0b, 0x6e,
0x5a, 0x6e, 0x6c, 0x86, 0x26, 0xf1, 0x34, 0x2d, 0xf7, 0xc8, 0x86, 0x4a, 0xd8, 0x44, 0x24, 0x77,
0xe4, 0x5c, 0xd6, 0x5a, 0x09, 0xe9, 0xe8, 0x26, 0x83, 0x8f, 0x61, 0xeb, 0xa5, 0xb2, 0xce, 0x66,
0x91, 0x87, 0xef, 0x93, 0xdf, 0xef, 0x20, 0xa1, 0x8d, 0xf8, 0xdc, 0xb9, 0x74, 0xa6, 0x3b, 0x8d,
0x32, 0x44, 0x03, 0x87, 0x9f, 0xc3, 0xa8, 0x5a, 0x09, 0x2e, 0xdd, 0xa5, 0xd0, 0x59, 0x3c, 0x41,
0xd3, 0xb4, 0x3c, 0xf8, 0xcb, 0x92, 0xb9, 0xcf, 0x2e, 0x96, 0x74, 0x18, 0xa8, 0x85, 0xc6, 0x0b,
0xd8, 0xb6, 0x8e, 0x39, 0x51, 0x5d, 0x7e, 0xf2, 0x26, 0x89, 0x37, 0x79, 0xf0, 0x0f, 0x93, 0x57,
0x4c, 0x6b, 0x21, 0xaf, 0x69, 0x1a, 0x58, 0x2f, 0x93, 0x7f, 0x00, 0xf8, 0x69, 0x89, 0xc7, 0x10,
0x7f, 0xe6, 0x5d, 0x86, 0x26, 0x68, 0x3a, 0xa2, 0xfd, 0x4f, 0xfc, 0x04, 0xb6, 0x5a, 0xb6, 0x5a,
0xf3, 0x2c, 0xf2, 0xa2, 0xf7, 0x6e, 0x79, 0xaa, 0xc5, 0xf2, 0x8d, 0x39, 0x53, 0x0d, 0x13, 0x92,
0x86, 0xfc, 0xb3, 0xe8, 0x29, 0xca, 0x1f, 0xc1, 0xf0, 0xc6, 0x1e, 0xef, 0x40, 0xd4, 0xce, 0xfc,
0xe6, 0x6d, 0x1a, 0xb5, 0x33, 0x3f, 0x1f, 0xf9, 0xad, 0xfd, 0x7c, 0x94, 0x7f, 0x41, 0x90, 0x6e,
0x58, 0xe2, 0x63, 0x48, 0x5c, 0xa7, 0xb9, 0x27, 0x76, 0xca, 0xc3, 0xff, 0xbb, 0x8d, 0xbc, 0xed,
0x34, 0xa7, 0x1e, 0xc4, 0xbb, 0x30, 0xa8, 0xbd, 0x91, 0x2f, 0x19, 0xd1, 0x1f, 0x53, 0x5f, 0xec,
0xdf, 0x3d, 0xee, 0x8b, 0x85, 0xde, 0xdf, 0x83, 0xa4, 0xa7, 0xf0, 0x10, 0x92, 0x17, 0xeb, 0xd5,
0x6a, 0x7c, 0x07, 0xdf, 0x85, 0xd1, 0xc5, 0xfa, 0x2a, 0x9c, 0x33, 0x46, 0xa7, 0x33, 0xd8, 0xad,
0x54, 0xf3, 0x07, 0x81, 0x25, 0x7a, 0x1f, 0xd7, 0xd2, 0x7e, 0x8d, 0xf0, 0xbb, 0x92, 0xb2, 0x8e,
0xcc, 0xfb, 0xff, 0x4e, 0xb4, 0x26, 0x67, 0xd2, 0x5e, 0x0d, 0xfc, 0xb7, 0xf7, 0xf8, 0x7b, 0x00,
0x00, 0x00, 0xff, 0xff, 0xee, 0x90, 0x3f, 0xc9, 0x0c, 0x03, 0x00, 0x00,
}

View File

@ -15,7 +15,8 @@ message Config {
repeated v2ray.core.common.net.Endpoint NameServers = 1;
// Static hosts. Domain to IP.
map<string, v2ray.core.common.net.IPOrDomain> Hosts = 2;
// Deprecated. Use static_hosts.
map<string, v2ray.core.common.net.IPOrDomain> Hosts = 2 [deprecated = true];
message ClientIP {
// IPv4 address of the client. Must be 4 bytes.
@ -26,4 +27,17 @@ message Config {
// Client IP for EDNS client subnet.
ClientIP client_ip = 3;
message HostMapping {
enum Type {
Full = 0;
SubDomain = 1;
}
Type type = 1;
string domain = 2;
repeated bytes ip = 3;
}
repeated HostMapping static_hosts = 4;
}

67
app/dns/hosts.go Normal file
View File

@ -0,0 +1,67 @@
package dns
import (
"v2ray.com/core/common"
"v2ray.com/core/common/net"
"v2ray.com/core/common/strmatcher"
)
type StaticHosts struct {
ips map[uint32][]net.IP
matchers *strmatcher.MatcherGroup
}
var typeMap = map[Config_HostMapping_Type]strmatcher.Type{
Config_HostMapping_Full: strmatcher.Full,
Config_HostMapping_SubDomain: strmatcher.Domain,
}
func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDomain) (*StaticHosts, error) {
g := strmatcher.NewMatcherGroup()
sh := &StaticHosts{
ips: make(map[uint32][]net.IP),
matchers: g,
}
if legacy != nil {
for domain, ip := range legacy {
matcher, err := strmatcher.Full.New(domain)
common.Must(err)
id := g.Add(matcher)
address := ip.AsAddress()
if address.Family().IsDomain() {
return nil, newError("ignoring domain address in static hosts: ", address.Domain()).AtWarning()
}
sh.ips[id] = []net.IP{address.IP()}
}
}
for _, mapping := range hosts {
strMType, f := typeMap[mapping.Type]
if !f {
return nil, newError("unknown mapping type", mapping.Type).AtWarning()
}
matcher, err := strMType.New(mapping.Domain)
if err != nil {
return nil, newError("failed to create domain matcher").Base(err)
}
id := g.Add(matcher)
ips := make([]net.IP, len(mapping.Ip))
for idx, ip := range mapping.Ip {
ips[idx] = net.IP(ip)
}
sh.ips[id] = ips
}
return sh, nil
}
func (h *StaticHosts) LookupIP(domain string) []net.IP {
id := h.matchers.Match(domain)
if id == 0 {
return nil
}
return h.ips[id]
}

44
app/dns/hosts_test.go Normal file
View File

@ -0,0 +1,44 @@
package dns_test
import (
"testing"
. "v2ray.com/core/app/dns"
. "v2ray.com/ext/assert"
)
func TestStaticHosts(t *testing.T) {
assert := With(t)
pb := []*Config_HostMapping{
{
Type: Config_HostMapping_Full,
Domain: "v2ray.com",
Ip: [][]byte{
{1, 1, 1, 1},
},
},
{
Type: Config_HostMapping_SubDomain,
Domain: "v2ray.cn",
Ip: [][]byte{
{2, 2, 2, 2},
},
},
}
hosts, err := NewStaticHosts(pb, nil)
assert(err, IsNil)
{
ips := hosts.LookupIP("v2ray.com")
assert(len(ips), Equals, 1)
assert([]byte(ips[0]), Equals, []byte{1, 1, 1, 1})
}
{
ips := hosts.LookupIP("www.v2ray.cn")
assert(len(ips), Equals, 1)
assert([]byte(ips[0]), Equals, []byte{2, 2, 2, 2})
}
}

View File

@ -14,7 +14,7 @@ import (
type Server struct {
sync.Mutex
hosts map[string]net.IP
hosts *StaticHosts
servers []NameServer
clientIP *Config_ClientIP
}
@ -22,12 +22,17 @@ type Server struct {
func New(ctx context.Context, config *Config) (*Server, error) {
server := &Server{
servers: make([]NameServer, len(config.NameServers)),
hosts: config.GetInternalHosts(),
}
if config.ClientIp != nil {
server.clientIP = config.ClientIp
}
hosts, err := NewStaticHosts(config.StaticHosts, config.Hosts)
if err != nil {
return nil, newError("failed to create hosts").Base(err)
}
server.hosts = hosts
v := core.MustFromContext(ctx)
if err := v.RegisterFeature((*core.DNSClient)(nil), server); err != nil {
return nil, newError("unable to register DNSClient.").Base(err)
@ -65,8 +70,8 @@ func (s *Server) Close() error {
}
func (s *Server) LookupIP(domain string) ([]net.IP, error) {
if ip, found := s.hosts[domain]; found {
return []net.IP{ip}, nil
if ip := s.hosts.LookupIP(domain); len(ip) > 0 {
return ip, nil
}
var lastErr error