-
-
Notifications
You must be signed in to change notification settings - Fork 35.5k
Expand file tree
/
Copy pathdata.cc
More file actions
544 lines (466 loc) Β· 16.7 KB
/
data.cc
File metadata and controls
544 lines (466 loc) Β· 16.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
#if HAVE_OPENSSL && HAVE_QUIC
#include "guard.h"
#ifndef OPENSSL_NO_QUIC
#include <env-inl.h>
#include <memory_tracker-inl.h>
#include <ngtcp2/ngtcp2.h>
#include <node_sockaddr-inl.h>
#include <openssl/ssl.h>
#include <string_bytes.h>
#include <v8.h>
#include "data.h"
#include "defs.h"
#include "util.h"
namespace node {
using v8::Array;
using v8::ArrayBuffer;
using v8::ArrayBufferView;
using v8::BackingStore;
using v8::BigInt;
using v8::Just;
using v8::Local;
using v8::Maybe;
using v8::MaybeLocal;
using v8::NewStringType;
using v8::Nothing;
using v8::String;
using v8::Uint8Array;
using v8::Undefined;
using v8::Value;
namespace quic {
thread_local int DebugIndentScope::indent_ = 0;
Path::Path(const SocketAddress& local, const SocketAddress& remote) {
ngtcp2_addr_init(&this->local, local.data(), local.length());
ngtcp2_addr_init(&this->remote, remote.data(), remote.length());
}
std::string Path::ToString() const {
DebugIndentScope indent;
auto prefix = indent.Prefix();
const sockaddr* local_in = reinterpret_cast<const sockaddr*>(local.addr);
auto local_addr = SocketAddress::GetAddress(local_in);
auto local_port = SocketAddress::GetPort(local_in);
const sockaddr* remote_in = reinterpret_cast<const sockaddr*>(remote.addr);
auto remote_addr = SocketAddress::GetAddress(remote_in);
auto remote_port = SocketAddress::GetPort(remote_in);
std::string res("{");
res += prefix + "local: " + local_addr + ":" + std::to_string(local_port);
res += prefix + "remote: " + remote_addr + ":" + std::to_string(remote_port);
res += indent.Close();
return res;
}
PathStorage::PathStorage() {
Reset();
}
void PathStorage::Reset() {
ngtcp2_path_storage_zero(this);
}
void PathStorage::CopyTo(PathStorage* path) const {
ngtcp2_path_copy(&path->path, &this->path);
}
bool PathStorage::operator==(const PathStorage& other) const {
return ngtcp2_path_eq(&path, &other.path) != 0;
}
bool PathStorage::operator!=(const PathStorage& other) const {
return ngtcp2_path_eq(&path, &other.path) == 0;
}
// ============================================================================
Store::Store(std::shared_ptr<BackingStore> store, size_t length, size_t offset)
: store_(std::move(store)), length_(length), offset_(offset) {
CHECK_LE(offset_, store_->ByteLength());
CHECK_LE(length_, store_->ByteLength() - offset_);
}
Store::Store(std::unique_ptr<BackingStore> store, size_t length, size_t offset)
: store_(std::move(store)), length_(length), offset_(offset) {
CHECK_LE(offset_, store_->ByteLength());
CHECK_LE(length_, store_->ByteLength() - offset_);
}
Maybe<Store> Store::From(Local<ArrayBuffer> buffer) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
Environment* env = Environment::GetCurrent(isolate->GetCurrentContext());
auto length = buffer->ByteLength();
auto dest = ArrayBuffer::NewBackingStore(
isolate,
length,
v8::BackingStoreInitializationMode::kUninitialized,
v8::BackingStoreOnFailureMode::kReturnNull);
if (!dest) {
THROW_ERR_MEMORY_ALLOCATION_FAILED(env);
return Nothing<Store>();
}
if (length > 0) {
memcpy(dest->Data(), buffer->Data(), length);
}
return Just(Store(std::move(dest), length, 0));
}
Maybe<Store> Store::From(Local<ArrayBufferView> view) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
Environment* env = Environment::GetCurrent(isolate->GetCurrentContext());
auto length = view->ByteLength();
auto offset = view->ByteOffset();
auto dest = ArrayBuffer::NewBackingStore(
isolate,
length,
v8::BackingStoreInitializationMode::kUninitialized,
v8::BackingStoreOnFailureMode::kReturnNull);
if (!dest) {
THROW_ERR_MEMORY_ALLOCATION_FAILED(env);
return Nothing<Store>();
}
if (length > 0) {
memcpy(dest->Data(),
static_cast<const uint8_t*>(view->Buffer()->Data()) + offset,
length);
}
return Just(Store(std::move(dest), length, 0));
}
Store Store::CopyFrom(Local<ArrayBuffer> buffer) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
auto backing = buffer->GetBackingStore();
auto length = buffer->ByteLength();
auto dest = ArrayBuffer::NewBackingStore(
isolate, length, v8::BackingStoreInitializationMode::kUninitialized);
// copy content
memcpy(dest->Data(), backing->Data(), length);
return Store(std::move(dest), length, 0);
}
Store Store::CopyFrom(Local<ArrayBufferView> view) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
auto backing = view->Buffer()->GetBackingStore();
auto length = view->ByteLength();
auto offset = view->ByteOffset();
auto dest = ArrayBuffer::NewBackingStore(
isolate, length, v8::BackingStoreInitializationMode::kUninitialized);
// copy content
memcpy(dest->Data(), static_cast<char*>(backing->Data()) + offset, length);
return Store(std::move(dest), length, 0);
}
Local<Uint8Array> Store::ToUint8Array(Environment* env) const {
return !store_
? Uint8Array::New(ArrayBuffer::New(env->isolate(), 0), 0, 0)
: Uint8Array::New(
ArrayBuffer::New(env->isolate(), store_), offset_, length_);
}
Store::operator bool() const {
return store_ != nullptr;
}
size_t Store::length() const {
return length_;
}
size_t Store::total_length() const {
return store_ ? store_->ByteLength() : 0;
}
template <typename T, OneByteType N>
T Store::convert() const {
// We can only safely convert to T if we have a valid store.
CHECK(store_);
T buf;
buf.base = static_cast<N*>(store_->Data()) + offset_;
buf.len = length_;
return buf;
}
Store::operator uv_buf_t() const {
return convert<uv_buf_t, char>();
}
Store::operator ngtcp2_vec() const {
return convert<ngtcp2_vec, uint8_t>();
}
Store::operator nghttp3_vec() const {
return convert<nghttp3_vec, uint8_t>();
}
void Store::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("store", store_);
}
// ============================================================================
namespace {
constexpr std::string_view TypeName(QuicError::Type type) {
switch (type) {
case QuicError::Type::APPLICATION:
return "application";
case QuicError::Type::TRANSPORT:
return "transport";
case QuicError::Type::VERSION_NEGOTIATION:
return "version_negotiation";
case QuicError::Type::IDLE_CLOSE:
return "idle_close";
case QuicError::Type::DROP_CONNECTION:
return "drop_connection";
case QuicError::Type::RETRY:
return "retry";
default:
return "<unknown>";
}
}
} // namespace
QuicError::QuicError(const std::string& reason)
: reason_(reason), error_(), ptr_(&error_) {
ngtcp2_ccerr_default(&error_);
}
// Keep in mind that reason_ in each of the constructors here will copy
// the string from the ngtcp2_ccerr input.
QuicError::QuicError(const ngtcp2_ccerr* ptr)
: reason_(reinterpret_cast<const char*>(ptr->reason), ptr->reasonlen),
error_(),
ptr_(ptr) {}
QuicError::QuicError(const ngtcp2_ccerr& error)
: reason_(reinterpret_cast<const char*>(error.reason), error.reasonlen),
error_(error),
ptr_(&error_) {}
QuicError::QuicError(QuicError&& other) noexcept
: reason_(std::move(other.reason_)),
error_(other.error_),
ptr_(other.ptr_ == &other.error_ ? &error_ : other.ptr_) {
// Fix up the internal reason pointer after moving.
error_.reason = reason_c_str();
error_.reasonlen = reason_.length();
}
QuicError& QuicError::operator=(QuicError&& other) noexcept {
if (this != &other) {
reason_ = std::move(other.reason_);
error_ = other.error_;
ptr_ = (other.ptr_ == &other.error_) ? &error_ : other.ptr_;
error_.reason = reason_c_str();
error_.reasonlen = reason_.length();
}
return *this;
}
QuicError::QuicError(const QuicError& other)
: reason_(other.reason_),
error_(other.error_),
ptr_(other.ptr_ == &other.error_ ? &error_ : other.ptr_) {
error_.reason = reason_c_str();
error_.reasonlen = reason_.length();
}
QuicError& QuicError::operator=(const QuicError& other) {
if (this != &other) {
reason_ = other.reason_;
error_ = other.error_;
ptr_ = (other.ptr_ == &other.error_) ? &error_ : other.ptr_;
error_.reason = reason_c_str();
error_.reasonlen = reason_.length();
}
return *this;
}
const uint8_t* QuicError::reason_c_str() const {
return reinterpret_cast<const uint8_t*>(reason_.c_str());
}
bool QuicError::operator!=(const QuicError& other) const {
return !(*this == other);
}
bool QuicError::operator==(const QuicError& other) const {
if (this == &other) return true;
return type() == other.type() && code() == other.code() &&
frame_type() == other.frame_type();
}
QuicError::Type QuicError::type() const {
return static_cast<Type>(ptr_->type);
}
error_code QuicError::code() const {
return ptr_->error_code;
}
uint64_t QuicError::frame_type() const {
return ptr_->frame_type;
}
const std::string_view QuicError::reason() const {
return reason_;
}
QuicError::operator const ngtcp2_ccerr&() const {
return *ptr_;
}
QuicError::operator const ngtcp2_ccerr*() const {
return ptr_;
}
std::string QuicError::reason_for_liberr(int liberr) {
return ngtcp2_strerror(liberr);
}
std::string QuicError::reason_for_h3_liberr(int liberr) {
return nghttp3_strerror(liberr);
}
bool QuicError::is_fatal_liberror(int liberr) {
return ngtcp2_err_is_fatal(liberr) != 0;
}
bool QuicError::is_fatal_h3_liberror(int liberr) {
return nghttp3_err_is_fatal(liberr) != 0;
}
error_code QuicError::liberr_to_code(int liberr) {
return ngtcp2_err_infer_quic_transport_error_code(liberr);
}
error_code QuicError::h3_liberr_to_code(int liberr) {
return nghttp3_err_infer_quic_app_error_code(liberr);
}
bool QuicError::is_crypto_error() const {
return code() & NGTCP2_CRYPTO_ERROR;
}
std::optional<int> QuicError::get_crypto_error() const {
if (!is_crypto_error()) return std::nullopt;
return code() & ~NGTCP2_CRYPTO_ERROR;
}
const char* QuicError::name() const {
// CRYPTO_ERROR carries a TLS alert in its low byte (RFC 9001 sec. 4.8).
// OpenSSL's SSL_alert_desc_string_long owns a stable string for every
// alert it knows about; we filter out the "unknown" placeholder so the
// JS side can present `errorName` as undefined for unrecognised alerts.
if (auto alert = get_crypto_error()) {
const char* n = SSL_alert_desc_string_long(*alert);
if (n != nullptr && std::string_view(n) != "unknown") return n;
return nullptr;
}
// Named transport-layer error codes from RFC 9000 sec. 20.1 (and the
// RFC 9368 version-negotiation extension). Application error codes are
// opaque to QUIC, so we only decode for transport.
if (type() != Type::TRANSPORT) return nullptr;
switch (code()) {
case NGTCP2_NO_ERROR:
return "NO_ERROR";
case NGTCP2_INTERNAL_ERROR:
return "INTERNAL_ERROR";
case NGTCP2_CONNECTION_REFUSED:
return "CONNECTION_REFUSED";
case NGTCP2_FLOW_CONTROL_ERROR:
return "FLOW_CONTROL_ERROR";
case NGTCP2_STREAM_LIMIT_ERROR:
return "STREAM_LIMIT_ERROR";
case NGTCP2_STREAM_STATE_ERROR:
return "STREAM_STATE_ERROR";
case NGTCP2_FINAL_SIZE_ERROR:
return "FINAL_SIZE_ERROR";
case NGTCP2_FRAME_ENCODING_ERROR:
return "FRAME_ENCODING_ERROR";
case NGTCP2_TRANSPORT_PARAMETER_ERROR:
return "TRANSPORT_PARAMETER_ERROR";
case NGTCP2_CONNECTION_ID_LIMIT_ERROR:
return "CONNECTION_ID_LIMIT_ERROR";
case NGTCP2_PROTOCOL_VIOLATION:
return "PROTOCOL_VIOLATION";
case NGTCP2_INVALID_TOKEN:
return "INVALID_TOKEN";
case NGTCP2_APPLICATION_ERROR:
return "APPLICATION_ERROR";
case NGTCP2_CRYPTO_BUFFER_EXCEEDED:
return "CRYPTO_BUFFER_EXCEEDED";
case NGTCP2_KEY_UPDATE_ERROR:
return "KEY_UPDATE_ERROR";
case NGTCP2_AEAD_LIMIT_REACHED:
return "AEAD_LIMIT_REACHED";
case NGTCP2_NO_VIABLE_PATH:
return "NO_VIABLE_PATH";
case NGTCP2_VERSION_NEGOTIATION_ERROR:
return "VERSION_NEGOTIATION_ERROR";
default:
return nullptr;
}
}
MaybeLocal<Value> QuicError::ToV8Value(Environment* env) const {
if ((type() == Type::TRANSPORT && code() == NGTCP2_NO_ERROR) ||
(type() == Type::APPLICATION && code() == NGHTTP3_H3_NO_ERROR) ||
type() == Type::IDLE_CLOSE) {
// Note that we only return undefined for *known* no-error application
// codes. It is possible that other application types use other specific
// no-error codes, but since we don't know which application is being used,
// we'll just return the error code value for those below.
// Idle close is always clean β the session timed out normally.
return Undefined(env->isolate());
}
Local<Value> type_str;
if (!node::ToV8Value(env->context(), TypeName(type())).ToLocal(&type_str)) {
return {};
}
Local<Value> argv[] = {
type_str,
BigInt::NewFromUnsigned(env->isolate(), code()),
Undefined(env->isolate()),
Undefined(env->isolate()),
};
// Note that per the QUIC specification, the reason, if present, is
// expected to be UTF-8 encoded. The spec uses the term "SHOULD" here,
// which means that is is entirely possible that some QUIC implementation
// could choose a different encoding, in which case the conversion here
// will produce garbage. That's ok though, we're going to use the default
// assumption that the impl is following the guidelines.
if (reason_.length() > 0 &&
!node::ToV8Value(env->context(), reason()).ToLocal(&argv[2])) {
return {};
}
// Attach a human-readable name for known wire codes (RFC 9000 sec. 20.1
// names and OpenSSL TLS alert descriptions for CRYPTO_ERROR). Unknown
// codes leave the slot as undefined.
if (const char* n = name()) {
if (!String::NewFromUtf8(env->isolate(), n, NewStringType::kInternalized)
.ToLocal(&argv[3])) {
return {};
}
}
return Array::New(env->isolate(), argv, arraysize(argv)).As<Value>();
}
std::string QuicError::ToString() const {
std::string str = "QuicError(";
str += TypeName(type());
str += ") ";
str += std::to_string(code());
if (!reason_.empty()) str += ": " + reason_;
return str;
}
void QuicError::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("reason", reason_.length());
}
QuicError QuicError::ForTransport(TransportError code, std::string reason) {
return ForTransport(static_cast<error_code>(code), std::move(reason));
}
QuicError QuicError::ForTransport(error_code code, std::string reason) {
QuicError error(std::move(reason));
ngtcp2_ccerr_set_transport_error(
&error.error_, code, error.reason_c_str(), error.reason().length());
return error;
}
QuicError QuicError::ForApplication(Http3Error code, std::string reason) {
return ForApplication(static_cast<error_code>(code), std::move(reason));
}
QuicError QuicError::ForApplication(error_code code, std::string reason) {
QuicError error(std::move(reason));
ngtcp2_ccerr_set_application_error(
&error.error_, code, error.reason_c_str(), error.reason().length());
return error;
}
QuicError QuicError::ForVersionNegotiation(std::string reason) {
return ForNgtcp2Error(NGTCP2_ERR_RECV_VERSION_NEGOTIATION, std::move(reason));
}
QuicError QuicError::ForIdleClose(std::string reason) {
return ForNgtcp2Error(NGTCP2_ERR_IDLE_CLOSE, std::move(reason));
}
QuicError QuicError::ForDropConnection(std::string reason) {
return ForNgtcp2Error(NGTCP2_ERR_DROP_CONN, std::move(reason));
}
QuicError QuicError::ForRetry(std::string reason) {
return ForNgtcp2Error(NGTCP2_ERR_RETRY, std::move(reason));
}
QuicError QuicError::ForNgtcp2Error(int code, std::string reason) {
QuicError error(std::move(reason));
ngtcp2_ccerr_set_liberr(
&error.error_, code, error.reason_c_str(), error.reason().length());
return error;
}
QuicError QuicError::ForTlsAlert(int code, std::string reason) {
QuicError error(std::move(reason));
ngtcp2_ccerr_set_tls_alert(
&error.error_, code, error.reason_c_str(), error.reason().length());
return error;
}
QuicError QuicError::FromConnectionClose(ngtcp2_conn* session) {
return QuicError(ngtcp2_conn_get_ccerr(session));
}
#define V(name) \
const QuicError QuicError::TRANSPORT_##name = \
ForTransport(TransportError::name);
QUIC_TRANSPORT_ERRORS(V)
#undef V
const QuicError QuicError::TRANSPORT_NO_ERROR =
ForTransport(TransportError::NO_ERROR_);
const QuicError QuicError::HTTP3_NO_ERROR = ForApplication(NGHTTP3_H3_NO_ERROR);
const QuicError QuicError::VERSION_NEGOTIATION = ForVersionNegotiation();
const QuicError QuicError::IDLE_CLOSE = ForIdleClose();
const QuicError QuicError::DROP_CONNECTION = ForDropConnection();
const QuicError QuicError::RETRY = ForRetry();
const QuicError QuicError::INTERNAL_ERROR = ForNgtcp2Error(NGTCP2_ERR_INTERNAL);
} // namespace quic
} // namespace node
#endif // OPENSSL_NO_QUIC
#endif // HAVE_OPENSSL && HAVE_QUIC