summaryrefslogtreecommitdiff
path: root/components/lwp/syscall/syscall_socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'components/lwp/syscall/syscall_socket.c')
-rw-r--r--components/lwp/syscall/syscall_socket.c1544
1 files changed, 1544 insertions, 0 deletions
diff --git a/components/lwp/syscall/syscall_socket.c b/components/lwp/syscall/syscall_socket.c
new file mode 100644
index 0000000000..ff4ec5b717
--- /dev/null
+++ b/components/lwp/syscall/syscall_socket.c
@@ -0,0 +1,1544 @@
+/*
+ * Copyright (c) 2006-2025 RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscall_internal.h"
+
+#ifdef RT_USING_SAL
+/* network interfaces */
+
+union lwp_sockaddr_buffer
+{
+ struct sockaddr address;
+ struct sockaddr_un unix_address;
+};
+
+static int lwp_sockaddr_from_user(union lwp_sockaddr_buffer *kernel_address,
+ const struct musl_sockaddr *user_address,
+ socklen_t length)
+{
+ uint16_t family;
+ struct musl_sockaddr musl_address;
+
+ if (user_address == RT_NULL || length < sizeof(family) ||
+ length > sizeof(*kernel_address))
+ {
+ return -EINVAL;
+ }
+ if (!lwp_user_accessable((void *)user_address, length))
+ {
+ return -EFAULT;
+ }
+
+ rt_memset(kernel_address, 0, sizeof(*kernel_address));
+ lwp_get_from_user(&family, (void *)user_address, sizeof(family));
+ if (family == AF_UNIX || family == AF_NETLINK)
+ {
+ lwp_get_from_user(kernel_address, (void *)user_address, length);
+ }
+ else
+ {
+ if (length > sizeof(musl_address))
+ {
+ return -EINVAL;
+ }
+ rt_memset(&musl_address, 0, sizeof(musl_address));
+ lwp_get_from_user(&musl_address, (void *)user_address, length);
+ sockaddr_tolwip(&musl_address, &kernel_address->address);
+ }
+ return 0;
+}
+
+static socklen_t lwp_sockaddr_to_user(
+ struct musl_sockaddr *user_address, socklen_t user_length,
+ const union lwp_sockaddr_buffer *kernel_address,
+ socklen_t kernel_length)
+{
+ socklen_t actual_length;
+ socklen_t copy_length;
+ struct musl_sockaddr musl_address;
+
+ if (kernel_address->unix_address.sa_family == AF_UNIX)
+ {
+ actual_length = kernel_length;
+ copy_length = user_length < actual_length ? user_length : actual_length;
+ lwp_put_to_user(user_address, (void *)kernel_address, copy_length);
+ }
+ else
+ {
+ rt_memset(&musl_address, 0, sizeof(musl_address));
+ sockaddr_tomusl(&kernel_address->address, &musl_address);
+ actual_length = sizeof(musl_address);
+ copy_length = user_length < actual_length ? user_length : actual_length;
+ lwp_put_to_user(user_address, &musl_address, copy_length);
+ }
+ return actual_length;
+}
+
+/**
+ * @brief Accepts a connection on a socket.
+ *
+ * This system call is used to accept a pending connection request on a socket. It extracts the
+ * first connection request from the incoming queue, creates a new socket for the connection,
+ * and stores the address information of the remote peer in the provided address structure.
+ * The original socket must be a listening socket that has been previously bound and set to listen
+ * for incoming connections.
+ *
+ * @param[in] socket The listening socket file descriptor that is waiting for incoming connections.
+ * This socket must be in a listening state, created using `sys_socket` and
+ * bound to an address using `sys_bind`.
+ * @param[out] addr A pointer to a `musl_sockaddr` structure where the address of the connecting
+ * client will be stored. The structure is populated with information such as
+ * the client's IP address and port number.
+ * @param[in,out] addrlen A pointer to a socklen_t variable that specifies the size of the `addr`
+ * structure on input. On output, it will contain the actual size of the
+ * address returned in `addr`.
+ *
+ * @return sysret_t Returns a socket descriptor for the new connection on success, or
+ * `SYSRET_ERROR` on failure:
+ * - On success: A new socket descriptor that can be used for further
+ * communication with the connected peer.
+ * - On failure: A negative error code indicating the reason for the failure.
+ *
+ * @note The socket passed as `socket` must be in a listening state and ready to accept incoming
+ * connections. The `addr` structure is populated with the client's address details, which can
+ * be used for further operations, such as identifying the client.
+ *
+ * @warning Ensure that the `addr` structure is sufficiently large to hold the address information.
+ * The `addrlen` parameter must be set to the size of the `musl_sockaddr` structure before
+ * calling this function. Calling the function with an incorrectly sized `addrlen` may lead
+ * to buffer overflows or undefined behavior.
+ */
+sysret_t sys_accept(int socket, struct musl_sockaddr *addr, socklen_t *addrlen)
+{
+ int ret = -1;
+ union lwp_sockaddr_buffer kernel_address;
+ socklen_t uaddrlen;
+ socklen_t kaddrlen;
+
+ if (addr)
+ {
+ if (!lwp_user_accessable(addrlen, sizeof(socklen_t)))
+ {
+ return -EFAULT;
+ }
+ lwp_get_from_user(&uaddrlen, addrlen, sizeof(socklen_t));
+ if (!uaddrlen)
+ {
+ return -EINVAL;
+ }
+
+ if (!lwp_user_accessable(addr, uaddrlen))
+ {
+ return -EFAULT;
+ }
+ }
+
+ rt_memset(&kernel_address, 0, sizeof(kernel_address));
+ kaddrlen = sizeof(kernel_address);
+ ret = accept(socket, &kernel_address.address, &kaddrlen);
+ if (ret >= 0)
+ {
+ if (addr)
+ {
+ kaddrlen = lwp_sockaddr_to_user(addr, uaddrlen,
+ &kernel_address, kaddrlen);
+ lwp_put_to_user(addrlen, &kaddrlen, sizeof(socklen_t));
+ }
+ }
+ return ret;
+}
+
+/**
+ * @brief Binds a socket to a local address.
+ *
+ * This system call binds a socket to a specific local address and port. The socket must be created
+ * using `sys_socket` before it can be bound. The `bind` operation allows the application to associate
+ * a socket with a specific address, such as an IP address and port number, which can then be used
+ * for sending or receiving data.
+ *
+ * @param[in] socket The socket descriptor to which the address will be bound. This socket must
+ * be created using `sys_socket` and should not already be bound to another address.
+ * @param[in] name A pointer to a `musl_sockaddr` structure that contains the address to which
+ * the socket will be bound. This can represent an IP address and port number.
+ * @param[in] namelen The size of the `musl_sockaddr` structure in bytes. This value should be set
+ * to the actual size of the `name` structure before calling this function.
+ *
+ * @return sysret_t Returns `0` on success, or a negative error code on failure.
+ *
+ * @note The `socket` must be created before calling this function, and the address provided in `name`
+ * should be a valid local address for the socket. This operation is typically used for server-side
+ * sockets that listen for incoming connections or for any socket that needs to specify its local
+ * address before communication.
+ *
+ * @warning If the specified address is already in use by another socket or if the socket type is incompatible
+ * with the address, the function will return an error. Ensure that the address is not in use and is
+ * valid for the socket type.
+ */
+sysret_t sys_bind(int socket, const struct musl_sockaddr *name, socklen_t namelen)
+{
+ int ret;
+ union lwp_sockaddr_buffer kernel_address;
+
+ ret = lwp_sockaddr_from_user(&kernel_address, name, namelen);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ ret = bind(socket, &kernel_address.address, namelen);
+
+ return (ret < 0 ? GET_ERRNO() : ret);
+}
+
+/**
+ * @brief Shuts down part of a full-duplex connection.
+ *
+ * This system call disables further send or receive operations on a socket. The `how` parameter
+ * specifies which part of the connection should be shut down. This operation is typically used
+ * when the application no longer needs to send or receive data, but wants to keep the connection open
+ * for other purposes (e.g., receiving shutdown acknowledgment).
+ *
+ * @param[in] socket The socket descriptor to be shut down. This socket must be an open socket that
+ * has been created using `sys_socket`.
+ * @param[in] how The shutdown behavior:
+ * - `SHUT_RD`: Disables further receives on the socket. The application can no longer
+ * receive data from this socket.
+ * - `SHUT_WR`: Disables further sends on the socket. The application can no longer
+ * send data through this socket.
+ * - `SHUT_RDWR`: Disables both sends and receives on the socket. The socket is
+ * fully shut down for communication.
+ *
+ * @return sysret_t Returns `0` on success, or a negative error code (e.g., `SYSRET_ERROR`) on failure.
+ *
+ * @note The `socket` should be open and in a connected state when calling this function. The `how`
+ * parameter determines which part of the connection is to be shut down, allowing for finer control
+ * over communication termination.
+ *
+ * @warning Once a socket is shut down with `SHUT_RD`, no further data can be received on it. Similarly,
+ * after `SHUT_WR`, no more data can be sent. Calling `sys_shutdown` with incompatible `how` values
+ * or on an invalid socket may result in errors.
+ */
+sysret_t sys_shutdown(int socket, int how)
+{
+ return shutdown(socket, how);
+}
+
+/**
+ * @brief Retrieves the address of the peer connected to a socket.
+ *
+ * This system call retrieves the address of the peer (remote endpoint) that is connected to the specified
+ * socket. The socket must be connected (i.e., for stream-oriented protocols such as TCP). The address
+ * of the peer is stored in the `name` structure, and the size of the structure is updated in `namelen`.
+ *
+ * @param[in] socket The socket descriptor of the connected socket. This socket must be in a connected
+ * state (e.g., after a successful `sys_connect` or `sys_accept` call).
+ * @param[out] name A pointer to a `musl_sockaddr` structure where the address of the peer will be
+ * stored. This structure will be populated with the peer's IP address and port number.
+ * @param[in,out] namelen A pointer to a `socklen_t` variable that specifies the size of the `name` structure
+ * on input. On output, it will contain the actual size of the address returned in `name`.
+ *
+ * @return sysret_t Returns `SYSRET_OK` on success, or a negative error code on failure.
+ *
+ * @note The `socket` must be in a connected state when calling this function. The `name` structure will
+ * contain the peer's address information, which can be used for logging, debugging, or further
+ * communication.
+ *
+ * @warning Ensure that the `name` structure is sufficiently large to hold the peer's address information.
+ * The `namelen` parameter must be set to the size of the `name` structure before calling this function.
+ * Failing to provide a correctly sized `namelen` could result in buffer overflows or undefined behavior.
+ */
+sysret_t sys_getpeername(int socket, struct musl_sockaddr *name, socklen_t *namelen)
+{
+ int ret = -1;
+ union lwp_sockaddr_buffer kernel_address;
+ socklen_t unamelen;
+ socklen_t knamelen;
+
+ if (!lwp_user_accessable(namelen, sizeof(socklen_t)))
+ {
+ return -EFAULT;
+ }
+ lwp_get_from_user(&unamelen, namelen, sizeof(socklen_t));
+ if (!unamelen)
+ {
+ return -EINVAL;
+ }
+
+ if (!lwp_user_accessable(name, unamelen))
+ {
+ return -EFAULT;
+ }
+
+ rt_memset(&kernel_address, 0, sizeof(kernel_address));
+ knamelen = sizeof(kernel_address);
+ ret = getpeername(socket, &kernel_address.address, &knamelen);
+
+ if (ret == 0)
+ {
+ knamelen = lwp_sockaddr_to_user(name, unamelen,
+ &kernel_address, knamelen);
+ lwp_put_to_user(namelen, &knamelen, sizeof(socklen_t));
+ }
+ else
+ {
+ ret = GET_ERRNO();
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Retrieves the local address of the socket.
+ *
+ * This system call retrieves the local address (local endpoint) that is bound to the specified socket.
+ * The socket must be created and, if necessary, bound to a local address using `sys_bind`. The address
+ * of the local endpoint is stored in the `name` structure, and the size of the structure is updated in `namelen`.
+ *
+ * @param[in] socket The socket descriptor of the socket. This socket must be created and, if required,
+ * bound to a local address.
+ * @param[out] name A pointer to a `musl_sockaddr` structure where the local address of the socket will
+ * be stored. This structure will be populated with the local IP address and port number.
+ * @param[in,out] namelen A pointer to a `socklen_t` variable that specifies the size of the `name` structure
+ * on input. On output, it will contain the actual size of the address returned in `name`.
+ *
+ * @return sysret_t Returns `0` on success, or a negative error code on failure.
+ *
+ * @note The `socket` must be created and, if needed, bound to a local address using `sys_bind`.
+ * The `name` structure will contain the local address of the socket, which can be used for logging,
+ * debugging, or further communication.
+ *
+ * @warning Ensure that the `name` structure is sufficiently large to hold the local address information.
+ * The `namelen` parameter must be set to the size of the `name` structure before calling this function.
+ * Failing to provide a correctly sized `namelen` could result in buffer overflows or undefined behavior.
+ */
+sysret_t sys_getsockname(int socket, struct musl_sockaddr *name, socklen_t *namelen)
+{
+ int ret = -1;
+ union lwp_sockaddr_buffer kernel_address;
+ socklen_t unamelen;
+ socklen_t knamelen;
+
+ if (!lwp_user_accessable(namelen, sizeof(socklen_t)))
+ {
+ return -EFAULT;
+ }
+ lwp_get_from_user(&unamelen, namelen, sizeof(socklen_t));
+ if (!unamelen)
+ {
+ return -EINVAL;
+ }
+
+ if (!lwp_user_accessable(name, unamelen))
+ {
+ return -EFAULT;
+ }
+
+ rt_memset(&kernel_address, 0, sizeof(kernel_address));
+ knamelen = sizeof(kernel_address);
+ ret = getsockname(socket, &kernel_address.address, &knamelen);
+ if (ret == 0)
+ {
+ knamelen = lwp_sockaddr_to_user(name, unamelen,
+ &kernel_address, knamelen);
+ lwp_put_to_user(namelen, &knamelen, sizeof(socklen_t));
+ }
+ else
+ {
+ ret = GET_ERRNO();
+ }
+ return ret;
+}
+
+/**
+ * @brief Retrieves the value of a socket option.
+ *
+ * This system call retrieves the current value of a socket option for the specified socket. The socket
+ * options allow fine-grained control over various aspects of socket behavior, such as timeouts, buffering,
+ * and connection settings. The option value is stored in the `optval` buffer, and the size of the buffer
+ * is specified by the `optlen` parameter.
+ *
+ * @param[in] socket The socket descriptor for which the option value is being retrieved. The socket
+ * must be valid and open.
+ * @param[in] level The level at which the option is defined. Typically, this is `SOL_SOCKET` for general
+ * socket options, or a protocol-specific level (e.g., `IPPROTO_TCP` for TCP options).
+ * @param[in] optname The option name. This specifies which socket option to retrieve (e.g., `SO_RCVBUF`
+ * for receive buffer size or `SO_RCVBUF` for send buffer size).
+ * @param[out] optval A pointer to a buffer where the option value will be stored. The buffer's type depends
+ * on the option being retrieved.
+ * @param[in,out] optlen A pointer to a `socklen_t` variable that specifies the size of the `optval` buffer on
+ * input. On output, it will contain the actual size of the option value retrieved.
+ *
+ * @return sysret_t Returns `0` on success, or a negative error code on failure.
+ *
+ * @note The `socket` must be valid and open when calling this function. The `level` and `optname` parameters
+ * define the specific option to be retrieved. The `optval` buffer will contain the option value after
+ * the function call, and `optlen` will be updated to reflect the size of the retrieved value.
+ *
+ * @warning Ensure that the `optval` buffer is large enough to hold the value for the specified option.
+ * The `optlen` parameter must be set to the correct size of the buffer before calling this function.
+ * Failing to provide a correctly sized buffer could result in undefined behavior or buffer overflows.
+ */
+sysret_t sys_getsockopt(int socket, int level, int optname, void *optval, socklen_t *optlen)
+{
+ int ret = 0;
+ socklen_t koptlen = 0;
+ void *koptval = RT_NULL;
+
+ if (!lwp_user_accessable((void *)optlen, sizeof(uint32_t)))
+ {
+ return -EFAULT;
+ }
+
+ if (lwp_get_from_user(&koptlen, optlen, sizeof(uint32_t)) != sizeof(uint32_t))
+ {
+ return -EINVAL;
+ }
+
+ if (!lwp_user_accessable((void *)optval, koptlen))
+ {
+ return -EFAULT;
+ }
+
+ koptval = kmem_get(koptlen);
+ if (koptval == RT_NULL)
+ {
+ return -ENOMEM;
+ }
+
+ if (lwp_get_from_user(koptval, optval, koptlen) != koptlen)
+ {
+ kmem_put(koptval);
+ return -EINVAL;
+ }
+
+ convert_sockopt(&level, &optname);
+ ret = getsockopt(socket, level, optname, koptval, &koptlen);
+
+ lwp_put_to_user((void *)optval, koptval, koptlen);
+ lwp_put_to_user((void *)optlen, &koptlen, sizeof(uint32_t));
+
+ kmem_put(koptval);
+
+ return (ret < 0 ? GET_ERRNO() : ret);
+}
+
+/**
+ * @brief Sets the value of a socket option.
+ *
+ * This system call sets a socket option for the specified socket. Socket options control various aspects
+ * of socket behavior, such as timeouts, buffer sizes, or connection parameters. The option value is
+ * provided in the `optval` buffer, and its length is specified by the `optlen` parameter.
+ *
+ * @param[in] socket The socket descriptor for which the option is being set. The socket must be valid and open.
+ * @param[in] level The level at which the option is defined. This can be `SOL_SOCKET` for general socket options,
+ * or a protocol-specific level (e.g., `IPPROTO_TCP` for TCP options).
+ * @param[in] optname The option name. This specifies which socket option to set (e.g., `SO_RCVBUF` for receive buffer size).
+ * @param[in] optval A pointer to the buffer that contains the option value to set. The format of this buffer depends
+ * on the option being set.
+ * @param[in] optlen The size of the `optval` buffer. This specifies the length of the data to be used when setting
+ * the option value.
+ *
+ * @return sysret_t Returns `0` on success, or a negative error code on failure:
+ *
+ * @note The `socket` must be valid and open. The `level` and `optname` parameters define the specific option to be set,
+ * and the `optval` buffer should contain the appropriate value for that option. The `optlen` parameter must
+ * match the size of the `optval` buffer.
+ *
+ * @warning Ensure that the `optval` buffer contains valid data for the specified option. The `optlen` parameter must be
+ * set to the correct size of the `optval` buffer before calling this function. Failing to provide a correctly
+ * sized buffer could result in undefined behavior or errors.
+ */
+sysret_t sys_setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen)
+{
+ int ret;
+ void *koptval = RT_NULL;
+
+ if (!lwp_user_accessable((void *)optval, optlen))
+ {
+ return -EFAULT;
+ }
+
+ koptval = kmem_get(optlen);
+ if (koptval == RT_NULL)
+ {
+ return -ENOMEM;
+ }
+
+ if (lwp_get_from_user(koptval, (void *)optval, optlen) != optlen)
+ {
+ kmem_put(koptval);
+ return -EINVAL;
+ }
+
+ convert_sockopt(&level, &optname);
+ ret = setsockopt(socket, level, optname, koptval, optlen);
+
+ kmem_put(koptval);
+
+ return (ret < 0 ? GET_ERRNO() : ret);
+}
+
+/**
+ * @brief Establishes a connection to a remote socket.
+ *
+ * This system call attempts to establish a connection to a remote socket specified by the `name` parameter.
+ * It is typically used for client-side socket operations to connect to a server. The connection is established
+ * by using the specified socket descriptor and the address of the remote server.
+ *
+ * @param[in] socket The socket descriptor for the connection attempt. The socket must be valid and in a
+ * `SOCK_STREAM` or `SOCK_DGRAM` state, depending on the protocol.
+ * @param[in] name A pointer to a `sockaddr` structure containing the address of the remote host. This address
+ * must include the correct IP address and port number for the connection.
+ * @param[in] namelen The size of the `sockaddr` structure pointed to by `name`. This should match the size
+ * of the address structure (e.g., `sizeof(struct sockaddr_in)` for IPv4).
+ *
+ * @return sysret_t Returns `0` on success, or a negative error code on failure:
+ *
+ * @note This function may block if the connection is being established, depending on the socket's configuration.
+ * For non-blocking sockets, it will return immediately, even if the connection has not been established.
+ *
+ * @warning The `socket` must be of a valid type (e.g., `SOCK_STREAM` for TCP or `SOCK_DGRAM` for UDP).
+ * The `name` parameter must point to a valid `sockaddr` structure that contains a correctly formatted address.
+ */
+sysret_t sys_connect(int socket, const struct musl_sockaddr *name, socklen_t namelen)
+{
+ int ret;
+ union lwp_sockaddr_buffer kernel_address;
+
+ ret = lwp_sockaddr_from_user(&kernel_address, name, namelen);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ ret = connect(socket, &kernel_address.address, namelen);
+ return (ret < 0 ? GET_ERRNO() : ret);
+}
+
+/**
+ * @brief Prepares a socket to accept incoming connection requests.
+ *
+ * This system call marks a socket as a passive socket, indicating that it will be used to accept
+ * incoming connection requests. It is typically used on server-side sockets after binding a
+ * local address and port using `sys_bind()`.
+ *
+ * @param[in] socket The socket descriptor to be set up for listening. The socket must be valid
+ * and of type `SOCK_STREAM` (for TCP connections).
+ * @param[in] backlog The maximum number of pending connections that can be queued. This value
+ * is system-dependent and may be limited to a lower maximum.
+ *
+ * @return sysret_t Returns `0` on success, or a negative error code on failure.
+ *
+ * @note Before calling this function, the socket must be bound to a local address and port using
+ * `sys_bind()`. Only sockets of type `SOCK_STREAM` can be used for listening. The actual
+ * maximum queue length may be less than the specified `backlog` value, depending on system limits.
+ *
+ * @warning Ensure that the socket is properly initialized, bound, and of the correct type before calling this function.
+ * Failure to do so will result in undefined behavior or errors.
+ */
+sysret_t sys_listen(int socket, int backlog)
+{
+ return listen(socket, backlog);
+}
+
+#define MUSLC_MSG_OOB 0x0001
+#define MUSLC_MSG_PEEK 0x0002
+#define MUSLC_MSG_CTRUNC 0x0008
+#define MUSLC_MSG_DONTWAIT 0x0040
+#define MUSLC_MSG_WAITALL 0x0100
+#define MUSLC_MSG_MORE 0x8000
+
+static int netflags_muslc_2_lwip(int flags)
+{
+ int flgs = 0;
+
+ if (flags & MUSLC_MSG_PEEK)
+ {
+ flgs |= MSG_PEEK;
+ }
+ if (flags & MUSLC_MSG_WAITALL)
+ {
+ flgs |= MSG_WAITALL;
+ }
+ if (flags & MUSLC_MSG_OOB)
+ {
+ flgs |= MSG_OOB;
+ }
+ if (flags & MUSLC_MSG_DONTWAIT)
+ {
+ flgs |= MSG_DONTWAIT;
+ }
+ if (flags & MUSLC_MSG_MORE)
+ {
+ flgs |= MSG_MORE;
+ }
+ if (flags & MSG_ERRQUEUE)
+ {
+ flgs |= MSG_ERRQUEUE;
+ }
+
+ return flgs;
+}
+
+static int netflags_lwip_2_muslc(int flags)
+{
+ int flgs = 0;
+
+ if (flags & MSG_CTRUNC)
+ {
+ flgs |= MUSLC_MSG_CTRUNC;
+ }
+ return flgs;
+}
+
+static void cmsg_level_muslc_2_lwip(struct msghdr *message)
+{
+ struct cmsghdr *cmsg;
+
+ for_each_cmsghdr(cmsg, message)
+ {
+ if (!CMSG_OK(message, cmsg))
+ {
+ break;
+ }
+ if (cmsg->cmsg_level == INTF_SOL_SOCKET)
+ {
+ cmsg->cmsg_level = IMPL_SOL_SOCKET;
+ }
+ }
+}
+
+static void cmsg_level_lwip_2_muslc(struct msghdr *message)
+{
+ struct cmsghdr *cmsg;
+
+ for_each_cmsghdr(cmsg, message)
+ {
+ if (!CMSG_OK(message, cmsg))
+ {
+ break;
+ }
+ if (cmsg->cmsg_level == IMPL_SOL_SOCKET)
+ {
+ cmsg->cmsg_level = INTF_SOL_SOCKET;
+ }
+ }
+}
+
+#ifdef ARCH_MM_MMU
+static int copy_msghdr_from_user(struct msghdr *kmsg,
+ struct musl_msghdr *umsg,
+ struct iovec **out_iov, void **out_msg_control,
+ void **out_msg_name, void **out_buffer,
+ socklen_t *out_user_name_length,
+ rt_bool_t receive)
+{
+ int index;
+ size_t iovs_size;
+ struct musl_msghdr user_message;
+ struct iovec *uiov, *kiov;
+ size_t iovs_buffer_size = 0;
+ char *iovs_buffer;
+ char *buffer_cursor;
+
+ if (!lwp_user_accessable(umsg, sizeof(user_message)))
+ {
+ return -EFAULT;
+ }
+
+ lwp_get_from_user(&user_message, umsg, sizeof(user_message));
+ kmsg->msg_name = user_message.msg_name;
+ kmsg->msg_namelen = user_message.msg_namelen;
+ kmsg->msg_iov = user_message.msg_iov;
+ kmsg->msg_iovlen = user_message.msg_iovlen;
+ kmsg->msg_control = user_message.msg_control;
+ kmsg->msg_controllen = user_message.msg_controllen;
+ kmsg->msg_flags = user_message.msg_flags;
+
+ if (out_user_name_length != RT_NULL)
+ {
+ *out_user_name_length = kmsg->msg_namelen;
+ }
+
+ if (kmsg->msg_iovlen < 0 ||
+ (size_t)kmsg->msg_iovlen > SIZE_MAX / sizeof(*kmsg->msg_iov) ||
+ (!receive &&
+ kmsg->msg_namelen > sizeof(union lwp_sockaddr_buffer)))
+ {
+ return -EINVAL;
+ }
+ if (receive)
+ {
+ if (kmsg->msg_name == RT_NULL)
+ {
+ kmsg->msg_namelen = 0;
+ }
+ else if (kmsg->msg_namelen > sizeof(union lwp_sockaddr_buffer))
+ {
+ kmsg->msg_namelen = sizeof(union lwp_sockaddr_buffer);
+ }
+ if (kmsg->msg_control == RT_NULL)
+ {
+ kmsg->msg_controllen = 0;
+ }
+ }
+ if (kmsg->msg_name != RT_NULL &&
+ !lwp_user_accessable(kmsg->msg_name, kmsg->msg_namelen))
+ {
+ return -EFAULT;
+ }
+ if (kmsg->msg_control != RT_NULL &&
+ !lwp_user_accessable(kmsg->msg_control, kmsg->msg_controllen))
+ {
+ return -EFAULT;
+ }
+ if (kmsg->msg_control == RT_NULL)
+ {
+ if (kmsg->msg_controllen != 0)
+ {
+ return -EFAULT;
+ }
+ }
+ if (kmsg->msg_name == RT_NULL)
+ {
+ kmsg->msg_namelen = 0;
+ }
+
+ iovs_size = sizeof(*kmsg->msg_iov) * kmsg->msg_iovlen;
+ if (!lwp_user_accessable(kmsg->msg_iov, iovs_size))
+ {
+ return -EFAULT;
+ }
+
+ /* user and kernel */
+ kiov = kmem_get(iovs_size * 2);
+ if (!kiov)
+ {
+ return -ENOMEM;
+ }
+
+ uiov = (void *)kiov + iovs_size;
+ lwp_get_from_user(uiov, kmsg->msg_iov, iovs_size);
+
+ if (out_iov)
+ {
+ *out_iov = uiov;
+ }
+ kmsg->msg_iov = kiov;
+
+ for (index = 0; index < kmsg->msg_iovlen; ++index)
+ {
+ /*
+ * We MUST check we can copy data to user after socket done in uiov
+ * otherwise we will be lost the messages from the network!
+ */
+ if (!lwp_user_accessable(uiov->iov_base, uiov->iov_len))
+ {
+ kmem_put(kmsg->msg_iov);
+
+ return -EPERM;
+ }
+
+ if (SIZE_MAX - iovs_buffer_size < uiov->iov_len)
+ {
+ kmem_put(kmsg->msg_iov);
+ return -EINVAL;
+ }
+ iovs_buffer_size += uiov->iov_len;
+ kiov->iov_len = uiov->iov_len;
+
+ ++kiov;
+ ++uiov;
+ }
+
+ /* msg_iov and msg_control */
+ if (SIZE_MAX - iovs_buffer_size < kmsg->msg_controllen ||
+ SIZE_MAX - iovs_buffer_size - kmsg->msg_controllen <
+ kmsg->msg_namelen)
+ {
+ kmem_put(kmsg->msg_iov);
+ return -EINVAL;
+ }
+ iovs_buffer = kmem_get(iovs_buffer_size + kmsg->msg_controllen +
+ kmsg->msg_namelen);
+
+ if (!iovs_buffer)
+ {
+ kmem_put(kmsg->msg_iov);
+
+ return -ENOMEM;
+ }
+
+ *out_buffer = iovs_buffer;
+ buffer_cursor = iovs_buffer;
+ kiov = kmsg->msg_iov;
+
+ for (index = 0; index < kmsg->msg_iovlen; ++index)
+ {
+ kiov->iov_base = buffer_cursor;
+ buffer_cursor += kiov->iov_len;
+ ++kiov;
+ }
+
+ *out_msg_control = kmsg->msg_control;
+ if (kmsg->msg_control != RT_NULL)
+ {
+ kmsg->msg_control = buffer_cursor;
+ buffer_cursor += kmsg->msg_controllen;
+ }
+ *out_msg_name = kmsg->msg_name;
+ if (kmsg->msg_name != RT_NULL && kmsg->msg_namelen != 0)
+ {
+ kmsg->msg_name = buffer_cursor;
+ }
+ else
+ {
+ kmsg->msg_name = RT_NULL;
+ kmsg->msg_namelen = 0;
+ }
+
+ return 0;
+}
+#endif /* ARCH_MM_MMU */
+
+/**
+ * @brief Receives a message from a socket using a message header structure.
+ *
+ * This system call is used to receive data and associated metadata from a socket.
+ * It supports both stream-oriented and message-oriented sockets and allows control
+ * over the behavior of the receiving operation via the `flags` parameter.
+ *
+ * @param[in] socket The socket descriptor from which the message will be received.
+ * The socket must be in a valid and connected state for stream-oriented
+ * sockets or bound for message-oriented sockets.
+ * @param[out] msg A pointer to an `msghdr` structure that specifies message buffers and
+ * will receive the incoming data. The structure also stores ancillary
+ * data for advanced socket operations.
+ * @param[in] flags Modifiers that control the behavior of the receive operation. Common
+ * flags include:
+ * - `MSG_PEEK`: Peek at the incoming data without removing it from the queue.
+ * - `MSG_WAITALL`: Wait for the full request to be satisfied before returning.
+ * - `MSG_DONTWAIT`: Perform a non-blocking receive operation.
+ *
+ * @return sysret_t Returns `0` on success, or a negative error code on failure.
+ *
+ * @note The `msghdr` structure should be initialized properly, including setting up buffers for
+ * receiving the data. The function can return fewer bytes than expected depending on socket
+ * type and flags. For stream-oriented sockets, partial data may be received.
+ *
+ * @warning The `socket` must be valid and in an appropriate state. If the socket is non-blocking
+ * and no data is available, the function will return immediately.
+ *
+ * @see sys_sendmsg()
+ */
+sysret_t sys_recvmsg(int socket, struct musl_msghdr *msg, int flags)
+{
+ int flgs, ret = -1;
+ int error = 0;
+ struct msghdr kmsg;
+#ifdef ARCH_MM_MMU
+ int index;
+ size_t remaining;
+ socklen_t user_name_length;
+ void *buffer;
+ void *msg_control;
+ void *msg_name;
+ struct iovec *uiov, *kiov;
+#endif
+
+ if (!msg)
+ {
+ return -EPERM;
+ }
+
+ flgs = netflags_muslc_2_lwip(flags);
+
+#ifdef ARCH_MM_MMU
+ ret = copy_msghdr_from_user(&kmsg, msg, &uiov, &msg_control,
+ &msg_name, &buffer, &user_name_length,
+ RT_TRUE);
+
+ if (ret < 0)
+ {
+ return ret;
+ }
+ ret = recvmsg(socket, &kmsg, flgs);
+ if (ret < 0)
+ {
+ error = GET_ERRNO();
+ }
+
+ if (ret < 0)
+ {
+ goto _free_res;
+ }
+
+ kiov = kmsg.msg_iov;
+ remaining = (size_t)ret;
+
+ for (index = 0; index < kmsg.msg_iovlen && remaining != 0; ++index)
+ {
+ size_t copy_length = kiov->iov_len;
+
+ if (copy_length > remaining)
+ {
+ copy_length = remaining;
+ }
+ lwp_put_to_user(uiov->iov_base, kiov->iov_base, copy_length);
+ remaining -= copy_length;
+
+ ++kiov;
+ ++uiov;
+ }
+
+ if (msg_control != RT_NULL && kmsg.msg_controllen != 0)
+ {
+ cmsg_level_lwip_2_muslc(&kmsg);
+ lwp_put_to_user(msg_control, kmsg.msg_control,
+ kmsg.msg_controllen);
+ }
+ if (msg_name != RT_NULL && kmsg.msg_name != RT_NULL)
+ {
+ socklen_t name_length;
+
+ name_length = lwp_sockaddr_to_user(
+ (struct musl_sockaddr *)msg_name, user_name_length,
+ (const union lwp_sockaddr_buffer *)kmsg.msg_name,
+ kmsg.msg_namelen);
+ lwp_put_to_user(&msg->msg_namelen, &name_length,
+ sizeof(name_length));
+ }
+ kmsg.msg_flags = netflags_lwip_2_muslc(kmsg.msg_flags);
+ lwp_put_to_user(&msg->msg_flags, &kmsg.msg_flags, sizeof(kmsg.msg_flags));
+ lwp_put_to_user(&msg->msg_controllen, &kmsg.msg_controllen,
+ sizeof(kmsg.msg_controllen));
+
+_free_res:
+ kmem_put(buffer);
+ kmem_put(kmsg.msg_iov);
+#else
+ kmsg.msg_name = msg->msg_name;
+ kmsg.msg_namelen = msg->msg_namelen;
+ kmsg.msg_iov = msg->msg_iov;
+ kmsg.msg_iovlen = msg->msg_iovlen;
+ kmsg.msg_control = msg->msg_control;
+ kmsg.msg_controllen = msg->msg_controllen;
+ kmsg.msg_flags = msg->msg_flags;
+
+ ret = recvmsg(socket, &kmsg, flgs);
+ if (ret < 0)
+ {
+ error = GET_ERRNO();
+ }
+
+ if (!ret)
+ {
+ msg->msg_flags = kmsg.msg_flags;
+ }
+#endif /* ARCH_MM_MMU */
+
+ if (ret >= 0)
+ {
+ return ret;
+ }
+ return error != 0 ? error : ret;
+}
+
+/**
+ * @brief Receives data from a socket, optionally capturing the source address.
+ *
+ * This system call is used to receive data from a socket. For connectionless sockets, it also
+ * retrieves the address of the sender. It supports both blocking and non-blocking operations
+ * depending on the socket configuration and the specified flags.
+ *
+ * @param[in] socket The socket descriptor from which the data will be received. The socket
+ * must be valid and appropriately configured (e.g., connected or bound).
+ * @param[out] mem A pointer to the buffer where the received data will be stored.
+ * @param[in] len The maximum number of bytes to receive. This defines the size of the `mem` buffer.
+ * @param[in] flags Modifiers that control the behavior of the receive operation. Common
+ * flags include:
+ * - `MSG_PEEK`: Peek at the incoming data without removing it from the queue.
+ * - `MSG_WAITALL`: Wait for the full request to be satisfied before returning.
+ * - `MSG_DONTWAIT`: Perform a non-blocking receive operation.
+ * @param[out] from A pointer to a `musl_sockaddr` structure that will hold the address of
+ * the sender. This parameter can be `NULL` if the sender's address is not needed.
+ * @param[in,out] fromlen A pointer to a `socklen_t` variable indicating the size of the `from` buffer.
+ * On return, it will be updated with the actual size of the sender's address.
+ * This parameter is ignored if `from` is `NULL`.
+ *
+ * @return sysret_t Returns `0` on success, or a negative error code on failure:
+ *
+ * @note For stream-oriented sockets, this function behaves like `sys_recv()`, and the `from` and
+ * `fromlen` parameters are ignored. For datagram-oriented sockets, the function fills `from`
+ * with the address of the sender.
+ *
+ * @warning The `socket` must be valid and configured for receiving data. If the `mem` buffer is
+ * smaller than the received data, excess data may be discarded. In non-blocking mode,
+ * if no data is available, the function returns immediately.
+ *
+ * @see sys_sendto(), sys_recv()
+ */
+sysret_t sys_recvfrom(int socket, void *mem, size_t len, int flags,
+ struct musl_sockaddr *from, socklen_t *fromlen)
+{
+ int flgs = 0;
+ int ret = -1;
+ socklen_t user_from_length = 0;
+ socklen_t kernel_from_length = 0;
+ union lwp_sockaddr_buffer kernel_address;
+#ifdef ARCH_MM_MMU
+ void *kmem = RT_NULL;
+#endif
+
+ flgs = netflags_muslc_2_lwip(flags);
+ if (from != RT_NULL)
+ {
+ if (fromlen == RT_NULL ||
+ !lwp_user_accessable(fromlen, sizeof(*fromlen)))
+ {
+ return -EFAULT;
+ }
+ lwp_get_from_user(&user_from_length, fromlen,
+ sizeof(user_from_length));
+ if (user_from_length == 0 ||
+ !lwp_user_accessable(from, user_from_length))
+ {
+ return -EFAULT;
+ }
+ rt_memset(&kernel_address, 0, sizeof(kernel_address));
+ kernel_from_length = sizeof(kernel_address);
+ }
+#ifdef ARCH_MM_MMU
+ if (!lwp_user_accessable((void *)mem, len))
+ {
+ return -EFAULT;
+ }
+
+ kmem = kmem_get(len);
+ if (!kmem)
+ {
+ return -ENOMEM;
+ }
+
+ if (from)
+ {
+ ret = recvfrom(socket, kmem, len, flgs,
+ &kernel_address.address, &kernel_from_length);
+ }
+ else
+ {
+ ret = recvfrom(socket, kmem, len, flgs, NULL, NULL);
+ }
+
+ if (ret >= 0)
+ {
+ if (ret > 0)
+ {
+ lwp_put_to_user(mem, kmem, ret);
+ }
+ if (from != RT_NULL)
+ {
+ kernel_from_length = lwp_sockaddr_to_user(
+ from, user_from_length, &kernel_address,
+ kernel_from_length);
+ lwp_put_to_user(fromlen, &kernel_from_length,
+ sizeof(kernel_from_length));
+ }
+ }
+
+ if (ret < 0)
+ {
+ ret = GET_ERRNO();
+ }
+
+ kmem_put(kmem);
+
+ return ret;
+#else
+ if (from)
+ {
+ ret = recvfrom(socket, mem, len, flgs,
+ &kernel_address.address, &kernel_from_length);
+ if (ret >= 0)
+ {
+ kernel_from_length = lwp_sockaddr_to_user(
+ from, user_from_length, &kernel_address,
+ kernel_from_length);
+ lwp_put_to_user(fromlen, &kernel_from_length,
+ sizeof(kernel_from_length));
+ }
+ }
+ else
+ {
+ ret = recvfrom(socket, mem, len, flgs, NULL, NULL);
+ }
+ return (ret < 0 ? GET_ERRNO() : ret);
+#endif
+}
+
+/**
+ * @brief Receives data from a connected socket.
+ *
+ * This system call is used to receive data from a socket that is in a connected state.
+ * It supports various flags to modify the behavior of the receive operation.
+ *
+ * @param[in] socket The socket descriptor from which the data will be received.
+ * The socket must be in a valid and connected state.
+ * @param[out] mem A pointer to the buffer where the received data will be stored.
+ * @param[in] len The maximum number of bytes to receive. This defines the size of the `mem` buffer.
+ * @param[in] flags Modifiers that control the behavior of the receive operation. Common flags include:
+ * - `MSG_PEEK`: Peek at the incoming data without removing it from the queue.
+ * - `MSG_WAITALL`: Wait for the full request to be satisfied before returning.
+ * - `MSG_DONTWAIT`: Perform a non-blocking receive operation.
+ *
+ * @return sysret_t Returns `0` on success, or a negative error code on failure.
+ *
+ * @note The function is designed for connected sockets, such as stream-oriented sockets (e.g., TCP).
+ * For datagram-oriented sockets (e.g., UDP), use `sys_recvfrom()` to capture the sender's address if needed.
+ *
+ * @warning The `socket` must be valid and in a connected state. If the `mem` buffer is smaller than the
+ * received data, excess data may be discarded. For non-blocking sockets, the function returns
+ * immediately if no data is available.
+ *
+ * @see sys_send(), sys_recvfrom()
+ */
+sysret_t sys_recv(int socket, void *mem, size_t len, int flags)
+{
+ int flgs = 0;
+ int ret;
+ void *kmem = RT_NULL;
+
+ if (!lwp_user_accessable((void *)mem, len))
+ {
+ return -EFAULT;
+ }
+
+ kmem = kmem_get(len);
+ if (kmem == RT_NULL)
+ {
+ return -ENOMEM;
+ }
+
+ flgs = netflags_muslc_2_lwip(flags);
+ ret = recvfrom(socket, kmem, len, flgs, NULL, NULL);
+
+ if (ret > 0)
+ {
+ lwp_put_to_user((void *)mem, kmem, ret);
+ }
+ kmem_put(kmem);
+
+ return (ret < 0 ? GET_ERRNO() : ret);
+}
+
+/**
+ * @brief Sends a message from a socket using scatter-gather I/O.
+ *
+ * This system call sends data from multiple buffers specified in the `msghdr` structure. It is particularly
+ * useful for advanced socket operations requiring ancillary data or scatter-gather I/O.
+ *
+ * @param[in] socket The socket descriptor through which the data will be sent. The socket must be
+ * valid and, for connection-oriented sockets, in a connected state.
+ * @param[in] msg A pointer to a `msghdr` structure that specifies the message to be sent. This structure includes:
+ * - `msg_name`: Optional address of the target (used for connectionless sockets like UDP).
+ * - `msg_namelen`: Length of the address in `msg_name`.
+ * - `msg_iov`: An array of `iovec` structures pointing to the data buffers.
+ * - `msg_iovlen`: The number of elements in the `msg_iov` array.
+ * - `msg_control`: Optional ancillary data (e.g., file descriptors).
+ * - `msg_controllen`: Length of the ancillary data.
+ * - `msg_flags`: Flags for the message (e.g., end-of-record markers).
+ * @param[in] flags Flags that modify the behavior of the send operation. Common flags include:
+ * - `MSG_DONTWAIT`: Perform a non-blocking send operation.
+ * - `MSG_EOR`: Indicates the end of a record.
+ * - `MSG_NOSIGNAL`: Prevent the function from raising `SIGPIPE` on errors.
+ *
+ * @return sysret_t Returns `0` on success, or a negative error code on failure.
+ *
+ * @note This function is versatile and supports both connection-oriented (e.g., TCP) and
+ * connectionless (e.g., UDP) sockets. For simpler use cases, consider using `sys_send()` or `sys_sendto()`.
+ *
+ * @warning The `socket` must be configured correctly for the intended communication. For non-blocking sockets,
+ * the function may return immediately if the send buffer is full. Ancillary data in `msg_control`
+ * must be formatted correctly to avoid undefined behavior.
+ *
+ * @see sys_send(), sys_sendto(), sys_recvmsg()
+ */
+sysret_t sys_sendmsg(int socket, const struct musl_msghdr *msg, int flags)
+{
+ int flgs, ret = -1;
+ int error = 0;
+ struct msghdr kmsg;
+#ifdef ARCH_MM_MMU
+ int index;
+ void *buffer;
+ void *msg_control;
+ void *msg_name;
+ struct iovec *uiov, *kiov;
+#endif
+ if (!msg)
+ {
+ return -EPERM;
+ }
+
+ flgs = netflags_muslc_2_lwip(flags);
+
+#ifdef ARCH_MM_MMU
+ ret = copy_msghdr_from_user(&kmsg, (struct musl_msghdr *)msg, &uiov,
+ &msg_control, &msg_name, &buffer, RT_NULL,
+ RT_FALSE);
+
+ if (ret < 0)
+ {
+ return ret;
+ }
+ kiov = kmsg.msg_iov;
+
+ for (index = 0; index < kmsg.msg_iovlen; ++index)
+ {
+ lwp_get_from_user(kiov->iov_base, uiov->iov_base, kiov->iov_len);
+
+ ++kiov;
+ ++uiov;
+ }
+
+ if (msg_control != RT_NULL && kmsg.msg_controllen != 0)
+ {
+ lwp_get_from_user(kmsg.msg_control, msg_control,
+ kmsg.msg_controllen);
+ cmsg_level_muslc_2_lwip(&kmsg);
+ }
+ if (msg_name != RT_NULL && kmsg.msg_namelen != 0)
+ {
+ union lwp_sockaddr_buffer kernel_address;
+
+ ret = lwp_sockaddr_from_user(&kernel_address,
+ (struct musl_sockaddr *)msg_name,
+ kmsg.msg_namelen);
+ if (ret < 0)
+ {
+ kmem_put(buffer);
+ kmem_put(kmsg.msg_iov);
+ return ret;
+ }
+ rt_memcpy(kmsg.msg_name, &kernel_address, kmsg.msg_namelen);
+ }
+
+ ret = sendmsg(socket, &kmsg, flgs);
+ if (ret < 0)
+ {
+ error = GET_ERRNO();
+ }
+
+ kmem_put(buffer);
+ kmem_put(kmsg.msg_iov);
+#else
+ kmsg.msg_name = msg->msg_name;
+ kmsg.msg_namelen = msg->msg_namelen;
+ kmsg.msg_iov = msg->msg_iov;
+ kmsg.msg_iovlen = msg->msg_iovlen;
+ kmsg.msg_control = msg->msg_control;
+ kmsg.msg_controllen = msg->msg_controllen;
+ kmsg.msg_flags = msg->msg_flags;
+
+ ret = sendmsg(socket, &kmsg, flgs);
+ if (ret < 0)
+ {
+ error = GET_ERRNO();
+ }
+
+#endif /* ARCH_MM_MMU */
+
+ if (ret >= 0)
+ {
+ return ret;
+ }
+ return error != 0 ? error : ret;
+}
+
+/**
+ * @brief Sends data to a specific address using a socket.
+ *
+ * This system call is used to send data from a socket to a specified address. It is commonly
+ * used with connectionless sockets (e.g., UDP) but can also be used with connection-oriented
+ * sockets if the destination address needs to be overridden.
+ *
+ * @param[in] socket The socket descriptor used for sending data. It must be valid and
+ * properly configured for communication.
+ * @param[in] dataptr A pointer to the buffer containing the data to be sent.
+ * @param[in] size The size, in bytes, of the data to be sent.
+ * @param[in] flags Flags that modify the behavior of the send operation. Common flags include:
+ * - `MSG_DONTWAIT`: Perform a non-blocking send operation.
+ * - `MSG_NOSIGNAL`: Prevent the function from raising `SIGPIPE` on errors.
+ * @param[in] to A pointer to a `musl_sockaddr` structure that specifies the destination address.
+ * This parameter can be `NULL` if the socket is connection-oriented.
+ * @param[in] tolen The length of the address structure pointed to by `to`.
+ *
+ * @return sysret_t Returns `0` on success, or a negative error code on failure.
+ *
+ * @note For connectionless sockets (e.g., UDP), `to` and `tolen` must specify a valid destination address.
+ * For connection-oriented sockets (e.g., TCP), these parameters can be ignored if the connection
+ * is already established.
+ *
+ * @warning Ensure that the buffer size (`size`) matches the expected size for the data protocol in use.
+ * For non-blocking sockets, this function may return immediately if the send buffer is full.
+ *
+ * @see sys_send(), sys_sendmsg(), sys_recvfrom()
+ */
+sysret_t sys_sendto(int socket, const void *dataptr, size_t size, int flags,
+ const struct musl_sockaddr *to, socklen_t tolen)
+{
+ int flgs = 0;
+ int ret = -1;
+ union lwp_sockaddr_buffer kernel_address;
+#ifdef ARCH_MM_MMU
+ void *kmem = RT_NULL;
+#endif
+
+ flgs = netflags_muslc_2_lwip(flags);
+ if (to != RT_NULL)
+ {
+ ret = lwp_sockaddr_from_user(&kernel_address, to, tolen);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+#ifdef ARCH_MM_MMU
+ if (!lwp_user_accessable((void *)dataptr, size))
+ {
+ return -EFAULT;
+ }
+
+ kmem = kmem_get(size);
+ if (!kmem)
+ {
+ return -ENOMEM;
+ }
+
+ lwp_get_from_user(kmem, (void *)dataptr, size);
+
+ if (to)
+ {
+ ret = sendto(socket, kmem, size, flgs,
+ &kernel_address.address, tolen);
+ }
+ else
+ {
+ ret = sendto(socket, kmem, size, flgs, NULL, tolen);
+ }
+
+ if (ret < 0)
+ {
+ ret = GET_ERRNO();
+ }
+
+ kmem_put(kmem);
+
+ return ret;
+#else
+ if (to)
+ {
+ ret = sendto(socket, dataptr, size, flgs,
+ &kernel_address.address, tolen);
+ }
+ else
+ {
+ ret = sendto(socket, dataptr, size, flgs, NULL, tolen);
+ }
+ return (ret < 0 ? GET_ERRNO() : ret);
+#endif
+}
+
+/**
+ * @brief Sends data over a connected socket.
+ *
+ * This system call sends data from the specified buffer through a connected socket. It is typically
+ * used with connection-oriented sockets (e.g., TCP) but can also work with connectionless sockets
+ * (e.g., UDP) if a connection is established using `sys_connect()`.
+ *
+ * @param[in] socket The socket descriptor used for sending data. It must be a valid socket
+ * and, for connection-oriented sockets, in a connected state.
+ * @param[in] dataptr A pointer to the buffer containing the data to be sent.
+ * @param[in] size The size, in bytes, of the data to be sent.
+ * @param[in] flags Flags that modify the behavior of the send operation. Common flags include:
+ * - `MSG_DONTWAIT`: Perform a non-blocking send operation.
+ * - `MSG_NOSIGNAL`: Prevent the function from raising `SIGPIPE` on errors.
+ *
+ * @return sysret_t Returns `0` on success, or a negative error code on failure.
+ *
+ * @note For connection-oriented sockets, the socket must already be connected using `sys_connect()` or
+ * `sys_accept()`. For connectionless sockets, use `sys_sendto()` to specify the destination address.
+ *
+ * @warning If the socket is in non-blocking mode and the send buffer is full, this function may return
+ * immediately with an error. Ensure that the size of the data (`size`) matches the protocol's
+ * expectations to avoid truncation or overflow issues.
+ *
+ * @see sys_sendto(), sys_sendmsg(), sys_recv()
+ */
+sysret_t sys_send(int socket, const void *dataptr, size_t size, int flags)
+{
+ int flgs = 0;
+ int ret = 0;
+ void *kdataptr = RT_NULL;
+
+ if (!lwp_user_accessable((void *)dataptr, size))
+ {
+ return -EFAULT;
+ }
+
+ kdataptr = kmem_get(size);
+ if (kdataptr == RT_NULL)
+ {
+ return -ENOMEM;
+ }
+
+ if (lwp_get_from_user(kdataptr, (void *)dataptr, size) != size)
+ {
+ kmem_put(kdataptr);
+ return -EINVAL;
+ }
+
+ flgs = netflags_muslc_2_lwip(flags);
+ ret = sendto(socket, kdataptr, size, flgs, NULL, 0);
+
+ kmem_put(kdataptr);
+
+ return (ret < 0 ? GET_ERRNO() : ret);
+}
+
+/**
+ * @brief Creates a new socket for communication.
+ *
+ * This system call creates a new socket endpoint for communication and returns a file descriptor
+ * that can be used for subsequent socket operations, such as binding, connecting, or data transfer.
+ *
+ * @param[in] domain Specifies the protocol family to be used for the socket. Common values include:
+ * - `AF_INET`: IPv4 Internet protocols.
+ * - `AF_INET6`: IPv6 Internet protocols.
+ * - `AF_UNIX`: Local communication using UNIX domain sockets.
+ * @param[in] type Specifies the type of socket to be created. Common values include:
+ * - `SOCK_STREAM`: Provides sequenced, reliable, two-way, connection-based byte streams.
+ * - `SOCK_DGRAM`: Supports datagrams (connectionless, unreliable messages of fixed maximum length).
+ * - `SOCK_RAW`: Provides raw network protocol access.
+ * @param[in] protocol Specifies the specific protocol to be used. Typically set to `0` to use the default
+ * protocol for the specified domain and type.
+ *
+ * @return sysret_t Returns the socket file descriptor on success, or a negative error code on failure.
+ *
+ * @note The returned socket descriptor must be closed using `sys_close()` to release system resources.
+ *
+ * @warning Ensure the combination of `domain`, `type`, and `protocol` is valid. Invalid combinations
+ * may result in errors. For example, specifying `AF_INET` with `SOCK_DGRAM` is valid for UDP,
+ * but with `SOCK_STREAM` it is used for TCP.
+ *
+ * @see sys_bind(), sys_connect(), sys_accept(), sys_close()
+ */
+sysret_t sys_socket(int domain, int type, int protocol)
+{
+ int fd = -1;
+ int nonblock = 0;
+ /* not support SOCK_CLOEXEC type */
+ if (type & SOCK_CLOEXEC)
+ {
+ type &= ~SOCK_CLOEXEC;
+ }
+ if (type & SOCK_NONBLOCK)
+ {
+ nonblock = 1;
+ type &= ~SOCK_NONBLOCK;
+ }
+
+ fd = socket(domain, type, protocol);
+ if (fd < 0)
+ {
+ goto out;
+ }
+ if (nonblock)
+ {
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ }
+
+out:
+ return (fd < 0 ? GET_ERRNO() : fd);
+}
+
+/**
+ * @brief Creates a pair of connected sockets.
+ *
+ * This system call creates two connected sockets that can be used for bidirectional communication
+ * between processes or threads. The sockets are returned as file descriptors in the `fd` array.
+ *
+ * @param[in] domain Specifies the protocol family to be used for the sockets. Common values include:
+ * - `AF_UNIX`: Local communication using UNIX domain sockets.
+ * @param[in] type Specifies the type of socket to be created. Common values include:
+ * - `SOCK_STREAM`: Provides sequenced, reliable, two-way, connection-based byte streams.
+ * - `SOCK_DGRAM`: Supports datagrams (connectionless, unreliable messages of fixed maximum length).
+ * @param[in] protocol Specifies the specific protocol to be used. Typically set to `0` to use the default
+ * protocol for the specified domain and type.
+ * @param[out] fd An array of two integers where the connected socket descriptors will be stored.
+ * After a successful call:
+ * - `fd[0]`: The first socket descriptor.
+ * - `fd[1]`: The second socket descriptor.
+ *
+ * @return sysret_t Returns `0` on success, or a negative error code on failure.
+ *
+ * @note The sockets in the pair are connected and can be used for inter-process communication
+ * (IPC) or between threads in the same process.
+ *
+ * @warning Ensure the `domain`, `type`, and `protocol` combination is valid. This function is typically
+ * supported only for `AF_UNIX` domain.
+ *
+ * @see sys_socket(), sys_close()
+ */
+sysret_t sys_socketpair(int domain, int type, int protocol, int fd[2])
+{
+#ifdef RT_USING_SAL
+ int ret = 0;
+ int k_fd[2];
+
+ if (!lwp_user_accessable((void *)fd, sizeof(int[2])))
+ {
+ return -EFAULT;
+ }
+
+ ret = socketpair(domain, type, protocol, k_fd);
+
+ if (ret == 0)
+ {
+ lwp_put_to_user(fd, k_fd, sizeof(int[2]));
+ }
+
+ return ret;
+#else
+ return -ELIBACC;
+#endif
+}
+
+/**
+ * @brief Closes an open socket.
+ *
+ * This system call is used to close a previously opened socket. Once the socket is closed, it is no longer
+ * valid for any further operations, such as sending, receiving, or other socket-related functions.
+ *
+ * @param[in] socket The socket descriptor to be closed. This descriptor must be a valid socket that was
+ * previously created with `sys_socket()` or related functions.
+ *
+ * @return sysret_t Returns `0` on success, or a negative error code on failure.
+ *
+ * @note Once a socket is closed, any attempts to use the socket for communication will result in an error.
+ * The system will release any resources associated with the socket.
+ *
+ * @warning Make sure that no data is being transferred or pending on the socket before closing it.
+ * Closing an active socket might lead to data loss.
+ *
+ * @see sys_socket(), sys_shutdown()
+ */
+sysret_t sys_closesocket(int socket)
+{
+ sysret_t ret;
+ ret = closesocket(socket);
+ return ret;
+}
+
+#endif