mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-30 15:26:54 -05:00
4a35569921
- Add missing virtual destructor on `SSLBackend`. - On Windows, filter out `POLLWRBAND` (one of the new flags added) when calling `WSAPoll`, because despite the constant being defined on Windows, passing it calls `WSAPoll` to yield `EINVAL`. - Reduce OpenSSL version requirement to satisfy CI; I haven't tested whether it actually builds (or runs) against 1.1.1, but if not, I'll figure it out. - Change an instance of memcpy to memmove, even though the arguments cannot overlap, to avoid a [strange GCC error](https://github.com/yuzu-emu/yuzu/pull/10912#issuecomment-1606283351).
295 lines
11 KiB
C++
295 lines
11 KiB
C++
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <string_view>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "common/string_util.h"
|
|
#include "common/swap.h"
|
|
#include "core/core.h"
|
|
#include "core/hle/service/ipc_helpers.h"
|
|
#include "core/hle/service/sockets/sfdnsres.h"
|
|
#include "core/hle/service/sockets/sockets.h"
|
|
#include "core/hle/service/sockets/sockets_translate.h"
|
|
#include "core/internal_network/network.h"
|
|
#include "core/memory.h"
|
|
|
|
namespace Service::Sockets {
|
|
|
|
SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"} {
|
|
static const FunctionInfo functions[] = {
|
|
{0, nullptr, "SetDnsAddressesPrivateRequest"},
|
|
{1, nullptr, "GetDnsAddressPrivateRequest"},
|
|
{2, &SFDNSRES::GetHostByNameRequest, "GetHostByNameRequest"},
|
|
{3, nullptr, "GetHostByAddrRequest"},
|
|
{4, nullptr, "GetHostStringErrorRequest"},
|
|
{5, nullptr, "GetGaiStringErrorRequest"},
|
|
{6, &SFDNSRES::GetAddrInfoRequest, "GetAddrInfoRequest"},
|
|
{7, nullptr, "GetNameInfoRequest"},
|
|
{8, nullptr, "RequestCancelHandleRequest"},
|
|
{9, nullptr, "CancelRequest"},
|
|
{10, &SFDNSRES::GetHostByNameRequestWithOptions, "GetHostByNameRequestWithOptions"},
|
|
{11, nullptr, "GetHostByAddrRequestWithOptions"},
|
|
{12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"},
|
|
{13, nullptr, "GetNameInfoRequestWithOptions"},
|
|
{14, &SFDNSRES::ResolverSetOptionRequest, "ResolverSetOptionRequest"},
|
|
{15, nullptr, "ResolverGetOptionRequest"},
|
|
};
|
|
RegisterHandlers(functions);
|
|
}
|
|
|
|
SFDNSRES::~SFDNSRES() = default;
|
|
|
|
enum class NetDbError : s32 {
|
|
Internal = -1,
|
|
Success = 0,
|
|
HostNotFound = 1,
|
|
TryAgain = 2,
|
|
NoRecovery = 3,
|
|
NoData = 4,
|
|
};
|
|
|
|
static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) {
|
|
// These combinations have been verified on console (but are not
|
|
// exhaustive).
|
|
switch (result) {
|
|
case GetAddrInfoError::SUCCESS:
|
|
return NetDbError::Success;
|
|
case GetAddrInfoError::AGAIN:
|
|
return NetDbError::TryAgain;
|
|
case GetAddrInfoError::NODATA:
|
|
return NetDbError::HostNotFound;
|
|
case GetAddrInfoError::SERVICE:
|
|
return NetDbError::Success;
|
|
default:
|
|
return NetDbError::HostNotFound;
|
|
}
|
|
}
|
|
|
|
static Errno GetAddrInfoErrorToErrno(GetAddrInfoError result) {
|
|
// These combinations have been verified on console (but are not
|
|
// exhaustive).
|
|
switch (result) {
|
|
case GetAddrInfoError::SUCCESS:
|
|
// Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
|
|
// some reason, but that doesn't seem useful to implement.
|
|
return Errno::SUCCESS;
|
|
case GetAddrInfoError::AGAIN:
|
|
return Errno::SUCCESS;
|
|
case GetAddrInfoError::NODATA:
|
|
return Errno::SUCCESS;
|
|
case GetAddrInfoError::SERVICE:
|
|
return Errno::INVAL;
|
|
default:
|
|
return Errno::SUCCESS;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
static void Append(std::vector<u8>& vec, T t) {
|
|
size_t off = vec.size();
|
|
vec.resize(off + sizeof(T));
|
|
std::memcpy(vec.data() + off, &t, sizeof(T));
|
|
}
|
|
|
|
static void AppendNulTerminated(std::vector<u8>& vec, std::string_view str) {
|
|
size_t off = vec.size();
|
|
vec.resize(off + str.size() + 1);
|
|
std::memmove(vec.data() + off, str.data(), str.size());
|
|
}
|
|
|
|
// We implement gethostbyname using the host's getaddrinfo rather than the
|
|
// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
|
|
// behaves the same on Unix and Windows, unlike gethostbyname where Windows
|
|
// doesn't implement h_errno.
|
|
static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::AddrInfo>& vec,
|
|
std::string_view host) {
|
|
|
|
std::vector<u8> data;
|
|
// h_name: use the input hostname (append nul-terminated)
|
|
AppendNulTerminated(data, host);
|
|
// h_aliases: leave empty
|
|
|
|
Append<u32_be>(data, 0); // count of h_aliases
|
|
// (If the count were nonzero, the aliases would be appended as nul-terminated here.)
|
|
Append<u16_be>(data, static_cast<u16>(Domain::INET)); // h_addrtype
|
|
Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
|
|
// h_addr_list:
|
|
size_t count = vec.size();
|
|
ASSERT(count <= UINT32_MAX);
|
|
Append<u32_be>(data, static_cast<uint32_t>(count));
|
|
for (const Network::AddrInfo& addrinfo : vec) {
|
|
// On the Switch, this is passed through htonl despite already being
|
|
// big-endian, so it ends up as little-endian.
|
|
Append<u32_le>(data, Network::IPv4AddressToInteger(addrinfo.addr.ip));
|
|
|
|
LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host,
|
|
Network::IPv4AddressToString(addrinfo.addr.ip));
|
|
}
|
|
return data;
|
|
}
|
|
|
|
static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
|
|
struct Parameters {
|
|
u8 use_nsd_resolve;
|
|
u32 cancel_handle;
|
|
u64 process_id;
|
|
};
|
|
|
|
IPC::RequestParser rp{ctx};
|
|
const auto parameters = rp.PopRaw<Parameters>();
|
|
|
|
LOG_WARNING(
|
|
Service,
|
|
"called with ignored parameters: use_nsd_resolve={}, cancel_handle={}, process_id={}",
|
|
parameters.use_nsd_resolve, parameters.cancel_handle, parameters.process_id);
|
|
|
|
const auto host_buffer = ctx.ReadBuffer(0);
|
|
const std::string host = Common::StringFromBuffer(host_buffer);
|
|
// For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions.
|
|
|
|
auto res = Network::GetAddrInfo(host, /*service*/ std::nullopt);
|
|
if (!res.has_value()) {
|
|
return {0, Translate(res.error())};
|
|
}
|
|
|
|
std::vector<u8> data = SerializeAddrInfoAsHostEnt(res.value(), host);
|
|
u32 data_size = static_cast<u32>(data.size());
|
|
ctx.WriteBuffer(data, 0);
|
|
|
|
return {data_size, GetAddrInfoError::SUCCESS};
|
|
}
|
|
|
|
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
|
auto [data_size, emu_gai_err] = GetHostByNameRequestImpl(ctx);
|
|
|
|
IPC::ResponseBuilder rb{ctx, 5};
|
|
rb.Push(ResultSuccess);
|
|
rb.Push(static_cast<s32>(GetAddrInfoErrorToNetDbError(emu_gai_err))); // netdb error code
|
|
rb.Push(static_cast<s32>(GetAddrInfoErrorToErrno(emu_gai_err))); // errno
|
|
rb.Push(data_size); // serialized size
|
|
}
|
|
|
|
void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
|
|
auto [data_size, emu_gai_err] = GetHostByNameRequestImpl(ctx);
|
|
|
|
IPC::ResponseBuilder rb{ctx, 5};
|
|
rb.Push(ResultSuccess);
|
|
rb.Push(data_size); // serialized size
|
|
rb.Push(static_cast<s32>(GetAddrInfoErrorToNetDbError(emu_gai_err))); // netdb error code
|
|
rb.Push(static_cast<s32>(GetAddrInfoErrorToErrno(emu_gai_err))); // errno
|
|
}
|
|
|
|
static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& vec,
|
|
std::string_view host) {
|
|
// Adapted from
|
|
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
|
|
std::vector<u8> data;
|
|
|
|
for (const Network::AddrInfo& addrinfo : vec) {
|
|
// serialized addrinfo:
|
|
Append<u32_be>(data, 0xBEEFCAFE); // magic
|
|
Append<u32_be>(data, 0); // ai_flags
|
|
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family
|
|
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype
|
|
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol
|
|
Append<u32_be>(data, sizeof(SockAddrIn)); // ai_addrlen
|
|
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
|
|
|
|
// ai_addr:
|
|
Append<u16_be>(data, static_cast<u16>(Translate(addrinfo.addr.family))); // sin_family
|
|
// On the Switch, the following fields are passed through htonl despite
|
|
// already being big-endian, so they end up as little-endian.
|
|
Append<u16_le>(data, addrinfo.addr.portno); // sin_port
|
|
Append<u32_le>(data, Network::IPv4AddressToInteger(addrinfo.addr.ip)); // sin_addr
|
|
data.resize(data.size() + 8, 0); // sin_zero
|
|
|
|
if (addrinfo.canon_name.has_value()) {
|
|
AppendNulTerminated(data, *addrinfo.canon_name);
|
|
} else {
|
|
data.push_back(0);
|
|
}
|
|
|
|
LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host,
|
|
Network::IPv4AddressToString(addrinfo.addr.ip));
|
|
}
|
|
|
|
data.resize(data.size() + 4, 0); // 4-byte sentinel value
|
|
|
|
return data;
|
|
}
|
|
|
|
static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
|
|
struct Parameters {
|
|
u8 use_nsd_resolve;
|
|
u32 cancel_handle;
|
|
u64 process_id;
|
|
};
|
|
|
|
IPC::RequestParser rp{ctx};
|
|
const auto parameters = rp.PopRaw<Parameters>();
|
|
|
|
LOG_WARNING(
|
|
Service,
|
|
"called with ignored parameters: use_nsd_resolve={}, cancel_handle={}, process_id={}",
|
|
parameters.use_nsd_resolve, parameters.cancel_handle, parameters.process_id);
|
|
|
|
// TODO: If use_nsd_resolve is true, pass the name through NSD::Resolve
|
|
// before looking up.
|
|
|
|
const auto host_buffer = ctx.ReadBuffer(0);
|
|
const std::string host = Common::StringFromBuffer(host_buffer);
|
|
|
|
std::optional<std::string> service = std::nullopt;
|
|
if (ctx.CanReadBuffer(1)) {
|
|
std::span<const u8> service_buffer = ctx.ReadBuffer(1);
|
|
service = Common::StringFromBuffer(service_buffer);
|
|
}
|
|
|
|
// Serialized hints are also passed in a buffer, but are ignored for now.
|
|
|
|
auto res = Network::GetAddrInfo(host, service);
|
|
if (!res.has_value()) {
|
|
return {0, Translate(res.error())};
|
|
}
|
|
|
|
std::vector<u8> data = SerializeAddrInfo(res.value(), host);
|
|
u32 data_size = static_cast<u32>(data.size());
|
|
ctx.WriteBuffer(data, 0);
|
|
|
|
return {data_size, GetAddrInfoError::SUCCESS};
|
|
}
|
|
|
|
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
|
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
|
|
|
|
IPC::ResponseBuilder rb{ctx, 5};
|
|
rb.Push(ResultSuccess);
|
|
rb.Push(static_cast<s32>(GetAddrInfoErrorToErrno(emu_gai_err))); // errno
|
|
rb.Push(static_cast<s32>(emu_gai_err)); // getaddrinfo error code
|
|
rb.Push(data_size); // serialized size
|
|
}
|
|
|
|
void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) {
|
|
// Additional options are ignored
|
|
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
|
|
|
|
IPC::ResponseBuilder rb{ctx, 6};
|
|
rb.Push(ResultSuccess);
|
|
rb.Push(data_size); // serialized size
|
|
rb.Push(static_cast<s32>(emu_gai_err)); // getaddrinfo error code
|
|
rb.Push(static_cast<s32>(GetAddrInfoErrorToNetDbError(emu_gai_err))); // netdb error code
|
|
rb.Push(static_cast<s32>(GetAddrInfoErrorToErrno(emu_gai_err))); // errno
|
|
}
|
|
|
|
void SFDNSRES::ResolverSetOptionRequest(HLERequestContext& ctx) {
|
|
LOG_WARNING(Service, "(STUBBED) called");
|
|
|
|
IPC::ResponseBuilder rb{ctx, 3};
|
|
|
|
rb.Push(ResultSuccess);
|
|
rb.Push<s32>(0); // bsd errno
|
|
}
|
|
|
|
} // namespace Service::Sockets
|