| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 | package dns_testimport (	"strconv"	"testing"	"time"	"github.com/google/go-cmp/cmp"	"github.com/miekg/dns"	"github.com/xtls/xray-core/app/dispatcher"	dnsapp "github.com/xtls/xray-core/app/dns"	"github.com/xtls/xray-core/app/policy"	"github.com/xtls/xray-core/app/proxyman"	_ "github.com/xtls/xray-core/app/proxyman/inbound"	_ "github.com/xtls/xray-core/app/proxyman/outbound"	"github.com/xtls/xray-core/common"	"github.com/xtls/xray-core/common/net"	"github.com/xtls/xray-core/common/serial"	"github.com/xtls/xray-core/core"	dns_proxy "github.com/xtls/xray-core/proxy/dns"	"github.com/xtls/xray-core/proxy/dokodemo"	"github.com/xtls/xray-core/testing/servers/tcp"	"github.com/xtls/xray-core/testing/servers/udp")type staticHandler struct{}func (*staticHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {	ans := new(dns.Msg)	ans.Id = r.Id	var clientIP net.IP	opt := r.IsEdns0()	if opt != nil {		for _, o := range opt.Option {			if o.Option() == dns.EDNS0SUBNET {				subnet := o.(*dns.EDNS0_SUBNET)				clientIP = subnet.Address			}		}	}	for _, q := range r.Question {		switch {		case q.Name == "google.com." && q.Qtype == dns.TypeA:			if clientIP == nil {				rr, _ := dns.NewRR("google.com. IN A 8.8.8.8")				ans.Answer = append(ans.Answer, rr)			} else {				rr, _ := dns.NewRR("google.com. IN A 8.8.4.4")				ans.Answer = append(ans.Answer, rr)			}		case q.Name == "facebook.com." && q.Qtype == dns.TypeA:			rr, _ := dns.NewRR("facebook.com. IN A 9.9.9.9")			ans.Answer = append(ans.Answer, rr)		case q.Name == "ipv6.google.com." && q.Qtype == dns.TypeA:			rr, err := dns.NewRR("ipv6.google.com. IN A 8.8.8.7")			common.Must(err)			ans.Answer = append(ans.Answer, rr)		case q.Name == "ipv6.google.com." && q.Qtype == dns.TypeAAAA:			rr, err := dns.NewRR("ipv6.google.com. IN AAAA 2001:4860:4860::8888")			common.Must(err)			ans.Answer = append(ans.Answer, rr)		case q.Name == "notexist.google.com." && q.Qtype == dns.TypeAAAA:			ans.MsgHdr.Rcode = dns.RcodeNameError		}	}	w.WriteMsg(ans)}func TestUDPDNSTunnel(t *testing.T) {	port := udp.PickPort()	dnsServer := dns.Server{		Addr:    "127.0.0.1:" + port.String(),		Net:     "udp",		Handler: &staticHandler{},		UDPSize: 1200,	}	defer dnsServer.Shutdown()	go dnsServer.ListenAndServe()	time.Sleep(time.Second)	serverPort := udp.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_UDP},				}),				ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{					PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},					Listen:   net.NewIPOrDomain(net.LocalHostIP),				}),			},		},		Outbound: []*core.OutboundHandlerConfig{			{				ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{}),			},		},	}	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{Name: "google.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET}		c := new(dns.Client)		in, _, err := c.Exchange(m1, "127.0.0.1:"+strconv.Itoa(int(serverPort)))		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)		}	}	{		m1 := new(dns.Msg)		m1.Id = dns.Id()		m1.RecursionDesired = true		m1.Question = make([]dns.Question, 1)		m1.Question[0] = dns.Question{Name: "ipv4only.google.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}		c := new(dns.Client)		c.Timeout = 10 * time.Second		in, _, err := c.Exchange(m1, "127.0.0.1:"+strconv.Itoa(int(serverPort)))		common.Must(err)		if len(in.Answer) != 0 {			t.Fatal("len(answer): ", len(in.Answer))		}	}	{		m1 := new(dns.Msg)		m1.Id = dns.Id()		m1.RecursionDesired = true		m1.Question = make([]dns.Question, 1)		m1.Question[0] = dns.Question{Name: "notexist.google.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}		c := new(dns.Client)		in, _, err := c.Exchange(m1, "127.0.0.1:"+strconv.Itoa(int(serverPort)))		common.Must(err)		if in.Rcode != dns.RcodeNameError {			t.Error("expected NameError, but got ", in.Rcode)		}	}}func TestTCPDNSTunnel(t *testing.T) {	port := udp.PickPort()	dnsServer := dns.Server{		Addr:    "127.0.0.1:" + port.String(),		Net:     "udp",		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{					PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(serverPort)}},					Listen:   net.NewIPOrDomain(net.LocalHostIP),				}),			},		},		Outbound: []*core.OutboundHandlerConfig{			{				ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{}),			},		},	}	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{Name: "google.com.", Qtype: dns.TypeA, Qclass: 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)	}}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{					PortList: &net.PortList{Range: []*net.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{Name: "google.com.", Qtype: dns.TypeA, Qclass: 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)	}}
 |