Implement required bsd calls

Co-authored-by: PabloG02 <tioo23000@gmail.com>
This commit is contained in:
Dima 2023-04-20 16:25:34 +03:00
parent 64d914639f
commit b22471fb46
2 changed files with 238 additions and 25 deletions

View File

@ -1,13 +1,14 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <poll.h>
#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<u32>(0);
response.Push<i32>(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>()};
i32 type{request.Pop<i32>()};
i32 protocol{request.Pop<i32>()};
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>()};
i32 timeout{request.Pop<i32>()};
if (fdsCount == 0)
return PushBsdResult(response, -1, 0);
span outputBuf{request.outputBuf.at(0)};
auto fds{span<pollfd>(reinterpret_cast<pollfd*>(outputBuf.data()), static_cast<u32>(fdsCount))};
i32 result{poll(fds.data(), static_cast<u32>(fdsCount), static_cast<i32>(timeout))};
return PushBsdResult(response, result, errno);
}
Result IClient::Recv(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
return {};
i32 fd{request.Pop<i32>()};
i32 flags{request.Pop<i32>()};
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>()};
i32 flags{request.Pop<i32>()};
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>()};
i32 flags{request.Pop<i32>()};
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<u32>(0);
return {};
i32 fd{request.Pop<i32>()};
i32 flags{request.Pop<i32>()};
sockaddr addrIn{request.inputBuf.at(1).as<sockaddr>()};
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<i32>()};
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<i32>()};
sockaddr addr{request.inputBuf.at(0).as<sockaddr>()};
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<i32>()};
sockaddr addr{request.inputBuf.at(0).as<sockaddr>()};
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<i32>()};
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<i32>()};
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>()};
i32 level{request.Pop<i32>()};
OptionName optionName{request.Pop<OptionName>()};
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>()};
i32 backlog{request.Pop<i32>()};
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>()};
i32 cmd{request.Pop<i32>()};
i32 arg{request.Pop<i32>()};
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>()};
i32 level{request.Pop<i32>()};
OptionName optionName{request.Pop<OptionName>()};
if (level == 0xFFFF)
level = SOL_SOCKET;
i32 result{setsockopt(fd, level, GetOption(optionName), request.inputBuf.at(0).data(), static_cast<socklen_t>(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>()};
i32 how{request.Pop<i32>()};
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>()};
i32 flags{request.Pop<i32>()};
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<u32>(0);
response.Push<u32>(0);
return {};
i32 fd{request.Pop<i32>()};
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>()};
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;
}
}
}

View File

@ -4,8 +4,18 @@
#pragma once
#include <services/serviceman.h>
#include <netinet/in.h>
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)
)
};
}