0
mirror of https://github.com/XTLS/Xray-core.git synced 2025-06-12 11:29:36 +03:00
This commit is contained in:
RPRX
2020-11-25 19:01:53 +08:00
parent 47d23e9972
commit c7f7c08ead
711 changed files with 82154 additions and 2 deletions

59
features/dns/client.go Normal file
View File

@ -0,0 +1,59 @@
package dns
import (
"github.com/xtls/xray-core/v1/common/errors"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/common/serial"
"github.com/xtls/xray-core/v1/features"
)
// Client is a Xray feature for querying DNS information.
//
// xray:api:stable
type Client interface {
features.Feature
// LookupIP returns IP address for the given domain. IPs may contain IPv4 and/or IPv6 addresses.
LookupIP(domain string) ([]net.IP, error)
}
// IPv4Lookup is an optional feature for querying IPv4 addresses only.
//
// xray:api:beta
type IPv4Lookup interface {
LookupIPv4(domain string) ([]net.IP, error)
}
// IPv6Lookup is an optional feature for querying IPv6 addresses only.
//
// xray:api:beta
type IPv6Lookup interface {
LookupIPv6(domain string) ([]net.IP, error)
}
// ClientType returns the type of Client interface. Can be used for implementing common.HasType.
//
// xray:api:beta
func ClientType() interface{} {
return (*Client)(nil)
}
// ErrEmptyResponse indicates that DNS query succeeded but no answer was returned.
var ErrEmptyResponse = errors.New("empty response")
type RCodeError uint16
func (e RCodeError) Error() string {
return serial.Concat("rcode: ", uint16(e))
}
func RCodeFromError(err error) uint16 {
if err == nil {
return 0
}
cause := errors.Cause(err)
if r, ok := cause.(RCodeError); ok {
return uint16(r)
}
return 0
}

View File

@ -0,0 +1,80 @@
package localdns
import (
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/features/dns"
)
// Client is an implementation of dns.Client, which queries localhost for DNS.
type Client struct{}
// Type implements common.HasType.
func (*Client) Type() interface{} {
return dns.ClientType()
}
// Start implements common.Runnable.
func (*Client) Start() error { return nil }
// Close implements common.Closable.
func (*Client) Close() error { return nil }
// LookupIP implements Client.
func (*Client) LookupIP(host string) ([]net.IP, error) {
ips, err := net.LookupIP(host)
if err != nil {
return nil, err
}
parsedIPs := make([]net.IP, 0, len(ips))
for _, ip := range ips {
parsed := net.IPAddress(ip)
if parsed != nil {
parsedIPs = append(parsedIPs, parsed.IP())
}
}
if len(parsedIPs) == 0 {
return nil, dns.ErrEmptyResponse
}
return parsedIPs, nil
}
// LookupIPv4 implements IPv4Lookup.
func (c *Client) LookupIPv4(host string) ([]net.IP, error) {
ips, err := c.LookupIP(host)
if err != nil {
return nil, err
}
ipv4 := make([]net.IP, 0, len(ips))
for _, ip := range ips {
if len(ip) == net.IPv4len {
ipv4 = append(ipv4, ip)
}
}
if len(ipv4) == 0 {
return nil, dns.ErrEmptyResponse
}
return ipv4, nil
}
// LookupIPv6 implements IPv6Lookup.
func (c *Client) LookupIPv6(host string) ([]net.IP, error) {
ips, err := c.LookupIP(host)
if err != nil {
return nil, err
}
ipv6 := make([]net.IP, 0, len(ips))
for _, ip := range ips {
if len(ip) == net.IPv6len {
ipv6 = append(ipv6, ip)
}
}
if len(ipv6) == 0 {
return nil, dns.ErrEmptyResponse
}
return ipv6, nil
}
// New create a new dns.Client that queries localhost for DNS.
func New() *Client {
return &Client{}
}

View File

@ -0,0 +1,9 @@
package features
import "github.com/xtls/xray-core/v1/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
}

17
features/feature.go Normal file
View File

