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