raioquic 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,471 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../buffer"
|
4
|
+
require_relative "rangeset"
|
5
|
+
require_relative "../crypto/aesgcm"
|
6
|
+
require "socket"
|
7
|
+
require "ipaddr"
|
8
|
+
|
9
|
+
module Raioquic
|
10
|
+
module Quic
|
11
|
+
# Raioquic::Quic::Packet
|
12
|
+
# Migrated from aioquic/src/aioquic/quic/packet.py
|
13
|
+
class Packet
|
14
|
+
PACKET_LONG_HEADER = 0x80
|
15
|
+
PACKET_FIXED_BIT = 0x40
|
16
|
+
PACKET_SPIN_BIT = 0x20
|
17
|
+
|
18
|
+
PACKET_TYPE_INITIAL = PACKET_LONG_HEADER | PACKET_FIXED_BIT | 0x00
|
19
|
+
PACKET_TYPE_ZERO_RTT = PACKET_LONG_HEADER | PACKET_FIXED_BIT | 0x10
|
20
|
+
PACKET_TYPE_HANDSHAKE = PACKET_LONG_HEADER | PACKET_FIXED_BIT | 0x20
|
21
|
+
PACKET_TYPE_RETRY = PACKET_LONG_HEADER | PACKET_FIXED_BIT | 0x30
|
22
|
+
PACKET_TYPE_ONE_RTT = PACKET_FIXED_BIT
|
23
|
+
PACKET_TYPE_MASK = 0xf0
|
24
|
+
|
25
|
+
CONNECTION_ID_MAX_SIZE = 20
|
26
|
+
PACKET_NUMBER_MAX_SIZE = 4
|
27
|
+
RETRY_AEAD_KEY_VERSION_1 = ["be0c690b9f66575a1d766b54e368c84e"].pack("H*") # https://www.rfc-editor.org/rfc/rfc9001.html#section-5.8
|
28
|
+
RETRY_AEAD_NONCE_VERSION_1 = ["461599d35d632bf2239825bb"].pack("H*") # https://www.rfc-editor.org/rfc/rfc9001.html#section-5.8
|
29
|
+
RETRY_INTEGRITY_TAG_SIZE = 16
|
30
|
+
STATELESS_RESET_TOKEN_SIZE = 16
|
31
|
+
|
32
|
+
class QuicErrorCode
|
33
|
+
NO_ERROR = 0x0
|
34
|
+
INTERNAL_ERROR = 0x1
|
35
|
+
CONNECTION_REFUSED = 0x2
|
36
|
+
FLOW_CONTROL_ERROR = 0x3
|
37
|
+
STREAM_LIMIT_ERROR = 0x4
|
38
|
+
STREAM_STATE_ERROR = 0x5
|
39
|
+
FINAL_SIZE_ERROR = 0x6
|
40
|
+
FRAME_ENCODING_ERROR = 0x7
|
41
|
+
TRANSPORT_PARAMETER_ERROR = 0x8
|
42
|
+
CONNECTION_ID_LIMIT_ERROR = 0x9
|
43
|
+
PROTOCOL_VIOLATION = 0xa
|
44
|
+
INVALID_TOKEN = 0xb
|
45
|
+
APPLICATION_ERROR = 0xc
|
46
|
+
CRYPTO_BUFFER_EXCEEDED = 0xd
|
47
|
+
KEY_UPDATE_ERROR = 0xe
|
48
|
+
AEAD_LIMIT_REACHED = 0xf
|
49
|
+
CRYPTO_ERROR = 0x100
|
50
|
+
end
|
51
|
+
|
52
|
+
class QuicProtocolVersion
|
53
|
+
NEGOTIATION = 0x0
|
54
|
+
VERSION_1 = 0x00000001
|
55
|
+
end
|
56
|
+
|
57
|
+
class QuicHeader
|
58
|
+
attr_accessor :is_long_header
|
59
|
+
attr_accessor :version
|
60
|
+
attr_accessor :packet_type
|
61
|
+
attr_accessor :destination_cid
|
62
|
+
attr_accessor :source_cid
|
63
|
+
attr_accessor :token
|
64
|
+
attr_accessor :integrity_tag
|
65
|
+
attr_accessor :rest_length
|
66
|
+
end
|
67
|
+
|
68
|
+
# Recover a packet number from a truncated packet number.
|
69
|
+
# See: Appendix A - Sample Packet Number Decoding Algorithm
|
70
|
+
def self.decode_packet_number(truncated:, num_bits:, expected:)
|
71
|
+
window = 1 << num_bits
|
72
|
+
half_window = (window / 2).floor
|
73
|
+
candidate = (expected & ~(window - 1)) | truncated
|
74
|
+
|
75
|
+
if candidate <= expected - half_window && candidate < (1 << 62) - window
|
76
|
+
candidate + window
|
77
|
+
elsif candidate > expected + half_window && candidate >= window
|
78
|
+
candidate - window
|
79
|
+
else
|
80
|
+
candidate
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Calculate the integrity tag for a RETRY packet.
|
85
|
+
def self.get_retry_integrity_tag(packet_without_tag:, original_destination_cid:)
|
86
|
+
buf = Buffer.new(capacity: 1 + original_destination_cid.size + packet_without_tag.size)
|
87
|
+
buf.push_uint8(original_destination_cid.size)
|
88
|
+
buf.push_bytes(original_destination_cid)
|
89
|
+
buf.push_bytes(packet_without_tag)
|
90
|
+
aead_key = RETRY_AEAD_KEY_VERSION_1
|
91
|
+
aead_nonce = RETRY_AEAD_NONCE_VERSION_1
|
92
|
+
aead = ::Raioquic::Crypto::AESGCM.new(aead_key)
|
93
|
+
integrity_tag = aead.encrypt(nonce: aead_nonce, data: "", associated_data: buf.data)
|
94
|
+
raise RuntimeError if integrity_tag.length != RETRY_INTEGRITY_TAG_SIZE
|
95
|
+
|
96
|
+
integrity_tag
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.get_spin_bit(first_byte)
|
100
|
+
(first_byte.unpack1("C") & PACKET_SPIN_BIT) != 0
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.is_draft_version(_version)
|
104
|
+
return false # raioquic drops draft version's implementation
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.is_long_header(first_byte)
|
108
|
+
(first_byte & PACKET_LONG_HEADER) != 0
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.pull_quic_header(buf:, host_cid_length:) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
112
|
+
first_byte = buf.pull_uint8
|
113
|
+
integrity_tag = ""
|
114
|
+
token = ""
|
115
|
+
|
116
|
+
if is_long_header(first_byte)
|
117
|
+
version = buf.pull_uint32
|
118
|
+
destination_cid_length = buf.pull_uint8
|
119
|
+
raise ValueError, "Destination CID is too long (#{destination_cid_length} bytes)" if destination_cid_length > CONNECTION_ID_MAX_SIZE
|
120
|
+
|
121
|
+
destination_cid = buf.pull_bytes(destination_cid_length)
|
122
|
+
source_cid_length = buf.pull_uint8
|
123
|
+
raise ValueError, "Souce CID is too long (#{source_cid_length} bytes)" if source_cid_length > CONNECTION_ID_MAX_SIZE
|
124
|
+
|
125
|
+
source_cid = buf.pull_bytes(source_cid_length)
|
126
|
+
|
127
|
+
if version == QuicProtocolVersion::NEGOTIATION
|
128
|
+
packet_type = nil
|
129
|
+
rest_length = buf.capacity - buf.tell
|
130
|
+
else
|
131
|
+
raise ValueError, "Packet fixed bit is zero" if (first_byte & PACKET_FIXED_BIT) == 0
|
132
|
+
|
133
|
+
packet_type = first_byte & PACKET_TYPE_MASK
|
134
|
+
# rubocop:disable Style/CaseLikeIf
|
135
|
+
if packet_type == PACKET_TYPE_INITIAL
|
136
|
+
token_length = buf.pull_uint_var
|
137
|
+
token = buf.pull_bytes(token_length)
|
138
|
+
rest_length = buf.pull_uint_var
|
139
|
+
elsif packet_type == PACKET_TYPE_RETRY
|
140
|
+
token_length = buf.capacity - buf.tell - RETRY_INTEGRITY_TAG_SIZE
|
141
|
+
token = buf.pull_bytes(token_length)
|
142
|
+
integrity_tag = buf.pull_bytes(RETRY_INTEGRITY_TAG_SIZE)
|
143
|
+
rest_length = 0
|
144
|
+
else
|
145
|
+
rest_length = buf.pull_uint_var
|
146
|
+
end
|
147
|
+
|
148
|
+
# check remainder length
|
149
|
+
raise ValueError, "Packet payload is truncated" if rest_length > buf.capacity - buf.tell
|
150
|
+
end
|
151
|
+
|
152
|
+
return QuicHeader.new.tap do |hdr|
|
153
|
+
hdr.is_long_header = true
|
154
|
+
hdr.version = version
|
155
|
+
hdr.packet_type = packet_type
|
156
|
+
hdr.destination_cid = destination_cid
|
157
|
+
hdr.source_cid = source_cid
|
158
|
+
hdr.token = token
|
159
|
+
hdr.integrity_tag = integrity_tag
|
160
|
+
hdr.rest_length = rest_length
|
161
|
+
end
|
162
|
+
else
|
163
|
+
# short header packet
|
164
|
+
raise ValueError, "Packet fixed bit is zero" if (first_byte & PACKET_FIXED_BIT) == 0
|
165
|
+
|
166
|
+
packet_type = first_byte & PACKET_TYPE_MASK
|
167
|
+
destination_cid = buf.pull_bytes(host_cid_length)
|
168
|
+
|
169
|
+
return QuicHeader.new.tap do |hdr|
|
170
|
+
hdr.is_long_header = false
|
171
|
+
hdr.version = nil
|
172
|
+
hdr.packet_type = packet_type
|
173
|
+
hdr.destination_cid = destination_cid
|
174
|
+
hdr.source_cid = ""
|
175
|
+
hdr.token = ""
|
176
|
+
hdr.integrity_tag = integrity_tag
|
177
|
+
hdr.rest_length = buf.capacity - buf.tell
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def self.encode_quic_retry(version:, source_cid:, destination_cid:, original_destination_cid:, retry_token:)
|
183
|
+
buf = Buffer.new(capacity: 7 + destination_cid.size + source_cid.size + retry_token.size + RETRY_INTEGRITY_TAG_SIZE)
|
184
|
+
buf.push_uint8(PACKET_TYPE_RETRY)
|
185
|
+
buf.push_uint32(version)
|
186
|
+
buf.push_uint8(destination_cid.length)
|
187
|
+
buf.push_bytes(destination_cid)
|
188
|
+
buf.push_uint8(source_cid.length)
|
189
|
+
buf.push_bytes(source_cid)
|
190
|
+
buf.push_bytes(retry_token)
|
191
|
+
buf.push_bytes(get_retry_integrity_tag(packet_without_tag: buf.data, original_destination_cid: original_destination_cid))
|
192
|
+
|
193
|
+
return buf.data
|
194
|
+
end
|
195
|
+
|
196
|
+
def self.encode_quic_version_negotiation(source_cid:, destination_cid:, supported_versions:)
|
197
|
+
buf = Buffer.new(capacity: 7 + destination_cid.length + source_cid.length + (4 * supported_versions.length))
|
198
|
+
buf.push_uint8(get_urandom_byte | PACKET_LONG_HEADER)
|
199
|
+
buf.push_uint32(QuicProtocolVersion::NEGOTIATION)
|
200
|
+
buf.push_uint8(destination_cid.length)
|
201
|
+
buf.push_bytes(destination_cid)
|
202
|
+
buf.push_uint8(source_cid.length)
|
203
|
+
buf.push_bytes(source_cid)
|
204
|
+
supported_versions.each do |version|
|
205
|
+
buf.push_uint32(version)
|
206
|
+
end
|
207
|
+
buf.data
|
208
|
+
end
|
209
|
+
|
210
|
+
# private
|
211
|
+
def self.get_urandom_byte
|
212
|
+
Random.urandom(1)[0].unpack1("C")
|
213
|
+
end
|
214
|
+
private_class_method :get_urandom_byte
|
215
|
+
|
216
|
+
QuicPreferredAddress = _ = Struct.new( # rubocop:disable Naming/ConstantName
|
217
|
+
:ipv4_address,
|
218
|
+
:ipv6_address,
|
219
|
+
:connection_id,
|
220
|
+
:stateless_reset_token,
|
221
|
+
)
|
222
|
+
|
223
|
+
QuicTransportParameters = _ = Struct.new( # rubocop:disable Naming/ConstantName
|
224
|
+
:original_destination_connection_id,
|
225
|
+
:max_idle_timeout,
|
226
|
+
:stateless_reset_token,
|
227
|
+
:max_udp_payload_size,
|
228
|
+
:initial_max_data,
|
229
|
+
:initial_max_stream_data_bidi_local,
|
230
|
+
:initial_max_stream_data_bidi_remote,
|
231
|
+
:initial_max_stream_data_uni,
|
232
|
+
:initial_max_streams_bidi,
|
233
|
+
:initial_max_streams_uni,
|
234
|
+
:ack_delay_exponent,
|
235
|
+
:max_ack_delay,
|
236
|
+
:disable_active_migration,
|
237
|
+
:preferred_address,
|
238
|
+
:active_connection_id_limit,
|
239
|
+
:initial_source_connection_id,
|
240
|
+
:retry_source_connection_id,
|
241
|
+
:max_datagram_frame_size,
|
242
|
+
:quantum_readiness,
|
243
|
+
)
|
244
|
+
|
245
|
+
PARAMS = {
|
246
|
+
0x00 => { name: :original_destination_connection_id, type: :bytes },
|
247
|
+
0x01 => { name: :max_idle_timeout, type: :int },
|
248
|
+
0x02 => { name: :stateless_reset_token, type: :bytes },
|
249
|
+
0x03 => { name: :max_udp_payload_size, type: :int },
|
250
|
+
0x04 => { name: :initial_max_data, type: :int },
|
251
|
+
0x05 => { name: :initial_max_stream_data_bidi_local, type: :int },
|
252
|
+
0x06 => { name: :initial_max_stream_data_bidi_remote, type: :int },
|
253
|
+
0x07 => { name: :initial_max_stream_data_uni, type: :int },
|
254
|
+
0x08 => { name: :initial_max_streams_bidi, type: :int },
|
255
|
+
0x09 => { name: :initial_max_streams_uni, type: :int },
|
256
|
+
0x0a => { name: :ack_delay_exponent, type: :int },
|
257
|
+
0x0b => { name: :max_ack_delay, type: :int },
|
258
|
+
0x0c => { name: :disable_active_migration, type: :bool },
|
259
|
+
0x0d => { name: :preferred_address, type: :quicpreferredaddress },
|
260
|
+
0x0e => { name: :active_connection_id_limit, type: :int },
|
261
|
+
0x0f => { name: :initial_source_connection_id, type: :bytes },
|
262
|
+
0x10 => { name: :retry_source_connection_id, type: :bytes },
|
263
|
+
# extensions
|
264
|
+
0x0020 => { name: :max_datagram_frame_size, type: :int },
|
265
|
+
0x0c37 => { name: :quantum_readiness, type: :bytes },
|
266
|
+
}.freeze
|
267
|
+
|
268
|
+
def self.pull_quic_preferred_address(buf)
|
269
|
+
ipv4_address = nil
|
270
|
+
ipv4_host = buf.pull_bytes(4)
|
271
|
+
ipv4_port = buf.pull_uint16
|
272
|
+
|
273
|
+
# rubocop:disable Style/IfUnlessModifier
|
274
|
+
if ipv4_host != "\x00\x00\x00\x00"
|
275
|
+
ipv4_address = { host: IPAddr.new_ntoh(ipv4_host), port: ipv4_port }
|
276
|
+
end
|
277
|
+
|
278
|
+
ipv6_address = nil
|
279
|
+
ipv6_host = buf.pull_bytes(16)
|
280
|
+
ipv6_port = buf.pull_uint16
|
281
|
+
|
282
|
+
if ipv6_host != "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
283
|
+
ipv6_address = { host: IPAddr.new_ntoh(ipv6_host), port: ipv6_port }
|
284
|
+
end
|
285
|
+
# rubocop:enable Style/IfUnlessModifier
|
286
|
+
|
287
|
+
connection_id_length = buf.pull_uint8
|
288
|
+
connection_id = buf.pull_bytes(connection_id_length)
|
289
|
+
stateless_reset_token = buf.pull_bytes(16)
|
290
|
+
|
291
|
+
QuicPreferredAddress.new.tap do |addr|
|
292
|
+
addr.ipv4_address = ipv4_address
|
293
|
+
addr.ipv6_address = ipv6_address
|
294
|
+
addr.connection_id = connection_id
|
295
|
+
addr.stateless_reset_token = stateless_reset_token
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def self.push_quic_preferred_address(buf:, preferred_address:)
|
300
|
+
if preferred_address[:ipv4_address]
|
301
|
+
buf.push_bytes(preferred_address[:ipv4_address][:host].hton)
|
302
|
+
buf.push_uint16(preferred_address[:ipv4_address][:port])
|
303
|
+
else
|
304
|
+
buf.push_bytes("\x00\x00\x00\x00\x00\x00")
|
305
|
+
end
|
306
|
+
|
307
|
+
if preferred_address[:ipv6_address]
|
308
|
+
buf.push_bytes(preferred_address[:ipv6_address][:host].hton)
|
309
|
+
buf.push_uint16(preferred_address[:ipv6_address][:port])
|
310
|
+
else
|
311
|
+
buf.push_bytes("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
|
312
|
+
end
|
313
|
+
|
314
|
+
buf.push_uint8(preferred_address[:connection_id].bytesize)
|
315
|
+
buf.push_bytes(preferred_address[:connection_id])
|
316
|
+
buf.push_bytes(preferred_address[:stateless_reset_token])
|
317
|
+
end
|
318
|
+
|
319
|
+
def self.pull_quic_transport_parameters(buf) # rubocop:disable Metrics/PerceivedComplexity
|
320
|
+
params = QuicTransportParameters.new
|
321
|
+
while !buf.eof # rubocop:disable Style/NegatedWhile
|
322
|
+
param_id = buf.pull_uint_var
|
323
|
+
param_len = buf.pull_uint_var
|
324
|
+
param_start = buf.tell
|
325
|
+
if PARAMS.key? param_id
|
326
|
+
param = PARAMS[param_id]
|
327
|
+
# rubocop:disable Style/ConditionalAssignment
|
328
|
+
if param[:type] == :int
|
329
|
+
params[param[:name]] = buf.pull_uint_var
|
330
|
+
elsif param[:type] == :bytes
|
331
|
+
params[param[:name]] = buf.pull_bytes(param_len)
|
332
|
+
elsif param[:type] == :quicpreferredaddress
|
333
|
+
params[param[:name]] = pull_quic_preferred_address(buf)
|
334
|
+
else
|
335
|
+
params[param[:name]] = true
|
336
|
+
end
|
337
|
+
# rubocop:enable Style/ConditionalAssignment
|
338
|
+
else
|
339
|
+
# skip unknown parameter
|
340
|
+
buf.pull_bytes(param_len)
|
341
|
+
end
|
342
|
+
raise RuntimeError if buf.tell != param_start + param_len
|
343
|
+
end
|
344
|
+
params
|
345
|
+
end
|
346
|
+
|
347
|
+
def self.push_quic_transport_parameters(buf:, params:)
|
348
|
+
PARAMS.each do |param_id, param_obj|
|
349
|
+
param_value = params[param_obj[:name]]
|
350
|
+
if param_value # rubocop:disable Style/Next
|
351
|
+
param_buf = Buffer.new(capacity: 65536)
|
352
|
+
# aaaa
|
353
|
+
if param_obj[:type] == :int
|
354
|
+
param_buf.push_uint_var(param_value)
|
355
|
+
elsif param_obj[:type] == :bytes
|
356
|
+
param_buf.push_bytes(param_value.to_s)
|
357
|
+
elsif param_obj[:type] == :quicpreferredaddress
|
358
|
+
push_quic_preferred_address(buf: param_buf, preferred_address: param_value)
|
359
|
+
end
|
360
|
+
# rubocop:enable Style/CaseLikeIf
|
361
|
+
buf.push_uint_var(param_id)
|
362
|
+
buf.push_uint_var(param_buf.tell)
|
363
|
+
buf.push_bytes(param_buf.data)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
class QuicFrameType
|
369
|
+
PADDING = 0x00
|
370
|
+
PING = 0x01
|
371
|
+
ACK = 0x02
|
372
|
+
ACK_ECN = 0x03
|
373
|
+
RESET_STREAM = 0x04
|
374
|
+
STOP_SENDING = 0x05
|
375
|
+
CRYPTO = 0x06
|
376
|
+
NEW_TOKEN = 0x07
|
377
|
+
STREAM_BASE = 0x08
|
378
|
+
MAX_DATA = 0x10
|
379
|
+
MAX_STREAM_DATA = 0x11
|
380
|
+
MAX_STREAMS_BIDI = 0x12
|
381
|
+
MAX_STREAMS_UNI = 0x13
|
382
|
+
DATA_BLOCKED = 0x14
|
383
|
+
STREAM_DATA_BLOCKED = 0x15
|
384
|
+
STREAMS_BLOCKED_BIDI = 0x16
|
385
|
+
STREAMS_BLOCKED_UNI = 0x17
|
386
|
+
NEW_CONNECTION_ID = 0x18
|
387
|
+
RETIRE_CONNECTION_ID = 0x19
|
388
|
+
PATH_CHALLENGE = 0x1A
|
389
|
+
PATH_RESPONSE = 0x1B
|
390
|
+
TRANSPORT_CLOSE = 0x1C
|
391
|
+
APPLICATION_CLOSE = 0x1D
|
392
|
+
HANDSHAKE_DONE = 0x1E
|
393
|
+
DATAGRAM = 0x30
|
394
|
+
DATAGRAM_WITH_LENGTH = 0x31
|
395
|
+
end
|
396
|
+
|
397
|
+
NON_ACK_ELICITING_FRAME_TYPES = [
|
398
|
+
QuicFrameType::ACK,
|
399
|
+
QuicFrameType::ACK_ECN,
|
400
|
+
QuicFrameType::PADDING,
|
401
|
+
QuicFrameType::TRANSPORT_CLOSE,
|
402
|
+
QuicFrameType::APPLICATION_CLOSE,
|
403
|
+
].freeze
|
404
|
+
NON_IN_FLIGHT_FRAME_TYPES = [
|
405
|
+
QuicFrameType::ACK,
|
406
|
+
QuicFrameType::ACK_ECN,
|
407
|
+
QuicFrameType::TRANSPORT_CLOSE,
|
408
|
+
QuicFrameType::APPLICATION_CLOSE,
|
409
|
+
].freeze
|
410
|
+
PROBING_FRAME_TYPES = [
|
411
|
+
QuicFrameType::PATH_CHALLENGE,
|
412
|
+
QuicFrameType::PATH_RESPONSE,
|
413
|
+
QuicFrameType::PADDING,
|
414
|
+
QuicFrameType::NEW_CONNECTION_ID,
|
415
|
+
].freeze
|
416
|
+
|
417
|
+
QuicResetStreamFrame = _ = Struct.new( # rubocop:disable Naming/ConstantName
|
418
|
+
:error_code,
|
419
|
+
:final_size,
|
420
|
+
:stream_id,
|
421
|
+
)
|
422
|
+
|
423
|
+
QuicStopSendingFrame = _ = Struct.new( # rubocop:disable Naming/ConstantName
|
424
|
+
:error_code,
|
425
|
+
:stream_id,
|
426
|
+
)
|
427
|
+
|
428
|
+
QuicStreamFrame = _ = Struct.new( # rubocop:disable Naming/ConstantName
|
429
|
+
:data,
|
430
|
+
:fin,
|
431
|
+
:offset,
|
432
|
+
)
|
433
|
+
|
434
|
+
def self.pull_ack_frame(buf)
|
435
|
+
rangeset = Rangeset.new
|
436
|
+
ends = buf.pull_uint_var # largeset acknowledged
|
437
|
+
delay = buf.pull_uint_var
|
438
|
+
ack_range_count = buf.pull_uint_var
|
439
|
+
ack_count = buf.pull_uint_var # first ack range
|
440
|
+
rangeset.add(ends - ack_count, ends + 1)
|
441
|
+
ends -= ack_count
|
442
|
+
ack_range_count.times do
|
443
|
+
ends -= buf.pull_uint_var + 2
|
444
|
+
ack_count = buf.pull_uint_var
|
445
|
+
rangeset.add(ends - ack_count, ends + 1)
|
446
|
+
ends -= ack_count
|
447
|
+
end
|
448
|
+
[rangeset, delay]
|
449
|
+
end
|
450
|
+
|
451
|
+
def self.push_ack_frame(buf:, rangeset:, delay:)
|
452
|
+
ranges = rangeset.length
|
453
|
+
index = ranges - 1
|
454
|
+
r = rangeset.list[index]
|
455
|
+
buf.push_uint_var(r.last - 1)
|
456
|
+
buf.push_uint_var(delay)
|
457
|
+
buf.push_uint_var(index)
|
458
|
+
buf.push_uint_var(r.last - 1 - r.first)
|
459
|
+
start = r.first
|
460
|
+
while index > 0
|
461
|
+
index -= 1
|
462
|
+
r = rangeset.list[index]
|
463
|
+
buf.push_uint_var(start - r.last - 1)
|
464
|
+
buf.push_uint_var(r.last - r.first - 1)
|
465
|
+
start = r.first
|
466
|
+
end
|
467
|
+
return ranges
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|