Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 42 additions & 4 deletions features/dns/localdns/client.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
package localdns

import (
"context"
"syscall"
"time"

"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/transport/internet"
)

// Client is an implementation of dns.Client, which queries localhost for DNS.
type Client struct{}
type Client struct {
d *net.Dialer
r *net.Resolver
}

// Type implements common.HasType.
func (*Client) Type() interface{} {
Expand All @@ -20,8 +29,14 @@ func (*Client) Start() error { return nil }
func (*Client) Close() error { return nil }

// LookupIP implements Client.
func (*Client) LookupIP(host string, option dns.IPOption) ([]net.IP, uint32, error) {
ips, err := net.LookupIP(host)
func (c *Client) LookupIP(host string, option dns.IPOption) ([]net.IP, uint32, error) {
var ips []net.IP
var err error
if len(internet.Controllers) > 0 {
ips, err = c.r.LookupIP(context.Background(), "ip", host)
} else {
ips, err = net.LookupIP(host)
}
if err != nil {
return nil, 0, err
}
Expand Down Expand Up @@ -62,5 +77,28 @@ func (*Client) LookupIP(host string, option dns.IPOption) ([]net.IP, uint32, err

// New create a new dns.Client that queries localhost for DNS.
func New() *Client {
return &Client{}
d := &net.Dialer{
Timeout: time.Second * 16,
Control: func(network, address string, c syscall.RawConn) error {
for _, ctl := range internet.Controllers {
if err := ctl(network, address, c); err != nil {
errors.LogInfoInner(context.Background(), err, "failed to apply external controller")
return err
}
}
return nil
},
}

r := &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
return d.DialContext(ctx, network, address)
},
}

return &Client{
d: d,
r: r,
}
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ require (
golang.org/x/sys v0.42.0
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
golang.zx2c4.com/wireguard/windows v0.5.3
google.golang.org/grpc v1.80.0
google.golang.org/protobuf v1.36.11
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A=
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw=
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU=
Expand Down
14 changes: 11 additions & 3 deletions infra/conf/tun.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,24 @@ import (
)

type TunConfig struct {
Name string `json:"name"`
MTU uint32 `json:"MTU"`
UserLevel uint32 `json:"userLevel"`
Name string `json:"name"`
MTU uint32 `json:"MTU"`
UserLevel uint32 `json:"userLevel"`
Interface string `json:"interface"`
Address []string `json:"address"`
Route []string `json:"route"`
Dns []string `json:"dns"`
}

func (v *TunConfig) Build() (proto.Message, error) {
config := &tun.Config{
Name: v.Name,
MTU: v.MTU,
UserLevel: v.UserLevel,
Interface: v.Interface,
Address: v.Address,
Route: v.Route,
Dns: v.Dns,
}

if v.Name == "" {
Expand Down
71 changes: 71 additions & 0 deletions proxy/tun/config.go
Original file line number Diff line number Diff line change
@@ -1 +1,72 @@
package tun

import (
"context"
"net"
"sync"

"github.com/xtls/xray-core/common/errors"
)

type InterfaceUpdater struct {
sync.Mutex

tunIndex int
fixedName string
iface *net.Interface
}

var updater *InterfaceUpdater

func (updater *InterfaceUpdater) Get() *net.Interface {
updater.Lock()
defer updater.Unlock()

return updater.iface
}

func (updater *InterfaceUpdater) Update() {
updater.Lock()
defer updater.Unlock()

if updater.iface != nil {
iface, err := net.InterfaceByIndex(updater.iface.Index)
if err == nil && iface.Name == updater.iface.Name {
return
}
}

updater.iface = nil

interfaces, err := net.Interfaces()
if err != nil {
errors.LogInfoInner(context.Background(), err, "[tun] failed to update interface")
return
}

var got *net.Interface
for _, iface := range interfaces {
if iface.Index == updater.tunIndex {
continue
}
if updater.fixedName != "" {
if iface.Name == updater.fixedName {
got = &iface
break
}
} else {
if iface.Flags&net.FlagLoopback == 0 {
got = &iface
break
}
}
}

if got == nil {
errors.LogInfo(context.Background(), "[tun] failed to update interface > got == nil")
return
}

updater.iface = got
errors.LogInfo(context.Background(), "[tun] update interface ", got.Name, " ", got.Index)
}
40 changes: 38 additions & 2 deletions proxy/tun/config.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions proxy/tun/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ message Config {
string name = 1;
uint32 MTU = 2;
uint32 user_level = 3;
string interface = 4;
repeated string address = 5;
repeated string route = 6;
repeated string dns = 7;
}
39 changes: 29 additions & 10 deletions proxy/tun/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tun

import (
"context"
"syscall"

"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
Expand All @@ -15,6 +16,7 @@ import (
"github.com/xtls/xray-core/features/policy"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/stat"
)

Expand All @@ -38,11 +40,6 @@ type ConnectionHandler interface {
// Handler implements ConnectionHandler
var _ ConnectionHandler = (*Handler)(nil)

func (t *Handler) policy() policy.Session {
p := t.policyManager.ForLevel(t.config.UserLevel)
return p
}

// Init the Handler instance with necessary parameters
func (t *Handler) Init(ctx context.Context, pm policy.Manager, dispatcher routing.Dispatcher) error {
var err error
Expand All @@ -60,15 +57,37 @@ func (t *Handler) Init(ctx context.Context, pm policy.Manager, dispatcher routin
t.dispatcher = dispatcher

tunName := t.config.Name
tunOptions := TunOptions{
Name: tunName,
MTU: t.config.MTU,
}
tunInterface, err := NewTun(tunOptions)
tunInterface, err := NewTun(t.config)
if err != nil {
return err
}

if t.config.Interface != "" {
tunIndex, err := tunInterface.Index()
if err != nil {
_ = tunInterface.Close()
return err
}
if t.config.Interface == "auto" {
t.config.Interface = ""
}
updater = &InterfaceUpdater{tunIndex: tunIndex, fixedName: t.config.Interface}
updater.Update()
internet.RegisterDialerController(func(network, address string, c syscall.RawConn) error {
iface := updater.Get()
if iface == nil {
errors.LogInfo(context.Background(), "[tun] falied to set interface > iface == nil")
return nil
}
return c.Control(func(fd uintptr) {
err := setinterface(network, address, fd, iface)
if err != nil {
errors.LogInfoInner(context.Background(), err, "[tun] falied to set interface")
}
})
})
}

errors.LogInfo(t.ctx, tunName, " created")

tunStackOptions := StackOptions{
Expand Down
9 changes: 2 additions & 7 deletions proxy/tun/stack_gvisor.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,18 @@ const (
// stackGVisor is ip stack implemented by gVisor package
type stackGVisor struct {
ctx context.Context
tun GVisorTun
tun Tun
idleTimeout time.Duration
handler *Handler
stack *stack.Stack
endpoint stack.LinkEndpoint
}

// GVisorTun implements a bridge to connect gVisor ip stack to tun interface
type GVisorTun interface {
newEndpoint() (stack.LinkEndpoint, error)
}

// NewStack builds new ip stack (using gVisor)
func NewStack(ctx context.Context, options StackOptions, handler *Handler) (Stack, error) {
gStack := &stackGVisor{
ctx: ctx,
tun: options.Tun.(GVisorTun),
tun: options.Tun,
idleTimeout: options.IdleTimeout,
handler: handler,
}
Expand Down
11 changes: 5 additions & 6 deletions proxy/tun/tun.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package tun

import "gvisor.dev/gvisor/pkg/tcpip/stack"

// Tun interface implements tun interface interaction
type Tun interface {
Start() error
Close() error
}

// TunOptions for tun interface implementation
type TunOptions struct {
Name string
MTU uint32
Name() (string, error)
Index() (int, error)
newEndpoint() (stack.LinkEndpoint, error)
}
Loading