/src/tdengine/source/os/src/osSocket.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com> |
3 | | * |
4 | | * SPDX-License-Identifier: AGPL-3.0-or-later |
5 | | * |
6 | | * This program is free software: you can use, redistribute, and/or modify |
7 | | * it under the terms of the GNU Affero General Public License, version 3 |
8 | | * or later ("AGPL"), as published by the Free Software Foundation. |
9 | | * |
10 | | * This program is distributed in the hope that it will be useful, but WITHOUT |
11 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
12 | | * FITNESS FOR A PARTICULAR PURPOSE. |
13 | | * |
14 | | * You should have received a copy of the GNU Affero General Public License |
15 | | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | |
18 | | #define _DEFAULT_SOURCE |
19 | | #define ALLOW_FORBID_FUNC |
20 | | #include "os.h" |
21 | | |
22 | | #if defined(WINDOWS) |
23 | | #include <IPHlpApi.h> |
24 | | #include <WS2tcpip.h> |
25 | | #include <Winsock2.h> |
26 | | #include <stdio.h> |
27 | | #include <string.h> |
28 | | #include <tchar.h> |
29 | | #include <winbase.h> |
30 | | #else |
31 | | #include <arpa/inet.h> |
32 | | #include <fcntl.h> |
33 | | #include <net/if.h> |
34 | | #include <netdb.h> |
35 | | #include <netinet/in.h> |
36 | | #include <netinet/ip.h> |
37 | | #include <netinet/tcp.h> |
38 | | #include <netinet/udp.h> |
39 | | #include <sys/socket.h> |
40 | | #include <unistd.h> |
41 | | |
42 | | #if defined(DARWIN) |
43 | | #include <dispatch/dispatch.h> |
44 | | #include "osEok.h" |
45 | | #elif !defined(TD_ASTRA) |
46 | | #include <sys/epoll.h> |
47 | | #endif |
48 | | #endif |
49 | | |
50 | | #ifdef TD_ASTRA |
51 | | #ifndef __BYTE_ORDER |
52 | | #define __BYTE_ORDER _BYTE_ORDER |
53 | | #endif |
54 | | |
55 | | #ifndef __BIG_ENDIAN |
56 | | #define __BIG_ENDIAN _BIG_ENDIAN |
57 | | #endif |
58 | | |
59 | | #ifndef __LITTLE_ENDIAN |
60 | | #define __LITTLE_ENDIAN _LITTLE_ENDIAN |
61 | | #endif |
62 | | #endif // TD_ASTRA |
63 | | |
64 | | #ifndef INVALID_SOCKET |
65 | 0 | #define INVALID_SOCKET -1 |
66 | | #endif |
67 | | |
68 | | typedef struct TdSocket { |
69 | | #if SOCKET_WITH_LOCK |
70 | | TdThreadRwlock rwlock; |
71 | | #endif |
72 | | int refId; |
73 | | SocketFd fd; |
74 | | } * TdSocketPtr, TdSocket; |
75 | | |
76 | | typedef struct TdSocketServer { |
77 | | #if SOCKET_WITH_LOCK |
78 | | TdThreadRwlock rwlock; |
79 | | #endif |
80 | | int refId; |
81 | | SocketFd fd; |
82 | | } * TdSocketServerPtr, TdSocketServer; |
83 | | |
84 | | typedef struct TdEpoll { |
85 | | #if SOCKET_WITH_LOCK |
86 | | TdThreadRwlock rwlock; |
87 | | #endif |
88 | | int refId; |
89 | | EpollFd fd; |
90 | | } * TdEpollPtr, TdEpoll; |
91 | | |
92 | 0 | int32_t taosCloseSocketNoCheck1(SocketFd fd) { |
93 | | #ifdef WINDOWS |
94 | | int ret = closesocket(fd); |
95 | | if (ret == SOCKET_ERROR) { |
96 | | int errorCode = WSAGetLastError(); |
97 | | return terrno = TAOS_SYSTEM_WINSOCKET_ERROR(errorCode); |
98 | | } |
99 | | return 0; |
100 | | #else |
101 | 0 | int32_t code = close(fd); |
102 | 0 | if (-1 == code) { |
103 | 0 | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
104 | 0 | return terrno; |
105 | 0 | } |
106 | 0 | return code; |
107 | 0 | #endif |
108 | 0 | } |
109 | | |
110 | 0 | int32_t taosCloseSocket(TdSocketPtr *ppSocket) { |
111 | 0 | int32_t code; |
112 | 0 | if (ppSocket == NULL || *ppSocket == NULL || (*ppSocket)->fd < 0) { |
113 | 0 | terrno = TSDB_CODE_INVALID_PARA; |
114 | 0 | return terrno; |
115 | 0 | } |
116 | 0 | code = taosCloseSocketNoCheck1((*ppSocket)->fd); |
117 | 0 | (*ppSocket)->fd = -1; |
118 | 0 | taosMemoryFree(*ppSocket); |
119 | |
|
120 | 0 | return code; |
121 | 0 | } |
122 | | |
123 | 0 | int32_t taosSetSockOpt(TdSocketPtr pSocket, int32_t level, int32_t optname, void *optval, int32_t optlen) { |
124 | 0 | if (pSocket == NULL || pSocket->fd < 0) { |
125 | 0 | terrno = TSDB_CODE_INVALID_PARA; |
126 | 0 | return terrno; |
127 | 0 | } |
128 | | |
129 | | #ifdef WINDOWS |
130 | | #ifdef TCP_KEEPCNT |
131 | | if (level == SOL_SOCKET && optname == TCP_KEEPCNT) { |
132 | | return 0; |
133 | | } |
134 | | #endif |
135 | | |
136 | | #ifdef TCP_KEEPIDLE |
137 | | if (level == SOL_TCP && optname == TCP_KEEPIDLE) { |
138 | | return 0; |
139 | | } |
140 | | #endif |
141 | | |
142 | | #ifdef TCP_KEEPINTVL |
143 | | if (level == SOL_TCP && optname == TCP_KEEPINTVL) { |
144 | | return 0; |
145 | | } |
146 | | #endif |
147 | | |
148 | | #ifdef TCP_KEEPCNT |
149 | | if (level == SOL_TCP && optname == TCP_KEEPCNT) { |
150 | | return 0; |
151 | | } |
152 | | #endif |
153 | | |
154 | | int ret = setsockopt(pSocket->fd, level, optname, optval, optlen); |
155 | | if (ret == SOCKET_ERROR) { |
156 | | int errorCode = WSAGetLastError(); |
157 | | return terrno = TAOS_SYSTEM_WINSOCKET_ERROR(errorCode); |
158 | | } |
159 | | #else |
160 | 0 | int32_t code = setsockopt(pSocket->fd, level, optname, optval, (int)optlen); |
161 | 0 | if (-1 == code) { |
162 | 0 | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
163 | 0 | return terrno; |
164 | 0 | } |
165 | 0 | return 0; |
166 | 0 | #endif |
167 | 0 | } |
168 | | |
169 | 0 | const char *taosInetNtop(struct in_addr ipInt, char *dstStr, int32_t len) { |
170 | 0 | const char *r = inet_ntop(AF_INET, &ipInt, dstStr, len); |
171 | 0 | if (NULL == r) { |
172 | 0 | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
173 | 0 | } |
174 | |
|
175 | 0 | return r; |
176 | 0 | } |
177 | | |
178 | | #ifndef SIGPIPE |
179 | | #define SIGPIPE EPIPE |
180 | | #endif |
181 | | |
182 | | #define TCP_CONN_TIMEOUT 3000 // conn timeout |
183 | | |
184 | 0 | int8_t taosValidIpAndPort(uint32_t ip, uint16_t port) { |
185 | 0 | struct sockaddr_in serverAdd; |
186 | 0 | SocketFd fd; |
187 | 0 | int32_t reuse; |
188 | 0 | int32_t code = 0; |
189 | | |
190 | | // printf("open tcp server socket:0x%x:%hu", ip, port); |
191 | |
|
192 | 0 | bzero((char *)&serverAdd, sizeof(serverAdd)); |
193 | 0 | serverAdd.sin_family = AF_INET; |
194 | | #ifdef WINDOWS |
195 | | serverAdd.sin_addr.s_addr = INADDR_ANY; |
196 | | #else |
197 | 0 | serverAdd.sin_addr.s_addr = ip; |
198 | 0 | #endif |
199 | 0 | serverAdd.sin_port = (uint16_t)htons(port); |
200 | |
|
201 | 0 | fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
202 | 0 | if (-1 == fd) { // exception |
203 | 0 | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
204 | 0 | return 0; |
205 | 0 | } |
206 | | |
207 | 0 | TdSocketPtr pSocket = (TdSocketPtr)taosMemoryMalloc(sizeof(TdSocket)); |
208 | 0 | if (pSocket == NULL) { |
209 | 0 | TAOS_SKIP_ERROR(taosCloseSocketNoCheck1(fd)); |
210 | 0 | return 0; |
211 | 0 | } |
212 | 0 | pSocket->refId = 0; |
213 | 0 | pSocket->fd = fd; |
214 | | |
215 | | /* set REUSEADDR option, so the portnumber can be re-used */ |
216 | 0 | reuse = 1; |
217 | 0 | if (taosSetSockOpt(pSocket, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(reuse)) < 0) { |
218 | 0 | TAOS_SKIP_ERROR(taosCloseSocket(&pSocket)); |
219 | 0 | return 0; |
220 | 0 | } |
221 | | |
222 | | /* bind socket to server address */ |
223 | 0 | if (-1 == bind(pSocket->fd, (struct sockaddr *)&serverAdd, sizeof(serverAdd))) { |
224 | 0 | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
225 | 0 | TAOS_SKIP_ERROR(taosCloseSocket(&pSocket)); |
226 | 0 | return 0; |
227 | 0 | } |
228 | | |
229 | 0 | TAOS_SKIP_ERROR(taosCloseSocket(&pSocket)); |
230 | |
|
231 | 0 | return 1; |
232 | 0 | } |
233 | | |
234 | 0 | int32_t taosBlockSIGPIPE() { |
235 | | #ifdef WINDOWS |
236 | | return 0; |
237 | | #else |
238 | 0 | sigset_t signal_mask; |
239 | 0 | (void)sigemptyset(&signal_mask); |
240 | 0 | (void)sigaddset(&signal_mask, SIGPIPE); |
241 | 0 | int32_t rc = pthread_sigmask(SIG_BLOCK, &signal_mask, NULL); |
242 | 0 | if (rc != 0) { |
243 | 0 | terrno = TAOS_SYSTEM_ERROR(rc); |
244 | 0 | return terrno; |
245 | 0 | } |
246 | | |
247 | 0 | return 0; |
248 | 0 | #endif |
249 | 0 | } |
250 | | |
251 | | // Classify a socket address as private/internal or public. |
252 | | // Recognizes RFC-1918, loopback, link-local, CGNAT, ULA, and other reserved ranges. |
253 | | // Used as the core validation primitive by taosIsPrivateUrl (DNS check) and by the |
254 | | // libcurl opensocket callback in tanalytics.c (connection-time check). Cross-platform: |
255 | | // works identically on POSIX and Winsock2 (sockaddr_in, sockaddr_in6, ntohl, memcmp are |
256 | | // standard). |
257 | | // Fail-closed: returns true (block) for NULL, unknown address families (AF_UNIX, |
258 | | // AF_UNSPEC, etc.), and all private/reserved ranges; returns false only for |
259 | | // recognized public IPv4/IPv6 addresses. |
260 | 0 | bool taosIsPrivateIp(const struct sockaddr *addr) { |
261 | 0 | if (addr == NULL) return true; |
262 | 0 | if (addr->sa_family == AF_INET) { |
263 | 0 | uint32_t ip = ntohl(((const struct sockaddr_in *)addr)->sin_addr.s_addr); |
264 | 0 | return (ip >> 24) == 0 || /* 0.0.0.0/8 this-network */ |
265 | 0 | (ip >> 24) == 10 || /* 10.0.0.0/8 RFC-1918 */ |
266 | 0 | (ip >> 24) == 127 || /* 127.0.0.0/8 loopback */ |
267 | 0 | (ip & 0xffc00000U) == 0x64400000U || /* 100.64.0.0/10 CGNAT */ |
268 | 0 | (ip & 0xffff0000U) == 0xa9fe0000U || /* 169.254.0.0/16 link-local */ |
269 | 0 | (ip & 0xfff00000U) == 0xac100000U || /* 172.16.0.0/12 RFC-1918 */ |
270 | 0 | (ip & 0xffffff00U) == 0xc0000000U || /* 192.0.0.0/24 IETF protocol */ |
271 | 0 | (ip & 0xffff0000U) == 0xc0a80000U || /* 192.168.0.0/16 RFC-1918 */ |
272 | 0 | (ip & 0xff000000U) == 0xe0000000U || /* 224.0.0.0/4 multicast */ |
273 | 0 | (ip & 0xf0000000U) == 0xf0000000U || /* 240.0.0.0/4 reserved */ |
274 | 0 | (ip & 0xfffe0000U) == 0xc6120000U || /* 198.18.0.0/15 benchmarking */ |
275 | 0 | ip == 0xffffffffU; /* 255.255.255.255 broadcast */ |
276 | |
|
277 | 0 | } |
278 | 0 | if (addr->sa_family == AF_INET6) { |
279 | 0 | const uint8_t *b = ((const struct sockaddr_in6 *)addr)->sin6_addr.s6_addr; |
280 | 0 | static const uint8_t v4mapped[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}; |
281 | | /* ::ffff:a.b.c.d IPv4-mapped address, apply the IPv4 rules to the low 32 bits */ |
282 | 0 | if (memcmp(b, v4mapped, sizeof(v4mapped)) == 0) { |
283 | 0 | struct sockaddr_in v4 = {0}; |
284 | 0 | v4.sin_family = AF_INET; |
285 | 0 | memcpy(&v4.sin_addr, b + 12, 4); |
286 | 0 | return taosIsPrivateIp((const struct sockaddr *)&v4); |
287 | 0 | } |
288 | | |
289 | 0 | static const uint8_t loopback6[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; |
290 | 0 | bool isUnspecified = true; |
291 | 0 | for (int32_t i = 0; i < 16; ++i) { |
292 | 0 | if (b[i] != 0) { |
293 | 0 | isUnspecified = false; |
294 | 0 | break; |
295 | 0 | } |
296 | 0 | } |
297 | |
|
298 | 0 | return isUnspecified || /* :: unspecified */ |
299 | 0 | memcmp(b, loopback6, sizeof(loopback6)) == 0 || /* ::1 loopback */ |
300 | 0 | (b[0] == 0xfe && (b[1] & 0xc0) == 0x80) || /* fe80::/10 link-local */ |
301 | 0 | (b[0] == 0xfe && (b[1] & 0xc0) == 0xc0) || /* fec0::/10 site-local */ |
302 | 0 | (b[0] == 0xff) || /* ff00::/8 multicast */ |
303 | 0 | (b[0] & 0xfe) == 0xfc; /* fc00::/7 ULA */ |
304 | |
|
305 | 0 | } |
306 | | // fail-closed: unknown address families (AF_UNIX, AF_UNSPEC, etc.) are treated as |
307 | | // internal/blocked rather than allowed through as "public". |
308 | 0 | return true; |
309 | 0 | } |
310 | | |
311 | | /* |
312 | | * Extract the host part from a url into host. |
313 | | * Handles the optional "userinfo@" prefix, the optional ":port" suffix and |
314 | | * bracketed IPv6 literals such as http://[fe80::1]:6041/path. |
315 | | * IPv6 zone IDs (e.g. http://[fe80::1%25eth0]:6041) are stripped before |
316 | | * returning so that getaddrinfo receives a bare address without the zone suffix. |
317 | | * Returns 0 on success, -1 when the authority component can not be parsed. |
318 | | */ |
319 | 0 | static int32_t taosParseUrlHost(const char *url, char *host, int32_t hostLen) { |
320 | 0 | if (url == NULL || host == NULL || hostLen <= 0) return -1; |
321 | | |
322 | 0 | const char *p = strstr(url, "://"); |
323 | 0 | if (p == NULL) return -1; |
324 | 0 | p += 3; |
325 | | |
326 | | /* the authority component ends at the first '/', '?' or '#' */ |
327 | 0 | const char *authEnd = p + strcspn(p, "/?#"); |
328 | | |
329 | | /* the host starts after the last '@' of the userinfo part */ |
330 | 0 | const char *hostStart = p; |
331 | 0 | for (const char *q = p; q < authEnd; ++q) { |
332 | 0 | if (*q == '@') hostStart = q + 1; |
333 | 0 | } |
334 | 0 | if (hostStart >= authEnd) return -1; |
335 | | |
336 | 0 | if (*hostStart == '[') { |
337 | | /* bracketed IPv6 literal, the port follows the closing ']' */ |
338 | 0 | const char *close = memchr(hostStart + 1, ']', (size_t)(authEnd - hostStart - 1)); |
339 | 0 | if (close == NULL) return -1; |
340 | | |
341 | 0 | int32_t len = (int32_t)(close - hostStart - 1); |
342 | 0 | if (len <= 0 || len >= hostLen) return -1; |
343 | | |
344 | 0 | memcpy(host, hostStart + 1, len); |
345 | 0 | host[len] = '\0'; |
346 | | |
347 | | /* strip IPv6 zone ID: "fe80::1%25eth0" -> "fe80::1" (RFC 6874 percent-encodes '%' as %25) */ |
348 | 0 | char *zone = strchr(host, '%'); |
349 | 0 | if (zone != NULL) *zone = '\0'; |
350 | 0 | return 0; |
351 | 0 | } |
352 | | |
353 | | /* plain host name or IPv4 literal, exclude the optional ":port" */ |
354 | 0 | int32_t len = (int32_t)(authEnd - hostStart); |
355 | 0 | const char *colon = memchr(hostStart, ':', (size_t)len); |
356 | 0 | if (colon != NULL) len = (int32_t)(colon - hostStart); |
357 | 0 | if (len <= 0 || len >= hostLen) return -1; |
358 | | |
359 | 0 | memcpy(host, hostStart, len); |
360 | 0 | host[len] = '\0'; |
361 | 0 | return 0; |
362 | 0 | } |
363 | | |
364 | | // Perform a fast, early rejection of URLs that resolve to private/internal addresses. |
365 | | // IMPORTANT: This is a front-gate check only, not a complete SSRF defense. |
366 | | // DNS rebinding (TOCTOU window) can occur between this check and actual connection: |
367 | | // - At check time, getaddrinfo(host) may resolve to a public IP, passing this check. |
368 | | // - At connection time (curl), the same hostname may resolve to a private IP. |
369 | | // Mitigation: all callers MUST validate the peer address at connection time. |
370 | | // For curl-based transports, install a CURLOPT_OPENSOCKETFUNCTION callback: it runs |
371 | | // before connect() and is handed the resolved peer address (see taosCurlOpensocketCb |
372 | | // in tanalytics.c), so rejecting there closes this window and no connection to the |
373 | | // internal address is ever made. CURLOPT_SOCKOPTFUNCTION can not be used for that: |
374 | | // it also runs before connect(), where getpeername() reports no peer yet. |
375 | 0 | bool taosIsPrivateUrl(const char *url) { |
376 | 0 | if (url == NULL) return true; |
377 | | |
378 | 0 | char host[256] = {0}; |
379 | 0 | if (taosParseUrlHost(url, host, (int32_t)sizeof(host)) != 0) return true; |
380 | | |
381 | | /* fail closed: only a resolved public address clears the verdict */ |
382 | 0 | bool blocked = true; |
383 | |
|
384 | | #ifdef WINDOWS |
385 | | // Initialize Winsock, getaddrinfo is a Winsock 2 API and fails with |
386 | | // WSANOTINITIALISED when no WSAStartup 2.x has been performed in this process |
387 | | // (taosWinSocketInit negotiates 1.1 only), which would block every URL. |
388 | | WSADATA wsaData; |
389 | | int iResult; |
390 | | iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); |
391 | | if (iResult != 0) { |
392 | | uError("taosIsPrivateUrl: WSAStartup failed: %d, treating '%s' as blocked", iResult, host); |
393 | | return true; |
394 | | } |
395 | | #endif |
396 | |
|
397 | 0 | struct addrinfo hints = {0}, *res = NULL, *rp; |
398 | 0 | hints.ai_family = AF_UNSPEC; |
399 | 0 | hints.ai_socktype = SOCK_STREAM; |
400 | 0 | int rc = getaddrinfo(host, NULL, &hints, &res); |
401 | 0 | if (rc != 0) { |
402 | 0 | uError("taosIsPrivateUrl: DNS resolution failed for '%s': %s; treating as blocked", host, gai_strerror(rc)); |
403 | 0 | goto _err; |
404 | 0 | } |
405 | | |
406 | 0 | blocked = false; |
407 | 0 | for (rp = res; rp != NULL; rp = rp->ai_next) { |
408 | 0 | if (taosIsPrivateIp(rp->ai_addr)) { |
409 | 0 | uDebug("taosIsPrivateUrl: '%s' resolves to a private/internal address, blocked", host); |
410 | 0 | blocked = true; |
411 | 0 | break; |
412 | 0 | } |
413 | 0 | } |
414 | 0 | freeaddrinfo(res); |
415 | |
|
416 | 0 | _err: |
417 | | #ifdef WINDOWS |
418 | | WSACleanup(); |
419 | | #endif |
420 | 0 | return blocked; |
421 | 0 | } |
422 | | |
423 | 0 | int32_t taosGetIpv4FromFqdn(const char *fqdn, uint32_t *ip) { |
424 | 0 | int32_t code = 0; |
425 | 0 | OS_PARAM_CHECK(fqdn); |
426 | 0 | OS_PARAM_CHECK(ip); |
427 | 0 | int64_t limitMs = 1000; |
428 | 0 | int64_t st = taosGetTimestampMs(), cost = 0; |
429 | | #ifdef WINDOWS |
430 | | // Initialize Winsock |
431 | | WSADATA wsaData; |
432 | | int iResult; |
433 | | iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); |
434 | | if (iResult != 0) { |
435 | | code = TAOS_SYSTEM_WINSOCKET_ERROR(WSAGetLastError()); |
436 | | goto _err; |
437 | | } |
438 | | #endif |
439 | |
|
440 | 0 | #if defined(LINUX) |
441 | 0 | struct addrinfo hints = {0}; |
442 | 0 | hints.ai_family = AF_INET; |
443 | 0 | hints.ai_socktype = SOCK_STREAM; |
444 | |
|
445 | 0 | struct addrinfo *result = NULL; |
446 | 0 | bool inRetry = false; |
447 | |
|
448 | 0 | while (true) { |
449 | 0 | int32_t ret = getaddrinfo(fqdn, NULL, &hints, &result); |
450 | 0 | if (ret) { |
451 | 0 | if (EAI_AGAIN == ret && !inRetry) { |
452 | 0 | inRetry = true; |
453 | 0 | continue; |
454 | 0 | } else if (EAI_SYSTEM == ret) { |
455 | 0 | code = TAOS_SYSTEM_ERROR(ERRNO); |
456 | 0 | goto _err; |
457 | 0 | } |
458 | | |
459 | 0 | code = TAOS_SYSTEM_ERROR(ERRNO); |
460 | 0 | goto _err; |
461 | 0 | } |
462 | | |
463 | 0 | struct sockaddr *sa = result->ai_addr; |
464 | 0 | struct sockaddr_in *si = (struct sockaddr_in *)sa; |
465 | 0 | struct in_addr ia = si->sin_addr; |
466 | |
|
467 | 0 | *ip = ia.s_addr; |
468 | |
|
469 | 0 | freeaddrinfo(result); |
470 | 0 | goto _err; |
471 | 0 | } |
472 | | #else |
473 | | struct addrinfo hints = {0}; |
474 | | hints.ai_family = AF_INET; |
475 | | hints.ai_socktype = SOCK_STREAM; |
476 | | |
477 | | struct addrinfo *result = NULL; |
478 | | |
479 | | int32_t ret = getaddrinfo(fqdn, NULL, &hints, &result); |
480 | | if (result) { |
481 | | struct sockaddr *sa = result->ai_addr; |
482 | | struct sockaddr_in *si = (struct sockaddr_in *)sa; |
483 | | struct in_addr ia = si->sin_addr; |
484 | | *ip = ia.s_addr; |
485 | | freeaddrinfo(result); |
486 | | goto _err; |
487 | | } else { |
488 | | #ifdef EAI_SYSTEM |
489 | | if (ret == EAI_SYSTEM) { |
490 | | // printf("failed to get the ip address, fqdn:%s, errno:%d, since:%s", fqdn, ERRNO, strerror(ERRNO)); |
491 | | } else { |
492 | | // printf("failed to get the ip address, fqdn:%s, ret:%d, since:%s", fqdn, ret, gai_strerror(ret)); |
493 | | } |
494 | | #else |
495 | | // printf("failed to get the ip address, fqdn:%s, ret:%d, since:%s", fqdn, ret, gai_strerror(ret)); |
496 | | #endif |
497 | | |
498 | | *ip = 0xFFFFFFFF; |
499 | | code = TSDB_CODE_RPC_FQDN_ERROR; |
500 | | goto _err; |
501 | | } |
502 | | #endif |
503 | 0 | _err: |
504 | 0 | cost = taosGetTimestampMs() - st; |
505 | 0 | if (cost >= limitMs) { |
506 | 0 | uWarn("get ip from fqdn:%s, cost:%" PRId64 "ms", fqdn, cost); |
507 | 0 | } |
508 | 0 | return code; |
509 | 0 | } |
510 | 0 | int32_t taosGetIpv6FromFqdn(const char *fqdn, SIpAddr *pAddr) { |
511 | 0 | int32_t code = 0; |
512 | 0 | OS_PARAM_CHECK(fqdn); |
513 | 0 | int64_t limitMs = 1000; |
514 | 0 | int64_t st = taosGetTimestampMs(), cost = 0; |
515 | | #ifdef WINDOWS |
516 | | // Initialize Winsock |
517 | | WSADATA wsaData; |
518 | | int iResult; |
519 | | iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); |
520 | | if (iResult != 0) { |
521 | | code = TAOS_SYSTEM_WINSOCKET_ERROR(WSAGetLastError()); |
522 | | goto _err; |
523 | | } |
524 | | #endif |
525 | |
|
526 | 0 | #if defined(LINUX) |
527 | 0 | struct addrinfo hints = {0}; |
528 | 0 | hints.ai_family = AF_UNSPEC; |
529 | 0 | hints.ai_socktype = SOCK_STREAM; |
530 | |
|
531 | 0 | struct addrinfo *result = NULL; |
532 | 0 | bool inRetry = false; |
533 | |
|
534 | 0 | char ipStr[INET6_ADDRSTRLEN]; |
535 | 0 | while (true) { |
536 | 0 | int32_t ret = getaddrinfo(fqdn, NULL, &hints, &result); |
537 | 0 | if (ret) { |
538 | 0 | if (EAI_AGAIN == ret && !inRetry) { |
539 | 0 | inRetry = true; |
540 | 0 | continue; |
541 | 0 | } else if (EAI_SYSTEM == ret) { |
542 | 0 | code = TAOS_SYSTEM_ERROR(ERRNO); |
543 | 0 | goto _err; |
544 | 0 | } |
545 | | |
546 | 0 | code = TAOS_SYSTEM_ERROR(ERRNO); |
547 | 0 | goto _err; |
548 | 0 | } |
549 | | |
550 | 0 | if (result->ai_family == AF_INET6) { |
551 | 0 | struct sockaddr_in6 *p6 = (struct sockaddr_in6 *)result->ai_addr; |
552 | |
|
553 | 0 | const char *t = inet_ntop(AF_INET6, &p6->sin6_addr, pAddr->ipv6, sizeof(pAddr->ipv6)); |
554 | 0 | TAOS_UNUSED(t); |
555 | |
|
556 | 0 | pAddr->type = 1; |
557 | 0 | } else if (result->ai_family == AF_INET) { |
558 | 0 | struct sockaddr_in *p4 = (struct sockaddr_in *)result->ai_addr; |
559 | |
|
560 | 0 | const char *t = inet_ntop(AF_INET, &p4->sin_addr, pAddr->ipv4, sizeof(pAddr->ipv4)); |
561 | 0 | TAOS_UNUSED(t); |
562 | |
|
563 | 0 | pAddr->type = 0; |
564 | 0 | } else { |
565 | 0 | code = TSDB_CODE_RPC_FQDN_ERROR; |
566 | 0 | goto _err; |
567 | 0 | } |
568 | | |
569 | 0 | freeaddrinfo(result); |
570 | 0 | goto _err; |
571 | 0 | } |
572 | | #else |
573 | | struct addrinfo hints = {0}; |
574 | | hints.ai_family = AF_UNSPEC; |
575 | | hints.ai_socktype = SOCK_STREAM; |
576 | | |
577 | | struct addrinfo *result = NULL; |
578 | | |
579 | | int32_t ret = getaddrinfo(fqdn, NULL, &hints, &result); |
580 | | if (result) { |
581 | | if (result->ai_family == AF_INET6) { |
582 | | struct sockaddr_in6 *p6 = (struct sockaddr_in6 *)result->ai_addr; |
583 | | inet_ntop(AF_INET6, &p6->sin6_addr, pAddr->ipv6, sizeof(pAddr->ipv6)); |
584 | | pAddr->type = 1; |
585 | | } else if (result->ai_family == AF_INET) { |
586 | | struct sockaddr_in *p4 = (struct sockaddr_in *)result->ai_addr; |
587 | | inet_ntop(AF_INET, &p4->sin_addr, pAddr->ipv4, sizeof(pAddr->ipv4)); |
588 | | pAddr->type = 0; |
589 | | } else { |
590 | | code = TSDB_CODE_RPC_FQDN_ERROR; |
591 | | goto _err; |
592 | | } |
593 | | freeaddrinfo(result); |
594 | | goto _err; |
595 | | } else { |
596 | | #ifdef EAI_SYSTEM |
597 | | if (ret == EAI_SYSTEM) { |
598 | | // printf("failed to get the ip address, fqdn:%s, errno:%d, since:%s", fqdn, ERRNO, strerror(ERRNO)); |
599 | | } else { |
600 | | // printf("failed to get the ip address, fqdn:%s, ret:%d, since:%s", fqdn, ret, gai_strerror(ret)); |
601 | | } |
602 | | #else |
603 | | // printf("failed to get the ip address, fqdn:%s, ret:%d, since:%s", fqdn, ret, gai_strerror(ret)); |
604 | | #endif |
605 | | } |
606 | | #endif |
607 | 0 | _err: |
608 | 0 | cost = taosGetTimestampMs() - st; |
609 | 0 | if (cost >= limitMs) { |
610 | 0 | uWarn("get ip from fqdn:%s, cost:%" PRId64 "ms", fqdn, cost); |
611 | 0 | } |
612 | |
|
613 | | #ifdef WINDOWS |
614 | | WSACleanup(); |
615 | | #endif |
616 | 0 | return code; |
617 | 0 | } |
618 | | |
619 | 0 | int32_t taosGetIp4FromFqdn(const char *fqdn, SIpAddr *pAddr) { |
620 | 0 | int32_t code = 0; |
621 | 0 | OS_PARAM_CHECK(fqdn); |
622 | 0 | int64_t limitMs = 1000; |
623 | 0 | int64_t st = taosGetTimestampMs(), cost = 0; |
624 | | #ifdef WINDOWS |
625 | | // Initialize Winsock |
626 | | WSADATA wsaData; |
627 | | int iResult; |
628 | | iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); |
629 | | if (iResult != 0) { |
630 | | code = TAOS_SYSTEM_WINSOCKET_ERROR(WSAGetLastError()); |
631 | | goto _err; |
632 | | } |
633 | | #endif |
634 | |
|
635 | 0 | #if defined(LINUX) |
636 | 0 | struct addrinfo hints = {0}; |
637 | 0 | hints.ai_family = AF_INET; |
638 | 0 | hints.ai_socktype = SOCK_STREAM; |
639 | |
|
640 | 0 | struct addrinfo *result = NULL; |
641 | 0 | bool inRetry = false; |
642 | |
|
643 | 0 | char ipStr[INET6_ADDRSTRLEN]; |
644 | 0 | while (true) { |
645 | 0 | int32_t ret = getaddrinfo(fqdn, NULL, &hints, &result); |
646 | 0 | if (ret) { |
647 | 0 | if (EAI_AGAIN == ret && !inRetry) { |
648 | 0 | inRetry = true; |
649 | 0 | continue; |
650 | 0 | } else if (EAI_SYSTEM == ret) { |
651 | 0 | code = TAOS_SYSTEM_ERROR(ERRNO); |
652 | 0 | goto _err; |
653 | 0 | } |
654 | | |
655 | 0 | code = TAOS_SYSTEM_ERROR(ERRNO); |
656 | 0 | goto _err; |
657 | 0 | } |
658 | | |
659 | 0 | if (result->ai_family == AF_INET) { |
660 | 0 | struct sockaddr_in *p4 = (struct sockaddr_in *)result->ai_addr; |
661 | 0 | const char *t = inet_ntop(AF_INET, &p4->sin_addr, pAddr->ipv4, sizeof(pAddr->ipv4)); |
662 | 0 | TAOS_UNUSED(t); |
663 | |
|
664 | 0 | pAddr->type = 0; |
665 | 0 | } else { |
666 | 0 | code = TSDB_CODE_RPC_FQDN_ERROR; |
667 | 0 | goto _err; |
668 | 0 | } |
669 | | |
670 | 0 | freeaddrinfo(result); |
671 | 0 | goto _err; |
672 | 0 | } |
673 | | #else |
674 | | struct addrinfo hints = {0}; |
675 | | hints.ai_family = AF_INET; |
676 | | hints.ai_socktype = SOCK_STREAM; |
677 | | |
678 | | struct addrinfo *result = NULL; |
679 | | |
680 | | int32_t ret = getaddrinfo(fqdn, NULL, &hints, &result); |
681 | | if (result) { |
682 | | struct sockaddr_in *p4 = (struct sockaddr_in *)result->ai_addr; |
683 | | inet_ntop(AF_INET, &p4->sin_addr, pAddr->ipv4, sizeof(pAddr->ipv4)); |
684 | | freeaddrinfo(result); |
685 | | pAddr->type = 0; |
686 | | goto _err; |
687 | | } else { |
688 | | #ifdef EAI_SYSTEM |
689 | | if (ret == EAI_SYSTEM) { |
690 | | // printf("failed to get the ip address, fqdn:%s, errno:%d, since:%s", fqdn, ERRNO, strerror(ERRNO)); |
691 | | } else { |
692 | | // printf("failed to get the ip address, fqdn:%s, ret:%d, since:%s", fqdn, ret, gai_strerror(ret)); |
693 | | } |
694 | | code = TSDB_CODE_RPC_FQDN_ERROR; |
695 | | #else |
696 | | // printf("failed to get the ip address, fqdn:%s, ret:%d, since:%s", fqdn, ret, gai_strerror(ret)); |
697 | | #endif |
698 | | } |
699 | | #endif |
700 | | |
701 | 0 | _err: |
702 | 0 | cost = taosGetTimestampMs() - st; |
703 | 0 | if (cost >= limitMs) { |
704 | 0 | uWarn("get ip from fqdn:%s, cost:%" PRId64 "ms", fqdn, cost); |
705 | 0 | } |
706 | | #ifdef WINDOWS |
707 | | WSACleanup(); |
708 | | #endif |
709 | 0 | return code; |
710 | 0 | } |
711 | 0 | int32_t taosGetIpFromFqdn(int8_t enableIpv6, const char *fqdn, SIpAddr *addr) { |
712 | 0 | int32_t code = 0; |
713 | 0 | if (enableIpv6) { |
714 | 0 | code = taosGetIpv6FromFqdn(fqdn, addr); |
715 | 0 | } else { |
716 | 0 | code = taosGetIp4FromFqdn(fqdn, addr); |
717 | 0 | } |
718 | 0 | return code; |
719 | 0 | } |
720 | | |
721 | 0 | int8_t taosIpAddrIsEqual(SIpAddr *ip1, SIpAddr *ip2) { |
722 | 0 | if (ip1->type == ip2->type) { |
723 | 0 | if (ip1->type == 0) { |
724 | 0 | return (strcmp(ip1->ipv4, ip2->ipv4) == 0 ? 1 : 0); |
725 | 0 | } else { |
726 | 0 | return (strcmp(ip1->ipv6, ip2->ipv6) == 0 ? 1 : 0); |
727 | 0 | } |
728 | 0 | } |
729 | 0 | return 0; |
730 | 0 | } |
731 | | |
732 | 0 | int32_t taosGetFqdnWithTimeCost(char *fqdn, int64_t *timeoutMs) { |
733 | 0 | int32_t code = 0; |
734 | 0 | int64_t startMs = taosGetTimestampMs(); |
735 | |
|
736 | 0 | code = taosGetFqdn(fqdn); |
737 | 0 | int64_t cost = taosGetTimestampMs() - startMs; |
738 | 0 | if (timeoutMs != NULL) { |
739 | 0 | *timeoutMs = cost; |
740 | 0 | } |
741 | 0 | return code; |
742 | 0 | } |
743 | 0 | int32_t taosGetFqdn(char *fqdn) { |
744 | 0 | OS_PARAM_CHECK(fqdn); |
745 | | #ifdef WINDOWS |
746 | | // Initialize Winsock |
747 | | WSADATA wsaData; |
748 | | int iResult; |
749 | | iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); |
750 | | if (iResult != 0) { |
751 | | // printf("WSAStartup failed: %d\n", iResult); |
752 | | return TAOS_SYSTEM_WINSOCKET_ERROR(WSAGetLastError()); |
753 | | } |
754 | | #endif |
755 | 0 | char hostname[1024]; |
756 | 0 | hostname[1023] = '\0'; |
757 | 0 | int32_t code = taosGetlocalhostname(hostname, 1023); |
758 | 0 | if (code) { |
759 | 0 | return code; |
760 | 0 | } |
761 | | |
762 | | #ifdef __APPLE__ |
763 | | // on macosx, hostname -f has the form of xxx.local |
764 | | // which will block getaddrinfo for a few seconds if AI_CANONNAME is set |
765 | | // thus, we choose AF_INET (ipv4 for the moment) to make getaddrinfo return |
766 | | // immediately |
767 | | // hints.ai_family = AF_INET; |
768 | | tstrncpy(fqdn, hostname, TD_FQDN_LEN); |
769 | | tstrncpy(fqdn + strlen(hostname), ".local", TD_FQDN_LEN - strlen(hostname)); |
770 | | #else // linux |
771 | | |
772 | 0 | #endif // linux |
773 | | #if defined(TD_ASTRA) |
774 | | tstrncpy(fqdn, hostname, TD_FQDN_LEN); |
775 | | #elif defined(LINUX) |
776 | | |
777 | 0 | struct addrinfo hints = {0}; |
778 | 0 | struct addrinfo *result = NULL; |
779 | 0 | hints.ai_flags = AI_CANONNAME; |
780 | 0 | bool inRetry = false; |
781 | |
|
782 | 0 | while (true) { |
783 | 0 | int32_t ret = getaddrinfo(hostname, NULL, &hints, &result); |
784 | 0 | if (ret) { |
785 | 0 | if (EAI_AGAIN == ret && !inRetry) { |
786 | 0 | inRetry = true; |
787 | 0 | continue; |
788 | 0 | } else if (EAI_SYSTEM == ret) { |
789 | 0 | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
790 | 0 | return terrno; |
791 | 0 | } |
792 | | |
793 | 0 | terrno = TAOS_SYSTEM_ERROR(ret); |
794 | 0 | return terrno; |
795 | 0 | } |
796 | | |
797 | 0 | break; |
798 | 0 | } |
799 | | |
800 | 0 | tstrncpy(fqdn, result->ai_canonname, TD_FQDN_LEN); |
801 | |
|
802 | 0 | freeaddrinfo(result); |
803 | |
|
804 | | #elif WINDOWS |
805 | | struct addrinfo hints = {0}; |
806 | | struct addrinfo *result = NULL; |
807 | | hints.ai_flags = AI_CANONNAME; |
808 | | |
809 | | int32_t ret = getaddrinfo(hostname, NULL, &hints, &result); |
810 | | if (!result) { |
811 | | // fprintf(stderr, "failed to get fqdn, code:%d, hostname:%s, reason:%s\n", ret, hostname, gai_strerror(ret)); |
812 | | return TAOS_SYSTEM_WINSOCKET_ERROR(WSAGetLastError()); |
813 | | } |
814 | | tstrncpy(fqdn, result->ai_canonname, TD_FQDN_LEN); |
815 | | freeaddrinfo(result); |
816 | | |
817 | | #endif |
818 | |
|
819 | 0 | return 0; |
820 | 0 | } |
821 | | |
822 | 0 | void taosInetNtoa(char *ipstr, uint32_t ip) { |
823 | 0 | if (ipstr == NULL) { |
824 | 0 | return; |
825 | 0 | } |
826 | 0 | unsigned char *bytes = (unsigned char *)&ip; |
827 | 0 | (void)snprintf(ipstr, TD_IP_LEN, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3]); |
828 | 0 | } |
829 | | |
830 | 0 | uint32_t taosInetAddr(const char *ipstr) { |
831 | 0 | if (ipstr == NULL) { |
832 | 0 | return 0; |
833 | 0 | } |
834 | 0 | return inet_addr(ipstr); |
835 | 0 | } |
836 | | |
837 | 0 | int32_t taosIgnSIGPIPE() { |
838 | 0 | sighandler_t h = signal(SIGPIPE, SIG_IGN); |
839 | 0 | if (SIG_ERR == h) { |
840 | 0 | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
841 | 0 | return terrno; |
842 | 0 | } |
843 | 0 | return 0; |
844 | 0 | } |
845 | | |
846 | | /* |
847 | | * Set TCP connection timeout per-socket level. |
848 | | * ref [https://github.com/libuv/help/issues/54] |
849 | | */ |
850 | 0 | int32_t taosCreateSocketWithTimeout(uint32_t timeout, int8_t t) { |
851 | | #if defined(WINDOWS) |
852 | | SOCKET fd; |
853 | | #else |
854 | 0 | int fd; |
855 | 0 | #endif |
856 | 0 | int32_t type = t == 0 ? AF_INET : AF_INET6; |
857 | |
|
858 | 0 | if ((fd = socket(type, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { |
859 | 0 | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
860 | 0 | return terrno; |
861 | 0 | } |
862 | | |
863 | | #if defined(WINDOWS) |
864 | | if (0 != setsockopt(fd, IPPROTO_TCP, TCP_MAXRT, (char *)&timeout, sizeof(timeout))) { |
865 | | taosCloseSocketNoCheck1(fd); |
866 | | return TAOS_SYSTEM_WINSOCKET_ERROR(WSAGetLastError()); |
867 | | } |
868 | | #elif defined(_TD_DARWIN_64) |
869 | | // invalid config |
870 | | // uint32_t conn_timeout_ms = timeout * 1000; |
871 | | // if (0 != setsockopt(fd, IPPROTO_TCP, TCP_CONNECTIONTIMEOUT, (char *)&conn_timeout_ms, sizeof(conn_timeout_ms))) { |
872 | | // taosCloseSocketNoCheck1(fd); |
873 | | // return -1; |
874 | | //} |
875 | | #elif defined(TD_ASTRA) // TD_ASTRA_TODO |
876 | | uint32_t conn_timeout_ms = timeout; |
877 | | if (0 != setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&conn_timeout_ms, sizeof(conn_timeout_ms)) || |
878 | | 0 != setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&conn_timeout_ms, sizeof(conn_timeout_ms))) { |
879 | | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
880 | | TAOS_SKIP_ERROR(taosCloseSocketNoCheck1(fd)); |
881 | | return terrno; |
882 | | } |
883 | | #else // Linux like systems |
884 | 0 | uint32_t conn_timeout_ms = timeout; |
885 | 0 | if (-1 == setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, (char *)&conn_timeout_ms, sizeof(conn_timeout_ms))) { |
886 | 0 | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
887 | 0 | TAOS_SKIP_ERROR(taosCloseSocketNoCheck1(fd)); |
888 | 0 | return terrno; |
889 | 0 | } |
890 | 0 | #endif |
891 | | |
892 | 0 | return (int)fd; |
893 | 0 | } |
894 | | |
895 | 0 | int32_t taosWinSocketInit() { |
896 | | #ifdef WINDOWS |
897 | | static int8_t flag = 0; |
898 | | if (atomic_val_compare_exchange_8(&flag, 0, 1) == 0) { |
899 | | WORD wVersionRequested; |
900 | | WSADATA wsaData; |
901 | | wVersionRequested = MAKEWORD(1, 1); |
902 | | if (WSAStartup(wVersionRequested, &wsaData) != 0) { |
903 | | atomic_store_8(&flag, 0); |
904 | | int errorCode = WSAGetLastError(); |
905 | | return terrno = TAOS_SYSTEM_WINSOCKET_ERROR(errorCode); |
906 | | } |
907 | | } |
908 | | return 0; |
909 | | #else |
910 | 0 | #endif |
911 | 0 | return 0; |
912 | 0 | } |
913 | | |
914 | 0 | uint64_t taosHton64(uint64_t val) { |
915 | | #if defined(WINDOWS) || defined(DARWIN) |
916 | | return ((val & 0x00000000000000ff) << 7 * 8) | ((val & 0x000000000000ff00) << 5 * 8) | |
917 | | ((val & 0x0000000000ff0000) << 3 * 8) | ((val & 0x00000000ff000000) << 1 * 8) | |
918 | | ((val & 0x000000ff00000000) >> 1 * 8) | ((val & 0x0000ff0000000000) >> 3 * 8) | |
919 | | ((val & 0x00ff000000000000) >> 5 * 8) | ((val & 0xff00000000000000) >> 7 * 8); |
920 | | #else |
921 | 0 | if (__BYTE_ORDER == __LITTLE_ENDIAN) { |
922 | 0 | return (((uint64_t)htonl((int)((val << 32) >> 32))) << 32) | (unsigned int)htonl((int)(val >> 32)); |
923 | 0 | } else if (__BYTE_ORDER == __BIG_ENDIAN) { |
924 | 0 | return val; |
925 | 0 | } |
926 | 0 | #endif |
927 | 0 | } |
928 | | |
929 | 0 | uint64_t taosNtoh64(uint64_t val) { |
930 | | #if defined(WINDOWS) || defined(DARWIN) |
931 | | return taosHton64(val); |
932 | | #else |
933 | 0 | if (__BYTE_ORDER == __LITTLE_ENDIAN) { |
934 | 0 | return (((uint64_t)htonl((int)((val << 32) >> 32))) << 32) | (unsigned int)htonl((int)(val >> 32)); |
935 | 0 | } else if (__BYTE_ORDER == __BIG_ENDIAN) { |
936 | 0 | return val; |
937 | 0 | } |
938 | 0 | #endif |
939 | 0 | } |
940 | | |
941 | 0 | int32_t taosSetSockOpt2(int32_t fd) { |
942 | | #if defined(WINDOWS) |
943 | | #ifndef SIO_TCP_SET_ACK_FREQUENCY |
944 | | #define SIO_TCP_SET_ACK_FREQUENCY _WSAIOW(IOC_VENDOR, 23) |
945 | | #endif |
946 | | int freq = 1; |
947 | | DWORD bytesReturned = 0; |
948 | | if (WSAIoctl((SOCKET)(uintptr_t)fd, SIO_TCP_SET_ACK_FREQUENCY, &freq, sizeof(freq), NULL, 0, &bytesReturned, NULL, |
949 | | NULL) == SOCKET_ERROR) { |
950 | | terrno = TAOS_SYSTEM_ERROR(WSAGetLastError()); |
951 | | return terrno; |
952 | | } |
953 | | return 0; |
954 | | #elif defined(DARWIN) || defined(TD_ASTRA) |
955 | | return 0; |
956 | | #else |
957 | 0 | int32_t ret = setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, (int[]){1}, sizeof(int)); |
958 | 0 | if (ret < 0) { |
959 | 0 | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
960 | 0 | return terrno; |
961 | 0 | } else { |
962 | 0 | return 0; |
963 | 0 | } |
964 | 0 | #endif |
965 | 0 | } |
966 | | |
967 | 0 | int32_t taosSetTcpKeepalive(int fd, int keepalive_sec) { |
968 | | #if defined(WINDOWS) |
969 | | #ifndef TD_TCP_KEEPALIVE_DEFINED |
970 | | #define TD_TCP_KEEPALIVE_DEFINED |
971 | | #ifndef SIO_KEEPALIVE_VALS |
972 | | #define SIO_KEEPALIVE_VALS 0x98000004 |
973 | | #endif |
974 | | struct td_tcp_keepalive { |
975 | | uint32_t onoff; |
976 | | uint32_t keepalivetime; |
977 | | uint32_t keepaliveinterval; |
978 | | }; |
979 | | #endif |
980 | | struct td_tcp_keepalive vals; |
981 | | vals.onoff = 1; |
982 | | vals.keepalivetime = keepalive_sec * 1000; |
983 | | vals.keepaliveinterval = 1000; |
984 | | DWORD bytesReturned = 0; |
985 | | if (WSAIoctl((SOCKET)(uintptr_t)fd, SIO_KEEPALIVE_VALS, &vals, sizeof(vals), NULL, 0, &bytesReturned, NULL, |
986 | | NULL) == SOCKET_ERROR) { |
987 | | terrno = TAOS_SYSTEM_ERROR(WSAGetLastError()); |
988 | | return terrno; |
989 | | } |
990 | | return 0; |
991 | | #elif defined(DARWIN) |
992 | | // macOS: setsockopt-based keepalive |
993 | | int32_t on = 1; |
994 | | if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) { |
995 | | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
996 | | return terrno; |
997 | | } |
998 | | int32_t idle = keepalive_sec; |
999 | | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &idle, sizeof(idle)) < 0) { |
1000 | | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
1001 | | return terrno; |
1002 | | } |
1003 | | return 0; |
1004 | | #else |
1005 | | // Linux: setsockopt-based keepalive |
1006 | 0 | int32_t on = 1; |
1007 | 0 | if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) { |
1008 | 0 | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
1009 | 0 | return terrno; |
1010 | 0 | } |
1011 | 0 | int32_t idle = keepalive_sec; |
1012 | 0 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)) < 0) { |
1013 | 0 | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
1014 | 0 | return terrno; |
1015 | 0 | } |
1016 | 0 | int32_t intvl = 1; |
1017 | 0 | if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)) < 0) { |
1018 | 0 | terrno = TAOS_SYSTEM_ERROR(ERRNO); |
1019 | 0 | return terrno; |
1020 | 0 | } |
1021 | 0 | return 0; |
1022 | 0 | #endif |
1023 | 0 | } |
1024 | | |
1025 | 0 | int32_t taosValidFqdn(int8_t enableIpv6, char *fqdn) { |
1026 | 0 | int32_t code = 0; |
1027 | 0 | SIpAddr addr = {.type = -1}; |
1028 | 0 | code = taosGetIpFromFqdn(enableIpv6, fqdn, &addr); |
1029 | 0 | if (code != 0) { |
1030 | 0 | return code; |
1031 | 0 | } |
1032 | | |
1033 | 0 | if (enableIpv6) { |
1034 | | // when ipv6 is enabled, allow both IPv4 and IPv6 addresses |
1035 | | // taosGetIpFromFqdn will use AF_UNSPEC and return first available address |
1036 | | // both IPv4 (type=0) and IPv6 (type=1) are acceptable |
1037 | 0 | if (addr.type != 0 && addr.type != 1) { |
1038 | 0 | return TSDB_CODE_RPC_FQDN_ERROR; |
1039 | 0 | } |
1040 | 0 | return code; // validation always succeeds when ipv6 is enabled (dual-stack mode) |
1041 | 0 | } else { |
1042 | 0 | if (addr.type == 1) return TSDB_CODE_RPC_FQDN_ERROR; |
1043 | 0 | } |
1044 | | |
1045 | 0 | return code; |
1046 | 0 | } |