Skip to content

Commit d013d0c

Browse files
committedJul 15, 2024·
On Read Retransmit send FSM to SENDING
RFC6347 Section-4.2.4 states ``` The implementation reads a retransmitted flight from the peer: the implementation transitions to the SENDING state, where it retransmits the flight, resets the retransmit timer, and returns to the WAITING state. The rationale here is that the receipt of a duplicate message is the likely result of timer expiry on the peer and therefore suggests that part of one's previous flight was lost. ``` Resolves #478
1 parent ec76652 commit d013d0c

9 files changed

+119
-55
lines changed
 

‎conn.go

+41-29
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ type addrPkt struct {
5454
data []byte
5555
}
5656

57+
type recvHandshakeState struct {
58+
done chan struct{}
59+
isRetransmit bool
60+
}
61+
5762
// Conn represents a DTLS connection
5863
type Conn struct {
5964
lock sync.RWMutex // Internal lock (must not be public)
@@ -82,7 +87,7 @@ type Conn struct {
8287
log logging.LeveledLogger
8388

8489
reading chan struct{}
85-
handshakeRecv chan chan struct{}
90+
handshakeRecv chan recvHandshakeState
8691
cancelHandshaker func()
8792
cancelHandshakeReader func()
8893

@@ -137,7 +142,7 @@ func createConn(nextConn net.PacketConn, rAddr net.Addr, config *Config, isClien
137142
writeDeadline: deadline.New(),
138143

139144
reading: make(chan struct{}, 1),
140-
handshakeRecv: make(chan chan struct{}),
145+
handshakeRecv: make(chan recvHandshakeState),
141146
closed: closer.NewCloser(),
142147
cancelHandshaker: func() {},
143148

@@ -704,9 +709,9 @@ func (c *Conn) readAndBuffer(ctx context.Context) error {
704709
return err
705710
}
706711

707-
var hasHandshake bool
712+
var hasHandshake, isRetransmit bool
708713
for _, p := range pkts {
709-
hs, alert, err := c.handleIncomingPacket(ctx, p, rAddr, true)
714+
hs, rtx, alert, err := c.handleIncomingPacket(ctx, p, rAddr, true)
710715
if alert != nil {
711716
if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil {
712717
if err == nil {
@@ -725,14 +730,20 @@ func (c *Conn) readAndBuffer(ctx context.Context) error {
725730
if hs {
726731
hasHandshake = true
727732
}
733+
if rtx {
734+
isRetransmit = true
735+
}
728736
}
729737
if hasHandshake {
730-
done := make(chan struct{})
738+
s := recvHandshakeState{
739+
done: make(chan struct{}),
740+
isRetransmit: isRetransmit,
741+
}
731742
select {
732-
case c.handshakeRecv <- done:
743+
case c.handshakeRecv <- s:
733744
// If the other party may retransmit the flight,
734745
// we should respond even if it not a new message.
735-
<-done
746+
<-s.done
736747
case <-c.fsm.Done():
737748
}
738749
}
@@ -744,7 +755,7 @@ func (c *Conn) handleQueuedPackets(ctx context.Context) error {
744755
c.encryptedPackets = nil
745756

746757
for _, p := range pkts {
747-
_, alert, err := c.handleIncomingPacket(ctx, p.data, p.rAddr, false) // don't re-enqueue
758+
_, _, alert, err := c.handleIncomingPacket(ctx, p.data, p.rAddr, false) // don't re-enqueue
748759
if alert != nil {
749760
if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil {
750761
if err == nil {
@@ -771,7 +782,7 @@ func (c *Conn) enqueueEncryptedPackets(packet addrPkt) bool {
771782
return false
772783
}
773784

774-
func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, rAddr net.Addr, enqueue bool) (bool, *alert.Alert, error) { //nolint:gocognit
785+
func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, rAddr net.Addr, enqueue bool) (bool, bool, *alert.Alert, error) { //nolint:gocognit
775786
h := &recordlayer.Header{}
776787
// Set connection ID size so that records of content type tls12_cid will
777788
// be parsed correctly.
@@ -782,7 +793,7 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, rAddr net.A
782793
// Decode error must be silently discarded
783794
// [RFC6347 Section-4.1.2.7]
784795
c.log.Debugf("discarded broken packet: %v", err)
785-
return false, nil, nil
796+
return false, false, nil, nil
786797
}
787798
// Validate epoch
788799
remoteEpoch := c.state.getRemoteEpoch()
@@ -791,14 +802,14 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, rAddr net.A
791802
c.log.Debugf("discarded future packet (epoch: %d, seq: %d)",
792803
h.Epoch, h.SequenceNumber,
793804
)
794-
return false, nil, nil
805+
return false, false, nil, nil
795806
}
796807
if enqueue {
797808
if ok := c.enqueueEncryptedPackets(addrPkt{rAddr, buf}); ok {
798809
c.log.Debug("received packet of next epoch, queuing packet")
799810
}
800811
}
801-
return false, nil, nil
812+
return false, false, nil, nil
802813
}
803814

804815
// Anti-replay protection
@@ -812,7 +823,7 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, rAddr net.A
812823
c.log.Debugf("discarded duplicated packet (epoch: %d, seq: %d)",
813824
h.Epoch, h.SequenceNumber,
814825
)
815-
return false, nil, nil
826+
return false, false, nil, nil
816827
}
817828

818829
// originalCID indicates whether the original record had content type
@@ -827,14 +838,14 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, rAddr net.A
827838
c.log.Debug("handshake not finished, queuing packet")
828839
}
829840
}
830-
return false, nil, nil
841+
return false, false, nil, nil
831842
}
832843

833844
// If a connection identifier had been negotiated and encryption is
834845
// enabled, the connection identifier MUST be sent.
835846
if len(c.state.getLocalConnectionID()) > 0 && h.ContentType != protocol.ContentTypeConnectionID {
836847
c.log.Debug("discarded packet missing connection ID after value negotiated")
837-
return false, nil, nil
848+
return false, false, nil, nil
838849
}
839850

840851
var err error
@@ -845,7 +856,7 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, rAddr net.A
845856
buf, err = c.state.cipherSuite.Decrypt(hdr, buf)
846857
if err != nil {
847858
c.log.Debugf("%s: decrypt failed: %s", srvCliStr(c.state.isClient), err)
848-
return false, nil, nil
859+
return false, false, nil, nil
849860
}
850861
// If this is a connection ID record, make it look like a normal record for
851862
// further processing.
@@ -854,7 +865,7 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, rAddr net.A
854865
ip := &recordlayer.InnerPlaintext{}
855866
if err := ip.Unmarshal(buf[h.Size():]); err != nil { //nolint:govet
856867
c.log.Debugf("unpacking inner plaintext failed: %s", err)
857-
return false, nil, nil
868+
return false, false, nil, nil
858869
}
859870
unpacked := &recordlayer.Header{
860871
ContentType: ip.RealType,
@@ -866,26 +877,27 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, rAddr net.A
866877
buf, err = unpacked.Marshal()
867878
if err != nil {
868879
c.log.Debugf("converting CID record to inner plaintext failed: %s", err)
869-
return false, nil, nil
880+
return false, false, nil, nil
870881
}
871882
buf = append(buf, ip.Content...)
872883
}
873884

874885
// If connection ID does not match discard the packet.
875886
if !bytes.Equal(c.state.getLocalConnectionID(), h.ConnectionID) {
876887
c.log.Debug("unexpected connection ID")
877-
return false, nil, nil
888+
return false, false, nil, nil
878889
}
879890
}
880891

881-
isHandshake, err := c.fragmentBuffer.push(append([]byte{}, buf...))
892+
isHandshake, isRetransmit, err := c.fragmentBuffer.push(append([]byte{}, buf...))
882893
if err != nil {
883894
// Decode error must be silently discarded
884895
// [RFC6347 Section-4.1.2.7]
885896
c.log.Debugf("defragment failed: %s", err)
886-
return false, nil, nil
897+
return false, false, nil, nil
887898
} else if isHandshake {
888899
markPacketAsValid()
900+
889901
for out, epoch := c.fragmentBuffer.pop(); out != nil; out, epoch = c.fragmentBuffer.pop() {
890902
header := &handshake.Header{}
891903
if err := header.Unmarshal(out); err != nil {
@@ -895,12 +907,12 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, rAddr net.A
895907
c.handshakeCache.push(out, epoch, header.MessageSequence, header.Type, !c.state.isClient)
896908
}
897909

898-
return true, nil, nil
910+
return true, isRetransmit, nil, nil
899911
}
900912

901913
r := &recordlayer.RecordLayer{}
902914
if err := r.Unmarshal(buf); err != nil {
903-
return false, &alert.Alert{Level: alert.Fatal, Description: alert.DecodeError}, err
915+
return false, false, &alert.Alert{Level: alert.Fatal, Description: alert.DecodeError}, err
904916
}
905917

906918
isLatestSeqNum := false
@@ -913,15 +925,15 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, rAddr net.A
913925
a = &alert.Alert{Level: alert.Warning, Description: alert.CloseNotify}
914926
}
915927
_ = markPacketAsValid()
916-
return false, a, &alertError{content}
928+
return false, false, a, &alertError{content}
917929
case *protocol.ChangeCipherSpec:
918930
if c.state.cipherSuite == nil || !c.state.cipherSuite.IsInitialized() {
919931
if enqueue {
920932
if ok := c.enqueueEncryptedPackets(addrPkt{rAddr, buf}); ok {
921933
c.log.Debugf("CipherSuite not initialized, queuing packet")
922934
}
923935
}
924-
return false, nil, nil
936+
return false, false, nil, nil
925937
}
926938

927939
newRemoteEpoch := h.Epoch + 1
@@ -933,7 +945,7 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, rAddr net.A
933945
}
934946
case *protocol.ApplicationData:
935947
if h.Epoch == 0 {
936-
return false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, errApplicationDataEpochZero
948+
return false, false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, errApplicationDataEpochZero
937949
}
938950

939951
isLatestSeqNum = markPacketAsValid()
@@ -945,7 +957,7 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, rAddr net.A
945957
}
946958

947959
default:
948-
return false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, fmt.Errorf("%w: %d", errUnhandledContextType, content.ContentType())
960+
return false, false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, fmt.Errorf("%w: %d", errUnhandledContextType, content.ContentType())
949961
}
950962

951963
// Any valid connection ID record is a candidate for updating the remote
@@ -959,10 +971,10 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, rAddr net.A
959971
}
960972
}
961973

