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 {
Config(Side side,const CID & ocid,const CID & retry_scid)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
From(Environment * env,Local<Value> value)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
TransportParams(Type type)58 TransportParams::TransportParams(Type type) : type_(type), ptr_(¶ms_) {}
59
TransportParams(Type type,const ngtcp2_transport_params * ptr)60 TransportParams::TransportParams(Type type, const ngtcp2_transport_params* ptr)
61 : type_(type), ptr_(ptr) {}
62
TransportParams(const Config & config,const Options & options)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
TransportParams(Type type,const ngtcp2_vec & vec)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
Encode(Environment * env)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
SetPreferredAddress(const SocketAddress & address)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
GenerateStatelessResetToken(const TokenSecret & token_secret,const CID & cid)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
GeneratePreferredAddressToken(const Session & session)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
type() const193 TransportParams::Type TransportParams::type() const {
194 return type_;
195 }
196
operator const ngtcp2_transport_params&() const197 TransportParams::operator const ngtcp2_transport_params&() const {
198 DCHECK_NOT_NULL(ptr_);
199 return *ptr_;
200 }
201
operator const ngtcp2_transport_params*() const202 TransportParams::operator const ngtcp2_transport_params*() const {
203 DCHECK_NOT_NULL(ptr_);
204 return ptr_;
205 }
206
operator bool() const207 TransportParams::operator bool() const {
208 return ptr_ != nullptr;
209 }
210
error() const211 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
219