@ -0,0 +1,17 @@
package features
import "github.com/xtls/xray-core/v1/common"
//go:generate go run github.com/xtls/xray-core/v1/common/errors/errorgen
// Feature is the interface for Xray features. All features must implement this interface.
// All existing features have an implementation in app directory. These features can be replaced by third-party ones.
type Feature interface {
common.HasType
common.Runnable
}
// PrintDeprecatedFeatureWarning prints a warning for deprecated feature.
func PrintDeprecatedFeatureWarning(feature string) {
newError("You are using a deprecated feature: " + feature + ". Please update your config file with latest configuration format, or update your client software.").WriteToLog()
}

View File

@ -0,0 +1,42 @@
package inbound
import (
"context"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/features"
)
// Handler is the interface for handlers that process inbound connections.
//
// xray:api:stable
type Handler interface {
common.Runnable
// The tag of this handler.
Tag() string
// Deprecated: Do not use in new code.
GetRandomInboundProxy() (interface{}, net.Port, int)
}
// Manager is a feature that manages InboundHandlers.
//
// xray:api:stable
type Manager interface {
features.Feature
// GetHandlers returns an InboundHandler for the given tag.
GetHandler(ctx context.Context, tag string) (Handler, error)
// AddHandler adds the given handler into this Manager.
AddHandler(ctx context.Context, handler Handler) error
// RemoveHandler removes a handler from Manager.
RemoveHandler(ctx context.Context, tag string) error
}
// ManagerType returns the type of Manager interface. Can be used for implementing common.HasType.
//
// xray:api:stable
func ManagerType() interface{} {
return (*Manager)(nil)
}

View File

@ -0,0 +1,45 @@
package outbound
import (
"context"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/features"
"github.com/xtls/xray-core/v1/transport"
)
// Handler is the interface for handlers that process outbound connections.
//
// xray:api:stable
type Handler interface {
common.Runnable
Tag() string
Dispatch(ctx context.Context, link *transport.Link)
}
type HandlerSelector interface {
Select([]string) []string
}
// Manager is a feature that manages outbound.Handlers.
//
// xray:api:stable
type Manager interface {
features.Feature
// GetHandler returns an outbound.Handler for the given tag.
GetHandler(tag string) Handler
// GetDefaultHandler returns the default outbound.Handler. It is usually the first outbound.Handler specified in the configuration.
GetDefaultHandler() Handler
// AddHandler adds a handler into this outbound.Manager.
AddHandler(ctx context.Context, handler Handler) error
// RemoveHandler removes a handler from outbound.Manager.
RemoveHandler(ctx context.Context, tag string) error
}
// ManagerType returns the type of Manager interface. Can be used to implement common.HasType.
//
// xray:api:stable
func ManagerType() interface{} {
return (*Manager)(nil)
}

View File

@ -0,0 +1,37 @@
package policy
import (
"time"
)
// DefaultManager is the implementation of the Manager.
type DefaultManager struct{}
// Type implements common.HasType.
func (DefaultManager) Type() interface{} {
return ManagerType()
}
// ForLevel implements Manager.
func (DefaultManager) ForLevel(level uint32) Session {
p := SessionDefault()
if level == 1 {
p.Timeouts.ConnectionIdle = time.Second * 600
}
return p
}
// ForSystem implements Manager.
func (DefaultManager) ForSystem() System {
return System{}
}
// Start implements common.Runnable.
func (DefaultManager) Start() error {
return nil
}
// Close implements common.Closable.
func (DefaultManager) Close() error {
return nil
}

151
features/policy/policy.go Normal file
View File

