diff --git a/infra/conf/vless.go b/infra/conf/vless.go index 1dd9bad6..3d7728df 100644 --- a/infra/conf/vless.go +++ b/infra/conf/vless.go @@ -96,16 +96,18 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) { default: return false } - if s[2] != "1rtt" { - t := strings.TrimSuffix(s[2], "s") - if t == s[2] { - return false - } - i, err := strconv.Atoi(t) + t := strings.SplitN(strings.TrimSuffix(s[2], "s"), "-", 2) + i, err := strconv.Atoi(t[0]) + if err != nil { + return false + } + config.SecondsFrom = uint32(i) + if len(t) > 1 { + i, err := strconv.Atoi(t[1]) if err != nil { return false } - config.Seconds = uint32(i) + config.SecondsTo = uint32(i) } padding := 0 for _, r := range s[3:] { diff --git a/proxy/vless/encryption/server.go b/proxy/vless/encryption/server.go index 48924a7e..132cd1b0 100644 --- a/proxy/vless/encryption/server.go +++ b/proxy/vless/encryption/server.go @@ -18,7 +18,6 @@ import ( ) type ServerSession struct { - Expire time.Time PfsKey []byte NfsKeys sync.Map } @@ -29,16 +28,16 @@ type ServerInstance struct { Hash32s [][32]byte RelaysLength int XorMode uint32 - Seconds uint32 + SecondsFrom uint32 + SecondsTo uint32 PaddingLens [][3]int PaddingGaps [][3]int RWLock sync.RWMutex Sessions map[[16]byte]*ServerSession - Closed bool } -func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32, padding string) (err error) { +func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, secondsFrom, secondsTo uint32, padding string) (err error) { if i.NfsSKeys != nil { return errors.New("already initialized") } @@ -67,37 +66,12 @@ func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32, p } i.RelaysLength -= 32 i.XorMode = xorMode - if seconds > 0 { - i.Seconds = seconds - i.Sessions = make(map[[16]byte]*ServerSession) - go func() { - for { - time.Sleep(time.Minute) - i.RWLock.Lock() - if i.Closed { - i.RWLock.Unlock() - return - } - now := time.Now() - for ticket, session := range i.Sessions { - if now.After(session.Expire) { - delete(i.Sessions, ticket) - } - } - i.RWLock.Unlock() - } - }() - } + i.SecondsFrom = secondsFrom + i.SecondsTo = secondsTo + i.Sessions = make(map[[16]byte]*ServerSession) return ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps) } -func (i *ServerInstance) Close() (err error) { - i.RWLock.Lock() - i.Closed = true - i.RWLock.Unlock() - return -} - func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn, error) { if i.NfsSKeys == nil { return nil, errors.New("uninitialized") @@ -132,7 +106,7 @@ func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn return nil, err } if publicKey.Bytes()[31] > 127 { // we just don't want the observer can change even one bit without breaking the connection, though it has nothing to do with security - return nil, errors.New("the highest bit of the last byte of the peer-sent X25519 public key must be 0") + return nil, errors.New("the highest bit of the last byte of the peer-sent X25519 public key is not 0") } nfsKey, err = k.ECDH(publicKey) if err != nil { @@ -180,7 +154,7 @@ func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn length := DecodeLength(decryptedLength) if length == 32 { - if i.Seconds == 0 { + if i.SecondsFrom == 0 && i.SecondsTo == 0 { return nil, errors.New("0-RTT is not allowed") } encryptedTicket := make([]byte, 32) @@ -252,14 +226,23 @@ func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn ticket := make([]byte, 16) rand.Read(ticket) - copy(ticket, EncodeLength(int(i.Seconds*4/5))) - if i.Seconds > 0 { + seconds := 0 + if i.SecondsTo == 0 { + seconds = int(i.SecondsFrom) * int(crypto.RandBetween(50, 100)) / 100 + } else { + seconds = int(crypto.RandBetween(int64(i.SecondsFrom), int64(i.SecondsTo))) + } + copy(ticket, EncodeLength(int(seconds))) + if seconds > 0 { i.RWLock.Lock() - i.Sessions[[16]byte(ticket)] = &ServerSession{ - Expire: time.Now().Add(time.Duration(i.Seconds) * time.Second), - PfsKey: pfsKey, - } + i.Sessions[[16]byte(ticket)] = &ServerSession{PfsKey: pfsKey} i.RWLock.Unlock() + go func() { + time.Sleep(time.Duration(seconds)*time.Second + time.Minute) + i.RWLock.Lock() + delete(i.Sessions, [16]byte(ticket)) + i.RWLock.Unlock() + }() } pfsKeyExchangeLength := 1088 + 32 + 16 diff --git a/proxy/vless/inbound/config.pb.go b/proxy/vless/inbound/config.pb.go index ec28db8d..3a897411 100644 --- a/proxy/vless/inbound/config.pb.go +++ b/proxy/vless/inbound/config.pb.go @@ -111,12 +111,13 @@ type Config struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Clients []*protocol.User `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"` - Fallbacks []*Fallback `protobuf:"bytes,2,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"` - Decryption string `protobuf:"bytes,3,opt,name=decryption,proto3" json:"decryption,omitempty"` - XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"` - Seconds uint32 `protobuf:"varint,5,opt,name=seconds,proto3" json:"seconds,omitempty"` - Padding string `protobuf:"bytes,6,opt,name=padding,proto3" json:"padding,omitempty"` + Clients []*protocol.User `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"` + Fallbacks []*Fallback `protobuf:"bytes,2,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"` + Decryption string `protobuf:"bytes,3,opt,name=decryption,proto3" json:"decryption,omitempty"` + XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"` + SecondsFrom uint32 `protobuf:"varint,5,opt,name=seconds_from,json=secondsFrom,proto3" json:"seconds_from,omitempty"` + SecondsTo uint32 `protobuf:"varint,6,opt,name=seconds_to,json=secondsTo,proto3" json:"seconds_to,omitempty"` + Padding string `protobuf:"bytes,7,opt,name=padding,proto3" json:"padding,omitempty"` } func (x *Config) Reset() { @@ -177,9 +178,16 @@ func (x *Config) GetXorMode() uint32 { return 0 } -func (x *Config) GetSeconds() uint32 { +func (x *Config) GetSecondsFrom() uint32 { if x != nil { - return x.Seconds + return x.SecondsFrom + } + return 0 +} + +func (x *Config) GetSecondsTo() uint32 { + if x != nil { + return x.SecondsTo } return 0 } @@ -207,7 +215,7 @@ var file_proxy_vless_inbound_config_proto_rawDesc = []byte{ 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65, - 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0xee, 0x01, + 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0x96, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 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, @@ -219,18 +227,20 @@ var file_proxy_vless_inbound_config_proto_rawDesc = []byte{ 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, - 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x6a, - 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, - 0x5a, 0x2d, 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, 0x2f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, - 0x02, 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, - 0x73, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x0d, 0x52, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0b, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x1d, 0x0a, + 0x0a, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x5f, 0x74, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x09, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x54, 0x6f, 0x12, 0x18, 0x0a, 0x07, + 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x6a, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, + 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, + 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2d, 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, 0x2f, + 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, + 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proxy/vless/inbound/config.proto b/proxy/vless/inbound/config.proto index 2a7ad833..febf2ef4 100644 --- a/proxy/vless/inbound/config.proto +++ b/proxy/vless/inbound/config.proto @@ -23,6 +23,7 @@ message Config { string decryption = 3; uint32 xorMode = 4; - uint32 seconds = 5; - string padding = 6; + uint32 seconds_from = 5; + uint32 seconds_to = 6; + string padding = 7; } diff --git a/proxy/vless/inbound/inbound.go b/proxy/vless/inbound/inbound.go index cfe2059d..10646b97 100644 --- a/proxy/vless/inbound/inbound.go +++ b/proxy/vless/inbound/inbound.go @@ -93,7 +93,7 @@ func New(ctx context.Context, config *Config, dc dns.Client, validator vless.Val nfsSKeysBytes = append(nfsSKeysBytes, b) } handler.decryption = &encryption.ServerInstance{} - if err := handler.decryption.Init(nfsSKeysBytes, config.XorMode, config.Seconds, config.Padding); err != nil { + if err := handler.decryption.Init(nfsSKeysBytes, config.XorMode, config.SecondsFrom, config.SecondsTo, config.Padding); err != nil { return nil, errors.New("failed to use decryption").Base(err).AtError() } } @@ -176,9 +176,6 @@ func isMuxAndNotXUDP(request *protocol.RequestHeader, first *buf.Buffer) bool { // Close implements common.Closable.Close(). func (h *Handler) Close() error { - if h.decryption != nil { - h.decryption.Close() - } return errors.Combine(common.Close(h.validator)) }