Skip to content

Commit 6b5d2fe

Browse files
committed
Merge github.com:/v2fly/v2ray-core tag v4.23.4
2 parents 85633ec + b610fc0 commit 6b5d2fe

File tree

11 files changed

+409
-62
lines changed

11 files changed

+409
-62
lines changed

common/dice/dice.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,25 @@ func RollUint16() uint16 {
2828
return uint16(rand.Intn(65536))
2929
}
3030

31+
func RollUint64() uint64 {
32+
return rand.Uint64()
33+
}
34+
35+
func NewDeterministicDice(seed int64) *deterministicDice {
36+
return &deterministicDice{rand.New(rand.NewSource(seed))}
37+
}
38+
39+
type deterministicDice struct {
40+
*rand.Rand
41+
}
42+
43+
func (dd *deterministicDice) Roll(n int) int {
44+
if n == 1 {
45+
return 0
46+
}
47+
return dd.Intn(n)
48+
}
49+
3150
func init() {
3251
rand.Seed(time.Now().Unix())
3352
}

common/protocol/headers.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ const (
3838
RequestOptionChunkMasking bitmask.Byte = 0x04
3939

4040
RequestOptionGlobalPadding bitmask.Byte = 0x08
41+
42+
RequestOptionEarlyChecksum bitmask.Byte = 0x16
4143
)
4244

4345
type RequestHeader struct {

core.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
)
2020

