From 64c7c6b2a5ea80e9996c8d9502a54eb4c3032a72 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Feb 2021 02:28:57 -0800 Subject: [PATCH] ams: implement socket api for htclow socket driver --- .../source/socket/impl/socket_api.hpp | 17 + .../socket/impl/socket_api.os.horizon.cpp | 322 +++++++++++++++++- .../source/socket/socket_api.cpp | 48 +++ .../include/vapours/results/hipc_results.hpp | 3 +- 4 files changed, 387 insertions(+), 3 deletions(-) diff --git a/libraries/libstratosphere/source/socket/impl/socket_api.hpp b/libraries/libstratosphere/source/socket/impl/socket_api.hpp index 74124bfd6..d45c2c93d 100644 --- a/libraries/libstratosphere/source/socket/impl/socket_api.hpp +++ b/libraries/libstratosphere/source/socket/impl/socket_api.hpp @@ -33,6 +33,23 @@ namespace ams::socket::impl { Result InitializeAllocatorForInternal(void *buffer, size_t size); + ssize_t RecvFrom(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags, SockAddr *out_address, SockLenT *out_addr_len); + ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags); + + ssize_t SendTo(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags, const SockAddr *address, SockLenT len); + ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags); + s32 Shutdown(s32 desc, ShutdownMethod how); + s32 SocketExempt(Family domain, Type type, Protocol protocol); + + s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len); + s32 Bind(s32 desc, const SockAddr *address, SockLenT len); + + s32 GetSockName(s32 desc, SockAddr *out_address, SockLenT *out_addr_len); + s32 SetSockOpt(s32 desc, Level level, Option option_name, const void *option_value, SockLenT option_size); + + s32 Listen(s32 desc, s32 backlog); + + s32 Close(s32 desc); } diff --git a/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp b/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp index ffca3e38c..9efa5e3ef 100644 --- a/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp +++ b/libraries/libstratosphere/source/socket/impl/socket_api.os.horizon.cpp @@ -50,7 +50,7 @@ namespace ams::socket::impl { if (!g_heap_handle) { socket::impl::SetLastError(Errno::EOpNotSupp); } else if ((ptr = lmem::AllocateFromExpHeap(g_heap_handle, size)) == nullptr) { - socket::impl::SetLastError(Errno::EOpNotSupp); + socket::impl::SetLastError(Errno::ENoMem); } return ptr; @@ -66,7 +66,7 @@ namespace ams::socket::impl { if (!g_heap_handle) { socket::impl::SetLastError(Errno::EOpNotSupp); } else if ((ptr = lmem::AllocateFromExpHeap(g_heap_handle, size * num)) == nullptr) { - socket::impl::SetLastError(Errno::EOpNotSupp); + socket::impl::SetLastError(Errno::ENoMem); } else { std::memset(ptr, 0, size * num); } @@ -210,6 +210,43 @@ namespace ams::socket::impl { return ResultSuccess(); } + ALWAYS_INLINE struct sockaddr *ConvertForLibnx(SockAddr *addr) { + static_assert(sizeof(SockAddr) == sizeof(struct sockaddr)); + static_assert(alignof(SockAddr) == alignof(struct sockaddr)); + return reinterpret_cast(addr); + } + + ALWAYS_INLINE const struct sockaddr *ConvertForLibnx(const SockAddr *addr) { + static_assert(sizeof(SockAddr) == sizeof(struct sockaddr)); + static_assert(alignof(SockAddr) == alignof(struct sockaddr)); + return reinterpret_cast(addr); + } + + static_assert(std::same_as); + + Errno TranslateResultToBsdErrorImpl(const Result &result) { + if (R_SUCCEEDED(result)) { + return Errno::ESuccess; + } else if (svc::ResultInvalidCurrentMemory::Includes(result) || svc::ResultOutOfAddressSpace::Includes(result)) { + return Errno::EFault; + } else if (sf::hipc::ResultCommunicationError::Includes(result)) { + return Errno::EL3Hlt; + } else if (sf::hipc::ResultOutOfResource::Includes(result)) { + return Errno::EAgain; + } else { + R_ABORT_UNLESS(result); + return static_cast(-1); + } + } + + ALWAYS_INLINE void TranslateResultToBsdError(Errno &bsd_error, int &result) { + Errno translate_error = Errno::ESuccess; + if ((translate_error = TranslateResultToBsdErrorImpl(static_cast<::ams::Result>(::g_bsdResult))) != Errno::ESuccess) { + bsd_error = translate_error; + result = -1; + } + } + } Result Initialize(const Config &config) { @@ -249,4 +286,285 @@ namespace ams::socket::impl { return ResultSuccess(); } + ssize_t RecvFrom(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags, SockAddr *out_address, SockLenT *out_addr_len) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Check input. */ + if (buffer_size == 0) { + return 0; + } else if (buffer == nullptr) { + socket::impl::SetLastError(Errno::EInval); + return -1; + } else if (buffer_size > std::numeric_limits::max()) { + socket::impl::SetLastError(Errno::EFault); + return -1; + } + + /* If this is just a normal receive call, perform a normal receive. */ + if (out_address == nullptr || out_addr_len == nullptr || *out_addr_len == 0) { + return impl::Recv(desc, buffer, buffer_size, flags); + } + + /* Perform the call. */ + socklen_t length; + Errno error = Errno::ESuccess; + int result = ::bsdRecvFrom(desc, buffer, buffer_size, static_cast(flags), ConvertForLibnx(out_address), std::addressof(length)); + TranslateResultToBsdError(error, result); + + if (result >= 0) { + *out_addr_len = length; + } else { + socket::impl::SetLastError(error); + } + + return result; + } + + ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Check input. */ + if (buffer_size == 0) { + return 0; + } else if (buffer == nullptr) { + socket::impl::SetLastError(Errno::EInval); + return -1; + } else if (buffer_size > std::numeric_limits::max()) { + socket::impl::SetLastError(Errno::EFault); + return -1; + } + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdRecv(desc, buffer, buffer_size, static_cast(flags)); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + ssize_t SendTo(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags, const SockAddr *address, SockLenT len) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* If this is a normal send, perform a normal send. */ + if (address == nullptr || len == 0) { + return impl::Send(desc, buffer, buffer_size, flags); + } + + /* Check input. */ + if (buffer_size == 0) { + return 0; + } else if (buffer == nullptr) { + socket::impl::SetLastError(Errno::EInval); + return -1; + } else if (buffer_size > std::numeric_limits::max()) { + socket::impl::SetLastError(Errno::EFault); + return -1; + } + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdSendTo(desc, buffer, buffer_size, static_cast(flags), ConvertForLibnx(address), len); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Check input. */ + if (buffer_size == 0) { + return 0; + } else if (buffer == nullptr) { + socket::impl::SetLastError(Errno::EInval); + return -1; + } else if (buffer_size > std::numeric_limits::max()) { + socket::impl::SetLastError(Errno::EFault); + return -1; + } + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdSend(desc, buffer, buffer_size, static_cast(flags)); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + s32 Shutdown(s32 desc, ShutdownMethod how) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdShutdown(desc, static_cast(how)); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + s32 SocketExempt(Family domain, Type type, Protocol protocol) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdSocketExempt(static_cast(domain), static_cast(type), static_cast(protocol)); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Check input. */ + if (out_address == nullptr && out_addr_len != nullptr && *out_addr_len != 0) { + socket::impl::SetLastError(Errno::EFault); + return -1; + } + + socklen_t addrlen = static_cast((out_address && out_addr_len) ? *out_addr_len : 0); + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdAccept(desc, ConvertForLibnx(out_address), std::addressof(addrlen)); + TranslateResultToBsdError(error, result); + + if (result >= 0) { + if (out_addr_len != nullptr) { + *out_addr_len = addrlen; + } + } else { + socket::impl::SetLastError(error); + } + + return result; + } + s32 Bind(s32 desc, const SockAddr *address, SockLenT len) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Check input. */ + if (address == nullptr || len == 0) { + socket::impl::SetLastError(Errno::EInval); + return -1; + } + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdBind(desc, ConvertForLibnx(address), len); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + s32 GetSockName(s32 desc, SockAddr *out_address, SockLenT *out_addr_len) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Check input. */ + if (out_address == nullptr || out_addr_len == nullptr || *out_addr_len == 0) { + socket::impl::SetLastError(Errno::EInval); + return -1; + } + + /* Perform the call. */ + socklen_t length; + Errno error = Errno::ESuccess; + int result = ::bsdGetSockName(desc, ConvertForLibnx(out_address), std::addressof(length)); + TranslateResultToBsdError(error, result); + + if (result >= 0) { + *out_addr_len = length; + } else { + socket::impl::SetLastError(error); + } + + return result; + } + + s32 SetSockOpt(s32 desc, Level level, Option option_name, const void *option_value, SockLenT option_size) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Check input. */ + if (option_value == nullptr) { + socket::impl::SetLastError(Errno::EInval); + return -1; + } + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdSetSockOpt(desc, static_cast(level), static_cast(option_name), option_value, option_size); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + s32 Listen(s32 desc, s32 backlog) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdListen(desc, backlog); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + + s32 Close(s32 desc) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(IsInitialized()); + + /* Perform the call. */ + Errno error = Errno::ESuccess; + int result = ::bsdClose(desc); + TranslateResultToBsdError(error, result); + + if (result < 0) { + socket::impl::SetLastError(error); + } + + return result; + } + } diff --git a/libraries/libstratosphere/source/socket/socket_api.cpp b/libraries/libstratosphere/source/socket/socket_api.cpp index 41a25c543..0723f766b 100644 --- a/libraries/libstratosphere/source/socket/socket_api.cpp +++ b/libraries/libstratosphere/source/socket/socket_api.cpp @@ -54,4 +54,52 @@ namespace ams::socket { return impl::InitializeAllocatorForInternal(buffer, size); } + ssize_t RecvFrom(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags, SockAddr *out_address, SockLenT *out_addr_len){ + return impl::RecvFrom(desc, buffer, buffer_size, flags, out_address, out_addr_len); + } + + ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags) { + return impl::Recv(desc, buffer, buffer_size, flags); + } + + ssize_t SendTo(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags, const SockAddr *address, SockLenT len) { + return impl::SendTo(desc, buffer, buffer_size, flags, address, len); + } + + ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags) { + return impl::Send(desc, buffer, buffer_size, flags); + } + + s32 Shutdown(s32 desc, ShutdownMethod how) { + return impl::Shutdown(desc, how); + } + + s32 SocketExempt(Family domain, Type type, Protocol protocol) { + return impl::SocketExempt(domain, type, protocol); + } + + s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len) { + return impl::Accept(desc, out_address, out_addr_len); + } + + s32 Bind(s32 desc, const SockAddr *address, SockLenT len) { + return impl::Bind(desc, address, len); + } + + s32 GetSockName(s32 desc, SockAddr *out_address, SockLenT *out_addr_len) { + return impl::GetSockName(desc, out_address, out_addr_len); + } + + s32 SetSockOpt(s32 desc, Level level, Option option_name, const void *option_value, SockLenT option_size) { + return impl::SetSockOpt(desc, level, option_name, option_value, option_size); + } + + s32 Listen(s32 desc, s32 backlog) { + return impl::Listen(desc, backlog); + } + + s32 Close(s32 desc) { + return impl::Close(desc); + } + } diff --git a/libraries/libvapours/include/vapours/results/hipc_results.hpp b/libraries/libvapours/include/vapours/results/hipc_results.hpp index dec237e1a..84fcc4744 100644 --- a/libraries/libvapours/include/vapours/results/hipc_results.hpp +++ b/libraries/libvapours/include/vapours/results/hipc_results.hpp @@ -28,7 +28,8 @@ namespace ams::sf::hipc { R_DEFINE_ERROR_RESULT(OutOfDomains, 200); - R_DEFINE_ERROR_RESULT(SessionClosed, 301); + R_DEFINE_ABSTRACT_ERROR_RANGE(CommunicationError, 300, 349); + R_DEFINE_ERROR_RESULT(SessionClosed, 301); R_DEFINE_ERROR_RESULT(InvalidRequestSize, 402); R_DEFINE_ERROR_RESULT(UnknownCommandType, 403);