Skip to content

Commit 748c25c

Browse files
committed
Fix DTLS client role in long delay connections
Fixes pion/webrtc#2089 When a retranmission from the remote side arrives after the handshake is complete, the `finish` routine puts it back into retransmit loop. With Chrome, this fails after 15 seconds. Firefox does not error out though. Testing: --------- - Tested with Firefox and Chrome with long delay (500 ms up and down) in network link conditioner. - Tested the above with no introduced delays too. - Added test for slow server.
1 parent 17f86a3 commit 748c25c

3 files changed

Lines changed: 185 additions & 23 deletions

File tree

AUTHORS.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Arlo Breault <arlolra@gmail.com>
99
Atsushi Watanabe <atsushi.w@ieee.org>
1010
backkem <mail@backkem.me>
1111
bjdgyc <bjdgyc@163.com>
12+
boks1971 <raja.gobi@tutanota.com>
1213
Bragadeesh <bragboy@gmail.com>
1314
Carson Hoffman <c@rsonhoffman.com>
1415
Cecylia Bocovich <cohosh@torproject.org>

handshaker.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ func (s *handshakeFSM) finish(ctx context.Context, c flightConn) (handshakeState
326326
if nextFlight == 0 {
327327
break
328328
}
329+
if nextFlight.isLastRecvFlight() && s.currentFlight == nextFlight {
330+
return handshakeFinished, nil
331+
}
329332
<-retransmitTimer.C
330333
// Retransmit last flight
331334
return handshakeSending, nil

handshaker_test.go

Lines changed: 181 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,20 @@ func TestHandshaker(t *testing.T) {
5757
t.Fatal(err)
5858
}
5959

60-
genFilters := map[string]func() (packetFilter, packetFilter, func(t *testing.T)){
61-
"PassThrough": func() (packetFilter, packetFilter, func(t *testing.T)) {
62-
return nil, nil, nil
60+
genFilters := map[string]func() (TestEndpoint, TestEndpoint, func(t *testing.T)){
61+
"PassThrough": func() (TestEndpoint, TestEndpoint, func(t *testing.T)) {
62+
return TestEndpoint{}, TestEndpoint{}, nil
6363
},
64-
"HelloVerifyRequestLost": func() (packetFilter, packetFilter, func(t *testing.T)) {
64+
65+
"HelloVerifyRequestLost": func() (TestEndpoint, TestEndpoint, func(t *testing.T)) {
6566
var (
6667
cntHelloVerifyRequest = 0
6768
cntClientHelloNoCookie = 0
6869
)
6970
const helloVerifyDrop = 5
70-
return func(p *packet) bool {
71+
72+
clientEndpoint := TestEndpoint{
73+
Filter: func(p *packet) bool {
7174
h, ok := p.record.Content.(*handshake.Handshake)
7275
if !ok {
7376
return true
@@ -79,7 +82,10 @@ func TestHandshaker(t *testing.T) {
7982
}
8083
return true
8184
},
82-
func(p *packet) bool {
85+
}
86+
87+
serverEndpoint := TestEndpoint{
88+
Filter: func(p *packet) bool {
8389
h, ok := p.record.Content.(*handshake.Handshake)
8490
if !ok {
8591
return true
@@ -90,31 +96,161 @@ func TestHandshaker(t *testing.T) {
9096
}
9197
return true
9298
},
93-
func(t *testing.T) {
94-
if cntHelloVerifyRequest != helloVerifyDrop+1 {
95-
t.Errorf("Number of HelloVerifyRequest retransmit is wrong, expected: %d times, got: %d times", helloVerifyDrop+1, cntHelloVerifyRequest)
99+
}
100+
101+
report := func(t *testing.T) {
102+
if cntHelloVerifyRequest != helloVerifyDrop+1 {
103+
t.Errorf("Number of HelloVerifyRequest retransmit is wrong, expected: %d times, got: %d times", helloVerifyDrop+1, cntHelloVerifyRequest)
104+
}
105+
if cntClientHelloNoCookie != cntHelloVerifyRequest {
106+
t.Errorf(
107+
"HelloVerifyRequest must be triggered only by ClientHello, but HelloVerifyRequest was sent %d times and ClientHello was sent %d times",
108+
cntHelloVerifyRequest, cntClientHelloNoCookie,
109+
)
110+
}
111+
}
112+
113+
return clientEndpoint, serverEndpoint, report
114+
},
115+
116+
"NoLatencyTest": func() (TestEndpoint, TestEndpoint, func(t *testing.T)) {
117+
var (
118+
cntClientFinished = 0
119+
cntServerFinished = 0
120+
)
121+
122+
clientEndpoint := TestEndpoint{
123+
Filter: func(p *packet) bool {
124+
h, ok := p.record.Content.(*handshake.Handshake)
125+
if !ok {
126+
return true
127+
}
128+
if _, ok := h.Message.(*handshake.MessageFinished); ok {
129+
cntClientFinished++
130+
}
131+
return true
132+
},
133+
}
134+
135+
serverEndpoint := TestEndpoint{
136+
Filter: func(p *packet) bool {
137+
h, ok := p.record.Content.(*handshake.Handshake)
138+
if !ok {
139+
return true
140+
}
141+
if _, ok := h.Message.(*handshake.MessageFinished); ok {
142+
cntServerFinished++
143+
}
144+
return true
145+
},
146+
}
147+
148+
report := func(t *testing.T) {
149+
if cntClientFinished != 1 {
150+
t.Errorf("Number of client finished is wrong, expected: %d times, got: %d times", 1, cntClientFinished)
151+
}
152+
if cntServerFinished != 1 {
153+
t.Errorf("Number of server finished is wrong, expected: %d times, got: %d times", 1, cntServerFinished)
154+
}
155+
}
156+
157+
return clientEndpoint, serverEndpoint, report
158+
},
159+
160+
"SlowServerTest": func() (TestEndpoint, TestEndpoint, func(t *testing.T)) {
161+
var (
162+
cntClientFinished = 0
163+
isClientFinished = false
164+
cntClientFinishedLastRetransmit = 0
165+
cntServerFinished = 0
166+
isServerFinished = false
167+
cntServerFinishedLastRetransmit = 0
168+
)
169+
170+
clientEndpoint := TestEndpoint{
171+
Filter: func(p *packet) bool {
172+
h, ok := p.record.Content.(*handshake.Handshake)
173+
if !ok {
174+
return true
175+
}
176+
if _, ok := h.Message.(*handshake.MessageFinished); ok {
177+
if isClientFinished {
178+
cntClientFinishedLastRetransmit++
179+
} else {
180+
cntClientFinished++
181+
}
182+
}
183+
return true
184+
},
185+
Delay: 0,
186+
OnFinished: func() {
187+
isClientFinished = true
188+
},
189+
FinishWait: 2000 * time.Millisecond,
190+
}
191+
192+
serverEndpoint := TestEndpoint{
193+
Filter: func(p *packet) bool {
194+
h, ok := p.record.Content.(*handshake.Handshake)
195+
if !ok {
196+
return true
96197
}
97-
if cntClientHelloNoCookie != cntHelloVerifyRequest {
98-
t.Errorf(
99-
"HelloVerifyRequest must be triggered only by ClientHello, but HelloVerifyRequest was sent %d times and ClientHello was sent %d times",
100-
cntHelloVerifyRequest, cntClientHelloNoCookie,
101-
)
198+
if _, ok := h.Message.(*handshake.MessageFinished); ok {
199+
if isServerFinished {
200+
cntServerFinishedLastRetransmit++
201+
} else {
202+
cntServerFinished++
203+
}
102204
}
205+
return true
206+
},
207+
Delay: 1000 * time.Millisecond,
208+
OnFinished: func() {
209+
isServerFinished = true
210+
},
211+
FinishWait: 2000 * time.Millisecond,
212+
}
213+
214+
report := func(t *testing.T) {
215+
// with one second server delay and 100 ms retransmit, there should be close to 10 `Finished` from client
216+
// using a range of 9 - 11 for checking
217+
if cntClientFinished < 8 || cntClientFinished > 11 {
218+
t.Errorf("Number of client finished is wrong, expected: %d - %d times, got: %d times", 9, 11, cntClientFinished)
219+
}
220+
if !isClientFinished {
221+
t.Errorf("Client is not finished")
103222
}
223+
// there should be no `Finished` last retransmit from client
224+
if cntClientFinishedLastRetransmit != 0 {
225+
t.Errorf("Number of client finished last retransmit is wrong, expected: %d times, got: %d times", 0, cntClientFinishedLastRetransmit)
226+
}
227+
if cntServerFinished < 1 {
228+
t.Errorf("Number of server finished is wrong, expected: at least %d times, got: %d times", 1, cntServerFinished)
229+
}
230+
if !isServerFinished {
231+
t.Errorf("Server is not finished")
232+
}
233+
// there should be `Finished` last retransmit from server. Because of slow server, client would have sent several `Finished`.
234+
if cntServerFinishedLastRetransmit < 1 {
235+
t.Errorf("Number of server finished last retransmit is wrong, expected: at least %d times, got: %d times", 1, cntServerFinishedLastRetransmit)
236+
}
237+
}
238+
239+
return clientEndpoint, serverEndpoint, report
104240
},
105241
}
106242

107243
for name, filters := range genFilters {
108-
f1, f2, report := filters()
244+
clientEndpoint, serverEndpoint, report := filters()
109245
t.Run(name, func(t *testing.T) {
110-
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
246+
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
111247
defer cancel()
112248

113249
if report != nil {
114250
defer report(t)
115251
}
116252

117-
ca, cb := flightTestPipe(ctx, f1, f2)
253+
ca, cb := flightTestPipe(ctx, clientEndpoint, serverEndpoint)
118254
ca.state.isClient = true
119255

120256
var wg sync.WaitGroup
@@ -132,7 +268,12 @@ func TestHandshaker(t *testing.T) {
132268
log: logger,
133269
onFlightState: func(f flightVal, s handshakeState) {
134270
if s == handshakeFinished {
135-
cancelCli()
271+
if clientEndpoint.OnFinished != nil {
272+
clientEndpoint.OnFinished()
273+
}
274+
time.AfterFunc(clientEndpoint.FinishWait, func() {
275+
cancelCli()
276+
})
136277
}
137278
},
138279
retransmitInterval: nonZeroRetransmitInterval,
@@ -158,7 +299,12 @@ func TestHandshaker(t *testing.T) {
158299
log: logger,
159300
onFlightState: func(f flightVal, s handshakeState) {
160301
if s == handshakeFinished {
161-
cancelSrv()
302+
if serverEndpoint.OnFinished != nil {
303+
serverEndpoint.OnFinished()
304+
}
305+
time.AfterFunc(serverEndpoint.FinishWait, func() {
306+
cancelSrv()
307+
})
162308
}
163309
},
164310
retransmitInterval: nonZeroRetransmitInterval,
@@ -183,9 +329,16 @@ func TestHandshaker(t *testing.T) {
183329
}
184330
}
185331

186-
type packetFilter func(*packet) bool
332+
type packetFilter func(p *packet) bool
333+
334+
type TestEndpoint struct {
335+
Filter packetFilter
336+
Delay time.Duration
337+
OnFinished func()
338+
FinishWait time.Duration
339+
}
187340

188-
func flightTestPipe(ctx context.Context, filter1 packetFilter, filter2 packetFilter) (*flightTestConn, *flightTestConn) {
341+
func flightTestPipe(ctx context.Context, clientEndpoint TestEndpoint, serverEndpoint TestEndpoint) (*flightTestConn, *flightTestConn) {
189342
ca := newHandshakeCache()
190343
cb := newHandshakeCache()
191344
chA := make(chan chan struct{})
@@ -196,14 +349,16 @@ func flightTestPipe(ctx context.Context, filter1 packetFilter, filter2 packetFil
196349
recv: chA,
197350
otherEndRecv: chB,
198351
done: ctx.Done(),
199-
filter: filter1,
352+
filter: clientEndpoint.Filter,
353+
delay: clientEndpoint.Delay,
200354
}, &flightTestConn{
201355
handshakeCache: cb,
202356
otherEndCache: ca,
203357
recv: chB,
204358
otherEndRecv: chA,
205359
done: ctx.Done(),
206-
filter: filter2,
360+
filter: serverEndpoint.Filter,
361+
delay: serverEndpoint.Delay,
207362
}
208363
}
209364

@@ -216,6 +371,8 @@ type flightTestConn struct {
216371

217372
filter packetFilter
218373

374+
delay time.Duration
375+
219376
otherEndCache *handshakeCache
220377
otherEndRecv chan chan struct{}
221378
}
@@ -233,6 +390,7 @@ func (c *flightTestConn) notify(ctx context.Context, level alert.Level, desc ale
233390
}
234391

235392
func (c *flightTestConn) writePackets(ctx context.Context, pkts []*packet) error {
393+
time.Sleep(c.delay)
236394
for _, p := range pkts {
237395
if c.filter != nil && !c.filter(p) {
238396
continue

0 commit comments

Comments
 (0)