2121
var (
22-
version = "4.23.3"
22+
version = "4.23.4"
2323
build = "Custom"
2424
codename = "V2Fly, a community-driven edition of V2Ray."
2525
intro = "A unified platform for anti-censorship."

features/policy/policy.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,9 @@ func defaultBufferPolicy() Buffer {
113113
func SessionDefault() Session {
114114
return Session{
115115
Timeouts: Timeout{
116-
Handshake: time.Second * 4,
116+
//Align Handshake timeout with nginx client_header_timeout
117+
//So that this value will not indicate server identity
118+
Handshake: time.Second * 60,
117119
ConnectionIdle: time.Second * 300,
118120
UplinkOnly: time.Second * 1,
119121
DownlinkOnly: time.Second * 1,

proxy/vmess/encoding/server.go

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,37 @@ func parseSecurityType(b byte) protocol.SecurityType {
125125
// DecodeRequestHeader decodes and returns (if successful) a RequestHeader from an input stream.
126126
func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.RequestHeader, error) {
127127
buffer := buf.New()
128-
defer buffer.Release()
128+
behaviorRand := dice.NewDeterministicDice(int64(s.userValidator.GetBehaviorSeed()))
129+
BaseDrainSize := behaviorRand.Roll(3266)
130+
RandDrainMax := behaviorRand.Roll(64) + 1
131+
RandDrainRolled := dice.Roll(RandDrainMax)
132+
DrainSize := BaseDrainSize + 16 + 38 + RandDrainRolled
133+
readSizeRemain := DrainSize
134+
135+
drainConnection := func(e error) error {
136+
//We read a deterministic generated length of data before closing the connection to offset padding read pattern
137+
readSizeRemain -= int(buffer.Len())
138+
if readSizeRemain > 0 {
139+
err := s.DrainConnN(reader, readSizeRemain)
140+
if err != nil {
141+
return newError("failed to drain connection DrainSize = ", BaseDrainSize, " ", RandDrainMax, " ", RandDrainRolled).Base(err).Base(e)
142+
}
143+
return newError("connection drained DrainSize = ", BaseDrainSize, " ", RandDrainMax, " ", RandDrainRolled).Base(e)
144+
}
145+
return e
146+
}
147+
148+
defer func() {
149+
buffer.Release()
150+
}()
129151

130152
if _, err := buffer.ReadFullFrom(reader, protocol.IDBytesLen); err != nil {
131153
return nil, newError("failed to read request header").Base(err)
132154
}
133155

134156
user, timestamp, valid := s.userValidator.Get(buffer.Bytes())
135157
if !valid {
136-
return nil, newError("invalid user")
158+
return nil, drainConnection(newError("invalid user"))
137159
}
138160

139161
iv := hashTimestamp(md5.New(), timestamp)
@@ -142,6 +164,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
142164
aesStream := crypto.NewAesDecryptionStream(vmessAccount.ID.CmdKey(), iv[:])
143165
decryptor := crypto.NewCryptionReader(aesStream, reader)
144166

167+
readSizeRemain -= int(buffer.Len())
145168
buffer.Clear()
146169
if _, err := buffer.ReadFullFrom(decryptor, 38); err != nil {
147170
return nil, newError("failed to read request header").Base(err)
@@ -159,7 +182,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
159182
sid.key = s.requestBodyKey
160183
sid.nonce = s.requestBodyIV
161184
if !s.sessionHistory.addIfNotExits(sid) {
162-
return nil, newError("duplicated session id, possibly under replay attack")
185+
return nil, drainConnection(newError("duplicated session id, possibly under replay attack"))
163186
}
164187

165188
s.responseHeader = buffer.Byte(33) // 1 byte
@@ -197,12 +220,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
197220

198221
if actualHash != expectedHash {
199222
//It is possible that we are under attack described in https://github.com/v2ray/v2ray-core/issues/2523
200-
//We read a deterministic generated length of data before closing the connection to offset padding read pattern
201-
drainSum := dice.RollDeterministic(48, int64(actualHash))
202-
if err := s.DrainConnN(reader, drainSum); err != nil {
203-
return nil, newError("invalid auth, failed to drain connection").Base(err)
204-
}
205-
return nil, newError("invalid auth, connection drained")
223+
return nil, drainConnection(newError("invalid auth"))
206224
}
207225

208226
if request.Address == nil {

proxy/vmess/validator.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
package vmess
44

55
import (
6+
"hash/crc64"
67
"strings"
78
"sync"
89
"time"
10+
"v2ray.com/core/common/dice"
911

1012
"v2ray.com/core/common"
1113
"v2ray.com/core/common/protocol"
@@ -26,11 +28,13 @@ type user struct {
2628
// TimedUserValidator is a user Validator based on time.
2729
type TimedUserValidator struct {
2830
sync.RWMutex
29-
users []*user
30-
userHash map[[16]byte]indexTimePair
31-
hasher protocol.IDHash
32-
baseTime protocol.Timestamp
33-
task *task.Periodic
31+
users []*user
32+
userHash map[[16]byte]indexTimePair
33+
hasher protocol.IDHash
34+
baseTime protocol.Timestamp
35+
task *task.Periodic
36+
behaviorSeed uint64
37+
behaviorFused bool
3438
}
3539

3640
type indexTimePair struct {
@@ -124,13 +128,20 @@ func (v *TimedUserValidator) Add(u *protocol.MemoryUser) error {
124128
v.users = append(v.users, uu)
125129
v.generateNewHashes(protocol.Timestamp(nowSec), uu)
126130

131+
if v.behaviorFused == false {
132+
account := uu.user.Account.(*MemoryAccount)
133+
v.behaviorSeed = crc64.Update(v.behaviorSeed, crc64.MakeTable(crc64.ECMA), account.ID.Bytes())
134+
}
135+
127136
return nil
128137
}
129138

130139
func (v *TimedUserValidator) Get(userHash []byte) (*protocol.MemoryUser, protocol.Timestamp, bool) {
131140
defer v.RUnlock()
132141
v.RLock()
133142

143+
v.behaviorFused = true
144+
134145
var fixedSizeHash [16]byte
135146
copy(fixedSizeHash[:], userHash)
136147
pair, found := v.userHash[fixedSizeHash]
@@ -170,3 +181,13 @@ func (v *TimedUserValidator) Remove(email string) bool {
170181
func (v *TimedUserValidator) Close() error {
171182
return v.task.Close()
172183
}
184+
185+
func (v *TimedUserValidator) GetBehaviorSeed() uint64 {
186+
v.Lock()
187+
defer v.Unlock()
188+
v.behaviorFused = true
189+
if v.behaviorSeed == 0 {
190+
v.behaviorSeed = dice.RollUint64()
191+
}
192+
return v.behaviorSeed
193+
}

testing/scenarios/command_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"io"
7+
"strings"
78
"testing"
89
"time"
910

@@ -265,7 +266,9 @@ func TestCommanderAddRemoveUser(t *testing.T) {
265266
common.Must(err)
266267
defer CloseAllServers(servers)
267268

268-
if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != io.EOF {
269+
if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != io.EOF &&
270+
/*We might wish to drain the connection*/
271+
(err != nil && !strings.HasSuffix(err.Error(), "i/o timeout")) {
269272
t.Fatal("expected error: ", err)
270273
}
271274

0 commit comments

Comments
 (0)