Explorar o código

VLESS outbound: Add pre-connect (early test, for Vision Seed)

https://t.me/projectXtls/1034
RPRX hai 2 meses
pai
achega
2dc9729864

+ 2 - 0
infra/conf/vless.go

@@ -212,6 +212,7 @@ type VLessOutboundConfig struct {
 	Seed       string                `json:"seed"`
 	Encryption string                `json:"encryption"`
 	Reverse    *vless.Reverse        `json:"reverse"`
+	Testpre    uint32                `json:"testpre"`
 	Vnext      []*VLessOutboundVnext `json:"vnext"`
 }
 
@@ -258,6 +259,7 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
 				//account.Seed = c.Seed
 				account.Encryption = c.Encryption
 				account.Reverse = c.Reverse
+				account.Testpre = c.Testpre
 			} else {
 				if err := json.Unmarshal(rawUser, account); err != nil {
 					return nil, errors.New(`VLESS users: invalid user`).Base(err)

+ 4 - 0
proxy/vless/account.go

@@ -22,6 +22,7 @@ func (a *Account) AsAccount() (protocol.Account, error) {
 		Seconds:    a.Seconds,
 		Padding:    a.Padding,
 		Reverse:    a.Reverse,
+		Testpre:    a.Testpre,
 	}, nil
 }
 
@@ -38,6 +39,8 @@ type MemoryAccount struct {
 	Padding    string
 
 	Reverse *Reverse
+
+	Testpre uint32
 }
 
 // Equals implements protocol.Account.Equals().
@@ -58,5 +61,6 @@ func (a *MemoryAccount) ToProto() proto.Message {
 		Seconds:    a.Seconds,
 		Padding:    a.Padding,
 		Reverse:    a.Reverse,
+		Testpre:    a.Testpre,
 	}
 }

+ 18 - 8
proxy/vless/account.pb.go

@@ -79,6 +79,7 @@ type Account struct {
 	Seconds    uint32   `protobuf:"varint,5,opt,name=seconds,proto3" json:"seconds,omitempty"`
 	Padding    string   `protobuf:"bytes,6,opt,name=padding,proto3" json:"padding,omitempty"`
 	Reverse    *Reverse `protobuf:"bytes,7,opt,name=reverse,proto3" json:"reverse,omitempty"`
+	Testpre    uint32   `protobuf:"varint,8,opt,name=testpre,proto3" json:"testpre,omitempty"`
 }
 
 func (x *Account) Reset() {
@@ -160,6 +161,13 @@ func (x *Account) GetReverse() *Reverse {
 	return nil
 }
 
+func (x *Account) GetTestpre() uint32 {
+	if x != nil {
+		return x.Testpre
+	}
+	return 0
+}
+
 var File_proxy_vless_account_proto protoreflect.FileDescriptor
 
 var file_proxy_vless_account_proto_rawDesc = []byte{
@@ -167,7 +175,7 @@ var file_proxy_vless_account_proto_rawDesc = []byte{
 	0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61,
 	0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x22, 0x1b, 0x0a,
 	0x07, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0xd0, 0x01, 0x0a, 0x07, 0x41,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0xea, 0x01, 0x0a, 0x07, 0x41,
 	0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
 	0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02,
 	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e,
@@ -180,13 +188,15 @@ var file_proxy_vless_account_proto_rawDesc = []byte{
 	0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x33, 0x0a, 0x07, 0x72, 0x65, 0x76, 0x65,
 	0x72, 0x73, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79,
 	0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x52, 0x65, 0x76,
-	0x65, 0x72, 0x73, 0x65, 0x52, 0x07, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x42, 0x52, 0x0a,
-	0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e,
-	0x76, 0x6c, 0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x25, 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, 0x76, 0x6c, 0x65, 0x73, 0x73, 0xaa, 0x02,
-	0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73,
-	0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x65, 0x72, 0x73, 0x65, 0x52, 0x07, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x12, 0x18, 0x0a,
+	0x07, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
+	0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x65, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
+	0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x50,
+	0x01, 0x5a, 0x25, 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, 0x76, 0x6c, 0x65, 0x73, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e,
+	0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x33,
 }
 
 var (

+ 2 - 0
proxy/vless/account.proto

@@ -22,4 +22,6 @@ message Account {
   string padding = 6;
 
   Reverse reverse = 7;
+
+  uint32 testpre = 8;
 }

+ 44 - 8
proxy/vless/outbound/outbound.go

@@ -7,6 +7,7 @@ import (
 	"encoding/base64"
 	"reflect"
 	"strings"
+	"sync"
 	"time"
 	"unsafe"
 
@@ -52,6 +53,10 @@ type Handler struct {
 	cone          bool
 	encryption    *encryption.ClientInstance
 	reverse       *Reverse
+
+	testpre uint32
+	locker  sync.Mutex
+	conns   []stat.Connection
 }
 
 // New creates a new VLess outbound handler.
@@ -105,6 +110,8 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
 		}()
 	}
 
+	handler.testpre = a.Testpre
+
 	return handler, nil
 }
 
@@ -128,15 +135,44 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
 	rec := h.server
 	var conn stat.Connection
 
-	if err := retry.ExponentialBackoff(5, 200).On(func() error {
-		var err error
-		conn, err = dialer.Dial(ctx, rec.Destination)
-		if err != nil {
-			return err
+	if h.testpre > 0 && h.reverse == nil {
+		h.locker.Lock()
+		if h.conns == nil {
+			h.conns = make([]stat.Connection, 0)
+			go func() {
+				for { // TODO: close & inactive
+					time.Sleep(100 * time.Millisecond) // TODO: customize & randomize
+					h.locker.Lock()
+					if len(h.conns) >= int(h.testpre) {
+						h.locker.Unlock()
+						continue
+					}
+					h.locker.Unlock()
+					if conn, err := dialer.Dial(context.Background(), rec.Destination); err == nil { // TODO: timeout & concurrency? & ctx mitm?
+						h.locker.Lock()
+						h.conns = append(h.conns, conn) // TODO: vision paddings
+						h.locker.Unlock()
+					}
+				}
+			}()
+		} else if len(h.conns) > 0 {
+			conn = h.conns[0]
+			h.conns = h.conns[1:]
+		}
+		h.locker.Unlock()
+	}
+
+	if conn == nil {
+		if err := retry.ExponentialBackoff(5, 200).On(func() error {
+			var err error
+			conn, err = dialer.Dial(ctx, rec.Destination)
+			if err != nil {
+				return err
+			}
+			return nil
+		}); err != nil {
+			return errors.New("failed to find an available destination").Base(err).AtWarning()
 		}
-		return nil
-	}); err != nil {
-		return errors.New("failed to find an available destination").Base(err).AtWarning()
 	}
 	defer conn.Close()