Skip to content

Commit

Permalink
support client subnet
Browse files Browse the repository at this point in the history
  • Loading branch information
DarienRaymond committed Jun 26, 2018
1 parent ff0ae91 commit e9e9de5
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 50 deletions.
120 changes: 91 additions & 29 deletions app/dns/config.pb.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,21 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
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" json:"NameServers,omitempty"`
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" json:"Hosts,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
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"`
// 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:"-"`
}

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_862435acfeec6b70, []int{0}
return fileDescriptor_config_209d2630698ab6f5, []int{0}
}
func (m *Config) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Config.Unmarshal(m, b)
Expand Down Expand Up @@ -65,33 +67,93 @@ func (m *Config) GetHosts() map[string]*net.IPOrDomain {
return nil
}

func (m *Config) GetClientIp() *Config_ClientIP {
if m != nil {
return m.ClientIp
}
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"`
// IPv6 address of the client. Must be 4 bytes.
V6 []byte `protobuf:"bytes,2,opt,name=v6,proto3" json:"v6,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}

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}
}
func (m *Config_ClientIP) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Config_ClientIP.Unmarshal(m, b)
}
func (m *Config_ClientIP) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Config_ClientIP.Marshal(b, m, deterministic)
}
func (dst *Config_ClientIP) XXX_Merge(src proto.Message) {
xxx_messageInfo_Config_ClientIP.Merge(dst, src)
}
func (m *Config_ClientIP) XXX_Size() int {
return xxx_messageInfo_Config_ClientIP.Size(m)
}
func (m *Config_ClientIP) XXX_DiscardUnknown() {
xxx_messageInfo_Config_ClientIP.DiscardUnknown(m)
}

var xxx_messageInfo_Config_ClientIP proto.InternalMessageInfo

func (m *Config_ClientIP) GetV4() []byte {
if m != nil {
return m.V4
}
return nil
}

func (m *Config_ClientIP) GetV6() []byte {
if m != nil {
return m.V6
}
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")
}

func init() {
proto.RegisterFile("v2ray.com/core/app/dns/config.proto", fileDescriptor_config_862435acfeec6b70)
}

var fileDescriptor_config_862435acfeec6b70 = []byte{
// 286 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x90, 0x41, 0x4b, 0x33, 0x31,
0x10, 0x86, 0xc9, 0x96, 0x16, 0xbe, 0xf4, 0xf2, 0x91, 0x83, 0x2c, 0xbd, 0x58, 0x15, 0xb1, 0x20,
0x4c, 0x60, 0x15, 0x14, 0x3d, 0xd5, 0xb6, 0xa0, 0x17, 0x2d, 0x2b, 0x78, 0xd0, 0x53, 0xdc, 0x44,
0x59, 0x34, 0x33, 0x21, 0x89, 0x0b, 0xfb, 0x97, 0xfc, 0x5f, 0xfe, 0x0f, 0xe9, 0x06, 0xb1, 0xa8,
0xbd, 0x85, 0xe4, 0x79, 0xf2, 0xbe, 0x33, 0x7c, 0xaf, 0x29, 0xbc, 0x6a, 0xa1, 0x22, 0x2b, 0x2b,
0xf2, 0x46, 0x2a, 0xe7, 0xa4, 0xc6, 0x20, 0x2b, 0xc2, 0xa7, 0xfa, 0x19, 0x9c, 0xa7, 0x48, 0x42,
0x7c, 0x41, 0xde, 0x80, 0x72, 0x0e, 0x34, 0x86, 0xd1, 0xc1, 0x0f, 0xb1, 0x22, 0x6b, 0x09, 0x25,
0x9a, 0x28, 0x95, 0xd6, 0xde, 0x84, 0x90, 0xe4, 0xd1, 0xe1, 0x66, 0x50, 0x9b, 0x10, 0x6b, 0x54,
0xb1, 0x26, 0x4c, 0xf0, 0xee, 0x07, 0xe3, 0x83, 0x59, 0x17, 0x2d, 0xa6, 0x7c, 0x78, 0xad, 0xac,
0xb9, 0x35, 0xbe, 0x31, 0x3e, 0xe4, 0x6c, 0xdc, 0x9b, 0x0c, 0x8b, 0x6d, 0x58, 0xab, 0x92, 0x7e,
0x02, 0x34, 0x11, 0x16, 0xa8, 0x1d, 0xd5, 0x18, 0xcb, 0x75, 0x47, 0x9c, 0xf3, 0xfe, 0x25, 0x85,
0x18, 0xf2, 0xac, 0x93, 0xf7, 0xe1, 0xf7, 0x1c, 0x90, 0xd2, 0xa0, 0xe3, 0x16, 0x18, 0x7d, 0x5b,
0x26, 0x67, 0xf4, 0xc0, 0xf9, 0xf7, 0xa5, 0xf8, 0xcf, 0x7b, 0x2f, 0xa6, 0xcd, 0xd9, 0x98, 0x4d,
0xfe, 0x95, 0xab, 0xa3, 0x38, 0xe1, 0xfd, 0x46, 0xbd, 0xbe, 0x99, 0x3c, 0x1b, 0xb3, 0xc9, 0xb0,
0xd8, 0xd9, 0xd0, 0xec, 0x6a, 0x79, 0xe3, 0xe7, 0x64, 0x55, 0x8d, 0x65, 0xe2, 0xcf, 0xb2, 0x53,
0x76, 0x71, 0xcc, 0xb7, 0x2a, 0xb2, 0x7f, 0xf4, 0x59, 0xb2, 0xfb, 0x9e, 0xc6, 0xf0, 0x9e, 0x89,
0xbb, 0xa2, 0x54, 0x2d, 0xcc, 0x56, 0x6f, 0x53, 0xe7, 0x60, 0x8e, 0xe1, 0x71, 0xd0, 0x2d, 0xe9,
0xe8, 0x33, 0x00, 0x00, 0xff, 0xff, 0x2d, 0xe1, 0x96, 0x19, 0xb5, 0x01, 0x00, 0x00,
proto.RegisterFile("v2ray.com/core/app/dns/config.proto", fileDescriptor_config_209d2630698ab6f5)
}

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,
}
10 changes: 10 additions & 0 deletions app/dns/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,14 @@ message Config {

// Static hosts. Domain to IP.
map<string, v2ray.core.common.net.IPOrDomain> Hosts = 2;

message ClientIP {
// IPv4 address of the client. Must be 4 bytes.
bytes v4 = 1;
// IPv6 address of the client. Must be 4 bytes.
bytes v6 = 2;
}

// Client IP for EDNS client subnet.
ClientIP client_ip = 3;
}
11 changes: 8 additions & 3 deletions app/dns/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,20 @@ import (

type Server struct {
sync.Mutex
hosts map[string]net.IP
servers []NameServer
hosts map[string]net.IP
servers []NameServer
clientIP *Config_ClientIP
}

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
}

v := core.MustFromContext(ctx)
if err := v.RegisterFeature((*core.DNSClient)(nil), server); err != nil {
return nil, newError("unable to register DNSClient.").Base(err)
Expand All @@ -38,7 +43,7 @@ func New(ctx context.Context, config *Config) (*Server, error) {
dest.Network = net.Network_UDP
}
if dest.Network == net.Network_UDP {
server.servers[idx] = NewClassicNameServer(dest, v.Dispatcher())
server.servers[idx] = NewClassicNameServer(dest, v.Dispatcher(), server.clientIP)
}
}
}
Expand Down
74 changes: 56 additions & 18 deletions app/dns/udpns.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@ type ClassicNameServer struct {
udpServer *udp.Dispatcher
cleanup *task.Periodic
reqID uint32
clientIP *Config_ClientIP
}

func NewClassicNameServer(address net.Destination, dispatcher core.Dispatcher) *ClassicNameServer {
func NewClassicNameServer(address net.Destination, dispatcher core.Dispatcher, clientIP *Config_ClientIP) *ClassicNameServer {
s := &ClassicNameServer{
address: address,
ips: make(map[string][]IPRecord),
udpServer: udp.NewDispatcher(dispatcher),
clientIP: clientIP,
}
s.cleanup = &task.Periodic{
Interval: time.Minute,
Expand Down Expand Up @@ -134,27 +136,66 @@ func (s *ClassicNameServer) updateIP(domain string, ips []IPRecord) {
s.updated.Signal()
}

func (s *ClassicNameServer) getMsgOptions() *dns.OPT {
if s.clientIP == nil {
return nil
}

o := new(dns.OPT)
o.Hdr.Name = "."
o.Hdr.Rrtype = dns.TypeOPT

if len(s.clientIP.V4) == 4 {
e := new(dns.EDNS0_SUBNET)
e.Code = dns.EDNS0SUBNET
e.Family = 1 // 1 for IPv4 source address, 2 for IPv6
e.SourceNetmask = 24 // 32 for IPV4, 128 for IPv6
e.SourceScope = 0
e.Address = net.IP(s.clientIP.V4)
o.Option = append(o.Option, e)
}

if len(s.clientIP.V6) == 16 {
e := new(dns.EDNS0_SUBNET)
e.Code = dns.EDNS0SUBNET
e.Family = 2 // 1 for IPv4 source address, 2 for IPv6
e.SourceNetmask = 24 // 32 for IPV4, 128 for IPv6
e.SourceScope = 0
e.Address = net.IP(s.clientIP.V6)
o.Option = append(o.Option, e)
}

return o

}

func (s *ClassicNameServer) buildMsgs(domain string) []*dns.Msg {
allowMulti := multiQuestionDNS[s.address.Address]

qA := dns.Question{
Name: domain,
Qtype: dns.TypeA,
Qclass: dns.ClassINET,
}

qAAAA := dns.Question{
Name: domain,
Qtype: dns.TypeAAAA,
Qclass: dns.ClassINET,
}

var msgs []*dns.Msg

{
msg := new(dns.Msg)
msg.Id = uint16(atomic.AddUint32(&s.reqID, 1))
msg.RecursionDesired = true
msg.Question = []dns.Question{
{
Name: domain,
Qtype: dns.TypeA,
Qclass: dns.ClassINET,
}}
msg.Question = []dns.Question{qA}
if allowMulti {
msg.Question = append(msg.Question, dns.Question{
Name: domain,
Qtype: dns.TypeAAAA,
Qclass: dns.ClassINET,
})
msg.Question = append(msg.Question, qAAAA)
}
if opt := s.getMsgOptions(); opt != nil {
msg.Extra = append(msg.Extra, opt)
}
msgs = append(msgs, msg)
}
Expand All @@ -163,12 +204,9 @@ func (s *ClassicNameServer) buildMsgs(domain string) []*dns.Msg {
msg := new(dns.Msg)
msg.Id = uint16(atomic.AddUint32(&s.reqID, 1))
msg.RecursionDesired = true
msg.Question = []dns.Question{
{
Name: domain,
Qtype: dns.TypeAAAA,
Qclass: dns.ClassINET,
},
msg.Question = []dns.Question{qAAAA}
if opt := s.getMsgOptions(); opt != nil {
msg.Extra = append(msg.Extra, opt)
}
msgs = append(msgs, msg)
}
Expand Down

0 comments on commit e9e9de5

Please sign in to comment.