forked from quic-go/quic-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
session.go
1283 lines (1155 loc) · 39.1 KB
/
session.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package quic
import (
"context"
"crypto/rand"
"crypto/tls"
"errors"
"fmt"
"net"
"sync"
"time"
"github.com/bifurcation/mint"
"github.com/lucas-clemente/quic-go/internal/ackhandler"
"github.com/lucas-clemente/quic-go/internal/congestion"
"github.com/lucas-clemente/quic-go/internal/flowcontrol"
"github.com/lucas-clemente/quic-go/internal/handshake"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/internal/wire"
"github.com/lucas-clemente/quic-go/qerr"
)
type unpacker interface {
Unpack(headerBinary []byte, hdr *wire.Header, data []byte) (*unpackedPacket, error)
}
type streamGetter interface {
GetOrOpenReceiveStream(protocol.StreamID) (receiveStreamI, error)
GetOrOpenSendStream(protocol.StreamID) (sendStreamI, error)
}
type streamManager interface {
GetOrOpenSendStream(protocol.StreamID) (sendStreamI, error)
GetOrOpenReceiveStream(protocol.StreamID) (receiveStreamI, error)
OpenStream() (Stream, error)
OpenUniStream() (SendStream, error)
OpenStreamSync() (Stream, error)
OpenUniStreamSync() (SendStream, error)
AcceptStream() (Stream, error)
AcceptUniStream() (ReceiveStream, error)
DeleteStream(protocol.StreamID) error
UpdateLimits(*handshake.TransportParameters)
HandleMaxStreamIDFrame(*wire.MaxStreamIDFrame) error
CloseWithError(error)
}
type cryptoStreamHandler interface {
HandleCryptoStream() error
ConnectionState() handshake.ConnectionState
}
type divNonceSetter interface {
SetDiversificationNonce([]byte) error
}
type receivedPacket struct {
remoteAddr net.Addr
header *wire.Header
data []byte
rcvTime time.Time
}
var (
newCryptoSetup = handshake.NewCryptoSetup
newCryptoSetupClient = handshake.NewCryptoSetupClient
)
type closeError struct {
err error
remote bool
sendClose bool
}
// A Session is a QUIC session
type session struct {
sessionRunner sessionRunner
destConnID protocol.ConnectionID
srcConnID protocol.ConnectionID
perspective protocol.Perspective
version protocol.VersionNumber
config *Config
conn connection
streamsMap streamManager
cryptoStream cryptoStream
rttStats *congestion.RTTStats
sentPacketHandler ackhandler.SentPacketHandler
receivedPacketHandler ackhandler.ReceivedPacketHandler
streamFramer *streamFramer
windowUpdateQueue *windowUpdateQueue
connFlowController flowcontrol.ConnectionFlowController
unpacker unpacker
packer *packetPacker
cryptoStreamHandler cryptoStreamHandler
receivedPackets chan *receivedPacket
sendingScheduled chan struct{}
// closeChan is used to notify the run loop that it should terminate.
closeChan chan closeError
closeOnce sync.Once
ctx context.Context
ctxCancel context.CancelFunc
// when we receive too many undecryptable packets during the handshake, we send a Public reset
// but only after a time of protocol.PublicResetTimeout has passed
undecryptablePackets []*receivedPacket
receivedTooManyUndecrytablePacketsTime time.Time
// this channel is passed to the CryptoSetup and receives the transport parameters, as soon as the peer sends them
paramsChan <-chan handshake.TransportParameters
// the handshakeEvent channel is passed to the CryptoSetup.
// It receives when it makes sense to try decrypting undecryptable packets.
handshakeEvent <-chan struct{}
handshakeComplete bool
receivedFirstPacket bool // since packet numbers start at 0, we can't use largestRcvdPacketNumber != 0 for this
receivedFirstForwardSecurePacket bool
lastRcvdPacketNumber protocol.PacketNumber
// Used to calculate the next packet number from the truncated wire
// representation, and sent back in public reset packets
largestRcvdPacketNumber protocol.PacketNumber
sessionCreationTime time.Time
lastNetworkActivityTime time.Time
// pacingDeadline is the time when the next packet should be sent
pacingDeadline time.Time
peerParams *handshake.TransportParameters
timer *utils.Timer
// keepAlivePingSent stores whether a Ping frame was sent to the peer or not
// it is reset as soon as we receive a packet from the peer
keepAlivePingSent bool
logger utils.Logger
}
var _ Session = &session{}
var _ streamSender = &session{}
// newSession makes a new session
func newSession(
conn connection,
sessionRunner sessionRunner,
v protocol.VersionNumber,
destConnID protocol.ConnectionID,
srcConnID protocol.ConnectionID,
scfg *handshake.ServerConfig,
tlsConf *tls.Config,
config *Config,
logger utils.Logger,
) (quicSession, error) {
logger.Debugf("Creating new session. Destination Connection ID: %s, Source Connection ID: %s", destConnID, srcConnID)
paramsChan := make(chan handshake.TransportParameters)
handshakeEvent := make(chan struct{}, 1)
s := &session{
conn: conn,
sessionRunner: sessionRunner,
srcConnID: srcConnID,
destConnID: destConnID,
perspective: protocol.PerspectiveServer,
version: v,
config: config,
handshakeEvent: handshakeEvent,
paramsChan: paramsChan,
logger: logger,
}
s.preSetup()
transportParams := &handshake.TransportParameters{
StreamFlowControlWindow: protocol.ReceiveStreamFlowControlWindow,
ConnectionFlowControlWindow: protocol.ReceiveConnectionFlowControlWindow,
MaxStreams: uint32(s.config.MaxIncomingStreams),
IdleTimeout: s.config.IdleTimeout,
}
divNonce := make([]byte, 32)
if _, err := rand.Read(divNonce); err != nil {
return nil, err
}
cs, err := newCryptoSetup(
s.cryptoStream,
srcConnID,
s.conn.RemoteAddr(),
s.version,
divNonce,
scfg,
transportParams,
s.config.Versions,
s.config.AcceptCookie,
paramsChan,
handshakeEvent,
s.logger,
)
if err != nil {
return nil, err
}
s.cryptoStreamHandler = cs
s.unpacker = newPacketUnpackerGQUIC(cs, s.version)
s.streamsMap = newStreamsMapLegacy(s.newStream, s.config.MaxIncomingStreams, s.perspective)
s.streamFramer = newStreamFramer(s.cryptoStream, s.streamsMap, s.version)
s.packer = newPacketPacker(
destConnID,
srcConnID,
1,
s.sentPacketHandler.GetPacketNumberLen,
s.RemoteAddr(),
nil, // no token
divNonce,
cs,
s.streamFramer,
s.perspective,
s.version,
)
return s, s.postSetup()
}
// declare this as a variable, so that we can it mock it in the tests
var newClientSession = func(
conn connection,
sessionRunner sessionRunner,
hostname string,
v protocol.VersionNumber,
destConnID protocol.ConnectionID,
srcConnID protocol.ConnectionID,
tlsConf *tls.Config,
config *Config,
initialVersion protocol.VersionNumber,
negotiatedVersions []protocol.VersionNumber, // needed for validation of the GQUIC version negotiation
logger utils.Logger,
) (quicSession, error) {
logger.Debugf("Creating new session. Destination Connection ID: %s, Source Connection ID: %s", destConnID, srcConnID)
paramsChan := make(chan handshake.TransportParameters)
handshakeEvent := make(chan struct{}, 1)
s := &session{
conn: conn,
sessionRunner: sessionRunner,
srcConnID: srcConnID,
destConnID: destConnID,
perspective: protocol.PerspectiveClient,
version: v,
config: config,
handshakeEvent: handshakeEvent,
paramsChan: paramsChan,
logger: logger,
}
s.preSetup()
transportParams := &handshake.TransportParameters{
StreamFlowControlWindow: protocol.ReceiveStreamFlowControlWindow,
ConnectionFlowControlWindow: protocol.ReceiveConnectionFlowControlWindow,
MaxStreams: uint32(s.config.MaxIncomingStreams),
IdleTimeout: s.config.IdleTimeout,
OmitConnectionID: s.config.RequestConnectionIDOmission,
}
cs, err := newCryptoSetupClient(
s.cryptoStream,
hostname,
destConnID,
s.version,
tlsConf,
transportParams,
paramsChan,
handshakeEvent,
initialVersion,
negotiatedVersions,
s.logger,
)
if err != nil {
return nil, err
}
s.cryptoStreamHandler = cs
s.unpacker = newPacketUnpackerGQUIC(cs, s.version)
s.streamsMap = newStreamsMapLegacy(s.newStream, s.config.MaxIncomingStreams, s.perspective)
s.streamFramer = newStreamFramer(s.cryptoStream, s.streamsMap, s.version)
s.packer = newPacketPacker(
destConnID,
srcConnID,
1,
s.sentPacketHandler.GetPacketNumberLen,
s.RemoteAddr(),
nil, // no token
nil, // no diversification nonce
cs,
s.streamFramer,
s.perspective,
s.version,
)
return s, s.postSetup()
}
func newTLSServerSession(
conn connection,
runner sessionRunner,
origConnID protocol.ConnectionID,
destConnID protocol.ConnectionID,
srcConnID protocol.ConnectionID,
initialPacketNumber protocol.PacketNumber,
config *Config,
mintConf *mint.Config,
peerParams *handshake.TransportParameters,
logger utils.Logger,
v protocol.VersionNumber,
) (quicSession, error) {
handshakeEvent := make(chan struct{}, 1)
s := &session{
conn: conn,
sessionRunner: runner,
config: config,
srcConnID: srcConnID,
destConnID: destConnID,
perspective: protocol.PerspectiveServer,
version: v,
handshakeEvent: handshakeEvent,
logger: logger,
}
s.preSetup()
cs, err := handshake.NewCryptoSetupTLSServer(
s.cryptoStream,
origConnID,
mintConf,
handshakeEvent,
v,
)
if err != nil {
return nil, err
}
s.cryptoStreamHandler = cs
s.streamsMap = newStreamsMap(s, s.newFlowController, s.config.MaxIncomingStreams, s.config.MaxIncomingUniStreams, s.perspective, s.version)
s.streamFramer = newStreamFramer(s.cryptoStream, s.streamsMap, s.version)
s.packer = newPacketPacker(
s.destConnID,
s.srcConnID,
initialPacketNumber,
s.sentPacketHandler.GetPacketNumberLen,
s.RemoteAddr(),
nil, // no token
nil, // no diversification nonce
cs,
s.streamFramer,
s.perspective,
s.version,
)
if err := s.postSetup(); err != nil {
return nil, err
}
s.peerParams = peerParams
s.processTransportParameters(peerParams)
s.unpacker = newPacketUnpacker(cs, s.version)
return s, nil
}
// declare this as a variable, such that we can it mock it in the tests
var newTLSClientSession = func(
conn connection,
runner sessionRunner,
token []byte,
destConnID protocol.ConnectionID,
srcConnID protocol.ConnectionID,
conf *Config,
mintConf *mint.Config,
paramsChan <-chan handshake.TransportParameters,
initialPacketNumber protocol.PacketNumber,
logger utils.Logger,
v protocol.VersionNumber,
) (quicSession, error) {
handshakeEvent := make(chan struct{}, 1)
s := &session{
conn: conn,
sessionRunner: runner,
config: conf,
srcConnID: srcConnID,
destConnID: destConnID,
perspective: protocol.PerspectiveClient,
version: v,
handshakeEvent: handshakeEvent,
paramsChan: paramsChan,
logger: logger,
}
s.preSetup()
cs, err := handshake.NewCryptoSetupTLSClient(
s.cryptoStream,
s.destConnID,
mintConf,
handshakeEvent,
v,
)
if err != nil {
return nil, err
}
s.cryptoStreamHandler = cs
s.unpacker = newPacketUnpacker(cs, s.version)
s.streamsMap = newStreamsMap(s, s.newFlowController, s.config.MaxIncomingStreams, s.config.MaxIncomingUniStreams, s.perspective, s.version)
s.streamFramer = newStreamFramer(s.cryptoStream, s.streamsMap, s.version)
s.packer = newPacketPacker(
s.destConnID,
s.srcConnID,
initialPacketNumber,
s.sentPacketHandler.GetPacketNumberLen,
s.RemoteAddr(),
token,
nil, // no diversification nonce
cs,
s.streamFramer,
s.perspective,
s.version,
)
return s, s.postSetup()
}
func (s *session) preSetup() {
s.rttStats = &congestion.RTTStats{}
s.sentPacketHandler = ackhandler.NewSentPacketHandler(s.rttStats, s.logger, s.version)
s.connFlowController = flowcontrol.NewConnectionFlowController(
protocol.ReceiveConnectionFlowControlWindow,
protocol.ByteCount(s.config.MaxReceiveConnectionFlowControlWindow),
s.onHasConnectionWindowUpdate,
s.rttStats,
s.logger,
)
s.cryptoStream = s.newCryptoStream()
}
func (s *session) postSetup() error {
s.receivedPackets = make(chan *receivedPacket, protocol.MaxSessionUnprocessedPackets)
s.closeChan = make(chan closeError, 1)
s.sendingScheduled = make(chan struct{}, 1)
s.undecryptablePackets = make([]*receivedPacket, 0, protocol.MaxUndecryptablePackets)
s.ctx, s.ctxCancel = context.WithCancel(context.Background())
s.timer = utils.NewTimer()
now := time.Now()
s.lastNetworkActivityTime = now
s.sessionCreationTime = now
s.receivedPacketHandler = ackhandler.NewReceivedPacketHandler(s.rttStats, s.logger, s.version)
s.windowUpdateQueue = newWindowUpdateQueue(s.streamsMap, s.cryptoStream, s.connFlowController, s.packer.QueueControlFrame)
return nil
}
// run the session main loop
func (s *session) run() error {
defer s.ctxCancel()
go func() {
if err := s.cryptoStreamHandler.HandleCryptoStream(); err != nil {
s.closeLocal(err)
}
}()
var closeErr closeError
runLoop:
for {
// Close immediately if requested
select {
case closeErr = <-s.closeChan:
break runLoop
case _, ok := <-s.handshakeEvent:
// when the handshake is completed, the channel will be closed
s.handleHandshakeEvent(!ok)
default:
}
s.maybeResetTimer()
select {
case closeErr = <-s.closeChan:
break runLoop
case <-s.timer.Chan():
s.timer.SetRead()
// We do all the interesting stuff after the switch statement, so
// nothing to see here.
case <-s.sendingScheduled:
// We do all the interesting stuff after the switch statement, so
// nothing to see here.
case p := <-s.receivedPackets:
err := s.handlePacketImpl(p)
if err != nil {
if qErr, ok := err.(*qerr.QuicError); ok && qErr.ErrorCode == qerr.DecryptionFailure {
s.tryQueueingUndecryptablePacket(p)
continue
}
s.closeLocal(err)
continue
}
// This is a bit unclean, but works properly, since the packet always
// begins with the public header and we never copy it.
putPacketBuffer(&p.header.Raw)
case p := <-s.paramsChan:
s.processTransportParameters(&p)
case _, ok := <-s.handshakeEvent:
// when the handshake is completed, the channel will be closed
s.handleHandshakeEvent(!ok)
}
now := time.Now()
if timeout := s.sentPacketHandler.GetAlarmTimeout(); !timeout.IsZero() && timeout.Before(now) {
// This could cause packets to be retransmitted.
// Check it before trying to send packets.
if err := s.sentPacketHandler.OnAlarm(); err != nil {
s.closeLocal(err)
}
}
var pacingDeadline time.Time
if s.pacingDeadline.IsZero() { // the timer didn't have a pacing deadline set
pacingDeadline = s.sentPacketHandler.TimeUntilSend()
}
if s.config.KeepAlive && !s.keepAlivePingSent && s.handshakeComplete && time.Since(s.lastNetworkActivityTime) >= s.peerParams.IdleTimeout/2 {
// send a PING frame since there is no activity in the session
s.logger.Debugf("Sending a keep-alive ping to keep the connection alive.")
s.packer.QueueControlFrame(&wire.PingFrame{})
s.keepAlivePingSent = true
} else if !pacingDeadline.IsZero() && now.Before(pacingDeadline) {
// If we get to this point before the pacing deadline, we should wait until that deadline.
// This can happen when scheduleSending is called, or a packet is received.
// Set the timer and restart the run loop.
s.pacingDeadline = pacingDeadline
continue
}
if err := s.sendPackets(); err != nil {
s.closeLocal(err)
}
if !s.receivedTooManyUndecrytablePacketsTime.IsZero() && s.receivedTooManyUndecrytablePacketsTime.Add(protocol.PublicResetTimeout).Before(now) && len(s.undecryptablePackets) != 0 {
s.closeLocal(qerr.Error(qerr.DecryptionFailure, "too many undecryptable packets received"))
}
if !s.handshakeComplete && now.Sub(s.sessionCreationTime) >= s.config.HandshakeTimeout {
s.closeLocal(qerr.Error(qerr.HandshakeTimeout, "Crypto handshake did not complete in time."))
}
if s.handshakeComplete && now.Sub(s.lastNetworkActivityTime) >= s.config.IdleTimeout {
s.closeLocal(qerr.Error(qerr.NetworkIdleTimeout, "No recent network activity."))
}
}
if err := s.handleCloseError(closeErr); err != nil {
s.logger.Infof("Handling close error failed: %s", err)
}
s.logger.Infof("Connection %s closed.", s.srcConnID)
s.sessionRunner.removeConnectionID(s.srcConnID)
return closeErr.err
}
func (s *session) Context() context.Context {
return s.ctx
}
func (s *session) ConnectionState() ConnectionState {
return s.cryptoStreamHandler.ConnectionState()
}
func (s *session) maybeResetTimer() {
var deadline time.Time
if s.config.KeepAlive && s.handshakeComplete && !s.keepAlivePingSent {
deadline = s.lastNetworkActivityTime.Add(s.peerParams.IdleTimeout / 2)
} else {
deadline = s.lastNetworkActivityTime.Add(s.config.IdleTimeout)
}
if ackAlarm := s.receivedPacketHandler.GetAlarmTimeout(); !ackAlarm.IsZero() {
deadline = utils.MinTime(deadline, ackAlarm)
}
if lossTime := s.sentPacketHandler.GetAlarmTimeout(); !lossTime.IsZero() {
deadline = utils.MinTime(deadline, lossTime)
}
if !s.handshakeComplete {
handshakeDeadline := s.sessionCreationTime.Add(s.config.HandshakeTimeout)
deadline = utils.MinTime(deadline, handshakeDeadline)
}
if !s.receivedTooManyUndecrytablePacketsTime.IsZero() {
deadline = utils.MinTime(deadline, s.receivedTooManyUndecrytablePacketsTime.Add(protocol.PublicResetTimeout))
}
if !s.pacingDeadline.IsZero() {
deadline = utils.MinTime(deadline, s.pacingDeadline)
}
s.timer.Reset(deadline)
}
func (s *session) handleHandshakeEvent(completed bool) {
if !completed {
s.tryDecryptingQueuedPackets()
return
}
s.handshakeComplete = true
s.handshakeEvent = nil // prevent this case from ever being selected again
s.sessionRunner.onHandshakeComplete(s)
// In gQUIC, the server completes the handshake first (after sending the SHLO).
// In TLS 1.3, the client completes the handshake first (after sending the CFIN).
// We need to make sure they learn about the peer completing the handshake,
// in order to stop retransmitting handshake packets.
// They will stop retransmitting handshake packets when receiving the first forward-secure packet.
// We need to make sure that a retransmittable forward-secure packet is sent,
// independent from the application protocol.
if (!s.version.UsesTLS() && s.perspective == protocol.PerspectiveClient) ||
(s.version.UsesTLS() && s.perspective == protocol.PerspectiveServer) {
s.queueControlFrame(&wire.PingFrame{})
s.sentPacketHandler.SetHandshakeComplete()
}
}
func (s *session) handlePacketImpl(p *receivedPacket) error {
hdr := p.header
// The server can change the source connection ID with the first Handshake packet.
// After this, all packets with a different source connection have to be ignored.
if s.receivedFirstPacket && hdr.IsLongHeader && !hdr.SrcConnectionID.Equal(s.destConnID) {
s.logger.Debugf("Dropping packet with unexpected source connection ID: %s (expected %s)", p.header.SrcConnectionID, s.destConnID)
return nil
}
if s.perspective == protocol.PerspectiveClient {
if divNonce := p.header.DiversificationNonce; len(divNonce) > 0 {
if err := s.cryptoStreamHandler.(divNonceSetter).SetDiversificationNonce(divNonce); err != nil {
return err
}
}
}
if p.rcvTime.IsZero() {
// To simplify testing
p.rcvTime = time.Now()
}
// Calculate packet number
hdr.PacketNumber = protocol.InferPacketNumber(
hdr.PacketNumberLen,
s.largestRcvdPacketNumber,
hdr.PacketNumber,
s.version,
)
packet, err := s.unpacker.Unpack(hdr.Raw, hdr, p.data)
if s.logger.Debug() {
if err != nil {
s.logger.Debugf("<- Reading packet 0x%x (%d bytes) for connection %s", hdr.PacketNumber, len(p.data)+len(hdr.Raw), hdr.DestConnectionID)
} else {
s.logger.Debugf("<- Reading packet 0x%x (%d bytes) for connection %s, %s", hdr.PacketNumber, len(p.data)+len(hdr.Raw), hdr.DestConnectionID, packet.encryptionLevel)
}
hdr.Log(s.logger)
}
// if the decryption failed, this might be a packet sent by an attacker
if err != nil {
return err
}
// The server can change the source connection ID with the first Handshake packet.
if s.perspective == protocol.PerspectiveClient && !s.receivedFirstPacket && hdr.IsLongHeader && !hdr.SrcConnectionID.Equal(s.destConnID) {
s.logger.Debugf("Received first packet. Switching destination connection ID to: %s", hdr.SrcConnectionID)
s.destConnID = hdr.SrcConnectionID
s.packer.ChangeDestConnectionID(s.destConnID)
}
s.receivedFirstPacket = true
s.lastNetworkActivityTime = p.rcvTime
s.keepAlivePingSent = false
// In gQUIC, the server completes the handshake first (after sending the SHLO).
// In TLS 1.3, the client completes the handshake first (after sending the CFIN).
// We know that the peer completed the handshake as soon as we receive a forward-secure packet.
if (!s.version.UsesTLS() && s.perspective == protocol.PerspectiveServer) ||
(s.version.UsesTLS() && s.perspective == protocol.PerspectiveClient) {
if !s.receivedFirstForwardSecurePacket && packet.encryptionLevel == protocol.EncryptionForwardSecure {
s.receivedFirstForwardSecurePacket = true
s.sentPacketHandler.SetHandshakeComplete()
}
}
s.lastRcvdPacketNumber = hdr.PacketNumber
// Only do this after decrypting, so we are sure the packet is not attacker-controlled
s.largestRcvdPacketNumber = utils.MaxPacketNumber(s.largestRcvdPacketNumber, hdr.PacketNumber)
// If this is a Retry packet, there's no need to send an ACK.
// The session will be closed and recreated as soon as the crypto setup processed the HRR.
if hdr.Type != protocol.PacketTypeRetry {
isRetransmittable := ackhandler.HasRetransmittableFrames(packet.frames)
if err := s.receivedPacketHandler.ReceivedPacket(hdr.PacketNumber, p.rcvTime, isRetransmittable); err != nil {
return err
}
}
return s.handleFrames(packet.frames, packet.encryptionLevel)
}
func (s *session) handleFrames(fs []wire.Frame, encLevel protocol.EncryptionLevel) error {
for _, ff := range fs {
var err error
wire.LogFrame(s.logger, ff, false)
switch frame := ff.(type) {
case *wire.StreamFrame:
err = s.handleStreamFrame(frame, encLevel)
case *wire.AckFrame:
err = s.handleAckFrame(frame, encLevel)
case *wire.ConnectionCloseFrame:
s.closeRemote(qerr.Error(frame.ErrorCode, frame.ReasonPhrase))
case *wire.GoawayFrame:
err = errors.New("unimplemented: handling GOAWAY frames")
case *wire.StopWaitingFrame: // ignore STOP_WAITINGs
case *wire.RstStreamFrame:
err = s.handleRstStreamFrame(frame)
case *wire.MaxDataFrame:
s.handleMaxDataFrame(frame)
case *wire.MaxStreamDataFrame:
err = s.handleMaxStreamDataFrame(frame)
case *wire.MaxStreamIDFrame:
err = s.handleMaxStreamIDFrame(frame)
case *wire.BlockedFrame:
case *wire.StreamBlockedFrame:
case *wire.StreamIDBlockedFrame:
case *wire.StopSendingFrame:
err = s.handleStopSendingFrame(frame)
case *wire.PingFrame:
case *wire.PathChallengeFrame:
s.handlePathChallengeFrame(frame)
case *wire.PathResponseFrame:
// since we don't send PATH_CHALLENGEs, we don't expect PATH_RESPONSEs
err = errors.New("unexpected PATH_RESPONSE frame")
default:
return errors.New("Session BUG: unexpected frame type")
}
if err != nil {
return err
}
}
return nil
}
// handlePacket is called by the server with a new packet
func (s *session) handlePacket(p *receivedPacket) {
// Discard packets once the amount of queued packets is larger than
// the channel size, protocol.MaxSessionUnprocessedPackets
select {
case s.receivedPackets <- p:
default:
}
}
func (s *session) handleStreamFrame(frame *wire.StreamFrame, encLevel protocol.EncryptionLevel) error {
if frame.StreamID == s.version.CryptoStreamID() {
if frame.FinBit {
return errors.New("Received STREAM frame with FIN bit for the crypto stream")
}
return s.cryptoStream.handleStreamFrame(frame)
} else if encLevel <= protocol.EncryptionUnencrypted {
return qerr.Error(qerr.UnencryptedStreamData, fmt.Sprintf("received unencrypted stream data on stream %d", frame.StreamID))
}
str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID)
if err != nil {
return err
}
if str == nil {
// Stream is closed and already garbage collected
// ignore this StreamFrame
return nil
}
return str.handleStreamFrame(frame)
}
func (s *session) handleMaxDataFrame(frame *wire.MaxDataFrame) {
s.connFlowController.UpdateSendWindow(frame.ByteOffset)
}
func (s *session) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) error {
if frame.StreamID == s.version.CryptoStreamID() {
s.cryptoStream.handleMaxStreamDataFrame(frame)
return nil
}
str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID)
if err != nil {
return err
}
if str == nil {
// stream is closed and already garbage collected
return nil
}
str.handleMaxStreamDataFrame(frame)
return nil
}
func (s *session) handleMaxStreamIDFrame(frame *wire.MaxStreamIDFrame) error {
return s.streamsMap.HandleMaxStreamIDFrame(frame)
}
func (s *session) handleRstStreamFrame(frame *wire.RstStreamFrame) error {
if frame.StreamID == s.version.CryptoStreamID() {
return errors.New("Received RST_STREAM frame for the crypto stream")
}
str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID)
if err != nil {
return err
}
if str == nil {
// stream is closed and already garbage collected
return nil
}
return str.handleRstStreamFrame(frame)
}
func (s *session) handleStopSendingFrame(frame *wire.StopSendingFrame) error {
if frame.StreamID == s.version.CryptoStreamID() {
return errors.New("Received a STOP_SENDING frame for the crypto stream")
}
str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID)
if err != nil {
return err
}
if str == nil {
// stream is closed and already garbage collected
return nil
}
str.handleStopSendingFrame(frame)
return nil
}
func (s *session) handlePathChallengeFrame(frame *wire.PathChallengeFrame) {
s.queueControlFrame(&wire.PathResponseFrame{Data: frame.Data})
}
func (s *session) handleAckFrame(frame *wire.AckFrame, encLevel protocol.EncryptionLevel) error {
if err := s.sentPacketHandler.ReceivedAck(frame, s.lastRcvdPacketNumber, encLevel, s.lastNetworkActivityTime); err != nil {
return err
}
s.receivedPacketHandler.IgnoreBelow(s.sentPacketHandler.GetLowestPacketNotConfirmedAcked())
return nil
}
// closeLocal closes the session and send a CONNECTION_CLOSE containing the error
func (s *session) closeLocal(e error) {
s.closeOnce.Do(func() {
s.closeChan <- closeError{err: e, sendClose: true, remote: false}
})
}
// destroy closes the session without sending the error on the wire
func (s *session) destroy(e error) {
s.closeOnce.Do(func() {
s.closeChan <- closeError{err: e, sendClose: false, remote: false}
})
}
func (s *session) closeRemote(e error) {
s.closeOnce.Do(func() {
s.closeChan <- closeError{err: e, remote: true}
})
}
// Close the connection. It sends a qerr.PeerGoingAway.
// It waits until the run loop has stopped before returning
func (s *session) Close() error {
s.closeLocal(nil)
<-s.ctx.Done()
return nil
}
func (s *session) CloseWithError(code protocol.ApplicationErrorCode, e error) error {
s.closeLocal(qerr.Error(qerr.ErrorCode(code), e.Error()))
<-s.ctx.Done()
return nil
}
func (s *session) handleCloseError(closeErr closeError) error {
if closeErr.err == nil {
closeErr.err = qerr.PeerGoingAway
}
var quicErr *qerr.QuicError
var ok bool
if quicErr, ok = closeErr.err.(*qerr.QuicError); !ok {
quicErr = qerr.ToQuicError(closeErr.err)
}
// Don't log 'normal' reasons
if quicErr.ErrorCode == qerr.PeerGoingAway || quicErr.ErrorCode == qerr.NetworkIdleTimeout {
s.logger.Infof("Closing connection %s.", s.srcConnID)
} else {
s.logger.Errorf("Closing session with error: %s", closeErr.err.Error())
}
s.cryptoStream.closeForShutdown(quicErr)
s.streamsMap.CloseWithError(quicErr)
if !closeErr.sendClose {
return nil
}
// If this is a remote close we're done here
if closeErr.remote {
return nil
}
if quicErr.ErrorCode == qerr.DecryptionFailure ||
quicErr == handshake.ErrNSTPExperiment {
return s.sendPublicReset(s.lastRcvdPacketNumber)
}
return s.sendConnectionClose(quicErr)
}
func (s *session) processTransportParameters(params *handshake.TransportParameters) {
s.peerParams = params
s.streamsMap.UpdateLimits(params)
if params.OmitConnectionID {
s.packer.SetOmitConnectionID()
}
if params.MaxPacketSize != 0 {
s.packer.SetMaxPacketSize(params.MaxPacketSize)
}
s.connFlowController.UpdateSendWindow(params.ConnectionFlowControlWindow)
// the crypto stream is the only open stream at this moment
// so we don't need to update stream flow control windows
}
func (s *session) sendPackets() error {
s.pacingDeadline = time.Time{}
sendMode := s.sentPacketHandler.SendMode()
if sendMode == ackhandler.SendNone { // shortcut: return immediately if there's nothing to send
return nil
}
numPackets := s.sentPacketHandler.ShouldSendNumPackets()
var numPacketsSent int
sendLoop:
for {
switch sendMode {
case ackhandler.SendNone:
break sendLoop
case ackhandler.SendAck:
// We can at most send a single ACK only packet.
// There will only be a new ACK after receiving new packets.
// SendAck is only returned when we're congestion limited, so we don't need to set the pacingt timer.
return s.maybeSendAckOnlyPacket()
case ackhandler.SendTLP, ackhandler.SendRTO:
if err := s.sendProbePacket(); err != nil {
return err
}
numPacketsSent++
case ackhandler.SendRetransmission:
sentPacket, err := s.maybeSendRetransmission()
if err != nil {
return err
}
if sentPacket {
numPacketsSent++
// This can happen if a retransmission queued, but it wasn't necessary to send it.
// e.g. when an Initial is queued, but we already received a packet from the server.
}
case ackhandler.SendAny:
sentPacket, err := s.sendPacket()
if err != nil {
return err
}
if !sentPacket {
break sendLoop
}
numPacketsSent++
default:
return fmt.Errorf("BUG: invalid send mode %d", sendMode)
}
if numPacketsSent >= numPackets {
break
}
sendMode = s.sentPacketHandler.SendMode()
}
// Only start the pacing timer if we sent as many packets as we were allowed.
// There will probably be more to send when calling sendPacket again.
if numPacketsSent == numPackets {
s.pacingDeadline = s.sentPacketHandler.TimeUntilSend()
}
return nil
}
func (s *session) maybeSendAckOnlyPacket() error {
ack := s.receivedPacketHandler.GetAckFrame()
if ack == nil {
return nil
}
s.packer.QueueControlFrame(ack)
if s.version.UsesStopWaitingFrames() { // for gQUIC, maybe add a STOP_WAITING
if swf := s.sentPacketHandler.GetStopWaitingFrame(false); swf != nil {
s.packer.QueueControlFrame(swf)
}
}
packet, err := s.packer.PackAckPacket()
if err != nil {
return err
}
s.sentPacketHandler.SentPacket(packet.ToAckHandlerPacket())
return s.sendPackedPacket(packet)
}
// maybeSendRetransmission sends retransmissions for at most one packet.