* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
*
* CM is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
*cm_ip.cpp
*
*
* IDENTIFICATION
* src/cm_common/cm_ip.cpp
*
* -------------------------------------------------------------------------
*/
#include <cstring>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/if.h>
#include <netdb.h>
#ifndef WIN32
#include <netdb.h>
#include <net/if.h>
#else
#include <ws2tcpip.h>
#endif
#include "cm_msg_common.h"
#include "cm_misc_base.h"
#include "cm_elog.h"
#include "cm_text.h"
#include "cm_ip.h"
#ifdef __cplusplus
extern "C" {
#endif
static const char *const IPV4_LOCAL_HOST_ARRAY[] = {"localhost", "127.0.0.1"};
static const char *const IPV6_LOCAL_HOST_ARRAY[] = {"::1"};
int32 GetIpVersion(const char *ip_address)
{
if (ip_address == NULL) {
write_runlog(ERROR, "[%s] ip is null.\n", __FUNCTION__);
return -1;
}
struct in_addr ipv4_addr;
struct in6_addr ipv6_addr;
if (inet_pton(AF_INET, ip_address, &ipv4_addr) == 1) {
return AF_INET;
}
if (inet_pton(AF_INET6, ip_address, &ipv6_addr) == 1) {
return AF_INET6;
}
write_runlog(ERROR, "[%s] invalid, get ip %s version faild.\n", __FUNCTION__, ip_address);
return -1;
}
char *GetPingStr(int32 family)
{
if (family == AF_INET6) {
return "ping6";
} else {
return "ping";
}
}
status_t BuildSockAddrIpV4(const char *host, int port, sock_addr_t *sockAddr)
{
struct sockaddr_in *in4 = NULL;
sockAddr->salen = (socklen_t)sizeof(struct sockaddr_in);
in4 = SOCKADDR_IN4(sockAddr);
errno_t rc = memset_s(in4, sizeof(struct sockaddr_in), 0, sizeof(struct sockaddr_in));
securec_check_errno(rc, (void)rc);
in4->sin_family = AF_INET;
in4->sin_port = htons((uint16)port);
#ifndef WIN32
in4->sin_addr.s_addr = inet_addr(host);
if (in4->sin_addr.s_addr == (in_addr_t)(-1) || (inet_pton(AF_INET, host, &in4->
sin_addr.s_addr) != 1)) {
#else
if (InetPton(AF_INET, host, &in4->sin_addr.s_addr) != 1) {
#endif
write_runlog(ERROR, "[%s] ip(%s) is invalid.\n", __FUNCTION__, host);
return CM_ERROR;
}
return CM_SUCCESS;
}
char *IpV6LocalLink(const char *host, char *ip, uint32 ipLen)
{
errno_t errcode;
size_t hostLen;
int i = 0;
while (host[i] && host[i] != '%') {
i++;
}
if (host[i] == '\0') {
return NULL;
} else {
hostLen = (uint32)strlen(host);
errcode = strncpy_s(ip, (size_t)ipLen, host, (size_t)hostLen);
if (SECUREC_UNLIKELY(errcode != EOK)) {
write_runlog(ERROR, "[%s] strncpy_s failed, error code %d.\n", __FUNCTION__, errcode);
return NULL;
}
ip[i] = '\0';
return ip + i + 1;
}
}
status_t BuildSockAddrIpV6(const char *host, int port, sock_addr_t *sockAddr)
{
struct sockaddr_in6 *in6 = NULL;
#ifndef WIN32
char ip[CM_IP_LENGTH];
char *scope = NULL;
#endif
sockAddr->salen = (socklen_t)sizeof(struct sockaddr_in6);
in6 = SOCKADDR_IN6(sockAddr);
errno_t rc = memset_s(in6, sizeof(struct sockaddr_in6), 0, sizeof(struct sockaddr_in6));
securec_check_errno(rc, (void)rc);
in6->sin6_family = AF_INET6;
in6->sin6_port = htons((uint16)port);
#ifndef WIN32
scope = IpV6LocalLink(host, ip, CM_IP_LENGTH);
if (scope != NULL) {
in6->sin6_scope_id = if_nametoindex(scope);
if (in6->sin6_scope_id == 0) {
write_runlog(ERROR, "[%s] invalid local link (%s).\n", __FUNCTION__, scope);
return CM_ERROR;
}
host = ip;
}
if (inet_pton(AF_INET6, host, &in6->sin6_addr) != 1) {
#else
if (InetPton(AF_INET6, host, &in6->sin6_addr) != 1) {
#endif
write_runlog(ERROR, "[%s] ip(%s) invalid.\n", __FUNCTION__, host);
return CM_ERROR;
}
return CM_SUCCESS;
}
status_t BuildSockAddr(const char *host, int port, sock_addr_t *sockAddr)
{
int family = GetIpVersion(host);
switch (family) {
case AF_INET:
return BuildSockAddrIpV4(host, port, sockAddr);
case AF_INET6:
return BuildSockAddrIpV6(host, port, sockAddr);
default:
write_runlog(ERROR, "[%s] ip(%s) invalid.\n", __FUNCTION__, host);
return CM_ERROR;
}
}
status_t IpToSockAddr(const char *host, sock_addr_t *sockAddr)
{
#define CM_INVALID_IP_PORT 0
return BuildSockAddr(host, CM_INVALID_IP_PORT, sockAddr);
}
bool CheckIpValid(const char *ip)
{
if (CM_IS_EMPTY_STR(ip)) {
return false;
}
char tempIp[CM_IP_LENGTH] = {0};
if (NeedAndRemoveSquareBracketsForIpV6(ip, tempIp, CM_IP_LENGTH)) {
write_runlog(ERROR, "[%s] ip (%s) is invalid.\n", __FUNCTION__, ip);
return false;
}
if (IsLocalHostIp(tempIp)) {
return true;
}
sock_addr_t sockAddr;
if (IpToSockAddr(tempIp, &sockAddr) != CM_SUCCESS) {
write_runlog(ERROR, "[%s] ip(%s) is invalid.\n", __FUNCTION__, ip);
return false;
}
return true;
}
bool IsEqualIp(const char *clientIp, const char *localIp)
{
if (IsLocalHostIp(clientIp) && IsLocalHostIp(localIp)) {
return true;
}
sock_addr_t clientSockAddr = {0};
sock_addr_t localSockAddr = {0};
if (IpToSockAddr(localIp, &localSockAddr) != CM_SUCCESS ||
IpToSockAddr(clientIp, &clientSockAddr) != CM_SUCCESS) {
return false;
}
if (memcmp(&localSockAddr, &clientSockAddr, sizeof(sock_addr_t)) == 0) {
return true;
}
return false;
}
bool8 IsLocalHostIp(const char *ip)
{
if (CM_IS_EMPTY_STR(ip)) {
return CM_FALSE;
}
char tempIp[CM_IP_LENGTH] = {0};
if (NeedAndRemoveSquareBracketsForIpV6(ip, tempIp, CM_IP_LENGTH) != CM_SUCCESS) {
return false;
}
uint32 len = ELEMENT_COUNT(IPV4_LOCAL_HOST_ARRAY);
uint32 i;
for (i = 0; i < len; ++i) {
if (cm_str_equal(tempIp, IPV4_LOCAL_HOST_ARRAY[i])) {
return CM_TRUE;
}
}
len = ELEMENT_COUNT(IPV6_LOCAL_HOST_ARRAY);
for (i = 0; i < len; ++i) {
if (cm_str_equal(tempIp, IPV6_LOCAL_HOST_ARRAY[i])) {
return CM_TRUE;
}
}
return CM_FALSE;
}
bool ChangeIpV6ToOsFormat(const char *sourceIp, char *destIp, uint32 destSize)
{
if (CM_IS_EMPTY_STR(sourceIp)) {
return false;
}
if (GetIpVersion(sourceIp) != AF_INET6) {
return false;
}
struct in6_addr sin6Addr;
errno_t rc = memset_s(&sin6Addr, sizeof(sin6Addr), 0, sizeof(sin6Addr));
securec_check_errno(rc, (void)rc);
if (inet_pton(AF_INET6, sourceIp, &sin6Addr) != 1) {
return false;
}
if (inet_ntop(AF_INET6, &sin6Addr, destIp, destSize) == NULL) {
return false;
}
return true;
}
status_t NeedAndRemoveSquareBracketsForIpV6(const char *sourceIp, char *destIp, uint32 size)
{
if (sourceIp == NULL) {
return CM_ERROR;
}
text_t text;
CmStr2Text((char *)sourceIp, &text);
CmRemoveSquareBrackets(&text);
return CmText2Str(&text, destIp, size);
}
ProcessStatus GetPopenCmdResult(const char *cmd, const char *str, int32 tryTimes)
{
if (CM_IS_EMPTY_STR(cmd)) {
write_runlog(ERROR, "%s cmd is NULL, cannot popen cmd.\n", str);
return PROCESS_STATUS_UNKNOWN;
}
char buf[MAX_PATH_LEN + MAX_PATH_LEN];
FILE *fp;
errno_t rc;
ProcessStatus pSt = PROCESS_STATUS_INIT;
do {
rc = memset_s(buf, sizeof(buf), 0, sizeof(buf));
securec_check_intval(rc, (void)rc);
fp = popen(cmd, "re");
if (fp == NULL) {
write_runlog(ERROR, "%s popen failed, cmd is %s, error is %d.\n", str, cmd, errno);
return PROCESS_STATUS_UNKNOWN;
}
while (fgets(buf, sizeof(buf), fp) != NULL) {
if (strstr(buf, "success") != NULL) {
(void)pclose(fp);
return PROCESS_STATUS_SUCCESS;
} else if (strstr(buf, "unknown") != NULL) {
pSt = PROCESS_STATUS_UNKNOWN;
} else if (pSt == PROCESS_STATUS_INIT && strstr(buf, "fail") != NULL) {
pSt = PROCESS_STATUS_FAIL;
}
}
(void)pclose(fp);
--tryTimes;
if (tryTimes > 0) {
cm_sleep(1);
}
} while (tryTimes > 0);
if (pSt != PROCESS_STATUS_SUCCESS) {
write_runlog(ERROR, "%s failed to popen: %s, cmd is %s.\n", str, buf, cmd);
}
return pSt;
}
ProcessStatus CheckPeerIp(const char *peerIp, const char *str, const char *localIp,
int32 tryTimes, int32 family)
{
if (CM_IS_EMPTY_STR(peerIp)) {
write_runlog(ERROR, "%s failed to check peer ip, because peerIp is NULL.\n", str);
return PROCESS_STATUS_UNKNOWN;
}
char tempPeerIp[CM_IP_LENGTH] = {0};
if (NeedAndRemoveSquareBracketsForIpV6(peerIp, tempPeerIp, CM_IP_LENGTH) != CM_SUCCESS) {
write_runlog(ERROR, "%s peer ip %s invalid.\n", __FUNCTION__, peerIp);
return PROCESS_STATUS_UNKNOWN;
}
const char *pingStr = GetPingStr(family);
char cmd[MAX_PATH_LEN + MAX_PATH_LEN] = {0};
errno_t rc;
if (CM_IS_EMPTY_STR(localIp)) {
rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "tmp0=$(%s -c 1 -w 1 %s);", pingStr, tempPeerIp);
} else {
char tempLocalIp[CM_IP_LENGTH] = {0};
if (NeedAndRemoveSquareBracketsForIpV6(localIp, tempLocalIp, CM_IP_LENGTH) != CM_SUCCESS) {
write_runlog(ERROR, "%s local ip %s invalid.\n", __FUNCTION__, localIp);
return PROCESS_STATUS_UNKNOWN;
}
rc = snprintf_s(cmd,
sizeof(cmd),
sizeof(cmd) - 1,
"tmp0=$(%s -c 1 w 1 -I %s %s);",
pingStr,
tempLocalIp,
tempPeerIp);
}
securec_check_intval(rc, (void)rc);
uint32 len = (uint32)strlen(cmd);
const char *temp1 = family == AF_INET ? "ping -c 1 -w 1 127.0.0.1 2>&1" : "ping6 -w 1 ::1 2>&1";
rc = snprintf_s(cmd +len,
sizeof(cmd) - len,
(sizeof(cmd) - len) -1,
"if [ $? == 0]; then echo \"%s\"; "
"else tmp1=$(%s)"
"if [ $? == 0 ]; then echo \"%s ${tmp0}\"|tr '\\n' '|'; "
"else echo \"%s ${tmp0} || ${tmp1}\"|tr '\\n' '|'; fi; fi",
PING_SUCCESS,
temp1,
PING_FAIL,
PING_UNKNOWN);
securec_check_intval(rc, (void)rc);
write_runlog(DEBUG1, "%s ping command is %s.\n", str, cmd);
return GetPopenCmdResult(cmd, str, tryTimes);
}
#ifdef __cplusplus
}
#endif