/Users/deen/code/yugabyte-db/src/yb/util/net/net_util.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | // |
18 | | // The following only applies to changes made to this file as part of YugaByte development. |
19 | | // |
20 | | // Portions Copyright (c) YugaByte, Inc. |
21 | | // |
22 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
23 | | // in compliance with the License. You may obtain a copy of the License at |
24 | | // |
25 | | // http://www.apache.org/licenses/LICENSE-2.0 |
26 | | // |
27 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
28 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
29 | | // or implied. See the License for the specific language governing permissions and limitations |
30 | | // under the License. |
31 | | // |
32 | | |
33 | | #include "yb/util/net/net_util.h" |
34 | | |
35 | | #include <ifaddrs.h> |
36 | | #include <sys/types.h> |
37 | | |
38 | | #include <algorithm> |
39 | | #include <unordered_set> |
40 | | #include <utility> |
41 | | #include <vector> |
42 | | |
43 | | #include <boost/algorithm/string.hpp> |
44 | | |
45 | | #include "yb/gutil/map-util.h" |
46 | | #include "yb/gutil/strings/join.h" |
47 | | #include "yb/gutil/strings/numbers.h" |
48 | | #include "yb/gutil/strings/split.h" |
49 | | #include "yb/gutil/strings/strip.h" |
50 | | #include "yb/gutil/strings/substitute.h" |
51 | | |
52 | | #include "yb/util/debug/trace_event.h" |
53 | | #include "yb/util/env.h" |
54 | | #include "yb/util/env_util.h" |
55 | | #include "yb/util/errno.h" |
56 | | #include "yb/util/faststring.h" |
57 | | #include "yb/util/flag_tags.h" |
58 | | #include "yb/util/locks.h" |
59 | | #include "yb/util/net/inetaddress.h" |
60 | | #include "yb/util/net/sockaddr.h" |
61 | | #include "yb/util/net/socket.h" |
62 | | #include "yb/util/random_util.h" |
63 | | #include "yb/util/result.h" |
64 | | #include "yb/util/scope_exit.h" |
65 | | #include "yb/util/status_format.h" |
66 | | #include "yb/util/stopwatch.h" |
67 | | #include "yb/util/subprocess.h" |
68 | | |
69 | | // Mac OS 10.9 does not appear to define HOST_NAME_MAX in unistd.h |
70 | | #ifndef HOST_NAME_MAX |
71 | 77.2k | #define HOST_NAME_MAX 64 |
72 | | #endif |
73 | | |
74 | | using std::unordered_set; |
75 | | using std::vector; |
76 | | using strings::Substitute; |
77 | | |
78 | | DEFINE_string( |
79 | | net_address_filter, |
80 | | "ipv4_external,ipv4_all,ipv6_external,ipv6_non_link_local,ipv6_all", |
81 | | "Order in which to select ip addresses returned by the resolver" |
82 | | "Can be set to something like \"ipv4_all,ipv6_all\" to prefer IPv4 over " |
83 | | "IPv6 addresses." |
84 | | "Can be set to something like \"ipv4_external,ipv4_all,ipv6_all\" to " |
85 | | "prefer external IPv4 " |
86 | | "addresses first. Other options include ipv6_external,ipv6_non_link_local"); |
87 | | |
88 | | namespace yb { |
89 | | |
90 | | namespace { |
91 | | struct AddrinfoDeleter { |
92 | 31.0k | void operator()(struct addrinfo* info) { |
93 | 31.0k | freeaddrinfo(info); |
94 | 31.0k | } |
95 | | }; |
96 | | } |
97 | | |
98 | | HostPort::HostPort() |
99 | 4.54M | : port_(0) { |
100 | 4.54M | } |
101 | | |
102 | | HostPort::HostPort(Slice host, uint16_t port) |
103 | 165 | : host_(host.cdata(), host.size()), port_(port) {} |
104 | | |
105 | | HostPort::HostPort(std::string host, uint16_t port) |
106 | 71.8k | : host_(std::move(host)), port_(port) {} |
107 | | |
108 | | HostPort::HostPort(const Endpoint& endpoint) |
109 | 8.03k | : host_(endpoint.address().to_string()), port_(endpoint.port()) { |
110 | 8.03k | } |
111 | | |
112 | | HostPort::HostPort(const char* host, uint16_t port) |
113 | 165 | : HostPort(Slice(host), port) { |
114 | 165 | } Unexecuted instantiation: yb::HostPort::HostPort(char const*, unsigned short) yb::HostPort::HostPort(char const*, unsigned short) Line | Count | Source | 113 | 165 | : HostPort(Slice(host), port) { | 114 | 165 | } |
|
115 | | |
116 | | Status HostPort::RemoveAndGetHostPortList( |
117 | | const Endpoint& remove, |
118 | | const std::vector<std::string>& multiple_server_addresses, |
119 | | uint16_t default_port, |
120 | 0 | std::vector<HostPort> *res) { |
121 | 0 | bool found = false; |
122 | | // Note that this outer loop is over a vector of comma-separated strings. |
123 | 0 | for (const string& master_server_addr : multiple_server_addresses) { |
124 | 0 | std::vector<std::string> addr_strings = |
125 | 0 | strings::Split(master_server_addr, ",", strings::SkipEmpty()); |
126 | 0 | for (const auto& single_addr : addr_strings) { |
127 | 0 | HostPort host_port; |
128 | 0 | RETURN_NOT_OK(host_port.ParseString(single_addr, default_port)); |
129 | 0 | if (host_port.equals(remove)) { |
130 | 0 | found = true; |
131 | 0 | continue; |
132 | 0 | } else { |
133 | 0 | res->push_back(host_port); |
134 | 0 | } |
135 | 0 | } |
136 | 0 | } |
137 | | |
138 | 0 | if (!found) { |
139 | 0 | std::ostringstream out; |
140 | 0 | out.str("Current list of master addresses: "); |
141 | 0 | for (const string& master_server_addr : multiple_server_addresses) { |
142 | 0 | out.str(master_server_addr); |
143 | 0 | out.str(" "); |
144 | 0 | } |
145 | 0 | LOG(ERROR) << out.str(); |
146 | |
|
147 | 0 | return STATUS_SUBSTITUTE(NotFound, |
148 | 0 | "Cannot find $0 in addresses: $1", |
149 | 0 | yb::ToString(remove), |
150 | 0 | out.str()); |
151 | 0 | } |
152 | | |
153 | 0 | return Status::OK(); |
154 | 0 | } |
155 | | |
156 | | // Accepts entries like: [::1], 127.0.0.1, [::1]:7100, 0.0.0.0:7100, |
157 | | // f.q.d.n:7100 |
158 | 365k | Status HostPort::ParseString(const string &str_in, uint16_t default_port) { |
159 | 365k | uint32_t port; |
160 | 365k | string host; |
161 | | |
162 | 365k | string str(str_in); |
163 | 365k | StripWhiteSpace(&str); |
164 | 365k | size_t pos = str.rfind(':'); |
165 | 365k | if (str[0] == '[' && str[str.length() - 1] == ']'8 && str.length() > 23 ) { |
166 | | // The whole thing is an IPv6 address. |
167 | 3 | host = str.substr(1, str.length() - 2); |
168 | 3 | port = default_port; |
169 | 365k | } else if (pos == string::npos) { |
170 | | // No port was specified, the whole thing must be a host. |
171 | 3.88k | host = str; |
172 | 3.88k | port = default_port; |
173 | 361k | } else if (361k pos > 1361k && pos + 1 < str.length() && |
174 | 361k | SimpleAtoi(str.substr(pos + 1), &port)361k ) { |
175 | | |
176 | 361k | if (port > numeric_limits<uint16_t>::max()) { |
177 | 1 | return STATUS(InvalidArgument, "Invalid port", str); |
178 | 1 | } |
179 | | |
180 | | // Got a host:port |
181 | 361k | host = str.substr(0, pos); |
182 | 361k | if (host[0] == '[' && host[host.length() - 1] == ']'5 && host.length() > 25 ) { |
183 | | // Remove brackets if we have an IPv6 address |
184 | 5 | host = host.substr(1, host.length() - 2); |
185 | 5 | } |
186 | 18.4E | } else { |
187 | 18.4E | return STATUS(InvalidArgument, |
188 | 18.4E | Format( |
189 | 18.4E | "Invalid port: expected port after ':' " |
190 | 18.4E | "at position $0 in $1", |
191 | 18.4E | pos, str)); |
192 | 18.4E | } |
193 | | |
194 | 365k | host_ = host; |
195 | 365k | port_ = port; |
196 | 365k | return Status::OK(); |
197 | 365k | } |
198 | | |
199 | 62.1k | Result<HostPort> HostPort::FromString(const std::string& str, uint16_t default_port) { |
200 | 62.1k | HostPort result; |
201 | 62.1k | RETURN_NOT_OK(result.ParseString(str, default_port)); |
202 | 62.1k | return result; |
203 | 62.1k | } |
204 | | |
205 | | Result<std::vector<HostPort>> HostPort::ParseStrings( |
206 | | const std::string& comma_sep_addrs, uint16_t default_port, |
207 | 4.01k | const char* separator) { |
208 | 4.01k | std::vector<HostPort> result; |
209 | 4.01k | RETURN_NOT_OK(ParseStrings(comma_sep_addrs, default_port, &result, separator)); |
210 | 4.01k | return result; |
211 | 4.01k | } |
212 | | |
213 | 37.7M | size_t HostPortHash::operator()(const HostPort& hostPort) const { |
214 | 37.7M | return GStringPiece(std::to_string(hostPort.port()) + hostPort.host()).hash(); |
215 | 37.7M | } |
216 | | |
217 | | namespace { |
218 | 345 | const string getaddrinfo_rc_to_string(int rc) { |
219 | 345 | const char* s = nullptr; |
220 | 345 | switch (rc) { |
221 | 0 | case EAI_ADDRFAMILY: s = "EAI_ADDRFAMILY"; break; |
222 | 0 | case EAI_AGAIN: s = "EAI_AGAIN"; break; |
223 | 0 | case EAI_BADFLAGS: s = "EAI_BADFLAGS"; break; |
224 | 0 | case EAI_FAIL: s = "EAI_FAIL"; break; |
225 | 0 | case EAI_FAMILY: s = "EAI_FAMILY"; break; |
226 | 0 | case EAI_MEMORY: s = "EAI_MEMORY"; break; |
227 | 0 | case EAI_NODATA: s = "EAI_NODATA"; break; |
228 | 346 | case EAI_NONAME: s = "EAI_NONAME"; break; |
229 | 0 | case EAI_SERVICE: s = "EAI_SERVICE"; break; |
230 | 0 | case EAI_SOCKTYPE: s = "EAI_SOCKTYPE"; break; |
231 | 0 | case EAI_SYSTEM: s = "EAI_SYSTEM"; break; |
232 | 0 | default: s = "UNKNOWN"; |
233 | 345 | } |
234 | 346 | return Substitute("$0 ($1)", rc, s); |
235 | 345 | } |
236 | | |
237 | 30.9k | Result<std::unique_ptr<addrinfo, AddrinfoDeleter>> HostToInetAddrInfo(const std::string& host) { |
238 | 30.9k | struct addrinfo hints; |
239 | 30.9k | memset(&hints, 0, sizeof(hints)); |
240 | 30.9k | hints.ai_family = AF_UNSPEC; |
241 | 30.9k | hints.ai_socktype = SOCK_STREAM; |
242 | 30.9k | struct addrinfo* res = nullptr; |
243 | 30.9k | int rc = 0; |
244 | 30.9k | LOG_SLOW_EXECUTION(WARNING, 200, |
245 | 31.1k | Substitute("resolving address for $0", host)) { |
246 | 31.1k | rc = getaddrinfo(host.c_str(), nullptr, &hints, &res); |
247 | 31.1k | } |
248 | 30.9k | if (rc != 0) { |
249 | 68 | return STATUS_FORMAT(NetworkError, "Unable to resolve address $0, getaddrinfo returned $1: $2", |
250 | 68 | host.c_str(), getaddrinfo_rc_to_string(rc).c_str(), gai_strerror(rc)); |
251 | 30.9k | } else { |
252 | 30.9k | return std::unique_ptr<addrinfo, AddrinfoDeleter>(res); |
253 | 30.9k | } |
254 | 30.9k | } |
255 | | |
256 | | template <typename F> |
257 | 205k | CHECKED_STATUS ResolveInetAddresses(const std::string& host, F func) { |
258 | 205k | boost::optional<IpAddress> fast_resolve = TryFastResolve(host); |
259 | 205k | if (fast_resolve) { |
260 | 174k | func(*fast_resolve); |
261 | 18.4E | VLOG(4) << "Fast resolved " << host << " to " << fast_resolve->to_string(); |
262 | 174k | return Status::OK(); |
263 | 174k | } |
264 | | |
265 | 31.0k | auto addrinfo_holder = VERIFY_RESULT30.9k (HostToInetAddrInfo(host));30.9k |
266 | 0 | struct addrinfo* addrinfo = addrinfo_holder.get(); |
267 | 93.0k | for (; addrinfo != nullptr; addrinfo = addrinfo->ai_next62.0k ) { |
268 | 62.0k | if (addrinfo->ai_family == AF_INET) { |
269 | 61.4k | auto* addr = reinterpret_cast<struct sockaddr_in*>(addrinfo->ai_addr); |
270 | 61.4k | Endpoint endpoint; |
271 | 61.4k | memcpy(endpoint.data(), addr, sizeof(*addr)); |
272 | 61.4k | func(endpoint.address()); |
273 | 61.4k | } else if (675 addrinfo->ai_family == AF_INET6675 ) { |
274 | 676 | auto* addr = reinterpret_cast<struct sockaddr_in6*>(addrinfo->ai_addr); |
275 | 676 | Endpoint endpoint; |
276 | 676 | memcpy(endpoint.data(), addr, sizeof(*addr)); |
277 | 676 | func(endpoint.address()); |
278 | 18.4E | } else { |
279 | 18.4E | return STATUS_FORMAT(NetworkError, "Unexpected address family: $0", addrinfo->ai_family); |
280 | 18.4E | } |
281 | 62.0k | } |
282 | 30.9k | return Status::OK(); |
283 | 30.9k | } net_util.cc:yb::Status yb::(anonymous namespace)::ResolveInetAddresses<yb::HostPort::ResolveAddresses(std::__1::vector<boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>, std::__1::allocator<boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> > >*) const::$_0>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, yb::HostPort::ResolveAddresses(std::__1::vector<boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>, std::__1::allocator<boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> > >*) const::$_0) Line | Count | Source | 257 | 131k | CHECKED_STATUS ResolveInetAddresses(const std::string& host, F func) { | 258 | 131k | boost::optional<IpAddress> fast_resolve = TryFastResolve(host); | 259 | 131k | if (fast_resolve) { | 260 | 100k | func(*fast_resolve); | 261 | 100k | VLOG(4) << "Fast resolved " << host << " to " << fast_resolve->to_string()0 ; | 262 | 100k | return Status::OK(); | 263 | 100k | } | 264 | | | 265 | 31.0k | auto addrinfo_holder = VERIFY_RESULT30.9k (HostToInetAddrInfo(host));30.9k | 266 | 0 | struct addrinfo* addrinfo = addrinfo_holder.get(); | 267 | 93.0k | for (; addrinfo != nullptr; addrinfo = addrinfo->ai_next62.0k ) { | 268 | 62.0k | if (addrinfo->ai_family == AF_INET) { | 269 | 61.4k | auto* addr = reinterpret_cast<struct sockaddr_in*>(addrinfo->ai_addr); | 270 | 61.4k | Endpoint endpoint; | 271 | 61.4k | memcpy(endpoint.data(), addr, sizeof(*addr)); | 272 | 61.4k | func(endpoint.address()); | 273 | 61.4k | } else if (675 addrinfo->ai_family == AF_INET6675 ) { | 274 | 676 | auto* addr = reinterpret_cast<struct sockaddr_in6*>(addrinfo->ai_addr); | 275 | 676 | Endpoint endpoint; | 276 | 676 | memcpy(endpoint.data(), addr, sizeof(*addr)); | 277 | 676 | func(endpoint.address()); | 278 | 18.4E | } else { | 279 | 18.4E | return STATUS_FORMAT(NetworkError, "Unexpected address family: $0", addrinfo->ai_family); | 280 | 18.4E | } | 281 | 62.0k | } | 282 | 30.9k | return Status::OK(); | 283 | 30.9k | } |
net_util.cc:yb::Status yb::(anonymous namespace)::ResolveInetAddresses<yb::HostToAddresses(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, boost::container::small_vector_base<boost::asio::ip::address, void, void>*)::$_2>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, yb::HostToAddresses(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, boost::container::small_vector_base<boost::asio::ip::address, void, void>*)::$_2) Line | Count | Source | 257 | 74.1k | CHECKED_STATUS ResolveInetAddresses(const std::string& host, F func) { | 258 | 74.1k | boost::optional<IpAddress> fast_resolve = TryFastResolve(host); | 259 | 74.1k | if (fast_resolve) { | 260 | 74.0k | func(*fast_resolve); | 261 | 18.4E | VLOG(4) << "Fast resolved " << host << " to " << fast_resolve->to_string(); | 262 | 74.0k | return Status::OK(); | 263 | 74.0k | } | 264 | | | 265 | 21 | auto addrinfo_holder = VERIFY_RESULT19 (HostToInetAddrInfo(host));19 | 266 | 0 | struct addrinfo* addrinfo = addrinfo_holder.get(); | 267 | 24 | for (; addrinfo != nullptr; addrinfo = addrinfo->ai_next5 ) { | 268 | 5 | if (addrinfo->ai_family == AF_INET) { | 269 | 5 | auto* addr = reinterpret_cast<struct sockaddr_in*>(addrinfo->ai_addr); | 270 | 5 | Endpoint endpoint; | 271 | 5 | memcpy(endpoint.data(), addr, sizeof(*addr)); | 272 | 5 | func(endpoint.address()); | 273 | 5 | } else if (0 addrinfo->ai_family == AF_INET60 ) { | 274 | 0 | auto* addr = reinterpret_cast<struct sockaddr_in6*>(addrinfo->ai_addr); | 275 | 0 | Endpoint endpoint; | 276 | 0 | memcpy(endpoint.data(), addr, sizeof(*addr)); | 277 | 0 | func(endpoint.address()); | 278 | 0 | } else { | 279 | 0 | return STATUS_FORMAT(NetworkError, "Unexpected address family: $0", addrinfo->ai_family); | 280 | 0 | } | 281 | 5 | } | 282 | 19 | return Status::OK(); | 283 | 19 | } |
|
284 | | |
285 | | } // namespace |
286 | | |
287 | 131k | Status HostPort::ResolveAddresses(std::vector<Endpoint>* addresses) const { |
288 | 131k | TRACE_EVENT1("net", "HostPort::ResolveAddresses", |
289 | 131k | "host", host_); |
290 | 131k | if (!addresses) { |
291 | 1 | return Status::OK(); |
292 | 1 | } |
293 | 131k | vector<IpAddress> ip_addresses; |
294 | 131k | RETURN_NOT_OK(ResolveInetAddresses( |
295 | 131k | host_, [this, &ip_addresses](const IpAddress &ip_address) { |
296 | 131k | ip_addresses.push_back(ip_address); |
297 | 131k | VLOG(3) << "Resolved address " << ip_address.to_string() << " for host " |
298 | 131k | << host_; |
299 | 131k | })); |
300 | | |
301 | 131k | FilterAddresses(FLAGS_net_address_filter, &ip_addresses); |
302 | | |
303 | 131k | VLOG(2) << "Returned " << ip_addresses.size() << " addresses for host " |
304 | 16 | << host_; |
305 | 162k | for (const auto &ip_addr : ip_addresses) { |
306 | 162k | VLOG(2) << "Returned address " << ip_addr.to_string() << " for host " |
307 | 13 | << host_; |
308 | 162k | addresses->push_back(Endpoint(ip_addr, port_)); |
309 | 162k | } |
310 | 131k | return Status::OK(); |
311 | 131k | } |
312 | | |
313 | | Status HostPort::ParseStrings(const string& comma_sep_addrs, |
314 | | uint16_t default_port, |
315 | | std::vector<HostPort>* res, |
316 | 211k | const char* separator) { |
317 | 211k | std::vector<string> addr_strings = strings::Split( |
318 | 211k | comma_sep_addrs, separator, strings::SkipEmpty()); |
319 | 211k | std::vector<HostPort> host_ports; |
320 | 288k | for (string& addr_string : addr_strings) { |
321 | 288k | HostPort host_port; |
322 | 288k | RETURN_NOT_OK(host_port.ParseString(addr_string, default_port)); |
323 | 288k | host_ports.push_back(host_port); |
324 | 288k | } |
325 | 211k | *res = host_ports; |
326 | 211k | return Status::OK(); |
327 | 211k | } |
328 | | |
329 | 97.2M | string HostPort::ToString() const { return HostPortToString(host_, port_); } |
330 | | |
331 | 6.04k | string HostPort::ToCommaSeparatedString(const std::vector<HostPort>& hostports) { |
332 | 6.04k | vector<string> hostport_strs; |
333 | 6.54k | for (const HostPort& hostport : hostports) { |
334 | 6.54k | hostport_strs.push_back(hostport.ToString()); |
335 | 6.54k | } |
336 | 6.04k | return JoinStrings(hostport_strs, ","); |
337 | 6.04k | } |
338 | | |
339 | 25.8k | bool IsPrivilegedPort(uint16_t port) { return port < 1024 && port != 054 ; } |
340 | | |
341 | | Status ParseAddressList(const std::string& addr_list, |
342 | | uint16_t default_port, |
343 | 53.6k | std::vector<Endpoint>* addresses) { |
344 | 53.6k | std::vector<HostPort> host_ports; |
345 | 53.6k | RETURN_NOT_OK(HostPort::ParseStrings(addr_list, default_port, &host_ports)); |
346 | 53.6k | std::unordered_set<Endpoint, EndpointHash> uniqued; |
347 | | |
348 | 53.6k | for (const HostPort& host_port : host_ports) { |
349 | 53.6k | std::vector<Endpoint> this_addresses; |
350 | 53.6k | RETURN_NOT_OK(host_port.ResolveAddresses(&this_addresses)); |
351 | | |
352 | | // Only add the unique ones -- the user may have specified |
353 | | // some IP addresses in multiple ways |
354 | 54.3k | for (const auto& addr : this_addresses)53.6k { |
355 | 54.3k | if (InsertIfNotPresent(&uniqued, addr)) { |
356 | 54.3k | addresses->push_back(addr); |
357 | 54.3k | } else { |
358 | 0 | LOG(INFO) << "Address " << addr << " for " << host_port.ToString() |
359 | 0 | << " duplicates an earlier resolved entry."; |
360 | 0 | } |
361 | 54.3k | } |
362 | 53.6k | } |
363 | 53.6k | return Status::OK(); |
364 | 53.6k | } |
365 | | |
366 | 77.2k | Status GetHostname(string* hostname) { |
367 | 77.2k | TRACE_EVENT0("net", "GetHostname"); |
368 | 77.2k | char name[HOST_NAME_MAX]; |
369 | 77.2k | int ret = gethostname(name, HOST_NAME_MAX); |
370 | 77.2k | if (ret != 0) { |
371 | 0 | return STATUS(NetworkError, "Unable to determine local hostname", Errno(errno)); |
372 | 0 | } |
373 | 77.2k | *hostname = name; |
374 | 77.2k | return Status::OK(); |
375 | 77.2k | } |
376 | | |
377 | 11.9k | Result<string> GetHostname() { |
378 | 11.9k | std::string result; |
379 | 11.9k | RETURN_NOT_OK(GetHostname(&result)); |
380 | 11.9k | return result; |
381 | 11.9k | } |
382 | | |
383 | | Status GetLocalAddresses(const string &filter_spec, |
384 | 0 | std::vector<IpAddress> *result) { |
385 | 0 | RETURN_NOT_OK(GetLocalAddresses(result, AddressFilter::ANY)); |
386 | 0 | FilterAddresses(filter_spec, result); |
387 | 0 | return Status::OK(); |
388 | 0 | } |
389 | | |
390 | 30.7k | Status GetLocalAddresses(std::vector<IpAddress>* result, AddressFilter filter) { |
391 | 30.7k | ifaddrs* addresses; |
392 | 30.7k | if (getifaddrs(&addresses)) { |
393 | 0 | return STATUS(NetworkError, "Failed to list network interfaces", Errno(errno)); |
394 | 0 | } |
395 | | |
396 | 30.7k | auto se = ScopeExit([addresses] { |
397 | 30.7k | freeifaddrs(addresses); |
398 | 30.7k | }); |
399 | | |
400 | 32.2M | for (auto address = addresses; address; address = address->ifa_next32.2M ) { |
401 | 32.2M | if (address->ifa_addr != nullptr32.2M ) { |
402 | 32.2M | Endpoint temp; |
403 | 32.2M | auto family = address->ifa_addr->sa_family; |
404 | 32.2M | size_t size; |
405 | 32.2M | if (family == AF_INET) { |
406 | 31.1M | size = sizeof(sockaddr_in); |
407 | 31.1M | } else if (1.00M family == AF_INET61.00M ) { |
408 | 269k | size = sizeof(sockaddr_in6); |
409 | 735k | } else { |
410 | 735k | continue; |
411 | 735k | } |
412 | 31.4M | memcpy(temp.data(), address->ifa_addr, size); |
413 | 31.4M | switch (filter) { |
414 | 2.05k | case AddressFilter::ANY: |
415 | 2.05k | result->push_back(temp.address()); |
416 | 2.05k | break; |
417 | 31.4M | case AddressFilter::EXTERNAL: |
418 | 31.4M | if (!temp.address().is_unspecified()31.4M && !temp.address().is_loopback()) { |
419 | 361k | result->push_back(temp.address()); |
420 | 361k | } |
421 | 31.4M | break; |
422 | 31.4M | } |
423 | 31.4M | } |
424 | 32.2M | } |
425 | 37.1k | return Status::OK(); |
426 | 30.7k | } |
427 | | |
428 | 32.7k | Status GetFQDN(string* hostname) { |
429 | 32.7k | TRACE_EVENT0("net", "GetFQDN"); |
430 | | // Start with the non-qualified hostname |
431 | 32.7k | RETURN_NOT_OK(GetHostname(hostname)); |
432 | | |
433 | 32.7k | struct addrinfo hints; |
434 | 32.7k | memset(&hints, 0, sizeof(hints)); |
435 | 32.7k | hints.ai_socktype = SOCK_DGRAM; |
436 | 32.7k | hints.ai_flags = AI_CANONNAME; |
437 | | |
438 | 32.7k | struct addrinfo* result; |
439 | 32.7k | LOG_SLOW_EXECUTION(WARNING, 200, |
440 | 32.7k | Substitute("looking up canonical hostname for localhost $0", *hostname)) { |
441 | 32.7k | TRACE_EVENT0("net", "getaddrinfo"); |
442 | 32.7k | const int rc = getaddrinfo(hostname->c_str(), nullptr, &hints, &result); |
443 | 32.7k | if (rc != 0) { |
444 | 277 | return STATUS(NetworkError, |
445 | 277 | Substitute("Unable to lookup FQDN ($0), getaddrinfo returned $1", |
446 | 277 | *hostname, getaddrinfo_rc_to_string(rc)), |
447 | 277 | Errno(errno)); |
448 | 277 | } |
449 | 32.7k | } |
450 | | |
451 | 32.4k | if (!result->ai_canonname) { |
452 | 0 | return STATUS(NetworkError, "Canonical name not specified"); |
453 | 0 | } |
454 | | |
455 | 32.4k | *hostname = result->ai_canonname; |
456 | 32.4k | freeaddrinfo(result); |
457 | 32.4k | return Status::OK(); |
458 | 32.4k | } |
459 | | |
460 | 0 | Status EndpointFromHostPort(const HostPort& host_port, Endpoint* endpoint) { |
461 | 0 | vector<Endpoint> addrs; |
462 | 0 | RETURN_NOT_OK(host_port.ResolveAddresses(&addrs)); |
463 | 0 | if (addrs.empty()) { |
464 | 0 | return STATUS(NetworkError, "Unable to resolve address", host_port.ToString()); |
465 | 0 | } |
466 | 0 | *endpoint = addrs[0]; |
467 | 0 | if (addrs.size() > 1) { |
468 | 0 | VLOG(1) << "Hostname " << host_port.host() << " resolved to more than one address. " |
469 | 0 | << "Using address: " << *endpoint; |
470 | 0 | } |
471 | 0 | return Status::OK(); |
472 | 0 | } |
473 | | |
474 | 2 | Status HostPortFromEndpointReplaceWildcard(const Endpoint& addr, HostPort* hp) { |
475 | 2 | string host; |
476 | 2 | if (addr.address().is_unspecified()) { |
477 | 1 | auto status = GetFQDN(&host); |
478 | 1 | if (!status.ok()) { |
479 | 0 | std::vector<IpAddress> locals; |
480 | 0 | if (GetLocalAddresses(&locals, AddressFilter::EXTERNAL).ok() && !locals.empty()) { |
481 | 0 | hp->set_host(locals.front().to_string()); |
482 | 0 | hp->set_port(addr.port()); |
483 | 0 | return Status::OK(); |
484 | 0 | } |
485 | 0 | return status; |
486 | 0 | } |
487 | 1 | } else { |
488 | 1 | host = addr.address().to_string(); |
489 | 1 | } |
490 | 2 | hp->set_host(host); |
491 | 2 | hp->set_port(addr.port()); |
492 | 2 | return Status::OK(); |
493 | 2 | } |
494 | | |
495 | 96 | void TryRunLsof(const Endpoint& addr, vector<string>* log) { |
496 | 96 | #if defined(__APPLE__) |
497 | 96 | string cmd = strings::Substitute( |
498 | 96 | "lsof -n -i 'TCP:$0' -sTCP:LISTEN ; " |
499 | 96 | "for pid in $$(lsof -F p -n -i 'TCP:$0' -sTCP:LISTEN | cut -f 2 -dp) ; do" |
500 | 96 | " pstree $$pid || ps h -p $$pid;" |
501 | 96 | "done", |
502 | 96 | addr.port()); |
503 | | #else |
504 | | // Little inline bash script prints the full ancestry of any pid listening |
505 | | // on the same port as 'addr'. We could use 'pstree -s', but that option |
506 | | // doesn't exist on el6. |
507 | | // |
508 | | // Note the sed command to check for the process name wrapped in (). |
509 | | // Example prefix of /proc/$pid/stat output, with a process with spaces in the name: |
510 | | // 3917 (tmux: server) S 1 |
511 | | string cmd = strings::Substitute( |
512 | | "export PATH=$$PATH:/usr/sbin ; " |
513 | | "lsof -n -i 'TCP:$0' -sTCP:LISTEN ; " |
514 | | "for pid in $$(lsof -F p -n -i 'TCP:$0' -sTCP:LISTEN | cut -f 2 -dp) ; do" |
515 | | " while [ $$pid -gt 1 ] ; do" |
516 | | " ps h -fp $$pid ;" |
517 | | " pid=$$(sed 's/.* (.*) [^ ] \\([0-9]*\\).*/\\1/g' /proc/$$pid/stat);" |
518 | | " done ; " |
519 | | "done", |
520 | | addr.port()); |
521 | | #endif // defined(__APPLE__) |
522 | | |
523 | 96 | LOG_STRING(WARNING, log) << "Failed to bind to " << addr << ". " |
524 | 96 | << "Trying to use lsof to find any processes listening " |
525 | 96 | << "on the same port:"; |
526 | 96 | LOG_STRING(INFO, log) << "$ " << cmd; |
527 | 96 | vector<string> argv = { "bash", "-c", cmd }; |
528 | 96 | string results; |
529 | 96 | Status s = Subprocess::Call(argv, &results); |
530 | 96 | if (PREDICT_FALSE(!s.ok())) { |
531 | 94 | LOG_STRING(WARNING, log) << s.ToString(); |
532 | 94 | } |
533 | 96 | LOG_STRING(WARNING, log) << results; |
534 | 96 | } |
535 | | |
536 | 17.5k | uint16_t GetFreePort(std::unique_ptr<FileLock>* file_lock) { |
537 | | // To avoid a race condition where the free port returned to the caller gets used by another |
538 | | // process before this caller can use it, we will lock the port using a file level lock. |
539 | | // First create the directory, if it doesn't already exist, where these lock files will live. |
540 | 17.5k | Env* env = Env::Default(); |
541 | 17.5k | bool created = false; |
542 | 17.5k | const string lock_file_dir = "/tmp/yb-port-locks"; |
543 | 17.5k | Status status = env_util::CreateDirIfMissing(env, lock_file_dir, &created); |
544 | 17.5k | if (!status.ok()) { |
545 | 0 | LOG(FATAL) << "Could not create " << lock_file_dir << " directory: " |
546 | 0 | << status.ToString(); |
547 | 0 | } |
548 | | |
549 | | // Now, find a unused port in the [kMinPort..kMaxPort] range. |
550 | 17.5k | constexpr uint16_t kMinPort = 15000; |
551 | 17.5k | constexpr uint16_t kMaxPort = 30000; |
552 | 17.5k | Status s; |
553 | 33.0k | for (int i = 0; i < 1000; ++i15.5k ) { |
554 | 33.0k | const uint16_t random_port = RandomUniformInt(kMinPort, kMaxPort); |
555 | 33.0k | VLOG(1) << "Trying to bind to port " << random_port0 ; |
556 | | |
557 | 33.0k | Endpoint sock_addr(boost::asio::ip::address_v4::loopback(), random_port); |
558 | 33.0k | Socket sock; |
559 | 33.0k | s = sock.Init(0); |
560 | 33.0k | if (!s.ok()) { |
561 | 465 | VLOG(1) << "Failed to call Init() on socket ith address " << sock_addr0 ; |
562 | 465 | continue; |
563 | 465 | } |
564 | | |
565 | 32.5k | s = sock.Bind(sock_addr, /* explain_addr_in_use */ false); |
566 | 32.5k | if (s.ok()) { |
567 | | // We found an unused port. |
568 | | |
569 | | // Now, lock this "port" for use by the current process before 'sock' goes out of scope. |
570 | | // This will ensure that no other process can get this port while this process is still |
571 | | // running. LockFile() returns immediately if we can't get the lock. That's the behavior |
572 | | // we want. In that case, we'll just try another port. |
573 | 29.5k | const string lock_file = lock_file_dir + "/" + std::to_string(random_port) + ".lck"; |
574 | 29.5k | FileLock *lock = nullptr; |
575 | 29.5k | s = env->LockFile(lock_file, &lock, false /* recursive_lock_ok */); |
576 | 29.5k | if (s.ok()) { |
577 | 17.4k | CHECK(lock) << "Lock should not be NULL"0 ; |
578 | 17.4k | file_lock->reset(lock); |
579 | 17.4k | LOG(INFO) << "Selected random free RPC port " << random_port; |
580 | 17.4k | return random_port; |
581 | 17.4k | } else { |
582 | 12.0k | VLOG(1) << "Could not lock file " << lock_file << ": " << s.ToString()0 ; |
583 | 12.0k | } |
584 | 29.5k | } else { |
585 | 3.00k | VLOG(1) << "Failed to bind to port " << random_port << ": " << s.ToString()0 ; |
586 | 3.00k | } |
587 | 32.5k | } |
588 | | |
589 | 14 | LOG(FATAL) << "Could not find a free random port between " << kMinPort << " and " |
590 | 14 | << kMaxPort << " inclusively" << ": " << s.ToString(); |
591 | 14 | return 0; // never reached |
592 | 17.5k | } |
593 | | |
594 | 0 | bool HostPort::equals(const Endpoint& endpoint) const { |
595 | 0 | return endpoint.address().to_string() == host() && endpoint.port() == port(); |
596 | 0 | } |
597 | | |
598 | 2.02k | HostPort HostPort::FromBoundEndpoint(const Endpoint& endpoint) { |
599 | 2.02k | if (endpoint.address().is_unspecified()) { |
600 | 84 | return HostPort(endpoint.address().is_v4() ? "127.0.0.1" : "::1"0 , endpoint.port()); |
601 | 1.94k | } else { |
602 | 1.94k | return HostPort(endpoint); |
603 | 1.94k | } |
604 | 2.02k | } |
605 | | |
606 | 97.2M | std::string HostPortToString(const std::string& host, int port) { |
607 | 97.2M | DCHECK_GE(port, 0); |
608 | 97.2M | DCHECK_LE(port, 65535); |
609 | 97.2M | if (host.find(':') != string::npos) { |
610 | 0 | return Format("[$0]:$1", host, port); |
611 | 97.2M | } else { |
612 | 97.2M | return Format("$0:$1", host, port); |
613 | 97.2M | } |
614 | 97.2M | } |
615 | | |
616 | | Status HostToAddresses( |
617 | | const std::string& host, |
618 | 74.1k | boost::container::small_vector_base<IpAddress>* addresses) { |
619 | 74.1k | return ResolveInetAddresses(host, [&addresses](const IpAddress& address) { |
620 | 74.1k | addresses->push_back(address); |
621 | 74.1k | }); |
622 | 74.1k | } |
623 | | |
624 | 74.1k | Result<IpAddress> HostToAddress(const std::string& host) { |
625 | 74.1k | boost::container::small_vector<IpAddress, 1> addrs; |
626 | 74.1k | RETURN_NOT_OK(HostToAddresses(host, &addrs)); |
627 | 74.1k | if (addrs.empty()) { |
628 | 0 | return STATUS(NetworkError, "Unable to resolve address", host); |
629 | 0 | } |
630 | 74.1k | auto addr = addrs.front(); |
631 | 74.1k | if (addrs.size() > 1) { |
632 | 0 | VLOG(1) << "Hostname " << host << " resolved to more than one address. " |
633 | 0 | << "Using address: " << addr; |
634 | 0 | } |
635 | 74.1k | return addr; |
636 | 74.1k | } |
637 | | |
638 | 89.3k | bool IsWildcardAddress(const std::string& host_str) { |
639 | 89.3k | boost::system::error_code ec; |
640 | 89.3k | auto addr = IpAddress::from_string(host_str, ec); |
641 | 89.3k | return !ec && addr.is_unspecified()87.1k ; |
642 | 89.3k | } |
643 | | |
644 | 2.47M | Result<IpAddress> ParseIpAddress(const std::string& host) { |
645 | 2.47M | boost::system::error_code ec; |
646 | 2.47M | auto addr = IpAddress::from_string(host, ec); |
647 | 2.47M | if (ec) { |
648 | 78.5k | return STATUS_FORMAT(InvalidArgument, "Failed to parse $0: $1", host, ec.message()); |
649 | 78.5k | } |
650 | | |
651 | 2.40M | VLOG(4) << "Resolving ip address to itself for input: " << host973 ; |
652 | 2.40M | return addr; |
653 | 2.47M | } |
654 | | |
655 | | simple_spinlock fail_to_fast_resolve_address_mutex; |
656 | | std::string fail_to_fast_resolve_address; |
657 | | |
658 | 0 | void TEST_SetFailToFastResolveAddress(const std::string& address) { |
659 | 0 | { |
660 | 0 | std::lock_guard<simple_spinlock> lock(fail_to_fast_resolve_address_mutex); |
661 | 0 | fail_to_fast_resolve_address = address; |
662 | 0 | } |
663 | 0 | LOG(INFO) << "Setting fail_to_fast_resolve_address to: " << address; |
664 | 0 | } |
665 | | |
666 | 2.48M | boost::optional<IpAddress> TryFastResolve(const std::string& host) { |
667 | 2.48M | auto result = ParseIpAddress(host); |
668 | 2.48M | if (result.ok()) { |
669 | 2.39M | return *result; |
670 | 2.39M | } |
671 | | |
672 | | // For testing purpose we resolve A.B.C.D.ip.yugabyte to A.B.C.D. |
673 | 81.1k | static const std::string kYbIpSuffix = ".ip.yugabyte"; |
674 | 81.1k | if (boost::ends_with(host, kYbIpSuffix)) { |
675 | 47.6k | { |
676 | 47.6k | std::lock_guard<simple_spinlock> lock(fail_to_fast_resolve_address_mutex); |
677 | 47.6k | if (PREDICT_FALSE(host == fail_to_fast_resolve_address)) { |
678 | 0 | return boost::none; |
679 | 0 | } |
680 | 47.6k | } |
681 | 47.6k | boost::system::error_code ec; |
682 | 47.6k | auto address = IpAddress::from_string( |
683 | 47.6k | host.substr(0, host.length() - kYbIpSuffix.length()), ec); |
684 | 47.6k | if (!ec47.6k ) { |
685 | 47.6k | return address; |
686 | 47.6k | } |
687 | 47.6k | } |
688 | | |
689 | 33.4k | return boost::none; |
690 | 81.1k | } |
691 | | |
692 | | } // namespace yb |