Coverage Report

Created: 2025-07-04 09:33

/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(&params->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(&params->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