emsApplication/3rdPartner/libhv/base/hsocket.c

409 lines
10 KiB
C

#include "hsocket.h"
#include "hdef.h"
#ifdef OS_WIN
#include "hatomic.h"
static hatomic_flag_t s_wsa_initialized = HATOMIC_FLAG_INIT;
void WSAInit() {
if (!hatomic_flag_test_and_set(&s_wsa_initialized)) {
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);
}
}
void WSADeinit() {
if (hatomic_flag_test_and_set(&s_wsa_initialized)) {
hatomic_flag_clear(&s_wsa_initialized);
WSACleanup();
}
}
#endif
static inline int socket_errno_negative(int sockfd) {
int err = socket_errno();
if (sockfd >= 0) closesocket(sockfd);
return err > 0 ? -err : -1;
}
const char* socket_strerror(int err) {
#ifdef OS_WIN
static char buffer[128];
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
0, ABS(err), 0, buffer, sizeof(buffer), NULL);
return buffer;
#else
return strerror(ABS(err));
#endif
}
bool is_ipv4(const char* host) {
struct sockaddr_in sin;
return inet_pton(AF_INET, host, &sin) == 1;
}
bool is_ipv6(const char* host) {
struct sockaddr_in6 sin6;
return inet_pton(AF_INET6, host, &sin6) == 1;
}
int ResolveAddr(const char* host, sockaddr_u* addr) {
#ifdef OS_WIN
WSAInit();
#endif
if (inet_pton(AF_INET, host, &addr->sin.sin_addr) == 1) {
addr->sa.sa_family = AF_INET; // host is ipv4, so easy ;)
return 0;
}
if (inet_pton(AF_INET6, host, &addr->sin6.sin6_addr) == 1) {
addr->sa.sa_family = AF_INET6; // host is ipv6
}
struct addrinfo* ais = NULL;
int ret = getaddrinfo(host, NULL, NULL, &ais);
if (ret != 0 || ais == NULL || ais->ai_addr == NULL || ais->ai_addrlen == 0) {
printd("unknown host: %s err:%d:%s\n", host, ret, gai_strerror(ret));
return ret;
}
struct addrinfo* pai = ais;
while (pai != NULL) {
if (pai->ai_family == AF_INET) break;
pai = pai->ai_next;
}
if (pai == NULL) pai = ais;
memcpy(addr, pai->ai_addr, pai->ai_addrlen);
freeaddrinfo(ais);
return 0;
}
const char* sockaddr_ip(sockaddr_u* addr, char *ip, int len) {
if (addr->sa.sa_family == AF_INET) {
return inet_ntop(AF_INET, &addr->sin.sin_addr, ip, len);
}
else if (addr->sa.sa_family == AF_INET6) {
return inet_ntop(AF_INET6, &addr->sin6.sin6_addr, ip, len);
}
return ip;
}
uint16_t sockaddr_port(sockaddr_u* addr) {
uint16_t port = 0;
if (addr->sa.sa_family == AF_INET) {
port = ntohs(addr->sin.sin_port);
}
else if (addr->sa.sa_family == AF_INET6) {
port = ntohs(addr->sin6.sin6_port);
}
return port;
}
int sockaddr_set_ip(sockaddr_u* addr, const char* host) {
if (!host || *host == '\0') {
addr->sin.sin_family = AF_INET;
addr->sin.sin_addr.s_addr = htonl(INADDR_ANY);
return 0;
}
return ResolveAddr(host, addr);
}
void sockaddr_set_port(sockaddr_u* addr, int port) {
if (addr->sa.sa_family == AF_INET) {
addr->sin.sin_port = htons(port);
}
else if (addr->sa.sa_family == AF_INET6) {
addr->sin6.sin6_port = htons(port);
}
}
int sockaddr_set_ipport(sockaddr_u* addr, const char* host, int port) {
#ifdef ENABLE_UDS
if (port < 0) {
sockaddr_set_path(addr, host);
return 0;
}
#endif
int ret = sockaddr_set_ip(addr, host);
if (ret != 0) return ret;
sockaddr_set_port(addr, port);
// SOCKADDR_PRINT(addr);
return 0;
}
socklen_t sockaddr_len(sockaddr_u* addr) {
if (addr->sa.sa_family == AF_INET) {
return sizeof(struct sockaddr_in);
}
else if (addr->sa.sa_family == AF_INET6) {
return sizeof(struct sockaddr_in6);
}
#ifdef ENABLE_UDS
else if (addr->sa.sa_family == AF_UNIX) {
return sizeof(struct sockaddr_un);
}
#endif
return sizeof(sockaddr_u);
}
const char* sockaddr_str(sockaddr_u* addr, char* buf, int len) {
char ip[SOCKADDR_STRLEN] = {0};
uint16_t port = 0;
if (addr->sa.sa_family == AF_INET) {
inet_ntop(AF_INET, &addr->sin.sin_addr, ip, len);
port = ntohs(addr->sin.sin_port);
snprintf(buf, len, "%s:%d", ip, port);
}
else if (addr->sa.sa_family == AF_INET6) {
inet_ntop(AF_INET6, &addr->sin6.sin6_addr, ip, len);
port = ntohs(addr->sin6.sin6_port);
snprintf(buf, len, "[%s]:%d", ip, port);
}
#ifdef ENABLE_UDS
else if (addr->sa.sa_family == AF_UNIX) {
snprintf(buf, len, "%s", addr->sun.sun_path);
}
#endif
return buf;
}
static int sockaddr_bind(sockaddr_u* localaddr, int type) {
// socket -> setsockopt -> bind
#ifdef SOCK_CLOEXEC
type |= SOCK_CLOEXEC;
#endif
int sockfd = socket(localaddr->sa.sa_family, type, 0);
if (sockfd < 0) {
perror("socket");
goto error;
}
#ifdef OS_UNIX
so_reuseaddr(sockfd, 1);
// so_reuseport(sockfd, 1);
#endif
if (localaddr->sa.sa_family == AF_INET6) {
ip_v6only(sockfd, 0);
}
if (bind(sockfd, &localaddr->sa, sockaddr_len(localaddr)) < 0) {
perror("bind");
goto error;
}
return sockfd;
error:
return socket_errno_negative(sockfd);
}
static int sockaddr_connect(sockaddr_u* peeraddr, int nonblock) {
// socket -> nonblocking -> connect
int ret = 0;
int connfd = socket(peeraddr->sa.sa_family, SOCK_STREAM, 0);
if (connfd < 0) {
perror("socket");
goto error;
}
if (nonblock) {
nonblocking(connfd);
}
ret = connect(connfd, &peeraddr->sa, sockaddr_len(peeraddr));
#ifdef OS_WIN
if (ret < 0 && socket_errno() != WSAEWOULDBLOCK) {
#else
if (ret < 0 && socket_errno() != EINPROGRESS) {
#endif
// perror("connect");
goto error;
}
return connfd;
error:
return socket_errno_negative(connfd);
}
static int ListenFD(int sockfd) {
if (sockfd < 0) return sockfd;
if (listen(sockfd, SOMAXCONN) < 0) {
perror("listen");
return socket_errno_negative(sockfd);
}
return sockfd;
}
static int ConnectFDTimeout(int connfd, int ms) {
int err = 0;
socklen_t optlen = sizeof(err);
struct timeval tv = { ms / 1000, (ms % 1000) * 1000 };
fd_set writefds;
FD_ZERO(&writefds);
FD_SET(connfd, &writefds);
int ret = select(connfd+1, 0, &writefds, 0, &tv);
if (ret < 0) {
perror("select");
goto error;
}
if (ret == 0) {
errno = ETIMEDOUT;
goto error;
}
if (getsockopt(connfd, SOL_SOCKET, SO_ERROR, (char*)&err, &optlen) < 0 || err != 0) {
if (err != 0) errno = err;
goto error;
}
blocking(connfd);
return connfd;
error:
return socket_errno_negative(connfd);
}
int Bind(int port, const char* host, int type) {
#ifdef OS_WIN
WSAInit();
#endif
sockaddr_u localaddr;
memset(&localaddr, 0, sizeof(localaddr));
int ret = sockaddr_set_ipport(&localaddr, host, port);
if (ret != 0) {
return NABS(ret);
}
return sockaddr_bind(&localaddr, type);
}
int Listen(int port, const char* host) {
int sockfd = Bind(port, host, SOCK_STREAM);
if (sockfd < 0) return sockfd;
return ListenFD(sockfd);
}
int Connect(const char* host, int port, int nonblock) {
#ifdef OS_WIN
WSAInit();
#endif
sockaddr_u peeraddr;
memset(&peeraddr, 0, sizeof(peeraddr));
int ret = sockaddr_set_ipport(&peeraddr, host, port);
if (ret != 0) {
return NABS(ret);
}
return sockaddr_connect(&peeraddr, nonblock);
}
int ConnectNonblock(const char* host, int port) {
return Connect(host, port, 1);
}
int ConnectTimeout(const char* host, int port, int ms) {
int connfd = Connect(host, port, 1);
if (connfd < 0) return connfd;
return ConnectFDTimeout(connfd, ms);
}
#ifdef ENABLE_UDS
int BindUnix(const char* path, int type) {
sockaddr_u localaddr;
memset(&localaddr, 0, sizeof(localaddr));
sockaddr_set_path(&localaddr, path);
return sockaddr_bind(&localaddr, type);
}
int ListenUnix(const char* path) {
int sockfd = BindUnix(path, SOCK_STREAM);
if (sockfd < 0) return sockfd;
return ListenFD(sockfd);
}
int ConnectUnix(const char* path, int nonblock) {
sockaddr_u peeraddr;
memset(&peeraddr, 0, sizeof(peeraddr));
sockaddr_set_path(&peeraddr, path);
return sockaddr_connect(&peeraddr, nonblock);
}
int ConnectUnixNonblock(const char* path) {
return ConnectUnix(path, 1);
}
int ConnectUnixTimeout(const char* path, int ms) {
int connfd = ConnectUnix(path, 1);
if (connfd < 0) return connfd;
return ConnectFDTimeout(connfd, ms);
}
#endif
int Socketpair(int family, int type, int protocol, int sv[2]) {
#if defined(OS_UNIX) && HAVE_SOCKETPAIR
return socketpair(AF_LOCAL, type, protocol, sv);
#endif
if (family != AF_INET || type != SOCK_STREAM) {
return -1;
}
#ifdef OS_WIN
WSAInit();
#endif
int listenfd, connfd, acceptfd;
listenfd = connfd = acceptfd = -1;
struct sockaddr_in localaddr;
socklen_t addrlen = sizeof(localaddr);
memset(&localaddr, 0, addrlen);
localaddr.sin_family = AF_INET;
localaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
localaddr.sin_port = 0;
// listener
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0) {
perror("socket");
goto error;
}
if (bind(listenfd, (struct sockaddr*)&localaddr, addrlen) < 0) {
perror("bind");
goto error;
}
if (listen(listenfd, 1) < 0) {
perror("listen");
goto error;
}
if (getsockname(listenfd, (struct sockaddr*)&localaddr, &addrlen) < 0) {
perror("getsockname");
goto error;
}
// connector
connfd = socket(AF_INET, SOCK_STREAM, 0);
if (connfd < 0) {
perror("socket");
goto error;
}
if (connect(connfd, (struct sockaddr*)&localaddr, addrlen) < 0) {
perror("connect");
goto error;
}
// acceptor
acceptfd = accept(listenfd, (struct sockaddr*)&localaddr, &addrlen);
if (acceptfd < 0) {
perror("accept");
goto error;
}
closesocket(listenfd);
sv[0] = connfd;
sv[1] = acceptfd;
return 0;
error:
if (listenfd != -1) {
closesocket(listenfd);
}
if (connfd != -1) {
closesocket(connfd);
}
if (acceptfd != -1) {
closesocket(acceptfd);
}
return -1;
}