Coverage Report

Created: 2026-10-22 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}