mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-06-12 11:29:36 +03:00
v1.0.0
This commit is contained in:
59
features/dns/client.go
Normal file
59
features/dns/client.go
Normal 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
|
||||
}
|
80
features/dns/localdns/client.go
Normal file
80
features/dns/localdns/client.go
Normal 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{}
|
||||
}
|
9
features/errors.generated.go
Normal file
9
features/errors.generated.go
Normal 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
17
features/feature.go
Normal 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()
|
||||
}
|
42
features/inbound/inbound.go
Normal file
42
features/inbound/inbound.go
Normal 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)
|
||||
}
|
45
features/outbound/outbound.go
Normal file
45
features/outbound/outbound.go
Normal 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)
|
||||
}
|
37
features/policy/default.go
Normal file
37
features/policy/default.go
Normal 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
151
features/policy/policy.go
Normal 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)
|
||||
}
|
40
features/routing/context.go
Normal file
40
features/routing/context.go
Normal 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
|
||||
}
|
27
features/routing/dispatcher.go
Normal file
27
features/routing/dispatcher.go
Normal 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)
|
||||
}
|
44
features/routing/dns/context.go
Normal file
44
features/routing/dns/context.go
Normal 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}
|
||||
}
|
9
features/routing/dns/errors.generated.go
Normal file
9
features/routing/dns/errors.generated.go
Normal 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{})
|
||||
}
|
60
features/routing/router.go
Normal file
60
features/routing/router.go
Normal 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
|
||||
}
|
119
features/routing/session/context.go
Normal file
119
features/routing/session/context.go
Normal 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),
|
||||
}
|
||||
}
|
9
features/stats/errors.generated.go
Normal file
9
features/stats/errors.generated.go
Normal 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
151
features/stats/stats.go
Normal 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 }
|
Reference in New Issue
Block a user