962-
return false, nil, nil
974+
return false, false, nil, nil
963975
}
964976

965-
func (c *Conn) recvHandshake() <-chan chan struct{} {
977+
func (c *Conn) recvHandshake() <-chan recvHandshakeState {
966978
return c.handshakeRecv
967979
}
968980

‎conn_test.go

+21
Original file line numberDiff line numberDiff line change
@@ -3482,3 +3482,24 @@ func TestOnConnectionAttempt(t *testing.T) {
34823482
t.Fatal("OnConnectionAttempt fired for client")
34833483
}
34843484
}
3485+
3486+
func TestFragmentBuffer_Retransmission(t *testing.T) {
3487+
fragmentBuffer := newFragmentBuffer()
3488+
frag := []byte{0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x30, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xfe, 0xff, 0x01, 0x01}
3489+
3490+
if _, isRetransmission, err := fragmentBuffer.push(frag); err != nil {
3491+
t.Fatal(err)
3492+
} else if isRetransmission {
3493+
t.Fatal("fragment should not be retransmission")
3494+
}
3495+
3496+
if v, _ := fragmentBuffer.pop(); v == nil {
3497+
t.Fatal("Failed to pop fragment")
3498+
}
3499+
3500+
if _, isRetransmission, err := fragmentBuffer.push(frag); err != nil {
3501+
t.Fatal(err)
3502+
} else if !isRetransmission {
3503+
t.Fatal("fragment should be retransmission")
3504+
}
3505+
}

