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 13namespace node { 14 15using v8::ArrayBuffer; 16using v8::Just; 17using v8::Local; 18using v8::Maybe; 19using v8::Nothing; 20using v8::Object; 21using v8::Value; 22 23namespace quic { 24TransportParams::Config::Config(Side side, 25 const CID& ocid, 26 const CID& retry_scid) 27 : side(side), ocid(ocid), retry_scid(retry_scid) {} 28 29Maybe<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 58TransportParams::TransportParams(Type type) : type_(type), ptr_(¶ms_) {} 59 60TransportParams::TransportParams(Type type, const ngtcp2_transport_params* ptr) 61 : type_(type), ptr_(ptr) {} 62 63TransportParams::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 103TransportParams::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 117Store 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 145void 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 171void 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 180CID 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 193TransportParams::Type TransportParams::type() const { 194 return type_; 195} 196 197TransportParams::operator const ngtcp2_transport_params&() const { 198 DCHECK_NOT_NULL(ptr_); 199 return *ptr_; 200} 201 202TransportParams::operator const ngtcp2_transport_params*() const { 203 DCHECK_NOT_NULL(ptr_); 204 return ptr_; 205} 206 207TransportParams::operator bool() const { 208 return ptr_ != nullptr; 209} 210 211const QuicError& TransportParams::error() const { 212 return error_; 213} 214 215} // namespace quic 216} // namespace node 217 218#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC 219