mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-04-30 09:18:34 +00:00
v1.0.0
This commit is contained in:
parent
47d23e9972
commit
c7f7c08ead
711 changed files with 82154 additions and 2 deletions
14
core/annotations.go
Normal file
14
core/annotations.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package core
|
||||
|
||||
// Annotation is a concept in Xray. This struct is only for documentation. It is not used anywhere.
|
||||
// Annotations begin with "xray:" in comment, as metadata of functions or types.
|
||||
type Annotation struct {
|
||||
// API is for types or functions that can be used in other libs. Possible values are:
|
||||
//
|
||||
// * xray:api:beta for types or functions that are ready for use, but maybe changed in the future.
|
||||
// * xray:api:stable for types or functions with guarantee of backward compatibility.
|
||||
// * xray:api:deprecated for types or functions that should not be used anymore.
|
||||
//
|
||||
// Types or functions without api annotation should not be used externally.
|
||||
API string
|
||||
}
|
107
core/config.go
Normal file
107
core/config.go
Normal file
|
@ -0,0 +1,107 @@
|
|||
// +build !confonly
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common"
|
||||
"github.com/xtls/xray-core/v1/common/buf"
|
||||
"github.com/xtls/xray-core/v1/common/cmdarg"
|
||||
"github.com/xtls/xray-core/v1/main/confloader"
|
||||
)
|
||||
|
||||
// ConfigFormat is a configurable format of Xray config file.
|
||||
type ConfigFormat struct {
|
||||
Name string
|
||||
Extension []string
|
||||
Loader ConfigLoader
|
||||
}
|
||||
|
||||
// ConfigLoader is a utility to load Xray config from external source.
|
||||
type ConfigLoader func(input interface{}) (*Config, error)
|
||||
|
||||
var (
|
||||
configLoaderByName = make(map[string]*ConfigFormat)
|
||||
configLoaderByExt = make(map[string]*ConfigFormat)
|
||||
)
|
||||
|
||||
// RegisterConfigLoader add a new ConfigLoader.
|
||||
func RegisterConfigLoader(format *ConfigFormat) error {
|
||||
name := strings.ToLower(format.Name)
|
||||
if _, found := configLoaderByName[name]; found {
|
||||
return newError(format.Name, " already registered.")
|
||||
}
|
||||
configLoaderByName[name] = format
|
||||
|
||||
for _, ext := range format.Extension {
|
||||
lext := strings.ToLower(ext)
|
||||
if f, found := configLoaderByExt[lext]; found {
|
||||
return newError(ext, " already registered to ", f.Name)
|
||||
}
|
||||
configLoaderByExt[lext] = format
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getExtension(filename string) string {
|
||||
idx := strings.LastIndexByte(filename, '.')
|
||||
if idx == -1 {
|
||||
return ""
|
||||
}
|
||||
return filename[idx+1:]
|
||||
}
|
||||
|
||||
// LoadConfig loads config with given format from given source.
|
||||
// input accepts 2 different types:
|
||||
// * []string slice of multiple filename/url(s) to open to read
|
||||
// * io.Reader that reads a config content (the original way)
|
||||
func LoadConfig(formatName string, filename string, input interface{}) (*Config, error) {
|
||||
ext := getExtension(filename)
|
||||
if len(ext) > 0 {
|
||||
if f, found := configLoaderByExt[ext]; found {
|
||||
return f.Loader(input)
|
||||
}
|
||||
}
|
||||
|
||||
if f, found := configLoaderByName[formatName]; found {
|
||||
return f.Loader(input)
|
||||
}
|
||||
|
||||
return nil, newError("Unable to load config in ", formatName).AtWarning()
|
||||
}
|
||||
|
||||
func loadProtobufConfig(data []byte) (*Config, error) {
|
||||
config := new(Config)
|
||||
if err := proto.Unmarshal(data, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(RegisterConfigLoader(&ConfigFormat{
|
||||
Name: "Protobuf",
|
||||
Extension: []string{"pb"},
|
||||
Loader: func(input interface{}) (*Config, error) {
|
||||
switch v := input.(type) {
|
||||
case cmdarg.Arg:
|
||||
r, err := confloader.LoadConfig(v[0])
|
||||
common.Must(err)
|
||||
data, err := buf.ReadAllToBytes(r)
|
||||
common.Must(err)
|
||||
return loadProtobufConfig(data)
|
||||
case io.Reader:
|
||||
data, err := buf.ReadAllToBytes(v)
|
||||
common.Must(err)
|
||||
return loadProtobufConfig(data)
|
||||
default:
|
||||
return nil, newError("unknow type")
|
||||
}
|
||||
},
|
||||
}))
|
||||
}
|
439
core/config.pb.go
Normal file
439
core/config.pb.go
Normal file
|
@ -0,0 +1,439 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.14.0
|
||||
// source: core/config.proto
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
serial "github.com/xtls/xray-core/v1/common/serial"
|
||||
transport "github.com/xtls/xray-core/v1/transport"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
// Config is the master config of Xray. Xray takes this config as input and
|
||||
// functions accordingly.
|
||||
type Config struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Inbound handler configurations. Must have at least one item.
|
||||
Inbound []*InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbound,proto3" json:"inbound,omitempty"`
|
||||
// Outbound handler configurations. Must have at least one item. The first
|
||||
// item is used as default for routing.
|
||||
Outbound []*OutboundHandlerConfig `protobuf:"bytes,2,rep,name=outbound,proto3" json:"outbound,omitempty"`
|
||||
// App is for configurations of all features in Xray. A feature must
|
||||
// implement the Feature interface, and its config type must be registered
|
||||
// through common.RegisterConfig.
|
||||
App []*serial.TypedMessage `protobuf:"bytes,4,rep,name=app,proto3" json:"app,omitempty"`
|
||||
// Transport settings.
|
||||
// Deprecated. Each inbound and outbound should choose their own transport
|
||||
// config. Date to remove: 2020-01-13
|
||||
//
|
||||
// Deprecated: Do not use.
|
||||
Transport *transport.Config `protobuf:"bytes,5,opt,name=transport,proto3" json:"transport,omitempty"`
|
||||
// Configuration for extensions. The config may not work if corresponding
|
||||
// extension is not loaded into Xray. Xray will ignore such config during
|
||||
// initialization.
|
||||
Extension []*serial.TypedMessage `protobuf:"bytes,6,rep,name=extension,proto3" json:"extension,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_core_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Config) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_core_config_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_core_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Config) GetInbound() []*InboundHandlerConfig {
|
||||
if x != nil {
|
||||
return x.Inbound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetOutbound() []*OutboundHandlerConfig {
|
||||
if x != nil {
|
||||
return x.Outbound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetApp() []*serial.TypedMessage {
|
||||
if x != nil {
|
||||
return x.App
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: Do not use.
|
||||
func (x *Config) GetTransport() *transport.Config {
|
||||
if x != nil {
|
||||
return x.Transport
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetExtension() []*serial.TypedMessage {
|
||||
if x != nil {
|
||||
return x.Extension
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InboundHandlerConfig is the configuration for inbound handler.
|
||||
type InboundHandlerConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Tag of the inbound handler. The tag must be unique among all inbound
|
||||
// handlers
|
||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
// Settings for how this inbound proxy is handled.
|
||||
ReceiverSettings *serial.TypedMessage `protobuf:"bytes,2,opt,name=receiver_settings,json=receiverSettings,proto3" json:"receiver_settings,omitempty"`
|
||||
// Settings for inbound proxy. Must be one of the inbound proxies.
|
||||
ProxySettings *serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"`
|
||||
}
|
||||
|
||||
func (x *InboundHandlerConfig) Reset() {
|
||||
*x = InboundHandlerConfig{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_core_config_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *InboundHandlerConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*InboundHandlerConfig) ProtoMessage() {}
|
||||
|
||||
func (x *InboundHandlerConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_core_config_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use InboundHandlerConfig.ProtoReflect.Descriptor instead.
|
||||
func (*InboundHandlerConfig) Descriptor() ([]byte, []int) {
|
||||
return file_core_config_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *InboundHandlerConfig) GetTag() string {
|
||||
if x != nil {
|
||||
return x.Tag
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *InboundHandlerConfig) GetReceiverSettings() *serial.TypedMessage {
|
||||
if x != nil {
|
||||
return x.ReceiverSettings
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *InboundHandlerConfig) GetProxySettings() *serial.TypedMessage {
|
||||
if x != nil {
|
||||
return x.ProxySettings
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OutboundHandlerConfig is the configuration for outbound handler.
|
||||
type OutboundHandlerConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Tag of this outbound handler.
|
||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||
// Settings for how to dial connection for this outbound handler.
|
||||
SenderSettings *serial.TypedMessage `protobuf:"bytes,2,opt,name=sender_settings,json=senderSettings,proto3" json:"sender_settings,omitempty"`
|
||||
// Settings for this outbound proxy. Must be one of the outbound proxies.
|
||||
ProxySettings *serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"`
|
||||
// If not zero, this outbound will be expired in seconds. Not used for now.
|
||||
Expire int64 `protobuf:"varint,4,opt,name=expire,proto3" json:"expire,omitempty"`
|
||||
// Comment of this outbound handler. Not used for now.
|
||||
Comment string `protobuf:"bytes,5,opt,name=comment,proto3" json:"comment,omitempty"`
|
||||
}
|
||||
|
||||
func (x *OutboundHandlerConfig) Reset() {
|
||||
*x = OutboundHandlerConfig{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_core_config_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *OutboundHandlerConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*OutboundHandlerConfig) ProtoMessage() {}
|
||||
|
||||
func (x *OutboundHandlerConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_core_config_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use OutboundHandlerConfig.ProtoReflect.Descriptor instead.
|
||||
func (*OutboundHandlerConfig) Descriptor() ([]byte, []int) {
|
||||
return file_core_config_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *OutboundHandlerConfig) GetTag() string {
|
||||
if x != nil {
|
||||
return x.Tag
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *OutboundHandlerConfig) GetSenderSettings() *serial.TypedMessage {
|
||||
if x != nil {
|
||||
return x.SenderSettings
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *OutboundHandlerConfig) GetProxySettings() *serial.TypedMessage {
|
||||
if x != nil {
|
||||
return x.ProxySettings
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *OutboundHandlerConfig) GetExpire() int64 {
|
||||
if x != nil {
|
||||
return x.Expire
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *OutboundHandlerConfig) GetComment() string {
|
||||
if x != nil {
|
||||
return x.Comment
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_core_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_core_config_proto_rawDesc = []byte{
|
||||
0x0a, 0x11, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x12, 0x09, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x1a, 0x21,
|
||||
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79,
|
||||
0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x1a, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x63, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb5, 0x02, 0x0a, 0x06, 0x43, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18,
|
||||
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72,
|
||||
0x65, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72,
|
||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12,
|
||||
0x3c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x4f, 0x75,
|
||||
0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x32, 0x0a,
|
||||
0x03, 0x61, 0x70, 0x70, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e,
|
||||
0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x03, 0x61, 0x70,
|
||||
0x70, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x05,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x02, 0x18, 0x01,
|
||||
0x52, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x09, 0x65,
|
||||
0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20,
|
||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72,
|
||||
0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||
0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x03, 0x10,
|
||||
0x04, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e,
|
||||
0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61,
|
||||
0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x4d, 0x0a, 0x11,
|
||||
0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
|
||||
0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70,
|
||||
0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x10, 0x72, 0x65, 0x63, 0x65, 0x69,
|
||||
0x76, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x70,
|
||||
0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
|
||||
0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65,
|
||||
0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65, 0x74, 0x74,
|
||||
0x69, 0x6e, 0x67, 0x73, 0x22, 0xef, 0x01, 0x0a, 0x15, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e,
|
||||
0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10,
|
||||
0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67,
|
||||
0x12, 0x49, 0x0a, 0x0f, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69,
|
||||
0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54,
|
||||
0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0e, 0x73, 0x65, 0x6e,
|
||||
0x64, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x70,
|
||||
0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
|
||||
0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65,
|
||||
0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65, 0x74, 0x74,
|
||||
0x69, 0x6e, 0x67, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x12, 0x18, 0x0a, 0x07,
|
||||
0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63,
|
||||
0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x40, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
|
||||
0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d,
|
||||
0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0xaa, 0x02, 0x09, 0x58,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_core_config_proto_rawDescOnce sync.Once
|
||||
file_core_config_proto_rawDescData = file_core_config_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_core_config_proto_rawDescGZIP() []byte {
|
||||
file_core_config_proto_rawDescOnce.Do(func() {
|
||||
file_core_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_core_config_proto_rawDescData)
|
||||
})
|
||||
return file_core_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_core_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_core_config_proto_goTypes = []interface{}{
|
||||
(*Config)(nil), // 0: xray.core.Config
|
||||
(*InboundHandlerConfig)(nil), // 1: xray.core.InboundHandlerConfig
|
||||
(*OutboundHandlerConfig)(nil), // 2: xray.core.OutboundHandlerConfig
|
||||
(*serial.TypedMessage)(nil), // 3: xray.common.serial.TypedMessage
|
||||
(*transport.Config)(nil), // 4: xray.transport.Config
|
||||
}
|
||||
var file_core_config_proto_depIdxs = []int32{
|
||||
1, // 0: xray.core.Config.inbound:type_name -> xray.core.InboundHandlerConfig
|
||||
2, // 1: xray.core.Config.outbound:type_name -> xray.core.OutboundHandlerConfig
|
||||
3, // 2: xray.core.Config.app:type_name -> xray.common.serial.TypedMessage
|
||||
4, // 3: xray.core.Config.transport:type_name -> xray.transport.Config
|
||||
3, // 4: xray.core.Config.extension:type_name -> xray.common.serial.TypedMessage
|
||||
3, // 5: xray.core.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage
|
||||
3, // 6: xray.core.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage
|
||||
3, // 7: xray.core.OutboundHandlerConfig.sender_settings:type_name -> xray.common.serial.TypedMessage
|
||||
3, // 8: xray.core.OutboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage
|
||||
9, // [9:9] is the sub-list for method output_type
|
||||
9, // [9:9] is the sub-list for method input_type
|
||||
9, // [9:9] is the sub-list for extension type_name
|
||||
9, // [9:9] is the sub-list for extension extendee
|
||||
0, // [0:9] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_core_config_proto_init() }
|
||||
func file_core_config_proto_init() {
|
||||
if File_core_config_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_core_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Config); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_core_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*InboundHandlerConfig); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_core_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*OutboundHandlerConfig); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_core_config_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_core_config_proto_goTypes,
|
||||
DependencyIndexes: file_core_config_proto_depIdxs,
|
||||
MessageInfos: file_core_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_core_config_proto = out.File
|
||||
file_core_config_proto_rawDesc = nil
|
||||
file_core_config_proto_goTypes = nil
|
||||
file_core_config_proto_depIdxs = nil
|
||||
}
|
63
core/config.proto
Normal file
63
core/config.proto
Normal file
|
@ -0,0 +1,63 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package xray.core;
|
||||
option csharp_namespace = "Xray.Core";
|
||||
option go_package = "github.com/xtls/xray-core/v1/core";
|
||||
option java_package = "com.xray.core";
|
||||
option java_multiple_files = true;
|
||||
|
||||
import "common/serial/typed_message.proto";
|
||||
import "transport/config.proto";
|
||||
|
||||
// Config is the master config of Xray. Xray takes this config as input and
|
||||
// functions accordingly.
|
||||
message Config {
|
||||
// Inbound handler configurations. Must have at least one item.
|
||||
repeated InboundHandlerConfig inbound = 1;
|
||||
|
||||
// Outbound handler configurations. Must have at least one item. The first
|
||||
// item is used as default for routing.
|
||||
repeated OutboundHandlerConfig outbound = 2;
|
||||
|
||||
reserved 3;
|
||||
|
||||
// App is for configurations of all features in Xray. A feature must
|
||||
// implement the Feature interface, and its config type must be registered
|
||||
// through common.RegisterConfig.
|
||||
repeated xray.common.serial.TypedMessage app = 4;
|
||||
|
||||
// Transport settings.
|
||||
// Deprecated. Each inbound and outbound should choose their own transport
|
||||
// config. Date to remove: 2020-01-13
|
||||
xray.transport.Config transport = 5 [deprecated = true];
|
||||
|
||||
// Configuration for extensions. The config may not work if corresponding
|
||||
// extension is not loaded into Xray. Xray will ignore such config during
|
||||
// initialization.
|
||||
repeated xray.common.serial.TypedMessage extension = 6;
|
||||
}
|
||||
|
||||
// InboundHandlerConfig is the configuration for inbound handler.
|
||||
message InboundHandlerConfig {
|
||||
// Tag of the inbound handler. The tag must be unique among all inbound
|
||||
// handlers
|
||||
string tag = 1;
|
||||
// Settings for how this inbound proxy is handled.
|
||||
xray.common.serial.TypedMessage receiver_settings = 2;
|
||||
// Settings for inbound proxy. Must be one of the inbound proxies.
|
||||
xray.common.serial.TypedMessage proxy_settings = 3;
|
||||
}
|
||||
|
||||
// OutboundHandlerConfig is the configuration for outbound handler.
|
||||
message OutboundHandlerConfig {
|
||||
// Tag of this outbound handler.
|
||||
string tag = 1;
|
||||
// Settings for how to dial connection for this outbound handler.
|
||||
xray.common.serial.TypedMessage sender_settings = 2;
|
||||
// Settings for this outbound proxy. Must be one of the outbound proxies.
|
||||
xray.common.serial.TypedMessage proxy_settings = 3;
|
||||
// If not zero, this outbound will be expired in seconds. Not used for now.
|
||||
int64 expire = 4;
|
||||
// Comment of this outbound handler. Not used for now.
|
||||
string comment = 5;
|
||||
}
|
29
core/context.go
Normal file
29
core/context.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
// +build !confonly
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// XrayKey is the key type of Instance in Context, exported for test.
|
||||
type XrayKey int
|
||||
|
||||
const xrayKey XrayKey = 1
|
||||
|
||||
// FromContext returns an Instance from the given context, or nil if the context doesn't contain one.
|
||||
func FromContext(ctx context.Context) *Instance {
|
||||
if s, ok := ctx.Value(xrayKey).(*Instance); ok {
|
||||
return s
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MustFromContext returns an Instance from the given context, or panics if not present.
|
||||
func MustFromContext(ctx context.Context) *Instance {
|
||||
x := FromContext(ctx)
|
||||
if x == nil {
|
||||
panic("X is not in context.")
|
||||
}
|
||||
return x
|
||||
}
|
19
core/context_test.go
Normal file
19
core/context_test.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package core_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
. "github.com/xtls/xray-core/v1/core"
|
||||
)
|
||||
|
||||
func TestContextPanic(t *testing.T) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r == nil {
|
||||
t.Error("expect panic, but nil")
|
||||
}
|
||||
}()
|
||||
|
||||
MustFromContext(context.Background())
|
||||
}
|
39
core/core.go
Normal file
39
core/core.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Package core provides an entry point to use Xray core functionalities.
|
||||
//
|
||||
// Xray makes it possible to accept incoming network connections with certain
|
||||
// protocol, process the data, and send them through another connection with
|
||||
// the same or a difference protocol on demand.
|
||||
//
|
||||
// It may be configured to work with multiple protocols at the same time, and
|
||||
// uses the internal router to tunnel through different inbound and outbound
|
||||
// connections.
|
||||
package core
|
||||
|
||||
//go:generate go run github.com/xtls/xray-core/v1/common/errors/errorgen
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common/serial"
|
||||
)
|
||||
|
||||
var (
|
||||
version = "1.0.0"
|
||||
build = "Custom"
|
||||
codename = "Xray, Penetrates Everything."
|
||||
intro = "A unified platform for anti-censorship."
|
||||
)
|
||||
|
||||
// Version returns Xray's version as a string, in the form of "x.y.z" where x, y and z are numbers.
|
||||
// ".z" part may be omitted in regular releases.
|
||||
func Version() string {
|
||||
return version
|
||||
}
|
||||
|
||||
// VersionStatement returns a list of strings representing the full version info.
|
||||
func VersionStatement() []string {
|
||||
return []string{
|
||||
serial.Concat("Xray ", Version(), " (", codename, ") ", build, " (", runtime.Version(), " ", runtime.GOOS, "/", runtime.GOARCH, ")"),
|
||||
intro,
|
||||
}
|
||||
}
|
9
core/errors.generated.go
Normal file
9
core/errors.generated.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package core
|
||||
|
||||
import "github.com/xtls/xray-core/v1/common/errors"
|
||||
|
||||
type errPathObjHolder struct{}
|
||||
|
||||
func newError(values ...interface{}) *errors.Error {
|
||||
return errors.New(values...).WithPathObj(errPathObjHolder{})
|
||||
}
|
79
core/functions.go
Normal file
79
core/functions.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
// +build !confonly
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common"
|
||||
"github.com/xtls/xray-core/v1/common/net"
|
||||
"github.com/xtls/xray-core/v1/features/routing"
|
||||
"github.com/xtls/xray-core/v1/transport/internet/udp"
|
||||
)
|
||||
|
||||
// CreateObject creates a new object based on the given Xray instance and config. The Xray instance may be nil.
|
||||
func CreateObject(v *Instance, config interface{}) (interface{}, error) {
|
||||
ctx := v.ctx
|
||||
if v != nil {
|
||||
ctx = context.WithValue(ctx, xrayKey, v)
|
||||
}
|
||||
return common.CreateObject(ctx, config)
|
||||
}
|
||||
|
||||
// StartInstance starts a new Xray instance with given serialized config.
|
||||
// By default Xray only support config in protobuf format, i.e., configFormat = "protobuf". Caller need to load other packages to add JSON support.
|
||||
//
|
||||
// xray:api:stable
|
||||
func StartInstance(configFormat string, configBytes []byte) (*Instance, error) {
|
||||
config, err := LoadConfig(configFormat, "", bytes.NewReader(configBytes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instance, err := New(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := instance.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
// Dial provides an easy way for upstream caller to create net.Conn through Xray.
|
||||
// It dispatches the request to the given destination by the given Xray instance.
|
||||
// Since it is under a proxy context, the LocalAddr() and RemoteAddr() in returned net.Conn
|
||||
// will not show real addresses being used for communication.
|
||||
//
|
||||
// xray:api:stable
|
||||
func Dial(ctx context.Context, v *Instance, dest net.Destination) (net.Conn, error) {
|
||||
dispatcher := v.GetFeature(routing.DispatcherType())
|
||||
if dispatcher == nil {
|
||||
return nil, newError("routing.Dispatcher is not registered in Xray core")
|
||||
}
|
||||
r, err := dispatcher.(routing.Dispatcher).Dispatch(ctx, dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var readerOpt net.ConnectionOption
|
||||
if dest.Network == net.Network_TCP {
|
||||
readerOpt = net.ConnectionOutputMulti(r.Reader)
|
||||
} else {
|
||||
readerOpt = net.ConnectionOutputMultiUDP(r.Reader)
|
||||
}
|
||||
return net.NewConnection(net.ConnectionInputMulti(r.Writer), readerOpt), nil
|
||||
}
|
||||
|
||||
// DialUDP provides a way to exchange UDP packets through Xray instance to remote servers.
|
||||
// Since it is under a proxy context, the LocalAddr() in returned PacketConn will not show the real address.
|
||||
//
|
||||
// TODO: SetDeadline() / SetReadDeadline() / SetWriteDeadline() are not implemented.
|
||||
//
|
||||
// xray:api:beta
|
||||
func DialUDP(ctx context.Context, v *Instance) (net.PacketConn, error) {
|
||||
dispatcher := v.GetFeature(routing.DispatcherType())
|
||||
if dispatcher == nil {
|
||||
return nil, newError("routing.Dispatcher is not registered in Xray core")
|
||||
}
|
||||
return udp.DialDispatcher(ctx, dispatcher.(routing.Dispatcher))
|
||||
}
|
231
core/functions_test.go
Normal file
231
core/functions_test.go
Normal file
|
@ -0,0 +1,231 @@
|
|||
package core_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/xtls/xray-core/v1/app/dispatcher"
|
||||
"github.com/xtls/xray-core/v1/app/proxyman"
|
||||
"github.com/xtls/xray-core/v1/common"
|
||||
"github.com/xtls/xray-core/v1/common/net"
|
||||
"github.com/xtls/xray-core/v1/common/serial"
|
||||
core "github.com/xtls/xray-core/v1/core"
|
||||
"github.com/xtls/xray-core/v1/proxy/freedom"
|
||||
"github.com/xtls/xray-core/v1/testing/servers/tcp"
|
||||
"github.com/xtls/xray-core/v1/testing/servers/udp"
|
||||
)
|
||||
|
||||
func xor(b []byte) []byte {
|
||||
r := make([]byte, len(b))
|
||||
for i, v := range b {
|
||||
r[i] = v ^ 'c'
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func xor2(b []byte) []byte {
|
||||
r := make([]byte, len(b))
|
||||
for i, v := range b {
|
||||
r[i] = v ^ 'd'
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func TestXrayDial(t *testing.T) {
|
||||
tcpServer := tcp.Server{
|
||||
MsgProcessor: xor,
|
||||
}
|
||||
dest, err := tcpServer.Start()
|
||||
common.Must(err)
|
||||
defer tcpServer.Close()
|
||||
|
||||
config := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&dispatcher.Config{}),
|
||||
serial.ToTypedMessage(&proxyman.InboundConfig{}),
|
||||
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
|
||||
},
|
||||
Outbound: []*core.OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cfgBytes, err := proto.Marshal(config)
|
||||
common.Must(err)
|
||||
|
||||
server, err := core.StartInstance("protobuf", cfgBytes)
|
||||
common.Must(err)
|
||||
defer server.Close()
|
||||
|
||||
conn, err := core.Dial(context.Background(), server, dest)
|
||||
common.Must(err)
|
||||
defer conn.Close()
|
||||
|
||||
const size = 10240 * 1024
|
||||
payload := make([]byte, size)
|
||||
common.Must2(rand.Read(payload))
|
||||
|
||||
if _, err := conn.Write(payload); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
receive := make([]byte, size)
|
||||
if _, err := io.ReadFull(conn, receive); err != nil {
|
||||
t.Fatal("failed to read all response: ", err)
|
||||
}
|
||||
|
||||
if r := cmp.Diff(xor(receive), payload); r != "" {
|
||||
t.Error(r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestXrayDialUDPConn(t *testing.T) {
|
||||
udpServer := udp.Server{
|
||||
MsgProcessor: xor,
|
||||
}
|
||||
dest, err := udpServer.Start()
|
||||
common.Must(err)
|
||||
defer udpServer.Close()
|
||||
|
||||
config := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&dispatcher.Config{}),
|
||||
serial.ToTypedMessage(&proxyman.InboundConfig{}),
|
||||
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
|
||||
},
|
||||
Outbound: []*core.OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cfgBytes, err := proto.Marshal(config)
|
||||
common.Must(err)
|
||||
|
||||
server, err := core.StartInstance("protobuf", cfgBytes)
|
||||
common.Must(err)
|
||||
defer server.Close()
|
||||
|
||||
conn, err := core.Dial(context.Background(), server, dest)
|
||||
common.Must(err)
|
||||
defer conn.Close()
|
||||
|
||||
const size = 1024
|
||||
payload := make([]byte, size)
|
||||
common.Must2(rand.Read(payload))
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
if _, err := conn.Write(payload); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
|
||||
receive := make([]byte, size*2)
|
||||
for i := 0; i < 2; i++ {
|
||||
n, err := conn.Read(receive)
|
||||
if err != nil {
|
||||
t.Fatal("expect no error, but got ", err)
|
||||
}
|
||||
if n != size {
|
||||
t.Fatal("expect read size ", size, " but got ", n)
|
||||
}
|
||||
|
||||
if r := cmp.Diff(xor(receive[:n]), payload); r != "" {
|
||||
t.Fatal(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestXrayDialUDP(t *testing.T) {
|
||||
udpServer1 := udp.Server{
|
||||
MsgProcessor: xor,
|
||||
}
|
||||
dest1, err := udpServer1.Start()
|
||||
common.Must(err)
|
||||
defer udpServer1.Close()
|
||||
|
||||
udpServer2 := udp.Server{
|
||||
MsgProcessor: xor2,
|
||||
}
|
||||
dest2, err := udpServer2.Start()
|
||||
common.Must(err)
|
||||
defer udpServer2.Close()
|
||||
|
||||
config := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&dispatcher.Config{}),
|
||||
serial.ToTypedMessage(&proxyman.InboundConfig{}),
|
||||
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
|
||||
},
|
||||
Outbound: []*core.OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cfgBytes, err := proto.Marshal(config)
|
||||
common.Must(err)
|
||||
|
||||
server, err := core.StartInstance("protobuf", cfgBytes)
|
||||
common.Must(err)
|
||||
defer server.Close()
|
||||
|
||||
conn, err := core.DialUDP(context.Background(), server)
|
||||
common.Must(err)
|
||||
defer conn.Close()
|
||||
|
||||
const size = 1024
|
||||
{
|
||||
payload := make([]byte, size)
|
||||
common.Must2(rand.Read(payload))
|
||||
|
||||
if _, err := conn.WriteTo(payload, &net.UDPAddr{
|
||||
IP: dest1.Address.IP(),
|
||||
Port: int(dest1.Port),
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
receive := make([]byte, size)
|
||||
if _, _, err := conn.ReadFrom(receive); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if r := cmp.Diff(xor(receive), payload); r != "" {
|
||||
t.Error(r)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
payload := make([]byte, size)
|
||||
common.Must2(rand.Read(payload))
|
||||
|
||||
if _, err := conn.WriteTo(payload, &net.UDPAddr{
|
||||
IP: dest2.Address.IP(),
|
||||
Port: int(dest2.Port),
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
receive := make([]byte, size)
|
||||
if _, _, err := conn.ReadFrom(receive); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if r := cmp.Diff(xor2(receive), payload); r != "" {
|
||||
t.Error(r)
|
||||
}
|
||||
}
|
||||
}
|
8
core/mocks.go
Normal file
8
core/mocks.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
package core
|
||||
|
||||
//go:generate go run github.com/golang/mock/mockgen -package mocks -destination testing/mocks/io.go -mock_names Reader=Reader,Writer=Writer io Reader,Writer
|
||||
//go:generate go run github.com/golang/mock/mockgen -package mocks -destination testing/mocks/log.go -mock_names Handler=LogHandler github.com/xtls/xray-core/v1/common/log Handler
|
||||
//go:generate go run github.com/golang/mock/mockgen -package mocks -destination testing/mocks/mux.go -mock_names ClientWorkerFactory=MuxClientWorkerFactory github.com/xtls/xray-core/v1/common/mux ClientWorkerFactory
|
||||
//go:generate go run github.com/golang/mock/mockgen -package mocks -destination testing/mocks/dns.go -mock_names Client=DNSClient github.com/xtls/xray-core/v1/features/dns Client
|
||||
//go:generate go run github.com/golang/mock/mockgen -package mocks -destination testing/mocks/outbound.go -mock_names Manager=OutboundManager,HandlerSelector=OutboundHandlerSelector github.com/xtls/xray-core/v1/features/outbound Manager,HandlerSelector
|
||||
//go:generate go run github.com/golang/mock/mockgen -package mocks -destination testing/mocks/proxy.go -mock_names Inbound=ProxyInbound,Outbound=ProxyOutbound github.com/xtls/xray-core/v1/proxy Inbound,Outbound
|
12
core/proto.go
Normal file
12
core/proto.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package core
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
//go:generate go install -v google.golang.org/protobuf/cmd/protoc-gen-go
|
||||
//go:generate go install -v google.golang.org/grpc/cmd/protoc-gen-go-grpc
|
||||
//go:generate go install -v github.com/gogo/protobuf/protoc-gen-gofast
|
||||
//go:generate go run ../infra/vprotogen/main.go
|
||||
|
||||
// ProtoFilesUsingProtocGenGoFast is the map of Proto files
|
||||
// that use `protoc-gen-gofast` to generate pb.go files
|
||||
var ProtoFilesUsingProtocGenGoFast = map[string]bool{filepath.Join("proxy", "vless", "encoding", "addons.proto"): true}
|
345
core/xray.go
Normal file
345
core/xray.go
Normal file
|
@ -0,0 +1,345 @@
|
|||
// +build !confonly
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/xtls/xray-core/v1/common"
|
||||
"github.com/xtls/xray-core/v1/common/serial"
|
||||
"github.com/xtls/xray-core/v1/features"
|
||||
"github.com/xtls/xray-core/v1/features/dns"
|
||||
"github.com/xtls/xray-core/v1/features/dns/localdns"
|
||||
"github.com/xtls/xray-core/v1/features/inbound"
|
||||
"github.com/xtls/xray-core/v1/features/outbound"
|
||||
"github.com/xtls/xray-core/v1/features/policy"
|
||||
"github.com/xtls/xray-core/v1/features/routing"
|
||||
"github.com/xtls/xray-core/v1/features/stats"
|
||||
)
|
||||
|
||||
// Server is an instance of Xray. At any time, there must be at most one Server instance running.
|
||||
type Server interface {
|
||||
common.Runnable
|
||||
}
|
||||
|
||||
// ServerType returns the type of the server.
|
||||
func ServerType() interface{} {
|
||||
return (*Instance)(nil)
|
||||
}
|
||||
|
||||
type resolution struct {
|
||||
deps []reflect.Type
|
||||
callback interface{}
|
||||
}
|
||||
|
||||
func getFeature(allFeatures []features.Feature, t reflect.Type) features.Feature {
|
||||
for _, f := range allFeatures {
|
||||
if reflect.TypeOf(f.Type()) == t {
|
||||
return f
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *resolution) resolve(allFeatures []features.Feature) (bool, error) {
|
||||
var fs []features.Feature
|
||||
for _, d := range r.deps {
|
||||
f := getFeature(allFeatures, d)
|
||||
if f == nil {
|
||||
return false, nil
|
||||
}
|
||||
fs = append(fs, f)
|
||||
}
|
||||
|
||||
callback := reflect.ValueOf(r.callback)
|
||||
var input []reflect.Value
|
||||
callbackType := callback.Type()
|
||||
for i := 0; i < callbackType.NumIn(); i++ {
|
||||
pt := callbackType.In(i)
|
||||
for _, f := range fs {
|
||||
if reflect.TypeOf(f).AssignableTo(pt) {
|
||||
input = append(input, reflect.ValueOf(f))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(input) != callbackType.NumIn() {
|
||||
panic("Can't get all input parameters")
|
||||
}
|
||||
|
||||
var err error
|
||||
ret := callback.Call(input)
|
||||
errInterface := reflect.TypeOf((*error)(nil)).Elem()
|
||||
for i := len(ret) - 1; i >= 0; i-- {
|
||||
if ret[i].Type() == errInterface {
|
||||
v := ret[i].Interface()
|
||||
if v != nil {
|
||||
err = v.(error)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return true, err
|
||||
}
|
||||
|
||||
// Instance combines all functionalities in Xray.
|
||||
type Instance struct {
|
||||
access sync.Mutex
|
||||
features []features.Feature
|
||||
featureResolutions []resolution
|
||||
running bool
|
||||
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func AddInboundHandler(server *Instance, config *InboundHandlerConfig) error {
|
||||
inboundManager := server.GetFeature(inbound.ManagerType()).(inbound.Manager)
|
||||
rawHandler, err := CreateObject(server, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
handler, ok := rawHandler.(inbound.Handler)
|
||||
if !ok {
|
||||
return newError("not an InboundHandler")
|
||||
}
|
||||
if err := inboundManager.AddHandler(server.ctx, handler); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addInboundHandlers(server *Instance, configs []*InboundHandlerConfig) error {
|
||||
for _, inboundConfig := range configs {
|
||||
if err := AddInboundHandler(server, inboundConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func AddOutboundHandler(server *Instance, config *OutboundHandlerConfig) error {
|
||||
outboundManager := server.GetFeature(outbound.ManagerType()).(outbound.Manager)
|
||||
rawHandler, err := CreateObject(server, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
handler, ok := rawHandler.(outbound.Handler)
|
||||
if !ok {
|
||||
return newError("not an OutboundHandler")
|
||||
}
|
||||
if err := outboundManager.AddHandler(server.ctx, handler); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addOutboundHandlers(server *Instance, configs []*OutboundHandlerConfig) error {
|
||||
for _, outboundConfig := range configs {
|
||||
if err := AddOutboundHandler(server, outboundConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequireFeatures is a helper function to require features from Instance in context.
|
||||
// See Instance.RequireFeatures for more information.
|
||||
func RequireFeatures(ctx context.Context, callback interface{}) error {
|
||||
v := MustFromContext(ctx)
|
||||
return v.RequireFeatures(callback)
|
||||
}
|
||||
|
||||
// New returns a new Xray instance based on given configuration.
|
||||
// The instance is not started at this point.
|
||||
// To ensure Xray instance works properly, the config must contain one Dispatcher, one InboundHandlerManager and one OutboundHandlerManager. Other features are optional.
|
||||
func New(config *Config) (*Instance, error) {
|
||||
var server = &Instance{ctx: context.Background()}
|
||||
|
||||
done, err := initInstanceWithConfig(config, server)
|
||||
if done {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func NewWithContext(ctx context.Context, config *Config) (*Instance, error) {
|
||||
var server = &Instance{ctx: ctx}
|
||||
|
||||
done, err := initInstanceWithConfig(config, server)
|
||||
if done {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func initInstanceWithConfig(config *Config, server *Instance) (bool, error) {
|
||||
if config.Transport != nil {
|
||||
features.PrintDeprecatedFeatureWarning("global transport settings")
|
||||
}
|
||||
if err := config.Transport.Apply(); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
for _, appSettings := range config.App {
|
||||
settings, err := appSettings.GetInstance()
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
obj, err := CreateObject(server, settings)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
if feature, ok := obj.(features.Feature); ok {
|
||||
if err := server.AddFeature(feature); err != nil {
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
essentialFeatures := []struct {
|
||||
Type interface{}
|
||||
Instance features.Feature
|
||||
}{
|
||||
{dns.ClientType(), localdns.New()},
|
||||
{policy.ManagerType(), policy.DefaultManager{}},
|
||||
{routing.RouterType(), routing.DefaultRouter{}},
|
||||
{stats.ManagerType(), stats.NoopManager{}},
|
||||
}
|
||||
|
||||
for _, f := range essentialFeatures {
|
||||
if server.GetFeature(f.Type) == nil {
|
||||
if err := server.AddFeature(f.Instance); err != nil {
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if server.featureResolutions != nil {
|
||||
return true, newError("not all dependency are resolved.")
|
||||
}
|
||||
|
||||
if err := addInboundHandlers(server, config.Inbound); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
if err := addOutboundHandlers(server, config.Outbound); err != nil {
|
||||
return true, err
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Type implements common.HasType.
|
||||
func (s *Instance) Type() interface{} {
|
||||
return ServerType()
|
||||
}
|
||||
|
||||
// Close shutdown the Xray instance.
|
||||
func (s *Instance) Close() error {
|
||||
s.access.Lock()
|
||||
defer s.access.Unlock()
|
||||
|
||||
s.running = false
|
||||
|
||||
var errors []interface{}
|
||||
for _, f := range s.features {
|
||||
if err := f.Close(); err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
}
|
||||
if len(errors) > 0 {
|
||||
return newError("failed to close all features").Base(newError(serial.Concat(errors...)))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequireFeatures registers a callback, which will be called when all dependent features are registered.
|
||||
// The callback must be a func(). All its parameters must be features.Feature.
|
||||
func (s *Instance) RequireFeatures(callback interface{}) error {
|
||||
callbackType := reflect.TypeOf(callback)
|
||||
if callbackType.Kind() != reflect.Func {
|
||||
panic("not a function")
|
||||
}
|
||||
|
||||
var featureTypes []reflect.Type
|
||||
for i := 0; i < callbackType.NumIn(); i++ {
|
||||
featureTypes = append(featureTypes, reflect.PtrTo(callbackType.In(i)))
|
||||
}
|
||||
|
||||
r := resolution{
|
||||
deps: featureTypes,
|
||||
callback: callback,
|
||||
}
|
||||
if finished, err := r.resolve(s.features); finished {
|
||||
return err
|
||||
}
|
||||
s.featureResolutions = append(s.featureResolutions, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddFeature registers a feature into current Instance.
|
||||
func (s *Instance) AddFeature(feature features.Feature) error {
|
||||
s.features = append(s.features, feature)
|
||||
|
||||
if s.running {
|
||||
if err := feature.Start(); err != nil {
|
||||
newError("failed to start feature").Base(err).WriteToLog()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if s.featureResolutions == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var pendingResolutions []resolution
|
||||
for _, r := range s.featureResolutions {
|
||||
finished, err := r.resolve(s.features)
|
||||
if finished && err != nil {
|
||||
return err
|
||||
}
|
||||
if !finished {
|
||||
pendingResolutions = append(pendingResolutions, r)
|
||||
}
|
||||
}
|
||||
if len(pendingResolutions) == 0 {
|
||||
s.featureResolutions = nil
|
||||
} else if len(pendingResolutions) < len(s.featureResolutions) {
|
||||
s.featureResolutions = pendingResolutions
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFeature returns a feature of the given type, or nil if such feature is not registered.
|
||||
func (s *Instance) GetFeature(featureType interface{}) features.Feature {
|
||||
return getFeature(s.features, reflect.TypeOf(featureType))
|
||||
}
|
||||
|
||||
// Start starts the Xray instance, including all registered features. When Start returns error, the state of the instance is unknown.
|
||||
// A Xray instance can be started only once. Upon closing, the instance is not guaranteed to start again.
|
||||
//
|
||||
// xray:api:stable
|
||||
func (s *Instance) Start() error {
|
||||
s.access.Lock()
|
||||
defer s.access.Unlock()
|
||||
|
||||
s.running = true
|
||||
for _, f := range s.features {
|
||||
if err := f.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
newError("Xray ", Version(), " started").AtWarning().WriteToLog()
|
||||
|
||||
return nil
|
||||
}
|
90
core/xray_test.go
Normal file
90
core/xray_test.go
Normal file
|
@ -0,0 +1,90 @@
|
|||
package core_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/xtls/xray-core/v1/app/dispatcher"
|
||||
"github.com/xtls/xray-core/v1/app/proxyman"
|
||||
"github.com/xtls/xray-core/v1/common"
|
||||
"github.com/xtls/xray-core/v1/common/net"
|
||||
"github.com/xtls/xray-core/v1/common/protocol"
|
||||
"github.com/xtls/xray-core/v1/common/serial"
|
||||
"github.com/xtls/xray-core/v1/common/uuid"
|
||||
. "github.com/xtls/xray-core/v1/core"
|
||||
"github.com/xtls/xray-core/v1/features/dns"
|
||||
"github.com/xtls/xray-core/v1/features/dns/localdns"
|
||||
_ "github.com/xtls/xray-core/v1/main/distro/all"
|
||||
"github.com/xtls/xray-core/v1/proxy/dokodemo"
|
||||
"github.com/xtls/xray-core/v1/proxy/vmess"
|
||||
"github.com/xtls/xray-core/v1/proxy/vmess/outbound"
|
||||
"github.com/xtls/xray-core/v1/testing/servers/tcp"
|
||||
)
|
||||
|
||||
func TestXrayDependency(t *testing.T) {
|
||||
instance := new(Instance)
|
||||
|
||||
wait := make(chan bool, 1)
|
||||
instance.RequireFeatures(func(d dns.Client) {
|
||||
if d == nil {
|
||||
t.Error("expected dns client fulfilled, but actually nil")
|
||||
}
|
||||
wait <- true
|
||||
})
|
||||
instance.AddFeature(localdns.New())
|
||||
<-wait
|
||||
}
|
||||
|
||||
func TestXrayClose(t *testing.T) {
|
||||
port := tcp.PickPort()
|
||||
|
||||
userID := uuid.New()
|
||||
config := &Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&dispatcher.Config{}),
|
||||
serial.ToTypedMessage(&proxyman.InboundConfig{}),
|
||||
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
|
||||
},
|
||||
Inbound: []*InboundHandlerConfig{
|
||||
{
|
||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||
PortRange: net.SinglePortRange(port),
|
||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||
}),
|
||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(0),
|
||||
NetworkList: &net.NetworkList{
|
||||
Network: []net.Network{net.Network_TCP, net.Network_UDP},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
Outbound: []*OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&outbound.Config{
|
||||
Receiver: []*protocol.ServerEndpoint{
|
||||
{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(0),
|
||||
User: []*protocol.User{
|
||||
{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: userID.String(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cfgBytes, err := proto.Marshal(config)
|
||||
common.Must(err)
|
||||
|
||||
server, err := StartInstance("protobuf", cfgBytes)
|
||||
common.Must(err)
|
||||
server.Close()
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue