/*
 * This file is part of the openHiTLS project.
 *
 * openHiTLS is licensed under the 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.
 */

#include "hitls_build.h"
#if defined(HITLS_BSL_SAL_LINUX) && defined(HITLS_BSL_SAL_NET)

#include <stdbool.h>

#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include "securec.h"
#include "bsl_sal.h"
#include "bsl_errno.h"
#include "sal_net.h"

typedef union {
    struct sockaddr addr;
    struct sockaddr_in6 addrIn6;
    struct sockaddr_in addrIn;
    struct sockaddr_un addrUn;
} LinuxSockAddr;

int32_t SAL_NET_Write(int32_t fd, const void *buf, uint32_t len, int32_t *err)
{
    if (err == NULL) {
        return BSL_NULL_INPUT;
    }
    int32_t ret = (int32_t)write(fd, buf, len);
    if (ret < 0) {
        *err = errno;
    }
    return ret;
}

int32_t SAL_NET_Read(int32_t fd, void *buf, uint32_t len, int32_t *err)
{
    if (err == NULL) {
        return BSL_NULL_INPUT;
    }
    int32_t ret = (int32_t)read(fd, buf, len);
    if (ret < 0) {
        *err = errno;
    }
    return ret;
}

int32_t SAL_NET_Sendto(int32_t sock, const void *buf, size_t len, int32_t flags, void *address, int32_t addrLen,
                       int32_t *err)
{
    if (err == NULL) {
        return BSL_NULL_INPUT;
    }
    int32_t ret = (int32_t)sendto(sock, buf, len, flags, (struct sockaddr *)address, (socklen_t)addrLen);
    if (ret <= 0) {
        *err = errno;
    }
    return ret;
}

int32_t SAL_NET_RecvFrom(int32_t sock, void *buf, size_t len, int32_t flags, void *address, int32_t *addrLen,
                         int32_t *err)
{
    if (err == NULL) {
        return BSL_NULL_INPUT;
    }
    int32_t ret = (int32_t)recvfrom(sock, buf, len, flags, (struct sockaddr *)address, (socklen_t *)addrLen);
    if (ret <= 0) {
        *err = errno;
    }
    return ret;
}

int32_t SAL_NET_SockAddrNew(BSL_SAL_SockAddr *sockAddr)
{
    LinuxSockAddr *addr = (LinuxSockAddr *)BSL_SAL_Calloc(1, sizeof(LinuxSockAddr));
    if (addr == NULL) {
        return BSL_MALLOC_FAIL;
    }
    *sockAddr = (BSL_SAL_SockAddr)addr;
    return BSL_SUCCESS;
}

void SAL_NET_SockAddrFree(BSL_SAL_SockAddr sockAddr)
{
    BSL_SAL_Free(sockAddr);
}

int32_t SAL_NET_SockAddrGetFamily(const BSL_SAL_SockAddr sockAddr)
{
    const LinuxSockAddr *addr = (const LinuxSockAddr *)sockAddr;
    if (addr == NULL) {
        return AF_UNSPEC;
    }
    return addr->addr.sa_family;
}

uint32_t SAL_NET_SockAddrSize(const BSL_SAL_SockAddr sockAddr)
{
    const LinuxSockAddr *addr = (const LinuxSockAddr *)sockAddr;
    if (addr == NULL) {
        return 0;
    }
    switch (addr->addr.sa_family) {
        case AF_INET:
            return sizeof(addr->addrIn);
        case AF_INET6:
            return sizeof(addr->addrIn6);
        case AF_UNIX:
            return sizeof(addr->addrUn);
        default:
            break;
    }
    return sizeof(LinuxSockAddr);
}

void SAL_NET_SockAddrCopy(BSL_SAL_SockAddr dst, BSL_SAL_SockAddr src)
{
    memcpy_s(dst, sizeof(LinuxSockAddr), src, sizeof(LinuxSockAddr));
}

int32_t SAL_Socket(int32_t af, int32_t type, int32_t protocol)
{
    return (int32_t)socket(af, type, protocol);
}

int32_t SAL_SockClose(int32_t sockId)
{
    if (close((int32_t)(long)sockId) != 0) {
        return BSL_SAL_ERR_NET_SOCKCLOSE;
    }
    return BSL_SUCCESS;
}

int32_t SAL_SetSockopt(int32_t sockId, int32_t level, int32_t name, const void *val, int32_t len)
{
    if (setsockopt((int32_t)sockId, level, name, (char *)(uintptr_t)val, (socklen_t)len) != 0) {
        return BSL_SAL_ERR_NET_SETSOCKOPT;
    }
    return BSL_SUCCESS;
}

int32_t SAL_GetSockopt(int32_t sockId, int32_t level, int32_t name, void *val, int32_t *len)
{
    if (getsockopt((int32_t)sockId, level, name, val, (socklen_t *)len) != 0) {
        return BSL_SAL_ERR_NET_GETSOCKOPT;
    }
    return BSL_SUCCESS;
}

int32_t SAL_SockListen(int32_t sockId, int32_t backlog)
{
    if (listen(sockId, backlog) != 0) {
        return BSL_SAL_ERR_NET_LISTEN;
    }
    return BSL_SUCCESS;
}

int32_t SAL_SockBind(int32_t sockId, BSL_SAL_SockAddr addr, size_t len)
{
    if (bind(sockId, (struct sockaddr *)addr, (socklen_t)len) != 0) {
        return BSL_SAL_ERR_NET_BIND;
    }
    return BSL_SUCCESS;
}

int32_t SAL_SockConnect(int32_t sockId, BSL_SAL_SockAddr addr, size_t len)
{
    if (connect(sockId, (struct sockaddr *)addr, (socklen_t)len) != 0) {
        return BSL_SAL_ERR_NET_CONNECT;
    }
    return BSL_SUCCESS;
}

int32_t SAL_SockSend(int32_t sockId, const void *msg, size_t len, int32_t flags)
{
    return (int32_t)send(sockId, msg, len, flags);
}

int32_t SAL_SockRecv(int32_t sockfd, void *buff, size_t len, int32_t flags)
{
    return (int32_t)recv(sockfd, (char *)buff, len, flags);
}

int32_t SAL_Select(int32_t nfds, void *readfds, void *writefds, void *exceptfds, void *timeout)
{
    return select(nfds, (fd_set *)readfds, (fd_set *)writefds, (fd_set *)exceptfds, (struct timeval *)timeout);
}

int32_t SAL_Ioctlsocket(int32_t sockId, long cmd, unsigned long *arg)
{
    if (ioctl(sockId, (unsigned long)cmd, arg) != 0) {
        return BSL_SAL_ERR_NET_IOCTL;
    }
    return BSL_SUCCESS;
}

int32_t SAL_SockGetLastSocketError(void)
{
    return errno;
}

#endif