summaryrefslogtreecommitdiff
path: root/components/net
diff options
context:
space:
mode:
authorBernard Xiong <[email protected]>2026-08-17 17:15:34 +0800
committerGitHub <[email protected]>2026-08-17 17:15:34 +0800
commit991f0ffca6ca475adcffc7833876bb8151f16e9c (patch)
tree5b69ef28de0b15e1a1d23e53136d37c5e86a8a59 /components/net
parentd2acd7587eec680b57f8777c5c7a8dce7369471e (diff)
Add AF_UNIX sockets implementation (#11681)
* components/dfs: support unix socket nodes AF_UNIX pathname binding and descriptor passing require DFSv2 socket nodes and retained open file descriptions across fd tables. Add socket-node creation for tmpfs and devtmpfs, fd reference helpers, and socket F_SETFL forwarding. Impact: DFSv2 socket nodes and descriptor reference handling. Validation: git diff --cached --check. * components/net/sal: support local protocol providers Local IPC protocol families do not have a backing network device. Store the selected provider in each SAL socket and add a local provider registry while preserving netdev checks for Internet sockets. Handle DFSv2 close semantics, socketpair flags, and MSG_CTRUNC for AF_UNIX integration. Impact: SAL protocol dispatch for all socket families. Validation: git diff --cached --check. * components/lwp: support unix socket messages Musl AF_UNIX addresses and ancillary data require explicit ABI and user-memory conversion at the LWP syscall boundary. Add the musl msghdr layout, bounded address and message copying, control-message level conversion, and MSG_CTRUNC translation. Correct receive buffer allocation and copy lengths while handling messages. Impact: LWP socket syscalls when SAL is enabled. Validation: git diff --cached --check. * components/net/af_unix: add local sockets Add an opt-in AF_UNIX provider for pathname-based local IPC without a synthetic network device. Support datagram and stream sockets, blocking and nonblocking I/O, timeouts, poll, socketpair, and SCM_RIGHTS descriptor passing. Include bounded Kconfig settings, component documentation, and utest coverage. Impact: enabled only by RT_USING_AF_UNIX and requires SAL POSIX with DFSv2. Validation: git diff --cached --check. * Fix AF_UNIX CI checks Apply the repository clang-format rules to the affected source lines. Suppress the cppcheck false positive for the devtmpfs list iterator. No functional behavior is changed. * components/lwp: preserve NULL optional msghdr buffers When msg_name or msg_control is NULL, keep the kernel pointer NULL instead of substituting an uninitialized buffer. Reject a NULL control buffer with a nonzero length as EFAULT. Impact: LWP sendmsg/recvmsg conversion on MMU targets. Validation: git diff --cached --check. * components/net/af_unix: fix poll UAF, namespace leak, and SCM_RIGHTS cycles Poll only the local wait queue and wake writers from the receiver. Free namespace entries on detach, and reject AF_UNIX descriptors in SCM_RIGHTS until cycle collection exists. Impact: AF_UNIX poll, bind lifetime, and descriptor passing. Validation: git diff --cached --check. * components/dfs: add DFSv2 AF_UNIX utest suite Add opt-in tests under dfs/utest/v2/af_unix for socket nodes, IPC, poll peer-close, namespace cleanup, and SCM_RIGHTS rejection. Sources are built only when RT_UTEST_TC_USING_DFS_V2_AF_UNIX and the selected test groups are enabled. Impact: DFS utest menu and build; no production AF_UNIX behavior change. Validation: git diff --cached --check.
Diffstat (limited to 'components/net')
-rw-r--r--components/net/Kconfig1
-rw-r--r--components/net/af_unix/Kconfig42
-rw-r--r--components/net/af_unix/README.md57
-rw-r--r--components/net/af_unix/SConscript12
-rw-r--r--components/net/af_unix/include/af_unix.h20
-rw-r--r--components/net/af_unix/src/af_unix_core.c1149
-rw-r--r--components/net/af_unix/src/af_unix_dgram.c253
-rw-r--r--components/net/af_unix/src/af_unix_internal.h164
-rw-r--r--components/net/af_unix/src/af_unix_namespace.c292
-rw-r--r--components/net/af_unix/src/af_unix_rights.c364
-rw-r--r--components/net/af_unix/src/af_unix_stream.c483
-rw-r--r--components/net/af_unix/testcases/af_unix_test.c767
-rw-r--r--components/net/sal/include/sal_low_lvl.h5
-rw-r--r--components/net/sal/include/sal_socket.h3
-rw-r--r--components/net/sal/socket/net_sockets.c20
-rw-r--r--components/net/sal/src/sal_socket.c246
16 files changed, 3798 insertions, 80 deletions
diff --git a/components/net/Kconfig b/components/net/Kconfig
index 7dfd1b101b..621448375f 100644
--- a/components/net/Kconfig
+++ b/components/net/Kconfig
@@ -1,6 +1,7 @@
menu "Network"
rsource "sal/Kconfig"
+rsource "af_unix/Kconfig"
rsource "netdev/Kconfig"
rsource "lwip/Kconfig"
rsource "at/Kconfig"
diff --git a/components/net/af_unix/Kconfig b/components/net/af_unix/Kconfig
new file mode 100644
index 0000000000..b078ae4534
--- /dev/null
+++ b/components/net/af_unix/Kconfig
@@ -0,0 +1,42 @@
+menuconfig RT_USING_AF_UNIX
+ bool "AF_UNIX local sockets"
+ depends on RT_USING_SAL
+ depends on SAL_USING_POSIX
+ depends on RT_USING_DFS_V2
+ default n
+ help
+ Enable pathname-based AF_UNIX SOCK_DGRAM and SOCK_STREAM sockets.
+
+if RT_USING_AF_UNIX
+
+config AF_UNIX_DGRAM_MAX_SIZE
+ int "Maximum datagram size"
+ range 128 65535
+ default 4096
+
+config AF_UNIX_DGRAM_QUEUE_LEN
+ int "Datagram receive queue length"
+ range 1 256
+ default 16
+
+config AF_UNIX_STREAM_BUFFER_SIZE
+ int "Stream receive buffer size"
+ range 256 65535
+ default 4096
+
+config AF_UNIX_LISTEN_BACKLOG_MAX
+ int "Maximum stream listen backlog"
+ range 1 128
+ default 16
+
+config AF_UNIX_RIGHTS_MAX
+ int "Maximum file descriptors per SCM_RIGHTS message"
+ range 1 253
+ default 16
+
+config RT_AF_UNIX_USING_TESTCASES
+ bool "Build AF_UNIX test cases"
+ depends on RT_USING_UTESTCASES
+ default n
+
+endif
diff --git a/components/net/af_unix/README.md b/components/net/af_unix/README.md
new file mode 100644
index 0000000000..6301402635
--- /dev/null
+++ b/components/net/af_unix/README.md
@@ -0,0 +1,57 @@
+# AF_UNIX local sockets
+
+The AF_UNIX component provides pathname-based local IPC through the existing
+SAL and POSIX socket APIs. It supports `SOCK_DGRAM`, `SOCK_STREAM`, and
+`socketpair()` for both socket types.
+
+## Configuration
+
+Enable `RT_USING_AF_UNIX`. The component requires `RT_USING_SAL`,
+`SAL_USING_POSIX`, and `RT_USING_DFS_V2`.
+
+- `AF_UNIX_DGRAM_MAX_SIZE` bounds one datagram.
+- `AF_UNIX_DGRAM_QUEUE_LEN` bounds queued datagrams per socket.
+- `AF_UNIX_STREAM_BUFFER_SIZE` bounds each stream receive buffer.
+- `AF_UNIX_LISTEN_BACKLOG_MAX` caps the stream accept queue.
+- `AF_UNIX_RIGHTS_MAX` bounds the file descriptors in one `SCM_RIGHTS` send.
+- `RT_AF_UNIX_USING_TESTCASES` builds the component utest suite.
+
+## Pathname behavior
+
+`bind()` creates an `S_IFSOCK` node through DFSv2. The mounted filesystem must
+support special nodes through `create_vnode()`; tmpfs and devtmpfs support
+socket nodes directly. Closing a bound socket leaves its pathname in the
+filesystem. Applications should call `unlink()` before rebinding, which
+matches common Unix daemon behavior.
+
+Removing a pathname prevents new lookups. Existing stream connections and
+connected datagram endpoints continue to reference their established peers.
+
+## Descriptor passing
+
+`sendmsg()` and `recvmsg()` support one or more file descriptors in
+`SOL_SOCKET`/`SCM_RIGHTS` control messages. The queued reference remains valid
+after the sender closes its descriptor. On receive, each reference is installed
+as a new descriptor in the receiving process and retains the same open file
+description, including its shared file position. Passing an AF_UNIX socket
+descriptor is not supported and returns `EOPNOTSUPP`.
+
+For datagram sockets, the control message is atomic with its datagram. For
+stream sockets, it is associated with the first byte written by `sendmsg()` and
+is delivered when a receive consumes that byte. A receive without a control
+buffer discards associated descriptors and reports `MSG_CTRUNC`; `MSG_PEEK`
+does not install or consume descriptors. At least one payload byte is required
+when sending `SCM_RIGHTS`.
+
+Only `SCM_RIGHTS` ancillary data is supported. Credentials and other control
+message types return `EOPNOTSUPP`.
+
+## Supported operations
+
+The component implements bind, connect, listen, accept, send/receive,
+sendto/recvfrom, sendmsg/recvmsg with descriptor passing, shutdown, socket
+options, nonblocking I/O, timeouts, poll/select readiness, address queries, and
+socketpair.
+
+Linux abstract namespace addresses and credential ancillary data are not
+supported. The component requires DFSv2.
diff --git a/components/net/af_unix/SConscript b/components/net/af_unix/SConscript
new file mode 100644
index 0000000000..c85fea549f
--- /dev/null
+++ b/components/net/af_unix/SConscript
@@ -0,0 +1,12 @@
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('src/*.c')
+CPPPATH = [cwd + '/include', cwd + '/src']
+
+if GetDepend('RT_AF_UNIX_USING_TESTCASES'):
+ src += Glob('testcases/*.c')
+
+group = DefineGroup('AF_UNIX', src, depend=['RT_USING_AF_UNIX'], CPPPATH=CPPPATH)
+
+Return('group')
diff --git a/components/net/af_unix/include/af_unix.h b/components/net/af_unix/include/af_unix.h
new file mode 100644
index 0000000000..cd14d0efe3
--- /dev/null
+++ b/components/net/af_unix/include/af_unix.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef AF_UNIX_H__
+#define AF_UNIX_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int af_unix_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AF_UNIX_H__ */
diff --git a/components/net/af_unix/src/af_unix_core.c b/components/net/af_unix/src/af_unix_core.c
new file mode 100644
index 0000000000..a6880f5c63
--- /dev/null
+++ b/components/net/af_unix/src/af_unix_core.c
@@ -0,0 +1,1149 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <af_unix.h>
+#include "af_unix_internal.h"
+
+static struct rt_mutex af_unix_core_lock;
+static struct af_unix_socket *af_unix_handles[SAL_SOCKETS_NUM];
+
+int af_unix_error(int error)
+{
+ rt_set_errno(error);
+ return -1;
+}
+
+void af_unix_lock(void)
+{
+ rt_mutex_take(&af_unix_core_lock, RT_WAITING_FOREVER);
+}
+
+void af_unix_unlock(void)
+{
+ rt_mutex_release(&af_unix_core_lock);
+}
+
+int af_unix_is_nonblocking(const struct af_unix_socket *sock, int flags)
+{
+ return ((sock->flags & O_NONBLOCK) != 0 || (flags & MSG_DONTWAIT) != 0);
+}
+
+int af_unix_wait(rt_wqueue_t *queue, int timeout)
+{
+ int result;
+
+ result = rt_wqueue_wait_interruptible(queue, 0, timeout);
+ if (result == -RT_EINTR)
+ {
+ return af_unix_error(EINTR);
+ }
+ if (result != RT_EOK)
+ {
+ return af_unix_error(EAGAIN);
+ }
+ return 0;
+}
+
+static void af_unix_message_queue_clear_locked(struct af_unix_socket *sock)
+{
+ while (!rt_list_isempty(&sock->message_queue))
+ {
+ struct af_unix_message *message;
+
+ message = rt_list_entry(sock->message_queue.next,
+ struct af_unix_message, node);
+ rt_list_remove(&message->node);
+ if (message->rights != RT_NULL)
+ {
+ af_unix_rights_defer_locked(message->rights);
+ }
+ rt_free(message);
+ }
+ sock->message_count = 0;
+}
+
+static void af_unix_socket_destroy_locked(struct af_unix_socket *sock)
+{
+ af_unix_message_queue_clear_locked(sock);
+ af_unix_rights_list_defer_locked(&sock->rights_queue);
+ if (sock->stream_buffer != RT_NULL)
+ {
+ rt_free(sock->stream_buffer);
+ }
+ rt_free(sock);
+}
+
+void af_unix_socket_ref_locked(struct af_unix_socket *sock)
+{
+ RT_ASSERT(sock != RT_NULL);
+ RT_ASSERT(sock->ref_count > 0);
+ sock->ref_count++;
+}
+
+void af_unix_socket_unref_locked(struct af_unix_socket *sock)
+{
+ RT_ASSERT(sock != RT_NULL);
+ RT_ASSERT(sock->ref_count > 0);
+
+ sock->ref_count--;
+ if (sock->ref_count == 0)
+ {
+ af_unix_socket_destroy_locked(sock);
+ }
+}
+
+struct af_unix_socket *af_unix_socket_create_locked(int type)
+{
+ struct af_unix_socket *sock;
+
+ sock = (struct af_unix_socket *)rt_calloc(1, sizeof(*sock));
+ if (sock == RT_NULL)
+ {
+ return RT_NULL;
+ }
+
+ if (type == SOCK_STREAM)
+ {
+ sock->stream_buffer = (char *)rt_malloc(AF_UNIX_STREAM_BUFFER_SIZE);
+ if (sock->stream_buffer == RT_NULL)
+ {
+ rt_free(sock);
+ return RT_NULL;
+ }
+ }
+
+ sock->handle = -1;
+ sock->type = type;
+ sock->ref_count = 1;
+ sock->receive_timeout = RT_WAITING_FOREVER;
+ sock->send_timeout = RT_WAITING_FOREVER;
+ rt_wqueue_init(&sock->wait_queue);
+ rt_list_init(&sock->message_queue);
+ rt_list_init(&sock->rights_queue);
+ rt_list_init(&sock->pending_queue);
+ rt_list_init(&sock->pending_node);
+ af_unix_address_set(&sock->local_address, &sock->local_length, RT_NULL);
+ af_unix_address_set(&sock->peer_address, &sock->peer_length, RT_NULL);
+
+ return sock;
+}
+
+int af_unix_handle_alloc_locked(struct af_unix_socket *sock)
+{
+ int handle;
+
+ for (handle = 0; handle < SAL_SOCKETS_NUM; handle++)
+ {
+ if (af_unix_handles[handle] == RT_NULL)
+ {
+ af_unix_handles[handle] = sock;
+ sock->handle = handle;
+ return handle;
+ }
+ }
+
+ return -1;
+}
+
+void af_unix_handle_remove_locked(struct af_unix_socket *sock)
+{
+ int handle;
+
+ handle = sock->handle;
+ if (handle >= 0 && handle < SAL_SOCKETS_NUM &&
+ af_unix_handles[handle] == sock)
+ {
+ af_unix_handles[handle] = RT_NULL;
+ sock->handle = -1;
+ af_unix_socket_unref_locked(sock);
+ }
+}
+
+struct af_unix_socket *af_unix_socket_get(int handle)
+{
+ struct af_unix_socket *sock = RT_NULL;
+
+ af_unix_lock();
+ if (handle >= 0 && handle < SAL_SOCKETS_NUM)
+ {
+ sock = af_unix_handles[handle];
+ if (sock != RT_NULL)
+ {
+ af_unix_socket_ref_locked(sock);
+ }
+ }
+ af_unix_unlock();
+
+ if (sock == RT_NULL)
+ {
+ af_unix_error(EBADF);
+ }
+ return sock;
+}
+
+void af_unix_socket_put(struct af_unix_socket *sock)
+{
+ int error;
+
+ error = rt_get_errno();
+ af_unix_lock();
+ af_unix_socket_unref_locked(sock);
+ af_unix_unlock();
+ af_unix_rights_drain();
+ rt_set_errno(error);
+}
+
+static void af_unix_disconnect_locked(struct af_unix_socket *sock)
+{
+ int reciprocal;
+ struct af_unix_socket *peer;
+
+ peer = sock->peer;
+ if (peer == RT_NULL)
+ {
+ return;
+ }
+
+ rt_wqueue_wakeup_all(&sock->wait_queue,
+ (void *)(rt_ubase_t)(POLLIN | POLLOUT | POLLHUP));
+ rt_wqueue_wakeup_all(&peer->wait_queue,
+ (void *)(rt_ubase_t)(POLLIN | POLLOUT | POLLHUP));
+
+ reciprocal = (peer->peer == sock);
+ sock->peer = RT_NULL;
+ if (reciprocal)
+ {
+ peer->peer = RT_NULL;
+ af_unix_socket_unref_locked(sock);
+ }
+ af_unix_socket_unref_locked(peer);
+}
+
+void af_unix_set_peer_locked(struct af_unix_socket *sock,
+ struct af_unix_socket *peer)
+{
+ if (sock->peer == peer)
+ {
+ return;
+ }
+ if (sock->peer != RT_NULL)
+ {
+ af_unix_disconnect_locked(sock);
+ }
+ if (peer != RT_NULL)
+ {
+ af_unix_socket_ref_locked(peer);
+ sock->peer = peer;
+ }
+}
+
+int af_unix_connect_peers_locked(struct af_unix_socket *first,
+ struct af_unix_socket *second)
+{
+ if (first->peer != RT_NULL || second->peer != RT_NULL)
+ {
+ return af_unix_error(EISCONN);
+ }
+
+ af_unix_socket_ref_locked(second);
+ first->peer = second;
+ af_unix_socket_ref_locked(first);
+ second->peer = first;
+ first->connected = 1;
+ second->connected = 1;
+ return 0;
+}
+
+void af_unix_socket_close_locked(struct af_unix_socket *sock)
+{
+ if (sock->closed)
+ {
+ return;
+ }
+
+ sock->closed = 1;
+ sock->read_shutdown = 1;
+ sock->write_shutdown = 1;
+ af_unix_namespace_detach_locked(sock);
+
+ while (!rt_list_isempty(&sock->pending_queue))
+ {
+ struct af_unix_socket *pending;
+
+ pending = rt_list_entry(sock->pending_queue.next,
+ struct af_unix_socket, pending_node);
+ rt_list_remove(&pending->pending_node);
+ pending->pending = 0;
+ sock->pending_count--;
+ af_unix_handle_remove_locked(pending);
+ af_unix_socket_close_locked(pending);
+ }
+
+ af_unix_message_queue_clear_locked(sock);
+ rt_wqueue_wakeup_all(&sock->wait_queue,
+ (void *)(rt_ubase_t)(POLLIN | POLLOUT | POLLHUP | POLLERR));
+ af_unix_disconnect_locked(sock);
+}
+
+void af_unix_wakeup_writable_locked(struct af_unix_socket *sock)
+{
+ int handle;
+ struct af_unix_socket *other;
+
+ rt_wqueue_wakeup_all(&sock->wait_queue, (void *)(rt_ubase_t)POLLOUT);
+ for (handle = 0; handle < SAL_SOCKETS_NUM; handle++)
+ {
+ other = af_unix_handles[handle];
+ if (other != RT_NULL && other->peer == sock)
+ {
+ rt_wqueue_wakeup_all(&other->wait_queue,
+ (void *)(rt_ubase_t)POLLOUT);
+ }
+ }
+}
+
+static int af_unix_socket_create(int domain, int type, int protocol)
+{
+ int handle;
+ struct af_unix_socket *sock;
+
+ if (domain != AF_UNIX || protocol != 0 ||
+ (type != SOCK_DGRAM && type != SOCK_STREAM))
+ {
+ return af_unix_error(EPROTONOSUPPORT);
+ }
+
+ af_unix_lock();
+ sock = af_unix_socket_create_locked(type);
+ if (sock == RT_NULL)
+ {
+ af_unix_unlock();
+ return af_unix_error(ENOMEM);
+ }
+
+ handle = af_unix_handle_alloc_locked(sock);
+ if (handle < 0)
+ {
+ af_unix_socket_unref_locked(sock);
+ af_unix_unlock();
+ return af_unix_error(EMFILE);
+ }
+ af_unix_unlock();
+ return handle;
+}
+
+static int af_unix_close(int handle)
+{
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+
+ af_unix_lock();
+ af_unix_handle_remove_locked(sock);
+ af_unix_socket_close_locked(sock);
+ af_unix_unlock();
+ af_unix_socket_put(sock);
+ return 0;
+}
+
+static int af_unix_bind(int handle, const struct sockaddr *address,
+ socklen_t length)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ result = af_unix_namespace_bind(sock, address, length);
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_listen(int handle, int backlog)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ result = af_unix_stream_listen(sock, backlog);
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_connect(int handle, const struct sockaddr *address,
+ socklen_t length)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ if (sock->type == SOCK_DGRAM)
+ {
+ result = af_unix_dgram_connect(sock, address, length);
+ }
+ else
+ {
+ result = af_unix_stream_connect(sock, address, length);
+ }
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_accept(int handle, struct sockaddr *address,
+ socklen_t *length)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ result = af_unix_stream_accept(sock, address, length);
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_sendto_with_rights(
+ int handle, const void *data, size_t size, int flags,
+ const struct sockaddr *to, socklen_t to_length,
+ struct af_unix_rights *rights)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ if (sock->type == SOCK_DGRAM)
+ {
+ result = af_unix_dgram_send(sock, data, size, flags, to, to_length,
+ rights);
+ }
+ else
+ {
+ if (to != RT_NULL)
+ {
+ result = af_unix_error(EISCONN);
+ }
+ else
+ {
+ result = af_unix_stream_send(sock, data, size, flags, rights);
+ }
+ }
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_sendto(int handle, const void *data, size_t size, int flags,
+ const struct sockaddr *to, socklen_t to_length)
+{
+ return af_unix_sendto_with_rights(handle, data, size, flags, to,
+ to_length, RT_NULL);
+}
+
+static int af_unix_recvfrom_with_rights(
+ int handle, void *data, size_t size, int flags, struct sockaddr *from,
+ socklen_t *from_length, rt_list_t *rights)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ if (sock->type == SOCK_DGRAM)
+ {
+ result = af_unix_dgram_receive(sock, data, size, flags,
+ from, from_length, rights);
+ }
+ else
+ {
+ result = af_unix_stream_receive(sock, data, size, flags, rights);
+ if (result >= 0 && from != RT_NULL)
+ {
+ (void)af_unix_address_copy(from, from_length,
+ &sock->peer_address,
+ sock->peer_length);
+ }
+ }
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_recvfrom(int handle, void *data, size_t size, int flags,
+ struct sockaddr *from, socklen_t *from_length)
+{
+ int result;
+ rt_list_t rights;
+
+ rt_list_init(&rights);
+ result = af_unix_recvfrom_with_rights(handle, data, size, flags, from,
+ from_length, &rights);
+ af_unix_rights_list_release(&rights);
+ return result;
+}
+
+static int af_unix_sendmsg(int handle, const struct msghdr *message, int flags)
+{
+ int error;
+ int result;
+ int index;
+ size_t total = 0;
+ size_t offset = 0;
+ char *buffer = RT_NULL;
+ struct af_unix_rights *rights = RT_NULL;
+
+ if (message == RT_NULL || message->msg_iov == RT_NULL ||
+ message->msg_iovlen < 0)
+ {
+ return af_unix_error(EINVAL);
+ }
+ for (index = 0; index < message->msg_iovlen; index++)
+ {
+ if (SIZE_MAX - total < message->msg_iov[index].iov_len)
+ {
+ return af_unix_error(EMSGSIZE);
+ }
+ total += message->msg_iov[index].iov_len;
+ }
+ if (message->msg_controllen != 0 && total == 0)
+ {
+ return af_unix_error(EINVAL);
+ }
+ if (af_unix_rights_create(message, &rights) < 0)
+ {
+ return -1;
+ }
+
+ if (total != 0)
+ {
+ buffer = (char *)rt_malloc(total);
+ if (buffer == RT_NULL)
+ {
+ af_unix_rights_release(rights);
+ return af_unix_error(ENOMEM);
+ }
+ for (index = 0; index < message->msg_iovlen; index++)
+ {
+ rt_memcpy(buffer + offset, message->msg_iov[index].iov_base,
+ message->msg_iov[index].iov_len);
+ offset += message->msg_iov[index].iov_len;
+ }
+ }
+
+ result = af_unix_sendto_with_rights(
+ handle, buffer, total, flags,
+ (const struct sockaddr *)message->msg_name,
+ message->msg_namelen, rights);
+ if (result >= 0)
+ {
+ rights = RT_NULL;
+ }
+ if (buffer != RT_NULL)
+ {
+ error = rt_get_errno();
+ rt_free(buffer);
+ rt_set_errno(error);
+ }
+ af_unix_rights_release(rights);
+ return result;
+}
+
+static int af_unix_recvmsg(int handle, struct msghdr *message, int flags)
+{
+ int error;
+ int result;
+ int index;
+ size_t total = 0;
+ size_t offset = 0;
+ char *buffer = RT_NULL;
+ rt_list_t rights;
+
+ if (message == RT_NULL || message->msg_iov == RT_NULL ||
+ message->msg_iovlen < 0)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ for (index = 0; index < message->msg_iovlen; index++)
+ {
+ if (SIZE_MAX - total < message->msg_iov[index].iov_len)
+ {
+ return af_unix_error(EMSGSIZE);
+ }
+ total += message->msg_iov[index].iov_len;
+ }
+ if (total != 0)
+ {
+ buffer = (char *)rt_malloc(total);
+ if (buffer == RT_NULL)
+ {
+ return af_unix_error(ENOMEM);
+ }
+ }
+
+ rt_list_init(&rights);
+ message->msg_flags = 0;
+ result = af_unix_recvfrom_with_rights(
+ handle, buffer, total, flags,
+ (struct sockaddr *)message->msg_name, &message->msg_namelen,
+ &rights);
+ if (result >= 0)
+ {
+ size_t remaining = (size_t)result;
+
+ for (index = 0; index < message->msg_iovlen && remaining != 0; index++)
+ {
+ size_t copy_length = message->msg_iov[index].iov_len;
+
+ if (copy_length > remaining)
+ {
+ copy_length = remaining;
+ }
+ rt_memcpy(message->msg_iov[index].iov_base,
+ buffer + offset, copy_length);
+ offset += copy_length;
+ remaining -= copy_length;
+ }
+ if ((flags & MSG_PEEK) != 0)
+ {
+ message->msg_controllen = 0;
+ }
+ else
+ {
+ (void)af_unix_rights_deliver(&rights, message);
+ }
+ }
+ af_unix_rights_list_release(&rights);
+ if (buffer != RT_NULL)
+ {
+ error = rt_get_errno();
+ rt_free(buffer);
+ rt_set_errno(error);
+ }
+ return result;
+}
+
+static int af_unix_getsockname(int handle, struct sockaddr *address,
+ socklen_t *length)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ af_unix_lock();
+ result = af_unix_address_copy(address, length, &sock->local_address,
+ sock->local_length);
+ af_unix_unlock();
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_getpeername(int handle, struct sockaddr *address,
+ socklen_t *length)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ af_unix_lock();
+ if (!sock->connected)
+ {
+ result = af_unix_error(ENOTCONN);
+ }
+ else
+ {
+ result = af_unix_address_copy(address, length, &sock->peer_address,
+ sock->peer_length);
+ }
+ af_unix_unlock();
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_timeout_from_timeval(const struct timeval *time_value)
+{
+ long long milliseconds;
+
+ if (time_value->tv_sec < 0 || time_value->tv_usec < 0 ||
+ time_value->tv_usec >= 1000000)
+ {
+ return -2;
+ }
+ if (time_value->tv_sec == 0 && time_value->tv_usec == 0)
+ {
+ return RT_WAITING_FOREVER;
+ }
+
+ milliseconds = (long long)time_value->tv_sec * 1000LL;
+ milliseconds += ((long long)time_value->tv_usec + 999LL) / 1000LL;
+ if (milliseconds > INT_MAX)
+ {
+ milliseconds = INT_MAX;
+ }
+ return (int)milliseconds;
+}
+
+static void af_unix_timeval_from_timeout(int timeout,
+ struct timeval *time_value)
+{
+ if (timeout == RT_WAITING_FOREVER)
+ {
+ time_value->tv_sec = 0;
+ time_value->tv_usec = 0;
+ }
+ else
+ {
+ time_value->tv_sec = timeout / 1000;
+ time_value->tv_usec = (timeout % 1000) * 1000;
+ }
+}
+
+static int af_unix_getsockopt(int handle, int level, int option,
+ void *value, socklen_t *length)
+{
+ int result = 0;
+ int int_value = 0;
+ struct timeval time_value;
+ struct af_unix_socket *sock;
+
+ if (level != SOL_SOCKET || value == RT_NULL || length == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ af_unix_lock();
+ switch (option)
+ {
+ case SO_RCVTIMEO:
+ case SO_SNDTIMEO:
+ if (*length < sizeof(time_value))
+ {
+ result = af_unix_error(EINVAL);
+ break;
+ }
+ af_unix_timeval_from_timeout(option == SO_RCVTIMEO ? sock->receive_timeout : sock->send_timeout,
+ &time_value);
+ rt_memcpy(value, &time_value, sizeof(time_value));
+ *length = sizeof(time_value);
+ break;
+ case SO_TYPE:
+ int_value = sock->type;
+ break;
+ case SO_DOMAIN:
+ int_value = AF_UNIX;
+ break;
+ case SO_PROTOCOL:
+ int_value = 0;
+ break;
+ case SO_ACCEPTCONN:
+ int_value = sock->listening;
+ break;
+ case SO_ERROR:
+ int_value = sock->socket_error;
+ sock->socket_error = 0;
+ break;
+ case SO_SNDBUF:
+ case SO_RCVBUF:
+ int_value = sock->type == SOCK_DGRAM ? AF_UNIX_DGRAM_MAX_SIZE : AF_UNIX_STREAM_BUFFER_SIZE;
+ break;
+ default:
+ result = af_unix_error(ENOPROTOOPT);
+ break;
+ }
+
+ if (result == 0 && option != SO_RCVTIMEO && option != SO_SNDTIMEO)
+ {
+ if (*length < sizeof(int_value))
+ {
+ result = af_unix_error(EINVAL);
+ }
+ else
+ {
+ rt_memcpy(value, &int_value, sizeof(int_value));
+ *length = sizeof(int_value);
+ }
+ }
+ af_unix_unlock();
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_setsockopt(int handle, int level, int option,
+ const void *value, socklen_t length)
+{
+ int result = 0;
+ int timeout;
+ const struct timeval *time_value;
+ struct af_unix_socket *sock;
+
+ if (level != SOL_SOCKET || value == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+ if (option != SO_RCVTIMEO && option != SO_SNDTIMEO)
+ {
+ return af_unix_error(ENOPROTOOPT);
+ }
+ if (length < sizeof(struct timeval))
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ time_value = (const struct timeval *)value;
+ timeout = af_unix_timeout_from_timeval(time_value);
+ if (timeout == -2)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ af_unix_lock();
+ if (option == SO_RCVTIMEO)
+ {
+ sock->receive_timeout = timeout;
+ }
+ else
+ {
+ sock->send_timeout = timeout;
+ }
+ af_unix_unlock();
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_shutdown(int handle, int how)
+{
+ int result;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+ if (sock->type == SOCK_STREAM)
+ {
+ result = af_unix_stream_shutdown(sock, how);
+ }
+ else
+ {
+ if (how < SHUT_RD || how > SHUT_RDWR)
+ {
+ result = af_unix_error(EINVAL);
+ }
+ else
+ {
+ af_unix_lock();
+ if (how == SHUT_RD || how == SHUT_RDWR)
+ {
+ sock->read_shutdown = 1;
+ }
+ if (how == SHUT_WR || how == SHUT_RDWR)
+ {
+ sock->write_shutdown = 1;
+ }
+ rt_wqueue_wakeup_all(&sock->wait_queue,
+ (void *)(rt_ubase_t)(POLLIN | POLLOUT));
+ af_unix_unlock();
+ result = 0;
+ }
+ }
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_ioctl(int handle, long command, void *argument)
+{
+ int result = 0;
+ struct af_unix_socket *sock;
+
+ sock = af_unix_socket_get(handle);
+ if (sock == RT_NULL)
+ {
+ return -1;
+ }
+
+ af_unix_lock();
+ switch (command)
+ {
+ case F_GETFL:
+ result = sock->flags;
+ break;
+ case F_SETFL:
+ sock->flags &= ~O_NONBLOCK;
+ sock->flags |= ((int)(rt_base_t)argument & O_NONBLOCK);
+ break;
+ case FIONBIO:
+ if (argument == RT_NULL)
+ {
+ result = af_unix_error(EINVAL);
+ }
+ else if (*(int *)argument != 0)
+ {
+ sock->flags |= O_NONBLOCK;
+ }
+ else
+ {
+ sock->flags &= ~O_NONBLOCK;
+ }
+ break;
+ case FIONREAD:
+ if (argument == RT_NULL)
+ {
+ result = af_unix_error(EINVAL);
+ }
+ else if (sock->type == SOCK_STREAM)
+ {
+ *(int *)argument = (int)sock->stream_length;
+ }
+ else if (rt_list_isempty(&sock->message_queue))
+ {
+ *(int *)argument = 0;
+ }
+ else
+ {
+ struct af_unix_message *message;
+
+ message = rt_list_entry(sock->message_queue.next,
+ struct af_unix_message, node);
+ *(int *)argument = (int)message->length;
+ }
+ break;
+ default:
+ result = af_unix_error(EINVAL);
+ break;
+ }
+ af_unix_unlock();
+ af_unix_socket_put(sock);
+ return result;
+}
+
+static int af_unix_socketpair(int domain, int type, int protocol, int *handles)
+{
+ int result = -1;
+ struct af_unix_socket *first;
+ struct af_unix_socket *second;
+
+ if (domain != AF_UNIX || protocol != 0 || handles == RT_NULL ||
+ (type != SOCK_DGRAM && type != SOCK_STREAM))
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ first = af_unix_socket_get(handles[0]);
+ if (first == RT_NULL)
+ {
+ return -1;
+ }
+ second = af_unix_socket_get(handles[1]);
+ if (second == RT_NULL)
+ {
+ af_unix_socket_put(first);
+ return -1;
+ }
+
+ af_unix_lock();
+ if (first->type != type || second->type != type ||
+ first->closed || second->closed)
+ {
+ result = af_unix_error(EINVAL);
+ }
+ else
+ {
+ result = af_unix_connect_peers_locked(first, second);
+ if (result == 0)
+ {
+ af_unix_address_set(&first->peer_address, &first->peer_length,
+ RT_NULL);
+ af_unix_address_set(&second->peer_address, &second->peer_length,
+ RT_NULL);
+ }
+ }
+ af_unix_unlock();
+ af_unix_socket_put(second);
+ af_unix_socket_put(first);
+ return result;
+}
+
+static int af_unix_poll(struct dfs_file *file, struct rt_pollreq *request)
+{
+ int mask = 0;
+ int sal_handle;
+ struct sal_socket *sal_sock;
+ struct af_unix_socket *sock;
+
+ sal_handle = (int)(size_t)file->vnode->data;
+ sal_sock = sal_get_socket(sal_handle);
+ if (sal_sock == RT_NULL)
+ {
+ return POLLNVAL;
+ }
+ sock = af_unix_socket_get((int)(size_t)sal_sock->user_data);
+ if (sock == RT_NULL)
+ {
+ return POLLNVAL;
+ }
+
+ /* Register only on this socket. The peer queue can be freed after
+ * peer close, while poll teardown still owns the wait-queue node. */
+ rt_poll_add(&sock->wait_queue, request);
+ af_unix_lock();
+ if (sock->closed)
+ {
+ mask = POLLHUP | POLLERR;
+ }
+ else if (sock->listening)
+ {
+ if (sock->pending_count > 0)
+ {
+ mask |= POLLIN;
+ }
+ }
+ else if (sock->type == SOCK_DGRAM)
+ {
+ if (sock->message_count > 0 || sock->read_shutdown)
+ {
+ mask |= POLLIN;
+ }
+ if (!sock->write_shutdown)
+ {
+ if (sock->peer != RT_NULL)
+ {
+ if (!sock->peer->closed &&
+ sock->peer->message_count < AF_UNIX_DGRAM_QUEUE_LEN)
+ {
+ mask |= POLLOUT;
+ }
+ }
+ else
+ {
+ mask |= POLLOUT;
+ }
+ }
+ }
+ else
+ {
+ if (sock->stream_length > 0 || sock->read_shutdown ||
+ sock->peer == RT_NULL || sock->peer->write_shutdown ||
+ sock->peer->closed)
+ {
+ mask |= POLLIN;
+ }
+ if (sock->peer != RT_NULL)
+ {
+ if (!sock->write_shutdown && !sock->peer->closed &&
+ !sock->peer->read_shutdown &&
+ sock->peer->stream_length < AF_UNIX_STREAM_BUFFER_SIZE)
+ {
+ mask |= POLLOUT;
+ }
+ if (sock->peer->closed)
+ {
+ mask |= POLLHUP;
+ }
+ }
+ else
+ {
+ mask |= POLLHUP;
+ }
+ }
+ af_unix_unlock();
+ af_unix_socket_put(sock);
+ return mask;
+}
+
+static const struct sal_socket_ops af_unix_socket_ops = {
+ .socket = af_unix_socket_create,
+ .closesocket = af_unix_close,
+ .bind = af_unix_bind,
+ .listen = af_unix_listen,
+ .connect = af_unix_connect,
+ .accept = af_unix_accept,
+ .sendto = af_unix_sendto,
+ .sendmsg = af_unix_sendmsg,
+ .recvmsg = af_unix_recvmsg,
+ .recvfrom = af_unix_recvfrom,
+ .getsockopt = af_unix_getsockopt,
+ .setsockopt = af_unix_setsockopt,
+ .shutdown = af_unix_shutdown,
+ .getpeername = af_unix_getpeername,
+ .getsockname = af_unix_getsockname,
+ .ioctlsocket = af_unix_ioctl,
+ .socketpair = af_unix_socketpair,
+ .poll = af_unix_poll,
+};
+
+static const struct sal_proto_family af_unix_family = {
+ .family = AF_UNIX,
+ .sec_family = AF_UNIX,
+ .skt_ops = &af_unix_socket_ops,
+ .netdb_ops = RT_NULL,
+};
+
+int af_unix_init(void)
+{
+ int result;
+
+ rt_memset(af_unix_handles, 0, sizeof(af_unix_handles));
+ rt_mutex_init(&af_unix_core_lock, "afunix", RT_IPC_FLAG_PRIO);
+ result = af_unix_rights_init();
+ if (result != RT_EOK)
+ {
+ return result;
+ }
+ result = af_unix_namespace_init();
+ if (result != RT_EOK)
+ {
+ return result;
+ }
+ return sal_proto_family_register(&af_unix_family);
+}
+INIT_ENV_EXPORT(af_unix_init);
diff --git a/components/net/af_unix/src/af_unix_dgram.c b/components/net/af_unix/src/af_unix_dgram.c
new file mode 100644
index 0000000000..d28fcf8b52
--- /dev/null
+++ b/components/net/af_unix/src/af_unix_dgram.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <string.h>
+
+#include "af_unix_internal.h"
+
+int af_unix_dgram_connect(struct af_unix_socket *sock,
+ const struct sockaddr *address, socklen_t length)
+{
+ int result = 0;
+ struct sockaddr_un peer_address;
+ socklen_t peer_length;
+ struct af_unix_socket *peer;
+
+ peer = af_unix_namespace_lookup(address, length, SOCK_DGRAM,
+ &peer_address, &peer_length);
+ if (peer == RT_NULL)
+ {
+ return -1;
+ }
+
+ af_unix_lock();
+ if (sock->closed)
+ {
+ result = af_unix_error(EBADF);
+ }
+ else if (sock->write_shutdown)
+ {
+ result = af_unix_error(EPIPE);
+ }
+ else
+ {
+ af_unix_set_peer_locked(sock, peer);
+ sock->peer_address = peer_address;
+ sock->peer_length = peer_length;
+ sock->connected = 1;
+ }
+ af_unix_unlock();
+ af_unix_socket_put(peer);
+ return result;
+}
+
+static struct af_unix_socket *af_unix_dgram_target_get(
+ struct af_unix_socket *sock, const struct sockaddr *to,
+ socklen_t to_length)
+{
+ struct af_unix_socket *peer = RT_NULL;
+
+ if (to != RT_NULL)
+ {
+ return af_unix_namespace_lookup(to, to_length, SOCK_DGRAM,
+ RT_NULL, RT_NULL);
+ }
+
+ af_unix_lock();
+ if (sock->connected && sock->peer != RT_NULL)
+ {
+ peer = sock->peer;
+ af_unix_socket_ref_locked(peer);
+ }
+ af_unix_unlock();
+
+ if (peer == RT_NULL)
+ {
+ af_unix_error(EDESTADDRREQ);
+ }
+ return peer;
+}
+
+int af_unix_dgram_send(struct af_unix_socket *sock, const void *data,
+ size_t size, int flags, const struct sockaddr *to,
+ socklen_t to_length, struct af_unix_rights *rights)
+{
+ int error;
+ int result;
+ int nonblocking;
+ int timeout;
+ struct af_unix_socket *peer;
+ struct af_unix_message *message;
+
+ if (size > AF_UNIX_DGRAM_MAX_SIZE)
+ {
+ return af_unix_error(EMSGSIZE);
+ }
+ if (size != 0 && data == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ peer = af_unix_dgram_target_get(sock, to, to_length);
+ if (peer == RT_NULL)
+ {
+ return -1;
+ }
+
+ message = (struct af_unix_message *)rt_malloc(sizeof(*message) + size);
+ if (message == RT_NULL)
+ {
+ af_unix_socket_put(peer);
+ return af_unix_error(ENOMEM);
+ }
+ rt_list_init(&message->node);
+ message->length = size;
+ message->rights = rights;
+ if (size != 0)
+ {
+ rt_memcpy(message->data, data, size);
+ }
+
+ for (;;)
+ {
+ af_unix_lock();
+ if (sock->closed)
+ {
+ result = af_unix_error(EBADF);
+ af_unix_unlock();
+ break;
+ }
+ if (sock->write_shutdown)
+ {
+ result = af_unix_error(EPIPE);
+ af_unix_unlock();
+ break;
+ }
+ if (peer->closed)
+ {
+ result = af_unix_error(ECONNREFUSED);
+ af_unix_unlock();
+ break;
+ }
+ if (peer->read_shutdown)
+ {
+ result = af_unix_error(EPIPE);
+ af_unix_unlock();
+ break;
+ }
+ if (peer->message_count < AF_UNIX_DGRAM_QUEUE_LEN)
+ {
+ message->source = sock->local_address;
+ message->source_length = sock->local_length;
+ rt_list_insert_before(&peer->message_queue, &message->node);
+ peer->message_count++;
+ rt_wqueue_wakeup_all(&peer->wait_queue,
+ (void *)(rt_ubase_t)POLLIN);
+ af_unix_unlock();
+ af_unix_socket_put(peer);
+ return (int)size;
+ }
+
+ nonblocking = af_unix_is_nonblocking(sock, flags);
+ timeout = sock->send_timeout;
+ af_unix_unlock();
+ if (nonblocking)
+ {
+ result = af_unix_error(EAGAIN);
+ break;
+ }
+ if (af_unix_wait(&peer->wait_queue, timeout) < 0)
+ {
+ result = -1;
+ break;
+ }
+ }
+
+ error = rt_get_errno();
+ rt_free(message);
+ af_unix_socket_put(peer);
+ rt_set_errno(error);
+ return result;
+}
+
+int af_unix_dgram_receive(struct af_unix_socket *sock, void *data,
+ size_t size, int flags, struct sockaddr *from,
+ socklen_t *from_length, rt_list_t *rights)
+{
+ int result;
+ int nonblocking;
+ int timeout;
+ size_t copy_length;
+ struct af_unix_message *message;
+
+ if (size != 0 && data == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ for (;;)
+ {
+ af_unix_lock();
+ if (sock->closed)
+ {
+ af_unix_unlock();
+ return af_unix_error(EBADF);
+ }
+ if (!rt_list_isempty(&sock->message_queue))
+ {
+ message = rt_list_entry(sock->message_queue.next,
+ struct af_unix_message, node);
+ copy_length = message->length;
+ if (copy_length > size)
+ {
+ copy_length = size;
+ }
+ if (copy_length != 0)
+ {
+ rt_memcpy(data, message->data, copy_length);
+ }
+ result = af_unix_address_copy(from, from_length,
+ &message->source,
+ message->source_length);
+ if (result < 0)
+ {
+ af_unix_unlock();
+ return result;
+ }
+ if ((flags & MSG_PEEK) == 0)
+ {
+ rt_list_remove(&message->node);
+ sock->message_count--;
+ if (message->rights != RT_NULL)
+ {
+ rt_list_insert_before(rights, &message->rights->node);
+ message->rights = RT_NULL;
+ }
+ af_unix_wakeup_writable_locked(sock);
+ rt_free(message);
+ }
+ af_unix_unlock();
+ return (int)copy_length;
+ }
+ if (sock->read_shutdown)
+ {
+ af_unix_unlock();
+ return 0;
+ }
+
+ nonblocking = af_unix_is_nonblocking(sock, flags);
+ timeout = sock->receive_timeout;
+ af_unix_unlock();
+ if (nonblocking)
+ {
+ return af_unix_error(EAGAIN);
+ }
+ if (af_unix_wait(&sock->wait_queue, timeout) < 0)
+ {
+ return -1;
+ }
+ }
+}
diff --git a/components/net/af_unix/src/af_unix_internal.h b/components/net/af_unix/src/af_unix_internal.h
new file mode 100644
index 0000000000..e59ff52084
--- /dev/null
+++ b/components/net/af_unix/src/af_unix_internal.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef AF_UNIX_INTERNAL_H__
+#define AF_UNIX_INTERNAL_H__
+
+#include <rtthread.h>
+#include <dfs.h>
+#include <dfs_file.h>
+#include <poll.h>
+#include <sal_low_lvl.h>
+#include <sal_socket.h>
+
+#ifndef AF_UNIX_DGRAM_MAX_SIZE
+#define AF_UNIX_DGRAM_MAX_SIZE 4096
+#endif
+
+#ifndef AF_UNIX_DGRAM_QUEUE_LEN
+#define AF_UNIX_DGRAM_QUEUE_LEN 16
+#endif
+
+#ifndef AF_UNIX_STREAM_BUFFER_SIZE
+#define AF_UNIX_STREAM_BUFFER_SIZE 4096
+#endif
+
+#ifndef AF_UNIX_LISTEN_BACKLOG_MAX
+#define AF_UNIX_LISTEN_BACKLOG_MAX 16
+#endif
+
+#ifndef AF_UNIX_RIGHTS_MAX
+#define AF_UNIX_RIGHTS_MAX 16
+#endif
+
+#define AF_UNIX_PATH_MAX ((int)sizeof(((struct sockaddr_un *)0)->sun_path))
+
+struct af_unix_rights
+{
+ rt_list_t node;
+ rt_uint64_t offset;
+ size_t count;
+ struct dfs_file *files[1];
+};
+
+struct af_unix_message
+{
+ rt_list_t node;
+ size_t length;
+ struct sockaddr_un source;
+ socklen_t source_length;
+ struct af_unix_rights *rights;
+ char data[1];
+};
+
+struct af_unix_socket
+{
+ int handle;
+ int type;
+ int flags;
+ int ref_count;
+ int closed;
+ int bound;
+ int connected;
+ int listening;
+ int read_shutdown;
+ int write_shutdown;
+ int socket_error;
+ int receive_timeout;
+ int send_timeout;
+
+ struct sockaddr_un local_address;
+ socklen_t local_length;
+ struct sockaddr_un peer_address;
+ socklen_t peer_length;
+ struct af_unix_socket *peer;
+ void *namespace_entry;
+
+ rt_wqueue_t wait_queue;
+ rt_list_t message_queue;
+ rt_size_t message_count;
+
+ char *stream_buffer;
+ rt_size_t stream_head;
+ rt_size_t stream_length;
+ rt_uint64_t stream_read_offset;
+ rt_uint64_t stream_write_offset;
+ rt_list_t rights_queue;
+
+ rt_list_t pending_queue;
+ rt_list_t pending_node;
+ int pending;
+ int backlog;
+ int pending_count;
+};
+
+void af_unix_lock(void);
+void af_unix_unlock(void);
+int af_unix_error(int error);
+int af_unix_is_nonblocking(const struct af_unix_socket *sock, int flags);
+int af_unix_wait(rt_wqueue_t *queue, int timeout);
+
+int af_unix_rights_create(const struct msghdr *message,
+ struct af_unix_rights **rights);
+void af_unix_rights_release(struct af_unix_rights *rights);
+void af_unix_rights_list_release(rt_list_t *list);
+void af_unix_rights_defer_locked(struct af_unix_rights *rights);
+void af_unix_rights_list_defer_locked(rt_list_t *list);
+void af_unix_rights_drain(void);
+int af_unix_rights_deliver(rt_list_t *list, struct msghdr *message);
+int af_unix_rights_init(void);
+
+struct af_unix_socket *af_unix_socket_create_locked(int type);
+struct af_unix_socket *af_unix_socket_get(int handle);
+void af_unix_socket_ref_locked(struct af_unix_socket *sock);
+void af_unix_socket_unref_locked(struct af_unix_socket *sock);
+void af_unix_socket_put(struct af_unix_socket *sock);
+int af_unix_handle_alloc_locked(struct af_unix_socket *sock);
+void af_unix_handle_remove_locked(struct af_unix_socket *sock);
+void af_unix_socket_close_locked(struct af_unix_socket *sock);
+void af_unix_wakeup_writable_locked(struct af_unix_socket *sock);
+int af_unix_connect_peers_locked(struct af_unix_socket *first,
+ struct af_unix_socket *second);
+void af_unix_set_peer_locked(struct af_unix_socket *sock,
+ struct af_unix_socket *peer);
+
+int af_unix_address_parse(const struct sockaddr *address, socklen_t length,
+ char path[AF_UNIX_PATH_MAX]);
+void af_unix_address_set(struct sockaddr_un *address, socklen_t *length,
+ const char *path);
+int af_unix_address_copy(struct sockaddr *address, socklen_t *length,
+ const struct sockaddr_un *source,
+ socklen_t source_length);
+int af_unix_namespace_bind(struct af_unix_socket *sock,
+ const struct sockaddr *address, socklen_t length);
+struct af_unix_socket *af_unix_namespace_lookup(
+ const struct sockaddr *address, socklen_t length, int type,
+ struct sockaddr_un *normalized_address, socklen_t *normalized_length);
+void af_unix_namespace_detach_locked(struct af_unix_socket *sock);
+int af_unix_namespace_init(void);
+
+int af_unix_dgram_connect(struct af_unix_socket *sock,
+ const struct sockaddr *address, socklen_t length);
+int af_unix_dgram_send(struct af_unix_socket *sock, const void *data,
+ size_t size, int flags, const struct sockaddr *to,
+ socklen_t to_length, struct af_unix_rights *rights);
+int af_unix_dgram_receive(struct af_unix_socket *sock, void *data,
+ size_t size, int flags, struct sockaddr *from,
+ socklen_t *from_length, rt_list_t *rights);
+
+int af_unix_stream_listen(struct af_unix_socket *sock, int backlog);
+int af_unix_stream_connect(struct af_unix_socket *sock,
+ const struct sockaddr *address, socklen_t length);
+int af_unix_stream_accept(struct af_unix_socket *sock,
+ struct sockaddr *address, socklen_t *length);
+int af_unix_stream_send(struct af_unix_socket *sock, const void *data,
+ size_t size, int flags,
+ struct af_unix_rights *rights);
+int af_unix_stream_receive(struct af_unix_socket *sock, void *data,
+ size_t size, int flags, rt_list_t *rights);
+int af_unix_stream_shutdown(struct af_unix_socket *sock, int how);
+
+#endif /* AF_UNIX_INTERNAL_H__ */
diff --git a/components/net/af_unix/src/af_unix_namespace.c b/components/net/af_unix/src/af_unix_namespace.c
new file mode 100644
index 0000000000..386dfdc05f
--- /dev/null
+++ b/components/net/af_unix/src/af_unix_namespace.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+#include "af_unix_internal.h"
+
+struct af_unix_namespace_entry
+{
+ rt_list_t node;
+ char path[AF_UNIX_PATH_MAX];
+ struct af_unix_socket *sock;
+};
+
+static rt_list_t af_unix_namespace;
+
+static struct af_unix_namespace_entry *af_unix_namespace_find_locked(
+ const char *path)
+{
+ struct af_unix_namespace_entry *entry;
+
+ rt_list_for_each_entry(entry, &af_unix_namespace, node)
+ {
+ if (rt_strcmp(entry->path, path) == 0)
+ {
+ return entry;
+ }
+ }
+ return RT_NULL;
+}
+
+int af_unix_address_parse(const struct sockaddr *address, socklen_t length,
+ char path[AF_UNIX_PATH_MAX])
+{
+ int index;
+ int path_length;
+ char input_path[AF_UNIX_PATH_MAX];
+ char *normalized;
+ const struct sockaddr_un *unix_address;
+ const int path_offset = (int)offsetof(struct sockaddr_un, sun_path);
+
+ if (address == RT_NULL || length <= (socklen_t)path_offset ||
+ length > sizeof(struct sockaddr_un))
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ unix_address = (const struct sockaddr_un *)address;
+ if (unix_address->sa_family != AF_UNIX)
+ {
+ return af_unix_error(EAFNOSUPPORT);
+ }
+
+ path_length = (int)length - path_offset;
+ if (path_length > AF_UNIX_PATH_MAX)
+ {
+ path_length = AF_UNIX_PATH_MAX;
+ }
+ for (index = 0; index < path_length; index++)
+ {
+ if (unix_address->sun_path[index] == '\0')
+ {
+ break;
+ }
+ }
+ if (index == 0)
+ {
+ return af_unix_error(EINVAL);
+ }
+ if (index == path_length || index >= AF_UNIX_PATH_MAX)
+ {
+ return af_unix_error(ENAMETOOLONG);
+ }
+
+ rt_memcpy(input_path, unix_address->sun_path, index);
+ input_path[index] = '\0';
+ normalized = dfs_normalize_path(RT_NULL, input_path);
+ if (normalized == RT_NULL)
+ {
+ return af_unix_error(ENOMEM);
+ }
+ if (rt_strlen(normalized) >= AF_UNIX_PATH_MAX)
+ {
+ rt_free(normalized);
+ return af_unix_error(ENAMETOOLONG);
+ }
+ rt_strncpy(path, normalized, AF_UNIX_PATH_MAX);
+ path[AF_UNIX_PATH_MAX - 1] = '\0';
+ rt_free(normalized);
+ return 0;
+}
+
+void af_unix_address_set(struct sockaddr_un *address, socklen_t *length,
+ const char *path)
+{
+ size_t path_length = 0;
+
+ rt_memset(address, 0, sizeof(*address));
+ address->sa_family = AF_UNIX;
+ if (path != RT_NULL)
+ {
+ path_length = rt_strlen(path);
+ if (path_length >= sizeof(address->sun_path))
+ {
+ path_length = sizeof(address->sun_path) - 1;
+ }
+ rt_memcpy(address->sun_path, path, path_length);
+ }
+ *length = (socklen_t)(offsetof(struct sockaddr_un, sun_path) +
+ path_length + (path != RT_NULL ? 1 : 0));
+}
+
+int af_unix_address_copy(struct sockaddr *address, socklen_t *length,
+ const struct sockaddr_un *source,
+ socklen_t source_length)
+{
+ socklen_t copy_length;
+
+ if (address == RT_NULL)
+ {
+ return 0;
+ }
+ if (length == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ copy_length = *length;
+ if (copy_length > source_length)
+ {
+ copy_length = source_length;
+ }
+ if (copy_length > 0)
+ {
+ rt_memcpy(address, source, copy_length);
+ }
+ *length = source_length;
+ return 0;
+}
+
+int af_unix_namespace_bind(struct af_unix_socket *sock,
+ const struct sockaddr *address, socklen_t length)
+{
+ int result;
+ char path[AF_UNIX_PATH_MAX];
+ struct af_unix_namespace_entry *entry;
+ struct af_unix_namespace_entry *new_entry;
+
+ result = af_unix_address_parse(address, length, path);
+ if (result < 0)
+ {
+ return result;
+ }
+
+ af_unix_lock();
+ if (sock->closed)
+ {
+ af_unix_unlock();
+ return af_unix_error(EBADF);
+ }
+ if (sock->bound)
+ {
+ af_unix_unlock();
+ return af_unix_error(EINVAL);
+ }
+ af_unix_unlock();
+
+ result = dfs_file_mknod(path, FT_SOCKET,
+ S_IRWXU | S_IRWXG | S_IRWXO);
+ if (result < 0)
+ {
+ return af_unix_error(result == -EEXIST ? EADDRINUSE : -result);
+ }
+
+ new_entry = (struct af_unix_namespace_entry *)rt_calloc(1,
+ sizeof(*new_entry));
+ if (new_entry == RT_NULL)
+ {
+ (void)dfs_file_unlink(path);
+ return af_unix_error(ENOMEM);
+ }
+ rt_strncpy(new_entry->path, path, sizeof(new_entry->path));
+ new_entry->path[sizeof(new_entry->path) - 1] = '\0';
+ rt_list_init(&new_entry->node);
+
+ af_unix_lock();
+ if (sock->closed || sock->bound)
+ {
+ af_unix_unlock();
+ rt_free(new_entry);
+ (void)dfs_file_unlink(path);
+ return af_unix_error(sock->closed ? EBADF : EINVAL);
+ }
+
+ entry = af_unix_namespace_find_locked(path);
+ if (entry == RT_NULL)
+ {
+ entry = new_entry;
+ new_entry = RT_NULL;
+ rt_list_insert_before(&af_unix_namespace, &entry->node);
+ }
+ entry->sock = sock;
+ sock->namespace_entry = entry;
+ sock->bound = 1;
+ af_unix_address_set(&sock->local_address, &sock->local_length, path);
+ af_unix_unlock();
+
+ if (new_entry != RT_NULL)
+ {
+ rt_free(new_entry);
+ }
+ return 0;
+}
+
+struct af_unix_socket *af_unix_namespace_lookup(
+ const struct sockaddr *address, socklen_t length, int type,
+ struct sockaddr_un *normalized_address, socklen_t *normalized_length)
+{
+ int result;
+ char path[AF_UNIX_PATH_MAX];
+ struct stat file_stat;
+ struct af_unix_socket *sock = RT_NULL;
+ struct af_unix_namespace_entry *entry;
+
+ result = af_unix_address_parse(address, length, path);
+ if (result < 0)
+ {
+ return RT_NULL;
+ }
+ result = dfs_file_stat(path, &file_stat);
+ if (result < 0)
+ {
+ af_unix_error(ENOENT);
+ return RT_NULL;
+ }
+ if (!S_ISSOCK(file_stat.st_mode))
+ {
+ af_unix_error(EPROTOTYPE);
+ return RT_NULL;
+ }
+
+ af_unix_lock();
+ entry = af_unix_namespace_find_locked(path);
+ if (entry != RT_NULL && entry->sock != RT_NULL && !entry->sock->closed)
+ {
+ if (entry->sock->type == type)
+ {
+ sock = entry->sock;
+ af_unix_socket_ref_locked(sock);
+ }
+ else
+ {
+ af_unix_error(EPROTOTYPE);
+ }
+ }
+ else
+ {
+ af_unix_error(ECONNREFUSED);
+ }
+ af_unix_unlock();
+
+ if (sock != RT_NULL && normalized_address != RT_NULL &&
+ normalized_length != RT_NULL)
+ {
+ af_unix_address_set(normalized_address, normalized_length, path);
+ }
+ return sock;
+}
+
+void af_unix_namespace_detach_locked(struct af_unix_socket *sock)
+{
+ struct af_unix_namespace_entry *entry;
+
+ entry = (struct af_unix_namespace_entry *)sock->namespace_entry;
+ if (entry != RT_NULL && entry->sock == sock)
+ {
+ entry->sock = RT_NULL;
+ rt_list_remove(&entry->node);
+ rt_free(entry);
+ }
+ sock->namespace_entry = RT_NULL;
+}
+
+int af_unix_namespace_init(void)
+{
+ rt_list_init(&af_unix_namespace);
+ return RT_EOK;
+}
diff --git a/components/net/af_unix/src/af_unix_rights.c b/components/net/af_unix/src/af_unix_rights.c
new file mode 100644
index 0000000000..18189dee1e
--- /dev/null
+++ b/components/net/af_unix/src/af_unix_rights.c
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <dfs_net.h>
+
+#include "af_unix_internal.h"
+
+static struct rt_mutex af_unix_deferred_lock;
+static rt_list_t af_unix_deferred_rights;
+
+static size_t af_unix_cmsg_next(size_t offset, size_t length,
+ size_t control_length)
+{
+ size_t next;
+
+ next = offset + CMSG_ALIGN(length);
+ if (next > control_length)
+ {
+ next = offset + length;
+ }
+ return next;
+}
+
+static int af_unix_file_is_unix_socket(const struct dfs_file *file)
+{
+ int sal_handle;
+ struct sal_socket *sal_sock;
+
+ if (file == RT_NULL || file->vnode == RT_NULL ||
+ file->vnode->type != FT_SOCKET)
+ {
+ return 0;
+ }
+ if (file->vnode->fops != dfs_net_get_fops() &&
+ file->fops != dfs_net_get_fops())
+ {
+ return 0;
+ }
+
+ sal_handle = (int)(size_t)file->vnode->data;
+ sal_sock = sal_get_socket(sal_handle);
+ return (sal_sock != RT_NULL && sal_sock->domain == AF_UNIX);
+}
+
+int af_unix_rights_create(const struct msghdr *message,
+ struct af_unix_rights **out_rights)
+{
+ int fds[AF_UNIX_RIGHTS_MAX];
+ int result;
+ size_t count = 0;
+ size_t file_index;
+ size_t offset = 0;
+ const char *control;
+ struct af_unix_rights *rights;
+
+ *out_rights = RT_NULL;
+ if (message->msg_controllen == 0)
+ {
+ return 0;
+ }
+ if (message->msg_control == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ control = (const char *)message->msg_control;
+ while (offset < message->msg_controllen)
+ {
+ size_t data_length;
+ size_t fd_count;
+ size_t index;
+ size_t next;
+ const struct cmsghdr *cmsg;
+
+ if (message->msg_controllen - offset < sizeof(*cmsg))
+ {
+ return af_unix_error(EINVAL);
+ }
+ cmsg = (const struct cmsghdr *)(control + offset);
+ if (cmsg->cmsg_len < sizeof(*cmsg) ||
+ cmsg->cmsg_len > message->msg_controllen - offset)
+ {
+ return af_unix_error(EINVAL);
+ }
+ if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
+ {
+ return af_unix_error(EOPNOTSUPP);
+ }
+
+ data_length = cmsg->cmsg_len - sizeof(*cmsg);
+ if (data_length == 0 || data_length % sizeof(int) != 0)
+ {
+ return af_unix_error(EINVAL);
+ }
+ fd_count = data_length / sizeof(int);
+ if (fd_count > AF_UNIX_RIGHTS_MAX - count)
+ {
+ return af_unix_error(EMSGSIZE);
+ }
+ for (index = 0; index < fd_count; index++)
+ {
+ rt_memcpy(&fds[count],
+ (const char *)CMSG_DATA(cmsg) + index * sizeof(int),
+ sizeof(int));
+ count++;
+ }
+
+ next = af_unix_cmsg_next(offset, cmsg->cmsg_len,
+ message->msg_controllen);
+ if (next <= offset || next > message->msg_controllen)
+ {
+ return af_unix_error(EINVAL);
+ }
+ offset = next;
+ }
+
+ rights = (struct af_unix_rights *)rt_calloc(
+ 1, sizeof(*rights) + (count - 1) * sizeof(rights->files[0]));
+ if (rights == RT_NULL)
+ {
+ return af_unix_error(ENOMEM);
+ }
+ rt_list_init(&rights->node);
+ rights->count = count;
+
+ result = dfs_file_get_refs(fds, count, rights->files);
+ if (result < 0)
+ {
+ rt_free(rights);
+ return af_unix_error(-result);
+ }
+
+ /* Passing AF_UNIX sockets can form an unreclaimable reference
+ * cycle between the in-flight right and the destination queue. */
+ for (file_index = 0; file_index < count; file_index++)
+ {
+ if (af_unix_file_is_unix_socket(rights->files[file_index]))
+ {
+ af_unix_rights_release(rights);
+ return af_unix_error(EOPNOTSUPP);
+ }
+ }
+
+ *out_rights = rights;
+ return 0;
+}
+
+void af_unix_rights_release(struct af_unix_rights *rights)
+{
+ size_t index;
+
+ if (rights == RT_NULL)
+ {
+ return;
+ }
+ for (index = 0; index < rights->count; index++)
+ {
+ if (rights->files[index] != RT_NULL)
+ {
+ dfs_file_put_ref(rights->files[index]);
+ }
+ }
+ rt_free(rights);
+}
+
+void af_unix_rights_list_release(rt_list_t *list)
+{
+ while (!rt_list_isempty(list))
+ {
+ struct af_unix_rights *rights;
+
+ rights = rt_list_entry(list->next, struct af_unix_rights, node);
+ rt_list_remove(&rights->node);
+ af_unix_rights_release(rights);
+ }
+}
+
+void af_unix_rights_defer_locked(struct af_unix_rights *rights)
+{
+ if (rights == RT_NULL)
+ {
+ return;
+ }
+
+ rt_mutex_take(&af_unix_deferred_lock, RT_WAITING_FOREVER);
+ rt_list_insert_before(&af_unix_deferred_rights, &rights->node);
+ rt_mutex_release(&af_unix_deferred_lock);
+}
+
+void af_unix_rights_list_defer_locked(rt_list_t *list)
+{
+ while (!rt_list_isempty(list))
+ {
+ struct af_unix_rights *rights;
+
+ rights = rt_list_entry(list->next, struct af_unix_rights, node);
+ rt_list_remove(&rights->node);
+ af_unix_rights_defer_locked(rights);
+ }
+}
+
+void af_unix_rights_drain(void)
+{
+ rt_list_t pending;
+
+ rt_list_init(&pending);
+ rt_mutex_take(&af_unix_deferred_lock, RT_WAITING_FOREVER);
+ while (!rt_list_isempty(&af_unix_deferred_rights))
+ {
+ rt_list_t *node = af_unix_deferred_rights.next;
+
+ rt_list_remove(node);
+ rt_list_insert_before(&pending, node);
+ }
+ rt_mutex_release(&af_unix_deferred_lock);
+
+ af_unix_rights_list_release(&pending);
+}
+
+static size_t af_unix_rights_count(const rt_list_t *list)
+{
+ size_t count = 0;
+ const rt_list_t *node;
+
+ for (node = list->next; node != list; node = node->next)
+ {
+ const struct af_unix_rights *rights;
+
+ rights = rt_list_entry(node, struct af_unix_rights, node);
+ count += rights->count;
+ }
+ return count;
+}
+
+static size_t af_unix_rights_capacity(const struct msghdr *message,
+ size_t total)
+{
+ size_t capacity;
+
+ if (message->msg_control == RT_NULL ||
+ message->msg_controllen < CMSG_SPACE(sizeof(int)))
+ {
+ return 0;
+ }
+
+ capacity = (message->msg_controllen - sizeof(struct cmsghdr)) /
+ sizeof(int);
+ if (capacity > total)
+ {
+ capacity = total;
+ }
+ while (capacity != 0 &&
+ CMSG_SPACE(capacity * sizeof(int)) > message->msg_controllen)
+ {
+ capacity--;
+ }
+ return capacity;
+}
+
+int af_unix_rights_deliver(rt_list_t *list, struct msghdr *message)
+{
+ int install_result = 0;
+ int *fds = RT_NULL;
+ size_t capacity;
+ size_t delivered = 0;
+ size_t index;
+ size_t total;
+ struct dfs_file **files = RT_NULL;
+ struct cmsghdr *cmsg;
+ rt_list_t *node;
+
+ total = af_unix_rights_count(list);
+ capacity = af_unix_rights_capacity(message, total);
+ if (capacity != 0)
+ {
+ files = (struct dfs_file **)rt_malloc(capacity * sizeof(*files));
+ fds = (int *)rt_malloc(capacity * sizeof(*fds));
+ if (files == RT_NULL || fds == RT_NULL)
+ {
+ capacity = 0;
+ }
+ }
+
+ for (node = list->next; node != list && delivered < capacity;
+ node = node->next)
+ {
+ struct af_unix_rights *rights;
+
+ rights = rt_list_entry(node, struct af_unix_rights, node);
+ for (index = 0; index < rights->count && delivered < capacity; index++)
+ {
+ files[delivered] = rights->files[index];
+ delivered++;
+ }
+ }
+
+ if (delivered != 0)
+ {
+ install_result = dfs_file_install_refs(files, delivered, fds);
+ if (install_result < 0)
+ {
+ delivered = 0;
+ }
+ }
+
+ if (delivered != 0)
+ {
+ size_t transferred = 0;
+
+ for (node = list->next; node != list && transferred < delivered;
+ node = node->next)
+ {
+ struct af_unix_rights *rights;
+
+ rights = rt_list_entry(node, struct af_unix_rights, node);
+ for (index = 0;
+ index < rights->count && transferred < delivered; index++)
+ {
+ rights->files[index] = RT_NULL;
+ transferred++;
+ }
+ }
+
+ cmsg = (struct cmsghdr *)message->msg_control;
+ cmsg->cmsg_len = CMSG_LEN(delivered * sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ rt_memcpy(CMSG_DATA(cmsg), fds, delivered * sizeof(int));
+ message->msg_controllen = CMSG_SPACE(delivered * sizeof(int));
+ }
+ else
+ {
+ message->msg_controllen = 0;
+ }
+
+ if (delivered < total)
+ {
+ message->msg_flags |= MSG_CTRUNC;
+ }
+
+ if (files != RT_NULL)
+ {
+ rt_free(files);
+ }
+ if (fds != RT_NULL)
+ {
+ rt_free(fds);
+ }
+ af_unix_rights_list_release(list);
+ return install_result;
+}
+
+int af_unix_rights_init(void)
+{
+ rt_list_init(&af_unix_deferred_rights);
+ return rt_mutex_init(&af_unix_deferred_lock, "afuright",
+ RT_IPC_FLAG_PRIO);
+}
diff --git a/components/net/af_unix/src/af_unix_stream.c b/components/net/af_unix/src/af_unix_stream.c
new file mode 100644
index 0000000000..cd78880cfb
--- /dev/null
+++ b/components/net/af_unix/src/af_unix_stream.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <string.h>
+
+#include "af_unix_internal.h"
+
+static void af_unix_stream_buffer_write_locked(struct af_unix_socket *sock,
+ const char *data,
+ size_t length)
+{
+ size_t first_length;
+ size_t tail;
+
+ tail = (sock->stream_head + sock->stream_length) %
+ AF_UNIX_STREAM_BUFFER_SIZE;
+ first_length = AF_UNIX_STREAM_BUFFER_SIZE - tail;
+ if (first_length > length)
+ {
+ first_length = length;
+ }
+ rt_memcpy(sock->stream_buffer + tail, data, first_length);
+ if (length > first_length)
+ {
+ rt_memcpy(sock->stream_buffer, data + first_length,
+ length - first_length);
+ }
+ sock->stream_length += length;
+ sock->stream_write_offset += length;
+}
+
+static void af_unix_stream_buffer_read_locked(struct af_unix_socket *sock,
+ char *data, size_t length,
+ int peek)
+{
+ size_t first_length;
+
+ first_length = AF_UNIX_STREAM_BUFFER_SIZE - sock->stream_head;
+ if (first_length > length)
+ {
+ first_length = length;
+ }
+ rt_memcpy(data, sock->stream_buffer + sock->stream_head, first_length);
+ if (length > first_length)
+ {
+ rt_memcpy(data + first_length, sock->stream_buffer,
+ length - first_length);
+ }
+
+ if (!peek)
+ {
+ sock->stream_head = (sock->stream_head + length) %
+ AF_UNIX_STREAM_BUFFER_SIZE;
+ sock->stream_length -= length;
+ sock->stream_read_offset += length;
+ }
+}
+
+static void af_unix_stream_collect_rights_locked(struct af_unix_socket *sock,
+ rt_list_t *rights)
+{
+ while (!rt_list_isempty(&sock->rights_queue))
+ {
+ struct af_unix_rights *entry;
+
+ entry = rt_list_entry(sock->rights_queue.next,
+ struct af_unix_rights, node);
+ if (entry->offset >= sock->stream_read_offset)
+ {
+ break;
+ }
+ rt_list_remove(&entry->node);
+ rt_list_insert_before(rights, &entry->node);
+ }
+}
+
+int af_unix_stream_listen(struct af_unix_socket *sock, int backlog)
+{
+ int result = 0;
+
+ af_unix_lock();
+ if (sock->closed)
+ {
+ result = af_unix_error(EBADF);
+ }
+ else if (sock->type != SOCK_STREAM)
+ {
+ result = af_unix_error(EOPNOTSUPP);
+ }
+ else if (!sock->bound)
+ {
+ result = af_unix_error(EINVAL);
+ }
+ else if (sock->connected)
+ {
+ result = af_unix_error(EINVAL);
+ }
+ else
+ {
+ if (backlog < 1)
+ {
+ backlog = 1;
+ }
+ if (backlog > AF_UNIX_LISTEN_BACKLOG_MAX)
+ {
+ backlog = AF_UNIX_LISTEN_BACKLOG_MAX;
+ }
+ sock->backlog = backlog;
+ sock->listening = 1;
+ }
+ af_unix_unlock();
+ return result;
+}
+
+int af_unix_stream_connect(struct af_unix_socket *sock,
+ const struct sockaddr *address, socklen_t length)
+{
+ int handle;
+ int nonblocking;
+ int timeout;
+ int result = -1;
+ struct sockaddr_un peer_address;
+ socklen_t peer_length;
+ struct af_unix_socket *listener;
+ struct af_unix_socket *accepted;
+
+ listener = af_unix_namespace_lookup(address, length, SOCK_STREAM,
+ &peer_address, &peer_length);
+ if (listener == RT_NULL)
+ {
+ return -1;
+ }
+
+ for (;;)
+ {
+ af_unix_lock();
+ if (sock->closed)
+ {
+ result = af_unix_error(EBADF);
+ af_unix_unlock();
+ break;
+ }
+ if (sock->connected)
+ {
+ result = af_unix_error(EISCONN);
+ af_unix_unlock();
+ break;
+ }
+ if (listener->closed || !listener->listening)
+ {
+ result = af_unix_error(ECONNREFUSED);
+ af_unix_unlock();
+ break;
+ }
+ if (listener->pending_count < listener->backlog)
+ {
+ accepted = af_unix_socket_create_locked(SOCK_STREAM);
+ if (accepted == RT_NULL)
+ {
+ result = af_unix_error(ENOMEM);
+ af_unix_unlock();
+ break;
+ }
+ handle = af_unix_handle_alloc_locked(accepted);
+ if (handle < 0)
+ {
+ af_unix_socket_unref_locked(accepted);
+ result = af_unix_error(EMFILE);
+ af_unix_unlock();
+ break;
+ }
+
+ result = af_unix_connect_peers_locked(sock, accepted);
+ if (result < 0)
+ {
+ af_unix_handle_remove_locked(accepted);
+ af_unix_unlock();
+ break;
+ }
+
+ sock->peer_address = peer_address;
+ sock->peer_length = peer_length;
+ accepted->local_address = listener->local_address;
+ accepted->local_length = listener->local_length;
+ accepted->peer_address = sock->local_address;
+ accepted->peer_length = sock->local_length;
+ accepted->pending = 1;
+ rt_list_insert_before(&listener->pending_queue,
+ &accepted->pending_node);
+ listener->pending_count++;
+ rt_wqueue_wakeup_all(&listener->wait_queue,
+ (void *)(rt_ubase_t)POLLIN);
+ af_unix_unlock();
+ result = 0;
+ break;
+ }
+
+ nonblocking = af_unix_is_nonblocking(sock, 0);
+ timeout = sock->send_timeout;
+ af_unix_unlock();
+ if (nonblocking)
+ {
+ result = af_unix_error(EAGAIN);
+ break;
+ }
+ if (af_unix_wait(&listener->wait_queue, timeout) < 0)
+ {
+ result = -1;
+ break;
+ }
+ }
+
+ af_unix_socket_put(listener);
+ return result;
+}
+
+int af_unix_stream_accept(struct af_unix_socket *sock,
+ struct sockaddr *address, socklen_t *length)
+{
+ int handle;
+ int nonblocking;
+ int timeout;
+ int result;
+ struct af_unix_socket *accepted;
+
+ for (;;)
+ {
+ af_unix_lock();
+ if (sock->closed)
+ {
+ af_unix_unlock();
+ return af_unix_error(EBADF);
+ }
+ if (!sock->listening)
+ {
+ af_unix_unlock();
+ return af_unix_error(EINVAL);
+ }
+ if (!rt_list_isempty(&sock->pending_queue))
+ {
+ accepted = rt_list_entry(sock->pending_queue.next,
+ struct af_unix_socket, pending_node);
+ result = af_unix_address_copy(address, length,
+ &accepted->peer_address,
+ accepted->peer_length);
+ if (result < 0)
+ {
+ af_unix_unlock();
+ return result;
+ }
+ rt_list_remove(&accepted->pending_node);
+ accepted->pending = 0;
+ sock->pending_count--;
+ handle = accepted->handle;
+ rt_wqueue_wakeup_all(&sock->wait_queue,
+ (void *)(rt_ubase_t)POLLOUT);
+ af_unix_unlock();
+ return handle;
+ }
+
+ nonblocking = af_unix_is_nonblocking(sock, 0);
+ timeout = sock->receive_timeout;
+ af_unix_unlock();
+ if (nonblocking)
+ {
+ return af_unix_error(EAGAIN);
+ }
+ if (af_unix_wait(&sock->wait_queue, timeout) < 0)
+ {
+ return -1;
+ }
+ }
+}
+
+int af_unix_stream_send(struct af_unix_socket *sock, const void *data,
+ size_t size, int flags,
+ struct af_unix_rights *rights)
+{
+ int nonblocking;
+ int timeout;
+ size_t copy_length;
+ size_t space;
+ struct af_unix_socket *peer;
+
+ if (size != 0 && data == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+ if (size == 0)
+ {
+ return 0;
+ }
+
+ for (;;)
+ {
+ af_unix_lock();
+ if (sock->closed)
+ {
+ af_unix_unlock();
+ return af_unix_error(EBADF);
+ }
+ if (sock->write_shutdown)
+ {
+ af_unix_unlock();
+ return af_unix_error(EPIPE);
+ }
+ peer = sock->peer;
+ if (!sock->connected || peer == RT_NULL)
+ {
+ af_unix_unlock();
+ return af_unix_error(ENOTCONN);
+ }
+ if (peer->closed || peer->read_shutdown)
+ {
+ af_unix_unlock();
+ return af_unix_error(EPIPE);
+ }
+
+ space = AF_UNIX_STREAM_BUFFER_SIZE - peer->stream_length;
+ if (space != 0)
+ {
+ copy_length = size;
+ if (copy_length > space)
+ {
+ copy_length = space;
+ }
+ if (rights != RT_NULL)
+ {
+ rights->offset = peer->stream_write_offset;
+ rt_list_insert_before(&peer->rights_queue, &rights->node);
+ }
+ af_unix_stream_buffer_write_locked(peer, (const char *)data,
+ copy_length);
+ rt_wqueue_wakeup_all(&peer->wait_queue,
+ (void *)(rt_ubase_t)POLLIN);
+ af_unix_unlock();
+ return (int)copy_length;
+ }
+
+ nonblocking = af_unix_is_nonblocking(sock, flags);
+ timeout = sock->send_timeout;
+ af_unix_socket_ref_locked(peer);
+ af_unix_unlock();
+ if (nonblocking)
+ {
+ af_unix_socket_put(peer);
+ return af_unix_error(EAGAIN);
+ }
+ if (af_unix_wait(&peer->wait_queue, timeout) < 0)
+ {
+ af_unix_socket_put(peer);
+ return -1;
+ }
+ af_unix_socket_put(peer);
+ }
+}
+
+int af_unix_stream_receive(struct af_unix_socket *sock, void *data,
+ size_t size, int flags, rt_list_t *rights)
+{
+ int nonblocking;
+ int timeout;
+ int wait_all;
+ size_t copy_length;
+ size_t received = 0;
+ struct af_unix_socket *peer;
+
+ if (size != 0 && data == RT_NULL)
+ {
+ return af_unix_error(EINVAL);
+ }
+ if (size == 0)
+ {
+ return 0;
+ }
+ wait_all = ((flags & MSG_WAITALL) != 0 && (flags & MSG_PEEK) == 0);
+
+ for (;;)
+ {
+ af_unix_lock();
+ if (sock->closed)
+ {
+ af_unix_unlock();
+ return received != 0 ? (int)received : af_unix_error(EBADF);
+ }
+ if (sock->read_shutdown)
+ {
+ af_unix_unlock();
+ return (int)received;
+ }
+ if (sock->stream_length != 0)
+ {
+ copy_length = size - received;
+ if (copy_length > sock->stream_length)
+ {
+ copy_length = sock->stream_length;
+ }
+ af_unix_stream_buffer_read_locked(sock,
+ (char *)data + received,
+ copy_length,
+ (flags & MSG_PEEK) != 0);
+ received += copy_length;
+ if ((flags & MSG_PEEK) == 0)
+ {
+ af_unix_stream_collect_rights_locked(sock, rights);
+ af_unix_wakeup_writable_locked(sock);
+ }
+ if (!wait_all || received == size)
+ {
+ af_unix_unlock();
+ return (int)received;
+ }
+ }
+
+ peer = sock->peer;
+ if (peer == RT_NULL || peer->closed || peer->write_shutdown)
+ {
+ af_unix_unlock();
+ return (int)received;
+ }
+
+ nonblocking = af_unix_is_nonblocking(sock, flags);
+ timeout = sock->receive_timeout;
+ af_unix_unlock();
+ if (nonblocking)
+ {
+ return received != 0 ? (int)received : af_unix_error(EAGAIN);
+ }
+ if (af_unix_wait(&sock->wait_queue, timeout) < 0)
+ {
+ return received != 0 ? (int)received : -1;
+ }
+ }
+}
+
+int af_unix_stream_shutdown(struct af_unix_socket *sock, int how)
+{
+ int result = 0;
+ struct af_unix_socket *peer;
+
+ if (how < SHUT_RD || how > SHUT_RDWR)
+ {
+ return af_unix_error(EINVAL);
+ }
+
+ af_unix_lock();
+ if (sock->closed)
+ {
+ result = af_unix_error(EBADF);
+ }
+ else if (!sock->connected)
+ {
+ result = af_unix_error(ENOTCONN);
+ }
+ else
+ {
+ if (how == SHUT_RD || how == SHUT_RDWR)
+ {
+ sock->read_shutdown = 1;
+ sock->stream_head = 0;
+ sock->stream_read_offset += sock->stream_length;
+ sock->stream_length = 0;
+ af_unix_rights_list_defer_locked(&sock->rights_queue);
+ }
+ if (how == SHUT_WR || how == SHUT_RDWR)
+ {
+ sock->write_shutdown = 1;
+ }
+ rt_wqueue_wakeup_all(&sock->wait_queue,
+ (void *)(rt_ubase_t)(POLLIN | POLLOUT));
+ peer = sock->peer;
+ if (peer != RT_NULL)
+ {
+ rt_wqueue_wakeup_all(&peer->wait_queue,
+ (void *)(rt_ubase_t)(POLLIN | POLLOUT));
+ }
+ }
+ af_unix_unlock();
+ return result;
+}
diff --git a/components/net/af_unix/testcases/af_unix_test.c b/components/net/af_unix/testcases/af_unix_test.c
new file mode 100644
index 0000000000..874546fed5
--- /dev/null
+++ b/components/net/af_unix/testcases/af_unix_test.c
@@ -0,0 +1,767 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <utest.h>
+
+#define AF_UNIX_DGRAM_SERVER_PATH "/dev/afudgs"
+#define AF_UNIX_DGRAM_CLIENT_PATH "/dev/afudgc"
+#define AF_UNIX_STREAM_PATH "/dev/afustr"
+#define AF_UNIX_PENDING_PATH "/dev/afupend"
+#define AF_UNIX_PERSIST_PATH "/var/afupath"
+
+static void af_unix_make_address(struct sockaddr_un *address,
+ const char *path)
+{
+ rt_memset(address, 0, sizeof(*address));
+ address->sa_family = AF_UNIX;
+ rt_strncpy(address->sun_path, path, sizeof(address->sun_path) - 1);
+}
+
+static int af_unix_send_rights(int socket_fd, const int *fds, size_t fd_count,
+ char payload)
+{
+ char control[CMSG_SPACE(2 * sizeof(int))];
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ struct msghdr message;
+
+ rt_memset(&message, 0, sizeof(message));
+ rt_memset(control, 0, sizeof(control));
+ iov.iov_base = &payload;
+ iov.iov_len = sizeof(payload);
+ message.msg_iov = &iov;
+ message.msg_iovlen = 1;
+ message.msg_control = control;
+ message.msg_controllen = CMSG_SPACE(fd_count * sizeof(int));
+ cmsg = CMSG_FIRSTHDR(&message);
+ cmsg->cmsg_len = CMSG_LEN(fd_count * sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ rt_memcpy(CMSG_DATA(cmsg), fds, fd_count * sizeof(int));
+ return sendmsg(socket_fd, &message, 0);
+}
+
+static int af_unix_receive_rights(int socket_fd, int flags,
+ size_t control_length, int *fds,
+ size_t *fd_count, int *message_flags,
+ char *payload)
+{
+ char control[CMSG_SPACE(2 * sizeof(int))];
+ int result;
+ size_t count = 0;
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ struct msghdr message;
+
+ rt_memset(&message, 0, sizeof(message));
+ rt_memset(control, 0, sizeof(control));
+ iov.iov_base = payload;
+ iov.iov_len = sizeof(*payload);
+ message.msg_iov = &iov;
+ message.msg_iovlen = 1;
+ message.msg_control = control_length != 0 ? control : RT_NULL;
+ message.msg_controllen = control_length;
+
+ result = recvmsg(socket_fd, &message, flags);
+ if (result >= 0)
+ {
+ cmsg = CMSG_FIRSTHDR(&message);
+ if (cmsg != RT_NULL && cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS &&
+ cmsg->cmsg_len >= CMSG_LEN(sizeof(int)))
+ {
+ count = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof(int);
+ rt_memcpy(fds, CMSG_DATA(cmsg), count * sizeof(int));
+ }
+ *fd_count = count;
+ *message_flags = message.msg_flags;
+ }
+ return result;
+}
+
+static void af_unix_test_dgram(void)
+{
+ int server = -1;
+ int client = -1;
+ int error;
+ int result;
+ char buffer[16];
+ const char payload[] = "datagram";
+ struct sockaddr_un server_address;
+ struct sockaddr_un client_address;
+ struct sockaddr_un source_address;
+ socklen_t source_length;
+
+ (void)unlink(AF_UNIX_DGRAM_SERVER_PATH);
+ (void)unlink(AF_UNIX_DGRAM_CLIENT_PATH);
+ af_unix_make_address(&server_address, AF_UNIX_DGRAM_SERVER_PATH);
+ af_unix_make_address(&client_address, AF_UNIX_DGRAM_CLIENT_PATH);
+
+ server = socket(AF_UNIX, SOCK_DGRAM, 0);
+ client = socket(AF_UNIX, SOCK_DGRAM, 0);
+ uassert_true(server >= 0);
+ uassert_true(client >= 0);
+ if (server < 0 || client < 0)
+ {
+ goto __exit;
+ }
+
+ uassert_int_equal(bind(server, (struct sockaddr *)&server_address,
+ sizeof(server_address)),
+ 0);
+ uassert_int_equal(bind(client, (struct sockaddr *)&client_address,
+ sizeof(client_address)),
+ 0);
+ uassert_int_equal(connect(client, (struct sockaddr *)&server_address,
+ sizeof(server_address)),
+ 0);
+ uassert_int_equal(send(client, payload, sizeof(payload), 0),
+ sizeof(payload));
+
+ source_length = sizeof(source_address);
+ result = recvfrom(server, buffer, sizeof(buffer), 0,
+ (struct sockaddr *)&source_address, &source_length);
+ uassert_int_equal(result, sizeof(payload));
+ uassert_buf_equal(buffer, payload, sizeof(payload));
+ uassert_str_equal(source_address.sun_path, AF_UNIX_DGRAM_CLIENT_PATH);
+
+ result = fcntl(server, F_SETFL, O_NONBLOCK);
+ uassert_int_equal(result, 0);
+ result = recv(server, buffer, sizeof(buffer), 0);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_true(error == EAGAIN || error == EWOULDBLOCK);
+
+__exit:
+ if (client >= 0)
+ {
+ closesocket(client);
+ }
+ if (server >= 0)
+ {
+ closesocket(server);
+ }
+ (void)unlink(AF_UNIX_DGRAM_CLIENT_PATH);
+ (void)unlink(AF_UNIX_DGRAM_SERVER_PATH);
+}
+
+static void af_unix_test_pathname_lifetime(void)
+{
+ int first = -1;
+ int second = -1;
+ int error;
+ int result;
+ struct stat file_stat;
+ struct sockaddr_un address;
+
+ (void)unlink(AF_UNIX_PERSIST_PATH);
+ af_unix_make_address(&address, AF_UNIX_PERSIST_PATH);
+
+ first = socket(AF_UNIX, SOCK_DGRAM, 0);
+ uassert_true(first >= 0);
+ if (first < 0)
+ {
+ goto __exit;
+ }
+ uassert_int_equal(bind(first, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ uassert_int_equal(stat(AF_UNIX_PERSIST_PATH, &file_stat), 0);
+ uassert_true(S_ISSOCK(file_stat.st_mode));
+
+ closesocket(first);
+ first = -1;
+ uassert_int_equal(stat(AF_UNIX_PERSIST_PATH, &file_stat), 0);
+ uassert_true(S_ISSOCK(file_stat.st_mode));
+
+ second = socket(AF_UNIX, SOCK_DGRAM, 0);
+ uassert_true(second >= 0);
+ if (second < 0)
+ {
+ goto __exit;
+ }
+ result = bind(second, (struct sockaddr *)&address, sizeof(address));
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EADDRINUSE);
+
+ uassert_int_equal(unlink(AF_UNIX_PERSIST_PATH), 0);
+ uassert_int_equal(bind(second, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+
+__exit:
+ if (second >= 0)
+ {
+ closesocket(second);
+ }
+ if (first >= 0)
+ {
+ closesocket(first);
+ }
+ (void)unlink(AF_UNIX_PERSIST_PATH);
+}
+
+static void af_unix_test_stream(void)
+{
+ int listener = -1;
+ int client = -1;
+ int accepted = -1;
+ char buffer[16];
+ const char request[] = "request";
+ const char response[] = "response";
+ struct sockaddr_un address;
+
+ (void)unlink(AF_UNIX_STREAM_PATH);
+ af_unix_make_address(&address, AF_UNIX_STREAM_PATH);
+
+ listener = socket(AF_UNIX, SOCK_STREAM, 0);
+ client = socket(AF_UNIX, SOCK_STREAM, 0);
+ uassert_true(listener >= 0);
+ uassert_true(client >= 0);
+ if (listener < 0 || client < 0)
+ {
+ goto __exit;
+ }
+
+ uassert_int_equal(bind(listener, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ uassert_int_equal(listen(listener, 2), 0);
+ uassert_int_equal(connect(client, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ accepted = accept(listener, RT_NULL, RT_NULL);
+ uassert_true(accepted >= 0);
+ if (accepted < 0)
+ {
+ goto __exit;
+ }
+
+ uassert_int_equal(send(client, request, sizeof(request), 0),
+ sizeof(request));
+ uassert_int_equal(recv(accepted, buffer, sizeof(buffer), 0),
+ sizeof(request));
+ uassert_buf_equal(buffer, request, sizeof(request));
+ uassert_int_equal(send(accepted, response, sizeof(response), 0),
+ sizeof(response));
+ uassert_int_equal(recv(client, buffer, sizeof(buffer), 0),
+ sizeof(response));
+ uassert_buf_equal(buffer, response, sizeof(response));
+
+ uassert_int_equal(shutdown(client, SHUT_WR), 0);
+ uassert_int_equal(recv(accepted, buffer, sizeof(buffer), 0), 0);
+
+__exit:
+ if (accepted >= 0)
+ {
+ closesocket(accepted);
+ }
+ if (client >= 0)
+ {
+ closesocket(client);
+ }
+ if (listener >= 0)
+ {
+ closesocket(listener);
+ }
+ (void)unlink(AF_UNIX_STREAM_PATH);
+}
+
+static void af_unix_test_dgram_readiness(void)
+{
+ int error;
+ int index;
+ int result;
+ int sockets[2] = { -1, -1 };
+ char buffer[2];
+ const char payload[] = "x";
+ struct pollfd poll_fd;
+
+ result = socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, sockets);
+ uassert_int_equal(result, 0);
+ if (result < 0)
+ {
+ return;
+ }
+
+ poll_fd.fd = sockets[1];
+ poll_fd.events = POLLIN;
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 0);
+
+ for (index = 0; index < AF_UNIX_DGRAM_QUEUE_LEN; index++)
+ {
+ uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
+ sizeof(payload));
+ }
+ result = send(sockets[0], payload, sizeof(payload), 0);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EAGAIN);
+
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 1);
+ uassert_true((poll_fd.revents & POLLIN) != 0);
+ uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0),
+ sizeof(payload));
+ uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
+ sizeof(payload));
+
+ result = send(sockets[0], payload, AF_UNIX_DGRAM_MAX_SIZE + 1, 0);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EMSGSIZE);
+
+ closesocket(sockets[0]);
+ closesocket(sockets[1]);
+}
+
+static void af_unix_test_stream_readiness(void)
+{
+ int result;
+ int sockets[2] = { -1, -1 };
+ char buffer[8];
+ const char payload[] = "poll";
+ struct pollfd poll_fd;
+
+ result = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+ uassert_int_equal(result, 0);
+ if (result < 0)
+ {
+ return;
+ }
+
+ poll_fd.fd = sockets[1];
+ poll_fd.events = POLLIN | POLLOUT;
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 1);
+ uassert_true((poll_fd.revents & POLLOUT) != 0);
+ uassert_true((poll_fd.revents & POLLIN) == 0);
+
+ uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
+ sizeof(payload));
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 1);
+ uassert_true((poll_fd.revents & POLLIN) != 0);
+ uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0),
+ sizeof(payload));
+
+ uassert_int_equal(shutdown(sockets[0], SHUT_WR), 0);
+ poll_fd.revents = 0;
+ uassert_int_equal(poll(&poll_fd, 1, 0), 1);
+ uassert_true((poll_fd.revents & POLLIN) != 0);
+ uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0), 0);
+
+ closesocket(sockets[0]);
+ closesocket(sockets[1]);
+}
+
+static void af_unix_test_pending_close(void)
+{
+ int client = -1;
+ int error;
+ int listener = -1;
+ int result;
+ const char payload[] = "pending";
+ struct sockaddr_un address;
+
+ (void)unlink(AF_UNIX_PENDING_PATH);
+ af_unix_make_address(&address, AF_UNIX_PENDING_PATH);
+
+ listener = socket(AF_UNIX, SOCK_STREAM, 0);
+ client = socket(AF_UNIX, SOCK_STREAM, 0);
+ uassert_true(listener >= 0);
+ uassert_true(client >= 0);
+ if (listener < 0 || client < 0)
+ {
+ goto __exit;
+ }
+
+ uassert_int_equal(bind(listener, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ uassert_int_equal(listen(listener, 1), 0);
+ uassert_int_equal(connect(client, (struct sockaddr *)&address,
+ sizeof(address)),
+ 0);
+ closesocket(listener);
+ listener = -1;
+
+ result = send(client, payload, sizeof(payload), 0);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_true(error == ENOTCONN || error == EPIPE);
+
+__exit:
+ if (client >= 0)
+ {
+ closesocket(client);
+ }
+ if (listener >= 0)
+ {
+ closesocket(listener);
+ }
+ (void)unlink(AF_UNIX_PENDING_PATH);
+}
+
+static void af_unix_test_socketpair(void)
+{
+ int type_index;
+ int types[2] = { SOCK_DGRAM, SOCK_STREAM };
+ int sockets[2];
+ char buffer[8];
+ char first_part[3];
+ char second_part[3];
+ const char payload[] = "pair";
+ struct iovec receive_iov[2];
+ struct iovec send_iov[2];
+ struct msghdr receive_message;
+ struct msghdr send_message;
+
+ for (type_index = 0; type_index < 2; type_index++)
+ {
+ uassert_int_equal(socketpair(AF_UNIX, types[type_index], 0,
+ sockets),
+ 0);
+ uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
+ sizeof(payload));
+ uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0),
+ sizeof(payload));
+ uassert_buf_equal(buffer, payload, sizeof(payload));
+
+ rt_memset(&send_message, 0, sizeof(send_message));
+ send_iov[0].iov_base = (void *)payload;
+ send_iov[0].iov_len = 2;
+ send_iov[1].iov_base = (void *)(payload + 2);
+ send_iov[1].iov_len = sizeof(payload) - 2;
+ send_message.msg_iov = send_iov;
+ send_message.msg_iovlen = 2;
+ uassert_int_equal(sendmsg(sockets[0], &send_message, 0),
+ sizeof(payload));
+
+ rt_memset(&receive_message, 0, sizeof(receive_message));
+ receive_iov[0].iov_base = first_part;
+ receive_iov[0].iov_len = sizeof(first_part);
+ receive_iov[1].iov_base = second_part;
+ receive_iov[1].iov_len = sizeof(second_part);
+ receive_message.msg_iov = receive_iov;
+ receive_message.msg_iovlen = 2;
+ uassert_int_equal(recvmsg(sockets[1], &receive_message, 0),
+ sizeof(payload));
+ uassert_buf_equal(first_part, payload, sizeof(first_part));
+ uassert_buf_equal(second_part, payload + sizeof(first_part),
+ sizeof(payload) - sizeof(first_part));
+ closesocket(sockets[0]);
+ closesocket(sockets[1]);
+ }
+}
+
+static void af_unix_test_rights_transfer(void)
+{
+ char data;
+ char payload;
+ int received_fds[2] = { -1, -1 };
+ int pipe_fds[2][2] = { { -1, -1 }, { -1, -1 } };
+ int send_fds[2];
+ int sockets[2] = { -1, -1 };
+ int type_index;
+ int types[2] = { SOCK_DGRAM, SOCK_STREAM };
+ size_t fd_count;
+ int message_flags;
+
+ for (type_index = 0; type_index < 2; type_index++)
+ {
+ received_fds[0] = -1;
+ received_fds[1] = -1;
+ pipe_fds[0][0] = -1;
+ pipe_fds[0][1] = -1;
+ pipe_fds[1][0] = -1;
+ pipe_fds[1][1] = -1;
+ sockets[0] = -1;
+ sockets[1] = -1;
+
+ uassert_int_equal(socketpair(AF_UNIX, types[type_index], 0,
+ sockets),
+ 0);
+ uassert_int_equal(pipe(pipe_fds[0]), 0);
+ uassert_int_equal(pipe(pipe_fds[1]), 0);
+ if (sockets[0] < 0 || pipe_fds[0][0] < 0 || pipe_fds[1][0] < 0)
+ {
+ goto __iteration_exit;
+ }
+
+ uassert_int_equal(write(pipe_fds[0][1], "a", 1), 1);
+ uassert_int_equal(write(pipe_fds[1][1], "b", 1), 1);
+ send_fds[0] = pipe_fds[0][0];
+ send_fds[1] = pipe_fds[1][0];
+ uassert_int_equal(af_unix_send_rights(sockets[0], send_fds, 2,
+ 'r'),
+ 1);
+ close(pipe_fds[0][0]);
+ pipe_fds[0][0] = -1;
+ close(pipe_fds[1][0]);
+ pipe_fds[1][0] = -1;
+
+ fd_count = 0;
+ message_flags = 0;
+ uassert_int_equal(af_unix_receive_rights(
+ sockets[1], 0,
+ CMSG_SPACE(2 * sizeof(int)), received_fds,
+ &fd_count, &message_flags, &payload),
+ 1);
+ uassert_int_equal(payload, 'r');
+ uassert_int_equal(fd_count, 2);
+ uassert_int_equal(message_flags & MSG_CTRUNC, 0);
+ uassert_int_equal(read(received_fds[0], &data, 1), 1);
+ uassert_int_equal(data, 'a');
+ uassert_int_equal(read(received_fds[1], &data, 1), 1);
+ uassert_int_equal(data, 'b');
+
+ __iteration_exit:
+ if (received_fds[0] >= 0)
+ {
+ close(received_fds[0]);
+ }
+ if (received_fds[1] >= 0)
+ {
+ close(received_fds[1]);
+ }
+ if (pipe_fds[0][0] >= 0)
+ {
+ close(pipe_fds[0][0]);
+ }
+ if (pipe_fds[0][1] >= 0)
+ {
+ close(pipe_fds[0][1]);
+ }
+ if (pipe_fds[1][0] >= 0)
+ {
+ close(pipe_fds[1][0]);
+ }
+ if (pipe_fds[1][1] >= 0)
+ {
+ close(pipe_fds[1][1]);
+ }
+ if (sockets[0] >= 0)
+ {
+ closesocket(sockets[0]);
+ }
+ if (sockets[1] >= 0)
+ {
+ closesocket(sockets[1]);
+ }
+ }
+}
+
+static void af_unix_test_rights_truncation(void)
+{
+ char payload;
+ int message_flags = 0;
+ int pipe_fds[2][2] = { { -1, -1 }, { -1, -1 } };
+ int received_fds[2] = { -1, -1 };
+ int send_fds[2];
+ int sockets[2] = { -1, -1 };
+ size_t fd_count = 0;
+
+ uassert_int_equal(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets), 0);
+ uassert_int_equal(pipe(pipe_fds[0]), 0);
+ uassert_int_equal(pipe(pipe_fds[1]), 0);
+ if (sockets[0] < 0 || pipe_fds[0][0] < 0 || pipe_fds[1][0] < 0)
+ {
+ goto __exit;
+ }
+
+ send_fds[0] = pipe_fds[0][0];
+ send_fds[1] = pipe_fds[1][0];
+ uassert_int_equal(af_unix_send_rights(sockets[0], send_fds, 2, 't'), 1);
+ uassert_int_equal(af_unix_receive_rights(
+ sockets[1], 0, 0,
+ received_fds, &fd_count, &message_flags, &payload),
+ 1);
+ uassert_int_equal(fd_count, 0);
+ uassert_true((message_flags & MSG_CTRUNC) != 0);
+
+__exit:
+ if (received_fds[0] >= 0)
+ {
+ close(received_fds[0]);
+ }
+ if (pipe_fds[0][0] >= 0)
+ {
+ close(pipe_fds[0][0]);
+ }
+ if (pipe_fds[0][1] >= 0)
+ {
+ close(pipe_fds[0][1]);
+ }
+ if (pipe_fds[1][0] >= 0)
+ {
+ close(pipe_fds[1][0]);
+ }
+ if (pipe_fds[1][1] >= 0)
+ {
+ close(pipe_fds[1][1]);
+ }
+ if (sockets[0] >= 0)
+ {
+ closesocket(sockets[0]);
+ }
+ if (sockets[1] >= 0)
+ {
+ closesocket(sockets[1]);
+ }
+}
+
+static void af_unix_test_rights_peek_and_invalid(void)
+{
+ char payload;
+ int error;
+ int invalid_fd = -1;
+ int message_flags = 0;
+ int pipe_fds[2] = { -1, -1 };
+ int received_fds[2] = { -1, -1 };
+ int sockets[2] = { -1, -1 };
+ int result;
+ size_t fd_count = 0;
+
+ uassert_int_equal(socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0,
+ sockets),
+ 0);
+ uassert_int_equal(pipe(pipe_fds), 0);
+ if (sockets[0] < 0 || pipe_fds[0] < 0)
+ {
+ goto __exit;
+ }
+
+ result = af_unix_send_rights(sockets[0], &invalid_fd, 1, 'x');
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EBADF);
+
+ uassert_int_equal(af_unix_send_rights(sockets[0], pipe_fds, 1, 'p'), 1);
+ fd_count = 0;
+ uassert_int_equal(af_unix_receive_rights(
+ sockets[1], MSG_PEEK, CMSG_SPACE(sizeof(int)),
+ received_fds, &fd_count, &message_flags, &payload),
+ 1);
+ uassert_int_equal(payload, 'p');
+ uassert_int_equal(fd_count, 0);
+
+ fd_count = 0;
+ message_flags = 0;
+ uassert_int_equal(af_unix_receive_rights(
+ sockets[1], 0, CMSG_SPACE(sizeof(int)),
+ received_fds, &fd_count, &message_flags, &payload),
+ 1);
+ uassert_int_equal(fd_count, 1);
+
+__exit:
+ if (received_fds[0] >= 0)
+ {
+ close(received_fds[0]);
+ }
+ if (pipe_fds[0] >= 0)
+ {
+ close(pipe_fds[0]);
+ }
+ if (pipe_fds[1] >= 0)
+ {
+ close(pipe_fds[1]);
+ }
+ if (sockets[0] >= 0)
+ {
+ closesocket(sockets[0]);
+ }
+ if (sockets[1] >= 0)
+ {
+ closesocket(sockets[1]);
+ }
+}
+
+static void af_unix_test_rights_socket(void)
+{
+ char payload = 0;
+ int carrier[2] = { -1, -1 };
+ int error;
+ int message_flags = 0;
+ int passed[2] = { -1, -1 };
+ int received_fds[2] = { -1, -1 };
+ int result;
+ size_t fd_count = 0;
+
+ uassert_int_equal(socketpair(AF_UNIX, SOCK_STREAM, 0, carrier), 0);
+ uassert_int_equal(socketpair(AF_UNIX, SOCK_DGRAM, 0, passed), 0);
+ if (carrier[0] < 0 || passed[0] < 0)
+ {
+ goto __exit;
+ }
+
+ result = af_unix_send_rights(carrier[0], &passed[1], 1, 's');
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_int_equal(error, EOPNOTSUPP);
+
+ result = af_unix_receive_rights(carrier[1], MSG_DONTWAIT,
+ CMSG_SPACE(sizeof(int)), received_fds,
+ &fd_count, &message_flags, &payload);
+ error = rt_get_errno();
+ uassert_int_equal(result, -1);
+ uassert_true(error == EAGAIN || error == EWOULDBLOCK);
+ uassert_int_equal(fd_count, 0);
+
+ uassert_int_equal(send(passed[0], "q", 1, 0), 1);
+ uassert_int_equal(recv(passed[1], &payload, 1, 0), 1);
+ uassert_int_equal(payload, 'q');
+
+__exit:
+ if (received_fds[0] >= 0)
+ {
+ closesocket(received_fds[0]);
+ }
+ if (passed[0] >= 0)
+ {
+ closesocket(passed[0]);
+ }
+ if (passed[1] >= 0)
+ {
+ closesocket(passed[1]);
+ }
+ if (carrier[0] >= 0)
+ {
+ closesocket(carrier[0]);
+ }
+ if (carrier[1] >= 0)
+ {
+ closesocket(carrier[1]);
+ }
+}
+
+static void af_unix_testcase(void)
+{
+ UTEST_UNIT_RUN(af_unix_test_dgram);
+ UTEST_UNIT_RUN(af_unix_test_stream);
+ UTEST_UNIT_RUN(af_unix_test_dgram_readiness);
+ UTEST_UNIT_RUN(af_unix_test_stream_readiness);
+ UTEST_UNIT_RUN(af_unix_test_pending_close);
+ UTEST_UNIT_RUN(af_unix_test_socketpair);
+ UTEST_UNIT_RUN(af_unix_test_pathname_lifetime);
+ UTEST_UNIT_RUN(af_unix_test_rights_transfer);
+ UTEST_UNIT_RUN(af_unix_test_rights_truncation);
+ UTEST_UNIT_RUN(af_unix_test_rights_peek_and_invalid);
+ UTEST_UNIT_RUN(af_unix_test_rights_socket);
+}
+
+UTEST_TC_EXPORT(af_unix_testcase, "components.net.af_unix", RT_NULL,
+ RT_NULL, 20);
diff --git a/components/net/sal/include/sal_low_lvl.h b/components/net/sal/include/sal_low_lvl.h
index c19c10ffc1..759bad730f 100644
--- a/components/net/sal/include/sal_low_lvl.h
+++ b/components/net/sal/include/sal_low_lvl.h
@@ -48,6 +48,7 @@ typedef uint32_t socklen_t;
struct sockaddr;
struct msghdr;
struct addrinfo;
+struct sal_proto_family;
struct sal_socket
{
uint32_t magic; /* SAL socket magic word */
@@ -58,6 +59,7 @@ struct sal_socket
int protocol;
struct netdev *netdev; /* SAL network interface device */
+ const struct sal_proto_family *protocol_family; /* selected protocol provider */
void *user_data; /* user-specific data */
#ifdef SAL_USING_TLS
@@ -109,6 +111,9 @@ struct sal_proto_family
/* SAL(Socket Abstraction Layer) initialize */
int sal_init(void);
+/* Register and find protocol providers which do not require a netdev. */
+int sal_proto_family_register(const struct sal_proto_family *pf);
+const struct sal_proto_family *sal_proto_family_find(int family);
/* Get SAL socket object by socket descriptor */
struct sal_socket *sal_get_socket(int sock);
diff --git a/components/net/sal/include/sal_socket.h b/components/net/sal/include/sal_socket.h
index 527f3fe440..7d74310094 100644
--- a/components/net/sal/include/sal_socket.h
+++ b/components/net/sal/include/sal_socket.h
@@ -119,6 +119,9 @@ typedef uint16_t in_port_t;
#define MSG_DONTWAIT 0x08 /* Nonblocking i/o for this operation only */
#define MSG_MORE 0x10 /* Sender will send more */
+/* Output-only flags returned through struct msghdr. */
+#define MSG_CTRUNC 0x08 /* Control data was discarded due to truncation */
+
#define MSG_ERRQUEUE 0x2000 /* Fetch message from error queue */
#define MSG_CONFIRM 0x0800 /* Confirm path validity */
diff --git a/components/net/sal/socket/net_sockets.c b/components/net/sal/socket/net_sockets.c
index 615bb0ed03..50c20b908a 100644
--- a/components/net/sal/socket/net_sockets.c
+++ b/components/net/sal/socket/net_sockets.c
@@ -725,7 +725,11 @@ int closesocket(int s)
return -1;
}
+#ifdef RT_USING_DFS_V2
+ if (dfs_file_close(d) == 0)
+#else
if (sal_closesocket(socket) == 0)
+#endif
{
error = 0;
}
@@ -771,8 +775,19 @@ RTM_EXPORT(closesocket);
int socketpair(int domain, int type, int protocol, int *fds)
{
rt_err_t ret = 0;
+ int nonblocking = 0;
int sock_fds[2];
+ if ((type & SOCK_CLOEXEC) != 0)
+ {
+ type &= ~SOCK_CLOEXEC;
+ }
+ if ((type & SOCK_NONBLOCK) != 0)
+ {
+ nonblocking = 1;
+ type &= ~SOCK_NONBLOCK;
+ }
+
fds[0] = socket(domain, type, protocol);
if (fds[0] < 0)
{
@@ -799,6 +814,11 @@ int socketpair(int domain, int type, int protocol, int *fds)
closesocket(fds[0]);
closesocket(fds[1]);
}
+ else if (nonblocking)
+ {
+ (void)fcntl(fds[0], F_SETFL, O_NONBLOCK);
+ (void)fcntl(fds[1], F_SETFL, O_NONBLOCK);
+ }
return ret;
}
diff --git a/components/net/sal/src/sal_socket.c b/components/net/sal/src/sal_socket.c
index fa9eb9cc74..b420eb2957 100644
--- a/components/net/sal/src/sal_socket.c
+++ b/components/net/sal/src/sal_socket.c
@@ -44,16 +44,14 @@
#include <rtdbg.h>
#define VALID_PROTOCOL(protocol) ((protocol) >= 0 && (protocol) <= IPPROTO_RAW)
-#define VALID_COMBO(domain, type, protocol) \
- ( \
- (((domain) == AF_INET || (domain) == AF_INET6) && \
- (((type) == SOCK_STREAM && ((protocol) == 0 || (protocol) == IPPROTO_TCP)) || \
- ((type) == SOCK_DGRAM && ((protocol) == 0 || (protocol) == IPPROTO_UDP)) || \
- ((type) == SOCK_RAW && ((protocol) == IPPROTO_RAW)) \
- )) || \
- ((domain) == AF_UNIX && (type) == SOCK_STREAM && (protocol) == 0) || \
- ((domain) == AF_NETLINK && (type) == SOCK_RAW && (protocol) == 0) \
- )
+#define VALID_COMBO(domain, type, protocol) \
+ ( \
+ (((domain) == AF_INET || (domain) == AF_INET6) && \
+ (((type) == SOCK_STREAM && ((protocol) == 0 || (protocol) == IPPROTO_TCP)) || \
+ ((type) == SOCK_DGRAM && ((protocol) == 0 || (protocol) == IPPROTO_UDP)) || \
+ ((type) == SOCK_RAW && ((protocol) == IPPROTO_RAW)))) || \
+ ((domain) == AF_UNIX && ((type) == SOCK_STREAM || (type) == SOCK_DGRAM) && (protocol) == 0) || \
+ ((domain) == AF_NETLINK && (type) == SOCK_RAW && (protocol) == 0))
/* the socket table used to dynamic allocate sockets */
struct sal_socket_table
@@ -89,9 +87,10 @@ static struct sal_socket_table socket_table;
static struct rt_mutex sal_core_lock;
static rt_bool_t init_ok = RT_FALSE;
static struct sal_netdev_res_table sal_dev_res_tbl[SAL_SOCKETS_NUM];
+static const struct sal_proto_family *local_proto_families[SAL_PROTO_FAMILIES_NUM];
-#define IS_SOCKET_PROTO_TLS(sock) (((sock)->protocol == PROTOCOL_TLS) || \
- ((sock)->protocol == PROTOCOL_DTLS))
+#define IS_SOCKET_PROTO_TLS(sock) (((sock)->protocol == PROTOCOL_TLS) || \
+ ((sock)->protocol == PROTOCOL_DTLS))
#define SAL_SOCKOPS_PROTO_TLS_VALID(sock, name) (proto_tls && (proto_tls->ops->name) && IS_SOCKET_PROTO_TLS(sock))
#define SAL_SOCKOPT_PROTO_TLS_EXEC(sock, name, optval, optlen) \
@@ -113,23 +112,42 @@ static struct sal_netdev_res_table sal_dev_res_tbl[SAL_SOCKETS_NUM];
} \
} while (0)
-#define SAL_NETDEV_IS_UP(netdev) \
- do \
- { \
- if (!netdev_is_up(netdev)) \
- { \
- return -1; \
- } \
+#define SAL_NETDEV_IS_UP(netdev) \
+ do \
+ { \
+ if ((netdev) && !netdev_is_up(netdev)) \
+ { \
+ return -1; \
+ } \
} while (0)
-#define SAL_NETDEV_SOCKETOPS_VALID(netdev, pf, ops) \
- do \
- { \
- (pf) = (struct sal_proto_family *)netdev->sal_user_data; \
- if ((pf)->skt_ops->ops == RT_NULL) \
- { \
- return -1; \
- } \
+#define SAL_SOCKETOPS_VALID(sock, pf, ops) \
+ do \
+ { \
+ (pf) = (sock)->protocol_family; \
+ if ((pf) == RT_NULL || (pf)->skt_ops == RT_NULL || \
+ (pf)->skt_ops->ops == RT_NULL) \
+ { \
+ rt_set_errno(EOPNOTSUPP); \
+ return -1; \
+ } \
+ } while (0)
+
+#define SAL_NETDEV_SOCKETOPS_VALID(netdev, pf, ops) \
+ do \
+ { \
+ if ((netdev) == RT_NULL) \
+ { \
+ rt_set_errno(EOPNOTSUPP); \
+ return -1; \
+ } \
+ (pf) = (struct sal_proto_family *)(netdev)->sal_user_data; \
+ if ((pf) == RT_NULL || (pf)->skt_ops == RT_NULL || \
+ (pf)->skt_ops->ops == RT_NULL) \
+ { \
+ rt_set_errno(EOPNOTSUPP); \
+ return -1; \
+ } \
} while (0)
#define SAL_NETDEV_NETDBOPS_VALID(netdev, pf, ops) \
@@ -181,6 +199,58 @@ int sal_init(void)
}
INIT_COMPONENT_EXPORT(sal_init);
+int sal_proto_family_register(const struct sal_proto_family *pf)
+{
+ int index;
+
+ if (pf == RT_NULL || pf->skt_ops == RT_NULL)
+ {
+ return -EINVAL;
+ }
+
+ for (index = 0; index < SAL_PROTO_FAMILIES_NUM; index++)
+ {
+ if (local_proto_families[index] == pf)
+ {
+ return RT_EOK;
+ }
+ if (local_proto_families[index] != RT_NULL &&
+ (local_proto_families[index]->family == pf->family ||
+ local_proto_families[index]->sec_family == pf->family))
+ {
+ return -EEXIST;
+ }
+ }
+
+ for (index = 0; index < SAL_PROTO_FAMILIES_NUM; index++)
+ {
+ if (local_proto_families[index] == RT_NULL)
+ {
+ local_proto_families[index] = pf;
+ return RT_EOK;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+const struct sal_proto_family *sal_proto_family_find(int family)
+{
+ int index;
+
+ for (index = 0; index < SAL_PROTO_FAMILIES_NUM; index++)
+ {
+ const struct sal_proto_family *pf = local_proto_families[index];
+
+ if (pf != RT_NULL && (pf->family == family || pf->sec_family == family))
+ {
+ return pf;
+ }
+ }
+
+ return RT_NULL;
+}
+
#ifdef SAL_INTERNET_CHECK
/* check SAL network interface device internet status */
static void check_netdev_internet_up_work(struct rt_work *work, void *work_data)
@@ -456,7 +526,7 @@ int sal_netdev_cleanup(struct netdev *netdev)
static int socket_init(int family, int type, int protocol, struct sal_socket **res)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
struct netdev *netdv_def = netdev_default;
struct netdev *netdev = RT_NULL;
rt_bool_t flag = RT_FALSE;
@@ -495,6 +565,14 @@ static int socket_init(int family, int type, int protocol, struct sal_socket **r
return -4;
}
+ pf = sal_proto_family_find(family);
+ if (pf != RT_NULL)
+ {
+ sock->protocol_family = pf;
+ sock->netdev = RT_NULL;
+ return 0;
+ }
+
/* Existing netdev selection logic */
if (netdv_def && netdev_is_up(netdv_def))
{
@@ -503,6 +581,7 @@ static int socket_init(int family, int type, int protocol, struct sal_socket **r
if (pf != RT_NULL && pf->skt_ops && (pf->family == family || pf->sec_family == family))
{
sock->netdev = netdv_def;
+ sock->protocol_family = pf;
flag = RT_TRUE;
}
}
@@ -518,6 +597,11 @@ static int socket_init(int family, int type, int protocol, struct sal_socket **r
}
sock->netdev = netdev;
+ sock->protocol_family = (const struct sal_proto_family *)netdev->sal_user_data;
+ if (sock->protocol_family == RT_NULL || sock->protocol_family->skt_ops == RT_NULL)
+ {
+ return -3;
+ }
}
LOG_D("Socket init success: domain=%d, type=%d, protocol=%d, netdev=%s",
@@ -607,6 +691,7 @@ static int socket_new(void)
sock->socket = idx + SAL_SOCKET_OFFSET;
sock->magic = SAL_SOCKET_MAGIC;
sock->netdev = RT_NULL;
+ sock->protocol_family = RT_NULL;
sock->user_data = RT_NULL;
#ifdef SAL_USING_TLS
sock->user_data_tls = RT_NULL;
@@ -633,6 +718,7 @@ static void socket_delete(int socket)
RT_ASSERT(sock != RT_NULL);
sock->magic = 0;
sock->netdev = RT_NULL;
+ sock->protocol_family = RT_NULL;
socket_free(st, idx);
sal_unlock();
}
@@ -641,7 +727,7 @@ int sal_accept(int socket, struct sockaddr *addr, socklen_t *addrlen)
{
int new_socket;
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
@@ -650,12 +736,11 @@ int sal_accept(int socket, struct sockaddr *addr, socklen_t *addrlen)
SAL_NETDEV_IS_UP(sock->netdev);
/* check the network interface socket operations */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, accept);
+ SAL_SOCKETOPS_VALID(sock, pf, accept);
new_socket = pf->skt_ops->accept((int)(size_t)sock->user_data, addr, addrlen);
if (new_socket != -1)
{
- int retval;
int new_sal_socket;
struct sal_socket *new_sock;
@@ -668,19 +753,11 @@ int sal_accept(int socket, struct sockaddr *addr, socklen_t *addrlen)
return -1;
}
- retval = socket_init(sock->domain, sock->type, sock->protocol, &new_sock);
- if (retval < 0)
- {
- pf->skt_ops->closesocket(new_socket);
- rt_memset(new_sock, 0x00, sizeof(struct sal_socket));
- /* socket init failed, delete socket */
- socket_delete(new_sal_socket);
- LOG_E("New socket registered failed, return error %d.", retval);
- return -1;
- }
-
- /* new socket create by accept should have the same netdev with server*/
+ new_sock->domain = sock->domain;
+ new_sock->type = sock->type;
+ new_sock->protocol = sock->protocol;
new_sock->netdev = sock->netdev;
+ new_sock->protocol_family = sock->protocol_family;
/* socket structure user_data used to store the acquired new socket */
new_sock->user_data = (void *)(size_t)new_socket;
@@ -707,7 +784,7 @@ static void sal_sockaddr_to_ipaddr(const struct sockaddr *name, ip_addr_t *local
int sal_bind(int socket, const struct sockaddr *name, socklen_t namelen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
ip_addr_t input_ipaddr;
RT_ASSERT(name);
@@ -751,19 +828,20 @@ int sal_bind(int socket, const struct sockaddr *name, socklen_t namelen)
return -1;
}
sock->netdev = new_netdev;
+ sock->protocol_family = input_pf;
sock->user_data = (void *)(size_t)new_socket;
}
}
}
/* check and get protocol families by the network interface device */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, bind);
+ SAL_SOCKETOPS_VALID(sock, pf, bind);
return pf->skt_ops->bind((int)(size_t)sock->user_data, name, namelen);
}
int sal_shutdown(int socket, int how)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
int error = 0;
/* get the socket object by socket descriptor */
@@ -771,7 +849,7 @@ int sal_shutdown(int socket, int how)
/* shutdown operation not need to check network interface status */
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, shutdown);
+ SAL_SOCKETOPS_VALID(sock, pf, shutdown);
if (pf->skt_ops->shutdown((int)(size_t)sock->user_data, how) == 0)
{
@@ -798,13 +876,13 @@ int sal_shutdown(int socket, int how)
int sal_getpeername(int socket, struct sockaddr *name, socklen_t *namelen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, getpeername);
+ SAL_SOCKETOPS_VALID(sock, pf, getpeername);
return pf->skt_ops->getpeername((int)(size_t)sock->user_data, name, namelen);
}
@@ -812,13 +890,13 @@ int sal_getpeername(int socket, struct sockaddr *name, socklen_t *namelen)
int sal_getsockname(int socket, struct sockaddr *name, socklen_t *namelen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, getsockname);
+ SAL_SOCKETOPS_VALID(sock, pf, getsockname);
return pf->skt_ops->getsockname((int)(size_t)sock->user_data, name, namelen);
}
@@ -826,13 +904,13 @@ int sal_getsockname(int socket, struct sockaddr *name, socklen_t *namelen)
int sal_getsockopt(int socket, int level, int optname, void *optval, socklen_t *optlen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, getsockopt);
+ SAL_SOCKETOPS_VALID(sock, pf, getsockopt);
return pf->skt_ops->getsockopt((int)(size_t)sock->user_data, level, optname, optval, optlen);
}
@@ -840,13 +918,13 @@ int sal_getsockopt(int socket, int level, int optname, void *optval, socklen_t *
int sal_setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, setsockopt);
+ SAL_SOCKETOPS_VALID(sock, pf, setsockopt);
#ifdef SAL_USING_TLS
if (level == SOL_TLS)
@@ -887,7 +965,7 @@ int sal_setsockopt(int socket, int level, int optname, const void *optval, sockl
int sal_connect(int socket, const struct sockaddr *name, socklen_t namelen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
int ret;
/* get the socket object by socket descriptor */
@@ -896,7 +974,7 @@ int sal_connect(int socket, const struct sockaddr *name, socklen_t namelen)
/* check the network interface is up status */
SAL_NETDEV_IS_UP(sock->netdev);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, connect);
+ SAL_SOCKETOPS_VALID(sock, pf, connect);
ret = pf->skt_ops->connect((int)(size_t)sock->user_data, name, namelen);
#ifdef SAL_USING_TLS
@@ -917,13 +995,13 @@ int sal_connect(int socket, const struct sockaddr *name, socklen_t namelen)
int sal_listen(int socket, int backlog)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, listen);
+ SAL_SOCKETOPS_VALID(sock, pf, listen);
return pf->skt_ops->listen((int)(size_t)sock->user_data, backlog);
}
@@ -931,7 +1009,7 @@ int sal_listen(int socket, int backlog)
int sal_sendmsg(int socket, const struct msghdr *message, int flags)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
@@ -939,7 +1017,7 @@ int sal_sendmsg(int socket, const struct msghdr *message, int flags)
/* check the network interface is up status */
SAL_NETDEV_IS_UP(sock->netdev);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, sendmsg);
+ SAL_SOCKETOPS_VALID(sock, pf, sendmsg);
#ifdef SAL_USING_TLS
if (SAL_SOCKOPS_PROTO_TLS_VALID(sock, send))
@@ -964,7 +1042,7 @@ int sal_sendmsg(int socket, const struct msghdr *message, int flags)
int sal_recvmsg(int socket, struct msghdr *message, int flags)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
@@ -972,7 +1050,7 @@ int sal_recvmsg(int socket, struct msghdr *message, int flags)
/* check the network interface is up status */
SAL_NETDEV_IS_UP(sock->netdev);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, recvmsg);
+ SAL_SOCKETOPS_VALID(sock, pf, recvmsg);
#ifdef SAL_USING_TLS
if (SAL_SOCKOPS_PROTO_TLS_VALID(sock, recv))
@@ -998,7 +1076,7 @@ int sal_recvfrom(int socket, void *mem, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
@@ -1006,7 +1084,7 @@ int sal_recvfrom(int socket, void *mem, size_t len, int flags,
/* check the network interface is up status */
SAL_NETDEV_IS_UP(sock->netdev);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, recvfrom);
+ SAL_SOCKETOPS_VALID(sock, pf, recvfrom);
#ifdef SAL_USING_TLS
if (SAL_SOCKOPS_PROTO_TLS_VALID(sock, recv))
@@ -1032,7 +1110,7 @@ int sal_sendto(int socket, const void *dataptr, size_t size, int flags,
const struct sockaddr *to, socklen_t tolen)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
@@ -1040,7 +1118,7 @@ int sal_sendto(int socket, const void *dataptr, size_t size, int flags,
/* check the network interface is up status */
SAL_NETDEV_IS_UP(sock->netdev);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, sendto);
+ SAL_SOCKETOPS_VALID(sock, pf, sendto);
#ifdef SAL_USING_TLS
if (SAL_SOCKOPS_PROTO_TLS_VALID(sock, send))
@@ -1067,7 +1145,7 @@ int sal_socket(int domain, int type, int protocol)
int retval;
int socket, proto_socket;
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
/* allocate a new socket and registered socket options */
socket = socket_new();
@@ -1094,7 +1172,7 @@ int sal_socket(int domain, int type, int protocol)
}
/* valid the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, socket);
+ SAL_SOCKETOPS_VALID(sock, pf, socket);
proto_socket = pf->skt_ops->socket(domain, type, protocol);
if (proto_socket >= 0)
@@ -1122,7 +1200,7 @@ int sal_socketpair(int domain, int type, int protocol, int *fds)
int unix_fd[2];
struct sal_socket *socka;
struct sal_socket *sockb;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
if (domain == AF_UNIX)
{
@@ -1131,15 +1209,17 @@ int sal_socketpair(int domain, int type, int protocol, int *fds)
SAL_SOCKET_OBJ_GET(sockb, fds[1]);
/* valid the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(socka->netdev, pf, socket);
+ if (socka->protocol_family != sockb->protocol_family)
+ {
+ rt_set_errno(EINVAL);
+ return -1;
+ }
+ SAL_SOCKETOPS_VALID(socka, pf, socketpair);
unix_fd[0] = (int)(size_t)socka->user_data;
unix_fd[1] = (int)(size_t)sockb->user_data;
- if (pf->skt_ops->socketpair)
- {
- return pf->skt_ops->socketpair(domain, type, protocol, unix_fd);
- }
+ return pf->skt_ops->socketpair(domain, type, protocol, unix_fd);
}
rt_set_errno(EINVAL);
@@ -1150,7 +1230,7 @@ int sal_socketpair(int domain, int type, int protocol, int *fds)
int sal_closesocket(int socket)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
int error = 0;
/* get the socket object by socket descriptor */
@@ -1158,7 +1238,7 @@ int sal_closesocket(int socket)
/* clsoesocket operation not need to vaild network interface status */
/* valid the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, closesocket);
+ SAL_SOCKETOPS_VALID(sock, pf, closesocket);
if (pf->skt_ops->closesocket((int)(size_t)sock->user_data) == 0)
{
@@ -1196,13 +1276,19 @@ int sal_ioctlsocket(int socket, long cmd, void *arg)
struct netdev *netdev = RT_NULL;
struct netdev *cur_netdev_list = netdev_list;
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
struct sockaddr_in *addr_in = RT_NULL;
struct sockaddr *addr = RT_NULL;
ip_addr_t input_ipaddr;
/* get the socket object by socket descriptor */
SAL_SOCKET_OBJ_GET(sock, socket);
+ if (sock->netdev == RT_NULL)
+ {
+ SAL_SOCKETOPS_VALID(sock, pf, ioctlsocket);
+ return pf->skt_ops->ioctlsocket((int)(size_t)sock->user_data, cmd, arg);
+ }
+
struct sal_ifreq *ifr = (struct sal_ifreq *)arg;
if (ifr != RT_NULL)
@@ -1528,7 +1614,7 @@ int sal_ioctlsocket(int socket, long cmd, void *arg)
int sal_poll(struct dfs_file *file, struct rt_pollreq *req)
{
struct sal_socket *sock;
- struct sal_proto_family *pf;
+ const struct sal_proto_family *pf;
int socket = (int)(size_t)file->vnode->data;
/* get the socket object by socket descriptor */
@@ -1537,7 +1623,7 @@ int sal_poll(struct dfs_file *file, struct rt_pollreq *req)
/* check the network interface is up status */
SAL_NETDEV_IS_UP(sock->netdev);
/* check the network interface socket opreation */
- SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, poll);
+ SAL_SOCKETOPS_VALID(sock, pf, poll);
return pf->skt_ops->poll(file, req);
}