Skip to content

Commit 9ffd96c

Browse files
committed
Drop invalid record silently during handshake
Fix issue: invalid record in handshake staging cause readloop exited then handshake failed.
1 parent 3e8a7d7 commit 9ffd96c

5 files changed

Lines changed: 95 additions & 12 deletions

File tree

conn.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,10 @@ func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFligh
10261026
} else {
10271027
switch {
10281028
case errors.Is(err, context.DeadlineExceeded), errors.Is(err, context.Canceled), errors.Is(err, io.EOF), errors.Is(err, net.ErrClosed):
1029+
case errors.Is(err, recordlayer.ErrInvalidPacketLength):
1030+
// Decode error must be silently discarded
1031+
// [RFC6347 Section-4.1.2.7]
1032+
continue
10291033
default:
10301034
if c.isHandshakeCompletedSuccessfully() {
10311035
// Keep read loop and pass the read error to Read()

conn_test.go

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ func TestHandshakeWithAlert(t *testing.T) {
389389
clientErr <- err
390390
}()
391391

392-
_, errServer := testServer(ctx, dtlsnet.PacketConnFromConn(cb), ca.RemoteAddr(), testCase.configServer, true)
392+
_, errServer := testServer(ctx, dtlsnet.PacketConnFromConn(cb), cb.RemoteAddr(), testCase.configServer, true)
393393
if !errors.Is(errServer, testCase.errServer) {
394394
t.Fatalf("Server error exp(%v) failed(%v)", testCase.errServer, errServer)
395395
}
@@ -402,6 +402,71 @@ func TestHandshakeWithAlert(t *testing.T) {
402402
}
403403
}
404404

405+
func TestHandshakeWithInvalidRecord(t *testing.T) {
406+
// Limit runtime in case of deadlocks
407+
lim := test.TimeOut(time.Second * 20)
408+
defer lim.Stop()
409+
410+
// Check for leaking routines
411+
report := test.CheckRoutines(t)
412+
defer report()
413+
414+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
415+
defer cancel()
416+
417+
type result struct {
418+
c *Conn
419+
err error
420+
}
421+
clientErr := make(chan result, 1)
422+
ca, cb := dpipe.Pipe()
423+
caWithInvalidRecord := &connWithCallback{Conn: ca}
424+
425+
var msgSeq atomic.Int32
426+
// Send invalid record after first message
427+
caWithInvalidRecord.onWrite = func(b []byte) {
428+
if msgSeq.Add(1) == 2 {
429+
if _, err := ca.Write([]byte{0x01, 0x02}); err != nil {
430+
t.Fatal(err)
431+
}
432+
}
433+
}
434+
go func() {
435+
client, err := testClient(ctx, dtlsnet.PacketConnFromConn(caWithInvalidRecord), caWithInvalidRecord.RemoteAddr(), &Config{
436+
CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
437+
}, true)
438+
clientErr <- result{client, err}
439+
}()
440+
441+
server, errServer := testServer(ctx, dtlsnet.PacketConnFromConn(cb), cb.RemoteAddr(), &Config{
442+
CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
443+
}, true)
444+
445+
errClient := <-clientErr
446+
447+
defer func() {
448+
if server != nil {
449+
if err := server.Close(); err != nil {
450+
t.Fatal(err)
451+
}
452+
}
453+
454+
if errClient.c != nil {
455+
if err := errClient.c.Close(); err != nil {
456+
t.Fatal(err)
457+
}
458+
}
459+
}()
460+
461+
if errServer != nil {
462+
t.Fatalf("Server failed(%v)", errServer)
463+
}
464+
465+
if errClient.err != nil {
466+
t.Fatalf("Client failed(%v)", errClient.err)
467+
}
468+
}
469+
405470
func TestExportKeyingMaterial(t *testing.T) {
406471
// Check for leaking routines
407472
report := test.CheckRoutines(t)
@@ -3096,3 +3161,15 @@ func TestSkipHelloVerify(t *testing.T) {
30963161
t.Error(err)
30973162
}
30983163
}
3164+
3165+
type connWithCallback struct {
3166+
net.Conn
3167+
onWrite func([]byte)
3168+
}
3169+
3170+
func (c *connWithCallback) Write(b []byte) (int, error) {
3171+
if c.onWrite != nil {
3172+
c.onWrite(b)
3173+
}
3174+
return c.Conn.Write(b)
3175+
}

pkg/protocol/recordlayer/errors.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ import (
1111
)
1212

1313
var (
14-
errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
15-
errInvalidPacketLength = &protocol.TemporaryError{Err: errors.New("packet length and declared length do not match")} //nolint:goerr113
16-
errSequenceNumberOverflow = &protocol.InternalError{Err: errors.New("sequence number overflow")} //nolint:goerr113
17-
errUnsupportedProtocolVersion = &protocol.FatalError{Err: errors.New("unsupported protocol version")} //nolint:goerr113
18-
errInvalidContentType = &protocol.TemporaryError{Err: errors.New("invalid content type")} //nolint:goerr113
14+
// ErrInvalidPacketLength is returned when the packet length too small or declared length do not match
15+
ErrInvalidPacketLength = &protocol.TemporaryError{Err: errors.New("packet length and declared length do not match")} //nolint:goerr113
16+
17+
errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
18+
errSequenceNumberOverflow = &protocol.InternalError{Err: errors.New("sequence number overflow")} //nolint:goerr113
19+
errUnsupportedProtocolVersion = &protocol.FatalError{Err: errors.New("unsupported protocol version")} //nolint:goerr113
20+
errInvalidContentType = &protocol.TemporaryError{Err: errors.New("invalid content type")} //nolint:goerr113
1921
)

pkg/protocol/recordlayer/recordlayer.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,12 @@ func UnpackDatagram(buf []byte) ([][]byte, error) {
100100

101101
for offset := 0; len(buf) != offset; {
102102
if len(buf)-offset <= FixedHeaderSize {
103-
return nil, errInvalidPacketLength
103+
return nil, ErrInvalidPacketLength
104104
}
105105

106106
pktLen := (FixedHeaderSize + int(binary.BigEndian.Uint16(buf[offset+11:])))
107107
if offset+pktLen > len(buf) {
108-
return nil, errInvalidPacketLength
108+
return nil, ErrInvalidPacketLength
109109
}
110110

111111
out = append(out, buf[offset:offset+pktLen])
@@ -129,12 +129,12 @@ func ContentAwareUnpackDatagram(buf []byte, cidLength int) ([][]byte, error) {
129129
lenIdx += cidLength
130130
}
131131
if len(buf)-offset <= headerSize {
132-
return nil, errInvalidPacketLength
132+
return nil, ErrInvalidPacketLength
133133
}
134134

135135
pktLen := (headerSize + int(binary.BigEndian.Uint16(buf[offset+lenIdx:])))
136136
if offset+pktLen > len(buf) {
137-
return nil, errInvalidPacketLength
137+
return nil, ErrInvalidPacketLength
138138
}
139139

140140
out = append(out, buf[offset:offset+pktLen])

pkg/protocol/recordlayer/recordlayer_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ func TestUDPDecode(t *testing.T) {
3939
{
4040
Name: "Invalid packet length",
4141
Data: []byte{0x14, 0xfe},
42-
WantError: errInvalidPacketLength,
42+
WantError: ErrInvalidPacketLength,
4343
},
4444
{
4545
Name: "Packet declared invalid length",
4646
Data: []byte{0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xFF, 0x01},
47-
WantError: errInvalidPacketLength,
47+
WantError: ErrInvalidPacketLength,
4848
},
4949
} {
5050
dtlsPkts, err := UnpackDatagram(test.Data)

0 commit comments

Comments
 (0)