Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a0bf239fc1 | |||
| dee64ef240 | |||
| 0a1b5bfb51 |
@@ -411,8 +411,10 @@ type MultiplexingConfig struct {
|
||||
XudpConcurrency int32 `protobuf:"varint,3,opt,name=xudpConcurrency,proto3" json:"xudpConcurrency,omitempty"`
|
||||
// "reject" (default), "allow" or "skip".
|
||||
XudpProxyUDP443 string `protobuf:"bytes,4,opt,name=xudpProxyUDP443,proto3" json:"xudpProxyUDP443,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
// MaxReuseTimes for an connection
|
||||
MaxReuseTimes int32 `protobuf:"varint,5,opt,name=maxReuseTimes,proto3" json:"maxReuseTimes,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *MultiplexingConfig) Reset() {
|
||||
@@ -473,6 +475,13 @@ func (x *MultiplexingConfig) GetXudpProxyUDP443() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *MultiplexingConfig) GetMaxReuseTimes() int32 {
|
||||
if x != nil {
|
||||
return x.MaxReuseTimes
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_app_proxyman_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_app_proxyman_config_proto_rawDesc = "" +
|
||||
@@ -503,12 +512,13 @@ const file_app_proxyman_config_proto_rawDesc = "" +
|
||||
"\x0eproxy_settings\x18\x03 \x01(\v2$.xray.transport.internet.ProxyConfigR\rproxySettings\x12T\n" +
|
||||
"\x12multiplex_settings\x18\x04 \x01(\v2%.xray.app.proxyman.MultiplexingConfigR\x11multiplexSettings\x12\x19\n" +
|
||||
"\bvia_cidr\x18\x05 \x01(\tR\aviaCidr\x12P\n" +
|
||||
"\x0ftarget_strategy\x18\x06 \x01(\x0e2'.xray.transport.internet.DomainStrategyR\x0etargetStrategy\"\xa4\x01\n" +
|
||||
"\x0ftarget_strategy\x18\x06 \x01(\x0e2'.xray.transport.internet.DomainStrategyR\x0etargetStrategy\"\xca\x01\n" +
|
||||
"\x12MultiplexingConfig\x12\x18\n" +
|
||||
"\aenabled\x18\x01 \x01(\bR\aenabled\x12 \n" +
|
||||
"\vconcurrency\x18\x02 \x01(\x05R\vconcurrency\x12(\n" +
|
||||
"\x0fxudpConcurrency\x18\x03 \x01(\x05R\x0fxudpConcurrency\x12(\n" +
|
||||
"\x0fxudpProxyUDP443\x18\x04 \x01(\tR\x0fxudpProxyUDP443BU\n" +
|
||||
"\x0fxudpProxyUDP443\x18\x04 \x01(\tR\x0fxudpProxyUDP443\x12$\n" +
|
||||
"\rmaxReuseTimes\x18\x05 \x01(\x05R\rmaxReuseTimesBU\n" +
|
||||
"\x15com.xray.app.proxymanP\x01Z&github.com/xtls/xray-core/app/proxyman\xaa\x02\x11Xray.App.Proxymanb\x06proto3"
|
||||
|
||||
var (
|
||||
|
||||
@@ -68,4 +68,6 @@ message MultiplexingConfig {
|
||||
int32 xudpConcurrency = 3;
|
||||
// "reject" (default), "allow" or "skip".
|
||||
string xudpProxyUDP443 = 4;
|
||||
// MaxReuseTimes for an connection
|
||||
int32 maxReuseTimes = 5;
|
||||
}
|
||||
|
||||
@@ -121,6 +121,12 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou
|
||||
|
||||
if h.senderSettings != nil && h.senderSettings.MultiplexSettings != nil {
|
||||
if config := h.senderSettings.MultiplexSettings; config.Enabled {
|
||||
// MaxReuseTimes use 60000 as default, and it also means the upper limit of MaxReuseTimes
|
||||
// In mux cool spec, connection ID is 2 bytes, so physical limit is 65535, bu we reserve some IDs for future use
|
||||
MaxReuseTimes := uint32(60000)
|
||||
if config.MaxReuseTimes != 0 && config.MaxReuseTimes < 60000 {
|
||||
MaxReuseTimes = uint32(config.MaxReuseTimes)
|
||||
}
|
||||
if config.Concurrency < 0 {
|
||||
h.mux = &mux.ClientManager{Enabled: false}
|
||||
}
|
||||
@@ -136,7 +142,7 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou
|
||||
Dialer: h,
|
||||
Strategy: mux.ClientStrategy{
|
||||
MaxConcurrency: uint32(config.Concurrency),
|
||||
MaxConnection: 128,
|
||||
MaxReuseTimes: MaxReuseTimes,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -157,7 +163,7 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou
|
||||
Dialer: h,
|
||||
Strategy: mux.ClientStrategy{
|
||||
MaxConcurrency: uint32(config.XudpConcurrency),
|
||||
MaxConnection: 128,
|
||||
MaxReuseTimes: 128,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -56,6 +56,10 @@ type readError struct {
|
||||
error
|
||||
}
|
||||
|
||||
func NewReadError(err error) error {
|
||||
return readError{err}
|
||||
}
|
||||
|
||||
func (e readError) Error() string {
|
||||
return e.error.Error()
|
||||
}
|
||||
@@ -74,6 +78,10 @@ type writeError struct {
|
||||
error
|
||||
}
|
||||
|
||||
func NewWriteError(err error) error {
|
||||
return writeError{err}
|
||||
}
|
||||
|
||||
func (e writeError) Error() string {
|
||||
return e.error.Error()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package mux_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/mux"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
"github.com/xtls/xray-core/transport/pipe"
|
||||
)
|
||||
|
||||
func BenchmarkMuxThroughput(b *testing.B) {
|
||||
serverCtx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{}})
|
||||
muxServerUplink, muxServerDownlink := newLinkPair()
|
||||
dispatcher := TestDispatcher{
|
||||
OnDispatch: func(ctx context.Context, dest net.Destination) (*transport.Link, error) {
|
||||
inputReader, inputWriter := pipe.New(pipe.WithSizeLimit(512 * 1024))
|
||||
outputReader, outputWriter := pipe.New(pipe.WithSizeLimit(512 * 1024))
|
||||
go func() {
|
||||
defer outputWriter.Close()
|
||||
for {
|
||||
mb, err := inputReader.ReadMultiBuffer()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
buf.ReleaseMulti(mb)
|
||||
}
|
||||
}()
|
||||
return &transport.Link{
|
||||
Reader: outputReader,
|
||||
Writer: inputWriter,
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
_, err := mux.NewServerWorker(serverCtx, &dispatcher, muxServerUplink)
|
||||
common.Must(err)
|
||||
client, err := mux.NewClientWorker(*muxServerDownlink, mux.ClientStrategy{})
|
||||
common.Must(err)
|
||||
clientCtx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
||||
Target: net.TCPDestination(net.DomainAddress("www.example.com"), 80),
|
||||
}})
|
||||
muxClientUplink, muxClientDownlink := newLinkPair()
|
||||
go func() {
|
||||
for {
|
||||
mb, err := muxClientDownlink.Reader.ReadMultiBuffer()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
buf.ReleaseMulti(mb)
|
||||
}
|
||||
}()
|
||||
ok := client.Dispatch(clientCtx, muxClientUplink)
|
||||
if !ok {
|
||||
b.Fatal("failed to dispatch")
|
||||
}
|
||||
data := buf.FromBytes(make([]byte, 8192))
|
||||
b.SetBytes(int64(8192))
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := muxClientUplink.Writer.WriteMultiBuffer(buf.MultiBuffer{data})
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
-6
@@ -170,7 +170,7 @@ func (f *DialingWorkerFactory) Create() (*ClientWorker, error) {
|
||||
|
||||
type ClientStrategy struct {
|
||||
MaxConcurrency uint32
|
||||
MaxConnection uint32
|
||||
MaxReuseTimes uint32
|
||||
}
|
||||
|
||||
type ClientWorker struct {
|
||||
@@ -179,6 +179,7 @@ type ClientWorker struct {
|
||||
done *done.Instance
|
||||
timer *time.Ticker
|
||||
strategy ClientStrategy
|
||||
timeCretaed time.Time
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -194,6 +195,7 @@ func NewClientWorker(stream transport.Link, s ClientStrategy) (*ClientWorker, er
|
||||
done: done.New(),
|
||||
timer: time.NewTicker(time.Second * 16),
|
||||
strategy: s,
|
||||
timeCretaed: time.Now(),
|
||||
}
|
||||
|
||||
go c.fetchOutput()
|
||||
@@ -288,7 +290,7 @@ func fetchInput(ctx context.Context, s *Session, output buf.Writer) {
|
||||
|
||||
func (m *ClientWorker) IsClosing() bool {
|
||||
sm := m.sessionManager
|
||||
if m.strategy.MaxConnection > 0 && sm.Count() >= int(m.strategy.MaxConnection) {
|
||||
if m.strategy.MaxReuseTimes > 0 && sm.Count() >= int(m.strategy.MaxReuseTimes) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -318,6 +320,7 @@ func (m *ClientWorker) Dispatch(ctx context.Context, link *transport.Link) bool
|
||||
if s == nil {
|
||||
return false
|
||||
}
|
||||
errors.LogInfo(ctx, "Allocated mux.cool sub connection ID: ", s.ID, "/", m.strategy.MaxReuseTimes, " living: ", m.ActiveConnections(), "/", m.strategy.MaxConcurrency, " age: ", time.Since(m.timeCretaed).Truncate(time.Second))
|
||||
s.input = link.Reader
|
||||
s.output = link.Writer
|
||||
go fetchInput(ctx, s, m.link.Writer)
|
||||
@@ -332,14 +335,14 @@ func (m *ClientWorker) Dispatch(ctx context.Context, link *transport.Link) bool
|
||||
|
||||
func (m *ClientWorker) handleStatueKeepAlive(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||
if meta.Option.Has(OptionData) {
|
||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||
return CopyChunk(reader, buf.Discard)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ClientWorker) handleStatusNew(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||
if meta.Option.Has(OptionData) {
|
||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||
return CopyChunk(reader, buf.Discard)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -355,7 +358,19 @@ func (m *ClientWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||
closingWriter := NewResponseWriter(meta.SessionID, m.link.Writer, protocol.TransferTypeStream)
|
||||
closingWriter.Close()
|
||||
|
||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||
return CopyChunk(reader, buf.Discard)
|
||||
}
|
||||
|
||||
if s.transferType == protocol.TransferTypeStream {
|
||||
err := CopyChunk(reader, s.output)
|
||||
if err != nil && buf.IsWriteError(err) {
|
||||
errors.LogInfoInner(context.Background(), err, "failed to write to downstream. closing session ", s.ID)
|
||||
s.Close(false)
|
||||
// down stream can have a write err but don't return the err to terminate the whole mux connection
|
||||
// because it's still available for other sessions
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
rr := s.NewReader(reader, &meta.Target)
|
||||
@@ -374,7 +389,7 @@ func (m *ClientWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.Buffered
|
||||
s.Close(false)
|
||||
}
|
||||
if meta.Option.Has(OptionData) {
|
||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||
return CopyChunk(reader, buf.Discard)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ func TestClientWorkerClose(t *testing.T) {
|
||||
Writer: w1,
|
||||
}, mux.ClientStrategy{
|
||||
MaxConcurrency: 4,
|
||||
MaxConnection: 4,
|
||||
MaxReuseTimes: 4,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
@@ -68,7 +68,7 @@ func TestClientWorkerClose(t *testing.T) {
|
||||
Writer: w2,
|
||||
}, mux.ClientStrategy{
|
||||
MaxConcurrency: 4,
|
||||
MaxConnection: 4,
|
||||
MaxReuseTimes: 4,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
|
||||
@@ -57,3 +57,32 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||
func NewStreamReader(reader *buf.BufferedReader) buf.Reader {
|
||||
return crypto.NewChunkStreamReaderWithChunkCount(crypto.PlainChunkSizeParser{}, reader, 1)
|
||||
}
|
||||
|
||||
func CopyChunk(reader *buf.BufferedReader, writer buf.Writer) error {
|
||||
size, err := serial.ReadUint16(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var writeErr error
|
||||
for size > 0 {
|
||||
mb, readErr := reader.ReadAtMost(int32(size))
|
||||
if !mb.IsEmpty() {
|
||||
size -= uint16(mb.Len())
|
||||
if writeErr == nil {
|
||||
if err := writer.WriteMultiBuffer(mb); err != nil {
|
||||
writeErr = err
|
||||
}
|
||||
} else {
|
||||
buf.ReleaseMulti(mb)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if readErr != nil {
|
||||
return buf.NewReadError(readErr)
|
||||
}
|
||||
}
|
||||
if writeErr != nil {
|
||||
return buf.NewWriteError(writeErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+25
-4
@@ -157,7 +157,7 @@ func (w *ServerWorker) Close() error {
|
||||
|
||||
func (w *ServerWorker) handleStatusKeepAlive(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||
if meta.Option.Has(OptionData) {
|
||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||
return CopyChunk(reader, buf.Discard)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -264,7 +264,7 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
|
||||
link, err := w.dispatcher.Dispatch(ctx, meta.Target)
|
||||
if err != nil {
|
||||
if meta.Option.Has(OptionData) {
|
||||
buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||
CopyChunk(reader, buf.Discard)
|
||||
}
|
||||
return errors.New("failed to dispatch request.").Base(err)
|
||||
}
|
||||
@@ -287,6 +287,15 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
|
||||
return nil
|
||||
}
|
||||
|
||||
if s.transferType == protocol.TransferTypeStream {
|
||||
err = CopyChunk(reader, s.output)
|
||||
if err != nil && buf.IsWriteError(err) {
|
||||
s.Close(false)
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
rr := s.NewReader(reader, &meta.Target)
|
||||
err = buf.Copy(rr, s.output)
|
||||
|
||||
@@ -308,7 +317,19 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||
closingWriter := NewResponseWriter(meta.SessionID, w.link.Writer, protocol.TransferTypeStream)
|
||||
closingWriter.Close()
|
||||
|
||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||
return CopyChunk(reader, buf.Discard)
|
||||
}
|
||||
|
||||
if s.transferType == protocol.TransferTypeStream {
|
||||
err := CopyChunk(reader, s.output)
|
||||
if err != nil && buf.IsWriteError(err) {
|
||||
errors.LogInfoInner(context.Background(), err, "failed to write to downstream writer. closing session ", s.ID)
|
||||
s.Close(false)
|
||||
// down stream can have a write err but don't return the err to terminate the whole mux connection
|
||||
// because it's still available for other sessions
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
rr := s.NewReader(reader, &meta.Target)
|
||||
@@ -328,7 +349,7 @@ func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.Buffered
|
||||
s.Close(false)
|
||||
}
|
||||
if meta.Option.Has(OptionData) {
|
||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||
return CopyChunk(reader, buf.Discard)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
func newLinkPair() (*transport.Link, *transport.Link) {
|
||||
opt := pipe.WithoutSizeLimit()
|
||||
opt := pipe.WithSizeLimit(512 * 1024)
|
||||
uplinkReader, uplinkWriter := pipe.New(opt)
|
||||
downlinkReader, downlinkWriter := pipe.New(opt)
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ func (m *SessionManager) Allocate(Strategy *ClientStrategy) *Session {
|
||||
defer m.Unlock()
|
||||
|
||||
MaxConcurrency := int(Strategy.MaxConcurrency)
|
||||
MaxConnection := uint16(Strategy.MaxConnection)
|
||||
MaxConnection := uint16(Strategy.MaxReuseTimes)
|
||||
|
||||
if m.closed || (MaxConcurrency > 0 && len(m.sessions) >= MaxConcurrency) || (MaxConnection > 0 && m.count >= MaxConnection) {
|
||||
return nil
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// AccessField can used to access unexported field of a struct
|
||||
// valueType must be the exact type of the field or it will panic
|
||||
func AccessField[valueType any](obj any, fieldName string) *valueType {
|
||||
field := reflect.ValueOf(obj).Elem().FieldByName(fieldName)
|
||||
if field.Type() != reflect.TypeOf(*new(valueType)) {
|
||||
panic("field type: " + field.Type().String() + ", valueType: " + reflect.TypeOf(*new(valueType)).String())
|
||||
}
|
||||
v := (*valueType)(unsafe.Pointer(field.UnsafeAddr()))
|
||||
return v
|
||||
}
|
||||
+1
-1
@@ -19,7 +19,7 @@ import (
|
||||
var (
|
||||
Version_x byte = 26
|
||||
Version_y byte = 2
|
||||
Version_z byte = 4
|
||||
Version_z byte = 2
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
@@ -11,7 +10,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
@@ -452,31 +450,6 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
type XDriveConfig struct {
|
||||
RemoteFolder string `json:"remoteFolder"`
|
||||
Service string `json:"service"`
|
||||
Secrets []string `json:"secrets"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
func (c *XDriveConfig) Build() (proto.Message, error) {
|
||||
switch c.Service {
|
||||
case "local":
|
||||
case "Google Drive":
|
||||
if len(c.Secrets) != 3 {
|
||||
return nil, errors.New("Google Drive needs 3 secrets in order of ClientID, ClientSecret, RefreshToken")
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("unsupported service")
|
||||
}
|
||||
config := &xdrive.Config{
|
||||
RemoteFolder: c.RemoteFolder,
|
||||
Service: c.Service,
|
||||
Secrets: c.Secrets,
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
const (
|
||||
Byte = 1
|
||||
Kilobyte = 1024 * Byte
|
||||
@@ -774,12 +747,7 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||
config.MasterKeyLog = c.MasterKeyLog
|
||||
|
||||
if c.AllowInsecure {
|
||||
if time.Now().After(time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC)) {
|
||||
return nil, errors.PrintRemovedFeatureError(`"allowInsecure"`, `"pinnedPeerCertSha256"`)
|
||||
} else {
|
||||
errors.LogWarning(context.Background(), `"allowInsecure" will be removed automatically after 2026-06-01, please use "pinnedPeerCertSha256"(pcs) and "verifyPeerCertByName"(vcn) instead, PLEASE CONTACT YOUR SERVICE PROVIDER (AIRPORT)`)
|
||||
config.AllowInsecure = true
|
||||
}
|
||||
return nil, errors.PrintRemovedFeatureError(`"allowInsecure"`, `"pinnedPeerCertSha256"`)
|
||||
}
|
||||
if c.PinnedPeerCertSha256 != "" {
|
||||
for v := range strings.SplitSeq(c.PinnedPeerCertSha256, ",") {
|
||||
|
||||
@@ -101,6 +101,7 @@ func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) {
|
||||
type MuxConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Concurrency int16 `json:"concurrency"`
|
||||
MaxReuseTimes int32 `json:"maxReuseTimes"`
|
||||
XudpConcurrency int16 `json:"xudpConcurrency"`
|
||||
XudpProxyUDP443 string `json:"xudpProxyUDP443"`
|
||||
}
|
||||
@@ -117,6 +118,7 @@ func (m *MuxConfig) Build() (*proxyman.MultiplexingConfig, error) {
|
||||
return &proxyman.MultiplexingConfig{
|
||||
Enabled: m.Enabled,
|
||||
Concurrency: int32(m.Concurrency),
|
||||
MaxReuseTimes: m.MaxReuseTimes,
|
||||
XudpConcurrency: int32(m.XudpConcurrency),
|
||||
XudpProxyUDP443: m.XudpProxyUDP443,
|
||||
}, nil
|
||||
|
||||
@@ -6,13 +6,8 @@ import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"text/tabwriter"
|
||||
|
||||
utls "github.com/refraction-networking/utls"
|
||||
|
||||
"github.com/xtls/xray-core/common/utils"
|
||||
"github.com/xtls/xray-core/main/commands/base"
|
||||
. "github.com/xtls/xray-core/transport/internet/tls"
|
||||
)
|
||||
@@ -51,7 +46,6 @@ func executePing(cmd *base.Command, args []string) {
|
||||
} else {
|
||||
TargetPort, _ = strconv.Atoi(port)
|
||||
}
|
||||
tabWriter := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
|
||||
var ip net.IP
|
||||
if len(*pingIPStr) > 0 {
|
||||
@@ -76,7 +70,7 @@ func executePing(cmd *base.Command, args []string) {
|
||||
if err != nil {
|
||||
base.Fatalf("Failed to dial tcp: %s", err)
|
||||
}
|
||||
tlsConn := GeneraticUClient(tcpConn, &gotls.Config{
|
||||
tlsConn := gotls.Client(tcpConn, &gotls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
NextProtos: []string{"h2", "http/1.1"},
|
||||
MaxVersion: gotls.VersionTLS13,
|
||||
@@ -87,9 +81,8 @@ func executePing(cmd *base.Command, args []string) {
|
||||
fmt.Println("Handshake failure: ", err)
|
||||
} else {
|
||||
fmt.Println("Handshake succeeded")
|
||||
printTLSConnDetail(tabWriter, tlsConn)
|
||||
printCertificates(tabWriter, tlsConn.ConnectionState().PeerCertificates)
|
||||
tabWriter.Flush()
|
||||
printTLSConnDetail(tlsConn)
|
||||
printCertificates(tlsConn.ConnectionState().PeerCertificates)
|
||||
}
|
||||
tlsConn.Close()
|
||||
}
|
||||
@@ -101,7 +94,7 @@ func executePing(cmd *base.Command, args []string) {
|
||||
if err != nil {
|
||||
base.Fatalf("Failed to dial tcp: %s", err)
|
||||
}
|
||||
tlsConn := GeneraticUClient(tcpConn, &gotls.Config{
|
||||
tlsConn := gotls.Client(tcpConn, &gotls.Config{
|
||||
ServerName: domain,
|
||||
NextProtos: []string{"h2", "http/1.1"},
|
||||
MaxVersion: gotls.VersionTLS13,
|
||||
@@ -112,9 +105,8 @@ func executePing(cmd *base.Command, args []string) {
|
||||
fmt.Println("Handshake failure: ", err)
|
||||
} else {
|
||||
fmt.Println("Handshake succeeded")
|
||||
printTLSConnDetail(tabWriter, tlsConn)
|
||||
printCertificates(tabWriter, tlsConn.ConnectionState().PeerCertificates)
|
||||
tabWriter.Flush()
|
||||
printTLSConnDetail(tlsConn)
|
||||
printCertificates(tlsConn.ConnectionState().PeerCertificates)
|
||||
}
|
||||
tlsConn.Close()
|
||||
}
|
||||
@@ -123,45 +115,38 @@ func executePing(cmd *base.Command, args []string) {
|
||||
fmt.Println("TLS ping finished")
|
||||
}
|
||||
|
||||
func printCertificates(tabWriter *tabwriter.Writer, certs []*x509.Certificate) {
|
||||
func printCertificates(certs []*x509.Certificate) {
|
||||
var leaf *x509.Certificate
|
||||
var CAs []*x509.Certificate
|
||||
var length int
|
||||
for _, cert := range certs {
|
||||
length += len(cert.Raw)
|
||||
if len(cert.DNSNames) != 0 {
|
||||
leaf = cert
|
||||
} else {
|
||||
CAs = append(CAs, cert)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(tabWriter, "Certificate chain's total length: \t %d (certs count: %s)\n", length, strconv.Itoa(len(certs)))
|
||||
fmt.Println("Certificate chain's total length: ", length, "(certs count: "+strconv.Itoa(len(certs))+")")
|
||||
if leaf != nil {
|
||||
fmt.Fprintf(tabWriter, "Cert's signature algorithm: \t %s\n", leaf.SignatureAlgorithm.String())
|
||||
fmt.Fprintf(tabWriter, "Cert's publicKey algorithm: \t %s\n", leaf.PublicKeyAlgorithm.String())
|
||||
fmt.Fprintf(tabWriter, "Cert's leaf SHA256: \t %s\n", hex.EncodeToString(GenerateCertHash(leaf)))
|
||||
for _, ca := range CAs {
|
||||
fmt.Fprintf(tabWriter, "Cert's CA: %s SHA256: \t %s\n", ca.Subject.CommonName, hex.EncodeToString(GenerateCertHash(ca)))
|
||||
}
|
||||
fmt.Fprintf(tabWriter, "Cert's allowed domains: \t %v\n", leaf.DNSNames)
|
||||
fmt.Println("Cert's signature algorithm: ", leaf.SignatureAlgorithm.String())
|
||||
fmt.Println("Cert's publicKey algorithm: ", leaf.PublicKeyAlgorithm.String())
|
||||
fmt.Println("Cert's allowed domains: ", leaf.DNSNames)
|
||||
fmt.Println("Cert's leaf SHA256: ", hex.EncodeToString(GenerateCertHash(leaf)))
|
||||
}
|
||||
}
|
||||
|
||||
func printTLSConnDetail(tabWriter *tabwriter.Writer, tlsConn *utls.UConn) {
|
||||
func printTLSConnDetail(tlsConn *gotls.Conn) {
|
||||
connectionState := tlsConn.ConnectionState()
|
||||
var tlsVersion string
|
||||
switch connectionState.Version {
|
||||
case gotls.VersionTLS13:
|
||||
if connectionState.Version == gotls.VersionTLS13 {
|
||||
tlsVersion = "TLS 1.3"
|
||||
case gotls.VersionTLS12:
|
||||
} else if connectionState.Version == gotls.VersionTLS12 {
|
||||
tlsVersion = "TLS 1.2"
|
||||
}
|
||||
fmt.Fprintf(tabWriter, "TLS Version: \t %s\n", tlsVersion)
|
||||
curveID := utils.AccessField[utls.CurveID](tlsConn.Conn, "curveID")
|
||||
if curveID != nil {
|
||||
PostQuantum := (*curveID == utls.X25519MLKEM768)
|
||||
fmt.Fprintf(tabWriter, "TLS Post-Quantum key exchange: \t %t (%s)\n", PostQuantum, curveID.String())
|
||||
fmt.Println("TLS Version: ", tlsVersion)
|
||||
curveID := connectionState.CurveID
|
||||
if curveID != 0 {
|
||||
PostQuantum := (curveID == gotls.X25519MLKEM768)
|
||||
fmt.Println("TLS Post-Quantum key exchange: ", PostQuantum, "("+curveID.String()+")")
|
||||
} else {
|
||||
fmt.Fprintf(tabWriter, "TLS Post-Quantum key exchange: false (RSA Exchange)\n")
|
||||
fmt.Println("TLS Post-Quantum key exchange: false (RSA Exchange)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,6 @@ import (
|
||||
_ "github.com/xtls/xray-core/transport/internet/tls"
|
||||
_ "github.com/xtls/xray-core/transport/internet/udp"
|
||||
_ "github.com/xtls/xray-core/transport/internet/websocket"
|
||||
_ "github.com/xtls/xray-core/transport/internet/xdrive"
|
||||
|
||||
// Transport headers
|
||||
_ "github.com/xtls/xray-core/transport/internet/headers/http"
|
||||
|
||||
@@ -54,15 +54,15 @@ func DialKCP(ctx context.Context, dest net.Destination, streamSettings *internet
|
||||
return nil, errors.New("failed to dial to dest: ", err).AtWarning().Base(err)
|
||||
}
|
||||
|
||||
wrapper, ok := rawConn.(*internet.PacketConnWrapper)
|
||||
if !ok {
|
||||
rawConn.Close()
|
||||
return nil, errors.New("raw is not PacketConnWrapper")
|
||||
}
|
||||
|
||||
raw := wrapper.Conn
|
||||
|
||||
if streamSettings.UdpmaskManager != nil {
|
||||
wrapper, ok := rawConn.(*internet.PacketConnWrapper)
|
||||
if !ok {
|
||||
rawConn.Close()
|
||||
return nil, errors.New("raw is not PacketConnWrapper")
|
||||
}
|
||||
|
||||
raw := wrapper.Conn
|
||||
|
||||
wrapper.Conn, err = streamSettings.UdpmaskManager.WrapPacketConnClient(raw)
|
||||
if err != nil {
|
||||
raw.Close()
|
||||
|
||||
@@ -384,7 +384,6 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
||||
PinnedPeerCertSha256: c.PinnedPeerCertSha256,
|
||||
}
|
||||
config := &tls.Config{
|
||||
InsecureSkipVerify: c.AllowInsecure,
|
||||
Rand: randCarrier,
|
||||
ClientSessionCache: globalSessionCache,
|
||||
RootCAs: root,
|
||||
|
||||
@@ -177,8 +177,7 @@ func (x *Certificate) GetBuildChain() bool {
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
AllowInsecure bool `protobuf:"varint,1,opt,name=allow_insecure,json=allowInsecure,proto3" json:"allow_insecure,omitempty"`
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// List of certificates to be served on server.
|
||||
Certificate []*Certificate `protobuf:"bytes,2,rep,name=certificate,proto3" json:"certificate,omitempty"`
|
||||
// Override server name.
|
||||
@@ -242,13 +241,6 @@ func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_transport_internet_tls_config_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Config) GetAllowInsecure() bool {
|
||||
if x != nil {
|
||||
return x.AllowInsecure
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *Config) GetCertificate() []*Certificate {
|
||||
if x != nil {
|
||||
return x.Certificate
|
||||
@@ -393,9 +385,8 @@ const file_transport_internet_tls_config_proto_rawDesc = "" +
|
||||
"\x05Usage\x12\x10\n" +
|
||||
"\fENCIPHERMENT\x10\x00\x12\x14\n" +
|
||||
"\x10AUTHORITY_VERIFY\x10\x01\x12\x13\n" +
|
||||
"\x0fAUTHORITY_ISSUE\x10\x02\"\xf5\x06\n" +
|
||||
"\x06Config\x12%\n" +
|
||||
"\x0eallow_insecure\x18\x01 \x01(\bR\rallowInsecure\x12J\n" +
|
||||
"\x0fAUTHORITY_ISSUE\x10\x02\"\xce\x06\n" +
|
||||
"\x06Config\x12J\n" +
|
||||
"\vcertificate\x18\x02 \x03(\v2(.xray.transport.internet.tls.CertificateR\vcertificate\x12\x1f\n" +
|
||||
"\vserver_name\x18\x03 \x01(\tR\n" +
|
||||
"serverName\x12#\n" +
|
||||
|
||||
@@ -38,8 +38,6 @@ message Certificate {
|
||||
}
|
||||
|
||||
message Config {
|
||||
bool allow_insecure = 1;
|
||||
|
||||
// List of certificates to be served on server.
|
||||
repeated Certificate certificate = 2;
|
||||
|
||||
|
||||
@@ -126,10 +126,6 @@ func UClient(c net.Conn, config *tls.Config, fingerprint *utls.ClientHelloID) ne
|
||||
return &UConn{UConn: utlsConn}
|
||||
}
|
||||
|
||||
func GeneraticUClient(c net.Conn, config *tls.Config) *utls.UConn {
|
||||
return utls.UClient(c, copyConfig(config), utls.HelloChrome_Auto)
|
||||
}
|
||||
|
||||
func copyConfig(c *tls.Config) *utls.Config {
|
||||
return &utls.Config{
|
||||
Rand: c.Rand,
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
@@ -21,24 +20,7 @@ func init() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if streamSettings != nil && streamSettings.UdpmaskManager != nil {
|
||||
wrapper, ok := conn.(*internet.PacketConnWrapper)
|
||||
if !ok {
|
||||
conn.Close()
|
||||
return nil, errors.New("conn is not PacketConnWrapper")
|
||||
}
|
||||
|
||||
raw := wrapper.Conn
|
||||
|
||||
wrapper.Conn, err = streamSettings.UdpmaskManager.WrapPacketConnClient(raw)
|
||||
if err != nil {
|
||||
raw.Close()
|
||||
return nil, errors.New("mask err").Base(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: handle dialer options
|
||||
return conn, nil
|
||||
return stat.Connection(conn), nil
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v6.33.5
|
||||
// source: transport/internet/xdrive/config.proto
|
||||
|
||||
package xdrive
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
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 Config struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
RemoteFolder string `protobuf:"bytes,1,opt,name=remote_folder,json=remoteFolder,proto3" json:"remote_folder,omitempty"`
|
||||
Service string `protobuf:"bytes,2,opt,name=service,proto3" json:"service,omitempty"`
|
||||
Secrets []string `protobuf:"bytes,3,rep,name=secrets,proto3" json:"secrets,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
mi := &file_transport_internet_xdrive_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_transport_internet_xdrive_config_proto_msgTypes[0]
|
||||
if 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_transport_internet_xdrive_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Config) GetRemoteFolder() string {
|
||||
if x != nil {
|
||||
return x.RemoteFolder
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetService() string {
|
||||
if x != nil {
|
||||
return x.Service
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetSecrets() []string {
|
||||
if x != nil {
|
||||
return x.Secrets
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_transport_internet_xdrive_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_transport_internet_xdrive_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"&transport/internet/xdrive/config.proto\x12\x1exray.transport.internet.xdrive\"a\n" +
|
||||
"\x06Config\x12#\n" +
|
||||
"\rremote_folder\x18\x01 \x01(\tR\fremoteFolder\x12\x18\n" +
|
||||
"\aservice\x18\x02 \x01(\tR\aservice\x12\x18\n" +
|
||||
"\asecrets\x18\x03 \x03(\tR\asecretsB5Z3github.com/xtls/xray-core/transport/internet/xdriveb\x06proto3"
|
||||
|
||||
var (
|
||||
file_transport_internet_xdrive_config_proto_rawDescOnce sync.Once
|
||||
file_transport_internet_xdrive_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_transport_internet_xdrive_config_proto_rawDescGZIP() []byte {
|
||||
file_transport_internet_xdrive_config_proto_rawDescOnce.Do(func() {
|
||||
file_transport_internet_xdrive_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_xdrive_config_proto_rawDesc), len(file_transport_internet_xdrive_config_proto_rawDesc)))
|
||||
})
|
||||
return file_transport_internet_xdrive_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_transport_internet_xdrive_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_transport_internet_xdrive_config_proto_goTypes = []any{
|
||||
(*Config)(nil), // 0: xray.transport.internet.xdrive.Config
|
||||
}
|
||||
var file_transport_internet_xdrive_config_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_transport_internet_xdrive_config_proto_init() }
|
||||
func file_transport_internet_xdrive_config_proto_init() {
|
||||
if File_transport_internet_xdrive_config_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_xdrive_config_proto_rawDesc), len(file_transport_internet_xdrive_config_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_transport_internet_xdrive_config_proto_goTypes,
|
||||
DependencyIndexes: file_transport_internet_xdrive_config_proto_depIdxs,
|
||||
MessageInfos: file_transport_internet_xdrive_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_transport_internet_xdrive_config_proto = out.File
|
||||
file_transport_internet_xdrive_config_proto_goTypes = nil
|
||||
file_transport_internet_xdrive_config_proto_depIdxs = nil
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package xray.transport.internet.xdrive;
|
||||
option go_package = "github.com/xtls/xray-core/transport/internet/xdrive";
|
||||
|
||||
message Config {
|
||||
string remote_folder = 1;
|
||||
string service = 2;
|
||||
repeated string secrets = 3;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package xdrive
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/stat"
|
||||
)
|
||||
|
||||
const protocolName = "xdrive"
|
||||
|
||||
func init() {
|
||||
common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} {
|
||||
return new(Config)
|
||||
}))
|
||||
common.Must(internet.RegisterTransportDialer(protocolName, Dial))
|
||||
common.Must(internet.RegisterTransportListener(protocolName, Serve))
|
||||
}
|
||||
|
||||
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
|
||||
//config := streamSettings.ProtocolSettings.(*Config)
|
||||
|
||||
var conn net.Conn
|
||||
return stat.Connection(conn), nil
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
config *Config
|
||||
}
|
||||
|
||||
func (s *Server) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Addr() net.Addr {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Serve(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) {
|
||||
var server Server
|
||||
|
||||
return &server, nil
|
||||
}
|
||||
Reference in New Issue
Block a user