@ -0,0 +1,151 @@
package policy
import (
"context"
"runtime"
"time"
"github.com/xtls/xray-core/v1/common/platform"
"github.com/xtls/xray-core/v1/features"
)
// Timeout contains limits for connection timeout.
type Timeout struct {
// Timeout for handshake phase in a connection.
Handshake time.Duration
// Timeout for connection being idle, i.e., there is no egress or ingress traffic in this connection.
ConnectionIdle time.Duration
// Timeout for an uplink only connection, i.e., the downlink of the connection has been closed.
UplinkOnly time.Duration
// Timeout for an downlink only connection, i.e., the uplink of the connection has been closed.
DownlinkOnly time.Duration
}
// Stats contains settings for stats counters.
type Stats struct {
// Whether or not to enable stat counter for user uplink traffic.
UserUplink bool
// Whether or not to enable stat counter for user downlink traffic.
UserDownlink bool
}
// Buffer contains settings for internal buffer.
type Buffer struct {
// Size of buffer per connection, in bytes. -1 for unlimited buffer.
PerConnection int32
}
// SystemStats contains stat policy settings on system level.
type SystemStats struct {
// Whether or not to enable stat counter for uplink traffic in inbound handlers.
InboundUplink bool
// Whether or not to enable stat counter for downlink traffic in inbound handlers.
InboundDownlink bool
// Whether or not to enable stat counter for uplink traffic in outbound handlers.
OutboundUplink bool
// Whether or not to enable stat counter for downlink traffic in outbound handlers.
OutboundDownlink bool
}
// System contains policy settings at system level.
type System struct {
Stats SystemStats
Buffer Buffer
}
// Session is session based settings for controlling Xray requests. It contains various settings (or limits) that may differ for different users in the context.
type Session struct {
Timeouts Timeout // Timeout settings
Stats Stats
Buffer Buffer
}
// Manager is a feature that provides Policy for the given user by its id or level.
//
// xray:api:stable
type Manager interface {
features.Feature
// ForLevel returns the Session policy for the given user level.
ForLevel(level uint32) Session
// ForSystem returns the System policy for Xray system.
ForSystem() System
}
// ManagerType returns the type of Manager interface. Can be used to implement common.HasType.
//
// xray:api:stable
func ManagerType() interface{} {
return (*Manager)(nil)
}
var defaultBufferSize int32
func init() {
const key = "xray.ray.buffer.size"
const defaultValue = -17
size := platform.EnvFlag{
Name: key,
AltName: platform.NormalizeEnvName(key),
}.GetValueAsInt(defaultValue)
switch size {
case 0:
defaultBufferSize = -1 // For pipe to use unlimited size
case defaultValue: // Env flag not defined. Use default values per CPU-arch.
switch runtime.GOARCH {
case "arm", "mips", "mipsle":
defaultBufferSize = 0
case "arm64", "mips64", "mips64le":
defaultBufferSize = 4 * 1024 // 4k cache for low-end devices
default:
defaultBufferSize = 512 * 1024
}
default:
defaultBufferSize = int32(size) * 1024 * 1024
}
}
func defaultBufferPolicy() Buffer {
return Buffer{
PerConnection: defaultBufferSize,
}
}
// SessionDefault returns the Policy when user is not specified.
func SessionDefault() Session {
return Session{
Timeouts: Timeout{
// Align Handshake timeout with nginx client_header_timeout
// So that this value will not indicate server identity
Handshake: time.Second * 60,
ConnectionIdle: time.Second * 300,
UplinkOnly: time.Second * 1,
DownlinkOnly: time.Second * 1,
},
Stats: Stats{
UserUplink: false,
UserDownlink: false,
},
Buffer: defaultBufferPolicy(),
}
}
type policyKey int32
const (
bufferPolicyKey policyKey = 0
)
func ContextWithBufferPolicy(ctx context.Context, p Buffer) context.Context {
return context.WithValue(ctx, bufferPolicyKey, p)
}
func BufferPolicyFromContext(ctx context.Context) Buffer {
pPolicy := ctx.Value(bufferPolicyKey)
if pPolicy == nil {
return defaultBufferPolicy()
}
return pPolicy.(Buffer)
}

View File

