/src/node/src/quic/preferredaddress.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC |
2 | | |
3 | | #include "preferredaddress.h" |
4 | | #include <env-inl.h> |
5 | | #include <ngtcp2/ngtcp2.h> |
6 | | #include <node_errors.h> |
7 | | #include <node_sockaddr-inl.h> |
8 | | #include <util-inl.h> |
9 | | #include <uv.h> |
10 | | #include <v8.h> |
11 | | |
12 | | namespace node { |
13 | | |
14 | | using v8::Just; |
15 | | using v8::Local; |
16 | | using v8::Maybe; |
17 | | using v8::Nothing; |
18 | | using v8::Uint32; |
19 | | using v8::Value; |
20 | | |
21 | | namespace quic { |
22 | | |
23 | | namespace { |
24 | | template <int FAMILY> |
25 | | std::optional<const PreferredAddress::AddressInfo> get_address_info( |
26 | 0 | const ngtcp2_preferred_addr& paddr) { |
27 | 0 | if constexpr (FAMILY == AF_INET) { |
28 | 0 | if (!paddr.ipv4_present) return std::nullopt; |
29 | 0 | PreferredAddress::AddressInfo address; |
30 | 0 | address.family = FAMILY; |
31 | 0 | address.port = paddr.ipv4.sin_port; |
32 | 0 | if (uv_inet_ntop( |
33 | 0 | FAMILY, &paddr.ipv4.sin_addr, address.host, sizeof(address.host)) == |
34 | 0 | 0) { |
35 | 0 | address.address = address.host; |
36 | 0 | } |
37 | 0 | return address; |
38 | 0 | } else { |
39 | 0 | if (!paddr.ipv6_present) return std::nullopt; |
40 | 0 | PreferredAddress::AddressInfo address; |
41 | 0 | address.family = FAMILY; |
42 | 0 | address.port = paddr.ipv6.sin6_port; |
43 | 0 | if (uv_inet_ntop(FAMILY, |
44 | 0 | &paddr.ipv6.sin6_addr, |
45 | 0 | address.host, |
46 | 0 | sizeof(address.host)) == 0) { |
47 | 0 | address.address = address.host; |
48 | 0 | } |
49 | 0 | return address; |
50 | 0 | } |
51 | 0 | } Unexecuted instantiation: preferredaddress.cc:std::__1::optional<node::quic::PreferredAddress::AddressInfo const> node::quic::(anonymous namespace)::get_address_info<2>(ngtcp2_preferred_addr const&) Unexecuted instantiation: preferredaddress.cc:std::__1::optional<node::quic::PreferredAddress::AddressInfo const> node::quic::(anonymous namespace)::get_address_info<10>(ngtcp2_preferred_addr const&) |
52 | | |
53 | | template <int FAMILY> |
54 | | void copy_to_transport_params(ngtcp2_transport_params* params, |
55 | 0 | const sockaddr* addr) { |
56 | 0 | params->preferred_addr_present = true; |
57 | 0 | if constexpr (FAMILY == AF_INET) { |
58 | 0 | const sockaddr_in* src = reinterpret_cast<const sockaddr_in*>(addr); |
59 | 0 | params->preferred_addr.ipv4.sin_port = SocketAddress::GetPort(addr); |
60 | 0 | memcpy(¶ms->preferred_addr.ipv4.sin_addr, |
61 | 0 | &src->sin_addr, |
62 | 0 | sizeof(params->preferred_addr.ipv4.sin_addr)); |
63 | 0 | } else { |
64 | 0 | DCHECK_EQ(FAMILY, AF_INET6); |
65 | 0 | const sockaddr_in6* src = reinterpret_cast<const sockaddr_in6*>(addr); |
66 | 0 | params->preferred_addr.ipv6.sin6_port = SocketAddress::GetPort(addr); |
67 | 0 | memcpy(¶ms->preferred_addr.ipv6.sin6_addr, |
68 | 0 | &src->sin6_addr, |
69 | 0 | sizeof(params->preferred_addr.ipv4.sin_addr)); |
70 | 0 | } |
71 | 0 | UNREACHABLE(); |
72 | 0 | } Unexecuted instantiation: preferredaddress.cc:void node::quic::(anonymous namespace)::copy_to_transport_params<2>(ngtcp2_transport_params*, sockaddr const*) Unexecuted instantiation: preferredaddress.cc:void node::quic::(anonymous namespace)::copy_to_transport_params<10>(ngtcp2_transport_params*, sockaddr const*) |
73 | | |
74 | | bool resolve(const PreferredAddress::AddressInfo& address, |
75 | 0 | uv_getaddrinfo_t* req) { |
76 | 0 | addrinfo hints{}; |
77 | 0 | hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; |
78 | 0 | hints.ai_family = address.family; |
79 | 0 | hints.ai_socktype = SOCK_DGRAM; |
80 | | |
81 | | // ngtcp2 requires the selection of the preferred address |
82 | | // to be synchronous, which means we have to do a sync resolve |
83 | | // using uv_getaddrinfo here. |
84 | 0 | return uv_getaddrinfo(nullptr, |
85 | 0 | req, |
86 | 0 | nullptr, |
87 | 0 | address.host, |
88 | | // TODO(@jasnell): The to_string here is not really |
89 | | // the most performant way of converting the uint16_t |
90 | | // port into a string. Depending on execution count, |
91 | | // the potential cost here could be mitigated with a |
92 | | // more efficient conversion. For now, however, this |
93 | | // works. |
94 | 0 | std::to_string(address.port).c_str(), |
95 | 0 | &hints) == 0 && |
96 | 0 | req->addrinfo != nullptr; |
97 | 0 | } |
98 | | } // namespace |
99 | | |
100 | | Maybe<PreferredAddress::Policy> PreferredAddress::GetPolicy( |
101 | 0 | Environment* env, Local<Value> value) { |
102 | 0 | CHECK(value->IsUint32()); |
103 | 0 | uint32_t val = 0; |
104 | 0 | if (value->Uint32Value(env->context()).To(&val)) { |
105 | 0 | switch (val) { |
106 | 0 | case QUIC_PREFERRED_ADDRESS_USE: |
107 | 0 | return Just(Policy::USE_PREFERRED_ADDRESS); |
108 | 0 | case QUIC_PREFERRED_ADDRESS_IGNORE: |
109 | 0 | return Just(Policy::IGNORE_PREFERRED_ADDRESS); |
110 | 0 | } |
111 | 0 | } |
112 | 0 | THROW_ERR_INVALID_ARG_VALUE( |
113 | 0 | env, "%d is not a valid preferred address policy", val); |
114 | 0 | return Nothing<Policy>(); |
115 | 0 | } |
116 | | |
117 | | PreferredAddress::PreferredAddress(ngtcp2_path* dest, |
118 | | const ngtcp2_preferred_addr* paddr) |
119 | 0 | : dest_(dest), paddr_(paddr) { |
120 | 0 | DCHECK_NOT_NULL(paddr); |
121 | 0 | DCHECK_NOT_NULL(dest); |
122 | 0 | } |
123 | | |
124 | | std::optional<const PreferredAddress::AddressInfo> PreferredAddress::ipv4() |
125 | 0 | const { |
126 | 0 | return get_address_info<AF_INET>(*paddr_); |
127 | 0 | } |
128 | | |
129 | | std::optional<const PreferredAddress::AddressInfo> PreferredAddress::ipv6() |
130 | 0 | const { |
131 | 0 | return get_address_info<AF_INET6>(*paddr_); |
132 | 0 | } |
133 | | |
134 | 0 | void PreferredAddress::Use(const AddressInfo& address) { |
135 | 0 | uv_getaddrinfo_t req; |
136 | 0 | auto on_exit = OnScopeLeave([&] { |
137 | 0 | if (req.addrinfo != nullptr) uv_freeaddrinfo(req.addrinfo); |
138 | 0 | }); |
139 | |
|
140 | 0 | if (resolve(address, &req)) { |
141 | 0 | DCHECK_NOT_NULL(req.addrinfo); |
142 | 0 | dest_->remote.addrlen = req.addrinfo->ai_addrlen; |
143 | 0 | memcpy(dest_->remote.addr, req.addrinfo->ai_addr, req.addrinfo->ai_addrlen); |
144 | 0 | } |
145 | 0 | } |
146 | | |
147 | | void PreferredAddress::Set(ngtcp2_transport_params* params, |
148 | 0 | const sockaddr* addr) { |
149 | 0 | DCHECK_NOT_NULL(params); |
150 | 0 | DCHECK_NOT_NULL(addr); |
151 | 0 | switch (addr->sa_family) { |
152 | 0 | case AF_INET: |
153 | 0 | return copy_to_transport_params<AF_INET>(params, addr); |
154 | 0 | case AF_INET6: |
155 | 0 | return copy_to_transport_params<AF_INET6>(params, addr); |
156 | 0 | } |
157 | | // Any other value is just ignored. |
158 | 0 | } |
159 | | |
160 | | Maybe<PreferredAddress::Policy> PreferredAddress::tryGetPolicy( |
161 | 0 | Environment* env, Local<Value> value) { |
162 | 0 | if (value->IsUndefined()) { |
163 | 0 | return Just(PreferredAddress::Policy::USE_PREFERRED_ADDRESS); |
164 | 0 | } |
165 | 0 | if (value->IsUint32()) { |
166 | 0 | auto val = value.As<Uint32>()->Value(); |
167 | 0 | if (val == static_cast<uint32_t>(Policy::IGNORE_PREFERRED_ADDRESS)) |
168 | 0 | return Just(Policy::IGNORE_PREFERRED_ADDRESS); |
169 | 0 | if (val == static_cast<uint32_t>(Policy::USE_PREFERRED_ADDRESS)) |
170 | 0 | return Just(Policy::USE_PREFERRED_ADDRESS); |
171 | 0 | } |
172 | 0 | THROW_ERR_INVALID_ARG_VALUE(env, "invalid preferred address policy"); |
173 | 0 | return Nothing<PreferredAddress::Policy>(); |
174 | 0 | } |
175 | | |
176 | | void PreferredAddress::Initialize(Environment* env, |
177 | 0 | v8::Local<v8::Object> target) { |
178 | 0 | NODE_DEFINE_CONSTANT(target, QUIC_PREFERRED_ADDRESS_IGNORE); |
179 | 0 | NODE_DEFINE_CONSTANT(target, QUIC_PREFERRED_ADDRESS_USE); |
180 | 0 | NODE_DEFINE_CONSTANT(target, DEFAULT_PREFERRED_ADDRESS_POLICY); |
181 | 0 | } |
182 | | |
183 | | } // namespace quic |
184 | | } // namespace node |
185 | | |
186 | | #endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC |