From c93b9f771ec5f01c0ecc5bfc24b760e32540dc06 Mon Sep 17 00:00:00 2001 From: cogu Date: Wed, 30 Sep 2026 21:12:33 +0200 Subject: [PATCH 1/2] Add ability to start socket server from file descriptor --- include/msocket.h | 9 ++++ include/msocket_server.h | 9 ++++ source/msocket_common.c | 8 ++++ source/msocket_internal.h | 1 + source/msocket_posix.c | 11 +++++ source/msocket_server.c | 25 +++++++++++ source/msocket_windows.c | 7 +++ tests/testsuite_msocket_server.c | 75 ++++++++++++++++++++++++++++++++ 8 files changed, 145 insertions(+) diff --git a/include/msocket.h b/include/msocket.h index 7e2b85f..c4c7dec 100644 --- a/include/msocket.h +++ b/include/msocket.h @@ -230,6 +230,15 @@ msocket_error_t msocket_listen(msocket_t *self, uint8_t mode, uint16_t port, con */ msocket_error_t msocket_unix_listen(msocket_t *self, const char *socket_path); +/** + * Adopts an already-bound and listening UNIX domain socket file descriptor (POSIX only). + * + * @param self Pointer to msocket_t instance. + * @param fd Pre-bound listening file descriptor (e.g. from systemd socket activation). + * @return MSOCKET_NO_ERROR on success, or error code on failure. + */ +msocket_error_t msocket_unix_listen_fd(msocket_t *self, int fd); + /** * Binds and configures a listening VSOCK socket (Linux only). * diff --git a/include/msocket_server.h b/include/msocket_server.h index a8a11e8..7f69040 100644 --- a/include/msocket_server.h +++ b/include/msocket_server.h @@ -35,6 +35,7 @@ typedef struct msocket_server_tag { uint16_t udp_port; char *udp_addr; char *socket_path; + int unix_fd; uint32_t vsock_cid; uint32_t vsock_port; adt_ary_t cleanup_items; @@ -126,6 +127,14 @@ void msocket_server_start(msocket_server_t *self, const char *udp_addr, uint16_t */ void msocket_server_unix_start(msocket_server_t *self, const char *socket_path); +/** + * Starts the server using an already-bound and listening UNIX domain socket file descriptor (POSIX only). + * + * @param self Pointer to msocket_server_t instance. + * @param fd Pre-bound listening file descriptor (e.g. from systemd socket activation). + */ +void msocket_server_unix_start_fd(msocket_server_t *self, int fd); + /** * Binds a listening VSOCK socket and starts the server threads (Linux only). * diff --git a/source/msocket_common.c b/source/msocket_common.c index d20818b..b4a1e6b 100644 --- a/source/msocket_common.c +++ b/source/msocket_common.c @@ -338,6 +338,14 @@ msocket_error_t msocket_unix_listen(msocket_t *self, const char *socket_path) return msocket_os_unix_listen(self, socket_path); } +msocket_error_t msocket_unix_listen_fd(msocket_t *self, int fd) +{ + if (self == NULL || fd < 0) { + return MSOCKET_INVALID_ARGUMENT_ERROR; + } + return msocket_os_unix_listen_fd(self, fd); +} + msocket_error_t msocket_vsock_listen(msocket_t *self, uint32_t cid, uint32_t port) { if (self == NULL || self->address_family != MSOCKET_ADDR_VSOCK || port == 0u) { diff --git a/source/msocket_internal.h b/source/msocket_internal.h index 79d0e3a..97f3b78 100644 --- a/source/msocket_internal.h +++ b/source/msocket_internal.h @@ -186,6 +186,7 @@ void msocket_os_mutex_lock(msocket_os_t *os); void msocket_os_mutex_unlock(msocket_os_t *os); msocket_error_t msocket_os_unix_listen(msocket_t *self, const char *socket_path); +msocket_error_t msocket_os_unix_listen_fd(msocket_t *self, int fd); msocket_error_t msocket_os_unix_connect(msocket_t *self, const char *socket_path); msocket_error_t msocket_os_vsock_listen(msocket_t *self, uint32_t cid, uint32_t port); diff --git a/source/msocket_posix.c b/source/msocket_posix.c index a41ea28..61ce7ed 100644 --- a/source/msocket_posix.c +++ b/source/msocket_posix.c @@ -184,6 +184,17 @@ msocket_error_t msocket_os_unix_listen(msocket_t *self, const char *socket_path) return MSOCKET_NO_ERROR; } +msocket_error_t msocket_os_unix_listen_fd(msocket_t *self, int fd) +{ + if (fd < 0) { + return MSOCKET_INVALID_ARGUMENT_ERROR; + } + self->os->tcp_sockfd = fd; + self->state = MSOCKET_STATE_LISTENING; + self->socket_mode |= MSOCKET_MODE_STREAM; + return MSOCKET_NO_ERROR; +} + msocket_error_t msocket_os_unix_connect(msocket_t *self, const char *socket_path) { struct sockaddr_un saddr; diff --git a/source/msocket_server.c b/source/msocket_server.c index a60118d..2731431 100644 --- a/source/msocket_server.c +++ b/source/msocket_server.c @@ -41,6 +41,7 @@ void msocket_server_create(msocket_server_t *self, uint8_t address_family, void self->udp_port = 0u; self->udp_addr = NULL; self->socket_path = NULL; + self->unix_fd = -1; self->vsock_cid = 0u; self->vsock_port = 0u; self->accept_socket = NULL; @@ -104,6 +105,7 @@ void msocket_server_destroy(msocket_server_t *self) free(self->socket_path); self->socket_path = NULL; } + self->unix_fd = -1; #if defined(MSOCKET_ENABLE_TLS) if (self->tls_server != NULL) { @@ -146,6 +148,7 @@ void msocket_server_start(msocket_server_t *self, const char *udp_addr, uint16_t if (self != NULL) { self->tcp_port = tcp_port; self->udp_port = udp_port; + self->unix_fd = -1; if (udp_addr != NULL) { size_t len = strlen(udp_addr) + 1u; self->udp_addr = (char *)malloc(len); @@ -162,6 +165,7 @@ void msocket_server_unix_start(msocket_server_t *self, const char *socket_path) if (self != NULL && socket_path != NULL) { self->tcp_port = 0u; self->udp_port = 0u; + self->unix_fd = -1; size_t len = (*socket_path == '\0') ? (strlen(socket_path + 1) + 2u) : (strlen(socket_path) + 1u); self->socket_path = (char *)malloc(len); if (self->socket_path != NULL) { @@ -171,11 +175,22 @@ void msocket_server_unix_start(msocket_server_t *self, const char *socket_path) } } +void msocket_server_unix_start_fd(msocket_server_t *self, int fd) +{ + if (self != NULL && fd >= 0) { + self->tcp_port = 0u; + self->udp_port = 0u; + self->unix_fd = fd; + msocket_server_start_threads(self); + } +} + void msocket_server_vsock_start(msocket_server_t *self, uint32_t cid, uint32_t port) { if (self != NULL && port != 0u) { self->tcp_port = 0u; self->udp_port = 0u; + self->unix_fd = -1; self->vsock_cid = cid; self->vsock_port = port; msocket_server_start_threads(self); @@ -271,6 +286,16 @@ static msocket_error_t msocket_server_bind(msocket_server_t *self) } } + if (self->unix_fd >= 0) { + rc = msocket_unix_listen_fd(self->accept_socket, self->unix_fd); + if (rc != MSOCKET_NO_ERROR) { + fprintf(stderr, "[MSOCKET_SERVER] Failed to adopt UNIX fd %d\n", self->unix_fd); + msocket_delete(self->accept_socket); + self->accept_socket = NULL; + return rc; + } + } + if (self->vsock_port != 0u) { rc = msocket_vsock_listen(self->accept_socket, self->vsock_cid, self->vsock_port); if (rc != MSOCKET_NO_ERROR) { diff --git a/source/msocket_windows.c b/source/msocket_windows.c index cd257ff..4720a80 100644 --- a/source/msocket_windows.c +++ b/source/msocket_windows.c @@ -164,6 +164,13 @@ msocket_error_t msocket_os_unix_listen(msocket_t *self, const char *socket_path) return MSOCKET_NOT_IMPLEMENTED_ERROR; } +msocket_error_t msocket_os_unix_listen_fd(msocket_t *self, int fd) +{ + (void)self; + (void)fd; + return MSOCKET_NOT_IMPLEMENTED_ERROR; +} + msocket_error_t msocket_os_unix_connect(msocket_t *self, const char *socket_path) { (void)self; diff --git a/tests/testsuite_msocket_server.c b/tests/testsuite_msocket_server.c index 294a235..c11bbe6 100644 --- a/tests/testsuite_msocket_server.c +++ b/tests/testsuite_msocket_server.c @@ -500,6 +500,78 @@ static void test_msocket_server_disable_cleanup(CuTest *tc) msocket_sem_delete(m_sem_disconnected); } +#ifndef _WIN32 +#include +#include +#include + +static void on_server_accept_unix_fd(void *arg, msocket_server_t *srv, void *socket) +{ + (void)arg; + (void)srv; + m_accepted_socket = (msocket_t *)socket; + msocket_sem_post(m_sem_accepted); +} + +static void test_msocket_server_unix_start_fd(CuTest *tc) +{ + m_sem_accepted = msocket_sem_new(0u); + m_accepted_socket = NULL; + + const char *test_sock_path = "/tmp/test_msocket_server_fd.sock"; + unlink(test_sock_path); + + int raw_fd = socket(PF_LOCAL, SOCK_STREAM, 0); + CuAssertTrue(tc, raw_fd >= 0); + + struct sockaddr_un saddr; + memset(&saddr, 0, sizeof(saddr)); + saddr.sun_family = AF_UNIX; + strncpy(saddr.sun_path, test_sock_path, sizeof(saddr.sun_path) - 1); + + int rc_bind = bind(raw_fd, (struct sockaddr *)&saddr, sizeof(saddr)); + CuAssertIntEquals(tc, 0, rc_bind); + + int rc_listen = listen(raw_fd, 5); + CuAssertIntEquals(tc, 0, rc_listen); + + msocket_server_t *srv = msocket_server_new(MSOCKET_ADDR_UNIX, NULL); + CuAssertPtrNotNull(tc, srv); + + msocket_handler_t handler; + memset(&handler, 0, sizeof(handler)); + handler.stream_accept = on_server_accept_unix_fd; + msocket_server_set_handler(srv, &handler, NULL); + + msocket_server_unix_start_fd(srv, raw_fd); + + msocket_t *cli = msocket_new(MSOCKET_ADDR_UNIX); + CuAssertPtrNotNull(tc, cli); + + msocket_handler_t cli_handler; + memset(&cli_handler, 0, sizeof(cli_handler)); + msocket_set_handler(cli, &cli_handler, NULL); + + msocket_error_t rc = msocket_unix_connect(cli, test_sock_path); + CuAssertIntEquals(tc, MSOCKET_NO_ERROR, rc); + msocket_start_io(cli); + + int attempts = 0; + while (msocket_sem_test(m_sem_accepted) <= 0 && attempts++ < 50) { + msocket_sleep_ms(20u); + } + CuAssertTrue(tc, attempts < 50); + CuAssertPtrNotNull(tc, m_accepted_socket); + + msocket_close(cli); + msocket_delete(cli); + + msocket_server_delete(srv); + msocket_sem_delete(m_sem_accepted); + unlink(test_sock_path); +} +#endif + CuSuite *testsuite_msocket_server(void) { CuSuite *suite = CuSuiteNew(); @@ -509,5 +581,8 @@ CuSuite *testsuite_msocket_server(void) SUITE_ADD_TEST(suite, test_msocket_server_custom_wrapper); SUITE_ADD_TEST(suite, test_msocket_server_manual_cleanup_default); SUITE_ADD_TEST(suite, test_msocket_server_disable_cleanup); +#ifndef _WIN32 + SUITE_ADD_TEST(suite, test_msocket_server_unix_start_fd); +#endif return suite; } From 18405f7bdf4472d8c68b14f591ddbf69f7985ad9 Mon Sep 17 00:00:00 2001 From: cogu Date: Wed, 30 Sep 2026 22:01:23 +0200 Subject: [PATCH 2/2] Add peer credential inspection for UNIX domain sockets --- include/msocket.h | 17 +++++++++ source/msocket_common.c | 11 ++++++ source/msocket_internal.h | 1 + source/msocket_posix.c | 26 ++++++++++++++ source/msocket_windows.c | 7 ++++ tests/testsuite_msocket_server.c | 62 ++++++++++++++++++++++++++++++++ 6 files changed, 124 insertions(+) diff --git a/include/msocket.h b/include/msocket.h index c4c7dec..77d9f51 100644 --- a/include/msocket.h +++ b/include/msocket.h @@ -144,6 +144,12 @@ typedef struct msocket_addr_info_tag { typedef msocket_addr_info_t msocketAddrInfo_t; +typedef struct msocket_credentials_tag { + int pid; + int uid; + int gid; +} msocket_credentials_t; + typedef struct msocket_tag { msocket_addr_info_t stream_info; msocket_addr_info_t udp_info; @@ -320,6 +326,17 @@ msocket_error_t msocket_connect(msocket_t *self, const char *addr, uint16_t port */ msocket_error_t msocket_unix_connect(msocket_t *self, const char *socket_path); +/** + * Retrieves the OS peer credentials (PID, UID, GID) for a connected UNIX socket. + * + * @param self Pointer to connected msocket_t instance. + * @param credentials Output pointer to receive the peer credentials. + * @return MSOCKET_NO_ERROR on success, MSOCKET_INVALID_ARGUMENT_ERROR on invalid parameters, + * MSOCKET_NOT_IMPLEMENTED_ERROR on unsupported platforms/socket types, + * or MSOCKET_SOCKET_ERROR on failure. + */ +msocket_error_t msocket_get_peer_credentials(const msocket_t *self, msocket_credentials_t *credentials); + /** * Initiates an outgoing VSOCK connection (Linux only). * diff --git a/source/msocket_common.c b/source/msocket_common.c index b4a1e6b..f3b6e87 100644 --- a/source/msocket_common.c +++ b/source/msocket_common.c @@ -628,6 +628,17 @@ msocket_error_t msocket_unix_connect(msocket_t *self, const char *socket_path) return msocket_os_unix_connect(self, socket_path); } +msocket_error_t msocket_get_peer_credentials(const msocket_t *self, msocket_credentials_t *credentials) +{ + if (self == NULL || credentials == NULL) { + return MSOCKET_INVALID_ARGUMENT_ERROR; + } + if (self->address_family != MSOCKET_ADDR_UNIX) { + return MSOCKET_NOT_IMPLEMENTED_ERROR; + } + return msocket_os_get_peer_credentials(self, credentials); +} + msocket_error_t msocket_vsock_connect(msocket_t *self, uint32_t cid, uint32_t port) { if (self == NULL || port == 0u || (self->socket_mode & MSOCKET_MODE_STREAM) != 0) { diff --git a/source/msocket_internal.h b/source/msocket_internal.h index 97f3b78..6da327f 100644 --- a/source/msocket_internal.h +++ b/source/msocket_internal.h @@ -188,6 +188,7 @@ void msocket_os_mutex_unlock(msocket_os_t *os); msocket_error_t msocket_os_unix_listen(msocket_t *self, const char *socket_path); msocket_error_t msocket_os_unix_listen_fd(msocket_t *self, int fd); msocket_error_t msocket_os_unix_connect(msocket_t *self, const char *socket_path); +msocket_error_t msocket_os_get_peer_credentials(const msocket_t *self, msocket_credentials_t *credentials); msocket_error_t msocket_os_vsock_listen(msocket_t *self, uint32_t cid, uint32_t port); msocket_error_t msocket_os_vsock_connect(msocket_t *self, uint32_t cid, uint32_t port); diff --git a/source/msocket_posix.c b/source/msocket_posix.c index 61ce7ed..bb81b88 100644 --- a/source/msocket_posix.c +++ b/source/msocket_posix.c @@ -9,6 +9,10 @@ * See LICENSE in project root for full license terms. ******************************************************************************/ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + ////////////////////////////////////////////////////////////////////////////// // INCLUDES ////////////////////////////////////////////////////////////////////////////// @@ -235,6 +239,28 @@ msocket_error_t msocket_os_unix_connect(msocket_t *self, const char *socket_path return MSOCKET_NO_ERROR; } +msocket_error_t msocket_os_get_peer_credentials(const msocket_t *self, msocket_credentials_t *credentials) +{ +#if defined(SO_PEERCRED) + struct ucred ucred; + socklen_t len = (socklen_t)sizeof(struct ucred); + if (self->os == NULL || OS_SOCKET_IS_INVALID(self->os->tcp_sockfd)) { + return MSOCKET_SOCKET_ERROR; + } + if (getsockopt(self->os->tcp_sockfd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) { + return MSOCKET_SOCKET_ERROR; + } + credentials->pid = (int)ucred.pid; + credentials->uid = (int)ucred.uid; + credentials->gid = (int)ucred.gid; + return MSOCKET_NO_ERROR; +#else + (void)self; + (void)credentials; + return MSOCKET_NOT_IMPLEMENTED_ERROR; +#endif +} + ////////////////////////////////////////////////////////////////////////////// // VSOCK SOCKETS ////////////////////////////////////////////////////////////////////////////// diff --git a/source/msocket_windows.c b/source/msocket_windows.c index 4720a80..9b031e3 100644 --- a/source/msocket_windows.c +++ b/source/msocket_windows.c @@ -178,6 +178,13 @@ msocket_error_t msocket_os_unix_connect(msocket_t *self, const char *socket_path return MSOCKET_NOT_IMPLEMENTED_ERROR; } +msocket_error_t msocket_os_get_peer_credentials(const msocket_t *self, msocket_credentials_t *credentials) +{ + (void)self; + (void)credentials; + return MSOCKET_NOT_IMPLEMENTED_ERROR; +} + ////////////////////////////////////////////////////////////////////////////// // VSOCK (WINDOWS STUBS) ////////////////////////////////////////////////////////////////////////////// diff --git a/tests/testsuite_msocket_server.c b/tests/testsuite_msocket_server.c index c11bbe6..d229d11 100644 --- a/tests/testsuite_msocket_server.c +++ b/tests/testsuite_msocket_server.c @@ -537,6 +537,7 @@ static void test_msocket_server_unix_start_fd(CuTest *tc) msocket_server_t *srv = msocket_server_new(MSOCKET_ADDR_UNIX, NULL); CuAssertPtrNotNull(tc, srv); + msocket_server_disable_cleanup(srv); msocket_handler_t handler; memset(&handler, 0, sizeof(handler)); @@ -566,6 +567,66 @@ static void test_msocket_server_unix_start_fd(CuTest *tc) msocket_close(cli); msocket_delete(cli); + msocket_close(m_accepted_socket); + msocket_delete(m_accepted_socket); + + msocket_server_delete(srv); + msocket_sem_delete(m_sem_accepted); + unlink(test_sock_path); +} + +static void test_msocket_unix_peer_credentials(CuTest *tc) +{ + const char *test_sock_path = "/tmp/test_msocket_peer_cred.sock"; + unlink(test_sock_path); + + m_sem_accepted = msocket_sem_new(0u); + m_accepted_socket = NULL; + + msocket_server_t *srv = msocket_server_new(MSOCKET_ADDR_UNIX, NULL); + CuAssertPtrNotNull(tc, srv); + msocket_server_disable_cleanup(srv); + + msocket_handler_t srv_handler; + memset(&srv_handler, 0, sizeof(srv_handler)); + srv_handler.stream_accept = on_server_accept_unix_fd; + msocket_server_set_handler(srv, &srv_handler, NULL); + + msocket_server_unix_start(srv, test_sock_path); + + msocket_sleep_ms(50u); + + msocket_t *cli = msocket_new(MSOCKET_ADDR_UNIX); + CuAssertPtrNotNull(tc, cli); + + msocket_handler_t cli_handler; + memset(&cli_handler, 0, sizeof(cli_handler)); + msocket_set_handler(cli, &cli_handler, NULL); + + msocket_error_t rc = msocket_unix_connect(cli, test_sock_path); + CuAssertIntEquals(tc, MSOCKET_NO_ERROR, rc); + msocket_start_io(cli); + + int attempts = 0; + while (msocket_sem_test(m_sem_accepted) <= 0 && attempts++ < 50) { + msocket_sleep_ms(20u); + } + CuAssertTrue(tc, attempts < 50); + CuAssertPtrNotNull(tc, m_accepted_socket); + + msocket_credentials_t creds; + msocket_error_t cred_rc = msocket_get_peer_credentials(m_accepted_socket, &creds); + CuAssertIntEquals(tc, MSOCKET_NO_ERROR, cred_rc); + CuAssertIntEquals(tc, (int)getpid(), creds.pid); + CuAssertIntEquals(tc, (int)getuid(), creds.uid); + CuAssertIntEquals(tc, (int)getgid(), creds.gid); + + msocket_close(cli); + msocket_delete(cli); + + msocket_close(m_accepted_socket); + msocket_delete(m_accepted_socket); + msocket_server_delete(srv); msocket_sem_delete(m_sem_accepted); unlink(test_sock_path); @@ -583,6 +644,7 @@ CuSuite *testsuite_msocket_server(void) SUITE_ADD_TEST(suite, test_msocket_server_disable_cleanup); #ifndef _WIN32 SUITE_ADD_TEST(suite, test_msocket_server_unix_start_fd); + SUITE_ADD_TEST(suite, test_msocket_unix_peer_credentials); #endif return suite; }