/root/bitcoin/src/netbase.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2009-2010 Satoshi Nakamoto |
2 | | // Copyright (c) 2009-2022 The Bitcoin Core developers |
3 | | // Distributed under the MIT software license, see the accompanying |
4 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
5 | | |
6 | | #include <bitcoin-build-config.h> // IWYU pragma: keep |
7 | | |
8 | | #include <netbase.h> |
9 | | |
10 | | #include <compat/compat.h> |
11 | | #include <logging.h> |
12 | | #include <sync.h> |
13 | | #include <tinyformat.h> |
14 | | #include <util/sock.h> |
15 | | #include <util/strencodings.h> |
16 | | #include <util/string.h> |
17 | | #include <util/time.h> |
18 | | |
19 | | #include <atomic> |
20 | | #include <chrono> |
21 | | #include <cstdint> |
22 | | #include <functional> |
23 | | #include <limits> |
24 | | #include <memory> |
25 | | |
26 | | #ifdef HAVE_SOCKADDR_UN |
27 | | #include <sys/un.h> |
28 | | #endif |
29 | | |
30 | | using util::ContainsNoNUL; |
31 | | |
32 | | // Settings |
33 | | static GlobalMutex g_proxyinfo_mutex; |
34 | | static Proxy proxyInfo[NET_MAX] GUARDED_BY(g_proxyinfo_mutex); |
35 | | static Proxy nameProxy GUARDED_BY(g_proxyinfo_mutex); |
36 | | int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; |
37 | | bool fNameLookup = DEFAULT_NAME_LOOKUP; |
38 | | |
39 | | // Need ample time for negotiation for very slow proxies such as Tor |
40 | | std::chrono::milliseconds g_socks5_recv_timeout = 20s; |
41 | | CThreadInterrupt g_socks5_interrupt; |
42 | | |
43 | | ReachableNets g_reachable_nets; |
44 | | |
45 | | std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup) |
46 | 0 | { |
47 | 0 | addrinfo ai_hint{}; |
48 | | // We want a TCP port, which is a streaming socket type |
49 | 0 | ai_hint.ai_socktype = SOCK_STREAM; |
50 | 0 | ai_hint.ai_protocol = IPPROTO_TCP; |
51 | | // We don't care which address family (IPv4 or IPv6) is returned |
52 | 0 | ai_hint.ai_family = AF_UNSPEC; |
53 | | |
54 | | // If we allow lookups of hostnames, use the AI_ADDRCONFIG flag to only |
55 | | // return addresses whose family we have an address configured for. |
56 | | // |
57 | | // If we don't allow lookups, then use the AI_NUMERICHOST flag for |
58 | | // getaddrinfo to only decode numerical network addresses and suppress |
59 | | // hostname lookups. |
60 | 0 | ai_hint.ai_flags = allow_lookup ? AI_ADDRCONFIG : AI_NUMERICHOST; |
61 | |
|
62 | 0 | addrinfo* ai_res{nullptr}; |
63 | 0 | const int n_err{getaddrinfo(name.c_str(), nullptr, &ai_hint, &ai_res)}; |
64 | 0 | if (n_err != 0) { |
65 | 0 | if ((ai_hint.ai_flags & AI_ADDRCONFIG) == AI_ADDRCONFIG) { |
66 | | // AI_ADDRCONFIG on some systems may exclude loopback-only addresses |
67 | | // If first lookup failed we perform a second lookup without AI_ADDRCONFIG |
68 | 0 | ai_hint.ai_flags = (ai_hint.ai_flags & ~AI_ADDRCONFIG); |
69 | 0 | const int n_err_retry{getaddrinfo(name.c_str(), nullptr, &ai_hint, &ai_res)}; |
70 | 0 | if (n_err_retry != 0) { |
71 | 0 | return {}; |
72 | 0 | } |
73 | 0 | } else { |
74 | 0 | return {}; |
75 | 0 | } |
76 | 0 | } |
77 | | |
78 | | // Traverse the linked list starting with ai_trav. |
79 | 0 | addrinfo* ai_trav{ai_res}; |
80 | 0 | std::vector<CNetAddr> resolved_addresses; |
81 | 0 | while (ai_trav != nullptr) { |
82 | 0 | if (ai_trav->ai_family == AF_INET) { |
83 | 0 | assert(ai_trav->ai_addrlen >= sizeof(sockaddr_in)); |
84 | 0 | resolved_addresses.emplace_back(reinterpret_cast<sockaddr_in*>(ai_trav->ai_addr)->sin_addr); |
85 | 0 | } |
86 | 0 | if (ai_trav->ai_family == AF_INET6) { |
87 | 0 | assert(ai_trav->ai_addrlen >= sizeof(sockaddr_in6)); |
88 | 0 | const sockaddr_in6* s6{reinterpret_cast<sockaddr_in6*>(ai_trav->ai_addr)}; |
89 | 0 | resolved_addresses.emplace_back(s6->sin6_addr, s6->sin6_scope_id); |
90 | 0 | } |
91 | 0 | ai_trav = ai_trav->ai_next; |
92 | 0 | } |
93 | 0 | freeaddrinfo(ai_res); |
94 | |
|
95 | 0 | return resolved_addresses; |
96 | 0 | } |
97 | | |
98 | | DNSLookupFn g_dns_lookup{WrappedGetAddrInfo}; |
99 | | |
100 | 0 | enum Network ParseNetwork(const std::string& net_in) { |
101 | 0 | std::string net = ToLower(net_in); |
102 | 0 | if (net == "ipv4") return NET_IPV4; |
103 | 0 | if (net == "ipv6") return NET_IPV6; |
104 | 0 | if (net == "onion") return NET_ONION; |
105 | 0 | if (net == "tor") { |
106 | 0 | LogPrintf("Warning: net name 'tor' is deprecated and will be removed in the future. You should use 'onion' instead.\n"); |
107 | 0 | return NET_ONION; |
108 | 0 | } |
109 | 0 | if (net == "i2p") { |
110 | 0 | return NET_I2P; |
111 | 0 | } |
112 | 0 | if (net == "cjdns") { |
113 | 0 | return NET_CJDNS; |
114 | 0 | } |
115 | 0 | return NET_UNROUTABLE; |
116 | 0 | } |
117 | | |
118 | | std::string GetNetworkName(enum Network net) |
119 | 0 | { |
120 | 0 | switch (net) { |
121 | 0 | case NET_UNROUTABLE: return "not_publicly_routable"; |
122 | 0 | case NET_IPV4: return "ipv4"; |
123 | 0 | case NET_IPV6: return "ipv6"; |
124 | 0 | case NET_ONION: return "onion"; |
125 | 0 | case NET_I2P: return "i2p"; |
126 | 0 | case NET_CJDNS: return "cjdns"; |
127 | 0 | case NET_INTERNAL: return "internal"; |
128 | 0 | case NET_MAX: assert(false); |
129 | 0 | } // no default case, so the compiler can warn about missing cases |
130 | | |
131 | 0 | assert(false); |
132 | 0 | } |
133 | | |
134 | | std::vector<std::string> GetNetworkNames(bool append_unroutable) |
135 | 0 | { |
136 | 0 | std::vector<std::string> names; |
137 | 0 | for (int n = 0; n < NET_MAX; ++n) { |
138 | 0 | const enum Network network{static_cast<Network>(n)}; |
139 | 0 | if (network == NET_UNROUTABLE || network == NET_INTERNAL) continue; |
140 | 0 | names.emplace_back(GetNetworkName(network)); |
141 | 0 | } |
142 | 0 | if (append_unroutable) { |
143 | 0 | names.emplace_back(GetNetworkName(NET_UNROUTABLE)); |
144 | 0 | } |
145 | 0 | return names; |
146 | 0 | } |
147 | | |
148 | | static std::vector<CNetAddr> LookupIntern(const std::string& name, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function) |
149 | 0 | { |
150 | 0 | if (!ContainsNoNUL(name)) return {}; |
151 | 0 | { |
152 | 0 | CNetAddr addr; |
153 | | // From our perspective, onion addresses are not hostnames but rather |
154 | | // direct encodings of CNetAddr much like IPv4 dotted-decimal notation |
155 | | // or IPv6 colon-separated hextet notation. Since we can't use |
156 | | // getaddrinfo to decode them and it wouldn't make sense to resolve |
157 | | // them, we return a network address representing it instead. See |
158 | | // CNetAddr::SetSpecial(const std::string&) for more details. |
159 | 0 | if (addr.SetSpecial(name)) return {addr}; |
160 | 0 | } |
161 | | |
162 | 0 | std::vector<CNetAddr> addresses; |
163 | |
|
164 | 0 | for (const CNetAddr& resolved : dns_lookup_function(name, fAllowLookup)) { |
165 | 0 | if (nMaxSolutions > 0 && addresses.size() >= nMaxSolutions) { |
166 | 0 | break; |
167 | 0 | } |
168 | | /* Never allow resolving to an internal address. Consider any such result invalid */ |
169 | 0 | if (!resolved.IsInternal()) { |
170 | 0 | addresses.push_back(resolved); |
171 | 0 | } |
172 | 0 | } |
173 | |
|
174 | 0 | return addresses; |
175 | 0 | } |
176 | | |
177 | | std::vector<CNetAddr> LookupHost(const std::string& name, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function) |
178 | 0 | { |
179 | 0 | if (!ContainsNoNUL(name)) return {}; |
180 | 0 | std::string strHost = name; |
181 | 0 | if (strHost.empty()) return {}; |
182 | 0 | if (strHost.front() == '[' && strHost.back() == ']') { |
183 | 0 | strHost = strHost.substr(1, strHost.size() - 2); |
184 | 0 | } |
185 | |
|
186 | 0 | return LookupIntern(strHost, nMaxSolutions, fAllowLookup, dns_lookup_function); |
187 | 0 | } |
188 | | |
189 | | std::optional<CNetAddr> LookupHost(const std::string& name, bool fAllowLookup, DNSLookupFn dns_lookup_function) |
190 | 0 | { |
191 | 0 | const std::vector<CNetAddr> addresses{LookupHost(name, 1, fAllowLookup, dns_lookup_function)}; |
192 | 0 | return addresses.empty() ? std::nullopt : std::make_optional(addresses.front()); |
193 | 0 | } |
194 | | |
195 | | std::vector<CService> Lookup(const std::string& name, uint16_t portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function) |
196 | 0 | { |
197 | 0 | if (name.empty() || !ContainsNoNUL(name)) { |
198 | 0 | return {}; |
199 | 0 | } |
200 | 0 | uint16_t port{portDefault}; |
201 | 0 | std::string hostname; |
202 | 0 | SplitHostPort(name, port, hostname); |
203 | |
|
204 | 0 | const std::vector<CNetAddr> addresses{LookupIntern(hostname, nMaxSolutions, fAllowLookup, dns_lookup_function)}; |
205 | 0 | if (addresses.empty()) return {}; |
206 | 0 | std::vector<CService> services; |
207 | 0 | services.reserve(addresses.size()); |
208 | 0 | for (const auto& addr : addresses) |
209 | 0 | services.emplace_back(addr, port); |
210 | 0 | return services; |
211 | 0 | } |
212 | | |
213 | | std::optional<CService> Lookup(const std::string& name, uint16_t portDefault, bool fAllowLookup, DNSLookupFn dns_lookup_function) |
214 | 0 | { |
215 | 0 | const std::vector<CService> services{Lookup(name, portDefault, fAllowLookup, 1, dns_lookup_function)}; |
216 | |
|
217 | 0 | return services.empty() ? std::nullopt : std::make_optional(services.front()); |
218 | 0 | } |
219 | | |
220 | | CService LookupNumeric(const std::string& name, uint16_t portDefault, DNSLookupFn dns_lookup_function) |
221 | 0 | { |
222 | 0 | if (!ContainsNoNUL(name)) { |
223 | 0 | return {}; |
224 | 0 | } |
225 | | // "1.2:345" will fail to resolve the ip, but will still set the port. |
226 | | // If the ip fails to resolve, re-init the result. |
227 | 0 | return Lookup(name, portDefault, /*fAllowLookup=*/false, dns_lookup_function).value_or(CService{}); |
228 | 0 | } |
229 | | |
230 | | bool IsUnixSocketPath(const std::string& name) |
231 | 0 | { |
232 | 0 | #ifdef HAVE_SOCKADDR_UN |
233 | 0 | if (!name.starts_with(ADDR_PREFIX_UNIX)) return false; |
234 | | |
235 | | // Split off "unix:" prefix |
236 | 0 | std::string str{name.substr(ADDR_PREFIX_UNIX.length())}; |
237 | | |
238 | | // Path size limit is platform-dependent |
239 | | // see https://manpages.ubuntu.com/manpages/xenial/en/man7/unix.7.html |
240 | 0 | if (str.size() + 1 > sizeof(((sockaddr_un*)nullptr)->sun_path)) return false; |
241 | | |
242 | 0 | return true; |
243 | | #else |
244 | | return false; |
245 | | #endif |
246 | 0 | } |
247 | | |
248 | | /** SOCKS version */ |
249 | | enum SOCKSVersion: uint8_t { |
250 | | SOCKS4 = 0x04, |
251 | | SOCKS5 = 0x05 |
252 | | }; |
253 | | |
254 | | /** Values defined for METHOD in RFC1928 */ |
255 | | enum SOCKS5Method: uint8_t { |
256 | | NOAUTH = 0x00, //!< No authentication required |
257 | | GSSAPI = 0x01, //!< GSSAPI |
258 | | USER_PASS = 0x02, //!< Username/password |
259 | | NO_ACCEPTABLE = 0xff, //!< No acceptable methods |
260 | | }; |
261 | | |
262 | | /** Values defined for CMD in RFC1928 */ |
263 | | enum SOCKS5Command: uint8_t { |
264 | | CONNECT = 0x01, |
265 | | BIND = 0x02, |
266 | | UDP_ASSOCIATE = 0x03 |
267 | | }; |
268 | | |
269 | | /** Values defined for REP in RFC1928 and https://spec.torproject.org/socks-extensions.html */ |
270 | | enum SOCKS5Reply: uint8_t { |
271 | | SUCCEEDED = 0x00, //!< RFC1928: Succeeded |
272 | | GENFAILURE = 0x01, //!< RFC1928: General failure |
273 | | NOTALLOWED = 0x02, //!< RFC1928: Connection not allowed by ruleset |
274 | | NETUNREACHABLE = 0x03, //!< RFC1928: Network unreachable |
275 | | HOSTUNREACHABLE = 0x04, //!< RFC1928: Network unreachable |
276 | | CONNREFUSED = 0x05, //!< RFC1928: Connection refused |
277 | | TTLEXPIRED = 0x06, //!< RFC1928: TTL expired |
278 | | CMDUNSUPPORTED = 0x07, //!< RFC1928: Command not supported |
279 | | ATYPEUNSUPPORTED = 0x08, //!< RFC1928: Address type not supported |
280 | | TOR_HS_DESC_NOT_FOUND = 0xf0, //!< Tor: Onion service descriptor can not be found |
281 | | TOR_HS_DESC_INVALID = 0xf1, //!< Tor: Onion service descriptor is invalid |
282 | | TOR_HS_INTRO_FAILED = 0xf2, //!< Tor: Onion service introduction failed |
283 | | TOR_HS_REND_FAILED = 0xf3, //!< Tor: Onion service rendezvous failed |
284 | | TOR_HS_MISSING_CLIENT_AUTH = 0xf4, //!< Tor: Onion service missing client authorization |
285 | | TOR_HS_WRONG_CLIENT_AUTH = 0xf5, //!< Tor: Onion service wrong client authorization |
286 | | TOR_HS_BAD_ADDRESS = 0xf6, //!< Tor: Onion service invalid address |
287 | | TOR_HS_INTRO_TIMEOUT = 0xf7, //!< Tor: Onion service introduction timed out |
288 | | }; |
289 | | |
290 | | /** Values defined for ATYPE in RFC1928 */ |
291 | | enum SOCKS5Atyp: uint8_t { |
292 | | IPV4 = 0x01, |
293 | | DOMAINNAME = 0x03, |
294 | | IPV6 = 0x04, |
295 | | }; |
296 | | |
297 | | /** Status codes that can be returned by InterruptibleRecv */ |
298 | | enum class IntrRecvError { |
299 | | OK, |
300 | | Timeout, |
301 | | Disconnected, |
302 | | NetworkError, |
303 | | Interrupted |
304 | | }; |
305 | | |
306 | | /** |
307 | | * Try to read a specified number of bytes from a socket. Please read the "see |
308 | | * also" section for more detail. |
309 | | * |
310 | | * @param data The buffer where the read bytes should be stored. |
311 | | * @param len The number of bytes to read into the specified buffer. |
312 | | * @param timeout The total timeout for this read. |
313 | | * @param sock The socket (has to be in non-blocking mode) from which to read bytes. |
314 | | * |
315 | | * @returns An IntrRecvError indicating the resulting status of this read. |
316 | | * IntrRecvError::OK only if all of the specified number of bytes were |
317 | | * read. |
318 | | * |
319 | | * @see This function can be interrupted by calling g_socks5_interrupt(). |
320 | | * Sockets can be made non-blocking with Sock::SetNonBlocking(). |
321 | | */ |
322 | | static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, std::chrono::milliseconds timeout, const Sock& sock) |
323 | 0 | { |
324 | 0 | auto curTime{Now<SteadyMilliseconds>()}; |
325 | 0 | const auto endTime{curTime + timeout}; |
326 | 0 | while (len > 0 && curTime < endTime) { |
327 | 0 | ssize_t ret = sock.Recv(data, len, 0); // Optimistically try the recv first |
328 | 0 | if (ret > 0) { |
329 | 0 | len -= ret; |
330 | 0 | data += ret; |
331 | 0 | } else if (ret == 0) { // Unexpected disconnection |
332 | 0 | return IntrRecvError::Disconnected; |
333 | 0 | } else { // Other error or blocking |
334 | 0 | int nErr = WSAGetLastError(); |
335 | 0 | if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) { |
336 | | // Only wait at most MAX_WAIT_FOR_IO at a time, unless |
337 | | // we're approaching the end of the specified total timeout |
338 | 0 | const auto remaining = std::chrono::milliseconds{endTime - curTime}; |
339 | 0 | const auto timeout = std::min(remaining, std::chrono::milliseconds{MAX_WAIT_FOR_IO}); |
340 | 0 | if (!sock.Wait(timeout, Sock::RECV)) { |
341 | 0 | return IntrRecvError::NetworkError; |
342 | 0 | } |
343 | 0 | } else { |
344 | 0 | return IntrRecvError::NetworkError; |
345 | 0 | } |
346 | 0 | } |
347 | 0 | if (g_socks5_interrupt) { |
348 | 0 | return IntrRecvError::Interrupted; |
349 | 0 | } |
350 | 0 | curTime = Now<SteadyMilliseconds>(); |
351 | 0 | } |
352 | 0 | return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout; |
353 | 0 | } |
354 | | |
355 | | /** Convert SOCKS5 reply to an error message */ |
356 | | static std::string Socks5ErrorString(uint8_t err) |
357 | 0 | { |
358 | 0 | switch(err) { |
359 | 0 | case SOCKS5Reply::GENFAILURE: |
360 | 0 | return "general failure"; |
361 | 0 | case SOCKS5Reply::NOTALLOWED: |
362 | 0 | return "connection not allowed"; |
363 | 0 | case SOCKS5Reply::NETUNREACHABLE: |
364 | 0 | return "network unreachable"; |
365 | 0 | case SOCKS5Reply::HOSTUNREACHABLE: |
366 | 0 | return "host unreachable"; |
367 | 0 | case SOCKS5Reply::CONNREFUSED: |
368 | 0 | return "connection refused"; |
369 | 0 | case SOCKS5Reply::TTLEXPIRED: |
370 | 0 | return "TTL expired"; |
371 | 0 | case SOCKS5Reply::CMDUNSUPPORTED: |
372 | 0 | return "protocol error"; |
373 | 0 | case SOCKS5Reply::ATYPEUNSUPPORTED: |
374 | 0 | return "address type not supported"; |
375 | 0 | case SOCKS5Reply::TOR_HS_DESC_NOT_FOUND: |
376 | 0 | return "onion service descriptor can not be found"; |
377 | 0 | case SOCKS5Reply::TOR_HS_DESC_INVALID: |
378 | 0 | return "onion service descriptor is invalid"; |
379 | 0 | case SOCKS5Reply::TOR_HS_INTRO_FAILED: |
380 | 0 | return "onion service introduction failed"; |
381 | 0 | case SOCKS5Reply::TOR_HS_REND_FAILED: |
382 | 0 | return "onion service rendezvous failed"; |
383 | 0 | case SOCKS5Reply::TOR_HS_MISSING_CLIENT_AUTH: |
384 | 0 | return "onion service missing client authorization"; |
385 | 0 | case SOCKS5Reply::TOR_HS_WRONG_CLIENT_AUTH: |
386 | 0 | return "onion service wrong client authorization"; |
387 | 0 | case SOCKS5Reply::TOR_HS_BAD_ADDRESS: |
388 | 0 | return "onion service invalid address"; |
389 | 0 | case SOCKS5Reply::TOR_HS_INTRO_TIMEOUT: |
390 | 0 | return "onion service introduction timed out"; |
391 | 0 | default: |
392 | 0 | return strprintf("unknown (0x%02x)", err); |
393 | 0 | } |
394 | 0 | } |
395 | | |
396 | | bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* auth, const Sock& sock) |
397 | 0 | { |
398 | 0 | try { |
399 | 0 | IntrRecvError recvr; |
400 | 0 | LogDebug(BCLog::NET, "SOCKS5 connecting %s\n", strDest); |
401 | 0 | if (strDest.size() > 255) { |
402 | 0 | LogError("Hostname too long\n"); |
403 | 0 | return false; |
404 | 0 | } |
405 | | // Construct the version identifier/method selection message |
406 | 0 | std::vector<uint8_t> vSocks5Init; |
407 | 0 | vSocks5Init.push_back(SOCKSVersion::SOCKS5); // We want the SOCK5 protocol |
408 | 0 | if (auth) { |
409 | 0 | vSocks5Init.push_back(0x02); // 2 method identifiers follow... |
410 | 0 | vSocks5Init.push_back(SOCKS5Method::NOAUTH); |
411 | 0 | vSocks5Init.push_back(SOCKS5Method::USER_PASS); |
412 | 0 | } else { |
413 | 0 | vSocks5Init.push_back(0x01); // 1 method identifier follows... |
414 | 0 | vSocks5Init.push_back(SOCKS5Method::NOAUTH); |
415 | 0 | } |
416 | 0 | sock.SendComplete(vSocks5Init, g_socks5_recv_timeout, g_socks5_interrupt); |
417 | 0 | uint8_t pchRet1[2]; |
418 | 0 | if (InterruptibleRecv(pchRet1, 2, g_socks5_recv_timeout, sock) != IntrRecvError::OK) { |
419 | 0 | LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port); |
420 | 0 | return false; |
421 | 0 | } |
422 | 0 | if (pchRet1[0] != SOCKSVersion::SOCKS5) { |
423 | 0 | LogError("Proxy failed to initialize\n"); |
424 | 0 | return false; |
425 | 0 | } |
426 | 0 | if (pchRet1[1] == SOCKS5Method::USER_PASS && auth) { |
427 | | // Perform username/password authentication (as described in RFC1929) |
428 | 0 | std::vector<uint8_t> vAuth; |
429 | 0 | vAuth.push_back(0x01); // Current (and only) version of user/pass subnegotiation |
430 | 0 | if (auth->username.size() > 255 || auth->password.size() > 255) { |
431 | 0 | LogError("Proxy username or password too long\n"); |
432 | 0 | return false; |
433 | 0 | } |
434 | 0 | vAuth.push_back(auth->username.size()); |
435 | 0 | vAuth.insert(vAuth.end(), auth->username.begin(), auth->username.end()); |
436 | 0 | vAuth.push_back(auth->password.size()); |
437 | 0 | vAuth.insert(vAuth.end(), auth->password.begin(), auth->password.end()); |
438 | 0 | sock.SendComplete(vAuth, g_socks5_recv_timeout, g_socks5_interrupt); |
439 | 0 | LogDebug(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password); |
440 | 0 | uint8_t pchRetA[2]; |
441 | 0 | if (InterruptibleRecv(pchRetA, 2, g_socks5_recv_timeout, sock) != IntrRecvError::OK) { |
442 | 0 | LogError("Error reading proxy authentication response\n"); |
443 | 0 | return false; |
444 | 0 | } |
445 | 0 | if (pchRetA[0] != 0x01 || pchRetA[1] != 0x00) { |
446 | 0 | LogError("Proxy authentication unsuccessful\n"); |
447 | 0 | return false; |
448 | 0 | } |
449 | 0 | } else if (pchRet1[1] == SOCKS5Method::NOAUTH) { |
450 | | // Perform no authentication |
451 | 0 | } else { |
452 | 0 | LogError("Proxy requested wrong authentication method %02x\n", pchRet1[1]); |
453 | 0 | return false; |
454 | 0 | } |
455 | 0 | std::vector<uint8_t> vSocks5; |
456 | 0 | vSocks5.push_back(SOCKSVersion::SOCKS5); // VER protocol version |
457 | 0 | vSocks5.push_back(SOCKS5Command::CONNECT); // CMD CONNECT |
458 | 0 | vSocks5.push_back(0x00); // RSV Reserved must be 0 |
459 | 0 | vSocks5.push_back(SOCKS5Atyp::DOMAINNAME); // ATYP DOMAINNAME |
460 | 0 | vSocks5.push_back(strDest.size()); // Length<=255 is checked at beginning of function |
461 | 0 | vSocks5.insert(vSocks5.end(), strDest.begin(), strDest.end()); |
462 | 0 | vSocks5.push_back((port >> 8) & 0xFF); |
463 | 0 | vSocks5.push_back((port >> 0) & 0xFF); |
464 | 0 | sock.SendComplete(vSocks5, g_socks5_recv_timeout, g_socks5_interrupt); |
465 | 0 | uint8_t pchRet2[4]; |
466 | 0 | if ((recvr = InterruptibleRecv(pchRet2, 4, g_socks5_recv_timeout, sock)) != IntrRecvError::OK) { |
467 | 0 | if (recvr == IntrRecvError::Timeout) { |
468 | | /* If a timeout happens here, this effectively means we timed out while connecting |
469 | | * to the remote node. This is very common for Tor, so do not print an |
470 | | * error message. */ |
471 | 0 | return false; |
472 | 0 | } else { |
473 | 0 | LogError("Error while reading proxy response\n"); |
474 | 0 | return false; |
475 | 0 | } |
476 | 0 | } |
477 | 0 | if (pchRet2[0] != SOCKSVersion::SOCKS5) { |
478 | 0 | LogError("Proxy failed to accept request\n"); |
479 | 0 | return false; |
480 | 0 | } |
481 | 0 | if (pchRet2[1] != SOCKS5Reply::SUCCEEDED) { |
482 | | // Failures to connect to a peer that are not proxy errors |
483 | 0 | LogPrintLevel(BCLog::NET, BCLog::Level::Debug, |
484 | 0 | "Socks5() connect to %s:%d failed: %s\n", strDest, port, Socks5ErrorString(pchRet2[1])); |
485 | 0 | return false; |
486 | 0 | } |
487 | 0 | if (pchRet2[2] != 0x00) { // Reserved field must be 0 |
488 | 0 | LogError("Error: malformed proxy response\n"); |
489 | 0 | return false; |
490 | 0 | } |
491 | 0 | uint8_t pchRet3[256]; |
492 | 0 | switch (pchRet2[3]) { |
493 | 0 | case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, g_socks5_recv_timeout, sock); break; |
494 | 0 | case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, g_socks5_recv_timeout, sock); break; |
495 | 0 | case SOCKS5Atyp::DOMAINNAME: { |
496 | 0 | recvr = InterruptibleRecv(pchRet3, 1, g_socks5_recv_timeout, sock); |
497 | 0 | if (recvr != IntrRecvError::OK) { |
498 | 0 | LogError("Error reading from proxy\n"); |
499 | 0 | return false; |
500 | 0 | } |
501 | 0 | int nRecv = pchRet3[0]; |
502 | 0 | recvr = InterruptibleRecv(pchRet3, nRecv, g_socks5_recv_timeout, sock); |
503 | 0 | break; |
504 | 0 | } |
505 | 0 | default: { |
506 | 0 | LogError("Error: malformed proxy response\n"); |
507 | 0 | return false; |
508 | 0 | } |
509 | 0 | } |
510 | 0 | if (recvr != IntrRecvError::OK) { |
511 | 0 | LogError("Error reading from proxy\n"); |
512 | 0 | return false; |
513 | 0 | } |
514 | 0 | if (InterruptibleRecv(pchRet3, 2, g_socks5_recv_timeout, sock) != IntrRecvError::OK) { |
515 | 0 | LogError("Error reading from proxy\n"); |
516 | 0 | return false; |
517 | 0 | } |
518 | 0 | LogDebug(BCLog::NET, "SOCKS5 connected %s\n", strDest); |
519 | 0 | return true; |
520 | 0 | } catch (const std::runtime_error& e) { |
521 | 0 | LogError("Error during SOCKS5 proxy handshake: %s\n", e.what()); |
522 | 0 | return false; |
523 | 0 | } |
524 | 0 | } |
525 | | |
526 | | std::unique_ptr<Sock> CreateSockOS(int domain, int type, int protocol) |
527 | 0 | { |
528 | | // Not IPv4, IPv6 or UNIX |
529 | 0 | if (domain == AF_UNSPEC) return nullptr; |
530 | | |
531 | | // Create a socket in the specified address family. |
532 | 0 | SOCKET hSocket = socket(domain, type, protocol); |
533 | 0 | if (hSocket == INVALID_SOCKET) { |
534 | 0 | return nullptr; |
535 | 0 | } |
536 | | |
537 | 0 | auto sock = std::make_unique<Sock>(hSocket); |
538 | |
|
539 | 0 | if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNIX) { |
540 | 0 | return sock; |
541 | 0 | } |
542 | | |
543 | | // Ensure that waiting for I/O on this socket won't result in undefined |
544 | | // behavior. |
545 | 0 | if (!sock->IsSelectable()) { |
546 | 0 | LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n"); |
547 | 0 | return nullptr; |
548 | 0 | } |
549 | | |
550 | | #ifdef SO_NOSIGPIPE |
551 | | int set = 1; |
552 | | // Set the no-sigpipe option on the socket for BSD systems, other UNIXes |
553 | | // should use the MSG_NOSIGNAL flag for every send. |
554 | | if (sock->SetSockOpt(SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)) == SOCKET_ERROR) { |
555 | | LogPrintf("Error setting SO_NOSIGPIPE on socket: %s, continuing anyway\n", |
556 | | NetworkErrorString(WSAGetLastError())); |
557 | | } |
558 | | #endif |
559 | | |
560 | | // Set the non-blocking option on the socket. |
561 | 0 | if (!sock->SetNonBlocking()) { |
562 | 0 | LogPrintf("Error setting socket to non-blocking: %s\n", NetworkErrorString(WSAGetLastError())); |
563 | 0 | return nullptr; |
564 | 0 | } |
565 | | |
566 | 0 | #ifdef HAVE_SOCKADDR_UN |
567 | 0 | if (domain == AF_UNIX) return sock; |
568 | 0 | #endif |
569 | | |
570 | 0 | if (protocol == IPPROTO_TCP) { |
571 | | // Set the no-delay option (disable Nagle's algorithm) on the TCP socket. |
572 | 0 | const int on{1}; |
573 | 0 | if (sock->SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == SOCKET_ERROR) { |
574 | 0 | LogDebug(BCLog::NET, "Unable to set TCP_NODELAY on a newly created socket, continuing anyway\n"); |
575 | 0 | } |
576 | 0 | } |
577 | |
|
578 | 0 | return sock; |
579 | 0 | } |
580 | | |
581 | | std::function<std::unique_ptr<Sock>(int, int, int)> CreateSock = CreateSockOS; |
582 | | |
583 | | template<typename... Args> |
584 | | static void LogConnectFailure(bool manual_connection, util::ConstevalFormatString<sizeof...(Args)> fmt, const Args&... args) |
585 | 0 | { |
586 | 0 | std::string error_message = tfm::format(fmt, args...); |
587 | 0 | if (manual_connection) { |
588 | 0 | LogPrintf("%s\n", error_message); |
589 | 0 | } else { |
590 | 0 | LogDebug(BCLog::NET, "%s\n", error_message); |
591 | 0 | } |
592 | 0 | } |
593 | | |
594 | | static bool ConnectToSocket(const Sock& sock, struct sockaddr* sockaddr, socklen_t len, const std::string& dest_str, bool manual_connection) |
595 | 0 | { |
596 | | // Connect to `sockaddr` using `sock`. |
597 | 0 | if (sock.Connect(sockaddr, len) == SOCKET_ERROR) { |
598 | 0 | int nErr = WSAGetLastError(); |
599 | | // WSAEINVAL is here because some legacy version of winsock uses it |
600 | 0 | if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) |
601 | 0 | { |
602 | | // Connection didn't actually fail, but is being established |
603 | | // asynchronously. Thus, use async I/O api (select/poll) |
604 | | // synchronously to check for successful connection with a timeout. |
605 | 0 | const Sock::Event requested = Sock::RECV | Sock::SEND; |
606 | 0 | Sock::Event occurred; |
607 | 0 | if (!sock.Wait(std::chrono::milliseconds{nConnectTimeout}, requested, &occurred)) { |
608 | 0 | LogPrintf("wait for connect to %s failed: %s\n", |
609 | 0 | dest_str, |
610 | 0 | NetworkErrorString(WSAGetLastError())); |
611 | 0 | return false; |
612 | 0 | } else if (occurred == 0) { |
613 | 0 | LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "connection attempt to %s timed out\n", dest_str); |
614 | 0 | return false; |
615 | 0 | } |
616 | | |
617 | | // Even if the wait was successful, the connect might not |
618 | | // have been successful. The reason for this failure is hidden away |
619 | | // in the SO_ERROR for the socket in modern systems. We read it into |
620 | | // sockerr here. |
621 | 0 | int sockerr; |
622 | 0 | socklen_t sockerr_len = sizeof(sockerr); |
623 | 0 | if (sock.GetSockOpt(SOL_SOCKET, SO_ERROR, (sockopt_arg_type)&sockerr, &sockerr_len) == |
624 | 0 | SOCKET_ERROR) { |
625 | 0 | LogPrintf("getsockopt() for %s failed: %s\n", dest_str, NetworkErrorString(WSAGetLastError())); |
626 | 0 | return false; |
627 | 0 | } |
628 | 0 | if (sockerr != 0) { |
629 | 0 | LogConnectFailure(manual_connection, |
630 | 0 | "connect() to %s failed after wait: %s", |
631 | 0 | dest_str, |
632 | 0 | NetworkErrorString(sockerr)); |
633 | 0 | return false; |
634 | 0 | } |
635 | 0 | } |
636 | | #ifdef WIN32 |
637 | | else if (WSAGetLastError() != WSAEISCONN) |
638 | | #else |
639 | 0 | else |
640 | 0 | #endif |
641 | 0 | { |
642 | 0 | LogConnectFailure(manual_connection, "connect() to %s failed: %s", dest_str, NetworkErrorString(WSAGetLastError())); |
643 | 0 | return false; |
644 | 0 | } |
645 | 0 | } |
646 | 0 | return true; |
647 | 0 | } |
648 | | |
649 | | std::unique_ptr<Sock> ConnectDirectly(const CService& dest, bool manual_connection) |
650 | 0 | { |
651 | 0 | auto sock = CreateSock(dest.GetSAFamily(), SOCK_STREAM, IPPROTO_TCP); |
652 | 0 | if (!sock) { |
653 | 0 | LogPrintLevel(BCLog::NET, BCLog::Level::Error, "Cannot create a socket for connecting to %s\n", dest.ToStringAddrPort()); |
654 | 0 | return {}; |
655 | 0 | } |
656 | | |
657 | | // Create a sockaddr from the specified service. |
658 | 0 | struct sockaddr_storage sockaddr; |
659 | 0 | socklen_t len = sizeof(sockaddr); |
660 | 0 | if (!dest.GetSockAddr((struct sockaddr*)&sockaddr, &len)) { |
661 | 0 | LogPrintf("Cannot get sockaddr for %s: unsupported network\n", dest.ToStringAddrPort()); |
662 | 0 | return {}; |
663 | 0 | } |
664 | | |
665 | 0 | if (!ConnectToSocket(*sock, (struct sockaddr*)&sockaddr, len, dest.ToStringAddrPort(), manual_connection)) { |
666 | 0 | return {}; |
667 | 0 | } |
668 | | |
669 | 0 | return sock; |
670 | 0 | } |
671 | | |
672 | | std::unique_ptr<Sock> Proxy::Connect() const |
673 | 0 | { |
674 | 0 | if (!IsValid()) return {}; |
675 | | |
676 | 0 | if (!m_is_unix_socket) return ConnectDirectly(proxy, /*manual_connection=*/true); |
677 | | |
678 | 0 | #ifdef HAVE_SOCKADDR_UN |
679 | 0 | auto sock = CreateSock(AF_UNIX, SOCK_STREAM, 0); |
680 | 0 | if (!sock) { |
681 | 0 | LogPrintLevel(BCLog::NET, BCLog::Level::Error, "Cannot create a socket for connecting to %s\n", m_unix_socket_path); |
682 | 0 | return {}; |
683 | 0 | } |
684 | | |
685 | 0 | const std::string path{m_unix_socket_path.substr(ADDR_PREFIX_UNIX.length())}; |
686 | |
|
687 | 0 | struct sockaddr_un addrun; |
688 | 0 | memset(&addrun, 0, sizeof(addrun)); |
689 | 0 | addrun.sun_family = AF_UNIX; |
690 | | // leave the last char in addrun.sun_path[] to be always '\0' |
691 | 0 | memcpy(addrun.sun_path, path.c_str(), std::min(sizeof(addrun.sun_path) - 1, path.length())); |
692 | 0 | socklen_t len = sizeof(addrun); |
693 | |
|
694 | 0 | if(!ConnectToSocket(*sock, (struct sockaddr*)&addrun, len, path, /*manual_connection=*/true)) { |
695 | 0 | return {}; |
696 | 0 | } |
697 | | |
698 | 0 | return sock; |
699 | | #else |
700 | | return {}; |
701 | | #endif |
702 | 0 | } |
703 | | |
704 | 0 | bool SetProxy(enum Network net, const Proxy &addrProxy) { |
705 | 0 | assert(net >= 0 && net < NET_MAX); |
706 | 0 | if (!addrProxy.IsValid()) |
707 | 0 | return false; |
708 | 0 | LOCK(g_proxyinfo_mutex); |
709 | 0 | proxyInfo[net] = addrProxy; |
710 | 0 | return true; |
711 | 0 | } |
712 | | |
713 | 0 | bool GetProxy(enum Network net, Proxy &proxyInfoOut) { |
714 | 0 | assert(net >= 0 && net < NET_MAX); |
715 | 0 | LOCK(g_proxyinfo_mutex); |
716 | 0 | if (!proxyInfo[net].IsValid()) |
717 | 0 | return false; |
718 | 0 | proxyInfoOut = proxyInfo[net]; |
719 | 0 | return true; |
720 | 0 | } |
721 | | |
722 | 0 | bool SetNameProxy(const Proxy &addrProxy) { |
723 | 0 | if (!addrProxy.IsValid()) |
724 | 0 | return false; |
725 | 0 | LOCK(g_proxyinfo_mutex); |
726 | 0 | nameProxy = addrProxy; |
727 | 0 | return true; |
728 | 0 | } |
729 | | |
730 | 0 | bool GetNameProxy(Proxy &nameProxyOut) { |
731 | 0 | LOCK(g_proxyinfo_mutex); |
732 | 0 | if(!nameProxy.IsValid()) |
733 | 0 | return false; |
734 | 0 | nameProxyOut = nameProxy; |
735 | 0 | return true; |
736 | 0 | } |
737 | | |
738 | 0 | bool HaveNameProxy() { |
739 | 0 | LOCK(g_proxyinfo_mutex); |
740 | 0 | return nameProxy.IsValid(); |
741 | 0 | } |
742 | | |
743 | 0 | bool IsProxy(const CNetAddr &addr) { |
744 | 0 | LOCK(g_proxyinfo_mutex); |
745 | 0 | for (int i = 0; i < NET_MAX; i++) { |
746 | 0 | if (addr == static_cast<CNetAddr>(proxyInfo[i].proxy)) |
747 | 0 | return true; |
748 | 0 | } |
749 | 0 | return false; |
750 | 0 | } |
751 | | |
752 | | std::unique_ptr<Sock> ConnectThroughProxy(const Proxy& proxy, |
753 | | const std::string& dest, |
754 | | uint16_t port, |
755 | | bool& proxy_connection_failed) |
756 | 0 | { |
757 | | // first connect to proxy server |
758 | 0 | auto sock = proxy.Connect(); |
759 | 0 | if (!sock) { |
760 | 0 | proxy_connection_failed = true; |
761 | 0 | return {}; |
762 | 0 | } |
763 | | |
764 | | // do socks negotiation |
765 | 0 | if (proxy.m_randomize_credentials) { |
766 | 0 | ProxyCredentials random_auth; |
767 | 0 | static std::atomic_int counter(0); |
768 | 0 | random_auth.username = random_auth.password = strprintf("%i", counter++); |
769 | 0 | if (!Socks5(dest, port, &random_auth, *sock)) { |
770 | 0 | return {}; |
771 | 0 | } |
772 | 0 | } else { |
773 | 0 | if (!Socks5(dest, port, nullptr, *sock)) { |
774 | 0 | return {}; |
775 | 0 | } |
776 | 0 | } |
777 | 0 | return sock; |
778 | 0 | } |
779 | | |
780 | | CSubNet LookupSubNet(const std::string& subnet_str) |
781 | 0 | { |
782 | 0 | CSubNet subnet; |
783 | 0 | assert(!subnet.IsValid()); |
784 | 0 | if (!ContainsNoNUL(subnet_str)) { |
785 | 0 | return subnet; |
786 | 0 | } |
787 | | |
788 | 0 | const size_t slash_pos{subnet_str.find_last_of('/')}; |
789 | 0 | const std::string str_addr{subnet_str.substr(0, slash_pos)}; |
790 | 0 | std::optional<CNetAddr> addr{LookupHost(str_addr, /*fAllowLookup=*/false)}; |
791 | |
|
792 | 0 | if (addr.has_value()) { |
793 | 0 | addr = static_cast<CNetAddr>(MaybeFlipIPv6toCJDNS(CService{addr.value(), /*port=*/0})); |
794 | 0 | if (slash_pos != subnet_str.npos) { |
795 | 0 | const std::string netmask_str{subnet_str.substr(slash_pos + 1)}; |
796 | 0 | uint8_t netmask; |
797 | 0 | if (ParseUInt8(netmask_str, &netmask)) { |
798 | | // Valid number; assume CIDR variable-length subnet masking. |
799 | 0 | subnet = CSubNet{addr.value(), netmask}; |
800 | 0 | } else { |
801 | | // Invalid number; try full netmask syntax. Never allow lookup for netmask. |
802 | 0 | const std::optional<CNetAddr> full_netmask{LookupHost(netmask_str, /*fAllowLookup=*/false)}; |
803 | 0 | if (full_netmask.has_value()) { |
804 | 0 | subnet = CSubNet{addr.value(), full_netmask.value()}; |
805 | 0 | } |
806 | 0 | } |
807 | 0 | } else { |
808 | | // Single IP subnet (<ipv4>/32 or <ipv6>/128). |
809 | 0 | subnet = CSubNet{addr.value()}; |
810 | 0 | } |
811 | 0 | } |
812 | |
|
813 | 0 | return subnet; |
814 | 0 | } |
815 | | |
816 | | bool IsBadPort(uint16_t port) |
817 | 0 | { |
818 | | /* Don't forget to update doc/p2p-bad-ports.md if you change this list. */ |
819 | |
|
820 | 0 | switch (port) { |
821 | 0 | case 1: // tcpmux |
822 | 0 | case 7: // echo |
823 | 0 | case 9: // discard |
824 | 0 | case 11: // systat |
825 | 0 | case 13: // daytime |
826 | 0 | case 15: // netstat |
827 | 0 | case 17: // qotd |
828 | 0 | case 19: // chargen |
829 | 0 | case 20: // ftp data |
830 | 0 | case 21: // ftp access |
831 | 0 | case 22: // ssh |
832 | 0 | case 23: // telnet |
833 | 0 | case 25: // smtp |
834 | 0 | case 37: // time |
835 | 0 | case 42: // name |
836 | 0 | case 43: // nicname |
837 | 0 | case 53: // domain |
838 | 0 | case 69: // tftp |
839 | 0 | case 77: // priv-rjs |
840 | 0 | case 79: // finger |
841 | 0 | case 87: // ttylink |
842 | 0 | case 95: // supdup |
843 | 0 | case 101: // hostname |
844 | 0 | case 102: // iso-tsap |
845 | 0 | case 103: // gppitnp |
846 | 0 | case 104: // acr-nema |
847 | 0 | case 109: // pop2 |
848 | 0 | case 110: // pop3 |
849 | 0 | case 111: // sunrpc |
850 | 0 | case 113: // auth |
851 | 0 | case 115: // sftp |
852 | 0 | case 117: // uucp-path |
853 | 0 | case 119: // nntp |
854 | 0 | case 123: // NTP |
855 | 0 | case 135: // loc-srv /epmap |
856 | 0 | case 137: // netbios |
857 | 0 | case 139: // netbios |
858 | 0 | case 143: // imap2 |
859 | 0 | case 161: // snmp |
860 | 0 | case 179: // BGP |
861 | 0 | case 389: // ldap |
862 | 0 | case 427: // SLP (Also used by Apple Filing Protocol) |
863 | 0 | case 465: // smtp+ssl |
864 | 0 | case 512: // print / exec |
865 | 0 | case 513: // login |
866 | 0 | case 514: // shell |
867 | 0 | case 515: // printer |
868 | 0 | case 526: // tempo |
869 | 0 | case 530: // courier |
870 | 0 | case 531: // chat |
871 | 0 | case 532: // netnews |
872 | 0 | case 540: // uucp |
873 | 0 | case 548: // AFP (Apple Filing Protocol) |
874 | 0 | case 554: // rtsp |
875 | 0 | case 556: // remotefs |
876 | 0 | case 563: // nntp+ssl |
877 | 0 | case 587: // smtp (rfc6409) |
878 | 0 | case 601: // syslog-conn (rfc3195) |
879 | 0 | case 636: // ldap+ssl |
880 | 0 | case 989: // ftps-data |
881 | 0 | case 990: // ftps |
882 | 0 | case 993: // ldap+ssl |
883 | 0 | case 995: // pop3+ssl |
884 | 0 | case 1719: // h323gatestat |
885 | 0 | case 1720: // h323hostcall |
886 | 0 | case 1723: // pptp |
887 | 0 | case 2049: // nfs |
888 | 0 | case 3659: // apple-sasl / PasswordServer |
889 | 0 | case 4045: // lockd |
890 | 0 | case 5060: // sip |
891 | 0 | case 5061: // sips |
892 | 0 | case 6000: // X11 |
893 | 0 | case 6566: // sane-port |
894 | 0 | case 6665: // Alternate IRC |
895 | 0 | case 6666: // Alternate IRC |
896 | 0 | case 6667: // Standard IRC |
897 | 0 | case 6668: // Alternate IRC |
898 | 0 | case 6669: // Alternate IRC |
899 | 0 | case 6697: // IRC + TLS |
900 | 0 | case 10080: // Amanda |
901 | 0 | return true; |
902 | 0 | } |
903 | 0 | return false; |
904 | 0 | } |
905 | | |
906 | | CService MaybeFlipIPv6toCJDNS(const CService& service) |
907 | 0 | { |
908 | 0 | CService ret{service}; |
909 | 0 | if (ret.IsIPv6() && ret.HasCJDNSPrefix() && g_reachable_nets.Contains(NET_CJDNS)) { |
910 | 0 | ret.m_net = NET_CJDNS; |
911 | 0 | } |
912 | 0 | return ret; |
913 | 0 | } |