* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <arpa/inet.h>
#include <netdb.h>
#include <securec.h>
#include <csignal>
#include <system_error>
#include "actor/buslog.hpp"
#include "actor/iomgr.hpp"
#include "socket_operate.hpp"
namespace litebus {
constexpr int IP_LEN_MAX = 128;
int SocketOperate::SetSocketKeepAlive(int fd, int keepalive, int keepidle, int keepinterval, int keepcount)
{
int optionVal;
int ret;
optionVal = keepalive;
ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &optionVal, sizeof(optionVal));
if (ret < 0) {
BUSLOG_ERROR("setsockopt SO_KEEPALIVE fail, fd:{},errno:{}", fd, errno);
return -1;
}
optionVal = keepidle;
ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &optionVal, sizeof(optionVal));
if (ret < 0) {
BUSLOG_ERROR("setsockopt TCP_KEEPIDLE fail, fd:{},errno:{}", fd, errno);
return -1;
}
optionVal = keepinterval;
ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &optionVal, sizeof(optionVal));
if (ret < 0) {
BUSLOG_ERROR("setsockopt TCP_KEEPINTVL fail, fd:{},errno:{}", fd, errno);
return -1;
}
* probes without getting a reply. */
optionVal = keepcount;
ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &optionVal, sizeof(optionVal));
if (ret < 0) {
BUSLOG_ERROR("setsockopt TCP_KEEPCNT fail, fd:{},errno:{}", fd, errno);
return -1;
}
return 0;
}
int SocketOperate::SetSocket(int fd)
{
int optionVal = 1;
int ret;
ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optionVal, sizeof(optionVal));
if (ret) {
BUSLOG_ERROR("setsockopt SO_REUSEADDR fail, fd:{},errno:{}", fd, errno);
return -1;
}
ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &optionVal, sizeof(optionVal));
if (ret) {
BUSLOG_ERROR("setsockopt TCP_NODELAY fail, fd:{},errno:{}", fd, errno);
return -1;
}
ret = SetSocketKeepAlive(fd, SOCKET_KEEPALIVE, SOCKET_KEEPIDLE, SOCKET_KEEPINTERVAL, SOCKET_KEEPCOUNT);
if (ret) {
BUSLOG_WARN("setsockopt keep alive fail, fd:{}", fd);
}
return 0;
}
int SocketOperate::CreateSocket(sa_family_t family)
{
int ret;
int fd;
fd = ::socket(family,
static_cast<int>(SOCK_STREAM) | static_cast<int>(SOCK_NONBLOCK) | static_cast<int>(SOCK_CLOEXEC), 0);
if (fd < 0) {
BUSLOG_WARN("create socket fail:{}", errno);
return -1;
}
ret = SetSocket(fd);
if (ret < 0) {
(void)close(fd);
return -1;
}
return fd;
}
uint16_t SocketOperate::GetPort(const std::string &url)
{
uint16_t port;
size_t index = url.rfind(URL_IP_PORT_SEPARATOR);
if (index == std::string::npos) {
BUSLOG_ERROR("not found ':' from {}", url);
return 0;
}
try {
auto ulPort = std::stoul(url.substr(index + URL_IP_PORT_SEPARATOR.length()));
if (ulPort > UINT16_MAX) {
BUSLOG_ERROR("port({}) out of range [0, {}]", ulPort, UINT16_MAX);
return 0;
}
port = static_cast<uint16_t>(ulPort);
} catch (...) {
BUSLOG_ERROR("not found port from {}", url);
return 0;
}
return port;
}
std::string SocketOperate::GetIP(const std::string &url)
{
size_t index1 = url.find("[");
if (index1 == std::string::npos) {
index1 = url.find(URL_PROTOCOL_IP_SEPARATOR);
if (index1 == std::string::npos) {
index1 = 0;
} else {
index1 = index1 + URL_PROTOCOL_IP_SEPARATOR.length();
}
} else {
index1 = index1 + 1;
}
size_t index2 = url.find("]");
if (index2 == std::string::npos) {
index2 = url.rfind(URL_IP_PORT_SEPARATOR);
if (index2 == std::string::npos) {
BUSLOG_ERROR("not found ':' from {}", url);
return "";
}
}
if (index1 > index2) {
BUSLOG_ERROR("parse ip failed from {}", url);
return "";
}
std::string ip = url.substr(index1, index2 - index1);
IOSockaddr addr;
int result = inet_pton(AF_INET, ip.c_str(), &addr.saIn.sin_addr);
if (result <= 0) {
result = inet_pton(AF_INET6, ip.c_str(), &addr.saIn6.sin6_addr);
if (result <= 0) {
if (GetIPFromHostname(ip, 0, addr)) {
return ip;
}
BUSLOG_ERROR("parse ip failed, result:{},url:{}", result, url);
return "";
}
}
return ip;
}
bool SocketOperate::GetIPFromHostname(const std::string &hostName, const uint16_t port, litebus::IOSockaddr &addr)
{
struct addrinfo hints;
(void)memset_s(&hints, sizeof hints, 0, sizeof hints);
struct addrinfo *res;
int result;
if ((result = getaddrinfo(hostName.c_str(), NULL, &hints, &res)) != 0) {
BUSLOG_WARN("parse hostname failed, result:{},hostname:{}", result, hostName);
return false;
}
if (res->ai_family == AF_INET) {
struct sockaddr_in *ipv4 = reinterpret_cast<struct sockaddr_in *>(res->ai_addr);
addr.saIn.sin_addr = ipv4->sin_addr;
addr.saIn.sin_family = AF_INET;
addr.saIn.sin_port = htons(port);
} else if (res->ai_family == AF_INET6) {
struct sockaddr_in6 *ipv6 = reinterpret_cast<struct sockaddr_in6 *>(res->ai_addr);
addr.saIn6.sin6_addr = ipv6->sin6_addr;
addr.saIn6.sin6_family = AF_INET6;
addr.saIn6.sin6_port = htons(port);
} else {
BUSLOG_WARN("parse hostname failed, invalid family:{},hostname:{}", res->ai_family, hostName);
freeaddrinfo(res);
return false;
}
freeaddrinfo(res);
return true;
}
bool SocketOperate::GetSockAddr(const std::string &url, IOSockaddr &addr)
{
std::string ip;
uint16_t port;
(void)memset_s(&addr, sizeof(addr), 0, sizeof(addr));
size_t index1 = url.find(URL_PROTOCOL_IP_SEPARATOR);
if (index1 == std::string::npos) {
index1 = 0;
} else {
index1 = index1 + URL_PROTOCOL_IP_SEPARATOR.length();
}
size_t index2 = url.rfind(':');
if (index2 == std::string::npos) {
BUSLOG_ERROR("Couldn't find the charater colon.");
return false;
}
ip = url.substr(index1, index2 - index1);
if (ip.empty()) {
BUSLOG_ERROR("Couldn't find ip in url:{}", url);
return false;
}
try {
auto ulPort = std::stoul(url.substr(index2 + URL_IP_PORT_SEPARATOR.length()));
if (ulPort > UINT16_MAX) {
BUSLOG_ERROR("port({}) out of range [0, {}]", ulPort, UINT16_MAX);
return false;
}
port = static_cast<uint16_t>(ulPort);
} catch (const std::exception &e) {
BUSLOG_ERROR("Couldn't find port in url:{}, error: {}", url, e.what());
return false;
}
int result = inet_pton(AF_INET, ip.c_str(), &addr.saIn.sin_addr);
if (result > 0) {
addr.saIn.sin_family = AF_INET;
addr.saIn.sin_port = htons(port);
return true;
}
result = inet_pton(AF_INET6, ip.c_str(), &addr.saIn6.sin6_addr);
if (result > 0) {
addr.saIn6.sin6_family = AF_INET6;
addr.saIn6.sin6_port = htons(port);
return true;
}
if (GetIPFromHostname(ip, port, addr)) {
return true;
}
BUSLOG_ERROR("parse ip failed,result:{},url:{}", result, url);
return false;
}
uint16_t SocketOperate::GetFdPort(int fd)
{
uint16_t port = 0;
int retval;
union IOSockaddr isa;
socklen_t isaLen = sizeof(struct sockaddr_storage);
retval = getsockname(fd, &isa.sa, &isaLen);
if (retval) {
BUSLOG_INFO("getsockname fail, fd:{},ret:{},errno:{}", fd, retval, errno);
return port;
}
if (isa.sa.sa_family == AF_INET) {
port = ntohs(isa.saIn.sin_port);
} else if (isa.sa.sa_family == AF_INET6) {
port = ntohs(isa.saIn6.sin6_port);
} else {
BUSLOG_INFO("getsockname unknown, fd:{},family:{}", fd, isa.sa.sa_family);
}
return port;
}
std::string SocketOperate::GetFdPeer(int fd)
{
std::string peer;
int retval;
union IOSockaddr isa;
socklen_t isaLen = sizeof(struct sockaddr_storage);
retval = getpeername(fd, &isa.sa, &isaLen);
if (retval < 0) {
BUSLOG_INFO("getpeername fail, fd:{},ret:{},errno:{}", fd, retval, errno);
return peer;
}
char ipdotdec[IP_LEN_MAX];
if (isa.sa.sa_family == AF_INET) {
(void)inet_ntop(AF_INET, static_cast<void *>(&isa.saIn.sin_addr), ipdotdec, IP_LEN_MAX);
peer = std::string(ipdotdec) + ":" + std::to_string(ntohs(isa.saIn.sin_port));
BUSLOG_DEBUG("getpeername for ipv4 after accept, fd:{},peer:{}", fd, peer);
} else if (isa.sa.sa_family == AF_INET6) {
(void)inet_ntop(AF_INET6, static_cast<void *>(&isa.saIn6.sin6_addr), ipdotdec, IP_LEN_MAX);
peer = std::string(ipdotdec) + ":" + std::to_string(ntohs(isa.saIn6.sin6_port));
BUSLOG_DEBUG("getpeername for ipv6 after accept, fd:{},peer:{}", fd, peer);
} else {
BUSLOG_INFO("getpeername unknown, fd:{},family:{}", fd, isa.sa.sa_family);
}
return peer;
}
int SocketOperate::Connect(int fd, const struct sockaddr *sa, socklen_t saLen, uint16_t &boundPort)
{
int retval;
retval = connect(fd, sa, saLen);
if (retval != 0) {
if (errno == EINPROGRESS) {
BUSLOG_DEBUG("connect in progress,fd:{},ret:{},errno:{}", fd, retval, errno);
} else {
BUSLOG_ERROR("tcp connect fail,fd:{},ret:{},errno:{}", fd, retval, errno);
return retval;
}
} else {
BUSLOG_DEBUG("connect, fd:{},ret:{},errno:{}", fd, retval, errno);
}
boundPort = GetFdPort(fd);
if (boundPort == 0) {
return BUS_ERROR;
}
BUSLOG_DEBUG("connect ok, fd:{},localport:{}", fd, boundPort);
return BUS_OK;
}
int SocketOperate::Listen(const std::string &url)
{
int listenFd;
IOSockaddr addr;
if (!GetSockAddr(url, addr)) {
return -1;
}
listenFd = CreateSocket(addr.sa.sa_family);
if (listenFd < 0) {
BUSLOG_ERROR("create socket fail, url:{}", url);
return -1;
}
if (::bind(listenFd, (struct sockaddr *)&addr, sizeof(IOSockaddr))) {
BUSLOG_ERROR("bind fail, fd:{},errno:{},url:{}", listenFd, errno, url);
(void)close(listenFd);
return -1;
}
if (::listen(listenFd, SOCKET_LISTEN_BACKLOG)) {
BUSLOG_ERROR("listen fail, fd:{},errno:{},url:{}", listenFd, errno, url);
(void)close(listenFd);
return -1;
}
return listenFd;
}
int SocketOperate::Accept(int server)
{
struct sockaddr storage;
socklen_t length = sizeof(storage);
auto acceptFd = ::accept4(server, &storage, &length,
static_cast<int>(SOCK_NONBLOCK) | static_cast<int>(SOCK_CLOEXEC));
if (acceptFd < 0) {
BUSLOG_ERROR("accept fail,errno:{},server:{}", errno, server);
return acceptFd;
}
(void)SetSocket(acceptFd);
return acceptFd;
}
}