/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/quic/sessionticket.cc
193 строки
6 KB
James M Snell
quic: complete the internal implementation of QUIC
06 май 2026, 05:16
06 май 2026, 05:16
cf91d18
Код
Авторство
О чём код?
#if HAVE_OPENSSL && HAVE_QUIC #include "guard.h" #ifndef OPENSSL_NO_QUIC #include <env-inl.h> #include <memory_tracker-inl.h> #include <ngtcp2/ngtcp2_crypto.h> #include <node_buffer.h> #include <node_errors.h> #include <node_sockaddr-inl.h> #include "session.h" #include "sessionticket.h" #include "tlscontext.h" namespace node { using v8::ArrayBufferView; using v8::Just; using v8::Local; using v8::Maybe; using v8::MaybeLocal; using v8::Nothing; using v8::Object; using v8::Value; using v8::ValueDeserializer; using v8::ValueSerializer; namespace quic { namespace { SessionTicket::AppData::Source* GetAppDataSource(SSL* ssl) { auto& tls_session = TLSSession::From(ssl); return &tls_session.session().ticket_app_data_source(); } } // namespace SessionTicket::SessionTicket(Store&& ticket, Store&& transport_params) : ticket_(std::move(ticket)), transport_params_(std::move(transport_params)) {} Maybe<SessionTicket> SessionTicket::FromV8Value(Environment* env, Local<Value> value) { if (!value->IsArrayBufferView()) { THROW_ERR_INVALID_ARG_TYPE(env, "The ticket must be an ArrayBufferView."); return Nothing<SessionTicket>(); } Store content; if (!Store::From(value.As<ArrayBufferView>()).To(&content)) { return Nothing<SessionTicket>(); } ngtcp2_vec vec = content; ValueDeserializer des(env->isolate(), vec.base, vec.len); if (des.ReadHeader(env->context()).IsNothing()) { THROW_ERR_INVALID_ARG_VALUE(env, "The ticket format is invalid."); return Nothing<SessionTicket>(); } Local<Value> ticket; Local<Value> transport_params; if (!des.ReadValue(env->context()).ToLocal(&ticket) || !des.ReadValue(env->context()).ToLocal(&transport_params)) { return Nothing<SessionTicket>(); } if (!ticket->IsArrayBufferView()) { THROW_ERR_INVALID_ARG_TYPE(env, "The ticket must be an ArrayBufferView"); return Nothing<SessionTicket>(); } if (!transport_params->IsArrayBufferView()) { THROW_ERR_INVALID_ARG_TYPE( env, "The transport parameters must be an ArrayBufferView"); return Nothing<SessionTicket>(); } Store ticket_store; Store transport_params_store; if (!Store::From(ticket.As<ArrayBufferView>()).To(&ticket_store)) { return Nothing<SessionTicket>(); } if (!Store::From(transport_params.As<ArrayBufferView>()) .To(&transport_params_store)) { return Nothing<SessionTicket>(); } return Just(SessionTicket(std::move(ticket_store), std::move(transport_params_store))); } MaybeLocal<Object> SessionTicket::encode(Environment* env) const { auto context = env->context(); ValueSerializer ser(env->isolate()); ser.WriteHeader(); if (ser.WriteValue(context, ticket_.ToUint8Array(env)).IsNothing() || ser.WriteValue(context, transport_params_.ToUint8Array(env)) .IsNothing()) { return MaybeLocal<Object>(); } auto result = ser.Release(); return Buffer::New(env, reinterpret_cast<char*>(result.first), result.second); } const uv_buf_t SessionTicket::ticket() const { return ticket_; } const ngtcp2_vec SessionTicket::transport_params() const { return transport_params_; } void SessionTicket::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("ticket", ticket_); tracker->TrackField("transport_params", transport_params_); } int SessionTicket::GenerateCallback(SSL* ssl, void* arg) { AppData::Collect(ssl); return 1; } SSL_TICKET_RETURN SessionTicket::DecryptedCallback(SSL* ssl, SSL_SESSION* session, const unsigned char* keyname, size_t keyname_len, SSL_TICKET_STATUS status, void* arg) { switch (status) { default: return SSL_TICKET_RETURN_IGNORE; case SSL_TICKET_EMPTY: [[fallthrough]]; case SSL_TICKET_NO_DECRYPT: return SSL_TICKET_RETURN_IGNORE_RENEW; case SSL_TICKET_SUCCESS_RENEW: return static_cast<SSL_TICKET_RETURN>( AppData::Extract(ssl, session, AppData::Source::Flag::STATUS_RENEW)); case SSL_TICKET_SUCCESS: return static_cast<SSL_TICKET_RETURN>(AppData::Extract(ssl, session)); } } SessionTicket::AppData::AppData(SSL* ssl, SSL_SESSION* session) : ssl_(ssl), session_(session) {} SSL_SESSION* SessionTicket::AppData::GetSession() const { return session_ != nullptr ? session_ : SSL_get0_session(ssl_); } bool SessionTicket::AppData::Set(const uv_buf_t& data) { if (set_ || data.base == nullptr || data.len == 0) return false; set_ = true; SSL_SESSION_set1_ticket_appdata(GetSession(), data.base, data.len); return set_; } std::optional<const uv_buf_t> SessionTicket::AppData::Get() const { auto* sess = GetSession(); if (sess == nullptr) return std::nullopt; uv_buf_t buf; int ret = SSL_SESSION_get0_ticket_appdata(sess, reinterpret_cast<void**>(&buf.base), reinterpret_cast<size_t*>(&buf.len)); if (ret != 1) return std::nullopt; return buf; } void SessionTicket::AppData::Collect(SSL* ssl) { AppData app_data(ssl); if (auto source = GetAppDataSource(ssl)) { source->CollectSessionTicketAppData(&app_data); } } SessionTicket::AppData::Status SessionTicket::AppData::Extract( SSL* ssl, SSL_SESSION* session, Source::Flag flag) { auto source = GetAppDataSource(ssl); if (source != nullptr) { AppData app_data(ssl, session); return source->ExtractSessionTicketAppData(app_data, flag); } return Status::TICKET_IGNORE; } } // namespace quic } // namespace node #endif // OPENSSL_NO_QUIC #endif // HAVE_OPENSSL && HAVE_QUIC