From b22471fb4612c444c228c503ea9fb1d2ae97f58d Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 20 Apr 2023 16:25:34 +0300 Subject: [PATCH] Implement required bsd calls Co-authored-by: PabloG02 --- .../skyline/services/socket/bsd/IClient.cpp | 207 ++++++++++++++++-- .../cpp/skyline/services/socket/bsd/IClient.h | 56 ++++- 2 files changed, 238 insertions(+), 25 deletions(-) diff --git a/app/src/main/cpp/skyline/services/socket/bsd/IClient.cpp b/app/src/main/cpp/skyline/services/socket/bsd/IClient.cpp index 316daaae..a2558e7b 100644 --- a/app/src/main/cpp/skyline/services/socket/bsd/IClient.cpp +++ b/app/src/main/cpp/skyline/services/socket/bsd/IClient.cpp @@ -1,13 +1,14 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) +#include #include "IClient.h" namespace skyline::service::socket { IClient::IClient(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {} Result IClient::RegisterClient(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - response.Push(0); + response.Push(0); return {}; } @@ -15,53 +16,185 @@ namespace skyline::service::socket { return {}; } - Result IClient::Select(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - return {}; + Result IClient::Socket(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + i32 domain{request.Pop()}; + i32 type{request.Pop()}; + i32 protocol{request.Pop()}; + i32 fd{::socket(domain, type, protocol)}; + Logger::Info("File Descriptor {} with Domain {}, Type {}, Protocol {}", fd, domain, type, protocol); + if (fd == -1) + Logger::Error("Error creating socket: {}", strerror(errno)); + return PushBsdResult(response, fd, 0); } Result IClient::Poll(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - return {}; + i32 fdsCount{request.Pop()}; + i32 timeout{request.Pop()}; + + if (fdsCount == 0) + return PushBsdResult(response, -1, 0); + + span outputBuf{request.outputBuf.at(0)}; + auto fds{span(reinterpret_cast(outputBuf.data()), static_cast(fdsCount))}; + i32 result{poll(fds.data(), static_cast(fdsCount), static_cast(timeout))}; + return PushBsdResult(response, result, errno); } Result IClient::Recv(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - return {}; + i32 fd{request.Pop()}; + i32 flags{request.Pop()}; + if (fcntl(fd, F_GETFL) == -1) + return PushBsdResult(response, -1, EBADF); + + bool shouldBlockAfterOperation{false}; + if (!(fcntl(fd, F_GETFL) & O_NONBLOCK) && (flags & MSG_EOR)) { + fcntl(fd, F_SETFL, O_NONBLOCK); + shouldBlockAfterOperation = true; + } + + ssize_t result{recv(fd, request.outputBuf.at(0).data(), request.outputBuf.at(0).size(), flags)}; + + if (shouldBlockAfterOperation) + fcntl(fd, F_SETFL, MSG_EOR); + return PushBsdResultErrno(response, result); } Result IClient::RecvFrom(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - return {}; + i32 fd{request.Pop()}; + i32 flags{request.Pop()}; + if (fcntl(fd, F_GETFL) == -1) + return PushBsdResult(response, -1, EBADF); + + bool shouldBlockAfterOperation{false}; + if (!(fcntl(fd, F_GETFL) & O_NONBLOCK) && (flags & MSG_EOR)) { + fcntl(fd, F_SETFL, O_NONBLOCK); + shouldBlockAfterOperation = true; + } + + sockaddr addrIn{}; + socklen_t addrLen{sizeof(addrIn)}; + span message{request.outputBuf.at(0)}; + ssize_t result{recvfrom(fd, message.data(), message.size(), 0, &addrIn, &addrLen)}; + + if (shouldBlockAfterOperation) + fcntl(fd, F_SETFL, MSG_EOR); + + request.outputBuf.at(0).copy_from(message); + if (!request.outputBuf.at(1).empty()) + request.outputBuf.at(1).copy_from(span{addrIn}); + response.Push(request.outputBuf.at(1).size()); + return PushBsdResultErrno(response, result); } Result IClient::Send(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - return {}; + i32 fd{request.Pop()}; + i32 flags{request.Pop()}; + + ssize_t result{send(fd, request.inputBuf.at(0).data(), request.inputBuf.at(0).size(), flags)}; + return PushBsdResultErrno(response, result); } Result IClient::SendTo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - response.Push(0); - return {}; + i32 fd{request.Pop()}; + i32 flags{request.Pop()}; + + sockaddr addrIn{request.inputBuf.at(1).as()}; + addrIn.sa_family = AF_INET; + ssize_t result{sendto(fd, request.inputBuf.at(0).data(), request.inputBuf.at(0).size(), flags, + &addrIn, sizeof(addrIn))}; + return PushBsdResultErrno(response, result); } Result IClient::Accept(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - return {}; + i32 fd{request.Pop()}; + sockaddr addr{}; + socklen_t addrLen{sizeof(addr)}; + i32 result{accept(fd, &addr, &addrLen)}; + if (errno != 0) + return PushBsdResult(response, -1, errno); + + request.outputBuf.at(0).copy_from(span{addr}); + response.Push(request.outputBuf.at(0).size()); + return PushBsdResult(response, result, errno); } Result IClient::Bind(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - return {}; + i32 fd{request.Pop()}; + sockaddr addr{request.inputBuf.at(0).as()}; + addr.sa_family = AF_INET; + + i32 result{bind(fd, &addr, sizeof(addr))}; + return PushBsdResult(response, 0, errno); } Result IClient::Connect(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - return {}; + i32 fd{request.Pop()}; + sockaddr addr{request.inputBuf.at(0).as()}; + addr.sa_family = AF_INET; + + i32 result{connect(fd, &addr, sizeof(addr))}; + return PushBsdResult(response, 0, errno); + } + + Result IClient::GetPeerName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + i32 fd{request.Pop()}; + sockaddr addr{}; + socklen_t addrLen{sizeof(addr)}; + i32 result{getpeername(fd, &addr, &addrLen)}; + request.outputBuf.at(0).copy_from(span{addr}); + response.Push(request.outputBuf.at(0).size()); + return PushBsdResult(response, 0, errno); + } + + Result IClient::GetSockName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + i32 fd{request.Pop()}; + sockaddr addr{}; + socklen_t addrLen{sizeof(addr)}; + i32 result{getsockname(fd, &addr, &addrLen)}; + request.outputBuf.at(0).copy_from(span{addr}); + response.Push(request.outputBuf.at(0).size()); + return PushBsdResult(response, 0, errno); + } + + Result IClient::GetSockOpt(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + i32 fd{request.Pop()}; + i32 level{request.Pop()}; + OptionName optionName{request.Pop()}; + socklen_t addrLen{sizeof(request.outputBuf.at(0))}; + i32 result{getsockopt(fd, level, GetOption(optionName), request.outputBuf.at(0).data(), &addrLen)}; + return PushBsdResult(response, 0, errno); } Result IClient::Listen(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - return {}; + i32 fd{request.Pop()}; + i32 backlog{request.Pop()}; + i32 result{listen(fd, backlog)}; + return PushBsdResult(response, 0, errno); + } + + Result IClient::Fcntl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + i32 fd{request.Pop()}; + i32 cmd{request.Pop()}; + i32 arg{request.Pop()}; + i32 result{fcntl(fd, cmd, arg)}; + return PushBsdResult(response, 0, errno); } Result IClient::SetSockOpt(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - return {}; + i32 fd{request.Pop()}; + i32 level{request.Pop()}; + OptionName optionName{request.Pop()}; + if (level == 0xFFFF) + level = SOL_SOCKET; + i32 result{setsockopt(fd, level, GetOption(optionName), request.inputBuf.at(0).data(), static_cast(request.inputBuf.at(0).size()))}; + return PushBsdResult(response, 0, errno); } Result IClient::Shutdown(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - return {}; + i32 fd{request.Pop()}; + i32 how{request.Pop()}; + i32 result{shutdown(fd, how)}; + return PushBsdResult(response, 0, errno); } Result IClient::ShutdownAllSockets(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { @@ -69,16 +202,52 @@ namespace skyline::service::socket { } Result IClient::Write(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - return {}; + i32 fd{request.Pop()}; + i32 flags{request.Pop()}; + ssize_t result{send(fd, request.inputBuf.at(0).data(), request.inputBuf.at(0).size(), flags)}; + return PushBsdResultErrno(response, result); } Result IClient::Read(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - response.Push(0); - response.Push(0); - return {}; + i32 fd{request.Pop()}; + ssize_t result{recv(fd, request.outputBuf.at(0).data(), request.outputBuf.at(0).size(), 0)}; + return PushBsdResultErrno(response, result); } Result IClient::Close(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + i32 fd{request.Pop()}; + i32 result{close(fd)}; + return PushBsdResult(response, 0, errno); + } + + Result IClient::EventFd(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + return PushBsdResult(response, 1, 0); + } + + Result IClient::PushBsdResult(ipc::IpcResponse &response, i32 result, i32 errorCode) { + if (errorCode != 0) + result = -1; + + response.Push(result); + response.Push(errorCode); return {}; } + + Result IClient::PushBsdResultErrno(ipc::IpcResponse &response, i64 result) { + response.Push(result); + response.Push(result == -1 ? errno : 0); + return {}; + } + + i32 IClient::GetOption(OptionName optionName) { + switch (optionName) { + case OptionName::ReuseAddr: return SO_REUSEADDR; + case OptionName::Broadcast: return SO_BROADCAST; + case OptionName::Linger: return SO_LINGER; + case OptionName::SndBuf: return SO_SNDBUF; + case OptionName::RcvBuf: return SO_RCVBUF; + case OptionName::SndTimeo: return SO_SNDTIMEO; + case OptionName::RcvTimeo: return SO_RCVTIMEO; + } + } } diff --git a/app/src/main/cpp/skyline/services/socket/bsd/IClient.h b/app/src/main/cpp/skyline/services/socket/bsd/IClient.h index 9ed6e0cb..45d55da1 100644 --- a/app/src/main/cpp/skyline/services/socket/bsd/IClient.h +++ b/app/src/main/cpp/skyline/services/socket/bsd/IClient.h @@ -4,8 +4,18 @@ #pragma once #include +#include namespace skyline::service::socket { + enum class OptionName : u32 { + ReuseAddr = 0x4, + Broadcast = 0x20, + Linger = 0x80, + SndBuf = 0x1001, + RcvBuf = 0x1002, + SndTimeo = 0x1005, + RcvTimeo = 0x1006, + }; /** * @brief IClient or bsd:u is used by applications create network sockets * @url https://switchbrew.org/wiki/Sockets_services#bsd:u.2C_bsd:s @@ -25,10 +35,7 @@ namespace skyline::service::socket { */ Result StartMonitoring(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - /** - * @brief Selects the socket - */ - Result Select(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + Result Socket(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); /** * @brief Polls the socket for events @@ -70,11 +77,32 @@ namespace skyline::service::socket { */ Result Connect(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + /** + * @brief Retrieves the address of the peer to which a socket is connected + */ + Result GetPeerName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Retrieves the current local address of the socket + */ + Result GetSockName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Retrieves socket options + */ + Result GetSockOpt(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + /** * @brief Places a socket in a state in which it is listening for an incoming connection */ Result Listen(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + /** + * @brief Performs a control operation on an open file descriptor + * @url https://switchbrew.org/wiki/Sockets_services#Fcntl + */ + Result Fcntl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + /** * @brief Manipulates the options associated with a socket */ @@ -105,10 +133,21 @@ namespace skyline::service::socket { */ Result Close(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + Result EventFd(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + Result PushBsdResult(ipc::IpcResponse &response, i32 result, i32 errorCode); + + Result PushBsdResultErrno(ipc::IpcResponse &response, i64 result); + + /** + * @brief Translates option name to a socket option + */ + i32 GetOption(OptionName optionName); + SERVICE_DECL( SFUNC(0x0, IClient, RegisterClient), SFUNC(0x1, IClient, StartMonitoring), - SFUNC(0x5, IClient, Select), + SFUNC(0x2, IClient, Socket), SFUNC(0x6, IClient, Poll), SFUNC(0x8, IClient, Recv), SFUNC(0x9, IClient, RecvFrom), @@ -117,13 +156,18 @@ namespace skyline::service::socket { SFUNC(0xC, IClient, Accept), SFUNC(0xD, IClient, Bind), SFUNC(0xE, IClient, Connect), + SFUNC(0xF, IClient, GetPeerName), + SFUNC(0x10, IClient, GetSockName), + SFUNC(0x11, IClient, GetSockOpt), SFUNC(0x12, IClient, Listen), + SFUNC(0x14, IClient, Fcntl), SFUNC(0x15, IClient, SetSockOpt), SFUNC(0x16, IClient, Shutdown), SFUNC(0x17, IClient, ShutdownAllSockets), SFUNC(0x18, IClient, Write), SFUNC(0x19, IClient, Read), - SFUNC(0x1A, IClient, Close) + SFUNC(0x1A, IClient, Close), + SFUNC(0x1F, IClient, EventFd) ) }; }