Files
communication_t2stack/nstackx_util/core/nstackx_socket.c
T
Agrant 471473609e sync to a576e8c57
Signed-off-by: Agrant <luojuheng@huawei.com>
2026-01-19 11:53:58 +08:00

542 lines
17 KiB
C

/*
* Copyright (C) 2021 Huawei Device Co., Ltd.
* 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 "nstackx_socket.h"
#include "nstackx_log.h"
#include "nstackx_error.h"
#include "nstackx_util.h"
#include "nstackx_dev.h"
#include "locale.h"
#include "securec.h"
#define NSTACKX_MAX_LISTEN_NUMBER 3
#define NSTACKX_TCP_SOCKET_BUFFER_SIZE (1 * 1024 * 1024)
#define TAG "nStackXSocket"
void CloseSocket(Socket *socket)
{
if (socket == NULL) {
return;
}
CloseSocketInner(socket->sockfd);
socket->sockfd = INVALID_SOCKET;
free(socket);
}
static void GetTcpSocketBufSize(SocketDesc fd)
{
int32_t ret;
int32_t bufSize;
socklen_t optLen = sizeof(bufSize);
ret = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bufSize, &optLen);
if (ret < 0) {
LOGE(TAG, "getsockopt SO_SNDBUF failed");
return;
}
LOGD(TAG, "SO_SNDBUF = %d", bufSize);
ret = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufSize, &optLen);
if (ret < 0) {
LOGE(TAG, "getsockopt SO_RCVBUF failed");
return;
}
LOGD(TAG, "SO_RCVBUF = %d", bufSize);
}
static int32_t SetTcpSocketBufSize(SocketDesc fd, int32_t bufSize)
{
int32_t ret;
socklen_t optLen = sizeof(bufSize);
if (bufSize < 0) {
return NSTACKX_EFAILED;
}
GetTcpSocketBufSize(fd);
ret = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bufSize, optLen);
if (ret < 0) {
LOGE(TAG, "setsockopt SO_SNDBUF failed");
return NSTACKX_EFAILED;
}
LOGD(TAG, "setsockopt SO_SNDBUF = %d", bufSize);
ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufSize, optLen);
if (ret < 0) {
LOGE(TAG, "setsockopt SO_RCVBUF failed");
return NSTACKX_EFAILED;
}
LOGD(TAG, "setsockopt SO_RCVBUF = %d", bufSize);
GetTcpSocketBufSize(fd);
return NSTACKX_EOK;
}
static int32_t ConnectTcpServerWithTargetDev(Socket *clientSocket, const struct sockaddr_in *sockAddr,
const char *localInterface)
{
socklen_t addrLen = sizeof(struct sockaddr_in);
clientSocket->sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket->sockfd == INVALID_SOCKET) {
LOGE(TAG, "socket create failed, error :%d", GetErrno());
return NSTACKX_EFAILED;
}
if (SetTcpSocketBufSize(clientSocket->sockfd, NSTACKX_TCP_SOCKET_BUFFER_SIZE) != NSTACKX_EOK) {
LOGE(TAG, "set socket buf failed");
}
if (SetSocketNonBlock(clientSocket->sockfd) != NSTACKX_EOK) {
LOGE(TAG, "set socket nonblock failed");
}
if (localInterface == NULL) {
BindToDevInTheSameLan(clientSocket->sockfd, sockAddr);
} else {
LOGI(TAG, "bind to target interface %s", localInterface);
if (BindToTargetDev(clientSocket->sockfd, localInterface) != NSTACKX_EOK) {
LOGE(TAG, "can't bind to target interface %s", localInterface);
} else {
LOGI(TAG, "bind to target interface %s successfully", localInterface);
}
}
int32_t ret = connect(clientSocket->sockfd, (struct sockaddr *)sockAddr, addrLen);
if (ret != 0) {
if (!SocketOpInProgress()) {
LOGE(TAG, "connect error, %d", GetErrno());
goto FAIL_SOCKET;
}
}
LOGI(TAG, "connect success");
clientSocket->dstAddr = *sockAddr;
return NSTACKX_EOK;
FAIL_SOCKET:
CloseSocketInner(clientSocket->sockfd);
clientSocket->sockfd = INVALID_SOCKET;
return NSTACKX_EFAILED;
}
static int32_t ConnectUdpServerWithTargetDev(Socket *clientSocket, const struct sockaddr_in *sockAddr,
const char *localInterface)
{
int32_t ret = 0;
struct sockaddr_in tmpAddr;
socklen_t srcAddrLen = sizeof(struct sockaddr_in);
clientSocket->sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (clientSocket->sockfd == INVALID_SOCKET) {
LOGE(TAG, "socket create failed, error :%d", GetErrno());
return NSTACKX_EFAILED;
}
if (SetSocketNonBlock(clientSocket->sockfd) != NSTACKX_EOK) {
LOGE(TAG, "set socket nonblock failed");
goto FAIL_SOCKET;
}
if (localInterface == NULL) {
BindToDevInTheSameLan(clientSocket->sockfd, sockAddr);
} else {
if (BindToTargetDev(clientSocket->sockfd, localInterface) != NSTACKX_EOK) {
LOGE(TAG, "can't bind to target interface %s", localInterface);
} else {
LOGI(TAG, "bind to target interface %s successfully", localInterface);
}
}
ret = connect(clientSocket->sockfd, (struct sockaddr *)sockAddr, sizeof(struct sockaddr));
if (ret != 0) {
LOGE(TAG, "connect to udp server failed %d", GetErrno());
goto FAIL_SOCKET;
}
(void)memset_s(&tmpAddr, sizeof(tmpAddr), 0, sizeof(tmpAddr));
ret = getsockname(clientSocket->sockfd, (struct sockaddr *)&tmpAddr, &srcAddrLen);
if (ret != 0) {
LOGE(TAG, "getsockname failed %d", GetErrno());
goto FAIL_SOCKET;
}
clientSocket->dstAddr = *sockAddr;
clientSocket->srcAddr = tmpAddr;
return NSTACKX_EOK;
FAIL_SOCKET:
CloseSocketInner(clientSocket->sockfd);
clientSocket->sockfd = INVALID_SOCKET;
return NSTACKX_EFAILED;
}
static int32_t CreateTcpServer(Socket *serverSocket, const struct sockaddr_in *sockAddr)
{
int32_t reuse = 1;
struct sockaddr_in localAddr;
socklen_t len = sizeof(localAddr);
serverSocket->sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket->sockfd == INVALID_SOCKET) {
LOGE(TAG, "create socket failed, error :%d", GetErrno());
return NSTACKX_EFAILED;
}
if (SetSocketNonBlock(serverSocket->sockfd) != NSTACKX_EOK) {
LOGE(TAG, "set socket nonblock failed");
goto FAIL_SOCKET;
}
if (setsockopt(serverSocket->sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) != 0) {
LOGE(TAG, "Failed to set server socket! error :%d", GetErrno());
goto FAIL_SOCKET;
}
(void)memset_s(&localAddr, sizeof(localAddr), 0, sizeof(localAddr));
/* Bind to ANY source ip address and random port number */
localAddr.sin_family = AF_INET;
localAddr.sin_port = sockAddr->sin_port;
if (sockAddr->sin_addr.s_addr != 0) {
localAddr.sin_addr.s_addr = sockAddr->sin_addr.s_addr;
} else {
localAddr.sin_addr.s_addr = INADDR_ANY;
}
if (bind(serverSocket->sockfd, (struct sockaddr *)&localAddr, len) != 0) {
LOGE(TAG, "Failed to bind socket error :%d", GetErrno());
goto FAIL_SOCKET;
}
if (sockAddr->sin_addr.s_addr != 0 &&
BindToDevice(serverSocket->sockfd, sockAddr) != NSTACKX_EOK) {
LOGE(TAG, "Failed to bind socket to device");
}
if (getsockname(serverSocket->sockfd, (struct sockaddr *)&(serverSocket->srcAddr), &len) != 0) {
LOGE(TAG, "Failed to get socket name! error :%d", GetErrno());
goto FAIL_SOCKET;
}
if (listen(serverSocket->sockfd, NSTACKX_MAX_LISTEN_NUMBER) != 0) {
LOGE(TAG, "Failed to listen TCP port! error :%d", GetErrno());
goto FAIL_SOCKET;
}
/* Note: Here we rely on that an accepted socket will inherit SO_SNDBUF and SO_RCVBUF
options from the listening socket. */
if (SetTcpSocketBufSize(serverSocket->sockfd, NSTACKX_TCP_SOCKET_BUFFER_SIZE) != NSTACKX_EOK) {
LOGE(TAG, "Failed to set socket buff size:%u", NSTACKX_TCP_SOCKET_BUFFER_SIZE);
}
return NSTACKX_EOK;
FAIL_SOCKET:
CloseSocketInner(serverSocket->sockfd);
serverSocket->sockfd = INVALID_SOCKET;
return NSTACKX_EFAILED;
}
static int32_t CreateUdpServer(Socket *serverSocket, const struct sockaddr_in *sockAddr)
{
if (sockAddr == NULL) {
LOGE(TAG, "sockAddr is null");
return NSTACKX_EFAILED;
}
struct sockaddr_in localAddr;
socklen_t len = sizeof(localAddr);
serverSocket->sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (serverSocket->sockfd == INVALID_SOCKET) {
LOGE(TAG, "create socket failed, error :%d", GetErrno());
return NSTACKX_EFAILED;
}
if (SetSocketNonBlock(serverSocket->sockfd) != NSTACKX_EOK) {
LOGE(TAG, "set socket nonblock failed");
goto FAIL_SOCKET;
}
(void)memset_s(&localAddr, sizeof(localAddr), 0, sizeof(localAddr));
/* Bind to ANY source ip address and random port number */
localAddr.sin_family = AF_INET;
localAddr.sin_port = sockAddr->sin_port;
if (sockAddr->sin_addr.s_addr != 0) {
localAddr.sin_addr.s_addr = sockAddr->sin_addr.s_addr;
} else {
localAddr.sin_addr.s_addr = INADDR_ANY;
}
if (bind(serverSocket->sockfd, (struct sockaddr *)&localAddr, len) != 0) {
LOGE(TAG, "Failed to bind socket, error :%d", GetErrno());
goto FAIL_SOCKET;
}
if (sockAddr->sin_addr.s_addr != 0 &&
BindToDevice(serverSocket->sockfd, sockAddr) != NSTACKX_EOK) {
LOGE(TAG, "Failed to bind socket to device");
}
if (getsockname(serverSocket->sockfd, (struct sockaddr *)(&serverSocket->srcAddr), &len) != 0) {
LOGE(TAG, "Failed to get socket name! error :%d", GetErrno());
goto FAIL_SOCKET;
}
return NSTACKX_EOK;
FAIL_SOCKET:
CloseSocketInner(serverSocket->sockfd);
serverSocket->sockfd = INVALID_SOCKET;
return NSTACKX_EFAILED;
}
Socket *ClientSocketWithTargetDev(SocketProtocol protocol, const struct sockaddr_in *sockAddr,
const char *localInterface)
{
int32_t ret;
if (sockAddr == NULL) {
return NULL;
}
Socket *socket = calloc(1, sizeof(Socket));
if (socket == NULL) {
LOGE(TAG, "malloc Socket failed\n");
return NULL;
}
switch (protocol) {
case NSTACKX_PROTOCOL_TCP:
socket->protocol = NSTACKX_PROTOCOL_TCP;
ret = ConnectTcpServerWithTargetDev(socket, sockAddr, localInterface);
break;
case NSTACKX_PROTOCOL_UDP:
socket->protocol = NSTACKX_PROTOCOL_UDP;
ret = ConnectUdpServerWithTargetDev(socket, sockAddr, localInterface);
break;
case NSTACKX_PROTOCOL_D2D:
LOGE(TAG, "d2d not support");
ret = NSTACKX_EFAILED;
break;
default:
LOGE(TAG, "protocol not support");
ret = NSTACKX_EFAILED;
break;
}
if (ret != NSTACKX_EOK) {
LOGE(TAG, "Create client socket failed! %d", ret);
free(socket);
return NULL;
}
socket->isServer = NSTACKX_FALSE;
return socket;
}
Socket *ClientSocket(SocketProtocol protocol, const struct sockaddr_in *sockAddr)
{
return ClientSocketWithTargetDev(protocol, sockAddr, NULL);
}
Socket *ServerSocket(SocketProtocol protocol, const struct sockaddr_in *sockAddr)
{
int32_t ret;
if (sockAddr == NULL) {
return NULL;
}
Socket *socket = calloc(1, sizeof(Socket));
if (socket == NULL) {
LOGE(TAG, "malloc Socket failed\n");
return NULL;
}
switch (protocol) {
case NSTACKX_PROTOCOL_TCP:
socket->protocol = NSTACKX_PROTOCOL_TCP;
ret = CreateTcpServer(socket, sockAddr);
break;
case NSTACKX_PROTOCOL_UDP:
socket->protocol = NSTACKX_PROTOCOL_UDP;
ret = CreateUdpServer(socket, sockAddr);
break;
case NSTACKX_PROTOCOL_D2D:
socket->protocol = NSTACKX_PROTOCOL_D2D;
ret = NSTACKX_EFAILED;
LOGE(TAG, "d2d not support");
break;
default:
LOGE(TAG, "protocol not support");
ret = NSTACKX_EFAILED;
break;
}
if (ret != NSTACKX_EOK) {
LOGE(TAG, "Create server socket failed! %d", ret);
free(socket);
return NULL;
}
socket->isServer = NSTACKX_TRUE;
return socket;
}
static int32_t CheckAcceptSocketValid(const Socket *serverSocket)
{
if (serverSocket == NULL || serverSocket->isServer == NSTACKX_FALSE ||
serverSocket->protocol != NSTACKX_PROTOCOL_TCP) {
LOGE(TAG, "invalue Socket for accept");
return NSTACKX_EINVAL;
}
return NSTACKX_EOK;
}
static int32_t SetAcceptSocket(SocketDesc acceptFd)
{
struct sockaddr_in localAddr;
socklen_t localAddrLen = sizeof(localAddr);
(void)memset_s(&localAddr, localAddrLen, 0, localAddrLen);
if (getsockname(acceptFd, (struct sockaddr *)&localAddr, &localAddrLen) != 0) {
LOGE(TAG, "get socket name fail %d", GetErrno());
return NSTACKX_EFAILED;
}
/* It will always failed on devices without system authority, such as third-party devices. */
if (BindToDevice(acceptFd, &localAddr) != NSTACKX_EOK) {
LOGW(TAG, "Accept client bind to device failed");
}
if (SetSocketNonBlock(acceptFd) != NSTACKX_EOK) {
LOGE(TAG, "set socket nonblock failed");
return NSTACKX_EFAILED;
}
return NSTACKX_EOK;
}
Socket *AcceptSocket(Socket *serverSocket)
{
struct sockaddr_in clientAddr;
socklen_t addrLen = sizeof(clientAddr);
(void)memset_s(&clientAddr, addrLen, 0, addrLen);
if (CheckAcceptSocketValid(serverSocket) != NSTACKX_EOK) {
LOGE(TAG, "invalue Socket for accept \n");
return NULL;
}
Socket *clientSocket = calloc(1, sizeof(Socket));
if (clientSocket == NULL) {
LOGE(TAG, "client socket malloc failed\n");
return NULL;
}
clientSocket->protocol = NSTACKX_PROTOCOL_TCP;
clientSocket->isServer = NSTACKX_FALSE;
clientSocket->sockfd = accept(serverSocket->sockfd, (struct sockaddr *)&clientAddr, &addrLen);
if (clientSocket->sockfd == INVALID_SOCKET) {
LOGE(TAG, "accept return error: %d", GetErrno());
goto L_SOCKET_FAIL;
}
if (SetAcceptSocket(clientSocket->sockfd) != NSTACKX_EOK) {
LOGE(TAG, "set accept socket failed");
goto L_SOCKET_FAIL;
}
clientSocket->dstAddr = clientAddr;
return clientSocket;
L_SOCKET_FAIL:
if (clientSocket->sockfd != INVALID_SOCKET) {
CloseSocketInner(clientSocket->sockfd);
clientSocket->sockfd = INVALID_SOCKET;
}
free(clientSocket);
return NULL;
}
int32_t CheckSocketError(void)
{
int32_t ret;
if (SocketOpWouldBlock()) {
ret = NSTACKX_EAGAIN;
} else {
LOGE(TAG, "sendto/recvfrom error: %d", GetErrno());
ret = NSTACKX_EFAILED;
}
return ret;
}
static int32_t SocketSendUdp(const Socket *socket, const uint8_t *buffer, size_t length)
{
socklen_t dstAddrLen = sizeof(struct sockaddr_in);
int32_t ret = (int32_t)sendto(socket->sockfd, buffer, length, 0, (struct sockaddr *)&socket->dstAddr, dstAddrLen);
if (ret <= 0) {
ret = CheckSocketError();
}
return ret;
}
int32_t SocketSend(const Socket *socket, const uint8_t *buffer, size_t length)
{
int32_t ret = NSTACKX_EFAILED;
if (socket == NULL || buffer == NULL) {
LOGE(TAG, "invalue socket input");
return ret;
}
if (socket->protocol == NSTACKX_PROTOCOL_TCP) {
ret = (int32_t)send(socket->sockfd, buffer, length, 0);
} else if (socket->protocol == NSTACKX_PROTOCOL_UDP) {
ret = SocketSendUdp(socket, buffer, length);
} else {
LOGE(TAG, "protocol not support %d", socket->protocol);
}
return ret;
}
static int32_t SocketRecvTcp(const Socket *socket, uint8_t *buffer, size_t length, struct sockaddr_in *srcAddr,
const socklen_t *addrLen)
{
int32_t ret = (int32_t)recv(socket->sockfd, buffer, length, 0);
if (srcAddr != NULL && *addrLen >= (socklen_t)sizeof(struct sockaddr_in)) {
*srcAddr = socket->dstAddr;
}
return ret;
}
static int32_t SocketRecvUdp(const Socket *socket, uint8_t *buffer, size_t length, struct sockaddr_in *srcAddr,
const socklen_t *addrLen)
{
struct sockaddr_in addr;
socklen_t len = sizeof(struct sockaddr_in);
(void)memset_s(&addr, sizeof(addr), 0, sizeof(addr));
int32_t ret = (int32_t)recvfrom(socket->sockfd, buffer, length, 0, (struct sockaddr *)&addr, &len);
if (ret < 0) {
ret = CheckSocketError();
} else if (ret == 0 || addr.sin_port == 0 || addr.sin_family != AF_INET) {
ret = NSTACKX_EAGAIN;
} else {
if (srcAddr != NULL && *addrLen >= (socklen_t)sizeof(struct sockaddr_in)) {
*srcAddr = addr;
}
}
return ret;
}
int32_t SocketRecv(Socket *socket, uint8_t *buffer, size_t length, struct sockaddr_in *srcAddr,
const socklen_t *addrLen)
{
int32_t ret = NSTACKX_EFAILED;
if (socket == NULL) {
LOGE(TAG, "invalue socket input");
return ret;
}
if (socket->protocol == NSTACKX_PROTOCOL_TCP) {
ret = SocketRecvTcp(socket, buffer, length, srcAddr, addrLen);
} else if (socket->protocol == NSTACKX_PROTOCOL_UDP) {
ret = SocketRecvUdp(socket, buffer, length, srcAddr, addrLen);
} else {
LOGE(TAG, "protocol not support %d", socket->protocol);
}
return ret;
}