From 90f6bf0516a815d871fb88e7fedbec4d7d81fc18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Sat, 6 Sep 2025 18:49:21 +0200 Subject: [PATCH] AF_UNIX preliminary support (#3506) * AF_UNIX preliminary support * fixed windows * added windows implemenation for socketpair * More gotos * added sys_socketpair for libkernel_ps2emu --------- Co-authored-by: georgemoralis --- CMakeLists.txt | 1 + src/core/libraries/kernel/kernel.cpp | 10 + src/core/libraries/network/net.h | 7 + src/core/libraries/network/sockets.h | 30 ++ src/core/libraries/network/sys_net.cpp | 106 ++++++ src/core/libraries/network/sys_net.h | 3 + src/core/libraries/network/unix_sockets.cpp | 393 ++++++++++++++++++++ 7 files changed, 550 insertions(+) create mode 100644 src/core/libraries/network/unix_sockets.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bf6acd4c..197ce3bf4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -390,6 +390,7 @@ set(NETWORK_LIBS src/core/libraries/network/http.cpp src/core/libraries/network/sys_net.h src/core/libraries/network/posix_sockets.cpp src/core/libraries/network/p2p_sockets.cpp + src/core/libraries/network/unix_sockets.cpp src/core/libraries/network/sockets.h ) diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index 1898e1849..3a5f47334 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -277,16 +277,26 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("XVL8So3QJUk", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_connect); LIB_FUNCTION("pG70GT5yRo4", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_socketex); LIB_FUNCTION("KuOmgKoqCdY", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_bind); + LIB_FUNCTION("6O8EwYOgH9Y", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_getsockopt); + LIB_FUNCTION("fFxGkxF2bVo", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_setsockopt); LIB_FUNCTION("pxnCmagrtao", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_listen); LIB_FUNCTION("3e+4Iv7IJ8U", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_accept); + LIB_FUNCTION("TUuiYS2kE8s", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_shutdown); LIB_FUNCTION("TU-d9PfIHPM", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_socket); + LIB_FUNCTION("MZb0GKT3mo8", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_socketpair); + LIB_FUNCTION("MZb0GKT3mo8", "libkernel_ps2emu", 1, "libkernel", 1, 1, + Libraries::Net::sys_socketpair); LIB_FUNCTION("K1S8oc61xiM", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_htonl); LIB_FUNCTION("jogUIsOV3-U", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_htons); + LIB_FUNCTION("fZOeZIOEmLw", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_send); LIB_FUNCTION("oBr313PppNE", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_sendto); + LIB_FUNCTION("Ez8xjo9UF4E", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_recv); LIB_FUNCTION("lUk6wrGXyMw", "libkernel", 1, "libkernel", 1, 1, Libraries::Net::sys_recvfrom); LIB_FUNCTION("TU-d9PfIHPM", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_socket); + LIB_FUNCTION("fZOeZIOEmLw", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_send); LIB_FUNCTION("oBr313PppNE", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_sendto); + LIB_FUNCTION("Ez8xjo9UF4E", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_recv); LIB_FUNCTION("lUk6wrGXyMw", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_recvfrom); LIB_FUNCTION("hI7oVeOluPM", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_recvmsg); LIB_FUNCTION("TXFFFiNldU8", "libScePosix", 1, "libkernel", 1, 1, diff --git a/src/core/libraries/network/net.h b/src/core/libraries/network/net.h index 53113b8de..fe809579b 100644 --- a/src/core/libraries/network/net.h +++ b/src/core/libraries/network/net.h @@ -23,6 +23,7 @@ namespace Libraries::Net { static int ConvertFamilies(int family); enum OrbisNetFamily : u32 { + ORBIS_NET_AF_UNIX = 1, ORBIS_NET_AF_INET = 2, ORBIS_NET_AF_INET6 = 28, }; @@ -126,6 +127,12 @@ struct OrbisNetInAddr { OrbisNetInAddr_t inaddr_addr; }; +struct OrbisNetSockaddrUn { + u8 sun_len; + u8 sun_family; // AF_UNIX + char sun_path[104]; +}; + struct OrbisNetIovec { void* iov_base; u64 iov_len; diff --git a/src/core/libraries/network/sockets.h b/src/core/libraries/network/sockets.h index f997ddb7b..281c83ab4 100644 --- a/src/core/libraries/network/sockets.h +++ b/src/core/libraries/network/sockets.h @@ -6,6 +6,7 @@ #ifdef _WIN32 #define _WINSOCK_DEPRECATED_NO_WARNINGS #include +#include #include #include #include @@ -148,4 +149,33 @@ struct P2PSocket : public Socket { } }; +struct UnixSocket : public Socket { + net_socket sock; + int socket_type; + explicit UnixSocket(int domain, int type, int protocol) + : Socket(domain, type, protocol), sock(socket(domain, type, protocol)) { + socket_type = type; + } + explicit UnixSocket(net_socket sock) : Socket(0, 0, 0), sock(sock) {} + bool IsValid() const override; + int Close() override; + int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override; + int GetSocketOptions(int level, int optname, void* optval, u32* optlen) override; + int Bind(const OrbisNetSockaddr* addr, u32 addrlen) override; + int Listen(int backlog) override; + int SendMessage(const OrbisNetMsghdr* msg, int flags) override; + int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, + u32 tolen) override; + int ReceiveMessage(OrbisNetMsghdr* msg, int flags) override; + int ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) override; + SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) override; + int Connect(const OrbisNetSockaddr* addr, u32 namelen) override; + int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override; + int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override; + int fstat(Libraries::Kernel::OrbisKernelStat* stat) override; + std::optional Native() override { + return sock; + } +}; + } // namespace Libraries::Net \ No newline at end of file diff --git a/src/core/libraries/network/sys_net.cpp b/src/core/libraries/network/sys_net.cpp index c49e07093..eaa3a659f 100644 --- a/src/core/libraries/network/sys_net.cpp +++ b/src/core/libraries/network/sys_net.cpp @@ -160,6 +160,11 @@ int PS4_SYSV_ABI sys_socketex(const char* name, int family, int type, int protoc switch (type) { case ORBIS_NET_SOCK_STREAM: case ORBIS_NET_SOCK_DGRAM: + if (family == ORBIS_NET_AF_UNIX) { + socket = std::make_shared(family, type, protocol); + break; + } + [[fallthrough]]; case ORBIS_NET_SOCK_RAW: socket = std::make_shared(family, type, protocol); break; @@ -188,6 +193,99 @@ int PS4_SYSV_ABI sys_socket(int family, int type, int protocol) { return sys_socketex(nullptr, family, type, protocol); } +#ifdef _WIN32 +int socketpair(int family, int type, int protocol, net_socket fd[2]) { + if (family != AF_UNIX && family != AF_INET) { + *Libraries::Kernel::__Error() = ORBIS_NET_EPROTONOSUPPORT; + return -1; + } + if (type != SOCK_STREAM) { + *Libraries::Kernel::__Error() = ORBIS_NET_EPROTONOSUPPORT; + return -1; + } + + SOCKET listener = INVALID_SOCKET; + SOCKET sock1 = INVALID_SOCKET; + SOCKET sock2 = INVALID_SOCKET; + sockaddr_in addr{}; + int addrlen = sizeof(addr); + + listener = socket(AF_INET, SOCK_STREAM, 0); + if (listener == INVALID_SOCKET) + goto fail1; + + ZeroMemory(&addr, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = 0; + + if (bind(listener, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) + goto fail1; + if (listen(listener, 1) == SOCKET_ERROR) + goto fail1; + + if (getsockname(listener, (sockaddr*)&addr, &addrlen) == SOCKET_ERROR) + goto fail1; + + sock1 = socket(AF_INET, SOCK_STREAM, 0); + if (sock1 == INVALID_SOCKET) + goto fail2; + + if (connect(sock1, (sockaddr*)&addr, addrlen) == SOCKET_ERROR) + goto fail2; + + sock2 = accept(listener, nullptr, nullptr); + if (sock2 == INVALID_SOCKET) + goto fail3; + + closesocket(listener); + fd[0] = sock1; + fd[1] = sock2; + return 0; + +fail3: + closesocket(sock2); +fail2: + closesocket(sock1); +fail1: + closesocket(listener); + return -1; +} +#endif + +int PS4_SYSV_ABI sys_socketpair(int family, int type, int protocol, int sv[2]) { + if (sv == nullptr) { + *Libraries::Kernel::__Error() = ORBIS_NET_EINVAL; + return -1; + } + if (family != ORBIS_NET_AF_UNIX) { + *Libraries::Kernel::__Error() = ORBIS_NET_EPROTONOSUPPORT; + return -1; + } + + net_socket fd[2]; + if (socketpair(family, type, protocol, fd) != 0) { + LOG_ERROR(Lib_Net, "socketpair failed with {}", Common::GetLastErrorMsg()); + return -1; + } + + auto fd1 = FDTable::Instance()->CreateHandle(); + auto fd2 = FDTable::Instance()->CreateHandle(); + auto* sock = FDTable::Instance()->GetFile(fd1); + sock->is_opened = true; + sock->type = Core::FileSys::FileType::Socket; + sock->socket = std::make_shared(fd[0]); + sock->m_guest_name = "anon_sock0"; + sock = FDTable::Instance()->GetFile(fd2); + sock->is_opened = true; + sock->type = Core::FileSys::FileType::Socket; + sock->socket = std::make_shared(fd[1]); + sock->m_guest_name = "anon_sock1"; + sv[0] = fd1; + sv[1] = fd2; + return 0; +} + int PS4_SYSV_ABI sys_netabort(OrbisNetId s, int flags) { LOG_ERROR(Lib_Net, "(STUBBED) called"); return -1; @@ -208,6 +306,10 @@ int PS4_SYSV_ABI sys_socketclose(OrbisNetId s) { return -1; } +int PS4_SYSV_ABI sys_send(OrbisNetId s, const void* buf, u64 len, int flags) { + return sys_sendto(s, buf, len, flags, nullptr, 0); +} + int PS4_SYSV_ABI sys_sendto(OrbisNetId s, const void* buf, u64 len, int flags, const OrbisNetSockaddr* addr, u32 addrlen) { auto file = FDTable::Instance()->GetSocket(s); @@ -240,6 +342,10 @@ int PS4_SYSV_ABI sys_sendmsg(OrbisNetId s, const OrbisNetMsghdr* msg, int flags) return -1; } +s64 PS4_SYSV_ABI sys_recv(OrbisNetId s, void* buf, u64 len, int flags) { + return sys_recvfrom(s, buf, len, flags, nullptr, nullptr); +} + s64 PS4_SYSV_ABI sys_recvfrom(OrbisNetId s, void* buf, u64 len, int flags, OrbisNetSockaddr* addr, u32* paddrlen) { auto file = FDTable::Instance()->GetSocket(s); diff --git a/src/core/libraries/network/sys_net.h b/src/core/libraries/network/sys_net.h index e2fb28507..5749803b5 100644 --- a/src/core/libraries/network/sys_net.h +++ b/src/core/libraries/network/sys_net.h @@ -20,11 +20,14 @@ int PS4_SYSV_ABI sys_setsockopt(OrbisNetId s, int level, int optname, const void int PS4_SYSV_ABI sys_shutdown(OrbisNetId s, int how); int PS4_SYSV_ABI sys_socketex(const char* name, int family, int type, int protocol); int PS4_SYSV_ABI sys_socket(int family, int type, int protocol); +int PS4_SYSV_ABI sys_socketpair(int family, int type, int protocol, int sv[2]); int PS4_SYSV_ABI sys_netabort(OrbisNetId s, int flags); int PS4_SYSV_ABI sys_socketclose(OrbisNetId s); +int PS4_SYSV_ABI sys_send(OrbisNetId s, const void* buf, u64 len, int flags); int PS4_SYSV_ABI sys_sendto(OrbisNetId s, const void* buf, u64 len, int flags, const OrbisNetSockaddr* addr, u32 addrlen); int PS4_SYSV_ABI sys_sendmsg(OrbisNetId s, const OrbisNetMsghdr* msg, int flags); +s64 PS4_SYSV_ABI sys_recv(OrbisNetId s, void* buf, u64 len, int flags); s64 PS4_SYSV_ABI sys_recvfrom(OrbisNetId s, void* buf, u64 len, int flags, OrbisNetSockaddr* addr, u32* paddrlen); s64 PS4_SYSV_ABI sys_recvmsg(OrbisNetId s, OrbisNetMsghdr* msg, int flags); diff --git a/src/core/libraries/network/unix_sockets.cpp b/src/core/libraries/network/unix_sockets.cpp new file mode 100644 index 000000000..29bbc5f16 --- /dev/null +++ b/src/core/libraries/network/unix_sockets.cpp @@ -0,0 +1,393 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/error.h" +#include "core/libraries/kernel/file_system.h" +#include "core/libraries/kernel/kernel.h" +#include "net.h" +#ifndef _WIN32 +#include +#include +#endif +#include "net_error.h" +#include "sockets.h" + +namespace Libraries::Net { + +#ifdef _WIN32 +#define ERROR_CASE(errname) \ + case (WSA##errname): \ + *Libraries::Kernel::__Error() = ORBIS_NET_##errname; \ + return -1; +#else +#define ERROR_CASE(errname) \ + case (errname): \ + *Libraries::Kernel::__Error() = ORBIS_NET_##errname; \ + return -1; +#endif + +static int ConvertReturnErrorCode(int retval) { + if (retval < 0) { +#ifdef _WIN32 + switch (WSAGetLastError()) { +#else + switch (errno) { +#endif +#ifndef _WIN32 // These errorcodes don't exist in WinSock + ERROR_CASE(EPERM) + ERROR_CASE(ENOENT) + // ERROR_CASE(ESRCH) + // ERROR_CASE(EIO) + // ERROR_CASE(ENXIO) + // ERROR_CASE(E2BIG) + // ERROR_CASE(ENOEXEC) + // ERROR_CASE(EDEADLK) + ERROR_CASE(ENOMEM) + // ERROR_CASE(ECHILD) + // ERROR_CASE(EBUSY) + ERROR_CASE(EEXIST) + // ERROR_CASE(EXDEV) + ERROR_CASE(ENODEV) + // ERROR_CASE(ENOTDIR) + // ERROR_CASE(EISDIR) + ERROR_CASE(ENFILE) + // ERROR_CASE(ENOTTY) + // ERROR_CASE(ETXTBSY) + // ERROR_CASE(EFBIG) + ERROR_CASE(ENOSPC) + // ERROR_CASE(ESPIPE) + // ERROR_CASE(EROFS) + // ERROR_CASE(EMLINK) + ERROR_CASE(EPIPE) + // ERROR_CASE(EDOM) + // ERROR_CASE(ERANGE) + // ERROR_CASE(ENOLCK) + // ERROR_CASE(ENOSYS) + // ERROR_CASE(EIDRM) + // ERROR_CASE(EOVERFLOW) + // ERROR_CASE(EILSEQ) + // ERROR_CASE(ENOTSUP) + ERROR_CASE(ECANCELED) + // ERROR_CASE(EBADMSG) + ERROR_CASE(ENODATA) + // ERROR_CASE(ENOSR) + // ERROR_CASE(ENOSTR) + // ERROR_CASE(ETIME) +#endif + ERROR_CASE(EINTR) + ERROR_CASE(EBADF) + ERROR_CASE(EACCES) + ERROR_CASE(EFAULT) + ERROR_CASE(EINVAL) + ERROR_CASE(EMFILE) + ERROR_CASE(EWOULDBLOCK) + ERROR_CASE(EINPROGRESS) + ERROR_CASE(EALREADY) + ERROR_CASE(ENOTSOCK) + ERROR_CASE(EDESTADDRREQ) + ERROR_CASE(EMSGSIZE) + ERROR_CASE(EPROTOTYPE) + ERROR_CASE(ENOPROTOOPT) + ERROR_CASE(EPROTONOSUPPORT) +#if defined(__APPLE__) || defined(_WIN32) + ERROR_CASE(EOPNOTSUPP) +#endif + ERROR_CASE(EAFNOSUPPORT) + ERROR_CASE(EADDRINUSE) + ERROR_CASE(EADDRNOTAVAIL) + ERROR_CASE(ENETDOWN) + ERROR_CASE(ENETUNREACH) + ERROR_CASE(ENETRESET) + ERROR_CASE(ECONNABORTED) + ERROR_CASE(ECONNRESET) + ERROR_CASE(ENOBUFS) + ERROR_CASE(EISCONN) + ERROR_CASE(ENOTCONN) + ERROR_CASE(ETIMEDOUT) + ERROR_CASE(ECONNREFUSED) + ERROR_CASE(ELOOP) + ERROR_CASE(ENAMETOOLONG) + ERROR_CASE(EHOSTUNREACH) + ERROR_CASE(ENOTEMPTY) + } + *Libraries::Kernel::__Error() = ORBIS_NET_EINTERNAL; + return -1; + } + // if it is 0 or positive return it as it is + return retval; +} + +static int ConvertLevels(int level) { + switch (level) { + case ORBIS_NET_SOL_SOCKET: + return SOL_SOCKET; + case ORBIS_NET_IPPROTO_IP: + return IPPROTO_IP; + case ORBIS_NET_IPPROTO_TCP: + return IPPROTO_TCP; + case ORBIS_NET_IPPROTO_IPV6: + return IPPROTO_IPV6; + } + return -1; +} + +static void convertOrbisNetSockaddrToUnix(const OrbisNetSockaddr* src, sockaddr_un* dst) { + if (src == nullptr || dst == nullptr) + return; + memset(dst, 0, sizeof(sockaddr_un)); + const OrbisNetSockaddrUn* src_in = (const OrbisNetSockaddrUn*)src; + sockaddr_un* dst_in = (sockaddr_un*)dst; + dst_in->sun_family = src_in->sun_family; + memcpy(&dst_in->sun_path, &src_in->sun_path, src_in->sun_len); +} + +static void convertUnixSockaddrToOrbis(sockaddr* src, OrbisNetSockaddr* dst) { + if (src == nullptr || dst == nullptr) + return; + memset(dst, 0, sizeof(OrbisNetSockaddrUn)); + OrbisNetSockaddrUn* dst_in = (OrbisNetSockaddrUn*)dst; + sockaddr_un* src_in = (sockaddr_un*)src; + dst_in->sun_len = strnlen(src_in->sun_path, 108); + dst_in->sun_family = src_in->sun_family; + memcpy(&dst_in->sun_path, &src_in->sun_path, dst_in->sun_len); +} + +bool UnixSocket::IsValid() const { +#ifdef _WIN32 + return sock != INVALID_SOCKET; +#else + return sock != -1; +#endif +} + +int UnixSocket::Close() { + std::scoped_lock lock{m_mutex}; +#ifdef _WIN32 + auto out = closesocket(sock); +#else + auto out = ::close(sock); +#endif + return ConvertReturnErrorCode(out); +} + +int UnixSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { + std::scoped_lock lock{m_mutex}; + sockaddr_un addr2; + convertOrbisNetSockaddrToUnix(addr, &addr2); + return ConvertReturnErrorCode(::bind(sock, (const sockaddr*)&addr2, sizeof(sockaddr_un))); +} + +int UnixSocket::Listen(int backlog) { + std::scoped_lock lock{m_mutex}; + return ConvertReturnErrorCode(::listen(sock, backlog)); +} + +int UnixSocket::SendMessage(const OrbisNetMsghdr* msg, int flags) { + std::scoped_lock lock{m_mutex}; +#ifdef _WIN32 + DWORD bytesSent = 0; + LPFN_WSASENDMSG wsasendmsg = nullptr; + GUID guid = WSAID_WSASENDMSG; + DWORD bytes = 0; + + if (WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &wsasendmsg, + sizeof(wsasendmsg), &bytes, nullptr, nullptr) != 0) { + return ConvertReturnErrorCode(-1); + } + + int res = wsasendmsg(sock, reinterpret_cast(const_cast(msg)), flags, + &bytesSent, nullptr, nullptr); + + if (res == SOCKET_ERROR) { + return ConvertReturnErrorCode(-1); + } + return static_cast(bytesSent); +#else + int res = sendmsg(sock, reinterpret_cast(msg), flags); + return ConvertReturnErrorCode(res); +#endif +} + +int UnixSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, + u32 tolen) { + std::scoped_lock lock{m_mutex}; + LOG_ERROR(Lib_Net, "called"); + if (to != nullptr) { + sockaddr_un addr; + convertOrbisNetSockaddrToUnix(to, &addr); + return ConvertReturnErrorCode( + sendto(sock, (const char*)msg, len, flags, (sockaddr*)&addr, sizeof(sockaddr_un))); + } else { + return ConvertReturnErrorCode(send(sock, (const char*)msg, len, flags)); + } +} + +int UnixSocket::ReceiveMessage(OrbisNetMsghdr* msg, int flags) { + std::scoped_lock lock{receive_mutex}; +#ifdef _WIN32 + LPFN_WSARECVMSG wsarecvmsg = nullptr; + GUID guid = WSAID_WSARECVMSG; + DWORD bytes = 0; + + if (WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &wsarecvmsg, + sizeof(wsarecvmsg), &bytes, nullptr, nullptr) != 0) { + return ConvertReturnErrorCode(-1); + } + + DWORD bytesReceived = 0; + int res = wsarecvmsg(sock, reinterpret_cast(msg), &bytesReceived, nullptr, nullptr); + + if (res == SOCKET_ERROR) { + return ConvertReturnErrorCode(-1); + } + return static_cast(bytesReceived); +#else + int res = recvmsg(sock, reinterpret_cast(msg), flags); + return ConvertReturnErrorCode(res); +#endif +} + +int UnixSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) { + std::scoped_lock lock{receive_mutex}; + LOG_ERROR(Lib_Net, "called"); + if (from != nullptr) { + sockaddr_un addr; + int res = recvfrom(sock, (char*)buf, len, flags, (sockaddr*)&addr, (socklen_t*)fromlen); + convertUnixSockaddrToOrbis((sockaddr*)&addr, from); + *fromlen = sizeof(OrbisNetSockaddrUn); + return ConvertReturnErrorCode(res); + } else { + return ConvertReturnErrorCode(recv(sock, (char*)buf, len, flags)); + } +} + +SocketPtr UnixSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) { + std::scoped_lock lock{m_mutex}; + sockaddr_un addr2; + socklen_t len = sizeof(addr2); + net_socket new_socket = ::accept(sock, (sockaddr*)&addr2, &len); +#ifdef _WIN32 + if (new_socket != INVALID_SOCKET) { +#else + if (new_socket >= 0) { +#endif + if (addr && addrlen) { + convertUnixSockaddrToOrbis((sockaddr*)&addr2, addr); + *addrlen = sizeof(OrbisNetSockaddrUn); + } + return std::make_shared(new_socket); + } + ConvertReturnErrorCode(new_socket); + return nullptr; +} + +int UnixSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) { + std::scoped_lock lock{m_mutex}; + sockaddr_un addr2; + convertOrbisNetSockaddrToUnix(addr, &addr2); + int result = 0; + do { + result = ::connect(sock, (sockaddr*)&addr2, sizeof(sockaddr_un)); + LOG_DEBUG(Lib_Net, "raw connect result = {}, errno = {}", result, + result == -1 ? Common::GetLastErrorMsg() : "none"); + } while (result == -1 && (errno == EINTR || errno == EINPROGRESS)); + return ConvertReturnErrorCode(result); +} + +int UnixSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) { + std::scoped_lock lock{m_mutex}; + + sockaddr_un addr; + convertOrbisNetSockaddrToUnix(name, &addr); + if (name != nullptr) { + *namelen = sizeof(sockaddr_un); + } + int res = getsockname(sock, (sockaddr*)&addr, (socklen_t*)namelen); + if (res >= 0) { + convertUnixSockaddrToOrbis((sockaddr*)&addr, name); + *namelen = sizeof(OrbisNetSockaddrUn); + } + return ConvertReturnErrorCode(res); +} + +#define CASE_SETSOCKOPT(opt) \ + case ORBIS_NET_##opt: \ + return ConvertReturnErrorCode( \ + setsockopt(sock, native_level, opt, (const char*)optval, optlen)) + +#define CASE_SETSOCKOPT_VALUE(opt, value) \ + case opt: \ + if (optlen != sizeof(*value)) { \ + *Libraries::Kernel::__Error() = ORBIS_NET_EFAULT; \ + return -1; \ + } \ + memcpy(value, optval, optlen); \ + return 0 + +int UnixSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) { + std::scoped_lock lock{m_mutex}; + LOG_ERROR(Lib_Net, "Unknown level = {} optname = {}", level, optname); + return -1; +} + +#define CASE_GETSOCKOPT(opt) \ + case ORBIS_NET_##opt: { \ + socklen_t optlen_temp = *optlen; \ + auto retval = ConvertReturnErrorCode( \ + getsockopt(sock, native_level, opt, (char*)optval, &optlen_temp)); \ + *optlen = optlen_temp; \ + return retval; \ + } +#define CASE_GETSOCKOPT_VALUE(opt, value) \ + case opt: \ + if (*optlen < sizeof(value)) { \ + *optlen = sizeof(value); \ + *Libraries::Kernel::__Error() = ORBIS_NET_EFAULT; \ + return -1; \ + } \ + *optlen = sizeof(value); \ + *(decltype(value)*)optval = value; \ + return 0; + +int UnixSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) { + std::scoped_lock lock{m_mutex}; + LOG_ERROR(Lib_Net, "Unknown level = {} optname = {}", level, optname); + return ConvertReturnErrorCode(-1); +} + +int UnixSocket::GetPeerName(OrbisNetSockaddr* name, u32* namelen) { + std::scoped_lock lock{m_mutex}; + LOG_DEBUG(Lib_Net, "called"); + + sockaddr_un addr; + convertOrbisNetSockaddrToUnix(name, &addr); + if (name != nullptr) { + *namelen = sizeof(sockaddr_un); + } + int res = ::getpeername(sock, (sockaddr*)&addr, (socklen_t*)namelen); + if (res >= 0) { + convertUnixSockaddrToOrbis((sockaddr*)&addr, name); + *namelen = sizeof(OrbisNetSockaddrUn); + } + return ConvertReturnErrorCode(res); +} + +int UnixSocket::fstat(Libraries::Kernel::OrbisKernelStat* sb) { +#ifdef _WIN32 + LOG_ERROR(Lib_Net, "(STUBBED) called"); + sb->st_mode = 0000777u | 0140000u; + return 0; +#else + struct stat st{}; + int result = ::fstat(sock, &st); + sb->st_mode = 0000777u | 0140000u; + sb->st_size = st.st_size; + sb->st_blocks = st.st_blocks; + sb->st_blksize = st.st_blksize; + // sb->st_flags = st.st_flags; + return ConvertReturnErrorCode(result); +#endif +} + +} // namespace Libraries::Net \ No newline at end of file