* This file is part of the oGRAC project.
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
*
* oGRAC 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.
* -------------------------------------------------------------------------
*
* cs_uds.c
*
*
* IDENTIFICATION
* src/protocol/cs_uds.c
*
* -------------------------------------------------------------------------
*/
#include "cs_uds.h"
#include "cs_pipe.h"
#include "cm_file.h"
#include "cm_signal.h"
#ifdef __cplusplus
extern "C" {
#endif
static status_t cs_uds_init(void)
{
return cs_tcp_init();
}
status_t cs_create_uds_socket(socket_t *sock)
{
OG_RETURN_IFERR(cs_uds_init());
#ifndef WIN32
*sock = (socket_t)socket(AF_UNIX, SOCK_STREAM, 0);
if (*sock == CS_INVALID_SOCKET) {
OG_THROW_ERROR(ERR_CREATE_SOCKET, errno);
return OG_ERROR;
}
#endif
return OG_SUCCESS;
}
status_t cs_uds_connect(const char *server_path, const char *client_path, uds_link_t *uds_link,
socket_attr_t *sock_attr)
{
if (CM_IS_EMPTY_STR(server_path) || uds_link == NULL) {
return OG_ERROR;
}
#ifdef WIN32
int port = 0;
FILE *hFile = fopen(server_path, "r");
if (NULL == hFile) {
return OG_ERROR;
}
if (fscanf_s(hFile, "%d", &port) < 0) {
(void)fclose(hFile);
return OG_ERROR;
}
(void)fclose(hFile);
if (cs_tcp_connect("127.0.0.1", port, (tcp_link_t *)uds_link, NULL, sock_attr) != OG_SUCCESS) {
return OG_ERROR;
}
#else
if (!CM_IS_EMPTY_STR(client_path)) {
cs_uds_build_addr(&uds_link->local, client_path);
unlink(uds_link->local.addr.sun_path);
if (bind(uds_link->sock, SOCKADDR(&uds_link->local), uds_link->local.salen) < 0) {
OG_THROW_ERROR(ERR_UDS_BIND, client_path, cm_get_os_error());
return OG_ERROR;
}
(void)chmod(client_path, SERVICE_FILE_PERMISSIONS);
}
cs_uds_build_addr(&uds_link->remote, server_path);
cs_set_buffer_size(uds_link->sock, OG_TCP_DEFAULT_BUFFER_SIZE, OG_TCP_DEFAULT_BUFFER_SIZE);
cs_set_socket_timeout(uds_link->sock, sock_attr->connect_timeout);
if (connect(uds_link->sock, SOCKADDR(&uds_link->remote), uds_link->remote.salen) != 0) {
OG_THROW_ERROR(ERR_ESTABLISH_UDS_CONNECTION, server_path, errno);
return OG_ERROR;
}
cs_set_io_mode(uds_link->sock, OG_TRUE, OG_TRUE);
cs_set_keep_alive(uds_link->sock, OG_TCP_KEEP_IDLE, OG_TCP_KEEP_INTERVAL, OG_TCP_KEEP_COUNT);
cs_set_linger(uds_link->sock, sock_attr->l_onoff, sock_attr->l_linger);
#endif
uds_link->closed = OG_FALSE;
return OG_SUCCESS;
}
void cs_uds_disconnect(uds_link_t *uds_link)
{
CM_POINTER(uds_link);
if (uds_link->closed) {
CM_ASSERT(uds_link->sock == CS_INVALID_SOCKET);
return;
}
#ifdef WIN32
cs_close_socket(uds_link->sock);
uds_link->sock = CS_INVALID_SOCKET;
#else
cs_uds_socket_close(&uds_link->sock);
#endif
uds_link->closed = OG_TRUE;
}
status_t cs_uds_wait_win(uds_link_t *uds_link, uint32 wait_for, int32 timeout, bool32 *ready)
{
int32 count;
fd_set socket_set;
struct timeval *tv_ptr = NULL;
struct timeval tv;
if (ready != NULL) {
*ready = OG_FALSE;
}
if (uds_link->closed) {
OG_THROW_ERROR(ERR_PEER_CLOSED, "uds");
return OG_ERROR;
}
FD_ZERO(&socket_set);
FD_SET(uds_link->sock, &socket_set);
if (timeout != 0) {
tv.tv_sec = timeout / OG_TIME_THOUSAND_UN;
tv.tv_usec = ((long)timeout - tv.tv_sec * OG_TIME_THOUSAND_UN) * (long)OG_TIME_THOUSAND_UN;
tv_ptr = &tv;
} else {
tv_ptr = NULL;
}
if (wait_for == CS_WAIT_FOR_WRITE) {
count = select((int)uds_link->sock + 1, NULL, &socket_set, NULL, tv_ptr);
} else {
count = select((int)uds_link->sock + 1, &socket_set, NULL, NULL, tv_ptr);
}
if (count >= 0) {
if (ready != NULL) {
*ready = (count > 0);
}
return OG_SUCCESS;
}
if (errno != EINTR) {
OG_THROW_ERROR(ERR_PEER_CLOSED, "uds");
return OG_ERROR;
}
return OG_SUCCESS;
}
status_t cs_uds_wait(uds_link_t *uds_link, uint32 wait_for, int32 timeout, bool32 *ready)
{
#ifdef WIN32
if (cs_uds_wait_win(uds_link, wait_for, timeout, ready) != OG_SUCCESS) {
return OG_ERROR;
}
#else
struct pollfd fd;
int32 ret;
int32 tv;
if (ready != NULL) {
*ready = OG_FALSE;
}
if (uds_link->closed) {
OG_THROW_ERROR(ERR_PEER_CLOSED, "uds");
return OG_ERROR;
}
tv = (timeout == 0 ? -1 : timeout);
fd.fd = uds_link->sock;
fd.revents = 0;
if (wait_for == CS_WAIT_FOR_WRITE) {
fd.events = POLLOUT;
} else {
fd.events = POLLIN;
}
ret = poll(&fd, 1, tv);
if (ret == 0) {
if (ready != NULL) {
*ready = OG_FALSE;
}
return OG_SUCCESS;
}
if (ret > 0) {
if (ready != NULL) {
*ready = OG_TRUE;
}
return OG_SUCCESS;
}
if (errno != EINTR) {
OG_THROW_ERROR(ERR_PEER_CLOSED, "uds");
return OG_ERROR;
}
#endif
return OG_SUCCESS;
}
status_t cs_uds_send(uds_link_t *uds_link, const char *buf, uint32 size, int32 *send_size)
{
int code;
if (size == 0) {
*send_size = 0;
return OG_SUCCESS;
}
*send_size = send(uds_link->sock, buf, size, 0);
if (*send_size <= 0) {
#ifdef WIN32
code = WSAGetLastError();
if (code == WSAEWOULDBLOCK) {
#else
code = errno;
if (errno == EWOULDBLOCK) {
#endif
*send_size = 0;
return OG_SUCCESS;
}
OG_THROW_ERROR(ERR_PEER_CLOSED_REASON, "uds", code);
return OG_ERROR;
}
return OG_SUCCESS;
}
status_t cs_uds_send_timed(uds_link_t *uds_link, const char *buf, uint32 size, uint32 timeout)
{
int32 remain_size;
int32 offset;
int32 writen_size;
uint32 wait_interval = 0;
bool32 ready = OG_FALSE;
if (uds_link->closed) {
OG_THROW_ERROR(ERR_PEER_CLOSED, "uds");
return OG_ERROR;
}
if (cs_uds_send(uds_link, buf, size, &writen_size) != OG_SUCCESS) {
return OG_ERROR;
}
remain_size = size - writen_size;
offset = writen_size;
while (remain_size > 0) {
if (cs_uds_wait(uds_link, CS_WAIT_FOR_WRITE, OG_POLL_WAIT, &ready) != OG_SUCCESS) {
return OG_ERROR;
}
if (!ready) {
wait_interval += OG_POLL_WAIT;
if (wait_interval >= timeout) {
OG_THROW_ERROR(ERR_TCP_TIMEOUT, "send data");
return OG_ERROR;
}
continue;
}
if (cs_uds_send(uds_link, buf + offset, remain_size, &writen_size) != OG_SUCCESS) {
return OG_ERROR;
}
remain_size -= writen_size;
offset += writen_size;
}
return OG_SUCCESS;
}
status_t cs_uds_recv(uds_link_t *uds_link, char *buf, uint32 size, int32 *recv_size, uint32 *wait_event)
{
int32 rsize = 0;
if (size == 0) {
*recv_size = 0;
return OG_SUCCESS;
}
for (;;) {
rsize = recv(uds_link->sock, buf, size, 0);
if (rsize > 0) {
break;
}
if (rsize == 0) {
OG_THROW_ERROR(ERR_PEER_CLOSED, "uds");
return OG_ERROR;
}
if (cm_get_sock_error() == EINTR || cm_get_sock_error() == EAGAIN) {
continue;
}
OG_THROW_ERROR(ERR_TCP_RECV, "uds", cm_get_sock_error());
return OG_ERROR;
}
*recv_size = rsize;
return OG_SUCCESS;
}
status_t cs_uds_recv_timed(uds_link_t *uds_link, char *buf, uint32 size, uint32 timeout)
{
int32 remain_size;
int32 offset;
uint32 wait_interval = 0;
int32 recv_size = 0;
bool32 ready = OG_FALSE;
remain_size = size;
offset = 0;
if (cs_uds_recv(uds_link, buf + offset, remain_size, &recv_size, NULL) != OG_SUCCESS) {
return OG_ERROR;
}
remain_size -= recv_size;
offset += recv_size;
while (remain_size > 0) {
if (cs_uds_wait(uds_link, CS_WAIT_FOR_READ, OG_POLL_WAIT, &ready) != OG_SUCCESS) {
return OG_ERROR;
}
if (!ready) {
wait_interval += OG_POLL_WAIT;
if (wait_interval >= timeout) {
OG_THROW_ERROR(ERR_TCP_TIMEOUT, "recv data");
return OG_ERROR;
}
continue;
}
if (cs_uds_recv(uds_link, buf + offset, remain_size, &recv_size, NULL) != OG_SUCCESS) {
return OG_ERROR;
}
remain_size -= recv_size;
offset += recv_size;
}
return OG_SUCCESS;
}
#ifndef WIN32
static bool32 cs_uds_try_connect(const char *path)
{
status_t status;
socket_t sock = CS_INVALID_SOCKET;
cs_sockaddr_un_t un;
bool32 result = OG_FALSE;
CM_POINTER(path);
status = cs_create_uds_socket(&sock);
if (status != OG_SUCCESS) {
return OG_FALSE;
}
cs_uds_build_addr(&un, path);
result = (0 == connect(sock, SOCKADDR(&un), un.salen));
cs_close_socket(sock);
return result;
}
#endif
status_t cs_uds_create_listener(const char *name, socket_t *sock, uint16 permissions)
{
#ifdef WIN32
char port[32];
DWORD bytes;
int32 code;
sock_addr_t sock_addr;
tcp_option_t option;
OVERLAPPED ovp;
HANDLE hFile = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
if (INVALID_HANDLE_VALUE == hFile) {
return OG_ERROR;
}
if (cm_ip_to_sockaddr("127.0.0.1", &sock_addr) != OG_SUCCESS) {
CloseHandle(hFile);
return OG_ERROR;
}
if (cs_create_socket(SOCKADDR_FAMILY(&sock_addr), sock) != OG_SUCCESS) {
CloseHandle(hFile);
return OG_ERROR;
}
cs_set_io_mode(*sock, OG_TRUE, OG_TRUE);
When a process is killed, the address bound by the process can not be bound
by other process immediately, this situation is unacceptable, so we use the
SO_REUSEADDR parameter which allows the socket to be bound to an address
that is already in use.
************************************************************************/
option = 1;
code = setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (char *)&option, sizeof(uint32));
if (-1 == code) {
cs_close_socket(*sock);
*sock = CS_INVALID_SOCKET;
CloseHandle(hFile);
OG_THROW_ERROR(ERR_SET_SOCKET_OPTION);
return OG_ERROR;
}
code = bind(*sock, SOCKADDR(&sock_addr), sock_addr.salen);
if (code != 0) {
cs_close_socket(*sock);
*sock = CS_INVALID_SOCKET;
CloseHandle(hFile);
OG_THROW_ERROR(ERR_SOCKET_BIND, "127.0.0.1", 0, cm_get_os_error());
return OG_ERROR;
}
sock_addr_t sockname;
sockname.salen = sizeof(sockname.addr);
(void)getsockname(*sock, SOCKADDR(&sockname), &sockname.salen);
int iret_snprintf = snprintf_s(port, sizeof(port), sizeof(port) - 1, "%u", ntohs(SOCKADDR_PORT(&sockname)));
if (iret_snprintf == -1) {
cs_close_socket(*sock);
*sock = CS_INVALID_SOCKET;
CloseHandle(hFile);
return OG_ERROR;
}
WriteFile(hFile, port, (DWORD)strlen(port), &bytes, NULL);
FlushFileBuffers(hFile);
code = memset_s(&ovp, sizeof(ovp), 0, sizeof(ovp));
if (SECUREC_UNLIKELY(code != EOK)) {
OG_THROW_ERROR(ERR_SYSTEM_CALL, code);
return OG_ERROR;
}
if (code != 0) {
cs_close_socket(*sock);
*sock = CS_INVALID_SOCKET;
CloseHandle(hFile);
OG_THROW_ERROR(ERR_SYSTEM_CALL, code);
return OG_ERROR;
}
if (!LockFileEx(hFile, LOCKFILE_FAIL_IMMEDIATELY, 0, 1, 0, &ovp)) {
CloseHandle(hFile);
cs_close_socket(*sock);
*sock = CS_INVALID_SOCKET;
return OG_ERROR;
}
code = listen(*sock, 20);
if (code != 0) {
CloseHandle(hFile);
cs_close_socket(*sock);
*sock = CS_INVALID_SOCKET;
OG_THROW_ERROR(ERR_SOCKET_LISTEN, "listen socket", cm_get_os_error());
return OG_ERROR;
}
#else
status_t status;
cs_sockaddr_un_t un;
TRY TO TEST IF DOMAIN SOCKET LISTEN EXIST.
************************************************************************/
if (cs_uds_try_connect(name)) {
OG_THROW_ERROR(ERR_UDS_CONFLICTED, name);
return OG_ERROR;
}
status = cs_create_uds_socket(sock);
OG_RETURN_IFERR(status);
cs_uds_build_addr(&un, name);
unlink(un.addr.sun_path);
if (bind(*sock, SOCKADDR(&un), un.salen) < 0) {
cs_uds_socket_close(sock);
OG_THROW_ERROR(ERR_UDS_BIND, name, cm_get_os_error());
return OG_ERROR;
}
if (listen(*sock, 20) < 0) {
cs_uds_socket_close(sock);
OG_THROW_ERROR(ERR_SOCKET_LISTEN, "listen uds socket", cm_get_os_error());
return OG_ERROR;
}
(void)chmod(name, permissions);
#endif
return OG_SUCCESS;
}
int32 cs_uds_getsockname(socket_t sock_ready, cs_sockaddr_un_t *un)
{
int ret = getsockname(sock_ready, SOCKADDR(un), &un->salen);
if (ret < 0) {
return ret;
}
#ifndef WIN32
if (un->salen >= sizeof(struct sockaddr_un)) {
un->salen = sizeof(struct sockaddr_un) - 1;
}
un->addr.sun_path[sizeof_sun_path(un->salen)] = 0;
#endif
return ret;
}
void cs_uds_socket_close(socket_t *sockfd)
{
cs_sockaddr_un_t un;
un.salen = sizeof(un.addr);
(void)cs_uds_getsockname(*sockfd, &un);
cs_close_socket(*sockfd);
*sockfd = CS_INVALID_SOCKET;
return;
}
#ifdef __cplusplus
}
#endif