Browse Source

support dest override in dns outbound

Darien Raymond 6 years ago
parent
commit
114ec4c74b

+ 16 - 2
infra/conf/dns_proxy.go

@@ -2,11 +2,25 @@ package conf
 
 import (
 	"github.com/golang/protobuf/proto"
+	"v2ray.com/core/common/net"
 	"v2ray.com/core/proxy/dns"
 )
 
-type DnsOutboundConfig struct{}
+type DnsOutboundConfig struct {
+	Network Network  `json:network`
+	Address *Address `json:address`
+	Port    uint16   `json:port`
+}
 
 func (c *DnsOutboundConfig) Build() (proto.Message, error) {
-	return new(dns.Config), nil
+	config := &dns.Config{
+		Server: &net.Endpoint{
+			Network: c.Network.Build(),
+			Port:    uint32(c.Port),
+		},
+	}
+	if c.Address != nil {
+		config.Server.Address = c.Address.Build()
+	}
+	return config, nil
 }

+ 33 - 0
infra/conf/dns_proxy_test.go

@@ -0,0 +1,33 @@
+package conf_test
+
+import (
+	"testing"
+
+	"v2ray.com/core/common/net"
+	. "v2ray.com/core/infra/conf"
+	"v2ray.com/core/proxy/dns"
+)
+
+func TestDnsProxyConfig(t *testing.T) {
+	creator := func() Buildable {
+		return new(DnsOutboundConfig)
+	}
+
+	runMultiTestCase(t, []TestCase{
+		{
+			Input: `{
+				"address": "8.8.8.8",
+				"port": 53,
+				"network": "tcp"
+			}`,
+			Parser: loadJSON(creator),
+			Output: &dns.Config{
+				Server: &net.Endpoint{
+					Network: net.Network_TCP,
+					Address: net.NewIPOrDomain(net.IPAddress([]byte{8, 8, 8, 8})),
+					Port:    53,
+				},
+			},
+		},
+	})
+}

+ 3 - 1
infra/conf/v2ray_test.go

@@ -221,7 +221,9 @@ func TestV2RayConfig(t *testing.T) {
 								},
 							},
 						}),