@ -0,0 +1,40 @@
package routing
import (
"github.com/xtls/xray-core/v1/common/net"
)
// Context is a feature to store connection information for routing.
//
// xray:api:stable
type Context interface {
// GetInboundTag returns the tag of the inbound the connection was from.
GetInboundTag() string
// GetSourcesIPs returns the source IPs bound to the connection.
GetSourceIPs() []net.IP
// GetSourcePort returns the source port of the connection.
GetSourcePort() net.Port
// GetTargetIPs returns the target IP of the connection or resolved IPs of target domain.
GetTargetIPs() []net.IP
// GetTargetPort returns the target port of the connection.
GetTargetPort() net.Port
// GetTargetDomain returns the target domain of the connection, if exists.
GetTargetDomain() string
// GetNetwork returns the network type of the connection.
GetNetwork() net.Network
// GetProtocol returns the protocol from the connection content, if sniffed out.
GetProtocol() string
// GetUser returns the user email from the connection content, if exists.
GetUser() string
// GetAttributes returns extra attributes from the conneciont content.
GetAttributes() map[string]string
}

View File

@ -0,0 +1,27 @@
package routing
import (
"context"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/features"
"github.com/xtls/xray-core/v1/transport"
)
// Dispatcher is a feature that dispatches inbound requests to outbound handlers based on rules.
// Dispatcher is required to be registered in a Xray instance to make Xray function properly.
//
// xray:api:stable
type Dispatcher interface {
features.Feature
// Dispatch returns a Ray for transporting data for the given request.
Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error)
}
// DispatcherType returns the type of Dispatcher interface. Can be used to implement common.HasType.
//
// xray:api:stable
func DispatcherType() interface{} {
return (*Dispatcher)(nil)
}

View File

@ -0,0 +1,44 @@
package dns
//go:generate go run github.com/xtls/xray-core/v1/common/errors/errorgen
import (
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/features/dns"
"github.com/xtls/xray-core/v1/features/routing"
)
// ResolvableContext is an implementation of routing.Context, with domain resolving capability.
type ResolvableContext struct {
routing.Context
dnsClient dns.Client
resolvedIPs []net.IP
}
// GetTargetIPs overrides original routing.Context's implementation.
func (ctx *ResolvableContext) GetTargetIPs() []net.IP {
if ips := ctx.Context.GetTargetIPs(); len(ips) != 0 {
return ips
}
if len(ctx.resolvedIPs) > 0 {
return ctx.resolvedIPs
}
if domain := ctx.GetTargetDomain(); len(domain) != 0 {
ips, err := ctx.dnsClient.LookupIP(domain)
if err == nil {
ctx.resolvedIPs = ips
return ips
}
newError("resolve ip for ", domain).Base(err).WriteToLog()
}
return nil
}
// ContextWithDNSClient creates a new routing context with domain resolving capability.
// Resolved domain IPs can be retrieved by GetTargetIPs().
func ContextWithDNSClient(ctx routing.Context, client dns.Client) routing.Context {
return &ResolvableContext{Context: ctx, dnsClient: client}
}

View File

@ -0,0 +1,9 @@
package dns
import "github.com/xtls/xray-core/v1/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
}

View File

@ -0,0 +1,60 @@
package routing
import (
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/features"
)
// Router is a feature to choose an outbound tag for the given request.
//
// xray:api:stable
type Router interface {
features.Feature
// PickRoute returns a route decision based on the given routing context.
PickRoute(ctx Context) (Route, error)
}
// Route is the routing result of Router feature.
//
// xray:api:stable
type Route interface {
// A Route is also a routing context.
Context
// GetOutboundGroupTags returns the detoured outbound group tags in sequence before a final outbound is chosen.
GetOutboundGroupTags() []string
// GetOutboundTag returns the tag of the outbound the connection was dispatched to.
GetOutboundTag() string
}
// RouterType return the type of Router interface. Can be used to implement common.HasType.
//
// xray:api:stable
func RouterType() interface{} {
return (*Router)(nil)
}
// DefaultRouter is an implementation of Router, which always returns ErrNoClue for routing decisions.
type DefaultRouter struct{}
// Type implements common.HasType.
func (DefaultRouter) Type() interface{} {
return RouterType()
}
// PickRoute implements Router.
func (DefaultRouter) PickRoute(ctx Context) (Route, error) {
return nil, common.ErrNoClue
}
// Start implements common.Runnable.
func (DefaultRouter) Start() error {
return nil
}
// Close implements common.Closable.
func (DefaultRouter) Close() error {
return nil
}

