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_(&params_) {}
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(&params_);
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      &params_,
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_), &params_);
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      &params_);
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_ == &params_);
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_ == &params_);
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_ == &params_);
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