-						ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{}),
+						ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{
+							Server: &net.Endpoint{},
+						}),
 					},
 				},
 				Inbound: []*core.InboundHandlerConfig{

+ 26 - 12
proxy/dns/config.pb.go

@@ -4,6 +4,7 @@ import (
 	fmt "fmt"
 	proto "github.com/golang/protobuf/proto"
 	math "math"
+	net "v2ray.com/core/common/net"
 )
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -18,9 +19,11 @@ var _ = math.Inf
 const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
 type Config struct {
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	// Server is the DNS server address. If specified, this address overrides the original one.
+	Server               *net.Endpoint `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}      `json:"-"`
+	XXX_unrecognized     []byte        `json:"-"`
+	XXX_sizecache        int32         `json:"-"`
 }
 
 func (m *Config) Reset()         { *m = Config{} }
@@ -48,6 +51,13 @@ func (m *Config) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_Config proto.InternalMessageInfo
 
+func (m *Config) GetServer() *net.Endpoint {
+	if m != nil {
+		return m.Server
+	}
+	return nil
+}
+
 func init() {
 	proto.RegisterType((*Config)(nil), "v2ray.core.proxy.dns.Config")
 }
@@ -57,13 +67,17 @@ func init() {
 }
 
 var fileDescriptor_c49bb2d51e576d57 = []byte{
-	// 123 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2d, 0x33, 0x2a, 0x4a,
-	0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x2f, 0x28, 0xca, 0xaf, 0xa8,
-	0xd4, 0x4f, 0xc9, 0x2b, 0xd6, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, 0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f,
-	0xc9, 0x17, 0x12, 0x81, 0x29, 0x2b, 0x4a, 0xd5, 0x03, 0x2b, 0xd1, 0x4b, 0xc9, 0x2b, 0x56, 0xe2,
-	0xe0, 0x62, 0x73, 0x06, 0xab, 0x72, 0xb2, 0xe0, 0x92, 0x48, 0xce, 0xcf, 0xd5, 0xc3, 0xa6, 0x2a,
-	0x80, 0x31, 0x8a, 0x39, 0x25, 0xaf, 0x78, 0x15, 0x93, 0x48, 0x98, 0x51, 0x50, 0x62, 0xa5, 0x9e,
-	0x33, 0x48, 0x36, 0x00, 0x2c, 0xeb, 0x92, 0x57, 0x9c, 0xc4, 0x06, 0xb6, 0xc0, 0x18, 0x10, 0x00,
-	0x00, 0xff, 0xff, 0xee, 0x22, 0xde, 0xc9, 0x89, 0x00, 0x00, 0x00,
+	// 190 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x8e, 0x31, 0xab, 0xc2, 0x30,
+	0x14, 0x46, 0xe9, 0x7b, 0xd0, 0x21, 0x6f, 0x2b, 0x1d, 0xca, 0x5b, 0xde, 0x43, 0x10, 0x04, 0xe1,
+	0x06, 0xea, 0xa0, 0xab, 0x56, 0xf7, 0xd2, 0xc1, 0xc1, 0xad, 0x26, 0x51, 0x32, 0xe4, 0xde, 0x72,
+	0x13, 0x8a, 0xfd, 0x4b, 0xfe, 0x4a, 0x31, 0x55, 0x10, 0x71, 0xfe, 0xce, 0x77, 0x38, 0x62, 0xda,
+	0x97, 0xdc, 0x0e, 0xa0, 0xc8, 0x49, 0x45, 0x6c, 0x64, 0xc7, 0x74, 0x19, 0xa4, 0x46, 0x2f, 0x15,
+	0xe1, 0xc9, 0x9e, 0xa1, 0x63, 0x0a, 0x94, 0xe5, 0x4f, 0x8c, 0x0d, 0x44, 0x04, 0x34, 0xfa, 0xdf,
+	0xf9, 0xdb, 0x59, 0x91, 0x73, 0x84, 0x12, 0x4d, 0x90, 0xda, 0xf8, 0x60, 0xb1, 0x0d, 0x96, 0x70,
+	0x54, 0x4c, 0xd6, 0x22, 0xad, 0xa2, 0x32, 0x5b, 0x8a, 0xd4, 0x1b, 0xee, 0x0d, 0x17, 0xc9, 0x7f,
+	0x32, 0xfb, 0x29, 0xff, 0xe0, 0xc5, 0x3e, 0x3a, 0x00, 0x4d, 0x80, 0x1d, 0xea, 0x8e, 0x2c, 0x86,
+	0xe6, 0x81, 0x6f, 0x56, 0xa2, 0x50, 0xe4, 0xe0, 0x53, 0x4b, 0x9d, 0x1c, 0xbe, 0x35, 0xfa, 0xeb,
+	0x57, 0xbe, 0x2f, 0x9b, 0x76, 0x80, 0xea, 0xbe, 0xd6, 0x71, 0xdd, 0xa2, 0x3f, 0xa6, 0xb1, 0x61,
+	0x71, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x1c, 0x84, 0x9c, 0x79, 0xef, 0x00, 0x00, 0x00,
 }

+ 4 - 0
proxy/dns/config.proto

@@ -6,5 +6,9 @@ option go_package = "dns";
 option java_package = "com.v2ray.core.proxy.dns";
 option java_multiple_files = true;
 
+import "v2ray.com/core/common/net/destination.proto";
+
 message Config {
+  // Server is the DNS server address. If specified, this address overrides the original one.
+  v2ray.core.common.net.Endpoint server = 1;
 }

+ 19 - 1
proxy/dns/dns.go

@@ -41,6 +41,7 @@ type Handler struct {
 	ipv4Lookup      dns.IPv4Lookup
 	ipv6Lookup      dns.IPv6Lookup
 	ownLinkVerifier ownLinkVerifier
+	server          net.Destination
 }
 
 func (h *Handler) Init(config *Config, dnsClient dns.Client) error {
@@ -59,6 +60,10 @@ func (h *Handler) Init(config *Config, dnsClient dns.Client) error {
 	if v, ok := dnsClient.(ownLinkVerifier); ok {
 		h.ownLinkVerifier = v
 	}
+
+	if config.Server != nil {
+		h.server = config.Server.AsDestination()
+	}
 	return nil
 }
 
@@ -97,7 +102,20 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
 		return newError("invalid outbound")
 	}
 
+	srcNetwork := outbound.Target.Network
+
 	dest := outbound.Target
+	if h.server.Network != net.Network_Unknown {
+		dest.Network = h.server.Network
+	}
+	if h.server.Address != nil {
+		dest.Address = h.server.Address
+	}
+	if h.server.Port != 0 {
+		dest.Port = h.server.Port
+	}
+
+	newError("handling DNS traffic to ", dest).WriteToLog(session.ExportIDToError(ctx))
 
 	conn := &outboundConn{
 		dialer: func() (internet.Connection, error) {
@@ -108,7 +126,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
 
 	var reader dns_proto.MessageReader
 	var writer dns_proto.MessageWriter
-	if dest.Network == net.Network_TCP {
+	if srcNetwork == net.Network_TCP {
 		reader = dns_proto.NewTCPReader(link.Reader)
 		writer = &dns_proto.TCPWriter{
 			Writer: link.Writer,

+ 90 - 0
proxy/dns/dns_test.go

@@ -236,3 +236,93 @@ func TestTCPDNSTunnel(t *testing.T) {
 		t.Error(r)
 	}
 }
+
+func TestUDP2TCPDNSTunnel(t *testing.T) {
+	port := tcp.PickPort()
+
+	dnsServer := dns.Server{
+		Addr:    "127.0.0.1:" + port.String(),
+		Net:     "tcp",
+		Handler: &staticHandler{},
+	}
+	defer dnsServer.Shutdown()
+
+	go dnsServer.ListenAndServe()
+	time.Sleep(time.Second)
+
+	serverPort := tcp.PickPort()
+	config := &core.Config{
+		App: []*serial.TypedMessage{
+			serial.ToTypedMessage(&dnsapp.Config{
+				NameServer: []*dnsapp.NameServer{
+					{
+						Address: &net.Endpoint{
+							Network: net.Network_UDP,
+							Address: &net.IPOrDomain{
+								Address: &net.IPOrDomain_Ip{
+									Ip: []byte{127, 0, 0, 1},
+								},
+							},
+							Port: uint32(port),
+						},
+					},
+				},
+			}),
+			serial.ToTypedMessage(&dispatcher.Config{}),
+			serial.ToTypedMessage(&proxyman.OutboundConfig{}),
+			serial.ToTypedMessage(&proxyman.InboundConfig{}),
+			serial.ToTypedMessage(&policy.Config{}),
+		},
+		Inbound: []*core.InboundHandlerConfig{
+			{
+				ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
+					Address:  net.NewIPOrDomain(net.LocalHostIP),
+					Port:     uint32(port),
+					Networks: []net.Network{net.Network_TCP},
+				}),
+				ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
+					PortRange: net.SinglePortRange(serverPort),
+					Listen:    net.NewIPOrDomain(net.LocalHostIP),
+				}),
+			},
+		},
+		Outbound: []*core.OutboundHandlerConfig{
+			{
+				ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{
+					Server: &net.Endpoint{
+						Network: net.Network_TCP,
+					},
+				}),
+			},
+		},
+	}
+
+	v, err := core.New(config)
+	common.Must(err)
+	common.Must(v.Start())
+	defer v.Close()
+
+	m1 := new(dns.Msg)
+	m1.Id = dns.Id()
+	m1.RecursionDesired = true
+	m1.Question = make([]dns.Question, 1)
+	m1.Question[0] = dns.Question{"google.com.", dns.TypeA, dns.ClassINET}
+
+	c := &dns.Client{
+		Net: "tcp",
+	}
+	in, _, err := c.Exchange(m1, "127.0.0.1:"+serverPort.String())
+	common.Must(err)
+
+	if len(in.Answer) != 1 {
+		t.Fatal("len(answer): ", len(in.Answer))
+	}
+
+	rr, ok := in.Answer[0].(*dns.A)
+	if !ok {
+		t.Fatal("not A record")
+	}
+	if r := cmp.Diff(rr.A[:], net.IP{8, 8, 8, 8}); r != "" {
+		t.Error(r)
+	}
+}