View File

@ -0,0 +1,119 @@
package session
import (
"context"
"github.com/xtls/xray-core/v1/common/net"
"github.com/xtls/xray-core/v1/common/session"
"github.com/xtls/xray-core/v1/features/routing"
)
// Context is an implementation of routing.Context, which is a wrapper of context.context with session info.
type Context struct {
Inbound *session.Inbound
Outbound *session.Outbound
Content *session.Content
}
// GetInboundTag implements routing.Context.
func (ctx *Context) GetInboundTag() string {
if ctx.Inbound == nil {
return ""
}
return ctx.Inbound.Tag
}
// GetSourceIPs implements routing.Context.
func (ctx *Context) GetSourceIPs() []net.IP {
if ctx.Inbound == nil || !ctx.Inbound.Source.IsValid() {
return nil
}
dest := ctx.Inbound.Source
if dest.Address.Family().IsDomain() {
return nil
}
return []net.IP{dest.Address.IP()}
}
// GetSourcePort implements routing.Context.
func (ctx *Context) GetSourcePort() net.Port {
if ctx.Inbound == nil || !ctx.Inbound.Source.IsValid() {
return 0
}
return ctx.Inbound.Source.Port
}
// GetTargetIPs implements routing.Context.
func (ctx *Context) GetTargetIPs() []net.IP {
if ctx.Outbound == nil || !ctx.Outbound.Target.IsValid() {
return nil
}
if ctx.Outbound.Target.Address.Family().IsIP() {
return []net.IP{ctx.Outbound.Target.Address.IP()}
}
return nil
}
// GetTargetPort implements routing.Context.
func (ctx *Context) GetTargetPort() net.Port {
if ctx.Outbound == nil || !ctx.Outbound.Target.IsValid() {
return 0
}
return ctx.Outbound.Target.Port
}
// GetTargetDomain implements routing.Context.
func (ctx *Context) GetTargetDomain() string {
if ctx.Outbound == nil || !ctx.Outbound.Target.IsValid() {
return ""
}
dest := ctx.Outbound.Target
if !dest.Address.Family().IsDomain() {
return ""
}
return dest.Address.Domain()
}
// GetNetwork implements routing.Context.
func (ctx *Context) GetNetwork() net.Network {
if ctx.Outbound == nil {
return net.Network_Unknown
}
return ctx.Outbound.Target.Network
}
// GetProtocol implements routing.Context.
func (ctx *Context) GetProtocol() string {
if ctx.Content == nil {
return ""
}
return ctx.Content.Protocol
}
// GetUser implements routing.Context.
func (ctx *Context) GetUser() string {
if ctx.Inbound == nil || ctx.Inbound.User == nil {
return ""
}
return ctx.Inbound.User.Email
}
// GetAttributes implements routing.Context.
func (ctx *Context) GetAttributes() map[string]string {
if ctx.Content == nil {
return nil
}
return ctx.Content.Attributes
}
// AsRoutingContext creates a context from context.context with session info.
func AsRoutingContext(ctx context.Context) routing.Context {
return &Context{
Inbound: session.InboundFromContext(ctx),
Outbound: session.OutboundFromContext(ctx),
Content: session.ContentFromContext(ctx),
}
}

View File

@ -0,0 +1,9 @@
package stats
import "github.com/xtls/xray-core/v1/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
}

151
features/stats/stats.go Normal file
View File

