raioquic 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.containerignore +4 -0
- data/.rubocop.yml +93 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Containerfile +6 -0
- data/Gemfile +24 -0
- data/Gemfile.lock +113 -0
- data/LICENSE +28 -0
- data/README.md +48 -0
- data/Rakefile +16 -0
- data/Steepfile +8 -0
- data/example/curlcatcher.rb +18 -0
- data/example/interoperability/README.md +9 -0
- data/example/interoperability/aioquic/aioquic_client.py +47 -0
- data/example/interoperability/aioquic/aioquic_server.py +34 -0
- data/example/interoperability/key.pem +28 -0
- data/example/interoperability/localhost-unasuke-dev.crt +21 -0
- data/example/interoperability/quic-go/sample_server.go +61 -0
- data/example/interoperability/raioquic_client.rb +42 -0
- data/example/interoperability/raioquic_server.rb +43 -0
- data/example/parse_curl_example.rb +108 -0
- data/lib/raioquic/buffer.rb +202 -0
- data/lib/raioquic/core_ext.rb +54 -0
- data/lib/raioquic/crypto/README.md +5 -0
- data/lib/raioquic/crypto/aesgcm.rb +52 -0
- data/lib/raioquic/crypto/backend/aead.rb +52 -0
- data/lib/raioquic/crypto/backend.rb +12 -0
- data/lib/raioquic/crypto.rb +10 -0
- data/lib/raioquic/quic/configuration.rb +81 -0
- data/lib/raioquic/quic/connection.rb +2776 -0
- data/lib/raioquic/quic/crypto.rb +317 -0
- data/lib/raioquic/quic/event.rb +69 -0
- data/lib/raioquic/quic/logger.rb +272 -0
- data/lib/raioquic/quic/packet.rb +471 -0
- data/lib/raioquic/quic/packet_builder.rb +301 -0
- data/lib/raioquic/quic/rangeset.rb +113 -0
- data/lib/raioquic/quic/recovery.rb +528 -0
- data/lib/raioquic/quic/stream.rb +343 -0
- data/lib/raioquic/quic.rb +20 -0
- data/lib/raioquic/tls.rb +1659 -0
- data/lib/raioquic/version.rb +5 -0
- data/lib/raioquic.rb +12 -0
- data/misc/export_x25519.py +43 -0
- data/misc/gen_rfc8448_keypair.rb +90 -0
- data/raioquic.gemspec +37 -0
- data/sig/raioquic/buffer.rbs +37 -0
- data/sig/raioquic/core_ext.rbs +7 -0
- data/sig/raioquic/crypto/aesgcm.rbs +20 -0
- data/sig/raioquic/crypto/backend/aead.rbs +11 -0
- data/sig/raioquic/quic/configuration.rbs +34 -0
- data/sig/raioquic/quic/connection.rbs +277 -0
- data/sig/raioquic/quic/crypto.rbs +88 -0
- data/sig/raioquic/quic/event.rbs +51 -0
- data/sig/raioquic/quic/logger.rbs +57 -0
- data/sig/raioquic/quic/packet.rbs +157 -0
- data/sig/raioquic/quic/packet_builder.rbs +76 -0
- data/sig/raioquic/quic/rangeset.rbs +17 -0
- data/sig/raioquic/quic/recovery.rbs +142 -0
- data/sig/raioquic/quic/stream.rbs +87 -0
- data/sig/raioquic/tls.rbs +444 -0
- data/sig/raioquic.rbs +9 -0
- metadata +121 -0
@@ -0,0 +1,2776 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
|
5
|
+
module Raioquic
|
6
|
+
module Quic
|
7
|
+
module Connection
|
8
|
+
CRYPTO_BUFFER_SIZE = 16384
|
9
|
+
EPOCH_SHORTCUTS = {
|
10
|
+
"I" => TLS::Epoch::INITIAL,
|
11
|
+
"H" => TLS::Epoch::HANDSHAKE,
|
12
|
+
"0" => TLS::Epoch::ZERO_RTT,
|
13
|
+
"1" => TLS::Epoch::ONE_RTT,
|
14
|
+
}
|
15
|
+
MAX_EARLY_DATA = 0xffffffff
|
16
|
+
SECRETS_LABELS = [
|
17
|
+
[
|
18
|
+
nil,
|
19
|
+
"CLIENT_EARLY_TRAFFIC_SECRET",
|
20
|
+
"CLIENT_HANDSHAKE_TRAFFIC_SECRET",
|
21
|
+
"CLIENT_TRAFFIC_SECRET_0",
|
22
|
+
],
|
23
|
+
[
|
24
|
+
nil,
|
25
|
+
nil,
|
26
|
+
"SERVER_HANDSHAKE_TRAFFIC_SECRET",
|
27
|
+
"SERVER_TRAFFIC_SECRET_0",
|
28
|
+
]
|
29
|
+
]
|
30
|
+
STREAM_FLAGS = 0x07
|
31
|
+
STREAM_COUNT_MAX = 0x1000000000000000
|
32
|
+
UDP_HEADER_SIZE = 8
|
33
|
+
|
34
|
+
# frame sizes
|
35
|
+
ACK_FRAME_CAPACITY = 64 # FIXME: this is arbitrary!
|
36
|
+
APPLICATION_CLOSE_FRAME_CAPACITY = 1 + (2 * Buffer::UINT_VAR_MAX_SIZE) # + reason length
|
37
|
+
CONNECTION_LIMIT_FRAME_CAPACITY = 1 + Buffer::UINT_VAR_MAX_SIZE
|
38
|
+
HANDSHAKE_DONE_FRAME_CAPACITY = 1
|
39
|
+
MAX_STREAM_DATA_FRAME_CAPACITY = 1 + (2 * Buffer::UINT_VAR_MAX_SIZE)
|
40
|
+
NEW_CONNECTION_ID_FRAME_CAPACITY = (
|
41
|
+
1 + (2 * Buffer::UINT_VAR_MAX_SIZE) + 1 + Packet::CONNECTION_ID_MAX_SIZE + Packet::STATELESS_RESET_TOKEN_SIZE
|
42
|
+
)
|
43
|
+
PATH_CHALLENGE_FRAME_CAPACITY = 1 + 8
|
44
|
+
PATH_RESPONSE_FRAME_CAPACITY = 1 + 8
|
45
|
+
PING_FRAME_CAPACITY = 1
|
46
|
+
RESET_STREAM_FRAME_CAPACITY = 1 + (3 * Buffer::UINT_VAR_MAX_SIZE)
|
47
|
+
RETIRE_CONNECTION_ID_CAPACITY = 1 + Buffer::UINT_VAR_MAX_SIZE
|
48
|
+
STOP_SENDING_FRAME_CAPACITY = 1 + (2 * Buffer::UINT_VAR_MAX_SIZE)
|
49
|
+
STREAMS_BLOCKED_CAPACITY = 1 + Buffer::UINT_VAR_MAX_SIZE
|
50
|
+
TRANSPORT_CLOSE_FLAME_CAPACITY = 1 + (3 * Buffer::UINT_VAR_MAX_SIZE) # + reason length
|
51
|
+
|
52
|
+
def self.epochs(shortcut)
|
53
|
+
shortcut.each_char.map do |s|
|
54
|
+
EPOCH_SHORTCUTS[s]
|
55
|
+
end.sort
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.dump_cid(cid)
|
59
|
+
cid.unpack1("H*")
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.get_epoch(packet_type)
|
63
|
+
case packet_type
|
64
|
+
when Quic::Packet::PACKET_TYPE_INITIAL
|
65
|
+
TLS::Epoch::INITIAL
|
66
|
+
when Quic::Packet::PACKET_TYPE_ZERO_RTT
|
67
|
+
TLS::Epoch::ZERO_RTT
|
68
|
+
when Quic::Packet::PACKET_TYPE_HANDSHAKE
|
69
|
+
TLS::Epoch::HANDSHAKE
|
70
|
+
else
|
71
|
+
TLS::Epoch::ONE_RTT
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.get_transport_parameters_extension(version)
|
76
|
+
if Quic::Packet.is_draft_version(version)
|
77
|
+
TLS::ExtensionType::QUIC_TRANSPORT_PARAMETERS_DRAFT
|
78
|
+
else
|
79
|
+
TLS::ExtensionType::QUIC_TRANSPORT_PARAMETERS
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns true if the stream is client initiated.
|
84
|
+
def self.stream_is_client_initiated(stream_id)
|
85
|
+
stream_id & 1 == 0
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns true if the stream is unidirectional.
|
89
|
+
def self.stream_is_unidirectional(stream_id)
|
90
|
+
stream_id & 2 != 0
|
91
|
+
end
|
92
|
+
|
93
|
+
class Limit
|
94
|
+
attr_reader :frame_type
|
95
|
+
attr_reader :name
|
96
|
+
attr_accessor :used
|
97
|
+
attr_accessor :sent
|
98
|
+
attr_accessor :value
|
99
|
+
|
100
|
+
def initialize(frame_type:, name:, value:)
|
101
|
+
@frame_type = frame_type
|
102
|
+
@name = name
|
103
|
+
@sent = value
|
104
|
+
@used = 0
|
105
|
+
@value = value
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class QuicConnectionError < StandardError # TODO: Raioquic::Error
|
110
|
+
attr_accessor :error_code
|
111
|
+
attr_accessor :frame_type
|
112
|
+
attr_accessor :reason_phrase
|
113
|
+
end
|
114
|
+
|
115
|
+
# TODO: Use logger with formatter
|
116
|
+
# class QuicConnectionAdapter
|
117
|
+
# def process(msd:, kwargs:)
|
118
|
+
# end
|
119
|
+
# end
|
120
|
+
|
121
|
+
QuicConnectionId = _ = Struct.new(
|
122
|
+
:cid,
|
123
|
+
:sequence_number,
|
124
|
+
:stateless_reset_token,
|
125
|
+
:was_sent,
|
126
|
+
)
|
127
|
+
|
128
|
+
class QuicConnectionState
|
129
|
+
FIRSTFLIGHT = 0
|
130
|
+
CONNECTED = 1
|
131
|
+
CLOSING = 2
|
132
|
+
DRAINING = 3
|
133
|
+
TERMINATED = 4
|
134
|
+
end
|
135
|
+
|
136
|
+
QuicNetworkPath = _ = Struct.new(
|
137
|
+
:addr,
|
138
|
+
:bytes_received,
|
139
|
+
:bytes_sent,
|
140
|
+
:is_validated,
|
141
|
+
:local_challenge,
|
142
|
+
:remote_challenge,
|
143
|
+
) do
|
144
|
+
def initialize(**)
|
145
|
+
super
|
146
|
+
self.bytes_sent ||= 0
|
147
|
+
self.bytes_received ||= 0
|
148
|
+
end
|
149
|
+
|
150
|
+
def can_send(size)
|
151
|
+
self.is_validated || (self.bytes_sent + size) <= 3 * self.bytes_received
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
QuicReceiveContext = _ = Struct.new(
|
156
|
+
:epoch,
|
157
|
+
:host_cid,
|
158
|
+
:network_path,
|
159
|
+
:quic_logger_frames,
|
160
|
+
:time,
|
161
|
+
)
|
162
|
+
|
163
|
+
END_STATES = [
|
164
|
+
QuicConnectionState::CLOSING,
|
165
|
+
QuicConnectionState::DRAINING,
|
166
|
+
QuicConnectionState::TERMINATED,
|
167
|
+
].freeze
|
168
|
+
|
169
|
+
# A QUIC connection.
|
170
|
+
#
|
171
|
+
# The state machine is driven by three kinds of sources:
|
172
|
+
#
|
173
|
+
# - the API user requesting data to be send out (see `connect`,
|
174
|
+
# `reset_stream`, `send_ping`, `send_datagram_frame` and `send_stream_data`)
|
175
|
+
# - data being received from the network (see `receive_datagram`)
|
176
|
+
# - a timer firing (see `handle_timer`)
|
177
|
+
#
|
178
|
+
# param configuration: The QUIC configuration to use.
|
179
|
+
class QuicConnection
|
180
|
+
attr_reader :configuration
|
181
|
+
attr_reader :loss
|
182
|
+
attr_reader :peer_cid_available
|
183
|
+
attr_reader :host_cid
|
184
|
+
attr_reader :host_cids
|
185
|
+
attr_reader :version
|
186
|
+
attr_reader :network_paths
|
187
|
+
attr_reader :peer_cid
|
188
|
+
attr_reader :local_max_stream_data_uni
|
189
|
+
attr_reader :remote_max_streams_uni
|
190
|
+
attr_reader :remote_max_streams_bidi
|
191
|
+
attr_reader :local_max_data
|
192
|
+
attr_reader :remote_max_data
|
193
|
+
attr_reader :streams
|
194
|
+
attr_reader :cryptos
|
195
|
+
attr_reader :close_event
|
196
|
+
attr_reader :local_max_stream_data_bidi_remote
|
197
|
+
attr_reader :local_max_stream_data_bidi_local
|
198
|
+
attr_reader :local_max_streams_bidi
|
199
|
+
attr_reader :tls
|
200
|
+
attr_reader :events
|
201
|
+
attr_reader :quic_logger
|
202
|
+
attr_reader :handshake_done_pending
|
203
|
+
attr_accessor :original_destination_connection_id
|
204
|
+
attr_accessor :ack_delay
|
205
|
+
attr_accessor :is_client
|
206
|
+
attr_accessor :initial_source_connection_id # TODO: did not used?
|
207
|
+
|
208
|
+
attr_reader :events # TODO: remove
|
209
|
+
|
210
|
+
def initialize(
|
211
|
+
configuration:,
|
212
|
+
original_destination_connection_id: nil,
|
213
|
+
retry_source_connection_id: nil,
|
214
|
+
session_ticket_fetcher: nil,
|
215
|
+
session_ticket_handler: nil
|
216
|
+
)
|
217
|
+
# binding.irb
|
218
|
+
if configuration.is_client
|
219
|
+
raise ArgumentError, "Cannot set original_destination_connection_id for a client" if original_destination_connection_id
|
220
|
+
raise ArgumentError, "Cannot set retry_source_connection_id for a client" if retry_source_connection_id
|
221
|
+
else
|
222
|
+
raise ArgumentError, "SSL certificate is required for a server" unless configuration.certificate
|
223
|
+
raise ArgumentError, "SSL private key is required for a server" unless configuration.private_key
|
224
|
+
end
|
225
|
+
|
226
|
+
# configuration
|
227
|
+
@configuration = configuration
|
228
|
+
@is_client = configuration.is_client
|
229
|
+
|
230
|
+
@ack_delay = Recovery::K_GRANULARITY
|
231
|
+
@close_at = nil
|
232
|
+
@close_event = nil
|
233
|
+
@connect_called = false
|
234
|
+
@cryptos = {}
|
235
|
+
@crypto_buffers = {}
|
236
|
+
@crypto_retransmitted = false
|
237
|
+
@crypto_streams = {}
|
238
|
+
@events = []
|
239
|
+
@handshake_complete = false
|
240
|
+
@handshake_confirmed = false
|
241
|
+
@host_cids = [
|
242
|
+
QuicConnectionId.new.tap do |conn|
|
243
|
+
conn.cid = Random.urandom(@configuration.connection_id_length)
|
244
|
+
conn.sequence_number = 0
|
245
|
+
conn.stateless_reset_token = @is_client ? nil : Random.urandom(16)
|
246
|
+
conn.was_sent = true
|
247
|
+
end
|
248
|
+
]
|
249
|
+
@host_cid = @host_cids[0].cid
|
250
|
+
@host_cid_seq = 1
|
251
|
+
@local_ack_delay_exponent = 3
|
252
|
+
@local_active_connection_id_limit = 8
|
253
|
+
@local_initial_source_connection_id = @host_cids[0].cid
|
254
|
+
@local_max_data = Limit.new(
|
255
|
+
frame_type: Packet::QuicFrameType::MAX_DATA,
|
256
|
+
name: "max_data",
|
257
|
+
value: configuration.max_data,
|
258
|
+
)
|
259
|
+
@local_max_stream_data_bidi_local = @configuration.max_stream_data
|
260
|
+
@local_max_stream_data_bidi_remote = @configuration.max_stream_data
|
261
|
+
@local_max_stream_data_uni = @configuration.max_stream_data
|
262
|
+
@local_max_streams_bidi = Limit.new(
|
263
|
+
frame_type: Packet::QuicFrameType::MAX_STREAMS_BIDI,
|
264
|
+
name: "max_streams_bidi",
|
265
|
+
value: 128,
|
266
|
+
)
|
267
|
+
@local_max_streams_uni = Limit.new(
|
268
|
+
frame_type: Packet::QuicFrameType::MAX_STREAMS_UNI,
|
269
|
+
name: "max_streams_uni",
|
270
|
+
value: 128,
|
271
|
+
)
|
272
|
+
@loss_at = nil
|
273
|
+
@network_paths = []
|
274
|
+
@pacing_at = nil
|
275
|
+
@packet_number = 0
|
276
|
+
@parameters_received = false
|
277
|
+
@peer_cid = QuicConnectionId.new.tap do |cid|
|
278
|
+
cid.cid = Random.urandom(@configuration.connection_id_length)
|
279
|
+
cid.sequence_number = nil
|
280
|
+
end
|
281
|
+
@peer_cid_available = []
|
282
|
+
@peer_cid_sequence_numbers = Set.new([0])
|
283
|
+
@peer_token = ""
|
284
|
+
@quic_logger = nil
|
285
|
+
@remote_ack_delay_exponent = 3
|
286
|
+
@remote_active_connection_id_limit = 2
|
287
|
+
@remote_initial_source_connection_id = nil
|
288
|
+
@remote_max_idle_timeout = 0.0 # seconds
|
289
|
+
@remote_max_data = 0
|
290
|
+
@remote_max_data_used = 0
|
291
|
+
@remote_max_datagram_frame_size = nil
|
292
|
+
@remote_max_stream_data_bidi_local = 0
|
293
|
+
@remote_max_stream_data_bidi_remote = 0
|
294
|
+
@remote_max_stream_data_uni = 0
|
295
|
+
@remote_max_streams_bidi = 0
|
296
|
+
@remote_max_streams_uni = 0
|
297
|
+
@retry_count = 0
|
298
|
+
@retry_source_connection_id = retry_source_connection_id
|
299
|
+
@spaces = {}
|
300
|
+
@spin_bit = false
|
301
|
+
@spin_highest_pn = 0
|
302
|
+
@state = QuicConnectionState::FIRSTFLIGHT
|
303
|
+
@streams = {}
|
304
|
+
@streams_blocked_bidi = []
|
305
|
+
@streams_blocked_uni = []
|
306
|
+
@streams_finished = Set.new
|
307
|
+
@version = nil
|
308
|
+
@version_negotiation_count = 0
|
309
|
+
|
310
|
+
@original_destination_connection_id = \
|
311
|
+
@is_client ? @peer_cid.cid : original_destination_connection_id
|
312
|
+
|
313
|
+
@logger = nil # TODO: logging
|
314
|
+
if @configuration.quic_logger
|
315
|
+
@quic_logger = @configuration.quic_logger.start_trace(
|
316
|
+
is_client: @configuration.is_client,
|
317
|
+
odcid: @original_destination_connection_id,
|
318
|
+
)
|
319
|
+
end
|
320
|
+
|
321
|
+
# loss recovery
|
322
|
+
@loss = Quic::Recovery::QuicPacketRecovery.new(
|
323
|
+
initial_rtt: @configuration.initial_rtt,
|
324
|
+
peer_completed_address_validation: !@is_client,
|
325
|
+
quic_logger: @quic_logger,
|
326
|
+
send_probe: method(:send_probe),
|
327
|
+
logger: nil,
|
328
|
+
)
|
329
|
+
|
330
|
+
# things to send
|
331
|
+
@close_pending = false
|
332
|
+
@datagrams_pending = [] # TODO: deque
|
333
|
+
@handshake_done_pending = false
|
334
|
+
@ping_pending = []
|
335
|
+
@probe_pending = false
|
336
|
+
@retire_connection_ids = []
|
337
|
+
@streams_blocked_pending = false
|
338
|
+
|
339
|
+
# callbacks
|
340
|
+
@session_ticket_fetcher = session_ticket_fetcher
|
341
|
+
@session_ticket_handler = session_ticket_handler
|
342
|
+
|
343
|
+
# frame handlers
|
344
|
+
@frame_handlers = {
|
345
|
+
0x00 => [:handle_padding_frame, Connection.epochs("IH01")],
|
346
|
+
0x01 => [:handle_ping_frame, Connection.epochs("IH01")],
|
347
|
+
0x02 => [:handle_ack_frame, Connection.epochs("IH1")],
|
348
|
+
0x03 => [:handle_ack_frame, Connection.epochs("IH1")],
|
349
|
+
0x04 => [:handle_reset_stream_frame, Connection.epochs("01")],
|
350
|
+
0x05 => [:handle_stop_sending_frame, Connection.epochs("01")],
|
351
|
+
0x06 => [:handle_crypto_frame, Connection.epochs("IH1")],
|
352
|
+
0x07 => [:handle_new_token_frame, Connection.epochs("1")],
|
353
|
+
0x08 => [:handle_stream_frame, Connection.epochs("01")],
|
354
|
+
0x09 => [:handle_stream_frame, Connection.epochs("01")],
|
355
|
+
0x0a => [:handle_stream_frame, Connection.epochs("01")],
|
356
|
+
0x0b => [:handle_stream_frame, Connection.epochs("01")],
|
357
|
+
0x0c => [:handle_stream_frame, Connection.epochs("01")],
|
358
|
+
0x0d => [:handle_stream_frame, Connection.epochs("01")],
|
359
|
+
0x0e => [:handle_stream_frame, Connection.epochs("01")],
|
360
|
+
0x0f => [:handle_stream_frame, Connection.epochs("01")],
|
361
|
+
0x10 => [:handle_max_data_frame, Connection.epochs("01")],
|
362
|
+
0x11 => [:handle_max_stream_data_frame, Connection.epochs("01")],
|
363
|
+
0x12 => [:handle_max_streams_bidi_frame, Connection.epochs("01")],
|
364
|
+
0x13 => [:handle_max_streams_uni_frame, Connection.epochs("01")],
|
365
|
+
0x14 => [:handle_data_blocked_frame, Connection.epochs("01")],
|
366
|
+
0x15 => [:handle_stream_data_blocked_frame, Connection.epochs("01")],
|
367
|
+
0x16 => [:handle_streams_blocked_frame, Connection.epochs("01")],
|
368
|
+
0x17 => [:handle_streams_blocked_frame, Connection.epochs("01")],
|
369
|
+
0x18 => [:handle_new_connection_id_frame, Connection.epochs("01")],
|
370
|
+
0x19 => [:handle_retire_connection_id_frame, Connection.epochs("01")],
|
371
|
+
0x1a => [:handle_path_challenge_frame, Connection.epochs("01")],
|
372
|
+
0x1b => [:handle_path_response_frame, Connection.epochs("01")],
|
373
|
+
0x1c => [:handle_connection_close_frame, Connection.epochs("IH01")],
|
374
|
+
0x1d => [:handle_connection_close_frame, Connection.epochs("01")],
|
375
|
+
0x1e => [:handle_handshake_done_frame, Connection.epochs("1")],
|
376
|
+
0x30 => [:handle_datagram_frame, Connection.epochs("01")],
|
377
|
+
0x31 => [:handle_datagram_frame, Connection.epochs("01")],
|
378
|
+
}
|
379
|
+
end
|
380
|
+
|
381
|
+
# Switch to the next available connection ID ans retire the previous one.
|
382
|
+
#
|
383
|
+
# After calling this method call `datagrams_to_send` to retrieve data which needs to be sent.
|
384
|
+
def change_connection_id
|
385
|
+
if @peer_cid_available
|
386
|
+
# retire precious CID
|
387
|
+
retire_peer_cid(@peer_cid)
|
388
|
+
|
389
|
+
# assign new CID
|
390
|
+
consume_peer_cid
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
# Close the connection.
|
395
|
+
#
|
396
|
+
# param error_code: An error code indication why the connection is being closed.
|
397
|
+
# param reason_phrase: A human-readable explanation of why the connection is being closed.
|
398
|
+
def close(error_code: Quic::Packet::QuicErrorCode::NO_ERROR, frame_type: nil, reason_phrase: "")
|
399
|
+
if @close_event.nil? && !END_STATES.include?(@state)
|
400
|
+
@close_event = Quic::Event::ConnectionTerminated.new.tap do |error|
|
401
|
+
error.error_code = error_code
|
402
|
+
error.frame_type = frame_type
|
403
|
+
error.reason_phrase = reason_phrase
|
404
|
+
end
|
405
|
+
@close_pending = true
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
# Initiate the TLS handshake.
|
410
|
+
#
|
411
|
+
# This method can only be called for clients and a single time.
|
412
|
+
#
|
413
|
+
# After calling this method call `datagrams_to_send` to retrieve data which needs to be sent.
|
414
|
+
#
|
415
|
+
# param addr: The network address of the remote peer
|
416
|
+
# param now: The current time.
|
417
|
+
def connect(addr:, now:)
|
418
|
+
raise "`connect` can only be called for clients and a single time" if !@is_client && @connect_called
|
419
|
+
|
420
|
+
@connect_called = true
|
421
|
+
# np = QuicNetworkPath.new.tap { |n| n.addr = addr; n.is_validated = true; }
|
422
|
+
# @network_paths = [np]
|
423
|
+
@network_paths = [QuicNetworkPath.new.tap { |n| n.addr = addr; n.is_validated = true; }]
|
424
|
+
@version = @configuration.supported_versions[0]
|
425
|
+
_connect(now: now)
|
426
|
+
end
|
427
|
+
|
428
|
+
# Return a list of `[data, addr]` array of datagrams which need to be sent, and the network address to which they need to be sent.
|
429
|
+
#
|
430
|
+
# After calling this method call `get_timer` to know when the next timer needs to be set.
|
431
|
+
#
|
432
|
+
# param now: The current time.
|
433
|
+
def datagrams_to_send(now:)
|
434
|
+
network_path = @network_paths[0]
|
435
|
+
|
436
|
+
return [] if END_STATES.include?(@state)
|
437
|
+
|
438
|
+
# build datagrams
|
439
|
+
builder = Quic::PacketBuilder::QuicPacketBuilder.new(
|
440
|
+
host_cid: @host_cid,
|
441
|
+
is_client: @is_client,
|
442
|
+
packet_number: @packet_number,
|
443
|
+
peer_cid: @peer_cid.cid,
|
444
|
+
peer_token: @peer_token,
|
445
|
+
quic_logger: @quic_logger,
|
446
|
+
spin_bit: @spin_bit,
|
447
|
+
version: @version,
|
448
|
+
)
|
449
|
+
if @close_pending
|
450
|
+
epoch_packet_types = []
|
451
|
+
unless @handshake_confirmed
|
452
|
+
epoch_packet_types += [
|
453
|
+
[TLS::Epoch::INITIAL, Quic::Packet::PACKET_TYPE_INITIAL],
|
454
|
+
[TLS::Epoch::HANDSHAKE, Quic::Packet::PACKET_TYPE_HANDSHAKE]
|
455
|
+
]
|
456
|
+
end
|
457
|
+
epoch_packet_types += [[TLS::Epoch::ONE_RTT, Quic::Packet::PACKET_TYPE_ONE_RTT]]
|
458
|
+
epoch_packet_types.each do |epoch, packet_type|
|
459
|
+
crypto = @cryptos[epoch]
|
460
|
+
# binding.irb
|
461
|
+
next unless crypto&.send&.is_valid
|
462
|
+
|
463
|
+
# binding.irb unless @is_client
|
464
|
+
builder.start_packet(packet_type: packet_type, crypto: crypto)
|
465
|
+
# binding.irb
|
466
|
+
write_connection_close_frame(
|
467
|
+
builder: builder,
|
468
|
+
epoch: epoch,
|
469
|
+
error_code: @close_event.error_code,
|
470
|
+
frame_type: @close_event.frame_type,
|
471
|
+
reason_phrase: @close_event.reason_phrase,
|
472
|
+
)
|
473
|
+
end
|
474
|
+
# TODO: logger "Connection close sent"
|
475
|
+
@close_pending = true
|
476
|
+
close_begin(is_initiator: true, now: now)
|
477
|
+
else
|
478
|
+
# congestion control
|
479
|
+
builder.max_flight_bytes = @loss.congestion_window - @loss.bytes_in_flight
|
480
|
+
if @probe_pending && builder.max_flight_bytes < Quic::PacketBuilder::PACKET_MAX_SIZE
|
481
|
+
builder.max_flight_bytes = Quic::PacketBuilder::PACKET_MAX_SIZE
|
482
|
+
end
|
483
|
+
|
484
|
+
# limit data on un-validated network paths
|
485
|
+
builder.max_total_bytes = (network_path.bytes_received * 3) - network_path.bytes_sent unless network_path.is_validated
|
486
|
+
|
487
|
+
begin
|
488
|
+
unless @handshake_confirmed
|
489
|
+
[TLS::Epoch::INITIAL, TLS::Epoch::HANDSHAKE].each do |epoch|
|
490
|
+
write_handshake(builder: builder, epoch: epoch, now: now)
|
491
|
+
end
|
492
|
+
end
|
493
|
+
write_application(builder: builder, network_path: network_path, now: now)
|
494
|
+
rescue Quic::PacketBuilder::QuicPacketBuilderStop
|
495
|
+
# pass
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
datagrams, packets = builder.flush
|
500
|
+
|
501
|
+
if datagrams
|
502
|
+
@packet_number = builder.packet_number
|
503
|
+
|
504
|
+
# register packets
|
505
|
+
sent_handshake = false
|
506
|
+
packets.each do |packet|
|
507
|
+
packet.sent_time = now
|
508
|
+
@loss.on_packet_sent(packet: packet, space: @spaces[packet.epoch])
|
509
|
+
sent_handshake = true if packet.epoch == TLS::Epoch::HANDSHAKE
|
510
|
+
|
511
|
+
# log packet
|
512
|
+
if @quic_logger
|
513
|
+
@quic_logger.log_event(
|
514
|
+
category: "transport",
|
515
|
+
event: "packet_sent",
|
516
|
+
data: {
|
517
|
+
frames: packet.quic_logger_frames,
|
518
|
+
header: {
|
519
|
+
packet_number: packet.packet_number,
|
520
|
+
packet_type: @quic_logger.packet_type(packet.packet_type),
|
521
|
+
scid: ( Quic::Packet.is_long_header(packet.packet_type) ? Connection.dump_cid(@host_cid) : ""),
|
522
|
+
dcid: Connection.dump_cid(@peer_cid.cid),
|
523
|
+
},
|
524
|
+
raw: { length: packet.sent_bytes },
|
525
|
+
},
|
526
|
+
)
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
# check if we can discard initial keys
|
531
|
+
if sent_handshake && @is_client
|
532
|
+
discard_epoch(TLS::Epoch::INITIAL)
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
# return datagrams to send and the destination network address
|
537
|
+
ret = []
|
538
|
+
datagrams.each do |datagram|
|
539
|
+
payload_length = datagram.bytesize
|
540
|
+
network_path.bytes_sent += payload_length
|
541
|
+
ret << [datagram, network_path.addr]
|
542
|
+
|
543
|
+
if @quic_logger
|
544
|
+
@quic_logger.log_event(
|
545
|
+
category: "transport",
|
546
|
+
event: "datagrams_sent",
|
547
|
+
data: {
|
548
|
+
count: 1,
|
549
|
+
raw: [{ length: UDP_HEADER_SIZE + payload_length, payload_length: payload_length }],
|
550
|
+
},
|
551
|
+
)
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
return ret
|
556
|
+
end
|
557
|
+
|
558
|
+
# Return the stream ID for the next stream created by this endpoint.
|
559
|
+
def get_next_available_stream_id(is_unidirectional: false)
|
560
|
+
uni_int = is_unidirectional ? 1 : 0
|
561
|
+
client_int = @is_client ? 0 : 1
|
562
|
+
stream_id = (uni_int << 1) | client_int
|
563
|
+
while @streams.keys.include?(stream_id) || @streams_finished.include?(stream_id)
|
564
|
+
stream_id += 4
|
565
|
+
end
|
566
|
+
return stream_id
|
567
|
+
end
|
568
|
+
|
569
|
+
# Return the time at which the timer should fire or nil if no timer is needed.
|
570
|
+
def get_timer
|
571
|
+
timer_at = @close_at
|
572
|
+
unless END_STATES.include?(@state)
|
573
|
+
# ack timer
|
574
|
+
@loss.spaces.each do |space|
|
575
|
+
timer_at = space.ack_at if space.ack_at && space.ack_at < timer_at
|
576
|
+
end
|
577
|
+
|
578
|
+
# loss detection timer
|
579
|
+
@loss_at = @loss.get_loss_detection_time
|
580
|
+
timer_at = @loss_at if @loss_at && @loss_at < timer_at
|
581
|
+
|
582
|
+
# pacing timer
|
583
|
+
timer_at = @pacing_at if @pacing_at && @pacing_at < timer_at
|
584
|
+
end
|
585
|
+
|
586
|
+
return timer_at
|
587
|
+
end
|
588
|
+
|
589
|
+
# Handle the timer.
|
590
|
+
#
|
591
|
+
# After calling this method call `datagram_to_send` to retrieve data which needs to be sent.
|
592
|
+
#
|
593
|
+
# param now: The current timer.
|
594
|
+
def handle_timer(now:)
|
595
|
+
# binding.b
|
596
|
+
# enf of closing period or idle timeout
|
597
|
+
if now >= @close_at
|
598
|
+
@close_event ||= Quic::Event::ConnectionTerminated.new.tap do |event|
|
599
|
+
event.error_code = Quic::Packet::QuicErrorCode::INTERNAL_ERROR
|
600
|
+
event.frame_type = Quic::Packet::QuicFrameType::PADDING
|
601
|
+
event.reason_phrase = "Ide timeout"
|
602
|
+
end
|
603
|
+
close_end
|
604
|
+
return
|
605
|
+
end
|
606
|
+
|
607
|
+
# loss detection timeout
|
608
|
+
if @loss_at && now >= @loss_at
|
609
|
+
# TODO: logger "Loss detection triggered"
|
610
|
+
@loss.on_loss_detection_timeout(now: now)
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
# Retrieve the next event from the event buffer.
|
615
|
+
#
|
616
|
+
# Returns `nil` if there are no buffered events.
|
617
|
+
def next_event
|
618
|
+
# binding.irb unless @is_client
|
619
|
+
@events.shift
|
620
|
+
rescue StandardError
|
621
|
+
return nil
|
622
|
+
end
|
623
|
+
|
624
|
+
# Handle an incoming datagram.
|
625
|
+
#
|
626
|
+
# After calling this method call `datagrams_to_send` to retrieve data which needs to be sent.
|
627
|
+
#
|
628
|
+
# param data: The datagram which was received.
|
629
|
+
# param addr: The network address from which the datagram was received.
|
630
|
+
# param now: The current time.
|
631
|
+
def receive_datagram(data:, addr:, now:)
|
632
|
+
# stop handling packets when closing
|
633
|
+
return if END_STATES.include?(@state)
|
634
|
+
|
635
|
+
# log datagram
|
636
|
+
if @quic_logger
|
637
|
+
@quic_logger.log_event(
|
638
|
+
category: "transport",
|
639
|
+
event: "datagrams_received",
|
640
|
+
data: {
|
641
|
+
count: 1,
|
642
|
+
raw: [{ length: UDP_HEADER_SIZE + data.bytesize, payload_length: data.bytesize }],
|
643
|
+
},
|
644
|
+
)
|
645
|
+
end
|
646
|
+
|
647
|
+
# for servers. arm the idle timeout on the first datagram
|
648
|
+
@close_at ||= now + @configuration.idle_timeout
|
649
|
+
|
650
|
+
buf = Buffer.new(data: data)
|
651
|
+
header = nil
|
652
|
+
until buf.eof
|
653
|
+
start_off = buf.tell
|
654
|
+
begin
|
655
|
+
header = Quic::Packet.pull_quic_header(buf: buf, host_cid_length: @configuration.connection_id_length)
|
656
|
+
rescue ValueError
|
657
|
+
if @quic_logger
|
658
|
+
@quic_logger.log_event(
|
659
|
+
category: "transport",
|
660
|
+
event: "packet_dropped",
|
661
|
+
data: {
|
662
|
+
trigger: "header_parse_error",
|
663
|
+
raw: { length: buf.capacity - start_off },
|
664
|
+
},
|
665
|
+
)
|
666
|
+
end
|
667
|
+
return
|
668
|
+
end
|
669
|
+
|
670
|
+
# check destination CID matches
|
671
|
+
destination_cid_seq = nil
|
672
|
+
@host_cids.each do |connection_id|
|
673
|
+
if header.destination_cid == connection_id.cid
|
674
|
+
destination_cid_seq = connection_id.sequence_number
|
675
|
+
break
|
676
|
+
end
|
677
|
+
end
|
678
|
+
|
679
|
+
if @is_client && destination_cid_seq.nil?
|
680
|
+
if @quic_logger
|
681
|
+
@quic_logger.log_event(
|
682
|
+
category: "transport",
|
683
|
+
event: "packet_dropped",
|
684
|
+
data: { trigger: "unknown_connection_id" },
|
685
|
+
)
|
686
|
+
end
|
687
|
+
return
|
688
|
+
end
|
689
|
+
|
690
|
+
# check protocol version
|
691
|
+
if @is_client &&
|
692
|
+
@state == QuicConnectionState::FIRSTFLIGHT &&
|
693
|
+
header.version == Quic::Packet::QuicProtocolVersion::NEGOTIATION &&
|
694
|
+
@version_negotiation_count == 0
|
695
|
+
# version negotiation
|
696
|
+
versions = []
|
697
|
+
|
698
|
+
versions << buf.pull_uint32 until buf.eof
|
699
|
+
|
700
|
+
if @quic_logger
|
701
|
+
@quic_logger.log_event(
|
702
|
+
category: "transport",
|
703
|
+
event: "packet_received",
|
704
|
+
data: {
|
705
|
+
frames: [],
|
706
|
+
header: {
|
707
|
+
packet_type: "version_negotiation",
|
708
|
+
scid: Connection.dump_cid(header.source_cid),
|
709
|
+
dcid: Connection.dump_cid(header.destination_cid),
|
710
|
+
},
|
711
|
+
raw: { length: buf.tell - start_off },
|
712
|
+
},
|
713
|
+
)
|
714
|
+
end
|
715
|
+
|
716
|
+
if versions.include?(@version)
|
717
|
+
# TODO: logging
|
718
|
+
return
|
719
|
+
end
|
720
|
+
|
721
|
+
common = @configuration.supported_versions & versions # TODO: correct?
|
722
|
+
if common.empty?
|
723
|
+
# TODO: logging
|
724
|
+
@close_event = Quic::Event::ConnectionTerminated.new.tap do |event|
|
725
|
+
event.error_code = Quic::Packet::QuicErrorCode::INTERNAL_ERROR
|
726
|
+
event.frame_type = Quic::Packet::QuicFrameType::PADDING
|
727
|
+
event.reason_phrase = "Could not find a common protocol version"
|
728
|
+
end
|
729
|
+
close_end
|
730
|
+
return
|
731
|
+
end
|
732
|
+
|
733
|
+
@packet_number = 0
|
734
|
+
@version = Quic::Packet::QuicProtocolVersion::VERSION_1 # TODO: check version
|
735
|
+
@version_negotiation_count += 1
|
736
|
+
_connect(now: now)
|
737
|
+
return
|
738
|
+
elsif header.version && !@configuration.supported_versions.include?(header.version)
|
739
|
+
# unsupported version
|
740
|
+
if @quic_logger
|
741
|
+
@quic_logger.log_event(
|
742
|
+
category: "transport",
|
743
|
+
event: "packet_dropped",
|
744
|
+
data: { trigger: "unsupported_version" },
|
745
|
+
)
|
746
|
+
end
|
747
|
+
return
|
748
|
+
end
|
749
|
+
|
750
|
+
# handle retry packet
|
751
|
+
# binding.irb
|
752
|
+
if header.packet_type == Quic::Packet::PACKET_TYPE_RETRY
|
753
|
+
tag = Quic::Packet.get_retry_integrity_tag(
|
754
|
+
packet_without_tag: buf.data_slice(
|
755
|
+
start: start_off,
|
756
|
+
ends: buf.tell - Quic::Packet::RETRY_INTEGRITY_TAG_SIZE,
|
757
|
+
),
|
758
|
+
original_destination_cid: @peer_cid.cid,
|
759
|
+
)
|
760
|
+
if @is_client && @retry_count.zero? && header.destination_cid == @host_cid && header.integrity_tag == tag
|
761
|
+
if @quic_logger
|
762
|
+
@quic_logger.log_event(
|
763
|
+
category: "transport",
|
764
|
+
event: "packet_received",
|
765
|
+
data: {
|
766
|
+
frames: [],
|
767
|
+
header: {
|
768
|
+
packet_type: "retry",
|
769
|
+
scid: Connection.dump_cid(header.source_cid),
|
770
|
+
dcid: Connection.dump_cid(header.destination_cid),
|
771
|
+
},
|
772
|
+
raw: { length: buf.tell - start_off },
|
773
|
+
},
|
774
|
+
)
|
775
|
+
end
|
776
|
+
@peer_cid.cid = header.source_cid
|
777
|
+
@peer_token = header.token
|
778
|
+
@retry_count += 1
|
779
|
+
@retry_source_connection_id = header.source_cid
|
780
|
+
_connect(now: now)
|
781
|
+
else
|
782
|
+
# unexpected or invalid retry packet
|
783
|
+
if @quic_logger
|
784
|
+
@quic_logger.log_event(
|
785
|
+
category: "transport",
|
786
|
+
event: "packet_dropped",
|
787
|
+
data: { trigger: "unexpected_packet" },
|
788
|
+
)
|
789
|
+
end
|
790
|
+
return
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
794
|
+
network_path = find_network_path(addr)
|
795
|
+
|
796
|
+
# server initialization
|
797
|
+
if !@is_client && @state == QuicConnectionState::FIRSTFLIGHT
|
798
|
+
raise "first packet must be initial" unless header.packet_type == Quic::Packet::PACKET_TYPE_INITIAL
|
799
|
+
|
800
|
+
@network_paths = [network_path]
|
801
|
+
@version = Quic::Packet::QuicProtocolVersion::VERSION_1 # TODO: version
|
802
|
+
initialize_connection(header.destination_cid)
|
803
|
+
end
|
804
|
+
|
805
|
+
# determine crypto and packet space
|
806
|
+
epoch = Connection.get_epoch(header.packet_type)
|
807
|
+
crypto = @cryptos[epoch]
|
808
|
+
space = if epoch == TLS::Epoch::ZERO_RTT
|
809
|
+
@spaces[TLS::Epoch::ONE_RTT]
|
810
|
+
else
|
811
|
+
@spaces[epoch]
|
812
|
+
end
|
813
|
+
|
814
|
+
# decrypt packet
|
815
|
+
encrypted_off = buf.tell - start_off
|
816
|
+
end_off = buf.tell + header.rest_length
|
817
|
+
buf.seek(end_off)
|
818
|
+
|
819
|
+
begin
|
820
|
+
plain_header, plain_payload, packet_number = crypto.decrypt_packet(
|
821
|
+
packet: data[start_off...end_off], encrypted_offset: encrypted_off, expected_packet_number: space.expected_packet_number,
|
822
|
+
)
|
823
|
+
rescue ArgumentError # TODO: KeyUnavailableError
|
824
|
+
@quic_logger&.log_event(category: "transport", event: "packet_dropped", data: { trigger: "key_unavailable" })
|
825
|
+
|
826
|
+
# if a client receives HANDSHAKE or 1-RTT packets before it has handshake keys, it can assume that the server's INITIAL was lost
|
827
|
+
if @is_client && [TLS::Epoch::HANDSHAKE, TLS::Epoch::ONE_RTT].include?(epoch) && !@crypto_retransmitted
|
828
|
+
@loss.reschedule_data(now: now)
|
829
|
+
@crypto_retransmitted = true
|
830
|
+
end
|
831
|
+
next
|
832
|
+
rescue Quic::Crypto::CryptoError, OpenSSL::Cipher::CipherError # TODO: raise CryptoError from crypto module
|
833
|
+
@quic_logger&.log_event(category: "transport", event: "packet_dropped", data: { trigger: "payload_decrypt_error" })
|
834
|
+
next
|
835
|
+
end
|
836
|
+
|
837
|
+
# check reserved bits
|
838
|
+
reserved_mask = header.is_long_header ? 0x0c : 0x18
|
839
|
+
if plain_header[0].unpack1("C*") & reserved_mask != 0
|
840
|
+
close(
|
841
|
+
error_code: Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION,
|
842
|
+
frame_type: Quic::Packet::QuicFrameType::PADDING,
|
843
|
+
reason_phrase: "Reserved bits must be zero",
|
844
|
+
)
|
845
|
+
end
|
846
|
+
|
847
|
+
# log packet
|
848
|
+
quic_logger_frames = []
|
849
|
+
if @quic_logger
|
850
|
+
@quic_logger.log_event(
|
851
|
+
category: "transport",
|
852
|
+
event: "packet_received",
|
853
|
+
data: {
|
854
|
+
frames: quic_logger_frames,
|
855
|
+
header: {
|
856
|
+
packet_number: packet_number,
|
857
|
+
packet_type: @quic_logger.packet_type(header.packet_type),
|
858
|
+
dcid: Connection.dump_cid(header.destination_cid),
|
859
|
+
scid: Connection.dump_cid(header.source_cid),
|
860
|
+
},
|
861
|
+
raw: { length: end_off - start_off },
|
862
|
+
},
|
863
|
+
)
|
864
|
+
end
|
865
|
+
|
866
|
+
# raise expected packet number
|
867
|
+
space.expected_packet_number = packet_number + 1 if packet_number > space.expected_packet_number
|
868
|
+
|
869
|
+
# discard initial keys and packet space
|
870
|
+
discard_epoch(TLS::Epoch::INITIAL) if !@is_client && epoch == TLS::Epoch::HANDSHAKE
|
871
|
+
|
872
|
+
# update status
|
873
|
+
if @peer_cid.sequence_number.nil?
|
874
|
+
@peer_cid.cid = header.source_cid
|
875
|
+
@peer_cid.sequence_number = 0
|
876
|
+
end
|
877
|
+
|
878
|
+
if @state == Quic::Connection::QuicConnectionState::FIRSTFLIGHT
|
879
|
+
@remote_initial_source_connection_id = header.source_cid
|
880
|
+
set_state(Quic::Connection::QuicConnectionState::CONNECTED)
|
881
|
+
end
|
882
|
+
|
883
|
+
# update spin bit
|
884
|
+
if !header.is_long_header && packet_number > @spin_highest_pn
|
885
|
+
spin_bit = Quic::Packet.get_spin_bit(plain_header[0])
|
886
|
+
@spin_bit = if @is_client
|
887
|
+
!spin_bit
|
888
|
+
else
|
889
|
+
spin_bit
|
890
|
+
end
|
891
|
+
@spin_highest_pn = packet_number
|
892
|
+
@quic_logger&.log_event(category: "connectivity", event: "spin_bit_updated", data: { state: @spin_bit })
|
893
|
+
end
|
894
|
+
|
895
|
+
# handle payload
|
896
|
+
context = Quic::Connection::QuicReceiveContext.new.tap do |ctx|
|
897
|
+
ctx.epoch = epoch
|
898
|
+
ctx.host_cid = header.destination_cid
|
899
|
+
ctx.network_path = network_path
|
900
|
+
ctx.quic_logger_frames = quic_logger_frames
|
901
|
+
ctx.time = now
|
902
|
+
end
|
903
|
+
|
904
|
+
begin
|
905
|
+
is_ack_eliciting, is_probing = payload_received(context: context, plain: plain_payload)
|
906
|
+
rescue QuicConnectionError => err
|
907
|
+
# TODO: logging
|
908
|
+
close(error_code: err.error_code, frame_type: err.frame_type, reason_phrase: err.reason_phrase)
|
909
|
+
end
|
910
|
+
return if END_STATES.include?(@state) || @close_pending
|
911
|
+
|
912
|
+
# update idle timeout
|
913
|
+
@close_at = now + @configuration.idle_timeout
|
914
|
+
|
915
|
+
# handle migration
|
916
|
+
if !@is_client && context.host_cid != @host_cid && epoch == TLS::Epoch::ONE_RTT
|
917
|
+
# TODO: logging
|
918
|
+
# raise RuntimeError
|
919
|
+
@host_cid = context.host_cid
|
920
|
+
change_connection_id
|
921
|
+
end
|
922
|
+
|
923
|
+
# update network path
|
924
|
+
if !network_path.is_validated && epoch == TLS::Epoch::HANDSHAKE
|
925
|
+
# TODO: logging
|
926
|
+
network_path.is_validated = true
|
927
|
+
end
|
928
|
+
network_path.bytes_received += end_off - start_off
|
929
|
+
@network_paths << network_path unless @network_paths.include?(network_path)
|
930
|
+
idx = @network_paths.index(network_path)
|
931
|
+
if idx && idx > 0 && !is_probing && packet_number > space.largest_received_packet
|
932
|
+
# TODO: logging
|
933
|
+
@network_paths.delete_at(idx)
|
934
|
+
@network_paths.insert(0, network_path)
|
935
|
+
end
|
936
|
+
|
937
|
+
# record packet as received
|
938
|
+
unless space.discarded
|
939
|
+
if packet_number > space.largest_received_packet
|
940
|
+
space.largest_received_packet = packet_number
|
941
|
+
space.largest_received_time = now
|
942
|
+
end
|
943
|
+
space.ack_queue.add(packet_number)
|
944
|
+
space.ack_at = now + @ack_delay if is_ack_eliciting && space.ack_at.nil?
|
945
|
+
end
|
946
|
+
end
|
947
|
+
end
|
948
|
+
|
949
|
+
# Request an update of the encryption kyes.
|
950
|
+
def request_key_update
|
951
|
+
raise "cannot change key before handshake compeletes" unless @handshake_complete
|
952
|
+
@cryptos[TLS::Epoch::ONE_RTT].update_key
|
953
|
+
end
|
954
|
+
|
955
|
+
# Abruptly terminate the sending part of a stream.
|
956
|
+
#
|
957
|
+
# param stream_id: The stream's ID.
|
958
|
+
# param error_code: An error code inidicating why the stream is being reset.
|
959
|
+
def reset_stream(stream_id:, error_code:)
|
960
|
+
stream = get_or_create_stream_for_send(stream_id)
|
961
|
+
stream.sender.reset(error_code: error_code)
|
962
|
+
end
|
963
|
+
|
964
|
+
# Send a PING frame to the peer.
|
965
|
+
#
|
966
|
+
# param: uid: A unique ID for this PING.
|
967
|
+
def send_ping(uid)
|
968
|
+
@ping_pending.append(uid)
|
969
|
+
end
|
970
|
+
|
971
|
+
# Send a DATAGRAM frame.
|
972
|
+
#
|
973
|
+
# param data: The data to be sent.
|
974
|
+
def send_datagram_frame(data)
|
975
|
+
@datagrams_pending.append(data)
|
976
|
+
end
|
977
|
+
|
978
|
+
# Send data on the specific stream.
|
979
|
+
#
|
980
|
+
# param stream_id: The stream's ID.
|
981
|
+
# param data: The data to be sent.
|
982
|
+
# param end_stream: If set to `true`, the FIN bit will be set.
|
983
|
+
def send_stream_data(stream_id:, data:, end_stream: false)
|
984
|
+
stream = get_or_create_stream_for_send(stream_id)
|
985
|
+
stream.sender.write(data: data, end_stream: end_stream)
|
986
|
+
end
|
987
|
+
|
988
|
+
# Request termination of the receiveing part of a stream.
|
989
|
+
#
|
990
|
+
# param stream_id: The stream's ID.
|
991
|
+
# param error_code: An error code indicatig why the stream is being stopped.
|
992
|
+
def stop_stream(stream_id:, error_code:)
|
993
|
+
raise ValueError, "Cannot stop receiving on a local-initiated unidirectional stream" unless stream_can_receive(stream_id)
|
994
|
+
|
995
|
+
stream = @streams[stream_id]
|
996
|
+
raise ValueError, "Cannot stop receiving on an unknown stream" unless stream
|
997
|
+
|
998
|
+
stream.receiver.stop(error_code: error_code)
|
999
|
+
end
|
1000
|
+
|
1001
|
+
# Callback which is invoked by the TLS engine when ALPN negotiation completes.
|
1002
|
+
private def alpn_handler(alpn_protocol)
|
1003
|
+
# binding.irb
|
1004
|
+
@events.append(Event::ProtocolNegotiated.new.tap { |e| e.alpn_protocol = alpn_protocol })
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
# Check the specified strem can receive data or raises a QuicConnectionError.
|
1008
|
+
private def assert_stream_can_receive(frame_type:, stream_id:)
|
1009
|
+
unless stream_can_receive(stream_id)
|
1010
|
+
err = QuicConnectionError.new.tap do |e|
|
1011
|
+
e.error_code = Quic::Packet::QuicErrorCode::STREAM_STATE_ERROR
|
1012
|
+
e.frame_type = frame_type
|
1013
|
+
e.reason_phrase = "Stream is send-only"
|
1014
|
+
end
|
1015
|
+
raise err
|
1016
|
+
end
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
# Check the specified strem can send data or raises a QuicConnectionError.
|
1020
|
+
private def assert_stream_can_send(frame_type:, stream_id:)
|
1021
|
+
unless stream_can_send(stream_id)
|
1022
|
+
err = QuicConnectionError.new.tap do |e|
|
1023
|
+
e.error_code = Quic::Packet::QuicErrorCode::STREAM_STATE_ERROR
|
1024
|
+
e.frame_type = frame_type
|
1025
|
+
e.reason_phrase = "Stream is receive-only"
|
1026
|
+
end
|
1027
|
+
raise err
|
1028
|
+
end
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
# Update the destination connection ID by taking the next available connection ID provided by the peer.
|
1032
|
+
private def consume_peer_cid
|
1033
|
+
@peer_cid = @peer_cid_available.delete_at(0)
|
1034
|
+
# TODO: logging
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
# Begin the close procedure.
|
1038
|
+
private def close_begin(is_initiator:, now:)
|
1039
|
+
@close_at = now + (3 * @loss.get_probe_timeout)
|
1040
|
+
if is_initiator
|
1041
|
+
set_state(QuicConnectionState::CLOSING)
|
1042
|
+
else
|
1043
|
+
set_state(QuicConnectionState::DRAINING)
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
# End the close procedure.
|
1048
|
+
private def close_end
|
1049
|
+
@close_at = nil
|
1050
|
+
@spaces.keys.each do |epoch|
|
1051
|
+
discard_epoch(epoch)
|
1052
|
+
end
|
1053
|
+
@events << @close_event
|
1054
|
+
set_state(QuicConnectionState::TERMINATED)
|
1055
|
+
# "signal log end"
|
1056
|
+
if @quic_logger
|
1057
|
+
@configuration.quic_logger.end_trace(@quic_logger)
|
1058
|
+
@quic_logger = nil
|
1059
|
+
end
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
# Start the client handshake.
|
1063
|
+
private def _connect(now:)
|
1064
|
+
raise unless @is_client
|
1065
|
+
|
1066
|
+
@close_at = now + @configuration.idle_timeout
|
1067
|
+
initialize_connection(@peer_cid.cid)
|
1068
|
+
|
1069
|
+
@tls.handle_message(input_data: "", output_buf: @crypto_buffers)
|
1070
|
+
push_crypto_data
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
private def discard_epoch(epoch)
|
1074
|
+
unless @spaces[epoch].discarded
|
1075
|
+
# TODO: logger
|
1076
|
+
@cryptos[epoch].teardown
|
1077
|
+
@loss.discard_space(space: @spaces[epoch])
|
1078
|
+
@spaces[epoch].discarded = true
|
1079
|
+
end
|
1080
|
+
end
|
1081
|
+
|
1082
|
+
private def find_network_path(addr)
|
1083
|
+
# check existing network paths
|
1084
|
+
@network_paths.each do |network_path|
|
1085
|
+
return network_path if network_path.addr == addr
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
# new network path
|
1089
|
+
network_path = QuicNetworkPath.new.tap { |n| n.addr = addr; n.bytes_received = 0; }
|
1090
|
+
# TODO: logging
|
1091
|
+
return network_path
|
1092
|
+
end
|
1093
|
+
|
1094
|
+
# Get or create a stream in response to a received frame.
|
1095
|
+
private def get_or_create_stream(frame_type:, stream_id:)
|
1096
|
+
raise Quic::Stream::StreamFinishedError if @streams_finished.include?(stream_id)
|
1097
|
+
|
1098
|
+
stream = @streams[stream_id]
|
1099
|
+
unless stream
|
1100
|
+
# check initiator
|
1101
|
+
if Connection.stream_is_client_initiated(stream_id || 0) == @is_client
|
1102
|
+
# binding.irb
|
1103
|
+
err = QuicConnectionError.new.tap do |e|
|
1104
|
+
e.error_code = Quic::Packet::QuicErrorCode::STREAM_STATE_ERROR
|
1105
|
+
e.frame_type = frame_type
|
1106
|
+
e.reason_phrase = "Wrong stream initiator"
|
1107
|
+
end
|
1108
|
+
raise err
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
# determine limits
|
1112
|
+
if Connection.stream_is_unidirectional(stream_id)
|
1113
|
+
max_stream_data_local = @local_max_stream_data_uni
|
1114
|
+
max_stream_data_remote = 0
|
1115
|
+
max_streams = @local_max_streams_uni
|
1116
|
+
else
|
1117
|
+
max_stream_data_local = @local_max_stream_data_bidi_local
|
1118
|
+
max_stream_data_remote = @remote_max_stream_data_bidi_local
|
1119
|
+
max_streams = @local_max_streams_bidi
|
1120
|
+
end
|
1121
|
+
|
1122
|
+
# check max streams
|
1123
|
+
stream_count = (stream_id.floor(4)) + 1
|
1124
|
+
if stream_count > max_streams.value
|
1125
|
+
err = QuicConnectionError.new.tap do |e|
|
1126
|
+
e.error_code = Quic::Packet::QuicErrorCode::STREAM_LIMIT_ERROR
|
1127
|
+
e.frame_type = frame_type
|
1128
|
+
e.reason_phrase = "Too many streams open"
|
1129
|
+
end
|
1130
|
+
raise err
|
1131
|
+
elsif stream_count > max_streams.used
|
1132
|
+
max_streams.used = stream_count
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
# create stream
|
1136
|
+
# TODO: logger
|
1137
|
+
stream = @streams[stream_id] = Stream::QuicStream.new(
|
1138
|
+
stream_id: stream_id,
|
1139
|
+
max_stream_data_local: max_stream_data_local,
|
1140
|
+
max_stream_data_remote: max_stream_data_remote,
|
1141
|
+
writable: !Connection.stream_is_unidirectional(stream_id),
|
1142
|
+
)
|
1143
|
+
end
|
1144
|
+
return stream
|
1145
|
+
end
|
1146
|
+
|
1147
|
+
# Get or create a QUIC stream in order to send datra to the peer.
|
1148
|
+
#
|
1149
|
+
# This always occurs as a result of an API call.
|
1150
|
+
private def get_or_create_stream_for_send(stream_id)
|
1151
|
+
raise ValueError, "Cannot send data on peer-initiated unidirectional stream" unless stream_can_send(stream_id)
|
1152
|
+
|
1153
|
+
stream = @streams[stream_id]
|
1154
|
+
unless stream
|
1155
|
+
# check initiator
|
1156
|
+
raise ValueError, "Cannot send data on unknown peer-initiated stream" if Connection.stream_is_client_initiated(stream_id) != @is_client
|
1157
|
+
|
1158
|
+
# determine limits
|
1159
|
+
if Connection.stream_is_unidirectional(stream_id)
|
1160
|
+
max_stream_data_local = 0
|
1161
|
+
max_stream_data_remote = @remote_max_stream_data_uni
|
1162
|
+
max_streams = @remote_max_streams_uni
|
1163
|
+
streams_blocked = @streams_blocked_uni
|
1164
|
+
else
|
1165
|
+
max_stream_data_local = @local_max_stream_data_bidi_local
|
1166
|
+
max_stream_data_remote = @remote_max_stream_data_bidi_remote
|
1167
|
+
max_streams = @remote_max_streams_bidi
|
1168
|
+
streams_blocked = @streams_blocked_bidi
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
# create stream
|
1172
|
+
stream = @streams[stream_id] = Stream::QuicStream.new(
|
1173
|
+
stream_id: stream_id,
|
1174
|
+
max_stream_data_local: max_stream_data_local,
|
1175
|
+
max_stream_data_remote: max_stream_data_remote,
|
1176
|
+
readable: !Connection.stream_is_unidirectional(stream_id),
|
1177
|
+
)
|
1178
|
+
|
1179
|
+
# mark stream as blocked if needed
|
1180
|
+
if stream_id.floor(4) >= max_streams
|
1181
|
+
stream.is_blocked = true
|
1182
|
+
streams_blocked << stream
|
1183
|
+
@streams_blocked_pending = true
|
1184
|
+
end
|
1185
|
+
end
|
1186
|
+
return stream
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
private def handle_session_ticket(session_ticket)
|
1190
|
+
if session_ticket.max_early_data_size && session_ticket.max_early_data_size != MAX_EARLY_DATA
|
1191
|
+
err = QuicConnectionError.new.tap do |e|
|
1192
|
+
e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
|
1193
|
+
e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
|
1194
|
+
e.reason_phrase = "Invalid max_early_data value #{session_ticket.max_early_data_size}"
|
1195
|
+
end
|
1196
|
+
raise err
|
1197
|
+
end
|
1198
|
+
@session_ticket_handler&.call(session_ticket)
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
private def initialize_connection(peer_cid)
|
1202
|
+
# TLS
|
1203
|
+
@tls = TLS::Context.new(
|
1204
|
+
alpn_protocols: @configuration.alpn_protocols,
|
1205
|
+
cadata: @configuration.cadata,
|
1206
|
+
cafile: @configuration.cafile,
|
1207
|
+
capath: @configuration.capath,
|
1208
|
+
cipher_suites: @configuration.cipher_suites,
|
1209
|
+
is_client: @is_client,
|
1210
|
+
logger: @logger,
|
1211
|
+
max_early_data: (@is_client ? nil : MAX_EARLY_DATA),
|
1212
|
+
server_name: @configuration.server_name,
|
1213
|
+
verify_mode: @configuration.verify_mode,
|
1214
|
+
)
|
1215
|
+
@tls.certificate = @configuration.certificate
|
1216
|
+
@tls.certificate_chain = @configuration.certificate_chain
|
1217
|
+
@tls.certificate_private_key = @configuration.private_key
|
1218
|
+
@tls.handshake_extensions = [[Connection.get_transport_parameters_extension(@version), serialize_transport_parameters]]
|
1219
|
+
|
1220
|
+
# TLS session resumption
|
1221
|
+
session_ticket = @configuration.session_ticket
|
1222
|
+
if @is_client && session_ticket && session_ticket.is_valid && session_ticket.server_name == @configuration.server_name
|
1223
|
+
@tls.session_ticket = @configuration.session_ticket
|
1224
|
+
|
1225
|
+
# parse saved QUIC transport parameters - for 0-RTT
|
1226
|
+
if session_ticket.max_early_data_size == MAX_EARLY_DATA
|
1227
|
+
session_ticket.other_extensions.each do |ext_type, ext_data|
|
1228
|
+
if ext_type == Connection.get_transport_parameters_extension(@version)
|
1229
|
+
parse_transport_parameters(data: ext_data, from_session_ticket: true)
|
1230
|
+
break
|
1231
|
+
end
|
1232
|
+
end
|
1233
|
+
end
|
1234
|
+
end
|
1235
|
+
|
1236
|
+
# TLS callbacks
|
1237
|
+
@tls.alpn_cb = method(:alpn_handler)
|
1238
|
+
@tls.get_session_ticket_cb = @session_ticket_fetcher if @session_ticket_fetcher
|
1239
|
+
@tls.new_session_ticket_cb = method(:handle_session_ticket) if @session_ticket_handler
|
1240
|
+
@tls.update_traffic_key_cb = method(:update_traffic_key)
|
1241
|
+
# binding.irb
|
1242
|
+
|
1243
|
+
# packet spaces
|
1244
|
+
create_crypto_pair = lambda do |epoch|
|
1245
|
+
eopch_name = %w(initial 0rtt handshake 1rtt)[epoch]
|
1246
|
+
secret_names = ["server_#{eopch_name}_secret", "client_#{eopch_name}_secret"]
|
1247
|
+
recv_secret_name = secret_names[@is_client ? 0 : 1]
|
1248
|
+
send_secret_name = secret_names[@is_client ? 1 : 0]
|
1249
|
+
# TODO: keylog
|
1250
|
+
return Crypto::CryptoPair.new(
|
1251
|
+
recv_setup_cb: ->(trigger) { log_key_updated(key_type: recv_secret_name, trigger: trigger) },
|
1252
|
+
recv_teardown_cb: ->(trigger) { log_key_retired(key_type: recv_secret_name, trigger: trigger) },
|
1253
|
+
send_setup_cb: ->(trigger) { log_key_updated(key_type: send_secret_name, trigger: trigger) },
|
1254
|
+
send_teardown_cb: ->(trigger) { log_key_retired(key_type: send_secret_name, trigger: trigger) },
|
1255
|
+
)
|
1256
|
+
end
|
1257
|
+
|
1258
|
+
@cryptos = [TLS::Epoch::INITIAL, TLS::Epoch::ZERO_RTT, TLS::Epoch::HANDSHAKE, TLS::Epoch::ONE_RTT].inject({}) do |hash, epoch|
|
1259
|
+
hash[epoch] = create_crypto_pair.call(epoch)
|
1260
|
+
hash
|
1261
|
+
end
|
1262
|
+
@crypto_buffers = {
|
1263
|
+
TLS::Epoch::INITIAL => Buffer.new(capacity: CRYPTO_BUFFER_SIZE),
|
1264
|
+
TLS::Epoch::HANDSHAKE => Buffer.new(capacity: CRYPTO_BUFFER_SIZE),
|
1265
|
+
TLS::Epoch::ONE_RTT => Buffer.new(capacity: CRYPTO_BUFFER_SIZE),
|
1266
|
+
}
|
1267
|
+
@crypto_streams = {
|
1268
|
+
TLS::Epoch::INITIAL => Stream::QuicStream.new,
|
1269
|
+
TLS::Epoch::HANDSHAKE => Stream::QuicStream.new,
|
1270
|
+
TLS::Epoch::ONE_RTT => Stream::QuicStream.new,
|
1271
|
+
}
|
1272
|
+
@spaces = {
|
1273
|
+
TLS::Epoch::INITIAL => Recovery::QuicPacketSpace.new,
|
1274
|
+
TLS::Epoch::HANDSHAKE => Recovery::QuicPacketSpace.new,
|
1275
|
+
TLS::Epoch::ONE_RTT => Recovery::QuicPacketSpace.new,
|
1276
|
+
}
|
1277
|
+
|
1278
|
+
@cryptos[TLS::Epoch::INITIAL].setup_initial(cid: peer_cid, is_client: @is_client, version: @version)
|
1279
|
+
@loss.spaces = @spaces.values
|
1280
|
+
end
|
1281
|
+
|
1282
|
+
# Handle an ACK frame.
|
1283
|
+
private def handle_ack_frame(context:, frame_type:, buf:)
|
1284
|
+
ack_rangeset, ack_delay_encoded = Quic::Packet.pull_ack_frame(buf)
|
1285
|
+
if frame_type == Quic::Packet::QuicFrameType::ACK_ECN
|
1286
|
+
3.times { buf.pull_uint_var }
|
1287
|
+
end
|
1288
|
+
ack_delay = (ack_delay_encoded << @remote_ack_delay_exponent) / 1000000
|
1289
|
+
|
1290
|
+
if @quic_logger
|
1291
|
+
context.quic_logger_frames << @quic_logger.encode_ack_frame(ranges: ack_rangeset, delay: ack_delay)
|
1292
|
+
end
|
1293
|
+
|
1294
|
+
# check whether per completed address validation
|
1295
|
+
if !@loss.peer_completed_address_validation && [TLS::Epoch::HANDSHAKE, TLS::Epoch::ONE_RTT].include?(context.epoch)
|
1296
|
+
@loss.peer_completed_address_validation = true
|
1297
|
+
end
|
1298
|
+
|
1299
|
+
@loss.on_ack_received(space: @spaces[context.epoch], ack_rangeset: ack_rangeset, ack_delay: ack_delay, now: context.time)
|
1300
|
+
end
|
1301
|
+
|
1302
|
+
# Handle a CONNECTION_CLOSE frame.
|
1303
|
+
private def handle_connection_close_frame(context:, frame_type:, buf:)
|
1304
|
+
error_code = buf.pull_uint_var
|
1305
|
+
if frame_type == Quic::Packet::QuicFrameType::TRANSPORT_CLOSE
|
1306
|
+
frame_type = buf.pull_uint_var
|
1307
|
+
else
|
1308
|
+
frame_type = nil
|
1309
|
+
end
|
1310
|
+
reason_length = buf.pull_uint_var
|
1311
|
+
begin
|
1312
|
+
reason_phrase = buf.pull_bytes(reason_length).encode(Encoding::UTF_8)
|
1313
|
+
rescue EncodingError
|
1314
|
+
reason_phrase = ""
|
1315
|
+
end
|
1316
|
+
|
1317
|
+
# log frame
|
1318
|
+
if @quic_logger
|
1319
|
+
context.quic_logger_frames << @quic_logger.encode_connection_close_frame(error_code: error_code, frame_type: frame_type, reason_phrase: reason_phrase)
|
1320
|
+
end
|
1321
|
+
|
1322
|
+
unless @close_event
|
1323
|
+
@close_event = Event::ConnectionTerminated.new.tap do |event|
|
1324
|
+
event.error_code = error_code
|
1325
|
+
event.frame_type = frame_type
|
1326
|
+
event.reason_phrase = reason_phrase
|
1327
|
+
end
|
1328
|
+
close_begin(is_initiator: false, now: context.time)
|
1329
|
+
end
|
1330
|
+
end
|
1331
|
+
|
1332
|
+
# Handle a CRYPTO frame.
|
1333
|
+
private def handle_crypto_frame(context:, frame_type:, buf:)
|
1334
|
+
# binding.b
|
1335
|
+
offset = buf.pull_uint_var
|
1336
|
+
length = buf.pull_uint_var
|
1337
|
+
if offset + length > Buffer::UINT_VAR_MAX
|
1338
|
+
err = QuicConnectionError.new.tap do |e|
|
1339
|
+
e.error_code = Quic::Packet::QuicErrorCode::FRAME_ENCODING_ERROR
|
1340
|
+
e.frame_type = frame_type
|
1341
|
+
e.reason_phrase = "offset + length cannot exceed 2^62 - 1"
|
1342
|
+
end
|
1343
|
+
raise err
|
1344
|
+
end
|
1345
|
+
frame = Quic::Packet::QuicStreamFrame.new.tap do |f|
|
1346
|
+
f.offset = offset
|
1347
|
+
f.data = buf.pull_bytes(length)
|
1348
|
+
end
|
1349
|
+
|
1350
|
+
# log frame
|
1351
|
+
if @quic_logger
|
1352
|
+
context.quic_logger_frames << @quic_logger.encode_crypto_frame(frame: frame)
|
1353
|
+
end
|
1354
|
+
|
1355
|
+
stream = @crypto_streams[context.epoch]
|
1356
|
+
event = stream.receiver.handle_frame(frame: frame)
|
1357
|
+
if event
|
1358
|
+
# pass data to TLS layer
|
1359
|
+
begin
|
1360
|
+
# binding.irb
|
1361
|
+
@tls.handle_message(input_data: event.data, output_buf: @crypto_buffers)
|
1362
|
+
push_crypto_data
|
1363
|
+
rescue TLS::Alert => e
|
1364
|
+
error = QuicConnectionError.new.tap do |err|
|
1365
|
+
err.error_code = Quic::Packet::QuicErrorCode::CRYPTO_ERROR + e.description
|
1366
|
+
err.frame_type = frame_type
|
1367
|
+
err.reason_phrase = e.message
|
1368
|
+
end
|
1369
|
+
raise error
|
1370
|
+
end
|
1371
|
+
|
1372
|
+
# parse transport parameters
|
1373
|
+
if !@parameters_received && @tls.received_extensions
|
1374
|
+
@tls.received_extensions.each do |ext_type, ext_data|
|
1375
|
+
if ext_type == Connection.get_transport_parameters_extension(@version)
|
1376
|
+
parse_transport_parameters(data: ext_data)
|
1377
|
+
@parameters_received = true
|
1378
|
+
break
|
1379
|
+
end
|
1380
|
+
end
|
1381
|
+
unless @parameters_received
|
1382
|
+
err = QuicConnectionError.new.tap do |e|
|
1383
|
+
e.error_code = Quic::Packet::QuicErrorCode::CRYPTO_ERROR + TLS::AlertDescription::MISSING_EXTENSION
|
1384
|
+
e.frame_type = frame_type
|
1385
|
+
e.reason_phrase = "No QUIC transport parameters received"
|
1386
|
+
end
|
1387
|
+
raise err
|
1388
|
+
end
|
1389
|
+
end
|
1390
|
+
|
1391
|
+
# update current epoch
|
1392
|
+
# binding.irb unless @is_client
|
1393
|
+
if !@handshake_complete && [TLS::State::CLIENT_POST_HANDSHAKE, TLS::State::SERVER_POST_HANDSHAKE].include?(@tls.state)
|
1394
|
+
@handshake_complete = true
|
1395
|
+
|
1396
|
+
# for servers, the handshake is now confirmed
|
1397
|
+
unless @is_client
|
1398
|
+
discard_epoch(TLS::Epoch::HANDSHAKE)
|
1399
|
+
@handshake_confirmed = true
|
1400
|
+
@handshake_done_pending = true
|
1401
|
+
end
|
1402
|
+
replenish_connection_ids
|
1403
|
+
@events.append(
|
1404
|
+
Event::HandshakeCompleted.new.tap do |ev|
|
1405
|
+
ev.alpn_protocol = @tls.alpn_negotiated
|
1406
|
+
ev.early_data_accepted = !!@tls.early_data_accepted
|
1407
|
+
ev.session_resumed = @tls.session_resumed
|
1408
|
+
end
|
1409
|
+
)
|
1410
|
+
unblock_streams(false)
|
1411
|
+
unblock_streams(true)
|
1412
|
+
# TODO: logger
|
1413
|
+
end
|
1414
|
+
else
|
1415
|
+
# TODO: logger
|
1416
|
+
|
1417
|
+
# if a server receives duplicate CRYPTO in an INITIAL packet, it can assume the client did not receive the server's CRYPTO
|
1418
|
+
if !@is_client && context.epoch == TLS::Epoch::INITIAL && !@crypto_retransmitted
|
1419
|
+
@loss.reschedule_data(now: context.time)
|
1420
|
+
@crypto_retransmitted = true
|
1421
|
+
end
|
1422
|
+
end
|
1423
|
+
end
|
1424
|
+
|
1425
|
+
# Handle a DATA_BLOCKED frame.
|
1426
|
+
private def handle_data_blocked_frame(context:, frame_type:, buf:)
|
1427
|
+
limit = buf.pull_uint_var
|
1428
|
+
|
1429
|
+
# log frame
|
1430
|
+
if @quic_logger
|
1431
|
+
context.quic_logger_frames << @quic_logger.encode_data_blocked_frame(limit: limit)
|
1432
|
+
end
|
1433
|
+
end
|
1434
|
+
|
1435
|
+
# Handle a DATAGRAM frmae.
|
1436
|
+
private def handle_datagram_frame(context:, frame_type:, buf:)
|
1437
|
+
start = buf.tell
|
1438
|
+
if frame_type == Quic::Packet::QuicFrameType::DATAGRAM_WITH_LENGTH
|
1439
|
+
length = buf.pull_uint_var
|
1440
|
+
else
|
1441
|
+
length = buf.capacity - start
|
1442
|
+
end
|
1443
|
+
data = buf.pull_bytes(length)
|
1444
|
+
|
1445
|
+
# log frame
|
1446
|
+
if @quic_logger
|
1447
|
+
context.quic_logger_frames << @quic_logger.encode_datagram_frame(length: length)
|
1448
|
+
end
|
1449
|
+
|
1450
|
+
# check frame is allowed
|
1451
|
+
if !@configuration.max_datagram_frame_size || buf.tell - start >= @configuration.max_datagram_frame_size
|
1452
|
+
err = QuicConnectionError.new.tap do |e|
|
1453
|
+
e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
|
1454
|
+
e.frame_type = frame_type
|
1455
|
+
e.reason_phrase = "Unexpected DATAGRAM frame"
|
1456
|
+
end
|
1457
|
+
raise err
|
1458
|
+
end
|
1459
|
+
@events.append(Event::DatagramFrameReceived.new.tap { |e| e.data = data })
|
1460
|
+
end
|
1461
|
+
|
1462
|
+
# Handle a HANDSHAKE_DONE frame.
|
1463
|
+
private def handle_handshake_done_frame(context:, frame_type:, buf:)
|
1464
|
+
# log frame
|
1465
|
+
context.quic_logger_frames << @quic_logger.encode_handshake_done_frame if @quic_logger
|
1466
|
+
|
1467
|
+
unless @is_client
|
1468
|
+
err = QuicConnectionError.new.tap do |e|
|
1469
|
+
e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
|
1470
|
+
e.frame_type = frame_type
|
1471
|
+
e.reason_phrase = "Clients must not send HANDSHAKE_DONE frames"
|
1472
|
+
end
|
1473
|
+
raise err
|
1474
|
+
end
|
1475
|
+
|
1476
|
+
# for clients. the handshake is now confirmed
|
1477
|
+
unless @handshake_confirmed
|
1478
|
+
discard_epoch(TLS::Epoch::HANDSHAKE)
|
1479
|
+
@handshake_confirmed = true
|
1480
|
+
@loss.peer_completed_address_validation = true
|
1481
|
+
end
|
1482
|
+
end
|
1483
|
+
|
1484
|
+
# Handle a MAX_DATA frame.
|
1485
|
+
#
|
1486
|
+
# This adjusts the total amount of we can send to the peer.
|
1487
|
+
private def handle_max_data_frame(context:, frame_type:, buf:)
|
1488
|
+
max_data = buf.pull_uint_var
|
1489
|
+
# log frame
|
1490
|
+
context.quic_logger_frames << @quic_logger.encode_connection_limit_frame(frame_type: frame_type, maximum: max_data) if @quic_logger
|
1491
|
+
|
1492
|
+
if max_data > @remote_max_data
|
1493
|
+
# TODO: logging
|
1494
|
+
@remote_max_data = max_data
|
1495
|
+
end
|
1496
|
+
end
|
1497
|
+
|
1498
|
+
# Handle a MAX_STREAM_DATA frame.
|
1499
|
+
#
|
1500
|
+
# This adjusts the total amount of we can send on a specific stream.
|
1501
|
+
private def handle_max_stream_data_frame(context:, frame_type:, buf:)
|
1502
|
+
stream_id = buf.pull_uint_var
|
1503
|
+
max_stream_data = buf.pull_uint_var
|
1504
|
+
|
1505
|
+
# log frame
|
1506
|
+
context.quic_logger_frames << @quic_logger.encode_max_stream_data_frame(maximum: max_stream_data, stream_id: stream_id) if @quic_logger
|
1507
|
+
|
1508
|
+
# check stream direction
|
1509
|
+
assert_stream_can_send(frame_type: frame_type, stream_id: stream_id)
|
1510
|
+
|
1511
|
+
stream = get_or_create_stream(frame_type: frame_type, stream_id: stream_id)
|
1512
|
+
if max_stream_data > stream.max_stream_data_remote
|
1513
|
+
# TODO: logging
|
1514
|
+
stream.max_stream_data_remote = max_stream_data
|
1515
|
+
end
|
1516
|
+
end
|
1517
|
+
|
1518
|
+
# Handle a MAX_STREAMS_BIDI frame.
|
1519
|
+
#
|
1520
|
+
# This raises number of bidirectional streams we can initiate to the peer.
|
1521
|
+
private def handle_max_streams_bidi_frame(context:, frame_type:, buf:)
|
1522
|
+
max_streams = buf.pull_uint_var
|
1523
|
+
if max_streams > STREAM_COUNT_MAX
|
1524
|
+
err = QuicConnectionError.new.tap do |e|
|
1525
|
+
e.error_code = Quic::Packet::QuicErrorCode::FRAME_ENCODING_ERROR
|
1526
|
+
e.frame_type = frame_type
|
1527
|
+
e.reason_phrase = "Maximum Streams cannot exceed 2^60"
|
1528
|
+
end
|
1529
|
+
raise err
|
1530
|
+
end
|
1531
|
+
|
1532
|
+
# log frame
|
1533
|
+
context.quic_logger_frames << @quic_logger.encode_connection_limit_frame(frame_type: frame_type, maximum: max_streams) if @quic_logger
|
1534
|
+
|
1535
|
+
if max_streams > @remote_max_streams_bidi
|
1536
|
+
# TODO: logging
|
1537
|
+
@remote_max_streams_bidi = max_streams
|
1538
|
+
unblock_streams(false)
|
1539
|
+
end
|
1540
|
+
end
|
1541
|
+
|
1542
|
+
# Handle a MAX_STREAMS_UNI frame.
|
1543
|
+
#
|
1544
|
+
# This raises number of unidirectional streams we can initiate to the peer.
|
1545
|
+
private def handle_max_streams_uni_frame(context:, frame_type:, buf:)
|
1546
|
+
max_streams = buf.pull_uint_var
|
1547
|
+
if max_streams > STREAM_COUNT_MAX
|
1548
|
+
err = QuicConnectionError.new.tap do |e|
|
1549
|
+
e.error_code = Quic::Packet::QuicErrorCode::FRAME_ENCODING_ERROR
|
1550
|
+
e.frame_type = frame_type
|
1551
|
+
e.reason_phrase = "Maximum Streams cannot exceed 2^60"
|
1552
|
+
end
|
1553
|
+
raise err
|
1554
|
+
end
|
1555
|
+
|
1556
|
+
# log frame
|
1557
|
+
context.quic_logger_frames << @quic_logger.encode_connection_limit_frame(frame_type: frame_type, maximum: max_streams) if @quic_logger
|
1558
|
+
|
1559
|
+
if max_streams > @remote_max_streams_uni
|
1560
|
+
# TODO: logging
|
1561
|
+
@remote_max_streams_uni = max_streams
|
1562
|
+
unblock_streams(true)
|
1563
|
+
end
|
1564
|
+
end
|
1565
|
+
|
1566
|
+
# Handle a NEW_CONNECTION_ID frame.
|
1567
|
+
private def handle_new_connection_id_frame(context:, frame_type:, buf:)
|
1568
|
+
sequence_number = buf.pull_uint_var
|
1569
|
+
retire_prior_to = buf.pull_uint_var
|
1570
|
+
length = buf.pull_uint8
|
1571
|
+
connection_id = buf.pull_bytes(length)
|
1572
|
+
stateless_reset_token = buf.pull_bytes(Quic::Packet::STATELESS_RESET_TOKEN_SIZE)
|
1573
|
+
if !connection_id || connection_id.bytesize > Quic::Packet::CONNECTION_ID_MAX_SIZE
|
1574
|
+
err = QuicConnectionError.new.tap do |e|
|
1575
|
+
e.error_code = Quic::Packet::QuicErrorCode::FRAME_ENCODING_ERROR
|
1576
|
+
e.frame_type = frame_type
|
1577
|
+
e.reason_phrase = "Length must be greater than 0 and less than 20"
|
1578
|
+
end
|
1579
|
+
raise err
|
1580
|
+
end
|
1581
|
+
|
1582
|
+
# log frame
|
1583
|
+
context.quic_logger_frames << @quic_logger.encode_new_connection_id_frame(
|
1584
|
+
connection_id: connection_id,
|
1585
|
+
retire_prior_to: retire_prior_to,
|
1586
|
+
sequence_number: sequence_number,
|
1587
|
+
stateless_reset_token: stateless_reset_token,
|
1588
|
+
) if @quic_logger
|
1589
|
+
|
1590
|
+
# sanity check
|
1591
|
+
if retire_prior_to > sequence_number
|
1592
|
+
err = QuicConnectionError.new.tap do |e|
|
1593
|
+
e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
|
1594
|
+
e.frame_type = frame_type
|
1595
|
+
e.reason_phrase = "Retire Prior To is greater than Sequence Number"
|
1596
|
+
end
|
1597
|
+
raise err
|
1598
|
+
end
|
1599
|
+
|
1600
|
+
# determine which CIDs to retire
|
1601
|
+
change_cid = false
|
1602
|
+
retire = @peer_cid_available.filter { |c| c.sequence_number < retire_prior_to }
|
1603
|
+
if @peer_cid.sequence_number < retire_prior_to
|
1604
|
+
change_cid = true
|
1605
|
+
retire.insert(0, @peer_cid)
|
1606
|
+
end
|
1607
|
+
|
1608
|
+
# update available CIDs
|
1609
|
+
@peer_cid_available = @peer_cid_available.filter { |c| c.sequence_number >= retire_prior_to }
|
1610
|
+
unless @peer_cid_sequence_numbers.include?(sequence_number)
|
1611
|
+
@peer_cid_available.append(
|
1612
|
+
QuicConnectionId.new.tap do |conn|
|
1613
|
+
conn.cid = connection_id
|
1614
|
+
conn.sequence_number = sequence_number
|
1615
|
+
conn.stateless_reset_token = stateless_reset_token
|
1616
|
+
end
|
1617
|
+
)
|
1618
|
+
@peer_cid_sequence_numbers.add(sequence_number)
|
1619
|
+
end
|
1620
|
+
|
1621
|
+
# retire previous CIDs
|
1622
|
+
retire.each { |quic_connection_id| retire_peer_cid(quic_connection_id) }
|
1623
|
+
|
1624
|
+
# assign new CID if we retired the active one
|
1625
|
+
consume_peer_cid if change_cid
|
1626
|
+
|
1627
|
+
# check number of active connection IDs, including the selected one
|
1628
|
+
if 1 + @peer_cid_available.length > @local_active_connection_id_limit
|
1629
|
+
err = QuicConnectionError.new.tap do |e|
|
1630
|
+
e.error_code = Quic::Packet::QuicErrorCode::CONNECTION_ID_LIMIT_ERROR
|
1631
|
+
e.frame_type = frame_type
|
1632
|
+
e.reason_phrase = "Too many active connection IDs"
|
1633
|
+
end
|
1634
|
+
raise err
|
1635
|
+
end
|
1636
|
+
end
|
1637
|
+
|
1638
|
+
# Handle a NEW_TOKEN frame.
|
1639
|
+
private def handle_new_token_frame(context:, frame_type:, buf:)
|
1640
|
+
length = buf.pull_uint_var
|
1641
|
+
token = buf.pull_bytes(length)
|
1642
|
+
|
1643
|
+
# log frame
|
1644
|
+
context.quic_logger_frames << @quic_logger.encode_new_token_frame(token: token) if @quic_logger
|
1645
|
+
|
1646
|
+
unless @is_client
|
1647
|
+
err = QuicConnectionError.new.tap do |e|
|
1648
|
+
e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
|
1649
|
+
e.frame_type = frame_type
|
1650
|
+
e.reason_phrase = "Clients must not send NEW_TOKEN frames"
|
1651
|
+
end
|
1652
|
+
raise err
|
1653
|
+
end
|
1654
|
+
end
|
1655
|
+
|
1656
|
+
# Handle a PADDING frame.
|
1657
|
+
private def handle_padding_frame(context:, frame_type:, buf:)
|
1658
|
+
pos = buf.tell
|
1659
|
+
# consume padding
|
1660
|
+
buf.data_slice(start: pos, ends: buf.capacity).each_byte do |byte|
|
1661
|
+
break if byte != 0 # 0x00
|
1662
|
+
pos += 1
|
1663
|
+
end
|
1664
|
+
buf.seek(pos)
|
1665
|
+
# log frame
|
1666
|
+
context.quic_logger_frames << @quic_logger.encode_padding_frame if @quic_logger
|
1667
|
+
end
|
1668
|
+
|
1669
|
+
# Handle a PATH_CHALLENGE frame.
|
1670
|
+
private def handle_path_challenge_frame(context:, frame_type:, buf:)
|
1671
|
+
data = buf.pull_bytes(8)
|
1672
|
+
# log frame
|
1673
|
+
context.quic_logger_frames << @quic_logger.encode_path_challenge_frame(data: data) if @quic_logger
|
1674
|
+
context.network_path.remote_challenge = data
|
1675
|
+
end
|
1676
|
+
|
1677
|
+
# Handle a PATH_RESPONSE frame.
|
1678
|
+
private def handle_path_response_frame(context:, frame_type:, buf:)
|
1679
|
+
data = buf.pull_bytes(8)
|
1680
|
+
# log frame
|
1681
|
+
context.quic_logger_frames << @quic_logger.encode_path_response_frame(data: data) if @quic_logger
|
1682
|
+
|
1683
|
+
if data != context.network_path.local_challenge
|
1684
|
+
err = QuicConnectionError.new.tap do |e|
|
1685
|
+
e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
|
1686
|
+
e.frame_type = frame_type
|
1687
|
+
e.reason_phrase = "Response does not match challenge"
|
1688
|
+
end
|
1689
|
+
raise err
|
1690
|
+
end
|
1691
|
+
# TODO: logging
|
1692
|
+
context.network_path.is_validated = true
|
1693
|
+
end
|
1694
|
+
|
1695
|
+
# Handle a PING frame.
|
1696
|
+
private def handle_ping_frame(context:, frame_type:, buf:)
|
1697
|
+
# log frame
|
1698
|
+
context.quic_logger_frames << @quic_logger.encode_ping_frame if @quic_logger
|
1699
|
+
end
|
1700
|
+
|
1701
|
+
# Handle a RESET_STREAM frame.
|
1702
|
+
private def handle_reset_stream_frame(context:, frame_type:, buf:)
|
1703
|
+
stream_id = buf.pull_uint_var
|
1704
|
+
error_code = buf.pull_uint_var
|
1705
|
+
final_size = buf.pull_uint_var
|
1706
|
+
|
1707
|
+
# log frame
|
1708
|
+
if @quic_logger
|
1709
|
+
context.quic_logger_frames << @quic_logger.encode_reset_stream_frame(error_code: error_code, final_size: final_size, stream_id: stream_id)
|
1710
|
+
end
|
1711
|
+
|
1712
|
+
# check stream direction
|
1713
|
+
assert_stream_can_receive(frame_type: frame_type, stream_id: stream_id)
|
1714
|
+
|
1715
|
+
# check flow-control limits
|
1716
|
+
stream = get_or_create_stream(frame_type: frame_type, stream_id: stream_id)
|
1717
|
+
if final_size > stream.max_stream_data_local
|
1718
|
+
err = QuicConnectionError.new.tap do |e|
|
1719
|
+
e.error_code = Quic::Packet::QuicErrorCode::FLOW_CONTROL_ERROR
|
1720
|
+
e.frame_type = frame_type
|
1721
|
+
e.reason_phrase = "Over stream data limit"
|
1722
|
+
end
|
1723
|
+
raise err
|
1724
|
+
end
|
1725
|
+
newly_received = [0, final_size - stream.receiver.highest_offset].max
|
1726
|
+
if @local_max_data.used + newly_received > @local_max_data.value
|
1727
|
+
err = QuicConnectionError.new.tap do |e|
|
1728
|
+
e.error_code = Quic::Packet::QuicErrorCode::FLOW_CONTROL_ERROR
|
1729
|
+
e.frame_type = frame_type
|
1730
|
+
e.reason_phrase = "Over connection data limit"
|
1731
|
+
end
|
1732
|
+
raise err
|
1733
|
+
end
|
1734
|
+
|
1735
|
+
# process reset
|
1736
|
+
# TODO: logging
|
1737
|
+
begin
|
1738
|
+
event = stream.receiver.handle_reset(final_size: final_size, error_code: error_code)
|
1739
|
+
rescue Stream::FinalSizeError => error
|
1740
|
+
err = QuicConnectionError.new.tap do |e|
|
1741
|
+
e.error_code = Quic::Packet::QuicErrorCode::FINAL_SIZE_ERROR
|
1742
|
+
e.frame_type = frame_type
|
1743
|
+
e.reason_phrase = error.message
|
1744
|
+
end
|
1745
|
+
raise err
|
1746
|
+
end
|
1747
|
+
@events.append(event) if event
|
1748
|
+
@local_max_data.used += newly_received
|
1749
|
+
end
|
1750
|
+
|
1751
|
+
# Handle a RETIRE_CONNECTION_ID frame.
|
1752
|
+
private def handle_retire_connection_id_frame(context:, frame_type:, buf:)
|
1753
|
+
sequence_number = buf.pull_uint_var
|
1754
|
+
|
1755
|
+
# log frame
|
1756
|
+
context.quic_logger_frames << @quic_logger.encode_retire_connection_id_frame(sequence_number: sequence_number) if @quic_logger
|
1757
|
+
|
1758
|
+
if sequence_number >= @host_cid_seq
|
1759
|
+
err = QuicConnectionError.new.tap do |e|
|
1760
|
+
e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
|
1761
|
+
e.frame_type = frame_type
|
1762
|
+
e.reason_phrase = "Cannot retire unknown connection ID"
|
1763
|
+
end
|
1764
|
+
raise err
|
1765
|
+
end
|
1766
|
+
|
1767
|
+
# find the connection ID by sequence number
|
1768
|
+
@host_cids.each_with_index do |connection_id, i|
|
1769
|
+
if connection_id.sequence_number == sequence_number
|
1770
|
+
if connection_id.cid == context.host_cid
|
1771
|
+
err = QuicConnectionError.new.tap do |e|
|
1772
|
+
e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
|
1773
|
+
e.frame_type = frame_type
|
1774
|
+
e.reason_phrase = "Cannot retire current connection ID"
|
1775
|
+
end
|
1776
|
+
raise err
|
1777
|
+
end
|
1778
|
+
# TODO: logging
|
1779
|
+
@host_cids.delete_at(i)
|
1780
|
+
@events.append(
|
1781
|
+
Event::ConnectionIdRetired.new.tap do |ev|
|
1782
|
+
ev.connection_id = connection_id.cid
|
1783
|
+
end
|
1784
|
+
)
|
1785
|
+
break
|
1786
|
+
end
|
1787
|
+
end
|
1788
|
+
# issue a new connection ID
|
1789
|
+
replenish_connection_ids
|
1790
|
+
end
|
1791
|
+
|
1792
|
+
# Handle a STOP_SENDING frame.
|
1793
|
+
private def handle_stop_sending_frame(context:, frame_type:, buf:)
|
1794
|
+
stream_id = buf.pull_uint_var
|
1795
|
+
error_code = buf.pull_uint_var # application error code
|
1796
|
+
|
1797
|
+
# log frame
|
1798
|
+
context.quic_logger_frames << @quic_logger.encode_stop_sending_frame(error_code: error_code, stream_id: stream_id) if @quic_logger
|
1799
|
+
|
1800
|
+
# check stream direction
|
1801
|
+
assert_stream_can_send(frame_type: frame_type, stream_id: stream_id)
|
1802
|
+
|
1803
|
+
# reset the stream
|
1804
|
+
stream = get_or_create_stream(frame_type: frame_type, stream_id: stream_id)
|
1805
|
+
stream.sender.reset(error_code: Quic::Packet::QuicErrorCode::NO_ERROR)
|
1806
|
+
end
|
1807
|
+
|
1808
|
+
# Handle a STREAM frame.
|
1809
|
+
private def handle_stream_frame(context:, frame_type:, buf:)
|
1810
|
+
stream_id = buf.pull_uint_var
|
1811
|
+
offset = if frame_type & 4 != 0
|
1812
|
+
buf.pull_uint_var
|
1813
|
+
else
|
1814
|
+
0
|
1815
|
+
end
|
1816
|
+
length = if frame_type & 2 != 0
|
1817
|
+
buf.pull_uint_var
|
1818
|
+
else
|
1819
|
+
buf.capacity - buf.tell
|
1820
|
+
end
|
1821
|
+
if offset + length > Buffer::UINT_VAR_MAX
|
1822
|
+
err = QuicConnectionError.new.tap do |e|
|
1823
|
+
e.error_code = Quic::Packet::QuicErrorCode::FRAME_ENCODING_ERROR
|
1824
|
+
e.frame_type = frame_type
|
1825
|
+
e.reason_phrase = "offset + length cannot exceed 2^62 - 1"
|
1826
|
+
end
|
1827
|
+
raise err
|
1828
|
+
end
|
1829
|
+
frame = Quic::Packet::QuicStreamFrame.new.tap do |f|
|
1830
|
+
f.offset = offset
|
1831
|
+
f.data = buf.pull_bytes(length)
|
1832
|
+
f.fin = ((frame_type & 1) == 1)
|
1833
|
+
end
|
1834
|
+
|
1835
|
+
# log frame
|
1836
|
+
context.quic_logger_frames << @quic_logger.encode_stream_frame(frame: frame, stream_id: stream_id) if @quic_logger
|
1837
|
+
|
1838
|
+
# check stream direction
|
1839
|
+
assert_stream_can_receive(frame_type: frame_type, stream_id: stream_id)
|
1840
|
+
|
1841
|
+
# check flow-control limits
|
1842
|
+
stream = get_or_create_stream(frame_type: frame_type, stream_id: stream_id)
|
1843
|
+
if offset + length > stream.max_stream_data_local
|
1844
|
+
err = QuicConnectionError.new.tap do |e|
|
1845
|
+
e.error_code = Quic::Packet::QuicErrorCode::FLOW_CONTROL_ERROR
|
1846
|
+
e.frame_type = frame_type
|
1847
|
+
e.reason_phrase = "Over stream data limit"
|
1848
|
+
end
|
1849
|
+
raise err
|
1850
|
+
end
|
1851
|
+
newly_received = [0, offset + length - stream.receiver.highest_offset].max
|
1852
|
+
if @local_max_data.used + newly_received > @local_max_data.value
|
1853
|
+
err = QuicConnectionError.new.tap do |e|
|
1854
|
+
e.error_code = Quic::Packet::QuicErrorCode::FLOW_CONTROL_ERROR
|
1855
|
+
e.frame_type = frame_type
|
1856
|
+
e.reason_phrase = "Over connection data limit"
|
1857
|
+
end
|
1858
|
+
raise err
|
1859
|
+
end
|
1860
|
+
|
1861
|
+
# process data
|
1862
|
+
begin
|
1863
|
+
event = stream.receiver.handle_frame(frame: frame)
|
1864
|
+
rescue Stream::FinalSizeError => error
|
1865
|
+
err = QuicConnectionError.new.tap do |e|
|
1866
|
+
e.error_code = Quic::Packet::QuicErrorCode::FINAL_SIZE_ERROR
|
1867
|
+
e.frame_type = frame_type
|
1868
|
+
e.reason_phrase = error.message
|
1869
|
+
end
|
1870
|
+
raise err
|
1871
|
+
end
|
1872
|
+
@events.append(event) if event
|
1873
|
+
@local_max_data.used += newly_received
|
1874
|
+
end
|
1875
|
+
|
1876
|
+
# Handle a STREAM_DATA_BLOCKED frame.
|
1877
|
+
private def handle_stream_data_blocked_frame(context:, frame_type:, buf:)
|
1878
|
+
stream_id = buf.pull_uint_var
|
1879
|
+
limit = buf.pull_uint_var
|
1880
|
+
|
1881
|
+
# log frame
|
1882
|
+
context.quic_logger_frames << @quic_logger.encode_stream_data_blocked_frame(limit: limit, stream_id: stream_id)
|
1883
|
+
|
1884
|
+
# check stream direction
|
1885
|
+
assert_stream_can_receive(frame_type: frame_type, stream_id: stream_id)
|
1886
|
+
|
1887
|
+
get_or_create_stream(frame_type: frame_type, stream_id: stream_id)
|
1888
|
+
end
|
1889
|
+
|
1890
|
+
# Handle a STREAMS_BLOCKED frame.
|
1891
|
+
private def handle_streams_blocked_frame(context:, frame_type:, buf:)
|
1892
|
+
limit = buf.pull_uint_var
|
1893
|
+
if limit > STREAM_COUNT_MAX
|
1894
|
+
err = QuicConnectionError.new.tap do |e|
|
1895
|
+
e.error_code = Quic::Packet::QuicErrorCode::FRAME_ENCODING_ERROR
|
1896
|
+
e.frame_type = frame_type
|
1897
|
+
e.reason_phrase = "Maximum Streams cannot exceed 2^60"
|
1898
|
+
end
|
1899
|
+
raise err
|
1900
|
+
end
|
1901
|
+
|
1902
|
+
# log frame
|
1903
|
+
context.quic_logger_frames << @quic_logger.encode_streams_blocked_frame(
|
1904
|
+
is_unidirectional: (frame_type == Quic::Packet::QuicFrameType::STREAMS_BLOCKED_UNI),
|
1905
|
+
limit: limit,
|
1906
|
+
) if @quic_logger
|
1907
|
+
end
|
1908
|
+
|
1909
|
+
# Log a key retirement.
|
1910
|
+
private def log_key_retired(key_type:, trigger:)
|
1911
|
+
# binding.irb
|
1912
|
+
@quic_logger&.log_event(category: "security", event: "key_retired", data: { key_type: key_type, trigger: trigger })
|
1913
|
+
end
|
1914
|
+
|
1915
|
+
# Log a key update
|
1916
|
+
private def log_key_updated(key_type:, trigger:)
|
1917
|
+
@quic_logger&.log_event(category: "security", event: "key_updated", data: { key_type: key_type, trigger: trigger })
|
1918
|
+
end
|
1919
|
+
|
1920
|
+
# Callback when an ACK frame is acknowledged or lost.
|
1921
|
+
private def on_ack_delivery(delivery:, space:, highest_acked:)
|
1922
|
+
if delivery == Quic::PacketBuilder::QuicDeliveryState::ACKED
|
1923
|
+
space.ack_queue.subtract(0, highest_acked + 1)
|
1924
|
+
end
|
1925
|
+
end
|
1926
|
+
|
1927
|
+
# Callback when a MAX_DATA or MAX_STREAMS frame is acknowledged or lost.
|
1928
|
+
private def on_connection_limit_delivery(delivery:, limit:)
|
1929
|
+
limit.sent = 0 if delivery != Quic::PacketBuilder::QuicDeliveryState::ACKED
|
1930
|
+
end
|
1931
|
+
|
1932
|
+
# Callback when a HANDSHAKE_DONE frame is acknowledged or lost.
|
1933
|
+
private def on_handshake_done_delivery(delivery:)
|
1934
|
+
@handshake_done_pending = true if delivery != Quic::PacketBuilder::QuicDeliveryState::ACKED
|
1935
|
+
end
|
1936
|
+
|
1937
|
+
# Callback when a MAX_STREAM_DATA frame is acknowleged or loss.
|
1938
|
+
private def on_max_stream_data_delivery(delivery:, stream:)
|
1939
|
+
stream.max_stream_data_local_sent = 0 if delivery != Quic::PacketBuilder::QuicDeliveryState::ACKED
|
1940
|
+
end
|
1941
|
+
|
1942
|
+
# Callback when a NEW_CONNECTION_ID frame is acknowledged or loss.
|
1943
|
+
private def on_new_connection_id_delivery(delivery:, connection_id:)
|
1944
|
+
connection_id.was_sent = false if delivery != Quic::PacketBuilder::QuicDeliveryState::ACKED
|
1945
|
+
end
|
1946
|
+
|
1947
|
+
# Callback when a PING frame is acknowledged or lost.
|
1948
|
+
private def on_ping_delivery(delivery:, uids:)
|
1949
|
+
if delivery == Quic::PacketBuilder::QuicDeliveryState::ACKED
|
1950
|
+
# TODO: logging
|
1951
|
+
uids.each do |uid|
|
1952
|
+
@events.append(Event::PingAcknowledged.new.tap { |e| e.uid = uid })
|
1953
|
+
end
|
1954
|
+
else
|
1955
|
+
@ping_pending += uids
|
1956
|
+
end
|
1957
|
+
end
|
1958
|
+
|
1959
|
+
# Callback when a RETIRE_CONNECTION_ID frame is acknowledged or lost.
|
1960
|
+
private def on_retire_connection_id_delivery(delivery:, sequence_number:)
|
1961
|
+
@retire_connection_ids.append(sequence_number) if delivery != Quic::PacketBuilder::QuicDeliveryState::ACKED
|
1962
|
+
end
|
1963
|
+
|
1964
|
+
# Handle a QUIC packet payload.
|
1965
|
+
private def payload_received(context:, plain:)
|
1966
|
+
buf = Buffer.new(data: plain)
|
1967
|
+
|
1968
|
+
frame_found = false
|
1969
|
+
is_ack_eliciting = false
|
1970
|
+
is_probing = nil
|
1971
|
+
until buf.eof
|
1972
|
+
frame_type = buf.pull_uint_var
|
1973
|
+
|
1974
|
+
# check frame type is known
|
1975
|
+
frame_handler, frame_epochs = @frame_handlers[frame_type]
|
1976
|
+
# puts "frame_type #{frame_handler}" unless @is_client
|
1977
|
+
if frame_handler.nil?
|
1978
|
+
err = QuicConnectionError.new.tap do |e|
|
1979
|
+
e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
|
1980
|
+
e.frame_type = frame_type
|
1981
|
+
e.reason_phrase = "Unknown frame type"
|
1982
|
+
end
|
1983
|
+
raise err
|
1984
|
+
end
|
1985
|
+
|
1986
|
+
# check frame is allowed for the epoch
|
1987
|
+
unless frame_epochs.include?(context.epoch)
|
1988
|
+
err = QuicConnectionError.new.tap do |e|
|
1989
|
+
e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
|
1990
|
+
e.frame_type = frame_type
|
1991
|
+
e.reason_phrase = "Unexpected frame type"
|
1992
|
+
end
|
1993
|
+
raise err
|
1994
|
+
end
|
1995
|
+
|
1996
|
+
# handle the frame
|
1997
|
+
begin
|
1998
|
+
# binding.irb if frame_type == :handle_max_data_frame
|
1999
|
+
method(frame_handler).call(context: context, frame_type: frame_type, buf: buf)
|
2000
|
+
rescue Buffer::BufferReadError
|
2001
|
+
# binding.irb
|
2002
|
+
err = QuicConnectionError.new.tap do |e|
|
2003
|
+
e.error_code = Quic::Packet::QuicErrorCode::FRAME_ENCODING_ERROR
|
2004
|
+
e.frame_type = frame_type
|
2005
|
+
e.reason_phrase = "Failed to parse frame"
|
2006
|
+
end
|
2007
|
+
raise err
|
2008
|
+
rescue Quic::Stream::StreamFinishedError
|
2009
|
+
# we lack the state for the stream, ignore the frame
|
2010
|
+
end
|
2011
|
+
|
2012
|
+
# update ACK only / probing flags
|
2013
|
+
frame_found = true
|
2014
|
+
|
2015
|
+
unless Quic::Packet::NON_ACK_ELICITING_FRAME_TYPES.include?(frame_type)
|
2016
|
+
is_ack_eliciting = true
|
2017
|
+
end
|
2018
|
+
|
2019
|
+
if !Quic::Packet::PROBING_FRAME_TYPES.include?(frame_type)
|
2020
|
+
is_probing = false
|
2021
|
+
elsif is_probing.nil?
|
2022
|
+
is_probing = true
|
2023
|
+
end
|
2024
|
+
end
|
2025
|
+
|
2026
|
+
unless frame_found
|
2027
|
+
err = QuicConnectionError.new.tap do |e|
|
2028
|
+
e.error_code = Quic::Packet::QuicErrorCode::PROTOCOL_VIOLATION
|
2029
|
+
e.frame_type = Quic::Packet::QuicFrameType::PADDING
|
2030
|
+
e.reason_phrase = "Packet contains no frames"
|
2031
|
+
end
|
2032
|
+
raise err
|
2033
|
+
end
|
2034
|
+
return [is_ack_eliciting, is_probing]
|
2035
|
+
end
|
2036
|
+
|
2037
|
+
# Generate new connection IDs.
|
2038
|
+
private def replenish_connection_ids
|
2039
|
+
while @host_cids.length < [8, @remote_active_connection_id_limit.to_i].min
|
2040
|
+
@host_cids.append(
|
2041
|
+
QuicConnectionId.new.tap do |conn|
|
2042
|
+
conn.cid = Random.urandom(@configuration.connection_id_length)
|
2043
|
+
conn.sequence_number = @host_cid_seq
|
2044
|
+
conn.stateless_reset_token = Random.urandom(16)
|
2045
|
+
end
|
2046
|
+
)
|
2047
|
+
@host_cid_seq += 1
|
2048
|
+
end
|
2049
|
+
end
|
2050
|
+
|
2051
|
+
# Retire a destication connection ID.
|
2052
|
+
private def retire_peer_cid(connection_id)
|
2053
|
+
# TODO: logger
|
2054
|
+
@retire_connection_ids.append(connection_id.sequence_number)
|
2055
|
+
end
|
2056
|
+
|
2057
|
+
private def push_crypto_data
|
2058
|
+
@crypto_buffers.each do |epoch, buf|
|
2059
|
+
@crypto_streams[epoch].sender.write(data: buf.data)
|
2060
|
+
buf.seek(0)
|
2061
|
+
end
|
2062
|
+
end
|
2063
|
+
|
2064
|
+
private def send_probe
|
2065
|
+
@probe_pending = true
|
2066
|
+
end
|
2067
|
+
|
2068
|
+
# Parse and apply remote transport parameters.
|
2069
|
+
#
|
2070
|
+
# `from_session_ticket` is `true` when restoring saved transport parameters, and `false` when handling received transport parameters.
|
2071
|
+
private def parse_transport_parameters(data:, from_session_ticket: false)
|
2072
|
+
begin
|
2073
|
+
quic_transport_parameters = Quic::Packet.pull_quic_transport_parameters(Buffer.new(data: data))
|
2074
|
+
rescue ::Raioquic::ValueError, ::Raioquic::Buffer::BufferReadError
|
2075
|
+
err = QuicConnectionError.new.tap do |e|
|
2076
|
+
e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
|
2077
|
+
e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
|
2078
|
+
e.reason_phrase = "Could not parse QUIC transport parameters"
|
2079
|
+
end
|
2080
|
+
raise err
|
2081
|
+
end
|
2082
|
+
|
2083
|
+
# log event
|
2084
|
+
if @quic_logger && !from_session_ticket
|
2085
|
+
@quic_logger.log_event(
|
2086
|
+
category: "transport",
|
2087
|
+
event: "parameters_set",
|
2088
|
+
data: @quic_logger.encode_transport_parameters(owner: "remote", parameters: quic_transport_parameters),
|
2089
|
+
)
|
2090
|
+
end
|
2091
|
+
|
2092
|
+
# validate remote parameters
|
2093
|
+
unless @is_client
|
2094
|
+
%w[original_destination_connection_id preferred_address retry_source_connection_id stateless_reset_token].each do |att|
|
2095
|
+
if quic_transport_parameters[att]
|
2096
|
+
err = QuicConnectionError.new.tap do |e|
|
2097
|
+
e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
|
2098
|
+
e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
|
2099
|
+
e.reason_phrase = "#{att} is not allowed for clients"
|
2100
|
+
end
|
2101
|
+
raise err
|
2102
|
+
end
|
2103
|
+
end
|
2104
|
+
end
|
2105
|
+
|
2106
|
+
unless from_session_ticket
|
2107
|
+
if quic_transport_parameters.initial_source_connection_id != @remote_initial_source_connection_id
|
2108
|
+
err = QuicConnectionError.new.tap do |e|
|
2109
|
+
e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
|
2110
|
+
e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
|
2111
|
+
e.reason_phrase = "initial_source_connection_id does not match"
|
2112
|
+
end
|
2113
|
+
raise err
|
2114
|
+
end
|
2115
|
+
|
2116
|
+
if @is_client && quic_transport_parameters.original_destination_connection_id != @original_destination_connection_id
|
2117
|
+
err = QuicConnectionError.new.tap do |e|
|
2118
|
+
e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
|
2119
|
+
e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
|
2120
|
+
e.reason_phrase = "original_source_connection_id does not match"
|
2121
|
+
end
|
2122
|
+
raise err
|
2123
|
+
end
|
2124
|
+
|
2125
|
+
if @is_client && quic_transport_parameters.retry_source_connection_id != @retry_source_connection_id
|
2126
|
+
err = QuicConnectionError.new.tap do |e|
|
2127
|
+
e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
|
2128
|
+
e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
|
2129
|
+
e.reason_phrase = "retry_source_connection_id does not match"
|
2130
|
+
end
|
2131
|
+
raise err
|
2132
|
+
end
|
2133
|
+
|
2134
|
+
if quic_transport_parameters.active_connection_id_limit && quic_transport_parameters.active_connection_id_limit.to_i < 2
|
2135
|
+
err = QuicConnectionError.new.tap do |e|
|
2136
|
+
e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
|
2137
|
+
e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
|
2138
|
+
e.reason_phrase = "active_connection_id_limit must be no less than 2"
|
2139
|
+
end
|
2140
|
+
raise err
|
2141
|
+
end
|
2142
|
+
|
2143
|
+
if quic_transport_parameters.ack_delay_exponent && quic_transport_parameters.ack_delay_exponent > 20
|
2144
|
+
err = QuicConnectionError.new.tap do |e|
|
2145
|
+
e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
|
2146
|
+
e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
|
2147
|
+
e.reason_phrase = "ack_delay_exponent must be <= 20"
|
2148
|
+
end
|
2149
|
+
raise err
|
2150
|
+
end
|
2151
|
+
|
2152
|
+
if quic_transport_parameters.max_ack_delay && quic_transport_parameters.max_ack_delay >= 2**14
|
2153
|
+
err = QuicConnectionError.new.tap do |e|
|
2154
|
+
e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
|
2155
|
+
e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
|
2156
|
+
e.reason_phrase = "max_ack_delay must be < 2^14"
|
2157
|
+
end
|
2158
|
+
raise err
|
2159
|
+
end
|
2160
|
+
|
2161
|
+
if quic_transport_parameters.max_udp_payload_size && quic_transport_parameters.max_udp_payload_size < 1200
|
2162
|
+
err = QuicConnectionError.new.tap do |e|
|
2163
|
+
e.error_code = Quic::Packet::QuicErrorCode::TRANSPORT_PARAMETER_ERROR
|
2164
|
+
e.frame_type = Quic::Packet::QuicFrameType::CRYPTO
|
2165
|
+
e.reason_phrase = "max_udp_payload_size must be >= 1200"
|
2166
|
+
end
|
2167
|
+
raise err
|
2168
|
+
end
|
2169
|
+
end
|
2170
|
+
|
2171
|
+
# store remote parameters
|
2172
|
+
unless from_session_ticket
|
2173
|
+
# TODO: check original implementation
|
2174
|
+
@remote_ack_delay_exponent = quic_transport_parameters.ack_delay_exponent if quic_transport_parameters.ack_delay_exponent
|
2175
|
+
@loss.max_ack_delay = quic_transport_parameters.max_ack_delay / 1000.0 if quic_transport_parameters.max_ack_delay
|
2176
|
+
|
2177
|
+
if @is_client && @peer_cid.sequence_number == 0 && quic_transport_parameters.stateless_reset_token
|
2178
|
+
@peer_cid.stateless_reset_token = quic_transport_parameters.stateless_reset_token
|
2179
|
+
end
|
2180
|
+
end
|
2181
|
+
|
2182
|
+
if quic_transport_parameters.active_connection_id_limit
|
2183
|
+
@remote_active_connection_id_limit = quic_transport_parameters.active_connection_id_limit.to_i
|
2184
|
+
end
|
2185
|
+
|
2186
|
+
if quic_transport_parameters.max_idle_timeout
|
2187
|
+
@remote_max_idle_timeout = quic_transport_parameters.max_idle_timeout / 1000.0
|
2188
|
+
end
|
2189
|
+
|
2190
|
+
@remote_max_datagram_frame_size = quic_transport_parameters.max_datagram_frame_size
|
2191
|
+
|
2192
|
+
%w[max_data max_stream_data_bidi_local max_stream_data_bidi_remote max_stream_data_uni max_streams_bidi max_streams_uni].each do |param|
|
2193
|
+
value = quic_transport_parameters["initial_#{param}"]
|
2194
|
+
instance_variable_set("@remote_#{param}", value) if value
|
2195
|
+
end
|
2196
|
+
end
|
2197
|
+
|
2198
|
+
private def serialize_transport_parameters
|
2199
|
+
quic_transport_parameters = Quic::Packet::QuicTransportParameters.new.tap do |param|
|
2200
|
+
param.ack_delay_exponent = @local_ack_delay_exponent
|
2201
|
+
param.active_connection_id_limit = @local_active_connection_id_limit
|
2202
|
+
param.max_idle_timeout = (@configuration.idle_timeout * 1000).to_i
|
2203
|
+
param.initial_max_data = @local_max_data.value
|
2204
|
+
param.initial_max_stream_data_bidi_local = @local_max_stream_data_bidi_local
|
2205
|
+
param.initial_max_stream_data_bidi_remote = @local_max_stream_data_bidi_remote
|
2206
|
+
param.initial_max_stream_data_uni = @local_max_stream_data_uni
|
2207
|
+
param.initial_max_streams_bidi = @local_max_streams_bidi.value
|
2208
|
+
param.initial_max_streams_uni = @local_max_streams_uni.value
|
2209
|
+
param.initial_source_connection_id = @local_initial_source_connection_id
|
2210
|
+
param.max_ack_delay = 25
|
2211
|
+
param.max_datagram_frame_size = @configuration.max_datagram_frame_size
|
2212
|
+
param.quantum_readiness = @configuration.quantam_readiness_test ? "Q" * 1200 : nil
|
2213
|
+
param.stateless_reset_token = @host_cids[0].stateless_reset_token
|
2214
|
+
end
|
2215
|
+
unless @is_client
|
2216
|
+
quic_transport_parameters.original_destination_connection_id = @original_destination_connection_id
|
2217
|
+
quic_transport_parameters.retry_source_connection_id = @retry_source_connection_id
|
2218
|
+
end
|
2219
|
+
|
2220
|
+
# log event
|
2221
|
+
if @quic_logger
|
2222
|
+
@quic_logger.log_event(
|
2223
|
+
category: "transport",
|
2224
|
+
event: "parameters_set",
|
2225
|
+
data: @quic_logger.encode_transport_parameters(owner: "local", parameters: quic_transport_parameters)
|
2226
|
+
)
|
2227
|
+
end
|
2228
|
+
|
2229
|
+
buf = Buffer.new(capacity: 3 * Quic::PacketBuilder::PACKET_MAX_SIZE)
|
2230
|
+
Quic::Packet.push_quic_transport_parameters(buf: buf, params: quic_transport_parameters)
|
2231
|
+
return buf.data
|
2232
|
+
end
|
2233
|
+
|
2234
|
+
private def set_state(state)
|
2235
|
+
# TODO: logging
|
2236
|
+
@state = state
|
2237
|
+
end
|
2238
|
+
|
2239
|
+
private def stream_can_receive(stream_id)
|
2240
|
+
Connection.stream_is_client_initiated(stream_id) != @is_client || !Connection.stream_is_unidirectional(stream_id)
|
2241
|
+
end
|
2242
|
+
|
2243
|
+
private def stream_can_send(stream_id)
|
2244
|
+
Connection.stream_is_client_initiated(stream_id) == @is_client || !Connection.stream_is_unidirectional(stream_id)
|
2245
|
+
end
|
2246
|
+
|
2247
|
+
private def unblock_streams(is_unidirectional)
|
2248
|
+
if is_unidirectional
|
2249
|
+
max_stream_data_remote = @remote_max_stream_data_uni
|
2250
|
+
max_streams = @remote_max_streams_uni
|
2251
|
+
streams_blocked = @streams_blocked_uni
|
2252
|
+
else
|
2253
|
+
max_stream_data_remote = @remote_max_stream_data_bidi_remote
|
2254
|
+
max_streams = @remote_max_streams_bidi
|
2255
|
+
streams_blocked = @streams_blocked_bidi
|
2256
|
+
end
|
2257
|
+
|
2258
|
+
while streams_blocked && streams_blocked[0] && streams_blocked[0].stream_id.div(4) < max_streams
|
2259
|
+
stream = streams_blocked.delete_at(0)
|
2260
|
+
stream.is_blocked = false
|
2261
|
+
stream.max_stream_data_remote = max_stream_data_remote
|
2262
|
+
end
|
2263
|
+
|
2264
|
+
@streams_blocked_pending = false if !@streams_blocked_bidi && !@streams_blocked_uni
|
2265
|
+
end
|
2266
|
+
|
2267
|
+
# Callback which is invoked by the TLS engine when new traffic keys are available.
|
2268
|
+
private def update_traffic_key(direction:, epoch:, cipher_suite:, secret:)
|
2269
|
+
secrets_log_file = @configuration.secrets_log_file
|
2270
|
+
if secrets_log_file
|
2271
|
+
# TODO: logging to file
|
2272
|
+
end
|
2273
|
+
crypto = @cryptos[epoch]
|
2274
|
+
if direction == TLS::Direction::ENCRYPT
|
2275
|
+
crypto.send.setup(cipher_suite: cipher_suite, secret: secret, version: @version)
|
2276
|
+
else
|
2277
|
+
crypto.recv.setup(cipher_suite: cipher_suite, secret: secret, version: @version)
|
2278
|
+
end
|
2279
|
+
end
|
2280
|
+
|
2281
|
+
private def write_application(builder:, network_path:, now:)
|
2282
|
+
crypto_stream = nil
|
2283
|
+
if @cryptos[TLS::Epoch::ONE_RTT].send.is_valid
|
2284
|
+
crypto = @cryptos[TLS::Epoch::ONE_RTT]
|
2285
|
+
crypto_stream = @crypto_streams[TLS::Epoch::ONE_RTT]
|
2286
|
+
packet_type = Quic::Packet::PACKET_TYPE_ONE_RTT
|
2287
|
+
elsif @cryptos[TLS::Epoch::ZERO_RTT].send.is_valid
|
2288
|
+
crypto = @cryptos[TLS::Epoch::ZERO_RTT]
|
2289
|
+
packet_type = Quic::Packet::PACKET_TYPE_ZERO_RTT
|
2290
|
+
else
|
2291
|
+
return
|
2292
|
+
end
|
2293
|
+
space = @spaces[TLS::Epoch::ONE_RTT]
|
2294
|
+
|
2295
|
+
c = -1
|
2296
|
+
while true
|
2297
|
+
c += 1
|
2298
|
+
# apply pacing, except if we have ACKs to send
|
2299
|
+
if space.ack_at.nil? || space.ack_at >= now
|
2300
|
+
@pacing_at = @loss.pacer.next_send_time(now: now)
|
2301
|
+
break if @pacing_at
|
2302
|
+
end
|
2303
|
+
builder.start_packet(packet_type: packet_type, crypto: crypto)
|
2304
|
+
|
2305
|
+
if @handshake_complete
|
2306
|
+
# ACK
|
2307
|
+
write_ack_frame(builder: builder, space: space, now: now) if space.ack_at && space.ack_at <= now
|
2308
|
+
|
2309
|
+
# HANDSHAKE_DONE
|
2310
|
+
if @handshake_done_pending
|
2311
|
+
write_handshake_done_frame(builder: builder)
|
2312
|
+
@handshake_done_pending = false
|
2313
|
+
end
|
2314
|
+
|
2315
|
+
# PATH CHALLENGE
|
2316
|
+
if !network_path.is_validated && network_path.local_challenge.nil?
|
2317
|
+
challenge = Random.urandom(8)
|
2318
|
+
write_path_challenge_frame(builder: builder, challenge: challenge)
|
2319
|
+
network_path.local_challenge = challenge
|
2320
|
+
end
|
2321
|
+
|
2322
|
+
# PATH RESPONSE
|
2323
|
+
if network_path.remote_challenge
|
2324
|
+
write_path_response_frame(builder: builder, challenge: network_path.remote_challenge)
|
2325
|
+
network_path.remote_challenge = nil
|
2326
|
+
end
|
2327
|
+
|
2328
|
+
# NEW_CONNECTION_ID
|
2329
|
+
@host_cids.each do |connection_id|
|
2330
|
+
unless connection_id.was_sent
|
2331
|
+
write_new_connection_id_frame(builder: builder, connection_id: connection_id)
|
2332
|
+
end
|
2333
|
+
# write_new_connection_id_frame(builder: builder, connection_id: connection_id) unless connection_id.was_sent
|
2334
|
+
end
|
2335
|
+
|
2336
|
+
# RETIRE_CONNECTION_ID
|
2337
|
+
@retire_connection_ids.each do |sequence_number|
|
2338
|
+
write_retire_connection_id_frame(builder: builder, sequence_number: sequence_number)
|
2339
|
+
end
|
2340
|
+
@retire_connection_ids.clear
|
2341
|
+
|
2342
|
+
# STREAMS_BLOCKED
|
2343
|
+
if @streams_blocked_pending
|
2344
|
+
if @streams_blocked_bidi
|
2345
|
+
write_streams_blocked_frame(
|
2346
|
+
builder: builder, frame_type: Quic::Packet::QuicFrameType::STREAMS_BLOCKED_BIDI, limit: @remote_max_streams_bidi,
|
2347
|
+
)
|
2348
|
+
end
|
2349
|
+
if @streams_blocked_uni
|
2350
|
+
write_streams_blocked_frame(
|
2351
|
+
builder: builder, frame_type: Quic::Packet::QuicFrameType::STREAMS_BLOCKED_UNI, limit: @remote_max_streams_uni,
|
2352
|
+
)
|
2353
|
+
end
|
2354
|
+
@streams_blocked_pending = false
|
2355
|
+
end
|
2356
|
+
|
2357
|
+
# MAX_DATA and MAX_STREAMS
|
2358
|
+
write_connection_limits(builder: builder, space: space)
|
2359
|
+
end
|
2360
|
+
|
2361
|
+
# stream-level limits
|
2362
|
+
@streams.each_value { |stream| write_stream_limits(builder: builder, space: space, stream: stream) }
|
2363
|
+
|
2364
|
+
# PING (user-request)
|
2365
|
+
if @ping_pending.size > 0
|
2366
|
+
write_ping_frame(builder: builder, uids: @ping_pending)
|
2367
|
+
@ping_pending.clear
|
2368
|
+
end
|
2369
|
+
|
2370
|
+
# PING (probe)
|
2371
|
+
if @probe_pending
|
2372
|
+
write_ping_frame(builder: builder, comment: "probe")
|
2373
|
+
@probe_pending = false
|
2374
|
+
end
|
2375
|
+
|
2376
|
+
# CRYPTO
|
2377
|
+
if crypto_stream && !crypto_stream.sender.buffer_is_empty
|
2378
|
+
write_crypto_frame(builder: builder, space: space, stream: crypto_stream)
|
2379
|
+
end
|
2380
|
+
|
2381
|
+
# DATAGRAM
|
2382
|
+
while @datagrams_pending.length > 0
|
2383
|
+
begin
|
2384
|
+
write_datagram_frame(builder: builder, data: @datagrams_pending[0], frame_type: Quic::Packet::QuicFrameType::DATAGRAM_WITH_LENGTH)
|
2385
|
+
@datagrams_pending.pop
|
2386
|
+
rescue Quic::PacketBuilder::QuicPacketBuilderStop
|
2387
|
+
break
|
2388
|
+
end
|
2389
|
+
end
|
2390
|
+
|
2391
|
+
@streams.each_value do |stream|
|
2392
|
+
# if the stream is finished, discard it
|
2393
|
+
if stream.is_finished
|
2394
|
+
# TODO: logging
|
2395
|
+
@streams.delete(stream.stream_id)
|
2396
|
+
@streams_finished << stream.stream_id
|
2397
|
+
next
|
2398
|
+
end
|
2399
|
+
|
2400
|
+
# STOP_SENDING
|
2401
|
+
write_stop_sending_frame(builder: builder, stream: stream) if stream.receiver.stop_pending
|
2402
|
+
|
2403
|
+
if stream.sender.reset_pending
|
2404
|
+
# RESET_STREAM
|
2405
|
+
write_reset_stream_frame(builder: builder, stream: stream)
|
2406
|
+
elsif !stream.is_blocked && !stream.sender.buffer_is_empty
|
2407
|
+
# STREAM
|
2408
|
+
@remote_max_data_used += write_stream_frame(
|
2409
|
+
builder: builder,
|
2410
|
+
space: space,
|
2411
|
+
stream: stream,
|
2412
|
+
max_offset: [stream.sender.highest_offset + @remote_max_data - @remote_max_data_used, stream.max_stream_data_remote].min,
|
2413
|
+
)
|
2414
|
+
end
|
2415
|
+
end
|
2416
|
+
# puts "builder.packer_is_emprty: #{builder.packet_is_empty}"
|
2417
|
+
if builder.packet_is_empty
|
2418
|
+
break
|
2419
|
+
else
|
2420
|
+
@loss.pacer.update_after_send(now: now)
|
2421
|
+
end
|
2422
|
+
end
|
2423
|
+
end
|
2424
|
+
|
2425
|
+
private def write_handshake(builder:, epoch:, now:)
|
2426
|
+
crypto = @cryptos[epoch]
|
2427
|
+
return unless crypto.send.is_valid
|
2428
|
+
|
2429
|
+
crypto_stream = @crypto_streams[epoch]
|
2430
|
+
space = @spaces[epoch]
|
2431
|
+
|
2432
|
+
while true
|
2433
|
+
if epoch == TLS::Epoch::INITIAL
|
2434
|
+
packet_type = Quic::Packet::PACKET_TYPE_INITIAL
|
2435
|
+
else
|
2436
|
+
packet_type = Quic::Packet::PACKET_TYPE_HANDSHAKE
|
2437
|
+
end
|
2438
|
+
builder.start_packet(packet_type: packet_type, crypto: crypto)
|
2439
|
+
|
2440
|
+
# ACK
|
2441
|
+
write_ack_frame(builder: builder, space: space, now: now) if space.ack_at
|
2442
|
+
|
2443
|
+
# CRYPTO
|
2444
|
+
unless crypto_stream.sender.buffer_is_empty
|
2445
|
+
if write_crypto_frame(builder: builder, space: space, stream: crypto_stream)
|
2446
|
+
@probe_pending = false
|
2447
|
+
end
|
2448
|
+
end
|
2449
|
+
|
2450
|
+
# PING (probe)
|
2451
|
+
if @probe_pending && !@handshake_complete && (epoch == TLS::Epoch::HANDSHAKE || !@cryptos[TLS::Epoch::HANDSHAKE].send.is_valid)
|
2452
|
+
write_ping_frame(builder: builder, comment: "probe")
|
2453
|
+
@probe_pending = false
|
2454
|
+
end
|
2455
|
+
|
2456
|
+
break if builder.packet_is_empty
|
2457
|
+
end
|
2458
|
+
end
|
2459
|
+
|
2460
|
+
private def write_ack_frame(builder:, space:, now:)
|
2461
|
+
# calculate AKC delay
|
2462
|
+
ack_delay = now - space.largest_received_time
|
2463
|
+
ack_delay_encoded = (ack_delay * 1000000).to_i >> @local_ack_delay_exponent
|
2464
|
+
|
2465
|
+
buf = builder.start_frame(
|
2466
|
+
frame_type: Quic::Packet::QuicFrameType::ACK,
|
2467
|
+
capacity: ACK_FRAME_CAPACITY,
|
2468
|
+
handler: method(:on_ack_delivery),
|
2469
|
+
handler_args: [space, space.largest_received_packet],
|
2470
|
+
)
|
2471
|
+
ranges = Quic::Packet.push_ack_frame(buf: buf, rangeset: space.ack_queue, delay: ack_delay_encoded)
|
2472
|
+
space.ack_at = nil
|
2473
|
+
|
2474
|
+
# log frame
|
2475
|
+
builder.quic_logger_frames << @quic_logger.encode_ack_frame(ranges: space.ack_queue, delay: ack_delay) if @quic_logger
|
2476
|
+
|
2477
|
+
# check if we need to trigger an ACK-of-ACK
|
2478
|
+
write_ping_frame(builder: builder, comment: "ACK-of-ACK trigger") if ranges > 1 && builder.packet_number % 8 == 0
|
2479
|
+
end
|
2480
|
+
|
2481
|
+
private def write_connection_close_frame(builder:, epoch:, error_code:, frame_type: nil, reason_phrase: "")
|
2482
|
+
# convert application-level close to transport-level close in early stages
|
2483
|
+
ec = error_code
|
2484
|
+
ft = frame_type
|
2485
|
+
rp = reason_phrase
|
2486
|
+
if frame_type.nil? && [TLS::Epoch::INITIAL, TLS::Epoch::HANDSHAKE].include?(epoch)
|
2487
|
+
ec = Quic::Packet::QuicErrorCode::APPLICATION_ERROR
|
2488
|
+
ft = Quic::Packet::QuicFrameType::PADDING
|
2489
|
+
rp = ""
|
2490
|
+
end
|
2491
|
+
reason_bytes = rp # encode to utf8 bytes in original code
|
2492
|
+
# binding.irb if reason_bytes.nil?
|
2493
|
+
reason_length = reason_bytes.bytesize
|
2494
|
+
|
2495
|
+
unless ft
|
2496
|
+
buf = builder.start_frame(
|
2497
|
+
frame_type: Quic::Packet::QuicFrameType::APPLICATION_CLOSE, capacity: APPLICATION_CLOSE_FRAME_CAPACITY + reason_length,
|
2498
|
+
)
|
2499
|
+
buf.push_uint_var(ec)
|
2500
|
+
buf.push_uint_var(reason_length)
|
2501
|
+
buf.push_bytes(reason_bytes)
|
2502
|
+
else
|
2503
|
+
buf = builder.start_frame(
|
2504
|
+
frame_type: Quic::Packet::QuicFrameType::TRANSPORT_CLOSE, capacity: TRANSPORT_CLOSE_FLAME_CAPACITY + reason_length,
|
2505
|
+
)
|
2506
|
+
buf.push_uint_var(ec)
|
2507
|
+
buf.push_uint_var(ft)
|
2508
|
+
buf.push_uint_var(reason_length)
|
2509
|
+
buf.push_bytes(reason_bytes)
|
2510
|
+
end
|
2511
|
+
# log frame
|
2512
|
+
if @quic_logger
|
2513
|
+
builder.quic_logger_frames << @quic_logger.encode_connection_close_frame(error_code: error_code, frame_type: frame_type, reason_phrase: reason_phrase)
|
2514
|
+
end
|
2515
|
+
end
|
2516
|
+
|
2517
|
+
# Raise MAX_DATA or MAX_STREAMS if needed.
|
2518
|
+
private def write_connection_limits(builder:, space:)
|
2519
|
+
[@local_max_data, @local_max_streams_bidi, @local_max_streams_uni].each do |limit|
|
2520
|
+
if limit.used * 2 > limit.value
|
2521
|
+
limit.value *= 2
|
2522
|
+
# TODO: logging
|
2523
|
+
end
|
2524
|
+
if limit.value != limit.sent
|
2525
|
+
buf = builder.start_frame(
|
2526
|
+
frame_type: limit.frame_type,
|
2527
|
+
capacity: CONNECTION_LIMIT_FRAME_CAPACITY,
|
2528
|
+
handler: method(:on_connection_limit_delivery),
|
2529
|
+
handler_args: [limit, nil],
|
2530
|
+
)
|
2531
|
+
buf.push_uint_var(limit.value)
|
2532
|
+
limit.sent = limit.value
|
2533
|
+
|
2534
|
+
# log frame
|
2535
|
+
builder.quic_logger_frames << @quic_logger.encode_connection_limit_frame(frame_type: limit.frame_type, maximum: limit.value) if @quic_logger
|
2536
|
+
end
|
2537
|
+
end
|
2538
|
+
end
|
2539
|
+
|
2540
|
+
private def write_crypto_frame(builder:, space:, stream:)
|
2541
|
+
# binding.b
|
2542
|
+
frame_overhead = 3 + Buffer.size_uint_var(stream.sender.next_offset)
|
2543
|
+
frame = stream.sender.get_frame(max_size: builder.remaining_flight_space - frame_overhead)
|
2544
|
+
if frame
|
2545
|
+
buf = builder.start_frame(
|
2546
|
+
frame_type: Quic::Packet::QuicFrameType::CRYPTO,
|
2547
|
+
capacity: frame_overhead,
|
2548
|
+
handler: stream.sender.method(:on_data_delivery),
|
2549
|
+
handler_args: [frame.offset, frame.offset + frame.data.bytesize],
|
2550
|
+
)
|
2551
|
+
buf.push_uint_var(frame.offset)
|
2552
|
+
buf.push_uint16(frame.data.bytesize | 0x4000)
|
2553
|
+
buf.push_bytes(frame.data)
|
2554
|
+
|
2555
|
+
# log frame
|
2556
|
+
builder.quic_logger_frames << @quic_logger.encode_crypto_frame(frame: frame) if @quic_logger
|
2557
|
+
|
2558
|
+
return true
|
2559
|
+
end
|
2560
|
+
return false
|
2561
|
+
end
|
2562
|
+
|
2563
|
+
# Write a DATAGRAM frame.
|
2564
|
+
#
|
2565
|
+
# Returns true if the frame was processed, false otherwise.
|
2566
|
+
private def write_datagram_frame(builder:, data:, frame_type:)
|
2567
|
+
raise RuntimeError unless frame_type == Quic::Packet::QuicFrameType::DATAGRAM_WITH_LENGTH
|
2568
|
+
|
2569
|
+
length = data&.bytesize || 0
|
2570
|
+
frame_size = 1 + Buffer.size_uint_var(length) + length
|
2571
|
+
|
2572
|
+
buf = builder.start_frame(frame_type: frame_type, capacity: frame_size)
|
2573
|
+
buf.push_uint_var(length)
|
2574
|
+
buf.push_bytes(data)
|
2575
|
+
|
2576
|
+
# log frame
|
2577
|
+
builder.quic_logger_frames << @quic_logger.encode_datagram_frame(length: length) if @quic_logger
|
2578
|
+
|
2579
|
+
return true
|
2580
|
+
end
|
2581
|
+
|
2582
|
+
private def write_handshake_done_frame(builder:)
|
2583
|
+
builder.start_frame(
|
2584
|
+
frame_type: Quic::Packet::QuicFrameType::HANDSHAKE_DONE,
|
2585
|
+
capacity: HANDSHAKE_DONE_FRAME_CAPACITY,
|
2586
|
+
handler: method(:on_handshake_done_delivery),
|
2587
|
+
)
|
2588
|
+
|
2589
|
+
# log frame
|
2590
|
+
builder.quic_logger_frames << @quic_logger.encode_handshake_done_frame if @quic_logger
|
2591
|
+
end
|
2592
|
+
|
2593
|
+
private def write_new_connection_id_frame(builder:, connection_id:)
|
2594
|
+
retire_prior_to = 0 # FIXME: from original
|
2595
|
+
|
2596
|
+
buf = builder.start_frame(
|
2597
|
+
frame_type: Quic::Packet::QuicFrameType::NEW_CONNECTION_ID,
|
2598
|
+
capacity: NEW_CONNECTION_ID_FRAME_CAPACITY,
|
2599
|
+
handler: method(:on_new_connection_id_delivery),
|
2600
|
+
handler_args: [connection_id],
|
2601
|
+
)
|
2602
|
+
buf.push_uint_var(connection_id.sequence_number)
|
2603
|
+
buf.push_uint_var(retire_prior_to)
|
2604
|
+
buf.push_uint8(connection_id.cid.bytesize)
|
2605
|
+
buf.push_bytes(connection_id.cid)
|
2606
|
+
buf.push_bytes(connection_id.stateless_reset_token)
|
2607
|
+
|
2608
|
+
connection_id.was_sent = true
|
2609
|
+
@events.append(Event::ConnectionIdIssued.new.tap { |e| e.connection_id = connection_id.cid })
|
2610
|
+
|
2611
|
+
# log frame
|
2612
|
+
if @quic_logger
|
2613
|
+
builder.quic_logger_frames << @quic_logger.encode_new_connection_id_frame(
|
2614
|
+
connection_id: connection_id.cid,
|
2615
|
+
retire_prior_to: retire_prior_to,
|
2616
|
+
sequence_number: connection_id.sequence_number,
|
2617
|
+
stateless_reset_token: connection_id.stateless_reset_token,
|
2618
|
+
)
|
2619
|
+
end
|
2620
|
+
end
|
2621
|
+
|
2622
|
+
private def write_path_challenge_frame(builder:, challenge:)
|
2623
|
+
buf = builder.start_frame(
|
2624
|
+
frame_type: Quic::Packet::QuicFrameType::PATH_CHALLENGE,
|
2625
|
+
capacity: PATH_CHALLENGE_FRAME_CAPACITY,
|
2626
|
+
)
|
2627
|
+
buf.push_bytes(challenge)
|
2628
|
+
|
2629
|
+
# log frame
|
2630
|
+
builder.quic_logger_frames << @quic_logger.encode_path_challenge_frame(data: challenge) if @quic_logger
|
2631
|
+
end
|
2632
|
+
|
2633
|
+
private def write_path_response_frame(builder:, challenge:)
|
2634
|
+
buf = builder.start_frame(
|
2635
|
+
frame_type: Quic::Packet::QuicFrameType::PATH_RESPONSE,
|
2636
|
+
capacity: PATH_RESPONSE_FRAME_CAPACITY,
|
2637
|
+
)
|
2638
|
+
buf.push_bytes(challenge)
|
2639
|
+
|
2640
|
+
# log frame
|
2641
|
+
builder.quic_logger_frames << @quic_logger.encode_path_response_frame(data: challenge) if @quic_logger
|
2642
|
+
end
|
2643
|
+
|
2644
|
+
private def write_ping_frame(builder:, uids: [], comment: "")
|
2645
|
+
builder.start_frame(
|
2646
|
+
frame_type: Quic::Packet::QuicFrameType::PING,
|
2647
|
+
capacity: PING_FRAME_CAPACITY,
|
2648
|
+
handler: method(:on_ping_delivery),
|
2649
|
+
handler_args: [uids.dup],
|
2650
|
+
)
|
2651
|
+
|
2652
|
+
# log frame
|
2653
|
+
builder.quic_logger_frames << @quic_logger.encode_ping_frame if @quic_logger
|
2654
|
+
end
|
2655
|
+
|
2656
|
+
private def write_reset_stream_frame(builder:, stream:)
|
2657
|
+
buf = builder.start_frame(
|
2658
|
+
frame_type: Quic::Packet::QuicFrameType::RESET_STREAM,
|
2659
|
+
capacity: RESET_STREAM_FRAME_CAPACITY,
|
2660
|
+
handler: stream.sender.method(:on_reset_delivery),
|
2661
|
+
)
|
2662
|
+
frame = stream.sender.get_reset_frame
|
2663
|
+
buf.push_uint_var(frame.stream_id)
|
2664
|
+
buf.push_uint_var(frame.error_code)
|
2665
|
+
buf.push_uint_var(frame.final_size)
|
2666
|
+
|
2667
|
+
# log frame
|
2668
|
+
if @quic_logger
|
2669
|
+
builder.quic_logger_frames << @quic_logger.encode_reset_stream_frame(error_code: frame.error_code, final_size: frame.final_size, stream_id: frame.stream_id)
|
2670
|
+
end
|
2671
|
+
end
|
2672
|
+
|
2673
|
+
private def write_retire_connection_id_frame(builder:, sequence_number:)
|
2674
|
+
buf = builder.start_frame(
|
2675
|
+
frame_type: Quic::Packet::QuicFrameType::RETIRE_CONNECTION_ID,
|
2676
|
+
capacity: RETIRE_CONNECTION_ID_CAPACITY,
|
2677
|
+
handler: method(:on_retire_connection_id_delivery),
|
2678
|
+
handler_args: [sequence_number],
|
2679
|
+
)
|
2680
|
+
buf.push_uint_var(sequence_number)
|
2681
|
+
|
2682
|
+
# log frame
|
2683
|
+
builder.quic_logger_frames << @quic_logger.encode_retire_connection_id_frame(sequence_number: sequence_number) if @quic_logger
|
2684
|
+
end
|
2685
|
+
|
2686
|
+
private def write_stop_sending_frame(builder:, stream:)
|
2687
|
+
buf = builder.start_frame(
|
2688
|
+
frame_type: Quic::Packet::QuicFrameType::STOP_SENDING,
|
2689
|
+
capacity: STOP_SENDING_FRAME_CAPACITY,
|
2690
|
+
handler: stream.receiver.method(:on_stop_sending_delivery),
|
2691
|
+
)
|
2692
|
+
frame = stream.receiver.get_stop_frame
|
2693
|
+
buf.push_uint_var(frame.stream_id)
|
2694
|
+
buf.push_uint_var(frame.error_code)
|
2695
|
+
|
2696
|
+
# log frame
|
2697
|
+
builder.quic_logger_frames << @quic_logger.encode_stop_sending_frame(error_code: frame.error_code, stream_id: frame.stream_id) if @quic_logger
|
2698
|
+
end
|
2699
|
+
|
2700
|
+
private def write_stream_frame(builder:, space:, stream:, max_offset:)
|
2701
|
+
# the frame data size is constrained by our peer's MAX_DATA and the space available in the current packet
|
2702
|
+
frame_overhead = 3 +
|
2703
|
+
Buffer.size_uint_var(stream.stream_id) +
|
2704
|
+
(stream.sender.next_offset ? Buffer.size_uint_var(stream.sender.next_offset) : 0)
|
2705
|
+
previous_send_highest = stream.sender.highest_offset
|
2706
|
+
frame = stream.sender.get_frame(max_size: builder.remaining_flight_space - frame_overhead, max_offset: max_offset)
|
2707
|
+
|
2708
|
+
if frame
|
2709
|
+
frame_type = Quic::Packet::QuicFrameType::STREAM_BASE | 2 # length
|
2710
|
+
frame_type = frame_type | 4 if frame.offset
|
2711
|
+
frame_type = frame_type | 1 if frame.fin
|
2712
|
+
buf = builder.start_frame(
|
2713
|
+
frame_type: frame_type,
|
2714
|
+
capacity: frame_overhead,
|
2715
|
+
handler: stream.sender.method(:on_data_delivery),
|
2716
|
+
handler_args: [frame.offset, frame.offset + frame.data.bytesize],
|
2717
|
+
)
|
2718
|
+
buf.push_uint_var(stream.stream_id)
|
2719
|
+
buf.push_uint_var(frame.offset) if frame.offset
|
2720
|
+
buf.push_uint16(frame.data.bytesize | 0x4000)
|
2721
|
+
buf.push_bytes(frame.data)
|
2722
|
+
|
2723
|
+
# log frame
|
2724
|
+
builder.quic_logger_frames << @quic_logger.encode_stream_frame(frame: frame, stream_id: stream.stream_id) if @quic_logger
|
2725
|
+
|
2726
|
+
return stream.sender.highest_offset - previous_send_highest
|
2727
|
+
else
|
2728
|
+
return 0
|
2729
|
+
end
|
2730
|
+
end
|
2731
|
+
|
2732
|
+
# Raise MAX_STREAM_DATA if needed.
|
2733
|
+
#
|
2734
|
+
# The only case where `stream.max_stream_data_local` is zero is for locally created unidirectional streams.
|
2735
|
+
# We skip such streams to avoid spurious logging.
|
2736
|
+
private def write_stream_limits(builder:, space:, stream:)
|
2737
|
+
if stream.max_stream_data_local > 0 && stream.receiver.highest_offset * 2 > stream.max_stream_data_local
|
2738
|
+
# binding.irb if stream.max_stream_data_local > 1000
|
2739
|
+
stream.max_stream_data_local *= 2
|
2740
|
+
# TODO: logging
|
2741
|
+
end
|
2742
|
+
|
2743
|
+
if stream.max_stream_data_local_sent != stream.max_stream_data_local
|
2744
|
+
buf = builder.start_frame(
|
2745
|
+
frame_type: Quic::Packet::QuicFrameType::MAX_STREAM_DATA,
|
2746
|
+
capacity: MAX_STREAM_DATA_FRAME_CAPACITY,
|
2747
|
+
handler: method(:on_max_stream_data_delivery),
|
2748
|
+
handler_args: [stream],
|
2749
|
+
)
|
2750
|
+
buf.push_uint_var(stream.stream_id)
|
2751
|
+
buf.push_uint_var(stream.max_stream_data_local)
|
2752
|
+
stream.max_stream_data_local_sent = stream.max_stream_data_local
|
2753
|
+
|
2754
|
+
# log frame
|
2755
|
+
if @quic_logger
|
2756
|
+
builder.quic_logger_frames << @quic_logger.encode_max_stream_data_frame(maximum: stream.max_stream_data_local, stream_id: stream.stream_id)
|
2757
|
+
end
|
2758
|
+
end
|
2759
|
+
end
|
2760
|
+
|
2761
|
+
private def write_streams_blocked_frame(builder:, frame_type:, limit:)
|
2762
|
+
buf = builder.start_frame(frame_type: frame_type, capacity: STREAMS_BLOCKED_CAPACITY)
|
2763
|
+
buf.push_uint_var(limit)
|
2764
|
+
|
2765
|
+
# log frame
|
2766
|
+
if @quic_logger
|
2767
|
+
builder.quic_logger_frames << @quic_logger.encode_streams_blocked_frame(
|
2768
|
+
is_unidirectional: (frame_type == Quic::Packet::QuicFrameType::STREAMS_BLOCKED_UNI),
|
2769
|
+
limit: limit,
|
2770
|
+
)
|
2771
|
+
end
|
2772
|
+
end
|
2773
|
+
end
|
2774
|
+
end
|
2775
|
+
end
|
2776
|
+
end
|