mirror of
https://github.com/XTLS/Xray-core.git
synced 2024-11-22 23:13:01 +00:00
Remove mtproto
This commit is contained in:
parent
038f849dd3
commit
c9f517108c
@ -1,67 +0,0 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/proxy/mtproto"
|
||||
)
|
||||
|
||||
type MTProtoAccount struct {
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
// Build implements Buildable
|
||||
func (a *MTProtoAccount) Build() (*mtproto.Account, error) {
|
||||
if len(a.Secret) != 32 {
|
||||
return nil, newError("MTProto secret must have 32 chars")
|
||||
}
|
||||
secret, err := hex.DecodeString(a.Secret)
|
||||
if err != nil {
|
||||
return nil, newError("failed to decode secret: ", a.Secret).Base(err)
|
||||
}
|
||||
return &mtproto.Account{
|
||||
Secret: secret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type MTProtoServerConfig struct {
|
||||
Users []json.RawMessage `json:"users"`
|
||||
}
|
||||
|
||||
func (c *MTProtoServerConfig) Build() (proto.Message, error) {
|
||||
config := &mtproto.ServerConfig{}
|
||||
|
||||
if len(c.Users) == 0 {
|
||||
return nil, newError("zero MTProto users configured.")
|
||||
}
|
||||
config.User = make([]*protocol.User, len(c.Users))
|
||||
for idx, rawData := range c.Users {
|
||||
user := new(protocol.User)
|
||||
if err := json.Unmarshal(rawData, user); err != nil {
|
||||
return nil, newError("invalid MTProto user").Base(err)
|
||||
}
|
||||
account := new(MTProtoAccount)
|
||||
if err := json.Unmarshal(rawData, account); err != nil {
|
||||
return nil, newError("invalid MTProto user").Base(err)
|
||||
}
|
||||
accountProto, err := account.Build()
|
||||
if err != nil {
|
||||
return nil, newError("failed to parse MTProto user").Base(err)
|
||||
}
|
||||
user.Account = serial.ToTypedMessage(accountProto)
|
||||
config.User[idx] = user
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
type MTProtoClientConfig struct{}
|
||||
|
||||
func (c *MTProtoClientConfig) Build() (proto.Message, error) {
|
||||
config := new(mtproto.ClientConfig)
|
||||
return config, nil
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package conf_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
. "github.com/xtls/xray-core/infra/conf"
|
||||
"github.com/xtls/xray-core/proxy/mtproto"
|
||||
)
|
||||
|
||||
func TestMTProtoServerConfig(t *testing.T) {
|
||||
creator := func() Buildable {
|
||||
return new(MTProtoServerConfig)
|
||||
}
|
||||
|
||||
runMultiTestCase(t, []TestCase{
|
||||
{
|
||||
Input: `{
|
||||
"users": [{
|
||||
"email": "love@example.com",
|
||||
"level": 1,
|
||||
"secret": "b0cbcef5a486d9636472ac27f8e11a9d"
|
||||
}]
|
||||
}`,
|
||||
Parser: loadJSON(creator),
|
||||
Output: &mtproto.ServerConfig{
|
||||
User: []*protocol.User{
|
||||
{
|
||||
Email: "love@example.com",
|
||||
Level: 1,
|
||||
Account: serial.ToTypedMessage(&mtproto.Account{
|
||||
Secret: []byte{176, 203, 206, 245, 164, 134, 217, 99, 100, 114, 172, 39, 248, 225, 26, 157},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
@ -24,7 +24,6 @@ var (
|
||||
"vless": func() interface{} { return new(VLessInboundConfig) },
|
||||
"vmess": func() interface{} { return new(VMessInboundConfig) },
|
||||
"trojan": func() interface{} { return new(TrojanServerConfig) },
|
||||
"mtproto": func() interface{} { return new(MTProtoServerConfig) },
|
||||
}, "protocol", "settings")
|
||||
|
||||
outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
||||
@ -37,7 +36,6 @@ var (
|
||||
"vless": func() interface{} { return new(VLessOutboundConfig) },
|
||||
"vmess": func() interface{} { return new(VMessOutboundConfig) },
|
||||
"trojan": func() interface{} { return new(TrojanClientConfig) },
|
||||
"mtproto": func() interface{} { return new(MTProtoClientConfig) },
|
||||
"dns": func() interface{} { return new(DNSOutboundConfig) },
|
||||
"wireguard": func() interface{} { return new(WireGuardConfig) },
|
||||
}, "protocol", "settings")
|
||||
|
@ -40,7 +40,6 @@ import (
|
||||
_ "github.com/xtls/xray-core/proxy/freedom"
|
||||
_ "github.com/xtls/xray-core/proxy/http"
|
||||
_ "github.com/xtls/xray-core/proxy/loopback"
|
||||
_ "github.com/xtls/xray-core/proxy/mtproto"
|
||||
_ "github.com/xtls/xray-core/proxy/shadowsocks"
|
||||
_ "github.com/xtls/xray-core/proxy/socks"
|
||||
_ "github.com/xtls/xray-core/proxy/trojan"
|
||||
|
@ -1,148 +0,0 @@
|
||||
package mtproto
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
)
|
||||
|
||||
const (
|
||||
HeaderSize = 64
|
||||
)
|
||||
|
||||
type SessionContext struct {
|
||||
ConnectionType [4]byte
|
||||
DataCenterID uint16
|
||||
}
|
||||
|
||||
func DefaultSessionContext() SessionContext {
|
||||
return SessionContext{
|
||||
ConnectionType: [4]byte{0xef, 0xef, 0xef, 0xef},
|
||||
DataCenterID: 0,
|
||||
}
|
||||
}
|
||||
|
||||
type contextKey int32
|
||||
|
||||
const (
|
||||
sessionContextKey contextKey = iota
|
||||
)
|
||||
|
||||
func ContextWithSessionContext(ctx context.Context, c SessionContext) context.Context {
|
||||
return context.WithValue(ctx, sessionContextKey, c)
|
||||
}
|
||||
|
||||
func SessionContextFromContext(ctx context.Context) SessionContext {
|
||||
if c := ctx.Value(sessionContextKey); c != nil {
|
||||
return c.(SessionContext)
|
||||
}
|
||||
return DefaultSessionContext()
|
||||
}
|
||||
|
||||
type Authentication struct {
|
||||
Header [HeaderSize]byte
|
||||
DecodingKey [32]byte
|
||||
EncodingKey [32]byte
|
||||
DecodingNonce [16]byte
|
||||
EncodingNonce [16]byte
|
||||
}
|
||||
|
||||
func (a *Authentication) DataCenterID() uint16 {
|
||||
x := ((int16(a.Header[61]) << 8) | int16(a.Header[60]))
|
||||
if x < 0 {
|
||||
x = -x
|
||||
}
|
||||
return uint16(x) - 1
|
||||
}
|
||||
|
||||
func (a *Authentication) ConnectionType() [4]byte {
|
||||
var x [4]byte
|
||||
copy(x[:], a.Header[56:60])
|
||||
return x
|
||||
}
|
||||
|
||||
func (a *Authentication) ApplySecret(b []byte) {
|
||||
a.DecodingKey = sha256.Sum256(append(a.DecodingKey[:], b...))
|
||||
a.EncodingKey = sha256.Sum256(append(a.EncodingKey[:], b...))
|
||||
}
|
||||
|
||||
func generateRandomBytes(random []byte, connType [4]byte) {
|
||||
for {
|
||||
common.Must2(rand.Read(random))
|
||||
|
||||
if random[0] == 0xef {
|
||||
continue
|
||||
}
|
||||
|
||||
val := (uint32(random[3]) << 24) | (uint32(random[2]) << 16) | (uint32(random[1]) << 8) | uint32(random[0])
|
||||
if val == 0x44414548 || val == 0x54534f50 || val == 0x20544547 || val == 0x4954504f || val == 0xeeeeeeee {
|
||||
continue
|
||||
}
|
||||
|
||||
if (uint32(random[7])<<24)|(uint32(random[6])<<16)|(uint32(random[5])<<8)|uint32(random[4]) == 0x00000000 {
|
||||
continue
|
||||
}
|
||||
|
||||
copy(random[56:60], connType[:])
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func NewAuthentication(sc SessionContext) *Authentication {
|
||||
auth := getAuthenticationObject()
|
||||
random := auth.Header[:]
|
||||
generateRandomBytes(random, sc.ConnectionType)
|
||||
copy(auth.EncodingKey[:], random[8:])
|
||||
copy(auth.EncodingNonce[:], random[8+32:])
|
||||
keyivInverse := Inverse(random[8 : 8+32+16])
|
||||
copy(auth.DecodingKey[:], keyivInverse)
|
||||
copy(auth.DecodingNonce[:], keyivInverse[32:])
|
||||
return auth
|
||||
}
|
||||
|
||||
func ReadAuthentication(reader io.Reader) (*Authentication, error) {
|
||||
auth := getAuthenticationObject()
|
||||
|
||||
if _, err := io.ReadFull(reader, auth.Header[:]); err != nil {
|
||||
putAuthenticationObject(auth)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
copy(auth.DecodingKey[:], auth.Header[8:])
|
||||
copy(auth.DecodingNonce[:], auth.Header[8+32:])
|
||||
keyivInverse := Inverse(auth.Header[8 : 8+32+16])
|
||||
copy(auth.EncodingKey[:], keyivInverse)
|
||||
copy(auth.EncodingNonce[:], keyivInverse[32:])
|
||||
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
// Inverse returns a new byte array. It is a sequence of bytes when the input is read from end to beginning.Inverse
|
||||
// Visible for testing only.
|
||||
func Inverse(b []byte) []byte {
|
||||
lenb := len(b)
|
||||
b2 := make([]byte, lenb)
|
||||
for i, v := range b {
|
||||
b2[lenb-i-1] = v
|
||||
}
|
||||
return b2
|
||||
}
|
||||
|
||||
var authPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(Authentication)
|
||||
},
|
||||
}
|
||||
|
||||
func getAuthenticationObject() *Authentication {
|
||||
return authPool.Get().(*Authentication)
|
||||
}
|
||||
|
||||
func putAuthenticationObject(auth *Authentication) {
|
||||
authPool.Put(auth)
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package mtproto_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/xtls/xray-core/common"
|
||||
. "github.com/xtls/xray-core/proxy/mtproto"
|
||||
)
|
||||
|
||||
func TestInverse(t *testing.T) {
|
||||
const size = 64
|
||||
b := make([]byte, 64)
|
||||
for b[0] == b[size-1] {
|
||||
common.Must2(rand.Read(b))
|
||||
}
|
||||
|
||||
bi := Inverse(b)
|
||||
if b[0] == bi[0] {
|
||||
t.Fatal("seems bytes are not inversed: ", b[0], "vs", bi[0])
|
||||
}
|
||||
|
||||
bii := Inverse(bi)
|
||||
if r := cmp.Diff(bii, b); r != "" {
|
||||
t.Fatal(r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthenticationReadWrite(t *testing.T) {
|
||||
a := NewAuthentication(DefaultSessionContext())
|
||||
b := bytes.NewReader(a.Header[:])
|
||||
a2, err := ReadAuthentication(b)
|
||||
common.Must(err)
|
||||
|
||||
if r := cmp.Diff(a.EncodingKey[:], a2.DecodingKey[:]); r != "" {
|
||||
t.Error("decoding key: ", r)
|
||||
}
|
||||
|
||||
if r := cmp.Diff(a.EncodingNonce[:], a2.DecodingNonce[:]); r != "" {
|
||||
t.Error("decoding nonce: ", r)
|
||||
}
|
||||
|
||||
if r := cmp.Diff(a.DecodingKey[:], a2.EncodingKey[:]); r != "" {
|
||||
t.Error("encoding key: ", r)
|
||||
}
|
||||
|
||||
if r := cmp.Diff(a.DecodingNonce[:], a2.EncodingNonce[:]); r != "" {
|
||||
t.Error("encoding nonce: ", r)
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
package mtproto
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/task"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
)
|
||||
|
||||
type Client struct{}
|
||||
|
||||
func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
|
||||
return &Client{}, nil
|
||||
}
|
||||
|
||||
func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
||||
outbound := session.OutboundFromContext(ctx)
|
||||
if outbound == nil || !outbound.Target.IsValid() {
|
||||
return newError("unknown destination.")
|
||||
}
|
||||
dest := outbound.Target
|
||||
if dest.Network != net.Network_TCP {
|
||||
return newError("not TCP traffic", dest)
|
||||
}
|
||||
|
||||
conn, err := dialer.Dial(ctx, dest)
|
||||
if err != nil {
|
||||
return newError("failed to dial to ", dest).Base(err).AtWarning()
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
sc := SessionContextFromContext(ctx)
|
||||
auth := NewAuthentication(sc)
|
||||
defer putAuthenticationObject(auth)
|
||||
|
||||
request := func() error {
|
||||
encryptor := crypto.NewAesCTRStream(auth.EncodingKey[:], auth.EncodingNonce[:])
|
||||
|
||||
var header [HeaderSize]byte
|
||||
encryptor.XORKeyStream(header[:], auth.Header[:])
|
||||
copy(header[:56], auth.Header[:])
|
||||
|
||||
if _, err := conn.Write(header[:]); err != nil {
|
||||
return newError("failed to write auth header").Base(err)
|
||||
}
|
||||
|
||||
connWriter := buf.NewWriter(crypto.NewCryptionWriter(encryptor, conn))
|
||||
return buf.Copy(link.Reader, connWriter)
|
||||
}
|
||||
|
||||
response := func() error {
|
||||
decryptor := crypto.NewAesCTRStream(auth.DecodingKey[:], auth.DecodingNonce[:])
|
||||
|
||||
connReader := buf.NewReader(crypto.NewCryptionReader(decryptor, conn))
|
||||
return buf.Copy(connReader, link.Writer)
|
||||
}
|
||||
|
||||
responseDoneAndCloseWriter := task.OnSuccess(response, task.Close(link.Writer))
|
||||
if err := task.Run(ctx, request, responseDoneAndCloseWriter); err != nil {
|
||||
return newError("connection ends").Base(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
return NewClient(ctx, config.(*ClientConfig))
|
||||
}))
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package mtproto
|
||||
|
||||
import (
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
)
|
||||
|
||||
func (a *Account) Equals(another protocol.Account) bool {
|
||||
aa, ok := another.(*Account)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(a.Secret) != len(aa.Secret) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, v := range a.Secret {
|
||||
if v != aa.Secret[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
@ -1,272 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: proxy/mtproto/config.proto
|
||||
|
||||
package mtproto
|
||||
|
||||
import (
|
||||
protocol "github.com/xtls/xray-core/common/protocol"
|
||||
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)
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Secret []byte `protobuf:"bytes,1,opt,name=secret,proto3" json:"secret,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Account) Reset() {
|
||||
*x = Account{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proxy_mtproto_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Account) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Account) ProtoMessage() {}
|
||||
|
||||
func (x *Account) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proxy_mtproto_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 Account.ProtoReflect.Descriptor instead.
|
||||
func (*Account) Descriptor() ([]byte, []int) {
|
||||
return file_proxy_mtproto_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Account) GetSecret() []byte {
|
||||
if x != nil {
|
||||
return x.Secret
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// User is a list of users that allowed to connect to this inbound.
|
||||
// Although this is a repeated field, only the first user is effective for
|
||||
// now.
|
||||
User []*protocol.User `protobuf:"bytes,1,rep,name=user,proto3" json:"user,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ServerConfig) Reset() {
|
||||
*x = ServerConfig{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proxy_mtproto_config_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ServerConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ServerConfig) ProtoMessage() {}
|
||||
|
||||
func (x *ServerConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proxy_mtproto_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 ServerConfig.ProtoReflect.Descriptor instead.
|
||||
func (*ServerConfig) Descriptor() ([]byte, []int) {
|
||||
return file_proxy_mtproto_config_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *ServerConfig) GetUser() []*protocol.User {
|
||||
if x != nil {
|
||||
return x.User
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ClientConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *ClientConfig) Reset() {
|
||||
*x = ClientConfig{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proxy_mtproto_config_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ClientConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ClientConfig) ProtoMessage() {}
|
||||
|
||||
func (x *ClientConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proxy_mtproto_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 ClientConfig.ProtoReflect.Descriptor instead.
|
||||
func (*ClientConfig) Descriptor() ([]byte, []int) {
|
||||
return file_proxy_mtproto_config_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
var File_proxy_mtproto_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_proxy_mtproto_config_proto_rawDesc = []byte{
|
||||
0x0a, 0x1a, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x6d, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
|
||||
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x78, 0x72,
|
||||
0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x6d, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x1a, 0x1a, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,
|
||||
0x6c, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x21, 0x0a, 0x07,
|
||||
0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65,
|
||||
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22,
|
||||
0x3e, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
|
||||
0x2e, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22,
|
||||
0x0e, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42,
|
||||
0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78,
|
||||
0x79, 0x2e, 0x6d, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x27, 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, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x6d, 0x74, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78,
|
||||
0x79, 0x2e, 0x4d, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_proxy_mtproto_config_proto_rawDescOnce sync.Once
|
||||
file_proxy_mtproto_config_proto_rawDescData = file_proxy_mtproto_config_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_proxy_mtproto_config_proto_rawDescGZIP() []byte {
|
||||
file_proxy_mtproto_config_proto_rawDescOnce.Do(func() {
|
||||
file_proxy_mtproto_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_proxy_mtproto_config_proto_rawDescData)
|
||||
})
|
||||
return file_proxy_mtproto_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_proxy_mtproto_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_proxy_mtproto_config_proto_goTypes = []interface{}{
|
||||
(*Account)(nil), // 0: xray.proxy.mtproto.Account
|
||||
(*ServerConfig)(nil), // 1: xray.proxy.mtproto.ServerConfig
|
||||
(*ClientConfig)(nil), // 2: xray.proxy.mtproto.ClientConfig
|
||||
(*protocol.User)(nil), // 3: xray.common.protocol.User
|
||||
}
|
||||
var file_proxy_mtproto_config_proto_depIdxs = []int32{
|
||||
3, // 0: xray.proxy.mtproto.ServerConfig.user:type_name -> xray.common.protocol.User
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_proxy_mtproto_config_proto_init() }
|
||||
func file_proxy_mtproto_config_proto_init() {
|
||||
if File_proxy_mtproto_config_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_proxy_mtproto_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Account); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proxy_mtproto_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ServerConfig); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proxy_mtproto_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ClientConfig); 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_proxy_mtproto_config_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_proxy_mtproto_config_proto_goTypes,
|
||||
DependencyIndexes: file_proxy_mtproto_config_proto_depIdxs,
|
||||
MessageInfos: file_proxy_mtproto_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_proxy_mtproto_config_proto = out.File
|
||||
file_proxy_mtproto_config_proto_rawDesc = nil
|
||||
file_proxy_mtproto_config_proto_goTypes = nil
|
||||
file_proxy_mtproto_config_proto_depIdxs = nil
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.proxy.mtproto;
|
||||
option csharp_namespace = "Xray.Proxy.Mtproto";
|
||||
option go_package = "github.com/xtls/xray-core/proxy/mtproto";
|
||||
option java_package = "com.xray.proxy.mtproto";
|
||||
option java_multiple_files = true;
|
||||
|
||||
import "common/protocol/user.proto";
|
||||
|
||||
message Account {
|
||||
bytes secret = 1;
|
||||
}
|
||||
|
||||
message ServerConfig {
|
||||
// User is a list of users that allowed to connect to this inbound.
|
||||
// Although this is a repeated field, only the first user is effective for
|
||||
// now.
|
||||
repeated xray.common.protocol.User user = 1;
|
||||
}
|
||||
|
||||
message ClientConfig {}
|
@ -1,9 +0,0 @@
|
||||
package mtproto
|
||||
|
||||
import "github.com/xtls/xray-core/common/errors"
|
||||
|
||||
type errPathObjHolder struct{}
|
||||
|
||||
func newError(values ...interface{}) *errors.Error {
|
||||
return errors.New(values...).WithPathObj(errPathObjHolder{})
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
package mtproto
|
||||
|
||||
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
@ -1,160 +0,0 @@
|
||||
package mtproto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/signal"
|
||||
"github.com/xtls/xray-core/common/task"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/policy"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
)
|
||||
|
||||
var dcList = []net.Address{
|
||||
net.ParseAddress("149.154.175.50"),
|
||||
net.ParseAddress("149.154.167.51"),
|
||||
net.ParseAddress("149.154.175.100"),
|
||||
net.ParseAddress("149.154.167.91"),
|
||||
net.ParseAddress("149.154.171.5"),
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
user *protocol.User
|
||||
account *Account
|
||||
policy policy.Manager
|
||||
}
|
||||
|
||||
func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
|
||||
if len(config.User) == 0 {
|
||||
return nil, newError("no user configured.")
|
||||
}
|
||||
|
||||
user := config.User[0]
|
||||
rawAccount, err := config.User[0].GetTypedAccount()
|
||||
if err != nil {
|
||||
return nil, newError("invalid account").Base(err)
|
||||
}
|
||||
account, ok := rawAccount.(*Account)
|
||||
if !ok {
|
||||
return nil, newError("not a MTProto account")
|
||||
}
|
||||
|
||||
v := core.MustFromContext(ctx)
|
||||
|
||||
return &Server{
|
||||
user: user,
|
||||
account: account,
|
||||
policy: v.GetFeature(policy.ManagerType()).(policy.Manager),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) Network() []net.Network {
|
||||
return []net.Network{net.Network_TCP}
|
||||
}
|
||||
|
||||
var (
|
||||
ctype1 = []byte{0xef, 0xef, 0xef, 0xef}
|
||||
ctype2 = []byte{0xee, 0xee, 0xee, 0xee}
|
||||
)
|
||||
|
||||
func isValidConnectionType(c [4]byte) bool {
|
||||
if bytes.Equal(c[:], ctype1) {
|
||||
return true
|
||||
}
|
||||
if bytes.Equal(c[:], ctype2) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
||||
sPolicy := s.policy.ForLevel(s.user.Level)
|
||||
|
||||
if err := conn.SetDeadline(time.Now().Add(sPolicy.Timeouts.Handshake)); err != nil {
|
||||
newError("failed to set deadline").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
auth, err := ReadAuthentication(conn)
|
||||
if err != nil {
|
||||
return newError("failed to read authentication header").Base(err)
|
||||
}
|
||||
defer putAuthenticationObject(auth)
|
||||
|
||||
if err := conn.SetDeadline(time.Time{}); err != nil {
|
||||
newError("failed to clear deadline").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
|
||||
auth.ApplySecret(s.account.Secret)
|
||||
|
||||
decryptor := crypto.NewAesCTRStream(auth.DecodingKey[:], auth.DecodingNonce[:])
|
||||
decryptor.XORKeyStream(auth.Header[:], auth.Header[:])
|
||||
|
||||
ct := auth.ConnectionType()
|
||||
if !isValidConnectionType(ct) {
|
||||
return newError("invalid connection type: ", ct)
|
||||
}
|
||||
|
||||
dcID := auth.DataCenterID()
|
||||
if dcID >= uint16(len(dcList)) {
|
||||
return newError("invalid datacenter id: ", dcID)
|
||||
}
|
||||
|
||||
dest := net.Destination{
|
||||
Network: net.Network_TCP,
|
||||
Address: dcList[dcID],
|
||||
Port: net.Port(443),
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
timer := signal.CancelAfterInactivity(ctx, cancel, sPolicy.Timeouts.ConnectionIdle)
|
||||
ctx = policy.ContextWithBufferPolicy(ctx, sPolicy.Buffer)
|
||||
|
||||
sc := SessionContext{
|
||||
ConnectionType: ct,
|
||||
DataCenterID: dcID,
|
||||
}
|
||||
ctx = ContextWithSessionContext(ctx, sc)
|
||||
|
||||
link, err := dispatcher.Dispatch(ctx, dest)
|
||||
if err != nil {
|
||||
return newError("failed to dispatch request to: ", dest).Base(err)
|
||||
}
|
||||
|
||||
request := func() error {
|
||||
defer timer.SetTimeout(sPolicy.Timeouts.DownlinkOnly)
|
||||
|
||||
reader := buf.NewReader(crypto.NewCryptionReader(decryptor, conn))
|
||||
return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer))
|
||||
}
|
||||
|
||||
response := func() error {
|
||||
defer timer.SetTimeout(sPolicy.Timeouts.UplinkOnly)
|
||||
|
||||
encryptor := crypto.NewAesCTRStream(auth.EncodingKey[:], auth.EncodingNonce[:])
|
||||
writer := buf.NewWriter(crypto.NewCryptionWriter(encryptor, conn))
|
||||
return buf.Copy(link.Reader, writer, buf.UpdateActivity(timer))
|
||||
}
|
||||
|
||||
responseDoneAndCloseWriter := task.OnSuccess(response, task.Close(link.Writer))
|
||||
if err := task.Run(ctx, request, responseDoneAndCloseWriter); err != nil {
|
||||
common.Interrupt(link.Reader)
|
||||
common.Interrupt(link.Writer)
|
||||
return newError("connection ends").Base(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
return NewServer(ctx, config.(*ServerConfig))
|
||||
}))
|
||||
}
|
Loading…
Reference in New Issue
Block a user