@ -0,0 +1,151 @@
package stats
//go:generate go run github.com/xtls/xray-core/v1/common/errors/errorgen
import (
"context"
"github.com/xtls/xray-core/v1/common"
"github.com/xtls/xray-core/v1/features"
)
// Counter is the interface for stats counters.
//
// xray:api:stable
type Counter interface {
// Value is the current value of the counter.
Value() int64
// Set sets a new value to the counter, and returns the previous one.
Set(int64) int64
// Add adds a value to the current counter value, and returns the previous value.
Add(int64) int64
}
// Channel is the interface for stats channel.
//
// xray:api:stable
type Channel interface {
// Channel is a runnable unit.
common.Runnable
// Publish broadcasts a message through the channel with a controlling context.
Publish(context.Context, interface{})
// SubscriberCount returns the number of the subscribers.
Subscribers() []chan interface{}
// Subscribe registers for listening to channel stream and returns a new listener channel.
Subscribe() (chan interface{}, error)
// Unsubscribe unregisters a listener channel from current Channel object.
Unsubscribe(chan interface{}) error
}
// SubscribeRunnableChannel subscribes the channel and starts it if there is first subscriber coming.
func SubscribeRunnableChannel(c Channel) (chan interface{}, error) {
if len(c.Subscribers()) == 0 {
if err := c.Start(); err != nil {
return nil, err
}
}
return c.Subscribe()
}
// UnsubscribeClosableChannel unsubcribes the channel and close it if there is no more subscriber.
func UnsubscribeClosableChannel(c Channel, sub chan interface{}) error {
if err := c.Unsubscribe(sub); err != nil {
return err
}
if len(c.Subscribers()) == 0 {
return c.Close()
}
return nil
}
// Manager is the interface for stats manager.
//
// xray:api:stable
type Manager interface {
features.Feature
// RegisterCounter registers a new counter to the manager. The identifier string must not be empty, and unique among other counters.
RegisterCounter(string) (Counter, error)
// UnregisterCounter unregisters a counter from the manager by its identifier.
UnregisterCounter(string) error
// GetCounter returns a counter by its identifier.
GetCounter(string) Counter
// RegisterChannel registers a new channel to the manager. The identifier string must not be empty, and unique among other channels.
RegisterChannel(string) (Channel, error)
// UnregisterCounter unregisters a channel from the manager by its identifier.
UnregisterChannel(string) error
// GetChannel returns a channel by its identifier.
GetChannel(string) Channel
}
// GetOrRegisterCounter tries to get the StatCounter first. If not exist, it then tries to create a new counter.
func GetOrRegisterCounter(m Manager, name string) (Counter, error) {
counter := m.GetCounter(name)
if counter != nil {
return counter, nil
}
return m.RegisterCounter(name)
}
// GetOrRegisterChannel tries to get the StatChannel first. If not exist, it then tries to create a new channel.
func GetOrRegisterChannel(m Manager, name string) (Channel, error) {
channel := m.GetChannel(name)
if channel != nil {
return channel, nil
}
return m.RegisterChannel(name)
}
// ManagerType returns the type of Manager interface. Can be used to implement common.HasType.
//
// xray:api:stable
func ManagerType() interface{} {
return (*Manager)(nil)
}
// NoopManager is an implementation of Manager, which doesn't has actual functionalities.
type NoopManager struct{}
// Type implements common.HasType.
func (NoopManager) Type() interface{} {
return ManagerType()
}
// RegisterCounter implements Manager.
func (NoopManager) RegisterCounter(string) (Counter, error) {
return nil, newError("not implemented")
}
// UnregisterCounter implements Manager.
func (NoopManager) UnregisterCounter(string) error {
return nil
}
// GetCounter implements Manager.
func (NoopManager) GetCounter(string) Counter {
return nil
}
// RegisterChannel implements Manager.
func (NoopManager) RegisterChannel(string) (Channel, error) {
return nil, newError("not implemented")
}
// UnregisterChannel implements Manager.
func (NoopManager) UnregisterChannel(string) error {
return nil
}
// GetChannel implements Manager.
func (NoopManager) GetChannel(string) Channel {
return nil
}
// Start implements common.Runnable.
func (NoopManager) Start() error { return nil }
// Close implements common.Closable.
func (NoopManager) Close() error { return nil }