‎e2e/e2e_lossy_test.go

+27-4
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@ func TestPionE2ELossy(t *testing.T) {
4545
}
4646

4747
for _, test := range []struct {
48-
LossChanceRange int
49-
DoClientAuth bool
50-
CipherSuites []dtls.CipherSuiteID
51-
MTU int
48+
LossChanceRange int
49+
DoClientAuth bool
50+
CipherSuites []dtls.CipherSuiteID
51+
MTU int
52+
DisableServerFlightInterval bool
5253
}{
5354
{
5455
LossChanceRange: 0,
@@ -109,6 +110,20 @@ func TestPionE2ELossy(t *testing.T) {
109110
MTU: 100,
110111
DoClientAuth: true,
111112
},
113+
// Incoming retransmitted handshakes should cause us to retransmit. Disabling the FlightInterval on one side
114+
// means that a incoming re-transmissions causes the retransmission to be fired
115+
{
116+
LossChanceRange: 10,
117+
DisableServerFlightInterval: true,
118+
},
119+
{
120+
LossChanceRange: 20,
121+
DisableServerFlightInterval: true,
122+
},
123+
{
124+
LossChanceRange: 50,
125+
DisableServerFlightInterval: true,
126+
},
112127
} {
113128
name := fmt.Sprintf("Loss%d_MTU%d", test.LossChanceRange, test.MTU)
114129
if test.DoClientAuth {
@@ -117,6 +132,10 @@ func TestPionE2ELossy(t *testing.T) {
117132
for _, ciph := range test.CipherSuites {
118133
name += "_With" + ciph.String()
119134
}
135+
if test.DisableServerFlightInterval {
136+
name += "_WithNoServerFlightInterval"
137+
}
138+
120139
test := test
121140
t.Run(name, func(t *testing.T) {
122141
// Limit runtime in case of deadlocks
@@ -162,6 +181,10 @@ func TestPionE2ELossy(t *testing.T) {
162181
cfg.ClientAuth = dtls.RequireAnyClientCert
163182
}
164183

184+
if test.DisableServerFlightInterval {
185+
cfg.FlightInterval = time.Hour
186+
}
187+
165188
server, startupErr := dtls.Server(dtlsnet.PacketConnFromConn(br.GetConn1()), br.GetConn1().RemoteAddr(), cfg)
166189
serverDone <- runResult{server, startupErr}
167190
}()

‎flight1handler_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func (f *flight1TestMockFlightConn) notify(context.Context, alert.Level, alert.D
2121
return nil
2222
}
2323
func (f *flight1TestMockFlightConn) writePackets(context.Context, []*packet) error { return nil }
24-
func (f *flight1TestMockFlightConn) recvHandshake() <-chan chan struct{} { return nil }
24+
func (f *flight1TestMockFlightConn) recvHandshake() <-chan recvHandshakeState { return nil }
2525
func (f *flight1TestMockFlightConn) setLocalEpoch(uint16) {}
2626
func (f *flight1TestMockFlightConn) handleQueuedPackets(context.Context) error { return nil }
2727
func (f *flight1TestMockFlightConn) sessionKey() []byte { return nil }

‎flight4handler_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func (f *flight4TestMockFlightConn) notify(context.Context, alert.Level, alert.D
2727
return nil
2828
}
2929
func (f *flight4TestMockFlightConn) writePackets(context.Context, []*packet) error { return nil }
30-
func (f *flight4TestMockFlightConn) recvHandshake() <-chan chan struct{} { return nil }
30+
func (f *flight4TestMockFlightConn) recvHandshake() <-chan recvHandshakeState { return nil }
3131
func (f *flight4TestMockFlightConn) setLocalEpoch(uint16) {}
3232
func (f *flight4TestMockFlightConn) handleQueuedPackets(context.Context) error { return nil }
3333
func (f *flight4TestMockFlightConn) sessionKey() []byte { return nil }

‎fragment_buffer.go

+9-6
Original file line numberDiff line numberDiff line change
@@ -43,26 +43,29 @@ func (f *fragmentBuffer) size() int {
4343
// Attempts to push a DTLS packet to the fragmentBuffer
4444
// when it returns true it means the fragmentBuffer has inserted and the buffer shouldn't be handled
4545
// when an error returns it is fatal, and the DTLS connection should be stopped
46-
func (f *fragmentBuffer) push(buf []byte) (bool, error) {
46+
func (f *fragmentBuffer) push(buf []byte) (isHandshake, isRetransmit bool, err error) {
4747
if f.size()+len(buf) >= fragmentBufferMaxSize {
48-
return false, errFragmentBufferOverflow
48+
return false, false, errFragmentBufferOverflow
4949
}
5050

5151
frag := new(fragment)
5252
if err := frag.recordLayerHeader.Unmarshal(buf); err != nil {
53-
return false, err
53+
return false, false, err
5454
}
5555

5656
// fragment isn't a handshake, we don't need to handle it
5757
if frag.recordLayerHeader.ContentType != protocol.ContentTypeHandshake {
58-
return false, nil
58+
return false, false, nil
5959
}
6060

6161
for buf = buf[recordlayer.FixedHeaderSize:]; len(buf) != 0; frag = new(fragment) {
6262
if err := frag.handshakeHeader.Unmarshal(buf); err != nil {
63-
return false, err
63+
return false, false, err
6464
}
6565

66+
// Fragment is a retransmission. We have already assembled it before successfully
67+
isRetransmit = frag.handshakeHeader.FragmentOffset == 0 && frag.handshakeHeader.MessageSequence < f.currentMessageSequenceNumber
68+
6669
if _, ok := f.cache[frag.handshakeHeader.MessageSequence]; !ok {
6770
f.cache[frag.handshakeHeader.MessageSequence] = []*fragment{}
6871
}
@@ -80,7 +83,7 @@ func (f *fragmentBuffer) push(buf []byte) (bool, error) {
8083
buf = buf[end:]
8184
}
8285

83-
return true, nil
86+
return true, isRetransmit, nil
8487
}
8588

8689
func (f *fragmentBuffer) pop() (content []byte, epoch uint16) {

‎fragment_buffer_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func TestFragmentBuffer(t *testing.T) {
9494
} {
9595
fragmentBuffer := newFragmentBuffer()
9696
for _, frag := range test.In {
97-
status, err := fragmentBuffer.push(frag)
97+
status, _, err := fragmentBuffer.push(frag)
9898
if err != nil {
9999
t.Error(err)
100100
} else if !status {
@@ -122,13 +122,13 @@ func TestFragmentBuffer_Overflow(t *testing.T) {
122122
fragmentBuffer := newFragmentBuffer()
123123

124124
// Push a buffer that doesn't exceed size limits
125-
if _, err := fragmentBuffer.push([]byte{0x16, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfe, 0xff, 0x00}); err != nil {
125+
if _, _, err := fragmentBuffer.push([]byte{0x16, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfe, 0xff, 0x00}); err != nil {
126126
t.Fatal(err)
127127
}
128128

129129
// Allocate a buffer that exceeds cache size
130130
largeBuffer := make([]byte, fragmentBufferMaxSize)
131-
if _, err := fragmentBuffer.push(largeBuffer); !errors.Is(err, errFragmentBufferOverflow) {
131+
if _, _, err := fragmentBuffer.push(largeBuffer); !errors.Is(err, errFragmentBufferOverflow) {
132132
t.Fatalf("Pushing a large buffer returned (%s) expected(%s)", err, errFragmentBufferOverflow)
133133
}
134134
}

‎handshaker.go

+10-5
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ type handshakeConfig struct {
137137
type flightConn interface {
138138
notify(ctx context.Context, level alert.Level, desc alert.Description) error
139139
writePackets(context.Context, []*packet) error
140-
recvHandshake() <-chan chan struct{}
140+
recvHandshake() <-chan recvHandshakeState
141141
setLocalEpoch(epoch uint16)
142142
handleQueuedPackets(context.Context) error
143143
sessionKey() []byte
@@ -280,10 +280,15 @@ func (s *handshakeFSM) wait(ctx context.Context, c flightConn) (handshakeState,
280280
retransmitTimer := time.NewTimer(s.retransmitInterval)
281281
for {
282282
select {
283-
case done := <-c.recvHandshake():
283+
case state := <-c.recvHandshake():
284+
if state.isRetransmit {
285+
close(state.done)
286+
return handshakeSending, nil
287+
}
288+
284289
nextFlight, alert, err := parse(ctx, c, s.state, s.cache, s.cfg)
285290
s.retransmitInterval = s.cfg.initialRetransmitInterval
286-
close(done)
291+
close(state.done)
287292
if alert != nil {
288293
if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil {
289294
if err != nil {
@@ -328,8 +333,8 @@ func (s *handshakeFSM) wait(ctx context.Context, c flightConn) (handshakeState,
328333

329334
func (s *handshakeFSM) finish(ctx context.Context, c flightConn) (handshakeState, error) {
330335
select {
331-
case done := <-c.recvHandshake():
332-
close(done)
336+
case state := <-c.recvHandshake():
337+
close(state.done)
333338
return handshakeSending, nil
334339
case <-ctx.Done():
335340
return handshakeErrored, ctx.Err()

‎handshaker_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -349,8 +349,8 @@ type TestEndpoint struct {
349349
func flightTestPipe(ctx context.Context, clientEndpoint TestEndpoint, serverEndpoint TestEndpoint) (*flightTestConn, *flightTestConn) {
350350
ca := newHandshakeCache()
351351
cb := newHandshakeCache()
352-
chA := make(chan chan struct{})
353-
chB := make(chan chan struct{})
352+
chA := make(chan recvHandshakeState)
353+
chB := make(chan recvHandshakeState)
354354
return &flightTestConn{
355355
handshakeCache: ca,
356356
otherEndCache: cb,
@@ -373,7 +373,7 @@ func flightTestPipe(ctx context.Context, clientEndpoint TestEndpoint, serverEndp
373373
type flightTestConn struct {
374374
state State
375375
handshakeCache *handshakeCache
376-
recv chan chan struct{}
376+
recv chan recvHandshakeState
377377
done <-chan struct{}
378378
epoch uint16
379379

@@ -382,10 +382,10 @@ type flightTestConn struct {
382382
delay time.Duration
383383

384384
otherEndCache *handshakeCache
385-
otherEndRecv chan chan struct{}
385+
otherEndRecv chan recvHandshakeState
386386
}
387387

388-
func (c *flightTestConn) recvHandshake() <-chan chan struct{} {
388+
func (c *flightTestConn) recvHandshake() <-chan recvHandshakeState {
389389
return c.recv
390390
}
391391

@@ -427,7 +427,7 @@ func (c *flightTestConn) writePackets(_ context.Context, pkts []*packet) error {
427427
}
428428
go func() {
429429
select {
430-
case c.otherEndRecv <- make(chan struct{}):
430+
case c.otherEndRecv <- recvHandshakeState{done: make(chan struct{})}:
431431
case <-c.done:
432432
}
433433
}()

0 commit comments

Comments
 (0)
Please sign in to comment.