mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-10-26 13:06:58 +00:00
Compare commits
6
Commits
v26.6.27
...
65f6f0a43b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65f6f0a43b | ||
|
|
3263ae9255 | ||
|
|
3dc8bf3d8b | ||
|
|
695e68ef9e | ||
|
|
dfdbcf86cc | ||
|
|
2b828b7bc2 |
@@ -8,12 +8,13 @@ require (
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344
|
||||
github.com/golang/mock v1.7.0-rc.1
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/klauspost/cpuid/v2 v2.3.0
|
||||
github.com/klauspost/cpuid/v2 v2.4.0
|
||||
github.com/miekg/dns v1.1.72
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/pion/stun/v3 v3.1.6
|
||||
github.com/pires/go-proxyproto v0.12.0
|
||||
github.com/pires/go-proxyproto v0.14.0
|
||||
github.com/refraction-networking/utls v1.8.3-0.20260301010127-aa6edf4b11af
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/sagernet/sing v0.5.1
|
||||
@@ -30,7 +31,7 @@ require (
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
|
||||
golang.zx2c4.com/wireguard/windows v1.0.1
|
||||
google.golang.org/grpc v1.81.1
|
||||
google.golang.org/grpc v1.82.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0
|
||||
h12.io/socks v1.0.3
|
||||
@@ -55,7 +56,7 @@ require (
|
||||
golang.org/x/text v0.38.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.45.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -33,8 +33,8 @@ github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
|
||||
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
|
||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
|
||||
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
@@ -53,8 +53,8 @@ github.com/pion/stun/v3 v3.1.6 h1:WnhsD0eHCiwCfKNkVx0VJJwr2Y3eV4Ueih3KJ+dfZy8=
|
||||
github.com/pion/stun/v3 v3.1.6/go.mod h1:zRUghXSQU32Lx5orJsz3uYMkIihweXb3mu5gIns02fs=
|
||||
github.com/pion/transport/v4 v4.0.2 h1:ifYlPqNwsy6aKQ9y8yzxXlHae5431ZrH2avkD/Rn6Tk=
|
||||
github.com/pion/transport/v4 v4.0.2/go.mod h1:06hFI+jCFcok2X2MekVufNZ/uzNZXivGBPfviSVcjgM=
|
||||
github.com/pires/go-proxyproto v0.12.0 h1:TTCxD66dU898tahivkqc3hoceZp7P44FnorWyo9d5vM=
|
||||
github.com/pires/go-proxyproto v0.12.0/go.mod h1:qUvfqUMEoX7T8g0q7TQLDnhMjdTrxnG0hvpMn+7ePNI=
|
||||
github.com/pires/go-proxyproto v0.14.0 h1:2vIGIfVG8eVRsKF0xukEoeT5RWhDXxBU0uv6smLOKdI=
|
||||
github.com/pires/go-proxyproto v0.14.0/go.mod h1:OXsCrKwrK2tXS9YrI5tkHx5xaQlO8FH3lFW76orFh24=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
@@ -147,10 +147,10 @@ golang.zx2c4.com/wireguard/windows v1.0.1 h1:eOxiDVbywPC+ZQqvdCK7x+ZwWXKbYv50TtH
|
||||
golang.zx2c4.com/wireguard/windows v1.0.1/go.mod h1:+fbT3FFdX4zzYDLwJh5+HPEcNN/3HyNdzhNSVsQM+zs=
|
||||
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-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
|
||||
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.82.0 h1:vguDnZUPjE26w09A63VoxZPnvPjB5Riyc0mkXPFmAIU=
|
||||
google.golang.org/grpc v1.82.0/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -5,14 +5,14 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/uuid"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func (a *Account) AsAccount() (protocol.Account, error) {
|
||||
var VR net.Port
|
||||
if id, err := uuid.ParseString(a.Auth); err == nil {
|
||||
if id, err := uuid.Parse(a.Auth); err == nil {
|
||||
VR = net.PortFromBytes(id[6:8])
|
||||
}
|
||||
return &MemoryAccount{
|
||||
@@ -41,29 +41,71 @@ func (a *MemoryAccount) ToProto() proto.Message {
|
||||
|
||||
type Validator struct {
|
||||
users sync.Map
|
||||
ids sync.Map
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func NewValidator() *Validator {
|
||||
return &Validator{}
|
||||
}
|
||||
|
||||
func (v *Validator) Add(user *protocol.MemoryUser) error {
|
||||
func (v *Validator) Add(user *protocol.MemoryUser) (err error) {
|
||||
v.mu.Lock()
|
||||
v.users.Store(user.Account.(*MemoryAccount).Auth, user)
|
||||
return nil
|
||||
if id, err := uuid.Parse(user.Account.(*MemoryAccount).Auth); err == nil {
|
||||
id[6] = 0
|
||||
id[7] = 0
|
||||
v.ids.Store(id, user)
|
||||
}
|
||||
v.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (v *Validator) DelByEmail(email string) error {
|
||||
func (v *Validator) DelByEmail(email string) (err error) {
|
||||
v.mu.Lock()
|
||||
if user := v.GetByEmail(email); user != nil {
|
||||
v.users.Delete(user.Account.(*MemoryAccount).Auth)
|
||||
auth := user.Account.(*MemoryAccount).Auth
|
||||
v.users.Delete(auth)
|
||||
if id, err := uuid.Parse(auth); err == nil {
|
||||
id[6] = 0
|
||||
id[7] = 0
|
||||
v.ids.Delete(id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
v.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (v *Validator) Get(auth string) *protocol.MemoryUser {
|
||||
if value, ok := v.users.Load(auth); ok {
|
||||
return value.(*protocol.MemoryUser)
|
||||
func (v *Validator) Get(auth string) (user *protocol.MemoryUser) {
|
||||
if id, err := uuid.Parse(auth); err == nil {
|
||||
if user = v.GetByID(id); user != nil {
|
||||
VR := net.PortFromBytes(id[6:8])
|
||||
if user.Account.(*MemoryAccount).VR != VR {
|
||||
user = &protocol.MemoryUser{
|
||||
Email: user.Email,
|
||||
Level: user.Level,
|
||||
Account: &MemoryAccount{
|
||||
Auth: auth,
|
||||
VR: VR,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
return nil
|
||||
if value, ok := v.users.Load(auth); ok {
|
||||
user = value.(*protocol.MemoryUser)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (v *Validator) GetByID(id uuid.UUID) (user *protocol.MemoryUser) {
|
||||
id[6] = 0
|
||||
id[7] = 0
|
||||
if value, ok := v.ids.Load(id); ok {
|
||||
user = value.(*protocol.MemoryUser)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (v *Validator) GetByEmail(email string) (user *protocol.MemoryUser) {
|
||||
|
||||
+5
-5
@@ -14,11 +14,11 @@ Plainly enabling it in the config probably will result nothing, or lock your rou
|
||||
|
||||
## DETAILS
|
||||
|
||||
Current implementation does not contain options to configure network level addresses, routing or rules.
|
||||
Enabling the feature will result only tun interface up, and that's it. \
|
||||
This is explicit decision, significantly simplifying implementation, and allowing any number of custom configurations, consumers could come up with. Network interface is OS level entity, and OS is what should manage it. \
|
||||
Working configuration, is tun enabled in Xray config with specific name (e.g. xray0), and OS level configuration to manage "xray0" interface, applying routing and rules on interface up.
|
||||
This way consistency of system level routing and rules is ensured from single place of responsibility - the OS itself. \
|
||||
By default, enabling the feature will only bring the tun interface up. \
|
||||
When configured explicitly, Windows and Linux can also apply interface addresses from `gateway` and on-link routes from `autoSystemRoutingTable`.
|
||||
Linux does not configure system DNS from the `dns` field; system DNS remains managed by the OS or distribution-specific network services. \
|
||||
For more advanced routing policies or rules, OS level configuration can still manage the named interface (e.g. xray0) when it appears.
|
||||
This keeps complex system level routing and rules in a single place of responsibility - the OS itself. \
|
||||
Examples of how to achieve this on a simple Linux system (Ubuntu with systemd-networkd) can be found at the end of this README.
|
||||
|
||||
Due to this inbound not actually being a proxy, the configuration ignore required listen and port options, and never listen on any port. \
|
||||
|
||||
+85
-31
@@ -26,9 +26,10 @@ type LinuxTun struct {
|
||||
options *Config
|
||||
ownsTun bool
|
||||
|
||||
systemRoutes []netlink.Route
|
||||
routeMonitorStop chan struct{}
|
||||
routeMonitorOnce sync.Once
|
||||
interfaceAddresses []netlink.Addr
|
||||
systemRoutes []netlink.Route
|
||||
routeMonitorStop chan struct{}
|
||||
routeMonitorOnce sync.Once
|
||||
}
|
||||
|
||||
// LinuxTun implements Tun
|
||||
@@ -172,7 +173,14 @@ func (t *LinuxTun) Start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := t.setInterfaceAddresses(); err != nil {
|
||||
_ = netlink.LinkSetDown(t.tunLink)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := t.setSystemRoutes(); err != nil {
|
||||
_ = t.unsetInterfaceAddresses()
|
||||
_ = netlink.LinkSetDown(t.tunLink)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -193,6 +201,7 @@ func (t *LinuxTun) Close() error {
|
||||
})
|
||||
|
||||
_ = t.unsetSystemRoutes()
|
||||
_ = t.unsetInterfaceAddresses()
|
||||
|
||||
if t.ownsTun {
|
||||
_ = netlink.LinkSetDown(t.tunLink)
|
||||
@@ -223,6 +232,37 @@ func setinterface(network, address string, fd uintptr, iface *net.Interface) err
|
||||
return unix.BindToDevice(int(fd), iface.Name)
|
||||
}
|
||||
|
||||
func (t *LinuxTun) setInterfaceAddresses() error {
|
||||
if len(t.options.Gateway) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, address := range t.options.Gateway {
|
||||
addr, err := netlink.ParseAddr(address)
|
||||
if err != nil {
|
||||
_ = t.unsetInterfaceAddresses()
|
||||
return errors.New("invalid interface address ", address).Base(err)
|
||||
}
|
||||
if err := netlink.AddrAdd(t.tunLink, addr); err != nil {
|
||||
_ = t.unsetInterfaceAddresses()
|
||||
return errors.New("failed to add interface address ", address).Base(err)
|
||||
}
|
||||
t.interfaceAddresses = append(t.interfaceAddresses, *addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *LinuxTun) unsetInterfaceAddresses() error {
|
||||
var errs []error
|
||||
for i := len(t.interfaceAddresses) - 1; i >= 0; i-- {
|
||||
address := t.interfaceAddresses[i]
|
||||
if err := netlink.AddrDel(t.tunLink, &address); err != nil {
|
||||
errs = append(errs, errors.New("failed to delete interface address ", address.String()).Base(err))
|
||||
}
|
||||
}
|
||||
t.interfaceAddresses = nil
|
||||
return errors.Combine(errs...)
|
||||
}
|
||||
|
||||
func (t *LinuxTun) setSystemRoutes() error {
|
||||
if len(t.options.AutoSystemRoutingTable) == 0 {
|
||||
return nil
|
||||
@@ -308,36 +348,37 @@ func findOutboundInterface(tunIndex int, fixedName string) (*net.Interface, erro
|
||||
return iface, nil
|
||||
}
|
||||
|
||||
probeIPs := []net.IP{
|
||||
net.ParseIP("8.8.8.8"),
|
||||
net.ParseIP("2001:4860:4860::8888"),
|
||||
for _, family := range []int{
|
||||
netlink.FAMILY_V4,
|
||||
netlink.FAMILY_V6,
|
||||
} {
|
||||
iface, err := findDefaultInterface(family, tunIndex)
|
||||
if err == nil {
|
||||
return iface, nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, ip := range probeIPs {
|
||||
routes, err := netlink.RouteGet(ip)
|
||||
if err != nil || len(routes) == 0 {
|
||||
continue
|
||||
}
|
||||
route := routes[0]
|
||||
if route.LinkIndex == tunIndex {
|
||||
continue
|
||||
return nil, errors.New("no usable outbound interface found")
|
||||
}
|
||||
|
||||
func findDefaultInterface(family int, tunIndex int) (*net.Interface, error) {
|
||||
routes, err := netlink.RouteList(nil, family)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var selected *net.Interface
|
||||
selectedMetric := -1
|
||||
|
||||
for _, route := range routes {
|
||||
if route.Dst != nil {
|
||||
ones, _ := route.Dst.Mask.Size()
|
||||
if ones != 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
link, err := netlink.LinkByIndex(route.LinkIndex)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
attrs := link.Attrs()
|
||||
|
||||
if attrs.Flags&net.FlagUp == 0 {
|
||||
continue
|
||||
}
|
||||
operState := attrs.OperState
|
||||
if operState != netlink.OperUp && operState != netlink.OperUnknown {
|
||||
continue
|
||||
}
|
||||
|
||||
if route.Src == nil || route.Src.IsLoopback() || route.Src.IsLinkLocalUnicast() {
|
||||
if route.LinkIndex == 0 || route.LinkIndex == tunIndex {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -345,8 +386,21 @@ func findOutboundInterface(tunIndex int, fixedName string) (*net.Interface, erro
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
return iface, nil
|
||||
|
||||
if iface.Flags&net.FlagUp == 0 ||
|
||||
iface.Flags&net.FlagLoopback != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if selected == nil || route.Priority < selectedMetric {
|
||||
selected = iface
|
||||
selectedMetric = route.Priority
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("no usable outbound interface found")
|
||||
if selected == nil {
|
||||
return nil, errors.New("physical default route not found")
|
||||
}
|
||||
|
||||
return selected, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user