diff options
| author | Bernard Xiong <[email protected]> | 2026-08-17 17:15:34 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-08-17 17:15:34 +0800 |
| commit | 991f0ffca6ca475adcffc7833876bb8151f16e9c (patch) | |
| tree | 5b69ef28de0b15e1a1d23e53136d37c5e86a8a59 /components/net | |
| parent | d2acd7587eec680b57f8777c5c7a8dce7369471e (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/Kconfig | 1 | ||||
| -rw-r--r-- | components/net/af_unix/Kconfig | 42 | ||||
| -rw-r--r-- | components/net/af_unix/README.md | 57 | ||||
| -rw-r--r-- | components/net/af_unix/SConscript | 12 | ||||
| -rw-r--r-- | components/net/af_unix/include/af_unix.h | 20 | ||||
| -rw-r--r-- | components/net/af_unix/src/af_unix_core.c | 1149 | ||||
| -rw-r--r-- | components/net/af_unix/src/af_unix_dgram.c | 253 | ||||
| -rw-r--r-- | components/net/af_unix/src/af_unix_internal.h | 164 | ||||
| -rw-r--r-- | components/net/af_unix/src/af_unix_namespace.c | 292 | ||||
| -rw-r--r-- | components/net/af_unix/src/af_unix_rights.c | 364 | ||||
| -rw-r--r-- | components/net/af_unix/src/af_unix_stream.c | 483 | ||||
| -rw-r--r-- | components/net/af_unix/testcases/af_unix_test.c | 767 | ||||
| -rw-r--r-- | components/net/sal/include/sal_low_lvl.h | 5 | ||||
| -rw-r--r-- | components/net/sal/include/sal_socket.h | 3 | ||||
| -rw-r--r-- | components/net/sal/socket/net_sockets.c | 20 | ||||
| -rw-r--r-- | components/net/sal/src/sal_socket.c | 246 |
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); } |
