Files
Xray-core/proxy/dns/dns.go
T

458 lines
10 KiB
Go
Raw Normal View History

2020-11-25 19:01:53 +08:00
package dns
import (
"context"
go_errors "errors"
2020-11-25 19:01:53 +08:00
"io"
2025-09-04 23:51:21 +02:00
"strings"
2020-11-25 19:01:53 +08:00
"sync"
2021-10-16 21:02:51 +08:00
"time"
2020-11-25 19:01:53 +08:00
2020-12-04 09:36:16 +08:00
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
2023-04-26 22:35:12 +08:00
"github.com/xtls/xray-core/common/errors"
2020-12-04 09:36:16 +08:00
"github.com/xtls/xray-core/common/net"
dns_proto "github.com/xtls/xray-core/common/protocol/dns"
"github.com/xtls/xray-core/common/session"
2021-10-16 21:02:51 +08:00
"github.com/xtls/xray-core/common/signal"
2020-12-04 09:36:16 +08:00
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/dns"
2021-10-16 21:02:51 +08:00
"github.com/xtls/xray-core/features/policy"
2020-12-04 09:36:16 +08:00
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet"
2021-12-14 19:28:47 -05:00
"github.com/xtls/xray-core/transport/internet/stat"
2022-05-18 15:29:01 +08:00
"golang.org/x/net/dns/dnsmessage"
2020-11-25 19:01:53 +08:00
)
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
h := new(Handler)
2021-10-16 21:02:51 +08:00
if err := core.RequireFeatures(ctx, func(dnsClient dns.Client, policyManager policy.Manager) error {
core.OptionalFeatures(ctx, func(fdns dns.FakeDNSEngine) {
h.fdns = fdns
})
2021-10-16 21:02:51 +08:00
return h.Init(config.(*Config), dnsClient, policyManager)
2020-11-25 19:01:53 +08:00
}); err != nil {
return nil, err
}
return h, nil
}))
}
type ownLinkVerifier interface {
IsOwnLink(ctx context.Context) bool
}
type Handler struct {
2021-03-06 23:39:50 -05:00
client dns.Client
fdns dns.FakeDNSEngine
2020-11-25 19:01:53 +08:00
ownLinkVerifier ownLinkVerifier
server net.Destination
2021-10-16 21:02:51 +08:00
timeout time.Duration
nonIPQuery string
2024-11-09 14:16:11 +03:00
blockTypes []int32
2020-11-25 19:01:53 +08:00
}
2021-10-16 21:02:51 +08:00
func (h *Handler) Init(config *Config, dnsClient dns.Client, policyManager policy.Manager) error {
2021-03-06 23:39:50 -05:00
h.client = dnsClient
2021-10-16 21:02:51 +08:00
h.timeout = policyManager.ForLevel(config.UserLevel).Timeouts.ConnectionIdle
2020-11-25 19:01:53 +08:00
if v, ok := dnsClient.(ownLinkVerifier); ok {
h.ownLinkVerifier = v
}
if config.Server != nil {
h.server = config.Server.AsDestination()
}
h.nonIPQuery = config.Non_IPQuery
if h.nonIPQuery == "" {
h.nonIPQuery = "reject"
}
2024-09-15 12:21:51 +08:00
h.blockTypes = config.BlockTypes
2020-11-25 19:01:53 +08:00
return nil
}
func (h *Handler) isOwnLink(ctx context.Context) bool {
return h.ownLinkVerifier != nil && h.ownLinkVerifier.IsOwnLink(ctx)
}
func parseIPQuery(b []byte) (r bool, domain string, id uint16, qType dnsmessage.Type) {
var parser dnsmessage.Parser
header, err := parser.Start(b)
if err != nil {
2024-06-29 14:32:57 -04:00
errors.LogInfoInner(context.Background(), err, "parser start")
2020-11-25 19:01:53 +08:00
return
}
id = header.ID
q, err := parser.Question()
if err != nil {
2024-06-29 14:32:57 -04:00
errors.LogInfoInner(context.Background(), err, "question")
2020-11-25 19:01:53 +08:00
return
}
2024-09-15 12:21:51 +08:00
domain = q.Name.String()
2020-11-25 19:01:53 +08:00
qType = q.Type
if qType != dnsmessage.TypeA && qType != dnsmessage.TypeAAAA {
return
}
r = true
return
}
// Process implements proxy.Outbound.
func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.Dialer) error {
outbounds := session.OutboundsFromContext(ctx)
2024-06-29 14:32:57 -04:00
ob := outbounds[len(outbounds)-1]
if !ob.Target.IsValid() {
2024-06-29 14:32:57 -04:00
return errors.New("invalid outbound")
2020-11-25 19:01:53 +08:00
}
ob.Name = "dns"
2020-11-25 19:01:53 +08:00
srcNetwork := ob.Target.Network
2020-11-25 19:01:53 +08:00
dest := ob.Target
2020-11-25 19:01:53 +08:00
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
}
2024-06-29 14:32:57 -04:00
errors.LogInfo(ctx, "handling DNS traffic to ", dest)
2020-11-25 19:01:53 +08:00
conn := &outboundConn{
dialer: func() (stat.Connection, error) {
2020-11-25 19:01:53 +08:00
return d.Dial(ctx, dest)
},
connReady: make(chan struct{}, 1),
}
var reader dns_proto.MessageReader
var writer dns_proto.MessageWriter
if srcNetwork == net.Network_TCP {
reader = dns_proto.NewTCPReader(link.Reader)
writer = &dns_proto.TCPWriter{
Writer: link.Writer,
}
} else {
reader = &dns_proto.UDPReader{
Reader: link.Reader,
}
writer = &dns_proto.UDPWriter{
Writer: link.Writer,
}
}
var connReader dns_proto.MessageReader
var connWriter dns_proto.MessageWriter
if dest.Network == net.Network_TCP {
connReader = dns_proto.NewTCPReader(buf.NewReader(conn))
connWriter = &dns_proto.TCPWriter{
Writer: buf.NewWriter(conn),
}
} else {
connReader = &dns_proto.UDPReader{
Reader: buf.NewPacketReader(conn),
}
connWriter = &dns_proto.UDPWriter{
Writer: buf.NewWriter(conn),
}
}
if session.TimeoutOnlyFromContext(ctx) {
ctx, _ = context.WithCancel(context.Background())
}
2021-10-16 21:02:51 +08:00
ctx, cancel := context.WithCancel(ctx)
2025-09-04 23:51:21 +02:00
terminate := func() {
cancel()
conn.Close()
}
timer := signal.CancelAfterInactivity(ctx, terminate, h.timeout)
defer timer.SetTimeout(0)
2021-10-16 21:02:51 +08:00
2020-11-25 19:01:53 +08:00
request := func() error {
2025-09-04 23:51:21 +02:00
defer timer.SetTimeout(0)
2020-11-25 19:01:53 +08:00
for {
b, err := reader.ReadMessage()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
2021-10-16 21:02:51 +08:00
timer.Update()
2020-11-25 19:01:53 +08:00
if !h.isOwnLink(ctx) {
isIPQuery, domain, id, qType := parseIPQuery(b.Bytes())
2024-09-15 12:21:51 +08:00
if len(h.blockTypes) > 0 {
for _, blocktype := range h.blockTypes {
if blocktype == int32(qType) {
2025-09-04 23:51:21 +02:00
b.Release()
errors.LogInfo(ctx, "blocked type ", qType, " query for domain ", domain)
2025-06-23 10:48:24 +08:00
if h.nonIPQuery == "reject" {
2025-09-04 23:51:21 +02:00
err := h.rejectNonIPQuery(id, qType, domain, writer)
if err != nil {
return err
}
2025-06-23 10:48:24 +08:00
}
2024-09-15 12:21:51 +08:00
return nil
}
}
}
2020-11-25 19:01:53 +08:00
if isIPQuery {
2025-09-04 23:51:21 +02:00
b.Release()
go h.handleIPQuery(id, qType, domain, writer, timer)
continue
}
2025-09-04 23:51:21 +02:00
if h.nonIPQuery == "drop" {
b.Release()
2020-11-25 19:01:53 +08:00
continue
}
2025-06-23 10:48:24 +08:00
if h.nonIPQuery == "reject" {
b.Release()
2025-09-04 23:51:21 +02:00
err := h.rejectNonIPQuery(id, qType, domain, writer)
if err != nil {
return err
}
2025-06-23 10:48:24 +08:00
continue
}
2020-11-25 19:01:53 +08:00
}
if err := connWriter.WriteMessage(b); err != nil {
return err
}
}
}
response := func() error {
2025-09-04 23:51:21 +02:00
defer timer.SetTimeout(0)
2020-11-25 19:01:53 +08:00
for {
b, err := connReader.ReadMessage()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
2021-10-16 21:02:51 +08:00
timer.Update()
2020-11-25 19:01:53 +08:00
if err := writer.WriteMessage(b); err != nil {
return err
}
}
}
if err := task.Run(ctx, request, response); err != nil {
2024-06-29 14:32:57 -04:00
return errors.New("connection ends").Base(err)
2020-11-25 19:01:53 +08:00
}
return nil
}
2025-09-04 23:51:21 +02:00
func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter, timer *signal.ActivityTimer) {
2020-11-25 19:01:53 +08:00
var ips []net.IP
var err error
var ttl4 uint32
var ttl6 uint32
2021-03-06 23:39:50 -05:00
2020-11-25 19:01:53 +08:00
switch qType {
case dnsmessage.TypeA:
ips, ttl4, err = h.client.LookupIP(domain, dns.IPOption{
2021-03-06 23:39:50 -05:00
IPv4Enable: true,
IPv6Enable: false,
FakeEnable: true,
})
2020-11-25 19:01:53 +08:00
case dnsmessage.TypeAAAA:
ips, ttl6, err = h.client.LookupIP(domain, dns.IPOption{
2021-03-06 23:39:50 -05:00
IPv4Enable: false,
IPv6Enable: true,
FakeEnable: true,
})
2020-11-25 19:01:53 +08:00
}
rcode := dns.RCodeFromError(err)
if rcode == 0 && len(ips) == 0 && !go_errors.Is(err, dns.ErrEmptyResponse) {
2024-06-29 14:32:57 -04:00
errors.LogInfoInner(context.Background(), err, "ip query")
2020-11-25 19:01:53 +08:00
return
}
2021-10-16 21:02:51 +08:00
switch qType {
case dnsmessage.TypeA:
for i, ip := range ips {
ips[i] = ip.To4()
}
case dnsmessage.TypeAAAA:
for i, ip := range ips {
ips[i] = ip.To16()
}
}
2020-11-25 19:01:53 +08:00
b := buf.New()
rawBytes := b.Extend(buf.Size)
builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{
ID: id,
RCode: dnsmessage.RCode(rcode),
RecursionAvailable: true,
RecursionDesired: true,
Response: true,
Authoritative: true,
})
builder.EnableCompression()
common.Must(builder.StartQuestions())
common.Must(builder.Question(dnsmessage.Question{
Name: dnsmessage.MustNewName(domain),
Class: dnsmessage.ClassINET,
Type: qType,
}))
common.Must(builder.StartAnswers())
rHeader4 := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: ttl4}
rHeader6 := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: ttl6}
2020-11-25 19:01:53 +08:00
for _, ip := range ips {
if len(ip) == net.IPv4len {
var r dnsmessage.AResource
copy(r.A[:], ip)
common.Must(builder.AResource(rHeader4, r))
2020-11-25 19:01:53 +08:00
} else {
var r dnsmessage.AAAAResource
copy(r.AAAA[:], ip)
common.Must(builder.AAAAResource(rHeader6, r))
2020-11-25 19:01:53 +08:00
}
}
msgBytes, err := builder.Finish()
if err != nil {
2024-06-29 14:32:57 -04:00
errors.LogInfoInner(context.Background(), err, "pack message")
2020-11-25 19:01:53 +08:00
b.Release()
2025-09-04 23:51:21 +02:00
timer.SetTimeout(0)
2020-11-25 19:01:53 +08:00
}
b.Resize(0, int32(len(msgBytes)))
if err := writer.WriteMessage(b); err != nil {
2024-06-29 14:32:57 -04:00
errors.LogInfoInner(context.Background(), err, "write IP answer")
2025-09-04 23:51:21 +02:00
timer.SetTimeout(0)
2020-11-25 19:01:53 +08:00
}
}
2025-09-04 23:51:21 +02:00
func (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter) error {
domainT := strings.TrimSuffix(domain, ".")
if domainT == "" {
return errors.New("empty domain name")
}
2025-06-23 10:48:24 +08:00
b := buf.New()
rawBytes := b.Extend(buf.Size)
builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{
ID: id,
RCode: dnsmessage.RCodeRefused,
RecursionAvailable: true,
RecursionDesired: true,
Response: true,
Authoritative: true,
})
builder.EnableCompression()
common.Must(builder.StartQuestions())
err := builder.Question(dnsmessage.Question{
2025-06-23 10:48:24 +08:00
Name: dnsmessage.MustNewName(domain),
Class: dnsmessage.ClassINET,
Type: qType,
})
if err != nil {
errors.LogInfo(context.Background(), "unexpected domain ", domain, " when building reject message: ", err)
b.Release()
2025-09-04 23:51:21 +02:00
return err
}
2025-06-23 10:48:24 +08:00
msgBytes, err := builder.Finish()
if err != nil {
errors.LogInfoInner(context.Background(), err, "pack reject message")
b.Release()
2025-09-04 23:51:21 +02:00
return err
2025-06-23 10:48:24 +08:00
}
b.Resize(0, int32(len(msgBytes)))
if err := writer.WriteMessage(b); err != nil {
errors.LogInfoInner(context.Background(), err, "write reject answer")
2025-09-04 23:51:21 +02:00
return err
2025-06-23 10:48:24 +08:00
}
2025-09-04 23:51:21 +02:00
return nil
2025-06-23 10:48:24 +08:00
}
2020-11-25 19:01:53 +08:00
type outboundConn struct {
access sync.Mutex
dialer func() (stat.Connection, error)
2020-11-25 19:01:53 +08:00
conn net.Conn
connReady chan struct{}
2025-09-04 23:51:21 +02:00
closed bool
2020-11-25 19:01:53 +08:00
}
func (c *outboundConn) dial() error {
conn, err := c.dialer()
if err != nil {
return err
}
c.conn = conn
c.connReady <- struct{}{}
return nil
}
func (c *outboundConn) Write(b []byte) (int, error) {
c.access.Lock()
2025-09-04 23:51:21 +02:00
if c.closed {
c.access.Unlock()
return 0, errors.New("outbound connection closed")
}
2020-11-25 19:01:53 +08:00
if c.conn == nil {
if err := c.dial(); err != nil {
c.access.Unlock()
2024-06-29 14:32:57 -04:00
errors.LogWarningInner(context.Background(), err, "failed to dial outbound connection")
2025-09-04 23:51:21 +02:00
return 0, err
2020-11-25 19:01:53 +08:00
}
}
c.access.Unlock()
return c.conn.Write(b)
}
func (c *outboundConn) Read(b []byte) (int, error) {
c.access.Lock()
2025-09-04 23:51:21 +02:00
if c.closed {
c.access.Unlock()
return 0, io.EOF
}
2020-11-25 19:01:53 +08:00
2025-09-04 23:51:21 +02:00
if c.conn == nil {
c.access.Unlock()
2020-11-25 19:01:53 +08:00
_, open := <-c.connReady
if !open {
return 0, io.EOF
}
2025-09-04 23:51:21 +02:00
return c.conn.Read(b)
2020-11-25 19:01:53 +08:00
}
2025-09-04 23:51:21 +02:00
c.access.Unlock()
return c.conn.Read(b)
2020-11-25 19:01:53 +08:00
}
func (c *outboundConn) Close() error {
c.access.Lock()
2025-09-04 23:51:21 +02:00
c.closed = true
2020-11-25 19:01:53 +08:00
close(c.connReady)
if c.conn != nil {
c.conn.Close()
}
c.access.Unlock()
return nil
}