|
| 1 | +#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC |
| 2 | + |
| 3 | +#include "transportparams.h" |
| 4 | +#include <env-inl.h> |
| 5 | +#include <memory_tracker-inl.h> |
| 6 | +#include <node_sockaddr-inl.h> |
| 7 | +#include <util-inl.h> |
| 8 | +#include <v8.h> |
| 9 | +#include "bindingdata.h" |
| 10 | +#include "defs.h" |
| 11 | +#include "tokens.h" |
| 12 | + |
| 13 | +namespace node { |
| 14 | + |
| 15 | +using v8::ArrayBuffer; |
| 16 | +using v8::Just; |
| 17 | +using v8::Local; |
| 18 | +using v8::Maybe; |
| 19 | +using v8::Nothing; |
| 20 | +using v8::Object; |
| 21 | +using v8::Value; |
| 22 | + |
| 23 | +namespace quic { |
| 24 | +TransportParams::Config::Config(Side side, |
| 25 | + const CID& ocid, |
| 26 | + const CID& retry_scid) |
| 27 | + : side(side), ocid(ocid), retry_scid(retry_scid) {} |
| 28 | + |
| 29 | +Maybe<const TransportParams::Options> TransportParams::Options::From( |
| 30 | + Environment* env, Local<Value> value) { |
| 31 | + if (value.IsEmpty() || !value->IsObject()) { |
| 32 | + return Nothing<const Options>(); |
| 33 | + } |
| 34 | + |
| 35 | + auto& state = BindingData::Get(env); |
| 36 | + auto params = value.As<Object>(); |
| 37 | + Options options; |
| 38 | + |
| 39 | +#define SET(name) \ |
| 40 | + SetOption<TransportParams::Options, &TransportParams::Options::name>( \ |
| 41 | + env, &options, params, state.name##_string()) |
| 42 | + |
| 43 | + if (!SET(initial_max_stream_data_bidi_local) || |
| 44 | + !SET(initial_max_stream_data_bidi_remote) || |
| 45 | + !SET(initial_max_stream_data_uni) || !SET(initial_max_data) || |
| 46 | + !SET(initial_max_streams_bidi) || !SET(initial_max_streams_uni) || |
| 47 | + !SET(max_idle_timeout) || !SET(active_connection_id_limit) || |
| 48 | + !SET(ack_delay_exponent) || !SET(max_ack_delay) || |
| 49 | + !SET(max_datagram_frame_size) || !SET(disable_active_migration)) { |
| 50 | + return Nothing<const Options>(); |
| 51 | + } |
| 52 | + |
| 53 | +#undef SET |
| 54 | + |
| 55 | + return Just<const Options>(options); |
| 56 | +} |
| 57 | + |
| 58 | +TransportParams::TransportParams(Type type) : type_(type), ptr_(¶ms_) {} |
| 59 | + |
| 60 | +TransportParams::TransportParams(Type type, const ngtcp2_transport_params* ptr) |
| 61 | + : type_(type), ptr_(ptr) {} |
| 62 | + |
| 63 | +TransportParams::TransportParams(const Config& config, const Options& options) |
| 64 | + : TransportParams(Type::ENCRYPTED_EXTENSIONS) { |
| 65 | + ngtcp2_transport_params_default(¶ms_); |
| 66 | + params_.active_connection_id_limit = options.active_connection_id_limit; |
| 67 | + params_.initial_max_stream_data_bidi_local = |
| 68 | + options.initial_max_stream_data_bidi_local; |
| 69 | + params_.initial_max_stream_data_bidi_remote = |
| 70 | + options.initial_max_stream_data_bidi_remote; |
| 71 | + params_.initial_max_stream_data_uni = options.initial_max_stream_data_uni; |
| 72 | + params_.initial_max_streams_bidi = options.initial_max_streams_bidi; |
| 73 | + params_.initial_max_streams_uni = options.initial_max_streams_uni; |
| 74 | + params_.initial_max_data = options.initial_max_data; |
| 75 | + params_.max_idle_timeout = options.max_idle_timeout * NGTCP2_SECONDS; |
| 76 | + params_.max_ack_delay = options.max_ack_delay; |
| 77 | + params_.ack_delay_exponent = options.ack_delay_exponent; |
| 78 | + params_.max_datagram_frame_size = options.max_datagram_frame_size; |
| 79 | + params_.disable_active_migration = options.disable_active_migration ? 1 : 0; |
| 80 | + params_.preferred_address_present = 0; |
| 81 | + params_.stateless_reset_token_present = 0; |
| 82 | + params_.retry_scid_present = 0; |
| 83 | + |
| 84 | + if (config.side == Side::SERVER) { |
| 85 | + // For the server side, the original dcid is always set. |
| 86 | + CHECK(config.ocid); |
| 87 | + params_.original_dcid = config.ocid; |
| 88 | + |
| 89 | + // The retry_scid is only set if the server validated a retry token. |
| 90 | + if (config.retry_scid) { |
| 91 | + params_.retry_scid = config.retry_scid; |
| 92 | + params_.retry_scid_present = 1; |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + if (options.preferred_address_ipv4.has_value()) |
| 97 | + SetPreferredAddress(options.preferred_address_ipv4.value()); |
| 98 | + |
| 99 | + if (options.preferred_address_ipv6.has_value()) |
| 100 | + SetPreferredAddress(options.preferred_address_ipv6.value()); |
| 101 | +} |
| 102 | + |
| 103 | +TransportParams::TransportParams(Type type, const ngtcp2_vec& vec) |
| 104 | + : TransportParams(type) { |
| 105 | + int ret = ngtcp2_decode_transport_params( |
| 106 | + ¶ms_, |
| 107 | + static_cast<ngtcp2_transport_params_type>(type), |
| 108 | + vec.base, |
| 109 | + vec.len); |
| 110 | + |
| 111 | + if (ret != 0) { |
| 112 | + ptr_ = nullptr; |
| 113 | + error_ = QuicError::ForNgtcp2Error(ret); |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +Store TransportParams::Encode(Environment* env) { |
| 118 | + if (ptr_ == nullptr) { |
| 119 | + error_ = QuicError::ForNgtcp2Error(NGTCP2_INTERNAL_ERROR); |
| 120 | + return Store(); |
| 121 | + } |
| 122 | + |
| 123 | + // Preflight to see how much storage we'll need. |
| 124 | + ssize_t size = ngtcp2_encode_transport_params( |
| 125 | + nullptr, 0, static_cast<ngtcp2_transport_params_type>(type_), ¶ms_); |
| 126 | + |
| 127 | + DCHECK_GT(size, 0); |
| 128 | + |
| 129 | + auto result = ArrayBuffer::NewBackingStore(env->isolate(), size); |
| 130 | + |
| 131 | + auto ret = ngtcp2_encode_transport_params( |
| 132 | + static_cast<uint8_t*>(result->Data()), |
| 133 | + size, |
| 134 | + static_cast<ngtcp2_transport_params_type>(type_), |
| 135 | + ¶ms_); |
| 136 | + |
| 137 | + if (ret != 0) { |
| 138 | + error_ = QuicError::ForNgtcp2Error(ret); |
| 139 | + return Store(); |
| 140 | + } |
| 141 | + |
| 142 | + return Store(std::move(result), static_cast<size_t>(size)); |
| 143 | +} |
| 144 | + |
| 145 | +void TransportParams::SetPreferredAddress(const SocketAddress& address) { |
| 146 | + DCHECK(ptr_ == ¶ms_); |
| 147 | + params_.preferred_address_present = 1; |
| 148 | + switch (address.family()) { |
| 149 | + case AF_INET: { |
| 150 | + const sockaddr_in* src = |
| 151 | + reinterpret_cast<const sockaddr_in*>(address.data()); |
| 152 | + memcpy(params_.preferred_address.ipv4_addr, |
| 153 | + &src->sin_addr, |
| 154 | + sizeof(params_.preferred_address.ipv4_addr)); |
| 155 | + params_.preferred_address.ipv4_port = address.port(); |
| 156 | + return; |
| 157 | + } |
| 158 | + case AF_INET6: { |
| 159 | + const sockaddr_in6* src = |
| 160 | + reinterpret_cast<const sockaddr_in6*>(address.data()); |
| 161 | + memcpy(params_.preferred_address.ipv6_addr, |
| 162 | + &src->sin6_addr, |
| 163 | + sizeof(params_.preferred_address.ipv6_addr)); |
| 164 | + params_.preferred_address.ipv6_port = address.port(); |
| 165 | + return; |
| 166 | + } |
| 167 | + } |
| 168 | + UNREACHABLE(); |
| 169 | +} |
| 170 | + |
| 171 | +void TransportParams::GenerateStatelessResetToken( |
| 172 | + const TokenSecret& token_secret, const CID& cid) { |
| 173 | + DCHECK(ptr_ == ¶ms_); |
| 174 | + DCHECK(cid); |
| 175 | + params_.stateless_reset_token_present = 1; |
| 176 | + |
| 177 | + StatelessResetToken token(params_.stateless_reset_token, token_secret, cid); |
| 178 | +} |
| 179 | + |
| 180 | +CID TransportParams::GeneratePreferredAddressToken(const Session& session) { |
| 181 | + DCHECK(ptr_ == ¶ms_); |
| 182 | + // DCHECK(pscid); |
| 183 | + // TODO(@jasnell): To be implemented when Session is implemented |
| 184 | + // *pscid = session->cid_factory_.Generate(); |
| 185 | + // params_.preferred_address.cid = *pscid; |
| 186 | + // session->endpoint_->AssociateStatelessResetToken( |
| 187 | + // session->endpoint().GenerateNewStatelessResetToken( |
| 188 | + // params_.preferred_address.stateless_reset_token, *pscid), |
| 189 | + // session); |
| 190 | + return CID::kInvalid; |
| 191 | +} |
| 192 | + |
| 193 | +TransportParams::Type TransportParams::type() const { |
| 194 | + return type_; |
| 195 | +} |
| 196 | + |
| 197 | +TransportParams::operator const ngtcp2_transport_params&() const { |
| 198 | + DCHECK_NOT_NULL(ptr_); |
| 199 | + return *ptr_; |
| 200 | +} |
| 201 | + |
| 202 | +TransportParams::operator const ngtcp2_transport_params*() const { |
| 203 | + DCHECK_NOT_NULL(ptr_); |
| 204 | + return ptr_; |
| 205 | +} |
| 206 | + |
| 207 | +TransportParams::operator bool() const { |
| 208 | + return ptr_ != nullptr; |
| 209 | +} |
| 210 | + |
| 211 | +const QuicError& TransportParams::error() const { |
| 212 | + return error_; |
| 213 | +} |
| 214 | + |
| 215 | +} // namespace quic |
| 216 | +} // namespace node |
| 217 | + |
| 218 | +#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC |
0 commit comments