Line | Count | Source |
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 | | // This file is copied from |
18 | | // https://github.com/apache/impala/blob/branch-2.9.0/be/src/util/network-util.cc |
19 | | // and modified by Doris |
20 | | |
21 | | #include "util/network_util.h" |
22 | | |
23 | | #include <arpa/inet.h> |
24 | | // IWYU pragma: no_include <bits/local_lim.h> |
25 | | // IWYU pragma: no_include <bthread/errno.h> |
26 | | #include <errno.h> // IWYU pragma: keep |
27 | | #include <fmt/format.h> |
28 | | #include <gen_cpp/Types_types.h> |
29 | | #include <ifaddrs.h> |
30 | | #include <netdb.h> |
31 | | #include <netinet/in.h> |
32 | | #include <string.h> |
33 | | #include <sys/socket.h> |
34 | | #include <unistd.h> |
35 | | |
36 | | #include <chrono> |
37 | | #include <cstdint> |
38 | | #include <sstream> |
39 | | |
40 | | #include "common/cast_set.h" |
41 | | #include "util/hash_util.hpp" |
42 | | |
43 | | #ifdef __APPLE__ |
44 | | #ifndef HOST_NAME_MAX |
45 | | #define HOST_NAME_MAX MAXHOSTNAMELEN |
46 | | #endif |
47 | | #endif |
48 | | |
49 | | namespace doris { |
50 | | InetAddress::InetAddress(std::string ip, sa_family_t family, bool is_loopback) |
51 | 156 | : _ip_addr(ip), _family(family), _is_loopback(is_loopback) {} |
52 | | |
53 | 10 | bool InetAddress::is_loopback() const { |
54 | 10 | return _is_loopback; |
55 | 10 | } |
56 | | |
57 | 33 | std::string InetAddress::get_host_address() const { |
58 | 33 | return _ip_addr; |
59 | 33 | } |
60 | | |
61 | 20 | bool InetAddress::is_ipv6() const { |
62 | 20 | return _family == AF_INET6; |
63 | 20 | } |
64 | | |
65 | | static const std::string LOCALHOST("127.0.0.1"); |
66 | | |
67 | 0 | Status get_hostname(std::string* hostname) { |
68 | 0 | char name[HOST_NAME_MAX]; |
69 | 0 | int ret = gethostname(name, HOST_NAME_MAX); |
70 | |
|
71 | 0 | if (ret != 0) { |
72 | 0 | return Status::InternalError("Could not get hostname: errno: {}", errno); |
73 | 0 | } |
74 | | |
75 | 0 | *hostname = std::string(name); |
76 | 0 | return Status::OK(); |
77 | 0 | } |
78 | | |
79 | 1.25M | bool is_valid_ip(const std::string& ip) { |
80 | 1.25M | unsigned char buf[sizeof(struct in6_addr)]; |
81 | 1.26M | return (inet_pton(AF_INET6, ip.data(), buf) > 0) || (inet_pton(AF_INET, ip.data(), buf) > 0); |
82 | 1.25M | } |
83 | | |
84 | 1.06k | bool parse_endpoint(const std::string& endpoint, std::string* host, uint16_t* port) { |
85 | 1.06k | auto p = endpoint.find_last_of(':'); |
86 | 1.06k | if (p == std::string::npos || p + 1 == endpoint.size()) { |
87 | 0 | return false; |
88 | 0 | } |
89 | | |
90 | 1.06k | const char* port_base = endpoint.c_str() + p + 1; |
91 | 1.06k | char* end = nullptr; |
92 | 1.06k | long value = strtol(port_base, &end, 10); |
93 | 1.06k | if (port_base == end) { |
94 | 0 | return false; |
95 | 1.06k | } else if (*end) { |
96 | 0 | while (std::isspace(*end)) { |
97 | 0 | end++; |
98 | 0 | } |
99 | 0 | if (*end) { |
100 | 0 | return false; |
101 | 0 | } |
102 | 1.06k | } else if (value < 0 || 65535 < value) { |
103 | 0 | return false; |
104 | 0 | } |
105 | | |
106 | 1.06k | std::string::size_type i = 0; |
107 | 1.06k | const char* host_base = endpoint.c_str(); |
108 | 1.06k | while (std::isspace(host_base[i])) { |
109 | 0 | i++; |
110 | 0 | } |
111 | 1.06k | if (i < p && host_base[i] == '[' && host_base[p - 1] == ']') { |
112 | 0 | i += 1; |
113 | 0 | p -= 1; |
114 | 0 | } |
115 | 1.06k | if (i >= p) { |
116 | 0 | return false; |
117 | 0 | } |
118 | 1.06k | *host = endpoint.substr(i, p - i); |
119 | 1.06k | *port = cast_set<uint16_t>(value); |
120 | 1.06k | return true; |
121 | 1.06k | } |
122 | | |
123 | 0 | Status hostname_to_ip(const std::string& host, std::string& ip, int* gai_err) { |
124 | 0 | auto start = std::chrono::high_resolution_clock::now(); |
125 | 0 | Status status = hostname_to_ipv4(host, ip, gai_err); |
126 | 0 | if (status.ok()) { |
127 | 0 | return status; |
128 | 0 | } |
129 | 0 | status = hostname_to_ipv6(host, ip, gai_err); |
130 | |
|
131 | 0 | auto current = std::chrono::high_resolution_clock::now(); |
132 | 0 | auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(current - start); |
133 | 0 | if (duration.count() >= 500) { |
134 | 0 | LOG(WARNING) << "hostname_to_ip cost to mush time, cost_time:" << duration.count() |
135 | 0 | << "ms hostname:" << host << " ip:" << ip; |
136 | 0 | } |
137 | 0 | return status; |
138 | 0 | } |
139 | | |
140 | 89 | Status hostname_to_ip(const std::string& host, std::string& ip, bool ipv6, int* gai_err) { |
141 | 89 | if (ipv6) { |
142 | 0 | return hostname_to_ipv6(host, ip, gai_err); |
143 | 89 | } else { |
144 | 89 | return hostname_to_ipv4(host, ip, gai_err); |
145 | 89 | } |
146 | 89 | } |
147 | | |
148 | 89 | Status hostname_to_ipv4(const std::string& host, std::string& ip, int* gai_err) { |
149 | 89 | addrinfo hints, *res; |
150 | 89 | in_addr addr; |
151 | | |
152 | 89 | memset(&hints, 0, sizeof(addrinfo)); |
153 | 89 | hints.ai_socktype = SOCK_STREAM; |
154 | 89 | hints.ai_family = AF_INET; |
155 | 89 | int err = getaddrinfo(host.c_str(), NULL, &hints, &res); |
156 | 89 | if (gai_err != nullptr) { |
157 | 89 | *gai_err = err; |
158 | 89 | } |
159 | 89 | if (err != 0) { |
160 | 1 | LOG(WARNING) << "failed to get ip from host: " << host << "err:" << gai_strerror(err); |
161 | | // No stack trace: the WARNING above already carries the host and the resolver error, |
162 | | // and this failure is expected often enough (a decommissioned backend, a hostname |
163 | | // whose DNS record has not propagated yet) that attaching a stack to every |
164 | | // occurrence floods be.WARNING without adding information. |
165 | 1 | return Status::InternalError<false>("failed to get ip from host: {}, err: {}", host, |
166 | 1 | gai_strerror(err)); |
167 | 1 | } |
168 | | |
169 | 88 | addr.s_addr = ((sockaddr_in*)(res->ai_addr))->sin_addr.s_addr; |
170 | 88 | ip = inet_ntoa(addr); |
171 | | |
172 | 88 | freeaddrinfo(res); |
173 | 88 | return Status::OK(); |
174 | 89 | } |
175 | | |
176 | 0 | Status hostname_to_ipv6(const std::string& host, std::string& ip, int* gai_err) { |
177 | 0 | char ipstr2[128]; |
178 | 0 | struct sockaddr_in6* sockaddr_ipv6; |
179 | |
|
180 | 0 | struct addrinfo *answer, hint; |
181 | 0 | bzero(&hint, sizeof(hint)); |
182 | 0 | hint.ai_family = AF_INET6; |
183 | 0 | hint.ai_socktype = SOCK_STREAM; |
184 | |
|
185 | 0 | int err = getaddrinfo(host.c_str(), NULL, &hint, &answer); |
186 | 0 | if (gai_err != nullptr) { |
187 | 0 | *gai_err = err; |
188 | 0 | } |
189 | 0 | if (err != 0) { |
190 | 0 | LOG(WARNING) << "failed to get ip from host: " << host << "err:" << gai_strerror(err); |
191 | | // See hostname_to_ipv4() for why this error carries no stack trace. |
192 | 0 | return Status::InternalError<false>("failed to get ip from host: {}, err: {}", host, |
193 | 0 | gai_strerror(err)); |
194 | 0 | } |
195 | | |
196 | 0 | sockaddr_ipv6 = reinterpret_cast<struct sockaddr_in6*>(answer->ai_addr); |
197 | 0 | inet_ntop(AF_INET6, &sockaddr_ipv6->sin6_addr, ipstr2, sizeof(ipstr2)); |
198 | 0 | ip = ipstr2; |
199 | 0 | fflush(NULL); |
200 | 0 | freeaddrinfo(answer); |
201 | 0 | return Status::OK(); |
202 | 0 | } |
203 | | |
204 | 0 | bool find_first_non_localhost(const std::vector<std::string>& addresses, std::string* addr) { |
205 | 0 | for (const std::string& candidate : addresses) { |
206 | 0 | if (candidate != LOCALHOST) { |
207 | 0 | *addr = candidate; |
208 | 0 | return true; |
209 | 0 | } |
210 | 0 | } |
211 | | |
212 | 0 | return false; |
213 | 0 | } |
214 | | |
215 | 7 | Status get_hosts(std::vector<InetAddress>* hosts) { |
216 | 7 | ifaddrs* if_addrs = nullptr; |
217 | 7 | if (getifaddrs(&if_addrs)) { |
218 | 0 | std::stringstream ss; |
219 | 0 | char buf[64]; |
220 | 0 | ss << "getifaddrs failed because " << strerror_r(errno, buf, sizeof(buf)); |
221 | 0 | return Status::InternalError(ss.str()); |
222 | 0 | } |
223 | | |
224 | 248 | for (ifaddrs* if_addr = if_addrs; if_addr != nullptr; if_addr = if_addr->ifa_next) { |
225 | 241 | if (!if_addr->ifa_addr) { |
226 | 0 | continue; |
227 | 0 | } |
228 | 241 | auto addr = if_addr->ifa_addr; |
229 | 241 | if (addr->sa_family == AF_INET) { |
230 | | // check legitimacy of IP4 Address |
231 | 56 | char addr_buf[INET_ADDRSTRLEN]; |
232 | 56 | auto tmp_addr = &((struct sockaddr_in*)if_addr->ifa_addr)->sin_addr; |
233 | 56 | inet_ntop(AF_INET, tmp_addr, addr_buf, INET_ADDRSTRLEN); |
234 | | // check is loopback Address |
235 | 56 | in_addr_t s_addr = ((struct sockaddr_in*)addr)->sin_addr.s_addr; |
236 | 56 | bool is_loopback = (ntohl(s_addr) & 0xFF000000) == 0x7F000000; |
237 | 56 | hosts->emplace_back(std::string(addr_buf), AF_INET, is_loopback); |
238 | 185 | } else if (addr->sa_family == AF_INET6) { |
239 | | // check legitimacy of IP6 Address |
240 | 86 | auto tmp_addr = &((struct sockaddr_in6*)if_addr->ifa_addr)->sin6_addr; |
241 | 86 | char addr_buf[INET6_ADDRSTRLEN]; |
242 | 86 | inet_ntop(AF_INET6, tmp_addr, addr_buf, sizeof(addr_buf)); |
243 | | // check is loopback Address |
244 | 86 | bool is_loopback = IN6_IS_ADDR_LOOPBACK(tmp_addr); |
245 | 86 | hosts->emplace_back(std::string(addr_buf), AF_INET6, is_loopback); |
246 | 99 | } else { |
247 | 99 | continue; |
248 | 99 | } |
249 | 241 | } |
250 | | |
251 | 7 | if (if_addrs != nullptr) { |
252 | 7 | freeifaddrs(if_addrs); |
253 | 7 | } |
254 | | |
255 | 7 | return Status::OK(); |
256 | 7 | } |
257 | | |
258 | 30.0k | TNetworkAddress make_network_address(const std::string& hostname, int port) { |
259 | 30.0k | TNetworkAddress ret; |
260 | 30.0k | ret.__set_hostname(hostname); |
261 | 30.0k | ret.__set_port(port); |
262 | 30.0k | return ret; |
263 | 30.0k | } |
264 | | |
265 | 0 | Status get_inet_interfaces(std::vector<std::string>* interfaces, bool include_ipv6) { |
266 | 0 | ifaddrs* if_addrs = nullptr; |
267 | 0 | if (getifaddrs(&if_addrs)) { |
268 | 0 | std::stringstream ss; |
269 | 0 | char buf[64]; |
270 | 0 | ss << "getifaddrs failed, errno:" << errno << ", message" |
271 | 0 | << strerror_r(errno, buf, sizeof(buf)); |
272 | 0 | return Status::InternalError(ss.str()); |
273 | 0 | } |
274 | | |
275 | 0 | for (ifaddrs* if_addr = if_addrs; if_addr != nullptr; if_addr = if_addr->ifa_next) { |
276 | 0 | if (if_addr->ifa_addr == nullptr || if_addr->ifa_name == nullptr) { |
277 | 0 | continue; |
278 | 0 | } |
279 | 0 | if (if_addr->ifa_addr->sa_family == AF_INET || |
280 | 0 | (include_ipv6 && if_addr->ifa_addr->sa_family == AF_INET6)) { |
281 | 0 | interfaces->emplace_back(if_addr->ifa_name); |
282 | 0 | } |
283 | 0 | } |
284 | 0 | if (if_addrs != nullptr) { |
285 | 0 | freeifaddrs(if_addrs); |
286 | 0 | } |
287 | 0 | return Status::OK(); |
288 | 0 | } |
289 | | |
290 | 1.12k | std::string get_host_port(const std::string& host, int port) { |
291 | 1.12k | std::stringstream ss; |
292 | 1.12k | if (host.find(':') == std::string::npos) { |
293 | 1.12k | ss << host << ":" << port; |
294 | 18.4E | } else { |
295 | 18.4E | ss << "[" << host << "]" |
296 | 18.4E | << ":" << port; |
297 | 18.4E | } |
298 | 1.12k | return ss.str(); |
299 | 1.12k | } |
300 | | |
301 | 0 | std::string get_brpc_http_url(const std::string& host, int port) { |
302 | 0 | if (host.find(':') != std::string::npos) { |
303 | 0 | return fmt::format("list://[{}]:{}", host, port); |
304 | 0 | } else { |
305 | 0 | return fmt::format("http://{}:{}", host, port); |
306 | 0 | } |
307 | 0 | } |
308 | | } // namespace doris |
309 | | |
310 | 2.64M | size_t std::hash<doris::TNetworkAddress>::operator()(const doris::TNetworkAddress& address) const { |
311 | 2.64M | uint32_t seed = 0; |
312 | 2.64M | seed = doris::HashUtil::hash(address.hostname.data(), (uint32_t)address.hostname.size(), seed); |
313 | 2.64M | seed = doris::HashUtil::hash(&address.port, 4, seed); |
314 | 2.64M | return seed; |
315 | 2.64M | } |