Fix 1/67000000 chance's server panic; Refine comments

https://github.com/XTLS/Xray-core/pull/4952#issuecomment-3188118918
This commit is contained in:
RPRX
2025-08-14 15:28:40 +00:00
committed by GitHub
parent 2807ee432a
commit bfe4820f2f
4 changed files with 22 additions and 18 deletions

View File

@@ -104,9 +104,9 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
if _, err := c.Conn.Write(clientHello); err != nil { if _, err := c.Conn.Write(clientHello); err != nil {
return nil, err return nil, err
} }
// client can send more padding / NFS AEAD messages if needed // client can send more paddings / NFS AEAD messages if needed
_, t, l, err := ReadAndDiscardPaddings(c.Conn) _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before server hello
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -190,9 +190,9 @@ func (c *ClientConn) Read(b []byte) (int, error) {
return 0, nil return 0, nil
} }
if c.peerAead == nil { if c.peerAead == nil {
_, t, l, err := ReadAndDiscardPaddings(c.Conn) _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before random hello
if err != nil { if err != nil {
if c.instance != nil && strings.HasPrefix(err.Error(), "invalid header: ") { // from 0-RTT if c.instance != nil && strings.HasPrefix(err.Error(), "invalid header: ") { // 0-RTT's 0-RTT
c.instance.Lock() c.instance.Lock()
if bytes.Equal(c.ticket, c.instance.ticket) { if bytes.Equal(c.ticket, c.instance.ticket) {
c.instance.expire = time.Now() // expired c.instance.expire = time.Now() // expired

View File

@@ -47,7 +47,7 @@ func DecodeHeader(h []byte) (t byte, l int, err error) {
l = 0 l = 0
} }
if l < 17 || l > 17000 { // TODO: TLSv1.3 max length if l < 17 || l > 17000 { // TODO: TLSv1.3 max length
err = errors.New("invalid header: ", fmt.Sprintf("%v", h[:5])) // relied by client's Read() err = errors.New("invalid header: ", fmt.Sprintf("%v", h[:5])) // DO NOT CHANGE: relied by client's Read()
} }
return return
} }

View File

@@ -97,7 +97,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
} }
c := &ServerConn{Conn: conn} c := &ServerConn{Conn: conn}
_, t, l, err := ReadAndDiscardPaddings(c.Conn) _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before client/ticket hello
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -117,9 +117,13 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
s := i.sessions[[21]byte(peerTicketHello)] s := i.sessions[[21]byte(peerTicketHello)]
i.RUnlock() i.RUnlock()
if s == nil { if s == nil {
noise := make([]byte, crypto.RandBetween(100, 1000)) noises := make([]byte, crypto.RandBetween(100, 1000))
rand.Read(noise) var err error
c.Conn.Write(noise) // make client do new handshake for err == nil {
rand.Read(noises)
_, _, err = DecodeHeader(noises)
}
c.Conn.Write(noises) // make client do new handshake
return nil, errors.New("expired ticket") return nil, errors.New("expired ticket")
} }
if _, replay := s.randoms.LoadOrStore([32]byte(peerTicketHello[21:]), true); replay { if _, replay := s.randoms.LoadOrStore([32]byte(peerTicketHello[21:]), true); replay {
@@ -169,7 +173,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
if _, err := c.Conn.Write(serverHello); err != nil { if _, err := c.Conn.Write(serverHello); err != nil {
return nil, err return nil, err
} }
// server can send more padding / PFS AEAD messages if needed // server can send more paddings / PFS AEAD messages if needed
if i.minutes > 0 { if i.minutes > 0 {
i.Lock() i.Lock()
@@ -189,8 +193,8 @@ func (c *ServerConn) Read(b []byte) (int, error) {
return 0, nil return 0, nil
} }
if c.peerAead == nil { if c.peerAead == nil {
if c.peerRandom == nil { // from 1-RTT if c.peerRandom == nil { // 1-RTT's 0-RTT
_, t, l, err := ReadAndDiscardPaddings(c.Conn) _, t, l, err := ReadAndDiscardPaddings(c.Conn) // allow paddings before ticket hello
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@@ -22,7 +22,7 @@ func NewXorConn(conn net.Conn, key []byte) *XorConn {
//chacha20.NewUnauthenticatedCipher() //chacha20.NewUnauthenticatedCipher()
} }
func (c *XorConn) Write(b []byte) (int, error) { // two records at most func (c *XorConn) Write(b []byte) (int, error) { // whole one/two records
if len(b) == 0 { if len(b) == 0 {
return 0, nil return 0, nil
} }
@@ -34,10 +34,10 @@ func (c *XorConn) Write(b []byte) (int, error) { // two records at most
c.ctr = cipher.NewCTR(block, iv) c.ctr = cipher.NewCTR(block, iv)
} }
t, l, _ := DecodeHeader(b) t, l, _ := DecodeHeader(b)
if t != 23 { if t == 23 { // single 23
l += 10 // 5+l+5
} else {
l = 5 l = 5
} else { // 1/0 + 23, or noises only
l += 10
} }
c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b
if iv != nil { if iv != nil {
@@ -73,8 +73,8 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
return len(b), nil return len(b), nil
} }
c.peerCtr.XORKeyStream(b, b) c.peerCtr.XORKeyStream(b, b)
if c.isHeader { if c.isHeader { // always 5-bytes
if t, _, _ := DecodeHeader(b); t == 23 { // always 5-bytes if t, _, _ := DecodeHeader(b); t == 23 {
c.skipNext = true c.skipNext = true
} else { } else {
c.isHeader = false c.isHeader = false